# Change: Add Check ## Why The deterministic substrate is in place: the `SessionStart` reminder, the scanner (shortlist + per-path signals), the state store (timestamps, atomic writes, report rollover), and the token estimator all exist and are deterministic, zero-model seams. The reminder banner already advertises `/hygiene check` as the action the user should take when docs are stale — **but no `/hygiene` command and no check skill exist yet**, so following the reminder's own advice does nothing. This is build step #3 per CLAUDE.md. It is the plugin's first AI pass: it folds the scanner's intermediate artifact and a Sonnet classification into a schema-valid machine report and a human report, then stamps `last_check`. Four per-entry fields on the frozen report schema **cannot** be model-authored without violating an invariant or being physically impossible — `exact_edit.expected_sha256` (sha256 of real file bytes), `safety_tier` (invariant #10: derived only by `derive_safety_tier(...)`, never model-assigned), `is_destructive`/`is_reversible` for deterministic ops (fixed by `exact_edit.kind` via `KIND_TABLE`), and `token_estimate.raw_tokens` (invariant #6: from the local token estimator, never a model). Therefore check MUST interpose a **deterministic finalize pass** between model classification and the report write. This change introduces that pass as a standalone, model-free assembler. ## What Changes - **Deterministic finalize pass (`report_builder.py`):** a new standalone, model-free script that sits **between** the model's classification and `StateStore.write_report`. Input: the scanner artifact plus the model's slim per-file classification proposals. For each proposal it reads the anchored span, computes `expected_sha256` over the file's current bytes, looks up `(is_destructive, is_reversible)` from `KIND_TABLE[kind]`, calls `derive_safety_tier(...)` **imported** from `validate_report.py` (single source of truth, invariant #10), sources `raw_tokens` by calling `token_estimator.default_estimator().estimate_for_report(span_text)` (invariant #6), stamps each entry's `generated_at` at that file's hash instant, and assembles the schema-valid machine report envelope plus a deterministic human-report skeleton. Output: the full machine report + human skeleton. - **Command surface (`commands/hygiene.md`):** a single command file dispatching on `$ARGUMENTS`. `check [--scope ] [--category ]` invokes the `hygiene-check` skill; `status` reads lifecycle timestamps and report presence inline (read-only, no scan, no model); `clean` / `sweep` are reserved with a "not yet implemented (Phase 4)" message; no/unknown args print usage plus current status. - **Check skill (`skills/hygiene-check/SKILL.md`):** the orchestration. Modeled on the `commit` skill: the SKILL.md workflow runs deterministic scripts via Bash, dispatches a Sonnet subagent for judgment-only classification, then runs deterministic scripts to finalize, validate, and write. Enforces the validate-before-rollover sequencing (validation runs on a scratch path, never in `.dochygiene/`, because `write_report` deletes the prior pair first — invariant #4). - **Classifier golden examples + hermetic harness:** Layer-2 reversion-protection fixtures under `examples/golden/classifier/` (input doc tree → expected classification report), distinct from the schema-shape fixtures (`valid_report.json` / `invalid_report.json`). A hermetic pytest harness asserts only deterministic/stable parts (no live model call); the live model-classification regression is a separate, manually/agent-invoked harness. ## Capabilities ### New Capabilities - `doc-check`: the first AI pass and its deterministic guardrails — the `/hygiene` command surface (`check` / `status`, with `clean` / `sweep` reserved), the `hygiene-check` skill orchestration (scan → Sonnet classify → deterministic finalize → validate-before-write → write + stamp), the model-free `report_builder.py` finalize pass that owns the four non-model-authored fields, the validate-before-rollover sequencing constraint, and the hermetic classifier golden harness. One capability: the command, the skill, the finalize pass, and the report-write sequencing are a single check pipeline whose pieces are only meaningful together; splitting `report_builder` out would scatter a tightly coupled flow across capabilities. ### Modified Capabilities None. The `report-schema` capability is **consumed, not modified** — this change populates the frozen schema's `scan`, `shortlist`, and `entries` (including the four fields the schema requires be derived/computed, not model-assigned) and changes none of its requirements. ## Impact - **Affected specs:** creates one new capability — `doc-check`. Consumes the frozen `report-schema`, `doc-scanner`, and `state-store` capabilities without modifying them. - **Affected code:** introduces a new deterministic script `scripts/report_builder.py` (plus its unit tests), the command file `commands/hygiene.md`, the skill `skills/hygiene-check/SKILL.md`, and classifier golden fixtures + a hermetic test under `examples/golden/classifier/` and `tests/`. Imports `derive_safety_tier`/`KIND_TABLE` from `validate_report.py` and `default_estimator`/`estimate_for_report` from `token_estimator.py` (no re-implementation). - **Out of scope (named owners, so nothing is left ownerless):** - The **`clean` skill + patch-applier**, the **`sweep`** orchestration, and the **mtime/content guard application** — the upcoming `add-clean` change. Note the **deferred hole** this change documents: `insert-frontmatter` ops have `has_anchor = False` and therefore carry **no** `expected_sha256`, so the invariant #8 content guard does not protect them at clean time. Phase 4 (clean) MUST handle `insert-frontmatter` application explicitly. - **`token_estimate` weighting** (`injection_frequency`, `weighted_tokens`, bottom-up rollup) — the v2 bonus per PRD phase 5. This change populates only `raw_tokens`. - The **live model-classification regression harness** (running an actual check against a fixture tree and diffing) — a separate, manually/agent-invoked harness, not part of the unit suite. - **Dependencies:** depends on the frozen `report-schema` (the report shape and the `derive_safety_tier`/`KIND_TABLE` single source of truth), the `doc-scanner` intermediate artifact, the `state-store` (`write_report`, `set_last_check`, rollover), and the token estimator. Unblocks the `add-clean` change.