112 lines
6.8 KiB
Markdown
112 lines
6.8 KiB
Markdown
|
|
# 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.
|