cmux NEXT daemon update: Cloud machines follow the app's build

Merged
TE
10d ago
+513-6

Part 10 of the rebuild of #23199 and #24070, the last (adopted by hb-acpmux). On feat-cmux-next. #25393, #25394 and #25395 below it landed (c743a343, 30f84dd5, 3660113f), and this branch is rebased onto them: head 4381e75d, the same patch-ids as the reviewed e9e2fa2d (+513 āˆ’6).

Cloud machines now follow the app's build by themselves, also while an agent works there (no wait for idle), and the header shows the update.

Leo: "BRO CANT YOU JUST LIKE SUSPEND SHIT AFTER A TOOL CALL/TURN THEN JUST GO AND AUTOUPDATE OUR TEAMMATES NEED ACPMUX UPDATING TOO..."

Leo: "OUR SHIT IS HARDLY EVER GOING TO GO IDLE. IT JUST WONT. YOU'LL JUST HAVE TO ACCEPT IT BREAKING AT SOME POINT BUT IT RESUMES WHATEVER IT INTERRUPTED. END OF STORY FOR THE DAEMON UPDATE."

Leo-ask: ask-48bbaec1d4
Leo-ask: ask-061e3574a6

Change

From #24070 at bd4892c8:

  • CloudMachineUpdates.start (from AppServices.startCloud): every new handshake of a Cloud machine's daemon (first connect, reconnect, or one the app refused) offers the machine to the shared queue, behind cloud.updateMachines and agent.updatePeers. HandshakeFollower spends a handshake only on an offer that was made. One seen while the toggles are off, or before the app has a cmux client, is offered once they allow it; the follower observes the toggles too. A machine with no handshake forgets its last, so a second refusal after a drop is new.
  • CloudMachineSession.reconnectAfterUpdate(restarted:): after an update that may have restarted the daemon, the machine connects afresh. A legacy link's waiting connect is woken. An app link drops the current connect's ticket and connects again, after any connect already in flight. This holds also while the app still holds the old daemon's connection, whose drop can arrive after the CLI answered: that late drop is then an old ticket's and changes nothing. An update that restarted nothing leaves a healthy connection alone.
  • CloudLinkSession.currentSocket: the app link's socket without handing it to a connection, so a refused daemon on an app-server link can be updated (old review fix a187752d).
  • CloudCreationRows.withUpdate: the header shows the update's line while it runs or retries; a failure stays until the machine runs the build it was to (old review fix e6ff99d0).
  • The release-notes highlight now names Cloud machines too.

Timers and their stop conditions

  • The handshake follower (one ObservationStream task): app lifetime; it does no work between handshakes.
  • Each offer (one task): ends once the queue took the machine.

Tests

  • aRefusedDaemonOnAnAppLinkIsUpdatedAndConnectedAgain (CloudMachineUpdateAppLinkTests: a real CloudMachineSession on an app link, a stand-in daemon that answers an old protocol, a stand-in cmux): the refused daemon is updated through the link's socket with --to-bundled, and the machine connects again with a fresh link.

  • theHeaderShowsTheUpdateState: the line while it runs or retries; a failure stays while the machine runs another build, also one serving every capability, and goes once it runs the update's build.

  • CloudMachineUpdateFollowTests: a refusal while off is offered once the toggles turn on; an offer not made stays due; a handshake after a drop is new; an offer is made again after the toggles go off and on; and the reconnect decision after an update, including the CLI answering before the old connection's drop.

Review (Codex gpt-6-astra xhigh): the first round blocked on 2 P2s (a missed app-link reconnect when the CLI finishes before the old drop; refusals deduplicated for good). Both are fixed in 1c7615c1. The second round found that turning the toggles off and on again could leave an offered machine unoffered; fixed in e9e2fa2d (turning them off forgets every offer). SHIP at e9e2fa2d.

Red on base: these name CloudMachineUpdates and withUpdate, which base doesn't have.

Observed / Not run

No capture: the header line appears only during a real Cloud update; its text is DaemonUpdateBoard.line (#25393).

Agent session: big-red
šŸ¤– Generated with Claude Code