Skip to content

Honor keep workspace open on last surface close - #6475

Merged
austinywang merged 40 commits into
mainfrom
issue-6442-keep-workspace-open
Jun 22, 2026
Merged

austinywang merged 40 commits into
mainfrom
issue-6442-keep-workspace-open

Conversation

@austinywang

@austinywang austinywang commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Fix Setting ignored: Keep Workspace open when closing last surface #6442 by reading the existing close-on-last-surface preference in the shared last-surface close decision.
  • Apply the keep-open behavior to tab-strip close button and middle-click paths without changing the non-tab-strip shortcut path.
  • Add Swift Testing regression coverage for both preference states and for recently-closed history when a close-button close keeps the workspace open.

Testing

  • Not run locally: per issue instructions, no local build or bare xcodebuild before CI is green.
  • CI validation in progress on this PR: workflow guards, Swift package tests, app-host unit tests, release build, Vercel, Greptile, and CodeRabbit.

Demo Video

  • Not recorded before CI green: the user explicitly requested no launch/dev build until after green CI and an explicit launch instruction.

Review Notes

  • CodeRabbit source-artifacts warning is a false positive for this PR: git diff origin/main...HEAD --name-status only lists Sources/TabManager.swift, Sources/Workspace.swift, cmux.xcodeproj/project.pbxproj, and cmuxTests/LastSurfaceClosePreferenceTests.swift.

Fixes #6442

@vercel

vercel Bot commented Jun 20, 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 Jun 22, 2026 12:48am
cmux-staging Building Building Preview, Comment Jun 22, 2026 12:48am

@coderabbitai

coderabbitai Bot commented Jun 20, 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

Extracts a closeWorkspaceOnLastSurfacePreferenceEnabled() helper in TabManager. Introduces gesture-aware tracking in Workspace via a tabStripCloseSources dictionary to distinguish tab-close-button from middle-click closes. Wires the preference through Workspace.shouldCloseWorkspaceOnLastSurface(for:tabStripClose:) to gate workspace closure on both gesture context and user preference. Adds comprehensive test coverage for both preference states across tab-close and middle-click paths.

Changes

Last-surface close preference gating

Layer / File(s) Summary
Preference-enabled helper in TabManager
Sources/TabManager.swift
closeWorkspaceOnLastSurfacePreferenceEnabled() extracts the keep-workspace-open setting read, and shouldCloseWorkspaceOnLastSurfaceShortcut(_:panelId:) delegates to it before applying panel-count and panel-existence conditions. Stale comment about preference applying only to "Close Tab" path is removed.
Gesture-source tracking in Workspace
Sources/Workspace.swift
New TabStripCloseSource enum and tabStripCloseSources: [TabID: TabStripCloseSource] dictionary replace the prior tabCloseButtonCloseTabIds set to track close origins separately. The .middleClick request path routes through markTabStripMiddleClickClose(surfaceId:). Helpers markTabCloseButtonClose(surfaceId:) and markTabStripMiddleClickClose(surfaceId:) record gesture sources for later close-decision logic.
Close eligibility logic wired to gesture context and preference
Sources/Workspace.swift
shouldCloseWorkspaceOnLastSurface(for:tabStripClose:) gates closing based on gesture type: non-tab-strip gestures allow unconditional close; tab-strip/middle-click gestures delegate to TabManager.closeWorkspaceOnLastSurfacePreferenceEnabled(). In splitTabBar(_:shouldCloseTab:inPane:), gesture intent is extracted from tabStripCloseSources, included in explicitUserClose, and passed to the updated helper. Cleanup removes tabStripCloseSources entries.
Test coverage for gesture and preference paths
cmuxTests/LastSurfaceClosePreferenceTests.swift, cmux.xcodeproj/project.pbxproj
New LastSurfaceClosePreferenceTests suite with four tests: tab-close and middle-click paths in both enabled and disabled preference states. Uses isolated UserDefaults suites via withManager(closeWorkspaceOnLastSurface:run:) helper. Project file updated to compile the new test source.

Sequence Diagram

sequenceDiagram
  participant User
  participant Workspace as Workspace delegate
  participant TabManager
  participant UserDefaults

  User->>Workspace: close last surface (tab-strip, middle-click, or other)
  Workspace->>Workspace: extract tabStripClose and tabCloseButtonClose from tabStripCloseSources
  Workspace->>Workspace: check panels.count <= 1 and panel exists
  alt non-tab-strip close gesture
    Workspace-->>User: allow workspace close unconditionally
  else tab-strip or middle-click gesture
    Workspace->>TabManager: closeWorkspaceOnLastSurfacePreferenceEnabled()
    TabManager->>UserDefaults: read closeWorkspaceOnLastSurface setting
    UserDefaults-->>TabManager: Bool
    TabManager-->>Workspace: Bool
    Workspace-->>User: close workspace only if preference enabled
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 With care and context, each gesture finds its way,
Button click or middle-click, each has its say.
The preference now heeded, the workspace stays true,
No more ignored settings—the bug disappears too! 🎉


Important

Pre-merge checks failed

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

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Source Artifacts ❌ Error The PR adds local tool artifacts (.claude/, .agents/, .cursor/, .coderabbit.yaml, .vercelignore) to source control without a deliberate product/docs/test reason, violating source-control-artifacts.... Remove .claude/, .agents/, .cursor/, .coderabbit.yaml, .vercelignore from the commit, or add these artifact patterns to .gitignore.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (21 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Honor keep workspace open on last surface close' directly addresses the main objective: ensuring the 'Keep Workspace open' setting is honored when closing the last surface, which aligns with the PR's core fix for issue #6442.
Linked Issues check ✅ Passed The changes directly address issue #6442 by implementing the setting check in tab-close and middle-click paths [#6442], adding regression tests [#6442], and centralizing preference logic [#6442] to ensure the workspace respects the 'Keep Workspace open' setting.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing issue #6442: TabManager refactoring, Workspace tab-close handling, test file additions, and test implementation all directly support honoring the workspace-close preference as required.
Cmux Swift Actor Isolation ✅ Passed Both TabManager and Workspace are @MainActor classes. New method closeWorkspaceOnLastSurfacePreferenceEnabled() in TabManager and private enum/methods in Workspace correctly inherit @MainActor isol...
Cmux Swift Blocking Runtime ✅ Passed New production code in TabManager.swift and Workspace.swift introduces no blocking synchronization (semaphores, sleep, asyncAfter, sync, locks). Test file calls undefined drainMainQueue() but is de...
Cmux Expensive Synchronous Load ✅ Passed The new closeWorkspaceOnLastSurfacePreferenceEnabled() method only reads UserDefaults (fast), and all RestorableAgentSessionIndex.load() calls are guarded by SharedLiveAgentIndex.shared cache as re...
Cmux Cache Substitution Correctness ✅ Passed PR adds fresh UserDefaults reads (UserDefaultsSettingsClient, no caching layer) to control workspace-close logic, not to persist data. Not a cache substitution in persistence/history/undo/snapshot...
Cmux No Hacky Sleeps ✅ Passed Check scope excludes Swift code; all changes are Swift source/tests or Xcode project config. No TypeScript, JavaScript, shell, or non-Swift runtime script changes present.
Cmux Algorithmic Complexity ✅ Passed PR adds O(1) dict operations and single-call contains(where:) over ~10-100 workspaces per user interaction (not in loops), no nested scans, no batch rescans, all within acceptable complexity bounds.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns: new methods are synchronous helpers (getter, setters, decision logic), test uses Swift Testing with proper synchronization, no DispatchQueue.global/DispatchG...
Cmux Swift @Concurrent ✅ Passed All Swift changes are synchronous functions with no async/await constructs. No @concurrent annotations are used or needed. No actor isolation or concurrency rule violations detected.
Cmux Swift File And Package Boundaries ✅ Passed PR respects Swift file boundaries: focused bug fix with -1 net lines in TabManager, 0 net in Workspace (already at 12695 limit), new 140-line test file (under 400-line threshold), no responsibility...
Cmux Swiftpm Lockfiles ✅ Passed PR includes Xcode .pbxproj package-reference changes (XCRemoteSwiftPackageReference) with corresponding root Package.resolved diffs containing all 13 package pins, plus 11 package-local Package.res...
Cmux Swift Logging ✅ Passed No Swift logging rule violations found. All new code contains no print/NSLog statements; existing debug NSLog calls are properly wrapped in #if DEBUG guards.
Cmux User-Facing Error Privacy ✅ Passed PR contains no new user-facing error messages, alerts, or exposed implementation details. All changes are internal infrastructure code (new methods, enum, logic refactoring) and developer-only tests.
Cmux Full Internationalization ✅ Passed The PR adds helper methods and regression tests without introducing new user-facing Swift text or localization keys; test code is exempt per the internationalization rules.
Cmux Swiftui State Layout ✅ Passed Changes are to model-layer logic (closeWorkspaceOnLastSurfacePreferenceEnabled, TabStripCloseSource enum, internal state tracking), not SwiftUI state or layout. No new @Observable, @Published, @Sta...
Cmux Architecture Rethink ✅ Passed PR consolidates close-source tracking into single enum dictionary and centralizes preference check with clear decision logic: non-tab-strip closes always allowed, tab-strip closes require preferenc...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no new or materially changed standalone windows. Changes are logic refactoring (TabManager, Workspace) and test-only fixtures (LastSurfaceClosePreferenceTests), which are explicitly all...
Cmux No Test Or Debug Seam In Production Source ✅ Passed No test/debug seams introduced in production source. New methods (closeWorkspaceOnLastSurfacePreferenceEnabled, markTabCloseButtonClose, markTabStripMiddleClickClose) implement real product behavio...
Description check ✅ Passed The pull request description covers the core requirements: it explains what changed (fix for the close-on-last-surface preference), why (to respect user settings), and includes testing information and a note about CI validation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-6442-keep-workspace-open

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 Jun 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes #6442 by making the "Keep Workspace Open When Closing Last Surface" preference apply to the tab-strip close button and middle-click paths, not only the keyboard shortcut path. The central change is replacing the unconditional return true in Workspace.shouldCloseWorkspaceOnLastSurface with a lastSurfaceClosePreference helper that reads the stored setting and threads a tabStripClose flag through the decision so the keyboard shortcut path is unaffected.

  • Workspace.swift replaces the old tabCloseButtonCloseTabIds: Set<TabID> with tabStripCloseButtonByTabId: [TabID: Bool] to distinguish close-button from middle-click, adds a remote-tmux keep-workspace-open state machine, and exposes handleRemoteTmuxSessionEndedKeepingWorkspaceOpenIfNeeded for RemoteTmuxController to call before its normal close switch.
  • TabManager.swift extracts closeWorkspaceOnLastSurfacePreferenceEnabled() as an internal method called by Workspace, makes closeTabWarningDefaults injectable for test isolation, and correctly propagates the Bool return value of closeWorkspaceIfRunningProcess through all callers.
  • LastSurfaceClosePreferenceTests.swift covers both preference states for close-button, middle-click, and remote-tmux paths with isolated UserDefaults suites; WorkspaceCloseTabsContextMenuTests.swift migrates to Swift Testing and adds a recently-closed history regression test.

Confidence Score: 5/5

Safe to merge; the preference-reading fix is narrowly scoped to tab-strip close paths, the keyboard-shortcut path is structurally unchanged, and the remote-tmux state machine is covered by nine new regression tests with isolated UserDefaults suites.

All four supported locales are updated for both string-catalog keys. closeWorkspaceOnLastSurfacePreferenceEnabled() is exposed at internal visibility for a real production caller in Workspace.swift, not as a debug seam. The tabStripClose flag correctly threads through shouldCloseWorkspaceOnLastSurface so that tabStripClose=false (shortcut) preserves the pre-existing always-close behavior while tabStripClose=true (close button, middle-click) gates on the preference. No blocking or data-loss paths identified.

No files require special attention.

Important Files Changed

Filename Overview
Sources/Workspace.swift Core change: replaces unconditional return true in shouldCloseWorkspaceOnLastSurface with a new lastSurfaceClosePreference abstraction that reads the keep-open setting; adds tabStripClose flag to distinguish tab-strip closes from keyboard shortcuts; introduces remote-tmux keep-workspace-open state machine with clearly separated intent flags.
Sources/TabManager.swift Extracts closeWorkspaceOnLastSurfacePreferenceEnabled() as an internal method (called by Workspace.swift); makes closeTabWarningDefaults injectable; changes closeWorkspaceIfRunningProcess return type from Void to Bool and propagates the result through all callers.
Sources/RemoteTmuxController.swift Adds detachMirrorWorkspaceKeptOpenLocally for the keep-workspace-open path and intercepts handleSessionEndedRemotely to honour the preference before the normal close switch; comments condensed throughout.
Resources/Localizable.xcstrings Updates subtitle strings for both the on/off states of the last-surface close preference to mention tab close button, or middle-click; all four supported locales (en, ja, uk, ko) updated for both keys.
cmuxTests/LastSurfaceClosePreferenceTests.swift New Swift Testing suite covering both preference states for close-button, middle-click, and remote-tmux paths; includes the keep-workspace-open-after-session-end path and the single-window fallback. Properly isolates UserDefaults per test.
cmuxTests/WorkspaceCloseTabsContextMenuTests.swift Migrated from XCTest to Swift Testing; adds tabCloseButtonKeepOpenRecordsClosedSurfaceHistoryForLastSurface to verify recently-closed history is preserved when keep-open fires.
Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift Updates settings panel subtitle copy to reflect the expanded scope (close button and middle-click now also honour the preference), matching the xcstrings catalog entries.
cmux.xcodeproj/project.pbxproj Registers LastSurfaceClosePreferenceTests.swift in both the file reference table and the test target build phase; mechanical Xcode project change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User closes last surface] --> B{Close trigger}
    B -->|Keyboard shortcut| C[TabManager.closePanelWithConfirmation]
    B -->|Tab close button| D[Workspace.markTabCloseButtonClose]
    B -->|Middle-click| E[Workspace.markTabStripMiddleClickClose]
    C --> F{shouldCloseWorkspaceOnLastSurfaceShortcut reads keepOpen pref}
    F -->|pref = close workspace| G[markExplicitClose + closeWorkspaceWithConfirmation]
    F -->|pref = keep open| H[Close panel only workspace stays]
    D --> I[Bonsplit shouldCloseTab tabStripClose=true]
    E --> I
    I --> J{lastSurfaceClosePreference tabStripClose=true reads pref}
    J -->|pref = close workspace| K[closeWorkspaceFromTabCloseButton or closeWorkspaceFromCloseTabGesture]
    J -->|pref = keep open| L[Close panel only workspace stays]
    I --> M{isRemoteTmuxMirror?}
    M -->|Yes| N[recordRemoteTmuxWorkspaceCloseAfterWindowClose]
    N --> O{markRemoteTmuxWorkspaceCloseAfterWindowCloseIfNeeded}
    O -->|shouldClose| P[remoteTmuxWorkspaceCloseButtonByTabId set]
    O -->|shouldKeepOpen| Q[remoteTmuxKeepWorkspaceOpenAfterSessionEnd=true]
    P --> R[splitTabBar didCloseTab detachMirrorWorkspaceKeptOpenLocally + closeWorkspace]
    Q --> S[RemoteTmuxController handleSessionEndedRemotely handleRemoteTmuxSessionEndedKeepingWorkspaceOpenIfNeeded convert to local workspace]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User closes last surface] --> B{Close trigger}
    B -->|Keyboard shortcut| C[TabManager.closePanelWithConfirmation]
    B -->|Tab close button| D[Workspace.markTabCloseButtonClose]
    B -->|Middle-click| E[Workspace.markTabStripMiddleClickClose]
    C --> F{shouldCloseWorkspaceOnLastSurfaceShortcut reads keepOpen pref}
    F -->|pref = close workspace| G[markExplicitClose + closeWorkspaceWithConfirmation]
    F -->|pref = keep open| H[Close panel only workspace stays]
    D --> I[Bonsplit shouldCloseTab tabStripClose=true]
    E --> I
    I --> J{lastSurfaceClosePreference tabStripClose=true reads pref}
    J -->|pref = close workspace| K[closeWorkspaceFromTabCloseButton or closeWorkspaceFromCloseTabGesture]
    J -->|pref = keep open| L[Close panel only workspace stays]
    I --> M{isRemoteTmuxMirror?}
    M -->|Yes| N[recordRemoteTmuxWorkspaceCloseAfterWindowClose]
    N --> O{markRemoteTmuxWorkspaceCloseAfterWindowCloseIfNeeded}
    O -->|shouldClose| P[remoteTmuxWorkspaceCloseButtonByTabId set]
    O -->|shouldKeepOpen| Q[remoteTmuxKeepWorkspaceOpenAfterSessionEnd=true]
    P --> R[splitTabBar didCloseTab detachMirrorWorkspaceKeptOpenLocally + closeWorkspace]
    Q --> S[RemoteTmuxController handleSessionEndedRemotely handleRemoteTmuxSessionEndedKeepingWorkspaceOpenIfNeeded convert to local workspace]
Loading

Reviews (32): Last reviewed commit: "fix: preserve window on remote last tab ..." | Re-trigger Greptile

Comment thread Sources/Workspace.swift

@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 `@Sources/Workspace.swift`:
- Around line 3475-3481: The functions markTabStripMiddleClickClose and the
unnamed function at line 3475 are missing the markExplicitClose side effect that
populates closeHistoryEligibleTabIds and closeHistoryEligiblePanelIds, which
breaks close-undo history functionality. Restore the missing markExplicitClose
call in both functions to ensure that tab and panel closes are properly tracked
for close history eligibility, allowing pushClosedPanelHistoryIfEligible to
capture UI-initiated closes correctly.
🪄 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: e1bc5d7f-fe5d-4d3e-91a0-fb6bd3a95a92

📥 Commits

Reviewing files that changed from the base of the PR and between 4e8a04f and 2504aee.

📒 Files selected for processing (2)
  • Sources/Workspace.swift
  • cmuxTests/LastSurfaceClosePreferenceTests.swift

Comment thread Sources/Workspace.swift 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)
Sources/Workspace.swift (1)

11752-11759: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass the close trigger explicitly for the gesture close path.

This branch now routes last-surface middle-click / non-close-button closes through closeWorkspaceFromCloseTabGesture without an explicit trigger, so it can fall back to the wrong confirmation policy if the default changes or is not the intended .shortcut trigger.

Suggested fix
             if tabCloseButtonClose {
                 owningTabManager?.closeWorkspaceFromTabCloseButton(self)
             } else {
-                owningTabManager?.closeWorkspaceFromCloseTabGesture(self)
+                owningTabManager?.closeWorkspaceFromCloseTabGesture(self, trigger: .shortcut)
             }

Based on learnings, “close-tab confirmation trigger must be explicit and wired through all close flows; TabManager.closeWorkspaceFromCloseTabGesture(_:trigger:) must NOT rely on default parameter values for the trigger.”

🤖 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/Workspace.swift` around lines 11752 - 11759, The else branch that
calls owningTabManager?.closeWorkspaceFromCloseTabGesture(self) is not passing
an explicit trigger parameter, causing it to rely on a default parameter value
for the close confirmation trigger. Modify the closeWorkspaceFromCloseTabGesture
call to include an explicit trigger parameter (the appropriate trigger value
should match the close trigger semantics for gesture-based closes) rather than
relying on the method's default parameter, ensuring the confirmation policy is
explicitly defined for this close path.

Source: Learnings

🤖 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/Workspace.swift`:
- Around line 11752-11759: The else branch that calls
owningTabManager?.closeWorkspaceFromCloseTabGesture(self) is not passing an
explicit trigger parameter, causing it to rely on a default parameter value for
the close confirmation trigger. Modify the closeWorkspaceFromCloseTabGesture
call to include an explicit trigger parameter (the appropriate trigger value
should match the close trigger semantics for gesture-based closes) rather than
relying on the method's default parameter, ensuring the confirmation policy is
explicitly defined for this close path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7547c7e0-e091-486e-ad05-cb96f6434c11

📥 Commits

Reviewing files that changed from the base of the PR and between 2504aee and 4ae2b71.

📒 Files selected for processing (1)
  • Sources/Workspace.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

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/Workspace.swift (1)

11107-11115: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Invert the keep-open preference when deciding to close the workspace.

TabManager.closeWorkspaceOnLastSurfacePreferenceEnabled() reads keepWorkspaceOpenWhenClosingLastSurface, so Line 11115 currently closes the workspace when the keep-open setting is enabled and keeps it open when disabled.

🐛 Proposed fix
-        return !tabStripClose || manager.closeWorkspaceOnLastSurfacePreferenceEnabled()
+        let keepWorkspaceOpen = manager.closeWorkspaceOnLastSurfacePreferenceEnabled()
+        return !tabStripClose || !keepWorkspaceOpen

Also applies to: 11753-11760

🤖 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/Workspace.swift` around lines 11107 - 11115, The logic in the
shouldCloseWorkspaceOnLastSurface method is inverted because
closeWorkspaceOnLastSurfacePreferenceEnabled() actually returns the value of
keepWorkspaceOpenWhenClosingLastSurface. In the return statement on line 11115,
negate the result of manager.closeWorkspaceOnLastSurfacePreferenceEnabled() by
wrapping it with the NOT operator (!) so that when the keep-open preference is
enabled, the workspace correctly stays open instead of closing. Apply the same
fix to the similar code block referenced at lines 11753-11760.
🤖 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/Workspace.swift`:
- Around line 11107-11115: The logic in the shouldCloseWorkspaceOnLastSurface
method is inverted because closeWorkspaceOnLastSurfacePreferenceEnabled()
actually returns the value of keepWorkspaceOpenWhenClosingLastSurface. In the
return statement on line 11115, negate the result of
manager.closeWorkspaceOnLastSurfacePreferenceEnabled() by wrapping it with the
NOT operator (!) so that when the keep-open preference is enabled, the workspace
correctly stays open instead of closing. Apply the same fix to the similar code
block referenced at lines 11753-11760.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 23ae6b47-d618-4c46-a9ff-043990343b7a

📥 Commits

Reviewing files that changed from the base of the PR and between 2504aee and 79dbf26.

📒 Files selected for processing (2)
  • Sources/Workspace.swift
  • cmuxTests/LastSurfaceClosePreferenceTests.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

Addressing the latest CodeRabbit summary: the source-artifacts item is a false positive for this PR. The branch diff against origin/main only changes Sources/TabManager.swift, Sources/Workspace.swift, cmux.xcodeproj/project.pbxproj, and cmuxTests/LastSurfaceClosePreferenceTests.swift; it does not add .claude, .agents, .cursor, .coderabbit.yaml, or .vercelignore. The description warning was addressed by adding explicit Testing, Demo Video, and Review Notes sections.

@austinywang
austinywang merged commit 6c74b49 into main Jun 22, 2026
35 checks passed
@austinywang
austinywang deleted the issue-6442-keep-workspace-open branch June 22, 2026 01:06

This branch was successfully deployed

1 active deployment
Preview – cmux — 53dad7f3 Deployed Jun 22, 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.

Setting ignored: Keep Workspace open when closing last surface

1 participant