Repository navigation
Description
Refs: #12284
Owner: poteto (pr-merge). Measure before building.
Summary
The longest wait on the PR path is between a PR becoming ready and entering the merge queue. Measured over 7 days: p50 0.80 h, p90 7.41 h. Queue entry to merge is p50 0.11 h. Source: audit comment, gate #13.
pr-merge wakes only on three things (.github/workflows/pr-merge.yml:85, :93-98 at cb8429f):
- A 15-minute cron. It ran 607 times in 7 days, 173 run-hours, p50 16.1 min per run.
unlabeled. It ran 1,011 times in 7 days, 206 run-hours, mostly waiting on the shared concurrency group. A 2026-11-01 sample showed a run doing 26 s of work after 21 min queued.- push.
Proposal: wake pr-merge for one PR when pr-verify completes on that PR's head, using a per-PR concurrency group. Then slow the cron to a backstop.
The stated reason this was not done
pr-merge.yml:91-95 says the lane "does not wake on each CI or review completion: those arrive faster than runs finish, which would overflow the concurrency queue below." Any design must answer this. Possible answers:
- A per-PR group, so one PR's wakes collapse into each other and never queue behind other PRs.
- Waking only on pr-verify's completion, not every CI or review completion.
Prior cost cuts on event wakes
pr-merge event wakes have been cut twice on cost. Rework must not bring that cost back.
- Baseline 2026-10-31T08:45Z (hub
baselines.log). There were 237pull_request_targetruns in 24 h. 138 were superseded and 78 gated at a median of 47 calls each, and none enqueued or merged anything. Droppingeditedandready_for_reviewremoved about 200 runs, 3,591 github-token calls and 2,025 lanes-app calls a day. - perf(pr-merge): stop waking on edited and ready_for_review #11493.
Done when
- A baseline is recorded before any change: ready -> first queue entry p50/p90, pr-merge runs per day by event, and github-token and lanes-app calls per run.
- The design answers
pr-merge.yml:91-95(concurrency overflow) with a measured or reasoned bound on queued runs. - pr-merge wakes per PR on pr-verify completion, and the schedule becomes a backstop.
- Ready -> queue p50 and API calls per day are re-measured against the baseline and posted here. Calls per day must not exceed the 2026-10-31 post-cut level.
Metadata
Metadata
Assignees
Labels
Type
Fields
Work status
Activity
github-actions commentedon Nov 2, 2026
Proposed work class: work-class: scoped
Basis: automated intake triage; the lanes App applied this class.
Size: Medium
Complexity: High
Uncertainty: Knowable
Flags: none
Blocked by: none
Understood: wake pr-merge for a single PR when pr-verify completes on its head, using a per-PR concurrency group, and slow the 15-minute cron to a backstop. The aim is to cut the ready-to-queue wait (p50 0.80 h, p90 7.41 h) without raising API calls above the 2026-10-31 post-cut level.
The issue already names the design (per-PR group, pr-verify completion only), so I found nothing open that needs a decision. The measurement items (baseline and re-measure) are for a later step to confirm.
I found no open issue that asks for the same thing. #12284 is the referenced audit.
Automated triage, applied by the lanes App.
melodic-automation-lanes commentedon Nov 2, 2026
Not admitted to the issue lane: a criterion names .github/workflows/pr-merge.yml, which the issue lane cannot push (patch-protected-path); a builder session takes it. Admission takes the issue once each of those is met, when intake triages it again (
.github/actions/resolve-config/admission-rules.json).melodic-automation-lanes commentedon Nov 2, 2026
The latest triage verdict holds nothing: admission moved the issue to
status: confirmed; its comment says why.kyle-sexton commentedon Nov 2, 2026
BEFORE baseline (Done-when 1). Measured 2026-11-02T18:25Z from the hub's runs cache and the jobs'
api-callsannotations. Windows: 7d is 2026-10-26T00Z to 11-02T00Z; 24h is 11-01T17:48:58Z to 11-02T17:48:58Z.Method:
ready_for_reviewat or before the firstadded_to_merge_queue, else PR creation. The sample is spaced evenly by PR number.What this means for the design:
workflow_runfrom pr-verify carries main's head SHA, not the PR's, so the wake has to come from pr-verify with the PR number.kyle-sexton commentedon Nov 2, 2026
Design decided (owner, in the poteto session, 2026-11-02 ~21:4xZ). Asked as Q1, which offered three options:
melodic-automation-lanes) pull request when its verdict is pass or pass-notes. The cron slows from every 15 minutes to every 30, as a backstop..github/scripts/pr-merge/who.mjslets a run started bygithub-actions[bot]from pr-verify on main go on to mint the lanes App token.Owner's answer, verbatim: "Go with recommended." The recommended option was (a).
Evidence (BEFORE, posted above, plus):
post-merge-dispatch(pr-merge.yml:2181). It reads carried verify-answers, not the merge job's output.What the build must do, from the design checks:
actions: write, after the verdict jobs. It sends the PR number as an input, because theworkflow_runpayload carries main's head SHA, not the PR's.post-merge-dispatchand the merge job do not run on it.trustedRunacceptsworkflow_dispatch, so those uploads would feed the next sweep.superseded.mjsdoes not count it as a superseder.who.mjsadmits onlygithub-actions[bot]on aworkflow_dispatchthat carries the PR input, on main. Every other non-human run still stops.Built after #12494 (closes #12448) lands, since both edit
pr-verify.yml.fix(pr-pipeline): reuse the last sweep's report on a wake only under …
kyle-sexton commentedon Nov 3, 2026
kyle-sexton commentedon Nov 3, 2026
AFTER (#12550, window 02:21:57Z-06:14:41Z, 3.88 h)
workflow_dispatchfrom pr-verify, namedpr-merge: arm #N)actions/workflows/pr-merge.yml/runs?event=workflow_dispatch&created>=02:21:57Z, runs 41566007065..41583975932check-kill-switch: proceed=false reason=kill-switch, so the outcome text names the wrong step); 0 "a waiting run does this work"pr-merge-wakenotices from GraphQL check-suite annotations of all 18 runs; log of run 41579071416"reason": "post-merge answer code-changed"(run 41573426067),"reason": "idle tick runs-changed"(run 41583975932)settle --idle-tickoutput in 2 of the 16 logs; the other 14 reasons were not readpr-merge-wakenotices;limits/gh-job-calls.jsonlagrees on 17 of 18pr-merge-wakenotices11,41 * * * *): 7 runs, 02:43-05:44; 3 of them (03:43, 04:44, 05:44) stopped by the lanes kill switch at 0 callspr-merge.yml:118on main; RESTevent=schedule; merge-job notice "the lanes kill switch stops this run" on 41573921312, 41579821734, 41585630549baselines.log:96); 277.4 (237.5) / 339.6 (n=16, 21:57Z-01:57Z)api-callsannotations of 24 scheduled runs 41533006484..41585630549, one GraphQL readbaselines.log:96, the 11-02 post-cut sample); 26,630 schedule on the 21:57Z-01:57Z samplebaselines.log:96); 32,600 on the 21:57Z-01:57Z samplemelodic-automation-lanes), p50created_at, a proxy that runs seconds after the pass artifact. The end point, auto-merge enabled, comes before queue entry, so AFTER is a lower bound against the BEFOREadded_to_merge_queueby kyle-sexton)Three things distort the window:
ci-status. This is the L84 regression; its fix, fix(ci): read a lanes App head's pass by exact name in FAIL index mode #12717, is not merged. Queue-to-merge is not reported.Verdict. The wake fires per PR as designed: 18 dispatches, about 14 github-token calls each, at most 5 in any 15 minutes, and none waited on another run. It armed 1 PR in 18. The other 16 skips came from the standing-report check, which refuses once main or a watched run has moved since the last sweep. The 2 logs read show exactly that. That this holds for most wakes, because merges land every few minutes and the sweep now runs every 30, is inference. The cron still does nearly all lane arming, so pass -> lane arm p50 is 50 min, no better than the 0.57 h BEFORE. Cost: lanes-app is below its bar; github-token, at 13,200-16,600 a day, straddles the 15,275 bar. The two BEFORE samples disagree 1.7x per run (162.5 vs 277.4), so the github-token verdict depends on which one holds.
#12445 cannot close yet.
Re-measure once #12717 merges, the kill switch is off, and the wake's standing-report check stops refusing on a moved main.
Next (poteto): when the last sweep's report does not stand, the wake runs the idle-tick and post-merge check itself, fresh, instead of skipping. No report is reused and no check is skipped. Per-wake calls rise toward the original 45-50 estimate, and that rise is stated in the PR. The same change fixes the outcome text on a kill-switch stop (run 41579071416 says "the who step stopped it"). This issue stays open until done-when 4 is measured.
kyle-sexton commentedon Nov 3, 2026
AFTER v2 (#12771, wakes 2026-11-03T08:17:11Z-15:20:22Z)
Source:
.work/gh-monitor/wake-after-12771.sh: the executedpr-merge-wakeoutcome notice of each of the 25pr-merge: arm #Nruns, 41606554309..41662839689.rerun-not-allowedsettle --idle-tickrefused the fresh report (idle tick runs-changed)verify:earlier-fail, a stopped pr-unblock lane)Verdict. The wake armed 1 PR in 25, so ready -> queue p50 cannot have improved, and done-when 4 is still not met. The issue stays open.
failure-class=timeoutwhile the reviews credential was rejected at its usage limit, and that class did not let the successful attempt 2 bind. Fixed by fix(source-control): bind a review re-run after an unposted timeout #12863 (merged 17:16:43Z). Four of those PRs were queued by hand at about 16:02Z, and the other two carry holds.runs-changed(5), and an arm step that ends with no outcome (3; the outcome text names no step).Next: re-measure about 10 wakes after #12863, outside the review outage.