Skip to content

cmux NEXT panes: a pane in a column with rows closes in the click's turn, never after the daemon's tree - #32907

Merged
teamleaderleo merged 3 commits into
feat-cmux-nextfrom
lane/right-pane-close-fast-rows
Nov 3, 2026
Merged

teamleaderleo merged 3 commits into
feat-cmux-nextfrom
lane/right-pane-close-fast-rows

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Leo (2026-11-03): "it's so slow to open and close idk if there's some sort of threading issue but it's nigh instant for claude and codex so we nede to be at least as fast". (2026-10-25): "closing the right pane seems be like... waiting for the right tab's closing animation to play before closing".

leo-named-fix: closing a pane never waits for anything; the close takes effect on the click.

What changed

Before this change, a pane in a column with rows (rows-v1) waited for the daemon's tree when its last tab closed or on Close Pane: removingSplitPane returned nil for hasRows, so showPaneClose had nothing to show. Now such a pane leaves in the click's turn, the same way #25163 made a column's only pane leave.

LayoutModel.removingRowPane mirrors cmux-tui's close (remove_pane_from_screen_layout, then LayoutColumn::normalize_rows and project_layout_columns):

  • the pane leaves its row's split tree, and its sibling takes its place;
  • a row the close empties leaves the column. When the top row goes, the next row becomes the top and keeps its id and height. The chain split under it goes, as normalize_rows drops it;
  • the rows that stay keep their heights (the daemon does not rescale them);
  • a column left with one row becomes a plain column (rows = [], root = that row's tree), and a lone column like that becomes the screen's split tree again unless it is a pinned dock;
  • the compat root chain is folded again from the rows: chain split ids are kept, and each ratio is the heights above over the heights through, computed in f32 as fold does. That keeps ratio(of:) and hasSameStructure equal to the echo's;
  • the agent chat dock's role is released when its top pane leaves (release_agent_chat_role). The same applies to the plain-column path.

CloseSizing.tree(in:holding:) now reads the row's tree, so even sizing equalizes a chain inside a row. A row's only pane leaves no chain to size, and the rows that stay keep their heights. successor(of:in:) picks the row sibling's first pane, then for a row's only pane the row above, else the one below. In the app the focus reducer still makes the pick.

The close-animation DESIGN.md is updated to match.

Tests (PaneCloseControllerTests, through the real controller and the daemon store's echo)

  • aRowsOnlyPaneLeavesInTheKeysTurnAndItsEchoShowsTheSame: rows beside a column. The bottom row's pane leaves, the column becomes plain, and the echo's layout == what was shown.
  • closingRowsOfALoneColumnShowWhatTheDaemonsTreeShows: three rows. The top row's close keeps rows 22 and 23 with their heights, then the bottom row's close leaves the screen as a split tree. Each echo == what was shown.
  • aPaneLeavingARowsChainEvensTheChainThroughItsEcho: even sizing inside a row. The echo keeps the ratio, and it is sent once.
  • aRowsPaneLeavesFasterThanTheDaemonsTreeBringsIt: the timing below.

Without the fix, each #require(showPaneClose()) fails, because it returns nil for a row pane. All five PaneClose suites pass at 9955ef3 (fleet step 022f196b56f713a7b1d67db5), and the CmuxNext compile check passed (job d93c8e0e9905194939551043).

Performance

metric before after delta harness or run link
close to pane gone from the shown layout, p50 daemon round trip + 2.042 ms (the echo's apply) 1.849 ms, in the click's turn the whole daemon round trip comes off aRowsPaneLeavesFasterThanTheDaemonsTreeBringsIt, 20 rounds, fleet step 022f196b56f713a7b1d67db5
same, p95 daemon round trip + 2.086 ms 1.924 ms same same

The harness times the echo's apply alone with the daemon's reply already in hand, so the "before" numbers are a lower bound: in the app the pane also waits for the close-pane round trip and the snapshot that follows it.

Risk

This changes layout state, but only the shown overlay: nothing new is sent to the daemon, and a refused close brings the pane back (cancelPaneClose). If the mirror got a daemon row rule wrong, the echo would make a one-frame structural jump. The echo-equality tests cover the row-leaves, top-row-leaves, last-two-rows and chain-sizing cases. A chain that is not the daemon's fold, or a pane in no mapped row, falls back to a plain removal or to waiting for the daemon's tree. The terminal close guard and the browser beforeunload path are untouched.

Agent session: agent session

🤖 Generated with Claude Code


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


Summary by cubic

Makes closing a pane in a column with rows take effect in the click's turn instead of waiting for the daemon's tree, so the right pane closes as fast as other panes.

  • The optimistic close now mirrors cmux-tui's normalize_rows for row panes: an emptied row leaves its column, remaining rows keep their heights, and a lone column becomes the screen's split tree. Close sizing and the focus successor read the row's tree, and the agent chat dock releases its role when its top pane leaves.
  • Layout state changes only affect the shown overlay; nothing new is sent to the daemon, and a refused close still brings the pane back.

Written for commit 9955ef3. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 3 commits November 3, 2026 02:17
The layout's optimistic close now takes a pane out of its row as cmux-tui's
tree does after the close (remove_pane_from_screen_layout, normalize_rows):
an emptied row leaves its column, the rows left keep their ids and heights,
one row left is a plain column (and a lone one the screen's split tree), and
the compat chain refolds from the heights. Close sizing and the focus
successor read the row's tree. The agent chat dock's role is released as the
daemon releases it when its top pane leaves.

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

cursor Bot commented Nov 3, 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.

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 9955ef3: 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=pr32907 -f ref=lane/right-pane-close-fast-rows -f platform=macos -f backend_mode=local

Shipped in:

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 3, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at 9955ef3

Reviewer: codex gpt-6-astra high, fast tier.

Follow-ups: 1. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward.

SHIP at 9955ef3
Visual verdict: no images; Leo tries the build

No correctness findings from reviewing the close paths against daemon normalization and snapshot mapping. Swift tests could not run because this box lacks the toolchain.

Follow-ups:

  1. [P2] [follow-up] Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/PaneCloseControllerTests.swift:368: No after screenshots are available; visually verify row collapse and focus movement in the build.

@cmux-bot cmux-bot 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.

cmux-bot approves 9955ef3: an independent review passed this exact head (router-review receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

@teamleaderleo
teamleaderleo merged commit 11dfe91 into feat-cmux-next Nov 3, 2026
163 of 178 checks passed
@teamleaderleo
teamleaderleo deleted the lane/right-pane-close-fast-rows branch November 3, 2026 03:27
@lawrencecchen

Copy link
Copy Markdown
Contributor

Swift logic follow-up (tip watcher swift-logic row, STATUS 13027 "SWIFT LOGIC CULPRITS"; lead order).

This PR raised swift_logic_lines by +74 at its merge commit 11dfe91: scripts/cmux-next/swift-logic-lines.py before and after the merge, with the same table (the tip's, c6be943). The largest declarations of the rise:

  • extension LayoutModel in Packages/macOS/CmuxNext/Sources/CmuxNextLayout/Model/LayoutModel+PaneClose.swift: +72 lines counted as Swift logic (its row counts it as logic)
  • extension LayoutModel in Packages/macOS/CmuxNext/Sources/CmuxNextLayout/Model/LayoutModel+PaneClose.swift: +1 lines counted as Swift logic (its row counts it as logic)
  • struct CloseSizing in Packages/macOS/CmuxNext/Sources/CmuxNextLayout/Model/LayoutModel+CloseSizing.swift: +1 lines counted as Swift logic (its row counts it as logic)

plans/cmux-next/layer-ownership.md says Rust owns every state, list and rule and Swift is the platform shell. Please move the new logic of this PR in a follow-up: to Rust (the crate that owns the area; the owner lists in STATUS 12147-12165 name the target per area), or at least out of the app into its package. If a declaration above is platform code (AppKit, Core Animation, OS calls, strings, tokens, DEBUG tooling), reply here and the table gets a PLATFORM row with your reason instead.

The swift-logic row is a report, not a gate: nothing blocks on this comment.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 9955ef30 Deployed Nov 3, 2026 by teamleaderleo via namespace autofix #12861
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.

2 participants