ci(cmux-next): GUI-job re-runs and overflow follow the routing order (owned Mac first, largest live Blacksmith size last)
Summary
swift-test, daemon-test and generated-files now follow the routing order on attempt 1 and on the rescue's re-run. They take an owned GUI or session Mac first. They go to Blacksmith macOS last, at the largest size that can start them now.
Run 41123927916 (#28497) ran swift-test on blacksmith-6vcpu-macos-26 on both attempts. Tests that take under a second on an idle machine took 78-227 s there, and the 300 s stall watchdog cut them. Attempt 1's placement said: "glaeda-gui-std-xcode-26.6 has no room (54 queued, 11 online, none idle), so the GUI jobs take blacksmith-6vcpu-macos-26". Each attempt reached 6 vCPU through a fixed leg, not through a size anyone weighed:
- Attempt 1:
needs.macos-placement.outputs.runner != '' && 'blacksmith-6vcpu-macos-26'. Placement always writesrunner, so every declined GUI job took this leg beforegui_overflowwas read. - The rescue's attempt 2:
rerun_main()wrote nogui_runner. Itsgui_overflowwas always 6 vCPU, so an idle owned GUI Mac was never weighed.
Changes:
next_macos_cascade.gui_overflow_label()names 12 vCPU only when it can start the GUI jobs now. That means it is not stalled or dead, has no freshness work queued, has a free slot for each GUI job, and the shared 60 vCPU pool fits them. Otherwise it names 6 vCPU. It also names 6 vCPU when the counts or the snapshot can't be read.side_lane_placementmain()andrerun_main()writegui_overflowfrom it, after counting the slots the run's own cascade just took.rerun_main()also writesgui_runner,session_runnerand the free labels, naming only a runner that is idle now. The rescue still watches attempt 2, so a free runner lost to a race gets moved once more.cmux-next.yml: the runs-on and theCMUX_NEXT_RUNNERcopy of all three GUI jobs drop therunner != ''leg, therun_attempt != 1 && 'blacksmith-6vcpu-macos-26'leg andvars.MACOS_RUNNER_26. They now read the placement's role route, thengui_overflow, from the current attempt only. The attempt-1-gated owned role label stays. They end on'blacksmith-6vcpu-macos-26'as the last resort, which covers a person's re-run, a carried placement and a fork.
Honest limit: that day 12 vCPU was stalled ("blacksmith-12vcpu-macos-26 0 in flight + 158 queued of 5 (stalled: oldest queued 1431 min)"). With this change, run 41123927916's overflow would still have been 6 vCPU. What changes is that the bot's re-run takes an idle owned GUI Mac first, and that 12 vCPU is used whenever it is live. 6 vCPU starving swift-test is not fixed here. That needs fewer test workers on small runners, or a weighting that accounts for 6 vCPU running swift-test slower.
Not touched: the swift-test steps and scripts/cmux-next/swift-test-with-hang-sampler.sh (#28594), and the swift-test step sharding (#26809). #28249 and #28391 also edit rerun_main()'s output lines. Whichever lands second takes both sides.
Testing
Every new test fails on this base (cd2b03c1893) and passes on the head. The tests are in their own first commit.
python3 -I tests/test_ci_runs_on_fallback.py: newtest_no_fixed_blacksmith_leg_comes_before_the_gui_overflow(base: "cmux-next.yml swift-test runs-on: needs.macos-placement.outputs.runner != '' && 'blacksmith-6vcpu-macos-26'") andtest_the_overflow_rule_catches_what_it_is_for.python3 -I tests/test_ci_next_macos_cascade.py: newGuiOverflow(base: nogui_overflow_label). 12 vCPU when it moves and fits. 6 vCPU when it is stalled (run 41123927916's figures), dead, full, short of a slot per job, held by a pool queue or reserved, or when the counts or snapshot are unread.python3 -I tests/test_ci_side_lane_placement.py: newtest_the_rescues_full_rerun_takes_an_idle_gui_runner_first(base: nogui_runnerwritten) andtest_the_rescues_rerun_takes_the_owned_gui_runner_or_the_overflow_its_placement_found(base: an idle GUI runner still gotblacksmith-6vcpu-macos-26).- Updated: the pinned generated-files expression in
tests/test_cmux_next_checks_workflow.py, plus the expectations that change from 6 to 12 vCPU while 12 vCPU is free and moving.
Observed / Not run
Observed: all of test_ci_side_lane_placement (75), test_ci_next_macos_cascade (97), test_ci_runs_on_fallback (19), test_cmux_next_checks_workflow (165), test_seed_derived_data, test_cmux_next_visual_gate and test_ci_session_label_routing pass. test_ci_change_areas fails one test, test_repository_helpers_route_by_where_they_run, and it fails the same way on base. actionlint has nothing on the changed lines. The pre-push gate is clean.
Not run: a live CI run with a declined GUI queue. The first PR run after this lands shows its placement line in the step summary.
Changelog
none
Agent session: agent session
š¤ Generated with Claude Code
Summary by cubic
swift-test, daemon-test and generated-files now follow the macOS routing order on attempt 1 and on the bot's re-run: an owned GUI or session Mac first, Blacksmith macOS last at the largest size that can start the jobs now.
- The GUI overflow is now a decision, not a fixed 6 vCPU move:
gui_overflow_label()names 12 vCPU only when it can start every GUI job now (not stalled or dead, a slot per job, shared pool fits), else 6 vCPU. rerun_main()writesgui_runner,session_runnerand the free labels, so a re-run takes an idle owned Mac before any Blacksmith runner; with the merged dead-side-queue routing it still writesrunnerfor freshly allocated owned jobs anddeferred_runner.- The
runner != ''andrun_attempt != 1legs that sent declined GUI jobs straight to 6 vCPU are gone; 6 vCPU is only the last resort. A placement naming neither a GUI route nor an overflow gets the last resort from the place step (fork-aware viaGUI_LAST_RESORT:macos-26for forks,blacksmith-6vcpu-macos-26otherwise), so its GUI jobs never join the unmeasured owned queue.
Honest limit: on run 41123927916 12 vCPU was stalled, so that overflow is still 6 vCPU; what changes is that re-runs take an idle owned Mac first and 12 vCPU whenever it is live. 6 vCPU starving swift-test is not fixed here.