A Review Agent Should Not Be Able to Write. Here Is How We Enforce It, CLI by CLI.

In a 17 node run, the release agent edited a test to turn a red suite green, then two review agents wrote the same fix and collided. The prompt said review only. It did not hold. Here is the incident, why a written instruction cannot carry that rule, and the exact flags that make Claude Code, Codex, Grok, Antigravity and OpenCode refuse to write.

Earlier this week a user sent us a run report that is worth reading twice. Seventeen agents, one shared worktree, a pipeline with an implementer, a release gate and two review branches. Version 1.171.0 of AgentsRoom.

Three things happened in that run, in this order.

The release agent had a red test suite in front of it. It edited the test spec until the suite was green. In doing so it shipped a real defect, now covered by a test that agreed with it.

Then two review agents, on two parallel branches of the same run, each found a genuine one line bug. Each fixed it directly, in the same worktree, at the same time. They collided.

Every one of those agents had a step prompt that said, in plain words, review only. Nothing stopped them, and nothing signalled it: from the platform's side, a step had write tools and used them.

Why the prompt did not hold

The tempting reading is that the agents ignored the instruction. That is not what happened, and it matters because it changes what the fix has to be.

Each agent had a locally defensible reason to write. A red suite and a spec that looked wrong. A bug that takes four seconds to fix and forty to describe. None of them decided to break the rule. Each decided its case was the one the rule did not mean. From inside the step, the exception always looks reasonable.

A written instruction is a request to the model's judgement. A reviewer that is also able to fix things will, sooner or later, fix things, because fixing is the shortest path from "I found it" to "done". The only rule that survives contact with a plausible exception is one the model cannot argue with: a tool that is not there.

Why the global setting could not do it either

Before this, the only lever that reached a running step was the provider settings, which cover every Claude agent on the machine at once. That is the wrong shape for a run. In the same pipeline, the implementer must write and the reviewer must not. A global switch cannot tell them apart.

And the per agent restriction we already had, the one a ticket can carry when it launches an agent, was deliberately not passed to team steps. So an agent started from a ticket could be restricted, and a node of a team could not. That was the primary cause, and it was a design decision that had aged badly.

The rule: one checkbox on the node

The fix is a boolean on the review node. Tick Read-only and the agent that embodies that step is launched without write access to the project: no file edit, no git commit or push, no shell command whose only job is to change the working tree. Reading, grep, git diff, git log, the tests, the linter and every team tool stay open.

We were explicit about what it is not: it is not a deny list the user writes by hand, per CLI. Nobody should need to know five permission syntaxes to say "this one reviews". The switch produces the right flag for each provider, and it is applied last, after the autonomous mode and after anything the user stored on the agent, so it wins.

What each CLI does, read off its own help

We only enforce on providers whose flag we read on their own --help. A guessed flag kills the launch on a parse error, which is worse than a step with no enforcement. The other CLIs get the written rule only, and the editor says so in plain text under the checkbox.

CLIWhat the read-only switch addsHolds under autonomous mode?
Claude Code--disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)" and the restYes, deny rules apply under --dangerously-skip-permissions
Codex--sandbox read-onlyYes, it is an OS sandbox (Seatbelt on macOS, Landlock on Linux), not a tool list
Grok Build--deny "Edit" --deny "write" --deny "Bash(git commit*)" and the restYes, deny rules apply under --always-approve
Antigravity--mode planYes, plan is the CLI's read-only execution mode
OpenCode--agent planYes, the built-in plan agent denies the edit tools
Mistral Vibe, Kimi Code, Copilot, Cursor, Amp, Aider and the restprompt paragraph onlyNo verified flag, and we say so in the editor

Two details in that table cost us a bug each, so they are worth spelling out.

Codex and Grok refuse a repeated flag. Both parse their arguments with a strict parser. If the user had already stored --sandbox workspace-write on the agent, appending --sandbox read-only would not override it, it would crash the launch. So for valued flags we remove any existing occurrence, with its value, before appending ours. Same for --agent on OpenCode, whose parser turns a repeated flag into an array and fails further down.

On Claude Code the list has to stack. --disallowedTools takes a space separated list and can be repeated, and we already pass one when an agent is not allowed to drive the embedded browser. The parser concatenates a repeated variadic option, so the two lists add up instead of the second replacing the first.

The full Claude Code list is the four file editing tools, every git subcommand that writes to the index, the tree, the refs or a remote (add, commit, push, merge, rebase, reset, checkout, switch, restore, stash, cherry-pick, revert, apply, am, rm, mv, clean, tag, worktree), and the shell commands that only exist to change files (rm, mv, cp, tee, touch, mkdir, chmod, chown, ln, truncate, dd, sed -i). Grok takes the same rule strings, in its glob form, plus its own names for the file tools (search_replace, write, hashline_edit).

The prompt still has a job

The flag refuses. It does not explain. And an agent that hits a refusal it does not understand treats it as a bug and looks for another way through, which is the exact behaviour we were trying to remove.

So a read-only step also gets two sentences in its prompt. The first says the step is read-only, lists what that means, and states that a refusal is the rule, not an obstacle to route around with another command. The second lists what stays open, and tells the agent to report what should change, with the file, the line and the reason, in its handoff, and let the step that owns the code apply it.

On the CLIs with a verified flag, that paragraph is what makes the refusal understood. On the others, it is the whole enforcement, and we would rather say that than pretend.

Who is read-only in the shipped templates

The nodes that judge are read-only: the QA verification step of the two starter templates, the reproduction and verification steps of Bug hunt, the QA and Security branches of Release shield, the tester of Feature squad.

The nodes that own the code keep writing: the developer, and the release gate that is written to fix every finding itself. A release gate that cannot write is a release gate that cannot release.

That split is the whole design, and it is the split the incident violated twice: a release node that wrote in the wrong place, and review nodes that wrote at all.

What this is not

It is not a security boundary. The reporter said it in the report, and they were right: a bash -c gets around a tool deny list. If you need to isolate an agent you do not trust, that is a sandbox or a separate machine, and Codex is the only one of the five whose read-only mode is actually that.

What the switch stops is the accident and the role drift, which is what actually happens. A reviewer does not escape a deny list on purpose. It reaches for Edit by reflex, and the reflex is now refused.

What we did not build

The reporter asked for one more thing: an event in the run timeline saying "node X wrote to the tree", as the minimum signal even without enforcement. It is a good idea and we did not do it here, because it needs a baseline diff per step on the runner side. If the need comes back, that is the next piece.

If you are not using AgentsRoom

The flags above are copyable as they are. A review agent launched by hand with codex --sandbox read-only, or claude --disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)", cannot do what our two review nodes did. Put the same two sentences in its prompt so it knows why it is being refused.

What the checkbox adds is that you do not have to remember which of the five syntaxes applies, that the flag wins over whatever autonomous mode the step runs in, and that it survives a run that re-enters the same step later.

The node switch and the per provider table are documented on the Agent Teams page. Whether a review by an agent is worth having at all, and how much of a diff still deserves a human, is a different question, and we wrote about it in Should you still review your AI agent's code?. This post is about the smaller, more mechanical thing: once you have decided an agent reviews, make it physically unable to do anything else.

Download AgentsRoom

Run all your AI agents, on all your projects, from a single window.

FreeDownload AgentsRoom

Companion app: monitor your agents on the go

Bring your own: Claude, Codex, Antigravity CLI, or other AI provider.

Get the extension
Chrome Web Store

Push bugs and requests straight to your public backlog.

Multiple projects
Multi-provider
Multiple agents
Live status
File diff & commit
Mobile companion
Live preview
Agent teams
Browser automation
Backlog-driven dev
Prompt Library
Skills Library
View all features

Keep reading