Skip to content

Retract recovered daemon transport errors from the workspace sidebar - #9472

Merged
austinywang merged 5 commits into
mainfrom
issue-8917-recovered-daemon-transport-bounce-leaves
Aug 3, 2026
Merged

austinywang merged 5 commits into
mainfrom
issue-8917-recovered-daemon-transport-bounce-leaves

Conversation

@austinywang

@austinywang austinywang commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

A transient daemon-transport drop on an SSH remote workspace escalates to a .error daemon status, and applyRemoteDaemonStatusUpdate appends Remote daemon error (<host>): Remote daemon transport needs re-bootstrap after proxy failure ... to the sidebar log. When the transport re-bootstraps a few seconds later the handler only reset the dedup fingerprint, so that entry stayed the workspace's last log entry, which is exactly what the sidebar row renders (SidebarWorkspaceSnapshotFactory -> latestLog), and it persisted across restarts. The connection-state path already self-heals on .connected; the daemon path did not.

The daemon-status handler now retracts its own proxy-only artifacts when the daemon returns to .ready, via a new clearProxyOnlyRemoteDaemonSidebarArtifacts() that removes only remote-daemon-sourced entries matching isProxyOnlyRemoteError. It deliberately does not reuse clearProxyOnlyRemoteSidebarArtifacts(), which also drops the connection-state status entry and proxy notifications owned by applyRemoteConnectionStateUpdate; a healthy daemon does not imply a healthy connection.

Fixes #8917

Testing

  • New regression test WorkspaceRemoteConnectionTests.testRecoveredDaemonTransportBounceClearsSidebarDaemonError drives an ordinary SSH workspace with persistent PTY through .ready -> .error (proxy-only re-bootstrap message) -> .ready and asserts no remote-daemon entry remains and the last log entry is not an error.
  • Two commits so CI shows red then green: the first adds the test only, the second adds the fix.
  • Tagged Debug build of the branch head (scripts/reload.sh --tag sym8917) succeeded, launched, and answered on its debug socket; the app was then quit. A live end-to-end repro was not run because it needs a real SSH remote workspace plus a ~40s network interruption, which this machine has no sshd or remote host for. The state transition itself is covered deterministically by the unit test.

Summary by CodeRabbit

  • Bug Fixes

    • Cleared stale remote connection error messages after a successful reconnection.
    • Preserved relevant connection status information while removing outdated sidebar entries.
    • Improved workspace status updates when a remote connection recovers, ensuring the workspace no longer appears to be in an error state.
  • Tests

    • Added coverage for remote daemon recovery, repeated status changes, and error removal after re-establishing a connection.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1f235fac-fc1d-4484-8626-7ccbbfab1479

📥 Commits

Reviewing files that changed from the base of the PR and between 4eb16a4 and 1aaf4ee.

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

📝 Walkthrough

Walkthrough

The workspace now removes proxy-only remote-daemon sidebar errors when daemon status returns to .ready. A main-actor regression test covers the ready-to-error-to-ready sequence and verifies that the workspace row clears its error state.

Changes

Remote daemon recovery

Layer / File(s) Summary
Clear proxy-only errors on daemon recovery
Sources/Workspace.swift
Adds proxy-only remote-daemon artifact cleanup and invokes it when daemon status becomes .ready.
Validate daemon recovery behavior
cmuxTests/WorkspaceRemoteDaemonRecoveryTests.swift, cmux.xcodeproj/project.pbxproj
Adds coverage for ready-to-error-to-ready recovery and registers the test in the Xcode test target.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RemoteDaemon
  participant Workspace
  participant SidebarLog
  RemoteDaemon->>Workspace: Report ready status
  Workspace->>Workspace: Clear proxy-only daemon artifacts
  Workspace->>SidebarLog: Remove remote-daemon error
Loading
🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary change: removing recovered daemon transport errors from the workspace sidebar.
Description check ✅ Passed The description explains the cause, fix, scope, and testing; the omitted template sections are non-critical.
Linked Issues check ✅ Passed The change satisfies #8917 by retracting proxy-only daemon errors after recovery while preserving connection-state artifacts and persistent sessions.
Out of Scope Changes check ✅ Passed All changes support the linked issue through targeted cleanup logic, regression coverage, and test-target registration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cmux Swift Actor Isolation ✅ Passed The production diff only mutates sidebar state within @MainActor Workspace; its caller already hops to DispatchQueue.main. The new regression test is explicitly @MainActor.
Cmux Swift Blocking Runtime ✅ Passed The production diff only removes matching sidebar log entries on .ready; it adds no waits, sleeps, polling, delayed dispatch, main-queue sync, or locks. The test is deterministic.
Cmux Browser Automation Off-Main ✅ Passed The PR changes only Workspace cleanup, Xcode registration, and a recovery test; it adds no browser.* command, WebKit wait, worker routing, or browser policy test obligation.
Cmux Expensive Synchronous Load ✅ Passed The production diff only adds filtered sidebar-log removal and its .ready call; it adds no agent-history loader, file read, JSON parse, directory scan, or syscall.
Cmux Cache Substitution Correctness ✅ Passed Placeholder
Cmux No Hacky Sleeps ✅ Passed The PR changes only Swift, a Swift test, and .pbxproj metadata; no sleep, timer, polling, or fixed-delay lines were added, and the rule excludes Swift.
Cmux Algorithmic Complexity ✅ Passed The new logEntries.removeAll scan is linear and bounded by the sidebar model’s explicit 1–500 entry cap; no nested scan or unbounded repeated filtering was added.
Cmux Swift Concurrency ✅ Passed The PR adds no Dispatch, Combine, completion-handler, or fire-and-forget Task patterns; the new @MainActor Swift Testing test is an allowed test boundary.
Cmux Swift @Concurrent ✅ Passed The PR adds only synchronous functions; the helper inherits @MainActor from Workspace and accesses isolated state directly, with no new nonisolated async work, @concurrent use, or heavy async UI call.
Cmux Swift Package Boundaries ✅ Passed The 15-line change is app-specific Workspace sidebar/status lifecycle composition; Workspace is @MainActor and imports SwiftUI/AppKit, so it is not standalone reusable domain logic.
Cmux Swiftpm Lockfiles ✅ Passed The PR changes Swift source/tests and test-file Xcode registrations only; it has no Package.swift, Package.resolved, .gitignore, workflow, or SwiftPM package-reference changes.
Cmux Swift Logging ✅ Passed The PR adds no print, debugPrint, dump, NSLog, file/stdout diagnostic logging, Logger, or sensitive production logging; the new appendSidebarLog call is unchanged.
Cmux User-Facing Error Privacy ✅ Passed The production diff only retracts proxy-only daemon sidebar entries and adds internal comments; it adds no user-facing error text or sensitive diagnostics. New raw error strings are confined to tests.
Cmux Full Internationalization ✅ Passed Production additions contain only cleanup logic, comments, and the existing literal source token; no new user-facing text or localization/catalog changes. New strings are test-only and exempt.
Cmux Swiftui State Layout ✅ Passed The PR adds a Workspace cleanup helper and ready-state event call plus a non-view test; it adds no SwiftUI state, geometry, lazy-row store reference, or render-time mutation.
Cmux Architecture Rethink ✅ Passed The diff is a local, MainActor-owned cleanup on the .ready transition; it adds no timing, polling, locks, observers, side channels, or duplicate wiring, and documents the artifact ownership invar...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR only changes remote-daemon cleanup and adds a test; it introduces no NSWindow, NSPanel, Window, WindowGroup, identifier, or close-shortcut code.
Cmux Source Artifacts ✅ Passed The PR changes only hand-written Swift source, an Xcode project config, and a regression test; no artifact paths, generated outputs, caches, or scratch directories were added.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The only production Swift change is Workspace.swift: a real daemon-status cleanup helper called on .ready; it adds no DEBUG guard, test-named member, accessor, or visibility widening.
Cmux No Ambient Global State ✅ Passed The only production addition is Workspace.clearProxyOnlyRemoteDaemonSidebarArtifacts() (Sources/Workspace.swift:2734), an instance method on the existing owning type; no global function, mutable gl...
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8917-recovered-daemon-transport-bounce-leaves

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.

@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 6922-6927: In Sources/Workspace.swift lines 6922-6927, update the
ready-state recovery logic to clear sidebar artifacts only when the
authoritative typed lifecycle state is .connected, handling either event order
through one guarded cleanup path and failing closed when unavailable. In
cmuxTests/WorkspaceRemoteConnectionTests.swift lines 3271-3319, establish
.connected before the positive recovery sequence and add a disconnected-state
case verifying cleanup does not occur.
🪄 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: 2b543ab3-d5fb-406d-8278-89a0d645f1ac

📥 Commits

Reviewing files that changed from the base of the PR and between 8cc5dc4 and 8c29e8d.

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

Comment thread Sources/Workspace.swift
Comment on lines +6922 to +6927
if status.state == .ready {
// #8917: a transport bounce that re-bootstrapped successfully is
// not a workspace failure, so retract its sidebar error the same
// way the connection-state path retracts on `.connected`.
clearProxyOnlyRemoteDaemonSidebarArtifacts()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Enforce and test the connected-state recovery precondition.

The production code clears the daemon error on .ready alone, while the regression test starts with a .disconnected connection. This permits the test to pass for an implementation that hides an error before the connection recovers.

  • Sources/Workspace.swift#L6922-L6927: Require the authoritative connection state to be .connected before clearing. Handle either event order through one guarded cleanup path.
  • cmuxTests/WorkspaceRemoteConnectionTests.swift#L3271-L3319: Establish .connected before the positive sequence and add a negative disconnected-state case.

As per path instructions, use the authoritative typed lifecycle state and fail closed when it is unavailable.

📍 Affects 2 files
  • Sources/Workspace.swift#L6922-L6927 (this comment)
  • cmuxTests/WorkspaceRemoteConnectionTests.swift#L3271-L3319
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Workspace.swift` around lines 6922 - 6927, In Sources/Workspace.swift
lines 6922-6927, update the ready-state recovery logic to clear sidebar
artifacts only when the authoritative typed lifecycle state is .connected,
handling either event order through one guarded cleanup path and failing closed
when unavailable. In cmuxTests/WorkspaceRemoteConnectionTests.swift lines
3271-3319, establish .connected before the positive recovery sequence and add a
disconnected-state case verifying cleanup does not occur.

Source: Path instructions

@cursor

cursor Bot commented Aug 3, 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 ea8c7a6 into main Aug 3, 2026
6 checks passed
@austinywang
austinywang deleted the issue-8917-recovered-daemon-transport-bounce-leaves branch August 3, 2026 22:32
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.

Recovered daemon-transport bounce leaves a permanent error on the workspace row

1 participant