6.8 KiB
6.8 KiB
Tasks: Add Check
Execution shape: Task 1 is a human-gate (already discharged — the
report_builder.pyfinalize pass is approved). Group 2 (report_builder.py+ tests) and Group 3 (commands/hygiene.md) are independent and run in parallel. Group 4 (thehygiene-checkskill) 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.pyas a standalone, model-free finalize pass interposed between model classification andStateStore.write_report, owning the four fields the model must not author (expected_sha256,safety_tier,is_destructive/is_reversiblefor deterministic ops,raw_tokens).
2. report_builder.py + tests — parallel with Group 3 (model: Sonnet)
- 2.1 Implement
scripts/report_builder.pyas 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_sha256over each file's current bytes for anchor-bearing kinds only; note thatinsert-frontmatter(has_anchor = False) carries noexpected_sha256(deferred hole for clean). - 2.3 Set
(is_destructive, is_reversible)fromKIND_TABLE[kind]and computesafety_tier = derive_safety_tier(op_type, is_destructive, is_reversible), both imported fromvalidate_report.py(single source of truth — invariant #10; no re-implementation). - 2.4 Extract the span (anchor range /
reducible_range/ whole file formove-to-archive) and sourcetoken_estimateviatoken_estimator.default_estimator().estimate_for_report(span_text)— v1raw_tokensonly, weightingnull(invariant #6). - 2.5 Assemble the envelope:
schema_version"1.0",tool_versionread fromplugin.json(flag a mismatch withvalid_report.json), per-entrygenerated_at= that file's hash instant, envelopegenerated_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), andraw_tokensfor each kind; assert the assembled report round-trips throughvalidate_report.py(exit 0); assert the generative path countsraw_tokensover the non-persistedreducible_range; injected clock/fs.
3. commands/hygiene.md — parallel with Group 2 (model: Haiku)
- 3.1 Author a single
commands/hygiene.mdwith minimal frontmatter (name: hygiene; description naming/hygiene checkand/hygiene status) that dispatches on$ARGUMENTS. - 3.2 Route
check [--scope <glob-or-path>] [--category <class|subtype>]→ invoke thehygiene-checkskill (pass the flags through). - 3.3 Implement
statusinline (a fewpython3calls): readget_last_check/get_last_clean/get_last_remindedand whether a report exists. Read-only — no scan, no model. - 3.4 Reserve
clean/sweepwith 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.mdwith frontmatter mirroring thecommitskill. The subagent points at a workflow doc, not SKILL.md itself, to avoid recursion (per thecommit-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_checkwith the same run instant). - 4.3 Write the Sonnet classification subagent prompt: per signal-bearing
file, return a slim judgment-only proposal (
categoryfrom the closed enum,signalsverbatim + optionaldetailgloss,op,op_type, theexact_editskeleton for deterministic or a non-persistedreducible_rangefor generative,confidence). The model supplies noexpected_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-
entriesreport + stamp; malformed proposal → re-prompt. Record--scope/--categoryin 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, optionalnotes.md), each mapping a subtype to a distinctexact_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.pyhermetically (no live model call): (1)scanner.pyoninput/emits the expected signals on the right paths; (2)validate_report.pyon eachexpected.json→ exit 0; (3) stable-field match against a captured/committed check output (category.class,category.subtype,op_type, derivedsafety_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.mdto distinguish classifier goldens from the schema-shape fixtures.
6. CONTEXT.md updates — last (model: Haiku)
- 6.1 Author
CONTEXT.mdforcommands/andskills/(progressive disclosure). - 6.2 Update
scripts/CONTEXT.mdto addreport_builder.py(its role as the deterministic finalize pass and the symbols it imports).
7. Validation
- 7.1 Run
openspec validate add-check --strictand confirm it passes.