diff --git a/.github/workflows/claude-issue-to-pr.yml b/.github/workflows/claude-issue-to-pr.yml index 4ca78bf..d949195 100644 --- a/.github/workflows/claude-issue-to-pr.yml +++ b/.github/workflows/claude-issue-to-pr.yml @@ -22,11 +22,21 @@ concurrency: jobs: work: - uses: HarperFast/ai-review-prompts/.github/workflows/_claude-issue-to-pr.yml@be549ad08aa6d34b909ea8b542a7ffebdaae1e81 # main 2026-08-25 (#90 cost gates: draft skip, mechanical-diff skip, effort-by-size, debounce; #89 defaults; #88 lenses) + # Calling-job permissions ceiling — union of the reusable's + # authorize (contents: read) and work (contents/pull-requests/ + # issues/id-token: write) jobs, so grants don't depend on the + # repo's default-workflow-permissions setting (same rationale as + # the review callers' blocks). + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + uses: HarperFast/ai-review-prompts/.github/workflows/_claude-issue-to-pr.yml@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses) with: # Same SHA as the `uses:` ref above. See the comment in # claude-mention.yml for why the duplication is unavoidable. - ai-review-prompts-ref: be549ad08aa6d34b909ea8b542a7ffebdaae1e81 + ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde # Plugin repo — bun is part of the test path. setup-bun: true repo-specific-conventions: | diff --git a/.github/workflows/claude-mention.yml b/.github/workflows/claude-mention.yml index 5b9a885..4b20f08 100644 --- a/.github/workflows/claude-mention.yml +++ b/.github/workflows/claude-mention.yml @@ -24,7 +24,17 @@ concurrency: jobs: mention: - uses: HarperFast/ai-review-prompts/.github/workflows/_claude-mention.yml@be549ad08aa6d34b909ea8b542a7ffebdaae1e81 # main 2026-08-25 (#90 cost gates: draft skip, mechanical-diff skip, effort-by-size, debounce; #89 defaults; #88 lenses) + # Calling-job permissions ceiling — union of the reusable's + # authorize (contents: read) and work (contents/pull-requests/ + # issues/id-token: write) jobs, so grants don't depend on the + # repo's default-workflow-permissions setting (same rationale as + # the review callers' blocks). + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + uses: HarperFast/ai-review-prompts/.github/workflows/_claude-mention.yml@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses) with: # Same SHA as the `uses:` ref above. The reusable uses this to # check out HarperFast/ai-review-prompts (parse + auth scripts) @@ -34,7 +44,7 @@ jobs: # introspect their own ref (`github.workflow_ref` resolves to # the CALLER's ref in `workflow_call` context), and `uses: …@` # is parsed literally so we can't interpolate a variable. - ai-review-prompts-ref: be549ad08aa6d34b909ea8b542a7ffebdaae1e81 + ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde # Plugin repo — opt into bun setup so the agent can run # `bun test` and `bun run …` for repo-specific scripts. setup-bun: true diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 44e4ada..147947a 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -20,9 +20,9 @@ on: # bot-authored PRs (renovate, dependabot). See ai-review-prompts#38. types: [opened, synchronize, reopened, ready_for_review, labeled] -concurrency: - group: claude-review-${{ github.event.pull_request.number }} - cancel-in-progress: true +# No concurrency block: cancellation is owned by the reusable's review +# job (job-level group, engaged only after authorization), so an +# unauthorized or skipped event can never cancel a legitimate review. jobs: review: @@ -30,10 +30,11 @@ jobs: # always-on toggle". CLAUDE_ALWAYS_ON=true (repo/org variable) → auto- # review trusted-author PRs; unset → opt-in via the claude-review # label. The reusable's authorize job still owns WHO is admitted. - # Note: the `claude-review` label name is matched there too — - # `_claude-review.yml`'s authorize `if:`, not in this caller. - if: ${{ vars.CLAUDE_ALWAYS_ON == 'true' || github.event.action == 'labeled' }} - 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) + # Note: the `claude-review` label name is deliberately duplicated — + # the caller gate names it (below) AND the reusable's authorize + # matches it; renaming the label means changing both. + 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@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses) # Caller-side permissions, scoped at the calling-job level (NOT # workflow-level — that placement caps the reusable's per-job # grants below what they need and breaks the workflow at startup; @@ -58,7 +59,7 @@ jobs: # introspect their own ref (`github.workflow_ref` resolves to the # CALLER's ref in `workflow_call` context), and `uses: …@` # is parsed literally so we can't interpolate a variable. - ai-review-prompts-ref: be549ad08aa6d34b909ea8b542a7ffebdaae1e81 + ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde review-layers: | universal harper/common diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index dc82b02..3415d9d 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -27,13 +27,9 @@ on: # lists the union and the `review` job gates on GEMINI_ALWAYS_ON. types: [opened, synchronize, reopened, ready_for_review, labeled] -concurrency: - # Different group key from claude-review so the two providers can - # run 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 }} - cancel-in-progress: true +# No concurrency block: cancellation is owned by the reusable's review +# job (job-level group, engaged only after authorization), so an +# unauthorized or skipped event can never cancel a legitimate review. jobs: review: @@ -42,10 +38,11 @@ jobs: # Gemini calibration baseline running on every PR; unset → opt-in via # the gemini-review label. The reusable's authorize job still owns # WHO is admitted (CODEOWNERS trust set; the labeler on `labeled`). - # Note: the `gemini-review` label name is matched there too — - # `_gemini-review.yml`'s authorize `if:`, not in this caller. - if: ${{ vars.GEMINI_ALWAYS_ON == 'true' || github.event.action == 'labeled' }} - 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) + # Note: the `gemini-review` label name is deliberately duplicated — + # the caller gate names it (below) AND the reusable's authorize + # matches it; renaming the label means changing both. + 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@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses) # Caller-side permissions, scoped at the calling-job level (NOT # workflow-level — that placement caps the reusable's per-job # grants below what they need and breaks the workflow at startup; @@ -67,7 +64,7 @@ jobs: # in this repo for why the duplication is unavoidable # (reusable workflows can't introspect their own ref in # workflow_call context). - ai-review-prompts-ref: be549ad08aa6d34b909ea8b542a7ffebdaae1e81 + ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde review-layers: | universal harper/common diff --git a/.github/workflows/validate-caller-workflows.yml b/.github/workflows/validate-caller-workflows.yml index e7e08a8..cc3bc20 100644 --- a/.github/workflows/validate-caller-workflows.yml +++ b/.github/workflows/validate-caller-workflows.yml @@ -25,9 +25,11 @@ on: jobs: validate: - uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@be549ad08aa6d34b909ea8b542a7ffebdaae1e81 # main 2026-08-25 (#90 cost gates: draft skip, mechanical-diff skip, effort-by-size, debounce; #89 defaults; #88 lenses) + permissions: + contents: read + uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@af81872e56dd63da1160adb5eae82d359a456dde # main 2026-09-01 (#94 freshness-gated admission; #91 job-level cancellation; #90 cost gates; #89 defaults; #88 lenses) with: # Same SHA as the `uses:` ref above — the reusable uses this to # check out the validator script at the matching version. Same # SHA-twice pattern as the other caller workflows in this repo. - ai-review-prompts-ref: be549ad08aa6d34b909ea8b542a7ffebdaae1e81 + ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde