「筋はいいし、テストもある。通していいか」。AI エージェントが出してくる不具合の修正を 1 本ずつ見ると、たいていこう思う。症状を再現し、原因の行を指し、条件を 1 つ足し、テストまで添えてくる。文句のつけようがない。
1 本ずつなら通してよい修正のはずが、同じ場所に積み上がると話が変わる。筆者の手元では、不具合の起票から修正までを Claude Codeが回している。そこで、セキュリティ指摘を直す役の AI(以下、修正担当)への指示文に入力を 1 つずつ足す issue が、約 30 時間で 3 本続いた。3 本目は、2 本目の修正が merge された 5 分後に不要として閉じられた。起票から 21 分。一度も出番が来ないまま、楽屋で解散である。
文句のつけようがない修正が、なぜ積み上がるのか。この記事では、それを issue の段階で見分ける基準 2 つと、自分の repo で AI に確かめさせる方法をまとめる。
1 本ずつ見れば、3 本とも正しかった
| 順 | 起きた症状 | issue の直し方 | 結末 |
|---|---|---|---|
| 1 | 修正担当が人に返した指摘を、自動化の側が照合できず「自動修正失敗」で終わる | 指示文に、指摘の識別子を載せる | merge |
| 2 | 指摘の提案が「PR の変更を取り消す」で、修正担当はそれを人に返すしかなかった。意図を保ったまま塞ぐ解は実在した | PR の意図を、守る要件として修正担当に渡し、意図と指摘の両方を満たす修正を設計させる | merge |
| 3 | 修正担当が受入条件のテストごと撤去し、PR の差分が 0 行になる | 指示文に、PR が閉じる issue の受入条件を載せる | 2 の merge 後に不要と判断して close |
3 本目の症状に至っては、修正担当が指摘を消すために PR の変更ごと消していた。差分は 0 行。燃えるものが無ければ火事は起きない、という理屈だ。火は消えたが、今度は受入条件の側から差し戻しが来た。
症状は 3 つともばらばらなのに、直した場所は同じ。修正担当に渡す指示文の組み立てだ。どの issue も「この症状にはこの情報が足りない」を正しく突き止め、情報を 1 つ足していた。では、正解を 3 回重ねると何ができるか? 放っておけば、差し込み口が 3 つ並んだ指示文になっていた。止めたのは、2 本目が入力と一緒に入れた「PR の変更を取り消す修正は、修正とみなさない」という指示だった。足し算の列に、1 行だけ考え方が混ざっていたわけだ。
局所修正は、issue の点検を満点で通る
なぜ 3 本とも素通りしたのか。理由は 1 つにまとまる。起票の手順は「直し方が症状に効くか」しか確かめていなかった。issue の点検表も、受入条件・範囲・依存・変更対象パスなど issue の形を中心に見ていて、直し方の型を問う項目は無かった。「この直し方で、同じ型の別の症状は残らないか」は、どの段階でも聞いていない。
筆者の見た範囲では、AI の修正提案は目の前の症状を消す方に寄りやすい。点検が形しか見ていなければ、あなたの repo でも同じことが起きうる。形の整った局所修正は、形の点検を満点で通る。満点の答案を止める採点者はいない。
見分ける基準は 2 つ
1 つ目は直し方の型。直し方が分岐・ガード・入力を足すだけで、同じ型の別の症状が残るか、削除・統合で済む案を検討していなければ当たり。2 つ目は回数。同じ判定、同じ指示文の組み立て、同じガードを直した close 済みの issue が、直近 30 日に 2 件以上あれば当たり。数字は筆者の置いた線なので、自分の repo の修正の頻度に合わせて変えてよい。
どちらか 1 つで足りる。2 つそろうのを待つと、回数が 2 件にたまるまで差し込み口は増え続ける。
自分の repo で AI に確かめさせる
回数は、直す場所を関数の単位で書かせてから数えさせる。核になるのはこの 2 つのコマンドだ(関数名とパスは例)。
# 直近30日に、その関数を変えた commit(ファイル名を変えていても追える)
git log --since="30 days ago" -L ':buildFixPrompt:src/fix-prompt.js' --no-patch --format='%h %ad %s' --date=short
# 直近30日に close された issue のうち、本文にその関数名が出るもの(日付は30日前に置き換える)
gh issue list --state closed --search '"buildFixPrompt" in:body closed:>=2026-10-05' --json number,title,closedAt
パスで引けば、同じ場所の修正がきれいに並ぶ。そう思いたいところだが、実際は鍵の選び方で数が大きく動く。筆者の手元で直近 30 日に close された issue を引き比べると、よく触るファイルのパスでは 15 件が出てきた。指示文を組み立てる関数の名前では 2 件で、どちらも今回の連鎖の issue だった。ただし同じ指示文の定数名で引くと 8 件に増える。「同じ仕組みを直したか」は、本文を読ませて判断させるしかない。検索が集めるのは容疑者までで、判決は本文を読んでからだ。
| AI に頼むこと | 確かめさせる中身 |
|---|---|
| 直す場所を書かせる | ファイルだけでなく関数の単位で |
| 回数を数えさせる | 上の 2 つのコマンド。続けて起票しているなら open の issue も(後述)。パスだけで数えない |
| 引いた issue の本文を読ませる | 同じ判定・同じ指示文の組み立て・同じガードを直したか |
| 直し方の型を言わせる | 分岐・ガード・入力を足すだけか。同じ型の別の症状を 2 つ挙げさせ、この修正で消えるか |
| 削除・統合の案を出させる | その分岐ごと消して済む設計は無いか |
数えるときの注意は 3 つある。
| 状況 | 起きること | 対処 |
|---|---|---|
| クラスのメソッドなど、字下げされた関数 | 既定の設定では -L ':名前:ファイル' が no match で止まる | 行番号で範囲を渡す(-L 10,40:ファイル) |
| ファイル名を変えた | ファイル単位の git log -- パス は変更前の履歴を落とす | --follow を付ける。-L は追ってくれる |
| issue を立てずに直した修正 | gh の検索に出ない | git log 側でも数える |
当たったら、その場で直さず設計させる
どちらかの基準に当たった不具合は、その場で直させない。症状・原因・直し方を入力にして、同じ型の別の症状まで消える直し方を設計させる。削除・統合・一般化も選択肢に入れる。
ただし「根本」を口実に、広い範囲のリファクタを始めさせない。1 issue = 1 PR に収める。無人で回しているなら、設計に切り替えた後で人への質問を待たせない。足りない制約は、入力と対象コードから決めさせる。
白状すると、条件を足すだけの修正を止めるこの仕組みも、起票の手順に条件を 2 つ足して作った。言い訳をするなら、置き場所が違う。症状ごとの差し込み口ではなく、すべての修正が通る入口に一度だけ置いた。ミイラ取りがミイラにならないかは、運用で見るしかない。
取りこぼすところ
回数の基準は、同じ仕組みを 2 回直した後にしか効かない。1 回目と 2 回目を止めるのは、直し方の型を見る 1 つ目の基準の役目だ。
しかも数えるのは close 済みの issue と merge 済みの commit だけで、同じ仕組みを直している最中の open の issue は数に入らない。今回の 3 本目は 2 本目の PR が merge される 16 分前に起票されていて、その時点で関数名で引ける close 済みの issue は 1 件だけだった。数える仕組みを先に入れていても、3 本目は堂々とすり抜けていた。
issue を続けて起票しているなら、open の issue も引かせる。
gh issue list --state open --search '"buildFixPrompt" in:body' --json number,title,createdAt
「同じ仕組み」の判定は、AI が本文を読んで決める。機械的な判定ではないので、誤検出も見逃しも出る。切り替えた理由を残させておけば、どちらに外れたかを後から数えられる。見張り役にも、見張りが要る。
まとめ
AI の修正は、1 本ずつ見ると正しい。困るのは、正しいまま積み上がることだ。見るべきは、その直し方が条件を足すだけかと、同じ場所を直近で何回直したかの 2 つ。どちらかに当たったら、その場で直させず、同じ型の別の症状まで消える直し方を設計させる。
AI エージェントに issue から修正まで任せる開発の組み立ては、AI実装支援でも相談を受けている。範囲外の差分の扱いはAIエージェントのPRに混ざった範囲外の差分の記事にまとめた。



