6.4 KiB
6.4 KiB
doc-hygiene
Claude Code plugin that monitors and manages stale and bloated project
documentation. Installed globally, operates per-project. Reminds (deterministic,
zero-token) on SessionStart; checks and cleans on demand via skills.
Read
PRD.mdfirst — it is the source of truth for scope, decisions, and rationale. This file is the build map.
Core distinction (do not conflate)
- Stale = the doc is wrong (contradicted, orphaned, superseded, provisional, completed-in-place, duplicated). Remedy: fix or remove.
- Bloat = the doc is true but mostly irrelevant (distill, split, freeze). Remedy: change its altitude, almost never delete history.
Severity scales with injection frequency — a stale line in CLAUDE.md (auto
-injected every session) is worse than the same line in a doc nobody opens.
Design invariants (see PRD + reversion-protection pattern)
- The
SessionStarthook only reminds — it never runs analysis or mutates anything, and spends no AI tokens. All mutation is user-invoked. - Reminder snoozes (≤ once/day while stale) via
last_reminded. - State lives in-project under gitignored
.dochygiene/. No global index. - Report rollover: keep only the latest report. The tool must not become the bloat it polices.
- Cleanup is git-safe: clean/committed tree required (or auto WIP checkpoint), each run is one reviewable commit.
- Deterministic-first: scan/state/patch-apply/token-estimate are scripts (no model). AI does only classification and prose distillation.
- Safety tiers:
auto(deterministic+reversible+objective) runs without prompt;confirm(destructive/subjective/generative) escalates for approval. - mtime guard: never apply a cached edit to a file changed since the check.
- Frozen/ignored files (
hygiene: frozenfrontmatter,.dochygiene-ignore, append-only logs) are never flagged.
Changing any invariant requires updating invariants.md and the golden
examples, with explicit human approval.
Build order (report schema gates everything)
- Machine report schema — the contract every component consumes. Freeze first. (per-file: category, signals, op, op-type, safety tier, optional exact-edit, token estimate.)
- Deterministic core — build-spike #1 first: a trivial
hooks/hooks.jsonemitting asystemMessagebanner onSessionStart, confirmed to render visibly in a real session (no plugin in this collection wires a CC hook yet). Then: scanner (signals + shortlist), state store (timestamps, rollover, snooze, atomic writes, project-root resolution), reminder hook (SessionStart,matcher: startup|resume, injected-clock testable). checkskill — scanner shortlist → AI classify (Sonnet, judgment only) →report_builder.pydeterministic finalize pass → human + machine reports → updatelast_check. The finalize pass sits between model classification and the report write (invariants #6, #10): it computesexpected_sha256, looks upis_destructive/is_reversiblefromexact_edit.kind, derivessafety_tierviavalidate_report.py's single-sourcederive_safety_tier(...), sourcesraw_tokensfrom the token estimator, and assembles the schema-valid machine report + human-report skeleton. The model authors none of those four fields.cleanskill + patch-applier — consume report, mtime-guard, applydeterministicops mechanically, delegategenerativeto Sonnet, gateconfirm, git checkpoint + single commit, scopable by category/file → updatelast_clean. Thensweep= check-then-clean.- RESOLVED (Phase 4):
insert-frontmatterops havehas_anchor=Falseand carry noexpected_sha256, so invariant #8's content hash cannot protect them. The applier re-derives frontmatter freshness at apply time: re-read the file, parse frontmatter; key present with target value → idempotent no-op; key present with different value → skip (frontmatter-key-conflict); key absent → insert. No cached hash is trusted. This resolves the deferred hole noted inadd-check.
- RESOLVED (Phase 4):
- Bonus (v2) — deterministic patch-apply hardening + token estimator (local tokenizer, injection-frequency weighting, bottom-up rollup).
Intended layout (per cc-plugins conventions)
doc-hygiene/
├── .claude-plugin/plugin.json # done
├── PRD.md # done — source of truth
├── CLAUDE.md # this file
├── invariants.md # TODO — declare behavioral invariants
├── skills/ # slash-command entry points: /os-doc-hygiene:<skill>
│ ├── check/SKILL.md
│ ├── clean/SKILL.md
│ ├── status/SKILL.md
│ └── sweep/SKILL.md
├── scripts/ # Python OOP — scanner, state, reminder, estimator, report_builder, applier
│ └── ...
├── hooks/hooks.json # SessionStart wiring → reminder script (systemMessage banner)
├── examples/golden/ # classifier golden examples (input tree → expected report)
└── tests/ # unit tests per deterministic seam + fixture doc trees
Conventions (inherited from cc-plugins)
- Language: Python, OOP — small single-responsibility classes, dependency
injection, immutable transforms where possible. See
../cc-architect/references/tool-patterns/deterministic-scripting.md. - Scripts: structured JSON output, correct exit codes, idempotent, testable in isolation with injected clock/filesystem.
- Model routing: scripts = no model; classification = Sonnet (hard cases → Opus); generative distillation = Sonnet (NOT Haiku); orchestration = Opus.
- Reversion protection:
invariants.md+examples/golden/+ change-impact analysis + human gate for golden-example changes. See../cc-architect/references/tool-patterns/reversion-protection.md. - Read a directory's
CONTEXT.mdbefore its source files (progressive disclosure); generate one per directory as it's built.
Completion steps (do NOT do until functional)
- Add
invariants.mdand 3–5 golden examples. - Register in
../.claude-plugin/marketplace.json(name + source + description, matchingplugin.json). See../.claude/references/plugin-registration.md. - Verify installable via
/install doc-hygiene@cc-plugins.