Repository navigation
fix(pr-merge): take a cost decision's decided head from the review lane's check-run annotation - #10357
Conversation
…ne's check-run annotation The cost-decision readers took the decided head from the newest github-actions[bot] `claude-lane-hold ... criteria=cost` comment before the decision. Any workflow on GITHUB_TOKEN posts as github-actions[bot], so the head rested on a comment author login. All three merge-side readers (ci-status's cost-hold step, merge-holds.mjs costDecision, babysit_merge_policy.cost_decision) now read it from the review lanes' status-job notices instead: the newest cost notice recorded before the decision, only where its sha= is its own run's head, from runs vetted not to change either review-lane workflow. Two heads at the newest time decide nothing. The comment read, and the edit and deletion checks that guarded it, are removed. Refs: #9635 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion reads The ci-workflows partner change has pr-review-cli.yml's status job read the lane's earlier status-job notices, which needs actions: read and checks: read. A called workflow can only narrow its caller's grant, so both callers now grant checks: read beside actions: read. The scope is read-only, and the job that uses it runs no model. callers.test.mjs pins the review job's four scopes in both callers. Refs: #9635 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n-head-from-annotation # Conflicts: # .github/scripts/pr-merge/enable-auto-merge.mjs # .github/scripts/pr-merge/enable-auto-merge.test.mjs # .github/scripts/pr-merge/merge-holds.test.mjs # plugins/source-control/skills/babysit-prs/scripts/tests/test_merge_holds_sync.py
…n-head-from-annotation # Conflicts: # .github/scripts/pr-merge/merge-holds.test.mjs
…n-head-from-annotation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
claude-review reviewed No findings. I read the full diff ( What I checked
Cost: none. The change adds read-only API calls: a full run listing for the branch on each decision check, and a compare per distinct head. Those draw on GitHub API rate limits, not billing. Closing issue: Manifests: none changed. The changelog fragment is |
|
claude-security-review reviewed No security findings. I read the whole diff and the code it gates (all 13 files). Three changed paths are trust controls: critical-findings: 0 What changed. The cost-decision reader no longer takes the decided head from a Why it does not widen the hold:
Manifest items: none touched, so no Permission note (not a finding). The two caller workflows gain Residual risk, already named in the PR. Someone who can delete workflow runs could delete the newest lane run and promote an older notice. The earlier comment design had the same exposure through comment deletion. Closing it needs Cost: none. The change only adds read scopes and extra read-only API calls, which draw on the rate limit and not on money. |
… runs The workflow-runs branch= filter matches head_branch alone, so a fork pull request on a same-named branch could post the cost notice that decides another pull request's head. merge-holds.mjs noticedHead, babysit _noticed_head and the ci-status cost-hold step now skip any lane run whose head_repository.full_name is not this repository, before the vet, so a foreign run neither decides the head nor holds the step. run.pull_requests is not used: GitHub documents it as open pull requests matching the run's head branch or sha, not the one that triggered the run, and it is empty for closed pull requests and fork runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n-head-from-annotation
…ne run noticedHead skips a lane run whose head_repository is another repository, so the enable-auto-merge fixture's run carries this repository, as the merge-holds fixtures do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The pr-unblock lane stopped on this pull request and needs a person: the merge stages a change under .github/workflows/, which the lane does not push.
To resume, deal with the reason above, remove the gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=10357 -f activity=resolve-conflicts |
# Conflicts: # .github/scripts/pr-merge/merge-holds.test.mjs
# Conflicts: # scripts/ci-hold-cost-decision.test.sh
|
The pr-unblock lane escalated this pull request as |
# Conflicts: # .github/scripts/pr-merge/enable-auto-merge.mjs # plugins/source-control/skills/babysit-prs/scripts/tests/test_merge_holds_sync.py
|
Unattended merge: risk HIGH at head |
Post-merge mutation testingKilled 0, survived 0, timeout 0, no coverage 0, invalid 0, not run 8.
The run's summary has the full report. |
Refs: #9635
This PR covers the merge-side half of #9635. The ci-workflows partner covers
hold.cjs, closes the issue, and merges last.Summary
The cost-decision readers took a decision's decided head from the newest unedited
github-actions[bot]claude-lane-hold ... criteria=costcomment before it. Any workflow running onGITHUB_TOKENposts asgithub-actions[bot], so the same-head shortcut rested on a comment author login. All three merge-side readers now read the decided head from the review lanes' own check-run notices, the ones ci-status already vets.Fix
The rule is the same in all three readers:
pull_requestrun ofpr-review-hosted.ymlandpr-review-security-hosted.ymlon the head branch that started before the decision.pr-review / claude-review-status,pr-review-security / security-review) when it finished before the decision. Count itsclaude-lane-hold ... criteria=notices that includecost, and only those whosesha=is the run's own head.Changes per reader:
.github/workflows/pr-require-checks.yml, the cost-hold step):shaand its run's head.carriescompares it.costMarker/deletedSinceare no longer called; onlysameNetChangeandrefsIssueare.REVIEW_LANES,laneChange,costNotices,decidedHead, andnoticedHead, whichcarriescalls.costMarker,deletedSinceandCOMMENT_DELETED_QUERYare removed.PULL_OPENED_QUERYgainsheadRefName.costDecisiontakes an optionalactionsreader (default:github).actionsreader fromBABYSIT_VERIFY_GH_TOKEN. That is the job token, which holdsactions: readandchecks: read; the lanes App token holds neither.REVIEW_LANES,lane_change,cost_notices,decided_headand_noticed_headmirror the JS functions.BABYSIT_VERIFY_GH_TOKENwhen it is set._getand_paginatedtake an optionalenv, passed toghonly when set.cost_marker,deleted_sinceandCOMMENT_DELETEDare removed.pr-review-hosted.ymlandpr-review-security-hosted.ymlnow grantchecks: readon the job that callspr-review-cli.yml, beside theactions: readthey already grant. The ci-workflows partner's status job needs both to read its earlier cost notices, and a called workflow can only narrow its caller's grant. Both scopes are read-only on a job that runs no model. Granting more than the pinned reusable (ba7af78) requests changes nothing until the pin moves.enable-auto-mergeandmergejobs inpr-merge.ymland theci-statusjob inpr-require-checks.ymlalready holdactions: read/checks: read.Residual: someone who can delete workflow runs could delete the newest lane run and promote an older notice, the same way deleting a comment could promote an older marker before. Nothing detects run deletion. That needs
actions: write.Verification
scripts/ci-hold-cost-decision.test.sh: 138 pass, 0 fail.node --test .github/scripts/pr-merge/merge-holds.test.mjs: 29 pass. New cases coverlaneChange,costNotices,decidedHead, the vet, and a separateactionsreader.node --test .github/scripts/pr-merge/enable-auto-merge.test.mjs: 99 pass.python3 -m unittest tests.test_babysit_merge_cost tests.test_merge_holds_sync(babysit-prs scripts): 32 pass. The JS and Python readers agree on 21 carry cases.scripts/affected-tests.sh --run --base origin/main:ci-test-node-stop-commands.test.shandrun-hygiene-gate.test.sh.resolve-configsuites that cannot loadajvin this worktree (ERR_MODULE_NOT_FOUND).actionlint .github/workflows/pr-require-checks.yml: clean.scripts/run-ruff.sh check: clean.actionlinton both callers clean;node --test .github/actions/resolve-config/*.test.mjs(withnpm ciin that folder, which fixes the earlierajvload failure): 701 pass, 0 fail, including a new test pinning each caller's review-job scopes; the pr-merge, pr-unblock, pr-verify, check-* and reconcile node suites: 1750 pass; the eight shell suites that read the callers (ci-review-lanes,ci-hold-cost-decision,ci-rerun-ci-status,workflow-self-paths,read-runner-dispatcher-gate,ci-review-plugin-source,post-merge-demotion-alert,merge-base/prepare): all exit 0.Related
fix/9635-cost-head-from-annotationmakes the same change inapply-review-hold/hold.cjscarryDecision. This PR merges first. It only makes the merge side stricter, and the readers agree in every honest case, becausehold.cjsposts the marker and the notice in the same step.🤖 Generated with Claude Code