Cloud: a team machine's Claude sign-in card says it takes no personal login

Merged
TE
4d ago
+181-26

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.harnessLogin now keeps the CloudHarnessLoginDecision itself, not only its summary. cloud.machines still prints .summary.
  • CloudHarnessLoginDecision.skippedTeamMachine (package) is true when the reason is guest-not-owner and the guest kind is team.
  • The tab's remote host sets the new AgentPaneHandshake.cloudTeamMachine from that decision. passHarnessLogin is awaited before the chat opens, so the decision is there when the chat's handshake is built.
  • The page reads cloudTeamMachine into the failure route (FailureRoute.teamMachine). failureCopy.ts isTeamMachineSignIn decides the copy, and TurnRows decides the buttons (CloudSignIn keyOnly). The transfer is withheld both in App.tsx and 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 encodes cloudTeamMachine.
  • 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 Autofix with [code]smith
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 cloudTeamMachine when the start skipped the guest login as guest-not-owner on a team machine.
  • 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.ts and cloud-signin-card.test.tsx cover the team-machine card; CloudStartChatLoginTests covers the handshake flag.