Skip to content

fix(sidebar): keep manual worktree order stable across agent activity - #14284

Closed
blade035 wants to merge 1 commit into
stablyai:mainfrom
blade035:fix/sidebar-manual-order-stable-on-activity
Closed

blade035 wants to merge 1 commit into
stablyai:mainfrom
blade035:fix/sidebar-manual-order-stable-on-activity

Conversation

@blade035

@blade035 blade035 commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

When you manually drag workspaces into a custom order in the sidebar, rows you never touched were still ranked by an old activity snapshot (sortOrder). Whenever that snapshot got rewritten — a pass through the "Agent Activity" sort, a newly created workspace, a paired remote client pushing ranks — those rows re-ranked and your arrangement was lost. Manual ordering now sticks until you change it again.

What Changed

  • buildSparseManualOrderUpdates now backfills a manualOrder for every visible row in a reordered group whenever any non-moved row still lacks one, instead of persisting a rank for only the dragged row(s).
  • The manual-reorder rank-seeding call sites — the consolidated buildRankByWorktreeId helper for the list drag + cross-status drop (worktree-list/drag/use-status-mutations.ts:20-35), the board drawer (WorkspaceKanbanDrawer.tsx:420-425), and the kanban sidebar drop helper (workspace-kanban-sidebar-drop.ts:239-244) — now feed the ranker real manualOrder ranks only — not manualOrder ?? sortOrder — so a row lacking manualOrder is detected as a hole and backfilled, rather than masked by the mutable sortOrder.
  • The manual comparator (smart-sort.ts) is unchanged.

Why

A manual drag persisted manualOrder only for the moved row(s). Every other visible row kept manualOrder = undefined and the manual comparator ranked it by sortOrder. That fallback is the problem: sortOrder is written in three ways, none of which respect a manual arrangement —

  1. the sidebar persist effect re-snapshots it from the smart ranking, but only while sortBy === 'smart' (worktree-list/listing/use-sort-order.ts:212-219);
  2. creation-time and meta-row minting stamp Date.now() (src/main/persistence/loading-store/store.ts:3915, the default worktree-meta base);
  3. paired remote/CLI clients can push worktree.persistSortOrder at any time, mode-agnostic (src/main/runtime/rpc/methods/worktree.ts:174).

So staying inside Manual mode does not rewrite sortOrder on activity by itself — agent activity there writes only lastActivityAt/isUnread, which the manual comparator ignores. But any Smart episode ("Agent Activity" — the mode users pass through) rewrites every fallback rank, and rows sitting on the two mixed scales re-rank on return to Manual or on reload. That is what users experience as "agent activity broke my manual order" (issue #13847 has the fuller analysis). Mixing the scales also corrupted drag-time rank seeding, since the call sites seeded ranks with manualOrder ?? sortOrder, masking exactly the rows the sparse ranker's neighbour hole-check was meant to catch.

Backfilling every visible row on reorder means the ?? sortOrder fallback no longer drives any row after a drag, so the order survives Smart episodes, reloads, meta-row minting, and remote pushes. Legacy manual mode (a user who has never reordered — no manualOrder anywhere) is unchanged: it still restores the familiar sortOrder snapshot.

Linked Issue

Fixes #13847

Visual Proof

No visual/layout change — the fix is to ordering stability (an interaction behavior), not appearance. Repro in Testing; a short screen recording of the repro can be attached.

Testing

  • Automated tests added/updated

  • I manually tested these changes locally

  • worktree-manual-order.test.ts — new test: a reorder backfills manualOrder for every visible row when an unmoved row lacks one (previously only the moved row was updated).

  • workspace-kanban-sidebar-drop.test.ts — updated the cross-lane manual-drop case: with no prior manualOrder, the drop now backfills every visible row (doing-a > dropped > doing-b) instead of sparse-ranking only the dropped row onto sortOrder.

  • Full sidebar + worktree-store + sort-order-persistence suites pass (4733 tests) on the pre-rebase tree. pnpm typecheck:web, oxlint, and oxfmt clean on the changed files.

  • Rebased onto main on 2026-08-23 after the refactor(sidebar): group worktree-list files by domain #14486 worktree-list reorg: the two former WorktreeList.tsx seeding sites now live in buildRankByWorktreeId (worktree-list/drag/use-status-mutations.ts:20-35). Re-verified on the rebased tree: targeted suites (manual-order, kanban sidebar drop, sort-order persistence + host split — 47 tests), pnpm typecheck:web, oxlint, oxfmt all clean.

Repro a reviewer can follow (desktop sidebar):

  1. Drag two or more workspaces into a custom manual order (the drag itself switches the sort to Manual).
  2. Switch the sort menu to Agent Activity (Smart) and trigger agent activity — new output in a workspace that is NOT first.
  3. Switch the sort menu back to Manual.
  4. Before: rows that were never dragged have re-ranked against the fresh Smart snapshot — the manual arrangement is lost. After: the manual order holds.

(Staying in Manual and waiting for activity shows no before/after on a single desktop — nothing rewrites sortOrder in that mode; see Why. Newly created workspaces and remote persistSortOrder pushes hit the same fallback pre-fix.)

AI Disclosure

Developed with assistance from Claude (Anthropic). Root-cause analysis, approach, and tests reviewed by the contributor.

Review

  • Cross-platform: renderer-only change; no platform-specific code.
  • Backwards compatibility: the manual comparator is untouched; users with no prior manualOrder (legacy) still see the restored sortOrder snapshot. After the first manual reorder, all visible rows gain a stable manualOrder.
  • Performance: sparse ranking is preserved in steady state (all rows already have manualOrder); full backfill only runs when a hole exists (typically the first reorder). Cheap metadata writes only.
  • Wire / SSH / remote: no host or transport changes — renderer sort state only.
  • Security: no auth/transport surface changed.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (renderer-only)
  • pnpm lint, pnpm typecheck, and in-scope pnpm test pass (CI covers the full suite)

Author

  • X / Twitter: (optional — add your handle to be shouted out on merge)

@coderabbitai

coderabbitai Bot commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a070c46f-d774-41dc-bb68-e85619e12d15

📥 Commits

Reviewing files that changed from the base of the PR and between 853afdf and 88beb90.

📒 Files selected for processing (6)
  • src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx
  • src/renderer/src/components/sidebar/workspace-kanban-sidebar-drop.test.ts
  • src/renderer/src/components/sidebar/workspace-kanban-sidebar-drop.ts
  • src/renderer/src/components/sidebar/worktree-list/drag/use-status-mutations.ts
  • src/renderer/src/components/sidebar/worktree-manual-order-ranks.ts
  • src/renderer/src/components/sidebar/worktree-manual-order.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/renderer/src/components/sidebar/worktree-manual-order.test.ts
  • src/renderer/src/components/sidebar/worktree-manual-order-ranks.ts
  • src/renderer/src/components/sidebar/workspace-kanban-sidebar-drop.test.ts
  • src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx
  • src/renderer/src/components/sidebar/workspace-kanban-sidebar-drop.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Manual-order rank collection now excludes worktrees without a defined manualOrder instead of using sortOrder. Rank rebuilding detects missing or invalid persisted ranks on non-moved visible worktrees and updates the full ordered group. Tests cover stable visual ordering and manual-order updates for all visible rows.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #13847 by backfilling stable manual ranks and preventing activity-related reordering.
Out of Scope Changes check ✅ Passed The changes are limited to manual-order rank handling, related tests, and rank-seeding call sites.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Description check ✅ Passed The description covers the required sections, explains the change and rationale, links issue #13847, and documents testing and compatibility considerations.
Title check ✅ Passed The title clearly and concisely describes the primary change: preserving manual sidebar worktree order during agent activity.

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.

A manual reorder persisted manualOrder only for the dragged row(s); every other visible row kept falling back to the mutable sortOrder in the manual comparator, so the next agent activity jumped unmoved rows to the front and undid the user's order.

Backfill manualOrder for every visible row whenever a manual reorder leaves a non-moved row without one, and feed the ranker real manualOrder ranks only (not the sortOrder stand-in that masked the holes). The comparator is unchanged, so legacy manual mode (no manualOrder yet) still restores the familiar sortOrder snapshot.

Fixes stablyai#13847

Co-Authored-By: Claude <noreply@anthropic.com>
@blade035
blade035 force-pushed the fix/sidebar-manual-order-stable-on-activity branch from faeda9f to 88beb90 Compare August 23, 2026 19:48
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@nwparker

Copy link
Copy Markdown
Contributor

Thanks for this, @blade035 — keeping manual worktree order stable across agent activity was a real bug.

A more complete version already landed on main in #16488 (durable ranks for every known row, including hidden/collapsed ones, not only currently rendered rows). I'm closing this PR as no longer needed.

This note is from an automated scan of open PRs whose issue already shipped in a later merge. If that's wrong, please reopen and say so.

@nwparker nwparker closed this Aug 26, 2026
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.

Agents list: manual order jumps when an agent receives new activity

4 participants