Cloud: a Debug build on staging uses the app-server link, so start-session gets an agent chat

Closed
TE
6d ago
+111-5

HOLD: demo runs on the legacy link; app-server default waits for the Worker owner answer, deliver-secrets and credential recovery (see your FIX review).

Tracked in manaflow-ai/cmux-platform#89 (the Worker owner answer, Worker secret ops and the app wiring), being built now in parallel; legacy plus #27866 stays the demo's fallback. This PR comes off HOLD and runs end to end once those land.

HOLD: not landable as is. The independent review (codex gpt-6-astra, FIX at 428b86a13e3f771187a6732ad1277fe7a3a7090d) found a blocker, confirmed by reading the code: on the app-server link CloudMachineSession.link is nil, so the owner-link closure in AgentTabs+Wiring.swift:278-279 always answers nil, and AgentSessionAttachClient.ownerAnswer (AgentSessionAttachClient.swift:211-214) refuses every agent start and folder-trust call with agent_session.host_unverified. cmux cloud start-session would trade one refusal for another. Also, cmux cloud deliver-secrets and apply-machine-config use the classic /api/vm/{id}/fs and exec routes, which a Worker machine does not have, so Claude would start signed out and credential recovery is off. Flipping the default needs app-server owner verification and a secrets route first.

Leo: "and i mean the thing is... cloud. and its various ergo things. those are being worked on? how close are we to 'cool demo by saturday'/good product?"

What

cmux cloud start-session --agent claude in a DEV build signed in to staging refused with "Machine demo-agent started but its agent didn't (this machine's cmux-tui cannot hold agent chat tabs yet: update it)". Every Debug build without a hand-set CMUX_NEXT_CLOUD_LINK=app resolved CloudConfiguration.linkSource to .legacy. That path creates machines through the classic web /api/vm, and its image manifest's default images are from epoch 2026-10-14-r1, whose daemon lacks agent-session-tabs-v1. The app-server link (.appServer: machine list, create and cloud.machine.connect on the cmux-next API Worker, staging https://cloud-api-staging.cmux.dev) creates machines from the cmux-next dev image channel, which CI promotion keeps current.

This PR changes CloudConfiguration.resolve only:

BuildBackendLink beforeLink after
Release (any LSEnvironment)productionlegacylegacy (unchanged)
Debug with CMUX_AUTH_ENVIRONMENT=productionproductionlegacylegacy (unchanged)
Debug, loopback / unset origin.localOnlylegacylegacy (unchanged)
Debug on staging (CMUX_API_BASE_URL=https://cmux-staging.vercel.app, what the HQ cloud capture fixture bakes).developmentlegacyappServer
Debug on a dev stack (CMUX_DEV_BACKEND_URL).developmentlegacyappServer

CMUX_NEXT_CLOUD_LINK (launch env) and CMUX_CLOUD_LINK (baked) still pick the link in any Debug build. They now work both ways: app or legacy (case-insensitive). CMUX_NEXT_CLOUD_LINK is read first, and an unknown value keeps the backend's default. Production ignores both, as before.

Risk

risk:normal. Only Debug builds on a staging or dev backend change. They lose what is still .legacy-only today: the Claude credential transfer and recovery onto a Cloud machine (cloudCredentialTransferIsAvailable), the file route, repository setup in the New Cloud Workspace dialog, and size and idle-pause options on create (machineOptionsUnavailable). A create that no person started (CLI, agent) asks first in the sidebar card (G8, CloudMachineCreateFlow). Anyone who needs the old path sets CMUX_NEXT_CLOUD_LINK=legacy.

The HQ cloud capture state cloud-resume-from-card makes its machine through classic /api/vm. At this head the app lists Worker machines, so that state needs the fixture to bake CMUX_CLOUD_LINK=legacy. That is an HQ change, which this lane cannot push (see below).

Tests

Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/CloudConfigurationLinkSourceTests.swift has one case per row above, plus the overrides both ways, the key precedence and unknown values. The red test is committed first (84bb4b0898e), then the fix (5bea4003122). Focused command: scripts/ci/package-test-lane.sh for CmuxNextAppTests, filter CloudConfigurationLinkSourceTests. On base, aDebugBuildOnStagingUsesTheAppServer, aDebugBuildOnADevelopmentStackUsesTheAppServer and anUnknownValueKeepsTheBackendDefault (first expectation) are expected to fail; at this head all are expected to pass.

Observed / Not run

Head 428b86a13e3f771187a6732ad1277fe7a3a7090d (merges feat-cmux-next ea12ea637ff, which includes #27758; the PR's own diff is the 2 files above).

  • Observed: cmux-next fleet Release compile passed at the head (run 40992406206, job 123859502631). ci-status passed.
  • Observed: scripts/cmux-next/prepush-gate.py: no new violations.
  • Reds at the head, each compared by name against base ea12ea637ff (the head changes no file under scripts/ or .github/):
    • cmux-next checks, Sidebar look owner: the sweep from 342462ca6ee names 3 base commits (4367c16e12c, 6c1a5ab8bf3, eb8105b0747). prepush-gate.py --only sidebar --commits 342462ca6ee..ea12ea637ff gives the same 3 findings.
    • cmux-next checks, script tests: swift-test-summary.test.sh fails on its own stub-swift fixture ("the crash error does not name the crashed thread's test frame"). It fails the same way when run here on the identical script. The "signal 5" annotation is that fixture's output, not a test process crash.
    • nightly-guards / Guard status and workflow-guard-tests / release-notary: workflow guards. The head changes no workflow.
  • Review: codex gpt-6-astra at high, read-only, on this head: FIX (the blocker above; a nit on test coverage of whitespace and same-key precedence).
  • Not run yet: CloudConfigurationLinkSourceTests (the cmux-next swift test job was queued when this was written). This lane VM has no fleet client for a focused cmux-ci run.
  • Not run: the staging end-to-end. cloud-demo runs it after the merge on a fresh staging machine with the new image (#27866), including whether cmux cloud deliver-secrets carries the Claude token to a Worker-created machine. Staging CLOUD_ALLOWED_TEAMS is team_f77575bdfd932388f2e3 (backend/apps/api/wrangler.jsonc, unchanged over the last 40 commits that touched it), with CLOUD_PLAN_ENTITLEMENTS=on. A Worker create works only for that team or a team with a paid, operator (cmuxVmPlan) or company plan. Any other team gets cloud.plan.required.

Changelog

none (Debug builds only)

Agent session: agent session

šŸ¤– Generated with Claude Code