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

142 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)
- [x] 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.
- [x] 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.
- [x] 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.
- [x] 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.
- [x] 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.
- [x] 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)
- [x] 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.
- [x] 2.2 Write unit tests for `insert-frontmatter` re-derivation: idempotent no-op,
key-conflict skip, key-absent insert (with and without existing `---` block).
- [x] 2.3 Write unit tests for incompatible-ops detection: `move-to-archive`+other →
skip; overlapping anchors → skip; non-conflicting entries → proceed.
- [x] 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).
- [x] 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.