Why Shipping Got Slow Again

Deep dive, Aug 18 2026 · follow-up to Lead Time: The Top Moves (Aug 10–11) · status: Proposed — pending Fede's review

TL;DR — three separate things, one now fixed, one real, one an illusion.

Typical change today: ~49 min from opened to merged (July: ~25–30) plus ~18–20 min for the deploy to promote. The gap to July is mostly the CI doubling.

The numbers

Opened → merged, by week

WeekChanges mergedMedianOver 3 hours
Jul 6–1249729 min14%
Jul 13–1951530 min17%
Jul 20–2648218 min14%
Jul 27–Aug 256127 min18%
Aug 3–9 the cliff2364.2 h55%
Aug 10–16 recovering17354 min34%
Aug 17–18 still 2× July2949 min17%

Raw open→merge on every merged change, the one locked definition (Aug 10). Merge volume also fell from ~70/day (July) to ~10–30/day — not diagnosed here; flagged at the end.

Time to a green check (the test pipeline)

WindowMedianWorst 10%Jobs per run (median)Full-suite fires on
Jul 13–194.2 min9.2 min612.5% of runs
Aug 10–165.2 min11.3 min
Aug 17–187.8 min18.8 min1530% of runs

Both halves grew: the jobs run longer and they wait ~4× longer for a machine — because 2.5× more jobs per change now compete for the same shared runner capacity. It is not classic runner starvation from outside traffic; it's self-inflicted fan-out. The additions, all Aug 8–11: the always-on guard checks split into 6 parallel jobs per change (meant as a speedup; net effect was more queue contention), a new database-safety gate, a new debounce step, and heavier type-checking. Separately, the "escalate to the full 12-shard test suite" trigger now fires 2.4× as often as July even though an Aug 9 change was meant to reduce re-firing — why is unverified and worth its own look.

Review rounds: what the climbing metric actually measures

Dashboard says rounds per change went 6.7 → 9.9 → 11.4 (30-day → 7-day → last 24 h). Ground truth from the 40 most recent merged changes:

History for the record: during the Aug 3–9 cliff the churn was real — the worst change collected 22 review rounds and 59% of its commits were pure catch-up-to-main syncs, each restarting tests + review. That mechanism (the treadmill) was killed Aug 11 and is verifiably still off: 0 catch-up commits in 156 commits across the 40 recent changes. The freeze rule (round 2+ only pushes for blocking findings) was violated on its own motivating example the day it was written, but with advisory notes now non-blocking and few real findings per change, it's currently a minor factor.

Decisions

Multiple choice; recommendation marked. Answers save for everyone viewing this page.

1. The CI fan-out (the main remaining slowdown)

Each change now runs ~15 jobs instead of ~6, and the expensive full-suite path fires 2.4× as often. Getting green doubled from 4 → 8 min, and that cost repeats on every push of every change.

2. The rounds gauge

The "review rounds per PR" metric counts raw review events, so duplicate bot posts read as ballooning reviews. It misled this very investigation until we read the ground truth.

3. The two fixes built Aug 11 but never switched on

From the previous session: the auto-restart for checks that hang forever (~500 manual pokes in 3 weeks), and the sweeper that picks up approved-but-idle changes / expires stale holds. Both were built and left disarmed for your approval; their status hasn't changed since.

4. Known safety gaps from the Aug 11 incident (still open)

During that night's shipping queue: a "hold" label didn't actually stop the auto-merge sweeper, and a change can merge while a newer test run on itself is still in flight — 5 fixes merged onto a red main during a ~40-min window. Both were logged as known gaps, neither fixed.

Not diagnosed here (flagged honestly)

Sources: the locked pipeline-metrics script (30-day run, Aug 18); week-by-week raw open→merge over 2,473 merged changes; job-level timing on 80 sampled CI runs (July vs Aug); round-by-round timelines of the 10 slowest changes of Aug 11–18 plus review-state classification of the 40 most recent; session transcripts of the Aug 9–11 investigations. All quantitative claims trace to those pulls; items above marked unknown/unverified are exactly that.

PropFlow Docs