Use when the friend wants a framework health check, or after /ren:update
to confirm nothing broke. Triggers on /ren:doctor. Runs a battery of
small, isolated checks (env, wiki structure, frontmatter, schema
versions, budget lint, dangling L2 pointers, graphify status, backup
configuration, global-tier drift, harness neutrality) — all warn-not-block.
$npx -y skills add hazarsozer/ren-os --skill doctor --agent claude-code
Installs into .claude/skills of the current project.
Are you the author of Doctor?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/hazarsozer-doctor)
---
name: doctor
description: |
Use when the friend wants a framework health check, or after /ren:update
to confirm nothing broke. Triggers on /ren:doctor. Runs a battery of
small, isolated checks (env, wiki structure, frontmatter, schema
versions, budget lint, dangling L2 pointers, graphify status, backup
configuration, global-tier drift, harness neutrality) — all warn-not-block.
version: 0.8.8
license: MIT
type: skill
execution_tier: deterministic
schema_version: 1
framework_version: "0.8.8"
contract:
required_outputs:
- "One CheckResult per registered check, printed as a report"
budgets:
turns: 2
files_written: 0
duration_seconds: 30
permissions:
read:
- "~/.renos/wiki/**"
- "skills/**/SKILL.md"
- "skills/wiki-migration/schemas.json"
write: []
execute:
- "scripts/lint-yaml-frontmatter.py"
completion_conditions:
- "run_checks() returns one CheckResult per registered check, even if individual checks crash"
output_paths: []
tags: [doctor, health-check, diagnostics]
related_skills: [update, backup, wiki-migration, metric-watch]
references_required: []
references_on_demand: []
---
# doctor
Adapted from donor `skills/doctor/scripts/check-*.sh` (one bash script per section) into a single Python check-harness (`skills.doctor.lib.run_checks()`) — the new checks below need to call directly into this repo's Python lib modules (`collect`, `promotion`, `code-map`, `backup`, `portability`), so the harness itself is Python, not bash, per this task's ADAPT (not CARRY) label.
## Checks
**Carried (logic ported from donor's bash):**
| Check | What it verifies |
|---|---|
| `check_env` | git + python3 on PATH, `ANTHROPIC_API_KEY` set |
| `check_wiki_structure` | wiki root exists, `identity.md`/`log.md` present |
| `check_frontmatter` | `scripts/lint-yaml-frontmatter.py` passes over the wiki |
| `check_schema_versions` | every typed page vs. `skills.wiki-migration.lib`'s registry — behind-current pages named with their pending migration chain |
Donor's Node/gh/claude-cli checks, activity-feed/RC-channel/fleet checks, and the wikilink dead-link/stale-page/heavy-page check are all **dropped** — see the module docstring's "DROPPED entirely" note for why each one doesn't apply here.
**New (Task 7.3, all warn-not-block):**
| Check | What it verifies |
|---|---|
| `check_budget_lint` | measured `capability_tokens` (Task 3.1) vs. any SKILL.md-declared `tokens:` ceiling — `info` when nothing's declared to compare against yet, `skip` when no measured data exists |
| `check_dangling_pointers` | every l2-map page's "## Decision map" pointer targets actually exist |
| `check_hub_convention` | legacy `projects/*/knowledge/**/index.md` knowledge hubs still pending the folder-note-hubs-1 migration (#56) — names the first 5 |
| `check_graphify_status` | `importlib.import_module("skills.code-map.lib").status()` — not installed → `info` w/ companions.md pointer; version outside pin → `warn`; stale graph → `info` |
| `check_companions` | registry choices vs reality (lib/companions) — accepted-but-missing → warn; undecided-and-absent → info; consistent → ok |
| `check_backup_configured` | `skills.backup.lib.backup_configured()` |
| `check_global_drift` | `lib.memory.promotion.demote_check()` — non-doctrine/preference pages in `global/` |
| `check_harness_neutrality` | `lib.portability.agents_surface.lint_generated_surfaces` — **soft-wired**: skips cleanly if that module (Task 7.2, built in parallel) isn't present |
| `check_routing_audit` | 0.6.1 E4: harvested `subagent_spawn` metrics vs. `doctrine/model-classes.md`'s class table — `warn` when >30% of spawns used orchestrator-class models or any `parallel_peak` > 5; `info` with per-class counts otherwise; `info "no spawn data yet"` when empty |
| `check_model_map_staleness` | 0.6.1 E4: the `renos:model-map-updated` stamp in `doctrine/model-classes.md` — `warn` if older than 180 days, `info` otherwise |
| `check_orphaned_projects` | issue #19: every `projects/<slug>/` reachable from a repo — `warn` naming each slug with neither a `projects.json` repo mapping nor a `<dev_root>/<slug>/` dir (memory nothing can ever inject) |
| `check_execution_doctrine` | 0.6.4: `agents/ren-reviewer.md` present (doctrine card references it) → `error` if missing; a manual pre-0.6.4 `<!-- renos:doctrine-stopgap -->` block left in `~/.claude/CLAUDE.md` → `warn` (the wake-up hook injects the card now, the manual block is redundant residue) |
| `check_standing_instructions_drift` | #63: repo CLAUDE.md standing-instructions block matches a fresh render of the wiki page |
| `check_agent_shadowing` | 0.6.5: a user or project `.claude/agents/<name>.md` filename colliding with a shipped `agents/*.md` → `warn` naming the colliding agent(s); checks both `claude_user_dir()/agents` and, when the cwd resolves to a registered project, that project's `.claude/agents/`; `skip` when neither directory exists |
| `check_cache_env_hygiene` | #40: any `<cache>/<version>/.venv` inside the versioned plugin cache dir → `warn` naming the version(s) — invocations should redirect via `UV_PROJECT_ENVIRONMENT` (see `ren_paths.envs_dir()`); `ok` when none found; `skip` when the cache root is unresolvable |
| `check_interpreter_freshness` | spec 2026-08-21 (0.8.2) §8: the wake-up hook's recorded fast-path interpreter — dangling path or non-current version → `warn` (fast path degraded to cold `uv`); valid + current → `ok`; no record → `info`; another machine's synced record → `skip` |
| `check_doctrine_index_pins` | spec 2026-08-22 §5: the global CLAUDE.md doctrine index's absolute paths pin a plugin version — a bump leaves them naming the previous one, dead once that cache dir is GC'd. Pins match → `ok`; any pin naming another version → `warn` with both; no file, no managed block, or no pins → `skip` |
## Behavior
1. Call `skills.doctor.lib.run_checks()`.
2. Each check is isolated — a crashing check produces a `CheckResult(status="error", message="check crashed: ...")` rather than aborting the run; every OTHER check still executes (same isolation discipline as `skills.metric-watch.lib.watch`, Task 6.3).
3. Render the report: one line per check, `<name> | <status> | <message>`.
## Why every check is warn-not-block
Per the task brief: doctor is diagnostic, not a gate. Even the carried checks (env, wiki structure, frontmatter) report their findings as `ok`/`warn`/`skip`/`info` — none of them can fail a build or block a command. The risk-tier gate (Task 6.1) and the PreToolUse hooks (Task 6.2) are where blocking actually happens; doctor's job is visibility.
## What this skill does NOT do
- Fix anything. Every check is read-only; remediation is always a follow-up action the friend or another skill takes.
- Validate `verify.json`/schema conformance in the JSON-Schema sense. `check_schema_versions` only asks "is this page's declared `schema_version` behind the registry's current for its type" — that's `skills.wiki-migration.lib.migration_chain`'s job, reused here, not reimplemented.
- Require graphify, backup, or `lib.portability` to be present. Each of those checks degrades gracefully (info/skip) rather than erroring when its dependency is absent.
## References
- Task 3.1 (`lib/instrument/collect.py`) — `capability_tokens` data `check_budget_lint` reads
- Task 6.1 (`lib/memory/promotion.py`'s `demote_check`) — the global-tier drift this check surfaces
- Task 7.2 (`lib/portability/agents_surface.py`) — soft-wired dependency for `check_harness_neutrality`
- `skills/code-map/lib/__init__.py` — `status()`, the graphify-presence/freshness check
- `skills/backup/lib/__init__.py` — `backup_configured()`, shared with metric-watch's own backup check
- `skills/wiki-migration/lib/__init__.py` — the registry `check_schema_versions` reads