Skip to content

fix(next): carry chat tool opens into the existing right column - #29682

Merged
teamleaderleo merged 40 commits into
feat-cmux-nextfrom
lane/stranded-chat-tools
Oct 31, 2026
Merged

teamleaderleo merged 40 commits into
feat-cmux-nextfrom
lane/stranded-chat-tools

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Carries #28633 and its original commits forward onto current NEXT. Builds on merged #29684, the same-kind placement carry of #29606.

Leo: “opening up a new browser, a new terminal ... keeps opening up ... columns instead of going at the bottom ... Claude would just open it up on the right side’s bottom ... not ... in the main ... pane.”

Leo’s clarification: “just open it on the existing. you only need the rightmost bottom auto split when there’s a terminal that gets opened when there’s already a browser, and vice versa”.

Chat header Preview, Changes, and Files measure the existing right column instead of anchoring to the last header-created panel. New Terminal and New Browser from a docked chat use shared placement, preserve cwd/URL/profile, reuse a pane holding the same kind, and split below for the other kind. Header moves use the shared placement queue. The existing pure RightPanePlacement and WorkingURL helpers move unchanged into CmuxNextAgentPane; native adapters stay in the app.

Design: leo-workspace design/pane-placement.md. Risk: native creation and focus callbacks. Existing header Terminal/Browser toggle anchoring is retained. Nonblocking follow-ups: #29697 (wait for applied layout), #29698 (blank-browser address-bar focus), and #29726 (release placement when profile or cascade browser creation fails).

Validation: independent SHIP review and fleet compile including test targets passed at final head e20b361f12f8d4c6024416f0d0e2dee03c8f87b5 (compile job 5801c44c64dc618ce060804c, 185s). Seven placement/header suites passed at 4d8d629bc547cbf56b32c8081ef5f4d9fdd98b68 (2c55a2d5decb6739c40f4f0b); subsequent code changes move WorkingURL between modules, apply its namespace convention, and add an explicit placement-module import. Four display geometry tests skipped as advisory. Prepush checks pass against current NEXT without waivers.

Agent session: agent session
🤖 Generated with Claude Code

teamleaderleo and others added 30 commits October 30, 2026 01:13
Leo, 2026-10-28: "But I don't know if you want to do more splits versus just
adding more to the top tab or space pane, the top pane. I mean the right top
pane, and then you split to the bottom once so that you get the three items.
But you want four items. I don't know what the fourth item should go."

Transcript subagent and Task cards (chat.subagentPane, AgentSubagentPanes)
stacked a new split below the newest subagent pane for every subagent, with
no cap and no width floor. They now follow leo-workspace
design/pane-placement.md: an open subagent's tab is shown; the first opens in
a split right of the chat, the second in one split below it, and the third
and later are tabs in the newest subagent's pane, never another split. No
split leaves the chat under 480 pt or the right column under 420 pt; that
open is a tab instead. An unfocused tab open leaves focus where it is.

The decision is one function, AgentSubagentPanes.placement(chat:hooks:), so
it can fold into #26582's RightPanePlacement later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…cused subagent tab is shown in its pane

Review of #27789 at 1eed675 (Codex, BLOCK):
- openSubagent passed main: for every split, so in main pane layout the
  second subagent's split below became another tab in the right column.
  Only the first, chat-relative split goes through main pane layout now.
- An unfocused .tab open was created unselected, so the new subagent was
  hidden behind the one already showing. It is selected in its subagent
  pane without moving keyboard focus (selectWhenReported); in the chat's
  own pane (a narrow window) it waits in the strip so the chat stays shown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
… public API)

Fleet compile at b17ce6d: 'cannot be declared public because its type
uses an internal type' for the width floors and the width hook, under
InternalImportsByDefault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…he right already holds something

Leo, 2026-10-30: "subagent stuff... or anything we expect to open on "the
right"... if there's already a thing on the right, there should be logic for
just putting it on the lower pane of the right. that kinda split."

One rule, RightColumnPlacement.decide (CmuxNextAgentPane), now places every
open on the right of a pane, from the right column the window shows
(RightColumn.measure / RightColumn.panes):
- nothing on the right: a split right of the source;
- one pane on the right: a split below it (it keeps what it shows);
- two or more: a tab in the lowest pane, never another split;
- no split leaves the chat under 480 pt or the right column under 420 pt.
Closing the lower pane's last tab removes the pane, so the split collapses
and the next open splits below again.

Paths routed through it:
- transcript subagent / Task cards (AgentSubagentPanes, #27789's function,
  now shared and read from the window's geometry);
- side chats, /btw, Session Info sessions and transcript session cards
  (AgentSidePanes; chat.sideChat and chat.offer.open);
- links opened to the right and chats opened from Needs you (LinkOpener /
  LinkOpenPlacement gain .splitBelow, for a source that takes tabs).
A chat that is its screen's main pane keeps main pane layout's own rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
ux-owner, 2026-10-30, on Leo's "anything we expect to open on 'the right'":
in main pane layout the strip column right of the docked chat is that right
side. A chat there whose right side already holds a pane splits below it,
then fills the lower pane with tabs (links through LinkOpenPlacement.inColumn,
chat opens through openOnRight). With nothing on its right yet, main pane
layout still makes the column (dock the chat, or a tab in the strip pane).
Home's pinned dock is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
Review of #28044 at 20e433f (Codex, BLOCK, P1): RightColumn walked the
column with PaneHandlers.neighbor, whose horizontal step picks the adjacent
column's most recently focused pane, so a two-pane right column could read
as one and the next open split again or targeted the wrong pane.
RightColumnPlacement.column(beside:frames:gap:) now takes the panes whose
left edge is nearest the source's right edge and that overlap its height,
top to bottom, from the layout's navigation frames.

The review's other two P1s (main pane layout's chat opens and links skipped
the rule) were fixed at 36862fa.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…the tab

Review of #28044 at 34122a1 (Codex, BLOCK, P1): with no pane on its right,
LinkOpenPlacement returned .splitRight without the chat floor, so a 900-959 pt
source split to under 480 pt. RightColumnPlacement.decide now judges that
split too: measured too narrow, the tab opens in the source pane. A new
column (layout.linkPlacement new-column) adds width to the strip, so it keeps
opening one; an unmeasured source keeps the old behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…pane

Conflicts: #28044 replaces #27789's per-tracker placement with the shared
RightColumnPlacement, so #28044's side wins in AgentSubagentPanes,
AgentTabs, AgentTabs+Header and the placement tests, keeping #27789's two
later fixes:
- selectInPane uses selectWhenReportedKeepingFocus (review at 28ad8d1);
- main pane layout decides first when nothing is on the chat's right,
  including a chat too narrow to split (review at 84718cf): #28044's
  openOnRight already asks openInMainPane for .splitRight and .tab(nil).
SubagentMainPaneRoutingTests (from #27789) feeds rightColumn instead of
the removed paneWidth hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
… open again is found anywhere in it

Review of #28044 at bd2b918 (Codex, BLOCK, two P2s):
- RightColumn read navigationFrames, which holds the active screen only. An
  open still queued when the person switched screens read the chat's
  screen as empty, so it opened in the chat's own pane or split again
  beside panes already there. LayoutRootView.navigationFrames(ofScreenContaining:)
  reads the screen holding the pane, active or not (a hidden screen view
  keeps its geometry), and RightColumn.panes uses it.
- openWeb looked for a tab of the same page only in the pane this open
  would use. With A (upper) and B (lower) open, A again targets the lower
  pane and opened a second A. LinkOpenPlacement.reusePanes lists that pane
  first, then every pane of the right column, never the source.

Tests: RightColumnScreenTests (the chat's column survives a switch to
another screen; red with the active screen's frames) and
LinkOpenPlacementTests.aLinkOpenAgainIsFoundAnywhereInTheRightColumn
(A, B, A: A is selected in the upper pane).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…the active one

Review of #28044 at 1cfd9d1 (Codex, BLOCK, P1): LinkOpener.target measured
the right column on the chat's own screen but found the pane on its right
with PaneHandlers.neighbor, which read the active screen's frames. A link
opened from a chat on a screen the person left found no neighbour and
split again.

PaneHandlers.neighbor now reads navigationFrames(ofScreenContaining: pane),
as do the two PanePlacementRouting sites (New Pane auto layout and tiling
placement), the only other places that paired a pane's own screen layout
with the active screen's frames. On the active screen nothing changes.
RightColumnScreenTests covers the right-hand neighbour on a screen the
person left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…link reuses nothing on the right

Review of #28044 at c15e0d8 (Codex, BLOCK, two P2s):
- Two links opened before either's move landed both read a one-pane right
  column and both split below it: three panes on the right. A lower split
  under way is reserved (RightColumn.reserveBelow, released when its move
  lands or fails, 30 s backstop): until then the column reads as full, so
  an open meanwhile is a tab, in the right pane that holds the moving tab
  (RightColumn.realPane). LinkOpener and the agent split-below path
  (openOnRight) both reserve, so a link and a subagent can't race either.
  Subagent opens were already one at a time per tracker.
- LinkOpenPlacement.reusePanes reused a tab on the right even when the open
  lands in the source (Same Pane, or a source too narrow to split). It
  returns nothing then.

Tests: RightColumnPendingSplitTests (a second open during a split is a tab
in the right pane; overlapping reservations hold until the last ends) and
LinkOpenPlacementTests (Same Pane reuses nothing on the right).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…'s stand-in

Review of #28044 at a4ae195 (Codex, BLOCK, P2): while a lower split's move
was under way, RightColumn.panes listed a `pending-below:` stand-in for the
pane still moving in, and LinkOpener.target passed it on. openFolder looked
it up, found no pane and opened its terminal in the source over the chat;
DurableTabLink's fallback lost its destination the same way.

The stand-in now never leaves RightColumn: panes(beside:in:) maps it back
to the right pane (realPane) before returning, so a pending split reads as
its right pane listed twice. RightColumnPlacement still sees a full column,
and the tab goes to the pane that holds the moving tab. Every caller
(LinkOpener.target for links, folders, DurableTabLink, PaneBrowserTabOpener
and New Tab; RightColumn.measure for agent opens) gets real panes only, so
the per-call realPane mapping in LinkOpener.open and locatePane goes.

RightColumnPendingTargetTests: in a real two-pane window with a lower split
pending, LinkOpener.target names the real right pane (.pane, not a split
below and not the stand-in), DurableTabLink's existingPane resolves, and
every pane the column and its measure name exists. The test checks the
placement answer that openFolder and the fallback read, not a daemon-made
split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…rce pane

Five reviews of #28044 each found two opens that read one right column
before either landed, in a path the last fix missed (links at c15e0d8,
folders at e954bd8), or a pending split's stand-in id leaking to a caller
(a4ae195). The reservation and its stand-in pane are gone. In their place,
one entry point: RightColumn.serially(beside:_:) runs the opens beside one
source pane one at a time. Each decides its place from the column as the
open before it left it, and holds the turn until its move has landed or
failed (30 s backstop). Placement only ever sees real panes.

Every opener on the right goes through it:
- LinkOpener.open: links, files, browser tabs, New Tab picks, the diff
  page, Needs you (openRight). It decides inside its turn (place), and
  moved/moveBeside/dockChat end the turn when their move lands.
- LinkOpener.openFolder: its turn too. A split's terminal is made in the
  pane it splits and moved there, as a New Tab page's terminal already
  was, instead of a splitRight/splitDown action the turn could not see
  land. New Column still makes a split, as before.
- AgentTabStore.openOnRight: subagents, side chats, /btw, Session Info
  sessions, transcript session cards. It decides again in its turn (the
  trackers' decision is from when they asked), passes that decision to
  `created`, and ends the turn in `finished`. Wired in AgentTabs.serially.

Tests: RightColumnTurnTests (the turn; every pair of the seven opener
kinds from an empty column and from one pane on the right, both asked
before either lands, never leaves more than two panes; all of them at
once fill one column) and RightColumnEntryPointTests (a LinkOpener open
and an AgentTabStore open each wait for the open under way, then decide).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
PaneHandlers.neighbor and PanePlacementRouting's autoLayoutTarget and route
now read the frames of the pane's own screen (navigationFrames(ofScreenContaining:));
the defaults guard hashes them as tunable sources. Nothing a person gets by
default changes on the active screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
Fleet run of #28044 at 8e52036 (scratch #28392):
- SideOpenStripTests failed: its pane has no window, so its width was 0,
  and the narrow-chat floor (5ade38f) took 0 pt for too narrow to split,
  opening the link in place. RightColumn.width(of:) answers nil for a pane
  with no width yet; LinkOpener.target and RightColumn.measure use it, so
  an unmeasured pane sets no floor, as before the floor.
- RightColumnScreenTests required the chat's screen to be hidden after the
  switch, but its fade ends on later frames a headless window never draws.
  It requires the screen to be inactive instead; a hidden screen view keeps
  the same geometry, so the lookup reads it either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
…e is no width

Fleet run of #28044 at caf8e69 (scratch #28392): SideOpenStripTests still
opened its link in place. A PaneContentView starts at a 400 pt placeholder
frame, so a pane not yet laid out read as 400 pt, under the 480 pt chat
floor. caf8e69 only treated a 0 pt width as unmeasured; RightColumn.width(of:)
now answers nil for any pane that is not in a window, then for a 0 pt one.
SideOpenStripTests.aPaneNotInAWindowIsUnmeasured covers it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-0c8df2029ad2a028c4e7
Use the serialized LinkOpener placement for dock tools, before terminal tiling, while preserving working directory, browser URL and profile. Keep the New Tab page route unchanged.

Validation: git diff --check passed. verify-local portable checks passed except untouched localization debt; Swift parsing unavailable because this host has no swiftc. Fleet ChatDockToolPlacementTests remains required.
Agent-Session: https://cmux.com/deeplink/agent?id=aid-aed5521ea76b3c8830d5
…at already holds its kind

Leo, 2026-10-31: "imo in this case, just open it on the existing. you only
need the rightmost botom auto split when there's a terminal that gets opened
when there's already a browser, and vice versa".

RightColumnPlacement.decide takes the kind being opened (terminal or browser)
and which kinds each right-column pane holds: a pane already holding that kind
gets the tab; only a different kind splits below. Links (openWeb, a browser
page's Cmd-click) open as .browser, folders as .terminal. Chats, subagents,
side sessions and file pages pass no kind and place as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8451c317c43e92348de8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-8451c317c43e92348de8
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol xhigh, router-review): SHIP at 9538388

Independent delta review of 69ecf0f..9538388: the repository's mechanical namespace rewrite makes WorkingURL an instantiable struct, with unchanged static helpers. No findings.

Prior code review and review chain: #29682 (comment). The two nonblocking behavior follow-ups remain #29697 and #29698.

Visual verdict: no images supplied (range review).

Base automatically changed from lane/stranded-pane-placement to feat-cmux-next October 31, 2026 21:30
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review e20b361: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr29682 -f ref=lane/stranded-chat-tools -f platform=macos -f backend_mode=local

Default changes

This pull request changes 15 defaults at e20b361f12f8 (what everyone gets without configuring anything):

  • tunable-source.CmuxNextAgentPane/RightColumnPlacement.swift:column: (none) -> "2a6f9a4637294a3c8fcdada7548be78fe36c0385faeb39097a2091a6d0677edf"
  • tunable-source.CmuxNextAgentPane/RightColumnPlacement.swift:decide: (none) -> "2595c038c9ad673ebefe640c9109ae9f807c71eeb35bc54c7180a81a37e15afc"
  • tunable-source.CmuxNextAgentPane/RightColumnPlacement.swift:decide#: (none) -> "ad96bce8f9f2c511ca566613723ccc8fa1c8aa17cfbc008dbdd8311a42658f2e"
  • tunable-source.CmuxNextAgentPane/RightColumnPlacement.swift:minimumChatWidth: (none) -> "9d61969a82275c5ac7a5ff805a7e9594defd15e7afeee816579bb54a880b82e3"
  • tunable-source.CmuxNextAgentPane/RightColumnPlacement.swift:minimumRightColumnWidth: (none) -> "bb5f973f5de5341dd7a7d760f625ff570b6422fcb609325b1b898262605b1c38"
  • tunable-source.CmuxNextApp/Handlers/AgentBesidePlacement.swift:placed#: "d14d662b77e1bd9a079ce3e6f6634ee7777bf109a31d78435dcbd41ebbc84204" -> "d158c21552f3554cbf8131ca49ff1ef78961c09884eddd67f9813f0f2e325806"
  • tunable-source.CmuxNextApp/Handlers/ChatColumnPlacement.swift:openTool: (none) -> "e9289a1091e2675d689d4a436c2f5d8b40a783ab18cc17919d2d42c8d81226e6"
  • tunable-source.CmuxNextApp/Handlers/TabLifecycle.swift:newBrowser#: "c213c20a3e3fd5b41b00820e4bf42743c4320baac5dda03e17e7c5d5ff4d4f84" -> "289d320d916cea967ed06ddd57f6541232447a89f87021f49f0fbe6a2629f170"
  • tunable-source.CmuxNextApp/Handlers/TabLifecycle.swift:newTerminal: "c5ec6483cc1c31c25521c223e46110357ab5096ad0f360de67a2c3c4bc3f9919" -> "3a94f1e83faf49ed115e94d5ff8c9d829080482fbd8f6aeb5cc4f4628ebbfbb7"
  • tunable-source.CmuxNextApp/Handlers/TabLifecycle.swift:openBrowserFromDock: (none) -> "2b3e91de56b5c5f6a2558d5fa7d5e3825e5bac91d56fd258b6fee05498271a4e"
  • tunable-source.CmuxNextApp/Handlers/TabLifecycle.swift:openTerminalFromDock: (none) -> "08596061e78b8432b23829800b3026bc9d787df3bc9d0802349172e582327bd2"
  • tunable-source.CmuxNextApp/Links/LinkOpener.swift:holds: (none) -> "4e2f8e35812ba4b6fc6f657c405c42f746dbc471f492bc73c8cf81023aa051d8"
  • tunable-source.CmuxNextApp/Links/LinkOpener.swift:rightColumn: (none) -> "dce82f1c01bee788e5e828767cf048d588ef48ea1da25c66cc6489b90879dc4c"
  • tunable-source.CmuxNextApp/Links/LinkOpener.swift:rightPanes: (none) -> "2c20630a26f59762f5ad95b752cd5a1025090fe1954d7e6114b25d2ff9dc2f8d"
  • tunable-source.CmuxNextApp/Links/LinkOpener.swift:width: (none) -> "88921620fb240be6cd49261b3f1ba372c48fc8fb46a938b57600c3fcb01cf8a7"

Signed off on this commit by review (codex gpt-6.1-sol xhigh).

Merge receipt: merged as 065e5e3222cc with these defaults changed, signed off by review (codex gpt-6.1-sol xhigh).

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

Shipped in:

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/41247462102 at a7a448a failed: cmux-next generated files.
Those jobs last passed at 7bfa564. This pull request is one of 2 merged since: #29682, #29669.
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

Copy link
Copy Markdown
Collaborator Author

router-review: review-router runs on this host (a VM explicitly provisioned with Codex via lane-vm-bootstrap codex (GCP or Freestyle); codex-review-box check validates its runtime and SR2 route before a review); no objects pack
BLOCK at 1a39733
Visual verdict: no images supplied (range review); the visual verdict must come separately

  1. [P1][blocking] Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/TabLifecycle.swift:486 references RightColumnPlacement.Kind, but this file does not import CmuxNextAgentPane, where RightColumnPlacement is defined. Swift imports are file-scoped, so the head does not compile.

  2. [P1][follow-up] Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/TabLifecycle.swift:498-509 starts agent-profile creation asynchronously but never invokes placed or ends the opener turn when profile creation fails (or tab creation is refused). For a split placement, the outbound tab remains held and subsequent dock opens wait until the 30-second turn timeout.

router-review: BLOCK at 1a39733 (gpt-6.1-sol xhigh)

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol xhigh): SHIP at e20b361

Independent router review of the complete change against current NEXT. The explicit module import resolves the previous blocking finding.

Follow-ups:

No images supplied [follow-up]. macOS tests could not run in the review box; fleet validation is recorded in the PR body.

@teamleaderleo
teamleaderleo merged commit 065e5e3 into feat-cmux-next Oct 31, 2026
175 of 178 checks passed
@teamleaderleo
teamleaderleo deleted the lane/stranded-chat-tools branch October 31, 2026 22:00
lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…h for the split it is about (#30134)

ClickedLinkFocusTests.theHeadersPreviewBesideThePageTakesTheFocusAndItsCloseGivesItBack
failed at line 285: the Preview tab stayed in the page's own pane, which kept
the focus. It passed before #29682 (serial job 54e4c583d660 at f685985)
and fails after it on every display host.

#29682 (Leo, design/pane-placement.md) made AgentBesidePlacement place a
newColumnBeside open by RightColumnPlacement.decide instead of always moving
it to a new column: with nothing on the right it splits right only when both
halves keep minimumChatWidth (480 pt), else the tab stays in the source pane
("No split leaves the chat under 480 pt or the right column under 420 pt;
that open is a tab instead"). The restored test window's one pane is
narrower than 960 pt, so the open is now a tab in place, as designed.

The test is about a person's beside open taking the focus and its close
giving it back, so it now sizes the window to 1500 x 800 and waits until the
page's pane measures at least 2 x minimumChatWidth before it clicks Preview.
Every assertion is unchanged.

Red at the base 511033d (fleet job in the PR); green job in the PR.
lawrencecchen added a commit that referenced this pull request Nov 1, 2026
… so the dock role capability reads true (#30132)

All four ChatDockToolPlacementTests failed at openTool: it returned false.
ChatColumnPlacement.resolve checks DaemonCapability.dockColumnRole through
StateResourceQueries.supports, which reads DaemonStore.identity. That is set
by noteHandshake (since #15782, 2026-09-29), not by connectionState. The
fixture, added in #29682, set only connectionState, so the capability read
false, resolve answered .here and openTool refused. The suite never ran on a
display before it merged (#29682: "Four display geometry tests skipped as
advisory"), so it was red from its first display run.

The fixture now calls noteHandshake with the same identity, as a real connect
does and as the other app tests do (CanvasLayoutActionTests,
AgentSessionGroupPlacementTests). Every assertion is unchanged.

Red at the base 511033d (fleet job in the PR); green job in the PR.
teamleaderleo added a commit that referenced this pull request Nov 1, 2026
… the chat's right, never a third column (#29861)

* cmux NEXT: the chat header's Terminal and Browser open in the pane on the chat's right, never a third column

Leo, 2026-10-31: "do we have this fix yet wrt the thing opening up terminal
and browser" / "like having it go split instead of just adding a third
colujmn", then "imo in this case, just open it on the existing. you only need
the rightmost botom auto split when there's a terminal that gets opened when
there's already a browser, and vice versa".

Links, folders, Preview, Changes and Files already open on the chat's right by
RightColumnPlacement (#29682, #29684), but the header's Terminal (splitRight)
and Browser (splitBrowserRight) still ran Split Right beside the panel the
previous click opened (cx-qom0), so Terminal then Browser made chat | terminal
| browser. They now open through LinkOpener with besideChat and their kind
(AgentTabStore.openTool, the same path openFolder and the dock's tools use):
a split right of the chat when nothing is there, a tab in the right pane that
already holds that kind, a split below the one right pane for the other kind,
else a tab in the lowest pane. A chat no window shows keeps the split action.
The anchor lookup that chained splits (AgentChatSplitToggles.beside) goes.

Tests: HeaderSplitOrderTests (Terminal then Browser ask the right column for
a terminal and a browser, no Split Right runs), HeaderTerminalAddsTests (each
add asks the right column for a terminal). The rule itself is
RightColumnPlacementTests' same-kind cases (#29684).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-53d81bfaf9dd62794c6b

* fix(next): the header's tools leave a lone chat's and Home's dock route alone; Terminal starts in home without split inheritance

Pre-push review (subagent): openTool docked a lone chat (ChatColumnPlacement
.dockChat) on the first click, which its split never did, and a nil folder let
the daemon pick one where Split Right started in the home folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-53d81bfaf9dd62794c6b

* fix(next): the header's Browser keeps its address bar focused through the move to the right

Review of #29861 at ec04493 (Codex, BLOCK, P1): LinkOpener.move began a
newer focus intent targeting .content, which superseded newBrowserTab's
address-bar focus, so the URL could not be typed at once as with
splitBrowser. LinkOpener.open takes the focus target (focusing:) through
place and moved to move; the header's Browser passes .addressBar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-53d81bfaf9dd62794c6b

* fix(next): the header's Terminal with its daemon offline keeps the split's refusal; highlight title fits

Review of #29861 at e204276 (Codex, BLOCK, P1): the terminal make did not
check target.daemon.connection, so with the daemon offline newTerminalTab
returned without calling its placement and the right-column turn waited for its
30 s backstop. The make now refuses (false) up front: LinkOpener ends the turn
and openTool falls back to Split Right, whose refusal shows as before.

digest test: the highlight title is at most 60 characters and names a docs link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-53d81bfaf9dd62794c6b

* ci: re-run the fast tier (cancelled mid-compile at 4cc8ee2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-53d81bfaf9dd62794c6b

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — e20b361f Deployed Oct 31, 2026 by teamleaderleo via wait for same-key tree publisher #91638
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant