Skip to content

Option to close remote SSH workspace instead of demoting to local when the last SSH surface exits #3137

@moonfruit

Description

@moonfruit

Summary

When a workspace is created via cmux ssh user@host, closing the last SSH surface in that workspace does not close the workspace. Instead, the workspace is demoted to a local one and a fresh local shell surface is automatically spawned in its place. I'd like an option to opt out of this behavior so that the workspace (and, if it's the only workspace, the window) is closed when the last SSH surface exits.

Current behavior

Starting from a workspace created by cmux ssh user@host and exiting the only SSH surface (e.g. typing exit or Ctrl+D in the remote shell):

  1. Ghostty reports child-exit and TabManager.closePanelAfterChildExited is invoked (Sources/TabManager.swift:4601).
  2. The branch guarded by keepsRemoteWorkspaceOpen = tab.panels.count <= 1 && tab.shouldDemoteWorkspaceAfterChildExit(surfaceId:) is taken (Sources/TabManager.swift:4604-4623). The comment on that branch explicitly states: "Exiting the last SSH surface should demote the workspace back to a local one."
  3. shouldDemoteWorkspaceAfterChildExit returns true for any isRemoteWorkspace (Sources/Workspace.swift:8280-8282).
  4. Close is routed through closeRuntimeSurfaceWorkspace.closePanel(force: true)bonsplitController.closeTab(...).
  5. In the close-tab delegate (Sources/Workspace.swift:12127), clearRemoteConfigurationIfWorkspaceBecameLocal() clears the remote configuration once panels.isEmpty, and then createReplacementTerminalPanel() is called (Sources/Workspace.swift:12218-12234), creating a new local shell terminal that becomes the focused surface.

Result: the workspace stays open, now connected to nothing remote, with a freshly spawned local shell the user did not ask for.

Desired behavior

Provide a user-facing option (Settings toggle + settings.json key, mirroring the existing keepWorkspaceOpenWhenClosingLastSurface design) such that when it is enabled:

  • If the last surface of a cmux ssh-created (remote) workspace exits, the workspace is closed outright.
  • If it is the last workspace in the window, the window is closed (i.e. fall through to closeMainWindowContainingTabId / closeWorkspace(tab), as the non-remote branch already does at Sources/TabManager.swift:4627-4640).
  • No replacement local shell surface is spawned.

The existing keepWorkspaceOpenWhenClosingLastSurface preference (LastSurfaceCloseShortcutSettings, Sources/TabManager.swift:63) only affects the Cmd+W shortcut path (shouldCloseWorkspaceOnLastSurfaceShortcut / closePanelByShortcut at Sources/TabManager.swift:4472-4513) and does not influence the child-exit / SSH-session-end demote path, so it cannot be reused to solve this.

Proposed implementation sketch

  • Add a new preference, e.g. closeRemoteWorkspaceOnLastSSHSurface (default: false to preserve current behavior).
  • In closePanelAfterChildExited, consult the preference before taking the keepsRemoteWorkspaceOpen branch; when enabled, skip the demote path and fall through to the existing "last panel → closeWorkspace(tab) / closeMainWindowContainingTabId" branch.
  • Expose it in Settings and the settings.json schema, alongside keepWorkspaceOpenWhenClosingLastSurface.
  • Add regression tests analogous to the existing WorkspaceRemoteConnectionTests (e.g. testRemoteTerminalSessionEndClosesWorkspaceWhenOptIn).

Motivation

cmux ssh user@host is an explicit "open this remote host as a workspace" action. When the SSH session ends intentionally (exit / Ctrl+D) or unintentionally (network drop), the current demotion-plus-auto-local-shell behavior is surprising:

  • The workspace lingers with a local shell that has no relationship to the remote target it was opened for.
  • Users who treat the workspace as scoped to the SSH target have to manually close the workspace every time.
  • It conflicts with how other terminal apps handle ssh sessions (the session ending closes the tab/window).

Making this opt-in preserves the current default for users who rely on the demote-to-local flow (e.g. the PRs #2104 and #2697 scenarios) while giving users who prefer "SSH session ends → workspace closes" a clean way to get that behavior.

Related prior work

All three go in the opposite direction (keep the workspace, fall back to local). This issue asks only for an opt-in alternative, not a change of default.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions