Skip to content

cmux-next: tools opened from the docked agent chat open in the strip - #18170

Merged
teamleaderleo merged 24 commits into
feat-cmux-nextfrom
next-chat-dock-tools
Oct 9, 2026
Merged

teamleaderleo merged 24 commits into
feat-cmux-nextfrom
next-chat-dock-tools

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Section D of the 2026-10-06 call: the agent chat is a docked region on the left, and tools are tabs in the area to its right, like the Codex app. The chat dock is the left dock column with the agent_chat role (#18169), and it never gets tabs of its own.

What a person sees:

  • From the docked chat: New Terminal (Ctrl-, menu, palette), New Browser and Cmd-T open a tab in the scrolling strip, never a split. The tab goes to the strip pane focused last and takes focus. A terminal gets only an explicit cwd and otherwise sends none, so the daemon's resolver picks it (decision NEW-TERMINAL-INHERITS-CWD, capability new-terminal-cwd-v1`). Cmd-T on a chat would make another chat, so it opens the New Tab page in the strip instead.
  • A chat alone on its screen (one pane, one chat tab): New Terminal or New Browser moves the chat into a new left chat dock, and the new tab stays in the chat's old pane, focused. The move is one daemon commit (move-tab-to-column with a dock pin and respawn).
  • While the screen is zoomed: nothing docks or moves; the tab opens in the zoomed pane.
  • New Browser without a URL from the docked chat opens what the chat works on (cmux-next tabs: switch between terminal, browser and agent and keep the context #16620), in the strip.
  • Chats outside the chat dock: a chat a person put in a plain dock, or that sits beside other columns, is a normal pane, so new tabs open in it as before. Only the role makes the chat dock.
  • Unchanged: splits are still available when asked for. Scripts (CLI, MCP) keep opening tabs in the pane they name. Daemons without dock-column-role-v1 keep the old behavior.

Builds on #18169 (the role). Supersedes the inference-based next-chat-column-tools branch, which never got a PR.

Follow-ups: the dock refuses splits and drops, and layout operations skip it. A layout.dockAgentChat setting and Dock/Undock Chat actions come after that.

Testing

ChatColumnPlacementTests was committed red before the fix (b85c214), with a stub that always answers "here". Runs use scripts/measure/suite-runner.sh on a fleet runner.

Commit Result
b85c214 (red) the stub was missing import CmuxNextBridge, so it did not compile. Re-run with only that import added (scratch dc19226): 5 failed (theDockedChatSendsTheToolToTheStrip, theStripPaneFocusedLastGetsTheTab, theDockedChatSkipsOtherDocks, theDockedChatWithoutAStripGetsANewColumn, aChatAloneOnItsScreenDocks). The 4 tests that expect "here" passed.
90518c9 (green) passed: ChatColumnPlacementTests (9 tests), NewTabKindTests, EmptyWorkspaceNewTabTests, StripNewTabTests, NewTerminalWorkspaceSettingsTests, AgentBrowserProfileTests, DockColumnCapabilityTests, DockColumnMappingTests, EncodingTests

5353e55 applies NEW-TERMINAL-INHERITS-CWD to the strip tab and to the terminal a lone chat leaves behind. No test covers that wiring yet; the docked-column agent on Lawrence's side is handing over tests for open-pane routing.

The first live check (build of 5353e55 on a capture mini, real key presses) found a bug. New Terminal from a lone chat opened a terminal tab beside it, and the chat did not dock. A new workspace's screen is one split tree, and ScreenLayout.column(containing:) only searches real columns, so the chat's screen looked like it had none. LayoutScreen.column(containing:) gives the implicit column.

Commit Result
b998d19 (red) aSplitTreeScreenIsOneImplicitColumn failed: no columns, so the placement was "here"
977bcf8 (green) passed: ChatColumnPlacementTests, NewTabKindTests, StripNewTabTests, EmptyWorkspaceNewTabTests

Live check on a capture mini (fleet build of 977bcf8, job 2677fd2a). The routed steps used real foreground key presses; actions run from the CLI count as scripts and skip this routing. The setup was a new chat (Cmd-I) with the pane's other tabs closed via the CLI (setup only).

  • New Terminal (Cmd-Shift-Ctrl-T) from the lone chat: the chat moved into a left dock, and the terminal stayed in the old pane on the right with focus. The terminal started in ~, which the daemon picked because no cwd was sent.
  • New Browser (Cmd-Shift-L) from the docked chat: a browser tab opened in the right pane.
  • Cmd-T from the docked chat: the New Tab page opened in the right pane, not another chat.

The chat's dock kept only the chat throughout.

Cursor review fixes (after the base merge that took this PR to Swift only):

Commit Result
f5b6444 (red) hosted swift test (run 37860865718) fails theDockedChatWithoutAStripStaysHere (got .newColumn) and aZoomedChatDocksNothing (got .dockChat)
0a6fdc6 (green), 39ea26e (empty retrigger: the first run was refused by a runner disk floor) hosted swift test (run 37866107013) passes ChatColumnPlacementTests, PanePaintHoldTests, DebugPageTargetTests
  • The strip-less chat dock case (.newColumn) is gone: the daemon never keeps a dock without a scrolling column (an all-docked screen undocks), and the path dropped an explicit cwd and sent browsers into the dock.
  • A zoomed screen maps to its zoomed pane alone, so a zoomed chat looked lone and docked; zoom now keeps the tab here.
  • A URL-less browser from the docked chat takes the chat's context, and a browser a lone chat leaves when it docks is recorded for Auto tabs.newTabKind. No unit test covers these two wirings (nothing in the suite builds a PaneController with an agent page).

Proof

Window-only captures, with host names covered. Each image is before and after, side by side.

New Terminal from a lone chat docks it
Tools from the docked chat open on the right

Changelog

Changed: in cmux-next, a terminal, browser or New Tab opened from the docked agent chat opens as a tab in the area to its right.

🤖 Generated with Claude Code


Note

Medium Risk
Changes user-visible tab/pane routing and layout moves for agent chat; gated on daemon capabilities but affects focus, cwd, and dock commits when enabled.

Overview
Routes new terminals, browsers, and New Tab pages opened from the docked agent chat (agent_chat role) into the scrolling strip instead of the chat dock, using new ChatColumnPlacement logic (recent strip focus, zoom, lone-chat detection).

When a lone agent chat fills a screen, user-initiated New Terminal/Browser docks the chat in a new left column via TabMoves.toNewDockColumn with role: .agentChat and a single daemon commit (respawn for the tool left in the pane). TabMoves now forwards optional dock role to the daemon (dock-column-role-v1).

Tab lifecycle wiring: strip targets get focus; terminals from the dock use daemonResolvesCwd so cwd inheritance follows the daemon resolver; URL-less browsers from the dock use the chat pane’s working context while opening in the strip; lone-chat dock can respawn a browser in the vacated pane.

Adds ChatColumnPlacementTests for placement rules (implicit single-column screens, zoom, strip-less dock edge cases).

Reviewed by Cursor Bugbot for commit 39ea26e. Bugbot is set up for automated code reviews on this repo. Configure here.

teamleaderleo and others added 7 commits October 6, 2026 18:07
Red: set-column-dock and move-tab-to-column drop role, identify does not
advertise dock-column-role-v1, the registry record drops it, and the app
never sends it back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cmux-tui: ColumnDock gains an optional role (agent_chat), set with the pin
by set-column-dock and move-tab-to-column, journaled and restored with it,
dropped with it, and read as no role when unknown. identify advertises
dock-column-role-v1. Spec and generated bindings updated.

cmux next: DockSnapshot, the dock requests and the layout's DockColumn
carry the role; no behavior uses it yet.

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>
The agent chat dock (a left dock column with the agent_chat role) never
gets tabs of its own. A person's New Terminal, New Browser or Cmd-T from it
opens a tab in the scrolling strip, in the strip pane focused last; Cmd-T's
agent or page kind becomes a New Tab page there. A chat alone on its screen
moves into a new left chat dock and the new terminal or browser stays in its
pane, in one daemon commit. Chats outside the chat dock stay normal panes.

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

coderabbitai Bot commented Oct 7, 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: 1adf1b10-9e5d-4352-923e-311d997b0d45

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
  • Autopilot · 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.

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

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Not verified: macOS jobs did not run: compile and app tests were skipped on 39ea26e8ea; ci-status is green without them.

CI passes on 39ea26e8ea (run 37866107424 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

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.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/ChatColumnPlacement.swift Outdated
…its cwd

NEW-TERMINAL-INHERITS-CWD: a terminal opened from the docked agent chat
(a strip tab, or the tab a lone chat leaves when it docks) sends only an
explicit cwd and otherwise nil, so the daemon's resolver
(new-terminal-cwd-v1) decides.

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

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@lawrencecchen

Copy link
Copy Markdown
Contributor

Review (correctness read, nx-docked-column lane). Concrete defects:

  1. The chat dock with no strip still takes tabs. .newColumn handles only a terminal (guard case .terminal = respawn else { return controller }). New Browser from that state, and Cmd-T (.agent/.page, respawn: nil), get controller back, so the browser tab, the New Tab page or a new chat opens inside the chat dock. That breaks the invariant in the summary ("it never gets tabs of its own"). For a browser, a new column can be opened with the browser as its first tab (move-tab-to-column after the tab is created, or a respawn-style create). For Cmd-T, the New Tab page can take the same path.
  2. .newColumn drops the invocation's cwd and keep. route builds respawn: .terminal(SpawnOptions(cwd:keep:)), but .newColumn calls layoutModel.newColumn(after:), which spawns with the chat pane's selected-tab cwd and no keep. A person's New Terminal that carries a cwd (for example a Finder "New cmux Workspace Here"-style entry or a command action) opens in the wrong folder. Pass the respawn's options to the new column.
  3. Minor: in newBrowser the early return when route gives nil (dockChat) skips noteUserChoice. tabs.newTabKind: auto therefore does not learn this browser choice. The terminal path has the same gap.

teamleaderleo and others added 2 commits October 6, 2026 20:00
… column

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ScreenLayout.column(containing:) only searches real columns, so a new
workspace's screen (one split tree) had none and New Terminal from its
lone chat opened beside the chat. LayoutScreen.column(containing:) gives
the implicit column.

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

# Conflicts:
#	cmux-tui/bindings/cpp/.cmux-sdk-manifest.json
#	cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp
#	cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/go/raw/generated_commands.go
#	cmux-tui/bindings/go/raw/generated_events.go
#	cmux-tui/bindings/go/raw/generated_metadata.go
#	cmux-tui/bindings/go/raw/generated_presence_test.go
#	cmux-tui/bindings/go/raw/generated_types.go
#	cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java
#	cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/python/cmux/raw/_generated/_schema.py
#	cmux-tui/bindings/python/cmux/raw/_generated/metadata.py
#	cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/rust/src/generated/commands.rs
#	cmux-tui/bindings/rust/src/generated/events.rs
#	cmux-tui/bindings/rust/src/generated/metadata.rs
#	cmux-tui/bindings/rust/src/generated/mod.rs
#	cmux-tui/bindings/rust/src/generated/types.rs
#	cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/typescript/src/raw/generated/commands.ts
#	cmux-tui/bindings/typescript/src/raw/generated/events.ts
#	cmux-tui/bindings/typescript/src/raw/generated/index.ts
#	cmux-tui/bindings/typescript/src/raw/generated/metadata.ts
#	cmux-tui/bindings/typescript/src/raw/generated/types.ts
#	cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/zig/src/raw/generated/protocol.zig
#	cmux-tui/spec/commands.md
…ock-tools

# Conflicts:
#	cmux-tui/bindings/cpp/.cmux-sdk-manifest.json
#	cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp
#	cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/go/raw/generated_commands.go
#	cmux-tui/bindings/go/raw/generated_events.go
#	cmux-tui/bindings/go/raw/generated_metadata.go
#	cmux-tui/bindings/go/raw/generated_presence_test.go
#	cmux-tui/bindings/go/raw/generated_types.go
#	cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java
#	cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/python/cmux/raw/_generated/_schema.py
#	cmux-tui/bindings/python/cmux/raw/_generated/metadata.py
#	cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/rust/src/generated/commands.rs
#	cmux-tui/bindings/rust/src/generated/events.rs
#	cmux-tui/bindings/rust/src/generated/metadata.rs
#	cmux-tui/bindings/rust/src/generated/mod.rs
#	cmux-tui/bindings/rust/src/generated/types.rs
#	cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/typescript/src/raw/generated/commands.ts
#	cmux-tui/bindings/typescript/src/raw/generated/events.ts
#	cmux-tui/bindings/typescript/src/raw/generated/index.ts
#	cmux-tui/bindings/typescript/src/raw/generated/metadata.ts
#	cmux-tui/bindings/typescript/src/raw/generated/types.ts
#	cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/zig/src/raw/generated/protocol.zig
#	cmux-tui/spec/commands.md
teamleaderleo and others added 4 commits October 7, 2026 00:26
…hat-role

# Conflicts:
#	cmux-tui/bindings/cpp/.cmux-sdk-manifest.json
#	cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp
#	cmux-tui/bindings/cpp/src/raw/generated/protocol.cpp
#	cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/go/raw/generated_commands.go
#	cmux-tui/bindings/go/raw/generated_events.go
#	cmux-tui/bindings/go/raw/generated_metadata.go
#	cmux-tui/bindings/go/raw/generated_presence_test.go
#	cmux-tui/bindings/go/raw/generated_types.go
#	cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java
#	cmux-tui/bindings/java/src/com/cmux/raw/SetColumnDockRequest.java
#	cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/python/cmux/raw/_generated/_schema.py
#	cmux-tui/bindings/python/cmux/raw/_generated/client.py
#	cmux-tui/bindings/python/cmux/raw/_generated/metadata.py
#	cmux-tui/bindings/python/cmux/raw/_generated/models.py
#	cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/rust/src/generated/commands.rs
#	cmux-tui/bindings/rust/src/generated/events.rs
#	cmux-tui/bindings/rust/src/generated/metadata.rs
#	cmux-tui/bindings/rust/src/generated/mod.rs
#	cmux-tui/bindings/rust/src/generated/types.rs
#	cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/typescript/src/raw/generated/commands.ts
#	cmux-tui/bindings/typescript/src/raw/generated/events.ts
#	cmux-tui/bindings/typescript/src/raw/generated/index.ts
#	cmux-tui/bindings/typescript/src/raw/generated/metadata.ts
#	cmux-tui/bindings/typescript/src/raw/generated/types.ts
#	cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/zig/src/raw/generated/protocol.zig
#	cmux-tui/crates/cmux-tui-core/src/model/layout_columns.rs
#	cmux-tui/crates/cmux-tui-core/src/mux/dock_columns.rs
#	cmux-tui/crates/cmux-tui-core/src/mux/tests/column_update.rs
#	cmux-tui/crates/cmux-tui-core/src/mux/tests/dock_columns.rs
#	cmux-tui/crates/cmux-tui-core/src/server.rs
#	cmux-tui/crates/cmux-tui-core/src/server/capabilities.rs
#	cmux-tui/crates/cmux-tui-core/src/server/dock_columns_tests.rs
#	cmux-tui/crates/cmux-tui-core/src/workspace_registry/resource_store/screen_rows.rs
#	cmux-tui/crates/cmux-tui-core/src/workspace_registry/resource_store/viewport.rs
#	cmux-tui/spec/sdk-schema.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat-dock-tools

# Conflicts:
#	cmux-tui/bindings/cpp/.cmux-sdk-manifest.json
#	cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp
#	cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/go/raw/generated_commands.go
#	cmux-tui/bindings/go/raw/generated_events.go
#	cmux-tui/bindings/go/raw/generated_metadata.go
#	cmux-tui/bindings/go/raw/generated_presence_test.go
#	cmux-tui/bindings/go/raw/generated_types.go
#	cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json
#	cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java
#	cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/python/cmux/raw/_generated/_schema.py
#	cmux-tui/bindings/python/cmux/raw/_generated/metadata.py
#	cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/rust/src/generated/commands.rs
#	cmux-tui/bindings/rust/src/generated/events.rs
#	cmux-tui/bindings/rust/src/generated/metadata.rs
#	cmux-tui/bindings/rust/src/generated/mod.rs
#	cmux-tui/bindings/rust/src/generated/types.rs
#	cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/typescript/src/raw/generated/commands.ts
#	cmux-tui/bindings/typescript/src/raw/generated/events.ts
#	cmux-tui/bindings/typescript/src/raw/generated/index.ts
#	cmux-tui/bindings/typescript/src/raw/generated/metadata.ts
#	cmux-tui/bindings/typescript/src/raw/generated/types.ts
#	cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json
#	cmux-tui/bindings/zig/src/raw/generated/protocol.zig
…ock-tools

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/TabLifecycle.swift
@blacksmith-sh

This comment has been minimized.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

…ock-tools

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/TabLifecycle.swift
@blacksmith-sh

blacksmith-sh Bot commented Oct 7, 2026

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
unittest.case.FunctionTestCase (test_cmux_tui_artifacts_coalesces_queued_feat_cmux_next
_pushes)/
unittest.case.FunctionTestCase (test_cmux_tui_artifacts_coalesces_queued_feat_cmux_next
_pushes)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

teamleaderleo and others added 2 commits October 8, 2026 14:54
The conflicts were the generated cmux-tui SDK bindings: the dock column
role landed on base separately. Took base's side and regenerated them
with cmux-tui/bindings/codegen/generate.py (--check is clean).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picks up the base compile fix for AgentPaneDraftStore (#18669, fc10fdf); the last run built against the broken base.

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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5d3bca2. Configure here.

Comment thread Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/ChatColumnPlacement.swift Outdated
teamleaderleo and others added 2 commits October 8, 2026 16:41
…w tab here

Cursor review on #18170: .newColumn is built for a chat dock with no
scrolling column, which the daemon never keeps (an all-docked screen
undocks), and a zoomed screen maps to its zoomed pane alone, so a zoomed
chat looked like a lone chat and docked. resolve takes a zoomed seam,
still ignored here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ile zoomed, and opens the chat's context

Cursor review on #18170:
- The daemon never keeps a dock without a scrolling column (an
  all-docked screen undocks), so .newColumn is gone; it also dropped an
  explicit cwd and sent browsers and New Tab pages into the dock.
- A zoomed screen maps to its zoomed pane alone; nothing docks or moves
  until the zoom ends.
- A URL-less browser from the docked chat opens what the chat works on
  (#16620), in the strip pane.
- A browser a lone chat leaves when it docks is recorded for Auto
  tabs.newTabKind, like any other browser (Cursor on #18223).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 9, 2026
Takes #18170's Cursor fixes (no strip-less .newColumn, zoom keeps the
tab here, the chat's context for a URL-less browser). The one conflict
was ChatColumnPlacement.resolve: kept the zoom check and this branch's
isChat helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 9, 2026
Takes #18170's and #18223's Cursor fixes (routing, zoom, the chat's
browser context, dock-bound chats hidden across rekey and other docks,
the strip beside the chat dock).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo merged commit 90f0688 into feat-cmux-next Oct 9, 2026
62 checks passed
@teamleaderleo
teamleaderleo deleted the next-chat-dock-tools branch October 9, 2026 01:52
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/37871997540 at 9e3a1a2 failed: cmux-next swift test.
Those jobs last passed at cfe3a43. This pull request is one of 4 merged since: #18716, #18718, #18124, #18170.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

teamleaderleo added a commit that referenced this pull request Oct 9, 2026
…18223)

* test: the dock column keeps an agent chat role

Red: set-column-dock and move-tab-to-column drop role, identify does not
advertise dock-column-role-v1, the registry record drops it, and the app
never sends it back.

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

* feat: dock columns keep an agent chat role (dock-column-role-v1)

cmux-tui: ColumnDock gains an optional role (agent_chat), set with the pin
by set-column-dock and move-tab-to-column, journaled and restored with it,
dropped with it, and read as no role when unknown. identify advertises
dock-column-role-v1. Spec and generated bindings updated.

cmux next: DockSnapshot, the dock requests and the layout's DockColumn
carry the role; no behavior uses it yet.

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

* test(cmux-sdk): build ColumnPin with its optional role

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

* test(cmux-next): tools opened from the docked chat go to the strip

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

* feat(cmux-next): tools opened from the docked chat open in the strip

The agent chat dock (a left dock column with the agent_chat role) never
gets tabs of its own. A person's New Terminal, New Browser or Cmd-T from it
opens a tab in the scrolling strip, in the strip pane focused last; Cmd-T's
agent or page kind becomes a New Tab page there. A chat alone on its screen
moves into a new left chat dock and the new terminal or browser stays in its
pane, in one daemon commit. Chats outside the chat dock stay normal panes.

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

* fix(cmux-next): a lone New Tab page is not a chat to dock

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

* fix(cmux-next): a terminal from the docked chat lets the daemon pick its cwd

NEW-TERMINAL-INHERITS-CWD: a terminal opened from the docked agent chat
(a strip tab, or the tab a lone chat leaves when it docks) sends only an
explicit cwd and otherwise nil, so the daemon's resolver
(new-terminal-cwd-v1) decides.

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

* test(cmux-next): a lone chat on a split-tree screen sees its implicit column

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

* fix(cmux-next): a lone chat on a split-tree screen docks

ScreenLayout.column(containing:) only searches real columns, so a new
workspace's screen (one split tree) had none and New Terminal from its
lone chat opened beside the chat. LayoutScreen.column(containing:) gives
the implicit column.

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

* test(cmux-next): the chat dock shows no tab strip until it holds two tabs

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

* feat(cmux-next): the chat dock shows no tab strip until it holds two tabs

The agent chat is a docked column with no tab bar by default. The chat
dock's pane (the dock column with the agent_chat role) and a chat alone on
its screen, which docks when the first tool opens, hide their strip while
they hold one tab. The strip comes back at two tabs.

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

* test(cmux-next): a new agent chat opens in the chat dock

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

* feat(cmux-next): a new agent chat opens in the chat dock

Every new agent chat (New Agent Chat, Cmd-I on a pane, the CLI, Duplicate,
a session link) opens in the chat dock: a tab in the existing dock, else a
new left dock with the agent_chat role made from the chat once it exists.
A chat alone on its screen keeps a second chat in its pane; it docks when
the first tool opens. A New Tab page that becomes a chat stays where it is.

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

* test(cmux-next): a new chat bound for the chat dock never shows in the strip

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

* fix(cmux-next): a new chat moves into its dock without flashing in the strip

A new chat bound for a new chat dock opens unselected and hidden from its
pane's strip (pendingDock), then shows once, in its dock, focused. A failed
move shows it where it is.

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

* refactor(cmux-next): DockRole in its own file (god-file limit)

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

* style(cmux-tui): rustfmt the merged permanent-dock test

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

* refactor(cmux-next): new chat routing lives in NewChatPlacement

openAgentTab's dock routing moves to NewChatPlacement.opening, so
PaneController stays under the 1000-line god-type limit.

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

* refactor(cmux-next): fold PaneController's chat dock notes into fewer lines

Base grew PaneController; merged with the tip it spanned 1004 lines.

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

* ci: run cmux-next again (generated files never got a runner on 01c19d2)

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

* test(cmux-next): a strip-less chat dock and a zoomed chat keep the new tab here

Cursor review on #18170: .newColumn is built for a chat dock with no
scrolling column, which the daemon never keeps (an all-docked screen
undocks), and a zoomed screen maps to its zoomed pane alone, so a zoomed
chat looked like a lone chat and docked. resolve takes a zoomed seam,
still ignored here.

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

* fix(cmux-next): chat dock routing drops the strip-less case, holds while zoomed, and opens the chat's context

Cursor review on #18170:
- The daemon never keeps a dock without a scrolling column (an
  all-docked screen undocks), so .newColumn is gone; it also dropped an
  explicit cwd and sent browsers and New Tab pages into the dock.
- A zoomed screen maps to its zoomed pane alone; nothing docks or moves
  until the zoom ends.
- A URL-less browser from the docked chat opens what the chat works on
  (#16620), in the strip pane.
- A browser a lone chat leaves when it docks is recorded for Auto
  tabs.newTabKind, like any other browser (Cursor on #18223).

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

* test(cmux-next): a dock-bound chat stays hidden across its rekey and other docks; the strip shows beside the chat dock

Cursor review on #18223:
- pendingDock holds the provisional id, and the created id only after the
  creation reply, so the strip can list the created chat in between.
- A finished dock clears every pending chat of the pane.
- hidesStrip takes a chat alone in the strip for the screen's lone chat
  even when a chat dock exists.
dockFinished is the finish seam, still clearing everything here.

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

* fix(cmux-next): a dock-bound chat stays hidden across its rekey and other docks; the strip shows beside the chat dock

Cursor review on #18223:
- The strip hides pendingDock through the store's aliases, so the tab
  that replaces a provisional chat is hidden from its first snapshot.
- dockFinished removes only that chat; other chats on their way stay
  hidden.
- The new chat's focus waits until the layout shows it outside the pane
  it moved from.
- A chat alone in the strip beside a chat dock keeps its strip.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 9, 2026
* test: the dock column keeps an agent chat role

Red: set-column-dock and move-tab-to-column drop role, identify does not
advertise dock-column-role-v1, the registry record drops it, and the app
never sends it back.

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

* feat: dock columns keep an agent chat role (dock-column-role-v1)

cmux-tui: ColumnDock gains an optional role (agent_chat), set with the pin
by set-column-dock and move-tab-to-column, journaled and restored with it,
dropped with it, and read as no role when unknown. identify advertises
dock-column-role-v1. Spec and generated bindings updated.

cmux next: DockSnapshot, the dock requests and the layout's DockColumn
carry the role; no behavior uses it yet.

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

* test(cmux-sdk): build ColumnPin with its optional role

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

* test(cmux-next): tools opened from the docked chat go to the strip

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

* feat(cmux-next): tools opened from the docked chat open in the strip

The agent chat dock (a left dock column with the agent_chat role) never
gets tabs of its own. A person's New Terminal, New Browser or Cmd-T from it
opens a tab in the scrolling strip, in the strip pane focused last; Cmd-T's
agent or page kind becomes a New Tab page there. A chat alone on its screen
moves into a new left chat dock and the new terminal or browser stays in its
pane, in one daemon commit. Chats outside the chat dock stay normal panes.

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

* fix(cmux-next): a lone New Tab page is not a chat to dock

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

* fix(cmux-next): a terminal from the docked chat lets the daemon pick its cwd

NEW-TERMINAL-INHERITS-CWD: a terminal opened from the docked agent chat
(a strip tab, or the tab a lone chat leaves when it docks) sends only an
explicit cwd and otherwise nil, so the daemon's resolver
(new-terminal-cwd-v1) decides.

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

* test(cmux-next): a lone chat on a split-tree screen sees its implicit column

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

* fix(cmux-next): a lone chat on a split-tree screen docks

ScreenLayout.column(containing:) only searches real columns, so a new
workspace's screen (one split tree) had none and New Terminal from its
lone chat opened beside the chat. LayoutScreen.column(containing:) gives
the implicit column.

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

* test(cmux-next): the chat dock shows no tab strip until it holds two tabs

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

* feat(cmux-next): the chat dock shows no tab strip until it holds two tabs

The agent chat is a docked column with no tab bar by default. The chat
dock's pane (the dock column with the agent_chat role) and a chat alone on
its screen, which docks when the first tool opens, hide their strip while
they hold one tab. The strip comes back at two tabs.

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

* test(cmux-next): a new agent chat opens in the chat dock

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

* feat(cmux-next): a new agent chat opens in the chat dock

Every new agent chat (New Agent Chat, Cmd-I on a pane, the CLI, Duplicate,
a session link) opens in the chat dock: a tab in the existing dock, else a
new left dock with the agent_chat role made from the chat once it exists.
A chat alone on its screen keeps a second chat in its pane; it docks when
the first tool opens. A New Tab page that becomes a chat stays where it is.

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

* test(cmux-next): a new chat bound for the chat dock never shows in the strip

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

* fix(cmux-next): a new chat moves into its dock without flashing in the strip

A new chat bound for a new chat dock opens unselected and hidden from its
pane's strip (pendingDock), then shows once, in its dock, focused. A failed
move shows it where it is.

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

* test(cmux-next): the chat dock cannot be split and takes only chats

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

* fix(cmux-next): the chat dock refuses splits and non-chat tabs

The agent chat dock is a docked region, not a split pane. splitRoom,
which every split, auto layout and split drop goes through, refuses
splitting it, and moving a terminal, browser or tab group into it is
refused with a localized reason. Chats still move in, and tabs already
in the dock still reorder.

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

* refactor(cmux-next): DockRole in its own file (god-file limit)

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

* style(cmux-tui): rustfmt the merged permanent-dock test

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

* refactor(cmux-next): new chat routing lives in NewChatPlacement

openAgentTab's dock routing moves to NewChatPlacement.opening, so
PaneController stays under the 1000-line god-type limit.

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

* docs(cmux-next): splitRoom's doc names the chat dock rule (AppServices line budget)

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

* fix(cmux-next): ChatDockRules imports CmuxNextDaemon for PaneModel and TabModel

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

* refactor(cmux-next): fold PaneController's chat dock notes into fewer lines

Base grew PaneController; merged with the tip it spanned 1004 lines.

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

* ci: run cmux-next again (generated files never got a runner on 01c19d2)

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

* test(cmux-next): a strip-less chat dock and a zoomed chat keep the new tab here

Cursor review on #18170: .newColumn is built for a chat dock with no
scrolling column, which the daemon never keeps (an all-docked screen
undocks), and a zoomed screen maps to its zoomed pane alone, so a zoomed
chat looked like a lone chat and docked. resolve takes a zoomed seam,
still ignored here.

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

* fix(cmux-next): chat dock routing drops the strip-less case, holds while zoomed, and opens the chat's context

Cursor review on #18170:
- The daemon never keeps a dock without a scrolling column (an
  all-docked screen undocks), so .newColumn is gone; it also dropped an
  explicit cwd and sent browsers and New Tab pages into the dock.
- A zoomed screen maps to its zoomed pane alone; nothing docks or moves
  until the zoom ends.
- A URL-less browser from the docked chat opens what the chat works on
  (#16620), in the strip pane.
- A browser a lone chat leaves when it docks is recorded for Auto
  tabs.newTabKind, like any other browser (Cursor on #18223).

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

* test(cmux-next): a dock-bound chat stays hidden across its rekey and other docks; the strip shows beside the chat dock

Cursor review on #18223:
- pendingDock holds the provisional id, and the created id only after the
  creation reply, so the strip can list the created chat in between.
- A finished dock clears every pending chat of the pane.
- hidesStrip takes a chat alone in the strip for the screen's lone chat
  even when a chat dock exists.
dockFinished is the finish seam, still clearing everything here.

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

* fix(cmux-next): a dock-bound chat stays hidden across its rekey and other docks; the strip shows beside the chat dock

Cursor review on #18223:
- The strip hides pendingDock through the store's aliases, so the tab
  that replaces a provisional chat is hidden from its first snapshot.
- dockFinished removes only that chat; other chats on their way stay
  hidden.
- The new chat's focus waits until the layout shows it outside the pane
  it moved from.
- A chat alone in the strip beside a chat dock keeps its strip.

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

* test(cmux-next): an edge drop does not split the chat dock; a workspace drop skips it

Cursor review on #18290: a drop commits the split its preview measured
(roomDecided) without splitRoom, so the chat dock split; a drop on a
workspace in the sidebar targets the screen's first pane, the chat dock
when it is on the left, which refuses it. dropSplit and
workspaceDropPane are the seams, still with the old behaviour.

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

* fix(cmux-next): edge drops and group actions do not split the chat dock; workspace drops skip it

Cursor review on #18290:
- A drop committing the split its preview measured (roomDecided) still
  asks ChatDockRules: the chat dock refuses it, for tabs and groups.
- A drop on a workspace in the sidebar lands in its first pane outside
  the chat dock, so it is not refused.
- tabGroup.moveToNewSplit refuses the chat dock, and
  tabGroup.moveToWorkspace skips it, like the drag paths.

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

* ci: retrigger after an unrelated HomeCloudLinkTests timeout

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

* ci: retrigger after an unrelated DiffSidecarProcessTests flake

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

* ci: rerun swift tests after an unrelated timeout

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants