Commit Decomposition
Rebuild a range of oversized commits as a readable series of logical commits, with source parity guaranteed and zero risk of losing work. Mechanizes the process that succeeded in the August 2025 Alpha Prime decomposition: the lead never does surgery; durable state lives on disk; parity is the only in-flight gate.
The criterion: one logical change
A commit is one coherent, explainable step — kernel patch-series style. Read
references/patch-series-style.md before planning any split, and include it in
every subagent brief.
There are no size limits. No file counts, no line counts, no numeric triggers. A 3,000-line mechanical rename can be one commit; a 40-line diff mixing a bugfix with a refactor must be two. Justify boundaries by the change's logic, never its size. (Numeric limits were tried in 2025 and produced absurd granularity — agents optimized the metric instead of the history.) The one standing structural rule: auto-generated content (lockfiles, build outputs) gets its own isolated commit.
Roles
- Lead (you): owns the cursor, applies gates, dispatches subagents, updates state. NEVER edits code or does git surgery directly — your context must survive a 100-patch run.
- Per-patch subagent: fresh context each patch; decomposes exactly one patch.
Phase 0: Preflight (lead)
- Require a clean tree (
git status --porcelainempty). Stop if not. - Invoke the
git-anchoringskill: anchor tag + backup branch on the original tip; dedicated worktree; work branch at the base commit (git switch -c decompose/<slug> <base>inside the worktree). - Require a linear range:
git rev-list --merges <base>..<anchor_ref>must be empty.git format-patchsilently emits nothing for merge commits, breaking the patch↔commit correspondence. A range with merges needs a different strategy — stop and consult the user. - Export the source of truth:
git format-patch <base>..<anchor_ref> -o <scratchpad>/patches/(the anchor ref, not a remembered tip SHA). These files are the unloseable record; nothing that happens later can destroy the original commits. - The task scratchpad (
patches/,analysis/,state.json) MUST live outside the worktree — no git operation inside the worktree (including a retry'sgit clean -fd) may be able to touch it. - Write
state.json(git-anchoring schema;cursor: 0,cursor_meaning: "patches completed; next patch file is %04d of cursor+1",last_good: <base sha>).cursorcounts patches completed so far; the next patch file to process is%04dofcursor+1(git format-patchnumbers patches from 0001).
Phase 1: Per-patch loop (lead)
For each patch N — the 1-based patch-file number, N = cursor+1 read from
state.json at the start of the step, never from memory. The loop ends when
cursor equals the number of patch files in <scratchpad>/patches/ — count
them on disk, don't remember the total.
- Dispatch a fresh subagent with the brief template below. Model per the routing table: implementation tier; elevate when the patch looks judgment-heavy (tangled concerns, possible design questions).
- Parity gate (mechanical, non-negotiable): after the subagent reports,
run in the worktree — three checks, all mechanical, all must pass before
the cursor advances:
-
Tree parity:
git diff <sha-from-patch-header-N> <work-branch> --statis empty.<sha-from-patch-header-N>comes from the first line of the patch file itself (From <sha> Mon Sep 17 ...) — read off disk per git-anchoring Rule 2, never recalled from context. -
History intact:
git merge-base --is-ancestor <last_good> <work-branch>succeeds. A subagent that squashed or amended already-approved commits can land the right tree while breaking this — treat it as a gate failure, not a pass. -
Worktree clean:
git status --porcelainis empty in the worktree. Uncommitted leftovers contaminate patch N+1's apply. -
All three pass → record: update
state.json(cursor: N,last_good: <new tip>); append one line to<scratchpad>/progress.md. -
Any check fails → first capture the evidence for the retry brief (the failing
--statdiff for a tree-parity failure; for a history-intact failure there is no diff — record instead that ancestry broke, i.e. the previous attempt amended or squashed existing commits) — do this BEFORE resetting, since the reset destroys it. Thengit switch <work-branch>(ensure HEAD is on it),git reset --hard <last_good>,git clean -fd(safe here only because this runs inside the dedicated worktree and the scratchpad lives outside it), then re-dispatch ONCE with the captured evidence in the brief's retry-context section. Second failure → STOP, report to the user with the evidence. Never improvise past a failed gate.
-
- Do not review commit-boundary quality mid-run — that is the optional Phase 2. Parity and progress only.
Subagent brief template
Angle-bracket placeholders are filled in by the lead from state.json and the
patch file before every dispatch — never sent unresolved.
**Role:** You are decomposing one oversized patch into logical commits.
**Context:** Worktree <path>, work branch <branch>, currently at <last_good>.
Patch file: <scratchpad>/patches/<NNNN-name.patch>. Original commit message
is in the patch header.
**Task:**
1. Read <skill-dir>/references/patch-series-style.md first.
2. Read the target files you will touch before editing them (harness rule).
3. `git apply <patch-file>` (do NOT use git am).
4. Split the working-tree changes into logical commits by selective staging
(`git add <files>` / `git add -p`). Criterion: one coherent, explainable
step per commit — the style reference governs. NO size-based splitting.
Auto-generated files get an isolated commit.
5. Each commit message: conventional prefix, subject explaining the step,
body explaining why the boundary is where it is (when not obvious).
6. Write <scratchpad>/analysis/patch-NNNN.md: the boundaries you chose and
the reasoning, ~20 lines. This is a work product, not a formality.
7. Do not touch refs matching anchor/* or backup/*. Do not push. Do not
leave the worktree. Read `state.json` if you need the current tip, but
NEVER write `state.json` or `progress.md` — they are lead-only
bookkeeping, and the lead runs the gate. (A practice-run subagent
updated them "helpfully"; the lead then had to re-derive the true
last_good from progress.md to keep the ancestry check meaningful.)
8. **Deviations log:** if anything forces you off this brief, take the
conservative option and record it in the analysis file under
`## Deviations`.
**Done means:** working tree clean, all patch content committed, analysis
file written. Report: number of commits created, one-line summary each.
**Retry context (present only on re-dispatch):** the previous attempt
failed the parity gate's <failed-check-name> check.
- If tree parity failed: diagnose which hunks were missed or altered
from the diff below and correct the decomposition.
<failing-diff>
- If history-intact failed: the previous attempt amended or squashed
existing commits. Build strictly on top of <last_good> — never amend
or squash a commit that already passed the gate.
- If worktree-clean failed: the captured `git status --porcelain` output
follows; the previous attempt left these paths uncommitted. Everything
belonging to the patch must end up committed.
<porcelain-output>
Phase 2 (optional, after full parity): quality pass
Only after the final gate — git diff <anchor_ref> <work-branch> empty — walk
the new series for build/test health per commit (git rebase --exec 'make ...'
or equivalent) and message quality. Parity first, polish second: mixing these
concerns is what sank the 2025 attempts.
Finish
Final parity check against the anchor ref, then
superpowers:finishing-a-development-branch. The original branch was never
touched; the anchor tag and backup branch are deleted only by the user after
they accept the result.