ci(cmux-next): DEV Release compile builds whole-module when it has no host cache
The DEV Release compile picks its Swift compilation mode from whether the build has a host compilation cache. A build that has none now compiles whole-module, not singlefile. This is the cold-runner follow-up to #24994.
What changed
scripts/cmux-next/compile-dev-release.shresolvesCMUX_NEXT_SWIFT_COMPILATION_MODE=autofrom the build mode it has already chosen:- host mode (the per-host tree and DerivedData, under the host lock):
singlefile. That is what the host cache holds and replays. This includes a host's first build (noprevious_sha), which compiles singlefile so it seeds the cache. A whole-module first build would leave no singlefile entries, so the next landing on that host would be cold again. Placement also records any host build as a warm cache holder (dev-cas-holder.sh), so a whole-module seed would send the next landing to a host whose cache it can't use. - instance mode (another build holds the host root, host tree setup failed, cache off, or not a mini, which includes the Blacksmith fallback):
wholemodule. These builds have no cache to replay and never fill the host's cache. - An explicit
wholemoduleorsinglefilewins; anything else exits 2.
- host mode (the per-host tree and DerivedData, under the host lock):
- It logs the choice and the reason, e.g.
compile-dev-release: cache cold: another build holds the host root -> wholemodule,host cache from <sha> -> singlefile, ornew host tree: singlefile seeds the host cache -> singlefile. The report file and$GITHUB_OUTPUTgetswift_mode=, and the per-landing notice line now printsswift_mode=. .github/workflows/cmux-next.yml: everyvars.CMUX_NEXT_DEV_SWIFT_COMPILATION_MODE || 'singlefile'becomes|| 'auto'. That covers both compile steps, both product-reuse lookups and both REF-settings blocks.
Product reuse and the REF capture
The reuse key's swift_mode= extra now names the policy (auto), not the mode a build resolved to. Under auto, the product of a set of inputs is either a singlefile or a whole-module Release -O build of the same sources. Either is a valid DEV app, and every DEV app was whole-module before #24994. Keying by the resolved mode isn't possible anyway, because the lookup runs before the build knows its mode. A repo variable that names a mode still changes the key, as before. The script is part of the fingerprinted tree, so this PR changes the key by itself and existing records aren't reused across it.
HYBRID_REF_SETTINGS also says auto: test_the_capture_uses_the_compile_settings requires it to match the compile step's env. The REF capture only runs when build_mode == host, which resolves to singlefile, and its manifest records the real xcodebuild arguments.
Performance
xcodebuild wall time comes from the job logs' xcodebuild diagnostic wrapper start/finish lines, and cache state from CompilationCacheMetrics (note: N hits / 509 cacheable tasks). The data is 15 DEV landings on 28 Oct, all on Xcode 26.6.
| metric | before | after | delta | run link |
|---|---|---|---|---|
| instance-mode build (0/509 hits; here the host root was busy) | 804 s singlefile | ~545 s whole-module (expected) | about -260 s (-32%) | 40723039421 |
| whole-module, no cache (the #24994 baseline) | 518 / 545 / 560 s | 518 / 545 / 560 s (unchanged) | 0 (baseline) | #24994 |
| host build, warm (509/509 hits) | 111 / 124 / 136 / 248 s singlefile | unchanged (singlefile) | 0 | 40732582859, 40728187140, 40734056576, 40719692915 |
| host build, first in a new host tree (0/509 hits) | 750 / 811 / 923 s singlefile | unchanged (singlefile; seeds the cache, once per host) | 0 | 40716049919, 40735213435, 40709255352 |
| Blacksmith fallback (cache off) | 537 s singlefile | whole-module (expected about the same) | ~0 | 40729029641 |
The "after" numbers are expected values; I'll post the first measured instance-mode whole-module build on this PR. Three of today's four cold builds were hosts' first host-mode builds, the first landings after the per-host tree. Those are a one-time seed per host and stay singlefile on purpose. Seeding whole-module would only move the 808 s cold build to the host's next landing and add the ~545 s build in front of it. The case this PR fixes recurs: a landing whose host root is busy.
Follow-up (not in this PR): partial caches. With 460-490 of 509 hits (the host cache a few landings behind), singlefile took 396 / 442 / 510 / 554 / 557 / 738 s, median 532 s, about the same as whole-module (40724663782, 40726640916, 40706172149, 40736642083, 40713069376, 40711035956). Those stay singlefile so they keep the cache current. The fix there is affinity routing: place the landing on the host whose cache is nearest.
Tests
tests/test_cmux_next_dev_release_host_root.py now runs the real script against stub compilers and checks the mode the compile sees in each case:
- auto: a first host build and a later host build compile singlefile.
- auto: cache off, off a mini, and a busy host root compile whole-module, with their log lines.
- An explicit mode wins, and an unknown one exits 2 and releases the lock.
The workflow test checks that no || 'singlefile' default is left. test_cmux_next_dev_landing_workflow.py and test_cmux_next_hybrid_ref_capture.py pass unchanged.
CI proof
This PR changes no Swift. Swift test proof comes from the fleet, because GitHub's cmux-next swift test is cut short by its 300 s watchdog until #26809 lands.
-
Fleet, full CmuxNext suite, 6 shards (
run-swift-testing-suites.sh, Xcode 26.6), at head 5085129d and at base 287054cd. Every head shard finished and printed its summary. -
Isolated per-suite runs (
package-test-lane.sh suite, one process per suite) for every suite the parallel run flagged:- Head steps 8b37124bcfb2974b3243ce01 and 35f495eddac3c2013adf16dd; base steps 2178bad0c6339abd611f475f and 602b02b9daa2f8587aff59c5.
- Every head issue is the same test at the same file:line on base: AgentSessionInUnshownPaneTests :39/:66, TabSelectionActionsTests :134, SidebarGroupToggleScalingTests :163/:203/:206, UpdateStagedYieldsToNewerTests :333, SidebarHugeGroupTests :71-74. There are no head-only reds.
-
No new SwiftUI is a base red with the same two findings (HomeChatColorsPanel.swift and HomeChatColorsView.swift, from the Chat Colors commits). Base run 40862042431 prints both and reports them as warning-only.
-
DiffRemoteWiringTests after #27389, in isolation at head 911c80de: passes (step 5da85ff10a957c39f004eb26).
-
cmux-next daemon tests at 8b9256fd, after #27385 fixed the signal 11: rerun job 123541420098 ran to its full summary (449 tests in 113 suites) with no crash. Its remaining reds match base per test:
theFirstLaunchBringsEverythingOnce:49/:56 (order differs) andaDowngradeAndReUpgradeMergesOnlyNewWork:188, which fail the same way on base runs 40870840853, 40870846201, 40870863264, 40870868605 and 40881495139. This PR changes no Swift. -
Swift test on GitHub is cut short by the watchdog (fixed separately); it is covered by the fleet proof above at this head.
-
Independent review: gpt-6-astra (high, fast) gave SHIP at 8b9256fd.
Risk
Low. Only the mode of instance-mode DEV compiles changes, and it goes back to the mode every DEV compile used before #24994. Host builds compile exactly as before. The product-reuse key changes once, so the first landing after merge compiles instead of reusing.
Rollback
Set the repo variable CMUX_NEXT_DEV_SWIFT_COMPILATION_MODE=singlefile to get today's behavior on every build (or wholemodule for every build). Or revert this PR.
Agent session: agent session
š¤ Generated with Claude Code