| name | msg-review |
| description | msg-sys 通信基盤(常駐 Codex セッションとの Stop フック経由の非同期往復)の上で、
Codex とのレビュー依頼・所見受領・修正・完了判定を駆動する。3モード(依頼/受信/再開)を持つ。
依頼モードのトリガー句: "msg-reviewでレビュー依頼", "Codexとレビュー往復したい", "常駐Codexにレビューを依頼",
"msg-reviewを実行して", "Codexセッションにコードレビューを頼みたい"。
受信モードの起動契機(トリガー句ではなくメッセージ本文の形式で成立): Stop フックが差し戻した
メッセージ本文の先頭が `[msg-review] <種別> review_id=<review_id> round=<n>` である。
再開モードのトリガー句: "msg-reviewを再開したい", "レビューの往復上限到達通知が来た、状況を確認して",
"review_idの未解決所見を要約して", "msg-reviewの続きを確認したい"。
|
| user-invocable | true |
| argument-hint | <code|design|requirement|plan|uxui|generic> [--diff|--branch|--files a.md,b.py,...] |
| allowed-tools | Skill, Read, Write, Bash, Monitor, AskUserQuestion |
このスキルは、常駐 Codex セッションとの msg-sys 経由レビュー往復(依頼の組み立てと送信・受領した所見の評価と修正と再依頼・完了判定と再開時の要約報告)のみを行う。親が依頼している他の作業(実装・別レビュー経路 /forge:review(Skill ツールで起動される別スキル)の起動等)を引き継いではならない。
このスキル自身を Skill ツールで再起動しない(自己再帰禁止)。/forge:query-db-rules / /forge:query-db-specs は Skill ツールで起動する別スキルであり、依頼モードから呼んでよい。
概要
/forge:msg-review は msg-sys(通信路のみを提供)の上に成り立つレビューオーケストレーションである。単一ターンで完結せず、3つの動作モードを持つ(DES-045 §2.1)。
| モード | 起動契機 | このセクションへ |
|---|
| 依頼モード | 利用者による /forge:msg-review <種別> ... の明示起動 | 「依頼モード」 |
| 受信モード | 差し戻されたメッセージ本文の先頭に [msg-review] <種別> review_id=<review_id> round=<n> がある | 「受信モード」 |
| 再開モード | 往復上限到達の OS 通知を受けた利用者が状況確認・再開を明示指示したターン | 「再開モード」 |
受信モードの成立根拠: 依頼・返信メッセージ本文には常にこのプロトコルヘッダを含める。文脈が失われたターン(セッション再開・compaction 後等)でも、description に記載したヘッダ文字列をトリガーにこの SKILL.md を再読すれば受信モード手順へ復帰できる。
再開モードの成立根拠: msg-sys は往復上限到達時に対象メッセージを配信しない(受信モードの起動契機を持たない)。代わりに、上限到達を告げる OS 通知を受けた利用者が状況確認・再開を明示指示すること自体を起動契機とする。
コマンド構文
/forge:msg-review <種別> [--diff | --branch | --files a.md,b.py,...] [--interactive | --auto-critical | --auto]
| 軸 | 値 | 既定値 | 意味 |
|---|
| 種別(位置引数) | code / design / requirement / plan / uxui / generic | (必須) | /forge:review と同一の位置引数。参照する review_criteria の選択に使う |
| 対象軸 | --diff / --branch / --files | --diff | 未 commit 差分 / base ブランチ分岐以降の全変更 / 明示指定ファイル群 |
| 介入軸 | --interactive / --auto-critical / --auto | --interactive | 🔴🟡 を自動修正・🟢 は対象外(暫定的に --auto と同じ、DES-046) / 🔴 のみ自動修正 / 🔴🟡 を自動修正 |
引数解釈は AI が自然言語混在を許容して直接行う(リジッドなパーサーは使わない。docs/rules/implementation_guidelines.md)。種別・対象が不足・曖昧な場合は AskUserQuestion で補完する。
対象軸の二重指定はエラー終了する(--diff と --files を同時指定等)。既定動作を推定できないため、依頼を送信せず利用者へ再入力を促して終了する。
介入軸(DES-046): --auto-critical/--auto は所見の重大度に応じた自動修正範囲を実際に区別する(受信モード Step 2a 参照)。--interactive(既定・介入軸未指定時も同じ)は暫定的に --auto と同じ振り分けを適用する(ユーザー指示・ユーザー責任 [2026-07-19]。本来の /forge:present-findings 相当の段階的提示は次回以降に実装予定。DES-046 §1「暫定運用」・§2「やらないこと」参照。この仕組みを早期に多くのプロジェクトで実運用し問題を洗い出すための暫定措置であり、/forge:review の --interactive(本来は1件ずつ確認)とは異なる)。
非対応軸の警告付き続行(FNC-006・DES-045 §3.2): --codex / --claude、および将来 DROP されたフラグを検出した場合、「msg-review はエンジン軸を持たない(当該フラグは無視して続行する)」旨を警告したうえで、当該フラグを無視し既定動作で続行する。黙殺しない。エラー終了にしない理由: /forge:review を発行する既存の呼び出し元(/forge:start-implement の Phase 5 等)が --auto 付きで起動するため、rename 差し替えがエラー終了では成立しない。
引数解釈結果の定型出力 [MANDATORY]
依頼モードは送信前に必ず以下の定型表を出力する。「無視したフラグ」欄は無視したフラグが無い場合も省略せず「なし」と明示する(自由記述の注意書きに置き換えない)。
### 引数解釈結果
| 項目 | 値 |
| -------------- | ------------------------------ |
| レビュー種別 | <code|design|...> |
| 対象軸 | <diff|branch|files> |
| 対象ファイル | <一覧、または「なし」> |
| 介入軸 | <interactive|auto-critical|auto> |
| 無視したフラグ | <一覧、または「なし」> |
依頼モード(DES-045 §2.1・§3.2〜§3.5、UC-1・UC-5)
利用者が前節のコマンド構文で /forge:msg-review <種別> ... を明示起動したターンで実行する。
Step 1: 引数解釈
種別・対象軸・介入軸を解釈し、対象軸二重指定なら Step を進めずエラー終了する。介入軸未指定時は --interactive を既定値とする。非対応フラグ(エンジン軸等)があれば無視して続行する(上記「非対応軸の警告付き続行」)。確定した介入軸の値は受信モード Step 2a まで保持する。--interactive・介入軸未指定時は、受信モード Step 2a では暫定的に --auto として扱う(DES-046 §1「暫定運用」。ユーザー指示・ユーザー責任)。
Step 1.5: Codex 側フックの自己修復(DES-045 §3.8 補足)[MANDATORY]
Codex は Claude Code のプラグイン hooks 自動登録機構を持たず、.codex/hooks.json(Codex CLI 自身が固定のプロジェクトルート直下でのみ読む設定)の登録コマンドが指すスクリプトパスは常に静的な文字列である。このパスが実在しないまま Codex の Stop フックが発火すると、コマンド自体が実行に失敗し、Codex はそれを解消されるまでブロックし続ける無限ループに陥る(実インシデントで確認済み。単なる理論上のリスクではない)。これを避けるため、依頼を送信する前に毎回次を実行し、symlink・登録内容を自己修復する:
python3 "${CLAUDE_SKILL_DIR}/scripts/ensure_codex_hook.py" \
--project-root "$(git rev-parse --show-toplevel)" \
--plugin-msg-sys-dir "${CLAUDE_PLUGIN_ROOT}/scripts/msg-sys"
このスクリプトは <project_root>/.codex/msg-sys/scripts を、現在ロードされている forge プラグイン自身の scripts/msg-sys/ への symlink にする(コピーではない。プラグインが更新されても再インストール作業なしで常に最新版を参照する)。あわせて <project_root>/.codex/hooks.json の Stop フックに、この symlink 経由の git-root-relative パス($(git rev-parse --show-toplevel)/.codex/msg-sys/scripts/hooks/check_inbox.py)を指すエントリが無い・古ければ追加・修復する(既存の無関係な Stop フックは変更しない)。
symlink.status が "conflict" の場合(symlink であるべき場所に人間由来の実ファイル・ディレクトリが存在する)は書き換えを行わないため、その旨を利用者に報告し、手動での確認を促す(自動修復を諦めるのみで、依頼の送信自体は Step 2 の前提検査結果に従う)。hooks_json.status が "error" の場合(既存 .codex/hooks.json が壊れた JSON 等)も同様に書き換えず報告する。
Step 2: 前提検査(UC-5, DES-045 §3.5)
msg-sys 側の自己診断 CLI を Bash subprocess として呼ぶ:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/msg-sys/check_setup.py" [--project-root <path>]
出力 JSON の status が error の場合、依頼を送信せず終了する(fail closed)。checks の中で ok: false の項目を利用者に提示し、対処を具体的に案内する。claude_plugin_hook_registration(forge プラグイン同梱の hooks/hooks.json。Claude Code のプラグイン hooks 自動登録機構により、forge プラグイン導入だけで有効化される。手動での .claude/settings.json 編集は不要)が失敗している場合は forge プラグイン自体の破損・古いバージョンを疑い、再インストールを案内する。codex_hooks_registration(.codex/hooks.json。Codex CLI 自体の設定でありプラグイン機構の対象外。実在確認込み——登録コマンドの参照先スクリプトが実際に存在するファイルへ解決できない場合は ok: false になる)が Step 1.5 の自己修復後もなお失敗している場合は、Step 1.5 の symlink/hooks_json の conflict/error 報告を参照し、手動での確認を案内する。warnings(Codex 常駐・trust 登録は機械検査不能)は送信前の予告として提示するが、これのみでは送信を止めない(fail-open)。
Step 3: 対象解決(DES-045 §3.3)
python3 "${CLAUDE_SKILL_DIR}/scripts/resolve_targets.py" --mode <diff|branch|files> [--files a,b,...] [--project-root <path>]
出力 JSON の status が error の場合(対象 0 件・指定ファイル不在・base ブランチ解決不能)、依頼を送信せず理由を報告して終了する。status: ok の場合、files(対象ファイル一覧)・mode・base_branch(branch モード時)を後続 Step で使う。この files(target_files)は、受信モード Step 2a の allowlist 検証(DES-047)で参照するため、review_id と同様に以後のやり取りでコンテキストに保持する。
対象軸が diff または branch の場合、build_review_request.py へ渡す差分取得コマンド文字列を組み立てる。diff モードは git diff HEAD。branch モードは resolve_targets.py が返す base_branch が表示名(例: main)でありローカルに同名ブランチが存在しない環境(remote-tracking のみ)があるため、base_branch をそのまま git diff <base_branch>...HEAD に使わず、ローカルブランチが無ければ origin/<base_branch> へフォールバックするシェル式を組み立てる: git diff $(git rev-parse --verify --quiet <base_branch> || echo origin/<base_branch>)...HEAD。files モードでは差分取得コマンドを渡さない。
Step 4: 関連ルール・仕様の収集
対象ファイル・種別に関連するプロジェクトルール・仕様を以下の別スキル呼び出しで収集し、返却されたパス一覧を --extra-doc-json の JSON 配列としてまとめる:
/forge:query-db-rules
/forge:query-db-specs
forge 内蔵の review_criteria_<種別>.md はクエリせず、forge_doc_access_principle.md の経路A(直接参照)に従い実行時解決の絶対パスを直接組み立てる: ${CLAUDE_PLUGIN_ROOT}/skills/review/docs/review_criteria_<種別>.md。
Step 5: 依頼本文の組み立て(DES-045 §3.4)
python3 "${CLAUDE_SKILL_DIR}/scripts/build_review_request.py" \
--review-type <種別> --mode <diff|branch|files> \
--files-json '["path1","path2",...]' \
[--diff-command "<Step 3 で組み立てたコマンド文字列>"] \
--criteria-path "<Step 4 で解決した絶対パス>" \
[--extra-doc-json '["path1","path2",...]']
標準出力に依頼本文(テキスト)が書かれる。本文は review_id(このスクリプトが新規生成する不透明トークン)を含むプロトコルヘッダで始まる。この review_id を以後のやり取り(受信モード・再開モード)で参照するためコンテキストに保持する。
Step 6: 送信
Write ツールで依頼本文を一時ファイルへ書き出し、msg-sys の send.py を Bash subprocess として呼ぶ(シェル経由の本文書き出しは行わない。既存 msg-sys の返信ヒント手順と同じ安全原則):
FORGE_MSG_PROJECT_ROOT="$(git rev-parse --show-toplevel)" \
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/msg-sys/send.py" claude codex - < "一時ファイルパス"
送信後、一時ファイルを削除する。send.py が非ゼロ終了した場合は送信失敗として報告し終了する(部分送信は起こらない。send は単一 INSERT)。
Step 6.5: push型起床(DES-045 §3.8・cmux環境限定・ベストエフォート)
常駐 Codex の Stop hook は Codex 自身のターン終了時にしか発火しない(pull型)。送信直後に呼ぶことで、Codex がたまたま別の理由でターンを終えるまで配信されない事態(実測で約7時間かかった事例あり)を避ける。
"${CLAUDE_SKILL_DIR}/scripts/wake_codex.sh" "$(git rev-parse --show-toplevel)"
wake_codex.sh は対象ペイン(project_root の cwd と一致する Codex セッションの cmux pane)を、独立スクリプト find_codex_pane.py(read-only・副作用無し)で毎回その場で発見し、結果をファイルへキャッシュしない(ユーザー指摘による設計変更: 以前は .codex/cmux_target.json に発見結果をキャッシュしていたが、cmux が同じ pane を維持したまま workspace ID だけを再発行することがあり、キャッシュされた ID が stale 化して push 起床が恒久的に機能しなくなる実事故があった。発見自体は数回の cmux subprocess 呼び出しで軽量であり、依頼往復ごとに高々1回しか呼ばれないため、毎回発見し直す方が単純かつ頑健である)。発見ロジックを wake_codex.sh の inline Python に閉じ込めず独立スクリプトへ切り出したのは、DES-048(未実装)の Step 1.6 等、将来の別の呼び出し元が同じ発見ロジックを重複実装せずに再利用できるようにするため(実 Codex レビューで発見)。
{"status": "sent"|"skipped"|"failed"} を返すが、いずれの結果でも依頼モードの完了判定には影響しない(終了コードは常に0)。cmux環境でない、対象ペインが見つからない、複数候補で曖昧、のいずれの場合も skipped で何もせず次へ進む。これは既存のパッシブなポーリング待機(Step 6.6)を高速化するだけの最適化であり、無くても Step 6.6 単独で正しく動作する。
skipped と failed の区別(実 Codex レビューで発見・修正、ユーザー指摘対応): skipped は安全ゲート(作業中・入力欄に下書きあり・身元確認不一致等)による意図的な見送りであり、リトライしても同じ理由で再度見送られるだけなので許容する。一方 failed は、その安全ゲートを全て通過した(=送信して問題ない状況が整っていた)にもかかわらず cmux send/send-key 自体がエラー終了した場合であり、wake_codex.sh 内部で3回までリトライ済みの結果である。この failed は依頼モードの完了判定には引き続き影響させないが(cmux 側の一時的な不調である可能性があり、review の正しさとは無関係なため)、この結果を保持しておき、Step 7 で timeout に至った場合の報告に含める(cmux 環境が整っているのに push 起床が機能していないという診断情報を利用者に伝えるため)。
Step 6.6: 応答のブロッキング待機(DES-045 §3.7)
/forge:review を rename で差し替えても呼び出し元(/forge:start-implement Phase 5 等)の「結果を受け取ってから次工程に進む」という前提が崩れないよう、送信直後にその場で Codex の返信を待つ。
wait_for_reply.py を run_in_background: true で1回だけ起動する。wait_for_reply.py も send.py 等と同じ mailbox.resolve_db_path()(DES-034 §7、fail-closed)で DB パスを解決するため、--db-path を渡さない場合は Step 6 と同じ FORGE_MSG_PROJECT_ROOT の前置が必須である(実インシデントで発見: この前置を省略すると RuntimeError: DB path could not be resolved で即エラー終了する):
FORGE_MSG_PROJECT_ROOT="$(git rev-parse --show-toplevel)" \
python3 "${CLAUDE_SKILL_DIR}/scripts/wait_for_reply.py" claude codex <review_id> \
--max-seconds 1620 --progress-interval 10 [--db-path <path>]
Monitor ツールでこのジョブを監視し、10秒おきの進捗行(「経過N秒、まだ返信なし」)と最終結果を受け取る。
nohup・末尾 & での二重バックグラウンド化は禁止 [MANDATORY](実インシデントで発見): wait_for_reply.py は Bash ツールの run_in_background: true パラメータで起動する。これに加えて nohup ... & でシェル自身もバックグラウンド化すると、実際のポーリングプロセスがハーネスの追跡から外れ(ハーネスが完了を検知できるのは自身が起動したプロセスの終了のみ)、replied/timeout の完了通知が二度と届かなくなる(Bash ツールは echo "started pid $!" 等の直後に即座に完了扱いになり、その通知は本当の待機結果ではない)。run_in_background: true を指定した時点で既にバックグラウンド実行されるため、二重の nohup/& は不要かつ有害である。
通知が来ない場合の復旧手順 [MANDATORY]: 上記の誤りに限らず、何らかの理由で wait_for_reply.py の完了通知を受け取れなくなった場合、新たな一時ポーリング処理を手書きしない(DB へのアドホックな SQL 直接発行を含む)。既存の filter_review_history.py <review_id> を呼び、resolved および messages の最新の送信者・in_reply_to から返信の有無を確認する(このスクリプトは review_id のスレッド判定を in_reply_to の連鎖で正しく行う。手書きの SQL は本連鎖判定を再実装することになり、後述の --in-reply-to 必須化の意図を再度壊しかねない)。
- 最終結果が
{"status": "replied", "messages": [...]} → ターンを終えず、messages 内の新規メッセージ本文を使って同一ターン内で「受信モード Step 1」へ合流する。この場合 messages の中の直近の Codex 発メッセージの id を、後続の返信(受信モード Step 2a 手順5・Step 1 完了宣言行なし時の再送)で使う --in-reply-to の値として保持する(実 Codex レビューで発見の不具合対応。下記参照)
- 最終結果が
{"status": "timeout", ...} → Step 7 のタイムアウト報告へ
--in-reply-to を全ての送信で必須にする(実 Codex レビューで発見・修正): filter_review_history.py は review_id のスレッド判定を in_reply_to の連鎖で行う(body 先頭行のヘッダパースは連鎖の起点特定にのみ使う)。ヘッダ行は自由記述本文の一部として手で書く自己申告値であり、書き忘れ・省略に対して無防備だった(実際に Codex の返信でヘッダ行が丸ごと欠落し、wait_for_reply.py がその返信をスレッドの一部として検知できずフルの待機時間を浪費する事故が発生した)。したがって、Claude 側のあらゆる送信(Step 6 の初回依頼を除く。初回はスレッドの起点でありヘッダで足りる)は、直前に受信した Codex メッセージの id を --in-reply-to <id> として send.py に必ず渡す。Stop フック経由(check_inbox.py の返信ヒント)で受信した場合はヒントの中に既に --in-reply-to が組み込まれているためそのまま使えばよいが、wait_for_reply.py 経由(本 Step の messages から直接合流する場合)は返信ヒントが存在しないため、上記で保持した id を使い Claude 自身が --in-reply-to を明示的に組み立てる。
Step 7: 完了報告・タイムアウト報告
Step 6.6 が status: "replied" の場合、本 Step は使わない(受信モードの完了処理・要約報告がそのまま完了報告を兼ねる)。
Step 6.6 が status: "timeout" の場合、フォールバックしない(docs/rules/implementation_guidelines.md「フォールバックを反射的に書かない」)。種別・対象・review_id・経過時間を含め、「Codex からの返信が{経過時間}間ありませんでした。Codex 側セッションの稼働状況を確認してください」という確定した失敗を報告してターンを終える。非同期往復(受信モード・再開モード)への切り替えを装って処理を先に進めることはしない。
push起床が failed だった場合の追記(ユーザー指摘対応): Step 6.5 で保持した wake_codex.sh の結果が {"status": "failed", ...} だった場合、上記の報告に「なお、push起床(wake_codex.sh)も失敗していました(reason: {reason})。cmux 側の状態を確認してください」を追記する。安全ゲートを全て通過したにもかかわらず送信自体が失敗していたという事実は、「たまたま長い待機になっただけ」なのか「push 起床が構造的に壊れている」のかを利用者が切り分けるための診断情報になる(skipped の場合はこの追記を行わない。安全ゲートによる意図的な見送りであり、push 起床が壊れている signal ではないため)。
受信モード(DES-045 §3.4・UC-2・UC-3)
差し戻されたメッセージ本文の先頭が [msg-review] <種別> review_id=<review_id> round=<n> であるターンで実行する。文脈が失われている場合は、まずこの review_id を使って「再開モード」の filter_review_history.py を呼び、当該レビューの往復のみを復元してから以下に進む。
Step 1: 完了宣言行の照合
受信本文には Stop フックが付与する返信ヒント(複数行のコマンド案内)が末尾に連結されており、完了宣言行が本文の最終行になるとは限らない。受信本文を 1 行ずつ走査し、前後の空白を除去した上で行全体が正確に REVIEW_RESULT: approved または REVIEW_RESULT: findings に一致する行のみを完了宣言行の候補とする(行の一部として出現する場合は対象外)。候補が複数見つかった場合は、本文中で最後に出現した行を完了宣言行として採用する(返信の末尾に近いほど最終判断を反映するため):
- 採用された行が
REVIEW_RESULT: approved → 「UC-3 承認による完了」へ
- 採用された行が
REVIEW_RESULT: findings → 「UC-2 所見の受領・修正・再依頼」へ
- 候補が 1 つも無い(形式違反) → 修正は行わず、完了宣言行を含めて再送するよう返信する(プロトコル契約の再掲を含める)。返信は依頼モード Step 6 と同じ手順(Write で一時ファイル→送信→削除)に従い、受信メッセージ本文に含まれる返信ヒントのコマンドをそのまま使う
Step 2a: UC-2 所見の受領・修正・再依頼
-
所見評価 [MANDATORY]: 依頼時に Codex へ渡したものと同じ review_criteria_<種別>.md(Step 4 で解決した絶対パス。以後の往復でも同一ファイルを Read する)を基準に、所見ごとに以下の評価を行う。Step 2 の severity による振り分け(auto_fix/excluded)は自動修正の「対象範囲」を決めるだけであり、この評価を代替・省略しない(auto_fix に含まれる所見であっても、この評価を経ていない機械的な適用をしてはならない):
- 不要な指摘: criteria・対象コードの実態に照らして妥当でないと判断した場合 → ドロップする。対応表には「対応しない(理由: 検証の結果、該当しないと判断)」と記載する
- Codex の勘違いに基づく指摘: 対象・前提の理解に誤りがあると判断した場合 → ドロップするか、修正報告の中で Codex に確認・訂正を求める(次ラウンドで Codex の再考を促す。理由欄に具体的な確認事項を書く)
- 妥当な指摘: 鵜呑みにせず、影響範囲・代替案・よりよい修正方法を検討したうえで実施内容を決定する(本セッション中の実 Codex レビュー往復で、提案をそのまま採用した後に別の観点から反証を受け撤回した事例があり、この教訓を反映する)
依頼側と受信側で判断基準を揃え、Codex の指摘を別基準で恣意的に棄却することも、逆に無条件に採用することも防ぐ。実施する修正は当該所見が指摘した内容に限定し、関連する体裁修正・リファクタリングを合わせて行わない(DES-047 §3.4。
/forge:review の fixer Agent が持つ「無関係な refactor 禁止」制約と同趣旨)
-
介入軸による振り分け(DES-046): 以下を常に実行する。--interactive・未指定時は暫定的に mode = auto として扱う(DES-046 §1「暫定運用」。ユーザー指示・ユーザー責任 [2026-07-19]):
python3 "${CLAUDE_SKILL_DIR}/scripts/parse_findings.py" --body-file <受信本文の一時ファイルパス>
出力の findings 配列をそのまま次に渡す(--interactive・未指定時は --mode auto を渡す):
python3 "${CLAUDE_SKILL_DIR}/scripts/gate_findings.py" --findings-json '<findings 配列>' --mode <auto-critical|auto>
auto_fix は severity に基づく候補であり、確定した「修正する所見」ではない。auto_fix の各所見に対して Step 1 の評価を適用し、その結果に応じて以下の3群に分ける:
confirmed_fix: Step 1 で妥当と判断され、実施内容(影響範囲・代替案検討済み)が決まった所見 → Step 3 で実施する
- Step 1 でドロップした所見(不要な指摘・Codex の勘違い):
severity に関わらず修正しない。対応表には「対応しない(理由: 検証の結果、該当しないと判断)」または「対応しない(理由: <具体的な確認事項。Codex に再考を依頼>)」と記載する
excluded(gate_findings.py の出力): severity が critical/major/minor のものは「対応しない(理由: 重大度が現在のモード <mode> の自動修正範囲外)」、unclassified(parse_findings.py が severity マーカーを検出できなかった所見。DES-046 §3.1「フォーマット逸脱への対応」)のものは「対応しない(理由: 重大度を判定できませんでした。人間の確認が必要です)」として記載する
終了判定 [MANDATORY]: confirmed_fix が空の場合(= Step 1 の評価を経てもなお今回実施すべき新規の修正が無い。severity 除外のみ・Step 1 でのドロップのみ・所見自体が0件のいずれか)、Step 3〜5 を実行せず「Step 2c: 未対応所見を残した完了(承認以外)」へ進む。これは、対応しなかった所見を残したまま再レビューを依頼すると、Codex が同じ所見を指摘し続け REVIEW_RESULT: findings から抜け出せず往復上限まで往復し続ける(実 Codex レビューで発見)ことを避けるための必須の分岐であり、confirmed_fix が空でない限り再レビューを要求してはならない。この完了は Codex の承認とは異なる状態であり、「Step 2b: UC-3 承認による完了」とは別の Step として明確に区別する(REQ-012 FNC-005・実 Codex レビューで発見: 承認と同一視すると、Codex がなお指摘ありと判定している事実が要約報告で埋もれ、人間が見落とす)
-
修正の実施(Claude 直接修正 + 安全検証、DES-047 §3.1〜§3.3): Claude 自身が直接、confirmed_fix の所見の修正を実施する(FNC-004。専用の evaluator/fixer Agent は起動しない。v1 の意図的な判断は REQ-012 §2.3 参照)。ただし全件をまとめて修正してから検証するのではなく、finding 単位で「適用 → 検証 → 判断 → 次へ」を逐次繰り返す:
- baseline 取得(ループ開始前に1回): 依頼モード Step 3 で保持した target_files に対して構文検証の baseline を取得する:
python3 "${CLAUDE_SKILL_DIR}/scripts/capture_syntax_baseline.py" --files-json '<target_files の JSON 配列>'
confirmed_fix の finding を1件ずつ、以下を繰り返す:
- その finding の修正のみを実施する(他の
confirmed_fix 所見の編集は同時に行わない)
- この finding のために実際に Edit したファイルパスを自己申告する(Claude は自分が何を編集したか常に把握しているため、git diff 等の外部推測は不要)
- 検出専用の安全検証を実行する(ファイルは一切書き換えない。DES-047 §2.1):
python3 "${CLAUDE_SKILL_DIR}/scripts/verify_fix_safety.py" \
--allowed-files-json '<target_files>' \
--modified-files-json '<この finding で自己申告したファイル群>' \
--baseline-json '<Step i で取得した baseline>'
- 結果を Claude 自身が判断する(スクリプトは自動でロールバックしない):
allowlist_violations / syntax_errors が事故的な逸脱・意図しない構文破壊と判断した場合 → Claude 自身が Edit で元の内容に戻し、この finding を「対応しない(理由: 修正後の安全検証で問題を検出したため取り消し)」として記録する
allowlist_violations(target_files 外の関連ファイル修正)をレビュー基準に照らして正当な波及修正と判断した場合 → 変更を維持し、その理由を修正報告に明記する(沈黙したスコープ拡大を許さない)
- 検証結果が
status: "ok" の場合 → この finding を「対応した」として記録し、次の finding へ進む
- ラウンド終了時の独立検証 [MANDATORY](DES-047 §3.5、自己申告への依存を補完): 上記ループの
--modified-files-json は finding ごとの自己申告に基づく。申告漏れ(Claude が編集を忘れて申告しない等)が起きると、そのファイルは allowlist・構文検証のいずれも通過しないまま見過ごされうる(実 Codex レビューで発見)。すべての confirmed_fix 処理後、自己申告に依存しない形でこのラウンド全体の変更集合を独立に確認する。ファイルパスの抽出は行/矢印単位の手動パースでは行わない(git status --porcelain は空白・改行・非 ASCII を含むパスを quote し、rename/copy は -> を含む1行で表現するため、手動パースでは実パスを取り違える。実 Codex レビューで発見)。代わりに NUL 区切り・quote 無しの -z 出力を決定論的に解析する専用スクリプトを使う:
python3 "${CLAUDE_SKILL_DIR}/scripts/collect_modified_files.py"
出力の files 配列を target_files ∪ (このラウンドで正当な波及修正として維持したファイル) を allowlist として verify_fix_safety.py に渡す:
python3 "${CLAUDE_SKILL_DIR}/scripts/verify_fix_safety.py" \
--allowed-files-json '<target_files ∪ 波及修正で維持したファイル>' \
--modified-files-json '<collect_modified_files.py の files 配列>' \
--baseline-json '<Step i で取得した baseline>'
allowlist_violations が検出された場合、それはどの finding の自己申告にも含まれていなかった変更である。Claude 自身がその変更内容を確認し、意図した変更であれば理由を修正報告に明記し、意図しない変更であれば元に戻す。この独立検証は「今回のラウンドで新たに変更されたファイル集合」を追跡するものではなく(--diff モードでは target_files 自体が既にラウンド開始前から未 commit 差分として存在するため、finding 単位の変更を厳密に切り分ける独立検出は行わない。DES-047 §2.1 参照)、「ラウンド終了時点で allowlist 外の変更が実際に存在するか」の粗い最終確認である
-
修正報告メッセージを組み立てる: プロトコルヘッダ(同一 review_id、round は表示上の通し番号としてインクリメント)に続けて、所見ごとの対応表(対応した / 対応しない(理由))と再レビュー依頼を記述する。excluded が空でない場合は、再レビュー依頼に「対応しなかった所見は今回のモードでは意図的に対象外としています。今回対応した所見が正しく解消されているかのみご確認ください」を明記し、Codex に対象外所見の再指摘を求めない(Step 2「終了判定」が実際の歯止めであり、本文言は往復回数を減らすための補助に過ぎない)
-
既存 msg-sys の返信ヒント手順(受信メッセージ本文に含まれる、一時ファイル + 標準入力リダイレクトのコマンド案内)にそのまま従って送信する。返信ヒントが存在しない場合(Step 6.6 の wait_for_reply.py 経由で合流した場合)は、保持しておいた直近の Codex メッセージ id を使い --in-reply-to <id> を明示的に付けて送信する(必須。省略しない。Step 6.6 の説明参照)。返信本文はシェルコマンド(heredoc・echo・printf 等)ではなく Write ツールで一時ファイルへ書き出す
-
送信済み報告を出力してターンを終える(次の受信は次ターンの Stop フック起点)
Step 2b: UC-3 承認による完了
REVIEW_RESULT: approved を受けた場合に実行する。要約報告を出力して終了する。要約報告には以下を含める(FNC-005):
- 指摘の件数
- 対応した修正の概要
- 不採用とした所見とその理由
Step 2c: 未対応所見を残した完了(承認以外)(DES-046・REQ-012 FNC-005)
Step 2a の「終了判定」(confirmed_fix が空。--interactive・未指定時も暫定的に --auto として判定するため対象になりうる)により到達する。Codex は REVIEW_RESULT: findings(指摘あり)と判定し続けているため、Step 2b(承認による完了)とは異なる完了状態として扱う。要約報告には Step 2b の内容に加え、以下を明記し、承認された場合と混同しない形で人間の判断を仰ぐ(REQ-012 FNC-005 [MANDATORY]):
- Codex はなお指摘ありと判定していること(承認ではない旨を冒頭で明示する)
- 修正しなかった所見全ての一覧と理由(実 Codex レビューで発見:
excluded のみでは不十分。以下すべてを含める):
- severity によるモード範囲外(
excluded。重大度が既知の場合)
- 重大度を判定できなかった
unclassified(excluded)
- Step 1 の評価でドロップした所見(不要な指摘・Codex の勘違い)とその具体的根拠
- Step 3 の安全検証(DES-047)で問題を検出し取り消した所見とその内容(allowlist 逸脱・構文エラーの別)
- 現在のモード(
<mode>。--interactive・未指定時も暫定的に auto として扱っている旨)と、対応するには現時点では人間が直接内容を確認するしかない旨(本来の --interactive 段階的提示は未実装。DES-046 §1「暫定運用」)
再開モード(DES-045 §2.1・§3.6・UC-4)
往復上限到達の OS 通知を受けた利用者が状況確認・再開を明示指示したターンで実行する。msg-sys は上限到達時に対象メッセージを配信しないため、受信モードは自動発火しない(人間介在契機)。
Step 1: 対象 review_id の往復履歴を取得
対象の review_id(コンテキストに保持しているもの。失われていれば利用者に確認する)を使い、往復履歴の絞り込み CLI を呼ぶ:
python3 "${CLAUDE_SKILL_DIR}/scripts/filter_review_history.py" claude codex <review_id> [--db-path <path>]
出力 JSON(review_id / messages / round / resolved)は、history.py の全履歴から対象 review_id のメッセージのみを送信順に抽出済みである(決定論的処理としてスクリプトに切り出し済み。SKILL.md 側で手動パースしない)。
Step 2: 未解決所見の集計・要約報告
resolved が false の場合、messages の中の直近の Codex 所見(REVIEW_RESULT: findings を含む最新メッセージ)から未解決所見を抽出し、以下を含む要約報告を出力して人間の判断を仰ぐ:
- 往復回数(
round)
- 未解決所見の一覧
- これまでの対応・不採用理由(Claude 側の返信メッセージから)
resolved が true の場合は、既に承認済みであることを報告する(上限到達通知と承認が競合した場合の整合性確認)。
エラーフロー一覧(DES-045 §4.2)
| 異常系 | 挙動 |
|---|
| 前提検査 error(UC-5) | 依頼を送信せず、不足項目と対処を報告して終了 |
| 対象 0 件 / 指定ファイル不在 | 依頼を送信せず報告して終了 |
send.py 非ゼロ終了 | 送信失敗を報告して終了 |
| Codex から返信が来ない(待機予算内) | wait_for_reply.py が指数バックオフでポーリングを継続する(Step 6.6) |
| Codex から返信が来ない(待機予算超過) | フォールバックせず、確定したタイムアウト失敗として報告して終了(Step 7)。利用者に Codex 側の状態確認を促す |
| 受信メッセージに完了宣言行がない | 受信モード Step 1 のとおり、修正せず完了宣言行の再送を依頼する |
| 往復上限到達(UC-4) | msg-sys が人間通知へ降格。SKILL は往復回数管理を持たない。人間介在で再開モードへ |
| 受信モードで文脈が失われている | プロトコルヘッダをトリガーに本 SKILL.md を再読し、filter_review_history.py で当該 review_id の文脈のみを復元する |
対象外(v1 スコープ外・REQ-012 §2.2)
/forge:review パイプライン(session_dir / refs.yaml / findings JSON / evaluator / fixer)との統合
- evaluator / fixer Agent の起動(所見の評価・修正は本 SKILL 内で Claude が直接行う)。ただし fixer Agent が持つ安全境界のうち allowlist 検証・構文検証(検出のみ、ロールバックは Claude 自身が判断)は DES-047 により受信モード Step 2a に導入済み(Agent 分離とは独立な決定論的スクリプト、DES-046 §2 の決定を精緻化)
- Codex セッションの自動起動・管理(人間が手動起動して常駐させる前提)
- msg-sys 既存実装の変更(
send.py / inbox.py 等を利用者として呼ぶのみ)
--interactive(/forge:present-findings 相当の段階的提示)の実装(DES-046 §2。present-findings が session_dir 前提のレビューパイプラインに強く結合しているため。--interactive・介入軸未指定時は、DES-046 §1「暫定運用」によりユーザー指示・ユーザー責任で暫定的に --auto と同じ振り分けを適用する。次回以降に本来の段階的提示へ置き換え予定)