derot — truth-drift auditor
Rot = any place a repo's stated intent has drifted from what the code/build actually is. derot finds it, verifies it against ground truth, and fixes it — correcting where the knowledge is still useful, deleting only what is purely obsolete.
The five rot dimensions
- Comment rot — comments that lie about the code beneath them: stale version numbers, references to renamed/removed symbols/methods/files/diagnostic-IDs, resolved
TODO/FIXME/HACK/"temporary"/"workaround for X", descriptions of old behavior, copy-paste comments describing a different member, dead-code comments. - Doc rot —
CLAUDE.md/README/CHANGELOG/ workflow tables whose dependency versions, file lists, command names, or workflow names don't match reality. (Shipped pastCHANGELOGentries are history, not rot — never flag them.) - Infra rot — workflow comments that lie; unpinned actions (
@v1→ SHA-pin with a# vNcomment); hardcoded publish accounts → dynamic secrets; expired/long-lived API keys → OIDC trusted publishing. - Version sync — pins/claims out of sync across
Version.props/Directory.Packages.props/global.json/ docs; published-vs-pinned drift; a property referenced but defined only by an injected SDK (annotate, don't "fix"). - Dependency rationalization — a narrow sub-package used where a parent/meta suffices; a superseded package still used after its successor shipped; redundant direct references already present transitively. This dimension needs external knowledge, not just the repo — see
references/dependency-rationalization.md.
Discipline — non-negotiable
- Correct beats delete. If a comment explains a genuinely non-obvious why (a workaround's reason, an invariant), fix the wrong details and KEEP the explanation. Delete only when purely obsolete.
- Verify root + every transitive use. Before changing anything a comment/doc describes, read the code it refers to and everything that depends on it. Never "fix" a comment to match a wrong assumption — fix whichever of {comment, code} is actually wrong, and chase the change through every caller.
- Never invent. Can't confirm it's stale? Leave it; report it as
unverified. Do not guess. - Terse + behavioral corrections. Say what/why; don't restate the code.
- No drive-by behavior changes. Comments/docs + the named CI modernizations only — unless fixing rot exposes a real bug, in which case say so and fix the root cause.
Verification recipes (how a candidate becomes a confirmed finding)
- Version claim → read
Version.props/Directory.Packages.props/global.json, and the injected SDK props if a property is undefined locally; confirm the actual resolved value before calling a number stale. - Names a symbol / file / diagnostic ID / method / workflow →
grep/lsto confirm it still exists and is spelled right. - Describes behavior → read the code directly under/around the comment and confirm they agree.
- Dependency choice → see the references file: the NuGet graph for transitive-already-present + true subset; cited vendor docs /
nuget-opensrc/microsoft-docsfor succession.
Classify each candidate: confirmed-rot (wrong → fix/delete) · correct-keep-explanation (details wrong, why valid → corrected text that keeps the explanation) · obsolete-delete (purely obsolete → delete) · unverified (cannot confirm → leave + report).
Running a pass
Small scope: inline — scan → verify → fix → build → report.
Large repo: fan out rot-scout agents (read-only, one per area — they parallelise cleanly) and a dep-analyst for dimension 5; synthesize their verified findings into one apply-list; then YOU apply the edits, build/test, and commit. A Workflow is ideal for many areas. The orchestrator — not the scouts — performs the mutations, so comment removal stays under one reviewer's control.
Always finish with: build/test evidence (exact commands + results), a per-change root-cause summary, and a flagged, not changed list (secrets to delete, unverified candidates, dependency calls needing a human decision). derot proposes; the human disposes — especially for dependency swaps, which change the build.