prune
Finds doc drift, where documentation says something the code no longer does, and settles it in the code's favour.
npx skills
npx skills add JoshuaOliphant/compost --skill prune
Works for Codex, Cursor and other harnesses. Use -a <agent> to target one, e.g. -a codex.
Claude Code
/plugin marketplace add JoshuaOliphant/compost
/plugin install compost@compost
Prune
A doc that restates the code drifts from it, and an agent can't tell the stale sentence from the true one: it reads both as instructions. Code can't drift from itself. Prune checks each doc against the code, takes the code as the more likely truth, moves whatever is still worth saying into the code, and deletes the rest. What remains is the core docs, the few files that say what code can't. The why is in core docs and subtract before you add.
Steps
-
Read the policy. Read
docs/agents/docs.mdfor the repo's core docs and the files that are not docs at all. If it is missing, write it now from the template and say so. Files that are product rather than documentation stay out of the scan: skill and prompt files, templates, generated reference, vendored docs. Ask only when exploration finds a published docs site (mkdocs.yml,docusaurus.config.*, adocs/a build turns into pages), since pruning it changes what users read. -
Scan. Run
uv run ${CLAUDE_PLUGIN_ROOT}/scripts/doc_refs.py scan, with a--coreand an--excludefor each pattern docs.md lists. It prints every tracked markdown file, non-core first and most suspicious first, withmissing(paths it names that don't exist) andstale(files it names, with the commits each has had since the doc last changed). These are leads, not verdicts. A missing path can be an example (src/orders.pyin a template) or a file the tool creates at runtime; a doc with no leads can still describe behaviour the code dropped. -
Check every doc against the code. When the non-core docs fit in one careful read, check them yourself with the brief in check. When there are more, send subagents in one message, one per handful of docs, each with that brief and its docs' scan entries. Either way every doc's claims are sorted into four kinds with the file:line that settles each:
- Drifted: the code says otherwise.
- Restated: true today, and already said by the code: a signature, a config file, a test.
- Keeper: something the code can't say: why it is this way, an alternative that was rejected, a domain term, an operational step, a constraint from outside the repo.
- Plan: future work, a design never built, or a plan already carried out.
Code is the likely truth, not the certain one. When a doc states a requirement the code breaks,
look for backing: a test that pins the doc's version, an issue, an ADR, the user's word. Backed
means a bug, not drift: leave the doc, file an issue per docs/agents/issue-tracker.md, and
offer compost:diagnose. An ADR is a record and is never edited to match the code; flag the
conflict as docs/agents/domain.md says. Core docs get checked too, and fixed in place, never
deleted.
- Give each keeper a home in the code. Walk the ladder in homes and stop at the first rung that holds the claim:
- A name that says it, so the sentence becomes unnecessary.
- A type or a check that enforces it, so breaking it fails loudly.
- A test that shows it, named in the domain's terms.
- A comment where the code it explains lives: why, never what.
- A core doc: a term to
CONTEXT.md, a decision to an ADR (offered only for decisions that pass the test in domain docs), a command or convention toAGENTS.md, what the project is and how to run it to the README.
Drifted and restated claims have no home; they go. A plan still wanted becomes an issue, and one that isn't is deleted, since git keeps the history. A rename across a public interface or a deploy boundary doesn't happen here: file it as an issue (expand, migrate, contract).
-
Present the plan. Per doc: delete, shrink to its keepers, or fix in place. Per keeper: its rung and the file it lands in. Per backed contradiction: the issue you will file. Lead with the count of docs that go. Wait for the user's yes, for all of it or doc by doc. Inside an unattended run, prune nothing; post the plan where the run reports and stop.
-
Prune, then plant. Commit the deletions and trims first, on their own (
docs: prune docs that drifted from the code), then the code moves. Re-run the scan and fix everymissingthat points at a deleted doc. A rename or a new type is a code change, so runcompost:verifyon the moves before calling it done. -
Record the policy. Update
docs/agents/docs.mdto list the core docs as they now stand and the patterns the scan left out. Add or update the### Docsentry in the## Agent skillsblock, as in instruction files. That entry is what keeps the repo pruned: every agent that reads it learns that a new fact goes in the code or a core doc, not a new file. Commit it aschore: record the core docs.
Next moves
compost:diagnosewhen a doc turned out to be right and the code wrong.compost:deepenwhen keepers for one module keep landing as comments: the module is hard to read, and its shape is the fix.compost:specwhen a plan doc describes a feature that is still wanted.compost:finishto open the pull request.