114 lines
6.4 KiB
Markdown
114 lines
6.4 KiB
Markdown
# 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.md` first — 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)
|
||
|
||
1. The `SessionStart` hook **only reminds** — it never runs analysis or mutates
|
||
anything, and spends no AI tokens. All mutation is user-invoked.
|
||
2. Reminder **snoozes** (≤ once/day while stale) via `last_reminded`.
|
||
3. State lives **in-project** under gitignored `.dochygiene/`. No global index.
|
||
4. **Report rollover**: keep only the latest report. The tool must not become
|
||
the bloat it polices.
|
||
5. Cleanup is **git-safe**: clean/committed tree required (or auto WIP
|
||
checkpoint), each run is one reviewable commit.
|
||
6. **Deterministic-first**: scan/state/patch-apply/token-estimate are scripts
|
||
(no model). AI does only classification and prose distillation.
|
||
7. **Safety tiers**: `auto` (deterministic+reversible+objective) runs without
|
||
prompt; `confirm` (destructive/subjective/generative) escalates for approval.
|
||
8. **mtime guard**: never apply a cached edit to a file changed since the check.
|
||
9. Frozen/ignored files (`hygiene: frozen` frontmatter, `.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)
|
||
|
||
1. **Machine report schema** — the contract every component consumes. Freeze
|
||
first. (per-file: category, signals, op, op-type, safety tier, optional
|
||
exact-edit, token estimate.)
|
||
2. **Deterministic core** — *build-spike #1 first*: a trivial `hooks/hooks.json`
|
||
emitting a `systemMessage` banner on `SessionStart`, 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).
|
||
3. **`check` skill** — scanner shortlist → AI classify (Sonnet, judgment only) →
|
||
**`report_builder.py`** deterministic finalize pass → human + machine reports →
|
||
update `last_check`. The finalize pass sits **between** model classification and
|
||
the report write (invariants #6, #10): it computes `expected_sha256`, looks up
|
||
`is_destructive`/`is_reversible` from `exact_edit.kind`, derives `safety_tier`
|
||
via `validate_report.py`'s single-source `derive_safety_tier(...)`, sources
|
||
`raw_tokens` from the token estimator, and assembles the schema-valid machine
|
||
report + human-report skeleton. The model authors none of those four fields.
|
||
4. **`clean` skill + patch-applier** — consume report, mtime-guard, apply
|
||
`deterministic` ops mechanically, delegate `generative` to Sonnet, gate
|
||
`confirm`, git checkpoint + single commit, scopable by category/file →
|
||
update `last_clean`. Then **`sweep`** = check-then-clean.
|
||
- **RESOLVED (Phase 4):** `insert-frontmatter` ops have `has_anchor=False` and
|
||
carry **no** `expected_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 in
|
||
`add-check`.
|
||
5. **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
|
||
├── commands/ # /hygiene entry points (check / clean / sweep / status)
|
||
├── skills/
|
||
│ ├── hygiene-check/SKILL.md
|
||
│ └── hygiene-clean/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.md` before its source files (progressive
|
||
disclosure); generate one per directory as it's built.
|
||
|
||
## Completion steps (do NOT do until functional)
|
||
|
||
1. Add `invariants.md` and 3–5 golden examples.
|
||
2. Register in `../.claude-plugin/marketplace.json` (name + source + description,
|
||
matching `plugin.json`). See `../.claude/references/plugin-registration.md`.
|
||
3. Verify installable via `/install doc-hygiene@cc-plugins`.
|