From 37624e3ba858e6ec45d5a671c698c932eec8c1d2 Mon Sep 17 00:00:00 2001 From: aloekun Date: Sat, 11 Jul 2026 20:14:36 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix(pr-monitor):=20CodeRabbit=20=E7=99=BA?= =?UTF-8?q?=E3=82=A4=E3=83=99=E3=83=B3=E3=83=88=E3=81=A7=20backstop=20?= =?UTF-8?q?=E3=81=8C=20actor=20=E6=8B=92=E5=90=A6=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E4=B8=8D=E5=85=B7=E5=90=88=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20(allowed=5Fbots)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WP-09 の GitHub Actions バックストップの本命トリガー (CodeRabbit のレビュー完了 = pull_request_review / walkthrough = issue_comment、actor が coderabbitai[bot]) が、 claude-code-action の非人間 actor 拒否 ("Workflow initiated by non-human actor: coderabbitai") で常に失敗していた (PR #259 の pull_request_review run で実観測)。 #258 のスモークテストが通ったのは actor が人間 (pull_request opened) だったため、 本命トリガーの障害を見逃していた。 - action step に allowed_bots: "coderabbitai[bot]" を追加し当該 bot のみ許可 ("*" 不使用)。 - allowlist した bot は GitHub 権限チェックをバイパスする (公式 docs/security.md) ため、 本 job の安全性は読み取り専用多層防御が唯一の担保になる点をコメントで明記 (ADR-022 原則6)。 - 副次効果: github-actions[bot] は allowlist 外のため自己トリガーが二重に防止される。 --- .github/workflows/pr-monitor.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/pr-monitor.yml b/.github/workflows/pr-monitor.yml index 9dfe9ba4..bf8b9659 100644 --- a/.github/workflows/pr-monitor.yml +++ b/.github/workflows/pr-monitor.yml @@ -126,6 +126,20 @@ jobs: with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} + # claude-code-action は既定でトリガー元 actor の権限を検査し bot actor を + # 拒否する ("Workflow initiated by non-human actor" エラー)。本 backstop の + # 本命トリガー (CodeRabbit のレビュー = pull_request_review / CodeRabbit の + # walkthrough = issue_comment、いずれも actor が coderabbitai[bot]) を通すため + # この bot のみを allowlist する ("*" は使わない = 任意 bot トリガーは禁止)。 + # 副次効果: 本 workflow 自身の投稿は github-actions[bot] だが allowlist 外の + # ため bot トリガーは弾かれ、自己トリガーが二重に防止される。 + # 【重要・セキュリティ】allowlist した bot は権限チェックを完全にバイパスする + # (公式 docs/security.md: "Allowed bots are not checked for repository + # permissions")。GitHub の権限ゲートが効かないため、本 job の安全性は下段の + # 読み取り専用多層防御 (permissions: contents: read / allowedTools scope / + # エージェント無 write / persist-credentials: false) が唯一の担保となる + # (ADR-022 原則 6 の 2 不変条件)。 + allowed_bots: "coderabbitai[bot]" prompt: | あなたはリポジトリ ${{ github.repository }} の PR #${{ github.event.pull_request.number || github.event.issue.number }} に対する読み取り専用の監視バックストップです (ADR-022 原則 6 の GitHub Actions 経路)。 起動イベント: ${{ github.event_name }} (${{ github.event.action }}) From e7f95a17ba94afdd5f7293308b0d6febaf918ebd Mon Sep 17 00:00:00 2001 From: aloekun Date: Sun, 12 Jul 2026 00:57:39 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix(pr-monitor):=20=E7=9B=A3=E8=A6=96?= =?UTF-8?q?=E3=82=92=20CI=20=E3=81=8B=E3=82=89=E5=88=86=E9=9B=A2=E3=81=97?= =?UTF-8?q?=20PR=20=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E6=B1=9A=E6=9F=93?= =?UTF-8?q?=E3=82=92=E8=A7=A3=E6=B6=88=20(pull=5Frequest=20=E5=89=8A?= =?UTF-8?q?=E9=99=A4=20+=20workflow=5Fdispatch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pr-monitor の run が PR の Status Check として現れ、concurrency cancel や bot-actor 失敗が CI 失敗 (赤× / UNSTABLE) に見える問題を解消する (PR #260 の analyze run cancelled で実観測)。原因: check_run はイベントごとに異なる対象 SHA へ発行され、pull_request 起点の run のみ PR head SHA に紐づき PR チェック化する。CodeRabbit が数分でレビューを返すため opened run が review イベントで concurrency cancel され、cancelled が赤× として残る (ほぼ毎 PR で必然)。監視 ≠ CI として役割ベースでトリガーを分離: pull_request(opened/ready) 削除、pull_request_review/issue_comment 維持、workflow_dispatch(input pr_number) 追加、allowed_bots 維持、PR番号解決を 3 箇所で inputs.pr_number 対応。ADR-022 原則6 のトリガー記述を更新。 --- .github/workflows/pr-monitor.yml | 72 +++++++++++-------- ...22-automation-responsibility-separation.md | 4 +- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/.github/workflows/pr-monitor.yml b/.github/workflows/pr-monitor.yml index bf8b9659..bb2aeeae 100644 --- a/.github/workflows/pr-monitor.yml +++ b/.github/workflows/pr-monitor.yml @@ -29,37 +29,51 @@ # 平文で .git/config に書き込まれることを防ぐ (token exfiltration 対策)。 # 指示層 (プロンプト) が破られても、この多層によりコメント投稿を含む # 書き込み・既存コメントの改変・secrets 漏洩は実行できない。 -# - トリガーはレビュアー非依存 (pull_request_review は全レビュアー対象)。 -# 特定レビューツール (CodeRabbit) 固有の条件は issue_comment の 1 条件のみに -# 閉じ込める — ツールを外す/差し替える場合はそこだけ変更すればよい。 -# - CodeRabbit がレートリミット等でレビューを返さなくても何も待たない -# (イベント駆動でありポーリングしない)。required check にも登録しないため -# マージ・他ツールのレビューを一切ブロックしない。 -# - fork PR は secrets の有無に関わらず対象外とする。pull_request イベントは -# フォーク由来だと secrets が渡らないため head.repo.full_name の比較で -# 対象外化するだけで十分だが、pull_request_review / issue_comment は -# ベースリポジトリのコンテキストで実行され secrets が渡るため、いずれも -# 明示的な fork チェックで対象外化する。issue_comment は job の if: だけでは -# PR の head repo を判定できないため、先行 step で `gh pr view` により解決する。 -# pull_request_target は権限昇格リスクがあるため使わない。 -# - 自己トリガー防止: 本 workflow の投稿は github-actions[bot] 名義の -# issue_comment になるが、issue_comment 条件が coderabbitai[bot] 限定のため -# 構造的に再発火しない (pull_request_review 側の bot 除外は防御的措置)。 -# - Max 枠の暴走ガード: concurrency で PR 単位に集約 + pull_request の -# synchronize (毎 push 発火) は意図的に含めない + プロンプト内の重複ガード。 +# - 監視 ≠ CI (トリガー分離): 本 workflow は「状態監視 (CodeRabbit 補助)」であり +# build/test/lint のような CI ではない。CI 用トリガー (pull_request / push) は +# 使わない。理由: check_suite / check_run はイベントごとに異なる対象 SHA へ発行 +# されるため、pull_request 起点の run だけが PR head SHA に紐づき PR の Status +# Check になる。一方 pull_request_review / issue_comment / workflow_dispatch 起点 +# の run は default branch 側の SHA を対象とするため PR チェックにならない +# (Actions タブには出る)。監視 run を PR チェックに載せると、concurrency cancel や +# bot-actor 失敗が「CI 失敗 (赤×)」に見え、本物の失敗との区別を毀損する。よって +# 起動は pull_request_review (レビュー完了) / issue_comment (CodeRabbit walkthrough) +# / workflow_dispatch (手動スモークテスト + 障害調査、input pr_number) のみ。 +# - トリガーはレビュアー非依存 (pull_request_review は全レビュアー対象)。特定 +# レビューツール (CodeRabbit) 固有の条件は issue_comment の 1 条件のみに閉じ込める +# — ツールを外す/差し替える場合はそこだけ変更すればよい。 +# - CodeRabbit がレートリミット等でレビューを返さなくても何も待たない (イベント +# 駆動でありポーリングしない)。required check に登録せず、上記のとおり PR チェック +# 自体を汚さないため、マージ・他ツールのレビューを一切ブロックしない。 +# - fork PR は secrets の有無に関わらず対象外とする。pull_request_review / +# issue_comment はベースリポジトリのコンテキストで実行され secrets が渡るため、 +# 明示的な fork チェックで対象外化する。pull_request_review は job の if: で +# head.repo.full_name を比較。issue_comment は payload に PR head repo が無いため +# 先行 step で `gh pr view` により解決する。workflow_dispatch は write 権限者のみ +# 起動できる trusted human 経路のため fork チェックは不要。pull_request_target は +# 権限昇格リスクがあるため使わない。 +# - 自己トリガー防止: 本 workflow の投稿は github-actions[bot] 名義の issue_comment に +# なるが、(a) issue_comment 条件が coderabbitai[bot] 限定、かつ (b) allowed_bots が +# coderabbitai[bot] のみ許可のため github-actions[bot] トリガーは弾かれ、二重に防止。 +# - Max 枠の暴走ガード: concurrency で PR 単位に集約 (最新イベントのみ処理) + +# プロンプト内の重複ガード。 name: pr-monitor on: - pull_request: - types: [opened, ready_for_review] pull_request_review: types: [submitted] issue_comment: types: [created] + workflow_dispatch: + inputs: + pr_number: + description: 分析対象の PR 番号 + required: true + type: string concurrency: - group: pr-monitor-${{ github.event.pull_request.number || github.event.issue.number }} + group: pr-monitor-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} cancel-in-progress: true permissions: @@ -71,16 +85,14 @@ permissions: # 投稿失敗するため、初回 rollout では両方を保持し、実 run で使用 scope を # 確認してから follow-up で不要な方を削る # (security-review の非ブロッキング警告に対する意図的な保留)。 - pull-requests: write # gh pr comment (pull_request / pull_request_review 経路) + pull-requests: write # gh pr comment (pull_request_review / workflow_dispatch 経路) issues: write # gh pr comment (issue_comment 経路) actions: read # gh run view / gh run list による CI 状態取得に必要 (エージェント) jobs: analyze: if: >- - (github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.draft == false) || + github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request_review' && github.event.pull_request.head.repo.full_name == github.repository && github.event.review.user.login != 'github-actions[bot]' && @@ -107,8 +119,8 @@ jobs: - name: Resolve PR head repo (fork check for issue_comment path) # issue_comment イベントの payload には PR の head repo 情報が無く、 # job の if: (github context のみ参照可能) では fork 判定ができない。 - # pull_request / pull_request_review と同じ「fork 対象外」を issue_comment - # 経路でも成立させるため、ここで API 解決してから次 step を条件分岐する。 + # pull_request_review と同じ「fork 対象外」を issue_comment 経路でも + # 成立させるため、ここで API 解決してから次 step を条件分岐する。 if: github.event_name == 'issue_comment' id: pr_head env: @@ -141,8 +153,8 @@ jobs: # (ADR-022 原則 6 の 2 不変条件)。 allowed_bots: "coderabbitai[bot]" prompt: | - あなたはリポジトリ ${{ github.repository }} の PR #${{ github.event.pull_request.number || github.event.issue.number }} に対する読み取り専用の監視バックストップです (ADR-022 原則 6 の GitHub Actions 経路)。 - 起動イベント: ${{ github.event_name }} (${{ github.event.action }}) + あなたはリポジトリ ${{ github.repository }} の PR #${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} に対する読み取り専用の監視バックストップです (ADR-022 原則 6 の GitHub Actions 経路)。 + 起動イベント: ${{ github.event_name }} (${{ github.event.action || 'manual' }}) 実行 run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} ## 手順 @@ -243,7 +255,7 @@ jobs: if: steps.analyze.outcome == 'success' && steps.extract.outputs.skip == 'false' env: GH_TOKEN: ${{ steps.analyze.outputs.github_token || secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} ANALYSIS_FILE: ${{ steps.extract.outputs.analysis_file }} run: | gh pr comment "$PR_NUMBER" \ diff --git a/docs/adr/adr-022-automation-responsibility-separation.md b/docs/adr/adr-022-automation-responsibility-separation.md index b4b9ea8c..d8b180d0 100644 --- a/docs/adr/adr-022-automation-responsibility-separation.md +++ b/docs/adr/adr-022-automation-responsibility-separation.md @@ -236,8 +236,8 @@ Actions 経路の担保は、プロンプト指示 (指示層) 単独ではな #### 運用上の割り切り - 両経路が同一 PR に分析コメントを出す重複は許容する。Actions 側コメントは見出し「🤖 PR Monitor 分析」で識別可能で、直近の自分の分析以降に新情報が無ければ skip する重複ガードをプロンプトに持つ。 -- トリガーはレビュアー非依存 (pull_request_review は全レビュアー、pull_request は opened / ready_for_review)。特定レビューツール (CodeRabbit) 固有の起動条件は issue_comment の 1 条件のみに閉じ込め、ツール差し替え時の変更点を局所化する。バックストップはイベント駆動であり、特定ツールのレビュー完了を待機・ポーリングしない。 -- fork PR は一律対象外 (本人 push の PR のみ動作)。ただし除外の成立機序はイベントで異なる: `pull_request` は fork 由来だと secrets 自体が渡らないため `head.repo.full_name == github.repository` の比較で十分。一方 `pull_request_review` / `issue_comment` は**ベースリポジトリのコンテキストで実行され secrets が渡る**ため、明示的な fork チェックで対象外化しないと fork の外部テキストが token 付き実行に到達しうる。`pull_request_review` は payload の head repo で job `if:` 判定できるが、`issue_comment` は payload に head repo 情報が無いため、先行 step で `gh pr view --json isCrossRepository` により解決してから後続 step を条件分岐する。`pull_request_target` は権限昇格リスクがあるため使わない。 +- **監視 workflow は CI と分離する(トリガー役割分離)**: 本 workflow は build/test/lint のような CI ではないため、CI 用トリガー (`pull_request` / `push`) は使わない。理由は **check_suite / check_run の対象 SHA がイベントごとに異なる**点にある — `pull_request` 起点の run のみ PR head SHA に紐づき **PR の Status Check になる**が、`pull_request_review` / `issue_comment` / `workflow_dispatch` 起点の run は default branch 側の SHA を対象とするため **PR チェックにならない** (Actions タブには出る)。監視 run を PR チェックに載せると concurrency cancel や bot-actor 失敗が「CI 失敗 (赤×)」に見え、本物の失敗との識別性を毀損する。よってトリガーは `pull_request_review` (レビュー完了) / `issue_comment` (CodeRabbit walkthrough) / `workflow_dispatch` (手動スモークテスト + 障害調査、input `pr_number`) に限定する。レビュアー非依存 (pull_request_review は全レビュアー) を保ち、特定レビューツール (CodeRabbit) 固有の起動条件は issue_comment の 1 条件のみに閉じ込め、ツール差し替え時の変更点を局所化する。イベント駆動でありレビュー完了を待機・ポーリングしない。 +- fork PR は一律対象外 (本人 push の PR のみ動作)。`pull_request_review` / `issue_comment` は**ベースリポジトリのコンテキストで実行され secrets が渡る**ため、明示的な fork チェックで対象外化しないと fork の外部テキストが token 付き実行に到達しうる。`pull_request_review` は payload の head repo で job `if:` 判定できるが、`issue_comment` は payload に head repo 情報が無いため、先行 step で `gh pr view --json isCrossRepository` により解決してから後続 step を条件分岐する。`workflow_dispatch` は write 権限者のみ起動できる trusted human 経路のため fork チェックは不要。`pull_request_target` は権限昇格リスクがあるため使わない。 - 将来 fix push まで無人化する場合 (Phase B) は、自動実行可クラスの事前定義 (ADR-028 の 2 段化) と外部テキストに対する prompt injection 防御の整備を前提条件とする。 ### 原則 1 の適用例: 分離型 fix commit の自己記述 (2026-04-20 追記 / 2026-04-21 位置付け変更)