# Tasks: Add Check

> **Execution shape:** Task 1 is a **human-gate** (already discharged — the
> `report_builder.py` finalize pass is approved). Group 2 (`report_builder.py` +
> tests) and Group 3 (`commands/hygiene.md`) are **independent and run in
> parallel**. Group 4 (the `hygiene-check` skill) is **sequential after Groups 2
> and 3**. Group 5 (classifier goldens + hermetic harness) is **sequential after
> Groups 2 and 4** and is **human-gated** per the META-RULE. Group 6 (CONTEXT.md)
> runs last. Model routing is annotated per group.

## 1. Human-gate — approve the deterministic finalize pass (DONE)

- [x] 1.1 Approve `report_builder.py` as a standalone, model-free finalize pass
      interposed between model classification and `StateStore.write_report`, owning
      the four fields the model must not author (`expected_sha256`, `safety_tier`,
      `is_destructive`/`is_reversible` for deterministic ops, `raw_tokens`).

## 2. `report_builder.py` + tests — parallel with Group 3 (model: Sonnet)

- [ ] 2.1 Implement `scripts/report_builder.py` as a standalone, model-free
      assembler. Input: the scanner artifact + the model's slim per-file
      classification proposals. Output: a full schema-valid machine report + a
      deterministic human-report skeleton. Use an injected clock/filesystem.
- [ ] 2.2 Compute `exact_edit.expected_sha256` over each file's **current bytes**
      for anchor-bearing kinds only; note that `insert-frontmatter`
      (`has_anchor = False`) carries no `expected_sha256` (deferred hole for clean).
- [ ] 2.3 Set `(is_destructive, is_reversible)` from `KIND_TABLE[kind]` and compute
      `safety_tier = derive_safety_tier(op_type, is_destructive, is_reversible)`,
      both **imported** from `validate_report.py` (single source of truth —
      invariant #10; no re-implementation).
- [ ] 2.4 Extract the span (anchor range / `reducible_range` / whole file for
      `move-to-archive`) and source `token_estimate` via
      `token_estimator.default_estimator().estimate_for_report(span_text)` —
      v1 `raw_tokens` only, weighting `null` (invariant #6).
- [ ] 2.5 Assemble the envelope: `schema_version` `"1.0"`, `tool_version` read from
      `plugin.json` (flag a mismatch with `valid_report.json`), per-entry
      `generated_at` = that file's hash instant, envelope `generated_at` = the run
      instant. Emit the deterministic human-report skeleton (mechanical parts only).
- [ ] 2.6 Reject malformed proposal JSON before computing any field.
- [ ] 2.7 **Tests:** assert the sha256, the derived `(is_destructive,
      is_reversible, safety_tier)`, and `raw_tokens` for each kind; assert the
      assembled report round-trips through `validate_report.py` (exit 0); assert the
      generative path counts `raw_tokens` over the non-persisted `reducible_range`;
      injected clock/fs.

## 3. `commands/hygiene.md` — parallel with Group 2 (model: Haiku)

- [ ] 3.1 Author a single `commands/hygiene.md` with minimal frontmatter
      (`name: hygiene`; description naming `/hygiene check` and `/hygiene status`)
      that dispatches on `$ARGUMENTS`.
- [ ] 3.2 Route `check [--scope <glob-or-path>] [--category <class|subtype>]` →
      invoke the `hygiene-check` skill (pass the flags through).
- [ ] 3.3 Implement `status` **inline** (a few `python3` calls): read
      `get_last_check` / `get_last_clean` / `get_last_reminded` and whether a report
      exists. Read-only — no scan, no model.
- [ ] 3.4 Reserve `clean` / `sweep` with a "not yet implemented (Phase 4)" message;
      print usage + current status for no/unknown args.

## 4. `skills/hygiene-check/SKILL.md` — sequential after Groups 2, 3 (model: Sonnet)

- [ ] 4.1 Author `skills/hygiene-check/SKILL.md` with frontmatter mirroring the
      `commit` skill. The subagent points at a **workflow doc**, not SKILL.md
      itself, to avoid recursion (per the `commit`-skill precedent).
- [ ] 4.2 Implement the deterministic steps: scan (`scanner.py`, `--scope` →
      `--globs`/path filter), select signal-bearing candidates (zero-signal files
      presumptively cleared, not read by the model), finalize (`report_builder.py`),
      validate **on a scratch path** (validate-before-rollover, invariant #4), write
      (`write_report`), stamp (`set_last_check` with the same run instant).
- [ ] 4.3 Write the **Sonnet classification subagent prompt**: per signal-bearing
      file, return a slim judgment-only proposal (`category` from the closed enum,
      `signals` verbatim + optional `detail` gloss, `op`, `op_type`, the
      `exact_edit` **skeleton** for deterministic or a non-persisted
      `reducible_range` for generative, `confidence`). The model supplies **no**
      `expected_sha256` / `is_destructive` / `is_reversible` / `safety_tier`.
- [ ] 4.4 Encode the decision rules (subtype → kind → tier) and the Opus-escalation
      rule (low confidence on stale-vs-bloat or destructive-vs-generative).
- [ ] 4.5 Encode failure handling: validation fail (exit 1) → do not write, map
      violations to entries, re-prompt or drop and re-validate; usage error (exit 2)
      → stop; empty shortlist → write empty-`entries` report + stamp; malformed
      proposal → re-prompt. Record `--scope` / `--category` in the report header.

## 5. Classifier goldens + hermetic harness — after Groups 2, 4; HUMAN-GATED (model: Sonnet)

- [ ] 5.1 Create 3–5 static fixture trees under
      `examples/golden/classifier/<n>-<name>/` (`input/` with stable sha256s,
      `expected.json`, optional `notes.md`), each mapping a subtype to a **distinct**
      `exact_edit.kind`: `orphaned`→`delete-range`/`confirm`,
      `superseded`→`move-to-archive`/`auto`,
      `completed-in-place`→`insert-frontmatter`/`auto`, `duplicated`→`dedupe`/`auto`,
      `distill`→generative/`confirm`.
- [ ] 5.2 Implement `tests/test_classifier_golden.py` **hermetically** (no live
      model call): (1) `scanner.py` on `input/` emits the expected signals on the
      right paths; (2) `validate_report.py` on each `expected.json` → exit 0; (3)
      stable-field match against a captured/committed check output (`category.class`,
      `category.subtype`, `op_type`, derived `safety_tier`, `exact_edit.kind`).
      Op-prose and exact anchor line numbers are advisory (flag, do not hard-fail).
- [ ] 5.3 Document the **separate** live model-classification regression harness
      (manually/agent-invoked, NOT part of the unit suite).
- [ ] 5.4 Update `examples/golden/CONTEXT.md` to distinguish classifier goldens from
      the schema-shape fixtures.

## 6. CONTEXT.md updates — last (model: Haiku)

- [ ] 6.1 Author `CONTEXT.md` for `commands/` and `skills/` (progressive
      disclosure).
- [ ] 6.2 Update `scripts/CONTEXT.md` to add `report_builder.py` (its role as the
      deterministic finalize pass and the symbols it imports).

## 7. Validation

- [ ] 7.1 Run `openspec validate add-check --strict` and confirm it passes.
