use std::fmt::Write as _;
pub(crate) const DOCTOR_CHECKS: [(&str, &str); 21] = [
(
"instructions",
"Framework instructions deployed and non-empty for the target project.",
),
(
"agents",
"Bundled agent roster deployed under the operator/workspace `.claude/agents/` tier.",
),
(
"skills",
"Bundled skill catalog deployed under the operator/workspace `.claude/skills/` tier.",
),
(
"skill_source",
"The framework's own skill source directory is present and readable.",
),
(
"output_style",
"The `trusty-mpm` Claude Code output style is configured and its file exists (DOC-28 F4).",
),
(
"output_style_staleness",
"Deployed output-style file content matches the bundled catalog, and no orphaned files linger under `output-styles/` (issue #2333).",
),
(
"output_style_legacy_ids",
"Warns when a legacy/unresolvable `outputStyle` id lingers in a currently-shadowed settings layer (e.g. `settings.local.json`) even though the effective layer resolves fine (issue #3453).",
),
(
"deployment",
"Full manifest-completeness diff of the deployed payload against the canonical bundled roster (issue #2158).",
),
(
"skill_staleness",
"Deployed skill content matches the bundled/embedded source (issue #2876).",
),
(
"legacy_sources",
"No legacy global instruction sources linger from a pre-migration install (issue #2876).",
),
(
"agent_skills",
"Every agent's declared `skills:` frontmatter resolves to a real skill — dangling references fail (DOC-42, issue #2889).",
),
(
"agent_skills_prose_hints",
"Informational: skill names mentioned in agent prose but not declared in `skills:` frontmatter (always `Ok`, issue #2906).",
),
(
"memory",
"trusty-memory sidecar reachability probe (bounded by `PROBE_TIMEOUT`).",
),
(
"search",
"trusty-search sidecar reachability + expected-index-present probe (bounded by `PROBE_TIMEOUT`).",
),
(
"worktrees",
"No orphaned git worktrees under the managed workspace root (Fix 1b, #1840).",
),
(
"gh_account",
"Active `gh` CLI identity is unambiguous — warns on multi-account ambiguity.",
),
(
"oauth_token",
"Warns when a managed session risks the `CLAUDE_CONFIG_DIR`-keyed Keychain login loop (issue #2246).",
),
(
"hooks_contamination",
"Warns when a project's `.claude/settings*.json` still carries tm hook entries from a pre-fix `tm install` — suggests `tm hooks clean` (issue #2940).",
),
(
"hooks_foreign_conflict",
"Informational: warns when a project's `.claude/settings*.json` carries foreign (claude-mpm) hook entries that would fire inside a tm session — never auto-removed (issue #2940).",
),
(
"tcc_taint",
"macOS: whether managed panes spawn `claude` with TCC responsibility disclaimed so its data-access prompts aren't attributed to the shared tmux server (issue #2997).",
),
(
"scaffold_tracking",
"Warns when a harness-scaffolding path (`.claude/agents/`, `.claude/skills/`, `.claude/output-styles/`) is BOTH tracked in git AND regenerated locally by tm — the precondition for a `git merge --ff-only` \"would be overwritten\" collision; reports the exact true-intersection paths, never auto-modifies the index (issue #3427).",
),
];
pub(crate) fn render() -> String {
let mut out = String::new();
out.push_str("# Doctor Check Reference\n\n");
out.push_str(
"Generated from a maintained literal list cross-checked against \
`run_doctor`'s actual check names (see this module's \
`doctor_checks_match_run_doctor_names` test — an added, removed, or \
renamed check fails the test suite). Source: \
`crates/trusty-mpm/src/daemon/doctor.rs` and its five sibling \
`doctor_*.rs` files. Regenerate with `tm generate capabilities`.\n\n",
);
let _ = writeln!(out, "{} checks, in execution order.\n", DOCTOR_CHECKS.len());
out.push_str("| # | Check | What it probes |\n|---|---|---|\n");
for (i, (name, description)) in DOCTOR_CHECKS.iter().enumerate() {
let _ = writeln!(out, "| {} | `{name}` | {description} |", i + 1);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use trusty_mpm::daemon::doctor::run_doctor;
#[test]
fn doctor_render_contains_known_check() {
let rendered = render();
assert!(rendered.contains("`instructions`"), "{rendered}");
assert!(rendered.contains("`oauth_token`"), "{rendered}");
}
#[test]
fn doctor_render_is_deterministic() {
assert_eq!(render(), render());
}
#[tokio::test]
async fn doctor_checks_match_run_doctor_names() {
let report = run_doctor(None, None, &[]).await;
let actual: Vec<&str> = report.checks.iter().map(|c| c.name.as_str()).collect();
let expected: Vec<&str> = DOCTOR_CHECKS.iter().map(|(name, _)| *name).collect();
assert_eq!(
actual, expected,
"generate::doctor::DOCTOR_CHECKS has drifted from run_doctor's actual checks"
);
}
}