165 lines
9.9 KiB
Markdown
165 lines
9.9 KiB
Markdown
---
|
|
type: user-guide
|
|
title: os-doc-hygiene — User Guide
|
|
summary: Mental model and gotchas for using the os-doc-hygiene Claude Code plugin well — the stale-vs-bloat distinction, check/calibrate/clean/sweep semantics, the two rule files, rule-activation pitfalls, safety tiers, and report-reading traps.
|
|
tags:
|
|
- type/user-guide
|
|
- tool/os-doc-hygiene
|
|
- tool/claude-code
|
|
- project/cc-os
|
|
- domain/documentation
|
|
scope: global
|
|
last_updated: 2026-07-15
|
|
date: 2026-07-15
|
|
related:
|
|
- cc-os-hub
|
|
source: cc-os
|
|
---
|
|
|
|
# os-doc-hygiene — User Guide
|
|
|
|
## What it is
|
|
|
|
`os-doc-hygiene` is a globally-installed Claude Code plugin that monitors and manages
|
|
project documentation across two independent failure modes: docs that are *wrong* and
|
|
docs that are *true but bloated*. It reminds passively every session (zero tokens, no
|
|
mutation) and does its actual work — scanning, classifying, and cleaning — only on
|
|
demand via skills. It is not a linter for prose quality; it is a lifecycle manager for
|
|
`.md` files, extended (2026-07-15) with a rulebook that can also delete or archive
|
|
files on a schedule or trigger.
|
|
|
|
## Mental model
|
|
|
|
- **Stale vs bloat is the core axis, and they are not the same failure.** *Stale* means
|
|
the doc is *wrong* — contradicted, orphaned, superseded, provisional-but-abandoned,
|
|
completed-in-place, or duplicated; the remedy is fix or remove. *Bloat* means the doc
|
|
is *true but mostly irrelevant at its current altitude* — the remedy is to distill,
|
|
split, or freeze it, almost never to delete history. Conflating the two leads to
|
|
either deleting a correct-but-long doc or leaving a wrong-but-short one in place.
|
|
- **Severity scales with injection frequency, not file size.** A stale line in a file
|
|
that gets auto-injected every session (e.g. a `CLAUDE.md`) is worse than the same
|
|
line in a doc nobody opens, even if the second doc is larger.
|
|
- **The skill quartet has distinct jobs, not a redundant surface:**
|
|
- `:check` — scan + classify against the *existing* rules, writes a report. Does not
|
|
learn anything new.
|
|
- `:calibrate` — the *learn-new-rules* loop. It clusters the pool of files no
|
|
existing rule governs, proposes candidate globs, and gets them judged and
|
|
persisted. It is recurring and self-narrowing: each pass only looks at what's
|
|
still unmatched, and it hard-caps at 3 rounds per invocation (stops early at
|
|
<2 new rules or <10% unmatched shrink).
|
|
- `:clean` — applies the *most recent* report's findings (deterministic ops
|
|
mechanically, generative ops via a subagent, confirm-tier gated).
|
|
- `:sweep` — check-then-clean back to back; it does not skip clean's confirm gate,
|
|
it just chains the two.
|
|
- **Rules live in two places with different trust levels.** A committed,
|
|
per-project `.dochygiene-rules.json` at the repo root (reviewable, versioned,
|
|
travels with the repo — ADR-0038) holds the project's actual lifecycle rules. A
|
|
gitignored `.cc-os/dochygiene/` directory (legacy fallback path: `.dochygiene/`)
|
|
holds only local state and reports — never rules. The plugin also ships a global
|
|
`plugins/os-doc-hygiene/rulebook.json`, but that ships **only IGNORE seeds**
|
|
(`graphify-out/**`, `.dochygiene/**` as of 2026-07-15) — it is not a source of
|
|
general-purpose lifecycle rules for your project.
|
|
- **A rule with no `lifetime` field is not "keep" — it's an IGNORE sentinel.** IGNORE
|
|
paths are pruned from the scan walk entirely; they never appear in a report, not
|
|
even as "kept." This is distinct from `lifetime: keep`, which *is* walked and
|
|
reported, just never deleted. If you expect a file to show up as "kept" in a
|
|
report and it never does, check whether an IGNORE rule (no `lifetime`) is silently
|
|
swallowing it.
|
|
|
|
## Nuances & gotchas
|
|
|
|
- **`temporary` deletion needs BOTH conditions, not either.** A file is only deleted
|
|
under a `temporary`-lifetime rule once it is *both* ranked past `retain_recent`
|
|
(i.e. not one of the N newest matches) *and* older than `max_age_days`. A file that
|
|
is old but still in the top-N-newest survives; a file that's low-ranked but too
|
|
young also survives. Don't reason about temporary rules with just one of the two
|
|
numbers.
|
|
- **`served_when` vs `served_when_path` is a trust boundary, not a style choice.**
|
|
`served_when` is free text judged by the LLM classifier — it is *always* confirm-tier,
|
|
never auto, regardless of git state. `served_when_path` is a path pattern the scanner
|
|
can prove deterministically (e.g. "moved into `archive/{id}/`") — it *can* be
|
|
auto-tier. If you want a rule to eventually run silently, the path is to graduate it
|
|
from `served_when` to `served_when_path` by adopting a structural completion
|
|
convention (see conventions.json below) — not to just mark it `confirm: true` and
|
|
move on.
|
|
- **A rule missing `confirmed_by` is silently inactive.** It doesn't error, doesn't
|
|
warn loudly — it just never acts. If a rule you added doesn't seem to be doing
|
|
anything, check for a missing or malformed `confirmed_by`/`confirmed_on` before
|
|
assuming the glob is wrong.
|
|
- **Safety tiers are about evidence quality and recoverability, not file type.** `auto`
|
|
requires deterministic + reversible + objective; `confirm` covers anything
|
|
destructive, subjective, or generative. Critically: a dirty or untracked file
|
|
*always* escalates to confirm even if a rule would otherwise make it auto — there's
|
|
no git history to recover from. And git state is **re-verified at apply time**, not
|
|
trusted from when the report was generated — a file that was tracked+clean at
|
|
`:check` time but got dirtied before `:clean` runs will be downgraded to confirm (or
|
|
skipped as `git-state-changed-since-check`).
|
|
- **The report's "cleared" count is not what it sounds like.** `cleared` = shortlist
|
|
entries minus findings — i.e. candidates the deterministic scanner surfaced but the
|
|
classifier decided need no action. It can coincidentally equal (or come close to)
|
|
the total files-scanned count, which misleads you into thinking "cleared" means
|
|
"scanned." The AI classifier only ever sees the signal-bearing subset the scanner
|
|
flagged — in the first cc-os run, that was 44 of 384 files scanned; the rest were
|
|
cleared deterministically and never reached the model at all.
|
|
- **There is deliberately no calendar-date staleness signal.** The scanner does not
|
|
flag "this file hasn't been touched in N days" as a signal by itself. Staleness
|
|
comes from broken references, file length (bloat), edit-recency-vs-churn patterns,
|
|
and lifecycle rules — not raw age. A quiet-but-correct doc is not stale just because
|
|
it's old.
|
|
- **The scanner is a pure script — no tokens, full re-walk every run, `.md` files
|
|
only.** It doesn't cache or incrementally diff; every `:check` re-walks the whole
|
|
tree (minus exclusions). Don't expect it to pick up non-Markdown docs (READMEs in
|
|
other formats, code comments) as scan targets.
|
|
- **The `SessionStart` hook only reminds — never analyzes or mutates.** It fires at
|
|
most once per calendar day while docs are stale (keyed off `last_reminded`), and
|
|
spends zero AI tokens. If you see the reminder banner, no scan has happened yet —
|
|
you still have to run `:check` to get real findings.
|
|
- **New-project onboarding is not automatic.** A fresh project has no
|
|
`.dochygiene-rules.json`. Until you run `:calibrate` once to generate it, only the
|
|
global IGNORE seeds apply and *no* lifecycle deletions happen — `:check`/`:clean`
|
|
still work for stale/bloat findings, but the lifecycle layer is inert.
|
|
|
|
## When NOT to use it / limits
|
|
|
|
- Not a prose/style linter — it does not check grammar, tone, or writing quality.
|
|
- Not a general file manager — the lifecycle/delete features only ever act on paths
|
|
matched by an explicitly confirmed rule; unmatched files are left alone
|
|
("unmatched = unmanaged").
|
|
- Not a substitute for `:calibrate` when a project has structurally unusual doc
|
|
conventions (e.g. specs/plans as the shipped product) — the global rulebook alone
|
|
will under- or over-match.
|
|
- Don't expect deletions to be silently recoverable outside git — deletion is a real
|
|
`git rm` in a dedicated commit; git history is the only archive. There is no
|
|
graveyard directory.
|
|
- Not designed to run unattended in CI as a destructive step without review — confirm-
|
|
tier gates exist precisely because classifier judgment and dirty/untracked state
|
|
need a human.
|
|
|
|
## Command reference
|
|
|
|
| Command | Effect |
|
|
|---|---|
|
|
| `/os-doc-hygiene:status` | Read-only timestamps (last check/clean/reminded) + report presence. No model, no scan. |
|
|
| `/os-doc-hygiene:check [--scope <glob-or-path>] [--category <class\|subtype>]` | Scan + classify against existing rules, write a report. |
|
|
| `/os-doc-hygiene:calibrate` | Learn-new-rules loop over the unmatched pool; hard cap 3 rounds; nothing persists without the human seeing the rule report first. |
|
|
| `/os-doc-hygiene:clean [--scope <glob-or-path>] [--category <class\|subtype>]` | Apply the latest report's findings; confirm-tier entries gated. |
|
|
| `/os-doc-hygiene:sweep [--scope <glob-or-path>] [--category <class\|subtype>]` | check then clean, same confirm gate as standalone clean. |
|
|
|
|
## Pointers
|
|
|
|
- `plugins/os-doc-hygiene/CLAUDE.md` (cc-os repo) — build map + the stale-vs-bloat
|
|
invariant summary.
|
|
- `plugins/os-doc-hygiene/lifecycle-spec.md` (cc-os repo) — the full lifecycle-rules
|
|
design: taxonomy, rulebook schema, deletion semantics, `:calibrate` protocol.
|
|
- `plugins/os-doc-hygiene/invariants.md` (cc-os repo) — the reversion-protection
|
|
contract; read before changing any behavioral invariant.
|
|
- `plugins/os-doc-hygiene/conventions.json` (cc-os repo) — the determinism-promotion
|
|
catalog (`archive-bucket`, `status-frontmatter`) that graduates `served_when` rules
|
|
to auto-tier `served_when_path`.
|
|
- `docs/adr/0038-*.md` through `0041-*.md` (cc-os repo) — rulebook location, deletion
|
|
autonomy tiers, no-ignore-propagation, and determinism-promotion decisions.
|
|
|
|
## Related
|
|
|
|
- [[cc-os-hub]] — the cc-os plugin family this tool belongs to.
|