doctor — modular diagnostic skill
This SKILL.md is a thin router. It owns NO capability knowledge. Every
diagnostic fact lives in a self-contained capability module under modules/,
read on demand. The router derives its symptom map and sweep order from each
module's front-matter (symptoms:, depends-on:) — so adding a module MD
auto-registers it with no edit here.
Modules
Each modules/<id>.md follows a uniform 5-part contract: SCOPE · KNOWLEDGE ·
CHECKS · FIX ROUTING · DERIVES-FROM (+ a <id>.knowledge.hash sidecar).
| id |
scope |
env-node |
Node runtime + OS/platform baseline |
pi-resolution |
every pi install location, divergence + floor |
peers |
pi-flows + anthropic peer, tier-1/tier-2, name-skew |
plugins-bridges |
bridge registration (packages[] vs dashboardPluginBridges) + activation |
build-reload |
three-component rebuild/reload gaps |
install-topology |
npm-global / Electron / Docker / dev + topology fixes |
model-resolution |
model:resolve handler, roles/preset, @role resolvability |
apple-tools |
iMCP (Apple PIM) provisioning state for the apple-tools plugin |
oauth-redirect-base |
which OAuth redirect base won + its tier (reverse-proxy redirect_uri_mismatch) |
How to route
The router logic lives in _lib/router.ts (load modules, build the symptom
map, topo-sort the sweep DAG, plan short-circuit). Use it — do not hand-keep a
catalog.
- Symptom phrase (e.g. "flow won't show") →
routeSymptom(modules, phrase)
returns exactly one module id. Read that module MD and run its CHECKS.
- Named capability (e.g. "check peers") → read
modules/peers.md directly.
- Full sweep (no hint, or "full") →
buildSweepOrder(modules) orders
modules env → pi → peers → plugins → build → runtime. Run in order; when a
module fails, planSweep(modules, failed) marks every dependent module
suppressed so a lower-layer failure (missing pi) is reported as the ROOT
CAUSE and NOT re-reported as a broken bridge.
Fact provenance (server up or down)
Checks are shell-first: they read files + createRequire and work with the
dashboard server DOWN. When the server is reachable, _lib/server-tier.ts
(fetchHealth, fetchPiCoreVersions) enriches the report as an ADDITIVE tier.
Every reported fact is labelled file-derived or server-enriched
(_lib/provenance.ts) so a partial (server-down) run is never mistaken for a
clean bill.
Two-tier self-update
- Tier 1 (derive-on-run) — versions, peer names, resolved paths, the
recommended set are read from live sources every run; they can never rot.
- Tier 2 (knowledge-hash) — each module stores
<id>.knowledge.hash over
the semantic tokens of its derives-from sources (_lib/knowledge-hash.ts).
On run, checkDrift(id, liveTokens, hashPath) compares live vs stored; a
mismatch flags that module's authored prose as possibly stale.
--regenerate <module>
When a module's hash drifts:
- Re-derive the module's tables from its live
derives-from sources.
- Propose edits to the module's authored prose for confirmation — never
overwrite silently.
- After confirmation,
writeStoredHash(hashPath, liveHash) to clear the drift.
Regeneration is always confirmed. The AGENTS.md Documentation Update Protocol
maps each source-of-truth change to the single module to regenerate (peer
rename → peers; pi floor bump → pi-resolution; new install platform →
install-topology; new bridge slot → plugins-bridges).
Report format
For each module report: PASS / FAIL, the resolved path(s) + version(s) per
tier, the provenance of each fact, and — on FAIL — the matching FIX ROUTING
remediation for the detected install topology. Never report a version without
the resolved path it came from.
1---2name: doctor3description: Diagnose why pi-flows, the Anthropic-messages bridge, model resolution, a pi install, a peer, a plugin/bridge, or a build/reload is broken in the pi-agent-dashboard. Derives every fact live (works with the server down). Use on "flow won't show", "bridge waiting_peers", "pi version mismatch", "which pi is this using", "diagnose the dashboard", "doctor".4license: MIT5---67# doctor — modular diagnostic skill89This SKILL.md is a **thin router**. It owns NO capability knowledge. Every10diagnostic fact lives in a self-contained capability module under `modules/`,11read on demand. The router derives its symptom map and sweep order from each12module's front-matter (`symptoms:`, `depends-on:`) — so **adding a module MD13auto-registers it with no edit here**.1415## Modules1617Each `modules/<id>.md` follows a uniform 5-part contract: **SCOPE · KNOWLEDGE ·18CHECKS · FIX ROUTING · DERIVES-FROM** (+ a `<id>.knowledge.hash` sidecar).1920| id | scope |21|---|---|22| `env-node` | Node runtime + OS/platform baseline |23| `pi-resolution` | every pi install location, divergence + floor |24| `peers` | pi-flows + anthropic peer, tier-1/tier-2, name-skew |25| `plugins-bridges` | bridge registration (packages[] vs dashboardPluginBridges) + activation |26| `build-reload` | three-component rebuild/reload gaps |27| `install-topology` | npm-global / Electron / Docker / dev + topology fixes |28| `model-resolution` | model:resolve handler, roles/preset, @role resolvability |29| `apple-tools` | iMCP (Apple PIM) provisioning state for the apple-tools plugin |30| `oauth-redirect-base` | which OAuth redirect base won + its tier (reverse-proxy `redirect_uri_mismatch`) |3132## How to route3334The router logic lives in `_lib/router.ts` (load modules, build the symptom35map, topo-sort the sweep DAG, plan short-circuit). Use it — do not hand-keep a36catalog.37381. **Symptom phrase** (e.g. "flow won't show") → `routeSymptom(modules, phrase)`39 returns exactly one module id. Read that module MD and run its CHECKS.402. **Named capability** (e.g. "check peers") → read `modules/peers.md` directly.413. **Full sweep** (no hint, or "full") → `buildSweepOrder(modules)` orders42 modules env → pi → peers → plugins → build → runtime. Run in order; when a43 module fails, `planSweep(modules, failed)` marks every dependent module44 `suppressed` so a lower-layer failure (missing pi) is reported as the ROOT45 CAUSE and NOT re-reported as a broken bridge.4647## Fact provenance (server up or down)4849Checks are **shell-first**: they read files + `createRequire` and work with the50dashboard server DOWN. When the server is reachable, `_lib/server-tier.ts`51(`fetchHealth`, `fetchPiCoreVersions`) enriches the report as an ADDITIVE tier.52Every reported fact is labelled `file-derived` or `server-enriched`53(`_lib/provenance.ts`) so a partial (server-down) run is never mistaken for a54clean bill.5556## Two-tier self-update5758- **Tier 1 (derive-on-run)** — versions, peer names, resolved paths, the59 recommended set are read from live sources every run; they can never rot.60- **Tier 2 (knowledge-hash)** — each module stores `<id>.knowledge.hash` over61 the semantic tokens of its `derives-from` sources (`_lib/knowledge-hash.ts`).62 On run, `checkDrift(id, liveTokens, hashPath)` compares live vs stored; a63 mismatch flags that module's authored prose as possibly stale.6465### `--regenerate <module>`6667When a module's hash drifts:681. Re-derive the module's tables from its live `derives-from` sources.692. Propose edits to the module's authored prose **for confirmation** — never70 overwrite silently.713. After confirmation, `writeStoredHash(hashPath, liveHash)` to clear the drift.7273Regeneration is always confirmed. The AGENTS.md Documentation Update Protocol74maps each source-of-truth change to the single module to regenerate (peer75rename → `peers`; pi floor bump → `pi-resolution`; new install platform →76`install-topology`; new bridge slot → `plugins-bridges`).7778## Report format7980For each module report: `PASS / FAIL`, the resolved path(s) + version(s) per81tier, the provenance of each fact, and — on FAIL — the matching FIX ROUTING82remediation for the detected install topology. Never report a version without83the resolved path it came from.