cc-os/plugins/os-doc-hygiene/openspec/changes/archive/2026-06-24-add-check/tasks.md

112 lines
6.8 KiB
Markdown
Raw Normal View History

# 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 35 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.