Rudder

Generative Adversarial Model (GAM)

GAM puts two models on one task: a generator that writes the code, and an adversarial reviewer from a different provider that can only argue with it. The reviewer cannot edit a single file. You start one with /gam.

Starting a pair

/gam rewrite the retry logic to use exponential backoff

The pair is one piece of work in two panes. In the agents list the reviewer sits directly beneath its generator, hooked to it with a , and the two move between sections together rather than drifting apart as their statuses diverge.

Two panes open side by side. The left half is the generator, running your current model. The right half is the adversarial reviewer, which defaults to a different provider so it is not the same model marking its own homework: a Claude generator pairs with Codex, and a Codex or opencode generator pairs with Claude.

rudder~/code/api
gen · claude sonnet · revision 1 of 3
workspace .rudder-workspaces/retry

 Edit src/net/retry.ts +58
 Bash npm test · 41 passed

 writing the jitter test…
adv · codex gpt-5.5 · objected · 1 review
read-only · cannot edit any file

 Read src/net/retry.ts
 Bash npm test -- jitter · 0 matched

41 passing tests do not touch the branch this
task exists for.
The generator on the left writes. The reviewer on the right reads, runs its own checks, and objects.

You can name the reviewer instead of taking the default, and run the pair in your real checkout rather than an isolated workspace:

you typewhat you get
/gam <task>Reviewer picked for you, from the other provider.
/gam codex <task>Codex reviews, on its default model.
/gam codex gpt-5.5 <task>A named provider and model.
/gam fable <task>A bare model name works when Rudder recognises it.
/gam main <task>The pair runs in your checkout instead of a workspace.
/gam codex gpt-5.5 main <task>main works on either side of the model. The picker leaves your cursor after the model it inserted, which is where you would naturally type it.

Ordinary task words are never mistaken for a model. /gam fix the auth bug keeps every one of those words as the task, because fix is not a model name.

How the reviewer steers the work

The reviewer is not a gate at the end. It reads the diff after every turn the generator takes, while the work is still in motion, and what it sends back becomes the generator's next instruction. A pair is a correction loop, not an inspection.

That is what the second model is for. Left alone, a coding agent drifts in ways it cannot see from the inside: it settles on the first approach that compiles, treats the happy path as the whole problem, marks a task done because the code it wrote runs, and never revisits the framing it chose in its first thirty seconds. None of that shows up as an error. It shows up as a confident summary.

So the reviewer is prompted to do the opposite of agreeing. Its instructions tell it to refute first: to hunt for what is wrong, missing, oversimplified or untested, and to check the generator's claims against the actual files rather than against its narrative. Concretely, each round it is asked to establish

what it checksthe failure it is looking for
Does this do what was asked?A subset delivered as the whole thing. It is handed your ORIGINAL task every round, so this is measured against what you wanted, not against what the last message was about.
Was it actually verified?A check described as run. A Codex reviewer runs the tests itself rather than believing the transcript; a Claude reviewer reads the code and the test files instead (see what each reviewer can do).
What was not considered?The edge the generator never looked at: the error path, the empty case, the concurrent one, the migration that has to happen first.
Is the approach right?A solution that works and should not survive. Cheaper to say in round one than after the diff has grown around it.

Objections come back as concrete, actionable requests naming a file and a symptom, and they arrive as the generator's next prompt. The generator does not have to be started over or re-briefed; it is mid-task, holding all its context, and it gets a specific correction at the moment it can still act on it cheaply. That is the whole mechanism: steering while the work is happening, rather than judging it once it is finished.

How the two halves talk

The reviewer is not a subagent. Neither model calls the other, and neither can see the other's conversation. They are two ordinary CLI processes in two panes, each holding its own session, exactly like any other agent Rudder runs. Rudder is the pipe between them.

There is no live connection either. The exchange happens at turn boundaries, and Rudder moves it by typing into panes and reading them back:

GENERATOR  your model, writes, its own session
REVIEWER   other provider, read-only, its own session

  generator's turn ends
    │   it says so itself, through its own Stop hook
    │   or notify program. Rudder never guesses from
    ▼   the screen, so a half still thinking is safe.
  rudder reads the workspace diff
  rudder builds the packet
    ·  your ORIGINAL task, every round
    ·  the current diff
    ·  the generator's last rebuttal, if it made one
    │
    ▼   typed into the reviewer's pane, then Enter
  REVIEWER reads it, runs its own checks, answers
  reviewer's turn ends
    │
    ▼   rudder reads the verdict off its visible screen
  accept  →  the pair settles
  revise  →  the objection is typed into the generator
    │
    ▼
  generator's next turn: same session, context intact

Three details are worth knowing, because they are what make the loop reliable:

stephow it actually works
Whose turn it isRudder does not guess from the screen. Each CLI reports its own turn-end through its native hook, Claude's Stop hook or Codex's notify program, which writes a signal file Rudder watches. A half that is still thinking is never interrupted.
Delivering a messageRudder types it into the receiving pane as a bracketed paste and presses Enter, exactly as if you had typed it yourself. This is why the generator keeps its context: it is mid-conversation, not restarted with a new brief.
Reading the answerThe verdict is read off the reviewer's visible screen, which is why it must arrive wrapped in sentinel lines. No parseable block means Rudder stops and asks you rather than guessing what the reviewer meant.

Because each half is its own session, nothing leaks between them but the messages above. The reviewer never sees the generator's reasoning, and the generator never sees the reviewer's. That separation is not a policy Rudder asks them to respect; it is a property of running them as two processes that were never introduced.

Checking the generator complied

An objection is a message, not a command. The generator can read it, say something agreeable, and change nothing. So each round the reviewer is handed three things to judge that with: its own previous objection, quoted back verbatim; whether the generator argued back, and what it said; and the current diff. The contract then asks it, before anything else, to state whether that objection was ADDRESSED, PARTLY addressed, or IGNORED, citing the diff. An objection ignored twice is grounds to escalate.

Silence is reported as silence. Most of the time the generator just complies without arguing, and the packet says so in as many words rather than leaving the section out, because an absent section reads as "nothing happened" when what actually happened is "it did not defend itself, so judge the code".

What each reviewer can actually do

The reviewer is read-only in all three cases, but they are not read-only in the same way, and only some of them can run your tests:

reviewercan run checkshow writes are prevented
CodexYes. It runs commands inside a read-only filesystem sandbox.Refused by the runtime.
ClaudeNo. Its tools are Read, Grep, Glob and LS, so it reviews by reading the code and the tests rather than executing them.No writing tool exists in its allowlist.
opencodeYes.By prompt and an approval gate, not by a sandbox. This is the weakest of the three.

The default pairing is the strongest one: a Claude generator crosses to a Codex reviewer, which can run your checks and genuinely cannot write. Naming the reviewer yourself is how you choose otherwise, and worth knowing about before you do.

Why two models

A model reviewing its own work agrees with itself. It has already decided the approach was reasonable, and asking it to check that decision gets you a summary of the decision rather than a test of it.

So the two halves never share a conversation. Each keeps its own session, and the only things that cross between them are the original task, the diff, the reviewer's objections, and the generator's replies. The reviewer never sees the reasoning that produced the code, because a reviewer that has read the argument for a change is no longer independent of it.

The asymmetry is the other half of the idea. The reviewer has no write access at all, which means it cannot quietly fix what it dislikes and call the disagreement settled. It has to make its case in words, and the generator has to be persuaded.

What a round is

A round begins when the generator finishes a turn. Rudder then hands the reviewer a packet containing:

what is in the packetwhy
the original taskEvery round is anchored on what you typed, never on the last thing the reviewer asked for, so a long argument cannot drift off the ask.
the current diffThe reviewer judges the code, not the generator's account of it. Very large diffs are truncated with a visible marker, and the reviewer is told not to object to what it cannot see.
the generator's last replyOnly when the generator pushed back, so the argument carries forward.

The reviewer is told to refute first: to hunt for what is wrong, missing, oversimplified or untested, and to check claims against the actual files rather than the transcript. It ends its turn with a verdict.

The three verdicts

verdictwhat happens next
acceptThe pair settles. Reserved for work that plainly satisfies the original task with no blocking defect the reviewer can demonstrate.
reviseThe objection is delivered to the generator as a message and a new round starts.
escalateThe disagreement needs you: a scope dispute, two irreconcilable approaches, or a correct objection the generator keeps ignoring.

When the generator disagrees

The generator is not required to obey. It is told explicitly not to comply silently with an objection it thinks is wrong, but to state why in one short paragraph and keep implementing. Rudder lifts that reply out and puts it in front of the reviewer on the next round.

This matters more than it sounds. A reviewer that gets its way automatically turns every weak objection into a code change, and the work drifts toward whatever the reviewer happened to notice. Giving the generator a way to win the argument is what keeps the pair converging instead of wandering.

a use worth knowing

When one model refuses a task or quietly does a subset of it, the other one often does not. The reviewer is reading the diff against the original ask, so it is well placed to notice a job reported as done that was not, and to say so.

When it stops

There is no round limit. A pair keeps going for as long as the argument is getting somewhere, because a fixed count would end a productive exchange mid-sentence and let an unproductive one run to the same number anyway.

What ends it is one of these:

endingwhat happened
acceptedThe reviewer could not find a blocking defect to demonstrate.
escalatedThe reviewer asked for you: a scope dispute, two irreconcilable approaches, or a correct objection the generator keeps ignoring.
stalledThe generator handed back the same diff AND the same rebuttal as the round before. It did nothing with the objection, so another round would ask the identical question and get the identical answer. A NEW argument counts as progress even with no code change: refusing to edit while explaining why is a legitimate move.
no verdictThe reviewer produced no readable verdict block at all.
the reviewer can no longer answerIt failed, you stopped it, you deleted its row, or its pane is gone. Each of these used to leave the pair spinning in silence with the generator parked and nothing on screen to explain it; the pair now ends and names the reason.
a restart ends a pair

Quitting Rudder kills both panes. Only a RUNNING agent is resumed on the way back in, so a reviewer that was idle between rounds returns without a pane and the pair cannot continue. It ends and says so rather than looking alive and doing nothing. The generator's work is untouched in its workspace either way; start a new pair if you want the argument to carry on.

The stall check is what makes an unbounded loop safe to run: it ends on lack of progress rather than on a count. There is a runaway guard far above any argument that is still moving, but reaching it means something went wrong that the stall check did not catch.

In every one of those cases both panes stay live and Rudder names the reason. Nothing is discarded and nothing is merged behind your back: the generator's work sits in its workspace exactly like any other agent's, and it lands when you press m.

cost

A pair spends roughly twice what the same task costs alone per round, and takes longer in wall-clock time, because the reviewer reads a finished diff and the two halves cannot run at the same time. With no round limit that cost is bounded by convergence rather than by a number, so it earns its keep on work where being wrong is expensive, not on a rename.

Why this is worth the second model

The ceiling on a single coding agent is not model quality, and it moves less with each release than people expect. It is that a model cannot reliably audit its own work. The failure is rarely a crash: it is quiet. A subset delivered as the whole thing. A check described as run. A refusal dressed up as a completion. All three read identically to a summary, and all three are caught by reading the diff, which is the part you were hoping to do less of.

Rudder's answer everywhere else is isolation: work that cannot touch other work until you say so. A pair is that same idea pointed at judgment rather than at files. The reviewer cannot edit, comes from a different vendor, and is anchored on the task you actually typed, so its agreement is worth something. A model that could quietly fix what it disliked would never have to make its case, and you would never see the disagreement.

What it does not do is remove you from the loop. It costs about twice, it runs serially, and it is not a substitute for reading the diff before you press m. What it changes is how much you have to catch unaided: the obvious failures are argued out before they reach you, and the ones that survive arrive with a reviewer's objection and a generator's answer attached.