Cloud: a team machine's Claude sign-in card says it takes no personal login
The coordinator's ask, after cmux#31264's record showed skip (guest-not-owner): ... guest team ...: "on a guest-not-owner skip with no team Claude account, the card should name the actual way out ... instead of the bare "rejected" card and a sign-in that dead-ends in the transfer refusal." And: "land it now with the honest wording. It's true today. When staging or production get the CodeRouter edge, the card can add cr add claude."
What changes
On a team Cloud machine (/etc/cmux/vm-kind = team), cmux passes no personal Claude login by policy, and staging and production machines have no CodeRouter route, so Claude there is signed out. Until now every turn showed "The provider rejected Claude Code's credentials on this Cloud machine. Set them up again to continue." with Sign in to Claude, which ends in "Personal credentials can only be transferred to a Cloud machine you own."
Now a Claude sign-in failure (auth or subscription-login) on such a chat:
- reads: "Claude Code on this team Cloud machine has no sign-in it can use. cmux doesn't pass personal logins to shared team machines." (
turn.failure.cloudTeamMachineNoLogin, in all 21 locales of the pane's catalog) - leads with Switch model and keeps Use an API key
- offers neither Sign in to Claude nor the personal login transfer (the footer's Set up credentials and #31187's automatic cached-login recovery), since both end in the transfer's refusal.
It does not mention cr add claude. Owner machines, Codex, local chats and every other failure card are unchanged.
The wire
The handshake, not the start progress push. A start that succeeds closes the tab that receives cloudStartProgress, so the chat's own page never sees that push. Instead:
CloudMachineSession.harnessLoginnow keeps theCloudHarnessLoginDecisionitself, not only its summary.cloud.machinesstill prints.summary.CloudHarnessLoginDecision.skippedTeamMachine(package) is true when the reason isguest-not-ownerand the guest kind isteam.- The tab's remote host sets the new
AgentPaneHandshake.cloudTeamMachinefrom that decision.passHarnessLoginis awaited before the chat opens, so the decision is there when the chat's handshake is built. - The page reads
cloudTeamMachineinto the failure route (FailureRoute.teamMachine).failureCopy.tsisTeamMachineSignIndecides the copy, andTurnRowsdecides the buttons (CloudSignIn keyOnly). The transfer is withheld both inApp.tsxand in the footer.
CmuxNextApp physical lines: net 0. The App changes are a type change and one wiring line, offset by folding two handshake assignments.
Tests
failureCopy.test.ts: the team copy for rejected, signed-out and missing-credential failures. An owner machine, Codex, a local chat and a rate limit keep their own copy.test/cloud-signin-card.test.tsx: on a team machine the card shows the new sentence and [Switch model (filled), Use an API key], with no Sign in to Claude and no Set up credentials. The owner-machine card is unchanged.CloudStartChatLoginTests.onlyATeamGuestSkipMarksATeamMachine: only a team guest's skip sets the flag, and the handshake encodescloudTeamMachine.- Fleet runs (bun tests, fmt, lint, typecheck; app compile; the Swift suite) are posted below as comments.
Risk
Low. The change is limited to a Claude sign-in failure on a Cloud chat whose start recorded a team-machine skip in this app run. The decision is not persisted, so after an app restart, or for a chat opened on a team machine some other way, the card is the old one. The gate is the DEV/dogfood login pass (build-gate-off reads no guest kind), so shipping builds also keep the old card for now. Both are follow-ups, not regressions.
Agent session: agent session
š¤ Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
On a team Cloud machine, a Claude sign-in failure now says cmux passes no personal login there, and offers Switch model and Use an API key instead of Sign in to Claude or a login transfer that would dead-end in the transfer's refusal. Owner machines, Codex, local chats, and all other failure cards are unchanged.
The wire
- The handshake carries
cloudTeamMachinewhen the start skipped the guest login asguest-not-owneron ateammachine. - The new copy is localized in all 21 pane locales, and the login transfer (manual and automatic) is withheld for that route.
Tests
failureCopy.test.tsandcloud-signin-card.test.tsxcover the team-machine card;CloudStartChatLoginTestscovers the handshake flag.