feat(cmux-next): saved environments on the New Tab page, behind Labs (cx-ncc.104)

Closed
TE
17d ago
+10401-117

Bead: cx-ncc.104. Stacked on #20452 (Labs); this diff includes its commits until #20452 lands.

What changed

New Tab gets saved environments behind the Labs flag Saved Environments (labs.savedEnvironments, nightly-next cohort). With the flag off, nothing changes.

  • Choose environment ⌄ in the New Tab composer strip opens a searchable menu.
    • Saved environments are grouped Local / SSH / Cloud, with a check on the picked one.
    • The menu also has No environment, Edit environment and New environment.
    • Once picked, the strip shows the environment's name in place of Folder · Computer.
    • The chip's right-click menu has Edit / No environment / New / Remove.
  • New environment opens an inline panel on the page, not a modal. It has tabs:
    • Local: name, folders (recent folders or Choose folder…), a per-folder ⋮ with Use worktree / Remove, and environment variables.
    • SSH: name, a host (the machines the Computer menu lists, plus SSH…), folders on that host, and environment variables.
    • Cloud: name, network access (None / Limited with allowed domains / Full) and a setup script.
    • Remote: a short line plus Connect a machine…, which runs the existing remote.connect.
  • Asking with an environment picked goes through the existing chat.new, so it works for every harness:
    • Local starts in the first folder, or in a new worktree of it via git.worktree.create.
    • SSH starts on the environment's machine (peer).
    • Cloud runs the existing cmux Cloud… action.
  • Host storage: environment.list / environment.save / environment.remove, New Tab page only.
    • Fixed param keys; unknown keys are refused at any depth.
    • Caps: name 80, 16 folders, 32 env vars with POSIX keys, 64 domains, setup script 16 KiB.
    • Stored in <App Support>/<bundle id>/environments.json, written 0600 (temp file, fsync, rename), with file IO in an actor.

Not yet (follow-ups on the bead):

  • Env vars and extra folders: saved, but not yet applied to the agent. acpmux takes per-session env only from the app's unix socket and does not forward additionalDirectories. The follow-up PR covers that, with its security analysis.
  • Select branch: hidden until the New Tab page can list a folder's branches and git.worktree.create takes a base.
  • Cloud network settings: saved, but not applied to the machine yet.

Red / green

  • webviews/test/new-tab-saved-environments.test.tsx covers five behaviors:
    • flag off: no chip and no calls
    • grouped and searchable menu
    • Local with two folders, one in a worktree, saves the contract shape and names the strip
    • SSH asks with peer
    • No environment clears it
    • Red on 56794b8a3783 (tests only): 1 pass, 4 fail. The flag-off test passes by design. Green on d43d609bd8ee: 5 pass.
  • SavedEnvironmentsTests (Swift, at the page-request boundary) covers five behaviors:
    • a save gets an id and time, newest first
    • contract breaks are refused
    • remove deletes
    • a chat page cannot read or edit environments
    • the file is 0600
    • Red: it does not build without the feature. Green runs in hosted CI.

Verification

  • sbx build (CmuxNext sources): clean.
  • God-file check against base: passes. Wiring avoids the over-budget AppServices / AgentTabStore.
  • Crash ratchet: passes.
  • lint-ios-package-conventions.sh: passes.
  • gen-settings-sources.py --check and gen-strings.mjs --check (all pages): pass.
  • ui-rules and title-tooltip rules: pass.
  • Concurrency check: reds only in base's IdleOwnerRestart.swift / DaemonConnection+QuietHandoff.swift, which #20767 fixes.
  • Typecheck and lint run in CI.
  • Real-app captures (light, dark and a clip) follow in this body.

Localization

  • 36 environment.* pane strings and the Labs title/help in all 21 locales (needs_review).
  • agentPane.error.environment in 21 locales.
  • Generated strings regenerated for all pages.

Changelog

  • Added: Labs: saved environments on the New Tab page: pick a saved set of folders and machine when you start a chat.

🤖 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

Adds saved environments to the New Tab page behind the Labs flag savedEnvironments (nightly-next cohort), and carries the Labs flag registry from #20452; with the flag off, nothing on the New Tab page changes.

Saved environments

  • "Choose environment" in the composer strip opens a searchable menu grouped Local / SSH / Cloud, plus Edit and New environment; until one is picked it is a quiet glyph, so Folder and Computer keep their room; "New environment" opens an inline panel with tabs per kind.
  • Environments store a name, folders (a folder may ask for a new worktree), environment variables, and for SSH a host; Cloud adds network access and a setup script.
  • Starting a chat with an environment picked reuses the existing chat.new: Local begins in the first folder (in a new worktree via git.worktree.create), SSH on the environment's machine (peer), Cloud via the cmux Cloud action.
  • Environments persist in <App Support>/environments.json through environment.list / environment.save / environment.remove, New Tab pages only; writes are atomic in an actor, the file is 0600, and at most 200 environments are kept in CmuxNextAgentPane.
  • Caps: name 80, 16 folders, 32 env vars, 64 domains, setup script 16 KiB; unknown param keys are refused at any depth.
  • Follow-ups: env vars, extra folders and Cloud network settings are saved but not yet applied to the agent; branch selection is hidden until git.worktree.create takes a base.

Labs flags

  • schemas/settings/source/labs.json is the single registry for opt-in features (owner, created, expires, cohort, intent); the generator emits a Swift LabsFlag registry and the web flag table.
  • Swift reads a flag with snapshot.lab(_:teamBuild:), pages read data-labs on <html>; the existing labs.previewFeatures moves onto the registry.
  • Flags are agent-settable, appear in Settings > Labs, flip from the palette, and Help > Labs… opens the list; in team builds the New Tab page shows quick toggles.