cmux-next updates: pulled-build banner (Wait for the Fix / Roll Back) from pulled.json
Nightly train PR 4 (teamleaderleo/leo-workspace#624, section 2): the pulled-build banner. Follows #22590 (Roll Back to Previous Version), which merged as 010e772.
Problem. When nightly-next-rollback.yml pulls a bad build off the feed, Macs already on it keep running it with no signal, and the only way back is knowing about updates.rollback.
Behavior.
- The rollback workflow now also writes
pulled.jsonnext to the appcast (nightly-next/next/pulled.jsonin R2):[{build, reason, pulledAt, serve}], newest first, merged with the existing list, guarded bynightly-next-guard.py r2-keyand a write-once /--replace-sha256conditional upload. A build served again is dropped from the list. - The app (
PulledBuildWatch) reads that list at launch and after every Sparkle update cycle (no polling). The legacy NIGHTLY feed also reads/nightly-next/next/pulled.json. - If the running build is listed, the update notice card says This Nightly Was Pulled with the reason, and offers Wait for the Fix (default, prominent, also the ×; remembered per build) and Roll Back, which runs
UpdateDropBackand only shows when that rollback would run. A failed rollback shows its reason on the card.
Tests. PulledBuildWatchTests (list URLs, decode, wait-first/rollback actions, nothing-kept, not-listed, wait remembered across launches, rollback failure shown); tests/test_nightly_next_feed_rollback.py PulledListTests. Run on CI; not dogfooded in the app.
Localization. 5 new updater.pulled.* keys in Localizable.xcstrings with all 9 app locales.
Not in scope: the Updated toast / What's New work.
Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Adds a pulled-build banner to nightly updates so Macs running a build pulled from the feed get a signal instead of silently continuing.
Behavior
- The rollback workflow now writes
pulled.jsonnext to the appcast, listing pulled builds with reason, timestamp, and the build being served instead; a failed write only warns (and the rollback summary says so), so the fix-forward still runs. - The app reads that list at launch and after every Sparkle update cycle; a pulled build shows a This Nightly Was Pulled notice with the reason, offering Wait for the Fix (default, remembered per build) and Roll Back to the kept build, shown only when a rollback would run.
- The kept-build check runs once per pulled build, and a second Roll Back press while one runs is ignored.
- A failed rollback shows its reason on the card; a build served again is removed from the list, and a build leaving the list clears a stale rollback failure.
- The banner shows even when that update was snoozed.