最近、Claude Codeでコードレビューを行うスキルを育てていて、1つのPRを「correctness」「reuse」「efficiency」「altitude」といった複数の観点に分けて、並列で動くサブエージェントにレビューさせる運用をしています。人間が1人で全観点を見るより早く網羅的にレビューできるので気に入っているのですが、運用しているうちに、性質の異なる2つの事故に遭遇しました。どちらも「AIエージェントに何を触らせているか」を甘く見ていたことが原因だったので、整理しておきます。
以下のコード例やディレクトリ名は、実際のプロダクトそのものではなく、同じ構造が伝わるように書き起こしたサンプルです。
事故1: 並列エージェントの報告が互いに食い違う
あるPRを8体のサブエージェントで並列レビューしたときのことです。レビュー結果を見ていると、同じ関数についてサブエージェントごとに指摘している行番号や実装の中身が食い違っていました。しかも一部のサブエージェントは、レビュー対象のPRにはまだ存在しないはずの配列(changedElementsのようなもの)について、「この配列の扱いが怪しい」と指摘してきます。
原因はシンプルで、著者がレビュー中に新しいコミットを追加したタイミングと、サブエージェントたちの実行タイミングが重なったことでした。各サブエージェントは同じディレクトリ(同一のgit working tree)に対してそれぞれgit checkoutやgit fetchを実行しており、あるサブエージェントは古いコミットの状態を、別のサブエージェントは新しいコミットの状態を読んでいたのです。サブエージェント自身も完了報告の中で「working treeが他ブランチにドリフトしていた」と自己申告していました。
見分け方
複数の並列エージェントの報告で、同じ関数について行番号や実装の細部が食い違っている場合はこれを疑うとよさそうです。特に「PRに新しいコミットが追加された直後」のタイミングでレビューを走らせると起きやすいと感じました。
対策: HEADのコミットSHAを固定する
対策はレビュー対象のコミットを最初にSHAで固定してしまうことでした。
gh pr view <PR番号> --json headRefOid --jq '.headRefOid'
このSHAを明示的にgit fetch origin <SHA>し、以降はファイルの中身を見る際にgit checkoutやgit switchではなく、working treeを一切変更しないgit show <SHA>:<path>を使うようにしました。複数回に分けてレビューする場合は、毎回headRefOidを再取得して「前回レビュー時と同じコミットかどうか」を確認してから差分ベースで見るようにしています。地味な対策ですが、これだけで報告内容の食い違いは起きなくなりました。
事故2: サブエージェントがメインの作業ディレクトリを汚染する
別のPRを5回目までレビューしていたときには、もう少し実害の大きい事故が起きました。調査用に並列起動したサブエージェントの1つが、「実際に差分を適用して動作確認したい」という理由で、私が普段作業しているセッションと同じディレクトリに対して直接PRの差分を適用してしまったのです。気づいたときには、developブランチに3ファイルの変更と2つの新規ディレクトリが出現していました。自分が実際に触っているブランチが突然汚れるので、レース条件による食い違いよりも実害としては大きいと感じました。
見分け方
サブエージェントの完了報告に「clean checkoutに適用した」「実際にspecを実行した」のように、git操作を伴う検証をしたと書かれている場合は要注意です。また、サブエージェントの完了直後にメインセッションのgit statusが突然dirtyになっていたり、身に覚えのない新規ディレクトリが出現していたりする場合も同じサインだと思います。
対策: 隔離を明示的に指示する
サブエージェントに「実際に動かして確認して」と依頼するときは、git worktree add <隔離パス> <ブランチ/SHA>で別ディレクトリを切ってから作業するよう、プロンプトに明示的に書くようにしました。ここに書いておかないと、エージェントは特に疑問を持たず「今いるディレクトリ」を使ってしまいます。
git worktree add /tmp/pr-review <ブランチ名>
# 確認作業はすべて /tmp/pr-review 側で行う
あわせて、サブエージェントからの完了報告のたびにメインディレクトリのgit status --shortを確認する習慣もつけました。万一汚染を発見した場合は、セッション開始時点のgit statusがclean だったことを確認したうえで、汚染されたファイルがレビュー対象PRの差分と一致するか(git diff --statで変更ファイル・行数を照合)を確認してから復元します。git checkout --や新規ディレクトリのrm -rfは破壊的な操作なので、内容を確認せずに機械的に実行しないようにしています。作業が終わったworktreeはgit worktree remove <path> --forceと、必要であれば追跡用に切った一時ブランチのgit branch -Dで必ず片付けるようにしました。
まとめ
2つの事故を振り返ると、原因はどちらも「並列で動くAIエージェントに、共有された状態を無防備に触らせていたこと」に集約されます。
- 複数のエージェントが同じgit working treeに対して
checkoutやfetchを行うと、レース条件でレビュー結果自体の信頼性が崩れる - 単独のエージェントが「動作確認のため」という善意で本番の作業ディレクトリを操作すると、実害が自分の開発フローにまで及ぶ
対策自体は、レビュー前にHEADのSHAを固定する、動作確認はworktreeで隔離するようプロンプトに明記する、という地味なものです。ただ、これを書いていないとAIエージェントは驚くほど素直に「今ある状態」をそのまま使ってしまうので、便利さに頼って隔離の指示を省略しないことが大事だと感じました。並列サブエージェントでのコードレビューを試している方の参考になれば嬉しいです。