Skip to content

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

Closed
teamleaderleo wants to merge 41 commits into
feat-cmux-nextfrom
op-next-environments
Closed

teamleaderleo wants to merge 41 commits into
feat-cmux-nextfrom
op-next-environments

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

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 56794b8 (tests only): 1 pass, 4 fail. The flag-off test passes by design. Green on d43d609: 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 Fix base red: chief_brains state root asks the build identity (cx-8oudl) #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.

Written for commit 3dbfe8e. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 30 commits October 13, 2026 21:50
…on on/off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ors (hq#1980)

schemas/settings/source/labs.json is the flag registry: each labs.<name>
row carries owner, created, expires, cohort (off / nightly-next / default)
and its one-line intent (help). gen-settings-sources.py checks the fields,
generates the Swift LabsFlag enum and webviews/src/experiments/
labs.generated.ts, and warns (never fails) for a flag past its expiry.

- Swift reads a flag with snapshot.lab(.name): cmux.json's value, else
  the cohort for this build (nightly-next: nightly and dev builds).
  previewFeatures is the first flag; snapshot.previewFeatures now reads it.
- Pages get the on flags as data-labs on <html> (LabsState, updated by
  the settings controller); web reads labsFlag / useLabsFlag.
- Flags are agent-settable from the registry, show in Settings >
  Experimental > Labs, flip from the palette's settings scope, and
  Help > Feature Flags opens them.
- skills/cmux-architecture/references/labs-flags.md: how to add,
  promote (one-line cohort PR = the design call), extend or delete one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1980)

In nightly and dev builds the New Tab page lists every Labs flag with its
one-line intent and a toggle, so the team tries prototypes where they start
work; All Labs... opens Settings > Labs. A toggle is newTab.setLab, saved as
labs.<flag> through the schema like the style switch's template (one
AgentPaneNewTabSetting callback for both). Pages see a team build as
data-labs-team on <html>; labsTeamBuild() reads it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CmuxNextApp sees two JSONValue types (Settings and Daemon).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…call-keys/labs

# Conflicts:
#	Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/NewTabStyleSwitchTests.swift
Review of #20452: with agent.pageHost off (the default) the agent pane and
its New Tab page make their own WKWebView, so data-labs and data-labs-team
never reached them and the New Tab Labs section never showed. AgentPaneLabs
adds them at document start with the theme bootstrap and follows LabsState.
Also: the Labs tests await each save (#20423's fix for the yield wait), the
generator rejects a flag key that is not labs.<name> or has no default
without a traceback, and labsFlag() says when to use the hook instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gallery-coverage asks every component for an entry: the New Tab page gets a
labs variant (data-labs-team set), and the section follows data-labs-team
through useLabsTeamBuild so it shows when the attribute does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- vp fmt webviews/src/agent-session/acpmux/modelPickerLayout.ts

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38521627926
…call-keys/labs

# Conflicts:
#	webviews/src/agent-session/acpmux/newtab/NewTabScreen.gallery.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the DEV build it drew as a raised button; it now uses the All Chats
link's style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated webviews/src/agent-session/acpmux/generated/strings.json
- vp fmt webviews/test/ime-composition-keys.test.ts
- vp fmt webviews/test/new-tab-omnibar-pointer.test.ts
- cargo fmt in Native/OptChat/optchat-chief

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38568297033
…erated strings)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…merge

Reflow the action.run doc comment so the Labs mention does not grow the
type past the godfile baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- vp fmt webviews/src/pages/home-channels/AttachmentViews.tsx
- vp fmt webviews/src/pages/home-channels/Composer.tsx
- vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx
- vp fmt webviews/src/pages/home-channels/attachmentService.ts
- vp fmt webviews/src/pages/home-channels/icons.tsx

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38581729991
Catalogs rebuilt from the base text with the Labs blocks inserted; the
home-channels files take the base (the branch only had autofix formatting there).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collapsed by default to one line, "Labs (N on)", that opens in place;
open, each flag is a row with its name, intent, a New or Expires soon hint
and a toggle; the flags toggled last come first, else the newest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the palette

The Labs block shares the page's column edges and type scale: text on the
column edge, row hover 10px past it, as the Chats list rows. Closed it is one
line, "Labs (N on)"; open, each flag is a 28px row with its name, its intent,
a New or Expires soon hint and a small switch, the flags toggled last first
(per-viewer storage, try/catch), else the newest. The generated flag table
carries each flag's title.

Help > Feature Flags is now "Labs…" and is listed in the palette, with
"labs" and "feature flags" as search keywords.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…row menu

With no flag on the line says plain "Labs"; it opens as this viewer left it
(closed at first); a row's right-click menu has Turn On or Turn Off and
All Labs….

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo and others added 6 commits October 14, 2026 16:14
…and has a row menu

The line opens as this viewer left it (per-viewer storage, try/catch, closed
at first). A row's right-click menu turns its flag on or off and opens
All Labs… (two new strings, 21 locales). The intent stays one line and gives
way first, with an ellipsis before the hint and the switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fleet export-action-surfaces.sh at ad062d5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(cx-ncc.104)

Choose environment in the New Tab composer strip: a searchable menu of saved
environments (Local / SSH / Cloud) and New environment, an inline panel with
Local / SSH / Cloud / Remote tabs, folders with a worktree option, env vars and
Cloud network settings. A pick starts the chat through chat.new in its first
folder (in a new worktree when asked) on its machine, for any harness. Stored
by the host (environment.list/save/remove) in a 0600 file. Labs flag
labs.savedEnvironments (nightly-next cohort).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 15, 2026 00:46 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b3d9393c-8b45-4c98-ac17-68e3d739bb0d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

red: cmux-next CmuxNextApp size cap; do not merge until green; see lane-rules
red: web / Web status; do not merge until green; see lane-rules
red: web / react-apps-check; do not merge until green; see lane-rules

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Unclear: web / react-apps-check failed: no known signature; failed step: Check webviews (Oxlint, Oxfmt, type check).

CI failed on 270c0aa7fa (run 38665065908 attempt 1): 1 unknown.
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped.

Job Verdict Why
web / react-apps-check unknown no known signature; failed step: Check webviews (Oxlint, Oxfmt, type check)

Not re-run automatically: web / react-apps-check is not a machine failure.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards failed on 270c0aa7fa (https://github.com/manaflow-ai/cmux/actions/runs/38665065908). It does not block the merge; a red guard merged into main breaks it for every open PR.
The log named no failed step; see the run.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally. This comment is updated in place on each push.

teamleaderleo and others added 2 commits October 14, 2026 18:03
… exports for labs.savedEnvironments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 15, 2026 01:04 — with GitHub Actions Inactive
- regenerated plans/cmux-next/action-surfaces.json
- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38663615735
@cursor

cursor Bot commented Oct 15, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

…xNextApp size cap)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 15, 2026 04:53 — with GitHub Actions Inactive
…nd Computer keep their room

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 15, 2026 04:58 — with GitHub Actions Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Orphaned: no activity for 112 h, and no lane holds a lease on it. The orphan sweep could not merge feat-cmux-next in: conflicts in Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPageProvider.swift, Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneModel.swift, Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneView.swift, Packages/macOS/CmuxNext/Sources/CmuxNextApp/AgentTabs.swift, Packages/macOS/CmuxNext/Sources/CmuxNextSettings/CmuxConfigSnapshot+PreviewFeatures.swift, Packages/macOS/CmuxNext/Sources/CmuxNextSettings/CmuxConfigSnapshot.swift, Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Schema/SettingsSchema+AgentPolicy.swift, Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Schema/SettingsSchema+Labs.generated.swift and 11 more. Listed for a lane to resolve.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Rebuilt on #22131's environment picker as #22406. The rebuild branches fresh from feat-cmux-next, since this branch was stacked on the closed #20452 and conflicts in the catalog.

#22406 keeps saved environments. That covers the host storage, the picker's Saved section, the Create/Edit panel, and asking on an SSH machine or in a local folder or worktree. Its localization keys are regenerated, reusing this PR's translations.

It leaves out three things:

teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…ilt from #20831) (#22406)

* test(cmux-next): saved environments on the New Tab picker (failing)

The New Tab page's saved environments, rebuilt from #20831 on the
environment picker #22131 landed: the host keeps them
(environment.list/save/remove, New Tab page only, 0600 file), the picker
lists them in a Saved section, and a prompt with one picked starts on its
SSH machine in its folder, or on this Mac in its folder or a new worktree
of it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: saved environments in the New Tab environment picker (preview)

Rebuilt from #20831 on #22131's environment picker. With preview
features on, the New Tab page lists the host's saved environments in a
Saved section over the machines, Create environment under New environment
opens an inline panel (name, This computer or SSH, the machine, its
folder, Use worktree), and Edit environment shows while one is picked.

A prompt with one picked starts on its SSH machine in its folder there
(chat.new peer), or on this Mac in its folder, or in a new worktree of it
(the existing New Worktree path, whose refusal shows above the card).

The host keeps them in <App Support>/<bundle id>/environments.json,
written 0600 through a temp file and rename from an actor, and answers
environment.list/save/remove only on a New Tab page, with fixed keys and
caps. #20831's environment variables, Cloud network settings and setup
script are left out: it saved them without applying them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: saved environments review fixes

- The pane actor's reply is main-actor (it builds the unsupported reply);
  the store reads entries one by one and keeps ones this build cannot
  read, so an older build's save never drops a newer build's entries.
- The store lives beside the tag's sidebar snapshot, as the unsent
  drafts do, so a showcase or test run never shares the user's file.
- An SSH environment's prompt waits for its chat: a start the machine
  refused no longer sends it to a new chat on this Mac.
- A saved local folder is used even when the tab already sits there.
- A refused or unused worktree leaves New chat and the create buttons
  working; a project picked elsewhere on the page clears the saved pick;
  the first list load never drops a save that landed before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* cmux-next: saved SSH environments send only into a started chat

The page's chat.new resolves to the chat it started, so a peer start
held without a chat (the folder trust route) no longer lets the New Tab
prompt fall back to a new chat on this Mac. SavedEnvironments' public
init imports Foundation publicly (InternalImportsByDefault).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): a saved environments file that cannot be read is never replaced (failing)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c77-62d9-78b2-8cc3-73561b989bb5&host=huge-blue

* cmux-next: a saved environments file that cannot be read is never replaced

A read error other than a missing file used to read as an empty list, and the next save wrote
only its own entry over the user's file. Now save and remove refuse (unreadable) until the file
reads again. Failure is nonisolated, as the module's other error enums.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c77-62d9-78b2-8cc3-73561b989bb5&host=huge-blue

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

2 inactive (1 outdated) deployments
cmux-tui-merge-tree — 3dbfe8eb Deployed Oct 15, 2026 by teamleaderleo via publish the merge tree #1233
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant