Repository structure:
toolshedis the Claude Code marketplace;doc-lifecycleis the plugin it currently publishes. The repository and install commands use the marketplace name.
Documentation that can prove it's still true.
doc-lifecycle is a Claude Code plugin that treats your README, runbooks, and CLAUDE.md as sets of claims checkable against the code. Ask "is the README still accurate?" and it verifies each claim — then hands you a report where every verdict carries evidence and every stale claim carries a ready-to-land fix:
what the doc claims the code as of today
- make reset make clean (target renamed)
- schema 2 → exits 5 schema 3 → exits 4 (worker bumped)Stale docs like these don't fail loudly — an agent (or a teammate) just acts on them: runs a command that no longer exists, writes an error handler for the wrong exit code. doc-lifecycle turns that silent failure into a record, auditable on both axes docs fail on — drift (the claim is now false) and bloat (the claim is true but no longer earns its tokens) — and fixable surgically, instead of discovered mid-incident.
Scripted replay (generated by assets/demo/make_cast.py) — the code checked is the real tests/fixtures/taskflow fixture, and the record shown is the output contract's worked example.
/plugin marketplace add aj604/toolshed
/plugin install doc-lifecycle@toolshed
Developing against a local checkout? /plugin marketplace add /path/to/toolshed instead.
Then, in an ordinary session, just ask:
is the README still accurate?
No flags, no config — installing schedules nothing and changes nothing; the skills trigger on ordinary requests in your sessions, and every doc edit flows through your approval. Unattended automation exists — it's the last row of the table below, and explicitly opted into.
Start with the principles — one page on the model everything shares: docs as checkable claims, propose → approve → apply, automation as a graduation. Then:
| Your repo today | You say | Guide |
|---|---|---|
| No docs yet | "document this project" | Starting docs from scratch |
| Docs exist — are they still true? | "is the README still accurate?" | the drift loop — demoed above, dissected below |
| Docs exist — accurate, but heavy | "audit the docs for bloat" | Auditing and fixing bloat |
| The drift loop feels routine — audit it unattended | "set up doc sync" | Turning on the nightly |
Two skills need no starting point because they trigger on the way: any edit to a README / runbook / CLAUDE.md invokes writing-docs (every assertion discharges its review obligation), and "should we document X?" invokes growing-docs (demand signals grow docs; calendars don't).
Not "the docs look a bit stale" — a machine-checkable record per claim. This is a real record from an actual run of python3 -m doclifecycle drift-audit (reproduced verbatim, against a small repo whose README still said make reset after the Makefile's target was renamed clean):
{
"id": "DRIFT-001",
"digest": "e748c840071e26532216ed2c4381e3007bc532b58572d19123a8db2862e8bdf1",
"code": "STALE",
"path": "docs/README.md",
"units": ["239c43567bf0107d4104098d06a205a9800c289769dfada237743ec7849f3cb6"],
"assertion": "Reset state = `make reset`.",
"assertion_class": "factual",
"obligation": "evidence",
"location": "docs/README.md:3",
"kind": "command",
"tier": 1,
"evidence": {
"observed": "the Makefile defines `clean:`, no `reset` target",
"source": "Makefile",
"line": 1
},
"fix": "Reset state = `make clean`."
}Three properties make this more than a report:
- Every verdict carries evidence — including
VERIFIED. "Looks consistent" is not a verdict. fixis the complete replacement text, not an instruction — it may span multiple physical lines when the claim it corrects does — sofixing-docscan land it as a one-hunk diff without re-deciding anything.- The shape is enforced mechanically — a bundled validator (
validate-drift-output.py, stdlib-only) rejects malformed records and emits a recomputedsummaryline automation can gate on.
Closing the loop is one more request — "apply that drift report" — and fixing-docs lands each approved STALE record's fix at the unit it names — location above is a display string the engine derives for readers; the units digest is what actually anchors the edit — touching nothing the report didn't flag.
Bloat audits emit the engine's own verdict envelope ({"schema_version": 1, "verdicts": [...]}) — ID'd records, a fixed six-verdict enum, cited evidence, their own validator — and go through the same door: you approve fixes by record ID, and fixing-docs mints that selection into the approval set the applier will not write without — the ID is how you say it, the record's digest is what gets bound. The bloat guide walks one end to end.
| Component | Type | Use it when |
|---|---|---|
bootstrapping-docs |
skill | Pointing at an undocumented repo — produces the smallest high-leverage doc set, then deliberately stops, recording deferrals in docs/doc-scope.md. |
growing-docs |
skill | Baseline docs exist but a demand signal says they fall short — writes the one artifact that absorbs the signal, then stops. The demand-driven counterpart to bootstrapping-docs. |
writing-docs |
skill | Writing or editing a repo-tracking doc (README, runbook, CLAUDE.md/AGENTS.md, reference), human- or agent-facing — every assertion carries current evidence, governing authority, or a coherence judgment; carries the agent-density bar and routes heavy agent docs to the llm-doc-writer agent. |
detecting-doc-drift |
skill | Auditing docs against the code for accuracy — extracts each claim, verifies it at the cheapest sufficient tier, emits a structured, parseable record. |
detecting-doc-bloat |
skill | Auditing docs for low-value content — redundant, verbose, duplicated, or past its useful form — emits a structured prune/condense/distill proposal. Read-only: it proposes, a human approves. |
fixing-docs |
skill | Applying an audit report — drift and bloat records alike, through one door: the IDs you approve mint an approval set, which becomes an edit plan the applier (python3 -m doclifecycle apply-plan) lands, and you get the working-tree diff back — the applier never stages; committing it is the change approval. The approval set is the only authority; nothing the report didn't flag gets touched. |
scheduling-doc-sync |
skill | Wiring a repo for unattended auditing — installs read-only drift and budgeted bloat audit Actions, manual apply for the drift records you select, optional policy-minted mechanical-fix PRs, and the weekly self-upgrade check. |
llm-doc-writer |
agent | A dispatchable subagent that produces LLM-optimized documentation with maximum context efficiency. |
doc-distiller |
agent | Handles one approved DISTILL record — verifies each durable claim, then returns the edit-plan operations that put the extractions in their living docs and retire the planning artifact, landed by the same applier as every other approved record. Dispatched by fixing-docs. |
The suite shares one contract: every assertion in a repo-tracking doc discharges a current review obligation — evidence for facts, governing authority for rules, and coherence evidence for rationale; only non-assertive prose is unjudged. That bar is what makes drift detectable, bloat judgeable, and fixes reviewable; the full argument, with pointers into the skill files that enforce it, is one page: docs/guides/principles.md.
The same contract is what lets the pieces compose instead of fight:
writing-docs mandates verifiable claims
detecting-doc-drift audits those claims for accuracy ┐
├→ fixing-docs lands approved fixes
detecting-doc-bloat audits those claims for weight ┘
scheduling-doc-sync installs the Actions that run the drift loop on a schedule and open the PR you approve
All of the above ships today. The automation layer — scheduling-doc-sync — installs a nightly drift audit and a weekly, chunked bloat audit; both audit jobs are read-only. The bloat model has a closed Task/read-only tool inventory and isolated settings, while trusted scripts render work orders, validate structured returns, and refuse report assembly if the checkout nevertheless changes. Its manual drift-apply dispatch takes the record digests you approve and opens a real pull request, while an explicitly committed standing policy may mint only the engine's closed mechanical drift classes into a review PR. Scheduled bloat findings go through the interactive fixing-docs approval door. No scheduled lane writes the default branch: landing anything still requires a person to review and merge the proposed diff. These lanes are wiring on top of the contracts in the detect skills, the one fix door, and their shared engine.
Every skill was written test-first — RED (baseline agents fail) → GREEN (skill fixes it) → REFACTOR (pressure-test for loopholes). Rules target failures that actually showed up in baseline runs, not best-practice folklore. Test records live under tests/. This README follows the plugin's own contract — every line above is a claim you can check against this repo.
And the loop is closed in this repo's own history: on its first nightly run here (2026-07-02), the scheduled lane of the day caught two stale claims in these very docs — one falsified by the commit that installed it — and opened the evidence PR that fixed them. Full record: tests/baselines/doc-sync-setup-red/DOGFOOD-first-catch.md. That lane has since been replaced by the five this repo now runs on itself (.github/workflows/): the read-only drift and bloat audits, the manual apply dispatch, the audit-chained policy lane its committed .doc-lifecycle/auto-apply-policy.json enables, and the weekly upgrade check.
toolshed is a personal Claude Code plugin marketplace; doc-lifecycle is the one plugin it ships today.
.claude-plugin/marketplace.json # the toolshed marketplace
plugins/doc-lifecycle/ # the published plugin
.claude-plugin/plugin.json
skills/ # 7 skills
agents/ # llm-doc-writer, doc-distiller
assets/ # social-card.png (hero + GitHub social preview),
# drift-audit-demo.svg + demo/make_cast.py (its generator)
docs/ # guides: user-facing walkthroughs; plans: design docs +
# handoff (not part of the installed plugin)
tests/ # RED/GREEN records + fixtures (not part of the installed plugin)
MIT — see LICENSE.
