From 4bb05c2f0c843f28be29c0c585ff2903f461dc22 Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Fri, 28 Aug 2026 16:00:38 -0700 Subject: [PATCH 1/4] ci(v1.x): sync the #91 cancellation migration from main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Content matches fleet-cancellation-migration (#219) — byte-identical to main once that merges. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S94XethbGXpAb4DRKMD4kt --- .github/workflows/claude-issue-to-pr.yml | 14 ++++++++++++-- .github/workflows/claude-mention.yml | 14 ++++++++++++-- .github/workflows/claude-review.yml | 14 ++++++++------ .github/workflows/gemini-review.yml | 18 ++++++++---------- .../workflows/validate-caller-workflows.yml | 4 ++-- 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/.github/workflows/claude-issue-to-pr.yml b/.github/workflows/claude-issue-to-pr.yml index 4ca78bf..c6e108f 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization 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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac # 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..10ce796 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization 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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac # 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..1940740 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: @@ -32,8 +32,10 @@ jobs: # 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) + # The gate names the label (unrelated labels never start the job) + # and admits ready_for_review when the opt-in label is present. + 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization 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 +60,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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac review-layers: | universal harper/common diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index dc82b02..eb1b228 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: @@ -44,8 +40,10 @@ jobs: # 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) + # The gate names the label (unrelated labels never start the job) + # and admits ready_for_review when the opt-in label is present. + 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization 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 +65,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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac review-layers: | universal harper/common diff --git a/.github/workflows/validate-caller-workflows.yml b/.github/workflows/validate-caller-workflows.yml index e7e08a8..4db04c0 100644 --- a/.github/workflows/validate-caller-workflows.yml +++ b/.github/workflows/validate-caller-workflows.yml @@ -25,9 +25,9 @@ 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) + uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization 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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac From 99065cbfae9224557544d61e1857954ef84f4765 Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Fri, 28 Aug 2026 16:21:57 -0700 Subject: [PATCH 2/4] ci: explicit contents:read ceiling on the validate caller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review feedback on oauth#219 — consistency with the PR's permissions- ceiling goal, applied fleet-wide. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S94XethbGXpAb4DRKMD4kt --- .github/workflows/validate-caller-workflows.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/validate-caller-workflows.yml b/.github/workflows/validate-caller-workflows.yml index 4db04c0..160a9ed 100644 --- a/.github/workflows/validate-caller-workflows.yml +++ b/.github/workflows/validate-caller-workflows.yml @@ -25,6 +25,9 @@ on: jobs: validate: + # Explicit ceiling — the reusable only reads workflow files. + permissions: + contents: read uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) with: # Same SHA as the `uses:` ref above — the reusable uses this to From 0ee17e571be45dbb763bc2d1cc9931814e5f2a0a Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Fri, 28 Aug 2026 16:46:44 -0700 Subject: [PATCH 3/4] ci: comment hygiene from the pre-push lane The retained note claiming the label is matched only in the reusable contradicted the caller gate two lines below (it now names the label); reworded to state the deliberate duplication. Narrating comments that restated the adjacent declaration are dropped. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S94XethbGXpAb4DRKMD4kt --- .github/workflows/claude-review.yml | 7 +++---- .github/workflows/gemini-review.yml | 7 +++---- .github/workflows/validate-caller-workflows.yml | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 1940740..13f3082 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -30,10 +30,9 @@ 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. - # The gate names the label (unrelated labels never start the job) - # and admits ready_for_review when the opt-in label is present. + # 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) # Caller-side permissions, scoped at the calling-job level (NOT diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml index eb1b228..babe088 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -38,10 +38,9 @@ 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. - # The gate names the label (unrelated labels never start the job) - # and admits ready_for_review when the opt-in label is present. + # 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) # Caller-side permissions, scoped at the calling-job level (NOT diff --git a/.github/workflows/validate-caller-workflows.yml b/.github/workflows/validate-caller-workflows.yml index 160a9ed..c054403 100644 --- a/.github/workflows/validate-caller-workflows.yml +++ b/.github/workflows/validate-caller-workflows.yml @@ -25,7 +25,6 @@ on: jobs: validate: - # Explicit ceiling — the reusable only reads workflow files. permissions: contents: read uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) From c646b7e12bb8da7082e606b4593eb8754aed02a6 Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Tue, 1 Sep 2026 11:21:04 -0700 Subject: [PATCH 4/4] ci: advance ai-review-prompts pin to #94 (freshness-gated admission) Kris's precondition for this migration (harper#2385 thread, ai-review-prompts#92/#94): stale runs are refused admission to the review job's cancelling group and self-fail if superseded after acquiring the slot, so removing the caller-side cancellation cannot let an older run cancel a newer admitted review. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01S94XethbGXpAb4DRKMD4kt --- .github/workflows/claude-issue-to-pr.yml | 4 ++-- .github/workflows/claude-mention.yml | 4 ++-- .github/workflows/claude-review.yml | 4 ++-- .github/workflows/gemini-review.yml | 4 ++-- .github/workflows/validate-caller-workflows.yml | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/claude-issue-to-pr.yml b/.github/workflows/claude-issue-to-pr.yml index c6e108f..d949195 100644 --- a/.github/workflows/claude-issue-to-pr.yml +++ b/.github/workflows/claude-issue-to-pr.yml @@ -32,11 +32,11 @@ jobs: pull-requests: write issues: write id-token: write - uses: HarperFast/ai-review-prompts/.github/workflows/_claude-issue-to-pr.yml@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) + 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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac + 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 10ce796..4b20f08 100644 --- a/.github/workflows/claude-mention.yml +++ b/.github/workflows/claude-mention.yml @@ -34,7 +34,7 @@ jobs: pull-requests: write issues: write id-token: write - uses: HarperFast/ai-review-prompts/.github/workflows/_claude-mention.yml@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) + 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) @@ -44,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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac + 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 13f3082..147947a 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -34,7 +34,7 @@ jobs: # 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) + 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; @@ -59,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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac + 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 babe088..3415d9d 100644 --- a/.github/workflows/gemini-review.yml +++ b/.github/workflows/gemini-review.yml @@ -42,7 +42,7 @@ jobs: # 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@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) + 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; @@ -64,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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac + 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 c054403..cc3bc20 100644 --- a/.github/workflows/validate-caller-workflows.yml +++ b/.github/workflows/validate-caller-workflows.yml @@ -27,9 +27,9 @@ jobs: validate: permissions: contents: read - uses: HarperFast/ai-review-prompts/.github/workflows/_validate-caller-workflows.yml@765a51b545f1b2b6c1ffe3b3731baa1512e44dac # main 2026-08-28 (#91 job-level post-authorization cancellation; #90 cost gates; #89 defaults; #88 lenses) + 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: 765a51b545f1b2b6c1ffe3b3731baa1512e44dac + ai-review-prompts-ref: af81872e56dd63da1160adb5eae82d359a456dde