Doc Rot
Documentation lies as the code moves on. This skill finds the lies and fixes them — across *.md files, inline comments, docstrings, and commented-out code. It's the docs counterpart to dead-code-cleanup, and it enforces the comment hygiene rules in the user's CLAUDE.md ("explain WHY not WHAT"; "one canon version per file"; "update existing docs, don't proactively create new ones").
The core discipline: a doc isn't stale because it looks old — it's stale because it contradicts the code. Ground every call against the actual codebase before flagging it. A "removed" symbol might just be renamed; a "broken" path might live elsewhere now.
Pre-flight
Per the user's git workflow: git fetch && git status, confirm you're on the right base branch (develop if it exists, else main) and up to date. Then scope the doc set — don't boil the ocean. List what's in range (a directory, the README, the files a recent change touched) and confirm before sweeping a whole repo.
What counts as rot
Doc files (*.md)
- References to things that no longer exist — files/paths, npm scripts, CLI flags, config keys, env vars, API/function names. Verify each against the code before flagging.
- Stale setup/usage steps — install commands, toolchain versions, ports, or workflows the code has moved past.
- Code examples that wouldn't run — using removed or renamed APIs, old signatures, deleted imports.
- Dead links — broken internal anchors and relative file links (high confidence); external links (flag, don't chase every one).
- Superseded / duplicate docs — two docs covering the same thing, or a doc describing a feature that's gone. "One canon version per file" — pick the canonical one, fold or remove the rest.
Comments & docstrings
- Contradicts the code below it — the comment says one thing, the code does another. Highest-value find; almost always a real bug-in-waiting.
- Docstring drift —
@param/@returns/documented args that don't match the actual signature.
- Anti-pattern comments (per CLAUDE.md): archaeological ("Extracted from X to reduce complexity"), motion-tracking ("Moved from Y on DATE"), obvious ("increment counter" above
counter += 1).
- Stale TODO/FIXME — referencing a closed issue or a condition that's already resolved.
Commented-out code
- Just delete it. Git remembers. (Already an anti-pattern in CLAUDE.md.)
Detection (cheap first passes)
# Commented-out code blocks (tune the comment syntax per language)
grep -rnE '^\s*(//|#)\s*(if|for|while|function|def|class|return|const|let|import)\b' src/
# Anti-pattern comments
grep -rniE '(extracted from|moved from|refactored out|formerly|used to be|as of [0-9])' src/
# Stale TODO/FIXME with issue refs (cross-check against closed issues)
grep -rnE 'TODO|FIXME|XXX|HACK' src/
# Relative links in markdown (then verify each target exists)
grep -rnoE '\]\(([^)]+\.(md|ts|tsx|js|json|sh|py))\)' --include='*.md' .
# Pull the nouns a doc references (paths, scripts, symbols), then grep the code for each
For each markdown doc, the real check is semantic: read the doc, extract its factual claims (this command, this path, this flag, this default), and verify each against the code. A bare grep won't catch "the default timeout is 30s" when the code says 10s — you have to read both.
Validate before acting
This is where most false positives die — mirror the verify-before-acting discipline from dead-code-cleanup:
- "Gone" vs "moved/renamed." Before flagging a reference as dead, grep the whole repo for it. A function may have been renamed, a file relocated, a script moved into a workspace package. Renamed → update the doc. Actually gone → remove or rewrite.
- Is the comment encoding a non-obvious WHY? A comment that reads as "obvious" might capture a constraint that isn't visible in the code (a security note, an external-API quirk, a perf bound). Read the surrounding code before deleting. When in doubt, keep.
- Is this doc the only record of something still true? Don't delete accurate-but-unloved docs just because they're old. Old and correct is not rot.
- External links — a 404 might be transient. Flag, don't auto-delete.
Categorize and act
High confidence — auto-fix:
- Commented-out code (delete).
- Archaeological / motion-tracking / obvious comments (delete).
- Broken internal links / references to files that definitively don't exist (fix the path, or remove the line if the target is truly gone).
- Docstring signature mismatches where the correct value is unambiguous (update to match).
Judgment calls — propose and confirm:
- Rewriting vs deleting a whole doc section or file.
- Deciding which of two duplicate docs is canon.
- A comment that contradicts the code — surface it; the fix might be the code, not the comment (don't silently "fix" the doc over a real bug).
- Anything where the right answer is "the feature changed and the prose needs rethinking," not a mechanical swap.
Present judgment calls as a triaged list with file:line, what's wrong, and the proposed action — then apply what's approved.
Verify and commit
- After edits: re-run the link check; if the project builds its docs, build them; run lint if comments were touched.
- Focused commits by type, in the user's voice (apply the
voice skill):
docs: fix stale paths and dead links in README
docs: drop archaeological comments and commented-out code
docs: correct docstring drift in <module>
- A comment that contradicts the code and turned out to be a real bug isn't a docs fix — flag it separately so it doesn't get buried in a docs commit.
Don't
- Don't create new docs to "replace" rot — update in place (CLAUDE.md rule).
- Don't delete a comment whose WHY you can't reconstruct from the code.
- Don't flag a doc stale off a single failed grep — confirm the thing is actually gone, not moved.
- Don't auto-rewrite prose; mechanical fixes auto-apply, wording is a judgment call.
1---2name: doc-rot3description: Sniffs out stale and deprecated documentation — markdown files, code comments, docstrings, and commented-out code — then updates or removes it. Grounds every staleness call against the actual code before acting: auto-fixes the high-confidence rot, surfaces judgment calls for review. Trigger on: "find stale docs", "outdated documentation", "doc rot", "doc sniffer", "clean up docs", "are the docs current", "deprecated docs", "fix the README", "stale comments", "remove commented-out code". For a broader sweep of dead variables, functions, files, or tests (not just docs/comments), use dead-code-cleanup instead.4---56# Doc Rot78Documentation lies as the code moves on. This skill finds the lies and fixes them — across `*.md` files, inline comments, docstrings, and commented-out code. It's the docs counterpart to `dead-code-cleanup`, and it enforces the comment hygiene rules in the user's CLAUDE.md ("explain WHY not WHAT"; "one canon version per file"; "update existing docs, don't proactively create new ones").910The core discipline: **a doc isn't stale because it looks old — it's stale because it contradicts the code.** Ground every call against the actual codebase before flagging it. A "removed" symbol might just be renamed; a "broken" path might live elsewhere now.1112## Pre-flight1314Per the user's git workflow: `git fetch && git status`, confirm you're on the right base branch (`develop` if it exists, else `main`) and up to date. Then scope the doc set — don't boil the ocean. List what's in range (a directory, the README, the files a recent change touched) and confirm before sweeping a whole repo.1516## What counts as rot1718### Doc files (`*.md`)19- **References to things that no longer exist** — files/paths, npm scripts, CLI flags, config keys, env vars, API/function names. Verify each against the code before flagging.20- **Stale setup/usage steps** — install commands, toolchain versions, ports, or workflows the code has moved past.21- **Code examples that wouldn't run** — using removed or renamed APIs, old signatures, deleted imports.22- **Dead links** — broken internal anchors and relative file links (high confidence); external links (flag, don't chase every one).23- **Superseded / duplicate docs** — two docs covering the same thing, or a doc describing a feature that's gone. "One canon version per file" — pick the canonical one, fold or remove the rest.2425### Comments & docstrings26- **Contradicts the code below it** — the comment says one thing, the code does another. Highest-value find; almost always a real bug-in-waiting.27- **Docstring drift** — `@param`/`@returns`/documented args that don't match the actual signature.28- **Anti-pattern comments** (per CLAUDE.md): archaeological ("Extracted from X to reduce complexity"), motion-tracking ("Moved from Y on DATE"), obvious ("increment counter" above `counter += 1`).29- **Stale TODO/FIXME** — referencing a closed issue or a condition that's already resolved.3031### Commented-out code32- Just delete it. Git remembers. (Already an anti-pattern in CLAUDE.md.)3334## Detection (cheap first passes)3536```bash37# Commented-out code blocks (tune the comment syntax per language)38grep -rnE '^\s*(//|#)\s*(if|for|while|function|def|class|return|const|let|import)\b' src/3940# Anti-pattern comments41grep -rniE '(extracted from|moved from|refactored out|formerly|used to be|as of [0-9])' src/4243# Stale TODO/FIXME with issue refs (cross-check against closed issues)44grep -rnE 'TODO|FIXME|XXX|HACK' src/4546# Relative links in markdown (then verify each target exists)47grep -rnoE '\]\(([^)]+\.(md|ts|tsx|js|json|sh|py))\)' --include='*.md' .4849# Pull the nouns a doc references (paths, scripts, symbols), then grep the code for each50```5152For each markdown doc, the real check is semantic: read the doc, extract its factual claims (this command, this path, this flag, this default), and verify each against the code. A bare grep won't catch "the default timeout is 30s" when the code says 10s — you have to read both.5354## Validate before acting5556This is where most false positives die — mirror the verify-before-acting discipline from `dead-code-cleanup`:57581. **"Gone" vs "moved/renamed."** Before flagging a reference as dead, grep the whole repo for it. A function may have been renamed, a file relocated, a script moved into a workspace package. Renamed → update the doc. Actually gone → remove or rewrite.592. **Is the comment encoding a non-obvious WHY?** A comment that reads as "obvious" might capture a constraint that isn't visible in the code (a security note, an external-API quirk, a perf bound). Read the surrounding code before deleting. When in doubt, keep.603. **Is this doc the only record of something still true?** Don't delete accurate-but-unloved docs just because they're old. Old and correct is not rot.614. **External links** — a 404 might be transient. Flag, don't auto-delete.6263## Categorize and act6465**High confidence — auto-fix:**66- Commented-out code (delete).67- Archaeological / motion-tracking / obvious comments (delete).68- Broken internal links / references to files that definitively don't exist (fix the path, or remove the line if the target is truly gone).69- Docstring signature mismatches where the correct value is unambiguous (update to match).7071**Judgment calls — propose and confirm:**72- Rewriting vs deleting a whole doc section or file.73- Deciding which of two duplicate docs is canon.74- A comment that contradicts the code — surface it; the fix might be the *code*, not the comment (don't silently "fix" the doc over a real bug).75- Anything where the right answer is "the feature changed and the prose needs rethinking," not a mechanical swap.7677Present judgment calls as a triaged list with `file:line`, what's wrong, and the proposed action — then apply what's approved.7879## Verify and commit8081- After edits: re-run the link check; if the project builds its docs, build them; run lint if comments were touched.82- Focused commits by type, in the user's voice (apply the `voice` skill):83 - `docs: fix stale paths and dead links in README`84 - `docs: drop archaeological comments and commented-out code`85 - `docs: correct docstring drift in <module>`86- A comment that contradicts the code and turned out to be a real bug isn't a docs fix — flag it separately so it doesn't get buried in a docs commit.8788## Don't8990- Don't create new docs to "replace" rot — update in place (CLAUDE.md rule).91- Don't delete a comment whose WHY you can't reconstruct from the code.92- Don't flag a doc stale off a single failed grep — confirm the thing is actually gone, not moved.93- Don't auto-rewrite prose; mechanical fixes auto-apply, wording is a judgment call.