doctor
Diagnose and repair a broken or degraded skill-concierge install. Use this skill when the skill-search MCP won't connect, search_skills returns nothing or stale results, skills have gone dark, the MCP
SKILL.mdskills/doctor/SKILL.md
GitHubskill-concierge doctor
A deployment-layer health check with safe auto-fixes. It diagnoses what setup.sh
provisions and delegates the retrieval diagnostic (embedder, index, dark/stale skills) to
the engine's own skill-search --health, so the two never drift.
Steps
Diagnose (read-only) from the plugin root:
python3 "$CLAUDE_PLUGIN_ROOT/scripts/doctor.py"It prints a check matrix and an overall
status: OK | WARN | FAIL(exit1on FAIL).Read the matrix. Each row is
[✓|!|✗] <check> <detail>:Check What it verifies Python 3.10-3.12 a usable interpreter (only when the venv is missing) Engine venv the stable venv + skill-searchbin existEngine freshness the venv's COPIED engine matches the deployed vendored source — catches a stale MCP serving old code after /plugin update(the venv is built once by setup.sh and never refreshed by an update); WARN → rerun setup.shMCP wiring .mcp.jsonis valid +bin/skill-search-mcpis executableIndex owner the local index owner (ADR-0070; replaces the Qdrant container and the Docker embed shim) answers its Qdrant-compatible REST port + /health, itscode_version, and its SQLite file's integrity/point countsRetrieval health engine --health: embedder reachable, no dark/stale skills, index freshMulti-vector layer trigger points present (MAX-pool retrieval, ADR-0012); WARNs if SKILL_MULTIVECTORis on but none existCorpus health per-skill calibration ok/weak/no-signalcounts fromeval/thresholds.jsonRetrieval flywheel is the utterance-generation LLM configured + reachable, per-skill utterance coverage (which skills lack utterances), and the last flywheel run from the global manifest ( ~/.claude/skill-concierge/flywheel-manifest.json). Read-only, fail-open (never FAIL — the flywheel is optional). ADR-0027Trigger hygiene junk already stored in the utterance layer — empty / one-char / repeated phrases a degraded model wrote. Coverage only measures presence, so such a skill still counts as "covered" and the generator then skips it forever; this row is what catches it. WARN + auto-fix (purge). Read-only, fail-open Settings overrides skillOverridesapplied to~/.claude/settings.json; now detects drift from the installed catalogue (apply-overrides.py --check) → WARN + auto-fix (ADR-0025)Ledger dir the telemetry log directory is writable Duplicate MCP warns if a leftover user-scope skill-searchMCP also existsMCP reachable warns when the skill-search MCP is installed but not connected here — disabled for the project via /mcp, pending approval, or failed. Every other row answers is the index healthy; this one answers can the agent reach it, and the two come apart: a project-disabled MCP leaves the engine fully green whilesearch_skills/get_skilldo not exist for that session. The enforcer's per-turn offer is unaffected (it queries Qdrant over REST), so WARN not FAIL. Read-only, fail-open, no auto-fix — re-enabling is a user actionAuto-fix the safe failures:
python3 "$CLAUDE_PLUGIN_ROOT/scripts/doctor.py" --fix--fixapplies only fast, safe repairs, then re-checks: start a stopped index owner (also stopping + disabling a revivedskill-search-qdrant/skill-concierge-embed-shimcontainer if an old harness copy brought Docker back onto the owner's ports),--reindexa stale/dark index, re-apply the settings overrides, purge junk utterance layers. It never builds or refreshes the keep-off map — that is consent-only (ADR-0077); theKeep-offrow only reports.Heads-up: the overrides fix writes
~/.claude/settings.json(backed up first) viascripts/apply-overrides.py. The user invoking doctor is consent for that; mention it before running--fixso the change isn't a surprise.The
Trigger hygienefix DELETES data — it drops each junk utterance layer from the canonical corpus~/.claude/skill-concierge/triggers.json(backed up totriggers.json.bak-junk-<ts>first) and clears that skill's generation-cache key, then reindexes. It does not regenerate: doctor never calls the LLM. A purged skill falls back to description+body retrieval (no worse than junk phrases pointing the wrong way) until the next flywheel run rewrites it — which it now will, because the cleared cache key stops the generator skipping it as "covered".Handle what
--fixcan't. doctor never auto-builds the venv or the container (slow, heavyweight). IfEngine venvis FAIL orEngine freshnessis WARN, run theskill-concierge:setupskill (or./setup.sh) — that rebuilds/refreshes the stable venv from the deployed source. For a duplicate MCP, run the printedclaude mcp removecommand. After any fix, restart Claude Code if the MCP wiring or engine changed.Doctor is a precondition, not the proof. A green doctor shows files and config exist. It does not show that a harness runs the concierge: OpenCode's plugin failed to load, and Codex skipped the enforcer hook, while doctor stayed green (ADR-0090). To prove a harness works, run the live smoke:
python3 "$CLAUDE_PLUGIN_ROOT/scripts/smoke.py"It runs one real headless turn per harness and passes a harness only when its temp ledger holds an
offerrow and asearchrow. It spends one model call per harness. A missing CLI is FAIL; ZCode has no command line and shows UNPROVEN. A harness in--advisory(default$SMOKE_ADVISORY) still reports but does not block. Do not claim a harness works on doctor alone.
Symptom → check shortcuts
/mcpshows skill-search not connected →Engine venv/MCP wiring. Fix: run setup.search behaves like an OLD version after a plugin update →
Engine freshness. The MCP venv is stale (copied engine, not refreshed by/plugin update). Fix: rerun setup.sh, then restart.search returns nothing or stale →
Index owner/Retrieval health. Fix:--fix(reindex) — note the SessionStartauto_reindexhook now self-heals index staleness in the background.Keep-offrow only reports;--fixnever touches the map (ADR-0077). No map, or a map without"approved_by_user": true, hides nothing and is OK (an unapproved map's names are reported as ignored). WARN means the file is invalid JSON or has nokeep_offlist. To get suppression, runpython3 scripts/build_keep_off.pyto see a proposal and--applyonly after Thinh says yes. A skill you take inline that gets dropped → add it to keep-on (exempt).skills you expect aren't offered / a new skill leaks its full description →
Settings overrides(now flags override drift). Fix:--fix(re-apply) — or nothing: the SessionStartauto_overrideshook self-heals the budget on drift (ADR-0025).Doctor is green but a harness shows no menu or no search → run
scripts/smoke.py. A Codex row that fails with no offer row means Codex skipped a changed hook: open Codex and trust the skill-concierge hooks again (docs/caveats.md§27).
Full landmine reference: docs/caveats.md.