pk-doctor
Intro
/pk-doctor is the single user-visible health-check surface for a
processkit repository. It is the diagnostic and remediation entry point
you run after an upgrade, before a release, or any time you need to
answer the question "is this repo healthy?" in one command. The skill
pattern is deliberately modelled on npm doctor, brew doctor, and
rustup doctor, with one processkit-specific default: agents resolve
findings by default and stop at report-only mode only when the user
explicitly asks for info-only/dry-run output.
Container/host boundary
/pk-doctor, run_pk_doctor, and doctor.py are the derived-project
health surfaces for the running development container. In aibox-derived
projects, agents must use these processkit surfaces from inside the
container for processkit repository, MCP, migration, context, and
runtime-health checks.
Host-side installer diagnostics belong to the owner/operator outside the
container. pk-doctor skill text, command text, MCP descriptions, and
findings must not direct an in-container agent to run host-side
orchestrator health checks. When a finding requires host installer repair
such as sync or rebuild, phrase it as an explicit host action for the
owner and keep the processkit-side fix generic.
Severity and actionability are separate contracts. ERROR/WARN/INFO
describe blocking severity; action_required, action_kind,
default_agent_action, requires_user_confirmation, and
acceptable_resolution tell agents what must happen next. A severity
downgrade is not a resolution. Clean means no blocking findings and no
unresolved actionable findings, or a durable disposition for every
actionable finding.
This skill is the Phase 1 landing. It started with four checks. Phase 2 and
Phase 3 add more checks, lift the aggregator into an MCP server, and
harden the --fix paths — each with their own WorkItem.
Overview
Invocation
/pk-doctor # check, then resolve actionable findings
/pk-doctor info-only # report only; do not resolve
/pk-doctor dry-run # synonym for info-only
/pk-doctor --category=schemas,migrations
/pk-doctor --fix=migrations # script-level scoped fixer
/pk-doctor --fix-all # script-level fixers only; still review output
/pk-doctor --since=v0.18.2 # scope file-walk checks only
/pk-doctor --yes # non-interactive (auto-confirms safe fixes)
The MCP wrapper accepts the same confirmation explicitly as
run_pk_doctor(fix="...", yes=true). Calls without yes=true remain
detect-only when a category requires confirmation.
The script flags --fix and --fix-all are mutually exclusive. Exit
code is 0 if no ERRORs were produced, 1 otherwise. Every script run
— regardless of outcome — writes exactly one doctor.report LogEntry
via the event-log MCP.
Default remediation workflow
When a user invokes /pk-doctor or asks to check repository health, the
agent must treat remediation as the default:
- Run
run_pk_doctor() or the local doctor.py report.
- Build a queue of every
ERROR, every WARN, and every finding whose
action_required is true, including actionable INFO findings.
- Resolve safe local fixes immediately through the owning script,
command, or MCP tool. Use the reported
fix_mcp_tool,
suggested_fix, action_kind, and default_agent_action fields to
choose the narrowest correction.
- For migration findings, route through migration-management. Apply or
continue unambiguous migrations; reject malformed no-op migrations
only when the finding itself identifies the defect and a rejection
reason is clear. Ask the user only when the migration changes policy,
deletes data, has unclear intent, or conflicts with local work.
- For archive, policy, external-dependency, or user-confirmation
findings, either resolve them if the fix is already known and
non-destructive, create/link tracking when the finding calls for
tracking, or ask a concise question when a human decision is required.
- Rerun pk-doctor after remediation and report the final ERROR/WARN and
action totals.
If the user explicitly says "info only", "dry run", "report only",
"check only", "no fixes", or equivalent, stop after the report and do
not mutate anything. In that mode, still highlight the concrete next
actions so the report is actionable.
The four Phase 1 checks
schema_filename — walks every context/<kind>/**/*.md. Loads
the matching schema from src/context/schemas/<kind>.yaml (the LIVE
source, never the template mirror) and validates the file's frontmatter
against it. Also checks that the filename stem equals metadata.id
and that any date encoded in the filename matches metadata.created.
Phase 1 is WARN-only for rename suggestions — no auto-fix.
sharding — logs must live under context/logs/YYYY/MM/ subdirs;
migrations must live under their spec.state subdir (pending/,
in-progress/, applied/). WARN on any file in the wrong bucket.
migrations — calls
mcp__processkit-migration-management__list_migrations(state="pending").
INFO for the pending count; WARN for any pending entry older than
14 days. Under --fix=migrations or --fix-all, interactively
prompts Apply MIG-xxx? [y/N/s] per entry and invokes
apply_migration on yes.
drift — subprocess-invokes scripts/check-src-context-drift.sh
from the repo root. Exit 0 → one INFO "trees in sync". Exit 1 →
one WARN per offending line reported by the script.
commands_consistency — walks every
context/skills/*/*/SKILL.md and verifies that each entry in
metadata.processkit.commands: has a matching commands/<name>.md
file alongside the SKILL.md, uses the reserved pk- prefix, and has
matching argument-hint frontmatter. When .claude/commands/ or
.agents/skills/ exists, it also verifies those harness projections
exactly match the canonical command set.
Additional checks added after Phase 1:
team_consistency — wraps team-manager.check_all().
team_member_exports — reconciles active TeamMembers under context/team-members/ with Claude sub-agent export files under .claude/agents/<slug>.md (the path emitted by team-manager.export_claude_subagent). Emits WARN per active TeamMember missing an export (harness can't dispatch as subagent_type) and WARN per stale export whose slug no longer maps to an active TeamMember; INFO when the roster and the export dir are in sync. Detect-only — fix is to re-run export_claude_subagent (or export_claude_subagents) or delete the stale file. Lands the harness signal half of the sub-agent-dispatch clause from BACK-20260509_1317-WildPanda.
release_integrity — see DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing.
mcp_config_drift — reads context/.processkit-mcp-manifest.json
(produced at release time by scripts/generate-mcp-manifest.py) and
recomputes the per-skill mcp-config.json sha256es. A missing manifest
or stale aggregate surfaces as WARN (run the generator). In a
derived-project context (aibox.lock + .mcp.json both present at
the repo root), any processkit server missing from .mcp.json's
mcpServers map surfaces as ERROR with an explicit host-action hint
asking the owner to re-run the project installer/sync tool outside the
container. Exists because some host installers gate .mcp.json
re-merge on processkit version delta only, so per-skill MCP-config
edits within a release cycle may not reach derived projects until the
next version bump — see DEC-20260423_2049-VastLake.
server_header_drift — walks every context/skills/processkit/*/mcp/server.py,
hashes its PEP 723 inline metadata block (# /// script ... # ///),
and compares against the manifest's per_server_header baseline. WARN
on any drift listing the affected skill slugs — signal that someone
edited a dep header without regenerating the manifest, and the harness
needs a restart to pick up the new deps. Detect-only; the fix is
user-driven (regenerate manifest + restart harness). Per
DEC-20260424_0127-QuickPine (SharpBrook split, RapidSwan ships the
dep-drift half).
preauth_applied — compares
context/skills/processkit/skill-gate/assets/preauth.json against
.claude/settings.json and .codex/config.toml. WARNs when aibox has
not merged processkit MCP preauthorization into either harness config,
which is the common cause of reauthorization prompts after container
rebuilds.
context_consumption — emits an INFO-only estimate of processkit
context footprint by group (startup, command adapters, skill docs,
MCP configs) using ceil(utf8 bytes / 4) as the provider-neutral
token heuristic. It is intentionally not an MCP tool, so measurement
does not add another tool schema to the harness context.
It also exposes a local checkpoint/report CLI:python context/skills/processkit/pk-doctor/scripts/checks/context_consumption.py checkpoint <label>
python context/skills/processkit/pk-doctor/scripts/checks/context_consumption.py report <before> <after>
Checkpoints are JSON files under
context/.state/context-consumption/checkpoints/. Reports compare
observed processkit payloads and label token counts as local estimates,
not provider-billed usage.
v2_contracts — validates v2 API contract invariants across WorkItem, Binding, Artifact, and LogEntry entities. Catches process-instance definitions missing their definition reference, time-window bindings without a recurrence rule, orphaned cost-policy artifacts (not bound to a budget), policy supersession chains with breaks, uncalibrated eval-spec judges, and stale or missing agent-card projections. Emits ERROR for all contract violations; detect-only.
context_hygiene — validates artifact naming policies (model-spec and model-profile use timestamped ART-YYYYMMDD_HHMM-* scheme), model binding integrity (role/TeamMember defaults must target provider-neutral model-profile artifacts unless marked direct_model_pin), and cross-reference health. Also detects demoted schema kinds still present in src/, detects archive candidates, warns on mixed binding filename styles, and checks sqlite-vec semantic index health. Emits WARN / ERROR depending on severity; detect-only.
sensitive_data allowlist — intentional public or synthetic email
addresses may be listed in .pk-doctor-allowlist.toml:[sensitive_data.email_allowlist]
addresses = ["info@projectious.work", "deploy@example.local"]
The allowlist applies only to email-address findings; secrets and other PII
are never suppressed.
runtime_health — owns in-container runtime probes that host-side
installer diagnostics cannot verify directly. Checks lnav availability
for the Prefix L structured log viewer, sqlite-vec import/load health
in the pk-doctor/MCP runtime, Codex bubblewrap sandbox smoke status
when Codex is enabled, PID 1 sleep-infinity hygiene, cgroup
memory/pid/OOM pressure, processkit Python MCP process counts,
PowerKit image/plugin tree, status plugin scripts, local render
helpers, and runtime-home write probes. Emits WARN only for
container-local actionable failures; host-only runs emit a skip INFO.
entity_storage_hygiene — validates local context/
storage policy separately from template freshness: unmanaged host
artifacts, demoted legacy roots such as context/models/,
root-level migration briefings, mixed root/sharded entity layouts,
mixed filename policies, placeholder 0000 timestamps, and
TeamMember private/slug policy explanations. Emits WARN for
actionable storage drift; legacy layouts and mixed filename policies
are migration work. storage.root-migration-briefings can be
auto-resolved via --fix=entity_storage_hygiene by archiving
non-completed CLI migration briefings.
schema_vocabulary — validates closed-vocabulary subtype fields across v2 entities by comparing frontmatter values against Schema-declared known-kinds/known-types (Artifact kind, Binding type, WorkItem type, LogEntry event_type, Migration kind). legacy_known_* schema fields are not accepted as a terminal state; legacy values require an explicit data-fix Migration or a schema migration. Also validates that Migration entities include required v2 version metadata fields. Emits ERROR for unknown vocabulary values or missing version fields; detect-only.
migration_integrity — flags malformed pending migrations that exhibit the "empty-baseline" defect (same-version migrations with content in affected_groups/affected_files despite no-op intent, or affected_groups populated but affected_files empty). Per BACK-20260425_1711-CleverRiver, both patterns are defect signatures. Emits WARN with suggested fix to reject via migration-management MCP and refile the upstream bug; detect-only.
mcp_gateway — reports processkit-gateway MCP config health and harness registration state. Validates gateway config presence/structure at context/skills/processkit/processkit-gateway/mcp/mcp-config.json, checks env vars and command launch target, detects mixed gateway+granular server registrations in .mcp.json, and warns on insecure daemon bindings. Emits INFO on healthy state, WARN on minor gaps (missing proxy --url, nonlocal daemon bind), ERROR on parsing failure; detect-only.
skill_dag — builds the skill-dependency directed-acyclic-graph (DAG) from metadata.processkit.uses[*].skill entries in every context/skills/**/SKILL.md and validates references exist, graph is acyclic, and layer constraints are honored (skill layer N only uses skills with layer ≤ N). Emits ERROR for missing references, cycles (with full path), or layer violations; detect-only.
v1_entity_drift — walks every registered v2 entity directory under context/ and surfaces files whose frontmatter still declares apiVersion: …/v1. Emits WARN per file with a hardcoded successor table (Actor→TeamMember, Process→Scope+Gate, StateMachine→lifecycle metadata, Model→Artifact(model-spec)); v1 files in append-only buckets (context/logs/, context/migrations/applied/) are downgraded to a single INFO since they are intentionally historical. Under --fix=v1_entity_drift (or --fix-all) the check interactively records apply_migration intents when a pending v1->v2 Migration already covers the target, otherwise records propose_migration intents — it never auto-creates migrations or hand-edits files. Per BACK-20260509_1318-KindSpruce.
agents_md_hygiene — validates the derived-project startup
policy surface. Checks root AGENTS.md for pk-managed:* blocks
(pk-compliance-contract-v2, pk-commands), compares managed block
content against src/AGENTS.md or the latest template when available,
verifies processkit references for session start, skill routing,
entity IO, migrations, decision capture, sub-agent dispatch,
TeamMember/model binding, MCP topology, provider pointer files, and
command blocks, and flags stale guidance such as legacy actor roots,
spec.x_aibox, model-tier role tables, or duplicated provider-specific
policy. WARN findings include a briefing payload for a project agent
to reconcile the file after processkit upgrades. Detect-only; managed
block replacement and local policy merges remain user-reviewed.
sensitive_data — scans tracked text/config files for
deterministic high-signal secret and personal-data patterns: private
key blocks, provider tokens, JWTs, URL credentials, assigned
high-entropy secret values, email addresses, phone numbers, SSN-like
values, and credit-card-like numbers with a valid Luhn checksum.
Deterministic secret findings are ERROR/WARN and require human
confirmation because the correct response may be rotation, redaction,
migration, or explicit policy acceptance. The check also emits one
INFO sensitive-data.probabilistic-briefing that tells the
derived-project agent what regex cannot prove: real names and aliases,
addresses, dates of birth, customer/account IDs, medical/financial/
employment data, screenshots, archives, database dumps, logs, and
non-standard short secrets. The briefing includes non-triggering
deterministic and probabilistic example prompts so an agent knows what
to search for without adding realistic secrets to the shipped scanner.
This is an advisory layer, not a complete DLP engine.
supply_chain — performs offline supply-chain hygiene checks for
dependency manifests/lockfiles, license policy, and local scanner
outcomes:
- emits
ERROR on denied licenses, missing application lockfiles, and
high/critical vulnerabilities;
- emits
WARN on unknown/review/allowed-policy misses and skipped
security scanners;
- emits
INFO for inventory counts and SBOM discoveries.
Security/outdated/supplier-quality checks stay advisory and require
explicit core-surface opt-in data; pk-doctor performs no outbound
network calls during this check.
What doctor will NEVER do
- Hand-edit any file under
context/. All writes route through the
appropriate MCP tool (e.g. apply_migration).
- Touch
context/templates/ (read-only diff baseline).
- Touch
.mcp.json (merged file; use the generated per-skill
mcp/mcp-config.json and gateway catalog instead).
- Write to
context/logs/ directly — reports go through
mcp__processkit-event-log__log_event.
- Treat a clean severity tally as done while actionable INFO findings
remain unresolved.
- Stop after the report unless the user explicitly asked for info-only,
dry-run, report-only, check-only, or no-fix behavior.
Report shape
After the stdout human-readable summary, the skill emits a single
doctor.report LogEntry:
{
"event_type": "doctor.report",
"summary": "/pk-doctor --fix=migrations — 0 ERROR / 2 WARN / 3 INFO",
"details": {
"doctor_version": "1.0.0",
"invocation": "/pk-doctor --fix=migrations",
"categories": {
"schema_filename": {"ERROR": 0, "WARN": 1, "INFO": 140},
"sharding": {"ERROR": 0, "WARN": 0, "INFO": 1},
"migrations": {"ERROR": 0, "WARN": 1, "INFO": 1},
"drift": {"ERROR": 0, "WARN": 0, "INFO": 1}
},
"action_totals": {
"actionable": 2,
"needs_user_confirmation": 1,
"needs_tracking": 0,
"safe_fix": 1,
"migration_needed": 1,
"archive_needed": 0,
"policy_decision_needed": 0,
"external_dependency": 0
},
"top_findings": [
{
"severity": "WARN",
"id": "migration.stale-pending",
"entity_ref": "MIG-...",
"message": "...",
"action_required": true,
"action_kind": "migration_needed",
"default_agent_action": "create_migration",
"requires_user_confirmation": true,
"acceptable_resolution": "migrated"
}
],
"fixes_applied": [],
"duration_ms": 2340
}
}
top_findings caps at 20 to keep the log entry compact; the full
stdout report remains authoritative for the details of a specific run.
The structured JSON payload returned by run_pk_doctor includes the
same action_totals and actionability fields on every finding.
Interactive prompts
Interactive prompts gate on sys.stdin.isatty(). If a --fix was
requested but the process isn't attached to a terminal, the skill emits
a WARN (fix.non-interactive) and skips the fix — it does not silently
prompt and hang. --yes auto-confirms every prompt except any marked
data_loss=True (none in Phase 1, but the hook is present).
--since scope
--since=<git-ref> restricts only the file-walk checks
(schema_filename, sharding). The migrations and drift checks
always run full-scan: migrations pull from an MCP tool with its own
state, and drift compares entire trees.
Resolved policy defaults
These were agreed with the owner during shape review:
- YAML-datetime vs JSON-Schema string coercion failures surface as
WARN schema.datetime-coercion (not ERROR). Parser-layer quirk.
- Command projections — canonical command adapters live under
context/skills/**/commands/; slash-capable harness projections live
under .claude/commands/; natural-language command shims live under
.agents/skills/. src/context/ mirrors only the canonical source.
- Filename auto-rename — WARN only in Phase 1. Phase 2 adds
orchestrated rename + reindex.
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Confusing remediation mode with blind
--fix-all. The skill is
remediation-first by default, but that does not mean doctor.py --fix-all --yes is safe. First run the report, read actionability,
then apply the narrowest known fix through the owning tool. Ask only
when a finding is destructive, policy-sensitive, external, or unclear.
- Stopping after a report when the user did not ask for info-only.
/pk-doctor means "check and clean up what can be cleaned up." If the
user only wants a briefing, they must say info-only, dry-run,
report-only, check-only, or no fixes.
- Treating drift warnings as noise. The drift check exists because
v0.15.0–v0.18.0 shipped four releases with silent drift. If the
drift check produces a WARN, it has found a real divergence —
do not dismiss without investigating.
- Hand-editing files to "clear" a doctor warning. Doctor warnings
are diagnostic. Fixing the underlying cause through the right MCP
write tool (not by editing the offending file directly) preserves
the audit trail. A WARN that reappears next run is more useful than
a hand-fixed tree with no record of the fix.
- Validating against
context/templates/ schemas. The template
mirror is a diff baseline, not the live schema source. Validation
must read src/context/schemas/<kind>.yaml — the authoritative
copy. Validating against templates will produce stale false
positives after any schema evolution.
- Forgetting the
doctor.report LogEntry on dry-run. Even a
detect-only run must emit the report: it is the audit record of the
invocation. A run that prints to stdout but logs nothing strands
the next session with no record of what was checked.
- Running doctor mid-migration. Doctor's
migrations check will
list pending migrations and (under --fix) offer to apply them. If
you are already in the middle of a manual migration review, running
--fix=migrations can reorder work silently. Finish or pause the
manual review first.
Full reference
CLI contract
doctor.py [--category=LIST] [--fix=LIST | --fix-all] [--since=REF] [--yes]
| Flag |
Meaning |
--category |
Comma list of categories to run. Default: all four. |
--fix |
Comma list of categories to enable fixes for. Mutex with --fix-all. |
--fix-all |
Enable fixes for every category that supports them. Mutex with --fix. |
--since |
Git ref; restricts schema_filename + sharding to files changed since. |
--yes |
Auto-confirm all non-data_loss fix prompts. |
Adding a check in Phase 2
- Drop a new module in
scripts/checks/<name>.py exporting a
run(ctx) -> list[CheckResult] function (and optional
run_fix(ctx, results) -> list[dict]).
- Register it in
scripts/checks/__init__.py in REGISTRY.
- Add a gotcha for that check here in SKILL.md.
- Mirror both files into
src/context/skills/processkit/pk-doctor/.
The aggregator in doctor.py does not need to change.
What this skill does NOT do (yet)
- Validate cross-references (Phase 2).
- Check
PROVENANCE.toml drift (Phase 2).
- Check
mcp-config.json integrity (Phase 2).
- Smoke-import MCP servers (Phase 2).
- Detect version skew between
aibox.lock and installed SKILL.md pins
(Phase 2).
- Expose itself as an MCP server for machine invocation (Phase 3, only
if demand shows up).
1---2name: pk-doctor-183description: Aggregator health-check for a processkit-managed repository. Runs a fixed suite of checks over the live context/ tree plus src/context/ drift, then resolves ERROR / WARN / actionable INFO findings by default. Use when the user invokes `/pk-doctor`, asks "is this repo healthy", or has just finished an upgrade and wants a sanity pass. Info-only mode is opt-in: stop after the report only when the user explicitly says info-only, dry-run, report-only, check-only, or no fixes.4---56# pk-doctor78## Intro910`/pk-doctor` is the single user-visible health-check surface for a11processkit repository. It is the diagnostic and remediation entry point12you run after an upgrade, before a release, or any time you need to13answer the question "is this repo healthy?" in one command. The skill14pattern is deliberately modelled on `npm doctor`, `brew doctor`, and15`rustup doctor`, with one processkit-specific default: agents resolve16findings by default and stop at report-only mode only when the user17explicitly asks for info-only/dry-run output.1819### Container/host boundary2021`/pk-doctor`, `run_pk_doctor`, and `doctor.py` are the derived-project22health surfaces for the running development container. In aibox-derived23projects, agents must use these processkit surfaces from inside the24container for processkit repository, MCP, migration, context, and25runtime-health checks.2627Host-side installer diagnostics belong to the owner/operator outside the28container. pk-doctor skill text, command text, MCP descriptions, and29findings must not direct an in-container agent to run host-side30orchestrator health checks. When a finding requires host installer repair31such as sync or rebuild, phrase it as an explicit host action for the32owner and keep the processkit-side fix generic.3334Severity and actionability are separate contracts. `ERROR`/`WARN`/`INFO`35describe blocking severity; `action_required`, `action_kind`,36`default_agent_action`, `requires_user_confirmation`, and37`acceptable_resolution` tell agents what must happen next. A severity38downgrade is not a resolution. Clean means no blocking findings and no39unresolved actionable findings, or a durable disposition for every40actionable finding.4142This skill is the Phase 1 landing. It started with four checks. Phase 2 and43Phase 3 add more checks, lift the aggregator into an MCP server, and44harden the `--fix` paths — each with their own WorkItem.4546## Overview4748### Invocation4950```51/pk-doctor # check, then resolve actionable findings52/pk-doctor info-only # report only; do not resolve53/pk-doctor dry-run # synonym for info-only54/pk-doctor --category=schemas,migrations55/pk-doctor --fix=migrations # script-level scoped fixer56/pk-doctor --fix-all # script-level fixers only; still review output57/pk-doctor --since=v0.18.2 # scope file-walk checks only58/pk-doctor --yes # non-interactive (auto-confirms safe fixes)59```6061The MCP wrapper accepts the same confirmation explicitly as62`run_pk_doctor(fix="...", yes=true)`. Calls without `yes=true` remain63detect-only when a category requires confirmation.6465The script flags `--fix` and `--fix-all` are mutually exclusive. Exit66code is `0` if no ERRORs were produced, `1` otherwise. Every script run67— regardless of outcome — writes exactly one `doctor.report` LogEntry68via the event-log MCP.6970### Default remediation workflow7172When a user invokes `/pk-doctor` or asks to check repository health, the73agent must treat remediation as the default:74751. Run `run_pk_doctor()` or the local `doctor.py` report.762. Build a queue of every `ERROR`, every `WARN`, and every finding whose77 `action_required` is true, including actionable INFO findings.783. Resolve safe local fixes immediately through the owning script,79 command, or MCP tool. Use the reported `fix_mcp_tool`,80 `suggested_fix`, `action_kind`, and `default_agent_action` fields to81 choose the narrowest correction.824. For migration findings, route through migration-management. Apply or83 continue unambiguous migrations; reject malformed no-op migrations84 only when the finding itself identifies the defect and a rejection85 reason is clear. Ask the user only when the migration changes policy,86 deletes data, has unclear intent, or conflicts with local work.875. For archive, policy, external-dependency, or user-confirmation88 findings, either resolve them if the fix is already known and89 non-destructive, create/link tracking when the finding calls for90 tracking, or ask a concise question when a human decision is required.916. Rerun pk-doctor after remediation and report the final ERROR/WARN and92 action totals.9394If the user explicitly says "info only", "dry run", "report only",95"check only", "no fixes", or equivalent, stop after the report and do96not mutate anything. In that mode, still highlight the concrete next97actions so the report is actionable.9899### The four Phase 1 checks1001011. **`schema_filename`** — walks every `context/<kind>/**/*.md`. Loads102 the matching schema from `src/context/schemas/<kind>.yaml` (the LIVE103 source, never the template mirror) and validates the file's frontmatter104 against it. Also checks that the filename stem equals `metadata.id`105 and that any date encoded in the filename matches `metadata.created`.106 Phase 1 is WARN-only for rename suggestions — no auto-fix.1072. **`sharding`** — logs must live under `context/logs/YYYY/MM/` subdirs;108 migrations must live under their `spec.state` subdir (`pending/`,109 `in-progress/`, `applied/`). WARN on any file in the wrong bucket.1103. **`migrations`** — calls111 `mcp__processkit-migration-management__list_migrations(state="pending")`.112 INFO for the pending count; WARN for any pending entry older than113 14 days. Under `--fix=migrations` or `--fix-all`, interactively114 prompts `Apply MIG-xxx? [y/N/s]` per entry and invokes115 `apply_migration` on yes.1164. **`drift`** — subprocess-invokes `scripts/check-src-context-drift.sh`117 from the repo root. Exit 0 → one INFO "trees in sync". Exit 1 →118 one WARN per offending line reported by the script.1195. **`commands_consistency`** — walks every120 `context/skills/*/*/SKILL.md` and verifies that each entry in121 `metadata.processkit.commands:` has a matching `commands/<name>.md`122 file alongside the SKILL.md, uses the reserved `pk-` prefix, and has123 matching `argument-hint` frontmatter. When `.claude/commands/` or124 `.agents/skills/` exists, it also verifies those harness projections125 exactly match the canonical command set.126127Additional checks added after Phase 1:128129- **`team_consistency`** — wraps `team-manager.check_all()`.130- **`team_member_exports`** — reconciles active TeamMembers under `context/team-members/` with Claude sub-agent export files under `.claude/agents/<slug>.md` (the path emitted by `team-manager.export_claude_subagent`). Emits WARN per active TeamMember missing an export (harness can't dispatch as `subagent_type`) and WARN per stale export whose slug no longer maps to an active TeamMember; INFO when the roster and the export dir are in sync. Detect-only — fix is to re-run `export_claude_subagent` (or `export_claude_subagents`) or delete the stale file. Lands the harness signal half of the sub-agent-dispatch clause from BACK-20260509_1317-WildPanda.131- **`release_integrity`** — see DEC-20260422_1348-SnowyWolf-local-only-release-bulletproofing.132- **`mcp_config_drift`** — reads `context/.processkit-mcp-manifest.json`133 (produced at release time by `scripts/generate-mcp-manifest.py`) and134 recomputes the per-skill `mcp-config.json` sha256es. A missing manifest135 or stale aggregate surfaces as WARN (run the generator). In a136 derived-project context (`aibox.lock` + `.mcp.json` both present at137 the repo root), any processkit server missing from `.mcp.json`'s138 `mcpServers` map surfaces as ERROR with an explicit host-action hint139 asking the owner to re-run the project installer/sync tool outside the140 container. Exists because some host installers gate `.mcp.json`141 re-merge on processkit version delta only, so per-skill MCP-config142 edits within a release cycle may not reach derived projects until the143 next version bump — see DEC-20260423_2049-VastLake.144- **`server_header_drift`** — walks every `context/skills/processkit/*/mcp/server.py`,145 hashes its PEP 723 inline metadata block (`# /// script` ... `# ///`),146 and compares against the manifest's `per_server_header` baseline. WARN147 on any drift listing the affected skill slugs — signal that someone148 edited a dep header without regenerating the manifest, and the harness149 needs a restart to pick up the new deps. Detect-only; the fix is150 user-driven (regenerate manifest + restart harness). Per151 DEC-20260424_0127-QuickPine (SharpBrook split, RapidSwan ships the152 dep-drift half).153- **`preauth_applied`** — compares154 `context/skills/processkit/skill-gate/assets/preauth.json` against155 `.claude/settings.json` and `.codex/config.toml`. WARNs when aibox has156 not merged processkit MCP preauthorization into either harness config,157 which is the common cause of reauthorization prompts after container158 rebuilds.159- **`context_consumption`** — emits an INFO-only estimate of processkit160 context footprint by group (`startup`, command adapters, skill docs,161 MCP configs) using `ceil(utf8 bytes / 4)` as the provider-neutral162 token heuristic. It is intentionally not an MCP tool, so measurement163 does not add another tool schema to the harness context.164 It also exposes a local checkpoint/report CLI:165 ```sh166 python context/skills/processkit/pk-doctor/scripts/checks/context_consumption.py checkpoint <label>167 python context/skills/processkit/pk-doctor/scripts/checks/context_consumption.py report <before> <after>168 ```169 Checkpoints are JSON files under170 `context/.state/context-consumption/checkpoints/`. Reports compare171 observed processkit payloads and label token counts as local estimates,172 not provider-billed usage.173- **`v2_contracts`** — validates v2 API contract invariants across WorkItem, Binding, Artifact, and LogEntry entities. Catches process-instance definitions missing their definition reference, time-window bindings without a recurrence rule, orphaned cost-policy artifacts (not bound to a budget), policy supersession chains with breaks, uncalibrated eval-spec judges, and stale or missing agent-card projections. Emits ERROR for all contract violations; detect-only.174- **`context_hygiene`** — validates artifact naming policies (model-spec and model-profile use timestamped `ART-YYYYMMDD_HHMM-*` scheme), model binding integrity (role/TeamMember defaults must target provider-neutral model-profile artifacts unless marked direct_model_pin), and cross-reference health. Also detects demoted schema kinds still present in src/, detects archive candidates, warns on mixed binding filename styles, and checks sqlite-vec semantic index health. Emits WARN / ERROR depending on severity; detect-only.175- **`sensitive_data` allowlist** — intentional public or synthetic email176 addresses may be listed in `.pk-doctor-allowlist.toml`:177 ```toml178 [sensitive_data.email_allowlist]179 addresses = ["info@projectious.work", "deploy@example.local"]180 ```181 The allowlist applies only to email-address findings; secrets and other PII182 are never suppressed.183- **`runtime_health`** — owns in-container runtime probes that host-side184 installer diagnostics cannot verify directly. Checks lnav availability185 for the Prefix L structured log viewer, sqlite-vec import/load health186 in the pk-doctor/MCP runtime, Codex bubblewrap sandbox smoke status187 when Codex is enabled, PID 1 sleep-infinity hygiene, cgroup188 memory/pid/OOM pressure, processkit Python MCP process counts,189 PowerKit image/plugin tree, status plugin scripts, local render190 helpers, and runtime-home write probes. Emits WARN only for191 container-local actionable failures; host-only runs emit a skip INFO.192- **`entity_storage_hygiene`** — validates local `context/`193 storage policy separately from template freshness: unmanaged host194 artifacts, demoted legacy roots such as `context/models/`,195 root-level migration briefings, mixed root/sharded entity layouts,196 mixed filename policies, placeholder `0000` timestamps, and197 TeamMember private/slug policy explanations. Emits WARN for198 actionable storage drift; legacy layouts and mixed filename policies199 are migration work. `storage.root-migration-briefings` can be200 auto-resolved via `--fix=entity_storage_hygiene` by archiving201 non-completed CLI migration briefings.202- **`schema_vocabulary`** — validates closed-vocabulary subtype fields across v2 entities by comparing frontmatter values against Schema-declared known-kinds/known-types (Artifact kind, Binding type, WorkItem type, LogEntry event_type, Migration kind). `legacy_known_*` schema fields are not accepted as a terminal state; legacy values require an explicit data-fix Migration or a schema migration. Also validates that Migration entities include required v2 version metadata fields. Emits ERROR for unknown vocabulary values or missing version fields; detect-only.203- **`migration_integrity`** — flags malformed pending migrations that exhibit the "empty-baseline" defect (same-version migrations with content in affected_groups/affected_files despite no-op intent, or affected_groups populated but affected_files empty). Per BACK-20260425_1711-CleverRiver, both patterns are defect signatures. Emits WARN with suggested fix to reject via migration-management MCP and refile the upstream bug; detect-only.204- **`mcp_gateway`** — reports processkit-gateway MCP config health and harness registration state. Validates gateway config presence/structure at `context/skills/processkit/processkit-gateway/mcp/mcp-config.json`, checks env vars and command launch target, detects mixed gateway+granular server registrations in `.mcp.json`, and warns on insecure daemon bindings. Emits INFO on healthy state, WARN on minor gaps (missing proxy --url, nonlocal daemon bind), ERROR on parsing failure; detect-only.205- **`skill_dag`** — builds the skill-dependency directed-acyclic-graph (DAG) from `metadata.processkit.uses[*].skill` entries in every `context/skills/**/SKILL.md` and validates references exist, graph is acyclic, and layer constraints are honored (skill layer N only uses skills with layer ≤ N). Emits ERROR for missing references, cycles (with full path), or layer violations; detect-only.206- **`v1_entity_drift`** — walks every registered v2 entity directory under `context/` and surfaces files whose frontmatter still declares `apiVersion: …/v1`. Emits WARN per file with a hardcoded successor table (Actor→TeamMember, Process→Scope+Gate, StateMachine→lifecycle metadata, Model→Artifact(model-spec)); v1 files in append-only buckets (`context/logs/`, `context/migrations/applied/`) are downgraded to a single INFO since they are intentionally historical. Under `--fix=v1_entity_drift` (or `--fix-all`) the check interactively records `apply_migration` intents when a pending v1->v2 Migration already covers the target, otherwise records `propose_migration` intents — it never auto-creates migrations or hand-edits files. Per BACK-20260509_1318-KindSpruce.207- **`agents_md_hygiene`** — validates the derived-project startup208 policy surface. Checks root `AGENTS.md` for `pk-managed:*` blocks209 (`pk-compliance-contract-v2`, `pk-commands`), compares managed block210 content against `src/AGENTS.md` or the latest template when available,211 verifies processkit references for session start, skill routing,212 entity IO, migrations, decision capture, sub-agent dispatch,213 TeamMember/model binding, MCP topology, provider pointer files, and214 command blocks, and flags stale guidance such as legacy actor roots,215 `spec.x_aibox`, model-tier role tables, or duplicated provider-specific216 policy. WARN findings include a `briefing` payload for a project agent217 to reconcile the file after processkit upgrades. Detect-only; managed218 block replacement and local policy merges remain user-reviewed.219- **`sensitive_data`** — scans tracked text/config files for220 deterministic high-signal secret and personal-data patterns: private221 key blocks, provider tokens, JWTs, URL credentials, assigned222 high-entropy secret values, email addresses, phone numbers, SSN-like223 values, and credit-card-like numbers with a valid Luhn checksum.224 Deterministic secret findings are ERROR/WARN and require human225 confirmation because the correct response may be rotation, redaction,226 migration, or explicit policy acceptance. The check also emits one227 INFO `sensitive-data.probabilistic-briefing` that tells the228 derived-project agent what regex cannot prove: real names and aliases,229 addresses, dates of birth, customer/account IDs, medical/financial/230 employment data, screenshots, archives, database dumps, logs, and231 non-standard short secrets. The briefing includes non-triggering232 deterministic and probabilistic example prompts so an agent knows what233 to search for without adding realistic secrets to the shipped scanner.234 This is an advisory layer, not a complete DLP engine.235- **`supply_chain`** — performs offline supply-chain hygiene checks for236 dependency manifests/lockfiles, license policy, and local scanner237 outcomes:238 - emits `ERROR` on denied licenses, missing application lockfiles, and239 high/critical vulnerabilities;240 - emits `WARN` on unknown/review/allowed-policy misses and skipped241 security scanners;242 - emits `INFO` for inventory counts and SBOM discoveries.243 Security/outdated/supplier-quality checks stay advisory and require244 explicit core-surface opt-in data; pk-doctor performs no outbound245 network calls during this check.246247### What doctor will NEVER do248249- Hand-edit any file under `context/`. All writes route through the250 appropriate MCP tool (e.g. `apply_migration`).251- Touch `context/templates/` (read-only diff baseline).252- Touch `.mcp.json` (merged file; use the generated per-skill253 `mcp/mcp-config.json` and gateway catalog instead).254- Write to `context/logs/` directly — reports go through255 `mcp__processkit-event-log__log_event`.256- Treat a clean severity tally as done while actionable INFO findings257 remain unresolved.258- Stop after the report unless the user explicitly asked for info-only,259 dry-run, report-only, check-only, or no-fix behavior.260261### Report shape262263After the stdout human-readable summary, the skill emits a single264`doctor.report` LogEntry:265266```json267{268 "event_type": "doctor.report",269 "summary": "/pk-doctor --fix=migrations — 0 ERROR / 2 WARN / 3 INFO",270 "details": {271 "doctor_version": "1.0.0",272 "invocation": "/pk-doctor --fix=migrations",273 "categories": {274 "schema_filename": {"ERROR": 0, "WARN": 1, "INFO": 140},275 "sharding": {"ERROR": 0, "WARN": 0, "INFO": 1},276 "migrations": {"ERROR": 0, "WARN": 1, "INFO": 1},277 "drift": {"ERROR": 0, "WARN": 0, "INFO": 1}278 },279 "action_totals": {280 "actionable": 2,281 "needs_user_confirmation": 1,282 "needs_tracking": 0,283 "safe_fix": 1,284 "migration_needed": 1,285 "archive_needed": 0,286 "policy_decision_needed": 0,287 "external_dependency": 0288 },289 "top_findings": [290 {291 "severity": "WARN",292 "id": "migration.stale-pending",293 "entity_ref": "MIG-...",294 "message": "...",295 "action_required": true,296 "action_kind": "migration_needed",297 "default_agent_action": "create_migration",298 "requires_user_confirmation": true,299 "acceptable_resolution": "migrated"300 }301 ],302 "fixes_applied": [],303 "duration_ms": 2340304 }305}306```307308`top_findings` caps at 20 to keep the log entry compact; the full309stdout report remains authoritative for the details of a specific run.310The structured JSON payload returned by `run_pk_doctor` includes the311same `action_totals` and actionability fields on every finding.312313### Interactive prompts314315Interactive prompts gate on `sys.stdin.isatty()`. If a `--fix` was316requested but the process isn't attached to a terminal, the skill emits317a `WARN` (`fix.non-interactive`) and skips the fix — it does not silently318prompt and hang. `--yes` auto-confirms every prompt except any marked319`data_loss=True` (none in Phase 1, but the hook is present).320321### `--since` scope322323`--since=<git-ref>` restricts only the file-walk checks324(`schema_filename`, `sharding`). The `migrations` and `drift` checks325always run full-scan: migrations pull from an MCP tool with its own326state, and drift compares entire trees.327328### Resolved policy defaults329330These were agreed with the owner during shape review:331332- **YAML-datetime vs JSON-Schema string coercion** failures surface as333 `WARN schema.datetime-coercion` (not ERROR). Parser-layer quirk.334- **Command projections** — canonical command adapters live under335 `context/skills/**/commands/`; slash-capable harness projections live336 under `.claude/commands/`; natural-language command shims live under337 `.agents/skills/`. `src/context/` mirrors only the canonical source.338- **Filename auto-rename** — WARN only in Phase 1. Phase 2 adds339 orchestrated rename + reindex.340341## Gotchas342343Agent-specific failure modes — provider-neutral pause-and-self-check items:344345- **Confusing remediation mode with blind `--fix-all`.** The skill is346 remediation-first by default, but that does not mean `doctor.py347 --fix-all --yes` is safe. First run the report, read actionability,348 then apply the narrowest known fix through the owning tool. Ask only349 when a finding is destructive, policy-sensitive, external, or unclear.350- **Stopping after a report when the user did not ask for info-only.**351 `/pk-doctor` means "check and clean up what can be cleaned up." If the352 user only wants a briefing, they must say info-only, dry-run,353 report-only, check-only, or no fixes.354- **Treating drift warnings as noise.** The drift check exists because355 v0.15.0–v0.18.0 shipped four releases with silent drift. If the356 drift check produces a WARN, it has found a real divergence —357 do not dismiss without investigating.358- **Hand-editing files to "clear" a doctor warning.** Doctor warnings359 are diagnostic. Fixing the underlying cause through the right MCP360 write tool (not by editing the offending file directly) preserves361 the audit trail. A WARN that reappears next run is more useful than362 a hand-fixed tree with no record of the fix.363- **Validating against `context/templates/` schemas.** The template364 mirror is a diff baseline, not the live schema source. Validation365 must read `src/context/schemas/<kind>.yaml` — the authoritative366 copy. Validating against templates will produce stale false367 positives after any schema evolution.368- **Forgetting the `doctor.report` LogEntry on dry-run.** Even a369 detect-only run must emit the report: it is the audit record of the370 invocation. A run that prints to stdout but logs nothing strands371 the next session with no record of what was checked.372- **Running doctor mid-migration.** Doctor's `migrations` check will373 list pending migrations and (under `--fix`) offer to apply them. If374 you are already in the middle of a manual migration review, running375 `--fix=migrations` can reorder work silently. Finish or pause the376 manual review first.377378## Full reference379380### CLI contract381382```383doctor.py [--category=LIST] [--fix=LIST | --fix-all] [--since=REF] [--yes]384```385386| Flag | Meaning |387|----------------|---------|388| `--category` | Comma list of categories to run. Default: all four. |389| `--fix` | Comma list of categories to enable fixes for. Mutex with `--fix-all`. |390| `--fix-all` | Enable fixes for every category that supports them. Mutex with `--fix`. |391| `--since` | Git ref; restricts `schema_filename` + `sharding` to files changed since. |392| `--yes` | Auto-confirm all non-`data_loss` fix prompts. |393394### Adding a check in Phase 23953961. Drop a new module in `scripts/checks/<name>.py` exporting a397 `run(ctx) -> list[CheckResult]` function (and optional398 `run_fix(ctx, results) -> list[dict]`).3992. Register it in `scripts/checks/__init__.py` in `REGISTRY`.4003. Add a gotcha for that check here in SKILL.md.4014. Mirror both files into `src/context/skills/processkit/pk-doctor/`.402403The aggregator in `doctor.py` does not need to change.404405### What this skill does NOT do (yet)406407- Validate cross-references (Phase 2).408- Check `PROVENANCE.toml` drift (Phase 2).409- Check `mcp-config.json` integrity (Phase 2).410- Smoke-import MCP servers (Phase 2).411- Detect version skew between `aibox.lock` and installed SKILL.md pins412 (Phase 2).413- Expose itself as an MCP server for machine invocation (Phase 3, only414 if demand shows up).