#![allow(dead_code)]
pub(crate) const DOCTOR_ROBOT_DOCS_TOPIC_ID: &str = "doctor";
pub(crate) const DOCTOR_ROBOT_DOCS_TITLE: &str = "cass doctor — agent handbook";
pub(crate) fn doctor_robot_docs_body() -> &'static str {
DOCTOR_ROBOT_DOCS_BODY
}
const DOCTOR_ROBOT_DOCS_BODY: &str = r#"# cass doctor — agent handbook
## TL;DR
If you are an agent and `cass <something>` reported `recommended_action: ...`, run:
cass doctor --json
This is read-only and ≤2s on a healthy system. Branch on `health_class`:
* "ok" — proceed with your original task
* "degraded" — surface the issue, optionally apply safe repairs
* "unhealthy" — STOP; require operator review before mutating
Never run bare `cass doctor` (it has no TUI in this surface, but other cass
subcommands do — always pass `--json` or `--robot`).
## Read-only surface (safe to invoke any time)
cass doctor --json # full check
cass doctor --json --check # explicit read-only-check mode
cass doctor --json --verbose # show passed checks too
cass doctor --json --quarantine # include quarantine inventory
Exit codes:
0 healthy — no findings
1 findings present, no `--fix` was passed; inspect `recommended_action`
4 refused-unsafe — a precondition failed; inspect `checks[].name` for which
5 concurrency-lost — another doctor is running; retry after the lock's
`started_at_ms` exceeds 5 minutes of staleness (the documented heartbeat
threshold)
## Plan a repair (no mutation)
cass doctor repair --dry-run --json
Returns a `plan_fingerprint`. The fingerprint is content-addressed over the set
of planned actions; it changes if the underlying state changes between dry-run
and apply.
## Apply a repair (mutation, gated)
cass doctor repair --yes --plan-fingerprint=<fp> --json
The mutation is applied only if the fingerprint still matches. If the state
changed under your feet, exit 4 (refused-unsafe: plan-fingerprint-mismatch);
re-plan and re-apply.
## NEW pass-1 subcommands
### `cass doctor ls --json`
Lists all `.doctor/runs/<run-id>/` entries with status. Use this to find the
run-id for a recent invocation.
cass doctor ls --json | jq '.runs[0]'
Each run entry has `run_id`, `started_at_ms`, `ended_at_ms`, `mode`,
`exit_code`, `action_count`, `status` (one of completed|incomplete|unknown).
### `cass doctor undo <run-id> --json`
Restores the on-disk state from the per-run backups, byte-identically.
Verifies hashes at each step — refuses to proceed if any tampering is
detected.
cass doctor undo 2026-05-09T20-07-01Z__a3f9b2 --json
cass doctor undo latest --json
Exit codes:
0 all mutations restored
3 undo failed (hash mismatch on a backup); state is at the failing step
4 refused-unsafe (e.g., post-mutation file changed out of band)
### `cass doctor diff [<ref>] --json`
Read-only. Shows the diff between (a) the current state and (b) what a
`--fix` would produce (no `<ref>`), or between two prior runs (with `<ref>`).
### `cass doctor gc --before <iso8601> --yes --json`
Quarantines all runs older than `<iso8601>` into `.doctor/quarantine/runs/`.
Never deletes. Operator owns final disk reclamation. Both `--before` and
`--yes` are required.
### `cass doctor capabilities --json`
Self-describes the doctor surface: contract version, every detector name,
every fixer name, every exit code with kebab-case kind, every env var,
every artifact path. Always present in the JSON output:
{
"doctor_contract_version": 1,
"schema_version": 1,
"binary_version": "x.y.z",
"detectors": [...],
"fixers": [...],
"exit_codes": [...],
"data_paths": [...],
"env_vars": [...]
}
### `cass doctor --robot-triage`
The mega-command. Returns one envelope with everything you need to make a
decision in a single call:
{
"summary": {...},
"findings": [...],
"actions_planned": [...],
"recommended_command": "cass doctor repair --yes --plan-fingerprint=...",
"capabilities_url": "cass doctor capabilities --json"
}
## JSON envelope
Pass-1 introduces top-level `schema_version: 2`. Existing fields preserved.
{
"schema_version": 2, # NEW (root level)
"doctor_contract_version": 1, # NEW
"run_id": "...", # NEW (in repair/cleanup modes)
"started_at_ms": <i64>,
"ended_at_ms": <i64>, # NEW
"exit_code": 0,
"exit_code_kind": "success|...",
"checks": [...], # unchanged
"operation_outcome": {...}, # unchanged
"next_command": "...", # NEW (when applicable)
"capabilities_url": "cass doctor capabilities --json", # NEW
"report_url": ".doctor/runs/<run-id>/report.json" # NEW
}
Always branch on `exit_code_kind` (kebab-case string), not on the numeric
`exit_code`. Codes ≥4 are domain-specific and may map to multiple kinds.
## What the doctor will refuse
Per AGENTS.md and the world-class-doctor safety envelope (S1-S12):
* Delete files. Cleanup is via quarantine + `gc --before <ts> --yes`.
* Run destructive shell. No `rm -rf`, `git reset --hard`, etc.
* Bypass the mutate() chokepoint. New writes go through it (Phase 4+).
* Auto-fix P0 anomalies. Operator-only.
* Auto-update goldens. Reports the diff; operator owns regeneration.
* Touch .env, rust-toolchain.toml, scripts/git-hooks/pre-push.sh.
* Make network calls offline. `--online` is opt-in.
* Run while another doctor is running. Exit 5 (concurrency-lost).
## Common gotchas
* Two `cass doctor --fix` invocations race → the second exits 5 and waits.
* `cass doctor undo <run-id>` against a run created by an older cass version
may fail — the actions.jsonl schema is versioned; bump implies undo
refusal.
* `cass doctor capabilities --json` lists detectors that are compiled in,
not detectors enabled at runtime; some detectors require `--online`.
* The data dir is `~/.local/share/coding-agent-search/` by default; override
with `--data-dir <path>` everywhere.
## When to call which
| You want to | Run |
|----------------------------------------------------------|----------------------------------------------|
| Find out if cass is healthy (≤50ms) | cass health --json |
| Get the recommended action for the current state | cass triage --json |
| Comprehensive checks with explanation | cass doctor --json --verbose |
| Plan a repair, then apply | cass doctor repair --dry-run; --yes ... |
| Inspect history of doctor runs | cass doctor ls --json |
| Compare what a fix would change | cass doctor diff --json |
| Reclaim disk space from old runs | cass doctor gc --before <ts> --yes --json |
| Undo a recent repair | cass doctor undo <run-id> --json |
| Self-describe to a fresh agent | cass doctor capabilities --json |
| One-shot triage decision | cass doctor --robot-triage |
"#;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn body_is_non_empty_and_starts_with_title() {
let body = doctor_robot_docs_body();
assert!(body.len() > 1000);
assert!(body.starts_with("# cass doctor"));
}
#[test]
fn body_documents_new_subcommands() {
let body = doctor_robot_docs_body();
for needle in [
"cass doctor ls",
"cass doctor undo",
"cass doctor diff",
"cass doctor gc",
"cass doctor capabilities",
"cass doctor --robot-triage",
"schema_version: 2",
"exit_code_kind",
] {
assert!(
body.contains(needle),
"robot-docs body missing required marker: {needle}"
);
}
}
#[test]
fn body_warns_against_destructive_actions() {
let body = doctor_robot_docs_body();
for marker in [
"Delete files",
"rm -rf",
"Auto-fix P0",
"Auto-update goldens",
] {
assert!(
body.contains(marker),
"robot-docs missing safety marker: {marker}"
);
}
}
#[test]
fn topic_id_and_title_constants_are_stable() {
assert_eq!(DOCTOR_ROBOT_DOCS_TOPIC_ID, "doctor");
assert_eq!(DOCTOR_ROBOT_DOCS_TITLE, "cass doctor — agent handbook");
}
}