Doctor Pattern
A "doctor" is a CLI that auto-detects the project type, runs relevant health checks, normalizes findings into a 0-100 score, and outputs actionable diagnostics. One doctor per subsystem; in monorepos, every major surface gets its own.
The Pattern
The doctor pattern follows a universal shape across every implementation:
- Auto-detect - scan the project to determine what's present (framework, language, config files, directory structure)
- Run checks - execute a battery of domain-specific validations, each producing pass/warn/fail
- Normalize - compute a comparable health score (typically 0-100) scored on unique rules, not occurrences (see Scoring below)
- Report - output human-readable diagnostics with actionable fixes, optionally machine-readable JSON for CI, and a score trend over time
The pattern originated in flutter doctor and brew doctor but has expanded to cover every layer of the stack. The key insight: a codebase isn't "healthy" or "unhealthy" - it has independent subsystems that each need their own diagnostic. Source: ecosystem survey, 2026-05-17
Why Doctors Matter for Agent-Authored Code
Agents generate plausible but subtly wrong patterns at high velocity. Lint rules (see Lint-Enforced Agent Guardrails) catch individual violations. Doctors catch systemic drift - the kind where each file is individually valid but the aggregate is degrading. Source: User, 2026-05-17
A React codebase can pass every ESLint rule while still having a health score of 40/100 due to architectural anti-patterns (fetch in useEffect, derived state in effects, unstable query keys). The doctor catches what lint cannot. Source: react-doctor documentation, 2026-05-17
What doctors test (vs lint vs unit tests)
A doctor is a testing methodology for systemic health, not for correctness. Place it precisely:
| Layer | Question it answers | Unit |
|---|---|---|
| Unit/integration tests | Does this behavior produce the right output? | One behavior/invariant |
| Lint / types | Is this line/file individually valid? | One file |
| Doctor | Is the system drifting, and by how much? | One subsystem, scored 0-100 |
Doctors are a regression ratchet: the score is a single trend metric that makes aggregate drift visible and gives an agent a target to converge on. They don't replace tests (which prove behavior) — they catch the class of problem where every file passes lint and every test is green but the architecture is quietly rotting. Source: compiled from Security and Review Skills + Frontend and Design Skills, 2026-06-13
Scoring methodology (from react-doctor)
Security and Review Skills established the scoring shape worth copying into every doctor: Source: react-doctor "Understanding Scores", https://www.mintlify.com/millionco/react-doctor/guides/understanding-scores, 2026-06-13
score = 100 − 1.5 × (unique error rules) − 0.75 × (unique warn rules) # clamped to [0,100]
Two design principles fall out of this formula:
- Score on unique rules, not occurrences. A rule violated 200 times costs the same as violated once. This is the whole point — it rewards systemic fixes (silence a category) over whack-a-mole on individual items, and it stops a single noisy check from dominating the score.
- Errors weigh 2× warnings. Fix
failrules first for the biggest score lift; warnings are best-practice debt.
The score is only meaningful as a trend: persist it (a history file with date + score + rule counts), show the delta run-over-run, and you get a drift signal no single check provides. wiki-doctor now does exactly this (wiki/meta/doctor-history.json).
The fix loop (converge toward 100)
react-doctor's triage playbook is a reusable agent loop, not React-specific: Source: react-doctor agent playbook, https://www.react.doctor/prompts/react-doctor-agent.md, 2026-06-13
- Errors first, serially — fix one, re-validate (typecheck/targeted test), revert that single fix on failure, continue. Record reverts.
- Warnings in a batch — apply all, validate once; if the batch breaks, fall back to serial to isolate the offender.
- Re-scan and confirm the score rose; repeat until 100 or the remainder is genuine false-positives.
- Close the loop — emit a guardrail (a
.cursor/rules/*.mdcor a new doctor check) from recurring findings so the next run can't regress. Doctor diagnoses → rule prevents → score holds. This feedback loop is what makes a doctor compound instead of being a one-time audit.
Closing the loop: detection to self-healing
A doctor detects and scores; on its own it still relies on a human (or an ad-hoc agent session) to run the fix loop above. The Self-Maintaining Systems pattern from Ramp Labs closes that loop: detection feeds a triage step that decides which findings a machine can fix, which an agent should fix behind a PR, which a human must decide, and which are noise to suppress — with per-finding state so the same issue never re-alerts. Source: Ramp Labs, https://ramplabs.substack.com/p/self-maintaining, 2026-03-24
This wiki implements it on top of scripts/doctor.ts:
scripts/self-heal.tsconsumesdoctor.ts --json, triages each finding viascripts/lib/self-heal-triage.ts(auto-safe/agent-fix/escalate/noise), reconciles againstwiki/meta/self-heal-state.json(stand-down), applies the provably-safe deterministic fixes under--apply(regenerate index/backlinks/discovery; re-create config symlinks), re-runs the doctor to verify the finding cleared and the score did not regress (the reproduction test), and prints a selective digest.- The
self-healautomation (automations/self-heal.md) performs the content fixes (agent-fixclass) behind a PR and escalates the destructive ones.
The principle that keeps it honest is "detect everything, notify selectively": the doctor stays exhaustive, but only new, real, unresolved findings reach a human. Destructive or irreversible fixes (slug merges, deletions) always escalate — never auto-applied.
CI: gate on the diff, not the backlog
react-doctor's GitHub Action reports only the issues a PR introduces, not the existing backlog — so a low absolute score never blocks adoption. Mirror this: run the full doctor locally (and track the score trend), but in CI gate on regressions (or a --min-score floor that ratchets up as you pay down debt), not on reaching 100 immediately. Source: react-doctor GitHub, https://github.com/millionco/react-doctor, 2026-06-13
The Doctor Registry
Every project Kevin works on should have doctors covering its major surfaces.
| Doctor | Domain | Install | When to run |
|---|---|---|---|
| project-doctor (skill) | Scaffold a project's own parameterized doctor (seek 100) | skills/productivity/project-doctor/ template |
New project, or hardening an existing one |
| wiki-doctor | Wiki structure, skills, rules, config sync (0-100 score + history) | npx tsx scripts/doctor.ts |
After wiki edits, before commits |
| react-doctor | React/Next.js hygiene (state, perf, a11y) | npx react-doctor@latest |
After agent-authored React edits |
| security-doctor-chain (skill) | Security-sensitive pre-ship composition | skills/engineering/security-doctor-chain/SKILL.md |
When multiple security doctors need to run in order |
| framework-doctor | Multi-framework (Svelte, React, Vue, Angular) | npx framework-doctor |
On unfamiliar repos, onboarding |
| @sylphx/doctor | Monorepo structure and config (80+ checks) | npx @sylphx/doctor |
Monorepo bootstrap, periodic audit |
| Ultracite | Lint + format config health | bun x ultracite@latest init |
Project bootstrap |
agent-browser vitals |
Web Vitals health check | agent-browser vitals |
After frontend deploys |
tests (/tests skill) |
Test invariants at smallest honest layer | Read .agents/skills/tests/SKILL.md, run targeted vitest |
When tests or covered production code change |
Custom scripts/doctor.ts |
Project-specific invariants | Varies | Before major releases |
Monorepo Application
In a monorepo like Dedalus, each package or app surface should have its own doctor or doctor sub-command:
pnpm doctor:web- React/Next.js health for the website apppnpm doctor:api- API contract validation, schema drift detectionpnpm doctor:infra- Terraform state consistency, env var coveragepnpm doctor:wiki- Wiki structure (already built)
The CI pipeline runs all doctors on every PR. Failures block merge. Warnings are collected and triaged weekly. Source: User, 2026-05-17
Per-project doctors (parameterized, seek 100)
Every project Kevin works on should stand up its own doctor, not borrow a generic one. The shape is always the same engine re-pointed by parameters: a CONFIG block (name, source dirs, thresholds, target score) plus a CHECKS array of project-specific invariants. The same scoring engine runs everywhere; only the parameters and checks differ. This is "based on parameters" — one methodology, many instances. Source: User, 2026-06-13
The goal is an explicit, gamified target: drive the score to a perfect 100 and then ratchet --min-score to 100 in CI so it can never regress. 100 means every invariant the project declares currently holds. You get there with the fix loop (errors first, warnings batched, re-scan), and you keep it there by gating CI on the diff. Because scoring is on unique rules, the number is honest — it can't be gamed by chipping at occurrences.
Scaffolding one is codified, not a one-off: use the Frontend and Design Skills skill (skills/productivity/project-doctor/), which ships a dependency-free parameterized doctor.ts template (scoring + history + --min-score already implemented) — you only edit CONFIG and CHECKS. Derive checks from repeated review comments, postmortems, and project invariants; make must-hold rules fail (−1.5) and debt warn (−0.75). Source: User, 2026-06-13
Agent Integration
Agents should run the relevant doctor(s) automatically:
- After large edit sessions - if the agent touched 5+ files in a subsystem, run that subsystem's doctor before reporting completion
- Before committing - doctor pass is a quality gate alongside lint and type-check
- On session start -
check-freshness.tsalready covers wiki staleness; extend to project-level doctors - In CI - doctors produce JSON output (
--json) for programmatic evaluation
The doctor-enforcement skill codifies this trigger logic. When the diff touches tests, read Frontend and Design Skills first - run the smallest honest layer, not the full suite by default. When the diff is security-sensitive, route through security-doctor-chain so React Security Doctor, deepsec, threat modeling, adversarial review, and no-sus are composed deliberately. Source: User, 2026-05-17; updated 2026-06-29
Building a Custom Doctor
When a failure pattern repeats 3+ times in code review, it should become a doctor check:
- Write a check function:
(projectRoot: string) => { check: string, status: 'pass' | 'warn' | 'fail', message: string }. Thecheckkey is the rule identity — keep it stable, since the score counts unique rules. - Aggregate occurrences under one rule. Emit a single summary result per check plus per-item
infolines; never let 200 occurrences become 200 score hits. (This is why scoring on unique rules matters.) - Register it in the project's
scripts/doctor.ts(or equivalent); letsummarize()compute the score so weighting stays consistent. - Emit machine-readable JSON, support a
--min-scoreCI gate, and append the score to a history file for the trend. - Add it to the doctor table above and the relevant tool-hierarchy entry; wire it into CI as a diff-scoped check (gate regressions, not the backlog).
- Close the loop — if agents keep tripping the rule, emit a Cursor rule so it's prevented at write time, not just caught at session end.
The lifecycle: incident → postmortem → lint rule or doctor check → never again. Source: compiled from Lint-Enforced Agent Guardrails and No One-Off Work, 2026-05-17
The Hierarchy
Doctors sit at a specific layer in the enforcement hierarchy from Lint-Enforced Agent Guardrails:
1. Brin allowlist - blocks dangerous deps at install
2. Lint rules - blocks bad patterns at write time
3. Type system - blocks type errors at compile
4. Doctors - blocks systemic drift at session end ← HERE
5. Pre-commit hooks - blocks on push
6. CI checks - blocks before merge
7. Code review - advisory, requires human attention
8. System prompts - advisory, agent may ignore
Doctors fill the gap between "each file is valid" (lint/types) and "the system is healthy" (CI/review). They catch aggregate problems that no single-file check can detect. Source: User, 2026-05-17
Concept Position
| Field | Value |
|---|---|
| Concept family | Brain, memory, and retrieval |
| Concept owned | A "doctor" is a CLI that auto-detects the project type, runs relevant health checks, normalizes findings into a 0-100 score, and outputs ac... |
| Category map | Concept System Map |
Timeline
- 2026-07-01 | Concepts category refresh added this page to the Brain, memory, and retrieval family, linked it to Concept System Map, and kept it standalone because it owns this reusable mental model: A "doctor" is a CLI that auto-detects the project type, runs relevant health checks, normalizes findings into a 0-100 score, and outputs ac... Source: User request, 2026-07-01
- 2026-06-16 | Added "Closing the loop: detection to self-healing" — the Self-Maintaining Systems (Ramp Labs) pattern built on top of this doctor as
scripts/self-heal.ts+ theself-healautomation (triage, per-finding stand-down state, verified auto-safe fixes, selective notification). Source: Ramp Labs, 2026-03-24 - 2026-05-22 | Added
/testsskill to doctor registry and agent integration. Follows dedalus-labs/dedalus#2119 merge; routes test verification through invariant naming, smallest honest layer, and red-before-fix reporting. - 2026-06-13 | Added the per-project doctor pattern (parameterized CONFIG + CHECKS, seek 100, ratchet
--min-score) and the Frontend and Design Skills skill that scaffolds it. Relaxed the page-size guideline (length follows content;longform: trueopt-out; flag only ~400+ line unmarked pages). Source: User, 2026-06-13 - 2026-06-13 | Folded in Security and Review Skills's methodology (verified against react.doctor docs): 0-100 scoring on unique rules not occurrences, the
100 − 1.5e − 0.75wformula, the errors-serial/warnings-batch fix loop, score-over-time trend, diff-scoped CI gating, and the diagnose→rule→hold feedback loop. Added "what doctors test (vs lint vs tests)". Applied the scoring + history towiki-doctor(scripts/doctor.ts). Source: User, 2026-06-13 - 2026-05-17 | Concept page created. Codified the doctor pattern as a first-class enforcement layer between lint rules and CI, with a registry of known doctors and guidelines for building new ones. Source: User, 2026-05-17