# 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
> T3–T5 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.
