Skip to content

Preserve iOS workspace groups during reconnect - #9388

Merged
azooz2003-bit merged 3 commits into
mainfrom
fix-ios-group-reconnect-flat
Aug 3, 2026
Merged

azooz2003-bit merged 3 commits into
mainfrom
fix-ios-group-reconnect-flat

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Aug 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Preserve the last authoritative iOS workspace group snapshot while reconnecting, disconnected, rebinding, or receiving temporarily empty group metadata.
  • Track whether the RPC payload actually contained groups, so an omitted field no longer looks like an authoritative empty list.
  • Allow connected, healthy, complete ungrouped snapshots to clear group headers.

Related context: #9326

Testing


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Preserves iOS workspace group headers during reconnects and when groups is omitted or temporarily empty, so sections don’t disappear. Still clears stale headers on a connected, authoritative ungrouped snapshot.

  • Bug Fixes
    • CmuxMobileRPC: added groupsFieldWasPresent to MobileSyncWorkspaceListResponse and set it via container.contains(.groups) to distinguish omitted vs empty groups.
    • CmuxMobileShell: preserves last groups while reconnecting/rebinding or disconnected; accepts an empty group snapshot only when both iPhone and Mac are connected and no workspace has a groupID.
    • Tests: added coverage for omitted vs empty groups, disconnected empty refresh preservation, and connected empty/ungrouped clearing.

Written for commit 9f4e7b7. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved workspace synchronization to preserve group information when remote responses are incomplete or received during reconnects.
    • Prevented existing workspace groups from being cleared by disconnected or empty refreshes.
    • Ensured intentionally empty group data is applied correctly when the connection is healthy and authoritative.
    • Improved handling of workspace lists where group information is omitted versus explicitly empty.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The workspace-list response now records whether groups was present. Remote workspace refreshes use this state to preserve, update, or clear group metadata. RPC and preview tests cover missing, empty, disconnected, and connected refresh cases.

Changes

Workspace group refresh

Layer / File(s) Summary
Track groups field presence
Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncWorkspaceListResponse.swift, Packages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileCoreRPCClientTests.swift
The response records whether groups was present. Missing and empty fields still decode to empty collections, with distinct presence flags.
Resolve remote group metadata
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
Remote refresh handling preserves, updates, or clears group metadata according to field presence, connection health, workspace references, and reconnect state.
Validate refresh behavior
Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositePreviewTests.swift
Preview tests cover missing groups, empty groups, disconnected refreshes, connected ungrouped refreshes, and grouped fixtures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MobileCoreRPCClient
  participant MobileSyncWorkspaceListResponse
  participant MobileShellComposite
  participant WorkspaceGroupState
  MobileCoreRPCClient->>MobileSyncWorkspaceListResponse: decode workspace-list response
  MobileSyncWorkspaceListResponse-->>MobileCoreRPCClient: groups and groupsFieldWasPresent
  MobileCoreRPCClient->>MobileShellComposite: applyRemoteWorkspaceList
  MobileShellComposite->>WorkspaceGroupState: preserve, update, or clear group metadata
Loading

Possibly related PRs

  • manaflow-ai/cmux#8284: Both modify workspace-list response handling and remote workspace state projection.

Suggested reviewers: lawrencecchen

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed The PR adds only a Bool to an existing Sendable value model and group helpers inside explicit @MainActor MobileShellComposite; it adds no unsafe Sendable reference, protocol, or background UI access.
Cmux Swift Blocking Runtime ✅ Passed Changed production Swift files show no delta for sleeps, timers, asyncAfter, blocking waits, main sync, or locks; added polling/delay matches are test-only scaffolding.
Cmux Browser Automation Off-Main ✅ Passed The PR diff changes only iOS RPC and shell files; it adds no browser socket commands or edits to TerminalController.swift or CmuxControlSocket policy/router code.
Cmux Expensive Synchronous Load ✅ Passed Production diff adds only in-memory group metadata handling; the existing JSONDecoder RPC call is unchanged, with no agent-history file I/O, directory scan, syscall loop, or synchronous loader added.
Cmux Cache Substitution Correctness ✅ Passed The diff retains groups only in in-memory UI state; applyRemoteWorkspaceList documents the degradation policy and clears them on a healthy complete ungrouped snapshot.
Cmux No Hacky Sleeps ✅ Passed The full PR changes only four Swift files; it adds no non-Swift runtime code or fixed sleeps/timers, and this rule explicitly excludes Swift.
Cmux Algorithmic Complexity ✅ Passed The review diff adds only linear passes: one groups map and one workspace contains check; no nested scans, per-target rescans, sorting, joins, or slower-than-linear algorithm were introduced.
Cmux Swift Concurrency ✅ Passed The PR adds only synchronous decoding and group-state filtering; the added Swift lines contain no Dispatch, Combine, completion-handler, or fire-and-forget Task patterns.
Cmux Swift @Concurrent ✅ Passed The PR adds no async or nonisolated production work. Changed group helpers are synchronous inside @MainActor; the only new annotation is a synchronous @MainActor test fixture.
Cmux Swift Package Boundaries ✅ Passed The PR changes production code only in SwiftPM targets CmuxMobileRPC and CmuxMobileShell, with package-local tests; no app-root Sources changes or new reusable logic trapped in the app target were...
Cmux Swiftpm Lockfiles ✅ Passed The PR changes only four Swift source/test files; it changes no Package.swift, dependency, .gitignore, workflow, Xcode project reference, or Package.resolved file.
Cmux Swift Logging ✅ Passed The only added print is intended CLI event output; production diagnostics use existing MobileDebugLog or mobileShellLog with private error redaction, and no unsafe Logger constants or sensitive val...
Cmux User-Facing Error Privacy ✅ Passed The PR adds no user-facing errors, alerts, command output, API error bodies, or recovery copy; production changes only track group metadata and preserve state, while tests and comments are allowed.
Cmux Full Internationalization ✅ Passed The production diff adds only group-field tracking and preservation logic; it adds no user-facing copy or localization keys. Fixture strings are test-only and allowed.
Cmux Swiftui State Layout ✅ Passed The diff changes RPC decoding, a @Observable model helper, and tests; it adds no SwiftUI view, ObservableObject/@published state, layout measurement, lazy-row store reference, or render-time mutation.
Cmux Architecture Rethink ✅ Passed The change is a local correctness fix: immutable RPC field metadata feeds an explicit authoritative-snapshot invariant, while setForegroundWorkspaceState remains the sole group-state owner; no timi...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR changes RPC decoding, mobile shell state logic, and tests; it adds no NSWindow, NSPanel, NSWindowController, Window, or WindowGroup code.
Cmux Source Artifacts ✅ Passed All four changed paths are Swift source or test files under Packages/iOS; no artifact directories, logs, media, caches, or build outputs appear in the PR diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The PR adds no DEBUG guards or test/debug-named members in production Sources; groupsFieldWasPresent and private group-resolution methods serve live product behavior.
Cmux No Ambient Global State ✅ Passed The production diff adds response state and two private instance methods on MobileShellComposite; it adds no top-level API, mutable global, static namespace, or singleton.
Title check ✅ Passed The title clearly identifies the main change: preserving iOS workspace groups during reconnect.
Description check ✅ Passed The description clearly documents the change and testing, but it omits the template's demo video, review trigger, and checklist sections.
✨ 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 fix-ios-group-reconnect-flat

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
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 11894-11895: Remove the empty-workspace veto around existingGroups
so an authoritative response with groups: [] can clear stale group headers;
preserve any guard only for incomplete reconnects, using groupsAreAuthoritative
to distinguish them. Add coverage for a connected authoritative snapshot
containing empty workspaces and groups.
🪄 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: 472781e4-7a7d-4bc9-85d8-d196f3ce7191

📥 Commits

Reviewing files that changed from the base of the PR and between 80f4083 and adfb462.

📒 Files selected for processing (4)
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncWorkspaceListResponse.swift
  • Packages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileCoreRPCClientTests.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositePreviewTests.swift

Comment thread Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift Outdated
Resolve the workspace-list response test conflict and keep authoritative connected empty snapshots able to clear stale iOS group headers.
@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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift (1)

10812-10825: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Remove timer-based probe coordination.

Lines 10812-10825 add probe-and-repair work inside a task that an outer timer cancels. Both request paths already pass timeoutNanoseconds to client.sendRequest.

Keep timeout ownership in MobileCoreRPCClient.sendRequest. Move the switch directly into probeEventSubscriptionLiveness and remove the outer task and timer. This avoids a second cancellation owner in the socket liveness path.

As per coding guidelines, “Do not introduce or materially expand … timers … for synchronization … or delayed coordination.”

🤖 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/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`
around lines 10812 - 10825, Remove the outer timer-backed task around the
subscription probe in MobileShellComposite and move the
active/missing/unsupported switch directly into probeEventSubscriptionLiveness.
Invoke requestTerminalEventSubscriptionProbe and
requestTerminalEventSubscription there, preserving their timeoutNanoseconds
arguments and existing repair behavior, while leaving cancellation and timeout
ownership to MobileCoreRPCClient.sendRequest.

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.

Outside diff comments:
In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 10812-10825: Remove the outer timer-backed task around the
subscription probe in MobileShellComposite and move the
active/missing/unsupported switch directly into probeEventSubscriptionLiveness.
Invoke requestTerminalEventSubscriptionProbe and
requestTerminalEventSubscription there, preserving their timeoutNanoseconds
arguments and existing repair behavior, while leaving cancellation and timeout
ownership to MobileCoreRPCClient.sendRequest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2bde3d63-71eb-4c01-bc90-7a9d2324f920

📥 Commits

Reviewing files that changed from the base of the PR and between adfb462 and 9f4e7b7.

📒 Files selected for processing (4)
  • Packages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncWorkspaceListResponse.swift
  • Packages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileCoreRPCClientTests.swift
  • Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift
  • Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositePreviewTests.swift

@azooz2003-bit
azooz2003-bit merged commit be6516f into main Aug 3, 2026
7 checks passed
@azooz2003-bit
azooz2003-bit deleted the fix-ios-group-reconnect-flat branch August 3, 2026 22:21
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.

1 participant