Skip to content

ci(e2e): start owned E2E builds from the Mac's kept state, upload after tests - #14692

Merged
teamleaderleo merged 1 commit into
mainfrom
ci/e2e-owned-state
Sep 25, 2026
Merged

teamleaderleo merged 1 commit into
mainfrom
ci/e2e-owned-state

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

On an owned Mac (glaeda-*), test-e2e's build spent about 8 minutes on network before it compiled. It downloads at 6–7 MB/s there, against Blacksmith's 35–55 MB/s:

  • Swift packages: 229–282 s to restore 1.7 GB.
  • DerivedData seed: 212–269 s to adopt 1.6–1.9 GB.
  • Product upload: about 130 s for 573 MB before the tests could start.

That covers runs 36169238297 on cmux-austin-mini-1 and 36169200906 on cmux13s. Both hosts' job records say clear, so the cause is network, not heat or contention. The compile itself runs within about 1.1–1.2× of Blacksmith.

What changes

The new steps only run on glaeda- runners. Blacksmith and GitHub-hosted runs are unchanged.

  • Start from the Mac's kept state. Compile admission already keeps this per canonical root (owned_build_state.py), and the E2E build uses it after a reuse miss:
    • check clones the Mac's packages, and "Cache Swift packages" is skipped when it did.
    • prefer (with CI_OWNED_PREFER_SEED) can pick a near seed instead.
    • adopt clones the kept DerivedData where the seed would go. A failed adopt is discarded, so the build starts empty rather than from a partial clone.
    • The seed steps take CMUX_SEED_LOCAL_CACHE/CMUX_SEED_EXACT, as admission's do.
    • The helper is fetched at github.workflow_sha, since an older tested revision's copy moved kept state out of the store.
  • No record, keep or save. A dispatch builds whatever revision it names and must not become the next pull request's starting point. It does do admission's upkeep: seed-source.json, the size cap, and the local seed cache with CI_OWNED_PREFER_SEED.
  • Why the reads are safe: the glaeda runner hook gives this job the root's token for the whole job, and only a job holding root k writes root k's store.
  • Upload after the tests on an owned Mac. The tests restore the local archive, not the upload.
    • It runs whatever the tests did, even on cancel, so a failing or superseded run still publishes its product.
    • It runs only when the archive still hashes to what the package step sealed.
    • It uses a second step name, because product_input_identity.py needs unique names. e2e_sibling_build.py and reuse_app_host_products.py accept either name.
    • Consumers testing an older revision only see such a product once the whole build job succeeds.
    • A job timeout still loses it.

Status: draft until an owned-Mac run is measured

The validation dispatch (36179450718) was refused before it started. The live glaeda hook treats this job as an unknown id pinned to /private/tmp/cmux-ci, and root 1 on cmux9s was busy (capacity: the canonical root token is taken (build is compile)). teamleaderleo/glaeda#1259 classes the job properly and lets it take any free root. Once that is deployed, or root 1 is free, the dispatch needs repeating.

Review

An independent read-only review of the first version found two real defects, both fixed here. First, uploading after the tests on every runner lost the product on cancel or timeout, so that now only happens on owned Macs, with a cancel-safe step. Second, a half-cloned DerivedData could survive a failed adopt. It confirmed:

  • Blacksmith and GitHub-hosted jobs behave exactly as before.
  • Store selection per root and the fingerprints match compile admission.
  • Exactly one of the seed or the kept DerivedData is used in every combination.

Verification

  • The linux-guard tests touching this change pass: test_ci_e2e_compilation_cache, test_ci_owned_build_state (new E2EWiring), test_e2e_sibling_build, test_reuse_app_host_products, test_ci_change_areas, test_ci_self_hosted_guard.sh.
  • The one failure, test_a_missing_manifest_fails_instead_of_silently_compiling, also fails on main on macOS: bash 3.2 reports ONLY_TESTING[@] unbound under set -u.

🤖 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

On owned Macs (glaeda-*), the E2E build now starts from the build state compile admission already keeps there, cutting roughly 8 minutes of pre-compile network downloads and uploading the product after the tests instead of before. Blacksmith and GitHub-hosted runs are unchanged.

  • The build clones the Mac's kept Swift packages and DerivedData (or a preferred near seed when CI_OWNED_PREFER_SEED is set), skipping the package cache restore and seed adopt on a warm hit.
  • A failed DerivedData clone is discarded so the build starts empty rather than from a partial copy.
  • The build job never records, keeps, or saves state: a dispatch must not become the next PR's starting point. It does run admission's upkeep (seed-source.json, size cap, local seed cache).
  • On owned Macs that test in the build job, the product upload moves to a step running after the tests, even on failure or cancel, and only when the archive still hashes to what was packaged.
  • restore-app-host-test-product.sh no longer requires an artifact id, and e2e_sibling_build.py/reuse_app_host_products.py accept either upload step as publication.

Draft status: the validation dispatch was refused because the glaeda runner hook doesn't yet classify this job; it needs repeating once that's deployed or root 1 is free.

Written for commit d6ff15e. Summary will update on new commits.

Review in cubic

On an owned Mac (glaeda-*), test-e2e's build spent about 8 minutes on
network before it compiled: 229-282 s restoring 1.7 GB of Swift packages
and 212-269 s adopting a 1.6-1.9 GB DerivedData seed at 6-7 MB/s. Then it
uploaded the 573 MB product for about 130 s before the tests could start.

- The build now starts from compile admission's kept state for its
  canonical root, through owned_build_state.py:
  - `check` clones the Mac's packages, and the SwiftPM cache restore is
    skipped when it did. It also says whether the kept DerivedData matches
    this build's canonical fingerprint.
  - `prefer` (with CI_OWNED_PREFER_SEED) may pick a near seed instead.
  - `adopt` clones the kept DerivedData where the seed would go. A failed
    adopt is discarded, so the build starts empty rather than from a
    partial clone.
  - The seed steps take CMUX_SEED_LOCAL_CACHE and CMUX_SEED_EXACT as
    admission does.
  - It never records, keeps or saves a DerivedData: a dispatch builds
    whatever revision it names and must not become the next pull request's
    starting point. It does do admission's upkeep: seed-source.json, the
    size cap, and the local seed cache with CI_OWNED_PREFER_SEED.
  - The helper is fetched at the workflow's revision, since an older tested
    revision's copy moved kept state out of the store.
- On an owned Mac that tests in the job, the upload moves after the tests,
  under "Upload the compiled test product after the tests". The tests
  restore from the local archive, not the upload.
  - It runs even when the tests fail or the job is cancelled, so a failing
    or superseded run still publishes what it compiled.
  - It runs only when the archive still hashes to what the package step
    sealed.
  - e2e_sibling_build.py and reuse_app_host_products.py accept either
    upload step as publication.
  - restore-app-host-test-product.sh no longer needs an artifact id for
    its measurement record.

Blacksmith and GitHub-hosted runs are unchanged: every new step is gated
on a glaeda- runner, and the seed environment is empty elsewhere.

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

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 5 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 12a3cb59-64da-4f88-8463-555e891a4cc3

📥 Commits

Reviewing files that changed from the base of the PR and between ff02854 and d6ff15e.

📒 Files selected for processing (11)
  • .github/actions/e2e-run-tests/action.yml
  • .github/workflows/test-e2e.yml
  • scripts/ci/e2e_sibling_build.py
  • scripts/ci/restore-app-host-test-product.sh
  • scripts/ci/reuse_app_host_products.py
  • tests/test_ci_change_areas.py
  • tests/test_ci_e2e_compilation_cache.py
  • tests/test_ci_owned_build_state.py
  • tests/test_ci_self_hosted_guard.sh
  • tests/test_e2e_sibling_build.py
  • tests/test_reuse_app_host_products.py

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.

@github-actions

Copy link
Copy Markdown
Contributor

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

@teamleaderleo
teamleaderleo marked this pull request as ready for review September 25, 2026 20:34
@teamleaderleo
teamleaderleo merged commit ab5ac72 into main Sep 25, 2026
79 of 82 checks passed
@teamleaderleo
teamleaderleo deleted the ci/e2e-owned-state branch September 25, 2026 20:34
@github-actions

Copy link
Copy Markdown
Contributor

Merge receipt for d6ff15ec5c: every check was green at merge (15 verified; 13 skipped by policy). Full suite runs on main after merge.

rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 25, 2026
b63ab49 Fail remote-tmux review when a wait is a timer instead of an event (manaflow-ai#11264)
b436c92 Fail closed when CLI forwarding loops back to the GUI binary (manaflow-ai#8788)
136eb2a fix(ios): make the last intermittent CmuxMobileShell tests deterministic (manaflow-ai#14721)
dc7f5bd fix(ios): keep terminal composer dock at bottom (manaflow-ai#14702)
cb4429b ci: add owned_build_state.py warm-keys for warm admission routing (manaflow-ai#14717)
be1ab5e ci(ios): charge in-flight auto runs simulators only when they took the fleet (manaflow-ai#14716)
5110582 ci: clear fixed DerivedData by renaming it aside first (manaflow-ai#14710)
1b8a603 fix(ios): unregister terminal output streams by registration identity; fix stale render-grid tests (manaflow-ai#14711)
3cae7dd refactor: move the browser WebKit support layer into CmuxBrowser (manaflow-ai#14398)
6693884 Run the Codex monitor Stop replay outside the hook handler frame (manaflow-ai#14715)
133a083 ci(e2e): take the owned Mac's gui token just before testing in the build (manaflow-ai#14705)
4c36ace ci: re-run every job of an E2E run whose build did not succeed (manaflow-ai#14712)
ab5ac72 ci: start owned E2E builds from the Mac's kept state, upload after tests (manaflow-ai#14692)
6a3d2d0 test(ios): align Mac switch and pool tests with build-scoped identity (manaflow-ai#14708)
0b16a8b Keep closePanel's unmapped fallback from closing another panel's tab (manaflow-ai#14704)
69c9518 close-surface: reject a blank --workspace or --window (manaflow-ai#14706)
8476043 ci: route warm admission by static runner labels, never write labels (manaflow-ai#14696)
537d53f Guard direct GhosttyKit setup against incompatible Zig (manaflow-ai#4706)
4b200e1 Restore Pi wakeup alerts across reloads (manaflow-ai#12861)
9b291c0 ci: resolve an owned Mac's kept Swift packages offline (manaflow-ai#14709)
31c9105 Stop attaching the unverified per-resume relay MAC (manaflow-ai#14694)
5443f43 Merge pull request manaflow-ai#13565 from manaflow-ai/feat-recover-forgotten-computers
e817240 Add app.equalizeSplitsOnCreate to balance panes on new splits (manaflow-ai#14703)
a358095 fix: never use a shell bootstrap executable in a resume binding (manaflow-ai#5848)
3f177af Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
8b4c97b Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
dce9509 ci: update iOS checkout routing assertion
5dbf06c ci: update CLA workflow digest after main pin
44c00fb Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
d22189d Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
eeca51a fix: make event reconnect policy instance based
9e29c96 Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
d860564 Model first Cloud receipt before remote graph discovery
87f1305 Verify forgotten Mac recovery with the real paired store
2237f8c Merge main and retain upstream test repairs
9ccaa7a Avoid type-check timeout in process fixture
a848b5e Simplify AppKit accessibility test setup
82a9769 Isolate process generation fixture from runner TTY state
4c129bb Enable and restore AppKit assistive access in mounted tree test
9a9acca Enable accessibility output in the hosted SwiftUI test fixture
5ce39d0 Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
a29a7ec Read proxy accessibility children and text through one attribute bridge
12938b6 Test accessibility walkers against modern and legacy proxy nodes
04a30c5 Revoke the original pairing in the targeted dial authority regression
ff1888e Import the extracted Cloud module in the team picker
9c9f259 Merge main and adopt the verified focus recovery fixture
5d41fc8 Align hosted UI fixtures with runtime paths and presentation lifecycle
87dbbd9 Preserve unknown legacy restore liveness after main integration
41a8d4b Revert "Establish running agent state in auto-resume fixtures"
6c7d14b Revert "Use the running-agent fixture for second-restore cwd coverage"
9d276b5 Route Kiro permission-mode fixtures through the mock delivery target
cdb4eae Restore the host app delegate after registration tests
4bdc410 Use the running-agent fixture for second-restore cwd coverage
0b58cbc Establish running agent state in auto-resume fixtures
20c6415 Wait for mounted project content in accessibility test
3ba7b6a Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
2c3c680 Make reparent focus test geometry deterministic
d2f563b Stabilize canonical cache recipe guard
cc60792 Merge current main into forgotten Mac recovery
7dc515f Merge branch 'main' into feat-recover-forgotten-computers
cfd4773 Preserve per-instance forgotten Mac recovery
6a95584 Canonicalize recovered directory identities
f083246 Preserve queued forgotten Mac refreshes
e5202a6 test(ios): use canonical UUID duplicate fixture
ca7ee53 fix(ios): serialize forgotten Mac recovery retries
db851c2 Merge remote-tracking branch 'origin/main' into feat-recover-forgotten-computers
35269cb fix(ios): rehydrate Macs after forget recovery
d0266b7 Merge current CI workflow identity guard into device recovery
45b7c72 fix(i18n): distinguish signed-in Mac recovery from connectivity
730260c Merge remote-tracking branch 'origin/feat-recover-forgotten-computers' into feat-recover-forgotten-computers
a17f8dd fix: complete forgotten Mac recovery without replaying revocation
d64766a test: reproduce revocation during device recovery registration
faf5b55 test: keep recovery authority limited to enrollment
a9c437a test: cover duplicate forget and recovery lifecycle gaps
033e7a0 chore: normalize project ordering
28f63ec fix: recover forgotten Macs from revocation events
7b8e322 fix: recover forgotten Mac registrations
f1aca40 test: validate recovery enrollment proof
62b4e14 test: cover authenticated forgotten-device recovery
9cb2f97 test: cover recovery after device forget

# Conflicts:
#	.github/workflows/app-host-test-rerun.yml
#	.github/workflows/ci-guards.yml
#	.github/workflows/ci-macos.yml
#	.github/workflows/ci-queue-janitor.yml
#	.github/workflows/ci.yml
#	.github/workflows/nightly.yml
#	.github/workflows/seed-derived-data.yml
#	.github/workflows/test-e2e.yml
#	.github/workflows/test-ios.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