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 the other provider's flagship so it is not the same model marking its own homework: a Claude generator pairs with Codex gpt-6.1-sol, and a Codex or opencode generator pairs with Claude opus.

Before you press Enter, the task bar spells out the pair you are about to start, for example gam · claude sonnet(xhigh) writes · codex gpt-6.1-sol(xhigh) reviews (read-only) · isolated workspace.

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-6.1-sol · 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.

Type /gam and a picker opens, in the same style as /model. Its first row is the default pair, so Enter (or just typing the task) takes it. Otherwise pick the reviewer's provider, then its model, then optionally gen and the generator's provider and model, then type the task. Each row shows backend, model and effort. The model lists are the same ones /model shows.

The picker only writes text onto the task line, so everything it builds can also be typed directly:

you typewhat you get
/gam <task>Your model writes; the other provider's flagship reviews.
/gam codex <task>Codex reviews, on its flagship.
/gam codex gpt-6.1-sol high <task>A named provider, model and effort.
/gam fable <task>A bare model name works when Rudder recognises it.
/gam claude gen codex gpt-6.1-sol <task>gen names the generator too. Without it, the dashboard's current model writes.
/gam gen codex <task>Codex writes; the reviewer defaults across from it, so Claude reviews.
/gam main <task>The pair runs in your checkout instead of a workspace. main can go anywhere before the task.
/gam againRerun your last pair on its task. The picker also offers the last pair as a row, to put it back on the line with a new task.

Ordinary task words are never mistaken for a model. /gam fix the auth bug keeps every one of those words as the task, and so does /gam claude fix the auth bug: a provider on its own gets its flagship, and fix is not a model name. opencode can write but cannot review, because it has no read-only mode, so the reviewer picker only offers Claude and Codex.

The last pair is remembered across sessions, under gam.last in ~/.rudder/config.json.

Watching and driving a pair

A bar above the two halves says what the pair is doing as a whole: the round, who has the ball, the steers so far, and the last verdict.

gam · round 2 · sonnet is answering objection 1 · last verdict: revise: the jitter path has no test
                                                   ^w a swap · t dialogue · i brief · x stop

Once the pair ends, the same bar says how it ended, why, and what to do next:

✓ accepted · after 2 rounds: covers the jitter path · next: m merges it · /gam again reruns
keyswhat they do
^W aMove between the generator and reviewer halves. Clicking a half works too.
^W tHide or show the dialogue strip under the split.
^W iPut /tell on the task line to brief the reviewer.
^W xStop the loop. Any turn in flight is interrupted with Esc, never killed, and both panes stay live for you to drive.

The same keys work in nav mode (^G) without the ^W.

/tell <note> sends the reviewer a note from you, such as /tell the retry budget is per request, not global. While the loop runs, the note waits for the reviewer's next review and arrives in that packet, marked as coming from you. Typing it in straight away could end the reviewer's turn without a verdict. After the pair has ended, there is no next packet, so the note goes straight into the reviewer's session. Notes also show in the dialogue.

m on the reviewer's row merges its generator, because the reviewer has no work of its own to merge.

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.
stoppedYou stopped it with ^W x, or stopped the generator's row. The work stays where it is.
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.