This was generated by AI during triage.
Summary
The order of terminal panes in the grid does not always match the order of rows in the session sidebar, and there is no way to drag a pane to fix it — the panes can only be reordered indirectly, by dragging sidebar rows.
Why they drift
The sidebar and the grid render from two different collections, in two different orders, kept in sync only by hand at each mutation site:
|
Source |
Ordering applied |
| Sidebar |
SessionManager.Sessions (includes dormant) |
re-sectioned in InlineHeaders mode: ungrouped first, then each group by SortOrder; worktree cluster headers woven in; dormant rows appended last |
| Grid |
MainViewModel.Sessions (live only) |
flat collection order, optional active-group filter, then paged around the active session |
Nothing reconciles the two. Three concrete ways they diverge:
1. Group sectioning is sidebar-only. In GroupDisplayMode.InlineHeaders, the sidebar groups rows into sections by GroupId. The grid ignores grouping entirely and renders the flat collection order. Any session list where group members are interleaved therefore renders in a different order in the grid than in the sidebar — deterministically, with no drag needed.
2. The drag-drop target index is computed in the wrong index space. GetSidebarDropIndex returns a position among the visible sidebar Border children. That list contains worktree cluster headers and the dormant rows appended at the bottom, so it is not an index into either backing collection. That single integer is then applied to both:
SessionManager.MoveSession — over a dormant-inclusive, unfiltered, all-groups list
MainViewModel.Sessions.Move — over a live-only list, separately re-clamped to its own Count
Two lists of different lengths, one index, two independent clamps. One drop can move the row to index n in the sidebar model and index m in the grid model. This is why it is intermittent: with no dormant sessions, no groups and no clusters the three index spaces coincide and the bug is invisible.
3. Group filtering differs. The sidebar honours GroupDisplayMode / ActiveGroupId; the grid honours FilterGridByActiveGroup / EffectiveActiveGroupId. Different predicates over different collections.
Desired behavior
The visible pane order should be derivable from the visible sidebar order — one ordering concept, not two that are hand-synced. Specifically:
- Reading the sidebar top-to-bottom (skipping headers and dormant rows) and reading the grid in slot order should yield the same session sequence, in every
GroupDisplayMode, with clusters and dormant sessions present.
- A sidebar drag that visually lands a row in position n should put its pane in grid slot n — not slot n±k.
- Paging (
GetViewportSessions) may still show a window onto that sequence when sessions outnumber slots, but must not reorder within the window.
Key interfaces
MainViewModel.Sessions vs SessionManager.Sessions — the two orderings. Either one becomes authoritative and the other derives from it, or a single shared ordered projection is introduced that both the sidebar builder and the grid builder consume.
MainViewModel.MoveSession(sessionId, newIndex) — currently forwards the same newIndex to two collections of different length and lets each clamp independently. Needs an unambiguous index space.
- The sidebar drop-target resolvers, which currently return positions among rendered UI children rather than positions in a session sequence. Mapping "drop Y" → "index in the visible session sequence" is WPF-free logic and should be extracted so it can be unit-tested, matching the precedent set by
SessionConfigEditor and ShellIntegrationPayload.
- The layout signature that short-circuits grid rebuilds is computed from the grid's own ordering, so it must be recomputed from whatever becomes authoritative — otherwise a reorder that changes only the sidebar will be skipped as a no-op render.
Acceptance criteria
Out of scope
Summary
The order of terminal panes in the grid does not always match the order of rows in the session sidebar, and there is no way to drag a pane to fix it — the panes can only be reordered indirectly, by dragging sidebar rows.
Why they drift
The sidebar and the grid render from two different collections, in two different orders, kept in sync only by hand at each mutation site:
SessionManager.Sessions(includes dormant)InlineHeadersmode: ungrouped first, then each group bySortOrder; worktree cluster headers woven in; dormant rows appended lastMainViewModel.Sessions(live only)Nothing reconciles the two. Three concrete ways they diverge:
1. Group sectioning is sidebar-only. In
GroupDisplayMode.InlineHeaders, the sidebar groups rows into sections byGroupId. The grid ignores grouping entirely and renders the flat collection order. Any session list where group members are interleaved therefore renders in a different order in the grid than in the sidebar — deterministically, with no drag needed.2. The drag-drop target index is computed in the wrong index space.
GetSidebarDropIndexreturns a position among the visible sidebarBorderchildren. That list contains worktree cluster headers and the dormant rows appended at the bottom, so it is not an index into either backing collection. That single integer is then applied to both:SessionManager.MoveSession— over a dormant-inclusive, unfiltered, all-groups listMainViewModel.Sessions.Move— over a live-only list, separately re-clamped to its ownCountTwo lists of different lengths, one index, two independent clamps. One drop can move the row to index n in the sidebar model and index m in the grid model. This is why it is intermittent: with no dormant sessions, no groups and no clusters the three index spaces coincide and the bug is invisible.
3. Group filtering differs. The sidebar honours
GroupDisplayMode/ActiveGroupId; the grid honoursFilterGridByActiveGroup/EffectiveActiveGroupId. Different predicates over different collections.Desired behavior
The visible pane order should be derivable from the visible sidebar order — one ordering concept, not two that are hand-synced. Specifically:
GroupDisplayMode, with clusters and dormant sessions present.GetViewportSessions) may still show a window onto that sequence when sessions outnumber slots, but must not reorder within the window.Key interfaces
MainViewModel.SessionsvsSessionManager.Sessions— the two orderings. Either one becomes authoritative and the other derives from it, or a single shared ordered projection is introduced that both the sidebar builder and the grid builder consume.MainViewModel.MoveSession(sessionId, newIndex)— currently forwards the samenewIndexto two collections of different length and lets each clamp independently. Needs an unambiguous index space.SessionConfigEditorandShellIntegrationPayload.Acceptance criteria
InlineHeadersmode and interleaved members, sidebar row order and grid slot order match.FilterStripandNonedisplay modes.state.json.Out of scope