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

6.8 KiB
Raw Blame 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)

  • 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: orphaneddelete-range/confirm, supersededmove-to-archive/auto, completed-in-placeinsert-frontmatter/auto, duplicateddedupe/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.