Rudder

Three changes to one repo. None of them touching.

Run Claude Code, Codex and opencode in parallel on one repo. Each gets its own workspace, so they cannot touch each other's work, or yours.

$ npm install -g @viraatdas/rudder@latest

Read the docs View source

Everything on one screen.

Every agent, what it is doing, and what it changed.

rudder ~/code/api
agents
/main1 agent · shared checkout
tag the releaserunning
workspaces3 agents
rate-limit the public APIrunning
port the settings screendone
drop the legacy session tablemerged
rate-limit the public API
workspace .rudder-workspaces/rate-limit

 Read src/server/middleware.ts
 Edit src/server/rate-limit.ts +64
 Bash npm test · 41 passed

 writing the burst-window test…
agents
workspaces3 agents
rate-limit the public APIrunning
port the settings screendone
drop the legacy session tablemerged
you are typing straight to Claude
 also cap it per API key, not just per IP

Understood. Keying the bucket on the API key when
one is present, falling back to IP for anonymous
requests.

 Edit src/server/rate-limit.ts +18 −4
agents
workspaces3 agents
rate-limit the public APIrunning
port the settings screendone
drop the legacy session tablemerged
nothing here is yours yet
jj diff · .rudder-workspaces/settings-form

src/settings/Form.tsx
- const [state, setState] = useState(initial)
+ const form = useForm({ defaultValues: initial })
+ const { isDirty } = form.formState

src/settings/index.tsx
+ import { Form } from "./Form"
m merges it · u undoes that
agents
orchestratorship billing v2
plan · 4 nodesrunning
workspaces4 nodes
n0 schemamerged
n1 stripe clientrunning
n2 webhook handlerwaiting on n1
orchestrator · the graph it proposed
ship billing v2

 n0  schema migration            merged
 n1  stripe client               running
│   └─ hard dep: n0
 n2  webhook handler            waiting
│   └─ hard dep: n1
 n3  billing settings screen    waiting
    └─ hard dep: n0

n3 is independent of n1 and n2, so it runs
as soon as n0 lands.
type here to change the plan · v to edit a node
gen · claude sonnet · revision 1 of 3
workspace .rudder-workspaces/rate-limit

 Edit src/server/rate-limit.ts +64
 Bash npm test · 41 passed

RUDDER_GAM_REPLY_START
The bucket is keyed per API key by design.
You are right that nothing covers the burst
window. Writing that test now.
RUDDER_GAM_REPLY_END

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

 Read src/server/rate-limit.ts
 Bash npm test -- burst · 0 matched

41 passing tests do not touch the branch this
task exists for. No test drives the window to
its edge.

RUDDER_GAM_VERDICT_START
{"verdict":"revise", "message":"…"}
RUDDER_GAM_VERDICT_END

Four things you can type.

What you type decides where the work lands.

you type what happens where it works
fix the login redirect One isolated agent Never touches your checkout. its own workspace
/plan ship billing v2 An orchestrator A task graph you can edit before it runs. Several plans can run side by side. one workspace per node
/gam rewrite the retry logic A Generative Adversarial Model pair One implements. A second one, from the other provider, reviews it and cannot edit anything. one workspace, split pane
/main tag the release An agent in your checkout For jobs that are about your tree. Nothing to merge. your real checkout

A model reviewing its own work agrees with itself.

So /gam runs two of them, and only one is allowed to type.

GAM is a Generative Adversarial Model: one model generates, a second is adversarial to it, and the pressure between them is the point. The reviewer is not a gate at the end. It reads the diff after every turn, while the work is still moving, and what it sends back becomes the generator's next instruction.

What carries over is the part that matters: a critic that is genuinely separate from the thing it is criticising, and that gains nothing by being agreeable. The reviewer comes from the other provider by default, so it has nothing to be loyal to. The two never share a conversation. It is handed the original task, the diff, and whatever the generator wrote back to it, and never the reasoning that produced any of it, because a model that has read its own argument is no longer a second opinion.

half what it does permission
generator Implements the task Your model and provider. It answers each objection directly, and is told to say why it disagrees rather than silently comply. writes the code
adversarial reviewer Tries to refute it Reads the diff, runs the checks itself rather than trusting the transcript, and returns accept, revise or escalate. cannot edit a file

Every round is anchored on the task you typed, never on the last thing the reviewer happened to ask for, so a long argument cannot drift away from what you wanted. There is no round limit: it runs for as long as the argument is getting somewhere, and stops when the reviewer accepts, when it asks for you, or when the generator hands back the same diff and the same answer as the round before. Both panes stay open and Rudder tells you which it was.

The failures worth catching are the quiet ones: 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. A pair argues them out before they reach you. How it works.

Set once. The next agent picks it up.

/model sets these and remembers them. A running agent keeps what it launched with.

Backend

Which CLI actually runs.

  • claudeClaude Code
  • codexOpenAI Codex
  • opencodeopencode

Effort

How long it thinks before it writes.

  • lowmechanical edits
  • mediumbalanced
  • highdesign and debugging
  • xhighthe default
  • maxwhere it is offered

Where it runs

Your machine, or not.

  • localyour hardware
  • cloudRudder Cloud

Put four things in flight.

Type four tasks. Watch four workspaces fill up.

$ npm install -g @viraatdas/rudder@latest

Read the docs View source