diff --git a/apps/server/src/storageCleanup.test.ts b/apps/server/src/storageCleanup.test.ts index de1ccfefcf64f..3c1913439fa7f 100644 --- a/apps/server/src/storageCleanup.test.ts +++ b/apps/server/src/storageCleanup.test.ts @@ -2450,6 +2450,26 @@ describe("storage cleanup reports and local file policies", () => { }), ), ); + it.live("keeps a thread's recreated worktree folder that has no .git", () => + runCleanupTest( + Effect.gen(function* () { + const fixture = yield* cleanupFixture(); + yield* fixture.command(fixture.repo, ["worktree", "remove", fixture.worktree]); + yield* fixture.fs.makeDirectory(`${fixture.worktree}/node_modules`, { recursive: true }); + yield* fixture.fs.writeFileString(`${fixture.worktree}/yarn.lock`, "# lockfile\n"); + expect((yield* fixture.service.runNow).entries).toEqual([ + expect.objectContaining({ + path: fixture.worktree, + outcome: "kept", + reason: "Not a linked worktree", + }), + ]); + expect(yield* fixture.fs.readFileString(`${fixture.worktree}/yarn.lock`)).toBe( + "# lockfile\n", + ); + }), + ), + ); it.live.each([ ["any-local-files", ".env", "kept"], ["uncommitted-changes", ".env", "removed"], diff --git a/apps/server/src/storageCleanup.ts b/apps/server/src/storageCleanup.ts index a5f9cd7eefa12..a4e6064910f79 100644 --- a/apps/server/src/storageCleanup.ts +++ b/apps/server/src/storageCleanup.ts @@ -846,7 +846,8 @@ const make = Effect.gen(function* () { return "worktree is a symbolic link"; if (!roots.some((root) => inside(root, realPath))) return "outside the managed worktree folder"; - if ((yield* fs.stat(path.join(worktreePath, ".git"))).type !== "File") + const gitPath = path.join(worktreePath, ".git"); + if (!(yield* fs.exists(gitPath)) || (yield* fs.stat(gitPath)).type !== "File") return "not a linked worktree"; return null; });