ci(cmux-next): reuse the compiled DEV Release app when its inputs did not change
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
Part 2 of 2. It depends on #24963, the key: job_input_fingerprint.py dev-release-product.
Today every DEV run compiles the Mac Release app, even when the merge changed nothing that goes into it. This PR wraps the DEV Release compile (cmux-scheme-compile in cmux-next.yml, push and dev_daily runs on feat-cmux-next) in a lookup and a record step:
- Lookup (after Select Xcode, deadline 3 min, step timeout 5 min,
continue-on-error):- computes the key on the checked-out tree;
- finds the newest unexpired
cmux-next-dev-release-product-<key>record from this repository's own push or dispatch run ofcmux-next.ymlon feat-cmux-next. Records from forks, pull requests, other branches and other workflows are refused; - downloads the
cmux-next-dev-daily-unsigned-<sha>artifact that the record names and checks its size and SHA-256 against the record.
- On a hit: the compile step uses those bytes and skips xcodebuild. A
dev_dailyrun also skips the steps that only feed xcodebuild (cmux-tui fetch, Rust, acpmux, bun, node, web bundles). Push runs still run them because they also compile Debug. - On a miss: a missing record, an untrusted or expired one, a digest mismatch, an API or network error, or the deadline each give a miss. The job compiles exactly as before and never fails because of the lookup. The job summary prints
hit(with the source run, commit and key) ormiss (reason). - Record: after a successful compile or reuse and upload, the run writes
manifest.json: key, recipe, commit, product artifact, SHA-256, and provenance (origin: compiled or reused,compiled_run_id,reused_from). It uploads the manifest ascmux-next-dev-release-product-<key>, kept 2 days like the app artifact it names. A reuse writes a record too, which keeps the chain alive. - Unchanged:
- Signing, packaging and publishing still run fresh at the exact SHA.
cmux-next-dev-daily.ymlis untouched and stampsCMUXCommititself. - The store is the Actions artifacts the DEV path already uses, so no new secrets or permissions (the job already has
actions: read). - Switches:
no_reusedispatches skip the lookup, andvars.CI_NEXT_DEV_PRODUCT_REUSE=0turns it off.
- Signing, packaging and publishing still run fresh at the exact SHA.
- Scope guard (hq#3967): the script refuses unless
GITHUB_WORKFLOW_REFiscmux-next.yml@refs/heads/feat-cmux-next. A test fails if any other workflow references the script or the record name.nightly.ymland the stable workflows are untouched.
Before (measure_next_build.py --dev-chain, last 12 DEV publishes, 2026-10-24)
| compile_job (min) | |
|---|---|
| median | 10.7 |
| range | 9.6 to 14.6 |
On a warm mini (source run 40130016492): about 60 s of steps before the compile, then 533 s of xcodebuild, then 18 s of upload. A hit skips the xcodebuild and, on dev_daily, the ~45 s of feeder steps. Each hit saves the 10-14 min DEV compile job, not the older 20-35 min figure. Replaying the key over the last 199 merges, 25% would hit.
After: TBD. I will fill this in from the same harness once a product-unchanged merge has run after landing.
Success / failure
- Success: a product-unchanged merge's DEV compile job takes ⤠~60 s and its summary prints
DEV Release product: hit. - Failure:
- a stale product ships (an input outside the key, or a record whose bytes don't match);
- a cache outage blocks or fails a build;
- a reuse leaks into nightly or stable.
Risk
Medium-low, CI only.
- (1) Staleness depends on the key's closure (part 1). It defaults to including everything, and its exclusions are pinned by tests.
- (2) Trust: the GitHub-reported run must be a push or dispatch of
cmux-next.ymlon feat-cmux-next in this repository. Records from fork and pull request runs are never taken. The manifest must name its own run and that run's product artifact. The bytes must match the SHA-256 in the record. - (3) Availability: every error is a miss, the lookup has a 3 min deadline inside a 5 min step timeout, and every reuse step is
continue-on-error. - (4) Provenance: on a hit, the published DEV app's bytes come from an earlier run with the same key.
CMUXCommitis still the exact SHA, and the run's record namescompiled_run_id.
Tests: tests/test_dev_release_product_reuse.py, run in the cmux-next checks job. It covers a hit, newest record wins, fallback past an expired product, digest mismatch, fork, pull request, wrong branch and wrong workflow, lane guard, an unreachable store, the CLI never failing, a record round trip, and the workflow scope. actionlint shows no new findings.
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.