Skip to content

cloud.startChat: a new machine whose older cmux-tui refuses the copy is updated and copied again (re-land in CmuxNextCloud) - #30610

Merged
teamleaderleo merged 1 commit into
feat-cmux-nextfrom
cloud-start-retry-pkg
Nov 1, 2026
Merged

teamleaderleo merged 1 commit into
feat-cmux-nextfrom
cloud-start-retry-pkg

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Re-lands #30258 (reverted by #30438: "CmuxNextApp +21 lines turned the feat tip arch ratchet red; re-land with the code moved to a package").

Leo's ask this serves: "whenever i start a chat it should be able to send me to cloud automatically keep launching/downloading new dev builds and such"

What it does

A new Cloud machine's image may carry an older cmux-tui than the app, and the automatic update its first handshake queued may not have run yet. cloud.startChat's folder copy (cmux link start-chat copy) refused with machine_outdated about the machine (not side: "source") now updates the machine to the app's build (CloudMachineUpdates.updateNow, --to-bundled --if-older) and copies once more, instead of failing the first send with "Update the machine". Any other answer, or no update turn (toggles off, no client, machine gone), keeps the first answer.

Where the code lives (net CmuxNextApp lines: -16)

  • CmuxNextCloud/Move/CloudOutdatedMachine.swift (new): code (machine_outdated), the predicate updatesBeforeCopyingAgain(ok:errorCode:side:), and the generic copy, update, copy-again sequence over closures.
  • CmuxNextCloud/Move/CloudWait.swift (new): CloudMove.holds(within:until:) moved verbatim as CloudWait.holds, to offset the call site (a move with no behavior change; its 6 callers renamed).
  • CloudStartChat.swift: the copy goes through CloudOutdatedMachine.copy. CloudMoveStrings.outdatedCode now reads CloudOutdatedMachine.code.
  • Test: CloudStartChatMachineTests.onlyTheMachinesOutdatedRefusalUpdatesItBeforeCopyingAgain covers every answer kind and whether the update ran or not.

Arch ratchet (scripts/cmux-next/arch-ratchet.py --audit): app_lines 159583 at base 00085a3, 159567 at head. arch-ratchet.py --base ... --head HEAD and guardrails.py say "no architecture count rises"; check-concurrency.sh passes.

Review follow-up from #30258: wait for the daemon to reconnect

#30258 read the socket again from the app's own connection after the update, which may be nil or stale right then. This version copies again on the same socket and does not need the app's own connection to be back first:

  • cmux link machine update answers updated only after the new daemon answered over that socket: wait_for_outcome reads the outcome and identify through it (cmux-tui link/machine_update.rs:334-366), so when updateNow returns (it awaits the policy turn, CloudMachineUpdates.swift:94-98), the socket reaches the new daemon. The socket is the local cmux-tui remote connect link's, which redials, and each CLI dials its own connection over it (CloudMachineLink.swift:4-13).
  • Update Machine already reuses the pre-update socket the same way (CloudMove.swift:151-156).
  • updateNow does not wait for the app's own reconnect: the target calls source.reconnect (CloudMachineUpdateTarget.swift:111), and reconnectAfterUpdate (CloudMachineSession.swift:451) starts the connect without awaiting it. The open after the copy waits for that connection's tree for up to 10 s (CloudHandlers.existingWorkspace, CloudHandlers.swift:246-252).

Risk: low. The change is limited to the start-chat copy's machine_outdated path, and the moved wait helper is unchanged.

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

Re-lands cloud.startChat's handling of a new Cloud machine whose older cmux-tui refuses the folder copy with machine_outdated: the machine is now updated to the app's own build and the copy runs again, instead of failing the first send with "Update the machine". Any other answer, or an update that didn't run, keeps the first answer.

The logic moved into the CmuxNextCloud package — CloudOutdatedMachine for the refusal handling, and CloudMove.holds moved verbatim to CloudWait.holds — so CmuxNextApp's line count drops.

Written for commit 691c7a3. Summary will update on new commits.

View guided diff Turn on auto-fix

…is updated to the app's build and copied again (re-land in CmuxNextCloud)

Re-lands the behavior of #30258 (reverted by #30438 for raising CmuxNextApp's
line count): the decision and the copy-update-copy sequence live in
CmuxNextCloud (CloudOutdatedMachine), and CloudMove.holds moves there as
CloudWait.holds (no behavior change), so CmuxNextApp shrinks.

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

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 691c7a3: 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=pr30610 -f ref=cloud-start-retry-pkg -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on 691c7a30f9; ci-status is green without them.

CI passes on 691c7a30f9 (run 41291615442 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

Shipped in:

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Packages/macOS/CmuxNext/Sources/CmuxNextCloud/Move/CloudWait.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextCloud/Move/CloudWait.swift:10">
P3: This adds a second bounded-observation implementation alongside `AgentPaneMachineLink.settles`; extract the common primitive so timeout and cancellation fixes cannot diverge.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift:119">
P1: `updateNow` returns `true` for any completed update turn, not only a successful machine update, so this retries the copy after failed, busy, unsupported, or no-op updates. Return and propagate the update outcome, and retry only when the machine actually reached a usable build.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

copy = try await CloudOutdatedMachine.copy({
try await cli.startChatCopy(key: start.key, account: Self.accountText(account), machine: machine.machineID, socket: socket,
local: folder.root, subpath: folder.subpath, workRoots: services.cloud.move.workRoots)
}, answer: { ($0.0.ok, $0.0.errorCode, $0.0.side) }) { await CloudMachineUpdates.updateNow(machine.machineID, services: services) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: updateNow returns true for any completed update turn, not only a successful machine update, so this retries the copy after failed, busy, unsupported, or no-op updates. Return and propagate the update outcome, and retry only when the machine actually reached a usable build.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift, line 119:

<comment>`updateNow` returns `true` for any completed update turn, not only a successful machine update, so this retries the copy after failed, busy, unsupported, or no-op updates. Return and propagate the update outcome, and retry only when the machine actually reached a usable build.</comment>

<file context>
@@ -112,8 +112,11 @@ struct CloudStartChat {
+            copy = try await CloudOutdatedMachine.copy({
+                try await cli.startChatCopy(key: start.key, account: Self.accountText(account), machine: machine.machineID, socket: socket,
+                                            local: folder.root, subpath: folder.subpath, workRoots: services.cloud.move.workRoots)
+            }, answer: { ($0.0.ok, $0.0.errorCode, $0.0.side) }) { await CloudMachineUpdates.updateNow(machine.machineID, services: services) }
         } catch {
             // Timed out or didn't launch: Retry copies again on this machine, into a new folder.
</file context>

public static func holds(within limit: Duration, until condition: @escaping @MainActor @Sendable () -> Bool) async -> Bool {
if condition() { return true }
// concurrency-allow: the observation task exits on cancellation; this bounds a machine link's start
return await withTaskGroup(of: Bool.self) { group -> Bool in

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This adds a second bounded-observation implementation alongside AgentPaneMachineLink.settles; extract the common primitive so timeout and cancellation fixes cannot diverge.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextCloud/Move/CloudWait.swift, line 10:

<comment>This adds a second bounded-observation implementation alongside `AgentPaneMachineLink.settles`; extract the common primitive so timeout and cancellation fixes cannot diverge.</comment>

<file context>
@@ -0,0 +1,26 @@
+    public static func holds(within limit: Duration, until condition: @escaping @MainActor @Sendable () -> Bool) async -> Bool {
+        if condition() { return true }
+        // concurrency-allow: the observation task exits on cancellation; this bounds a machine link's start
+        return await withTaskGroup(of: Bool.self) { group -> Bool in
+            group.addTask { await observe(condition) }
+            group.addTask {
</file context>

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at 691c7a3

No blocking findings. The CloudWait.holds move is lossless, a CloudMove.Failure thrown inside the copy keeps its own message, and the retry needs no wait for the app's reconnect: cmux link machine update answers only after the new daemon serves on the same socket.

Follow-ups (filed as one issue):

  1. updateNow returns true even when the update failed. Return the DaemonUpdateOutcome and copy again only on updated or current.
  2. The account is not re-checked between the update and the second copy.
  3. On a legacy link, remoteEndpoint may change after a restart.
  4. No test covers the answer-tuple closure in CloudStartChat.

@teamleaderleo
teamleaderleo merged commit 7af5f2f into feat-cmux-next Nov 1, 2026
76 of 121 checks passed
@teamleaderleo
teamleaderleo deleted the cloud-start-retry-pkg branch November 1, 2026 09:19
teamleaderleo added a commit that referenced this pull request Nov 1, 2026

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 691c7a30 Deployed Nov 1, 2026 by teamleaderleo via namespace autofix #11067
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant