CI velocity plan

Getting a PR from open to live in under an hour.

Updated Tue Oct 20, 1:30 PM (3-day close-out written; watch stopped on Fede's call; all times Mountain). Fede's goal: open to live 3.6 h → under 1.5 h by Wed Oct 7, 1 PM, and 1 h by Fri Oct 9. Measured from GitHub and the runner job tables; dollars are estimates, not invoices.

Where we are (Oct 16). The queue is dialed in: 11-minute wait, 94% of PRs merge first try, main green. Merge to live is 60 minutes, tripled since Oct 12 by the 30-minute deploy wait; open to live is 116. Cost per merged PR is about $0.70. The Oct 16 deep dive below has the three opportunities and the three decisions.

Contents

  1. Big swings, Oct 16 night: one model, $142 → $68 a day, live rollout
  2. Halving plan, Oct 16: two big moves, then housekeeping
  3. Deep dive, Oct 16: what moved, and the top three opportunities
  4. Scoreboard: the six numbers
  5. Week of Oct 1–8: what changed
  6. Factory dashboard audit (Oct 7, 8 PM)
  7. How the queue loses time, and tonight's fixes
  8. Tonight: production held for two hours
  9. The plan: now, next, later
  10. Decisions still needed
  11. Retro: 48 hours to Oct 6
  12. Numbers and sources
Deep dive, Oct 16: what moved, and the top three opportunities  Proposed — pending review

Written Fri Oct 16 from three read-only inspections of the factory store, GitHub, AWS Cost Explorer and Slack, then fact-checked claim by claim (98 claims, 17 corrected). Nothing was changed. Every number traces to a command in the three reports or to the factory's live scorecard; days are UTC days, the factory's bucket, clock times are MT: merge to live · the Oct 14–15 jump · where the dollars go · the fact-check.

The short version. CI is stable: main was red for only 17 minutes in the last three days (Oct 15, 12:39–12:57 PM), the queue wait is 11 minutes against a 30-minute target, and 94% of PRs merge on the first try. Two things did move. Merge to live tripled on Oct 12 because the production deploy wait was switched from 5 back to 30 minutes. The Oct 14–15 cost jump is mostly more work, not more waste: the swarms merged 1.6 times as many PRs, and the cost per merged PR ($0.69–0.72) stayed inside the previous week's range ($0.51–0.77). The yellow reruns are real but small, and three quarters of them came from two flaky tests.

What moved

NumberBeforeAfterWhy
Merge to live, median20 min (Oct 7–11)60 min (Oct 12–16)The deploy wait went 5 → 30 min on Oct 11, 8:25 PM. Nothing else in the path changed: waiting for the full test on main 7–8 min, the test itself 9 min, both flat.
Open to live, median74 min116 minEssentially all of it is the deploy wait. Open to merge stayed at 51–52 min.
PRs live within an hour of opening32%3%Same cause.
Production deploys per day7939 (42 on the full days Oct 12–15)The only thing the 30-minute wait buys: fewer Vercel builds, worth $3–7 a day.
AWS cost per day$91–131 (Oct 10–13, Sat–Tue)$202–203 (Oct 14–15; Oct 15 not final)292 and 280 PRs merged, against 155–202 a day before. The previous Thu–Fri cost $211 and $217. Cost per merged PR: $0.69–0.72, inside the previous week's $0.51–0.77.
Yellow: reruns behind a red, machine hours0–31262 / 401About $9 and $14 a day. Two flaky tests caused 72% (Oct 14) and 78% (Oct 15) of it; see below.

Three things the Cost tab does not say

  • Nothing is idle. On a normal day, 92% of the machine time AWS bills is real jobs plus their boot. There is no idle fleet to switch off.
  • The tab's per-job dollar figures run about 2.5× what the same job costs on a normal day (estimate). Its 7-day window includes the surge, and it deliberately loads the whole EC2 bill onto the two kinds of CI hours it measures, which carry only 51–55% of the dollars. That is how it is built, not a bug.
  • A bigger waste than the yellow is counted nowhere: PR checks cancelled mid-run, 385 runs (371 replaced by a newer push) and about $34 over Oct 14–15. Twelve of thirteen sampled replacing pushes were new commits on the same PR, not merges from main.

Top three opportunities

#HypothesisChangeValidate before touching anythingUndoStability riskExpected gain
1The 30-minute deploy wait is essentially the whole merge-to-live regression, and it made deploys less stable. Since the wait went back to 30 there have been six “production deploy failed” alerts; five trace to the wait (four machines taken back by AWS while the deploy job sat idle, one job that could not read main while holding). The 5-minute week had one alert, a real build error. Gera's agent set it at 8:25 PM on Oct 11, at his request, to save Vercel builds; no PR, no test, and the decision page still shows it unanswered (the who-said-what is in the merge-to-live report).Set the wait back to 5 minutes.Already validated: five days at each setting (the second still running), same deploy code, similar load (246 vs 228 merged PRs a day). Merge to live 20 vs 60, deploys 79 vs 39, full test runs on main 83 vs 83, AWS unchanged.One setting.Low and likely a gainMerge to live 60 → 20 min; open to live 116 → 74. Costs $3–7 a day on Vercel.
2Each time a flaky test fails in the queue, GitHub throws away the 6–8 trains behind it. The two flaky tests on Oct 14–15 (each edited by the PR that merged just before) cost 44 and 69 trains, $8 and $13, over 50 minutes and 1 h 45. The red-main check did not stop it: it watches only the full test on main, went red once 73 minutes in, and cleared itself 17 minutes later while trains kept failing. A third episode ($5, 24 trains) was a test whose built-in review date passed.(a) Stop the line when one test fails for two unrelated PRs within 30 minutes. (b) When a PR edits a test file, run that file several times in its own PR checks.(a) A read-only card in the factory app for a week, no action. A replay since Oct 8 shows 5 fires: 2 flaky tests, 1 expired-date test, 1 real break, 1 unchecked. (b) A one-time local replay, 20 runs per test version; attempted and stopped because the repo rule caps local test loops at 3 runs per 15 min, so it needs Fede's one-line go. The CI record (same commit passing and failing within minutes) is the primary evidence either way.(a) Delete the card. (b) One workflow line.Low for the card and the replay; Medium once it actsAbout $6–13 on a bad day ($18.5 over Oct 14–15, estimate), and the trains behind the red.
3About $8–11 a day (estimate) could come off a normal day without changing which tests run. Machine labels like “r8g” also pick premium variants that cost 1.5–2.5× the base price (about $2.8 a day extra on normal days, $5.7 a day averaged over Oct 8–15). An unused stopped warm pool costs about $1 a day with zero jobs on it. Runner machine logs cost about $2 a day. PR test shards spend 66 of their 419 seconds pulling full git history.Name exact machine types and move 2-CPU helper jobs to arm (these two overlap); remove the warm pool; trim runner logs; poll Cost Explorer less often; shallow checkout on test shards.Machine types: spot placement scores read Oct 16: safe to narrow the PR-only jobs ($3.6 of the $4.4 a day premium cost; keep at least 4 types), hold the two queue job classes (zone scores drop). All 13 reclaims on Oct 14–15 were on a base type that stays. Re-run on a busy afternoon first. Shallow checkout: the read-only replay first, because full history is what makes “a known-red file this PR edits still blocks” work.One config line each.Low housekeeping; Low–medium machine types and checkoutAbout $2.8 + $3 + $2 a day (estimates); about 45 s off every test shard.

Decisions, one line each

  1. Put the deploy wait back to 5 minutes now? Recommend yes. It is a production setting and it reverses the change Gera's agent made on Oct 11 at his request, so it is Fede's and Gera's call, not an agent's. If Gera still wants fewer Vercel builds, the follow-up is a deploy rule that ships the newest tested code when the wait ends: a replay projects about 35 minutes at the same 40 builds a day, but it has never run for real and touches deploy ordering, so it comes after, as its own validated PR.
  2. Build the read-only “stop the line” card in the factory app, and allow the one-time 80-run local flaky replay? Recommend yes. No customer or CI effect. The factory app is Gera's repo, so the card goes to Smith on the bridge.
  3. Narrow the machine types and remove the unused warm pool, after the placement-score check? Recommend yes, validate first. These are runner-service and AWS config writes, so they wait for Fede's go after the check is posted here.

Still open

  • Does a freshly deployed site still load slowly for users? That was the original reason for batching deploys; the reports found no measurement since warm-up steps were added.
  • What is the $5–6 a day of data leaving AWS? Likely cache saves and artifact uploads to GitHub (not yet measured).
  • Why AWS Config still costs about $2 a day after the September fix.
  • The factory's flaky-test list missed the Oct 14 test, and the Reds tab splits one flaky test across 5–6 culprit labels, so this pattern is hard to see on the page itself. The scorecard also showed main 100% green on Oct 15 while the red-spell record shows 17 red minutes.
Big swings, Oct 16 night: one model, $142 → $68 a day, live rollout  Proposed — pending review

Sat Oct 17, 2:40 PM MT: Fede chose live rollout over shadow mode. Steps 1, 2b, 3 and 5 ship as small PRs with no switch and a 6-hourly watcher for three days; the fold and the deploy-wait change are dropped; batching (2a) is on hold on Fede's yes at 3:10 PM, because GitHub's queue setting tests every PR separately (no saving, up to 15 min slower; tried Sep 13) and the real saving needs our bot to become the merger, a decision for next week with Gera. Savings on the page are also read as percent of the $142 bill: chip 11–13%, precise tests up to 19%, batching 9–14% (held), checks at arm 4–7%, no second full test 4%. The two paid shadows are off; the selector shadow stays until the queue PR lands.

Live rollout, Sat Oct 17 evening: what is on main now

Percent is of the $150 a day the bill averaged Oct 11–15 (m, factory). Watch so far (Sun 12:55 PM): 4 ticks over 18 hours, 0 red main pushes, 0 missed tests, production level with main, sweeps green every 2 hours, big jobs on the newer chip. One train lost its machine to a spot take-back at 1:58 AM; the retry fired, passed, and the PR merged 10 minutes later. Dollars: AWS cost data arrives about a day late, so Saturday still reads as partial ($138, high because the shadows and bot trains ran until 2:40 PM and we merged ~10 PRs); the first clean day with every step live is Monday Oct 19, readable Tuesday morning. Each line is a small PR with no switch; the watcher checks red-main rate, selector misses, machine families, sweep and promotion, and the daily dollars every 6 hours for three days and a fixer acts on a regression.

StepWhat changedSaves (% of bill)Status
5. ChipEvery machine label asks for the newer, cheaper chip first and falls back to the older one (no more all-or-nothing, which broke the queue 3 times before).~7% (3–10)Merged 3:40 PM (#20837). Watch: machine families, launch failures.
1. Precise testsPR checks run only the test files the change can reach; the queue's full-set machines do the same, and run everything when any changed file is missing from the map.~3% measured by the builder ($4–5 a day, e), not the 19% modeled: the selector always pins ~600 tests so small PRs fall back to the full list, and the "any unmapped file runs everything" rule sends 68% of trains (36 of 53, Oct 16–17) to the full set. Replays: 51 of 51 PR failures caught; queue 97 of 109 caught, about 3 likely real misses in 14 days, each caught by the sweep and reverted.PR side merged 5:10 PM (#20856); queue side merged 7:45 PM (#20867); the selector shadow is off, all four shadows are now off. Decision for Fede: exempt newly added files from the "run everything" rule? Yes = 39 of 53 trains precise, about $1–2 a day more, likely misses ~12 per 14 days instead of ~3. Recommend hold a week, decide on live numbers.
2b. SweepThe full re-test after every merge becomes a full sweep every 2 hours; production advances only when the queue, the tip's checks and the newest sweep are all green.~5%Merged 4:55 PM (#20851); first sweep green, production moved at 5:27 PM. Watch: sweep reds, release age.
CI ChecksType check with the faster checker on queue trains; the checks machine is 8 cores on the newer chip instead of 16.~7%Merged 5:45 PM (#20858). Watch: train wall time, memory kills.
Guard pinsOne guard-pin job per train instead of three.~1%Merged 7:40 PM on its third train (#20859). Its first two trains died when AWS took back the spot machine running the checks job, twice in 10 minutes (confirmed in AWS); not this PR's code.
Spot-kill retry (fix found by the watch)When a spot machine is taken back mid-train, the runner is supposed to mark the job so our fail-fast holds and retries it in the same train. The mark was missing on all 3 cancelled trains since Oct 14, so each train died and the PR was thrown out of the queue. Fix: a kill signal (exit 137) with no real test failure now counts as a take-back and gets the one retry.stability, not dollars (fewer dead trains, fewer re-queues); matters more on the newer chip, whose take-back rate is 2.2% vs 0.26%Merged 8:35 PM (#20870), no switch; proof is the next machine take-back in a train. Out of scope: a kill that leaves only "canceled" behind (1 of the 3).
3. Checks at armPR checks run when the PR is armed to merge, not on every push.~3%Held for Fede: the repo rule says arm only a PR whose checks are already green (Gera's cap counter). Built (#20840), not armed.
2a. BatchingSeveral PRs tested per train.9–14%Held (Fede, 3:10 PM): GitHub's setting tests each PR anyway; needs our bot as merger, next week with Gera.

Close-out, Tue Oct 20 1:30 PM: three days live, before → after

The watch ended early on Fede's call (it was due at 5:52 PM). Twelve ticks over three days: no red push to main caused by a step, every red merge run either rescued by the spot retry or a real PR conflict, sweeps and releases green on schedule, every job got a machine. One regression (the first batch train stalled) was recovered by hand and fixed forward the same morning. All numbers below are measured (m) from the factory cost endpoint (fetched 12:48 PM Tue) unless marked (e). The Tuesday bill is the first full weekday with everything live; it posts Wed Oct 21.

WhatBefore (Oct 11–17 avg)AfterSource
Machine hours per merge-queue run5.284.28 Mon, 4.40 Tue so far (−17 to −19%)factory days[]: usefulH / runs (m)
Machine hours per PR check run3.502.07 Mon, 2.41 Tue so farfactory days[]: prH / prRuns (m). Not a clean compare: checks-at-arm adds cheap unarmed runs, so per commit (−14%, Mon morning) is the fair number.
Heavy re-test runs on pushes to main265 a day12 sweeps a dayworkflow 371849834 run counts (m)
Re-run hours (queue)197 a day (Oct 13–16)3.8 Mon, 9.5 Tue so farfactory days[]: rerunH (m); cause not separated
PRs per merge-queue run1batch bot live: first train landed 4 PRs in 1 run (Mon 11:32 PM)train #21369 (m)
AWS spend per day$152 total AWS (Oct 11–17 avg, m, factory spend[]); plan baseline $142Fri Oct 17 $137.94 (complete, m), Sun Oct 18 $43.11 (complete, quiet day, m), Mon Oct 19 $110.92 (incomplete, m, factory spend[] 12:48 PM), Tue Oct 20 pendingfactory /api/cost spend[]

Verdict. Per unit of work, CI is about 15–20% cheaper and stable. Measured savings at normal volume: about $22/day (m, table below), before batching and the precise selector, which only went live Mon Oct 19 (selector about $2.6/day, m; batching modeled $14/day, e). The plan's $63–68/day is not proven; the Oct 20–21 bills decide it. Open, owned elsewhere: the Fri Oct 23 merge-queue pass-rate check (bar 95%, was 90.2%) is handed to Smith on the engineering bridge; the batch bot's first real red train has not happened yet, so its break-up path is merged but unproven live.

Measured, Mon Oct 19 morning: what the rollout actually saved so far

Everything below is measured (m) unless marked (e). The AWS bill itself cannot show the rollout yet: the last complete billing day is Fri Oct 17, the day before the rollout, and Cost Explorer lags about 24 hours. The first full day with every change live is Tue Oct 20. Rate for every $ line: $0.0253 per vCPU-hour, all-in (Oct 17 AWS CI about $124 for 4,904 vCPU-h, m, next-savings report); at the factory's own EC2 rate ($0.0354) multiply by 1.4. Volume for $/day: the Oct 11–17 average (468 PR runs, 258 trains a day, m, factory). Sunday Oct 18 was quiet (79 PR runs), so a raw $/day compare would flatter us; these are per-unit changes times normal volume. Watch, ticks 7–8 (7 AM and 1 PM Mon): 0 red pushes to main and 0 red trains in the last 6 hours of each tick, sweeps and releases green on schedule, all test jobs on the newer chip. Batching bot live since 9:07 AM after a fix forward (its park step read a field the runner's GitHub CLI lacks); no train formed yet on a quiet Monday. Ticks 9–10 (7 PM Mon, 1 AM Tue): 0 red pushes to main. Every red-looking merge train was a spot machine taken back mid-run; the in-run retry passed and the PR merged on the same train, so none were lost. The spot pool for the checks job was widened to six machine families anyway. The batching bot formed its first real train at 11:57 PM (three PRs). The train caught a genuine conflict between one PR and main, but the bot had no way to break up a train that fails before reaching the queue, so the three PRs sat parked for about an hour. They were put back in the queue by hand and the bot returned to shadow while that gap is fixed.

LaneShippedPlan $/day (e)Measured so far (m)Proof
Whole PR sideall PR-side steps—3.88 → 3.33 vCPU-h per pushed commit (−14%), Oct 17 vs Oct 19 so far. About $6/day at normal volume.factory /api/cost, PR vCPU-h over distinct head commits
Whole merge queueall queue-side steps—5.28 → 4.22 vCPU-h per train (−20%), Oct 11–17 avg vs Oct 19; re-runs excluded. About $7/day at normal volume.factory usefulH / runs
2b. Sweep instead of re-test after every merge#20851 Sat 5 PM, #209616.0Heavy re-test runs 265 a day (Oct 11–16) → 12 (Oct 18) → 8 so far Oct 19, −95%. The 12 sweeps cost about $1.9/day. Saving at least $4.9/day; the "before" cost is not settled (two samples 406 and 42 vCPU-min).workflow 371849834 run counts; sweeps 39236991921, 39223690888
6. Advisory tests + harnesses nightly#20996 11 PM2.1–2.350 advisory and 0 harness runs on pushes to main since merge (45 advisory push runs on Oct 18, 40 vCPU-min each, about 30 vCPU-h). At Sunday's 45 push runs that is $0.74/day (m); at the normal 258 merges a day and the measured 40 vCPU-min per run it is about $4/day (e, from the measured per-run cost). Finding: the advisory run still hangs in its mutation-control step until the 20-minute cancel, before and after, so #20983 is a hanging step, not a missing machine.runs by workflow id 370674800 / 371576146; runs 39194923206, 39218825403
5. Chip swap, remaining x86 jobs#20979, #20981 ~10:40 PM0.8–0.9 (chip-swap report)Machine label moved x86 → arm64, durations unchanged (queue re-entry guard 0.3–0.4 min, harness filter 2.0 vCPU-min both sides). Worth about $0.79/day (e) from hourly prices; the billed instance was not pulled.guard runs 39199751564 / 39199454256 → 39250959217; harness 39194923126 → 39201346424
3. Checks at arm#20840 9:29 PM11.6 combinedFlat so far. An unarmed push now costs 52 vCPU-min instead of 276 (sec60, n=6 vs 16). But overnight sessions armed every PR at open, so commits got 1.56 runs instead of 1.05 and the arm run added back what the push saved. It pays once more than ~19% of commits are superseded before arming.jobs API for 30 runs, measure/sample_ids.json
1. Precise test selector#21014, #21015, #21033 12–1 AM~2.6 (re-modeled)Not separable yet. Indicative: unit-test shard groups per tested PR run 6.8 → 4.6 (n=5 / 7). Queue side is inside the −20% above.same job samples
2a. Batch trains#21032, #21048, #21061 1–3 AM, dark14.1 combined$0. First real train Mon 11:57 PM (3 PRs). It went red on a real conflict and stalled about an hour, because the bot could not yet break up a train that fails its own PR checks. Back in shadow while that is fixed. Before that: The bot was off overnight (its arm-triggered check showed red on PRs until the paging fix); set to shadow 6:23 AM; the dry run passed with an empty plan (nothing armed at that minute). Goes live on the first real shadow plan.run 39251728292; batch-trains report
4. Glue foldsnone4.4$0, nothing shipped: no fold clears $0.5/day without widening a job's token (Fede's call).glue-fold report

Sum of what is measurable this morning: about $22/day at normal volume: the two "whole side" rows ($13, which already contain the chip swap, checks at arm and the selector) plus the two push-to-main lanes that sit outside both columns, the re-test sweep (at least $4.9) and the nightly advisory move (about $4, e from the measured per-run cost). The plan modeled $63–68/day. The gap is mostly the lanes that have not had a chance to show yet: batching ($14 e, zero trains so far), checks at arm ($12 e, cancelled by arm-at-open), and the selector. AWS bill, last complete days (m, factory, UTC days, EC2 compute $ / total AWS $): Oct 14 $158 / $202, Oct 15 $155 / $203, Oct 16 $128 / $175, Oct 17 $90 / $138; Oct 11–17 average $111 / $152. Oct 18 and 19 are incomplete and not comparable yet.

Updated Sat Oct 17, 7:05 AM, after the overnight proof fleet (five lanes, five skeptics, one reconciler) and the shadows. Marks: (m) measured, (e) estimate, (r) replay of real records. Evidence, tracking issue #20581. What changed tonight: three dark workflow files and a shadow bot merged to main; the fold shadow was redesigned after a blocking review before it merged. They log only, gate nothing, comment on nothing (the bot creates and deletes its own shadow-batch branches); the required checks on main and the merge guard are untouched; nothing in AWS or GitHub settings changed.

The short version

The bill is a PR counter: 239 merged PRs a day (m), each starting about 117 machines (m) and paying for 2.9 complete test runs (m). Together, after tonight's replays: $142 to about $68 a day, 52% off (e, range $59–80) with 15-minute batches, if agents arm their PRs when the work is done. If they arm at PR open, as today's no-local-type-check rule pushes them to, it is $71, right on the 50% line. The evening model's $63 priced step 3 with the bugs per-push checks catch as free (+$4) and batching from a coarser replay (+$1). The $2,000-a-month line, about $53 of CI/CD, is not reached in any case: it needs about 160 merged PRs a day instead of 239, or a rented server.

The staircase ($/day, all-in, one model, 15-minute batches)

"Alone" is the swing by itself against today's $142; "stacked" is what it still removes after the earlier steps, which shrink the same machines. Only the stacked column adds up: $74. At 30-minute batches the end is $67 (range $58–79).

StepWhat changesAloneStackedBill afterConfidenceProof status, Sat 7:05 AM MT
Today142.0
1Test once + precise test choice (PR and queue)27.327.3114.8pendingShadow live on main since 11:58 PM Fri (#20623): every red train and 1 in 4 green logs the chosen list beside what ran; 11 trains logged by 6:40 AM (10 green, 1 cancelled, no red yet, so 0 misses of 0); on them the precise list would have run 21% of the test CPU (606 of 2,872 CPU-minutes, e) and 810 files at the median against 3,438 ran. Replay of 99 red trains: 83 caught, 24 of 29 real failures; 14 of the 16 misses sit on 4 files that also failed with main already red (hindsight: 44 trains used a map built after them). Bar: 0 misses on 10+ real failures, about Tue Oct 20 Final shadow tally (Tue Oct 20): 37 trains logged, 0 real failures in the window, so 0 misses proves nothing; bar (0 misses on 10+ real failures) not met. The precise list was 13–58% of the full set’s machine time (5 samples). Shipped live Oct 19 on the 14-day replay instead; measured ~$2.6/day (m).
2aBatch PRs per queue run (3 at 15-minute windows, 5–6 at 30)20.312.8102.0replay, in partReplay of 16 days of real merges: $17.4 a day at 15-minute batches, $22.4 at 30 (r, machine cost only). On the model's all-in basis that is $20.3 / $26.0 alone, so v2's $25.5 holds only at 30 minutes; the stacked figure is modeled. Shadow bot live since 12:17 AM (#20617, #20616): groups real armed PRs each 30-minute window, dispatches dark trains (cap 24 a night), bisects reds, merges nothing. Its first batch caught a defect in our own dark train (checkouts without credentials, 4 false reds), fixed 2:01 AM (#20659). Since then 5 dark trains finished, all green: 7 PRs in 5 batches, 7.6–8.5 min each (m); the real queue ran 18 trains in those windows, because many PRs enqueue without arming Final shadow tally (Tue Oct 20): 22 dark trains; the first 6 were false reds from a defect in the shadow itself (fixed, #20659), then 16 of 16 green. 41 PRs in 18 batches (median 2, max 5) against 61 real trains in the same windows: about half the trains at Oct 17 volume. Real bot live Oct 19; first train landed Mon Oct 19, 11:32 PM (4 PRs, one queue run).
2bDrop the post-merge re-test; full sweep every 2 h6.05.996.1modeledReplay of the record: in 9 days of today's setup, 1 real break the queue passed (#19094) and the re-test kept out of production; the other 13 red spells were flakes, a clock test or a dead machine, held deploys ~180 min and caught nothing. Since Sep 30: 8 real misses in 35 spells (r). The dollars are the model's prices on measured volume; no sweep has run
2cShip the batch's build; main's run becomes a trigger1.33.292.9modeledMeasured: the train's build cannot ship as-is (test build, arm64, never uploaded), so the Vercel part stays Gera's. A thin trigger alone is $1.3 and 1 minute; with 2b, merges reach production 13 minutes sooner (r)
3PR checks only when armed, not on every push9.66.486.5modeledReplay of 2,155 merged PRs: runs per merged PR 2.09 → 1.30 mechanically, 1.53 once each caught bug costs a re-run (r). Worth $9.6 alone only if agents arm when the work is done; arming at open gives $1.8; Option B (type check and build every push, tests at arm) $5.3 alone, $2.0 stacked. v2's $15.5 counted the up to 55 bugs a day per-push checks catch as free
4Fold the glue machines6.94.781.8pendingFold shadow #20628 live on main since 2:16 AM Sat. Its first version re-ran PR code inside a run that carries main's context (a cache-poisoning path the repo's rule 9 forbids) and was blocked; the merged version re-runs only main's own commits, 1 in 2 push runs, folded on one 8-CPU machine, paired with the merge-queue run that tested the same commit. 7 samples by 6:40 AM: 6 agree on every verdict, 1 shows a guard-pin red only in the folded run: diagnosed, the fold ran 103 bookkeeping pins the queue skips (fixed, #20736 merged 7:20 AM) and one test-runner timeout fired after every file had passed, a load effect of one process doing three shards' work, which the samples keep counting; 6 machines become 1, $0.103 becomes $0.050 per sampled run on the covered jobs (e, lane 1 rates), 6.6 min becomes 13.8 on the one machine. About 50–60 samples a day; the bar is 500 samples with every verdict agreeing and no more load reds than the real shards get, about Oct 26, when the shadow stops itself Final shadow tally (Tue Oct 20): 29 samples, 22 agree, 7 false reds from one cause (guard pins scanning a temp copy in parallel). One machine instead of six, $0.097 → $0.027 per run (m, shadow artifacts), but slower (median 7.2 → 11.1 min). Bar not met; stays dropped. Only 2 newer-vs-older chip pairs (ratio 0.74), too few to judge.
5Chip swap: Graviton4 first, x86 helpers to ARM25.413.668.3pending7-day spot prices support a $19.4–19.6 ceiling (m prices, e speed ratio; chip part only); the lane's $14.1 "realistic" is 75% of it and unmeasured, and the skeptic's $12.8 adds a take-back cost 5× higher. Take-backs, not launches, are the risk: 2.2% on Graviton4 vs 0.26% on Graviton3 (m, 9 days). The fold shadow records the chip each leg got: one cross-generation pair so far, Graviton4 at 0.81 of Graviton3's time (m, one pair); only a 48 h canary measures the rest

Each swing: the fence it crosses, what was tried, how we prove it, how we undo it

SwingFence it crossesTried before → what is different nowProof (zero stability risk) · undo
1. Test once + precise choice"Never red main" and the rule that no pre-merge guard goes until the post-merge net catches a real break (it did, Oct 6). Switch only after the shadow shows 0 misses; Fede and Gera's call.A tree-hash cache (Oct) skipped 1 run in 141. Per-test result keys were approved Oct 5 and never built because the test-read map did not exist. The map merged Oct 13; a chooser on it caught 83 of 99 red trains replayed tonight, 24 of 29 real failures (hindsight: 44 trains used a map built after them).Shadow on every real train logs the chosen list beside what ran; pass bar 0 misses over 10+ real failures. Result keys logged as "would skip" on main; pass bar 0 real failures on a passed key. Undo: revert the selection PR.
2a. Batch PRs per trainGitHub's merge queue, which cannot batch builds, and "the queue is the only way onto main": our bot (or Trunk, free tier, or Mergify at ~$2–4 a day) becomes that way. "Don't slow shipping" if batches wait to fill.Five native batching tries Jul–Oct 1 never batched; the Sep 13 one died in 42 min because required checks never reported. Trunk and Mergify were researched Oct 2–4, never tried. Planned: the bot reports its own check; the ruleset requires one job ("Nested Gate"), classic protection also names Build, Type Check, Lint, Unit Tests, the leasing gate and review; test jobs would retry once on a spot reclaim (35 of 132 queue reds were reclaims; queue jobs run with retry off today).The shadow bot is live: groups real armed PRs each 30-minute window, pushes a batch branch, dispatches a dark train, bisects on red, merges nothing. Plus the 16-day replay of real merges as 15- and 30-minute batches. Undo: stop the bot; GitHub's queue never left.
2b. Drop the post-merge re-testProduction ships only commits the post-merge run proved. The sweep every 2 h keeps that: red sweep bisects and reverts first; a break the queue misses could run live up to about 2.5 h (1 such case in 9 days)."Ship on queue-green, heavy run as alarm" was proposed Oct 6 and waited on Fede. Trinity's Oct 6 look-back: 42 reds, 0 production reverts in 14 days; since Sep 30 the run went red 95 times, and before the gate 2 of 3 real misses shipped. A clock-dependent test gave 4 main reds on Oct 10, which is why the sweep is hours, not nightly.Replay of the heavy run's record (done); the sweep runs beside it for two weeks. Undo: turn the heavy run back on.
2c. Ship the batch's buildThe merge-guard code and the Vercel build audit are Gera's. The deploy trigger moves to a thin run on main; it is not deleted."Build once" was raised Sep 13 and never started. Vercel's billing for prebuilt deploys is unconfirmed.One throwaway prebuilt deploy (Gera). Undo: a Vercel setting.
3. PR checks only when armedGera's rule that a push gets a verdict in the cloud (ADR-0040). Agents iterate with the cloud-test command, live since Oct (208 runs a day)."Test each change once" was proposed three times and blocked by the rulings. Now cloud-test exists and the train still gates main.Replay of 2,155 merged PRs (done): runs if only the arm run counted, and where their red catches surface. Then a 1-in-4 sampled live trial. Undo: re-enable the per-push trigger.
4. Fold the glueRequired-check names and the merge guard (Gera); one folded job gains read permissions (Fede).The Sep 9 fold combined real work, so one lost machine failed three required checks. Smith's Oct 5 "tiny jobs together" was never built. The one-machine trial's second version never ran. Now: fold only the small helper jobs; none of the Oct 14–15 reclaims hit them.Shadow live: main's own commits re-run folded, trusted code only; pass bar: same verdicts across 500 samples. Undo: Revert button.
5. Chip swapNone. 48 h canary, one job class at a time, PR side first, queue after 7 clean days.Graviton4-only on Sep 29 was reverted after 3 h 42 min when its spot pools ran dry (or hit the 512-vCPU spot limit; unresolved); on Sep 30 all ARM pools ran dry and small jobs went to x86. Now: Graviton3 stays in the list as fallback, and the six-zone fleet landed Sep 30. 42% of machine spend still runs on the older chip, which bills ~60% more per hour (m) and runs ~29% slower (e, job medians).The fold shadow records which chip each leg got; cross-generation pairs give the speed ratio, same-chip pairs the noise floor. Then the canary with an automatic revert if queue-to-start grows 30 s, on-demand passes 2%, take-backs pass 5%, or any launch fails. Undo: Revert button.
Reserve: rent serversNo self-hosting (Jul 30), spot only, no persistent runners, a purchase; Gera: "only if we need the bill under ~$40/day".A Mac mini runner ran 1 h 54 min on Jul 30, nothing broke, reverted. Alone it is the biggest lever ($40–60 for 2–3 servers, e); after the five swings it is worth $15–18 (e). GitHub's postponed per-minute fee would hit it too.Two weeks of one rented box taking only the folded glue jobs. Undo: remove the label.

Before → after by bucket ($/day, model v3, 15-minute batches)

BucketToday (m)After (e)Range (e)After, as a bar
Testing a change60.026.121.8–31.7
Per-machine overhead (boot, public IP, disk, logs, data transfer)46.224.220.7–28.3
Everything else20.916.315.7–17.7
Waste around tests14.91.61.2–2.4
AWS CI/CD14268 (−52%)59.4–80.1

Speed, the secondary goal: where the minutes go and what the staircase does

Measured (speed lane, Oct 7–16): a merged PR takes 61 minutes to go live, an opened PR 116 (medians, m). about 40 of those minutes are the 30-minute deploy window set Oct 11; the days before it sat at 20 and 72.

Minutes, mediansMerge → liveOpen → liveBasis
Today (deploy window 30 min)61116m, store, Oct 12–16
Where it goes (open → live)re-pushes 14 · last PR check 19 · waiting to be queued 7 · queue and train 12 · merge → live 61 (main's run 7 + heavy re-test 8.5 + window wait and build 46)m, middle-band means that add to the median
Per step1: 0 / −4.5 (e) · 2a: 0 / +8 at 15-min batches, +17 at 30 (r) · 2b: −5 (r) · 2c: −7.5 with 2b (r), about −1 alone (e) · 3: 0 / +4 (e; −6 to +24 by when agents arm) · 4: 0 / −1.7 (e) · 5: 0 / −1.1 (e)reconciler tally
After the staircase, window kept at 3049 (−13)108 (−8)r + e; at 30-minute batches open → live is +1 instead
Window back to 5 only20 (−42)72 (−44)m, Oct 7–11 vs 12–16; +$3–7 a day of Vercel builds (Gera's bill page)
Window 5 + staircase1272r + e
Free speed moves, rankedMinutes per PRMoneyOwner
1. Deploy window 30 → 5 (delete one repo variable)−42 merge → live, −44 open → live (m)+$3–7 a day Vercel (Gera's bill page); also ends the spot-reclaim deploy failures seen since Oct 12Fede + Gera
2. Gera's sessions arm auto-merge when the work is done (Fede's already do: 0.1 min from green to queued vs 6.8)−7 per Gera PR (m), ~1,100 PR-minutes a day (e)$0; arming at PR open instead would erase most of step 3's moneyGera
3. If the window stays at 30: ship release's newest commit when the window closes−29 (r)$0, same build countGera (deploy path)
4. Prefer 15-minute batches over 30 when 2a ships+8 instead of +17 (r)about $1.7 a day less saved (e)Fede
Checked, no free gainshards already balanced (0.2–0.4 min apart), runner wait 0.3 min flat under load, review bot lands 12 min before checks finish, cache hits in all sampled logs (m)

Decisions, one line each

  1. Test once + precise choice: switch the queue to the shadow's list at 0 misses on 10+ real failures, about Tue Oct 20? Recommend yes once it clears; the switch is yours and Gera's.
  2. Our own batch bot replaces GitHub's queue once its 30-minute shadow matches the replay's 30-minute figure, then runs at 15-minute windows? Recommend yes, 15 minutes (about $1.7 a day less than 30, 9 minutes less wait per PR); Gera owns the ruleset change.
  3. Drop the post-merge re-test for a 2 h sweep, together with the thin deploy trigger? Recommend decide after the test-choice shadow reports; Gera owns the gate.
  4. PR checks only when armed? Worth $6.4 stacked only if agents arm when done. Recommend Gera picks Option B or arm-when-done first.
  5. Chip canary, 48 h, PR test machines only, automatic revert, Mon–Tue after a weekday 1–2 PM capacity re-read? Recommend yes.
  6. Fold the glue once the shadow shows the same verdicts on 500 runs? Recommend yes then.
  7. Rent servers? Hold: worth $15–18 after the swings, and the GitHub fee hits it too.
  8. Deploy wait back to 5? Recommend yes: $3–7 a day (Gera's bill page) for 42 minutes per change (m). If the Vercel cap must hold, Gera's "ship the newest commit when the window closes" gets 29 of them.
  9. Gera's sessions arm auto-merge when the work is done, as yours do? Recommend yes; I post it on the bridge on your yes.

This week

The shadows log through the weekend: the selector's bar lands about Tuesday, the fold's about Oct 26, the bot runs up to 24 dark trains a night. The first night already paid off twice: the bot exposed a defect in the dark train, and the fold shadow found one guard-pin disagreement, explained and its fix merged by 7:20 AM. On a yes: Monday the chip canary and Gera's step-3 pick. At zero misses: switch test selection, then the bot takes the queue, then 2b with 2c.

Risks that cut across everything

GitHub has announced and postponed, with no date, a $0.002-per-minute charge on every Actions minute, self-hosted included: $99–136 a day today (m, market lane), $58–73 after the swings (e, on the evening model). Two hub files are loaded by about 4,000 tests each, so 41% of commits select over 200 test files; splitting them is Gera's. Shadows that run after CI appear as checks on main's newest commit; nothing reads those for a merge or deploy decision at today's window.

Not ours: Gera's open CI PRs, the Vercel build audit (prebuilt deploys possible per Vercel's docs, build-minute billing unconfirmed), ADR-0040, the ruleset, the merge guard.

Halving plan, Oct 16: two big moves, then housekeeping  Proposed — pending review

Written Fri Oct 16 from six read-only lanes: a census of every CI step and machine (Oct 7–15), the graveyard of every attempt since Sep 9 (the factory Log's 112 entries, 48 of them CI attempts from Oct 6–13; 14 session transcripts), Smith's bridge answer, a test compute study, outside references, dedicated-server pricing, and a paired re-read of the Oct 1 one-machine trial. Nothing was changed. Corrected Fri ~3:30 PM: Move 1 re-sized from the trial's paired runs (the first version said $25–35); 3:32 PM: a line-by-line number check applied (72 cells, 11 corrected). Evidence folder. Days are UTC; clock times MT.

The short version. The two busy days were 12% of the two-week bill. The steady state is the problem: about $160 a day of AWS, $0.51–0.77 per merged PR, 239 merges a day. Two moves that change how CI runs, not what it tests, plus housekeeping, take about a quarter off. Coverage trades reach 32–38%. Nothing inside the stability fence reaches half; that takes a different machine model, and the decision is whether to shadow one now.

$0 $200 $400 $600 $800 764 2 429 3 154 4 338 5 323 6 171 7 211 8 217 9 91 10 103 11 131 12 111 13 202 14 203 15 OCTOBER, UTC DAYS Oct 7–15 mean $160 budget $67 a day ($2,000 a month) Oct 2–6: queue thrash before the trim ($0.92–1.74 per merged PR)
AWS, all services, before credits, per UTC day. Shaded: the Oct 7–15 window every number below uses. Oct 14–15 were busy days (292 and 280 merges), not a new problem.

The bill, and what “half” means

Line$/day$/monthNote
AWS, everything (the factory's number)1604,800Oct 7–15 mean; median 171
  product, not CI14420DynamoDB reads, Lambda, backups
  one-off4—CloudTrail spike Oct 8–9, stopped
AWS CI/CD1424,270What this plan halves
Vercel build minutes7–9220–270After Gera's cuts; the $60 platform fee is not CI
GitHub Actions, metered AI in CI1.545The review bot is on the subscription
Half of AWS CI/CD−71−2,130First target
Budget, read as the AWS bill672,000“$200 a day is $6,000” was the AWS number. Half is a waypoint.

Where the $142 goes ($/day)

Bucket$/dayShareInside it
Testing a change60
PR first pass 25.7 · queue re-test 27.4 · main's full test, push CI 6.9
Per-machine overhead46
Boot, shutdown 16.9 · public IPs 6.7 · bytes leaving AWS 6.8 · logs 6.3 · disks 5.4 · webhooks 3.5 (1.2 of it may be product) · CPU credits 0.6. 27,900 machines a day; 63% work under a minute
Everything else21
Glue, bots 3.4 · extras, nightlies 3.9 · deploys 2.4 · other repos 2.8 · Config, GuardDuty 4.3 · control plane 4.1
Waste around tests15
Re-pushes 10.4 · trains rebuilt behind reds 3.6 · never merged 1.0

The moves

MoveSaves $/dayWhat changesTried before → different nowProve first · undo · risk
1. Fold the glue, not the tests5–8
and up to ~1.7 min off every push (estimate)
The ~11 one-to-two-CPU helper jobs in every PR run (plan, gates, verdict posters) become steps or one job; the same on queue trains and the follow-up workflows. Test shards stay. In the Oct 1–2 trial the verdict chain finished 1.7 min (median) after the last real check, on every push. About $4.5 of this is the PR runs (trial re-read, Oct 15 count: 10.9 glue machines per run; the first deep dive put all glue at $5–6); the rest, queue trains and the follow-up workflows, is an uncounted estimate.One machine per push, shadow Oct 1–2 (308 pushes, 39 paired): 1 machine vs 22, same time at the median (8.1 vs 8.0 min), ~5 CPU-min saved, $9–15 a day, nearly all per-machine overhead. The 16-core re-run (merged Oct 6) never ran: switched off Oct 2, never re-enabled. One box is wrong today: 44% of runs need the 8-core shards.Store count of glue machines per run (Oct 15: 10.9), then 48 h before vs after · Revert button · Medium: 100+ tests and scripts key on job names; names agreed with Gera first.
2. Build once, ship what the queue tested, wait back to 5AWS ~0
Vercel 7–9
merge→live 60 → ~20 min
The queue already production-builds every train (310 s on 16 vCPU). Keep that output in-region; on promote, ship it prebuilt instead of Vercel building again. The 30-minute wait loses its reason.Raised Sep 13, never started. “Ship the exact commit the queue tested” open since Oct 6 (Gera's call). Wait 30 → 5 (Oct 6) → 30 (Oct 11).One throwaway prebuilt deploy: $0 build minutes? env vars? Output leaves AWS only on promote (about 79 a day at a 5-minute wait, not 286 trains) · Vercel setting · Low–medium; Gera's Vercel lane.
3. Housekeeping22–27Shallow checkout on the test shards 3 · in-region caches with per-job isolation ~4 (bytes out cost 6.8 today) · single runner logging 2.5–3.2 · Config trim 1–2 · one runner stack 2–3 (Smith: ~$8 for three) · dead steps ~2 (the push-to-main CI run stays: it triggers every deploy; its test work, the advisory harnesses and red nightlies can go) · main's duplicate guard pass 1.5 · empty test-choice job 0.8 · PR machine types 2.5 · rule: no push while checks run unless red ~5 (re-pushes cost 10.4). All estimates from the store.S3 cache dropped Sep 13 (security) → per-job isolation since RunsOn v3.2. Stacks unanswered since Oct 5. Logs “Fede's call”. Config “smallest set”. Cancel groups rejected Sep 18 → the lever left is how sessions push.Each from the store, read-only · one step each · Low: only checks that never block or never fail stop running.
4. Owner trades (not asked)13–22Hub trains run their own list, up to 7–10 (ceiling; list sizes not measured) · test files share a process, opt-in 3.5–7 · queue build on 8 vCPU 3–5 · two PRs per train: GitHub's own queue cannot batch (its docs), so it needs a third-party queue; ceiling ≤6.9, not countedThin queue (Oct 4): fails per 100 trains 15.8 → 21.7. Shared process (Oct 3): 49 files failed. The 16-vCPU box came from out-of-memory deaths.Replay the 8 full-set reds at the higher cap; 5× A/B on one shard · one value each · Medium–high: misses reach main; flakes.
5. Platform change100+
on paper
Dedicated servers (4 × Hetzner AX162-R ≈ $30 a day, 384 x86 threads; unverified third-party price, setup fees excluded) or long-lived nodes with a micro-VM per job (Actuated, $150 + $125 per server a month)Gera, Oct 14: “only if we need the bill under ~$40/day”; the budget leaves about $53 for AWS CI/CD if the $2,000 is the AWS bill. No self-hosted (Jul 30); spot only. Demand peaks 401–477 cores vs p90 171–205: a fixed fleet queues at busy hours. x86 not ARM; agent code on shared hosts; patching.Two-week shadow fleet on a copy of the test lane, no required checks · switch off · High until shadowed.

Before → after, by bucket (no dollar counted twice)

BucketTodayMove 1HousekeepingAfter
First pass on the PR25.7−0.8−5.519.4
The queue's re-test27.4−0.5−0.826.1
Full test on main, push CI7.0−1.35.7
Re-pushes10.4−5.05.4
Rebuilds behind reds, never merged4.64.6
Boot and shutdown16.9−3.013.9
IPs, bytes out, disks, logs, Config, GuardDuty30.4−1.5−7.521.4
Runner control plane7.6−0.7−2.54.4
Glue and bots3.4−0.82.6
Extras, nightlies, deploys, other repos9.1−2.26.9
AWS CI/CD142−7−25110 (−23%)

Move 2 saves Vercel dollars and 40 minutes, not AWS dollars. Owner trades would take the line to $88–97 (32–38% off). After Moves 1–3 the dashboard would read about $124 a day with product ($102–111 with owner trades), against $160 today and a $67 budget.

What reaches half

Moves 1–3 keep every blocking check and its timing, and land at about a quarter off. Coverage trades reach 32–38% in all and still fall short (the hub trains' full set is the biggest, up to $7–10, and it caught 4–5 real breaks in nine days). Half, and the $67 budget, take the machine model: dedicated or long-lived servers, about $30 a day of rent (unverified price) plus per-job isolation, at the risks in row 5. Gera's Oct 14 line was “only if we need the bill under ~$40/day”; the budget leaves about $53 for AWS CI/CD if the $2,000 is the AWS bill.

Decisions, one line each

  1. Deploy wait back to 5 now? Recommend yes. Vercel's $20 monthly build allowance was spent 2.5 days into the Oct 14 cycle, so going back to 5 adds about $3–7 a day of build minutes: a few dollars for 40 minutes per change. Goes to Smith on the bridge; the 30-minute default PR is Gera's.
  2. The housekeeping config writes (in-region caches, single runner logging, Config trim, one runner stack)? Recommend yes; each undone in one step, done by the stack's owner.
  3. Fold the glue: one PR, job names agreed with Gera first? Recommend yes.
  4. The Move 2 spike, one throwaway prebuilt deploy, result to Gera's Vercel audit? Recommend yes.
  5. The fork: shadow a dedicated or long-lived fleet for two weeks, now and in parallel? About $900 of server rent plus ~$525 of per-job isolation licence, ≈ $1,425 for the month (estimate; unverified €199 per server, setup fees and minimum term not found); no required checks, nothing in production touched. Recommend yes: nothing inside the fence reaches half, so waiting only delays the one move that does. A purchase, so Fede's call either way.

Not ours: Gera's open CI PRs #20417, #20397, #20365, #20450, #18891; the Vercel build audit; the product DynamoDB read load (~$6 a day of reads, ~$10 of DynamoDB in all).

Scoreboard: the six numbers
MEDIAN PR, OPEN TO LIVE (MINUTES) Now24 h to Oct 6, 7:16 PM 130 open to merge 66 merge to live 195 min Target, Wed 1 PM 60 30 90 min Goal, Fri open to live, all in 60 min
Both halves have to shrink. Open to merge is PR checks, waiting for a slot, and re-runs after kick-outs (3.3 queue runs per merged PR). Merge to live includes a second full test run of code the queue already tested.
NumberBaseline
24 h to Oct 6, 2:06 PM
Now (last 6 h, Tue 8 AM MT)Now (last 6 h, Tue 2 PM MT)8 PM (Oct 7)
last 6 h, 75 PRs merged
2 AM (Oct 8)
last 6 h, 118 PRs merged
Wed 1 PM (24 h)Target
PRs kicked out of the queue, per day1001624 2 of 6 were the queue’s 30-min timeout561620 9 sibling merge conflicts, 9 red checks, 2 timeoutsunder 10better, still 2× target on the 24 h window
Earlier check (24 h to Oct 6, 7:16 PM): 101 a day; 63 failed checks, 35 merge conflicts, 3 timeouts. · 2 AM–8 AM Tue: 4 kick-outs, 2 red checks on the train, 2 merge conflicts, zero infrastructure.
Queue runs per merged PR3.351.141.57 just over1.791.011.20under 1.5at target
Wait for a queue slot, median17.8 min0.3 min0.3 min at target0.3 min0.3 min0.3 minunder 10 minat target
Open to merge, median128.6 min55 min76 min freeze hour inside the window54 min47 min50 minunder 60 minat target (barely)
Merge to live, median72.7 min20 min26 min at target21 min20 min22 minunder 30 minat target
Cost per merged PR (priced estimate, not invoices)$1.01 / 523 runner-min$0.44 / 307 runner-min$0.68 / 459 runner-min real bill ≈ $0.77, see note$0.59$0.41$0.49 (priced estimate, not invoices)under $0.70at target (priced estimate, not invoices)
Merged PRs in the window282——75118——
Production deploys, Tue 2 PM: 13 in the last 6 h, 67 in the last 24 h.

Rows at target, Wed 1 PM (24 h window): 5 of 6. PRs kicked out of the queue are 20 a day against a target of under 10; the 6-hour readings were 0.

Real AWS compute bill per merged PR: Mon $0.77, Tue $0.35 (Tue still settling).

Real dollars (new, Tue 2 PM). The Factory dashboard (factory.propflowai.co) carries the actual AWS bill. Monday: EC2 $225.92 / 295 PRs = $0.77 of real EC2 per merged PR (Cost Explorer revised it down from the $258 first read): $0.77 against a $0.44 priced estimate (Monday). The estimate counts runner minutes at spot rates only; the bill also carries the runners' disks, boot and idle time, orchestration and other EC2. From this report on, the dollar row shows both, and the $0.70 target is read against the real bill. At the 2 AM (Oct 8) column, five of six rows are at target on the priced estimate; PRs kicked out of the queue (16 a day against under 10) is the one still above the line. Tuesday’s real bill is still settling, so the real dollars row is not called yet.

Real EC2 bill per merged PR (Oct 8). Oct 6: $225.92 / 295 PRs = $0.77 (Cost Explorer revised it down from $258.62). Oct 7: $85.91 so far / 243 PRs = $0.35, still settling.

The window for "now" closed 90 minutes after tonight's fixes merged, so it barely includes them. Merge to live cannot reach 30 minutes while production waits for the second full test run; that is Gera's call (decisions).

Week of Oct 1–8: what changed, for stakeholders

A PR now goes from opened to live in about 67 minutes (median), down from 3.6 hours a week ago, and the machine bill per merged PR is roughly a third of what it was. This section is the plain-English week summary; the scoreboard above has the six-hourly ticks.

Six numbers, before and after

NumberTue Oct 6 (before)Wed Oct 8, 8 AMTarget
PRs kicked out of the queue per day660 in the last 6 h (16 overnight)under 10
Queue runs per merged PR3.51.03under 1.5
Wait for a queue slot, median40 min0.3 minunder 10 min
Open to merge, median102 min45 minunder 60 min
Merge to live, median45 min22 minunder 30 min
Dollars per merged PR (priced estimate, not invoices)$1.04$0.45under $0.70

Machine time and real bill by day

Source: factory.propflowai.co/cost for hours, AWS Cost Explorer for dollars.

DayUseful machine-hoursWasted machine-hours (re-runs after a kicked-out train)Real EC2 billPRs mergedReal bill per PR
Thu Oct 27,44311,629$216438$0.49
Fri Oct 36,7354,255$209468$0.45
Sat Oct 43,0092,030$91152$0.60
Sun Oct 56,2055,044$245343$0.71
Mon Oct 62,5832,626$226295$0.77
Tue Oct 71,632190$86 (still settling)243$0.35
Wed Oct 8, to 8 AM1,14351———
Week of Oct 1–7: $1,293 of EC2 for 2,521 merged PRs, about $0.51 per PR. Wasted hours fell from more than the useful hours on Thursday to about 3% of them on Tuesday. The whole AWS account for the week was about $1,930, which includes the database, logs and one tax line.

What we did

  • Moved CI onto our own AWS machines on Oct 1; 98% of hours run on spot instances.
  • Cut the queue's full test pass from 71 minutes to 10 by splitting it into more, smaller shards on bigger ARM machines.
  • Stopped the rebuild cascades: a kicked-out train no longer rebuilds everything behind it, and the queue fails fast on a bad entry.
  • Made PR checks catch what the queue used to catch: the targeted guard tests now run on the PR itself, and big PRs' tests run in parallel shards.
  • Put guardrails on how PRs enter the queue: no re-arming a head the queue already rejected, no arming into a known conflict, a dispatch guard for PRs GitHub never started, and an automatic revert when main goes red.
  • A dozen smaller fixes: a security scan that hung for 20 minutes now takes one, a weekly schedule-drift red closed, review-bot noise removed from the queue triggers.

What is left

  • Ejections: 16 a day overnight, 0 this morning; the target is under 10 sustained. The remaining ones are merge conflicts between sibling PRs that only the owner can resolve.
  • Merge to live below 20 minutes needs the release re-check cut, which is on Gera's side, together with approval of the change that starts test shards 8–10 minutes sooner.
  • One hour opened-to-live by Friday.

caution Caution on the dashboard: the cost page's dollar figures for Oct 2–6 are stale first reads and run two to three times above what Cost Explorer shows today (Oct 2: $638 there, $216 in AWS). This summary uses the AWS numbers. The fix is handed to the dashboard's owner.

Close-out, Wed Oct 8. The lane closed Wednesday at about 1 PM as good enough. Open to live went from 3.6 hours to about 72 minutes. Both CI workflow changes merged today: sharded tests start about 8 minutes sooner, and a reclaimed spot machine no longer re-runs a finished train. Two small follow-ups were filed: shard handoff for small PRs, and a daily cost alarm above $0.70.

Factory dashboard audit (Oct 7, 8 PM)

We checked every number on the Factory dashboard against GitHub and the AWS bill. Counts are good; the timings and dollars Fede would act on are not.

PageVerdictThe one number not to trustWhat it should be
Nowwrong“Merged → live 8 min”About 22 min. It credits the next deploy even when that deploy does not carry the PR.
Also: clock times are Central with no label. “Ready, not queued 8” are 8 blocked PRs. “Conflict 0” is really 14.
Costmisleading“$66 yesterday”A day AWS has only ~60% posted, shown as complete. The drop is real (usage fell ~49% on Oct 7), just smaller.
Also: priced at $0.014 per vCPU-hour, about half the real ~$0.032, so “$57/day waste” is nearer $130. Hours count queue runs only; PR checks (28–46% of runner time) are missing.
Healthwrong“Merged → live”: its rule gives 4.8 min on the audited PRs19.75 min. And there is no open → live number at all; the real median is ~84 min.
Also: the 7d and 30d arrows compare against Sep 29–30. “Full test on main 85%” counts skipped runs as passes (~80%).
Logmisleading“9 not merged yet”2. Seven of the 38 links point at the wrong repo; one PR is logged twice.
RedsmisleadingWho broke itOnly the first culprit in a failed batch, not every PR in it. The clock-bug test (now fixed) is blamed on 4 PRs.
Also: the Oct 7 red main, its auto-revert and the 85-min release hold are absent. Range buttons do nothing (data starts Sep 30). The flaky list is wrong.
Testsmisleading“Full set on main”Nearly empty: only 5 main runs read since Oct 4.
Also: “3 look flaky” with no file flagged. “▲70555%” is unreadable (it means 32 ms → 21 s). Slowest-file timings check out.

What checks out

  • Queue entries and trains building.
  • PRs merged in 24 h (250–255).
  • Run counts per lane, within 1 of GitHub.
  • AWS dollars for finished days match Cost Explorer.
  • The live feed is fresh within 2 seconds.
  • Slowest-test timings.

Fixes for the dashboard's owner, in order

  1. Merged → live counts from the first deploy that contains the PR.
  2. Label partial days and UTC days; show clock times with their zone.
  3. Price a vCPU-hour from the bill, not a list rate.
  4. Blame the first culprit in a failed batch only.
  5. Include PR checks in machine hours.
  6. Add open → live, the number the one-hour goal is about.

Method: headless render at 1440 and 390 px; every number cross-checked against GitHub and AWS by four independent passes. Evidence is in the lane's scratchpad.

How the queue loses time, and tonight's fixes
WHEN THE FRONT ENTRY MERGES mainwhat ships #1merges #2keeps going #3keeps going #4keeps going #5keeps going #6keeps going #7keeps going #8keeps going main jumps to #1's exact tested commit. #2 to #8 were built on top of it, so nothing restarts. WHEN AN ENTRY IS REMOVED (FAILED CHECK, CONFLICT, MACHINE TAKEN BACK) mainunchanged #1keeps going #2keeps going #3removed #4restarts #5restarts #6restarts #7restarts #8restarts every entry behind the removal builds again from scratch 17 of 18 big rebuild waves were removals, not merges or queue jumps.
The queue is one lane with 8 entries building at once. Merges are cheap; removals are what cost us. Of 13 removals studied, 10 were Spot machines (cheap cloud machines the provider can take back mid-run) dying during tests, not bad code.
Fix merged tonightWhat changesProof
#17277 · 5:43 PMAdding a label to a PR no longer starts a test run. That removes one whole class of stuck entries.merged
#17231 · 5:44 PMWhen a Spot machine is taken back mid-test, that test slice is retried inside the same run instead of removing the PR.proof pending on trains now, no machine taken back yet
#17227 · 5:46 PMA PR removed from the queue gets a comment saying why, and re-queues itself when the machine was at fault.merged

If the Spot retry works, most of the 63 failed-check removals a day should go away. The 35 conflict removals need a different fix: fewer shared files that every PR edits (plan).

Tonight: production held for two hours
PR mergesmain moves Full test run after mergered = nothing moves on Release branchadvances only on green Productiondeploys every 30 min Tonight: red from 5:50 PM. No alert fired; it was noticed when production was about two hours behind. Code that already passed the queue sat waiting to ship the whole time.
Production ships from a release branch that moves only when the full test run after merge is green. A red run holds every merge behind it, silently.
TimeWhat brokeFixState
5:50 PMA test broken by a review-bot change (#17001)#17283merged 6:08 PM
5:57 PMA voice change (#17175) changed the fingerprint a voice test compares against#17287 re-stamps itmerged 6:52 PM
laterA test that picks a date three days out landed on Columbus Day (a bank holiday) in UTC#17293 (Gera, urgent): pins the datemerged 7:27 PM
7:16 PMProduction restored: the urgent tours fix merged and its tests went green. The release branch moved to it at 7:21 PM, the web deploy finished at 7:20 PM, and the 33 waiting changes shipped with it. Lambdas and the renewal worker ship from the same release commit. The manual "promote now" was not used.#17285live
7:21 PMThe tours merge moved a voice fingerprint again. The voice harness check is red on main; it does not hold deploys.re-stamp PRpending
follow-upThat voice check only runs after merge#17289: run it on PRs tooissue open
follow-upThe local pre-push check fails on one Mac for tests that pass in CI#17298issue open
Mon 9:48 PMProduction deploy window cut from 30 to 5 minutes (#17354). First deploys after: 6 and 13 min from release move to live, vs 37 when batched.#17354merged
Mon 11:11 PMPost-merge full unit suite split into 4 shards instead of 2 (#17379). Unit shards 11 → 5–6 min; full post-merge run 71 → 22 min.#17379merged
Tue 12:23 AMPost-merge guard-pin shards moved to 8-core arm64 runners (#17397). Guard-pin shards 11–12 → 3–4 min; first full post-merge run after it: 10 min wall, 38 runner-min.#17397merged
Tue 2 AMSix-number check-in: nothing moved the wrong way; no revert. Production deploys in the 6 h: 25 release moves, 21 promoted.no revertchecked
findingVerified: merge-queue trains already run the full unit suite and guard pins on the exact tree, and the post-merge lane recognises that and skips its own full run (proven-head path). The 'extra full run per PR' only happens for direct pushes or unproven trees.—verified
Tue 2:29 AMVoice harness red on main again: the third voice PR of the night changed the leasing agent's prompt without re-stamping the fingerprint. Follow-up #17423 asks for the check to run on the PR itself.re-stamped (#17424)merged, main green
Tue 3:00 AMWeekly "Schedule Drift" check had been red two Tuesdays running: an hourly workflow was never declared in the automations registry.declared (#17434), then the check ran on main: 91 jobs in sync, zero findingsmerged, proven
Tue 7:50 AMProof: Semgrep checkout fix (#17264). On the 10 PR scans since it merged, the scan takes 1.1 min (was 2.2), checkout 16 s (was 94–146 s), no failures, retry never needed.—proven
Tue 7:50 AMProof: post-merge shards (#17379, #17397). Eight full runs this morning: 8–11 min wall, longest unit shard 8.5 min of a 16-min cap, longest guard-pin shard 4.1 min of a 20-min cap, nothing cancelled or timed out.—proven
Tue 8 AMSix-number check-in: nothing moved the wrong way; no revert. Production deploys in the 6 h: 21 release moves, 20 promoted.no revertchecked
findingNext lever for merge → live (already at target): the post-merge lane paces itself to one run per 12 minutes, sized when a full run took 71 minutes; at 10 minutes a 6-minute pace would take ~3 minutes off the median for ~1% more runner time. Parked for Thursday.—parked
Tue 9:01 AMMain's test run on a Gera-side merge ended “failure” with no failed job: GitHub created 37 of the usual 59 jobs. One rerun: green, 59 jobs. GitHub-side; nothing to revert.rerungreen
Tue 9:09 AMA heavy-lane run for that same commit sat “queued” with zero jobs and could not be cancelled (GitHub refused both cancel paths). The lane's pacing rule counted it as busy, so every heavy run after it waited and production froze an hour behind main.—froze
Tue 9:55 AMManual heavy run unfroze production (release = main at 10:13).manual rununfrozen
Tue 10:11 AMFix opened: a queued run older than 20 minutes no longer counts as busy. The first cut touched only the workflow file, which the review bot cannot review while the comment-driven reviewer is switched off (since Sep 30); reshaped so the rule lives in a small script with nine tests, including a planted control.fix PRopened
Tue 11:24 AMSecond manual run; production caught up again.manual runcaught up
Tue 11:42 AMFix merged. 12:05 PM: proof in production. With the ghost record still present, the next scheduled heavy run went ahead and release advanced on its own.mergedproven
Tue 11:30 AM–1:30 PMWaste audit against the new dashboard: about $85/day of re-run compute (priced estimate), 71% of it merge-queue trains discarded behind a red. Half of last week's queue-only reds were whole-repo “drift” tests PRs never ran; fixed since Monday evening, and that cost is near zero today. Re-run waste: 50% of compute Monday, about 7% so far Tuesday.—measured
Tue 12:30 PMDecided NOT to build a pre-queue gate (Fede, Mon: red main is caught in testing, never by a gate before the queue; saving would be ~$4/day net and it adds minutes to every PR). The remaining ~7 queue-only reds a day are mostly tests the PR never selected: fix selection instead.follow-updecided
Tue 1:00 PMSpot reclaims no longer turn a train red (fixed Monday; 10 of 10 trains since recovered in-run). Two things found instead, both sent to Gera's side via the bridge: the queue's 30-minute check timeout ejects big trains (one PR twice today), and the automatic merge guard has been off since Oct 1, so Fede-side arms skip the pre-queue type check.bridgewith Gera
Tue 1:15 PMThe runner provider's bot re-runs a merged train after our in-run retry already recovered it (9 times in 19 h, ~20 jobs each). Builder finding the trigger; a repo-side fix ships today if it is a label of ours.in progressopen
Oct 7, 10 PMProven live: guard pins (fence tests) now run on PRs. Merged. First 21 PR runs after the merge: the Guard pins step picked between 1 and 79 fence tests per PR (median 6), all in one shard, and on one PR it failed a leasing fence test that is green on main, so the author saw the defect on the PR instead of being ejected from the queue later.—proven

The lesson: a red run after merge holds production without telling anyone. Either it pages someone, or production stops waiting for it (decision 3).

Waste by cause (Tue, last 48 h)
CauseShare of re-run waste (priced estimate, not invoices)
Queue trains discarded because a PR ahead went red71%
PR runs replaced by a newer push10%
Post-merge heavy run re-testing what the queue proved9%
Spot machines reclaimed mid-run8%
Manual re-runs and main re-tests2%

The biggest slice is already shrinking (drift tests now run on PRs); the next cuts, in order, are the provider re-runs, superseded PR runs, and the heavy-lane re-test.

The plan: now, next, later

Now: make a PR run what the queue will run

ChangeWhy, measuredProof
Every PR runs every boundary-guard test, whatever it touched36 of 93 PR-caused queue failures were guard tests the PR had skipped#15807 merged
Big PRs run the always-run test set too24 of 47 sampled big-PR runs skipped about 650 tests; 13 of 93 failures#15811 merged #15808 open
Delete a dead "already passed" shortcutUnreachable today; stops a future skip#15806 merged
Stale-base PRs and file-reading tests13 and 8 of 93 failuresno PR yet
Shared lists every PR edits become one row per item, so unrelated PRs stop conflictingFour lists touched by 299, 124, 82 and 96 PRs in 30 days; 18 collisions#15789 #15795 #15790 #15793 #15796 merged #15794 open
Measuring tools, so every change has a baselineRead-only#15804 #15805 #15809 #15792 #15797 #15799 #15800 #15801 #15810 merged
Share one install cache across helper workflows; delete a dead scheduleAbout 580 runner-min a day, about $1 a day (estimate)measured in #15799, #15810

Next: stop paying for runs already thrown away

ChangeWhy, measuredProof
Cancel a queue run once its group is thrown out43% of queue compute (558,184 vCPU-min in 34.7 h) ran after the group was already doomed; only 20% went to groups that merged. GitHub let 196 of 227 such runs finish.#15805, #15800
Stop the early-failure watcher idling after every verdict is inUp to 26,002 runner-min a day#15799
Pause the queue by disarming PRs, not pulling entries out155 manual and 53 conflict removals against 86 failed checks in 34.7 h. Model: no removals lifts 15.2 to 20.2 merges an hour.#15805, #15800
The next three shared lists25, 21 and 8 collisions#15801
Log which PRs a classifier calls independent, next to real outcomes4 known collisions; a safe call needs 60#15792, #15797

Later: a second queue lane, only when proven

ChangeWhy, measuredProof
Untangle the shared test files that tie every PR togetherAll 177 open PRs form one group; exit test is the largest group at 50% or less (today 98–99%)#15797
Split the code into packages1,114 and 874 imports cross the two main code trees. Sizing only.#15797
A second merge laneNeeds 60 collisions predicted with zero misses over two weeks. "Different files" missed 1 of 4 real collisions.#15797
Browser tests against main after merge6 preview probes exist; 0 of 61 sampled runs completed one#15810

The "now" list was built from Oct 1–3 data, before removals were understood as the main cost. Tonight's three fixes are the new first priority.

Decisions still needed
DecisionOptionsRecommendationWho
1. Queue depth (8 build at once now)A. keep 8
B. 10: the model's knee (15.4 an hour at 6, 19.7 at 10, flat after)
C. 16 or more: real days ran 23.0 an hour at 16 but 11.5 at 24
A until the Spot retry is proven, then B for one measured dayFede, Gera
2. Remove checks from the queueA. keep all
B. drop the duplicate guard step inside the full set (about $41 a day; move one test first)
C. drop the 6 guard slices (about $99 a day, but they disagreed with the full set 5 of 85 times; the earlier attempt, #15231, was closed)
BGera
3. Release re-check after mergeA. ship the exact commit the queue tested; the after-merge run alerts instead of holding
B. keep the hold, add an alert when it goes red
C. no change
A: main is already the queue's tested commit. Saves the roughly 25-minute re-check and ends silent holds like tonight's.Gera
4. The arm64 runner pool in the private org repoA. delete it
B. keep it
A, once a check shows no job targets itSmith, Gera
5. Ten AI test suites nothing runsA. run weekly
B. delete
B for any replaced by newer evals; A for the restowner lanes

Dollar figures are estimates from runner prices (#15799). Depth numbers come from a model that says it runs optimistic, and depth was changed 24 times in a day, so no real day is a clean test.

Retro: 48 hours to Oct 6, 1 PM
Per dayMon Oct 5 (baseline)Estimate for TueTue Oct 6, to 1 PM
PRs merged343same213 so far (afternoon 2x Monday's)
CI machine minutes180,700about half112,500 so far; pace 140,000
Machine minutes per merged PR527about 260528 flat
Machine dollars (estimated)$281$110–150$221 so far; pace $275
Dollars per merged PR$0.82about $0.40$1.04 worse
Queue runs per merged PR2.3not estimated3.5 worse
Full-price machines$12$0$100 spike, reversed; $34 now
Median open to mergenot measurednot estimated102 min (27 to first green, 52 waiting in line)

The real bill (Cost Explorer) lags a day: Monday was $245 of compute.

What happenedHow we know
workedEach queue run got cheaperGuard jobs per run 6.0 → 2.9; full-set jobs 1.9 → 0.65; queue check 11.5 → 4.9 min
workedTests that never ran now run, including big PRs that went green with zero tests44 of 66 train failures and 7 of 22 red-main incidents came from that gap. First afternoon live, it caught a real violation.
workedThe queue stopped punishing Spot kills on re-entry14 PRs in 48 h had been re-pushed for nothing
workedThe review bot stopped dying on token limits49 of 53 reviews had failed since Oct 1; pool back to 9 accounts
workedOutside CI: the Western Slope guest-card sync stopped writing for nothing64 rows a minute → 0, from the sync's own write meter
did notCheaper runs, but more runs per PR, so cost per PR did not fallRuns per merged PR 2.3 → 3.5. Nobody had this number in the plan.
did notTwo red mains overnight from our own CI PRs6 h and 1.7 h of held deploys; the fix landed 18 h later than it should have
did notA cost change moved the queue onto full-price machines$12 → $100 a day for 8 h, then reversed
did notOne bad PR failed every train behind it12:34–1:02 PM: 8 failed trains, about 20 cancels, nothing merged for 53 min
did notNothing targeted time. The queue run is 12 min; the rest of open to merge is waiting for a slot.Merge to live added 45 min, 25 of it a re-check the queue already did
tonightA red run after merge held production silentlyFirst noticed when deploys were about two hours behind (above)

What this changes: the lane is now judged on minutes and dollars per merged PR, and open to live. The next levers are runs per merged PR, the release re-check, and telling owners when the queue removes their PR (now #17227).

Numbers and sources
NumberMeaningWindowSource
16.3–17.75 / hPRs merged per hourOct 1–2#15804, #15805
12%PRs whose first queue run failed on its own (52 of 432)Oct 2#15800
93 / 21 / 70PR-caused queue failures / test had passed on the PR / test never ran on the PRSep 30–Oct 2#15809
13Median groups thrown out behind one failureOct 1–2#15805
355Runner-min per mergeOct 1–2#15799
20% / 43%Queue compute on groups that merged / after the group was doomed34.7 h, Oct 1–2#15805
0–60%Share of groups that merged, from position 1 (60%) to past 20 (0%)Oct 1–2#15805
5 / 85Runs where the guard slices and the full set disagreed24 h sample#15799
4 of 2,450Real two-PR collisions in merged historySep 25–Oct 2#15797
17 of 18, 10 of 13Rebuild waves that were removals; removals that were Spot killsOct 6queue-jump study

Still open: the depth model's second part is not in a PR yet; the early-failure watcher's idle time is unmeasured; whether disarming survives sessions that re-arm PRs is unconfirmed. Out of scope: runner pool sizing and the deploy ordering wait.

PropFlow Docs