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

8.7 KiB
Raw Blame History

Tasks: Add Clean

Execution shape: T1 and T2 are already DONE (patch_applier.py + 263 tests pass). T3 (workflows/distill.md) has no upstream dependency among the remaining tasks and can start immediately. T4 (hygiene-clean SKILL.md, git-safety inline) is sequential after T3. T5 (integration tests) is sequential after T4. T6 (commands/hygiene.md update) and T7 (CONTEXT.md + CLAUDE.md) are independent of T3T5 and can run in parallel with any group. Model routing is annotated per task.

1. patch_applier.py — DONE (model: Sonnet)

  • 1.1 Implement scripts/patch_applier.py as a standalone, model-free applier. PatchApplier(project_root, fs, git) with apply_indexed([(orig_index, entry)]) → {applied, skipped, failed, staged_paths}. CLI: python patch_applier.py --report <path> --apply-indices 0,2,3. Exit 0 all / 1 partial / 2 usage.
  • 1.2 Implement per-file transaction: read bytes once, verify sha256 for every anchored entry, skip whole file on mismatch (content-changed-since-check), apply anchored edits descending by anchor.start_line, apply insert-frontmatter last, write once, stage once.
  • 1.3 Implement insert-frontmatter re-derivation at apply time: re-read file, parse frontmatter; key+target value → idempotent no-op; key+conflict → frontmatter-key-conflict skip; key absent → insert (create --- block if needed) as last in-memory step.
  • 1.4 Implement incompatible-ops detection: per file, skip whole file (incompatible-ops-on-file) if move-to-archive co-occurs with any other op, or if anchor ranges overlap. Never compose in v1.
  • 1.5 Implement all KIND_TABLE kinds: delete-range, move-to-archive (git mv, mkdir dest, auto), insert-frontmatter (auto), replace-text (first occurrence in span, auto), dedupe (auto). (is_destructive, is_reversible) from KIND_TABLE, not entry.
  • 1.6 Handle edge cases: move-to-archive source-gone+dest-present → idempotent success; replace-text/insert-frontmatter already applied → no-op success; non-UTF-8 file → non-utf8-content skip; generative/unknown kind → rejected/skip.

2. patch_applier.py tests — DONE (model: Sonnet)

  • 2.1 Write unit tests for per-file transaction: sha256 mismatch skips entire batch; clean match applies in descending-anchor order; insert-frontmatter applied last; write-once / stage-once assertions.
  • 2.2 Write unit tests for insert-frontmatter re-derivation: idempotent no-op, key-conflict skip, key-absent insert (with and without existing --- block).
  • 2.3 Write unit tests for incompatible-ops detection: move-to-archive+other → skip; overlapping anchors → skip; non-conflicting entries → proceed.
  • 2.4 Write unit tests for every KIND_TABLE kind: delete-range, move-to-archive (git mv both sides, mkdir dest), replace-text (first occ in span), dedupe, insert-frontmatter. Assert exit codes (0/1/2).
  • 2.5 Confirm 263 tests pass: pytest tests/ green.

3. workflows/distill.md — generative Sonnet workflow (model: Sonnet)

  • 3.1 Author workflows/distill.md as the Sonnet generative workflow (LOOP-GUARD: the skill points the subagent at this workflow doc, not SKILL.md, to avoid recursion). Document the LOOP-GUARD pattern in the workflow header.
  • 3.2 Specify the subagent inputs: live file contents (read at dispatch time, not from report cache), the report entry (op, category, signals, detail).
  • 3.3 Specify the output contract: distill → condensed prose replacing the span; split → new-primary prose + archived-section to append or move; skill writes and stages the result.
  • 3.4 Specify failure handling: if Sonnet returns malformed output, log and skip the entry (do not abort the run).

4. skills/hygiene-clean/SKILL.md (model: Sonnet)

  • 4.1 Author skills/hygiene-clean/SKILL.md with frontmatter mirroring the hygiene-check skill style. Precondition: CLAUDE_PLUGIN_ROOT is set; if not, halt with a clear error.
  • 4.2 Implement step 1: load report via StateStore.read_report; if none, tell user to run /hygiene check and stop.
  • 4.3 Implement step 2: re-validate loaded report via validate_report.py; if invalid, stop.
  • 4.4 Implement step 3: apply scope/category filter to entries (entry-stage, same as check).
  • 4.5 Implement step 4: partition entries into auto+deterministic, confirm+deterministic (delete-range), and generative (always confirm).
  • 4.6 Implement step 5 (GATE FIRST): present batch-confirm list for confirm-tier entries — path · category · op · tokens · why; per-entry opt-out; visually distinguish irreversible delete-range from reversible auto ops. Approved set = all auto + approved confirm.
  • 4.7 Implement step 6: git preflight — resolve project root; git status --porcelain; clean → baseline = HEAD; dirty → auto-create WIP checkpoint commit (user work only), baseline = that checkpoint.
  • 4.8 Implement step 7: apply deterministic approved entries via python patch_applier.py --report <path> --apply-indices <list>; capture JSON result ({applied, skipped, failed, staged_paths}).
  • 4.9 Implement step 8: for each approved generative entry — confirm file exists; read live file contents; dispatch Sonnet subagent → workflows/distill.md (LOOP-GUARD); receive new content; write + stage.
  • 4.10 Implement step 9: staging — git add <staged_paths> for non-move ops; DO NOT git add move-to-archive dest (applier already staged via git mv). NEVER git add -A or git add ..
  • 4.11 Implement step 10: single commit via git-context commit-apply --message-stdin with generated message: docs: hygiene cleanup (N edits across M files)\n\nAuto: ...\nConfirmed: ...\nSkipped (re-analysis recommended): ... No Co-Authored-By, no AI attribution, no emoji, no trailers.
  • 4.12 Implement step 11: stamp last_clean to commit instant via StateStore.set_last_clean(commit_instant).
  • 4.13 Implement step 12: surface summary — applied / skipped (with re-analysis note) / confirmed counts, commit SHA, note about checking for new orphans after move-to-archive.
  • 4.14 Implement failure handling: guard mismatch → skip-file-report-continue; applier exit 2 / git mv fail / write error → git restore/reset to baseline, abort with structured error; partial success (applier exit 1) → NOT rollback, commit what applied; zero in-scope → no-op report, no commit.

5. Integration tests (model: Sonnet)

  • 5.1 Implement tests/test_clean_integration.py using a fixture git repo. Test invariant #5 GAPs: (a) dirty-tree → WIP checkpoint + exactly one cleanup commit (two total); (b) clean-tree → exactly one cleanup commit; (c) all-guard-skipped → zero commits, no rollback noise.
  • 5.2 Test invariant #7 sweep-gating GAP: when entries include confirm-tier ops and user is running under /hygiene sweep, the confirm gate still fires identically (not bypassed).
  • 5.3 Test move-to-archive precise staging: applier stages both sides via git mv; skill does not call git add on the dest; git diff --cached shows the rename cleanly.
  • 5.4 Test rollback path: simulate applier exit 2 mid-run; assert tree is restored to baseline and no partial commit exists.
  • 5.5 Confirm all new integration tests pass alongside the 263 existing unit tests.

6. commands/hygiene.md — clean/sweep routing (model: Haiku)

  • 6.1 Replace the "not yet implemented (Phase 4)" stubs for clean and sweep in commands/hygiene.md with live routing to the hygiene-clean skill, passing --scope and --category flags through.
  • 6.2 Wire /hygiene sweep as command-level check-then-clean sequencing: invoke hygiene-check skill, then hygiene-clean skill, passing shared flags to both. No third skill; no double-commit.

7. CONTEXT.md updates and CLAUDE.md fix (model: Haiku)

  • 7.1 Update scripts/CONTEXT.md to add patch_applier.py — its role as the deterministic in-memory applier, the transaction model, and its exit codes.
  • 7.2 Author skills/hygiene-clean/CONTEXT.md (progressive disclosure index for the clean skill directory).
  • 7.3 Update CLAUDE.md build-order step 4 note: mark the deferred insert-frontmatter hole as RESOLVED — the applier re-derives frontmatter freshness at apply time (idempotent key-absence check) instead of trusting a cached hash.