The /change-heavy pipeline
How a risky, AI-written code change gets vetted before it ships: a locked plan, two independent critics, then a neutral referee that weighs which criticisms actually matter. The referee can read the code, but it can't change it.
Still a work in progress. If you'd cut a stage, we want to know which, and why. That's the call we're least sure of. Reply on LinkedIn.
How to read this map
Pick a stage to walk, or start at the top
The walkthrough, as text
Every stage of the interactive walkthrough above, as plain readable text. Each stage has its own link.
Stage 0Pre-flight scope checkEarn the rigor, flag the risk
Pre-flight gate
Before anything starts, the system decides whether this change is big or risky enough to deserve the full, heavyweight review, and separately flags whether it touches anything sensitive.
Decide whether the work truly earns heavy rigor, and flag whether it touches a risk surface, before a single agent runs.
What happens
The orchestrator (no subagent yet) reads the pending diff with git diff --stat and git status --porcelain, then re-reads your invocation argument. Two independent decisions come out of this stage.
First, the rigor check: is this genuinely heavy work, or is the full ceremony overkill? Second, the risk-surface check: does the change touch money, auth, or financial math? That second answer is set as a flag that survives all the way to Stage 6.
Who acts
- git (Bash), tool: Reads diff-stat + porcelain status to gauge scope.
- AskUserQuestion, command: Compels a real structural pause when the work looks light: a prose 'please confirm' is skippable in auto mode.
The rules
- Heavy-rigor indicators proceed silently. 3+ files in the diff, OR architectural keywords in the argument (
refactor, rename, architecture, data model, schema, auth, RLS, Stripe, webhook, calculation, financial, security), OR an explicit 'force heavy'. - Light-rigor only must ask. 2 or fewer files AND no architectural keywords triggers an
AskUserQuestion: run the full pipeline anyway, or exit to the lightweightPLAN, implement, REVIEWflow. - Risk-surface flag is independent. Scan argument + diff for
auth, RLS, Stripe, webhook, calculation, financial, security, exportor risk paths (api/**,auth/**,stripe/**,calculators/**, export routes). - Sets RISK SURFACE ENGAGED. Any match arms the flag for the whole run. This, not the rigor decision, is what re-triggers the Stage 6 security pass.
In: Your invocation argument + the current working tree. Out: A rigor verdict (proceed / ask / exit) and the RISK SURFACE ENGAGED flag.
Stage 1PLANWrite & lock the plan
Plan
It writes out exactly what it intends to do, gets your sign-off, and then freezes that plan so nothing drifts away from it later.
Write a complete, reviewable implementation plan, then freeze it so every later stage works against one canonical artifact.
What happens
The orchestrator follows the superpowers:writing-plans skill to produce a full plan written to docs/plans/YYYY-MM-DD-feature.md. You approve it before anything is locked.
On approval the plan is locked with frontmatter: status: approved + locked-at. The lock is the contract: from here on the plan is canonical and may be revised in content, but the lock is never silently removed.
Who acts
- superpowers:writing-plans, skill: Drives the structure of the plan document and its approval gate.
The rules
- Plan lives on disk. Written to
docs/plans/YYYY-MM-DD-feature.md: a durable artifact, not a chat message. - Lock = canonical. Frontmatter
status: approved+locked-atsignals downstream stages this plan is the source of truth. - User approval precedes the lock. This is the first hard pause boundary in the pipeline: nothing locks without your sign-off.
In: The rigor verdict from Stage 0. Out: A locked plan file: the spine every later stage reads from.
Stage 1.5Follow-up note verificationRe-check stale notes against the code
Verify · half-stage
Old to-do notes go stale, so every factual claim inside them is re-checked against the actual current code before the plan is allowed to rely on it.
Follow-up notes age out. Before the lock holds, prove every quantitative claim against the live code, plus sweep wider for siblings.
What happens
A follow-up's stated facts ('this selector has 4 callers', 'single-user only') drift as code lands after the FU was written. This mandatory half-stage empirically re-verifies every claim in the source FU body before the plan is trusted.
If a claim is wrong, the plan's Stage 1.5 audit results section is updated with corrected facts, visibly, never silently absorbed. If the gaps are severe enough that the plan's premise collapses, the run flips to abandon-and-replan.
Who acts
- grep / Read, tool: Re-counts callers and confirms the cited file:line actually contains the described construct.
- preview-server probe, tool: DOM-injection probe to confirm real rendered behavior for CSS-cascade / runtime claims.
- memory anchor, agent: Anchored in memory/feedback_fu_body_verification.md: the empirical record.
The rules
- Re-count caller claims.
grep -rnthe cited selector and compare to the FU's stated number. - Exhaustive 'single-user' checks. Grep alternate constructions too: template literals,
classnames(), dynamic class composition, inlinestyleoverrides. - Wider sibling sweep. Grep for sibling selectors in the same family that may share the anti-pattern under a different name.
- Corrections are visible. Wrong claims update the plan's audit section +
locked-at; the lock is preserved. Severe gaps trigger abandon-and-replan.
In: The locked plan + its source follow-up notes. Out: A fact-checked plan, or an abandon-and-replan trigger.
Stage 1.6Extraction justificationJustify splitting code into new files
Verify · half-stage
If the plan wants to pull code out into brand-new files, each split has to justify why it's actually worth it, no busywork file-shuffling.
If the plan proposes pulling code into new files, each extraction must earn it against the project's cohesion gate.
What happens
Fires only when the locked plan proposes any new file created by extracting from a component, hook, or store. Each extraction must state in one sentence what it wins, measured against CLAUDE.md's 'Cohesion gate before extraction'.
Weak justifications are rejected on the spot and the plan's extraction list is corrected before critique. This kills speculative file-splitting before it reaches an implementer.
Who acts
- Read (CLAUDE.md), tool: Checks each proposed extraction against the documented cohesion gate.
The rules
- Reject 'future testability' alone. Testability by itself is not a reason to extract.
- Reject 'visual isolation' alone. Visual-leaf extractions need more than 100 LOC and visual isolation; isolation alone is not enough.
- Reject 'pattern consistency' as primary. Matching sibling extractions is allowed as secondary support, never the main justification.
- Reject '2+ callers in one file'. That's within-file repetition; it becomes a private inner function, not a new file. The '2+ callers' rule is cross-file only.
In: The locked plan's proposed file list. Out: A pruned extraction list, or a revision before Stage 2.
Stage 2Parallel critiqueTwo critics review at once
Parallel critique
Two independent reviewers examine the plan at the same time, without seeing each other's notes: one watching for style and convention slips, one hunting for bugs.
Two independent critics review the plan at once (one for conventions, one for bugs) at matched model depth.
What happens
Both critics are dispatched in parallel (a single message with two Task calls), so neither anchors on the other. Each is forced to the orchestrator's own model family (opus / sonnet / haiku), because mismatched depth would bias the Adjudicator downstream.
Their outputs are concatenated into one structured CRITICS block (plan path plus each critic's verbatim findings) ready for adjudication.
Who acts
- pr-review-toolkit:code-reviewer, agent: Critic 2a, conventions & project-style lens: CLAUDE.md, STYLE_GUIDE.md, naming, organization.
- feature-dev:code-reviewer, agent: Critic 2b, bugs & quality lens: logic errors, security, quality, confidence-filtered to high-priority only.
The rules
- Dispatched in parallel. One message, two Task calls: independent signal, no anchoring, faster.
- Model parity is forced. Both get
model:matching the orchestrator; without it 2a runs Opus and 2b Sonnet, an uneven critique. - Structured output only. One finding per line:
file:line, severity, suggestion, rationale; compliant code is omitted, not logged.
In: The fact-checked, extraction-pruned locked plan. Out: A concatenated CRITICS block (plan path + both critics' findings).
Stage 3Adjudicate the critiqueA referee keeps or tosses each note
Adjudicate
A neutral referee reads every criticism and decides which ones actually matter, judging each against the project's own written rules. It can read the work but is not allowed to change it.
The read-only Adjudicator filters critic noise (KEEP / DITCH / MODIFY) against the project's own standards, never generic best practices.
What happens
The the-adjudicator agent receives the CRITICS block and the locked plan. It marks each finding KEEP, DITCH, or MODIFY against the project's actual rules. It is the one stage that judges findings against the project's own written standards rather than generic best practice. Its verdicts are presented to you verbatim.
It is read-only by design: its tools are Read, Grep, Glob only. It cannot write code, rewrite the plan, or invent new critiques (beyond max-3 'missed' one-liners). Enforcing that at the tool layer, not by instruction, is what keeps it from drifting. Then you choose what to absorb.
Who acts
- the-adjudicator, agent: Read-only judge (Read/Grep/Glob). The same agent serves Stage 3 and Stage 7.
- AskUserQuestion, command: Pause: absorb all KEEP+MODIFY / absorb none / name specific CRITIQUE_IDs.
The rules
- Rubric, first match wins.
file:line+ concrete failure mode is KEEP. Vibes / no citation, out-of-scope, abstraction at fewer than 2 callers (YAGNI), defensive check for an impossible condition, comment restating code, generic best-practice appeals are DITCH. - Anti-yes-man default. A false-positive costs attention; a false-negative is a missed bug. Default DITCH for ambiguous non-security findings with no concrete failure mode.
- Severity asymmetry. Downgrades allowed for any finding; upgrades only for security / correctness: the one direction MODIFY ever raises severity.
- Citation-existence precondition. Before any KEEP / MODIFY-up, the cited file:line must exist and contain the named construct. If not, it is flagged
CITATION-UNVERIFIEDand surfaced, never silently dropped (v1.7). - Selective steelman gate. A one-sentence steelman is mandatory on DITCH-for-scope/YAGNI/generic-best-practice and on every rendered 'missed'; forbidden on vibes-DITCH, downgrades, security-upgrades, and KEEP.
- Forbidden actions. No new critiques beyond 3 'missed' one-liners, no plan rewrites, no alternatives, no second-guessing locked design decisions.
In: The Stage 2 CRITICS block + locked plan. Out: A verdict block (KEEP/DITCH/MODIFY), your absorption choice, then a revised, still-locked plan.
Stage 4IMPLEMENTBuild it, stop at blockers
Implement
Now the code actually gets written, one task at a time, and if anything is unclear or repeatedly breaks, it stops and asks rather than guessing its way through.
Execute the revised plan with a fresh subagent per task, and stop, never guess, at any real blocker.
What happens
The superpowers:executing-plans skill runs in subagent-driven mode: one continuous session, a fresh subagent per task, with review between tasks. The plan path is the locked-and-revised file from Stage 3.
If execution hits a blocker (a missing dependency, a repeated test failure, an unclear instruction), it surfaces the blocker and pauses. Guessing past blockers is explicitly forbidden.
Who acts
- superpowers:executing-plans, skill: Subagent-driven execution, fresh subagent per task, review between.
The rules
- Subagent-driven mode. Single session; each task gets a clean subagent so context stays focused.
- Stop on blockers. Missing dep, repeated test failure, or unclear instruction surfaces and pauses.
- Never guess past a blocker. Ambiguity is escalated to you, not resolved by assumption.
In: The revised locked plan. Out: An implemented diff in the working tree.
Stage 5SIMPLIFYStrip needless complexity
Simplify
A dedicated cleanup pass strips out needless complexity from the new code, held to the same strict evidence bar as the referee, so it can't over-engineer in the name of tidiness.
Run /simplify on the new code at the same evidence bar the Adjudicator uses, and log any sibling patterns the FU never mentioned.
What happens
Once the implementation is complete and verified, the /simplify skill runs on the modified code. Suggestions are accepted only if they clear the same evidence bar the Adjudicator enforces: abstraction at fewer than 2 callers, defensive checks for impossible conditions, and generic best-practice appeals are rejected.
Afterward, any sibling patterns caught that were not in the original FU body are explicitly enumerated (file:line, type, absorbed vs deferred) and logged to the sibling-pattern miss log: the empirical signal for whether that sweep should move upstream into Stage 1.5.
Who acts
- /simplify, command: Quality-only pass over the implemented diff: reuse, simplification, altitude.
- miss-log, agent: Appends sibling catches to memory/feedback_fu_body_verification.md.
The rules
- Same evidence bar as the referee. Reject abstraction at fewer than 2 callers, impossible-condition guards, and generic best-practice appeals.
- Enumerate sibling patterns. For each catch not in the FU body: file:line, pattern type, and whether absorbed now or deferred to a new STACK.md FU.
- Log the misses. Re-evaluate moving the sibling sweep upstream after 3 to 5 documented misses across different files.
In: The implemented diff. Out: A simplified diff + sibling-miss log entries.
Stage 6REVIEWFive lenses + security pass
Review
Up to five specialist reviewers examine the finished code, and if the change touched money or logins, a separate security check runs too. Nothing is acted on yet; everything is just collected.
Five specialized review lenses on the diff, plus a conditional security pass: capture everything, absorb nothing yet.
What happens
/pr-review-toolkit:review-pr runs with the explicit aspects code tests errors types comments (never all, never simplify; Stage 5 already covered that). Each aspect maps to a specialist agent, fired only if relevant to the diff. It works on the current branch, no PR required.
If RISK SURFACE ENGAGED was set at Stage 0 or the actual diff touches a risk path (re-checked here), /security-review also runs on the diff as a labeled findings source. Nothing is auto-absorbed, auto-deferred, or auto-fixed here; all findings are captured for Stage 7.
Who acts
- code-reviewer, agent: Always: conventions, bugs, project-guideline adherence.
- pr-test-analyzer, agent: If test files changed: coverage quality, gaps, behavior vs implementation.
- silent-failure-hunter, agent: If error-handling changed: silent failures, weak catches, fallback misuse.
- type-design-analyzer, agent: If new types added: encapsulation, invariant expression.
- comment-analyzer, agent: If comments/docs added: accuracy, rot, completeness.
- /security-review, command: Conditional: fires on the risk-surface flag or a risk-path diff.
The rules
- Explicit aspect list.
code tests errors types comments; never substitute the lighterrequesting-code-review; it missed 4 specialist lenses worth keeping. - Security pass is conditional. Runs only when the Stage 0 flag is set OR
git diff --name-only HEADintersected with risk paths is non-empty. Pure rename/structure refactors skip it. - Capture, don't absorb. Findings (Critical / Important / Suggestions) are collected as input for Stage 7; no disposition decisions at this stage.
In: The simplified diff + the RISK SURFACE flag from Stage 0. Out: Raw review findings + optional security-review findings, handed to Stage 7.
Stage 7Adjudicate review findingsJudge the review, just once
Adjudicate · conditional
The referee returns one last time to sort which review findings are worth acting on, and a security problem can never be waved away. It gets just one round to revise; a second wave means the plan itself was wrong.
The Adjudicator returns to filter the review's output before you act: security findings can only KEEP or MODIFY-up. Max one revise cycle.
What happens
If the review (and the security pass, if it ran) surfaced zero findings, the pipeline is done. Otherwise the findings are formatted into the Adjudicator's canonical input and the same the-adjudicator agent adjudicates them, its security-asymmetric floor firing automatically, so Critical/security findings can only be kept or upgraded, never ditched.
Verdicts are shown verbatim, then you choose what to absorb. There is a hard ceiling of one revise cycle: if absorbing leads to a revision and a user-initiated second review surfaces new revise-here findings, the pipeline stops. That signals the Stage 1 plan itself needs revision, not another loop here.
Who acts
- the-adjudicator, agent: The same read-only judge as Stage 3; its rubric handles review findings unchanged.
- AskUserQuestion, command: Pause: absorb all KEEP+MODIFY / absorb none / name specific IDs.
The rules
- Zero findings means done. If both sources returned nothing, skip straight to a clean close.
- Security floor fires automatically. Critical/security findings can only KEEP or MODIFY-up; the severity floor is source-agnostic.
- Max one revise cycle. A second review (user-initiated only) that surfaces new revise-here findings stops the run and returns to Stage 1; never loop inside Stage 7.
- Declined-but-valid stays visible. KEEP/MODIFY items you decline route to STACK.md FOLLOW-UPS, not into silence.
In: Stage 6 review + security findings. Out: Final disposition: revise-and-close, clean close, or escalate to Stage 1.
Stage ✓Done: three valid endingsThree honest endings
Termination
A run can end three honest ways: it ships, it's closed as a false alarm, or it's sent back to start over with a better plan. Every judgement along the way is logged.
Not every run ships code. The pipeline recognizes three honest ways to finish, and records telemetry on every adjudication.
What happens
A run can end three ways. Standard close: code ships, the plan is marked RESOLVED. Validated-no-action close: Stage 1.5 invalidated the plan's framing, so it closes with rationale and any adjacent observations become new STACK follow-ups. Abandon-and-replan: Stage 2/3 critique surfaced audit gaps too severe to absorb, so frontmatter flips to status: abandoned and Stage 1 restarts with corrected scope.
Every completed adjudication appends one JSON line to ~/.claude/metrics.jsonl: rigor, files changed, KEEP/DITCH/MODIFY counts, whether security engaged. Over weeks this is the data that reveals whether heavy-mode actually earns its ceremony on lighter work.
Who acts
- metrics.jsonl, tool: Append-only effectiveness telemetry, one line per adjudication.
The rules
- Standard close. Code changes ship; plan marked RESOLVED.
- Validated-no-action close. Stage 1.5 invalidated the framing; close with rationale + new STACK FUs. Zero files changed.
- Abandon-and-replan. Critique gaps too severe to absorb flip frontmatter to
status: abandonedand restart Stage 1 with corrected scope. - Telemetry every run. One JSON line per adjudication to
~/.claude/metrics.jsonl: the missing signal on heavy-mode ROI.
In: Whatever state the pipeline reached. Out: A summarized close + an effectiveness telemetry line.