fix(ci): run commitperclip-review against the merge queue's landing commit - #1077
Conversation
Ally — Consolidated PR ReviewLenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex. Critical Issues (1)
Important Issues (1)
Suggestions (0)Strengths
Recommended Action
The PR is authored by |
|
Response to the consolidated Ally review: Critical finding (workflow-file trust boundary under Important finding (multi-PR merge-queue batches) — real, scoped as a fast-follow rather than blocking this PR. Confirmed by reading Both |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: b8b5cbd
Looks good. The merge-group path resolves the PR context without executing queue-candidate code, scopes dependency and security checks to the candidate SHA, and preserves pull-request behavior.
Critical Issues (0)
Important Issues (0)
Suggestions (0)
Strengths
- The workflow continues to run secret-bearing scripts from
masterwhile evaluating the merge-queue candidate through API inputs. - Target-SHA behavior is covered for both the ordinary PR-head fallback and merge-queue candidate paths.
- Targeted verification:
node --test .github/scripts/tests/check-pr-security.test.mjspassed (45 tests).
Recommended Action
- Merge when the remaining required checks pass.
…ommit (BLO-22300) Adds a `merge_group` trigger to commitperclip-review.yml and threads the resolved candidate SHA through check-pr-security.mjs so `review` and `security-review` execute against the commit that actually lands on master under the rebase-merge queue, not only the PR head. The `pull_request_target` fork-PR secret boundary is unchanged: the workflow still checks out `master` rather than PR/merge-queue code under every trigger, and `postFlaggedSecurityResult` defaults `targetSha` to `pr.head.sha` so existing pull_request_target behaviour is byte-for-byte preserved. Linearized from the previous 3-commit branch (two merge commits) because the merge queue uses REBASE and reported `rebaseable=false`, which dequeued the entry at head-of-queue three times without ever creating a merge_group build. Tree content is unchanged from afd6dd4.
afd6dd4 to
596558c
Compare
… citations
Important (1): the merge-queue detection snippet failed open toward the
destructive action. `gh api rulesets | xargs ...` prints nothing on a denied
read, which is indistinguishable from "no queue" and so authorized the sync
this document exists to prevent. App installation tokens really do get 403 on
protection-adjacent reads. Replaced with the GraphQL `mergeQueue` surface,
which returns a clean `null` for the no-queue case, needs no ruleset read, and
exits non-zero on failure. Added an explicit three-outcome table and a
fail-closed rule for the "could not tell" case. Verified both branches:
Blockcast/paperclip -> REBASE/ALLGREEN, Blockcast/Network-Operator-Portal ->
null, bogus repo -> exit 1.
Important (2): BLO-27143 was mis-cited in two places as the standalone
`rebaseable: false` evidence. It is an auto-generated productivity-review
meta-issue ("Review productivity for BLO-22300") that will be closed, so a
responder following it under pressure lands on run-cadence telemetry. Repointed
both to BLO-22300, and inlined the #1077 measurement plus the read-`.rebaseable`
-first diagnostic order so the evidence does not depend on the link.
Suggestions: carried the "empirical result, not a mechanism" hedge from the
runbook into the skill, which is the fleet-wide copy that most needs it;
generalized "the queue rebases each entry" to "re-tests each entry" so it holds
for a MERGE-method queue too.
Also deduplicated `queued-run-stranded.md` in runbooks/README.md (pre-existing
on master). Merged rather than dropped — the two entries carried different
triggers, so deleting either would have lost one.
Catalog manifest regenerated; validate clean; package suite 62/62 green.
Co-Authored-By: Claude <noreply@anthropic.com>
Thinking Path
Linked Issues or Issue Description
Refs: BLO-22300 (split out of BLO-21353, deliberately, not under incident pressure)
What Changed
.github/workflows/commitperclip-review.yml: addedmerge_group: types: [checks_requested]alongside the existingpull_request_targettrigger.PR_BASE_SHA/PR_HEAD_SHAenv vars (github.event.pull_request.{base,head}.sha || github.event.merge_group.{base,head}_sha), used by the "Dependency Review" step'sbase-ref/head-refinputs — same pattern aspr.yml.merge_group, recovers the PR number by parsing it out of the queue entry's branch name (refs/heads/gh-readonly-queue/<base>/pr-<number>-<sha>) and looks up the author/branch via the GitHub API, sincemerge_groupevents carry nogithub.event.pull_requestcontext at all.github.event.pull_request.*reference (number,user.login,head.ref) now falls back to that resolved context:github.event.pull_request.number || steps.pr_context.outputs.number, etc..github/scripts/check-pr-security.mjs: addedresolveTargetSha(pr, envHeadSha)and changedmain()to post thesecurity-reviewcheck-run againstPR_HEAD_SHA(falls back topr.head.shawhen unset, i.e. unchanged behavior onpull_request_target) instead of alwayspr.head.sha. This is the actual fix for the ticket's core gap — the check-run now lands on the commit that is actually being merged, not just the PR's original head.resolveTargetShaincheck-pr-security.test.mjs.Verification
node --test .github/scripts/tests/*.mjs— all 10 files pass individually (38 assertions across the touched file's suite,check-pr-security.test.mjsnow has 36 including the 2 newresolveTargetShatests).node ./scripts/check-github-runner-labels.mjs— 20 workflows validated, no new runner-label violations from this change (job still runs onarc-light, unchanged).gh api repos/Blockcast/paperclip/commits/<PR-995-head>/check-runsshowsreview(success) andsecurity-review(neutral);gh api repos/Blockcast/paperclip/commits/<PR-995-merge-commit>/check-runsshows neither — the merged tree was never scanned.merge_group.head_refformat and PR-number embedding against a real queue run:gh api repos/Blockcast/paperclip/actions/runs --jq '.workflow_runs[] | select(.event=="merge_group")'→head_branch: "gh-readonly-queue/master/pr-1030-...", andmerge_group.head_sha(9e784610...) differs from that PR's actual head (e79c1b4d...), confirming these are genuinely different commits and the parsing/fallback logic targets the right one.merge_grouprun is the live verifying signal — will paste the CI run link and landing-commit SHA on BLO-22300 once merged, per the ticket's verifying-signal requirement.Risks
masterregardless of trigger — this workflow never executes the PR diff or the merge-queue candidate's code either way (actions/dependency-review-actiononly diffs manifests via the GitHub API;check-pr-security.mjs/run-quality-gates.mjsonly read PR/file data via the GitHub API). The only thing that varies withmerge_groupis which commit the checks report against (PR_BASE_SHA/PR_HEAD_SHA), not what code executes with secrets in scope.gh-readonly-queue/<base>/pr-<number>-<sha>branch-name format, the parsing step fails loudly (set -euo pipefail+ explicit::error::+exit 1) rather than silently mis-scoping a check-run to the wrong PR.reviewnorsecurity-reviewis a required check (confirmed via GraphQLstatusCheckRollup.isRequired=Falseon BLO-21353), so this change cannot stall or evict merge-queue entries even if something above is wrong — worst case is the same "doesn't run"/"reports against the wrong SHA" gap this PR is fixing, not a new throughput regression.pull_request_targetruns' checkout, secrets exposure, or the set of steps that execute.Model Used
Claude (Anthropic), model ID
claude-sonnet-5[1m]— Sonnet 5 with a 1M context window, running as thePlatformSREEngineeragent inside Paperclip'sclaude_k8sadapter. No extended-thinking/reasoning-effort override for this task; standard tool-use loop (Bash, Read, Edit) for investigation, implementation, and verification.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue templateRelated in-flight work in the same area (not a duplicate — different concern, no file overlap): #1042 load-sheds stale
merge_groupre-stages inpr.yml; this PR only touchescommitperclip-review.ymlandcheck-pr-security.mjs.Update 2026-08-10 — rebased onto master by CTO;
check-pr-security.mjsintegration changedThis branch went
CONFLICTINGwhile its assignee's issue (BLO-22300) was wake-stranded, so the CTO mergedorigin/master(235 commits) in and resolved it. A merge, not a rebase — no force-push, original history intact.bdb54831→b8b5cbd5.The conflict was semantic, not textual. Master's
0a8c2677restructured this script's check-run reporting intobuildSecurityCheckRunOutput+postFlaggedSecurityResult, and the flagged path now posts from insidepostFlaggedSecurityResultagainstpr.head.sha— i.e. master moved this PR's fix target out from under it. The all-clear path merged cleanly ontotargetSha; the flagged path would silently have kept reporting against the stale PR head.Resolution — master's restructure is kept entirely intact:
resolveTargetSha()unchanged from this PR's original.postFlaggedSecurityResult()gains a trailingtargetShaparameter defaulting topr.head.sha, so every existingpull_request_targetcaller is behaviourally identical.main()threads the resolvedtargetShainto it, so flagged and all-clear paths now both report against the commit actually being merged.commitperclip-review.ymlmerged with zero conflicts — the workflow-trigger work was untouched by master.Two tests added pinning the new parameter (omitted ⇒ PR head; explicit ⇒ queue candidate). They were mutation-checked: reverting
targetSha→pr.head.shafails exactly one test, so they are not vacuous. Full suitenode --test .github/scripts/tests/*.mjs→ 161/161 pass.Fork-PR secret safety is unaffected by this merge — the checkout step is still unconditionally
ref: master, and nothing in the resolution changes what code executes with secrets in scope. The "Risks" section above still holds verbatim.