New Tab: a project picked there goes with a Cloud start (copied to the machine at its branch with local changes)

TE
5d ago
+43-0

Leo, 2026-10-31: "JUST PASSING THE SELECTED FOLDER/'PROJECT' WHEN YOU PICK CLOUD THAT'S THE BEST WAY. IT JUST GOES IT JUST WORKS". Also: "CODEX/CHATGPT CLOUD JUST WORKS. WE ALL HATE SETTING UP ENVIRONMENTS".

What was wrong

The Cloud start already carries the selected project: cloud.startChat (#28111) copies the chat's folder to the person's Cloud machine through Move to Cloud's sync (cmux link sync push, Rust), which takes a git working copy together with its .git. That brings the remote (minus URL user info), the branch, HEAD, index, stash, uncommitted changes and untracked files that aren't ignored. A folder outside git is copied file by file. Then the chat starts in that copy. No environment form is involved.

The New Tab page broke that path. cloud.startChat copies only the pane's own folders (AgentPaneHandOff.folderInScope: its roots and folders the person picked). A recent project picked on New Tab changed only page state, so a Cloud send from it was refused with pathOutsideRoots. This was found in #28686's review and tracked in #29294 (P2, "New Tab's Cloud send skips folder authorization"). The chat composer's own folder chip already hands its pick to the host (chooseProject → workspace.useFolder).

What changes (web only, no Swift)

  • NewTabScreen gets onProjectPicked(cwd), called from pickProject when the person picks a folder on this Mac (folder chip, widget card, Choose Folder, new worktree).
  • While Cloud is offered, App.tsx sends that pick to the host on the click (pickFolder → workspace.useFolder), the same call the chat composer's chip makes. It skips folders Cloud would never copy (home or above it, per cloudStartFolder). A home or root answer asks nothing here; the local start still asks on Return.
  • Why on the click and not on Return: grantPicked spends a gesture credit. Granting on Return would leave cloud.startChat without one and fail it with gesture_required. The click's credit pays for the grant, and Return keeps its own credit for the start.
  • The host's scope check is unchanged, as #29294 asked.

Prior art read

#28111 (cloud.startChat, the copy), #29046 (moves the start's pick, ledger and copy to Rust; open, stacked), #28686 and #29294 (Composer Cloud and its follow-ups), #29669 (Start New Chats In: Cloud), #29145 and #29556 (~/work default), #28823 (fs-v1 on a fresh VM), #19448 (Move to Cloud transfer). cmux-platform's derived environments (POST /v1/environments/derive) clone a repo from its remote. They don't carry local uncommitted changes, so this keeps the local copy path.

Tests

  • New: a project picked on the page is reported on its click (onProjectPicked), so Cloud can copy it.
  • Fleet run: NewTabScreen.test.tsx plus cloudRun.test.ts, 33 pass, 0 fail, and tsc --noEmit clean (run).
  • DEV-build verification on the Cloud test account: added in a comment below.

Follow-up (not in this PR): the first push of a large repo uploads its whole .git. A clone from the remote plus a push of only the local delta would be faster, but it needs git credentials on the machine, so it waits for the credential relay work.

Agent session: agent session

šŸ¤– Generated with Claude Code