Cloud: a Debug build on staging uses the app-server link, so start-session gets an agent chat
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:
| Build | Backend | Link before | Link after |
|---|---|---|---|
| Release (any LSEnvironment) | production | legacy | legacy (unchanged) |
Debug with CMUX_AUTH_ENVIRONMENT=production | production | legacy | legacy (unchanged) |
| Debug, loopback / unset origin | .localOnly | legacy | legacy (unchanged) |
Debug on staging (CMUX_API_BASE_URL=https://cmux-staging.vercel.app, what the HQ cloud capture fixture bakes) | .development | legacy | appServer |
Debug on a dev stack (CMUX_DEV_BACKEND_URL) | .development | legacy | appServer |
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 compilepassed at the head (run 40992406206, job 123859502631).ci-statuspassed. - 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 underscripts/or.github/):cmux-next checks, Sidebar look owner: the sweep from342462ca6eenames 3 base commits (4367c16e12c,6c1a5ab8bf3,eb8105b0747).prepush-gate.py --only sidebar --commits 342462ca6ee..ea12ea637ffgives the same 3 findings.cmux-next checks, script tests:swift-test-summary.test.shfails on its own stub-swiftfixture ("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 statusandworkflow-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(thecmux-next swift testjob was queued when this was written). This lane VM has no fleet client for a focusedcmux-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-secretscarries the Claude token to a Worker-created machine. StagingCLOUD_ALLOWED_TEAMSisteam_f77575bdfd932388f2e3(backend/apps/api/wrangler.jsonc, unchanged over the last 40 commits that touched it), withCLOUD_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 getscloud.plan.required.
Changelog
none (Debug builds only)
Agent session: agent session
š¤ Generated with Claude Code