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.
kyle-sexton commentedon Nov 4, 2026
AFTER v3 (#12863, window 2026-11-03T17:16:43Z-2026-11-04T00:00Z, 6.72 h)
Wake outcomes (23
pr-merge: arm #Nruns, 41682610597..41739698017; executedpr-merge-wakenotice of each,.work/gh-monitor/wake-after-12863.sh):rerun-not-allowedsettle --idle-tickrefused the fresh report (runs-changed8,needs-dispatch1)#12863 removed the largest refusal class; the wake now arms 43% of the time (from 4%).
Done-when 4:
issues/<n>/timelineper PRlimits/gh-job-calls.jsonl,pr-merge.yml:*, calls minus graphql, 160 rows dedupedactions/workflows/pr-merge.yml/runsVerdict. Done-when 4 does not hold: ready -> queue p50 is flat and github-token calls run about 13% over the bar. The issue stays open.
Next lever. The 9 idle-tick-refused wakes spent 63-71 calls each and armed nothing: about 590 calls in the window, roughly 2,100 a day, which alone covers the overage. Moving the idle-tick refusal ahead of the reads it does not depend on, so a refused wake stops near the cost of a kill-switch skip, is the next change. Caveats: the 24 h figures are linear extrapolations, and 12 scheduled runs in 6.72 h is about 43 a day against 48 expected (UNVERIFIED cause).
kyle-sexton commentedon Nov 4, 2026
AFTER v4 (calls, post-#13040 window). Done-when 4 is still unmet on calls. p50 is met.
Next: arm-on-green design (the hub's ci-timing audit puts green to first merge-group run at 53% of push-to-merge minutes), which folds in the zero-call wake skip for armed or held PRs. Re-measure on a full window after it lands.
Basis:
.work/gh-monitor/trends/12445-after-v5-c.pyoverlimits/gh-job-calls.jsonl(rows deduplicated by run, job and reset; GraphQL excluded).kyle-sexton commentedon Nov 4, 2026
AFTER v5 (calls, clean window). Calls/day is still over the bar, but calls per merged PR fell by a third.
Window: 2026-11-04T06:47:55Z (#13112 merged, after #13096 and #13107) to 08:48:00Z, 2.0 h. It comes after #13084's counting change (which raised counted calls), and with no hub merge-kick sweeps. The sampler last read at 08:45:59Z, so the tail is slightly undercounted.
Basis:
.work/gh-monitor/trends/12445-after-v5-c.py 2026-11-04T06:47:55Z 2026-11-04T08:48:00Zoverlimits/gh-job-calls.jsonl(deduplicated, GraphQL excluded); wake outcomes from the enable-auto-merge job annotations.kyle-sexton commentedon Nov 4, 2026
Agent choice record (poteto): amend done-when 4's calls bar from calls/day to calls per merged PR, plus no pr-merge-caused breaker trip.
Why: the bar "Calls per day must not exceed the 2026-10-31 post-cut level" sits under "Prior cost cuts on event wakes: rework must not bring that cost back". It guards against wasted event-wake runs (the 2026-10-31 baseline: 138 superseded and 78 gated runs that merged nothing). A per-day total also grows with merge volume, so it penalizes throughput, which is the goal of this issue.
Evidence:
Amended done-when 4: ready -> queue p50 at or below the baseline (met: 0.36 h vs 0.80 h). pr-merge github-token calls per merged PR at or below 56.5 over a 24 h window that starts after the last pr-merge call-cutting merge (#13143, 08:00:58Z), excluding 09:54-11:44Z. No breaker trip in that window that pr-merge's share explains.
Judged at 2026-11-05T08:07Z. On a pass this issue closes with the numbers posted here. On a fail, the next cuts are enable-auto-merge #5 and #7 (trends/eam-calls/result.md).