Finding what is stale in a checkout before you pay for a full test run
Find what's broken
People ask this as: check if my project is broken before editing anything
A read-only source-consistency check that answers what is structurally inconsistent or stale before you edit — a composition that no longer resolves, a plan bound to an application that has moved, a Skill whose two mirrors disagree, a documentation link that lost its file in a rename, a hand-edited module state file, a committed database. It runs no test, opens no database, makes no network call and has no --fix. Every check delegates to an authority that already exists and names it, and twelve limitation codes in the report say what a green result does not mean.
Where this stops
Read this before the rest of the page. Every line below is a thing this does not do.
- It runs no test. A green doctor means the source is self-consistent, not that the software works (NOT_A_SUBSTITUTE_FOR_VERIFY).
- It executes no action, evaluates no policy and drives no state transition. A perfectly consistent project can still compute the wrong number (DOMAIN_CORRECTNESS_NOT_PROVEN).
- It changes nothing and has no --fix. Every finding names the existing command that would fix it (NO_MUTATION).
- It opens no database, makes no network call, and reads no file content for secrets — the hygiene check asks git which paths are tracked, never what is in them (DATABASE_NOT_INSPECTED, PROVIDER_HEALTH_UNKNOWN, SECRETS_NOT_INSPECTED).
- Packages are checked for the source-boundary rule only. Full conformance means composing and booting each one, which is a different command and is deliberately not run here (PACKAGE_CONFORMANCE_NOT_RUN).
- A directory under packages/ that the composition does not reference is not classified at all — not as kernel code, not as a package. Guessing produced a false failure twice (UNCOMPOSED_PACKAGES_NOT_CLASSIFIED).
- Plans, skills and documentation are found in documented locations only. Something kept elsewhere is not seen, and its absence from the report is not a statement that it is fine (DISCOVERY_IS_BY_CONVENTION).
- Generated-source drift is detected only where a checked-in generator contract proves it. A hand-edited generated service is not caught (GENERATED_SOURCE_DRIFT_LIMITED).
- No report from it is a production-readiness statement, because there is no auth, tenancy or RBAC in this framework to assess (PRODUCTION_READINESS_NOT_ASSESSED).
No authentication, tenancy or RBAC. The server is local-development-only. An actor header is an assertion, not an identity. Do not expose it to a network. Every claim and every limitation is on one page.
What question it answers
"What is structurally inconsistent or stale in this project, before I edit it or pay for a full verification run?" An agent arriving at an unfamiliar checkout has one expensive way to find out whether the repository is coherent, and most of what goes wrong before you touch anything is not a failing test at all.
The command sits in a five-question ladder where each rung has a different cost and a different meaning: `app inspect` asks what is composed, `project doctor` asks what is inconsistent or stale in the source, `package test` asks whether one package satisfies the contract, `solution check` asks whether a plan still fits, and `npm run verify` asks whether everything actually works. Only the last one runs tests, and it is roughly a thousand times slower.
What it does
`npm run crm -- project doctor --json` emits `projectDoctorContract: 1` across seven categories — composition, packages, modules, plans, skills, docs, hygiene — with per-check `status`, `authority`, `subject`, `evidence`, `reason` and `remediation`, plus a compact `problems[]` view carrying every failure and nothing else.
Every check names the authority it speaks for, and `AUTHORITIES` is a closed list that `check()` throws on, so a future check cannot be added without naming who refuses it. There is deliberately no `doctor` authority: composition problems come verbatim from the inspector, module-state drift from `readModuleState` (the same ADR-019 authority the module factory evolves from), plan staleness from `bindSolutionPlan`, the package private-import rule from the same function `crm package validate` uses, hygiene from `git ls-files`.
Run here on 2026-08-09 it printed `"ok": true`, status `passed`, project kind `framework-repository`, composition `valid`, 136 documents scanned, three Solution Plans discovered, one candidate package located, and counts of 9 passed, 0 warning, 0 failed, 4 not applicable. The four not-applicable rows are the interesting ones: `packages.source-boundary — NO_PACKAGES_COMPOSED`, `modules.migration-history — NO_MODULE_STATE_FILES`, and two Solution Plans marked `PLAN_NOT_DECLARED_CURRENT — it no longer binds to this composition, which for an undeclared plan is a fact rather than a fault`.
That last verdict is a design decision with a name. Staleness is graded only for a plan the project declares in `package.json` under `agentCrm.solutionPlans`: a declared `required` plan that no longer binds fails, a declared `current` one warns, an undeclared one is not applicable and still carries its evidence. Two of this repository's three checked-in plans are historical design examples, and warning about them on every run is exactly the fatigue that teaches a reader to skim past the warnings that matter.
It refuses to invent rules, and the ExecPlan records both refusals in detail. No fuzzy comparison of generated source against a re-render, because the re-render differs for a dozen innocent reasons and a check that cries wolf is one people silence. No anchor parsing in documentation links, because heading-to-slug is a guess. Documentation links are read as prose with fenced blocks and inline code blanked first — a guide that shows a Markdown link inside a fence is documenting syntax, not linking anywhere.
What it refuses to claim
Twelve codes, printed in the report and in the human view under the heading "What this does not prove". Verbatim: `DOMAIN_CORRECTNESS_NOT_PROVEN` (nothing here executes an action, evaluates a policy or drives a state transition — a project can be perfectly consistent and compute the wrong number), `NOT_A_SUBSTITUTE_FOR_VERIFY` (no test is run; a green doctor means the source is self-consistent, not that the software works), `DATABASE_NOT_INSPECTED`, `PROVIDER_HEALTH_UNKNOWN`, `SECRETS_NOT_INSPECTED` (the hygiene check asks git which paths are tracked; it never opens them), `PRODUCTION_READINESS_NOT_ASSESSED`, `PACKAGE_CONFORMANCE_NOT_RUN` (packages are checked for the source-boundary rule only), `DISCOVERY_IS_BY_CONVENTION` (something kept elsewhere is not seen, and its absence from this report is not a statement that it is fine), `UNCOMPOSED_PACKAGES_NOT_CLASSIFIED`, `PLAN_CURRENCY_IS_DECLARED`, `GENERATED_SOURCE_DRIFT_LIMITED` and `NO_MUTATION` (this command changes nothing; every finding carries the existing command that would fix it, and there is no `--fix`).
`UNCOMPOSED_PACKAGES_NOT_CLASSIFIED` is the one with a scar behind it. Package discovery took three attempts, and two of them produced false failures — first by treating every directory under `packages/` with a `src/index.js` as a package and reporting eight boundary violations in a repository that has none, then by string-matching `definePackage(` and grading a package that did not exist while missing one whose import was aliased. The version that holds parses no package source at all: the inspector says which packages exist, the composition file's own import specifiers say where they are, and a directory the composition does not reference is not classified at all. Telling kernel code from an unreferenced domain package would mean executing it, and guessing is what produced the false failure twice.
A warning never fails the run. A stale Solution Plan and a one-sided Skill mirror are information owned by somebody else, and a doctor that failed the build for them is a doctor people stop running.
What it costs to run
Measured here on 2026-08-09: about 0.38 s wall clock for `--json`, exit code 0, nothing written. The ExecPlan's own measurements put it at ~155 ms against ~122 ms for the single inspection it performs and ~156,000 ms for `npm run verify` — roughly 1.3× the load it reuses, and about a thousandth of the cost of the run it is not a substitute for. The stated budget follows from those measurements rather than from an invented target: under a second on a project this size, and within roughly 1.5× the inspection it already performs.
It loads the composition exactly once and reuses it for the composition checks, every plan binding and the `inspectionFingerprint`; a test asserts the loader is called precisely once. That load gets a 10-second bound rather than the inspector's default 60, because a command whose entire proposition is that it costs 150 ms must not silently cost a minute — and a timeout is reported as a timeout, naming the command that waits longer.
Exit codes: `0` no failures, `1` inconsistencies, `2` not a readable project. Nothing in the document carries an absolute path, a stack frame, a source body, a secret or a timestamp.
Where it fits
Before you edit. It is the cheap rung of the ladder — the one you can afford to run on arrival, after a merge, or between edits, which is a different tool from the one you run before a merge.
It is deliberately not the runtime `crm doctor` that already exists: that one boots the application, opens the database and reports runtime counts, and cannot run on a project that does not boot. This one reads source, opens nothing, and works precisely when the application will not start — which is when a diagnostic is worth having.
The evidence this page rests on
Claims and limitations are printed from site/claims.json word for word. Job statuses come from docs/benchmarks/jobs.json; a job with no page of its own is listed with its status rather than linked.
Claims
- C-14 One command tells an agent what an application actually is — packages, capabilities, resources, actions, policies, providers — read from checked-in source, in a single deterministic JSON report.
LimitSource-only and read-only. It never opens the database, contacts a provider, reads a secret, or reports runtime, CI or authorization state — and it lists those blind spots as machine-readable limitations in its own output.
- C-15 A Solution Plan is a checked-in file with a contract and a canonical fingerprint, validated against a real inspection — so a plan written against a composition that has since moved reports itself stale.
LimitA document contract, not a planner and not a runtime. Nothing executes a plan, and the validator refuses a plan that carries a command.
- C-19 Generated modules evolve without rewriting history: explicit revisions, a checked-in state file and append-only named migrations.
LimitThe view is source-only: what the checked-in revisions and migrations say is knowable; what a particular database has actually applied is not.
Limitations
- L-01 No authentication, tenancy or RBAC. The server is local-development-only. An actor header is an assertion, not an identity. Do not expose it to a network.
- L-06 No import, export, dedupe, merge, bulk edit, saved views or global search. Table stakes in every commercial CRM, and none of them has a milestone yet. This is recorded deliberately rather than left for you to discover.
Jobs it covers
- JTBD-AX-01 Discover installed packages, capabilities and policies — validated end to end
- JTBD-AX-05 Verify the solution against defined acceptance — partially supported
- JTBD-AX-06 Report unavailable capabilities honestly — partially supported
- JTBD-AX-09 Deploy, observe and fix a solution in production — not supported