Repository navigation
nightly-next: sign once as cmux NIGHTLY and publish to the one feed - #36287
Conversation
Each nightly-next build is signed once, as cmux NIGHTLY (com.cmuxterm.app.nightly, the NIGHTLY profile and keychain group), and published to files-next.cmux.com/nightly-next, the feed that app polls. The NEXT-identity build, the copy jobs and the second signing are removed. The feed keeps the Nightly build before the newest one so a launch crash has a build to roll back to. The retired nightly-next/next/ feed gets a signed announcement card that points a stray cmux NEXT install to cmux NIGHTLY. Fixes #33050 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-90f71867d2222e9eecd0
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
|
This comment has been minimized.
This comment has been minimized.
|
Review (codex gpt-6-astra high, router-review): BLOCK at 71259b4 BLOCK at 71259b4 Visual verdict: no images; Leo tries the build
|
The one feed names its DMGs under nightly-next/, so the appcast guard of the retagged feed takes NIGHTLY_NEXT_LEGACY_FEED_BASE; against the verdicts' nightly-next/next/ base it refused every promotion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-90f71867d2222e9eecd0
test_gh_budget_wait.py's publish path no longer lists the removed copy jobs, and test_publish_release_assets_callers.py reads the names script's versioned DMG and its --immutable/--alias call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-90f71867d2222e9eecd0
|
Review (Claude Opus 5.5 subagent, correctness first): SHIP at 07ce809 Scope: the full diff at this head, read through the API. Nothing was run locally. Changes since cb6708c. That review found two reds. The compare shows a single commit (07ce809) that touches only the two tests behind them:
Correctness of the whole change
Non-blocking
agent-identity footer: claude-sr2-coord review subagent |
|
Review (codex gpt-6.1-sol xhigh, router-review): BLOCK at 07ce809 BLOCK at 07ce809 Visual verdict: no images; Leo tries the build
|
The canary reads the one feed, but verdicts stay in nightly-next/next/verdicts/, where nightly-next-verdict.yml writes them. Deriving the path from the feed found no verdict on any build, so every scheduled tick re-ran the canary and dispatched a verdict again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-90f71867d2222e9eecd0
|
Review (Claude Opus 5.5 subagent, correctness first): SHIP at ed3c5dd Scope: compare 07ce809...ed3c5dd is one commit, ahead 1 / behind 0, two files ( Checks:
Notes (non-blocking):
agent-identity footer: claude-sr2-coord review subagent |
|
Review (codex, router-review): SHIP at ed3c5dd Reviewer: codex gpt-6-astra high, fast tier. SHIP at ed3c5dd Visual verdict: unchanged by this diff Targeted nightly identity, feed, ring, publication, canary, rollback, recovery, and train tests pass (235 tests); changed shell and Python files also pass syntax checks. Follow-ups: none |
- cef-embedder.sh (#36421): a comment line named both kill(0) and the name lookup, which the pkill lint counts as a name-lookup kill; reword it. - journal_store.rs (#35866): SEAL_WAIT and LOCK_WAIT get a ratchet entry that says what each bounds. - nightly.yml (#36287): drop the gh-app-install and ring-token anchors no alias uses any more (actionlint). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-c21fa64be7cee139675a
* ci(guards): clear the guard steps red at the feat-cmux-next tip A push that touches what the guards read (#36321 added a workflow) judges the guard group at its tip with no base comparison, so every step already red there failed it. Each red, its cause and the fix: - no-shallow-fetch: base_red_excuse.py and agent-loop.sh fetch only into a repository they just made (`git init`); listed in OWN_REPOSITORY with that. - short-timeout ratchet: acpmux's ls relay deadline, harness read and rename pacing, and the cloud client's slack over its own timeout are bounds that refuse no send; listed with their reasons. server/mod.rs counts 2 now. - fleet lints, pkill: cloud-journey.ts recorded no stub pid and stopped the stub by name; it now records the pid at launch and signals only that pid while it still runs the stub. side-instance.sh already stopped the pid recorded at launch; the launch lookup and the stop now use their own names, so the lint no longer reads the stop as a name lookup. vm.sh quits the same two first-login apps inside its throwaway guest in two commands; one definition now. - fleet lints, personal host: TabMachineBadgeColorTests (#36295) named a personal machine as its sample three times; it uses my-vm, as its allowlist entry asked. - actionlint: nightly.yml kept the &pub-runner anchor after #35918 dropped its last alias. - cu-driver-eval.yml (#36252): its eval job gets its runners.json class (the build-fleet gateway runner, as screen-stream-eval), and its report artifact keeps 3 days, the guard's limit; the next daily run reads it. - visual gate wiring: the verdict job's timeout became `vars.CI_NIGHTLY_MINI != '' && 220 || 100` in 132f7ce; the test pins both. - registry blast radius: the test inherited the runner's GITHUB_EVENT_NAME=push, which adds --warn-only; it now sets the event and covers the push case. The signing-keychain guard's nightly reds go in their own PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-c21fa64be7cee139675a * ci: clear three guard reds the base gained since this branch - cef-embedder.sh (#36421): a comment line named both kill(0) and the name lookup, which the pkill lint counts as a name-lookup kill; reword it. - journal_store.rs (#35866): SEAL_WAIT and LOCK_WAIT get a ratchet entry that says what each bounds. - nightly.yml (#36287): drop the gh-app-install and ring-token anchors no alias uses any more (actionlint). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-c21fa64be7cee139675a --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…/next/ (#36686) * scripts: read the one nightly-next feed, not the retired nightly-next/next/ #36287 put every nightly-next build on nightly-next/appcast-arm64.xml, signed as cmux NIGHTLY, and retired nightly-next/next/. The signed-host check, the daemon swap check, the premain launch bench, three measure scripts and the perf-bench fetcher still read the retired feed, so they checked or measured a build that no longer moves. cua_signed_host_check.py now checks the one feed's newest build as com.cmuxterm.app.nightly and nothing else; daemon-swap-check and premain-launch say the feed is unreadable instead of reading an empty answer as "no builds". tests/test_nightly_next_one_feed_readers.py keeps scripts/ off the retired feed (red on the base: 2 failures; green here). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-8707001042ea634e8771 * scripts: the signed-host check's move goes to its own PR; the feed test is a manual lane Pointed at the one feed, cua_signed_host_check.py checks the live cmux NIGHTLY (4204712406201) and fails: "signed app pid 1326 never kept an accepted host connection" on the legacy helper's socket (run 42054552983). That is a finding about the shipped build, so the check's move goes to its own PR with it, and this one keeps the readers that pass. tests/test_nightly_next_one_feed_readers.py runs by hand (lane manual): the linux-guard lane runs in no pull-request workflow, and no new CI tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-8707001042ea634e8771 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… read beside this app's feed Review of f46c8ae (Codex gpt-6.1-sol): the Install op checked only the one-second gesture window, so a page could ask for a second build on the same click; it now spends the click (ImportHistoryPageProvider's spentGesture). And since #36287 the one feed publishes DMGs to /nightly-next/ while verdicts and pulls stay in /nightly-next/next/, so the fallback for a build off the feed read the wrong folder; it now reads the folder of this app's own feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef
…parkle update (#36375) * updater: Install this version checks the DMG's EdDSA signature, like every Sparkle update A Switch to a build this Mac did not keep (Settings > Channels, Install an Earlier Nightly…, `cmux nightly install`, and next What's New) downloaded the DMG and trusted it on the code signature alone. Sparkle checks every update's EdDSA signature against SUPublicEDKey first; this path now does the same, before the image is even mounted: - AppcastItem carries the enclosure's sparkle:edSignature and length. - The nightly publisher writes <dmg>.signature.json (the feed's signature and length) and uploads it beside the DMG, so a build the two-item feed has dropped still has its signature. It needs no trust of its own: the key is the app's. - BuildSwitchDownload refuses a download whose bytes or length do not match, and switchToBuild refuses a non-kept build with no signature anywhere (built before this file existed), saying why. A kept build swaps as before. Checked against the live feed: build 4198271655001's edSignature verifies as plain Ed25519 over the DMG bytes with the cmux NEXT key; one changed byte fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New: each day's Versions lists its builds, each with its commit, its changes and Install Leo, 2026-11-05: "Like literally with a click to a past nightly. Like that would be huge. Like just having that list of like, okay, you can just go and through the What's New page, you can just get back to a prior version if you wanted to." The Sheet's day header gets "Versions" (with the day's build count) at its end, at the header's own line height so nothing moves. Open, it lists the day's builds: version, its commit (linked), "Changes" (the compare from the build before), and Install (Installed for the running build). Install asks once ("Restart into it"), then cmux.changelog.install runs UpdaterService.switchToBuild: the download's EdDSA signature and team checked (previous commit), the stores check, the running build kept, a restart with every session. The op (ChangelogInstallProvider, CmuxNextUpdater, routed ahead of the notes) takes only the person's own click on What's New; a refusal (pulled, unsigned, a store the build cannot read) shows under its build. `installable` says whether to list Install: none on a DEV build, which follows no feed. Tests: ChangelogInstallTests (the op's gesture and page rules, refusal), whats-new-install.test.tsx (links, ask-once, refusal under its build). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * updater: on a build the person picked, a newer build waits for their click Coordinator's call on the What's New rollback ask (Leo, 2026-11-05: "just get back to a prior version if you wanted to"): a rolled-back user is offered the newest again, but it never installs straight back without asking. A rollback or Install this version (both run UpdaterService.rollback) remembers the build it went to (cmux.next.updates.pickedBuild). While that build runs, UpdateController.asksBeforeInstalling makes a found build wait in the update card for the person's click even with background install on; any other running build (they installed forward) clears it, and updates install as set again. The skip of the build left behind is unchanged. Tests: BackgroundInstallTests.skippedBuildsAreNotOfferedAgain (a newer build waits, then installs on accept; without the hold it installs as before), RollbackPickedBuildTests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New Install: type the install closure (the compile check's diagnostic failure); gallery states sheet-versions and sheet-versions-confirm The ternary over an async closure gave Swift no diagnostic at all (fleet compile check d73123de0e19c875c5aee570); an explicit closure type compiles. The gallery's mock host lists Install and answers installable, and two Sheet states show a day's Versions open and Install's one question, over the real nightly-next index fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * release notes: What's New installs an earlier version with a click Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * settings: updates.whatsNewInstall (off by default) gates What's New Install The Install button in What's New ships off until it is proven on a real nightly; the EdDSA check and the re-offer hold stay on for every path. Agents can read the setting but only the person turns it on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New gallery: open the second day's Versions (the first holds only the installed build) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * BuildSwitchDownloadTests: the image the test download writes is nonisolated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New gallery: Versions is a disclosure, so the rows below it move (layout-shift allowance with its reason) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New gallery: format Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New Install: one click installs once; a dropped build's DMG is read beside this app's feed Review of f46c8ae (Codex gpt-6.1-sol): the Install op checked only the one-second gesture window, so a page could ask for a second build on the same click; it now spends the click (ImportHistoryPageProvider's spentGesture). And since #36287 the one feed publishes DMGs to /nightly-next/ while verdicts and pulls stay in /nightly-next/next/, so the fallback for a build off the feed read the wrong folder; it now reads the folder of this app's own feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * What's New Install: import Foundation for the spent click's TimeInterval Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * action exports: regenerated for updates.whatsNewInstall scripts/cmux-next/regenerate-action-exports.sh on the fleet at 92d571a. The new toggle adds two settings actions (147 to 149). The workspace count (189 to 193) is base drift the gated export checks never caught on base; regenerating fixes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * ArchiveSignature: a signature that names no length is refused Review follow-up at fb77cb7: a missing or malformed enclosure length skipped the size check. Every signature the publisher writes carries one, so none is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef * settings exports: regenerated for updates.whatsNewInstall scripts/cmux-next/regenerate-settings-exports.sh on the fleet at fb77cb7 (the .patch form for a PR that changes CmuxNextSettings sources), applied without the bot-only defaults snapshot. Review of fb77cb7: the schema lacked the new row, so the CLI and MCP could not see or set it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-f0070936d9cbebdedaef --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Leo, 2026-11-05: "JUST POINT ME TO THE ONE APP/THE ONE FEED", "THERE SHOULDN'T BE 2"; and "if we're only doing 1, just do the best app version to stick with". The app the team runs is cmux NIGHTLY (
com.cmuxterm.app.nightly), with evidence in cmuxterm-hq#7156. So nightly-next now signs each build once, as cmux NIGHTLY, and publishes it to the feed that app polls. The cmux NEXT-identity build, its feed writes, and the second signing are gone. Carries out #33050.One signing
decide's nightly-next identity becomes cmux NIGHTLY:com.cmuxterm.app.nightly,cmux-nightlyandcmux.nightly.entitlements. That means the NIGHTLY Developer ID profile, keychain group7WLXT3NR37.com.cmuxterm.app.nightlyand passkeys.sign_channelisnightlyandprofile_channelisNIGHTLY. This is the same path the NIGHTLY-identity notary probe already takes. The embed step refuses a profile for any other App ID.LSEnvironment:CMUX_AUTH_CALLBACK_SCHEME) is dropped. cmux NIGHTLY takes its own default.legacy-identity-target,sign-,verify-,publish-nightly-identity-legacy,nightly-identity-legacy-published) and the 17 in-job copy steps. The sign job's budget goes from 320 back to 160 minutes.One feed
NIGHTLY_NEXT_FEED_BASEishttps://files-next.cmux.com/nightly-next(R2 prefixnightly-next/).publish-nightly's compare-and-swap write, guard, ring tags, read-back and notes now all go to that feed.feed=nightly-next-legacy, the id the app'sReleasePushListenergives that feed.publish-nightly-next-github-names, only while the build is the feed's newest.pulled.jsonstay innightly-next/next/, where the app reads them (UpdaterService.channelFolder).nightly_next_ring.py legacy, in both ring modes), so a launch crash has a build to roll back to. Today's live feed holds a single item, which leaves the launch smoke'sdecide()with nothing to serve. With the ring off, an unreadable live feed now warns and publishes the new build instead of stopping.Handover for a stray cmux NEXT install
release-notes/next/retired-feed-announcements.json, signed with the content key and written asnightly-next/next/notes/announcements.json. That is the one feed-side message the app shows. It is text only and points to cmux.com/next.Not touched
files.cmux.com/nightlyand every classic-frozen path.The scheduled watchers that run from main move in #36228, which lands first.
Follow-ups
Tests
4a2ed94e(ci(nightly): signing jobs queue only on the signer's label (held) #35918). Its signer-only routes are unchanged on every remaining job, and the signing-route testtest_no_signing_job_ever_resolves_to_a_shared_labelpasses.test_nightly_next_arm64_only.py,test_ci_upload_artifact_retention.py,test_short_timeout_guard.pyandtest_ci_signing_keychain_is_deleted_before_bundle_code.py. These four fail the same way on base4a2ed94e.test_nightly_next_identity.py,test_nightly_next_post_publish.pyandtest_nightly_next_rolling_dmg_identity.py.test_a_crash_rolls_back_on_the_one_feeds_shape(launch smokedecide()on a two-item cmux-next feed) and the ring tests for keeping the build before the newest.Agent session: agent session
🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Signs every nightly-next build once, as cmux NIGHTLY (
com.cmuxterm.app.nightly), and publishes it to the one feed the app polls, instead of building a cmux NEXT identity and re-signing a second copy for a separate feed. Implements #33050.One signing, one feed
nightly-next/, where its DMGs sit) instead of the verdicts' folder, so it no longer refuses every promotion.nightly-next/next/), where the app reads them; deriving that path from the feed matched nothing, so every scheduled tick re-ran the canary and dispatched another verdict.files.cmux.com/nightly) is untouched.Written for commit ed3c5dd. Summary will update on new commits.