Repository navigation
perf(pr-merge): reuse the last tick's post-merge report on an idle tick - #10475
Conversation
post-merge-dispatch's "Check earlier lane merges" ran in full on every tick (median 375 s over 72 ticks, ~115 gh calls), including ticks with no new lane merge since the tick before. It now reads the report the last trusted run on main kept (last-report.mjs, found as the lane records are) and reuses it when `settle --idle-tick` keeps it: the lane room's rule (settle_shared) plus idle_refusal, which covers the rest of what the check reads that can move without a merge (verifying runs completed since, quarantine issues, pins, quarantine excuses, the dispatch deadline, anything to start). The report keeps its computedAt, code and commit, so reuse ends SHARED_MAX_AGE after the check that made it, with 10 minutes left for the merge gate and lane room. pending_runs_unchanged now lists runs per verifying workflow, and the reuse rule refuses a report past an expired quarantine excuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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-security-review reviewed Security review of #10475 at fe29d06: no security findings. critical-findings: 0 Plugin manifests: the diff changes no I read the whole change for every trusted path the diff touches. These are Is any merge hold weakened? I judged no.
Residual risks, not findings:
Cost: this change only shortens or skips existing Actions work and adds no paid plan or billing setting, so no cost hold. |
|
claude-review reviewed No findings. I read the full diff for the non-test files and the cross-file code they depend on. I did not run any tests: this session had no shell, so the test results in the PR description are author-claimed and unverified. What I checked
Closing issues: none (the PR states "No related issue"), so there are no acceptance criteria to check. Manifests: no Cost: the change reduces Actions minutes and Trusted paths: this PR touches Minor, not counted: the README sentence in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ddd88bdfed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…indow An idle tick could reuse a passing report while an older canary in a verified merge's window was still queued: `_judge` verifies on a later green canary, and the queued one fails the merge once it turns red. Only unverified merges moved the run listing's floor past the fixed lookback, so a canary created before it was never listed and the reuse stood. The post-merge report now records when the earliest canary still running in a merge's window was created (`outstandingSince`). `idle_refusal` and `pending_runs_unchanged` move their listing floor back to it and refuse a report without the field. No extra API calls: the floor moves inside the same two per-workflow listings; a listing that no longer fits one page reads as not whole and runs the full check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No related issue: efficiency plan item 1 alternative 2 (idle-tick exit)
Summary
pr-merge's post-merge-dispatch job runs "Check earlier lane merges" in full on every scheduled tick. Over the 72 schedule runs from 2026-10-26 13:57Z to 2026-10-27 13:00Z it took a median 375 s (p90 509 s), and a sample tick made 114 gh calls. That includes ticks with no new lane merge since the tick before: 10:59, 11:23, 11:36, 11:47 and 11:58Z on 2026-10-27 all judged the same 178 merges at main
3c97a87086, each with exit 0.With this PR, an idle tick reuses the report the last tick kept and skips the check. A live probe of the reuse decision on a real 184-merge report took about 15 s and 15 calls (settle 2 s and 3 calls,
idle_refusal13.3 s and 12 calls).Fix
.github/scripts/pr-merge/last-report.mjsdownloads thepr-merge-post-merge-reportartifact. It takes the artifact from the newest scheduled or hand-started run of main'spr-merge.yml, never this run, and only when that run's head commit is on main. This is the samenewestTrustedRun+onMainrule the lane records use, so trusted provenance is unchanged. It shares one run listing with the lane-records read (PR_MERGE_READS_PATH). Any error leaves no report, and the check runs. An in-progress run already lists the artifact: run 40521502018 showedexpired=falsewhile its merge job was still running.babysit_verify_evidence.py settle --idle-tickapplies the lane room's rule first (settle_shared). That rule covers identity, lane, floor, config, check code, age, a new lane merge at a moved head, and the demotion. The report must then also passbabysit_postmerge.idle_refusal, which covers the rest of whatcheckreads that can change without a merge. See the input table below.computedAt, code and code commit as they were, so a chain of reuses endsSHARED_MAX_AGEafter the check that made the report. It writes this run's freshly readlane-records.jsonbeside the report, not the last run's copy. Any other code, refusal or crash runs the check exactly as before.pins), so a later tick can read them again.pending_runs_unchangedand the idle rule share_verifying_runs_unchanged, which lists runs once for each verifying workflow. The old listing covered every workflow on main and held 152 runs in under two hours, more than one page. Behavior change: because the listing is now whole, the merge job and lane room keep more unverified-stale (code 5) reports.shared_refusal(_shared_age_refusal) now refuses a report once a quarantine excuse it carries has expired (excuse-expired). This tightens the merge job and lane room as well.last-report.mjsis added as gate code on the risk floor (owner decision Q26: every pr-merge script is gate code). It is listed inmerge-holds.mjs,babysit_risk.py, thepr-require-checks.ymlhold patterns and both hosted review lanes. The floor count goes from 51 to 52. Nothing is loosened: no trusted-actor rule, token permission,SHARED_MAX_AGEorSHARED_UNVERIFIED_MAX_AGEchanges.Fingerprint inputs and where they are checked
checkreadssettle_shared→shared_refusal(new-merge:#N,merged-unreadable)shared_refusalfloor-differs/config-unreadable;demotion-changed, kept only on an equalread_demotioncheck_code_unchanged(code-changed)excusesExpireAt, quarantine excusesidle_refusalrefuses any excuse (excused, a missing key too) and anyquarantines;shared_refusalexcuse-expired_quarantine_issues)idle_refusalquarantine-changed: any issue with the marker touched sincecomputedAt(a full page counts as touched)idle_refusalruns-changed: no full or canary run completed sincecomputedAtamong runs created since the earliest unverified merge or the_lane_rerunslookback plus oneFULL_RUN_ALLOWANCEidle_refusaldispatch-deadlineread_pin): edits, new pins, deletionsidle_refusalre-reads every recorded pin (pins-changed)idle_refusalrefusesneedsDispatch,reruns,hold,unattributed, alane-rerun-unreadablewarning, and a non-passing verdictassess, lane room)IDLE_HEADROOM(10 min) short ofSHARED_MAX_AGE(stale-soon)Every read failure refuses (
unreadable: …), and the step then runs the full check.Verification
origin/main(ae46394c7f). There,an idle tick reuses the last run's report…failed because the check ran again, andpost-merge-dispatch reads the last run's report…andlast-report.test.mjsfailed. In Python, allIdleRefusaland idleSettleCommandcases failed, as didtest_an_answer_past_its_quarantine_excuse_is_refusedandtest_the_report_records_each_pin_it_read_once.test_babysit_lane_room.py:IdleRefusalcovers each report field, headroom, a full or canary run completed since, a listing that is not whole, the pending-merge floor and deadline, a quarantine-marker issue or a full issue page, and a pin edited, deleted or unreadable.settle_sharedcovers are tested in the existingSettleSharedtests:test_a_moved_head_with_a_lane_merge_the_answer_does_not_count_is_refused,test_a_changed_demotion_is_refused_unless_it_reads_the_same,test_an_answer_from_other_check_code_is_refused_before_any_keepandtest_a_stale_answer_is_refused_even_when_nothing_moved.python3 -m unittest discoverover the babysit-prs scripts: 3197 tests OK.node --test .github/scripts/pr-merge/*.test.mjs: 531 tests pass.scripts/ci-review-lanes.test.shpasses.actionlintis clean. Ruff runs throughscripts/run-ruff.sh.attempt 1 failure; re-run cancelled), which CI's 14:00Z tick also reports with exit 1.settle --idle-tickrefused it asnot-passingin 2 s. Run with the failure and excuse cleared,idle_refusalpassed in 13.3 s and 12 calls, and the 177 re-read pins matched the recorded ones.Related
settle), perf(source-control): index main's first-parent chain and batch pin reads in the post-merge check #10406 (the check's ancestry index and batched pins) and perf(pr-merge): keep the shared post-merge answer when only a no-op demotion edit refused it #10162 (the demotion keep path): this PR reuses their rule and readers.SHARED_MAX_AGE, or re-stampingcomputedAton reuse. Either would recover more ticks, but both are the owner's call.🤖 Generated with Claude Code