feat(vscode): restore multi-project section and drag-and-drop support - #12803
Conversation
Restore Agent Manager sections and worktree drag-and-drop when multiple projects are shown, with ordering and section moves scoped to the owning project. - Extract shared worktree ordering/grouping logic used by both single and multi-project modes - Add per-project drag-and-drop providers with scoped identifiers - Preserve live run statuses when state payloads omit them - Match single-project label resolution using ordered session titles - Make section auto-rename tracking request-scoped to prevent stale renames - Expand multi-project Storybook story with persisted ordering, sections, and grouped worktrees - Add project-store isolation and ordering tests
| const state = this.getStateManager() | ||
| if (state) { | ||
| state.setWorktreeOrder(m.order) | ||
| this.pushState() |
There was a problem hiding this comment.
SUGGESTION: The added pushState() on every drag end may not be needed, and it is not free
The multi-project body now applies the new order to its own store optimistically (ProjectSidebarBody.tsx:156-162), so unlike setSessionsCollapsed (which documents why it must round-trip) nothing here depends on the echoed state. Two side effects worth weighing:
pushState()callspushProjectSessions(), which re-lists sessions for the project root and every worktree directory whenever the 2s freshness window has lapsed — i.e. a backend round trip per worktree on each drop.- The pushed payload includes
tabOrder, andstore.applyState()overwrites the store's tab order with it. The persisted order has terminal/review tab ids stripped (AgentManagerApp.tsx:506-508), so a worktree drag end can snap those tabs back to the tail of the tab bar. That already happens forrenameWorktree/section ops, but this adds a new, much more frequent trigger.
If the push is only there so a background project's normalized order lands in the webview, the store already holds an equivalent order; consider dropping it (or gating on whether the normalization actually changed anything — WorktreeStateManager.setWorktreeOrder discards the changed result that moveSection already uses).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| const runs: Record<string, RunStatus> = {} | ||
| for (const item of state.runStatuses ?? []) runs[item.worktreeId] = item | ||
| setRunStatuses(runs) | ||
| if (state.runStatuses) { |
There was a problem hiding this comment.
SUGGESTION: Preserved run statuses are never reconciled against the worktree list
Guarding on state.runStatuses is the right fix for background payloads (pushState omits runStatuses for non-active projects), but it also means a status can now outlive the worktree it belongs to: a background project's payload never resends the list, so a run status for a deleted worktree stays in the map forever and keeps runs()[id]?.state === "running" truthy for a recreated/reused id. The busy map right below already prunes against state.worktrees; consider doing the same here, e.g. filtering the retained statuses by ids instead of leaving them untouched.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| return | ||
| } | ||
| if (section && sections().some((item) => item.id === section)) { | ||
| post({ type: "agentManager.moveToSection", worktreeIds: [from], sectionId: section }) |
There was a problem hiding this comment.
SUGGESTION: The section-drop branch skips the origin rewind that the other exit paths perform
By the time the pointer reaches a section header, onDragOver has already rewritten store.worktreeOrder for every worktree it passed over. Both invalid-drop branches rewind with store.setWorktreeOrder(origin), but this branch neither persists the new order nor rewinds it, so local order stays diverged from persisted until the moveToSection round-trip push arrives. It does self-heal today (the section handler always pushes state, and the payload always carries worktreeOrder), so this is cosmetic — but the asymmetry is easy to misread as intentional.
While here: origin is snapshotted at drag start, so if an agentManager.state push lands mid-drag, the rewind paths restore the pre-drag order and discard the newer persisted one until the next push.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| const detector = sectionAwareDetector(sectionIds, home) | ||
| const dragIds = createMemo(() => sorted().map((wt) => scope("worktree", wt.id))) | ||
|
|
||
| const onDragStart = (event: DragEvent) => { |
There was a problem hiding this comment.
SUGGESTION: These three handlers are a near-copy of SidebarBody.tsx:258-286
The only real differences are the scoped ids and the origin rewind. Since the PR already extracted sortWorktrees to keep single- and multi-project ordering in sync, it would be consistent to extract the drag handlers too (a small factory taking scope/parse, the sorted list accessor, and the persist callback), so the two sidebars cannot drift apart the next time drag behaviour changes. Not a blocker.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
|
|
||
| it("keeps multi-version siblings adjacent at the first group position", () => { | ||
| const all = [wt("a", { groupId: "g" }), wt("b"), wt("c", { groupId: "g" })] | ||
| expect(sortWorktrees(all, ["b", "c", "a"]).map((item) => item.id)).toEqual(["b", "c", "a"]) |
There was a problem hiding this comment.
SUGGESTION: This case does not actually exercise the grouping branch
With order = ["b", "c", "a"] the expected result is identical to plain applyTabOrder output, so the test passes even if the group-adjacency logic is removed. A case where the two differ would pin the behaviour the test name describes, e.g. sortWorktrees(all, ["a", "b", "c"]) should yield ["a", "c", "b"] — the g sibling pulled up next to its group start.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
Code Review SummaryStatus: 6 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Incremental review noteSince the previous review ( Files Reviewed (14 files)
|
…on-drag-and-drop feat(vscode): restore multi-project section and drag-and-drop support
Summary
Restore Agent Manager sections and worktree drag-and-drop when multiple projects are shown, with ordering and section moves scoped to the owning project.
Changes
Testing