diff-review: 並列多観点 diff レビュー
フローは 3 段階: 1. 差分収集(決定論)→ 2. レンズごとに diff-reviewer agent を並列起動 → 3. 統合報告。
レビューは読み取り専用。修正・コミットはこのスキルの範囲外で、ユーザーの指示があってから別途行う。
1. 差分収集
以下、<SKILL_DIR> はこのスキルの base directory(スキル起動時に表示される絶対パス)を指す。
agent への prompt に埋め込むときは必ず実際の絶対パスに展開する(<SKILL_DIR> のまま渡さない)。
<SKILL_DIR>/scripts/collect-diff.sh manifest [<base-ref>] を実行する。
- 出力: レビュー範囲(base / head の sha)・変更ファイルに適用される規約(CONVENTIONS 節)・コミット一覧と統計・未コミット変更・未追跡ファイル・除外ファイル(lockfile 等は統計のみ)。総変更が小さければ全文 diff も同梱される
NO_CHANGES が返ったら「レビュー対象の変更がない」と報告して終了する
- ユーザーがコミット範囲・ファイル等を指定した場合は base-ref 引数で範囲を合わせる
GROUND_TRUTH 節(あるときだけ現れる)
リポジトリに仕様・基本設計・テストケース(docs/dev/<対象>/spec.md 等)、規定パス外の仕様らしきファイル、
またはタスク境界ファイル(.claude/task-boundary.json)があると、manifest に == GROUND_TRUTH == 節が追記される。
無いリポジトリでは節ごと出ず、以降の分岐もすべて発火しない(従来動作)。
節は 3 系統を区別して出す:
| 節の内容 | 扱い |
|---|
対象: <名前> + 規定パス / 明示指定(DIFF_REVIEW_GROUND_TRUTH) | 確定。そのまま判断基準に使う |
候補(規定パス外...) | 未確定。採否をユーザーに確認するまで判断基準に使わない(次項) |
タスク境界ファイル: ... | 各指摘にスコープ分類(境界内 / 境界外)を付けさせる |
確定分が 1 つ以上あるとき、全レンズの prompt に共通ガードを追加し、spec レンズを既定に昇格する。
確定分が無ければ従来どおり(共通ガードなし・既定レンズは design / test / yagni・全指摘を境界内として扱う)。
CONVENTIONS 節(常に現れる)
manifest には == CONVENTIONS == 節が常に付く。変更ファイル(追加・変更 + 未追跡。削除は除く)に
適用されるリポジトリ内のコーディング規約を scripts/collect_conventions.py が決定論で列挙したもので(実行は
scripts/run-python.sh 経由。uv があれば pyproject.toml の依存を解決した venv、無ければ PATH の python3)、
全レンズにレビューの第 1 基準として渡す。
探索対象は .claude/rules/**/*.md(frontmatter paths: を変更ファイルに照合。paths 無しは常時適用。
変更ファイルの祖先ディレクトリ配下の .claude/rules/ も拾い、その paths はディレクトリ起点と root 起点の
両方で照合する)、変更ファイルの祖先ディレクトリの CLAUDE.md / AGENTS.md(CLAUDE.local.md は除外。
symlink は 1 件にまとめる)、CONTRIBUTING.md(ルート・.github/・docs/)、環境変数 DIFF_REVIEW_CONVENTIONS
の明示指定。
サブエージェントに自動注入されるのはルート CLAUDE.md だけで、.claude/rules/ の paths 発火やサブディレクトリ
CLAUDE.md の継承は文書化されていない(推測)。節に載った規約は Read しない限り reviewer の文脈に無い。
| 行 | 意味 |
|---|
status: found | 一致した規約がある。[root, injected] 以外は reviewer が全件 Read する(上限 20 件) |
status: none | 決定論探索で規約が見つからなかった。「無い」ことも reviewer に伝える(節は省略しない) |
status: explicit-only | DIFF_REVIEW_CONVENTIONS に none が含まれ自動探索を停止した。明示分だけ載る |
status: unavailable | uv も python3 も無い・スクリプト失敗・30 秒の打ち切り(DIFF_REVIEW_CONVENTIONS_TIMEOUT で変更可)で生成できなかった。規約照合が行われていない旨を統合報告に 1 行残す |
lint: ... | 機械 lint / formatter 設定の存在。書式系(空白・改行・import 順・lint が検出する命名規則)は指摘しない |
[paths: ...] [paths: ... @ <dir>/] [ancestor of ...] [root, injected] [contributing] [explicit: ...] | 各規約が載った根拠(@ <dir>/ はサブディレクトリ配下の .claude/rules/)。末尾の括弧は h1 / h2 見出し(200 文字まで) |
DIFF_REVIEW_CONVENTIONS の意味論(: 区切り。相対パスはリポジトリルート起点。存在しないパスは WARN して落とす):
- パスを列挙すると決定論探索の結果に追加される(
[explicit: DIFF_REVIEW_CONVENTIONS])。Explore が見つけた
規約文書の採用分を渡す用途(「候補の確定手順」)
- トークン
none を含めると自動探索(決定論 + Explore)を止め、明示列挙分だけを使う(none 単独なら空の一覧)。
個別除外の構文は無い
- 21 件以上は件数だけ示して省略される。絞りたいときは
none と必要なパスの明示列挙で対象を固定する
manifest はメインセッションで 1 回だけ実行し、出力を次段で各 agent の prompt にそのまま注入する(agent 側に再収集させない)。メインセッションで精読・分析はしない。
候補の確定手順(agent 起動前に必ず行う)
判断基準に載るべき文書が機械検出に載らないと、仕様に根拠のある実装を「過剰・不要」と誤指摘し(オーバーレビュー)、
規約に反する実装を見逃す。候補は 2 系統あり、どちらも未確定のまま agent に渡してはいけない:
- GROUND_TRUTH の
候補(規定パス外...)(manifest が機械検出)
- 規約文書の候補(Explore エージェントが探索。手順 2)
-
manifest を実行する(上記)
-
Explore エージェントを常に 1 体起動する(Agent ツール、subagent_type: Explore、read-only)。
例外として、呼び出し元(review-converge の本体・集約エージェント)が「規約の状態(固定した明示注入パス / なし)」を
prompt で渡してきたときは起動せず、渡された状態をそのまま使う(GROUND_TRUTH の「確定内容をそのまま渡し、
候補が出ても自分で判断しない」と同じ規則。確定済みの場面で再探索すると、確認できない候補を抱えたまま周回する)。
DIFF_REVIEW_CONVENTIONS に none が含まれるときも起動しない。指示文は一般名で書く:
探索範囲: docs/** / README* / ADR ディレクトリ(adr / adrs / decisions 等)/ .github/*.md
除外: 仕様書・設計書・テストケース(GROUND_TRUTH の領分)、議事録、戦略・ロードマップ文書、
tmp-agents/、node_modules/、生成物、および決定論で既に拾った次のファイル: <CONVENTIONS 節のパス一覧>
探すもの: コーディング規約・命名規則・レイヤー責務・テスト方針・エラー表現方針を「規範として」述べた文書
絞り込み: 変更ファイル <一覧、または言語・ディレクトリ> に関係するものだけ。上限 10 件
返却: 「パス + 規範文書と判断した理由 1 行」の一覧。無ければ「候補なし」
-
候補(GROUND_TRUTH・規約のどちらか、または両方)があれば、1 回の AskUserQuestion で採否を確認する
(片方しか無ければその分だけ)。系統ごとの選択肢は「列挙された候補を使う(推奨)」「一部だけ使う(パスを指定)」
「使わない」。質問の要点と候補パスはメッセージ本文にも列挙する。
ユーザーが依頼文でパスを既に示している場合(@path/to/spec.md 等)は、その系統の確認を省略して確定として扱う
-
採用分を環境変数に載せて manifest を再実行する(: 区切りで複数指定可。相対パスはリポジトリルート起点):
DIFF_REVIEW_GROUND_TRUTH="<採用した仕様書>" DIFF_REVIEW_CONVENTIONS="<採用した規約文書>" \
<SKILL_DIR>/scripts/collect-diff.sh manifest [<base-ref>]
再実行後、仕様書は「明示指定」として確定側に並び共通ガードと spec レンズ昇格が発火する。規約文書は
[explicit: DIFF_REVIEW_CONVENTIONS] として CONVENTIONS 節に並び、全レンズの Read 対象になる
-
「使わない」が選ばれた系統は候補を無視する(GROUND_TRUTH は確定分が無いものとして従来動作、規約は決定論分のみ)
-
AskUserQuestion が使えない文脈(親エージェント配下のワーカー等)では候補を採用せず、統合報告の末尾に
「未確定の候補」として両系統のパスを列挙する(採否は親・ユーザーが決める)
2. レンズ決定と並列起動
モード決定(fix のみ / improvement)
レンズ決定の前にモードを決める。指摘には fix(diff 内で完結する修正)と improvement(既存コードへの改修提案)の 2 種があり(定義は diff-reviewer.md の kind 体系)、既定モードでは improvement を検出も報告もしない。依頼されていないリファクタ提案が後段の修正工程(review-converge 等)で PR に混入するのを構造的に防ぐためで、レビューの手抜きではない。
- improvement モードの発火条件は起動引数のみ:
/diff-review --improvement のように引数 --improvement が明示されたときだけ有効化する。improvement は修正範囲を diff の外へ拡大させる種類のレビューなので、切替は曖昧さの余地が無い引数に限定する
- 自然文の依頼(「改善提案も出して」「リファクタ観点で」等)では勝手に有効化しない。改善提案を求められていると解釈できる場合は、AskUserQuestion で「
--improvement 付き実行に切り替えるか」を確認し(質問の要点と選択肢は本文にも書く)、承認されたときのみ有効化する。曖昧なら既定のまま実行し、統合報告の末尾に「改善提案は /diff-review --improvement で別途実行できる」と 1 行添える
- 有効化したら全 agent の prompt の「モード」行を
improvement にする(モードはレンズと独立で、全レンズ共通)。それ以外は常に既定(fix のみ)
レンズ registry
- 既定(無指定時に適用):
design(設計・可読性)/ test(テスト設計・網羅性)/ yagni(アンチ・オーバーエンジニアリング。過剰実装・混入の検出)
- 条件付き既定:
spec(仕様適合・あるべき変更の欠落)— manifest に GROUND_TRUTH 節があるときのみ既定に昇格する。節が無ければオプション扱い(明示指定時のみ)
- オプション(指定時のみ適用):
security(セキュリティ / OWASP Top 10)/ docs(ドキュメント整合)/ performance(計算量・N+1・データサイズ)/ compat(互換性・移行安全)/ ops(可観測性・運用)/ concurrency(並行性)
- キーワード対応: 「設計」「可読性」→
design、「テスト」→ test、「YAGNI」「過剰」「オーバーエンジニアリング」「最小差分」→ yagni、「仕様」「要求」「REQ」→ spec、「セキュリティ」「security」→ security、「ドキュメント」「docs」→ docs、「パフォーマンス」「性能」→ performance、「互換」「マイグレーション」「移行」→ compat、「運用」「ログ」「可観測性」→ ops、「並行」「排他」「race」→ concurrency
適用ルール:
- 追加指定(例: 「〇〇観点も見て」): 既定レンズに加えて指定レンズを適用する
- 絞り込み指定(例: 「〇〇だけ」「〇〇のみで」): 指定レンズだけを適用する。この場合
spec の昇格も行わない(ユーザーの明示指定が優先)
- registry に無い観点を指定されたら、その観点名で agent を 1 体起動し一般知識でレビューさせる
並列起動
適用レンズ 1 つにつき diff-reviewer agent を 1 体、1 メッセージ内で同時に起動する(直列にしない)。
起動方式(厳守): Agent ツールを 1 メッセージ内に複数並べて直接起動し、各 agent の報告を完了通知で受け取る。
- サブエージェントはバックグラウンドで走り、結果は完了通知として後のターンに届く。起動した
メッセージの中では結果は揃わない。全レンズの完了通知が届いてから統合に入る
- 待機は通知による起床で行う。
sleep のポーリングで待たない(完了通知が届けばハーネスが再開させる)
- エージェントチームを構成しない。team として起動する引数(
team_name / name など、agent に識別名を
与えるもの)は使わない。name を付けた agent は teammate になり、最終回答が完了通知ではなく idle 通知に
なってターン境界まで届かない。通知の取りこぼしでレビューを完走しても結果が親に届かず空転する事故が
実際に起きている
- 「並列で走らせる」と「チームを組む」は別物。並列は必須、チーム化は禁止。この 2 つは両立する
- 完了通知が届いたのに報告本文が無い agent の扱いは、「3. 統合報告」の受領突合に従う
各 agent の prompt には必ず以下を含める:
レンズ: <lens>
モード: <既定(fix のみ) または improvement(改善提案も検出する)>
基準ファイル: <SKILL_DIR>/references/<lens>.md(レビュー開始前に Read すること)
ユーザーの依頼: <元の依頼の要約(範囲・重点の指定があれば含める)>
以下は収集済みの差分 manifest。manifest の再実行はしない。全文 diff が必要な単位のみ
<SKILL_DIR>/scripts/collect-diff.sh の commit / worktree / cumulative で取得する:
<manifest の出力をそのまま貼る>
manifest を全 agent に同一内容で渡すことで、レビュー範囲のスナップショットを固定する。
GROUND_TRUTH 節があるときの追記(全レンズ共通)
manifest に == GROUND_TRUTH == 節があるときに限り、全レンズの prompt に次のブロックを追記する
(節が無いときは一切追記しない):
共通ガード(グラウンドトゥルース検出時):
- 仕様・テストケースに根拠のある実装を「過剰・不要」と指摘しない
- 削除・簡略化を提案する場合は、manifest の GROUND_TRUTH 節に挙がった spec.md / basic-design.md /
test-case.md を確認し、REQ-# / CASE-# に抵触しないことを確かめてから指摘する
- 確認できない場合は指摘を出さないか [PLAUSIBLE] を付けて残す
- 仕様・計画に無い構造変更・追加実装を修正方針として提案しない(該当する提案は improvement 分類。
既定モードでは報告しない)
- 仕様の解釈変更・検査の緩和を要する指摘は、修正案ではなく [要仕様裁定] を付けた質問として報告する
節にタスク境界ファイル(.claude/task-boundary.json)の内容が含まれているときは、さらに次を追記する:
スコープ分類(タスク境界ファイル検出時):
各指摘に「タスク境界内」「境界外」の分類を付ける。判定は境界ファイルの allow glob と
指摘対象のファイルパスの照合による。allow に一致すれば境界内、一致しなければ境界外
(別タスク・別 PR で対応すべき指摘)。境界外の指摘は消さずに分類だけ付けて報告する。
境界ファイルが無ければこの追記はせず、全指摘を境界内として扱う(従来動作)。
CONVENTIONS が found のときの追記(全レンズ共通)
manifest の CONVENTIONS 節が status: found のとき、全レンズの prompt に次のブロックを追記する
(none・一覧が空の explicit-only・unavailable のときは追記しない):
規約(CONVENTIONS 節が found):
- 節に載った規約のうち [root, injected] 以外を全件 Read し、規約との照合を第 1 パスにする
(候補の洗い出しの前に規約を読み、違反を先に列挙する)
- 規約違反の指摘は文頭に「規約: <path>#<見出し>」の引用を必須にする。規約に明記されていない
好みは規約違反として出さない
- severity は明文規約への違反を want+ 基本、違反が現に誤読・障害・退行を生んでいれば must。
規約違反を理由に一段引き上げない
- diff 内で直せる違反は fix、既存コードの構造を変えさせる規約指摘は improvement
- lint 行に設定がある言語の書式系(空白・改行・import 順・lint が検出する命名規則)は指摘しない
- 規約の引用が「この変更に対して具体的に要求している」と読めるときだけ、その実装に対する
yagni の「過剰」・design の「乖離」・spec の「欠落」を出さない。一般原則(高凝集・低結合・
一貫性・保守性)の引用ではガードしない
3. 統合報告
受領突合(統合前に必ず行う)
統合に入る前に、起動したレンズ数と結果を受け取ったレンズ数を突合する。
突合は全レンズの完了通知が届いてから行う。完了通知に報告本文が載る前提なので、欠落は必ず検知できる:
- 起動したレンズを列挙する(例:
design / test / spec の 3 体)
- 各レンズについて、severity セクションを備えた報告本文を受け取れたかを確認する。
未受領とは「完了通知は届いたが、severity セクション付きの本文が無い」ものだけを指す
(完了通知がまだ届いていないレンズは実行中であり、未受領ではない。通知を待つ)
- 未受領のレンズがあれば、待機せずそのレンズだけ再起動する(同じ manifest・同じ prompt を渡す)。
「待機せず」が当てはまるのはこの未受領の場合だけ
- 再起動しても受け取れなかったレンズは「結果なし」として確定し、統合報告の冒頭に
どのレンズが欠落したかを明記する。欠落を黙って省略してはいけない
(「3 レンズで見た」と読める報告を 2 レンズ分の内容で出すことが最も危険)
再起動は 1 レンズにつき 1 回まで。2 回目も駄目なら欠落として報告し、ユーザーの判断を仰ぐ。
統合
全レンズの受領を確認後、報告を 1 つに統合し、diff-reviewer の報告形式(must / want+ / want / nit の severity 順)で出力する:
- 冒頭に「対象: <範囲> / レンズ: <適用レンズ(受領/起動 の内訳)> / モード: <既定(fix のみ) または improvement> / 規約: <found(N 件)・none・explicit-only・unavailable>」を明記する。
unavailable なら「規約との照合は行われていない」と 1 行添える
- 同一
ファイルパス:行番号 に複数レンズの指摘が重なったら 1 件に統合する。severity は最も重いものを採用し、レンズタグを併記する(例: [design][security])
- 同一 severity 内では規約違反(
規約: <path>#<見出し> の引用付き)を先頭に並べる
- 同一箇所で yagni の「削れ」指摘と規約の「足せ」指摘が衝突したら、yagni 側を先に置き、規約側には「yagni 指摘の解消後に再評価」と注記する(削られるコードの上に規約対応を積ませない)
[PLAUSIBLE] / [STRUCTURAL] マークは統合後も維持する(両方付くときは [PLAUSIBLE][STRUCTURAL] の順)。[STRUCTURAL] は構造追加(新規ファイル / 型・抽象 / 層・間接化 / 単一使用の一般化)を伴う fix 指摘のマークで(定義は diff-reviewer.md)、後段の収束ループが機械修正の例外として扱う。剥がすと例外が働かない
- improvement モードのときのみ、fix の severity 節の後に「改善提案(別 PR 候補)」節を置き、improvement の指摘を severity 順で並べる(kind タグ・採用時の影響範囲 1 文・境界タグを各件に維持する)。既定モードではこの節を出さない
- 全レンズで指摘ゼロなら「指摘なし」と明言する。褒め言葉・社交辞令は書かない
タスク境界ファイルが manifest にあった場合、fix の指摘をスコープで二分する
(improvement は improvement モード時のみ存在し、どのみち「別 PR 候補」なので二分しない。境界タグの併記だけ維持する):
- 「タスク境界内」節に境界内の指摘を severity 順で並べる(この節が主たる修正対象)
- 「境界外(別タスク・別 PR で対応)」節に境界外の指摘を並べ、各件に境界外と判定した理由
(どの allow glob にも一致しないパスであること)を 1 文で添える
- 分類が agent 間で割れた指摘は境界内側に寄せる(見落としを避ける)
境界ファイルが無ければ二分せず、従来どおり severity 順の 1 本の並びで報告する。