ci(cmux-next): DEV Release compile reuses a content-keyed compilation cache, not kept build products
Leo: "the incrementally, you know, tracking the latest commit or whatever, building it off the right way, building the right things, like reusing as much as you can"
What changes
Only the DEV Release compile (cmux NEXT DEV Release compile, the unsigned app the DEV channel signs and publishes). Nightly and stable stay whole-module, with no cache change.
On a glaeda mini the compile now:
- starts from a tree that keeps only
CompilationCache.noindex(Xcode's content-addressed compilation cache) andSourcePackages. No kept build products, so nothing stale links into the shipped app (the kept-DerivedData autolink failure, #21761). The DerivedData path is real-path'd because it is part of the cache keys. - builds with
COMPILATION_CACHE_ENABLE_CACHING=YESand the 3 GiB limit nightly uses, then prunes dead generations (prune-xcode-compilation-cache.py) and drops the cache past 6 GiB. - uses
SWIFT_COMPILATION_MODE=incremental(still-O). Whole-module is one job per module, which no cache can replay; incremental lets the cache replay every file the merge didn't change. - uses
DEBUG_INFORMATION_FORMAT=dwarf, because the DEV channel ships no dSYM (only the.appis zipped).
Self-healing: a build that fails on the cache itself (CAS open or corrupt errors in the log) drops the cache and builds once more without it. A code failure is not retried. Blacksmith fallback VMs keep the cache off (a fresh VM would only pay to fill it).
Switch back with one repo variable each: CMUX_NEXT_DEV_COMPILATION_CACHE=0, CMUX_NEXT_DEV_SWIFT_COMPILATION_MODE=wholemodule.
check-release-compile.sh (a compile check whose products are never shipped) also gets DEBUG_INFORMATION_FORMAT=dwarf: GenerateDSYMFile is serial work after the last link.
Trust
No remote store and no new writer. The cache bytes come only from the same runner that builds and uploads the app. fleet-cas as a remote is left out on purpose: letting cmux15's writer feed bytes into the signed DEV binary is a trust-policy question, and the coordinator is raising it.
Numbers (harness measure_next_build.py --steps, glaeda std minis, same job)
Before (base): xcodebuild 518 s / 545 s / 560 s (SwiftCompile (129 tasks), 0 cache hits), compile step 8.9-9.4 min, job 10.2-12.5 min.
After: the DEV step only runs at refs/heads/feat-cmux-next, so it is measured on the first real merges after landing; numbers go in a comment here. Each runner's first cache build is cold (incremental -O, cache filling); later builds on that runner replay.
- Success: a one-Swift-file merge on a warm runner: xcodebuild <= 300 s, with cache hits in
summary.md(build_metrics.pyalready counts them per target). - Failure: a stale object in the app (impossible by construction: no kept products, and cache keys are content); a cache fault failing the build instead of falling back; a cold incremental build much slower than WMO (switch the var back).
Risk
Medium-low. Incremental -O gives up cross-file optimisation inside a module for the DEV app only, so it may run slightly slower than nightly; behaviour is the same Swift semantics. Rollback is a repo variable.
Agent session: cosmic-gray
š¤ Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.