お疲れ様です!IT業界で働くアライグマです!
個人開発している開発関連ニュースのキュレーションサービス「DevPick」では、コードの変更をAIコーディングエージェントに任せ、PRを出すたびにAIが自動でレビューする形で開発しています。あるとき、1本のPRでレビューの指摘に1件ずつ応えてpushし直す往復が6回続きました。この記事では、その往復を回数で止める仕組みを入れた経緯と、入れた直後に起きたことを書きます。
指摘に1件ずつ応えてpushを6回繰り返した
PRを出すたびに、AIが差分をレビューしてコメントを付ける
DevPickでは、PRを出したりpushし直したりするたびに、GitHub Actions上でAIのコードレビューが走ります。レビューが終わると、PRに「レビュー完了: 指摘 2 件」や「レビュー完了: 指摘なし」といったまとめのコメントが付きます。指摘が残っている間はマージできない作りです。再pushのときに前回からの差分だけを読ませる仕組みは「AIコードレビューを前回SHAからの差分に絞った記事」に書いています。
指摘を受けて直すのも、多くはAIコーディングエージェント(以下、エージェント)です。直してpushすると、またレビューが走ります。指摘が出なくなるまで、この往復が続きます。
6回の指摘は、どれも前回とは別の抜けだった
10月29日に、エージェントの利用量を集計するレポートを直すPRを出しました。エージェントの作業記録がどのリポジトリのものかを、作業していたフォルダのパスから判定する処理です。このPRのレビューは、次のように進みました(時刻は日本時間)。
| 時刻 | 指摘の内容(要約) |
|---|---|
| 20:00 | パスの途中に「devpick」を含む別のリポジトリも、DevPickと判定してしまう |
| 20:03 | 同じリポジトリでも、サブフォルダから作業すると別のリポジトリとして集計される |
| 20:07 | 決まった場所以外に作った作業用のコピー(git worktree)が、元のリポジトリと別に集計される |
| 20:14 | サブモジュールでは、作業フォルダではなくGitの内部フォルダが集計の単位になる |
| 20:21 | Gitの管理フォルダを別の場所に置いたリポジトリで、判定できなくなる |
| 20:31 | DevPickの中に置いた別のリポジトリが混ざる。CI上の作業記録の分類が漏れる(2件) |
| 20:34 | 指摘なし |
正しい指摘でも、例を1つずつ塞ぐと終わらない
エージェントは、指摘が付くたびにその1件を直してpushしていました。どの指摘も正しく、前回と同じ間違いを繰り返していたわけではありません。それでも、「パスからリポジトリを判定する」というやり方では、Gitのフォルダの置き方のパターンを1つ塞ぐと、次のパターンが見つかります。どこまで対応すれば終わりなのかを決めないまま、例を1つずつ塞いでいたことが、往復が続いた原因でした。
前からあったゲートは、記録の文字列さえ書けば何回目でも通った
実は、レビュー指摘への対応には以前からゲートがありました。Claude CodeのHook(エージェントがコマンドを実行する直前に、決めておいたスクリプトを走らせ、条件を満たさなければ実行を拒否できる仕組み)で、指摘の付いたPRへのpushを止めるものです。
止められたエージェントは、指摘の「観点」と、同じ観点で確認した箇所を記録するスクリプトを実行すれば、pushできます。指摘をその1箇所の問題として直すのではなく、同じ種類の漏れが他に無いかを見させるのが目的でした。
ところが、この記録は文字列を書けば通るので、何回目の往復でも条件は同じです。同じような往復は、10月22日のPRで指摘が9回、同じ10月29日の別のPRでも6回続いていました。エージェントに持たせているメモにも「2回続いたら方式を疑う」「3回続いたら止めて報告する」と書いていましたが、文章で書いていただけで、守らせる仕組みはありませんでした。
IT女子 アラ美往復の回数でpushの条件を上げた
回数が増えるほど、pushに必要なものを増やす
そこで、既存のゲートに「何回続いたか」で条件を上げる段階を足しました。以下は、1本のPRとしてマージした時点の内容です。数え方や承認の確かめ方の一部は、そのPRのレビュー指摘を受けてマージ前に直したもので、経緯は次の節に書きます。
| 指摘が続いた回数 | pushに必要なもの |
|---|---|
| 1回 | これまでどおり、指摘の観点と、同じ観点で確認した箇所の記録 |
| 2回以上 | 上の記録に加えて、「方式の閉じ方」の記録 |
| 3回以上 | さらに、最新の指摘が付いた後に、人(私)が続行を判断した記録 |
2回目で求める「方式の閉じ方」は、今のやり方で扱う範囲と、その範囲なら漏れが出ない理由、そして扱わない範囲とその持っていき先(別のIssueとして起票する)です。次の1件を直す前に、どこまでやったら終わりなのかを先に書かせるためのものです。
数えるのは「最新から続いている指摘」だけにした
回数は、PRに付いたレビューのまとめコメントを投稿順に並べ、最新のものからさかのぼって「指摘あり」が何回続いているかで数えます。途中に「指摘なし」があれば、そこで数え直しです。最初はPR全体の「指摘あり」を合計していましたが、それでは指摘が一度片付いた後の新しい指摘まで往復として数えてしまう、とレビューで指摘されて改めました。単純化すると、次のような判定です。
def consecutive_rounds(summaries):
"""レビューのまとめを古い順に受け取り、最新から続く「指摘あり」の回数を返す"""
count = 0
for summary in reversed(summaries):
if not summary.has_findings: # 「指摘なし」で途切れる
break
count += 1
return count
実際のHookはシェルスクリプトで、GitHubから取ったPRのコメントをjq(JSONを加工するコマンド)で数えています。回数を数えられなかったときは、通すのではなく拒否する側に倒しました。
3回目からは、選択肢で「続行」を選んだときだけ通す
3回目以降の「人の判断」は、Claude Codeの場合、エージェントが私に出す質問で確かめます。Claude Codeには、エージェントが選択肢つきの質問をユーザーに出すツール(AskUserQuestion)があり、そのやり取りは作業記録のファイルに残ります。Hookはこの記録を読み、最新の指摘より後に、その指摘についての質問で私が「続行」の選択肢を選んでいれば通します。
どの指摘への判断かを取り違えないよう、質問文には次のような目印を入れさせます。目印の形は、拒否するときのメッセージでエージェントに伝えます。
[PR #123 指摘 2026-10-29T12:34:56Z]
PR番号と最新の指摘の投稿時刻を入れておくことで、別のPRへの回答や、1つ前の指摘への回答を承認として数えないようにしています。承認は最新の指摘1回分だけ有効で、次に新しい指摘が付けば、もう一度判断が要ります。
ほかのエージェントでは、私が自分の端末で承認する
DevPickでは、Claude Code以外のエージェント(OpenAIのCodexなど)も使っています。そちらはClaude Codeと同じ形で回答を確かめられないので、承認を記録するスクリプトを別に用意し、私が自分の端末で実行する形にしました。
このスクリプトをエージェント自身が実行してしまうと、人の判断を挟む意味が無くなります。そのため、エージェントがこのスクリプトを実行しようとすると、Hookが拒否します。



ゲート自身のPRで、承認の判定を3回突かれた
往復を止める変更のPRでも、指摘が3回続いた
このゲートのPRは、10月29日の20時59分に出しました。皮肉なことに、このPR自身もレビューの指摘が3回続いています。指摘はどれも、マージ前のレビューで見つかったものです。
| 時刻 | 指摘の内容(要約) |
|---|---|
| 21:00 | 最新の指摘より後の質問への回答なら、別件の回答や「中断」でも承認になる。指摘の回数を合計していて、連続になっていない |
| 21:11 | PR番号の照合が甘く、別のPRや別の質問への「続行」でも通る。指摘より前に出した質問に後から答えても承認になる |
| 21:19 | 回答の照合が部分一致なので、自由記述の書き方次第で承認を装える |
| 21:24 | 指摘なし |
21時31分にマージしています。このPRの作業中に、新しいゲートがpushを止めた記録は残っていません。
1回目と2回目は、「承認」の範囲が広すぎた
最初の版は、最新の指摘より後に、エージェントの質問へ私が何か答えていれば通していました。これだと、まったく別の件への回答や、「中断」を選んだ回答でもpushできてしまいます。そこで、質問文にPR番号を含み、回答が「続行」で始まるものだけを承認にしました。
2回目の指摘は、それでもまだ広いというものでした。たとえばPR番号の照合では、PR番号が12のときに「#12」を探すと、「#123」のような別のPRにも当たります。回答が「続行」で始まるかどうかで見ると、自由記述の「続行しない」も通ります。指摘が付く前に出していた質問に、後から答えた場合も承認になっていました。ここで、前の節で書いた目印(PR番号と最新の指摘の時刻)を質問文に入れさせ、質問自体が最新の指摘より後に出されたことと、「続行」という選択肢が選ばれたことを確かめる形にしました。
3回目は、表示用の文字列から回答を読んでいたことが原因だった
2回目の修正では、作業記録に残る表示用の文字列(「質問」=”回答” という形)の中に、目印つきの質問と「続行」の組み合わせが含まれているかで判定していました。3回目の指摘は、「含まれているか」で見ている点でした。自由記述に引用符を混ぜて書けば、反対の意味の回答でも、その文字列の中に「続行」を選んだように見える部分を作れてしまいます。
最終的には、表示用の文字列を読むのをやめました。作業記録には、質問文と選ばれた回答の組が構造化されたデータとしても残っています。その回答が「続行」の選択肢のラベルと完全に一致するときだけ承認にしています。単純化すると、次のような形のデータです。
{
"answers": {
"[PR #123 指摘 2026-10-29T12:34:56Z] 指摘が3回続きました。……": "続行(推奨)"
}
}
選択肢のラベルの末尾に付く「(推奨)」だけは取り除いて比べます。構造化されたデータが無いときや形式が変わったときは、承認しない側に倒れます。
3回とも、承認をどこまで厳密に見るかが決まっていなかった
振り返ると、この3回の指摘はどれも「何をもって私が続行を選んだとみなすか」が決まっていなかったことから出ています。1回ずつ条件を足して狭めていったので、このPRでも例を1つずつ塞ぐ形になっていました。最後に「回答の中身は解釈せず、構造化された回答と選択肢のラベルの完全一致だけを見る」と決めたところで、指摘は止まっています。



入れた直後のPRで、効いた所と効かなかった所
2回目で閉じ方を書かせたPRは、次のレビューで指摘なしになった
ゲートを入れたその日の夜、エージェントの利用量レポートの集計漏れを直すPRで、指摘が2回続きました。22時57分と23時02分です。23時05分、Claude Codeのエージェントがpushしようとすると、ゲートが「方式の閉じ方」の記録を求めて拒否しました。
エージェントは、扱う範囲(作業記録の発話の先頭に自動で付く部分を、どの種類まで取り除くか)と、扱わない範囲(記録の形式が違う別のエージェントは対象外にすること)を書いて記録し直し、pushしました。23時06分のレビューは指摘なしで、23時09分にマージしています。閉じ方を書かせたことが指摘なしにつながったのかまでは、この1件からは分かりません。ただ、ゲートは意図した段階で、意図したとおりに止めていました。
ゲートを登録していないエージェントでは、12回目まで何も止まらなかった
同じ夜に出した別のPRでは、結果がまったく違いました。Codexに短い間隔での待機を繰り返させないよう、そうした書き方をHookで拒否するPRです。このPRは、Claude Codeではなく、別のAIコーディングエージェントのAntigravity(以下、AGY)に作業させていました。
22時12分にPRを出してから23時15分までの約1時間で、AGYのpushに対して指摘が12回続きました。Codexの待機はJavaScriptのコードとして書かれるため、AGYはその中身を読み解こうとし、修正のたびにやり方を変えていました。コミットの記録をたどると、構文解析、Node.jsの隔離環境で実際に実行してみる方法、構文木の解析と、判定の仕組みを大きくしていっています。
これだけ続いても、ゲートは一度も止めていません。DevPickでは、エージェントごとに別々のHook設定ファイルを持っています。AGYの設定には、作業の終了を止めるHookと、sleepでの待機を拒否するHookの2つしか登録されておらず、今回のゲートは入っていませんでした。AGYは、ルールの文書に従って観点の記録スクリプトは実行していましたが、回数に応じた段階は、AGYのpushには最初から働かない状態でした。
Claude Codeに替えて範囲を絞ってからは、指摘のたびに判断を求められた
23時18分、私はこのPRをClaude Codeに引き継がせました。Claude Codeは最初に、JavaScriptの解析をこのPRから外して別のIssueに切り出すことを推奨として示し、私はそれを選びました。修正後のpushはゲートが「12回続いています」と止め、エージェントは目印つきの質問を出して、私が「続行」を選んでから進めています。
ここからは、指摘が付くたびに同じ手順になりました。範囲を絞った後も、絞った範囲の中の抜けや、エージェントがファイルを追加し忘れたことによる指摘が続いたためです。その夜だけで、続行を尋ねる質問が8回出ました。そのうち1回は、エージェントが目印の時刻を間違えたため、同じ内容で聞き直しています。
自由記述で「とにかく進めて」と返すと、pushは止まったままだった
23時40分ごろ、私は選択肢を選ばず、自由記述で「確認せずに進めて」という趣旨の返事をしました。前の節で書いたとおり、ゲートは自由記述を承認として扱いません。pushは拒否され、エージェントは「このダイアログで続行を選んだときだけpushできます」と説明して、同じ質問を出し直しました。
このときは、PRのマージまでエージェントを終わらせないStopフック(「コミットだけして終わるAIエージェントを止めるStopフックの記事」に書いたもの)も動いていました。エージェントは作業を終えられず、私が選択肢を選ぶまでpushもできない状態です。23時52分に、私が自由記述で「いつ終わるのか」と聞くと、エージェントは最短で約25分後と答え、次にまた指摘が付いたら判定の範囲をさらに外して、あと1往復で終えると書いた質問を出しました。私はそこで作業を中断しています。
翌朝、判定をシェルの1パターンだけに絞っても、指摘はあと2回続いた
翌10月30日の8時46分に作業を再開しました。エージェントは、判定の範囲を「シェルのコマンドで、CIの完了を待つスクリプトに短い待機時間の指定を付けたら拒否する」だけに絞ることを推奨し、私は続行を選びました。JavaScriptのコードと待機用のツールの判定は外し、別のIssueに回しています。
それでも、8時52分の17回目のレビューでは、入力の形式によってはHookの判定を素通りする点と、オプションを省略形で書くとすり抜けられる点の2件が指摘されました。修正して8時54分に続行を選ぶと、8時55分の18回目でも1件の指摘が付きました。これも直して8時58分に続行を選び、8時59分の19回目のレビューで、ようやく指摘なしになっています。



まとめ
AIコードレビューの指摘に1件ずつ応えてpushを繰り返すと、1件ずつの対応が正しくても、どこまでやれば終わりなのかを決めない限り往復は終わりません。DevPickでは、指摘が2回続いたら方式の閉じ方の記録を、3回続いたら私の判断を求め、それが無ければpushを拒否するHookを入れました。そのPR自身も、「何をもって承認とみなすか」を1つずつ狭めて、指摘が3回続いています。
入れた直後には、2回目で閉じ方を書かせたPRが次のレビューで指摘なしになった一方、ゲートを登録していなかったAGYでは12回目まで何も止まりませんでした。Claude Codeに引き継いでからは、指摘のたびに私が続行を選ぶ形になり、範囲を絞った後も19回目のレビューまで指摘が続いています。
回数で止める仕組みは、往復が続いていることを私に見せ、判断を挟ませるところまではできました。ただ、往復そのものを減らしたのは、JavaScriptの解析を外す、判定をシェルの1パターンに絞るといった範囲の判断のほうでした。ゲートは、その判断を早い段階で出させるための場所だと今は考えています。なお、AGYのHook設定にこのゲートが入っていない状態は、この記事を書いている時点でも変わっていません。












