doc-audit
This skill runs a comprehensive, repo-wide documentation truth audit. It's designed for occasional invocation — quarterly, before major refactors, after a long absence from a project — not for routine use. When it runs, it is exhaustive: it spawns many subagents, asks many questions, and produces atomic commits.
The skill operates in 7 sequential phases: Discovery → Claim extraction → Cross-reference → Triage → Resolution (interactive) → Apply (auto-commit on current branch) → CLAUDE.md trim. Each phase has its own subsection in this file.
The skill's "brain" is a cross-reference engine that compares verbatim quotes from docs against deterministic codebase facts and against other docs' verbatim quotes. Every finding shown to the user includes the original quote — never paraphrase. The user is the final arbiter for every discrepancy.
Reference docs in references/ are loaded on demand, not eagerly. Subagent prompts live in prompts/ and are inlined into subagent invocations. Helper scripts in scripts/ handle deterministic data extraction (Haiku-equivalent work without invoking an LLM).
When invoked
When this skill is invoked, do the following in order:
-
Determine the target project.
- Default: the current working directory.
- If
--workspace=<path>was passed, treat that path as a parent of multiple repos. Discover all child repos that contain a CLAUDE.md, present a leaderboard ranked by(file_size_bytes / 1024), and ask the user which to audit. Then iterate per repo.
-
Read or create
.claude/audit-state/config.ymlin the target project.- If absent, create a default config (see references/move-target-detection.md for the auto-detect logic).
- Honor any user-set values:
move_target,trim_target_kb,trim_target_lines,exclude_paths,include_comments,stale_mode.
-
Check working tree state via
git status --porcelain.- If dirty AND not
--commit-dirty: warn the user, allow them to abort, otherwise stage-but-don't-commit at Phase 6. - If on a protected branch (
main,master,production) AND not--allow-protected-branch: bail with a warning before Phase 6.
- If dirty AND not
-
Estimate run cost. Count docs (
find . -name '*.md' -not -path './node_modules/*' -not -path './.git/*') and structured fact sources. Estimate tokens:#docs × 3000 + #facts × 500. If > 2M, ask the user to confirm. -
Resolve mode flags:
--report-only: stop after Phase 4.--resume: read existing state files, skip phases whose output is fresher than any source file.--include-comments: pass to Phase 2 prompts to expand scope.--stale=light|medium|deep: pass to Phase 3 (deep is default).--no-commit: skip Phase 6's git commit.--workspace=<path>: handled in step 1.
-
Run phases 1-7 sequentially. See each phase's section below.
Orchestration
Phase 1 Discovery → produces .claude/audit-state/discovery.json
Phase 2 Claim extraction → produces .claude/audit-state/claims.json
Phase 3 Cross-reference → produces .claude/audit-state/findings.json
Phase 4 Triage → produces .claude/audit-state/audit-report.md
Phase 5 Resolution → updates .claude/audit-state/verdicts.json
Phase 6 Apply → edits files + git commit on current branch
Phase 7 CLAUDE.md trim → edits CLAUDE.md + new doc files + git commit
Subagent dispatch table:
| Phase | Model | Concurrency | Prompt template |
|---|---|---|---|
| 1 Discovery | sonnet × 1 | sequential | prompts/discovery.md |
| 2 Doc claims | sonnet × N | parallel ≤ 8 | prompts/doc-claim-extraction.md |
| 2 Code facts | (script) | parallel | scripts/extract-fact-sources.sh |
| 3 Cross-ref | opus × 1 | sequential | prompts/cross-reference.md |
| 5 Resolution | parent | sequential | (interactive — see Phase 5 section) |
| 7 Trim | opus × 1 | sequential | prompts/trim-classification.md |
Idempotence rule: every phase reads its inputs and writes its outputs in .claude/audit-state/. Phases never write to source files except Phases 6 and 7. This means a --resume is always safe: re-running a phase reproduces the same output given the same inputs.
Verdict persistence: verdicts.json is the only state file that accumulates across runs. Findings have stable IDs (computed by scripts/finding-id.sh from verbatim quotes); when a source file changes, the quote changes, the ID changes, and the finding re-surfaces.
Phase 1 — Discovery
Purpose: catalog all docs and code-fact sources in the project. One subagent does the full scan and returns a structured catalog.
Subagent dispatch:
Spawn a Sonnet subagent with the prompt at prompts/discovery.md. The subagent's prompt is the stable cacheable prefix (the rules for what counts as a doc, classification rubric, output schema). The variable suffix is the project root path.
Inputs: project root path, exclude_paths from config.yml.
Subagent output schema (written to .claude/audit-state/discovery.json):
{
"scanned_at": "2026-05-03T12:00:00Z",
"project_root": "/path/to/repo",
"docs": [
{ "path": "CLAUDE.md", "size_bytes": 34447, "classification": "claude-md" },
{ "path": "README.md", "size_bytes": 2104, "classification": "readme" },
{ "path": "narrow-scope-docs/deployment.md", "size_bytes": 8421, "classification": "runbook" }
],
"fact_sources": [
{ "path": "package.json", "category": "package-manifest" },
{ "path": "pnpm-workspace.yaml", "category": "monorepo-shape" }
],
"doc_folders": ["narrow-scope-docs/", "docs/"],
"existing_doc_folder": "narrow-scope-docs/"
}
Failure handling: if the subagent fails or returns malformed JSON, retry once with a "your previous output was malformed; here it was: <output>; produce valid JSON" prompt suffix. If it fails twice, abort with a clear error.
Phase 2 — Claim extraction
Purpose: for each doc, extract every checkable claim with a verbatim quote. For each fact source, extract the deterministic facts.
2a — Doc claims (parallel Sonnet subagents)
For each entry in discovery.json#docs, spawn a Sonnet subagent with the prompt at prompts/doc-claim-extraction.md. Cap concurrency at 8 in flight. As each subagent completes, append its output to .claude/audit-state/claims.json#doc_claims.
Each claim has the schema:
{
"doc_path": "CLAUDE.md",
"doc_line": 42,
"verbatim_quote": "Hono 4 + @hono/zod-openapi",
"claim_type": "version",
"subject": "hono",
"value": "4"
}
claim_type is one of: path, version, count, port, command, service-id, architecture, behavior, cross-doc-reference.
2b — Code facts (deterministic, no LLM)
Run scripts/extract-fact-sources.sh <project-root>. The script reads each entry in discovery.json#fact_sources and writes .claude/audit-state/claims.json#code_facts:
{
"source_path": "package.json",
"facts": [
{ "fact_type": "version", "subject": "hono", "value": "4.6.10" },
{ "fact_type": "script", "subject": "dev", "value": "vite" }
]
}
Idempotence: before invoking each subagent, check if claims.json#doc_claims already has entries for that doc_path AND if the doc's mtime is older than last-run.json#completed_at. If so, skip — already extracted.
Phase 3 — Cross-reference
Purpose: build the discrepancy graph by comparing claims against facts and against each other.
Subagent dispatch: spawn one Opus subagent with prompts/cross-reference.md. Provide it with the full claims.json and the rubrics from references/severity-rubric.md, references/finding-types.md, and references/confidence-rubric.md inlined into the prompt.
Stale modes (controlled by --stale=light|medium|deep, default deep):
light: only direct path/version/count/port/command/service-id matchesmedium: light + cross-doc consistency checks (when two docs make claims about the same subject)