Skip to content

Fix SSH relay deadlock after app restart - #9105

Merged
austinywang merged 46 commits into
mainfrom
issue-8894-ssh-relay-controlmaster-lease
Jul 29, 2026
Merged

austinywang merged 46 commits into
mainfrom
issue-8894-ssh-relay-controlmaster-lease

Conversation

@austinywang

@austinywang austinywang commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • recover a pinned reverse-relay bind conflict by reaping the inherited cmux-owned SSH ControlMaster, instead of retrying the same occupied port forever
  • require the exact resolved cmux ControlPath, the retained workspace generation, exclusive cross-process ownership, and matching remote relay metadata before sending ssh -O exit
  • invalidate every local workspace sharing the reaped socket and re-enter the normal foreground-authentication, daemon, proxy, and relay reconnect state machine
  • preserve the persistent daemon slot, shell session, remote relay metadata, and pinned lease port while the SSH transport is replaced
  • fail closed for custom/unresolved ControlPaths, live foreign owners, stale workspace generations, mismatched metadata, transient probe failures, and rejected master exits

Closes #8894.

Issue: #8894

Reproduction

From #8894:

  1. Open a native SSH workspace to a remote host and let the relay come up.
  2. Restart the cmux app (quit + relaunch) while the host's ControlMaster is alive — i.e. within ControlPersist.
  3. The new instance reattaches its daemon session over the surviving master and the relay never rebinds.

Architecture

The reverse -R listener belongs to the SSH connection, so a ControlPersist master inherited from the previous app instance also inherits the stale listener. Reusing that master keeps the listener alive and guarantees that retrying the pinned port cannot succeed.

OpenSSH cannot reliably cancel this inherited forward from the listen address alone. ssh -O cancel -R must identify the original full forward, including the old app's ephemeral local relay target port, and that target is intentionally not persisted after a crash. A listen-only cancel therefore cannot be the recovery primitive. The authorized operation is ssh -O exit against the exact cmux-owned socket.

The broker now owns the disruptive recovery transaction:

  1. retain an app/workspace generation for the exact resolved cmux ControlPath;
  2. acquire the cross-process authentication and exclusive ownership locks;
  3. verify remote relay ownership using the persistent daemon slot for restored persistent workspaces, or exact relay credentials for nonpersistent workspaces;
  4. issue ssh -O exit;
  5. emit an ID-bearing reap event to every coordinator sharing that socket;
  6. invalidate daemon/relay/proxy transport state and reconnect normally.

The remote persistent daemon metadata is deliberately preserved. This lets the replacement SSH master bind the same lease port and lets the existing remote PTY survive the local transport restart. Blocking SSH probe/exit work runs outside the main actor through an @concurrent boundary, and stopping a workspace detaches promptly while an already-authorized shared reap completes.

Verification

Final pushed head: 3b6d7c268c235120e1a5c12a4213f884f4c43df0.

  • swift test --package-path Packages/macOS/CmuxRemoteSession — 145 tests passed in 25 suites
    • persistent rotated credentials authorize only the exact slot
    • nonpersistent workspaces require exact relay credentials
    • mismatched/transient metadata, rejected -O exit, custom ControlPaths, and live foreign owners fail closed
    • sibling coordinators invalidate on a successful reap
    • stopping detaches while a reap is in flight
  • focused inherited-master suite — 3/3 passed
  • ./scripts/lint-pbxproj-test-wiring.sh — passed for 626 test files
  • python3 scripts/check-package-resolved-policy.py — passed
  • python3 scripts/check-workspace-package-groups.py — passed
  • git diff --check — passed
  • final cloud build — passed on exact head in https://github.com/manaflow-ai/cmux/actions/runs/30467495470
  • final build warning audit — 0 cmux-owned Swift warnings; warning budget respected
  • all new Swift files are below 500 lines; neither Swift budget TSV was touched (scripts/swift_file_length_budget.py is absent on this branch)

Tagged dev-build restart dogfood

Built and launched with:

CMUX_SKIP_ZIG_BUILD=1 /Users/austinwang/manaflow/cmuxterm-hq/scripts/reload-cloud.sh --tag issue-8894-ssh-relay-controlmaster-lease --launch

Against an isolated native-SSH workspace and real local sshd:

  1. Confirmed workspace C2EFB5A3-F621-4053-B023-611C021E95FC connected through master PID 76234, with sshd listening on pinned port 53150 and returning a relay-auth challenge.
  2. Exported CMUX_8894_PERSIST_MARKER=alive in persistent shell PID 45603.
  3. Sent SIGKILL only to the tagged app.
  4. Confirmed master 76234 survived under PPID 1, sshd still owned port 53150, and the stale relay returned zero bytes.
  5. Relaunched the tagged app without any manual SSH cleanup.
  6. Confirmed master 76234 exited, fresh master 82444 appeared on the same resolved ControlPath, port 53150 rebound, and the relay-auth challenge returned.
  7. Confirmed the workspace daemon/proxy were connected and the remote shell was still PID 45603 with CMUX_8894_PERSIST_MARKER=alive.

No local xcodebuild test or XCUITest was run, per repository policy.

Review

  • CodeRabbit is successful and every inline review thread is resolved.
  • The canonical structured autoreview passed its merge-conflict gate against origin/main, but the Codex engine was blocked by the account's hard usage limit on all three required retries. This is recorded as review-infrastructure unavailability, not as a clean structured-review result; no finding was suppressed.

@coderabbitai

coderabbitai Bot commented Jul 29, 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

Reverse-relay SSH transport now resolves effective ControlMaster options, tracks cross-process socket ownership, recovers inherited forwards, and falls back to a dedicated SSH process. Foreground authentication, cleanup retries, lifecycle handling, localized statuses, and associated tests were updated.

Changes

SSH relay ownership and recovery

Layer / File(s) Summary
SSH option and authentication contracts
Packages/macOS/CmuxCore/..., Packages/macOS/CmuxFoundation/..., Sources/SSHPTYAttachStartupCommandBuilder.swift, Sources/Workspace*.swift, Packages/macOS/CmuxControlSocket/...
Effective SSH options and resolved ControlPaths flow through batch commands, foreground authentication, and control-socket APIs.
ControlMaster ownership and cleanup
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/...
Lease tracking, cross-process locking, adoption handoffs, exclusive recovery, and bounded cleanup retries are implemented.
Reverse-relay lifecycle
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/..., Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/...
Reverse relay startup selects ControlMaster forwarding or a standalone SSH process, handles readiness and termination, and recovers inherited forwards.
Validation and support
Packages/macOS/CmuxRemoteSession/Tests/..., cmuxTests/..., Resources/Localizable.xcstrings, Sources/RemoteSessionStrings+App.swift
Tests cover ownership, cleanup, transport fallback, relay lifecycle, inherited-forward recovery, and localized retry statuses.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: azooz2003-bit


Important

Pre-merge checks failed

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

❌ Failed checks (3 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error FAIL: Production sources add asyncAfter/Task.sleep backoffs and new NSLocks (relay lifecycle, adoption handoff, ownership registry), which the blocking-runtime rule flags. Replace timing waits with explicit signals/timer abstractions, and avoid manual locks in production unless a documented low-level bridge truly cannot be actor-isolated.
Cmux Full Internationalization ❌ Error Three new user-facing xcstrings keys have only en/ja; the catalog supports 20 locales, so 18 locales are missing for each key. Add translated entries for all 20 supported locales to the three new keys in Resources/Localizable.xcstrings, then keep the String(localized:defaultValue:) wiring.
Cmux Architecture Rethink ❌ Error The PR adds production Task.sleep retry loops for relay restart and cleanup, which are timing-based repair paths around shared-state/socket races instead of making the bad state impossible. Move ownership/recovery into one source of truth (the broker/lease registry) and remove delayed retry loops from production paths; fail synchronously or re-enter only on an explicit ownership event.
Docstring Coverage ⚠️ Warning Docstring coverage is 19.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description is substantive, but it omits required template sections like Demo Video, Review Trigger, and Checklist. Add the missing template sections: Testing, Demo Video, Review Trigger, and Checklist, and keep the Summary/Why and verification details aligned to the template.
✅ Passed checks (20 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement #8894’s ownership-gated ControlMaster recovery, preserve exact ControlPath reuse, and fail closed on the listed invalid cases.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes stand out; the added helpers, tests, and localization support all appear tied to the relay recovery work.
Cmux Swift Actor Isolation ✅ Passed Touched prod types are already MainActor or use locked/@unchecked Sendable wrappers; no new implicit MainActor or unsafe shared-mutable Sendable issue.
Cmux Browser Automation Off-Main ✅ Passed Patch only touches SSH/remote-session code; diff search found no browser/WebKit/socketWorkerMethods/processV2Command changes, so the browser automation rule isn’t implicated.
Cmux Expensive Synchronous Load ✅ Passed PASS: The diff refactors remote relay/control-master paths; no added or moved RestorableAgentSessionIndex.load or similar sync history loads appear in the changed interactive code.
Cmux Cache Substitution Correctness ✅ Passed readyDaemonStatus is event-driven on .ready and restoreReadyDaemonStatusLocked is cold-guarded by daemonReady/readyDaemonStatus with a documented re-publish rationale.
Cmux No Hacky Sleeps ✅ Passed PASS: Diff only touches Swift sources/tests and resources; no covered non-Swift runtime files (TS/JS/shell/build scripts) introduced sleeps/timers.
Cmux Algorithmic Complexity ✅ Passed Changed production paths use O(1) dictionary updates and a fixed 3-retry counter; no nested scans or repeated sorts/filters were introduced.
Cmux Swift Concurrency ✅ Passed New async work is lifecycle-bound or OS-callback-bound: Process/FileHandle callbacks, stored/cancelled Tasks, and no new Combine state.
Cmux Swift @Concurrent ✅ Passed No changed source adds invalid @concurrent or nonisolated-async work; new async paths either stay on MainActor intentionally or explicitly hop via Task.
Cmux Swift Package Boundaries ✅ Passed The new control-master/recovery logic is extracted into CmuxRemoteSession/CmuxCore; the app-target edits are workspace/terminal glue and lifecycle orchestration around it.
Cmux Swiftpm Lockfiles ✅ Passed Diff only changes source/tests; no Package.swift, Package.resolved, .gitignore, workflow, or Xcode project package-reference files were modified.
Cmux Swift Logging ✅ Passed No added print/debugPrint/dump/NSLog; reverse-relay uses existing DEBUG-only debugLog, and the new Logger is nonisolated private let.
Cmux User-Facing Error Privacy ✅ Passed User-facing relay/ownership copy is generic; raw SSH stderr only feeds debug/internal paths, not published errors or recovery text.
Cmux Swiftui State Layout ✅ Passed Only legacy model state in Workspace.swift changed; no new SwiftUI views, GeometryReader, store-held row subtrees, or render-time state writes were introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No added/edited standalone window code or cmuxAuxiliaryWindowIdentifiers lines appear in the diff; changes are SSH/reverse-relay and tests only, so the rule isn’t triggered.
Cmux Source Artifacts ✅ Passed All 60 changed paths are source/tests/resources; no artifact-like paths (.tmp, logs, DerivedData, caches, screenshots, etc.) were added.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The changed production files add only runtime relay-recovery logic; no new #if DEBUG/test-only members or widened test seams appear in the diff.
Cmux No Ambient Global State ✅ Passed No new ambient global state: the diff adds injectable instance types/pure helpers, with no new top-level mutable vars, singletons, or file-scope API functions.
Title check ✅ Passed The title is concise and matches the main change: fixing SSH relay deadlock after app restart.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8894-ssh-relay-controlmaster-lease

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.

@austinywang
austinywang marked this pull request as ready for review July 29, 2026 00:25

@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 `@cmuxTests/WorkspaceRemoteConnectionTests.swift`:
- Around line 1975-1985: Strengthen this test by adding a positive assertion
that reverse-relay startup was reached before asserting controlOperations is
empty. Use a relay-specific signal captured by the scripted runner, such as the
relay metadata installation command or relay-scoped SSH invocation, and retain
the existing negative ControlMaster assertion.
🪄 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 Plus

Run ID: 668c81e2-5d3c-4021-adf8-99323026d8c6

📥 Commits

Reviewing files that changed from the base of the PR and between 2757d9d and 54adb51.

📒 Files selected for processing (7)
  • 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/Tests/CmuxRemoteSessionTests/RemoteSessionSSHRemoteCommandOverrideTests.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift
💤 Files with no reviewable changes (4)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxCore/Tests/CmuxCoreTests/WorkspaceRemoteConfigurationSSHBatchCommandsTests.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+RelayProvisioning.swift
  • Packages/macOS/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration+SSHBatchCommands.swift

Comment thread cmuxTests/WorkspaceRemoteConnectionTests.swift Outdated
@cursor

cursor Bot commented Jul 29, 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

🤖 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 `@cmuxTests/WorkspaceRemoteConnectionTests.swift`:
- Around line 1952-1959: Update the relay startup test around the relay
invocation and relayStatusObservation to capture SSH arguments specifically for
the dedicated reverse-relay launch, then use that invocation as the startup
expectation. Assert the captured arguments include “-S none” and exclude the
configured shared ControlPath, while retaining the existing controlOperations
assertion only as supplemental coverage; do not use the generic “Remote SSH
relay unavailable” status string as the startup signal.
🪄 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 Plus

Run ID: d3d42549-853a-40c8-8042-593c311c892b

📥 Commits

Reviewing files that changed from the base of the PR and between 54adb51 and 448157c.

📒 Files selected for processing (1)
  • cmuxTests/WorkspaceRemoteConnectionTests.swift

Comment thread cmuxTests/WorkspaceRemoteConnectionTests.swift Outdated
@cursor

cursor Bot commented Jul 29, 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

🤖 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
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator`+ReverseRelay.swift:
- Around line 234-261: Refactor cancelInheritedReverseRelayForwardLocked so it
does not synchronously call sshExec on the locked coordinator path; move
relay-cancellation SSH work into a coordinator-owned cancellable asynchronous
phase, preserving the existing success and ignored-failure logging. Ensure any
dedicated-process launch is enqueued only after cancellation completes, and
allow termination, stop, and later relay state transitions to proceed without
waiting on the SSH timeout.
🪄 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 Plus

Run ID: eaf226b0-c883-4828-9003-98c7bb3479e2

📥 Commits

Reviewing files that changed from the base of the PR and between f2acd12 and dd17886.

📒 Files selected for processing (3)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift
💤 Files with no reviewable changes (1)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.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
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/RemoteProcessCancellationOperation.swift`:
- Around line 6-8: Add a one-line explanation to the documentation above
RemoteProcessCancellationOperation stating that installCancellationHandler and
cancel are invoked synchronously from non-isolated contexts that cannot await,
so actor-backed synchronization is not viable.
🪄 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 Plus

Run ID: 1fa0c6a6-0fa7-43de-8ff6-a03974a29f7e

📥 Commits

Reviewing files that changed from the base of the PR and between dd17886 and 0999730.

📒 Files selected for processing (6)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/RemoteProcessCancellationOperation.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelayStartup.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/ReverseRelayStartupPhase.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume
@coderabbitai review
@codex review
@greptile-apps review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown

@coderabbitai resume
@coderabbitai review
@codex review
@greptile-apps review
@cubic-dev-ai review

@austinywang I can't start this review because your workspace has reached its free monthly review limit. cubic has reviewed 241,631 of the 240,000 allowed lines of code this month. Reviews resume on 1 August 2026 (in 3 days). Paid plans include much higher monthly review limits. Upgrade now to resume reviews.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

@austinywang: Resuming reviews and reviewing the PR.

✅ Action performed

Reviews resumed.


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.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

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

🤖 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
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator`+ReverseRelayStartup.swift:
- Around line 131-141: Update the reverse relay startup status construction in
the coordinator to avoid placing raw startupFailure text in user-facing detail;
use a concise sanitized relay-unavailable summary while retaining the raw
failure only in debug logging. Replace the hardcoded English status text with
String(localized:defaultValue:) using a stable localization key, preserving the
retry timing information.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/ReverseRelayStartupPhase.swift`:
- Around line 6-10: Update the cancellation path in
RemoteSessionCoordinator+ReverseRelayStartup.swift so a cancelled exit task
still invokes finishConflictedControlMasterExitLocked through the queue-confined
state transition, rather than returning while the phase is
.exitingConflictedControlMaster. Preserve the normal completion behavior and add
coverage for cancellation followed by a new relay start, verifying recovery and
launch are no longer blocked.

In
`@Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift`:
- Around line 136-157: Update the coordinator setup in both reverse-relay
startup tests to generate a unique temporary manifest home and socket path for
each test invocation, rather than using FileManager.default.temporaryDirectory
and the fixed cancellation socket. Pass these per-invocation paths through each
coordinator’s configuration while preserving the existing test behavior and
cleanup.
🪄 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 Plus

Run ID: 8fe0fed9-8296-4abf-873c-e8c7221878c2

📥 Commits

Reviewing files that changed from the base of the PR and between dd17886 and f86b152.

📒 Files selected for processing (7)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/RemoteProcessCancellationOperation.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelayStartup.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/ReverseRelayStartupPhase.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift
💤 Files with no reviewable changes (1)
  • cmuxTests/WorkspaceRemoteConnectionTests.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
`@Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift`:
- Around line 30-97: Add a test covering the successful recovery path in
beginConflictedControlMasterExitIfNeededLocked: configure RecordingProcessRunner
to return status 0 for the -O exit request and succeed for the subsequent -N -R
relay launch, then assert finishConflictedControlMasterExitLocked triggers
launchReverseRelayLocked and retries the relay bind. Preserve existing
assertions for the ignored and cancellation paths.
🪄 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 Plus

Run ID: 8fe0fed9-8296-4abf-873c-e8c7221878c2

📥 Commits

Reviewing files that changed from the base of the PR and between dd17886 and f86b152.

📒 Files selected for processing (7)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/RemoteProcessCancellationOperation.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelayStartup.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Values/ReverseRelayStartupPhase.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift
💤 Files with no reviewable changes (1)
  • cmuxTests/WorkspaceRemoteConnectionTests.swift

@cursor

cursor Bot commented Jul 29, 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.

@austinywang

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 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.

Actionable comments posted: 13

Caution

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

⚠️ Outside diff range comments (6)
Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift (1)

145-177: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Race: handlers are stored after the stream yield, not before.

launchContinuation.yield(...) fires (potentially waking an awaiting Task on another thread) before self.startupHandler/self.terminationHandler are assigned under the lock a few lines later. A test that does let launch = try #require(await launches.next()) and then immediately calls emitStartupReady()/emitTermination() can race the producer and read a still-nil handler, silently no-op'ing the callback. This affects downstream consumers such as unresolvedOwnedControlPathUsesStandaloneFallback and standaloneConflictFailsClosed in RemoteSessionReverseRelayTransportTests.swift, which call emitStartupReady()/emitTermination() right after consuming the stream element.

Store the handlers under the lock before yielding to the stream so any awakened consumer is guaranteed to see them set.

🐛 Proposed fix
         let localRelayPort = try `#require`(
             Int(reverseArgument.split(separator: ":").last ?? "")
         )
-        launchContinuation.yield(RecordedReverseRelayLaunch(
-            arguments: arguments,
-            localRelayPort: localRelayPort,
-            startupMarker: startupMarker
-        ))
         lock.withLock {
             _launchCount += 1
             self.startupHandler = startupHandler
             self.terminationHandler = terminationHandler
         }
+        launchContinuation.yield(RecordedReverseRelayLaunch(
+            arguments: arguments,
+            localRelayPort: localRelayPort,
+            startupMarker: startupMarker
+        ))
         return process
🤖 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/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift`
around lines 145 - 177, Move the lock-protected assignments to startupHandler,
terminationHandler, and _launchCount in launch before
launchContinuation.yield(RecordedReverseRelayLaunch(...)). Preserve the existing
launch record contents and return behavior, ensuring consumers awakened by the
yield can immediately observe both handlers.
Packages/macOS/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration+SSHBatchCommands.swift (1)

71-75: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Duplicate "first SSH option value" parser — delegate to SSHAgentSocketResolver.

SSHAgentSocketResolver.optionValue(named:in:) already implements this exact first-value-wins parsing (and SSHConnectionSharingOptions.swift removed its own equivalent helper in this same PR to consolidate on it). This file still keeps a private duplicate (firstSSHOptionValue), used by reverseRelayControlMasterArguments. Since SSHAgentSocketResolver is already imported here (used in backgroundSSHOptions), delegating avoids two independently-maintained copies of security/behavior-sensitive SSH option parsing drifting apart.

♻️ Proposed refactor
-    private static func firstSSHOptionValue(
-        named key: String,
-        in options: [String]
-    ) -> String? {
-        let loweredKey = key.lowercased()
-        for option in trimmedSSHOptions(options) {
-            let parts = option.split(
-                maxSplits: 1,
-                omittingEmptySubsequences: true,
-                whereSeparator: { $0 == "=" || $0.isWhitespace }
-            )
-            guard parts.count == 2, parts[0].lowercased() == loweredKey else {
-                continue
-            }
-            let value = parts[1].trimmingCharacters(in: .whitespacesAndNewlines)
-            if !value.isEmpty {
-                return value
-            }
-        }
-        return nil
-    }
+    private static func firstSSHOptionValue(
+        named key: String,
+        in options: [String]
+    ) -> String? {
+        SSHAgentSocketResolver().optionValue(named: key, in: options)
+    }

Also applies to: 153-174

🤖 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/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration`+SSHBatchCommands.swift
around lines 71 - 75, Remove the private firstSSHOptionValue parser and update
reverseRelayControlMasterArguments to use
SSHAgentSocketResolver.optionValue(named:in:) for ControlMaster and related SSH
option lookups. Preserve first-value-wins behavior and the existing handling of
disabled ControlMaster values.
Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift (1)

795-813: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use empty-to-nil trimming for control_path.

control_path is a new optional SSH controlpath, not legacy v2RawString data. Parsing "" or whitespace as a non-nil resolvedControlPath currently forwards it into beginControlMasterAdoption(...) instead of being treated as "not provided"; use optionalTrimmedRawString(params, "control_path") like the nearby session_id parsing path.

🤖 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/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator`+Workspace.swift
around lines 795 - 813, Update workspaceRemoteForegroundAuthReady to parse
control_path with optionalTrimmedRawString instead of rawString plus trimming,
while preserving the existing foreground_auth_token parsing. Ensure empty or
whitespace-only control_path values become nil before passing
resolvedControlPath to controlWorkspaceRemoteForegroundAuthReady.
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker.swift (2)

41-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the three initializers onto the designated one.

Both public inits now repeat the same SSHConnectionSharingOptions() / jitter / registry construction; the third init already takes every dependency. Delegating removes the drift risk when another dependency is added.

♻️ Proposed refactor
     public nonisolated init(clock: any RemoteProxyRetryClock = SystemRemoteProxyRetryClock()) {
-        self.sharingOptions = SSHConnectionSharingOptions()
-        self.clock = clock
-        self.jitterMilliseconds = { Int.random(in: 100...350) }
-        self.cleanupLauncherOverride = nil
-        let ownershipRegistry =
-            NativeSSHControlMasterOwnershipRegistry(
-                sharingOptions: sharingOptions
-            )
-        self.controlMasterOwnershipRegistry = ownershipRegistry
+        self.init(clock: clock, cleanupLauncher: nil)
     }

...and give the cleanupLauncher-taking init an optional parameter that forwards to the designated initializer with a freshly built SSHConnectionSharingOptions() and registry.

🤖 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/Connection/NativeSSHConnectionBroker.swift`
around lines 41 - 90, Collapse the three initializers in
NativeSSHConnectionBroker onto the dependency-injecting initializer. Make the
cleanupLauncher parameter optional there, and have both public initializers
delegate to it with freshly constructed SSHConnectionSharingOptions, jitter
behavior, and NativeSSHControlMasterOwnershipRegistry while preserving their
existing defaults and cleanup-launcher behavior.

121-137: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Honor the cross-process ownership gate before reinstating the lease.

retainsWorkspace(_:) drops the controlMasterOwnershipRegistry.retain(...) result, then installs the lease and owner mapping before cancelCleanup(for:). Since beginCleanup(controlPath:) only authorizes recovery for an unowned socket, a live sibling process can prevent cleanup while the broker still records ownership of that socket. Fail closed when retained ownership is not acquired, or expose the registry result through adoption/cleanup handling if better-for-now stale cleanup is intentional.

🤖 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/Connection/NativeSSHConnectionBroker.swift`
around lines 121 - 137, The retainsWorkspace(_:) flow must honor the result of
controlMasterOwnershipRegistry.retain before recording ownership. Capture and
validate that result, and fail closed without reinstating ownerLeases or
ownersByControlMaster when retention is denied; ensure cleanup cancellation and
lease restoration occur only after successful cross-process ownership
acquisition.

Source: Path instructions

Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+ReverseRelay.swift (1)

207-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Route binding conflicts through the port-conflict status string.

RemoteSessionCoordinator+ReverseRelay.swift:97 and handleReverseRelayTerminationLocked:204 both call publishReverseRelayFailureLocked, but that always publishes strings.reverseRelayUnavailableRetrying. Since .bindingConflict is produced for relay-port conflicts, pass/use strings.reverseRelayPortUnavailableRetrying for that path so the added string is not dead/localization-only copy unless port conflicts are intentionally hidden behind the generic detail.

🤖 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`+ReverseRelay.swift
around lines 207 - 216, Update publishReverseRelayFailureLocked to accept or
select the status detail based on the failure reason, using
strings.reverseRelayPortUnavailableRetrying for .bindingConflict cases and
strings.reverseRelayUnavailableRetrying for other failures. Ensure the
reverse-relay failure callers, including handleReverseRelayTerminationLocked,
pass the conflict-specific path when appropriate while preserving the existing
retry scheduling.
🤖 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
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker`+Cleanup.swift:
- Around line 93-100: Preserve a single authentication-lock exit-status
contract: in NativeSSHConnectionBroker.launchCleanup, pass
request.authenticationLockPath into NativeSSHControlMasterCleanupRequest so the
zsh wrapper and its retry/no-op statuses remain observable, and retain the
related stale-pid cleanup; then remove any unused
resetSkippedExitStatus/noOpExitStatus API from
NativeSSHControlMasterCleanupRequest, or add the caller logic that consumes it.
Update both affected files accordingly:
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker+Cleanup.swift
lines 93-100 and
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterCleanupRequest.swift
lines 33-49.
- Around line 7-10: Document the intentional timing-based coordination around
the cleanup delay constants and the cleanup retry/termination logic near the
referenced cleanup flow. State that the cross-process lock holder provides no
completion signal, so retries use capped delays and stop after
cleanupMaximumRetryCount; make clear this is intentional staggering rather than
polling.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker`+ControlMasterOwnership.swift:
- Around line 17-30: Validate controlPath in beginControlMasterAdoption before
creating the lease or calling controlMasterOwnershipRegistry.retain, using the
same template and cmux-owned resolved-path checks as
beginReverseForwardRecovery. Return nil for any missing, template-bearing, or
non-cmux-owned path, and only proceed with registry retention for an exact
validated path.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterAdoptionHandoff.swift`:
- Around line 19-39: Update the default expiration behavior in
NativeSSHControlMasterAdoptionHandoff to match the authentication lock’s
staleness budget, or re-arm expirationTask while the authentication lock remains
held; ensure legitimate interactive authentication cannot lose the handoff
before durable-lease installation completes.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterCleanupRequest.swift`:
- Around line 33-49: Remove the unused resetSkippedExitStatus constant and the
noOpExitStatus parameterized processInvocation overload, retaining only the
active processInvocation path and its current behavior. Verify no remaining
callers or references depend on these exit codes.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterOwnershipRegistry.swift`:
- Around line 196-206: Update the exclusive-ownership path in
NativeSSHControlMasterOwnershipRegistry so it refuses conversion when
entry.leases is non-empty, preventing active workspace leases from being
stranded. Preserve the existing shared-lock reacquisition behavior for entries
without leases, and do not call removeEntryLocked(controlPath) while live leases
remain; if lease reporting is already supported, propagate any lost leases to
NativeSSHConnectionBroker.ownerLeases instead.

In
`@Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/FoundationRemoteReverseRelayProcessTests.swift`:
- Around line 72-82: Remove the wall-clock timing assertions from the three
tests terminationBoundsInheritedStderr, missingForwardConfirmationTerminates,
and startupDeadlineForceKillsAfterGracePeriod, including their startedAt Date()
setup and elapsed-time checks. Keep the existing asynchronous completion and
termination assertions unchanged.

In
`@Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/NativeSSHControlMasterAdoptionHandoffTests.swift`:
- Around line 24-30: The release assertion in the handoff test relies on
Task.yield() rather than guaranteed synchronization. Update the
releaseHandler/recorder flow to emit an explicit completion signal, such as an
AsyncStream continuation, and await that signal after handoff.release() before
asserting recorder.count == 1; remove the heuristic Task.yield().

In
`@Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/NativeSSHControlMasterOwnershipRegistryTests.swift`:
- Around line 252-254: Update resolvedControlPath(userID:) to place the
generated control path inside each test’s UUID-scoped temporary directory, using
the existing per-test scratch-directory symbol and preserving the deterministic
suffix. Ensure ownership locks derived through
sharingOptions.resolvedControlMasterOwnershipLockPath(controlPath:) no longer
resolve under shared /tmp across parallel tests.

In `@Sources/TerminalController`+ControlWorkspaceContext.swift:
- Around line 437-459: Update controlWorkspaceRemoteForegroundAuthReady to
detect a nil or blank foregroundAuthToken before calling
notifyRemoteForegroundAuthenticationReady and return the existing
invalid_params-style result for missing input. Only invoke
notifyRemoteForegroundAuthenticationReady with a non-empty normalized token,
preserving the unavailable ownership result for genuine adoption refusal.
- Around line 671-685: Update the shared RemoteSessionStrings
controlMasterOwnershipUnavailable accessor to cover this localized message, then
replace the inline String(localized:) construction in both error paths of the
workspace control flow with that accessor, preserving the existing error code,
data, and return behavior.

In `@Sources/Workspace.swift`:
- Around line 5530-5534: Remove the discarded-result behavior from
configureRemoteConnection and update reconnectRemoteConnection to consume its
Bool result, returning false when configuration or ControlMaster adoption fails
so callers receive the failure. Alternatively, ensure the failure branch in
configureRemoteConnection updates remoteConnectionState to .error before
returning false; preserve successful reconnection behavior.

In `@Sources/Workspace`+RemoteSessionLifecycle.swift:
- Around line 210-223: Normalize resolvedControlPath so an empty or
whitespace-only value becomes nil before the adoption branch in the readiness
notification flow. Update the logic surrounding
beginControlMasterAdoption(controlPath:ownerWorkspaceID:) to skip adoption and
preserve the existing nil-path behavior for malformed control_path input, while
continuing to adopt valid paths.

---

Outside diff comments:
In
`@Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator`+Workspace.swift:
- Around line 795-813: Update workspaceRemoteForegroundAuthReady to parse
control_path with optionalTrimmedRawString instead of rawString plus trimming,
while preserving the existing foreground_auth_token parsing. Ensure empty or
whitespace-only control_path values become nil before passing
resolvedControlPath to controlWorkspaceRemoteForegroundAuthReady.

In
`@Packages/macOS/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration`+SSHBatchCommands.swift:
- Around line 71-75: Remove the private firstSSHOptionValue parser and update
reverseRelayControlMasterArguments to use
SSHAgentSocketResolver.optionValue(named:in:) for ControlMaster and related SSH
option lookups. Preserve first-value-wins behavior and the existing handling of
disabled ControlMaster values.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker.swift`:
- Around line 41-90: Collapse the three initializers in
NativeSSHConnectionBroker onto the dependency-injecting initializer. Make the
cleanupLauncher parameter optional there, and have both public initializers
delegate to it with freshly constructed SSHConnectionSharingOptions, jitter
behavior, and NativeSSHControlMasterOwnershipRegistry while preserving their
existing defaults and cleanup-launcher behavior.
- Around line 121-137: The retainsWorkspace(_:) flow must honor the result of
controlMasterOwnershipRegistry.retain before recording ownership. Capture and
validate that result, and fail closed without reinstating ownerLeases or
ownersByControlMaster when retention is denied; ensure cleanup cancellation and
lease restoration occur only after successful cross-process ownership
acquisition.

In
`@Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator`+ReverseRelay.swift:
- Around line 207-216: Update publishReverseRelayFailureLocked to accept or
select the status detail based on the failure reason, using
strings.reverseRelayPortUnavailableRetrying for .bindingConflict cases and
strings.reverseRelayUnavailableRetrying for other failures. Ensure the
reverse-relay failure callers, including handleReverseRelayTerminationLocked,
pass the conflict-specific path when appropriate while preserving the existing
retry scheduling.

In
`@Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift`:
- Around line 145-177: Move the lock-protected assignments to startupHandler,
terminationHandler, and _launchCount in launch before
launchContinuation.yield(RecordedReverseRelayLaunch(...)). Preserve the existing
launch record contents and return behavior, ensuring consumers awakened by the
yield can immediately observe both handlers.
🪄 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 Plus

Run ID: 66b97f62-8421-4c02-8973-0b866c0a88bc

📥 Commits

Reviewing files that changed from the base of the PR and between 93abf0b and ecdc7dd.

📒 Files selected for processing (57)
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlWorkspaceContext.swift
  • Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlWorkspaceRemoteResolution.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandContextTestStubs.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/ControlCommandCoordinatorWorkspaceTests.swift
  • Packages/macOS/CmuxControlSocket/Tests/CmuxControlSocketTests/FakeWorkspaceControlCommandContext.swift
  • Packages/macOS/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration+SSHBatchCommands.swift
  • Packages/macOS/CmuxCore/Sources/CmuxCore/Remote/WorkspaceRemoteConfiguration+SSHControlPath.swift
  • Packages/macOS/CmuxCore/Tests/CmuxCoreTests/WorkspaceRemoteConfigurationSSHBatchCommandsTests.swift
  • Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/SSHAgentSocketResolver.swift
  • Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/SSHConnectionSharingOptions.swift
  • Packages/macOS/CmuxFoundation/Tests/CmuxFoundationTests/SSHConnectionSharingOptionsTests.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker+Cleanup.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker+ControlMasterOwnership.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHConnectionBroker.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterAdoptionHandoff.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterCleanupRequest.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterExclusiveUseAuthorization.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterLeaseIdentity.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterOwnershipRegistry.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterOwnershipTracking.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlMasterPendingCleanup.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Connection/NativeSSHControlPathResolver.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Hosting/RemoteSessionStrings.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/FoundationRemoteReverseRelayProcess.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/RemoteReverseRelayLauncher.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Process/RemoteReverseRelayLaunching.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+ReverseRelayControlMaster.swift
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/CleanupBlockingNativeSSHControlMasterOwnershipRegistry.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/FoundationRemoteReverseRelayProcessTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/NativeSSHConnectionBrokerTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/NativeSSHControlMasterAdoptionHandoffTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/NativeSSHControlMasterOwnershipRecoveryTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/NativeSSHControlMasterOwnershipRegistryTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/PermissiveNativeSSHControlMasterOwnershipRegistry.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemotePTYIntentionalCleanupTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemotePortScanGatingTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteReconnectPolicyTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteRelaySlotTeardownTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionInheritedForwardRecoveryTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayStartupTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionReverseRelayTransportTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteSessionSSHRemoteCommandOverrideTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/ResolvedControlPathProcessRunner.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/SynchronousEventRecorder.swift
  • Resources/Localizable.xcstrings
  • Sources/RemoteSessionStrings+App.swift
  • Sources/SSHPTYAttachStartupCommandBuilder.swift
  • Sources/TerminalController+ControlWorkspaceContext.swift
  • Sources/Workspace+RemoteSessionLifecycle.swift
  • Sources/Workspace.swift
  • cmuxTests/SSHForegroundAuthenticationMarkerCleanupTests.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift

Comment thread Sources/TerminalController+ControlWorkspaceContext.swift
Comment thread Sources/TerminalController+ControlWorkspaceContext.swift
Comment thread Sources/Workspace.swift
Comment thread Sources/Workspace+RemoteSessionLifecycle.swift
@cursor

cursor Bot commented Jul 29, 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 29, 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.

@austinywang
austinywang merged commit 2068b7c into main Jul 29, 2026
7 checks passed
kernelalex added a commit to Enigma-Labs-Technology/zerocmux that referenced this pull request Aug 2, 2026
The unit-test target did not compile on the merged tree. Three distinct
causes, fixed here; `xcodebuild -scheme zerocmux-unit build-for-testing`
now succeeds locally.

1. Collateral from the scrub's block parser. Its member detection kept
   scanning past one-line declarations (`private let x = ...`, no braces)
   and swallowed the following member, so removing mobile tests also
   deleted unrelated neighbours. Restored NotificationFeedHistoryTests
   from the merge commit and re-removed ONLY its three mobile-host RPC
   tests plus responsePayload (12 persistence tests are back, along with
   the waitUntil/notification/write helpers); restored recoveryURL in
   BrowserWebContentProcessTests. Removals now use a parser anchored on
   the func keyword that cannot cross member boundaries.
2. Merge truncation and dropped adjacent lines.
   SSHStartupSignalLifecycleTests lost its file tail, so its own call
   sites had no generatedVMSSHInitialStartupCommand /
   waitForSSHSignalLifecycleLog definitions; restored from upstream.
   WorkspaceUnitTests lost three `let fallbackCwd` declarations that
   upstream had added next to lines whose conflict resolved to ours.
3. Tests outliving their subject. Removed the four SidebarWidthPolicy
   cases exercising AppWebThemeSnapshot (it lives in the excluded
   ProWelcomeChecklist) and the seven WorkspaceRemoteConnection cases
   for remoteStaleRelayListenerCleanupScript /
   remoteReverseRelayControlOperation — verified those existed at the
   previous sync point and that upstream itself deleted the API and its
   tests in 'Fix SSH relay deadlock after app restart (manaflow-ai#9105)', so this
   adopts upstream's deletion rather than dropping fork behaviour.

Also: guarded eight sidebar test files' bare '@testable import cmux_DEV'
with the fork's canImport pattern (the module is zerocmux_DEV); rewrote
RemoteTmuxPaneSeedTransport's grid-wait helpers onto libghostty's
ghostty_surface_grid_metrics instead of the removed mobile render-grid
exporter, keeping the four tests that use them; dropped
CodexCodeModeRolloutIdentityTests (subject AgentChatSessionRegistry is
excluded) and swept its pbxproj entries.

cmux-tui: write the graphics_writer PTY sentinel before TCOOFF (CI showed
write() returning -1/EAGAIN against a stopped pty) and widen the
cancellable-connect deadline so two 10ms cancellation polls fit on a
loaded runner.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant