diff --git a/apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts b/apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts index 6f8ef830daf5c..8d0ad779842cf 100644 --- a/apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts +++ b/apps/web/src/components/settings/ProjectSettingsPanel.logic.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vite-plus/test"; -import { EnvironmentId, ProjectId } from "@t3tools/contracts"; +import { EnvironmentId, ProjectId, ThreadId } from "@t3tools/contracts"; import { planProjectRemoval } from "@t3tools/client-runtime/state/projects"; describe("project removal confirmation", () => { @@ -16,18 +16,32 @@ describe("project removal confirmation", () => { environmentLabel: "Remote", }; - it("limits a checkout's thread count to its environment and project", () => { + const parentId = ThreadId.make("parent"); + const listed = { rootThreadId: parentId, parentThreadId: null, relationshipToParent: null }; + const child = { rootThreadId: parentId, parentThreadId: parentId }; + + it("limits a checkout's thread count to its listed threads in that environment and project", () => { const plan = planProjectRemoval({ members: [local], groupMemberCount: 2, hasOtherMembers: false, displayName: "Repo", threads: [ - { environmentId: local.environmentId, projectId: local.id }, - { environmentId: remote.environmentId, projectId: remote.id }, + { environmentId: local.environmentId, projectId: local.id, lineage: listed }, + { + environmentId: local.environmentId, + projectId: local.id, + lineage: { ...child, relationshipToParent: "subagent" }, + }, + { + environmentId: local.environmentId, + projectId: local.id, + lineage: { ...child, relationshipToParent: "fork", sideChat: true }, + }, + { environmentId: remote.environmentId, projectId: remote.id, lineage: listed }, ], }); - expect(plan.projectThreads).toHaveLength(1); + expect(plan.projectThreads).toHaveLength(3); expect(plan.confirmationMessage).toBe( 'Remove checkout "Repo" and delete its 1 thread?\nPath: /work/repo\nEnvironment: Local\nThose threads and any archived threads are deleted from T3 Code. Provider sessions are kept.\nOther entries in this grouped project are unaffected.\nThis action cannot be undone.', ); diff --git a/packages/client-runtime/src/state/projectRemoval.ts b/packages/client-runtime/src/state/projectRemoval.ts index 09c65a772e689..2225e5758bd2d 100644 --- a/packages/client-runtime/src/state/projectRemoval.ts +++ b/packages/client-runtime/src/state/projectRemoval.ts @@ -1,7 +1,9 @@ +import { isListedThreadLineage } from "@t3tools/contracts"; + import type { EnvironmentProject, EnvironmentThreadShell } from "./models.ts"; export function planProjectRemoval< - Thread extends Pick, + Thread extends Pick, >(options: { readonly members: ReadonlyArray< Pick & { @@ -20,7 +22,10 @@ export function planProjectRemoval< const projectThreads = threads.filter((thread) => memberKeys.has(`${thread.environmentId}:${thread.projectId}`), ); - const threadCount = projectThreads.length + (options.unloadedThreadCount ?? 0); + // Side chats and subagents go with their parent but never show in thread lists. + const threadCount = + projectThreads.filter((thread) => isListedThreadLineage(thread.lineage)).length + + (options.unloadedThreadCount ?? 0); const isWholeGroup = members.length === groupMemberCount; const targetKind = hasOtherMembers || !isWholeGroup ? "checkout" : "project"; const singleMember = members.length === 1 ? members[0]! : null;