glaeda-cmux-runner-hook: give test-e2e's build and test the gui token - #1259
Merged
Merged
Conversation
test-e2e.yml's `build` and `test` were unknown job ids, so both were costed
as a pinned compile (persistent-dd + root 1) and neither took the gui token.
cmux's "ci: run E2E tests on the runner that built them" moves the E2E tests
into `build` (cmuxTests through run-in-console-session.sh, cmuxUITests in the
console user's GUI bootstrap), so on an owned mini it ran GUI tests next to
app-host shards holding the one console session. The old `test` had the same
gap.
- ("test-e2e.yml", "build") is a new class, compile-gui: 2 units, gui + root,
no persistent-dd (it never runs owned_build_state.py or sets
CMUX_SEED_LOCAL_CACHE). It is a producer, so it takes the root its token
picks and reads it from CMUX_CI_CANONICAL_ROOT, as every path in the job
already does; its own restore's take-root is a no-op.
- ("test-e2e.yml", "test") is gui, like app-host-test-rerun's rerun: a root
consumer that takes the producer's root from its restore step.
- Both wait GUI_WAIT_S for the gui token before refusing, like other GUI jobs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second root has its own seed and cache keys (compile-app-host-test-product.sh fingerprint adds root=), and the E2E build keeps no per-root state, so the own-root preference only made a build on runner instance 1 compile cold at root 2 while root 1 was free. compile-gui now tries root 1 first and falls back to any free root. Docs note the unpinning applies with canonicalRoots > 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 25, 2026
cmux#14692 starts owned E2E builds from compile admission's kept state for their root (clone only, never keep/save/record). The root token already guards that store, so compile-gui still takes no persistent-dd; the comment and docs said the job never touched it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The hook did not know manaflow-ai/cmux test-e2e.yml's
buildandtest, so it treated both as a compile pinned to root 1 (persistent-dd+root). Neither job took theguitoken. The cmux PR "ci: run E2E tests on the runner that built them" (branchci/e2e-single-runner) moves E2E test execution intobuild, through thee2e-run-testscomposite action. That action runs cmuxTests throughrun-in-console-session.sh, and cmuxUITests in the console user's GUI bootstrap.teststays only as a fallback. As a result, an E2E build on an owned mini ran tests in the shared console session while app-host-unit-tests, tests-build-and-lag or a rerun held the gui token. The oldtestjob had the same gap.What
("test-e2e.yml", "build")gets a new class, compile-gui: 2 units, tokensgui+root.persistent-dd. The job never runsowned_build_state.py, never setsCMUX_OWNED_STATE_ROOTorCMUX_SEED_LOCAL_CACHE, and adopts seeds from R2. The only DerivedData and CAS it wipes are<root>/derived-data-compile-admissionand<root>/compile-admission-casat the root it holds. The kept admission state lives in the store and is cloned in, so it is not touched.${CMUX_CI_CANONICAL_ROOT:-/private/tmp/cmux-ci}, so it is no longer pinned to root 1. Its own restore step re-takes that root throughglaeda-canonical-root take, which is a no-op.compile-app-host-test-product.shaddsroot=to the fingerprint for any other root, and the E2E build has no per-root state that would favor another root.gui, thenroot. When the gui token is taken, the refusal is the oneGUI_WAIT_S(60 s) waits for.("test-e2e.yml", "test")becomes gui, like app-host-test-rerun'srerun. With more than one root it is a consumer: its restore step takes the producer's root, whichrestore-app-host-test-product.shreads from the receipt.docs/CMUX_MINI_RUNNER.mdno longer say that barebuild/testname test-e2e's jobs. The stale doc line listing app-host-test-rerun as an unknown job is fixed.Trade-off
compile-gui takes the gui token at job start and holds it through the compile. The hook cannot see the later step where the tests start. So while an E2E build runs on a mini, other GUI jobs there wait 60 s and are then refused, and the owned-pool rescue moves them (REFUSAL_SECONDS 120 covers test-e2e too). Releasing the token during the compile would need a cmux change, such as a take-gui helper run before the test step, and this PR does not change cmux.
Tests
test_capacity_e2e_jobs_hold_the_gui_token(two roots). It checks:gui+root-1withoutpersistent-ddand exports the root.test_job_class_keys_on_workflow_file_and_job_idis extended, including a check that every class named in the tables has a cost.scripts/test-glaeda-cmux-runner.py: 152 OK.scripts/test-glaeda-fleet-status.py: 65 OK.Review
An independent read-only review of the first head found no correctness problems (persistent-dd, root paths, deadlock and stranding all checked). It found one performance problem: a build on runner instance 1 would compile cold at root 2. That is fixed in 22a2241, which also clarifies the docs for the one-root case. Full suite on 22a2241: 152 OK.
Rollout
After merge:
scripts/glaeda-cmux-runner-fleet --apply --hosts <canary>, then the rest. job-started runs the hook fresh for each job, so no restart is needed.🤖 Generated with Claude Code