Skip to content

Add read-only tmux control bridge - #4810

Closed
lawrencecchen wants to merge 64 commits into
mainfrom
issue-560-tmux-control-bridge
Closed

lawrencecchen wants to merge 64 commits into
mainfrom
issue-560-tmux-control-bridge

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Implements the first read-only vertical slice for #560.

Prior work checked before implementation:

Changes:

  • Adds a Ghostty tmux_control embedded action for enter, exit, windows_changed, and pane_output.
  • Serializes Ghostty viewer topology and pane IDs without native pane virtualization.
  • Mirrors read-only tmux state into TerminalSurface, TerminalPanel, Workspace, and surface.health.
  • Adds viewer coverage and Swift state reducer coverage.

Verification:

  • zig build test -Dtest-filter=tmux
  • ./scripts/lint-pbxproj-test-wiring.sh
  • ./scripts/reload.sh --tag tmuxcc

Dogfood tag: tmuxcc


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


Note

High Risk
Touches SSH persistent PTY startup, session restore, and a new Ghostty fork pin—areas where reconnect or cache mismatches could break remote workspaces or CI builds.

Overview
This PR adds a read-only tmux control-mode path from the pinned Ghostty fork into cmux, plus CLI/API attach and SSH persistent-PTY wiring so local and remote tmux sessions can be opened and observed without native pane virtualization.

Ghostty / CI: Bumps the fork pin and ghosttykit-checksums.txt, documents the embedder bridge, and extends CI cache keys so checksum changes invalidate GhosttyKit.xcframework caches.

Runtime observation: Ghostty tmux_control callbacks feed a per-surface reducer (topology JSON, pane IDs, bounded pane text) exposed through surface.health / surface-health (optional --include-tmux-pane-text) and related debug fields.

Attach & reconnect: New cmux tmux attach and v2 workspace.tmux.attach run tmux -CC locally (auto-resume binding + approval) or over SSH via persistent PTY (remote_pty_command, --literal-command / placeholder rules). Session restore and startup scripts preserve tmux commands and exit status.

Tests & strings: Broad CLI, persistence, and tmux-state tests; new localized CLI help for surface-health tmux output.

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


Summary by cubic

Adds a read-only tmux control-mode bridge that mirrors tmux state into surfaces and adds local/SSH tmux attach, aligning with Issue 560. Fixes tmux control reset ordering and improves SSH tmux reconnect startup.

  • New Features

    • Streams tmux_control events (enter/exit/windows_changed/pane_output) into per-surface, read-only state with ordered, bounded buffering; coalesces/dedupes pane output, caps at 64 KiB with UTF‑8 recovery; cleans up on teardown/reset and drops stale callbacks.
    • surface.health v2 returns per-surface tmux_control with surface_id/surface_ref; surface-health --include-tmux-pane-text can include pane text (or null); debug shows tmux_control_active.
    • Tmux attach: CLI cmux tmux attach <session> (local or --ssh <dest>; supports --existing/--create, --tmux-path, -L/--socket-name, -S/--socket-path) and v2 workspace.tmux.attach. Local attach respects focus/--cwd, resolves tmux (incl. MacPorts), and installs an auto‑resume binding with persisted approval. SSH attach uses a persistent remote PTY, saves a literal remote_pty_command, enforces scripted reconnect via --command-b64, validates socket flags, rejects --cwd, preserves literal attach targets, and controls placeholder expansion.
  • Bug Fixes

    • Reliable SSH tmux session reconnect startup.
    • Correct tmux control event reset ordering and simplified event buffering for strict delivery.
    • Preserves CLI tmux resume bindings and reusable startup command exit status during reconnect/restore.
    • Fixed SSH tmux attach test compile.

Written for commit 49e5a8e. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added a tmux attach CLI (local and SSH modes) and workspace.tmux.attach API; terminals/surfaces report tmux control status and can include pane text on request.
  • Documentation
    • Updated Ghostty fork pin, fork-change guidance, and release tag guidance.
  • Tests
    • New unit and integration tests covering tmux attach, SSH persistent-PTY behavior, tmux control state tracking, and session persistence.
  • CI
    • Improved GhosttyKit xcframework caching in CI.
  • Localization
    • Added CLI strings for tmux surface-health output.

Review Change Stack

@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 May 26, 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 May 28, 2026 11:49am
cmux-staging Building Building Preview, Comment May 28, 2026 11:49am

@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown

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

Adds tmux control-mode ingestion from Ghostty, an in-memory tmux-control state exported via panel/controller/workspace layers, a new cmux "tmux attach" command (local and SSH with persistent-PTY plumbing), unit and integration tests, docs/localization updates, and CI cache/checksum changes.

Changes

Tmux Control State Tracking and Reporting

Layer / File(s) Summary
TmuxControlState data model and state machine
Sources/GhosttyTerminalViewSupport.swift
Defines TmuxControlEvent, layout/window/topology models, TmuxControlState.apply(_:), debugPayload(includePaneText:), and payload data helpers.
Ghostty action handler and TerminalSurface state management
Sources/GhosttyTerminalView.swift
Handles GHOSTTY_ACTION_TMUX_CONTROL, decodes payloads to TmuxControlEvent, enqueues/coalesces events, maintains @Published private(set) tmuxControlState, drains/apply on MainActor, and resets state on teardown/hibernation.
TerminalPanel and Workspace propagation
Sources/Panels/TerminalPanel.swift, Sources/Workspace.swift
TerminalPanel forwards tmuxControlReportPayload(includePaneText:); Workspace.tmuxControlReport(forPanelId:includePaneText:) returns the panel payload or nil.
TerminalController payload augmentation and API routing
Sources/TerminalController.swift
Emits tmux_control_active in terminal payloads, accepts include_tmux_pane_text for panel lists, populates per-panel tmux_control via workspace report, and adds workspace.tmux.attach dispatch and helpers.
Remote PTY command storage and SSH integration
Sources/WorkspaceRemoteConfiguration.swift
Adds optional remotePTYCommand to snapshots/config, trims/normalizes it, extends PTY attach command builder to accept and base64-embed a --command-b64 payload, and persists remotePTYCommand only when PTY preservation is enabled.
CLI tmux attach command (local and SSH paths)
CLI/cmux.swift
New tmux attach command with TmuxAttachOptions; constructs a tmux -CC control-mode command, validates SSH persistent-PTY constraints, routes local attach via workspace.tmux.attach or SSH via the bootstrap with persistentPTYCommand and tmux metadata injection.
CLI surface-health tmux state display and localization
CLI/cmux.swift, Resources/Localizable.xcstrings
Adds --include-tmux-pane-text; request includes include_tmux_pane_text, and non-JSON output appends tmux=active or tmux=active panes=[...] when appropriate; updates help text and localization strings.
TmuxControlState unit tests
cmuxTests/WorkspaceSplitStartupCommandTests.swift
Tests enter/windowsChanged/paneOutput/exit lifecycle, topology JSON decoding and pane id extraction, pane output accumulation/truncation, debugPayload redaction/inclusion, invalid JSON handling, and exit cleanup.
CLI integration tests and helper enhancements
cmuxTests/CLINotifyProcessIntegrationRegressionTests.swift, cmuxTests/TabManagerSessionSnapshotTests.swift, cmuxTests/SessionPersistenceTests.swift
Adds tmux attach integration tests (local and SSH), asserts remote_pty_command and base64 --command-b64 embedding, strengthens startup script assertions, enhances runMockedSSH() with cliArguments override and workspace.rename mock, and adds session-snapshot restore tests for remotePTYCommand and resume-binding preservation.
CI caching updates
.github/workflows/ci.yml
Computes GhosttyKit git SHA in CI and incorporates it (and checksum file) into GhosttyKit.xcframework and DerivedData cache keys for tests, lag, and release jobs.
Documentation, submodule, and checksum updates
docs/ghostty-fork.md, ghostty, scripts/ghosttykit-checksums.txt
Updates Ghostty fork pin and docs with tmux embedder entry, updates ghostty submodule SHA, and adds GhosttyKit checksum mappings for xcframework releases.

Sequence Diagram

sequenceDiagram
  participant Ghostty
  participant Surface as TerminalSurface
  participant CLI as cmux_cli
  participant Controller as TerminalController
  participant Workspace
  participant SSH as SSHBootstrap

  Ghostty->>Surface: GHOSTTY_ACTION_TMUX_CONTROL(ptr,len)
  Surface->>Surface: decode to TmuxControlEvent
  Surface->>Surface: enqueue/coalesce events
  Surface->>Surface: drain -> applyTmuxControlEvent (MainActor)
  CLI->>Controller: invoke workspace.tmux.attach (local) or bootstrap SSH
  Controller->>Workspace: create/attach tmux workspace and return resume_binding + tmux_command
  CLI->>SSH: bootstrap with persistentPTYCommand and tmux metadata (SSH path)
  SSH->>Workspace: remote configure sets remote_pty_command and completes attach
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#4237: Related changes around surface resume binding persistence and session snapshot/restore that overlap with workspace resume-binding and session persistence behavior.

Poem

🐰 Whiskers twitch as panes align,

Events queued, topology drawn fine,
SSH and local bridges hum at night,
Resume bindings wake sessions right,
Rabbit hops—terminals gleam bright.


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (5 errors, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Cmux Swift Actor Isolation ❌ Error TmuxControl* types missing nonisolated on pure Codable/Sendable value models, violating swift-actor-isolation.md and codebase patterns (SessionRemoteWorkspaceSnapshot, SurfaceResumeBindingSnapshot). Add nonisolated to TmuxControlEvent, TmuxControlLayoutNode, TmuxControlWindow, TmuxControlTopology, and TmuxControlState to match Swift 6 isolation requirements.
Cmux Swift File And Package Boundaries ❌ Error CLI/cmux.swift adds +340 lines to oversized 30k-line file, exceeding 250-line threshold. GhosttyTerminalViewSupport.swift mixes domain logic with AppKit UI components. Extract TmuxControlState to SwiftPM package. Move GhosttyPassthroughVisualEffectView and focus logic to separate files. Reduce CLI additions or refactor to dedicated module.
Cmux Swift Logging ❌ Error Unguarded print() statements in production code (TerminalController.swift) for socket diagnostics violate swift-logging.md rule against stdout logging in app/runtime code. Replace print() with Logger instance or guard with #if DEBUG. Use reportSocketListenerFailure() breadcrumbs exclusively for production diagnostics.
Cmux User-Facing Error Privacy ❌ Error User-facing error message at CLI/cmux.swift:6844 exposes SSH provider-specific flags (ControlMaster, ControlPersist, LocalCommand), violating user-facing-errors.md privacy rules. Rewrite error message to describe the issue in user terms without mentioning SSH config flags: e.g., "tmux attach --ssh requires a compatible SSH setup; check your SSH configuration for compatibility overrides."
Cmux Full Internationalization ❌ Error PR introduces 12 non-localized error messages in CLI and help text for tmux attach command without String(localized:) API; 8 non-localized API error messages in TerminalController.swift. Wrap CLI errors and help text with String(localized:defaultValue:); add entries to Resources/Localizable.xcstrings with translations for all 20 supported locales.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Cmux Algorithmic Complexity ❓ Inconclusive The referenced rule file .github/review-bot-rules/algorithmic-complexity.md does not exist in the repository and is not listed in the review-bot-rules README.md. Clarify whether the algorithmic-complexity.md rule file exists; if it should exist, ensure it's present before reviewing code against it.
✅ Passed checks (11 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add read-only tmux control bridge' clearly and specifically summarizes the main change—a new tmux control-mode bridge feature.
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 Blocking Runtime ✅ Passed No new blocking patterns. NSLock usage for tmux event queueing is short-term; processing is async. Pre-existing v2MainSync pattern used appropriately.
Cmux No Hacky Sleeps ✅ Passed All production code changes are Swift (covered by separate check) or non-executable. No TypeScript, JavaScript, or shell scripts with sleeps/delays added. CI workflow changes out of scope.
Cmux Swift Concurrency ✅ Passed Task in tmuxControlEvent processing is managed via generation tracking and flag state. CLI DispatchQueue patterns are legitimate OS boundaries.
Cmux Swift @Concurrent ✅ Passed All async functions follow @concurrent rules. processPendingTmuxControlEvents is intentional UI coordination with proper locking and MainActor awaits. Other async methods properly actor-isolated.
Cmux Swiftui State Layout ✅ Passed Existing ObservableObject touched incidentally with new @Published property. State mutations only in @MainActor event handlers and lifecycle, never render paths.
Cmux Architecture Rethink ✅ Passed Required platform bridge: NSLock for Ghostty C callback→MainActor. Single ownership (TerminalSurface), clear invariants, no polling/timing repairs, proper lifecycle cleanup. Meets allowed cases.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds tmux control integration without creating any new standalone windows/panels/window controllers. No violations of auxiliary window close shortcut rules.
Description check ✅ Passed The pull request description follows the required template with complete Summary, Testing, and Checklist sections, includes verification commands and a dogfood tag.
✨ 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 issue-560-tmux-control-bridge

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 and usage tips.

@greptile-apps

greptile-apps Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements the first read-only vertical slice for the tmux control-mode bridge: Ghostty tmux callbacks are buffered in a per-surface TmuxControlEventStream with generation-based stale-event filtering, reduced into TmuxControlState, and exposed through surface.health v2 and a new cmux tmux attach command for both local and SSH modes.

  • Adds a TmuxControlEventStream using DispatchQueue + withCheckedContinuation for non-blocking async buffering with generation-counter\u2013based reset safety; addresses all prior NSLock/OSAllocatedUnfairLock blocking-on-main-actor findings from earlier review rounds.
  • Introduces local and SSH tmux attach paths (workspace.tmux.attach, cmux tmux attach), persisting the remotePTYCommand into SessionRemoteWorkspaceSnapshot for reliable reconnect after relaunch.
  • Wires TmuxControlState teardown and reset into teardownSurface and suspendRuntimeSurfaceForAgentHibernation, with a generation bump ensuring in-flight buffered events from the previous session are silently discarded.

Confidence Score: 4/5

Safe to merge; all previously identified concurrency, stale-state, and API-exposure concerns from prior rounds have been addressed in the current head.

All major concerns from prior review rounds are resolved: the NSLock/OSAllocatedUnfairLock blocking-main-actor pattern is replaced with DispatchQueue + withCheckedContinuation (non-blocking); the generation counter correctly filters stale events across reset/teardown; the topology parse error no longer leaks raw Swift decoder internals; the orphaned-workspace path now returns .ok with null surface fields; and the dead tmuxControlReports() helper has been removed. No new correctness issues were found in the current code. The modest score deduction reflects the large surface area — SSH persistent PTY lifecycle, session snapshot restoration, Swift concurrency coordination, and Ghostty callback threading all interact, and regressions in any of those paths would be subtle.

Sources/GhosttyTerminalView.swift (TmuxControlEventStream reset/drain ordering under rapid teardown) and Sources/TerminalController.swift (duplicated tmux command builder) are the areas most likely to need attention on follow-up changes.

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Adds TmuxControlEventStream with DispatchQueue+withCheckedContinuation (non-blocking), generation-gated applyTmuxControlQueuedItems, and reset/teardown lifecycle hooks; prior blocking-runtime and stale-event concerns from earlier reviews appear addressed.
Sources/GhosttyTerminalViewSupport.swift New file containing TmuxControlState, TmuxControlEvent, TmuxControlTopology, and associated types; topology parse errors return the stable token json_decode_failed, UTF-8 recovery loop is bounded O(1) per pane, and pane byte cap is enforced correctly.
Sources/TerminalController.swift Adds v2WorkspaceTmuxAttach, defaultTmuxExecutablePath resolution, and surface.health tmux_control enrichment; nil focusedPanelId now returns .ok with null surface fields rather than leaving an orphaned workspace; duplicate tmux command-building logic mirrors CLI counterpart.
CLI/cmux.swift Adds cmux tmux attach (local and SSH paths), surface-health --include-tmux-pane-text display, and tmux command building; requires SSH persistent PTY support and validates socket flag combinations correctly.
Sources/WorkspaceRemoteConfiguration.swift Adds remotePTYCommand to SessionRemoteWorkspaceSnapshot and WorkspaceRemoteConfiguration; passes it to SSHPTYAttachStartupCommandBuilder.command with --literal-command flag for safe SSH tmux reconnect.

Sequence Diagram

sequenceDiagram
    participant GCB as Ghostty Callback Thread
    participant TES as TmuxControlEventStream
    participant Task as Processing Task
    participant MA as MainActor TerminalSurface

    GCB->>TES: enqueue(event) via queue.async
    TES->>TES: append item with current generation
    Task->>TES: await drain() via withCheckedContinuation
    TES-->>Task: items
    Task->>MA: await applyTmuxControlQueuedItems
    MA->>MA: filter by generation, update state

    Note over MA: On teardown
    MA->>MA: tmuxControlGeneration plus plus
    MA->>TES: reset(generation N)
    TES->>TES: clear items, append reset N
Loading

Reviews (53): Last reviewed commit: "Simplify tmux control event buffering" | Re-trigger Greptile

Comment thread Sources/Workspace.swift Outdated
Comment thread Sources/Workspace.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
coderabbitai[bot]
coderabbitai Bot previously requested changes May 26, 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: 4

🤖 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 `@ghostty`:
- Line 1: Add the missing GhosttyKit checksum mapping for the updated gitlink
SHA by computing the correct checksum for the ghostty kit at commit
ed123a8c0937c70996372a917c68f5713b1a3cc1 and appending a line to
scripts/ghosttykit-checksums.txt in the format used by the file (e.g., "ghostty
ed123a8c0937c70996372a917c68f5713b1a3cc1 <computed-checksum>"); ensure the
checksum you add matches the exact artifact used by the build so the guard
succeeds.

In `@Sources/GhosttyTerminalView.swift`:
- Around line 24-174: The tmux control models and reducer (TmuxControlEvent,
TmuxControlLayoutNode, TmuxControlWindow, TmuxControlTopology, TmuxControlState
and their helpers like apply(_:), paneTextLimit, debugPayload, paneIds) are pure
parsing/state logic and should be moved out of GhosttyTerminalView.swift into a
new dedicated source file (e.g., TmuxControl.swift) so they can be compiled and
tested independently; to fix, create the new file, copy those type and method
definitions exactly, update any internal references from GhosttyTerminalView to
import/use the new file (no API changes), and ensure the new file has the same
module visibility (internal/public) and required imports so tests and the app
compile.
- Around line 156-172: The debugPayload currently includes raw terminal contents
via the "panes" array using paneTextById which exposes sensitive data; update
debugPayload (the computed var debugPayload) to stop exporting raw pane text by
either removing the "text" field from each panes entry or replacing it with a
redacted placeholder/NSNull, or make inclusion conditional behind an explicit
debug-only flag (e.g., a new isDebugExport boolean) so paneTextById is never
forwarded to production report APIs; keep other topology fields (pane_ids,
windows.map(\.debugPayload), topologyParseError) unchanged.
🪄 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: 52e2e000-e826-4f2c-8683-50dd4541733e

📥 Commits

Reviewing files that changed from the base of the PR and between 4faba0c and 4933d90.

📒 Files selected for processing (9)
  • CLI/cmux.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/GhosttyTerminalViewSupport.swift
  • Sources/Panels/TerminalPanel.swift
  • Sources/TerminalController.swift
  • Sources/Workspace.swift
  • cmuxTests/WorkspaceSplitStartupCommandTests.swift
  • docs/ghostty-fork.md
  • ghostty

Comment thread ghostty Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment thread Sources/GhosttyTerminalView.swift Outdated
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

@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 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)
Sources/GhosttyTerminalViewSupport.swift (1)

4-162: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Split tmux parsing/state logic into a dedicated non-UI Swift file.

This block adds feature-state/parsing code into a file that also owns AppKit/window-support helpers. Please extract TmuxControlEvent, TmuxControlLayoutNode, TmuxControlWindow, TmuxControlTopology, and TmuxControlState into a focused tmux-state file, and keep GhosttyTerminalViewSupport.swift for UI/platform support concerns.

As per coding guidelines: “Do not mix UI rendering, state ownership, persistence, networking, parsing, subprocess/socket protocol, and platform bridge code in one Swift file,” and “Do not implement feature logic directly in the app target/module's root Sources/ path when the logic is independent of cmux app lifecycle.”

🤖 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 `@Sources/GhosttyTerminalViewSupport.swift` around lines 4 - 162, The file
mixes tmux parsing/state with UI code; extract the tmux-only types into a new
Swift source file (e.g. TmuxControlState.swift) by moving TmuxControlEvent,
TmuxControlLayoutNode, TmuxControlWindow, TmuxControlTopology, and
TmuxControlState into that file, keep their definitions, add necessary imports
(Foundation) and appropriate access level (internal/public) so existing code in
GhosttyTerminalViewSupport.swift that references paneIds, apply(_:),
debugPayload(includePaneText:), and TmuxControlTopology.CodingKeys continues to
compile, and remove these definitions from GhosttyTerminalViewSupport.swift so
that file contains only UI/platform support. Ensure any conditional DEBUG
logging (cmuxDebugLog) still resolves by keeping visibility or importing the
module that defines it.
🤖 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 `@Sources/GhosttyTerminalViewSupport.swift`:
- Around line 4-162: The file mixes tmux parsing/state with UI code; extract the
tmux-only types into a new Swift source file (e.g. TmuxControlState.swift) by
moving TmuxControlEvent, TmuxControlLayoutNode, TmuxControlWindow,
TmuxControlTopology, and TmuxControlState into that file, keep their
definitions, add necessary imports (Foundation) and appropriate access level
(internal/public) so existing code in GhosttyTerminalViewSupport.swift that
references paneIds, apply(_:), debugPayload(includePaneText:), and
TmuxControlTopology.CodingKeys continues to compile, and remove these
definitions from GhosttyTerminalViewSupport.swift so that file contains only
UI/platform support. Ensure any conditional DEBUG logging (cmuxDebugLog) still
resolves by keeping visibility or importing the module that defines it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f9ac93a3-cb79-4882-8f3c-dfac5ebd6988

📥 Commits

Reviewing files that changed from the base of the PR and between 3f67a3e and de43b9c.

📒 Files selected for processing (7)
  • CLI/cmux.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/GhosttyTerminalViewSupport.swift
  • Sources/Panels/TerminalPanel.swift
  • Sources/TerminalController.swift
  • Sources/Workspace.swift
  • cmuxTests/WorkspaceSplitStartupCommandTests.swift

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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)
Sources/GhosttyTerminalView.swift (1)

5300-5318: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset tmuxControlState when the runtime surface is torn down.

This new surface-scoped state outlives the Ghostty runtime surface, but nothing in teardownSurface() or suspendRuntimeSurfaceForAgentHibernation(...) clears it. After a hibernation/recreate path, tmuxControlReportPayload(includePaneText:) can return stale pane IDs and old pane text from the previous tmux session until a fresh enter/exit event arrives.

Suggested direction
 final class TerminalSurface: Identifiable, ObservableObject {
     `@Published` private(set) var tmuxControlState = TmuxControlState()

+    `@MainActor`
+    private func resetTmuxControlState() {
+        tmuxControlState = TmuxControlState()
+    }
+
     `@MainActor`
     func applyTmuxControlEvent(_ event: TmuxControlEvent) {
         var next = tmuxControlState
         next.apply(event)
         guard next != tmuxControlState else { return }
@@
     `@MainActor`
     func teardownSurface() {
+        resetTmuxControlState()
         recordTeardownRequest(reason: "surface.teardown")
         markPortalLifecycleClosed(reason: "teardown")
         closeHeadlessStartupWindowIfNeeded()
@@
     `@MainActor`
     func suspendRuntimeSurfaceForAgentHibernation(reason: String) {
+        resetTmuxControlState()
         runtimeSurfaceSuspendedForAgentHibernation = true
         backgroundSurfaceStartQueued = false
         closeHeadlessStartupWindowIfNeeded()
🤖 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 `@Sources/GhosttyTerminalView.swift` around lines 5300 - 5318, tmuxControlState
is surface-scoped and isn't cleared when a runtime surface is torn down, causing
tmuxControlReportPayload(includePaneText:) to return stale data; fix by
resetting tmuxControlState to a fresh TmuxControlState() during surface teardown
and hibernation paths — specifically update teardownSurface() and
suspendRuntimeSurfaceForAgentHibernation(...) to set tmuxControlState =
TmuxControlState() (or call a helper resetTmuxControlState()) so
applyTmuxControlEvent(_:) and tmuxControlReportPayload(...) no longer observe
old pane IDs/text after recreate.
🤖 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 `@Sources/GhosttyTerminalView.swift`:
- Around line 5300-5318: tmuxControlState is surface-scoped and isn't cleared
when a runtime surface is torn down, causing
tmuxControlReportPayload(includePaneText:) to return stale data; fix by
resetting tmuxControlState to a fresh TmuxControlState() during surface teardown
and hibernation paths — specifically update teardownSurface() and
suspendRuntimeSurfaceForAgentHibernation(...) to set tmuxControlState =
TmuxControlState() (or call a helper resetTmuxControlState()) so
applyTmuxControlEvent(_:) and tmuxControlReportPayload(...) no longer observe
old pane IDs/text after recreate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2f545ac0-39d0-46a7-b3d6-481c540f5d90

📥 Commits

Reviewing files that changed from the base of the PR and between de43b9c and 4704b10.

📒 Files selected for processing (8)
  • Sources/GhosttyTerminalView.swift
  • Sources/GhosttyTerminalViewSupport.swift
  • Sources/Panels/TerminalPanel.swift
  • Sources/TerminalController.swift
  • cmuxTests/WorkspaceSplitStartupCommandTests.swift
  • docs/ghostty-fork.md
  • ghostty
  • scripts/ghosttykit-checksums.txt
💤 Files with no reviewable changes (1)
  • Sources/Panels/TerminalPanel.swift

Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment on lines +5254 to +5261
func drain() -> [TmuxControlQueuedItem] {
queue.sync { [self] in
let items = state.items
state.items.removeAll(keepingCapacity: true)
state.signalPending = false
return items
}
}

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 queue.sync in async Task context blocks a cooperative thread

drain() calls queue.sync, which is a blocking primitive. It is called at line 5512 inside a non-isolated Task (the startTmuxControlEventProcessing loop), so it blocks a Swift cooperative thread pool thread until the GCD queue completes the drain. Under Swift 6's cooperative pool contract, no async context may block a thread with a synchronous wait — this can stall the pool in the same way the previous NSLock round was flagged for the main actor.

The fix is to make TmuxControlEventStream a Swift actor (matching TmuxControlEventBuffer), which lets drain() become an isolated async method with no blocking wait. enqueue and reset are already called via queue.async today, so converting them to actor-isolated async calls from the Ghostty callback thread is straightforward (Task { await stream.enqueue(event) }).

Rule Used: Flag new blocking or timing-based synchronization ... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread Sources/TerminalController.swift

@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 and found 1 potential issue.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 23f9c2c. Configure here.

Comment thread Sources/GhosttyTerminalView.swift
@lawrencecchen lawrencecchen added the stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening. label Sep 23, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in cmux backlog Sep 23, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 49e5a8e9 Deployed May 28, 2026 by vercel[bot]
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