Doctor & trust
Honest environment validation, and why nothing runs before you approve it.
ways doctor answers one question before anyone runs a flow: is this environment actually
ready? It resolves everything the way (and its bound slots) requires and checks it — CLIs, env
vars, MCP servers, slot bindings, companion ways, pins.
Honest statuses, always
Every check reports exactly one of five statuses — and the CLI never pretends to know more than it does:
| Status | Meaning |
|---|---|
pass | verified present/valid |
fail | required and missing/broken — comes with a copy-pasteable remediation |
skipped | optional and absent — not an error |
unknown | present but unverifiable (e.g. a binding with no profile, an unrun check) |
not-inspectable | no inspector can see it (e.g. MCP config of an unsupported agent) |
Exit code 0 when nothing failed; --strict (CI) also fails on unknown. MCP configuration is
read through a pluggable agent inspector — the claude inspector ships first; other agents
honestly report not-inspectable rather than guessing.
Project mode
With no arguments, doctor validates the whole repo:
- Conflicts across installed ways (contract rule #14): the same slot id declared with incompatible contracts, or two ways colliding on a skill name.
- Pins: every way declared in
ways.yamlis installed, its content digest still matchesways.lock, and itsversionPinmatches the resolved ref. Drift =failwith the exact re-acquire command. - Then per-way checks for every declared way.
ways doctor incu/devscopes to one way — by installed name, no path needed.
Trust: approval is bound to content
The way's declared check commands (gh auth status, git --version) are executable content —
so doctor only runs them for trusted ways:
ways addshows the full plan — including everycheckcommand and the executable surface — and requires explicit approval.- Approval records
approvedDigestinways.lock: trust is bound to the content digest, not the name. - If the installed content changes in any way, the digest no longer matches:
doctordrops back to presence-only checks (unknown),listflags the drift, and re-approval is required.
No shell is ever used to execute checks (argv-spawn only), nothing runs before approval, and secrets are never echoed in any check output.
Contracts a bound impl must satisfy
Once a slot is bound, doctor resolves the impl's Binding profile through the
contract catalogs and checks it
for real:
- Coverage. A binding that doesn't cover all of its
SlotContract's operations (coversOperations) is reported — advisory ⚠ by default, a failure under--strict— so that field stops being decorative. - Unresolvable contract →
unknown. If a slot's contract can't be resolved (no catalog provides it),doctorreports that slotunknownwith the real reason, and every other check still runs — one gap never masks the rest.
A catalog is executable surface, so it's trusted exactly like a way: its Bindings contribute
check commands, ways add discloses each catalog in the approval plan (attributed to catalog +
impl), a changed catalog digest forces re-approval, and read-only commands (doctor, bind,
ways contracts) are cache-only — they never fetch. WAYS_NO_CATALOGS=1 turns resolution off.