レビューエージェントに書き込みをさせてはいけない。CLIごとにどう強制しているか。

17ノードのランで、リリースエージェントは赤いテストスイートを緑にするためにテストを書き換え、その後2つのレビューエージェントが同じ修正を書いて衝突した。プロンプトには「レビューのみ」と書いてあった。それは守られなかった。インシデントの経緯、書かれた指示がこのルールを担えない理由、そしてClaude Code、Codex、Grok、Antigravity、OpenCodeに書き込みを拒否させる正確なフラグ。

今週の初め、あるユーザーから、二度読む価値のあるラン報告が届きました。17のエージェント、1つの共有worktree、実装担当、リリースゲート、2本のレビューブランチからなるパイプライン。AgentsRoomのバージョン1.171.0です。

そのランでは、3つのことがこの順番で起きました。

リリースエージェントの目の前には、赤いテストスイートがありました。スイートが緑になるまで、テストのspecを書き換えました。その結果、本物の欠陥を、それを正しいと認めるテストに守られた状態で出荷してしまいました。

次に、同じランの並列する2本のブランチで、2つのレビューエージェントがそれぞれ本物の1行バグを見つけました。それぞれが、同じworktreeで、同じタイミングで、直接修正しました。衝突しました。

これらのエージェントはどれも、ステッププロンプトに「レビューのみ」とはっきり書かれていました。何もそれを止めず、何もそれを知らせませんでした。プラットフォーム側から見れば、あるステップが書き込みツールを持っていて、それを使った、それだけです。

プロンプトが守られなかった理由

つい飛びつきたくなる解釈は、エージェントが指示を無視した、というものです。実際に起きたことはそうではなく、そしてそれが重要です。修正が何であるべきかが変わるからです。

各エージェントには、書き込む局所的に正当な理由がありました。赤いスイートと、間違っているように見えるspec。直すのに4秒、説明するのに40秒かかるバグ。どれもルールを破ろうと決めたわけではありません。それぞれが、自分のケースはルールの意図する範囲外だと判断したのです。ステップの内側から見ると、例外はいつも合理的に見えます。

書かれた指示は、モデルの判断への依頼です。修正もできるレビュアーは、遅かれ早かれ修正します。修正することが「見つけた」から「完了」への最短経路だからです。もっともらしい例外に出会っても生き残る唯一のルールは、モデルが反論できないもの、つまりそこに存在しないツールです。

グローバル設定でも無理だった理由

それ以前、実行中のステップに届く唯一のレバーはプロバイダー設定で、これはマシン上のすべてのClaudeエージェントを一度に対象にします。ランにとってこれは間違った形です。同じパイプラインの中で、実装担当は書き込まなければならず、レビュアーは書き込んではいけません。グローバルなスイッチでは、この2つを区別できません。

そして、すでに持っていたエージェント単位の制限、チケットがエージェントを起動するときに持たせられるものは、意図的にチームのステップには渡されていませんでした。チケットから起動したエージェントは制限できるのに、チームのノードは制限できない。それが主因で、古びてしまった設計判断でした。

ルール:ノードのチェックボックス1つ

修正は、レビューノードに付けたブール値です。読み取り専用にチェックを入れると、そのステップを担うエージェントは、プロジェクトへの書き込み権限なしで起動されます。ファイル編集なし、git commitやpushなし、作業ツリーを変更することだけが仕事のシェルコマンドなし。読み取り、grep、git diffgit log、テスト、リンター、そしてすべてのチームツールは開いたままです。

これが何ではないかも明確にしました。ユーザーがCLIごとに手で書く拒否リストではありません。「これはレビューする」と言うために、5つのパーミッション構文を知っている必要はないはずです。このスイッチはプロバイダーごとに正しいフラグを生成し、自律モードの後、ユーザーがエージェントに保存した何よりも後、つまり最後に適用されるので、必ず勝ちます。

各CLIが何をするか、それぞれのヘルプから読み取ったもの

強制するのは、そのCLI自身の--helpでフラグを読み取れたプロバイダーだけです。推測したフラグはパースエラーで起動を殺し、それは強制のないステップより悪い。それ以外のCLIは書かれたルールだけを受け取り、エディタはチェックボックスの下にそのことを平文で表示します。

CLI読み取り専用スイッチが追加するもの自律モードでも効くか
Claude Code--disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)" など効く。拒否ルールは--dangerously-skip-permissionsの下でも適用される
Codex--sandbox read-only効く。OSのサンドボックス(macOSではSeatbelt、LinuxではLandlock)であり、ツールのリストではない
Grok Build--deny "Edit" --deny "write" --deny "Bash(git commit*)" など効く。拒否ルールは--always-approveの下でも適用される
Antigravity--mode plan効く。planはこのCLIの読み取り専用の実行モード
OpenCode--agent plan効く。組み込みのplanエージェントが編集ツールを拒否する
Mistral Vibe、Kimi Code、Copilot、Cursor、Amp、Aider、その他プロンプトの段落のみ検証済みのフラグなし。エディタにもそう表示する

この表の2つの細部で、それぞれ1つずつバグを踏んだので、書き出しておく価値があります。

CodexとGrokは、フラグの重複を拒否します。 どちらも厳格なパーサーで引数を解析します。ユーザーがすでにエージェントに--sandbox workspace-writeを保存していた場合、後ろに--sandbox read-onlyを付けても上書きされず、起動がクラッシュします。そのため、値を取るフラグについては、既存の出現を値ごと取り除いてから、こちらのフラグを追加します。OpenCodeの--agentも同じで、こちらのパーサーは重複したフラグを配列にしてしまい、その先で失敗します。

Claude Codeでは、リストは積み重なる必要があります。 --disallowedToolsはスペース区切りのリストを取り、繰り返し指定でき、エージェントに組み込みブラウザの操作を許可していないときにはすでに1つ渡しています。パーサーは繰り返された可変長オプションを連結するので、2つ目が1つ目を置き換えるのではなく、2つのリストが足し合わされます。

Claude Code向けの完全なリストは、4つのファイル編集ツール、インデックス、ツリー、ref、リモートのいずれかに書き込むすべてのgitサブコマンド(addcommitpushmergerebaseresetcheckoutswitchrestorestashcherry-pickrevertapplyamrmmvcleantagworktree)、そしてファイルを変更するためだけに存在するシェルコマンド(rmmvcpteetouchmkdirchmodchownlntruncateddsed -i)です。Grokは同じルール文字列をglob形式で受け取り、さらに自身のファイルツール名(search_replacewritehashline_edit)が加わります。

それでもプロンプトには役割がある

フラグは拒否します。説明はしません。そして、理解できない拒否にぶつかったエージェントは、それをバグとして扱い、別の通り道を探します。これはまさに、取り除こうとしていた振る舞いそのものです。

だから読み取り専用のステップは、プロンプトにも2つの文を受け取ります。1つ目は、このステップが読み取り専用であること、それが何を意味するかの一覧、そして拒否はルールであって別のコマンドで迂回すべき障害ではないこと。2つ目は、開いたままのものの一覧と、変更すべきことをファイル、行、理由とともに引き継ぎに書き、コードを所有するステップに適用を任せるようエージェントに求める指示です。

検証済みのフラグを持つCLIでは、この段落が拒否を理解させるものになります。それ以外では、これが強制のすべてであり、取り繕うよりはそう言うほうを選びます。

同梱テンプレートで誰が読み取り専用か

判定するノードが読み取り専用です。2つのスターターテンプレートのQA検証ステップ、Bug huntの再現ステップと検証ステップ、Release shieldのQAブランチとセキュリティブランチ、Feature squadのテスター。

コードを所有するノードは書き込みを続けます。開発者、そして見つかった問題をすべて自分で修正するように書かれたリリースゲートです。書き込めないリリースゲートは、リリースできないリリースゲートです。

この分担が設計のすべてであり、インシデントが2回破ったのもこの分担です。間違った場所に書き込んだリリースノードと、そもそも書き込んだレビューノード。

これが何ではないか

セキュリティ境界ではありません。報告者は報告の中でそう書いていて、正しかった。bash -cはツールの拒否リストをすり抜けます。信頼していないエージェントを隔離する必要があるなら、それはサンドボックスか別マシンの仕事で、5つのCLIのうち、読み取り専用モードが実際にそれであるのはCodexだけです。

このスイッチが止めるのは、事故と役割のずれで、実際に起きるのはそれです。レビュアーは拒否リストをわざと回避したりしません。反射的にEditに手を伸ばし、その反射が今は拒否されるのです。

作らなかったもの

報告者はもう1つ求めていました。強制がなくても最低限の合図として、ランのタイムラインに「ノードXがツリーに書き込んだ」というイベントを出すこと。良いアイデアですが、今回はやりませんでした。ランナー側でステップごとの基準diffが必要になるからです。必要がまた出てきたら、それが次のピースです。

AgentsRoomを使っていないなら

上のフラグはそのままコピーできます。codex --sandbox read-onlyclaude --disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)"で手動起動したレビューエージェントは、うちの2つのレビューノードがやったことをできません。なぜ拒否されるのかが分かるように、同じ2つの文をプロンプトに入れてください。

チェックボックスが加えるのは、5つの構文のどれが当てはまるかを覚えなくてよいこと、ステップがどの自律モードで動いていてもフラグが勝つこと、そして後で同じステップに再び入るランでも効き続けることです。

ノードのスイッチとプロバイダーごとの表はAgent Teamsページに載せています。エージェントによるレビューにそもそも価値があるのか、diffのどれだけがまだ人間に値するのかは別の問いで、AIエージェントのコードはまだレビューすべきか?で書きました。この記事はもっと小さく、もっと機械的なことについてです。エージェントにレビューさせると決めたら、それ以外のことを物理的にできなくしてください。

AgentsRoomをダウンロード

すべてのAIエージェントを、すべてのプロジェクトで、ひとつのウィンドウから実行。

無料AgentsRoomをダウンロード

コンパニオンアプリ:外出先でもエージェントを確認

Claude、Codex、Antigravity CLI、またはその他の AI プロバイダーを使用します。

拡張機能を入手
Chrome Web Store

バグや要望を公開バックログに直接送信できます。

マルチプロジェクト
マルチプロバイダー
マルチエージェント
ライブステータス
ファイル差分
モバイルアプリ
ライブプレビュー
エージェントチーム
ブラウザテスト
バックログ駆動開発
プロンプトライブラリ
スキルライブラリ
すべての機能を見る

続きを読む