iOS: workspace surface inventory with per-kind fallback cards (workspace.surfaces.v1) - #8620
azooz2003-bit wants to merge 6 commits into
Conversation
📝 WalkthroughWalkthroughThe change adds Mac-rendered surface inventories to mobile workspace payloads and state sync, projects them into iOS models, supports explicit surface selection and fallback presentation, and adds capability-gated, workspace-authorized RPC focusing on the owning Mac. ChangesSurface inventory and focus flow
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Picker
participant MobileShellComposite
participant MobileCoreRPCClient
participant TerminalController
Picker->>MobileShellComposite: selectMacSurface(surfaceID)
MobileShellComposite->>MobileCoreRPCClient: focusSurface(workspaceID, surfaceID)
MobileCoreRPCClient->>TerminalController: mobile.surface.focus
TerminalController-->>MobileCoreRPCClient: focus result
MobileCoreRPCClient-->>MobileShellComposite: success or error
Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (3 errors, 1 warning)
✅ Passed checks (21 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR adds Mac workspace surface inventory and fallback rendering on iOS. The main changes are:
Confidence Score: 5/5The latest focus-failure changes look safe to merge.
Important Files Changed
Reviews (4): Last reviewed commit: "Address review: focus-failure feedback, ..." | Re-trigger Greptile |
| let target = workspaceMutationTarget(for: workspaceID) | ||
| guard let client = target.client else { return } | ||
| do { | ||
| try await client.focusSurface( | ||
| workspaceID: workspace.rpcWorkspaceID.rawValue, | ||
| surfaceID: surfaceID.rawValue | ||
| ) | ||
| } catch { | ||
| if target.isForeground { markMacConnectionUnavailableIfNeeded(after: error) } | ||
| } |
There was a problem hiding this comment.
Secondary Focus Failures Stay Silent
When a secondary Mac still advertises this capability but has no live client, or its RPC fails, this action returns without changing connection state or reporting failure. The enabled “Open on Mac” button then appears to work but produces no visible result, leaving users unable to distinguish an unavailable Mac from a successful focus request.
There was a problem hiding this comment.
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 `@cmuxTests/MobileSurfaceKindMappingTests.swift`:
- Around line 12-18: Update mobileAndWorkspaceMappingsStayExhaustivelyInParity
to define the canonical expected PanelType-to-MobileSurfaceKind mapping for
every PanelType case, then assert each controller.mobileSurfaceKind result
against that expected value (and retain the workspace comparison if useful).
Compare typed enum values or canonical raw values directly so the test cannot
pass when both implementation mappings are identically incorrect.
In
`@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView`+DerivedState.swift:
- Around line 11-18: The selected non-terminal Mac surface lookup is duplicated
and inconsistently filters terminal surfaces. Add a shared
MobileWorkspacePreview.selectedMacSurface(_:) helper containing the id and
!kind.isTerminal predicate, then use it in
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift:4306-4311
within selectMacSurface,
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift:92-98
within activeSurface, and
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+DerivedState.swift:11-18
within selectedToolbarSubtitle.
In `@Sources/TerminalController`+MobileSurfaces.swift:
- Around line 73-78: Replace the internal “TabManager not available” message
with the product-level “Workspace context is unavailable” message in both
error-return branches of the mobile surface routing flow: the guard using
controlSurfaceRoutingResolvesTabManager and the .tabManagerUnavailable case in
controlSurfaceFocus. Keep the existing error code and data unchanged.
🪄 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: 5cec1db3-4a31-475e-b588-401ad2091b03
📒 Files selected for processing (41)
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileStateSyncRecords.swiftPackages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileSurfaceKind.swiftPackages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/MobileStateSyncFrameCodingTests.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient+SurfaceFocus.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncWorkspaceListResponse.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileWorkspacePreview+RemoteMapping.swiftPackages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileSurfaceInventoryTests.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+StateSync.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SurfaceFocus.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositePreviewTests.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellStateSyncTests.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileSurfacePreview.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspacePreview.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileSurfacePreview+Presentation.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenu.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenuActions.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenuValue.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceActiveSurface.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+DerivedState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+MenuState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swiftPackages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/TerminalPickerMenuValueTests.swiftPackages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceActiveSurfaceTests.swiftSources/Mobile/MobileHostService+Capabilities.swiftSources/Mobile/MobileHostService+TicketAuthorization.swiftSources/Mobile/MobileStateSync.swiftSources/Panels/Panel.swiftSources/TerminalController+MobileSurfaces.swiftSources/TerminalController+MobileWorkspaceList.swiftSources/TerminalController.swiftSources/Workspace+SurfaceNavigation.swiftcmux.xcodeproj/project.pbxprojcmuxTests/MobileHostConnectionLifecycleTests.swiftcmuxTests/MobileHostWorkspaceTicketAuthorizationTests.swiftcmuxTests/MobileSurfaceKindMappingTests.swiftcmuxTests/MobileWorkspaceListFidelityTests.swiftios/cmux/Resources/Localizable.xcstrings
There was a problem hiding this comment.
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/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swift`:
- Around line 11-12: Scope the focus state in SurfaceFallbackCardView to the
current surface by resetting focusFailed and cancelling and clearing focusTask
whenever surface.id changes, or apply a stable .id(surface.id.rawValue) to
recreate the card per surface. Preserve the existing focus behavior while
preventing state from one surface leaking into another.
🪄 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: 260d0204-7492-45d7-a300-44ec8fce22d8
📒 Files selected for processing (9)
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SurfaceFocus.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspacePreview.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+DerivedState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swiftSources/TerminalController+MobileSurfaces.swiftcmuxTests/MobileSurfaceKindMappingTests.swiftios/cmux/Resources/Localizable.xcstrings
| @State private var focusFailed = false | ||
| @State private var focusTask: Task<Void, Never>? |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target file with line numbers
sed -n '1,220p' Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swift | cat -n
printf '\n---\n'
# Find where this view is used
rg -n "SurfaceFallbackCardView|focusSurfaceOnMac|focusTask|focusFailed" Packages/iOS/CmuxMobileShellUI -SRepository: manaflow-ai/cmux
Length of output: 3729
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swift | cat -n
printf '\n---\n'
# Show any explicit identity used for the fallback card or its enclosing branch
rg -n "\.id\(|ForEach|SurfaceFallbackCardView|surfaceID|surface.id|workspaceID" Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swift -n -SRepository: manaflow-ai/cmux
Length of output: 3922
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "func focusSurfaceOnMac|focusSurfaceOnMac\(" Packages/iOS/CmuxMobileShellUI Packages -S
printf '\n---\n'
# Read the implementation if found
file=$(rg -l "func focusSurfaceOnMac|focusSurfaceOnMac\(" Packages -S | head -n 1 || true)
if [ -n "${file:-}" ]; then
sed -n '1,260p' "$file" | cat -n
fiRepository: manaflow-ai/cmux
Length of output: 4077
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,260p' Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SurfaceFocus.swift | cat -nRepository: manaflow-ai/cmux
Length of output: 2163
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check whether the fallback card has an explicit identity or whether the parent branch is the only identity boundary.
rg -n "\.id\(|SurfaceFallbackCardView\(" Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI -SRepository: manaflow-ai/cmux
Length of output: 830
Scope the task to the selected surface.
focusFailed and focusTask can be reused when SurfaceFallbackCardView stays mounted at the same structural position and surface.id changes, so a failed focus attempt for one Mac surface can leak into the next one. Cancel and clear that state when surface.id changes, or give the card a stable .id(surface.id.rawValue) so each surface gets its own task/state lifetime.
🤖 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/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swift`
around lines 11 - 12, Scope the focus state in SurfaceFallbackCardView to the
current surface by resetting focusFailed and cancelling and clearing focusTask
whenever surface.id changes, or apply a stable .id(surface.id.rawValue) to
recreate the card per surface. Preserve the existing focus behavior while
preventing state from one surface leaking into another.
84b1a91 to
98646cb
Compare
There was a problem hiding this comment.
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/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift (1)
78-82: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy liftDo not use a sleeping watchdog to release reconnect state.
This task clears
isReconnectingStoredMacafter 15 seconds even ifreconnectActiveMacOutcomeis still reading stores or dialing. A retry can therefore start while the previous reconnect remains active, leaving overlapping operations and duplicate state ownership. Tie the visible state to the reconnect operation’s explicit completion/cancellation instead of a second delayed task.As per coding guidelines and path instructions, production Swift must not add
ContinuousClock.sleep/delayed coordination as a lifecycle-race repair path; use an explicit owner and completion signal instead.Also applies to: 1016-1023, 1813-1827
🤖 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 78 - 82, Remove the sleeping/deadline-based release of isReconnectingStoredMac, including storedMacReconnectRestoringDeadlineSeconds and its related delayed-task logic. In reconnectActiveMacOutcome and the surrounding stored-Mac reconnect flow, keep the reconnect state owned until the operation explicitly completes or is cancelled, then clear it through that completion path so retries cannot overlap an active reconnect.Sources: Coding guidelines, Path instructions
🤖 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 `@cmux.xcodeproj/project.pbxproj`:
- Line 1121: Replace the duplicated PBXBuildFile and PBXFileReference UUIDs used
by MobileSurfaceKindMappingTests.swift and
TerminalController+MobileSurfaces.swift with unique identifiers. Update each
file’s PBXFileReference, PBXBuildFile, group membership, and Sources build-phase
references consistently throughout the project object graph.
---
Outside diff comments:
In
`@Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift`:
- Around line 78-82: Remove the sleeping/deadline-based release of
isReconnectingStoredMac, including storedMacReconnectRestoringDeadlineSeconds
and its related delayed-task logic. In reconnectActiveMacOutcome and the
surrounding stored-Mac reconnect flow, keep the reconnect state owned until the
operation explicitly completes or is cancelled, then clear it through that
completion path so retries cannot overlap an active reconnect.
🪄 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: b7fd237e-9a07-4a1a-99ac-b09d68cbdb5b
📒 Files selected for processing (36)
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileStateSyncRecords.swiftPackages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileSurfaceKind.swiftPackages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/MobileStateSyncFrameCodingTests.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient+SurfaceFocus.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncWorkspaceListResponse.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileWorkspacePreview+RemoteMapping.swiftPackages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileSurfaceInventoryTests.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+StateSync.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SurfaceFocus.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositePreviewTests.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellStateSyncTests.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileSurfacePreview.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspacePreview.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileSurfacePreview+Presentation.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenu.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenuActions.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenuValue.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceActiveSurface.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+DerivedState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+MenuState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swiftPackages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/TerminalPickerMenuValueTests.swiftPackages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceActiveSurfaceTests.swiftSources/Mobile/MobileHostService+Capabilities.swiftSources/Mobile/MobileHostService+TicketAuthorization.swiftSources/Mobile/MobileStateSync.swiftSources/Panels/Panel.swiftSources/TerminalController+MobileSurfaces.swiftSources/TerminalController+MobileWorkspaceList.swiftSources/TerminalController.swiftSources/Workspace+SurfaceNavigation.swiftcmux.xcodeproj/project.pbxproj
…rt parity Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cal kind assertions Greptile P2: focusSurfaceOnMac now reports success and the fallback card shows a localized caption when the Mac can't be reached instead of silently doing nothing. CodeRabbit: the selected-Mac-surface predicate lives once on MobileWorkspacePreview (one copy had dropped the non-terminal clause), the kind-mapping test asserts canonical wire strings rather than mere parity, and the surface-focus error message no longer names TabManager. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
98646cb to
5405e40
Compare
There was a problem hiding this comment.
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`+SurfaceNavigation.swift:
- Around line 13-18: Keep Workspace.surfaceKind(for:) as the sole canonical
PanelType-to-wire-kind mapping in
Sources/Workspace+SurfaceNavigation.swift:13-18. Update MobileSurfaceKind in
Sources/TerminalController+MobileSurfaces.swift:8-32 to delegate to that mapper
instead of defining a second switch, ensuring both legacy/state-sync and focus
inventories remain consistent.
🪄 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: 79b345c0-f19b-486c-add1-eabf1598c4de
📒 Files selected for processing (41)
Packages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileStateSyncRecords.swiftPackages/Shared/CMUXMobileCore/Sources/CMUXMobileCore/MobileSurfaceKind.swiftPackages/Shared/CMUXMobileCore/Tests/CMUXMobileCoreTests/MobileStateSyncFrameCodingTests.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient+SurfaceFocus.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileCoreRPCClient.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileSyncWorkspaceListResponse.swiftPackages/iOS/CmuxMobileRPC/Sources/CmuxMobileRPC/MobileWorkspacePreview+RemoteMapping.swiftPackages/iOS/CmuxMobileRPC/Tests/CmuxMobileRPCTests/MobileSurfaceInventoryTests.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+StateSync.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SurfaceFocus.swiftPackages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellCompositePreviewTests.swiftPackages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellStateSyncTests.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileSurfacePreview.swiftPackages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspacePreview.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileSurfacePreview+Presentation.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/SurfaceFallbackCardView.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenu.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenuActions.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenuValue.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceActiveSurface.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+DerivedState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+MenuState.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swiftPackages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swiftPackages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/TerminalPickerMenuValueTests.swiftPackages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceActiveSurfaceTests.swiftSources/Mobile/MobileHostService+Capabilities.swiftSources/Mobile/MobileHostService+TicketAuthorization.swiftSources/Mobile/MobileStateSync.swiftSources/Panels/Panel.swiftSources/TerminalController+MobileSurfaces.swiftSources/TerminalController+MobileWorkspaceList.swiftSources/TerminalController.swiftSources/Workspace+SurfaceNavigation.swiftcmux.xcodeproj/project.pbxprojcmuxTests/MobileHostConnectionLifecycleTests.swiftcmuxTests/MobileHostWorkspaceTicketAuthorizationTests.swiftcmuxTests/MobileSurfaceKindMappingTests.swiftcmuxTests/MobileWorkspaceListFidelityTests.swiftios/cmux/Resources/Localizable.xcstrings
| Self.surfaceKind(for: panel.panelType) | ||
| } | ||
|
|
||
| /// Surface-kind mapping used by snapshots and mobile mapping parity tests. | ||
| static func surfaceKind(for panelType: PanelType) -> String { | ||
| switch panelType { |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Keep the panel-to-mobile wire mapping in one place.
The same canonical PanelType → wire-kind table is now owned twice. Derive one mapper from the other (for example, create MobileSurfaceKind from Workspace.surfaceKind(for:)) so future panel kinds cannot make legacy/state-sync and focus inventories diverge.
Sources/Workspace+SurfaceNavigation.swift#L13-L18: retain or relocate the sole canonical mapping.Sources/TerminalController+MobileSurfaces.swift#L8-L32: delegate to that canonical mapping instead of maintaining another switch.
As per coding guidelines, correctness-critical state must use “one reliable source of truth.”
📍 Affects 2 files
Sources/Workspace+SurfaceNavigation.swift#L13-L18(this comment)Sources/TerminalController+MobileSurfaces.swift#L8-L32
🤖 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`+SurfaceNavigation.swift around lines 13 - 18, Keep
Workspace.surfaceKind(for:) as the sole canonical PanelType-to-wire-kind mapping
in Sources/Workspace+SurfaceNavigation.swift:13-18. Update MobileSurfaceKind in
Sources/TerminalController+MobileSurfaces.swift:8-32 to delegate to that mapper
instead of defining a second switch, ensuring both legacy/state-sync and focus
inventories remain consistent.
Sources: Coding guidelines, Path instructions
…ntory # Conflicts: # Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellStateSyncTests.swift # Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalPickerMenu.swift # Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceActiveSurface.swift # Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView+Surfaces.swift # Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift # Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceActiveSurfaceTests.swift # Sources/Mobile/MobileHostService+Capabilities.swift # cmux.xcodeproj/project.pbxproj # ios/cmux/Resources/Localizable.xcstrings
…-surface-inventory # Conflicts: # cmux.xcodeproj/project.pbxproj
Wave 0 of the iOS all-surfaces program: every Mac tab (all 11 SurfaceKinds, not just terminals) now syncs to the phone and appears in the surface picker with a per-kind icon; kinds without a native renderer yet show an informative fallback card with a working "Open on Mac" action.
Wire. New open-string
MobileSurfaceKindconstants in CMUXMobileCore (unknown kinds round-trip and route to the fallback card, so future kinds likesimulatordegrade gracefully).WorkspaceSyncRecordgains an optionalsurfacesarray{surface_id, kind, title, file_path?}, stamped by a singlemobileSurfaceDescriptors(in:)builder into BOTH sync-v2 rows and the legacymobile.workspace.listpayload (fidelity test asserts parity, includingfile_path). Descriptor fields are deliberately limited to what the workspace-list observer's summary hash already covers, so nothing on the wire can go silently stale; focus/URL/running-state fields are excluded on purpose and arrive with the waves that add their publishers. Capabilities:workspace.surfaces.v1, plus a newmobile.surface.focusRPC that reuses the control socket'ssurface.focuspath (including window/workspace activation) with an explicit workspace-scoped ticket-authorization entry and a scoped-ticket denial test.iOS. Surfaces flow through
applyStateSyncProjection→ legacy response DTO →MobileSurfacePreview(this projection previously dropped any non-terminal field). Selection is two-axis:selectedTerminalIDand all its call sites are untouched; non-terminal Mac surfaces are explicit-tap only, so entering a workspace still lands on the terminal exactly as today, and the terminal stays permanently mounted under the card (opacity swap). The picker gains a "Mac Surfaces" section with per-kind SF Symbols; exactly one row carries the checkmark (checkedRowID, unit-tested). All new strings localized EN+JA.In-flight coordination. This is deliberately minimal at contested seams: no picker rewrite, no browser selection behavior. #8188 (layout.v1) and #8289 (pane rack) carry layout/navigation payloads and can rebase their per-surface typing onto these shared kind constants whichever way the direction pick goes; #8298 stays authoritative for browser streaming and its "Mac Browsers" section folds into the kind-grouped list at rebase time. Browser tabs here are inventory-only cards until 8298 lands.
Verification. CMUXMobileCore 276 tests, CmuxMobileShellModel 166 tests, focused RPC/shell/state-sync suites, pbxproj wiring lint, package-group + Package.resolved policy checks, EN/JA localization audit for every new key, arm64 iOS simulator app build, and a Blacksmith cloud macOS build of tag
iosrf. Note: main's macOS build is currently broken by an unrelated actor-initializer error from #8521; the tagged build applied the one-line fix from #8604 as an uncommitted overlay. This PR does not touch that file.Next waves (specs staged): todo checklist renderer, panel-scoped file read context, filePreview/markdown viewers, agentSession chat binding, project snapshot, diff routing via #8221, generic frame-stream scaffold for the Simulator pane (#7857).
🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Syncs all Mac workspace surfaces to iOS and shows them in the picker with per‑kind icons. Unknown kinds render a fallback card with an “Open on Mac” action that focuses the surface on the Mac and shows a friendly error if the Mac can’t be reached.
New Features
CMUXMobileCore): addedWorkspaceSyncRecord.surfaces{surface_id, kind, title, file_path?}and open‑stringMobileSurfaceKindconstants; included in sync‑v2 and legacymobile.workspace.list; host advertisesworkspace.surfaces.v1andsurface.focus.v1; newmobile.surface.focusRPC implemented viaTerminalController+MobileSurfacesand routed throughsurface.focus.CmuxMobileRPC,CmuxMobileShellModel,CmuxMobileShellUI): newMobileSurfacePreview, a “Mac Surfaces” picker section with SF Symbols per kind, independent non‑terminal selection viaselectedMacSurfaceID, and exactly one checked row;SurfaceFallbackCardViewwith EN/JA strings and an “Open on Mac” button.Bug Fixes
focusSurfaceOnMacreports success; the fallback card shows a localized error when unreachable; capability checks centralized and sharedselectedMacSurfacelookup lives on the workspace preview; checkmark resolution consistently picks a single row, including browser overlay and stale selections.PanelType→ wire‑kind strings and legacy vs sync surface inventory parity (includingfile_path); ticket authorization updated formobile.surface.focus; unknown kind preservation and proper decoding whensurfacesis absent covered.Written for commit e505fb0. Summary will update on new commits.
Summary by CodeRabbit