Skip to content

iOS: preserve workspace groups in Recent Activity - #9960

Merged
azooz2003-bit merged 19 commits into
mainfrom
task-ios-all-computers-group-hierarchy
Aug 11, 2026
Merged

azooz2003-bit merged 19 commits into
mainfrom
task-ios-all-computers-group-hierarchy

Conversation

@azooz2003-bit

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

Copy link
Copy Markdown
Collaborator

Summary

  • keep All Computers workspace groups visible under Recent Activity
  • rank each group as one atomic block by its newest member while preserving sidebar order, collapse, and unread behavior
  • add package behavior coverage and a focused hosted iOS hierarchy regression

Task

Fix iOS All Computers so Recent Activity no longer flattens workspace groups into ordinary workspace rows. Group headers and indented members must remain contiguous across computers, with stable recency ordering and no regressions to filters or single-computer views.

Testing


Note

Medium Risk
Changes core workspace list ordering and grouped projection in the mobile shell; behavior is heavily tested but mistakes could affect selection, collapse, and drag reconciliation.

Overview
All Computers + Recent Activity no longer flattens workspace groups into a single interleaved list. Grouped sections stay visible; each group moves as one display block ranked by its newest lastActivityAt, with members still in Mac sidebar order (including timestamp-less members). Ungrouped rows rank on their own. Search, filters, and single-computer scope still use flat presentation where they did before.

MobileWorkspaceRecencyOrder adds groupedDisplayItems to build headers, indented members, and footers from those blocks while preserving pinned-first rules and stable tie-breaking.

WorkspaceListView wires recency into grouped rendering ( rendersGroupedSections no longer turns off for recency), feeds the table from precomputed groupedItems, and uses separate displayed vs authoritative WorkspaceListGroupedProjectionCache instances so optimistic drag reconciliation does not thrash the on-screen projection.

Also: Mac picker accessibility identifiers and connection status on the combined label; .isSelected on sidebar workspace rows; sort-picker schematic and preview fixtures for mixed-group UI/E2E coverage.

Reviewed by Cursor Bugbot for commit 4f3529a. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Keeps workspace groups intact in All Computers > Recent Activity by treating each group as one block ranked by its newest member. Search and filters flatten only while active; clearing restores grouped sections; selection and accessibility remain stable.

  • Bug Fixes

    • Aligned grouped sort contracts: the table renders from precomputed groupedItems, and drag reconciliation uses authoritative grouped order keys.
    • Selection survives sort changes, collapse/expand, and back navigation; sidebar and table rows expose .isSelected for visibility and accessibility.
    • Updated sort and filter behavior: Recent Activity schematic shows grouped sections; Unread and machine filters flatten rows and clearing restores groups; single-computer scope hides sort tiles.
    • Added stable accessibility identifiers for the Mac picker button, its “All Computers” menu item, and machine entries; the picker label reflects the current selection and announces connection status.
  • Performance

    • Introduced a synchronous WorkspaceListGroupedProjectionCache with separate displayed vs authoritative caches; reuses the grouped projection snapshot when optimism clears.
    • Reused a per-render workspacesByID map in grouped rows and appended group members in place.
    • Kept a 1,000‑workspace projection under 100 ms; added deterministic UI tests with dense sampling of grouped transitions covering selection, grouped recency ordering, search and filter flattening, single‑computer gating, stable picker/sort tile identifiers, and computer scoping before search.

Written for commit d3e502c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Improvements
    • Recent Activity now keeps workspace groups together as complete sections when sorting.
    • Groups are ordered by pin status and latest activity, while preserving the order of members.
    • Ungrouped workspaces remain separate and appear alongside grouped sections.
    • Collapsed groups, unread states, and groups with missing or invalid metadata are handled consistently.
    • Grouped workspace lists remain responsive and accurate across large workspace collections.

@coderabbitai

coderabbitai Bot commented Aug 11, 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

Recent Activity sorting now preserves grouped workspaces as contiguous display blocks. Group metadata is resolved and normalized before sorting. The workspace list caches projections, renders the resulting items, and tests grouped, ungrouped, collapsed, invalid, and large workspace configurations.

Changes

Grouped Recent Activity ordering

Layer / File(s) Summary
Recency block projection
Packages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspaceRecencyOrder.swift, Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceRecencyOrderTests.swift
groupedDisplayItems resolves group membership, ranks complete groups by pin status and activity, preserves member order, and flattens the blocks into list items.
Workspace list integration
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListGroupedProjectionCache.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift, Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+Table.swift
The view caches grouped projections, applies grouped recency ordering, derives lookup data once, and passes projected items to table and row rendering.
Ordering and UI validation
Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceListSortTests.swift, ios/cmuxUITests/cmuxUITests.swift
Tests cover grouped ordering, collapsed and unread states, metadata handling, cache behavior, large projections, unique items, and rendered indentation.

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

Sequence Diagram(s)

sequenceDiagram
  participant WorkspaceListView
  participant WorkspaceListGroupedProjectionCache
  participant MobileWorkspaceRecencyOrder
  participant WorkspaceListTable

  WorkspaceListView->>WorkspaceListGroupedProjectionCache: project workspaces, groups, and sort mode
  WorkspaceListGroupedProjectionCache->>MobileWorkspaceRecencyOrder: groupedDisplayItems(workspaces, groups)
  MobileWorkspaceRecencyOrder-->>WorkspaceListGroupedProjectionCache: atomic grouped and ungrouped items
  WorkspaceListGroupedProjectionCache-->>WorkspaceListView: cached grouped projection
  WorkspaceListView->>WorkspaceListTable: render grouped items and workspace lookup
Loading

Possibly related PRs

  • manaflow-ai/cmux#9828: The current changes extend its MobileWorkspaceRecencyOrder and WorkspaceListView behavior with atomic grouped ordering.

Important

Pre-merge checks failed

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

❌ Failed checks (3 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Algorithmic Complexity ❌ Error MobileWorkspaceRecencyOrder.swift:140 sorts up to about 1,000 display blocks in O(B log B); the added scale test checks shape only and provides no timing or profiling evidence. Add a benchmark or profiling measurement for 1,000 independent groups and document the UI budget, or replace the sort with a benchmark-backed linear-time ranking plan.
Cmux Swiftui State Layout ❌ Error New @State caches mutate input and projectedItems from WorkspaceListView.body via items(...), which is render-time state mutation. At the WorkspaceListView boundary, use immutable projection snapshots or update the cache from an explicit lifecycle/model observer, not during body evaluation.
Cmux Architecture Rethink ❌ Error The diff moves resume-directory state out of RestoredAgentLifecycleCoordinator into separate Workspace and DockSplitStore maps and duplicates seed logic, splitting one per-panel restore lifecycle a... Keep snapshots, resume state, invalidation, and resume directory in one RestoredAgentLifecycleCoordinator snapshot/API; route Workspace and DockSplitStore through shared seed, clear, and transfer transitions.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (21 passed)
Check name Status Explanation
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 production diff adds an explicitly @MainActor UI cache and pure Sendable recency value logic; no service protocols, shared Sendable references, or background UI-store access were introduced.
Cmux Swift Blocking Runtime ✅ Passed Changed production Swift adds only synchronous sorting, projection caching, and MainActor isolation; scans found no blocking waits, sleeps, delayed dispatch, polling, main-queue sync, or manual locks.
Cmux Browser Automation Off-Main ✅ Passed The PR changes only iOS workspace/UI files; the two browser-policy target files have no diff. The only screenshot match is an XCUITest attachment, not browser socket automation.
Cmux Expensive Synchronous Load ✅ Passed The production diff adds only in-memory workspace grouping/sorting and a @MainActor projection cache; no agent-history loader, file I/O, directory scan, or JSON/transcript parsing was added.
Cmux Cache Substitution Correctness ✅ Passed The cache is a transient grouped-list projection; cold state computes immediately, full Equatable workspace/group inputs invalidate stale entries, and no persistence, history, undo, or snapshot con...
Cmux No Hacky Sleeps ✅ Passed The complete PR range changes 8 Swift files and no non-Swift files; the added patch has no sleep, timer, polling, or fixed-delay candidates. The rule excludes Swift.
Cmux Swift Concurrency ✅ Passed Feature diff adds no DispatchQueue/DispatchGroup, Combine, completion-handler, or fire-and-forget Task patterns; the new projection cache is synchronous and @MainActor-isolated.
Cmux Swift @Concurrent ✅ Passed The feature diff adds no @concurrent or nonisolated async work. New projection APIs are synchronous, and the cache is explicitly @MainActor; existing async UI tasks remain unchanged.
Cmux Swift Package Boundaries ✅ Passed Production logic is behind SwiftPM targets: MobileWorkspaceRecencyOrder is in CmuxMobileShellModel with package tests, and the cache is in CmuxMobileShellUI; no app-target Sources production change...
Cmux Swiftpm Lockfiles ✅ Passed The feature diff changes only Swift source and tests in Packages/iOS/CmuxMobileShellModel and CmuxMobileShellUI; it changes no Package.swift, lockfile, .gitignore, workflow, or Xcode project refere...
Cmux Swift Logging ✅ Passed The full PR diff adds no print, debugPrint, dump, NSLog, Logger, file/stdout logging, or sensitive-data logging; changed code contains only projection and test logic.
Cmux User-Facing Error Privacy ✅ Passed The production diff adds ordering/cache logic and developer comments only; it adds no user-facing error, alert, command output, API body, or recovery copy.
Cmux Full Internationalization ✅ Passed The feature diff adds no user-facing copy or localization/catalog entries; production changes only reorder/project existing workspace data and reuse existing localized UI strings.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR changes iOS workspace projection/list views and tests only. The diff adds no NSWindow, NSPanel, Window, WindowGroup, controller, or close-shortcut code.
Cmux Source Artifacts ✅ Passed All 8 changed paths are tracked 100644 Swift source or test files; scans found no artifact-like paths, generated-output literals, binary objects, or NUL data.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The production diff adds no test/debug guards or seam-named members; the new public groupedDisplayItems API has production callers, and existing DEBUG code is unchanged.
Cmux No Ambient Global State ✅ Passed The diff adds instance methods on MobileWorkspaceRecencyOrder and a constructable cache owned by WorkspaceListView @State; checks found no new file-scope functions/vars, static namespace, or single...
Title check ✅ Passed The title clearly and concisely describes the primary change: preserving workspace groups in iOS Recent Activity.
Description check ✅ Passed The description clearly explains the change, rationale, behavior, and testing; the omitted demo video, review trigger, and checklist do not prevent review.
✨ 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 task-ios-all-computers-group-hierarchy

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: 2

Caution

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

⚠️ Outside diff range comments (1)
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift (1)

344-348: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Replace the per-header workspace scan with one lookup.

Recent Activity now renders grouped sections. groupedRows then calls workspaces.first(where:) at line 905 for every group header. With 200 groups and 1,000 workspaces, this performs up to 200,000 comparisons on each list render.

Build one workspace-ID-to-capabilities dictionary before ForEach. Use that dictionary for the anchor lookup.

Proposed fix
 private func groupedRows(
     items: [MobileWorkspaceListItem]
 ) -> some View {
     let enablesReorder = enablesWorkspaceReorder
     let groupLookup = groupsByID
+    let capabilitiesByWorkspaceID = Dictionary(
+        workspaces.map { ($0.id, $0.actionCapabilities) },
+        uniquingKeysWith: { first, _ in first }
+    )
     ForEach(items, id: \.id) { item in
         switch item {
         case .groupHeader(let group, let hasUnread):
-            let anchorCapabilities = workspaces.first(where: { $0.id == group.anchorWorkspaceID })?.actionCapabilities ?? .none
+            let anchorCapabilities = capabilitiesByWorkspaceID[group.anchorWorkspaceID] ?? .none

As per coding guidelines, production code must avoid repeated full scans for workloads around 1,000 records. As per path instructions, .github/review-bot-rules/algorithmic-complexity.md applies this rule to scalable user data.

🤖 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/WorkspaceListView.swift`
around lines 344 - 348, Update the grouped-row rendering path around
rendersGroupedSections and groupedRows to build a single
workspace-ID-to-capabilities dictionary before ForEach, then use that lookup for
each group header’s anchor workspace instead of calling
workspaces.first(where:). Preserve the existing grouping and anchor behavior
while eliminating repeated full workspace scans.

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
`@Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceRecencyOrderTests.swift`:
- Around line 101-114: Remove the ContinuousClock().measure wrapper and the
duration-based 100 ms expectation from MobileWorkspaceRecencyOrderTests. Keep
the groupedDisplayItems invocation and the existing workspaces.count,
items.count, and unique-ID assertions intact; move any performance measurement
to a non-gating benchmark or performance harness if needed.

In
`@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift`:
- Around line 455-460: Move currentDisplayedGroupedListItems out of
WorkspaceListView.body and into the existing state owner as its derived
snapshot. Recompute it only when workspace, grouping, filter, selection, or sort
inputs change, and have the view reuse that stored value for table rows, unread
headers, and optimistic-order observation. Do not introduce a second mutable
cache or repeat the grouped projection during unrelated body evaluations.

---

Outside diff comments:
In
`@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift`:
- Around line 344-348: Update the grouped-row rendering path around
rendersGroupedSections and groupedRows to build a single
workspace-ID-to-capabilities dictionary before ForEach, then use that lookup for
each group header’s anchor workspace instead of calling
workspaces.first(where:). Preserve the existing grouping and anchor behavior
while eliminating repeated full workspace scans.
🪄 Autofix

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: a4394d59-acc8-4aa4-bfbd-744e870104b3

📥 Commits

Reviewing files that changed from the base of the PR and between c05110b and 54e99df.

📒 Files selected for processing (4)
  • Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceRecencyOrderTests.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+DragDrop.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+Table.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift
💤 Files with no reviewable changes (1)
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+DragDrop.swift

Comment thread Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift (1)

457-481: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid filtering workspaces twice per render during an active drag.

currentDisplayedGroupedListItems computes displayedGroupedWorkspaces, which reads the computed property groupedWorkspaces (an O(n) filter over workspaces) once. When optimisticGroupedState.optimisticOrder is non-nil, currentAuthoritativeGroupedListItems reads groupedWorkspaces again directly. This filter runs twice per body evaluation while a drag is active, and drag gestures trigger many render passes.

Hoist groupedWorkspaces into a single local let at the top of body and pass that value to both the displayed and authoritative branches instead of letting each branch re-read the computed property.

As per path instructions, .github/review-bot-rules/algorithmic-complexity.md requires avoiding "repeated sort/filter/map work in hot paths" for production code handling around 1,000 workspace records.

🤖 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/WorkspaceListView.swift`
around lines 457 - 481, Hoist the computed groupedWorkspaces value into a single
local let at the top of body, then reuse that local when constructing both
displayedGroupedWorkspaces and currentAuthoritativeGroupedListItems. Update the
displayed projection path and authoritativeGroupedProjectionCache.items call so
an active drag does not re-read the groupedWorkspaces computed property during
the same body evaluation.

Source: 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.

Outside diff comments:
In
`@Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift`:
- Around line 457-481: Hoist the computed groupedWorkspaces value into a single
local let at the top of body, then reuse that local when constructing both
displayedGroupedWorkspaces and currentAuthoritativeGroupedListItems. Update the
displayed projection path and authoritativeGroupedProjectionCache.items call so
an active drag does not re-read the groupedWorkspaces computed property during
the same body evaluation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 86f64650-bcdf-4856-8c92-0e455520a2f9

📥 Commits

Reviewing files that changed from the base of the PR and between d4ade79 and a5733dd.

📒 Files selected for processing (6)
  • Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceRecencyOrderTests.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListGroupedProjectionCache.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView+Table.swift
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceListView.swift
  • Packages/iOS/CmuxMobileShellUI/Tests/CmuxMobileShellUITests/WorkspaceListSortTests.swift
  • ios/cmuxUITests/cmuxUITests.swift

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fc90bea. Configure here.

@azooz2003-bit
azooz2003-bit merged commit 9d5b8d9 into main Aug 11, 2026
9 of 12 checks passed
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