何をしたか
PRを作成すると、GitHub Actions上のClaude Codeが自動でコードレビューを投稿する運用をしている。
指摘はだいたい当たっている。
当たっているからこそ、毎回のように修正コミットとやり取りが発生して、それなりに面倒だった。
そこで、過去のマージ済みPRに付いたレビュー指摘をすべて収集して分類し、繰り返し指摘されているパターンを開発ルール(CLAUDE.mdと.claude/rules/)へ反映した。
結果は次のとおり。
- マージ済み176件のPRのレビューを走査し、355件の指摘を抽出した
- そのうち76%は、事前にルール化できる繰り返し型だった
- ただし、ルールを文書に書くだけでは防げない。すでに明文化されていたのに26件のPRで指摘され続けた項目があった
以下、収集から反映先の設計までを、そのまま再現できる手順で書く。
対象はLaravel + Livewireの業務システムだが、GitHubで自動レビューを運用しているリポジトリなら手順はそのまま使える。
レビューコメントの全件収集
コメントはPRごとに取得しない。
PRごとに叩くと数百リクエストになるが、リポジトリ単位のエンドポイント2本なら、ページネーション込みで数回のリクエストで全件取れる。
# トップレベルのレビューコメント(PRの会話欄)
gh api "repos/{owner}/{repo}/issues/comments?per_page=100" --paginate \
--jq '.[] | select(.user.login | test("claude"; "i")) |
{pr: (.issue_url | split("/") | last), body: .body}' > toplevel.jsonl
# inlineコメント(コード行に付くもの)
gh api "repos/{owner}/{repo}/pulls/comments?per_page=100" --paginate \
--jq '.[] | select(.user.login | test("claude"; "i")) |
{pr: (.pull_request_url | split("/") | last), path: .path, body: .body}' > inline.jsonl
# マージ済みPRのリスト(絞り込み用)
gh pr list --state merged --limit 500 --json number,title > merged-prs.json
botのlogin名は環境によって違う(github-actions[bot]名義で投稿される構成もある)ので、test("claude"; "i")の部分は自分の環境に合わせる。
取得後、マージ済みPRの番号で絞り、「指摘事項」「結論」などのマーカーを含むコメントだけをレビューとして残す。
今回はこのフィルタ後で176PR分、トップレベルとinline合わせて約65万字が残った。
サブエージェントによる並列分類
65万字は1つのコンテキストに収まらない。
PRごとにまとめたテキストを約8万字ずつ8チャンクに分割し、Claude Codeのサブエージェント8本へ並列で投げた。
1つのPRのレビューが2チャンクに跨がると分類の重複や欠落を生むため、分割は必ずPRの境界で行う。
各エージェントへの指示の骨子は次のとおり。
- 「指摘事項」だけを抽出する。「良い点」のセクションは無視する
- 各指摘を次の形式のJSONにする
- severity(指摘の深刻度):blocker / conditional(条件付きマージ可)/ minor / info の4段階
- category:提示した17カテゴリから選ぶ。合わなければ新設してよい
- summary:80字以内の要約
- generalizable:ルール化できる繰り返し型か(true / false)
- rule:trueの場合、一般化したルール文を1文で書く
- 結果をJSONファイルに書き出す(8本分のJSONは最後に1ファイルへ結合して集計に回す)
実際に使ったプロンプトを載せておく。
分類プロンプト(カテゴリ一覧つき)
あなたはPRレビューコメントの分析担当です。
chunk-N.md を読んでください。これはマージ済みPRに対するAI自動レビューのコメント集です。
タスク: 「指摘事項」(修正を求められた/推奨された内容)をすべて抽出し、構造化してください。
「良い点」セクションは無視。「参考情報」はseverity=infoとして含める。
各指摘を以下のJSONオブジェクトにする:
- pr: PR番号(数値)
- severity: "blocker" | "conditional"(条件付きマージ可) | "minor" | "info"
- category: 以下から最も近いものを1つ。当てはまらなければ新カテゴリを短い日本語で作ってよい:
"兄弟・類似箇所への修正漏れ", "テスト観点漏れ・テスト不備", "用語集との齟齬",
"ORM操作規約違反", "N+1・パフォーマンス", "バリデーション不備", "権限・認可漏れ",
"エッジケース・null安全", "ブランチ運用・リリースノート", "命名・コード規約",
"フレームワーク固有の罠", "トランザクション・整合性", "UI・表示不具合",
"ドキュメント・コメント不備", "既存バグの指摘(PR範囲外)", "設計・アーキテクチャ",
"デッドコード・不要コード"
- summary: 指摘内容の1文要約(80字以内)
- generalizable: true/false — 「事前のルール・チェックリストで防げた繰り返し型」か、
そのPR固有の文脈がないと気づけないものか
- rule: generalizable=true の場合のみ、一般化したルール文を1文で
結果を JSON 配列として findings-N.json に書き出してください。
分類軸の中ではルール化できる繰り返し型かが要になる。
「そのPR固有の文脈がないと気づけない指摘」と「毎回同じことを言われている指摘」を分けることで、ルール化に値する対象だけが残る。
重み付き集計とランキング
指摘の深刻度(severity)に重みを付けてカテゴリ別に集計した。
重み付きスコアの降順で、上位は次のようになった。
表で見る
| カテゴリ | 件数 | 出現PR数 | ルール化可能率 | スコア |
|---|---|---|---|---|
| テスト観点の漏れ | 60 | 56 | 92% | 65.0 |
| ドキュメントとコメントの同期漏れ | 41 | 36 | 78% | 41.7 |
| 命名とコード規約 | 32 | 32 | 56% | 31.2 |
| エッジケースとnull安全 | 25 | 23 | 68% | 28.5 |
| 兄弟箇所への修正漏れ | 23 | 22 | 96% | 27.4 |
| ブランチ運用とリリースノート | 26 | 26 | 69% | 26.6 |
個別に見ると、指摘には型があった。
たとえばテスト観点の指摘は、次の数パターンで大半を占める。
- 値なし時のフォールバック表示(「-」など)は、エラーが出ないことだけでなく表示自体をアサートする
- 権限のテストは、許可される側だけでなく拒否される側も書く
- バリデーション失敗のテストは、データが保存されていないことも確認する
- 同じ修正を複数箇所に入れたら、テストも全箇所に対称に揃える
ルール化可能率96%の「兄弟箇所への修正漏れ」は、不具合をパターンとして修正したのに、同じ構造を持つ別のメソッドや画面への展開を忘れる、という指摘で、「修正後に同構造をgrepで洗い出す」という1行にルール化できる。
断っておくと、76%はサブエージェントが付けた分類フラグをそのまま集計した値で、355件すべての目視検証はしていない。
ただし反映対象にした上位カテゴリのルール文200行あまりには目を通しており、明らかな誤分類は見当たらなかった。
反映先の3層への振り分け
集計結果をそのままCLAUDE.mdに追記したくなるが、そうするわけにはいかない。
CLAUDE.mdは毎セッション全文がコンテキストに載るため、1行増やすごとにすべての作業のコストが上がる。
今回は3層に振り分けた。
- PR作成前チェックリスト(新設、常時ロード):横展開、テスト観点、ドキュメント同期、ブランチ運用、後片付けの確認項目を1ファイルに集約する。CLAUDE.mdには「PR作成直前に必ず通す」という実行指示を1行だけ書く
-
paths付きルールファイル(新設):テスト観点の詳細は
.claude/rules/testing-guidelines.mdに置き、frontmatterのpaths: tests/**/*.phpでテスト編集時だけロードさせる -
既存ルールファイルへの追記:フレームワーク固有の罠(Livewireの
#[Computed]をメソッド呼び出しで参照するとメモ化が効かない、など)は、既存のコーディング規約ファイルへ数行ずつ追記する
チェックリストに実行タイミングを付けた理由
集計の途中で気づいたことがある。
ランキングそのものより、こちらの方が役に立った。
ブランチ運用とリリースノートに関する指摘は、運用ルールがCLAUDE.mdに明文化されているにもかかわらず26件のPRで出ており、直近のPRでもまだ出ていた。
ルールが読まれていないのではない。
CLAUDE.mdは毎セッション必ずコンテキストに載るため、届いていないという説明は成り立たない。
実装中の判断には反映されているのに、PRを作成する瞬間に差分と照合されていない、と見るのが自然だ。
だからチェックリストには、「PR作成直前に差分と突き合わせる」という実行タイミングを明記した。
LLMに向けたルールは内容と同じくらい適用タイミングの指定が効く、というのがこの26件から引き出した結論だ。
限界と効果測定
残り24%の指摘は、そのPR固有の文脈がないと気づけないものだった。
仕様理解の誤りや設計判断への指摘はルールでは防げないため、そこは自動レビューの仕事として残る。
info(参考情報)レベルの指摘までルール化するのも避けた。
ブロッカーにならない指摘を規則にしても、先に述べた「1行増やすごとに全作業のコストが上がる」問題が積み上がるだけになる。
今回は深刻度で足切りし、繰り返し性の高いものだけを反映した。
効果はこれから測る。
反映後のPRでカテゴリ別の指摘件数を追えば、どのルールが効いてどれが空振りかの見当がつく。
減らないカテゴリがあれば、次はルールの文面ではなくレビュー側のプロンプトを疑う。
まとめ
同じことを試すなら、最初の一歩は収集コマンド1本でいい。
分類まで自動化しなくても、自分のリポジトリの指摘を眺めるだけで、何が繰り返されているかはだいたい見える。
反映の効果はこれから数十PR分の指摘件数で測り、動きが見えたら続報を書く。



