Skip to content

ci(nightly-next): the fallback preparation carries the preparation result, one queued hop fewer - #29353

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
ci/nightly-fold-prepare-aggregator
Oct 31, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
ci/nightly-fold-prepare-aggregator

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-10-31: "DON'T WE HAVE THE DEDICATED NIGHTLY BUILDERS...". Rulebook: the NEXT nightly runs end to end on our own fleet, and Blacksmith is a capped, paid overflow.

Why the nightly's Linux jobs queue

NEXT nightly run 41187578702 attempt 2 took 101.7 min, 47.2 min of it queued. On the Linux side, every control job between the app build and publication ran on blacksmith-32vcpu-ubuntu-2404 and waited 2 to 6 min for it, even when the job itself ran for 0 s. Three causes, read from nightly.yml on feat-cmux-next and the repository variables:

  1. There is no fleet Linux runner for Actions. The GCP pool serves agent lanes and cmux-ci, not GitHub Actions jobs: no Actions runner outside the minis is registered, and LINUX_RUNNER is blacksmith-32vcpu-ubuntu-2404. Every Linux nightly job uses the runner-set expression from ci(nightly): Linux jobs take the runner set, not one small Blacksmith label #27125 (vars.LINUX_RUNNER == 16 vCPU && 16 vCPU || 32 vCPU), so all of them resolve to Blacksmith 32 vCPU, the label with the longest queue (409 queued, oldest 45 min at 12:08Z).
  2. The trusted set for nightly Linux jobs is {Blacksmith 16, Blacksmith 32} (tests/test_nightly_trusted_routing.py, ci(nightly): remote-daemon build and attest jobs take the Linux runner set (base red) #28209). A free owned mini is not in it, and ci: portable jobs take fleet Linux, then a free mini, then Blacksmith #29130 (portable placement: fleet Linux, then a free mini, then Blacksmith) explicitly leaves the nightly out.
  3. Serial hops. The Linux critical path is remote daemon, then prepare primary, then the prepare fallback, then the prepare-nightly-variant aggregate, then signer-app, then signer-dmg, then publish-nightly. Each hop pays a fresh Blacksmith queue.

This PR fixes the part that needs no new runner or trust change: one of those hops. Routing these jobs to owned capacity is a follow-up (see "Not in this PR").

What changes

prepare-nightly-variant was a job with one echo step. It succeeded when the primary or the fallback preparation did, so later jobs could keep a single need. In run 41187578702 attempt 2 it queued 4.8 min and ran 0 s, on the signing path.

The fallback job now carries that result itself. It is renamed from prepare-nightly-variant-fallback to prepare-nightly-variant, and the echo job is deleted. Nothing else about the job changes: its needs, its if (it never skips after a primary success or failure), its runner choice (the primary's label after a success, the other set size for a retry), and its steps. Its result is the same as the old aggregate's:

  • primary succeeded: the no-op leg succeeds, so the result is success;
  • primary failed: the result is the fallback preparation's result;
  • primary skipped or cancelled: the job is skipped, as the aggregate was.

None of the jobs that read needs.prepare-nightly-variant change: signer-app, pick-nightly-sign-runner, build-sign-notarize-nightly, legacy-identity-target and report-nightly-failure. PREPARE_FALLBACK in the primary's env compares github.job with the new id. That is still false for prepare-nightly-variant-primary, and the fallback still sets it to 'true' itself.

The tests that named the fallback job by id now use the new id. The aggregate's entries in .github/nightly-step-coverage.json and tests/test_ci_macos_xcode_selection.py are removed. The fallback's matrix legs are now named prepare-nightly-variant (<variant>). That is the name scripts/ci/nightly_sign_admit.py (the nightly-sign.yml shadow, off unless vars.NIGHTLY_SIGN is shadow) selects. After a primary success the no-op leg records no digest, so admission now takes the digest of the successful prepare-nightly-variant-primary (<variant>) leg, whose upload the no-op leg reused, and names that job as the producer. A leg with no record and no successful primary leg is still refused. New tests in tests/test_nightly_sign_admit.py cover both cases.

Performance

metric before after delta harness or run link
Linux hops on the nightly-next signing path (app build → publish) 7 6 −1 needs: graph of nightly.yml
queue on the removed hop (prepare-nightly-variant) 4.8 min 0 −4.8 min run 41187578702 attempt 2, REST jobs list (started_at − created_at)
Linux queue on the critical path (remote daemon 3.2, prepare primary 2.8, fallback 4.8, aggregate 4.8, signer-app 5.6, signer-dmg 2.2, publish-nightly 4.7) 28.1 min 0.4 min (26 s: remote daemon 1 s, prepare primary 1 s, prepare-nightly-variant 1 s, signer-app 2 s, signer-dmg 18 s, publish-nightly 3 s) −27.7 min before: run 41187578702 attempt 2; after: run 41217892491 (published build 4121789249101), REST jobs list (started_at − created_at)
whole-run queue 47.2 min 1.3 min (76 s over 45 jobs: Linux 64 s, Mac 12 s) −45.9 min before: NOW (HQ#5634) 12:00Z; after: run 41217892491
whole critical-path queue, Linux + Mac (app build → publish-nightly) not measured separately 0.6 min (33 s) run 41217892491
Blacksmith billed per nightly run for the removed job 1 job × 1 min minimum × 32 vCPU = 32 vCPU-min 0 −32 vCPU-min per run Blacksmith bills each job at least one minute
Blacksmith per day 32 vCPU-min × runs per day (at most 96 a day with NIGHTLY_NEXT_MIN_INTERVAL_HOURS 0.25) 0 up to −3,072 vCPU-min/day as above

The "after" queue numbers come from run 41217892491, the first published nightly-next run after this merged. Most of the drop comes from less contention on the Blacksmith 32 vCPU label at that hour and from the nine nightly slots (cmuxterm-hq#5641), not from this PR alone; this PR's own share is the removed hop.

Not in this PR (follow-ups)

  • Owned capacity for the nightly's light Linux jobs. The secret-free ones on the path (build-nightly-remote-daemon, await-nightly-tree-marker, the prepare no-op leg) could take an idle owned Mac, picked in the step before them, so no picker hop is added. That widens the trusted set in test_nightly_trusted_routing.py. The nightly app build already runs on owned side and nightly Macs, so this adds no new trust class. It builds on ci: portable jobs take fleet Linux, then a free mini, then Blacksmith #29130's placement once that lands.
  • Fleet Linux Actions runners. Registering GCP pool VMs as Actions runners for the nightly changes the runner trust list, so it is not in this PR.
  • The release-environment jobs (signer-app, signer-app-notary, signer-dmg, publish-nightly and the other publish jobs) keep their Blacksmith labels. The trust and keychain rules for them are unchanged.

Risk

Low. This is CI wiring only, with no change to environments, secrets, permissions, runner labels or any step. The one behaviour change: if the primary preparation succeeds but the fallback's no-op leg itself fails (for example at "Set up runner"), signing no longer runs. Before, the aggregate job would have run anyway, though it needed a runner of its own too. No timer, observer or loop is added.

Observed / Not run

  • Local reads: the YAML loads, no prepare-nightly-variant-fallback reference is left, and the needs graph is acyclic.
  • The nightly guard tests that read nightly.yml run in this PR's guard group. Their results are in the checks.
  • Not run: a nightly on this head. The first nightly-next run after merge is the measurement.

Changelog

none

🤖 Generated with Claude Code

Agent session: agent session

…sult, one hop fewer

The prepare-nightly-variant job only echoed whether the primary or the
fallback preparation succeeded. Every nightly paid a queued Linux hop for it
on the signing path (4.8 min queued for a 0 s job in run 41187578702
attempt 2). The fallback job, which never skips and succeeds exactly when the
primary or its own preparation did, now takes that id, so every reader of
needs.prepare-nightly-variant is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cabecb079fe356666905
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review cfaa354: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr29353 -f ref=ci/nightly-fold-prepare-aggregator -f platform=macos -f backend_mode=local

Shipped in:

@blacksmith-sh

blacksmith-sh Bot commented Oct 31, 2026

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)/
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, router-review): BLOCK at 3524b6a

BLOCK at 3524b6a

Visual verdict: no images; Leo tries the build

  1. [P1][blocking] The renamed fallback makes normal successful runs fail signing admission. When the primary succeeds, each prepare-nightly-variant matrix leg runs reuse, exits successfully, and skips the digest-recording/upload steps (nightly.yml:3762, nightly.yml:3784). These jobs are nevertheless named prepare-nightly-variant (<variant>), so nightly_sign_admit.py selects them and calls producer_digest, which rejects the missing annotation (recorded 0 prepared app digests). The stated “finds nothing to admit” behavior is not implemented; normal primary-success runs instead make the admission job fail.

@teamleaderleo teamleaderleo removed the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
Review at 3524b6a: after the rename the fallback's legs are named
prepare-nightly-variant (<variant>), which nightly_sign_admit.py selects.
After a successful primary the no-op leg records no digest, so admission
refused the run (recorded 0 prepared app digests). The no-op leg reused the
primary leg's upload, so the primary leg's record is the producer's: admit
reads it then, and names that job as the producer. With no successful
primary leg a leg without a record is still refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-cabecb079fe356666905
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at cfaa354

Reviewer: codex gpt-6.1-sol xhigh, fast tier.

SHIP at cfaa354

Visual verdict: no images; Leo tries the build

Focused workflow, admission, routing, and shell checks pass. No correctness findings.

Follow-ups: none

@teamleaderleo teamleaderleo removed the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@teamleaderleo
teamleaderleo merged commit 5d4dd8d into feat-cmux-next Oct 31, 2026
192 of 201 checks passed
@teamleaderleo
teamleaderleo deleted the ci/nightly-fold-prepare-aggregator branch October 31, 2026 13:04

This branch was previously deployed

1 inactive deployment
ci-routing-agent — cfaa354e Deployed Oct 31, 2026 by teamleaderleo via cmux-tui tree preflight #89825
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:high Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant