From b465b8b87b506efd26800faaa361d6ebcc575023 Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Wed, 26 Aug 2026 09:07:21 -0700 Subject: [PATCH 1/2] ci: keep ineligible runs out of the review concurrency group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kriszyp review finding on the pin-bump series: workflow-level cancel-in-progress fires before job if:, so in opt-in mode an ineligible event (a push, or a ready-flip without the opt-in label) cancels an in-flight label-triggered review and then skips — silently losing the requested review. Ineligible runs now take a unique run_id group and can never cancel an eligible one; eligible runs keep cancelling each other (the debounce contract). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S94XethbGXpAb4DRKMD4kt --- .github/workflows/claude-review.yml | 5 ++++- .github/workflows/gemini-review.yml | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index ebb021868a..87ecf38df0 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -21,7 +21,10 @@ on: types: [opened, synchronize, reopened, ready_for_review, labeled] concurrency: - group: claude-review-${{ github.event.pull_request.number }} + # Ineligible events (per the review job's opt-in gate) get a unique + # run_id group so they can never cancel an in-flight eligible review — + # workflow-level cancel-in-progress fires before job `if:` is evaluated. + group: claude-review-${{ github.event.pull_request.number }}-${{ (vars.CLAUDE_ALWAYS_ON == 'true' || github.event.action == 'labeled' || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'claude-review'))) && 'eligible' || github.run_id }} cancel-in-progress: true jobs: diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index 272e609d08..ceb34bc5d4 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -39,7 +39,8 @@ concurrency: # in parallel on the same PR. cancel-in-progress is per-group, so a # synchronize push cancels the in-flight Gemini run without touching # the Claude run (and vice versa). - group: gemini-review-${{ github.event.pull_request.number }} + # Ineligible events get a unique run_id group — see claude-review.yml. + group: gemini-review-${{ github.event.pull_request.number }}-${{ (vars.GEMINI_ALWAYS_ON == 'true' || github.event.action == 'labeled' || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'gemini-review'))) && 'eligible' || github.run_id }} cancel-in-progress: true jobs: From fa0411530ebd5177ca788738dae4aa5e404a46cc Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Wed, 26 Aug 2026 10:40:53 -0700 Subject: [PATCH 2/2] ci: only the provider's own label makes a labeled run eligible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review finding (Codex + kriszyp, independently): the eligibility predicate admitted every labeled event, so in opt-in mode an unrelated label applied mid-review joined the eligible concurrency group, cancelled the running review, and the replacement then failed the reusable's exact-label gate — no completed review. The same unrelated-label cancellation existed in always-on mode before this series (any labeled event shared the group and authorize then skipped). The labeled branch now requires the provider's own label, in both the concurrency predicate and the review job gate, and unrelated-label events are ineligible in both modes. Event matrix (opt-in / always-on): - labeled(provider label): eligible / eligible — supersedes in-flight - labeled(other): ineligible / ineligible (was: cancelled + no review) - synchronize: ineligible / eligible - ready_for_review + label: eligible / eligible - ready_for_review, no label: ineligible / eligible - opened, reopened: ineligible / eligible Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S94XethbGXpAb4DRKMD4kt --- .github/workflows/claude-review.yml | 6 ++++-- .github/workflows/gemini-review.yml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 87ecf38df0..3b91edabab 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -21,10 +21,12 @@ on: types: [opened, synchronize, reopened, ready_for_review, labeled] concurrency: + # NOTE: this predicate and the review job's `if:` must stay textually + # in sync — GitHub Actions YAML has no anchors to share them. # Ineligible events (per the review job's opt-in gate) get a unique # run_id group so they can never cancel an in-flight eligible review — # workflow-level cancel-in-progress fires before job `if:` is evaluated. - group: claude-review-${{ github.event.pull_request.number }}-${{ (vars.CLAUDE_ALWAYS_ON == 'true' || github.event.action == 'labeled' || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'claude-review'))) && 'eligible' || github.run_id }} + group: claude-review-${{ github.event.pull_request.number }}-${{ ((github.event.action == 'labeled' && github.event.label.name == 'claude-review') || (github.event.action != 'labeled' && vars.CLAUDE_ALWAYS_ON == 'true') || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'claude-review'))) && 'eligible' || github.run_id }} cancel-in-progress: true jobs: @@ -38,7 +40,7 @@ jobs: # `ready_for_review` is admitted when the PR still carries the # opt-in label, so a draft opted in via `claude-review` resumes # review when it flips ready even with CLAUDE_ALWAYS_ON unset. - if: ${{ vars.CLAUDE_ALWAYS_ON == 'true' || github.event.action == 'labeled' || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'claude-review')) }} + if: ${{ (github.event.action == 'labeled' && github.event.label.name == 'claude-review') || (github.event.action != 'labeled' && vars.CLAUDE_ALWAYS_ON == 'true') || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'claude-review')) }} uses: HarperFast/ai-review-prompts/.github/workflows/_claude-review.yml@be549ad08aa6d34b909ea8b542a7ffebdaae1e81 # main 2026-08-25 (#90 cost gates: draft skip, mechanical-diff skip, effort-by-size, debounce; #89 defaults; #88 lenses) # Caller-side permissions at the calling-job level (NOT workflow- # level — that placement caps the reusable's per-job grants below diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index ceb34bc5d4..c8941064f6 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -39,8 +39,10 @@ concurrency: # in parallel on the same PR. cancel-in-progress is per-group, so a # synchronize push cancels the in-flight Gemini run without touching # the Claude run (and vice versa). + # NOTE: this predicate and the review job's `if:` must stay textually + # in sync — GitHub Actions YAML has no anchors to share them. # Ineligible events get a unique run_id group — see claude-review.yml. - group: gemini-review-${{ github.event.pull_request.number }}-${{ (vars.GEMINI_ALWAYS_ON == 'true' || github.event.action == 'labeled' || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'gemini-review'))) && 'eligible' || github.run_id }} + group: gemini-review-${{ github.event.pull_request.number }}-${{ ((github.event.action == 'labeled' && github.event.label.name == 'gemini-review') || (github.event.action != 'labeled' && vars.GEMINI_ALWAYS_ON == 'true') || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'gemini-review'))) && 'eligible' || github.run_id }} cancel-in-progress: true jobs: @@ -54,7 +56,7 @@ jobs: # `_gemini-review.yml`'s authorize `if:`, not in this caller. # `ready_for_review` is admitted when the PR still carries the # opt-in label — mirrors claude-review.yml. - if: ${{ vars.GEMINI_ALWAYS_ON == 'true' || github.event.action == 'labeled' || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'gemini-review')) }} + if: ${{ (github.event.action == 'labeled' && github.event.label.name == 'gemini-review') || (github.event.action != 'labeled' && vars.GEMINI_ALWAYS_ON == 'true') || (github.event.action == 'ready_for_review' && contains(github.event.pull_request.labels.*.name, 'gemini-review')) }} uses: HarperFast/ai-review-prompts/.github/workflows/_gemini-review.yml@be549ad08aa6d34b909ea8b542a7ffebdaae1e81 # main 2026-08-25 (#90 cost gates: draft skip, mechanical-diff skip, effort-by-size, debounce; #89 defaults; #88 lenses) # Caller-side permissions at the calling-job level (NOT workflow- # level — that placement caps the reusable's per-job grants below