Environment picker: an Auto row and its scope line (1/2 of the #26151 fold)

TE
6d ago
+384-25

HOLD: stacked on #26920 (held behind its Swift stack); this lands after it, and #26151's New Tab wiring (the next PR in this stack) lands after this.

Environment picker: an Auto row and a scope line that names where Auto sends the chat. This is part 1 of 2 of folding #26151 ("ai-env: let new chats choose the environment automatically") into the run-location default of #26920, as one picker. Part 2 wires Auto into the New Tab page.

Leo, 2026-10-28: "whatever's convenient for cloud, like that can be productionized, operationalized, we ought to just put out there"

Leo, 2026-10-28: "you can just say ... where should this chat run and stuff, and that's just about it. Like, you can determine whether it's in the cloud or otherwise"

From #26151: "why doesn't ai just choose what environment i want", "AI just choose the environment", "Environment choose cloud that would be nice"

Gallery: agent-pane.composer-auto-scope, agent-pane.composer-context/environment-picker-auto-cloud, agent-pane.composer-context/environment-picker-cloud-picked

What changes

  • EnvironmentPicker.tsx takes an optional auto choice from #26151:

    • an Auto row at the top, found by search;
    • the row is checked while Auto holds.
  • Under Auto, Cloud is the folder's default (#26920), not a pick. So:

    • the Cloud row is not checked;
    • picking Auto asks for that default back (RunModeRows.onDefault, new and optional) instead of making a Persistent pick;
    • picking the Cloud row is a pick, as before.
  • Under Auto, a machine row that lands on a folder tells the caller about the machine first (onComputer), so the pick ends Auto. The Auto row draws its check.

  • ComposerContext.tsx: the scope line names Auto's destination. Its face is the auto-scope-look experiment, with three arms:

    • dot (default): one label, "Auto · Cloud", from #26151;
    • tag: the destination's icon and name, as an explicit pick shows them, then a quiet "Auto";
    • icon: Auto's own icon in place of the destination's.

    The accessible value is always "Auto · ". At pane widths of 460 and under, the tag hides with the label, as the machine name already does.

  • Gallery:

    • a new agent-pane.composer-auto-scope entry: the folder's Cloud default, the folder's local default, a prompt that names a machine, the open picker, and Cloud picked by hand (the before), with Compare arms;
    • #26151's three Auto states in agent-pane.composer-context, and its Auto Cloud state now on the Cloud row.

Nothing passes auto yet, so nothing changes in the app until part 2 lands.

Which scope line, and why

dot ("Auto · Cloud") is the default. I started with tag, but in the captures it reads as "Cloud Auto": the scope row's text is already quiet, so the tag doesn't stand apart. dot reads as one phrase, matches the picker's own Auto row ("Auto · Cloud"), and keeps the destination's icon. icon drops that icon, and the icon is how every other face of this row shows a machine's kind. The other two arms stay in the gallery's Compare arms.

Before / after

Gallery renders (dark, 620 px, Chromium) from a cmux-ci Linux run at this stack's tree. The gallery advisory captures only PRs into feat-cmux-next, so it runs once this PR's base retargets.

Before: Cloud picked, no AutoAfter: Auto over a folder whose last chat went to Cloud
beforeafter

The three scope-line arms for the same state:

dot (default)tagicon
dottagicon

Other Auto states: this Mac, a prompt naming gpu-box. Before, face only: Cloud picked.

Tests

  • test/environment-picker.test.tsx:
    • #26151's Auto tests: the destination before opening, explicit machines kept, a search for a machine skipping Auto, and Enter on a searched Auto;
    • the face reads "Auto · gpu-box", which is also its accessible value;
    • under Auto over the folder's Cloud default: Auto is checked and Cloud is not, Auto calls onDefault (never onMode("persistent")), and Cloud calls onMode("cloud").
  • NewTabScreen.test.tsx, EnvironmentPicker.test.tsx and gallery-agent-pane-entries.test.ts pass.
  • One base picker test, "with Sandboxed picked … a machine pick goes back to Persistent", clicked the first Cloud-kind element. Since #26801 that is the Cloud row's icon, and the test failed the same way on lane/run-location-default. It now clicks New Cloud Machine.
  • cmux-ci (Linux pool), at the tree before this head's format-only commit: all of the above pass, and bun run check is green apart from that one format fix.
  • Two gallery-coverage.test.ts tests fail with the same names on lane/run-location-default at fdb7d09. This PR adds no line to either list.

Risk: low. Nothing renders it until part 2 passes auto. The picker's rows and runMode handling don't change when auto is absent.

Agent session: agent session

🤖 Generated with Claude Code