---
name: tdd-adversarial-refactor-loop
description: "Process discipline for cleanup refactors: existing tests stay untouched by default; on an explicit TDD request, run the full loop with a persistent coder subagent and a persistent adversarial reviewer subagent, both spawned up front by an orchestrator that never edits code directly and always returns to the user between rounds, iterating one round at a time, then handing off for manual inspection without committing."
---

# TDD Adversarial Refactor Loop

Two rules for handling tests during a refactor: a default, and an exception the user must explicitly invoke.

## Default: tests are not editable

For a refactor (e.g. splitting a module, cleaning an API boundary), existing tests should keep passing unmodified. Edit or add tests only when the user explicitly signals it for that round (e.g. asking for a TDD process, or directly saying tests may change).

**Why:** A no-test-edits constraint does real design work: it forces the refactor toward parameterized functions and preserved seams, the exact globals and attributes existing tests monkeypatch, instead of letting the new shape break contracts.

**How to apply:** Before modifying a test file during a refactor, check whether a TDD-style process was requested this round. If not, treat unmodified, still-passing tests as a hard design constraint.

## Three roles, not two

The loop runs three distinct actors:

- **Orchestrator, that is you.** The keeper of the clipboard, not a third decision-maker. You hold the todo list, spawn both subagents up front, ask the user what the task is, relay context and feedback between the coder and the reviewer, and report back to the user after every round instead of deciding on your own to continue. You do not read implementation files end to end, write tests, or make implementation edits. When you think a step can be shortened, such as skipping the red phase for a purely mechanical change, propose it to the user and wait for their answer rather than deciding it yourself. Delegating the coding keeps your own context small enough to hold a whole multi-round refactor in working memory; returning to the user every round keeps the user in control of pacing.
- **Coder subagent.** A persistent agent, spawned once at the very start via the Agent tool in the foreground with a plain agent type such as general-purpose, that does all the actual file reading, test writing, and implementation editing. Resumed via SendMessage round after round so it carries forward its own memory of the design.
- **Reviewer subagent.** A second persistent agent, also spawned once at the very start, separate from the coder, with an adversarial persona. It reviews what the coder actually produced through its own fresh file reads, never through the coder's self-report treated as ground truth.

Track which agentId belongs to the coder and which to the reviewer (step 1 below records this) — sending a round's feedback to the wrong agent is the most likely mistake once both are live for many rounds.

**Why:** The reviewer alone was never the source of context bloat. Reading files, writing diffs, and reading raw test output in the orchestrator's own turns was. Two independently spawned agents, rather than one agent playing both roles, also keeps the review genuinely adversarial: a reviewer that saw its own prior work as coder would be reviewing itself. Convergence between the coder and the reviewer is not the same thing as the user's approval to keep going, and only the user can tell you which of those two things you have.

**How to apply:** Before delegating a round, decide whether it is coder work that touches files or orchestrator work that is judgment, relay, or a user-facing decision, and route it to the right actor. Reserve direct file reads and full command output for spot-checks, such as confirming a specific claim looks off or a final pre-handoff sanity check, not routine re-verification of every round. If you catch yourself reading a source file to understand an implementation detail rather than to verify a subagent's claim, that round should have gone to the coder instead. If you catch yourself starting a second round, unilaterally adopting a process shortcut instead of proposing it, or calling something converged without having heard back from the user since your last report, stop and ask first.

## The loop, when explicitly invoked

For a refactor cleaning up an abstraction boundary, not a bug fix, not a feature, follow this loop when the user asks for it. One round runs from step 2 through step 9. Steps 6 and 7 may repeat several times within a round as the coder fixes what the reviewer raises; step 9 is the only point where a round ends and control returns to the user.

1. Spawn both subagents up front, before any task is defined: the coder and the reviewer, each a separate persistent agent primed only with its role and the ground rules above. Record both agentIds, for instance as a todo item, so later rounds resume the correct one. Do this once for the whole refactor engagement, not once per round.
2. Ask the user what the task for this round is, or restate your understanding of it back to them if they already described it, and wait for their reply before the coder touches anything. If a step of the loop looks shortenable for this task, such as skipping the red phase for a purely mechanical change, say so as part of the same question and wait for their answer along with the rest.
3. Resume the coder to write or adjust tests first, targeting the intended cleaner interface, before touching implementation. Have it confirm the red phase itself, running the new tests and reporting the actual failing output.
4. Resume the reviewer to critique the test design and coverage before any implementation exists. Give it real context: file paths, the specific complaint driving the refactor, and the behavioral contract that must survive, relayed as facts rather than the coder's own framing of what it did.
5. Relay the reviewer's feedback to the coder and have it fix the tests.
6. Resume the coder to make the minimal code changes needed to pass the updated tests.
7. Resume the reviewer to review what the coder actually implemented, on its own fresh reads of the current files, not a summary from the coder.
8. Relay issues the reviewer raises back to the coder to fix, and repeat steps 6 and 7 until the reviewer stops raising new ones. If several fix-review cycles pass without the coder and reviewer converging, stop and surface the disagreement to the user instead of continuing to cycle.
9. Report the finished round back to the user: what changed, what the reviewer found, and how it was resolved. Ask how to proceed and wait for their reply before taking any further action. Do not commit, do not start another round, and do not treat the coder and reviewer agreeing with each other as the user's approval to continue.

**Why:** Adversarial review belongs at both the test-design and implementation stages, not once at the end, so bad test assumptions get caught before they lock in a flawed implementation. Resuming the same reviewer and the same coder across rounds preserves continuity that fresh agents cannot. Splitting the coder's test-writing resume from its implementation resume, steps 3 and 6, is what stops tests and code from being written in the same pass with no review landing in between.

**How to apply:** Invoke the full loop only on an explicit request for TDD plus adversarial review. Give both subagents a plain agent type with the role written into the prompt; unless told otherwise, don't reach for packaged review skills or agents. Sometimes the exercise is a one-off exploration meant to be discarded afterward with `git checkout`. Confirm what should happen to the result before assuming it should be committed.

## Refinements

**The coder's and reviewer's agentIds each carry an entire multi-round refactor.** Each round works through the loop above, scaled to what that round needs: ask the user for the next narrow request, run it through the coder and reviewer, relay fixes back to the coder, then stop and report before touching the next request. If the user's reply is only a general go-ahead rather than a new specific task, restate what you understand the next narrow request to be and treat their confirmation of that restatement as step 2. This works when the refactor is driven incrementally, moving a decorator, then a function, then some constants, then a default, rather than specifying the whole end design upfront. Recap only enough running history in each prompt to keep the agent oriented without re-litigating settled ground.

**Not every round needs a fresh red phase.** Two kinds of change inside an ongoing refactor sequence:
- New test design, meaning a genuinely new interface, default, or branch of behavior: run the full loop described above, both review passes included.
- Mechanical propagation of an already-decided architecture, such as renaming a monkeypatch target because state moved modules, reordering call-site arguments to match a signature change, or updating a doc reference: the red phase can likely be skipped, the coder can implement directly, and the full existing regression suite serves as the safety net. Still get one final reviewer pass on the finished diff before reporting back.
Propose which kind of round this looks like when you ask the user for the task at step 2, and wait for their answer. The distinction matters on its own merits too: forcing a red phase on a pure rename wastes a round, and skipping the final review on a rename-only change is how a swapped-argument bug or a stale reference slips through, since bulk edits such as sed or multi-site find and replace are exactly where mechanical mistakes hide.

**Prime the reviewer not to relitigate settled tradeoffs, but only when the user actually settled them.** When a round's design was an explicit, informed user decision, tell the reviewer plainly that whether the change is a good idea is already decided, and its job is to review the execution for correctness and half-measures. Mark something settled only when the user made that call. Do not accept the coder's own characterization of a choice as already decided; the coder choosing something is not the same as the user deciding it, and treating them the same gives the coder a way to suppress legitimate review pushback.

**Push both subagents to verify independently, not just narrate.** Ask the coder to report actual command output, test runs and red-phase failures included, rather than a summary. Ask the reviewer to re-read the current files fresh, since files change round to round, and to grep for stray or leftover references, hand-trace a sample of call sites against the actual new signature, and where useful run a small experiment such as monkeypatching a value to prove a default is really live rather than stale. This is what catches real bugs: an orphaned decorator left behind by a text edit, a docstring misattributing which function actually reads a default, a coverage gap where two independent optional parameters are always tested together or never separately.

**When multiple parameters become independently defaulted, two follow-ups are usually required, not optional.** Convert call sites to keyword arguments, since positional args become a silent-swap risk once more than one trailing parameter has a default, and add tests exercising each default in isolation. Omitting all defaults together and providing all of them together do not prove the defaults are independent of each other.

**A clean ready-to-ship verdict from the reviewer is information to report at step 9, not something you act on yourself.** A reviewer resumed across many rounds can also grow familiar with the coder's patterns and soften its scrutiny without either agent noticing. If a refactor runs long or the stakes are high, propose a final pass from a freshly spawned second reviewer that never saw the earlier rounds, purely as a sanity check before handoff, and let the user decide if that's worth the round.

**Watch what either subagent actually does, not just what it concludes.** Both the coder and the reviewer can run real commands. Skim their tool-use logs for anything broad or destructive, such as a wildcard delete in a shared directory like `/tmp/*`, and surface it to the user rather than accepting a clean-sounding verdict. A correct conclusion doesn't retroactively make a risky action safe.
