From e0201d3ab51b4593eff3c41d03ab9c9b2dc07471 Mon Sep 17 00:00:00 2001 From: ANSH SINGH Date: Wed, 7 Oct 2026 22:55:05 +0530 Subject: [PATCH] fix(server): free worktrees for terminal thread statuses (#15150) --- apps/server/src/storageCleanup.test.ts | 42 ++++++++++++++++++++++++++ apps/server/src/storageCleanup.ts | 7 ++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/server/src/storageCleanup.test.ts b/apps/server/src/storageCleanup.test.ts index 3bc9fb63465ceb47f2fd6ddf1251fe592047fa21..c66aee5302d6ca8d6bf49f7314197005808b617b 100644 --- a/apps/server/src/storageCleanup.test.ts +++ b/apps/server/src/storageCleanup.test.ts @@ -3,6 +3,7 @@ import { ProjectId, ProviderInstanceId, RunId, + RuntimeRequestId, ThreadId, type OrchestrationV2ThreadShell, } from "@t3tools/contracts"; @@ -84,6 +85,47 @@ describe("V2 storage cleanup eligibility", () => { }, ); + it.each(["idle", "completed", "interrupted", "failed", "cancelled", "rolled_back"] as const)( + "allows cleanup once its thread is %s", + (status) => { + expect(storageCleanupThreadIdle(candidateWithStatus(status), NOW_MS)).toBe(true); + }, + ); + + it.each(["completed", "interrupted", "cancelled", "rolled_back"] as const)( + "retains %s while background work is pending", + (status) => { + expect( + storageCleanupThreadIdle( + { + ...candidateWithStatus(status), + pendingBackgroundTasks: [{ taskId: "task-1", kind: "command" }], + }, + NOW_MS, + ), + ).toBe(false); + }, + ); + + it.each(["completed", "interrupted", "cancelled", "rolled_back"] as const)( + "retains %s while a runtime request is pending", + (status) => { + expect( + storageCleanupThreadIdle( + { + ...candidateWithStatus(status), + pendingRuntimeRequest: { + id: RuntimeRequestId.make("request-1"), + kind: "command", + createdAt: at(0), + }, + }, + NOW_MS, + ), + ).toBe(false); + }, + ); + it("retains an active run even if the shell status is idle", () => { expect( storageCleanupThreadIdle({ ...candidate(), activeRunId: RunId.make("run") }, NOW_MS), diff --git a/apps/server/src/storageCleanup.ts b/apps/server/src/storageCleanup.ts index 40ebd9cc3b44f8b1fc177bcc38bb8a356a80e2d3..d823e84e45ddc075d957ef1e7b681275908b6770 100644 --- a/apps/server/src/storageCleanup.ts +++ b/apps/server/src/storageCleanup.ts @@ -86,7 +86,12 @@ export function storageCleanupThreadIdle(thread: OrchestrationV2ThreadShell, now thread.branch !== null && thread.worktreePath !== null && thread.activeRunId === null && - (thread.status === "idle" || thread.status === "failed") && + (thread.status === "idle" || + thread.status === "completed" || + thread.status === "interrupted" || + thread.status === "failed" || + thread.status === "cancelled" || + thread.status === "rolled_back") && (thread.pendingBackgroundTasks?.length ?? 0) === 0 && thread.pendingRuntimeRequest === null && !threadHasQueuedTurnStart(thread, now) -- 2.54.0