Skip to content

Reap persistent SSH daemon on workspace close - #8073

Merged
austinywang merged 34 commits into
mainfrom
issue-8061-reap-persistent-server
Jul 15, 2026
Merged

austinywang merged 34 commits into
mainfrom
issue-8061-reap-persistent-server

Conversation

@austinywang

@austinywang austinywang commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • make final SSH workspace teardown own the persistent remote-daemon slot lifecycle
  • add authenticated serve --persistent-stop --slot <slot> control that waits a bounded interval for daemon ownership release
  • preserve .daemon_path, .slot, and .shell across reconnect, sleep, manual Disconnect, and same-identity controller replacement
  • serialize old-controller cleanup before replacement bootstrap so relay ownership cannot overlap
  • reject superseded transition work before it can clean up the newest controller
  • block same-identity replacement startup when transport cleanup fails, retain the failed owner, and retry it on later lifecycle events
  • fall back to authenticated slot shutdown when final cleanup cannot prove relay-metadata ownership, while retaining fail-closed behavior for every other cleanup failure
  • validate the expected workspace slot before any remote stop/delete operation and keep daemon rejection details out of user-facing errors
  • monitor one explicit relay lease port per daemon (O(1)), with defense-in-depth reaping and shell-directory cleanup
  • close the daemon PTY hub so its interactive shell child exits before relay state is removed

Test-first proof

The branch preserves red-before-green history:

  1. 5274a5a915 adds the original failing clean-close regression before the persistent-slot teardown implementation.
  2. 64ddc89ef5, 7ed5ea64ef, and dcded14a93 add/tighten lifecycle regressions, including A → B → A superseded-transition cleanup, before the follow-up production fix.
  3. 5c5065d2b8 adds the stale-transition guard and the narrowly scoped status-64 authenticated slot-stop fallback.
  4. f99e445120 adds explicit final test teardown and increases only the test-harness observation timeout; no production behavior changes.

The A → B → A regression was reproduced red on the pre-fix production code and green after 5c5065d2b8. On final merged HEAD 64d4f37c39, the app-host lifecycle suite executed 8 tests with 8 passed, 0 failed, and 0 skipped; the .xcresult specifically names supersededDifferentIdentityTransitionCannotDestroyLatestPersistentSession() as passed.

Latest-main integration

  • merged origin/main through 496a1c4bb9 in 64d4f37c39
  • the only conflict was duplicate arithmetic-overflow repairs in CanvasModel.reorderPanel; resolution takes main's canonical 6a00674d0f implementation verbatim
  • no unmerged paths remain, origin/main is an ancestor of HEAD, and the final PR diff no longer contains a Canvas or web change

Verification on final HEAD 64d4f37c39

  • cmux-unit app-host test bundle built and linked with build-for-testing
  • RemoteSessionCleanupLifecycleTests: 8/8 passed from a serialized app-host run; 0 failed/skipped
  • CmuxRemoteSession: 89/89 passed
  • CmuxCore: 51/51 passed
  • CmuxCanvasUI: 45/45 passed, including reorderPanelClampsExtremeOffsetsWithoutOverflow
  • remote daemon: go test ./..., go test -race ./..., go vet ./..., and gofmt clean
  • remote daemon cross-compiled as linux/amd64
  • scripts/check-pbxproj.sh
  • test wiring lint: 494 files
  • workspace package grouping and SwiftPM lockfile policy
  • strict test-determinism gate: 0 findings
  • git diff --check for both the worktree and origin/main...HEAD
  • isolated tagged Debug app build succeeded with ./scripts/reload.sh --tag issue-8061-reap-persistent-server

Native process proof

A linux/amd64 daemon binary was exercised as real separate processes with an active interactive Bash PTY and realistic relay metadata:

  • transient cleanup kept the persistent server, Bash child, .daemon_path, .slot, and .shell alive
  • final cleanup stopped the persistent server and Bash child and removed socket/auth/daemon/slot/tty/shell state
  • failed or unsupported shutdown retained persistent ownership state
  • deleting an observed exact-port lease reaped the daemon and shell via the defense-in-depth path

This proof passed natively on Ubuntu x86_64 and inside fresh Ubuntu 24.04.4 LTS x86_64 userspace, matching the reporter's platform.

Localization audit

No UI, Settings, menu, shortcut, schema, or localized message-catalog copy is changed by the final PR diff. The Swift/Go diff was checked for new user-facing surfaces; the only error-output change removes remote-provided detail and reuses the existing generic shutdown rejection. No localization entries are required.

Scope

This fixes the remote persistent-server leak on clean workspace close. Mac-side SSH/ControlMaster cleanup after an app crash remains explicitly out of scope.

Issue: #8061

Closes #8061

@vercel

vercel Bot commented Jul 14, 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 15, 2026 7:55am
cmux-staging Building Building Preview, Comment Jul 15, 2026 7:55am

@coderabbitai

coderabbitai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change adds scoped relay cleanup, authenticated persistent-daemon shutdown, daemon lock waiting, lease-aware reaping, and serialized workspace remote-session transitions. macOS teardown selects transport or persistent-slot cleanup, while remote daemon and lifecycle tests cover these flows.

Changes

Relay teardown lifecycle

Layer / File(s) Summary
Scoped relay cleanup and workspace transitions
Packages/macOS/CmuxRemoteSession/..., Sources/Workspace*.swift, cmuxTests/*, cmux.xcodeproj/project.pbxproj
Cleanup distinguishes transport metadata removal from persistent-slot teardown; workspace transitions retain controllers for deferred cleanup, serialize replacement, and handle cleanup failures.
Persistent stop control plane
daemon/remote/cmd/cmuxd-remote/{main.go,persistent_lifecycle.go}, daemon/remote/cmd/cmuxd-remote/*_test.go
The serve --persistent-stop --slot path sends authenticated shutdown requests and waits for daemon lock release, with validation and lifecycle tests.
Lease-aware daemon shutdown
daemon/remote/cmd/cmuxd-remote/*, daemon/remote/README.md, docs/remote-daemon-spec.md
Persistent servers poll exact slot leases, accept cooperative shutdown requests, drain active connections, and remove shell state after lease loss.
Relay lease transport wiring
Packages/macOS/CmuxCore/{Sources,Tests}/..., .vercelignore
Persistent SSH transport arguments include --persistent-lease-port only for valid configured relay ports; deployment ignore rules preserve the relay catalog generator.

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

Sequence Diagram(s)

sequenceDiagram
  participant Workspace
  participant RemoteSessionCoordinator
  participant CmuxdRemote
  participant PersistentDaemon
  participant RelayState
  Workspace->>RemoteSessionCoordinator: stop(cleanupScope)
  RemoteSessionCoordinator->>CmuxdRemote: serve --persistent-stop --slot
  CmuxdRemote->>PersistentDaemon: authenticated shutdown RPC
  PersistentDaemon->>RelayState: wait for daemon lock and poll slot lease
  RelayState-->>PersistentDaemon: lease absent
  PersistentDaemon-->>CmuxdRemote: shutting_down
Loading

Possibly related PRs

  • manaflow-ai/cmux#8116: Extends the coordinator stop flow in the same stopAllLocked teardown implementation.
🚥 Pre-merge checks | ✅ 23 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The .vercelignore change allowing a scripts file into Vercel deployment is unrelated to the #8061 daemon-cleanup work. Split the Vercel ignore change into a separate PR unless it is required for the workspace-close cleanup fix.
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 (23 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement #8061 by stopping the persistent slot on workspace close, removing .shell state, and handling stale relay metadata and lease reaping.
Cmux Swift Actor Isolation ✅ Passed Production changes are either @MainActor UI code, queue-confined coordinator code, or pure Sendable values; no new isolation mistake is introduced.
Cmux Swift Blocking Runtime ✅ Passed No new production Swift blocking primitives were introduced; lifecycle changes use MainActor tasks/continuations, and existing semaphore/asyncAfter code is preexisting.
Cmux Browser Automation Off-Main ✅ Passed No changes in the rule-scoped browser automation files, and the PR diff shows no browser.*, socketWorkerMethods, or processV2Command routing changes.
Cmux Expensive Synchronous Load ✅ Passed No new expensive agent-history load was added or moved; the diff is remote-session cleanup only, and the existing Workspace.swift load remains a cached fallback.
Cmux Cache Substitution Correctness ✅ Passed No persistence/snapshot path swaps a fresh authoritative read for a stale cache; the new cleanup ownership map is guarded by slot-file checks and controller-identity validation.
Cmux No Hacky Sleeps ✅ Passed PASS: no covered TS/JS/shell/build-runtime script changes add sleeps; the new polling/wait logic is in Go/Swift runtime code outside this rule’s scope.
Cmux Algorithmic Complexity ✅ Passed No new nested rescans or repeated sorts/filters in the changed hot paths; the added loops are single-pass or bounded cleanup/polling logic.
Cmux Swift Concurrency ✅ Passed No disallowed legacy async patterns were introduced; the new Tasks are stored/cancelled, and the queue hop is a confinement bridge.
Cmux Swift @Concurrent ✅ Passed No invalid @concurrent use found; the new async path is a queue-hop wrapper in a queue-confined class, while workspace orchestration stays @MainActor.
Cmux Swift File And Package Boundaries ✅ Passed New logic moved into small CmuxRemoteSession/CmuxCore package files; the app-target Workspace extension is only orchestration glue, and Workspace.swift shrank.
Cmux Swiftpm Lockfiles ✅ Passed Package.swift only adds linker settings; no Package.resolved or Xcode package-reference changes appear in the diff.
Cmux Swift Logging ✅ Passed No prohibited production logging was added; the new RemoteRelay logger is nonisolated private and the new error logs redact details.
Cmux User-Facing Error Privacy ✅ Passed PASS: the new shutdown path returns only generic user-facing errors, and raw remote details stay in internal logs/tests, not visible UI text.
Cmux Full Internationalization ✅ Passed No xcstrings or locale files changed; added text is debug logs, CLI tokens, and internal docs, which the rule allows.
Cmux Swiftui State Layout ✅ Passed Touched files add lifecycle plumbing only; no new SwiftUI views, GeometryReader, row-store refs, or render-time state writes were introduced.
Cmux Architecture Rethink ✅ Passed PASS: The Swift changes add an explicit Workspace-owned transition/cleanup model and scoped stop API; no sleeps or delayed-dispatch repairs were introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: The PR diff vs origin/main adds no NSWindow/NSPanel/NSWindowController/WindowGroup or cmuxAuxiliaryWindowIdentifiers changes; it only refactors remote-session cleanup and daemon teardown.
Cmux Source Artifacts ✅ Passed .vercelignore only narrows an ignore rule and whitelists a real checked-in script; no generated logs, caches, or scratch artifacts were added.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PASS: the PR adds no new test-only/debug members in production Sources; the existing ForTesting override is pre-existing and only consumed by tests.
Cmux No Ambient Global State ✅ Passed No new ambient global state: new behavior lives on Workspace/RemoteSessionCoordinator extensions; the only new top-level type is a non-caseless enum with cases.
Title check ✅ Passed The title is concise and accurately summarizes the main change to reap the persistent SSH daemon on workspace close.
Description check ✅ Passed The description covers the main change, testing, and verification, but it omits the template's demo video, review trigger, and checklist sections.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8061-reap-persistent-server

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 14, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds final teardown for persistent SSH daemons. The main changes are:

  • Separate transient transport cleanup from final persistent-slot cleanup.
  • Add authenticated slot shutdown with ownership checks and bounded waiting.
  • Retain failed cleanup owners for later lifecycle retries.
  • Serialize controller replacement and reject superseded transitions.
  • Monitor relay leases and close daemon PTY children during teardown.

Confidence Score: 5/5

This looks safe to merge.

  • Failed nonpersistent cleanup owners now remain available for later retries.
  • Conflicting replacement startup stays blocked when cleanup fails.
  • Persistent shutdown failures preserve ownership state rather than deleting live metadata.
  • No distinct blocking issue remains in the updated lifecycle paths.

Important Files Changed

Filename Overview
Sources/Workspace+RemoteSessionLifecycle.swift Serializes remote-session transitions, retains failed cleanup owners, and blocks conflicting replacement startup.
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+RelayProvisioning.swift Adds ownership-checked transport and persistent-slot cleanup scripts.
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift Propagates cleanup results and adds the authenticated fallback for unavailable relay metadata.
daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go Adds persistent-slot shutdown, relay lease monitoring, and daemon child cleanup.

Reviews (22): Last reviewed commit: "fix: retain failed transient cleanup own..." | Re-trigger Greptile

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

🤖 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 `@daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go`:
- Around line 113-123: Bound the lock wait in waitForPersistentDaemonStop by
using nonblocking exclusive flock attempts with a deadline based on
persistentDaemonStopWaitTimeout. Retry briefly while the lock is unavailable,
return other flock errors immediately, and return a descriptive timeout error
when the deadline expires; unlock successfully acquired locks as before.
- Around line 20-40: Update existingPersistentDaemonPathsForSlot so a missing
storedSocketDir is treated like an already-removed daemon: when
verifyPrivateDaemonDirectory(storedSocketDir) returns os.ErrNotExist, return the
existing paths with found=false and no error. Continue propagating other
verification errors and preserve the current socket override behavior for
existing directories.
- Around line 42-55: Update stopPersistentDaemon’s missing-token handling so
that when paths.tokenFile is absent but paths.socket still exists, it removes
the stale socket and returns success, matching the existing stale-socket cleanup
behavior used after dial errors. Preserve the current clean success path when
both files are absent and continue returning other token-read errors.
- Around line 125-163: Update persistentDaemonSlotLeasePresent so transient
os.ErrNotExist failures from entry.Info and os.ReadFile are skipped with
continue, allowing the scan to inspect remaining relay entries; preserve
returning other errors and the existing matching-slot behavior.
- Around line 130-163: Update persistentDaemonSlotLeasePresent and its polling
flow to avoid rescanning and rereading ~/.cmux/relay on every one-second tick.
Add a cached or indexed slot-to-daemon lookup that is refreshed only when relay
contents change, while preserving the existing ownership, regular-file,
valid-port, and missing-directory behavior.
🪄 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: c6ec4f9c-845c-4e3b-b62c-cf83d8d7d054

📥 Commits

Reviewing files that changed from the base of the PR and between bcae8ea and a466a03.

📒 Files selected for processing (11)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+RelayProvisioning.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemoteRelayCleanupScope.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteRelaySlotTeardownTests.swift
  • daemon/remote/README.md
  • daemon/remote/cmd/cmuxd-remote/main.go
  • daemon/remote/cmd/cmuxd-remote/main_test.go
  • daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
  • daemon/remote/cmd/cmuxd-remote/persistent_lifecycle_test.go
  • docs/remote-daemon-spec.md

Comment thread daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
Comment thread daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
Comment thread daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
Comment thread daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go Outdated
Comment thread daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go Outdated
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.

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 5306-5314: Serialize replacement-controller startup with cleanup
in the session transition flow around previousController?.stop: when
preservesPersistentSlot is true, wait until the old controller’s transport
cleanup has completed before assigning or bootstrapping
remoteSessionCleanupController; otherwise preserve the existing cleanup
behavior. Ensure the replacement cannot rewrite the preserved relay port’s files
while the old cleanup is still pending.
🪄 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: 77cb684e-2be8-40dd-be5d-df7104fa3a11

📥 Commits

Reviewing files that changed from the base of the PR and between a466a03 and bc16905.

📒 Files selected for processing (14)
  • Packages/macOS/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration+SSHBatchCommands.swift
  • Packages/macOS/CmuxCore/Tests/CmuxCoreTests/WorkspaceRemoteConfigurationSSHBatchCommandsTests.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+RelayProvisioning.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/RemoteRelayCleanupScope.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteRelaySlotTeardownTests.swift
  • Sources/Workspace.swift
  • cmuxTests/RemoteDisconnectLifecycleTests.swift
  • daemon/remote/README.md
  • daemon/remote/cmd/cmuxd-remote/main.go
  • daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
  • daemon/remote/cmd/cmuxd-remote/persistent_lifecycle_test.go
  • docs/remote-daemon-spec.md

Comment thread Sources/Workspace.swift Outdated
@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

@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

Caution

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

⚠️ Outside diff range comments (1)
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+RelayProvisioning.swift (1)

36-41: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject malformed persistent slots instead of treating them as nonpersistent.

A non-nil invalid slot normalizes to nil, selecting the nonpersistent branch. If the slot file is already absent, final cleanup then skips daemon shutdown, removes its metadata/shell, reports success, and lets upstream discard retry ownership. Preserve the distinction between “not configured” and “configured but invalid,” returning a failing script for the latter.

Proposed fix
 let normalizedSlot = normalizedPersistentDaemonSlotForRemoteCleanup(persistentDaemonSlot)
+if persistentDaemonSlot != nil, normalizedSlot == nil {
+    return "exit 64"
+}

Apply the same check in remoteRelayTransportMetadataCleanupScript.

As per coding guidelines, missing correctness-critical lifecycle state must fail closed rather than guess.

Also applies to: 80-82

🤖 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
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator`+RelayProvisioning.swift
around lines 36 - 41, The normalization flow in the relay provisioning cleanup
must distinguish an absent persistent daemon slot from a non-nil malformed slot.
Update the logic around normalizedPersistentDaemonSlotForRemoteCleanup in the
persistent-stop path and remoteRelayTransportMetadataCleanupScript so invalid
configured slots return a failing script, while nil remains the nonpersistent
case; do not proceed with cleanup or report success for malformed values.

Source: Coding guidelines

🤖 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 `@daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go`:
- Around line 193-208: The cleanup in
removePersistentDaemonRelayShellDirectoryIfUnleased must not perform an unlocked
Lstat-then-RemoveAll sequence. Coordinate it with runPersistentDaemonServer
using the same relay-port lock or lease token, and re-check the .slot while that
lock is held immediately before removing the .shell directory, preserving the
behavior that an active or newly created lease prevents cleanup.

---

Outside diff comments:
In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator`+RelayProvisioning.swift:
- Around line 36-41: The normalization flow in the relay provisioning cleanup
must distinguish an absent persistent daemon slot from a non-nil malformed slot.
Update the logic around normalizedPersistentDaemonSlotForRemoteCleanup in the
persistent-stop path and remoteRelayTransportMetadataCleanupScript so invalid
configured slots return a failing script, while nil remains the nonpersistent
case; do not proceed with cleanup or report success for malformed values.
🪄 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: 1f202c51-5ebd-452c-956b-ad62eb218b84

📥 Commits

Reviewing files that changed from the base of the PR and between bc16905 and e715dda.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (14)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+Lifecycle.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+RelayProvisioning.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemotePortScanGatingTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteRelaySlotTeardownTests.swift
  • Sources/Workspace+RemoteSessionLifecycle.swift
  • Sources/Workspace.swift
  • Sources/WorkspaceRemoteSessionHostAdapter.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/RemoteDisconnectLifecycleTests.swift
  • cmuxTests/RemoteSessionCleanupLifecycleTests.swift
  • daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
  • daemon/remote/cmd/cmuxd-remote/persistent_lifecycle_test.go
💤 Files with no reviewable changes (1)
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemotePortScanGatingTests.swift

Comment thread daemon/remote/cmd/cmuxd-remote/persistent_lifecycle.go
@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

Comment thread Sources/Workspace+RemoteSessionLifecycle.swift
@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

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

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

Comment thread Sources/Workspace+RemoteSessionLifecycle.swift Outdated
@austinywang
austinywang merged commit b250ada into main Jul 15, 2026
8 checks passed
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jul 16, 2026
* test: reproduce leaked persistent SSH slot teardown

* fix: reap persistent SSH daemon on workspace close

* fix: preserve persistent slot state until shutdown

* fix: harden persistent daemon lifecycle ownership

* fix: retain failed remote cleanup ownership

* chore: track merged event bus file budget

* fix: serialize remote session cleanup handoff

* fix: restore remote lifecycle test compatibility

* test: avoid virtual sleep determinism false positive

* fix: link terminal core tests with libc++

* test: make Ghostty runtime stubs weak fallbacks

* fix: include relay catalog generator in Vercel builds

* test: cover persistent cleanup ownership handoff

* test: cover remote cleanup lifecycle gaps

* test: keep remote lifecycle assertion target-visible

* test: tighten remote cleanup lifecycle proof

* fix: close persistent cleanup lifecycle gaps

* fix: clamp extreme canvas reorder offsets

* test: stabilize remote cleanup lifecycle teardown

* test: reject malformed persistent cleanup slots

* fix: reject malformed persistent cleanup slots

* test: expose superseded remote cleanup race

* fix: guard persistent cleanup ownership transitions

* fix: block relay replacement after cleanup failure

* test: retain failed transient cleanup owner

* fix: retain failed transient cleanup owners

(cherry picked from commit b250ada)
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.

cmuxd-remote --persistent-server process leaks on every clean SSH workspace close

1 participant