From ba18c4783b6df69068e316471260ec84f4601ecd Mon Sep 17 00:00:00 2001 From: spoukyii <61633921+spoukyii@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:35:41 -0800 Subject: [PATCH 1/2] test(web): deleting the open thread with a side chat or subagent beside it Adds side chat and subagent rows to the fallback table that already covers snoozed and settled threads. Neither has a sidebar row, yet the thread opened after deleting the current one can be either, and both rows fail here. --- apps/web/src/components/Sidebar.logic.test.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/Sidebar.logic.test.ts b/apps/web/src/components/Sidebar.logic.test.ts index 3e2e8be2ce18d..88de6144a7ec9 100644 --- a/apps/web/src/components/Sidebar.logic.test.ts +++ b/apps/web/src/components/Sidebar.logic.test.ts @@ -636,7 +636,28 @@ describe("getFallbackThreadIdAfterDelete", () => { { snoozedUntil: "2099-01-01T00:00:00.000Z", snoozedAt: "2026-03-09T10:06:00.000Z" }, ], ["settled", { settledOverride: "settled", settledAt: "2026-03-09T10:06:00.000Z" }], - ] as const)("skips a %s thread parked on its shelf", (_state, parked) => { + [ + "side chat", + { + lineage: { + rootThreadId: ThreadId.make("thread-visible"), + parentThreadId: ThreadId.make("thread-visible"), + relationshipToParent: "fork", + sideChat: true, + }, + }, + ], + [ + "subagent", + { + lineage: { + rootThreadId: ThreadId.make("thread-visible"), + parentThreadId: ThreadId.make("thread-visible"), + relationshipToParent: "subagent", + }, + }, + ], + ] as const)("skips a %s thread the sidebar does not list", (_state, parked) => { const fallbackThreadId = getFallbackThreadIdAfterDelete({ threads: [ makeThread({ From 07c7b1798cad28edde6a7c7627fa614c2af13e5b Mon Sep 17 00:00:00 2001 From: spoukyii <61633921+spoukyii@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:35:43 -0800 Subject: [PATCH 2/2] fix(web): deleting the open thread no longer opens a side chat or subagent Deleting the thread you are looking at opens the newest other thread in its project. That pick reads every thread shell the environment streams, and the shells include side chats and running subagents, which live inside their parent and never get a sidebar row. When one of them was the newest, deleting the open thread dropped you into it on its own. #29891 fixed the same pick in the command palette's "open project" with isSidebarHiddenThread, and #29643 taught this fallback to skip snoozed and settled threads. The fallback now skips threads the sidebar does not list as well, with the same helper. Bug: deleting the open thread opens a side chat or subagent instead of a listed thread --- apps/web/src/components/Sidebar.logic.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/Sidebar.logic.ts b/apps/web/src/components/Sidebar.logic.ts index c20789e2a9160..6b006352fabaa 100644 --- a/apps/web/src/components/Sidebar.logic.ts +++ b/apps/web/src/components/Sidebar.logic.ts @@ -1211,7 +1211,10 @@ export function resolveThreadStatusPill(input: { } export function getFallbackThreadIdAfterDelete< - T extends Pick & + T extends Pick< + Thread, + "id" | "projectId" | "createdAt" | "updatedAt" | "settledOverride" | "lineage" + > & ThreadSortInput & ThreadSnoozeShell, >(input: { @@ -1235,7 +1238,8 @@ export function getFallbackThreadIdAfterDelete< thread.id !== deletedThreadId && !deletedThreadIds?.has(thread.id) && thread.settledOverride !== "settled" && - !effectiveSnoozed(thread, { now }), + !effectiveSnoozed(thread, { now }) && + !isSidebarHiddenThread(thread), ), sortOrder, )[0]?.id ?? null