Skip to content

Add canonical Browser layout relocation and fix resize replay - #9235

Closed
lawrencecchen wants to merge 25 commits into
mainfrom
codex/fix-terminal-host-resize-replay
Closed

lawrencecchen wants to merge 25 commits into
mainfrom
codex/fix-terminal-host-resize-replay

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This is the cmux half of the canonical cmux-browser integration:

  • decode terminal-host Resized frames as
    cols:u16 + rows:u16 + replay_len:u32 + replay, rejecting truncated,
    oversized, and trailing data
  • publish canonical ordered niri columns, widths, split/stack trees, panes,
    mixed browser/PTY tabs, browser URLs, and stable backend IDs
  • add atomic tab/pane relocation commands for split edges, existing panes, and
    new columns
  • support exact insertion indices and nonactivating structural mutations
  • let detached frontends create a workspace with activate:false
  • preserve the owner TUI's complete stable-ID selection when an unrelated
    earlier Screen is removed or a terminal host is adopted
  • advertise canonical-layout-columns-v1,
    canonical-layout-relocation-v1, and
    independent-client-selection-v1

The resize decoder fixes attached TUIs showing a stray leading d and losing
the prompt's final space after a browser-driven resize. The old decoder fed
the replay-length prefix into Ghostty as terminal output.

State contract

cmux owns durable shared structure: workspaces, screens, niri columns and
widths, split ratios, stack membership, panes, surface identity/kind, tab
order, browser URLs, and PTY state.

Each frontend owns presentation and selection: active workspace/screen/pane/
tab, stack expansion, viewport/scroll, terminal grid size, and keyboard focus.
Non-focus structural operations preserve the complete owner-TUI focus tuple.
Closing or reordering an unrelated sibling preserves that tuple by stable ID;
only deleting the selected object may choose a fallback.

Exact revisions

  • PR head: 1239ef8a97328c6fb9a0fe16647746bfe031cf0d
  • cmux-tui/ tree: f3504ca71c96c08f589a7c0104944d28e4c2ffe7
  • merged cmux main: 541fe7f0c7d2dfa641de9c1fe1ad44f922083d1b
  • Ghostty pin: abcf5697d4fcd05e29a83ccfc090d6e234952849

Regression proof

The resize regression and fix remain split:

  1. bf714a2930 makes the consumer test use the real length-prefixed payload
    and fails before the decoder fix.
  2. de8ac6fef7 fixes decoding and makes it pass.
  3. 26651d19f9 covers truncated, trailing, and oversized frames.

Canonical relocation tests assert exact IDs and placement results while the
owner's {workspace, screen, pane, surface} focus tuple stays unchanged.
Dedicated tests cover detached workspace creation and deleting an earlier
Screen without changing that tuple.

Testing

  • cargo fmt --all -- --check: passed
  • cargo clippy -p cmux-tui-core --all-targets --locked -- -D warnings:
    passed with Zig 0.16
  • focused exact-head selection/relocation/Screen-deletion and review-regression tests: passed
  • exact-head sequential run: all topology, protocol, PTY, resize, and
    selection tests passed; 539/543 core tests passed
  • four unrelated CDP fake-server timing tests failed under controller load; two
    passed immediately in isolation, and the remaining two reproduce from the
    pre-feature checkout whose browser.rs is byte-identical
  • companion Browser suite at the exact pin: 28 passed, 0 failed
  • current GitHub checks are successful, neutral, or skipped; no failures

The final packaged Browser/TUI screenshot and interaction matrix is tracked in
cmux-browser#35.

Review trigger

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • Tests cover behavior changes
  • Protocol and frontend specs are updated
  • Current cmux main is integrated
  • Companion Browser PR pins the exact commit and tree

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds canonical layout columns, activation-aware creation and relocation APIs, placement metadata, and independent client selection. It updates protocol projections, specifications, frontend guidance, and tests. It also validates replay lengths in resized hosted frames and supports ordered splits.

Changes

Canonical layout placement and activation

Layer / File(s) Summary
Replay framing and ordered splits
cmux-tui/crates/cmux-tui-core/src/surface.rs, cmux-tui/crates/cmux-tui-core/src/model.rs
Resized frames validate explicit replay lengths and exact payload sizes. Split helpers support insertion before or after the target.
Activation-aware creation
cmux-tui/crates/cmux-tui-core/src/mux.rs
Creation APIs support exact panes, browser insertion indexes, browser or terminal surfaces, conditional activation, and placement results.
Atomic relocation and focus restoration
cmux-tui/crates/cmux-tui-core/src/mux.rs
Relocation APIs move tabs and panes across splits, merges, and columns. They preserve identity and restore focus for non-activating operations.
Protocol and canonical projections
cmux-tui/crates/cmux-tui-core/src/server.rs, cmux-tui/docs/protocol.md, cmux-tui/spec/commands.md, cmux-tui/spec/frontends.md
The protocol advertises canonical layout, relocation, and independent-selection capabilities. Commands accept placement and activation parameters, return placement metadata, and expose canonical columns.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error New Rust mux/server English errors are serialized as API response.error and surfaced raw by the TypeScript client/UI; the PR adds no locale-specific entries. Return stable error codes and localize client-facing text through next-intl; add matching entries to all 20 files in web/messages/.
✅ Passed checks (24 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed The PR range changes only Rust and documentation files; it introduces no production Swift changes or actor-isolation declarations.
Cmux Swift Blocking Runtime ✅ Passed The PR diff from advertised cmux main 541fe7f to HEAD contains only Rust and Markdown files; it introduces no production Swift changes to assess.
Cmux Browser Automation Off-Main ✅ Passed The PR changes only cmux-tui Rust/docs/spec files; the Swift controller, worker policy, and policy tests are unchanged, so no browser automation routing change is introduced.
Cmux Expensive Synchronous Load ✅ Passed The PR diff contains only Rust (.rs) and Markdown (.md) files; it adds no production Swift changes or synchronous agent-history loads.
Cmux Cache Substitution Correctness ✅ Passed The complete PR diff contains only four Rust and three Markdown files; it contains no production Swift, TypeScript, or JavaScript changes, so this check is not applicable.
Cmux No Hacky Sleeps ✅ Passed The full PR diff changes only Rust source and Markdown specs; it adds no TypeScript, JavaScript, shell, build/runtime files, or fixed sleep/timer patterns.
Cmux Algorithmic Complexity ✅ Passed The diff changes Rust runtime and docs only; added traversals are linear per target/layout, with no new nested batch rescans or sorting. Existing O(n²) helper code is unchanged.
Cmux Swift Concurrency ✅ Passed The PR-wide diff from merge base 541fe7f to HEAD changes only Rust and documentation files; no cmux-owned Swift code or legacy Swift concurrency pattern was introduced.
Cmux Swift @Concurrent ✅ Passed The PR changes only Rust and Markdown files; no Swift file or Swift concurrency code changed, so this check is not applicable.
Cmux Swift Package Boundaries ✅ Passed The PR merge-base diff contains no Swift files; all changed paths are Rust or documentation, so the Swift package-boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed The PR changes only seven cmux-tui Rust and specification files; no Package.swift, Package.resolved, Xcode project, .gitignore, workflow, or dependency paths changed.
Cmux Swift Logging ✅ Passed The PR changes only Rust and documentation files; no Swift files or Swift logging statements are added or materially changed.
Cmux User-Facing Error Privacy ✅ Passed Added runtime errors use generic cmux terms; the raw PTY diagnostic predates the PR, and vendor/internal names appear only in allowed docs or tests.
Cmux Swiftui State Layout ✅ Passed The PR diff contains only Rust and Markdown files; it has no Swift or SwiftUI changes, so this check is not applicable.
Cmux Architecture Rethink ✅ Passed The PR diff from the stated cmux base changes four Rust files and three Markdown files, with no Swift paths or Swift architecture changes.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The stated base-to-head diff contains only Rust, Markdown, and specification files, with no Swift changes or standalone cmux-owned windows to assess.
Cmux Source Artifacts ✅ Passed All seven changed paths are Rust source/tests or protocol/spec documentation; no artifact directories, media, logs, caches, build output, or dependency checkouts enter the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The PR range changes 7 files, all Rust or Markdown; it adds no Swift file under any production Sources path.
Cmux No Ambient Global State ✅ Passed The check applies only to production Swift changes; this PR changes Rust and Markdown files, with no Swift paths in the inspected commit.
Title check ✅ Passed The title clearly summarizes the canonical browser layout relocation changes and the resize replay fix.
Description check ✅ Passed The description provides a detailed summary, testing evidence, review trigger, and checklist, but omits the optional demo video.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-terminal-host-resize-replay

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmux-tui/crates/cmux-tui-core/src/surface.rs`:
- Around line 3992-3995: Add regression cases alongside the existing valid VT
replay fixture in the relevant surface test: assert rejection for truncated
length-prefixed data, payloads with trailing bytes, and replay lengths exceeding
VT_REPLAY_MAX_BYTES. Reuse the existing replay parsing/validation entry point
and preserve the current valid-payload assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 77b9e525-ff6c-4e9b-91ae-4732319cd770

📥 Commits

Reviewing files that changed from the base of the PR and between b86cce2 and de8ac6f.

📒 Files selected for processing (1)
  • cmux-tui/crates/cmux-tui-core/src/surface.rs

Comment thread cmux-tui/crates/cmux-tui-core/src/surface.rs Outdated
@lawrencecchen lawrencecchen changed the title Fix terminal-host resize replay corruption Add canonical Browser layout relocation and fix resize replay Jul 31, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cmux-tui/crates/cmux-tui-core/src/server.rs (2)

3468-3518: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

New Tab url field is undocumented in the canonical schema. pane_json in server.rs now serializes a url field for every tab, but the Tab schema in commands.md (the canonical schema referenced by protocol.md and frontends.md) does not list it.

  • cmux-tui/crates/cmux-tui-core/src/server.rs#L3468-L3518: this is the producer change; no action needed here beyond confirming the field is intentional and stable.
  • cmux-tui/spec/commands.md#L107-L131: add url:string|null to the Tab object schema so frontends relying on this canonical reference know the field exists.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmux-tui/crates/cmux-tui-core/src/server.rs` around lines 3468 - 3518,
Document the existing tab url field in cmux-tui/spec/commands.md at lines
107-131 by adding url:string|null to the canonical Tab schema;
cmux-tui/crates/cmux-tui-core/src/server.rs lines 3468-3518 requires no direct
change because pane_json already emits the intentional stable field.

3468-3518: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

New url Tab field is not reflected in the documented Tab schema.

Line 3494 adds "url": surface.and_then(|s| s.browser_url()) to every tab entry. commands.md's Tab object schema does not list a url field. See the consolidated comment for the required documentation update.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmux-tui/crates/cmux-tui-core/src/server.rs` around lines 3468 - 3518, The
tab JSON produced by pane_json now includes a url field, so update the
documented Tab object schema in commands.md to declare this field with its
nullable browser URL type and semantics. Keep the documentation aligned with the
existing browser-related tab fields and the runtime output from pane_json.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmux-tui/crates/cmux-tui-core/src/model.rs`:
- Around line 532-546: Add a `Node::Stack` case to
`ordered_split_places_a_new_leaf_on_the_requested_side` that invokes
`split_leaf_ordered` on a stack target and verifies the resulting split places
the new leaf on the requested side while preserving the stack node on the other
side.

In `@cmux-tui/crates/cmux-tui-core/src/mux.rs`:
- Around line 8396-8414: Update move_pane_to_new_column_with_activation so the
relocated column always applies the requested width, including when
detach_pane_layout_for_relocation returns an existing column; preserve the
existing width only when no requested override is applicable. Extend the
existing test around the 0.7-width case to assert that the resulting column
width is 0.7.
- Around line 5563-5591: Extract the duplicated viewport-width bounds check into
a free helper named validate_viewport_width returning Result<(),
ViewportWidthError>. Replace the inline validation in
new_pane_right_with_activation, new_browser_pane_right_with_activation,
move_tab_to_new_column_with_activation, move_pane_to_new_column_with_activation,
and set_viewport_pane_width_inner with calls to this helper, preserving the
existing error propagation and behavior.
- Around line 8886-8902: Update both root-removal branches in the surrounding
detach function so a failed remove_leaf does not leave Node::Leaf(0) installed.
In the layout-column branch and the screen-root branch, only assign the returned
root after removal succeeds, or restore the original root before returning None;
preserve the existing successful removal behavior and cleanup.
- Around line 4897-4901: Update the target selection logic around target_pane to
fail when an explicitly requested pane is no longer owned by workspace wi,
rather than falling back to active_screen_ref().active_pane. Preserve the
active-pane fallback only when no target_pane was requested, and return the
existing invalid-target/error result for a missing or moved requested pane.
- Around line 8325-8352: In the relocation block, validate the target pane and
its destination column before calling detach_pane_layout_for_relocation, using
the same layout branch later used for insertion. After this prevalidation, treat
a failed split_leaf_ordered result as an invariant violation rather than
returning a recoverable “target pane disappeared” error, so detachment cannot
leave the source unreachable; preserve the existing successful layout and zoom
updates.

In `@cmux-tui/crates/cmux-tui-core/src/server.rs`:
- Around line 5295-5318: The NewPaneRight and Split command handlers misreport a
supplied URL with kind "pty" as an invalid pane kind. Update both match flows to
handle this combination explicitly and return a user-facing error stating that
url is only valid with kind "browser", including a concrete correction such as
removing url or changing kind; preserve the existing PTY and browser creation
behavior for valid inputs.
- Around line 4969-4991: Enforce the documented pane requirement in the no-pane
branch of Command::NewBrowserTab: reject activate:false before calling
mux.new_browser_tab, analogous to the existing index.is_some() validation.
Preserve the current default activating behavior when activate is omitted or
true, and keep the pane-provided path unchanged.

In `@cmux-tui/spec/commands.md`:
- Around line 2622-2647: Escape or otherwise rewrite the raw pipe characters in
the dir parameter descriptions for move-tab-to-split and move-pane-to-split so
each Markdown table row retains exactly three columns. Follow the existing file
convention of describing the alternatives without an unescaped pipe, while
preserving the accepted right/down values.

---

Outside diff comments:
In `@cmux-tui/crates/cmux-tui-core/src/server.rs`:
- Around line 3468-3518: Document the existing tab url field in
cmux-tui/spec/commands.md at lines 107-131 by adding url:string|null to the
canonical Tab schema; cmux-tui/crates/cmux-tui-core/src/server.rs lines
3468-3518 requires no direct change because pane_json already emits the
intentional stable field.
- Around line 3468-3518: The tab JSON produced by pane_json now includes a url
field, so update the documented Tab object schema in commands.md to declare this
field with its nullable browser URL type and semantics. Keep the documentation
aligned with the existing browser-related tab fields and the runtime output from
pane_json.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db66d518-811c-4b20-b29e-2b6d4f7886b4

📥 Commits

Reviewing files that changed from the base of the PR and between 26651d1 and 45bea4b.

📒 Files selected for processing (6)
  • cmux-tui/crates/cmux-tui-core/src/model.rs
  • cmux-tui/crates/cmux-tui-core/src/mux.rs
  • cmux-tui/crates/cmux-tui-core/src/server.rs
  • cmux-tui/docs/protocol.md
  • cmux-tui/spec/commands.md
  • cmux-tui/spec/frontends.md

Comment thread cmux-tui/crates/cmux-tui-core/src/model.rs
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/server.rs
Comment thread cmux-tui/crates/cmux-tui-core/src/server.rs
Comment thread cmux-tui/spec/commands.md Outdated

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmux-tui/crates/cmux-tui-core/src/server.rs (1)

5462-5489: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

move-tab reports success even when the underlying move fails.

mux.move_tab_with_activation returns a bool (moved or not), but the return value is discarded at Line 5471. If the target pane disappears between the earlier valid check and the actual move (a real race, since valid is checked without holding the workspace lock, while the move itself re-validates under a lifecycle lock), move_tab_with_activation returns false and nothing moves. The handler still looks up state.pane_of(surface) afterward and returns that as a successful placement, so the response reports "ok":true with the tab's original pane instead of an error, even though the requested move to pane never happened.

Distinguishing this from the legitimate same-position no-op (which also returns false but leaves the surface already at the requested pane) is possible by comparing the resolved placement's pane against the requested pane rather than by checking the raw boolean.

🐛 Proposed fix
             mux.move_tab_with_activation(surface, pane, index, activate.unwrap_or(true));
             let placement = mux
                 .with_state(|state| {
                     let pane = state.pane_of(surface)?;
                     let (workspace_index, screen_index) = state.screen_of(pane)?;
                     Some((
                         pane,
                         state.workspaces[workspace_index].screens[screen_index].id,
                         state.workspaces[workspace_index].id,
                     ))
                 })
                 .ok_or_else(|| anyhow::anyhow!("moved surface has no placement"))?;
+            if placement.0 != pane {
+                anyhow::bail!("unknown surface/pane");
+            }
             Ok(json!({
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmux-tui/crates/cmux-tui-core/src/server.rs` around lines 5462 - 5489, Update
the Command::MoveTab handler to verify the resolved placement after
mux.move_tab_with_activation against the requested pane. Return an error when
placement.0 differs from pane, while preserving success for same-position no-ops
where the surface is already in the requested pane; do not rely solely on the
discarded boolean result.
♻️ Duplicate comments (1)
cmux-tui/crates/cmux-tui-core/src/mux.rs (1)

5588-5608: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated viewport-width bounds check into one helper.

new_pane_right_with_activation and new_browser_pane_right_with_activation both inline the same !width.is_finite() || !(MIN_VIEWPORT_PANE_WIDTH..=MAX_VIEWPORT_PANE_WIDTH).contains(&width) check. The same check also appears in set_viewport_pane_width_inner (Line 7086), move_tab_to_new_column_with_activation (Line 8167), and move_pane_to_new_column_with_activation (Line 8404). Five copies of one bound rule will diverge over time.

This finding is unchanged from the prior review round.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmux-tui/crates/cmux-tui-core/src/mux.rs` around lines 5588 - 5608, Extract
the repeated viewport-width validation into a shared helper near the existing
viewport-width logic, returning the established ViewportWidthError for
non-finite or out-of-range values. Replace the inline checks in
new_pane_right_with_activation, new_browser_pane_right_with_activation,
set_viewport_pane_width_inner, move_tab_to_new_column_with_activation, and
move_pane_to_new_column_with_activation with calls to that helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmux-tui/crates/cmux-tui-core/src/server.rs`:
- Around line 5462-5489: Update the Command::MoveTab handler to verify the
resolved placement after mux.move_tab_with_activation against the requested
pane. Return an error when placement.0 differs from pane, while preserving
success for same-position no-ops where the surface is already in the requested
pane; do not rely solely on the discarded boolean result.

---

Duplicate comments:
In `@cmux-tui/crates/cmux-tui-core/src/mux.rs`:
- Around line 5588-5608: Extract the repeated viewport-width validation into a
shared helper near the existing viewport-width logic, returning the established
ViewportWidthError for non-finite or out-of-range values. Replace the inline
checks in new_pane_right_with_activation,
new_browser_pane_right_with_activation, set_viewport_pane_width_inner,
move_tab_to_new_column_with_activation, and
move_pane_to_new_column_with_activation with calls to that helper.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0ddf38b2-73ed-42f1-840b-e0375301b5fe

📥 Commits

Reviewing files that changed from the base of the PR and between 45bea4b and e2d1fa3.

📒 Files selected for processing (5)
  • cmux-tui/crates/cmux-tui-core/src/mux.rs
  • cmux-tui/crates/cmux-tui-core/src/server.rs
  • cmux-tui/docs/protocol.md
  • cmux-tui/spec/commands.md
  • cmux-tui/spec/frontends.md

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cmux-tui/crates/cmux-tui-core/src/mux.rs (2)

4969-4994: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Gate stamp_pane_focus with activate when materializing the first screen.

In this branch of create_terminal_in_workspace_impl, when the target workspace has no active pane (first screen materialization), stamp_pane_focus(self, &mut state, pane_id) at line 4973 runs unconditionally. This ignores the activate parameter.

Compare this to new_screen_with_cwd (lines 4472-4480), where the identical call is correctly gated: if activate { stamp_pane_focus(self, &mut state, pane_id); }. Here, an activate:false request still steals the daemon's global active_workspace/active_pane and stamps focus/focused_at, even though attached.2 (selection_resync) is later set to false for this branch, hiding the state mutation from the emitted event while still corrupting the underlying selection state.

A detached frontend creating a terminal with activate:false in a previously empty workspace will unexpectedly steal the owner TUI's active workspace/pane selection, contradicting this PR's stated goal ("Allows detached frontends to create workspaces without activating them").

🐛 Proposed fix
             } else {
                 let (pane_id, pane) = self.make_pane(surface.id);
                 let screen_id = self.next_id();
                 state.insert_pane(pane);
-                stamp_pane_focus(self, &mut state, pane_id);
+                if activate {
+                    stamp_pane_focus(self, &mut state, pane_id);
+                }
                 state.workspaces[wi].screens.push(Screen {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmux-tui/crates/cmux-tui-core/src/mux.rs` around lines 4969 - 4994, In the
first-screen materialization branch of create_terminal_in_workspace_impl, gate
stamp_pane_focus(self, &mut state, pane_id) on the activate parameter, matching
new_screen_with_cwd. Preserve the existing pane and screen creation flow while
ensuring activate:false does not modify global workspace or pane focus state.

8052-8058: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Select the projection target with activate:true.

project_terminal_to_workspace_at_in_state moves the terminal into the destination, but then restore_focus_identity(previous_focus) restores the original workspace/screen/pane because only focus.surface becomes None. For activate:true, update active focus to the destination, for example by stamping the destination pane or applying the same active-workspace/screen/pane target logic used by the other activation paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmux-tui/crates/cmux-tui-core/src/mux.rs` around lines 8052 - 8058, Update
the activate branch in project_terminal_to_workspace_at_in_state so
preserved_focus targets the destination workspace, screen, and pane rather than
only clearing focus.surface. Reuse the existing destination-focus or activation
logic used by other activate paths, then pass that adjusted focus to
restore_focus_identity; preserve the current behavior when activate is false.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmux-tui/crates/cmux-tui-core/src/mux.rs`:
- Around line 4969-4994: In the first-screen materialization branch of
create_terminal_in_workspace_impl, gate stamp_pane_focus(self, &mut state,
pane_id) on the activate parameter, matching new_screen_with_cwd. Preserve the
existing pane and screen creation flow while ensuring activate:false does not
modify global workspace or pane focus state.
- Around line 8052-8058: Update the activate branch in
project_terminal_to_workspace_at_in_state so preserved_focus targets the
destination workspace, screen, and pane rather than only clearing focus.surface.
Reuse the existing destination-focus or activation logic used by other activate
paths, then pass that adjusted focus to restore_focus_identity; preserve the
current behavior when activate is false.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 46f6f01d-7d64-4020-9a89-b7fe7367ebf5

📥 Commits

Reviewing files that changed from the base of the PR and between e2d1fa3 and e2ff898.

📒 Files selected for processing (3)
  • cmux-tui/crates/cmux-tui-core/src/mux.rs
  • cmux-tui/docs/protocol.md
  • cmux-tui/spec/frontends.md

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f46686b896

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
cmux-lawrence added 3 commits July 31, 2026 20:26
…st-resize-replay

# Conflicts:
#	cmux-tui/crates/cmux-tui-core/src/mux.rs
#	cmux-tui/crates/cmux-tui-core/src/server.rs

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3604b88df1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/server.rs
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
Comment thread cmux-tui/crates/cmux-tui-core/src/mux.rs Outdated
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants