cmux NEXT Cloud: picking another machine for a Cloud chat whose first send failed moves it there and sends again

Merged
TE
5d ago
+430-28

Leo, 2026-10-31 (DEV build, Cloud chats): he "picked zesty-indigo-lemur in the machine picker, but the chat stayed on sr2-tunnel-probe (the location menu's top row and the tab title still show sr2-tunnel-probe) and failed again with: 'Claude is not signed in here. ...'". Fix: when a chat has not started, or its start failed, choosing another machine must move the chat to that machine and retry the send there. It must not stay on the old machine.

Cause

A Cloud chat whose first send failed still counts as a new chat, so the composer's environment picker stays open for it. A pick in that picker never reached the Cloud start:

  • An acpmux peer row started a new empty chat through the old machine's acpmux (chat.new {peer}).
  • An account Cloud machine row (zesty is not an sr2 peer) opened or resumed that machine and then waited for it to show up as a peer, which never happens.

Nothing moved the chat or sent its prompt again. The send also stayed pinned to sr2 in the start ledger (pick.rs destination()), because no pick ever re-pinned it.

What changes

  • Webview (cloudRepick.ts, ComposerContext.tsx, Composer.tsx, App.tsx): in a chat on a Cloud machine whose first send has not run, picking another Cloud machine calls cloud.repickChat {machine | name} first. If the App takes the pick, that is the whole move. If the App refuses before running anything (not a chat a Cloud start opened, an older App, no gesture), the pick does what it did before.
  • App (CloudStartChat+Repick.swift, CloudStartChat.swift, CloudStartChat+Machine.swift):
    • Every chat a Cloud start opens is kept with its start: the send key, the prompt, the folder on this Mac, the workspace and the account.
    • On a repick, the App re-pins the send to the picked machine with cmux link start-chat pin. That also makes it the Mac workspace's machine.
    • It then runs the same start as the first try: the folder is copied to a new place on the picked machine, the chat opens there seeded with the same prompt, and the old tab on sr2 closes.
    • run/open now take an Origin (the tab that sent, the Mac folder, the workspace, the account, and how to close the tab), so a first send and a repick share one path.
    • The harness-login result that run and runHome used to drop (_ = await passHarnessLogin) is now logged.
  • Agent pane (AgentPaneCloudStart.swift AgentPaneCloudRepick, request, decoding, model): adds cloud.repickChat, with checked params. It runs only for a chat on another machine and only on the pick's gesture.
  • Rust: no logic change. start-chat pin already replaces a send's pin, and the plan then names the picked machine. A regression test covers it.

Tests

  • webviews/test/cloud-chat-repick.test.ts (red before: the module and routing did not exist): a pick of another Cloud machine resolves to a move. An account row moves by its id, an acpmux peer by its name. This Mac, an SSH machine, the current machine and action rows are not moves. The App's answer decides whether the old pick behaviour runs.
  • CmuxNextAgentPaneTests/AgentPaneCloudStartTests.aRepickNamesTheMachineAndReachesTheAppOnlyForAChatElsewhereWithAGesture and CmuxNextAppTests/CloudStartChatMachineTests.aRepickFindsThePickedMachineByIdOrByName.
  • cmux-tui link/start_chat_cli/tests.rs a_repick_moves_a_sends_retry_to_the_picked_machine: after the pin, the retry plans the picked machine, copies afresh there, and the workspace takes it.
  • Fleet: cmux-ci compile-check --package CmuxNext, plus the webview tests, tsc and cargo test -p cmux-tui start_chat on the Linux pool.

Not covered: after an App relaunch, the record of which tab came from which start is gone. A pick in an old failed chat then does what it did before.

The CodeRouter fallback removal from the same report is in its own PR.

The machine auto-pick filter in pick.rs (never fleet or team machines) is another helper's work and is not touched here.

Risk: medium. This changes the Cloud start's Swift path (run and open now take an Origin); a first send behaves the same as before.

Agent session: agent session

šŸ¤– Generated with Claude Code


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


Summary by cubic

Fixes the Cloud chat machine picker so a chat whose first send failed moves to the machine you pick and sends its first prompt again there. Previously the pick left the chat on the old machine: a chat started through that machine's acpmux got a new empty chat, and an account Cloud machine row just opened the picked machine, so the send stayed pinned to the old machine and failed again.

Changes

  • The composer sends cloud.repickChat when another Cloud machine is picked for a chat whose first send has not run; if the App takes the pick, that is the move, otherwise the pick behaves as before.
  • The App keeps every chat a Cloud start opened, re-pins the send to the picked machine, copies the chat's folder there, opens the chat with the same prompt, and closes the old tab. First sends and repicks share one path.
  • Rust has no logic change; a regression test covers the re-pin and retry.

Not covered: after an App relaunch the record of which tab a start opened is gone, so a pick in an old failed chat behaves as before.