0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「また同じ指摘を受けた」を減らす:Claude Codeと実際のレビュー履歴で作る「レビュー番人」

0
Posted at

2026年上半期、Claude Codeを使ったコードレビューの進め方を大きく変えました。汎用的なAIレビューから、リポジトリ固有のレビュー基準を明示的に持たせる方向へ寄せた、その振り返りです。

AIに現在の差分を渡してコードレビューを依頼すると、命名、複雑度、例外処理など、一般的な観点はかなり拾ってくれます。

一方、実際のプルリクエストで繰り返し指摘されるのは、もっとリポジトリ固有の問題です。

  • 参照先のクラスが存在しない:deep()セレクタ
  • 複数コンポーネントへ重複して追加されたcomposableの接続処理
  • 非自明な分岐を持つ新規ヘルパー関数に対するテスト不足

こうした指摘は、一般的なベストプラクティスだけでは再現できません。
過去のプルリクエストで積み重ねられた、そのリポジトリ固有の基準が必要です。

そこで、実際のレビューコメントから再利用可能なルールを抽出し、現在の差分へ適用する「レビュー番人」をClaude Codeのサブエージェントとして作りました。

レビュー番人はプルリクエスト直前だけに実行するものではありません。
1つの実装ブロックが完了するたびに実行し、同じ設計を後続のコードへ広げる前に問題を止めます。

要点

  • 過去のプルリクエストで繰り返された指摘を抽出する
  • 指摘を、機械的に確認できるルールへ変換する
  • Claude Codeのサブエージェントとしてレビュー番人を用意する
  • コンポーネント、composable、テストなど、作業の区切りごとに現在の差分を確認する
  • レビュー番人専用の実行ログは作らない。レビュー基準が変化したときだけルールを再生成する

レビュー番人は、モデルを追加学習する仕組みではありません。
過去のレビューから作った明示的なルールを、現在の作業コンテキストへ適用する仕組みです。

汎用AIレビューでは拾いきれないもの

一般的なAIコードレビューは、コード単体の問題を探すことには向いています。

しかし、実際のレビューでは次のような質問が繰り返されます。

この:deep()の対象クラスは、現在もテンプレート内に存在しますか?

ヘッダー、行、本文で同じ処理を持っています。共通化できませんか?

このヘルパーは分岐を持っていますが、テストを追加しなくてよいですか?

重要なのは、誰が指摘したかではありません。
同じリポジトリで何度も現れた点です。

そのため、レビュー番人ではレビュアー個人のプロファイルを作りません。
複数のレビューから、リポジトリ全体で再利用できる観点だけを抽出します。

実際のレビューコメント レビュー番人のルール
「このクラスはまだ存在しますか?」 変更された:deep()の対象クラスを検索する
「共通composableに切り出せませんか?」 同じ接続処理が複数箇所へ追加されていないか確認する
「この分岐のテストがありません」 非自明な分岐を持つ新規ヘルパーとテストを対応付ける

全体の流れ

レビュー番人には、生成と実行の2つのフェーズがあります。

  • 生成フェーズ:過去のプルリクエストで繰り返された指摘からルールを抽出する
  • 実行フェーズ:生成済みルールと現在の差分だけを使う

過去のレビュー履歴を参照するのは、生成フェーズだけです。日々の実行時には履歴を使いません。

1. 実際のレビューコメントを収集する

GitHub CLIを使う場合、インラインレビューコメントは次のようにJSON Lines形式で取得できます。

mkdir -p .repository-review/.tmp

gh pr list \
  --state merged \
  --limit 100 \
  --json number \
  --jq '.[].number' |
while IFS= read -r pr_number; do
  gh api --paginate \
    "repos/{owner}/{repo}/pulls/${pr_number}/comments" |
    jq -c \
      --arg pr_number "$pr_number" \
      '.[] | {
        pull_request: ($pr_number | tonumber),
        path,
        start_line,
        line,
        diff_hunk,
        body,
        created_at
      }'
done > .repository-review/.tmp/review-comments.jsonl

この例は、直近100件のマージ済みプルリクエストに付いたインラインコメントだけを対象にしています。
レビュー本文や通常のプルリクエストコメントも基準として使う場合は、別途取得します。

.repository-review/.tmp/.gitignoreへ追加し、rules.mdを生成した後に削除します。
レビューコメントは非公開情報を含む可能性があるため、入力データをリポジトリへコミットしません。

収集範囲は広ければよいわけではありません。
現在の設計やディレクトリ構成と大きく異なる古いプルリクエストを混ぜると、すでに無効なルールまで生成されます。

私は次の条件で対象を絞ります。

  • 現在も保守されている領域のプルリクエスト
  • 最近の設計方針を反映しているプルリクエスト
  • 複数回レビューされている機能領域
  • 単純な誤字修正や仕様確認だけのコメントを除外したもの
  • Botによる自動コメントを除外したもの

2. レビューコメントをルールへ変換する

収集したコメントを、そのままレビュー番人へ渡してはいけません。

レビューコメントには、次のようなものが混在しています。

  • そのプルリクエストだけに必要な修正
  • 仕様確認
  • 好みの範囲にとどまる指摘
  • すでに解決済みの設計方針
  • 他の変更でも再利用できる指摘

レビュー番人へ残すのは、最後の種類だけです。

ルール生成時には、Claude Codeへ次のような指示を渡します。

このファイルには、同じリポジトリのプルリクエストに付いた実際のレビューコメントが入っています。

目的は、現在の差分へ繰り返し適用できるレビュー番人を作ることです。

次の条件を満たす指摘だけを抽出してください。

- 複数のプルリクエストで繰り返されている
- 特定の担当者の好みではなく、適用範囲を明示できる
- 差分、変更ファイル、関連テストから確認できる
- 問題を報告する条件と、報告しない例外を説明できる
- 修正方法を具体的に示せる

次の内容は除外してください。

- 単発の仕様変更
- 単なる質問
- 命名や書式だけの軽微な好み
- 現在のコードベースでは無効になった指摘
- 実行時の仕様情報がなければ判断できない内容

各ルールには、以下を含めてください。

- 対象
- 確認する条件
- 確認方法
- 指摘する条件
- 指摘しない条件
- 必要な根拠
- 修正案

生成結果は、人が一度確認します。
AIによる分類そのものを信頼するのではなく、レビューコメントを整理する作業へ使う形です。

3. ルールをMarkdownで管理する

構成は最小限にしています。

.repository-review/
└── rules.md

.claude/
└── agents/
    └── repository-review.md

rules.mdには、リポジトリ固有の観点だけを書きます。
ESLint、型チェック、テストランナーで検出できる内容は、既存ツールへ任せます。

例1:存在しないクラスを参照する:deep()

## R-001: 参照先が存在しない`:deep()`セレクタ

- 対象: `**/*.vue`
- 確認する条件:
  - `:deep(.<class-name>)`が追加または変更されたとき
- 確認方法:
  - 対象コンポーネントのテンプレートを検索する
  - 子コンポーネントが出力するクラスを確認する
  - 動的に付与されるクラスがないか確認する
- 指摘する条件:
  - 対象クラスの出所を確認できない
- 指摘しない条件:
  - 子コンポーネントが対象クラスを出力する
  - ライブラリまたは実行時処理がクラスを付与する
- 必要な根拠:
  - セレクタのファイルと行番号
  - 対象クラスを検索した範囲
- 修正案:
  - 不要なセレクタを削除する
  - 必要なセレクタなら、クラスの出所を確認できる実装または参照を示す

例2:重複したcomposableの接続処理

## R-002: composableの接続処理の重複

- 対象: Vueコンポーネントとcomposable
- 確認する条件:
  - 同じcomposableの呼び出し、引数変換、返り値の再公開が追加されたとき
- 確認方法:
  - 変更ファイルと近接コンポーネントを比較する
- 指摘する条件:
  - 意味的に同一の接続処理が複数箇所へ追加されている
- 指摘しない条件:
  - UI固有の変換やライフサイクル差分がある
  - 共通化によって依存関係が不明瞭になる
- 必要な根拠:
  - 重複している箇所を2つ以上示す
- 修正案:
  - 共通composableまたは小さなアダプターへ抽出する

例3:新規ヘルパー関数のテスト不足

## R-003: 非自明なヘルパー関数のテスト不足

- 対象: 新規または変更されたヘルパー関数
- 確認する条件:
  - 条件分岐、境界値、代替処理を持つヘルパーが追加されたとき
- 確認方法:
  - 対応するテストファイルと既存テストケースを確認する
- 指摘する条件:
  - 主要分岐または境界値を検証するテストがない
- 指摘しない条件:
  - 単純な値の再公開だけを行う
  - 既存の統合テストで同じ振る舞いが明示的に検証されている
- 必要な根拠:
  - 未検証の分岐を示す
- 修正案:
  - 主要分岐と境界条件を含む最小限のテストを追加する

ルール名だけを書いても、AIは一般論を補ってしまいます。

指摘する条件指摘しない条件必要な根拠まで定義すると、もっともらしい誤検出を減らせます。

4. Claude Codeのサブエージェントを作る

.claude/agents/repository-review.mdを作成します。

---
name: repository-review
description: 実際のプルリクエストに付いたレビューから抽出したルールで、現在の作業ツリーを確認する
tools: Read, Grep, Glob, Bash
---

`.repository-review/rules.md`を読む。

`git status --short --untracked-files=all`を実行する。
追跡済みの変更は`git diff HEAD`で確認する。
`git status`に表示された未追跡ファイルも読む。
ルールの確認に必要な場合は、変更ファイル、関連テスト、近接実装も読む。

適用対象となる各ルールについて、次のいずれかを返す。

- `指摘あり`: 問題を示す十分な根拠がある
- `問題なし`: ルールを確認したが問題は見つからない
- `判定保留`: 利用できる情報だけでは判断できない

必須条件:

- `.repository-review/rules.md`に定義された、リポジトリ固有の観点だけを報告する
- 一般的な書式や命名に関する提案を追加しない
- ファイルパス、行番号、具体的な根拠を示す
- 各ルールに記載された例外を守る
- 確認できないコードや実行時の挙動を推測しない
- ルールを満たす最小の修正を提案する

出力形式:

## レビュー番人の確認結果

### 指摘あり

| ルール | 場所 | 根拠 | 最小の修正 |
|---|---|---|---|

### 問題なし

- 確認済みで問題がなかったルールを列挙する

### 判定保留

- 判断に必要な追加情報を説明する

なぜCLAUDE.mdではなくサブエージェントに分けるのか

CLAUDE.mdへ詳細なレビュー規則を直接書くと、レビュー以外の作業でもメイン会話へ読み込まれます。指摘する条件指摘しない条件必要な根拠まで常駐させると、トークンを継続的に消費し、別の作業でも過剰に発火しやすくなります。

サブエージェントなら、メイン会話とは独立したコンテキストで、必要なルールと現在の差分を読み込み、構造化された結果だけを返せます。ここで重要なのはCLAUDE.mdの読み込み仕様ではなく、詳細なレビュー規則を専用ファイルへ分離し、必要なタイミングだけ明示的に適用する点です。

実行時には、Claude Codeへ次のように依頼します。

repository-reviewサブエージェントを使って、現在の作業ツリーを確認してください。

レビュー番人は、通常のAIレビューを繰り返すものではありません。
rules.mdに定義された観点だけを確認します。

5. プルリクエスト直前ではなく、作業の区切りごとに実行する

レビュー番人をプルリクエスト直前に一度だけ実行すると、修正範囲が大きくなります。

たとえば、最初のコンポーネントで重複した実装を作り、そのまま5つのコンポーネントへ展開した後で指摘を受けると、すべてを戻す必要があります。

そのため、次のような区切りで実行します。

  • 1つのコンポーネント変更が完了したとき
  • composableの追加が完了したとき
  • 1つのユーザー操作を実装し、対応テストを書き終えたとき
  • リファクタリングの1段階が完了したとき
  • 同じ変更を別画面へ展開する直前

ここでいう「作業ブロック」は、コミット単位である必要はありません。
自分で1つの設計判断が完了したと認識できる単位です。

このタイミングで回すと、レビュー番人は「最終検査」ではなく「設計のガードレール」になります。

6. 実際の出力例

次のような変更があるとします。

 <template>
   <div class="table-row">
     <span>{{ label }}</span>
   </div>
 </template>
 
 <style scoped>
+:deep(.cell-pad) {
+  padding-inline: 8px;
+}
 </style>

対象クラスは、テンプレートにも子コンポーネントにも存在しません。

レビュー番人は次のように返します。

## レビュー番人の確認結果

### 指摘あり

| ルール | 場所 | 根拠 | 最小の修正 |
|---|---|---|---|
| R-001 | `TableRow.vue:18` | `.cell-pad`をテンプレート、子コンポーネント、変更ファイル内で確認できない | 不要な`:deep(.cell-pad)`を削除する |

### 問題なし

- R-002: composableの接続処理の重複は追加されていない
- R-003: 非自明な新規ヘルパー関数は追加されていない

### 判定保留

- なし

重要なのは、「このCSSは不要かもしれません」という曖昧な感想ではありません。

  • どのルールに違反したか
  • どこを確認したか
  • 何を根拠にしたか
  • 最小の修正は何か

この4点が揃っています。

7. レビュー番人専用の実行ログは作らない

レビュー番人の実行結果は、独自のログファイルとして保存していません。

目的は、レビュー番人側で履歴を蓄積することではなく、現在の作業を次へ進めてよいか確認することです。

現在の差分 + 生成済みルール → 今回の確認結果

ここで「保存しない」とは、レビュー番人用の履歴ファイルを別途作らないという意味です。
Claude Code自身が管理するセッション履歴やサブエージェントのトランスクリプトは別です。

毎回の結果を学習データとして蓄積する仕組みにすると、次の問題が生まれます。

  • 誤検出が新しいルールとして固定される
  • 実行回数に比例してノイズが増える
  • レビュー番人自体の保守が主目的になる
  • レビュアーや開発者の行動監視に見えやすい

ルールを更新するのは、次のような場合だけです。

  • 新しい種類の指摘が複数のプルリクエストで繰り返された
  • コードベースの設計方針が変わった
  • 既存ルールで同じ誤検出が続いた
  • 既存ルールが現在の構成に合わなくなった

このときは、最新のレビュー履歴を使ってレビュー番人を再生成または手動更新します。

8. 判定できないものは判定保留にする

すべてのレビュー指摘を自動化できるわけではありません。

特に、次の内容は差分だけでは判断できません。

  • 仕様書やデザイン意図が必要な判断
  • ブラウザ操作を伴う挙動
  • IME、スクロール、フォーカスなど実行時の問題
  • 将来のロードマップを前提にした設計判断
  • レビュアー間で意見が分かれている方針

無理に結論を出させると、AIは不足した情報を推測で埋めます。

そこでレビュー番人には判定保留を許可します。

### 判定保留

- R-004: IME入力中の挙動
  - 静的な差分だけでは確認できない
  - 対象ブラウザで`composition`イベントを操作する必要がある

「分からない」と返せることは、コードレビュー用エージェントでは重要な機能です。

9. ルール生成に使っていない過去のプルリクエストで評価する

レビュー番人を作った直後は、ルール生成に使っていない過去のプルリクエストで一度評価します。

同じプルリクエストのレビューコメントからルールを作り、そのプルリクエストで精度を測ると、評価対象の情報がルール側へ混入します。これでは、未知の変更に対して有効か判断できません。

手順は次の通りです。

  1. プルリクエストを時系列で、ルール生成用と評価用に分ける
  2. 生成用プルリクエストのレビューだけからrules.mdを作る
  3. 評価用プルリクエストのレビューコメントをエージェントへ渡さず、レビュー前の差分を用意する
  4. レビュー番人を実行する
  5. 実際のレビューコメントと比較する
  6. 誤検出、見逃し、判定保留を確認し、条件と例外を調整する

見るべき指標は、単純な検出数ではありません。

指標 確認すること
一致した指摘 実際のレビューと同じ問題を事前に見つけたか
誤検出 実際には問題でない箇所を指摘していないか
見逃し ルール化できる指摘を見逃していないか
判定保留 推測せず、正しく判定を保留できたか

高い検出率だけを目指すと、ノイズの多いレビュー番人になります。

実際に使い続けられるかどうかは、誤検出の少なさと根拠の明確さで決まります。

注意点

レビュアー個人を再現しない

レビュー番人の対象は、個人の「クセ」ではありません。
リポジトリで繰り返し確認されている技術的な観点です。

記事や共有資料では、レビュアー名、社内パス、実際のプルリクエスト番号、非公開コードを匿名化します。

静的解析と重複させない

静的解析で検出できる問題は、ESLintや型チェックへ追加した方が確実です。

レビュー番人に向いているのは、複数ファイルの関係や、リポジトリ固有の設計意図を確認するルールです。

すべてのプロジェクトに入れない

作業ブロックごとにサブエージェントを実行するため、実行のたびにトークンを消費します。これは、厳格なレビュー基準と、繰り返される固有の指摘が蓄積されているクリティカルな環境向けの仕組みです。数個のルールと既存の静的解析、型チェックで足りるプロジェクトには、導入コストが見合いません。

ルールを増やしすぎない

ルールを増やしても、精度が上がるとは限りません。
現在も繰り返し問題になる観点だけを残します。

AIの出力を承認条件にしない

レビュー番人は補助ツールです。
最終的な設計判断やレビュー承認を置き換えるものではありません。

参考

まとめ

一般的なAIコードレビューは、汎用的な問題を見つけます。

一方、実際のプルリクエストで繰り返し発生する摩擦は、そのリポジトリ固有のレビュー基準から生まれます。

レビュー番人では、過去の実レビューから再利用可能な観点を抽出し、明示的なルールとして管理します。
そして、プルリクエスト直前ではなく、実装の区切りごとに現在の差分へ適用します。

過去のレビュー履歴 → レビュー番人を生成
現在の差分 + レビュー番人 → 作業ブロックを確認

これにより、同じ指摘をプルリクエストまで持ち越しにくくなります。

レビューの時間を、過去に何度も確認した問題ではなく、今回の変更で本当に議論すべき設計へ使えるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?