Skip to content

mux: server/client control commands (ping, reload-config, window-title, scroll-changed) - #7604

Merged
lawrencecchen merged 2 commits into
mainfrom
feat-mux-server-control
Jul 8, 2026
Merged

lawrencecchen merged 2 commits into
mainfrom
feat-mux-server-control

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Adds four control commands and one event to the cmux-mux protocol (v6, additive), clean-room (no third-party source consulted).

  • ping — { ok, version, protocol } liveness probe, lighter than identify.
  • reload-config — re-reads mux.json (config::load()) and live-applies theme/colors, tab and sidebar settings, scrollbar, and keybindings to the running TUI through the existing event loop (no timers); a headless server treats it as a no-op. Fields only read at startup still need a restart (documented).
  • set-window-title / clear-window-title — write OSC 0/2 to the local terminal and each attached client's own terminal (control chars sanitized); display-only, no focus/selection change.
  • scroll-changed event — { surface, offset, at_bottom }, emitted from the shared scroll/viewport helpers and coalesced; subscribe streams get all, attach streams get their surface.

Server + CLI verbs + Python binding (conformance dispatches through it) + a new conformance fixture + spec updates (commands.md/cli.md/events.md).

Verification note: local build was blocked by a host zig/ghostty toolchain regression (a libSystem link failure unrelated to this change — the same clean-cache build fails on main too on this machine). cargo fmt is clean and the JSON/Python parse; the Rust build + tests are validated here by CI (test-linux/macos, bindings-e2e, valgrind). Reviewed by the /fable judge for code-level correctness.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Touches control-socket protocol, event fan-out, and scroll/input paths across server and TUI; changes are additive but affect attach/subscribe clients and live config reload behavior.

Overview
Protocol v6 (additive) adds ping, reload-config, set-window-title, and clear-window-title, plus mux events config-reload-requested, window-title-requested, and scroll-changed (surface, offset, at_bottom).

Server behavior: Control commands emit the corresponding mux events; window titles go out as sanitized OSC 0/2. PTY scroll moves are centralized on Surface::scroll_delta / scroll_to_bottom (including PTY reader output), so subscribe and attach streams get consistent viewport notifications. surface_options is mutex-protected with update_surface_options so reload-config can refresh browser launch settings for new surfaces.

TUI: Handles reload and window-title events (reload via config::load() and apply_config; title via stdout OSC). Scrolling and key input use the shared surface scroll helpers instead of direct terminal mutation.

Bindings & docs: Python client and conformance runner dispatch the new commands; a server-client-control fixture exercises ping and title verbs. CLI verbs, integration tests, and commands.md / events.md / cli.md are updated for v6.

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


Summary by cubic

Add server/client control commands to the cmux-mux protocol (v6, additive): ping, reload-config, and set/clear window title, plus a scroll-changed event. Includes server, CLI verbs, Python bindings, and spec updates for live config reload and viewport change notifications.

  • New Features
    • Protocol: ping returns { ok, version, protocol }. reload-config emits config-reload-requested so frontends re-read config. set-window-title/clear-window-title emit window-title-requested and update the terminal window title (OSC 0/2, sanitized). New scroll-changed event carries { surface, offset, at_bottom }.
    • TUI: Live-applies theme/colors, tabs, sidebar, scrollbar, and keybindings on reload-config. Applies browser settings to future surfaces. Writes sanitized OSC 0/2 on window title requests. Headless servers no-op.
    • Scrolling: Unified helpers emit scroll-changed on user scroll, programmatic scroll, input snap-to-bottom, and output-driven moves. Subscribe streams get all; attach streams get their surface only.
    • CLI: New verbs ping, reload-config, set-window-title, clear-window-title. ping prints cmux-mux version=<version> protocol=<protocol>.
    • Bindings/Docs: Python adds ping, reload_config, set_window_title, clear_window_title and event fields offset/at_bottom. Conformance fixture and commands.md/events.md/cli.md updated to protocol v6.

Written for commit 07589e4. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added control commands: ping, reload-config, set-window-title, and clear-window-title.
    • Added support for new protocol v6 events: scroll-changed, config-reload-requested, and window-title-requested for attach/subscribe streams.
  • Bug Fixes
    • Improved scroll tracking and event consistency, including accurate offset and at_bottom reporting.
  • Documentation
    • Updated CLI verb table and command/event protocol documentation to protocol v6.
  • Tests
    • Extended CLI and conformance coverage for the new commands and event behavior.

…e, scroll-changed)

Adds four control commands and one event to the cmux-mux protocol
(protocol stays v6, additive), clean-room:

- ping: { ok, version, protocol } liveness probe, distinct from identify.
- reload-config: re-reads mux.json via config::load() and live-applies
  theme/colors, tabs, sidebar, scrollbar, and keybindings to the running
  TUI through the existing event loop (no timers); headless is a no-op.
- set-window-title / clear-window-title: write OSC 0/2 to the local and
  each attached client's own terminal (sanitized), no focus change.
- scroll-changed event: { surface, offset, at_bottom }, emitted from the
  shared scroll/viewport helpers, coalesced; subscribe gets all, attach
  gets its surface.

Server + CLI verbs + Python binding + conformance fixture + spec.

NOTE: local verification was blocked by a host zig/ghostty toolchain
regression (libSystem link failure, unrelated to this change); fmt is
clean and the fixtures/python parse, but the Rust build/tests must be
validated by CI (test-linux/macos/bindings-e2e).
@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.

@vercel

vercel Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 8, 2026 7:09pm
cmux-staging Building Building Preview, Comment Jul 8, 2026 7:09pm

@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds protocol v6 control commands, structured scroll-changed events, runtime surface-option updates, and corresponding changes in mux-tui, Python bindings, conformance fixtures, and protocol docs.

Changes

Protocol v6 Commands and Scroll Events

Layer / File(s) Summary
Core scroll state and shared options
mux/crates/mux-core/src/mux.rs, mux/crates/mux-core/src/surface.rs
Updates MuxEvent::ScrollChanged, changes surface_options to Mutex<SurfaceOptions>, adds update_surface_options, stores a Weak<Mux> in PtySurface, and emits scroll-change events from PTY output and scroll operations.
Core command handlers and event streams
mux/crates/mux-core/src/server.rs
Adds Ping, ReloadConfig, SetWindowTitle, and ClearWindowTitle, emits the new control events, maps scroll-changed through attach/subscribe streams, and adds server tests.
TUI session and app event handling
mux/crates/mux-tui/src/app.rs, mux/crates/mux-tui/src/session/mod.rs, mux/crates/mux-tui/src/session/remote.rs
Handles config reload and window title events, adds scroll helpers on SurfaceHandle, and forwards the new remote event types.
TUI CLI verbs and browser config mapping
mux/crates/mux-tui/src/cli.rs, mux/crates/mux-tui/src/config.rs, mux/crates/mux-tui/src/main.rs, mux/crates/mux-tui/tests/cli.rs
Adds CLI verbs and output formatting for the new commands, centralizes browser SurfaceOptions mapping, updates usage text, and extends CLI coverage.
Python bindings and conformance fixtures
mux/bindings/python/cmux/client.py, mux/bindings/conformance/runner.py, mux/bindings/conformance/fixtures.json
Adds typed command results, new client methods, scroll event fields, conformance dispatch support, and a new fixture exercising the new commands.
Protocol v6 documentation
mux/spec/cli.md, mux/spec/commands.md, mux/spec/events.md
Updates the command and event specs for protocol v6, the new verbs, and the new scroll and control events.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Server
  participant Mux
  participant App
  Client->>Server: Command::ReloadConfig
  Server->>Mux: emit ConfigReloadRequested
  Mux-->>App: MuxEvent::ConfigReloadRequested
  App->>App: reload_config()
  App->>App: write_window_title(title)
  Client->>Server: Command::SetWindowTitle{title}
  Server->>Mux: emit WindowTitleRequested
  Mux-->>App: MuxEvent::WindowTitleRequested(title)
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description summarizes the changes well, but it omits the required template sections for Testing, Demo Video, Review Trigger, and Checklist. Add the missing template sections and fill in testing, demo video if applicable, the review-trigger comment, and checklist items.
✅ Passed checks (24 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: new control commands and the scroll-changed event.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 diff only touches Rust files; no Swift files or actor-isolation-related Swift changes are introduced.
Cmux Swift Blocking Runtime ✅ Passed No Swift files are changed; the blocking-runtime rule applies only to non-test Swift code, so this PR does not trigger it.
Cmux Browser Automation Off-Main ✅ Passed PR diff only changes mux-tui/src/app.rs scroll handling; it doesn’t touch the Swift control-socket browser router or add any browser.* wait command routing.
Cmux Expensive Synchronous Load ✅ Passed No Swift files were changed in the referenced PR commit; only Rust/JSON/Python/docs paths were modified, so the Swift sync-load rule doesn't apply.
Cmux Cache Substitution Correctness ✅ Passed PR touches Rust/Python/JSON only; no Swift/TypeScript/JavaScript cache-substitution path is present, so the rule is not applicable.
Cmux No Hacky Sleeps ✅ Passed The diff only changes Rust/Python/docs/json files; no TypeScript/JavaScript/shell/build-runtime scripts are touched, so the rule doesn’t apply.
Cmux Algorithmic Complexity ✅ Passed Added commands/events are constant-time dispatch and single-pass scroll checks; no nested scans, per-target rescans, or repeated sorts/filtering in hot paths.
Cmux Swift Concurrency ✅ Passed PR diff has 16 changed files and 0 Swift files, so the Swift concurrency modernization check doesn’t apply.
Cmux Swift @Concurrent ✅ Passed No Swift files are changed in this PR diff, so the Swift concurrency annotation rule is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed No Swift files were changed in this diff; only a Rust app file was touched, so the Swift boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed Diff only changes mux/crates/mux-tui/src/app.rs; no SwiftPM/Xcode/.gitignore/workflow/dependency files were touched, so the lockfile rule isn’t implicated.
Cmux Swift Logging ✅ Passed No Swift source files were changed in this PR, so the Swift logging rule does not apply.
Cmux User-Facing Error Privacy ✅ Passed New user-facing strings are generic (ping/version, config reload, title OSC) and I found no exposed vendor, secret, or raw upstream details.
Cmux Full Internationalization ✅ Passed The only changed line is a non-user-facing Rust boolean simplification; no localized text, catalogs, or locale files were added or modified.
Cmux Swiftui State Layout ✅ Passed Only mux/crates/mux-tui/src/app.rs changed, and it’s a trivial Rust boolean simplification; no SwiftUI, @Observable/@published, GeometryReader, or layout-state changes.
Cmux Architecture Rethink ✅ Passed No Swift files are changed in this diff; the Swift architectural rethink rule is not applicable.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No Swift or window-related files changed; the PR only touches Rust, so the auxiliary-window close-shortcut rule is not applicable.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source, docs, specs, or fixtures; no local-generated artifacts or scratch dirs appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No production Swift Sources/** files are changed in this diff; the only modified file is Rust, so the Swift test/debug-seam rule is not applicable.
Cmux No Ambient Global State ✅ Passed PASS: The PR diff contains no Swift files; only Rust/Python/docs changes, so the no-ambient-global-state Swift rule doesn’t apply.
✨ 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 feat-mux-server-control

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.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds new cmux mux control commands and scroll events. The main changes are:

  • New ping, reload-config, set-window-title, and clear-window-title protocol commands.
  • New scroll-changed, config reload, and window title events for clients.
  • TUI handling for live config reloads, terminal title writes, and scroll state updates.
  • CLI, Python binding, conformance fixture, tests, and protocol docs updates.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
mux/crates/mux-core/src/server.rs Adds command handling, event forwarding, window title OSC helpers, scroll command wiring, and focused tests.
mux/crates/mux-core/src/surface.rs Adds scroll position tracking and emits scroll change events from PTY output and scroll helpers.
mux/crates/mux-tui/src/app.rs Handles config reload and window title events, and routes TUI scrolling through shared helpers.
mux/crates/mux-tui/src/session/mod.rs Adds config application for local sessions and shared scroll helper methods for surface handles.
mux/crates/mux-tui/src/session/remote.rs Parses the new config reload, window title, and scroll change events from remote sessions.
mux/bindings/python/cmux/client.py Adds Python binding support for the new commands and scroll event fields.

Reviews (2): Last reviewed commit: "mux: fix clippy needless-bool in scroll-..." | Re-trigger Greptile

Comment on lines +879 to +881
Command::SetWindowTitle { title } => {
mux.emit(MuxEvent::WindowTitleRequested(title));
Ok(json!({}))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Title Event Has No Receiver

When set-window-title is invoked from the non-streaming CLI against a headless server, this branch only broadcasts WindowTitleRequested and then returns {}. With no TUI or subscribe client present, no process writes the OSC sequence, so the command exits successfully while the terminal title never changes.

Comment on lines +496 to +502
.flatten()
.unwrap_or(0);
Some(before != after)
}
SurfaceHandle::Remote(surface, _) if surface.kind == SurfaceKind::Pty => {
let mut term = surface.term.lock().unwrap();
let before = term.scrollbar().map(|sb| sb.offset).unwrap_or(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Remote Scroll Skips Server State

For remote PTY surfaces, this branch mutates only the client's local terminal mirror and never sends the scroll through the server. The new scroll-changed event is then missing for other subscribers, and the server-side viewport can differ from the viewport the remote TUI is showing.

Comment on lines +1007 to 1008
surface.scroll_to_bottom()?;
surface.try_with_terminal(|term| {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Send Uses Split Snapshots

The send path now scrolls to bottom, releases the terminal lock, and reacquires it later for encoder.sync_from_terminal(term). On an active PTY, output can arrive between those two steps, so the viewport event and the encoded input can be based on different terminal snapshots.

@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: 2

🤖 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 `@mux/bindings/conformance/runner.py`:
- Around line 192-195: The `clear-window-title` entry in `runner.py` uses a
redundant lambda wrapper around `client.clear_window_title`, which also
introduces an unused `kw` parameter warning. Update the mapping alongside
`ping`, `reload-config`, and `set-window-title` so `clear-window-title` points
directly to `client.clear_window_title` without any wrapper.

In `@mux/crates/mux-core/src/server.rs`:
- Around line 825-849: The `spawn_attach_notification_stream` and `Subscribe`
paths are duplicating the `MuxEvent` to JSON mapping for `Notification` and
`ScrollChanged`, which can drift over time. Extract a shared helper like
`mux_event_json(&MuxEvent) -> Option<Value>` (or per-variant helpers such as
`scroll_changed_json`/`notification_json`) in `server.rs`, and have both the
attach stream and the subscribe handler call it so the JSON shape stays
consistent; keep the attach stream’s `surface_id` filtering separate from the
shared serialization logic.
🪄 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

Run ID: e30ce9c5-3cda-43b4-91c0-757363974078

📥 Commits

Reviewing files that changed from the base of the PR and between 82a1106 and cda3d5e.

📒 Files selected for processing (16)
  • mux/bindings/conformance/fixtures.json
  • mux/bindings/conformance/runner.py
  • mux/bindings/python/cmux/client.py
  • mux/crates/mux-core/src/mux.rs
  • mux/crates/mux-core/src/server.rs
  • mux/crates/mux-core/src/surface.rs
  • mux/crates/mux-tui/src/app.rs
  • mux/crates/mux-tui/src/cli.rs
  • mux/crates/mux-tui/src/config.rs
  • mux/crates/mux-tui/src/main.rs
  • mux/crates/mux-tui/src/session/mod.rs
  • mux/crates/mux-tui/src/session/remote.rs
  • mux/crates/mux-tui/tests/cli.rs
  • mux/spec/cli.md
  • mux/spec/commands.md
  • mux/spec/events.md

Comment on lines +192 to +195
"ping": client.ping,
"reload-config": client.reload_config,
"set-window-title": client.set_window_title,
"clear-window-title": lambda **kw: client.clear_window_title(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Drop the unnecessary lambda for clear-window-title.

clear_window_title() takes no arguments, so the lambda wrapper is redundant — ping and reload-config map their no-arg methods directly. The lambda also triggers a Ruff ARG005 warning for the unused kw parameter.

♻️ Proposed fix
-        "clear-window-title": lambda **kw: client.clear_window_title(),
+        "clear-window-title": client.clear_window_title,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"ping": client.ping,
"reload-config": client.reload_config,
"set-window-title": client.set_window_title,
"clear-window-title": lambda **kw: client.clear_window_title(),
"ping": client.ping,
"reload-config": client.reload_config,
"set-window-title": client.set_window_title,
"clear-window-title": client.clear_window_title,
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 195-195: Unused lambda argument: kw

(ARG005)

🤖 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 `@mux/bindings/conformance/runner.py` around lines 192 - 195, The
`clear-window-title` entry in `runner.py` uses a redundant lambda wrapper around
`client.clear_window_title`, which also introduces an unused `kw` parameter
warning. Update the mapping alongside `ping`, `reload-config`, and
`set-window-title` so `clear-window-title` points directly to
`client.clear_window_title` without any wrapper.

Source: Linters/SAST tools

Comment on lines +825 to 849
let value = match event {
MuxEvent::Notification(notification)
if notification.surface == Some(surface_id) =>
{
json!({
"event": "notification",
"notification": notification.notification,
"title": notification.title,
"body": notification.body,
"level": notification.level.as_str(),
"surface": notification.surface,
})
}
MuxEvent::ScrollChanged { surface, offset, at_bottom }
if surface == surface_id =>
{
json!({
"event": "scroll-changed",
"surface": surface,
"offset": offset,
"at_bottom": at_bottom,
})
}
_ => continue,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated ScrollChanged/Notification event→JSON mapping.

The scroll-changed (and notification) JSON shape is now hand-duplicated between spawn_attach_notification_stream (lines 838-847) and the Subscribe handler (lines 1387-1392). As more MuxEvent variants are added, this duplication risks silent drift (e.g. one path gets a field added/renamed and the other doesn't).

Consider extracting a shared fn mux_event_json(event: &MuxEvent) -> Option<Value> (or similar) used by both streams, with the attach stream additionally filtering by surface_id.

♻️ Sketch of a shared helper
+fn scroll_changed_json(surface: SurfaceId, offset: u64, at_bottom: bool) -> Value {
+    json!({
+        "event": "scroll-changed",
+        "surface": surface,
+        "offset": offset,
+        "at_bottom": at_bottom,
+    })
+}

Then call scroll_changed_json(surface, offset, at_bottom) from both call sites.

Also applies to: 1387-1392

🤖 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 `@mux/crates/mux-core/src/server.rs` around lines 825 - 849, The
`spawn_attach_notification_stream` and `Subscribe` paths are duplicating the
`MuxEvent` to JSON mapping for `Notification` and `ScrollChanged`, which can
drift over time. Extract a shared helper like `mux_event_json(&MuxEvent) ->
Option<Value>` (or per-variant helpers such as
`scroll_changed_json`/`notification_json`) in `server.rs`, and have both the
attach stream and the subscribe handler call it so the JSON shape stays
consistent; keep the attach stream’s `surface_id` filtering separate from the
shared serialization logic.

@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 2 potential issues.

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 07589e4. Configure here.

offset,
at_bottom,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Live bottom scroll event spam

Medium Severity

The PTY reader treats any change to the (offset, at_bottom) pair as a scroll-changed emit. While the viewport stays pinned at the live bottom, new output still bumps offset with growing scrollback even though at_bottom stays true, so subscribers can get a scroll-changed line on every read chunk during active output.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 07589e4. Configure here.

"offset": offset,
"at_bottom": at_bottom,
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicate scroll-changed on attach

Medium Severity

After attach-surface, the same connection already streams all mux events from subscribe, including scroll-changed. The attach notification thread also writes scroll-changed for that surface on the shared LineWriter, so typical attach clients receive two identical event lines per viewport change.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 07589e4. Configure here.

@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)
mux/crates/mux-tui/src/app.rs (1)

849-856: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Don't let a cosmetic title write tear down the event loop.

write_window_title returns Result and handle() propagates it with ? (Line 952), so a transient stdout write/flush error bubbles up to event_loop and terminates the TUI. Every other OSC writer here (sync_pointer_shape, copy_text_to_clipboard) intentionally swallows such errors. Setting/clearing the window title is non-essential; a failed write shouldn't end the session.

🛡️ Make title writes non-fatal (consistent with sibling OSC writers)
-    fn write_window_title(&self, title: &str) -> anyhow::Result<()> {
-        let lock = self.stdout_lock.clone();
-        let _guard = lock.lock().unwrap();
-        let mut stdout = std::io::stdout();
-        stdout.write_all(&mux_core::server::window_title_osc(title))?;
-        stdout.flush()?;
-        Ok(())
-    }
+    fn write_window_title(&self, title: &str) {
+        let lock = self.stdout_lock.clone();
+        let _guard = lock.lock().unwrap();
+        let mut stdout = std::io::stdout();
+        let _ = stdout.write_all(&mux_core::server::window_title_osc(title));
+        let _ = stdout.flush();
+    }

And update the call site:

             AppEvent::Mux(MuxEvent::WindowTitleRequested(title)) => {
-                self.write_window_title(&title)?;
+                self.write_window_title(&title);
                 Ok(RenderAction::None)
             }
🤖 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 `@mux/crates/mux-tui/src/app.rs` around lines 849 - 856, The write_window_title
helper currently returns a Result and is propagated from handle(), so a
transient stdout write or flush failure can tear down the TUI event loop. Make
write_window_title non-fatal like sync_pointer_shape and copy_text_to_clipboard
by swallowing stdout errors inside the method in app.rs, and update the handle()
call site so title updates do not use ? or otherwise propagate failures.
🤖 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 `@mux/crates/mux-tui/src/app.rs`:
- Around line 849-856: The write_window_title helper currently returns a Result
and is propagated from handle(), so a transient stdout write or flush failure
can tear down the TUI event loop. Make write_window_title non-fatal like
sync_pointer_shape and copy_text_to_clipboard by swallowing stdout errors inside
the method in app.rs, and update the handle() call site so title updates do not
use ? or otherwise propagate failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b8cf8a06-3c00-4edc-a778-79f63ac2b70a

📥 Commits

Reviewing files that changed from the base of the PR and between cda3d5e and 07589e4.

📒 Files selected for processing (1)
  • mux/crates/mux-tui/src/app.rs

This branch was successfully deployed

1 active deployment
Preview – cmux — 07589e4f Deployed Jul 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant