Skip to content

ci(e2e): start builds from main's DerivedData so test-only changes skip the app compile - #14016

Merged
teamleaderleo merged 2 commits into
mainfrom
ci/e2e-incremental-deriveddata
Sep 23, 2026
Merged

teamleaderleo merged 2 commits into
mainfrom
ci/e2e-incremental-deriveddata

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

A test-e2e.yml dispatch that changes one test file recompiles the whole app host. The build starts from an empty DerivedData, and a fresh checkout makes every source file newer than any earlier build. In run 35868678521 the compile took 14 min and the selected test ran for 18 s. CLAUDE.md's "Reading CI cost" puts the app scheme at 691 of the 735 s build-for-testing spends.

With this change, the build job adopts the newest DerivedData (build database, intermediates and products) that a main run of this workflow published. It then restores the recorded modification time onto every input file whose content is byte-identical. Changed files keep their checkout time, so Xcode rebuilds only what differs. Correctness does not depend on how far the adopted DerivedData is from the selected revision; the distance only affects compile time.

  • Producer: a run on main whose selected revision main already contains publishes its DerivedData, minus Logs and Index.noindex, as a 3-day artifact. This is the trust rule the compilation cache already uses. It is an artifact rather than actions/cache because the repository cache is at 10.5 GB, over its 10 GB cap.
  • Consumer: it adopts only artifacts from workflow_dispatch runs of test-e2e.yml on main, and archive members or links that escape DerivedData are rejected. The key is the existing compilation-cache fingerprint: Xcode version plus workspace and DerivedData paths, which is what the intermediates embed.
  • Failure: adopt, package and publish are optional, allowlisted in test_ci_self_hosted_guard.sh alongside the compilation-cache steps. A miss, expired artifact or bad archive leaves an empty DerivedData, which is today's cold build.

Tradeoffs. Adding steps to the build job changes the E2E product recipe fingerprint, so exact-product reuse misses once after merge. Archive size and transfer time are not measured yet; publishing is skipped above 12 GiB. The first producer appears when someone dispatches test-e2e.yml on a main-contained revision (for example ref=main).

Validation. tests/test_e2e_warm_derived_data.py (5 tests, registered in the linux-guard lane) covers: unchanged inputs take the producer's time and changed or new inputs do not; DerivedData and .git are not inputs; only main dispatches of this workflow are trusted; escaping archive members are rejected. All 137 linux-guard tests pass locally and actionlint is clean. Not yet verified on a Mac: that Xcode 26 in fact rebuilds only the changed test file from an adopted DerivedData. The cost report in the build summary now records the adoption, so the first two dispatches after merge (one ref=main producer, then a test-only branch) measure it.

— Dulcinea g1 🎐
run: run_cmux_main_app_host_baseline_inherited_failures_blocking_full_ci_20260923_576a13f4

🤖 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

Starts E2E builds from the newest DerivedData a main run of test-e2e.yml published, so a change to one test file rebuilds only that test instead of the whole app host. The build job adopts the archive, restores the recorded modification time onto every input whose content is byte-identical, and stamps changed or new inputs with the current time so they cannot keep an archive's older mtime and escape a rebuild. A miss, expired artifact, or bad archive falls back to today's cold build.

Tradeoffs and validation. Exact-product reuse misses once after merge because the added steps change the E2E product recipe fingerprint. Publishing is skipped above 12 GiB, requires a recorded input manifest, and archive transfer time is not measured yet. The adopt step times out after 10 minutes. tests/test_e2e_warm_derived_data.py covers unchanged, changed, and new inputs, trust rules, archive escape rejection (including absolute in-tree links), and all 137 linux-guard tests pass locally. Not yet verified on a Mac that Xcode 26 rebuilds only the changed test file from an adopted DerivedData.

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

Review in cubic

…ip the app compile

The E2E build adopts the newest DerivedData a main run of test-e2e.yml
published, then restores the recorded modification time onto every input
whose content is unchanged. Changed inputs keep their checkout time, so
Xcode rebuilds only what differs. Main runs of contained revisions publish
their DerivedData as a 3-day artifact. Every step is optional: any miss or
failure is today's cold build.

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

cursor Bot commented Sep 23, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 17 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: 43064714-7a17-47d8-aeb2-559bb44d129f

📥 Commits

Reviewing files that changed from the base of the PR and between 3ca19ad and eadec1a.

📒 Files selected for processing (8)
  • .github/workflows/ci-guards.yml
  • .github/workflows/test-e2e.yml
  • scripts/ci/e2e_warm_derived_data.py
  • scripts/ci/product_input_identity.py
  • tests/test-execution.toml
  • tests/test_ci_guard_workflow_structure.py
  • tests/test_ci_self_hosted_guard.sh
  • tests/test_e2e_warm_derived_data.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.

…ree links

Review of the warm DerivedData adoption found a changed input could keep an
archive's older mtime (GhosttyKit, SwiftPM binaries) and escape a rebuild.
Replay now stamps every changed or new input with the current time. Adoption
also skips archives over 12 GiB, times out after 10 minutes, treats any
error as a cold build, accepts absolute links that stay inside DerivedData,
and ignores fresh-checkout inode changes. The helper joins the product
identity inputs, and publishing requires a recorded input manifest.

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

Copy link
Copy Markdown
Collaborator Author

Independent review (a separate agent, read-only, at a6c46c35db). It re-ran the new test, 10 related guard tests and actionlint, all passing.

# Finding Disposition in HEAD
1 Blocker. A changed input unpacked from an archive (GhosttyKit, Sparkle, Sentry) keeps the archive's older mtime. swift-driver treats external dependencies as changed only when newer than the prior build, so importers would not be rebuilt. Fixed. replay stamps every changed or new input with the current time. Regression test test_a_changed_input_unpacked_with_an_old_time_is_still_rebuilt.
2 Adoption has no size cap or timeout. Fixed. Artifacts over 12 GiB are skipped, and the step has a 10-minute timeout.
3 The helper is outside product identity. Fixed. Added to PRODUCT_CI_INPUTS.
4 Every absolute symlink is rejected, so Xcode's in-tree absolute links would force a cold build every time. Fixed. Links are accepted when their target resolves inside DerivedData, and extraction uses the tar filter because Python 3.14's data default refuses absolute links. Regression test added.
5 A corrupt gzip escapes the handler. Fixed. Any exception now means a cold build.
6 Restore runs after "Resolve Swift packages". Kept. Replay needs the resolved package tree, and the failure path rebuilds resolver state.
7 "Record build input times" can fail the build. Fixed. It is now optional and allowlisted, and publishing requires it to have succeeded.
8 The newest artifact by creation time may be an old revision. Kept. That costs speed, not correctness.
9 llbuild compares inodes, and a fresh checkout changes every inode. Adoption sets com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges. The effect is measured after merge, as the PR body says.

Trust, tar bounds, if: wiring and output names were checked and hold. There are no remaining blockers. All 137 linux-guard tests pass locally on HEAD, and actionlint is clean.

— Dulcinea g1 🎐

@teamleaderleo
teamleaderleo enabled auto-merge (squash) September 23, 2026 16:18
@teamleaderleo
teamleaderleo merged commit 1773c54 into main Sep 23, 2026
63 checks passed
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 23, 2026
06c2101 ci: route streamed validation by capability instead of by lane name (manaflow-ai#14002)
1773c54 ci(e2e): start builds from main's DerivedData so test-only changes skip the app compile (manaflow-ai#14016)
c890374 ci: pin the nightly runner guards to the whole expression (manaflow-ai#13997)
8abd2e9 ci: flag condition polls bounded by a Task.yield() count (manaflow-ai#14019)
e4ca672 ci(ios): record the cmux.app upload once Apple accepts it (manaflow-ai#14014)
260b648 ci: check what the runner variables hold, not just what the workflows say (manaflow-ai#13992)
25ad5af feat(terminal): opt-in macOS text-editing gestures at the shell prompt (manaflow-ai#13921)
daf9649 test: drop six focus-history cases superseded by FocusHistoryScopeTests (manaflow-ai#13975)
11202e3 Name the workspace that workspace.reorder could not resolve (manaflow-ai#13961)
2a4f3f6 fix(fork): make the fallback refresh await its own queued validation (manaflow-ai#13960)
4b82298 ci: let test-depot run one app-host test by selector (manaflow-ai#14001)
5d1ecb8 test: give each drained write its own deadline in the short-chunks reader test (manaflow-ai#13999)

# Conflicts:
#	.github/workflows/ci-guards.yml
#	.github/workflows/ci-health-report.yml
#	.github/workflows/ios-appstore-upload.yml
#	.github/workflows/ios-streamed-validate.yml
#	.github/workflows/iroh-release-gate.yml
#	.github/workflows/nightly.yml
#	.github/workflows/test-depot.yml
#	.github/workflows/test-e2e.yml
teamleaderleo added a commit that referenced this pull request Sep 24, 2026
* ci: seed test-e2e's warm DerivedData from main every 6 hours

#14016 lets a test-e2e.yml build start from DerivedData that a
main-contained dispatch published, so a test-only change recompiles only
the tests. Only such a dispatch publishes it, and focused dispatches test
pull request revisions, so none ever did: builds report
"no-main-derived-data" and compile the whole app from empty DerivedData
(e.g. 12.4 min of build for a 36 ms suite in run 35939043846).

Dispatch test-e2e.yml on main's HEAD with one fast suite every 6 hours,
skipping a SHA that already has a seed dispatch.

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

* ci: make seed runs compile, and drop a half-adopted DerivedData

A seed dispatch could adopt an exact product, skip the compile, and so
publish nothing; seed dispatches now skip product reuse. A step timeout in
"Adopt main's DerivedData" kills the restore before its own cleanup, so the
compile could start on a half-extracted tree; a failed adoption now clears
the DerivedData first. The seed dedupe now looks back 300 runs (50 covered
about two hours) and retries only runs that never finished.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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