Skip to content

Fix workspace reorder crash for a pinned group's anchor - #13688

Merged
teamleaderleo merged 2 commits into
mainfrom
fix/workspace-reorder-pinned-group-crash
Sep 22, 2026
Merged

teamleaderleo merged 2 commits into
mainfrom
fix/workspace-reorder-pinned-group-crash

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #13417. Closes #13505.

Problem

WorkspacesModel.clampedReorderIndex could return tabs.count. WorkspaceReorderCoordinator removes the row and then inserts at that index, into an array one shorter, which traps in Array.insert.

This happens when every row in the sidebar belongs to a pinned group and the moved workspace is that group's anchor:

  • The anchor skips the group-member clamp; clampedGroupedMemberReorderIndex returns nil for anchors.
  • It was then tiered by its own isPinned, which is false because group pin is independent of per-workspace pin.
  • isGlobalPinnedRow does count the anchor as pinned, so pinnedCount == tabs.count, and max(clamped, pinnedCount) pushed it one past the end.

The index isn't coming from the client. Line 298 already clamps any requested index, and the out-of-range value is produced at the end of the function. That's why the client-side clamp workaround reported in #13505 didn't help.

Repro: one pinned group with anchor A and member B, neither individually pinned; workspace.reorder {workspace_id: A, index: 0} gives toIndex = 2 on a 2-element array.

Resulting behavior

  • Tiering: a workspace is now tiered by how its row renders (isGlobalPinnedRow). A pinned group's anchor clamps into the pinned tier like the rest of its group, instead of being treated as unpinned and pushed below the pinned rows.
  • Hard cap: the unpinned branch is capped at tabs.count - 1, so no pin configuration can produce an index past the end.
  • Repro case: the anchor reorder is now a no-op that returns true, and the order stays [A, B].
  • iOS copy: MobileWorkspaceMovePolicy, and MobileWorkspaceHostOrderSimulator in its tests, mirror this clamp to predict host order, so they get the same two changes. The iOS side guarded its insert, so it never crashed, but it predicted [B, A] for this case.

Validation and remaining gap

  • Commit 1: adds WorkspaceReorderPinnedGroupAnchorTests only. Locally, both tests fail: swift test --filter WorkspaceReorderPinnedGroupAnchor records 6 issues, with every requested index from -1 to 99 clamping to 2 on 2 rows. The coordinator test checks the clamp before reordering, so the unfixed code fails instead of trapping the test process. Commit 2 was pushed right behind it, so CI only ran on the fixed head.
  • Commit 2 locally: swift test in Packages/macOS/CmuxWorkspaces passes 230 tests in 32 suites, and in Packages/iOS/CmuxMobileShellModel passes 392 tests in 45 suites.
  • Not verified: I didn't reproduce the crash in a running app. The macOS app-host suite is red on main for unrelated reasons (see Make app-host unit tests green on main #13643).

🤖 Generated with Claude Code


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

Fixes a crash when reordering the anchor of a pinned group whose rows fill the entire sidebar.

clampedReorderIndex could return tabs.count for a group anchor whose group is pinned but whose workspace isn't individually pinned. The reorder coordinator then removed the row and inserted at that out-of-range index, trapping in Array.insert. The reorder now tiers by how the row renders and caps the index at tabs.count - 1.

Bug Fixes

  • Tier reorders by rendered pin state (isGlobalPinnedRow) so a pinned group's anchor clamps like the rest of its group instead of being treated as unpinned.
  • Cap the unpinned branch at tabs.count - 1 so no pin configuration can produce an index past the array end.
  • Mirror the clamp in MobileWorkspaceMovePolicy and its host-order simulator so iOS predictions agree with macOS.
  • Add regression tests covering the pinned-group-anchor reorder, including out-of-range requested indices.

Written for commit 0ac3bfb. Summary will update on new commits.

Review in cubic

teamleaderleo and others added 2 commits September 22, 2026 11:08
A pinned group whose rows fill the sidebar makes clampedReorderIndex return
tabs.count for the group's anchor, which is not individually pinned. The
coordinator then removes the row and inserts at that index into an array one
shorter, trapping in Array.insert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
clampedReorderIndex tiered a workspace by its own isPinned flag, but a group
anchor renders pinned when its group is pinned. With every row group-pinned,
the anchor was treated as unpinned and pushed to pinnedCount == tabs.count,
one past the last legal insert index after the coordinator removes the row.

Tier by the rendered pin (isGlobalPinnedRow) and cap the unpinned branch at
tabs.count - 1. The iOS move policy and its host-order simulator mirror the
macOS clamp, so they get the same change to keep predictions in agreement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 10 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 9aaa982e-21dc-4d61-b7bd-bb9de346e982

📥 Commits

Reviewing files that changed from the base of the PR and between 96f0c1f and 0ac3bfb.

📒 Files selected for processing (4)
  • Packages/iOS/CmuxMobileShellModel/Sources/CmuxMobileShellModel/MobileWorkspaceMovePolicy.swift
  • Packages/iOS/CmuxMobileShellModel/Tests/CmuxMobileShellModelTests/MobileWorkspaceHostOrderSimulator.swift
  • Packages/macOS/CmuxWorkspaces/Sources/CmuxWorkspaces/Model/WorkspacesModel+Ordering.swift
  • Packages/macOS/CmuxWorkspaces/Tests/CmuxWorkspacesTests/WorkspaceReorderPinnedGroupAnchorTests.swift

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.

@teamleaderleo
teamleaderleo enabled auto-merge (squash) September 22, 2026 15:10
@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@teamleaderleo
teamleaderleo merged commit c895b03 into main Sep 22, 2026
49 of 50 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

1 participant