Skip to content

ci: app-host rerun holds the product's canonical root - #14417

Merged
teamleaderleo merged 1 commit into
mainfrom
ci/rerun-canonical-root
Sep 25, 2026
Merged

teamleaderleo merged 1 commit into
mainfrom
ci/rerun-canonical-root

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

glaeda's review found that app-host-test-rerun.yml's rerun job hard-codes /private/tmp/cmux-ci, rm -rfs that root's derived-data-compile-admission, and never calls glaeda-canonical-root take. On an owned Mac with two roots it could replace a tree another job is using. It also rebuilt a product compiled in /private/tmp/cmux-ci-2 at root-1 paths, which its swiftmodules do not match.

  • scripts/ci/take-product-canonical-root.sh (new) reads the producer's root from the product receipt's derived, the same way restore-app-host-test-product.sh does. If the receipt has no canonical root, it uses CMUX_CI_CANONICAL_ROOT, then /private/tmp/cmux-ci. It rejects anything that is not /private/tmp/cmux-ci or /private/tmp/cmux-ci-<n>, holds the root with glaeda-canonical-root take <root> --wait 1800 when the helper exists, and prints the root.
  • In the rerun job, "Unpack products at the path CI compiled them" now extracts to $RUNNER_TEMP/rerun-products first. It takes the root, and only after that replaces <root>/derived-data-compile-admission (with a mv). It exports CMUX_CI_CANONICAL_ROOT, CANONICAL_ROOT and COMPILE_DERIVED_DATA to $GITHUB_ENV. The job-level hard-coded CANONICAL_ROOT / COMPILE_DERIVED_DATA env is gone. Exporting CMUX_CI_CANONICAL_ROOT also fixes a latent mismatch: canonical-resolve read that variable while the rest of the job used the hard-coded root.
  • The tests still run from $RUNNER_TEMP/cmux-derived-data-rerun, a DerivedData of the job's own, the same way the shard jobs do.
  • Blacksmith is unchanged: a receipt from a Blacksmith producer names /private/tmp/cmux-ci, and there is no helper to call.

Why the rebuild stays in <root>/derived-data-compile-admission and not a fresh directory: the cmuxTests bundle is compiled against the producer's swiftmodules, which carry absolute paths into that DerivedData and into <root>/src. In a different directory, those paths would point at whatever the root holds, possibly another revision's build. Taking the root first makes that directory this job's own until the job ends. test-e2e.yml owns the same path the same way.

glaeda follow-up (not in this PR): the hook still classifies rerun as an unknown job, which means compile, pinned to root 1. It holds root 1 from job start, so the take is a no-op for root-1 products and exits 2 ("one root per job") for a root-2 product. The rerun fails loudly in that case and no longer builds at the wrong paths. Adding ("app-host-test-rerun.yml", "rerun"): "gui" to WORKFLOW_JOB_CLASSES lets it take the producer's root like other consumers. It runs the app-host tests in the console session, so it needs the gui token.

Tests

  • python3 tests/test_app_host_test_rerun.py: 47 tests OK. The new CanonicalRootTests check that:
    • the workflow's code lines never name /private/tmp/cmux-ci; the fallback lives only in the helper
    • the take call is present, and it runs before the only rm -rf "$COMPILE_DERIVED_DATA"
    • products are staged outside the root
    • the three env vars are exported
    • the helper picks the producer's root over the one glaeda handed the job, keeps this job's root for receipts without one, rejects bad roots, fails when the take fails, and skips the take with no helper
  • ./tests/test_ci_self_hosted_guard.sh: 38 checks pass.
  • python3 tests/test_ci_canonical_build_root.py: OK.
  • tests/test_ci_change_areas.py::test_app_host_rerun_runs_on_the_products_pool: OK.
  • actionlint .github/workflows/app-host-test-rerun.yml: clean.

The existing test file is already wired into ci-guards.yml (app-host-execution group) and tests/test-execution.toml (linux-guard), so no new wiring is needed.

release-build audit

Scope: the release-build job in ci-macos.yml and the scripts it runs.

Paths it writes or deletes

  • Workspace:
    • build-universal/ is the Release DerivedData, plus CompilationCache.noindex. It is deleted at job start by "Reclaim release runner disk" and never cleaned at job end.
    • .spm-cache/ holds the SwiftPM checkouts and .package-cache. It is also deleted at start.
    • GhosttyKit.xcframework is deleted and re-extracted by download-prebuilt-ghosttykit.sh.
    • ghostty-cli-helper/ is a downloaded artifact.
    • .git/index.lock and .git/modules/**/*.lock are cleared by the first step.
    • All of these are per-runner-instance paths.
  • $RUNNER_TEMP: the cmux-tui manifests, cmux-release-product.tar.gz, and the reuse receipt.
  • $TMPDIR / mktemp: scratch dirs from test_thin_app_bundle.sh, test_install_cmux_tui_client.sh, download-prebuilt-ghosttykit.sh and build-cmux-cua.sh, all removed by traps.
  • $HOME, shared by every job on the mini:
    • ~/.cargo and ~/.rustup: install-rust-ci.sh may run rustup-init, rustup default stable, rustup target add, and install the DiffSidecar toolchain.
    • ~/Library/Caches/cmux/cmux-tui-client: install-cmux-tui-client.sh.
    • ~/Library/Caches/cmux/cmux-cua: build-cmux-cua.sh, which runs as an Xcode build phase.
    • Default Go caches, ~/Library/Caches/go-build and ~/go/pkg/mod, from the wireguard-go/tunnel build phase.
    • $RUNNER_TOOL_CACHE, from actions/setup-go.
  • Host-global: select-ci-xcode.sh runs xcode-select -s (sudo -n fallback) unless CMUX_CI_SKIP_XCODE_SELECT=1. It only reads /Applications to choose an Xcode.
  • Not touched: /private/tmp/cmux-ci* (no canonical root), /Users/Shared/cmux-build-fleet (owned build state), /Applications writes, launchd, defaults.

Signing and secrets

  • CODE_SIGNING_ALLOWED=NO. The only signatures are ad hoc (codesign --sign -) on cmux-cua and its helper app, plus codesign --verify. These do not use the keychain.
  • The only credential is github.token with read scopes (actions, attestations, contents). It is used for gh attestation verify, artifact download, and product restore.
  • The cache-restore action is restore-only.
  • No repository secrets, no keychain, no notarization.

GUI session: not needed. No app launch, no XCTest, and no run-in-console-session.sh.

Safe on a persistent owned mini that runs other jobs? Yes, with these caveats:

  1. xcode-select -s is host-global. It is harmless while every job on the mini selects the same Xcode. Otherwise, set CMUX_CI_SKIP_XCODE_SELECT=1 for this job; it already pins DEVELOPER_DIR.
  2. The rustup and ~/Library/Caches/cmux writes are shared $HOME state. Every other job writes it the same way, but two concurrent rustup installs can race on first use.
  3. Disk: a universal Release build-universal/ (several GB) stays in each runner instance's workspace until that instance's next release-build.
  4. runs-on is vars.MACOS_RUNNER_26, not the owned-pool picker, so it needs a routing change to reach a mini at all.

Recommended glaeda class: isolated (2 units, no root, no gui):

  • It compiles into its own workspace DerivedData, never a canonical root, so compile would take a root and the persistent-dd token for nothing.
  • It consumes no admission product, so it is not product.
  • It needs no console session, so it is not gui.
  • The 2-unit weight matches a universal Release compile.

The hook would treat it as an unknown job today (compile, pinned to root 1), so it needs an explicit "release-build": "isolated" entry in JOB_CLASSES before it is routed to minis.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

The rerun job now holds the product's canonical build root before rebuilding against it, so it no longer replaces trees another job is using and rebuilds at the paths the product was compiled at.

Bug Fixes

  • New scripts/ci/take-product-canonical-root.sh reads the producer's root from the product receipt's derived, falls back to CMUX_CI_CANONICAL_ROOT then /private/tmp/cmux-ci, and takes the root with glaeda-canonical-root take when the helper exists.
  • Products are staged in $RUNNER_TEMP/rerun-products and moved into the root only after the take succeeds.
  • CMUX_CI_CANONICAL_ROOT, CANONICAL_ROOT, and COMPILE_DERIVED_DATA are exported, fixing the mismatch where canonical-resolve read CMUX_CI_CANONICAL_ROOT while the job used a hard-coded root.
  • Blacksmith is unchanged: receipts there name /private/tmp/cmux-ci and there is no helper to call.

Written for commit 723aced. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Chores
    • App-host test reruns now validate the build location recorded with their artifacts and prepare that location before installing staged products. This makes reruns use the producer’s build area and helps prevent conflicts when multiple CI jobs run at the same time. Invalid locations are rejected, and reruns report an error if the build area cannot be acquired.

The rerun job hard-coded /private/tmp/cmux-ci, rm -rf'd that root's
DerivedData and never took the root, so on an owned Mac with two roots it
could replace a tree another job was using, and it rebuilt a root-2
product at root-1 paths.

It now unpacks the products beside the job, reads the producer's root
from the receipt (falling back to CMUX_CI_CANONICAL_ROOT, then
/private/tmp/cmux-ci), holds it with glaeda-canonical-root take, and only
then replaces that root's DerivedData. CMUX_CI_CANONICAL_ROOT is exported
so canonical-resolve builds in the same root. Blacksmith keeps
/private/tmp/cmux-ci and has no helper to call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai

coderabbitai Bot commented Sep 25, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: f8240dc9-ed30-4635-a644-29ba139e1eff

📥 Commits

Reviewing files that changed from the base of the PR and between a855dbf and 723aced.

📒 Files selected for processing (3)
  • .github/workflows/app-host-test-rerun.yml
  • scripts/ci/take-product-canonical-root.sh
  • tests/test_app_host_test_rerun.py
 ____________________________
< Try...catch me if you can. >
 ----------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@teamleaderleo
teamleaderleo merged commit 2d844cb into main Sep 25, 2026
53 of 55 checks passed
@teamleaderleo
teamleaderleo deleted the ci/rerun-canonical-root branch September 25, 2026 08:38
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 25, 2026
8409047 ci: run the suites that mention an app-source change (manaflow-ai#14418)
cbebee8 fix(homebrew): generate the symbol form of depends_on macos (manaflow-ai#14424)
e9bb38a ci(ios): only pick simulators the active Xcode SDK can target (manaflow-ai#14422)
5b2533c fix(ios): stop calling a mutating method inside #expect (manaflow-ai#14421)
4ab2739 ci: pick the pool with the least expected wait, bounded by every run's peak (manaflow-ai#14410)
26292a4 ci(nightly): warn instead of failing when GitHub refuses the tag move (manaflow-ai#14425)
f4b331d Merge pull request manaflow-ai#14090 from manaflow-ai/14078-cloud-codex-restore-garble
193f5d9 test: restore AppDelegate.shared after every XCTest case (manaflow-ai#14379)
31588d6 ci: run a tart-* pick as auto while the Tart VMs are offline (manaflow-ai#14416)
2d844cb ci: app-host rerun holds the product's canonical root (manaflow-ai#14417)
d0f485e Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
a855dbf test: fix the dead-key crash and sidebar AX walk failing on main (manaflow-ai#14406)
066f300 Merge pull request manaflow-ai#13938 from manaflow-ai/13893-desktop-click-ownership
0c2bb9d Merge remote-tracking branch 'origin/main' into 13893-desktop-click-ownership
9670d83 Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
cb88a4b Merge branch 'main' of https://github.com/manaflow-ai/cmux into 13893-desktop-click-ownership
86504fb fix: import Cloud package for team picker
885a39c test: import CmuxCloud in the Desktop navigation tests
75070d9 Merge remote-tracking branch 'origin/main' into 13893-desktop-click-ownership
3dfcfb9 Merge branch 'main' of https://github.com/manaflow-ai/cmux into 13893-desktop-click-ownership
52020d3 Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
9cafdf5 test: register cloud preview during materialization
bc09ec8 test: scope desktop registration hook to the preview resource
ea4242c Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
1be4c92 fix: count retained cloud previews as planned
4f98bd3 fix: align Xcode iroh package requirement
4a0bd3a chore: update Xcode package lockfile
8cda030 Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
ddeb03d fix: pin published iroh Swift release
1d9082a chore: update iroh package lockfiles
fc2b529 fix: pin attested iroh Swift artifact revision
4c33353 test: import surface catalog models in cloud actions
25c64f2 Merge branch 'main' of https://github.com/manaflow-ai/cmux into 13893-desktop-click-ownership
4bd5808 test: import shared surface catalog models
59eddd9 Merge remote-tracking branch 'origin/main' into 13893-desktop-click-ownership
a157f5c Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
cbc0118 ci: pin GhosttyKit for replay fix
4a48e3d Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
4784eb2 fix: preserve Cloud replay trailing rows
d081368 Merge origin/main and fix replay API visibility
7202960 Merge remote-tracking branch 'origin/main' into 13893-desktop-click-ownership
a846dfd Merge branch 'main' of https://github.com/manaflow-ai/cmux into 14078-cloud-codex-restore-garble
96d5686 fix: delimit replay rows when scrollback exists
6ac603e fix: use terminal history boundary for replay
054dc50 style: apply hosted replay formatting
90fa111 fix: preserve replay history and protect tagged resources
d2d6aa3 fix: refresh Cloud renderer after replay application
91601b8 revert: remove speculative Cloud replay grid overrides
cb2dc58 test: reproduce Cloud replay shifting sparse screens with history
c78ffdc fix: keep replay sizing helpers in app target
1e6f928 fix: preserve Cloud sizing intent across replay
e568942 fix: keep Cloud replay geometry transient
c094d63 Merge remote-tracking branch 'origin/14078-cloud-codex-restore-garble' into 14078-cloud-codex-restore-garble
8ed24b2 fix: align Cloud replay with remote grid
bbc466c test: cover Cloud replay grid alignment
cba191e test: cover self-registered Desktop materialization
105f24f fix: keep a Cloud Desktop pane that registers itself while materializing
9397594 Revert "fix: retain local Desktop projection provenance"
dc9e8af fix: retain authored colors when Cloud replay omits sidecar
58d4105 test: preserve authored Cloud colors across sidecar-free replay
a5af809 Merge remote-tracking branch 'origin/main' into issue-14078-cloud-codex-restore-garble
781a063 Merge origin/main into desktop click ownership
82b100a test: cover legacy applied resize responses
a9f6a92 Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
9d90d5e fix: clear Cloud ownership after replay confirms peer loss
6a36349 fix: defer cross-client Cloud loss until replay state
9bd3588 fix: ignore no-op Cloud resize acknowledgements
99329a1 fix: retain pending Cloud claims through handshake
4c0fa87 fix: demote Cloud mirror after cross-client rejection
509b984 fix: preserve explicit Cloud claim intent
dce99b4 fix: distinguish passive Cloud lease outcomes
5de372f test: allow automatic restore claim response
f7a3bc7 fix: wait for Cloud resize outcome before claiming
2fdaef3 fix: block rejected cross-client Cloud sizing claims
4804326 fix: stop passive Cloud mirror claim oscillation
223eb67 fix: restore debug title formatter linkage
68fb24d test: keep replay reset marker in restore fixture
674248c Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
1a11606 fix: reset Cloud VT state for replacement replays
2a2e092 test: reproduce stale Cloud replay cells after restore
15ba7c4 fix: preserve restore intent before process probing
a900e91 test: cover click Desktop graph reconciliation
ff2694f refactor: isolate workspace title debug formatting
53dd942 Read matchingObservation after it is declared in the restore liveness check
1b288aa Merge remote-tracking branch 'origin/main' into 14078-cloud-codex-restore-garble
8b8c669 test: fence passive Cloud claims with protocol traffic
62532fc fix: remove duplicate Cloud restore test registration
827d859 chore: sync Cloud restore test wiring
22a187b fix: import workspace liveness in Codex restore policy
92126bd test: assert restored Cloud resize dimensions
b7e457f fix: retain Cloud geometry claim policy across hidden restores
5dccec0 test: reproduce lost Cloud geometry eligibility after hidden restore
97c4673 test: preserve Cloud replay state across hidden restore geometry
e0d44a0 fix: retain local Desktop projection provenance
31f698b fix: preserve committed routes while proxy connects
b976180 fix: preserve preview provenance and committed Cloud routes
80f7087 fix: retain explicit Desktop placement provenance
3ee2ece fix: preserve Cloud Desktop panes during reconciliation
39b61fc test: keep Cloud Desktop previews during reconciliation
4ce4f4f fix: let activated Cloud browsers own route navigation
519bf26 test: reproduce desktop navigation without a mounted view
7d2b58a Merge origin/main and preserve per-run E2E cleanup
1b1feb8 test: use lifecycle-safe workspace creation in Desktop fixture
a722c20 ci: restore E2E products inside the owned runner temp root
903513c test: enforce E2E DerivedData cleanup ownership
c0f96a2 test: keep Desktop placement fixture windows hidden
e8a34f4 Merge main after Desktop ownership fix landed
fb9955b fix: keep Desktop view opens on the captured destination
1e696af test: give Desktop placement fixtures a complete native window route
9d3e2d8 fix: capture the Desktop view destination before scheduling
f327329 Merge remote-tracking branch 'origin/main' into 13893-desktop-click-ownership
6dc7d9f test: establish mouse event context for the Desktop regression baseline
7cdeac6 Merge remote-tracking branch 'origin/main' into 13893-desktop-click-ownership
1f9c925 fix: retain the Desktop click destination across queued work
b4f17f6 test: reproduce queued Desktop click targeting another Cloud workspace

# Conflicts:
#	.github/workflows/app-host-test-rerun.yml
#	.github/workflows/ci-guards.yml
#	.github/workflows/ci.yml
#	.github/workflows/nightly.yml
#	.github/workflows/test-e2e.yml
#	.github/workflows/test-ios.yml
#	.github/workflows/update-homebrew.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant