Implements the world-class-doctor pattern (24-axiom kernel) for cass: the
doctor subcommand becomes the single agent-ergonomic entrypoint for
self-diagnosis, repair, undo, audit, and capability reflection — all
routed through one mutate() chokepoint with capabilities reflection and
per-run artifact substrate.
New modules in src/:
- doctor_chokepoint.rs — single mutate() callsite with the 8-step contract
(capability check → backup → mutate → verify → log).
- doctor_runs.rs — per-run artifact substrate at <data_dir>/doctor/runs/<run-id>/
with manifest, actions.jsonl, and byte-identical backups.
- doctor_undo.rs — reverse-walk actions.jsonl with hash verification and
byte-identical restore from per-run backups; refuses on
any post-mutation tampering.
- doctor_robot_docs.rs — emits the doctor's capability surface (detectors,
fixers, exit codes, data paths, env vars) in one envelope.
CLI flags exposed on doctor (all hidden until stabilized):
- --ls list per-run dirs (newest-first JSON when --robot)
- --undo <run-id|latest> undo by id with hash verification
- --robot-triage one-shot mega-command: summary + findings +
actions_planned + recommended_command + capabilities_url
- --diff [<ref>] show what --fix would change (read-only)
- --gc-before <ISO8601> quarantine runs older than DATE (requires --yes)
- --watch / --watch-interval-ms / --watch-iterations
long-running monitor that emits JSONL events on run dir
changes; bounded poll interval [500, 60000] ms
- --explain <run-id|latest> emit run manifest + actions + flat band timeline
- --emit-capabilities extended capability surface (detectors[], fixers[],
exit_codes[], data_paths[], env_vars[])
- --allow-repeated-repair permit mutating repair when a prior failure marker exists
Tests:
- tests/cli_robot.rs new robot envelope assertions for the new flags
- tests/doctor_fixtures/ fixture inputs for each detector/fixer pairing
- tests/doctor_fixtures_round_trip.rs round-trip every fixture: detect → fix → undo
- tests/doctor_mutate_auditor.rs rejects any mutation that bypasses mutate()
- tests/doctor_pass1_runs_integration.rs persists run-id, manifest, actions.jsonl
Golden updates:
- robot/capabilities.json.golden, doctor.json.golden, doctor_shape.json.golden,
doctor_quarantine.json.golden, introspect.json.golden — reflect the new
doctor surface and capability schema.
Companion docs in README.md describe the recommended agent flow:
robot-triage → fix → ls → undo if needed.
Co-Authored-By: Claude <noreply@anthropic.com>
cass doctor fixture suite — pass-1
Phase 9 deliverable. One fixture per representative failure mode plus the round-trip regression test asserting
corrupt → doctor --fix → undo → byte-identical.Pass-1 ships a representative slice of fixtures (5 of the 136 catalogued FMs). Each fixture is self-contained and can be invoked from
tests/doctor_fixtures_round_trip.rs(added in this pass).Pass-2 extends to all P0/P1 FMs (~30 fixtures); pass-3 covers P2/P3.
Layout
tests/doctor_fixtures/
├── README.md (this file)
├── fm-pass1-foundation/
│ ├── corrupt.rs corruption helper (writes a known-bad state)
│ └── expected_after_fix.txt text describing the post-fix state
├── fm-storage-stale-wal-shm/
│ ├── corrupt.rs
│ └── expected_after_fix.txt
├── fm-tui-state-json-corrupt/
│ ├── corrupt.rs
│ └── expected_after_fix.txt
├── fm-cli_robot-schema-version-missing/
│ └── README.md (regression spec; no on-disk fixture needed)
└── fm-update_check-clock-rollback/
├── corrupt.rs
└── expected_after_fix.txt
Round-trip contract
Every fixture must satisfy this round-trip in the regression test:
1. corrupt(data_dir) → on-disk state matches expected corruption
2. cass doctor → finding emitted with the FM's stable id
3. cass doctor --fix → mutation applied; report.json::operation_outcome.kind == "fixed"
4. cass doctor → exit 0, no findings
5. cass doctor undo <run-id> → byte-identical to step 1's corrupted state
Step 5 is the strongest invariant: undo must be a true inverse, not a "best-effort restore."
Pass-1 scope
In pass-1 the round-trip is exercised against the new pass-1 modules
(doctor_runs, doctor_chokepoint, doctor_undo, doctor_robot_docs) directly
because the dispatch wiring into run_doctor_impl() is deferred to pass-2.
The fixture infrastructure is in place; pass-2 adds cass doctor invocation
in steps 2-5.
For pass-1, the fixtures mainly serve to:
- Document what "corruption" looks like for each FM (so the chokepoint integration knows what state to repair from).
- Provide a smoke target for
cargo test --test doctor_fixtures_round_trip. - Pin the fixture layout for future passes.