From 14050339d9ce185931c52b809252bd9087c68901 Mon Sep 17 00:00:00 2001 From: Lukas <340913497+LukeCommits@users.noreply.github.com> Date: Fri, 30 Oct 2026 16:50:06 +0200 Subject: [PATCH 1/2] fix(server): Antigravity no longer delays server startup by deleting old runtimes Creating the Antigravity driver deleted leftover runtime temp directories inline, so server readiness waited for a recursive delete of the previous session's extracted runtimes. On Windows that took 7 s of a 12 s startup. Now the leftovers are renamed aside, which is instant, and deleted in a scoped background fiber. Each start also sweeps retirees an earlier run did not finish deleting. If the rename fails, it falls back to the inline delete. Co-Authored-By: Claude Opus 5.5 --- .../src/provider/Drivers/AntigravityDriver.ts | 6 ++-- .../acp/AntigravitySessionFiles.test.ts | 35 +++++++++++++++++++ .../provider/acp/AntigravitySessionFiles.ts | 34 ++++++++++++++---- 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 apps/server/src/provider/acp/AntigravitySessionFiles.test.ts diff --git a/apps/server/src/provider/Drivers/AntigravityDriver.ts b/apps/server/src/provider/Drivers/AntigravityDriver.ts index f1f39e104b44c..e2a988355867b 100644 --- a/apps/server/src/provider/Drivers/AntigravityDriver.ts +++ b/apps/server/src/provider/Drivers/AntigravityDriver.ts @@ -46,8 +46,8 @@ import { import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; import type { ServerProviderDraft } from "@t3tools/provider-core/server/snapshotProbe"; import { - removeAntigravityRuntimeTempDirs, removeAntigravitySessionFiles, + retireAntigravityRuntimeTempDirs, } from "../acp/AntigravitySessionFiles.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; @@ -150,9 +150,11 @@ export const AntigravityDriver: ProviderDriver< directories.runtimeTemp, path.join(profileDirectory, "antigravity-acp", "tmp"), ]) { - yield* removeAntigravityRuntimeTempDirs(directory).pipe( + const removeRetired = yield* retireAntigravityRuntimeTempDirs(directory).pipe( Effect.provideService(FileSystem.FileSystem, fileSystem), + Effect.provideService(Path.Path, path), ); + yield* Effect.forkScoped(removeRetired); } const continuationIdentity = defaultProviderContinuationIdentity({ driverKind: DRIVER, diff --git a/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts b/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts new file mode 100644 index 0000000000000..483d292e65a0d --- /dev/null +++ b/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts @@ -0,0 +1,35 @@ +import * as NodeServices from "@effect/platform-node/NodeServices"; +import { expect, it } from "@effect/vitest"; +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Path from "effect/Path"; + +import { retireAntigravityRuntimeTempDirs } from "./AntigravitySessionFiles.ts"; + +it.layer(NodeServices.layer)("retireAntigravityRuntimeTempDirs", (it) => { + it.effect("moves leftovers aside at once and deletes them, plus older retirees, later", () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const parent = yield* fs.makeTempDirectoryScoped(); + const runtimeTemp = path.join(parent, "abc123"); + const orphan = path.join(runtimeTemp, "run-orphan", "_MEI123"); + yield* fs.makeDirectory(orphan, { recursive: true }); + yield* fs.writeFileString(path.join(orphan, "payload.bin"), "stale"); + yield* fs.makeDirectory(path.join(parent, "abc123.old-unfinished", "runtime"), { + recursive: true, + }); + yield* fs.makeDirectory(path.join(parent, "other-instance")); + + const removeRetired = yield* retireAntigravityRuntimeTempDirs(runtimeTemp); + expect(yield* fs.exists(runtimeTemp)).toBe(false); + const retired = (yield* fs.readDirectory(parent)).filter((entry) => + entry.startsWith("abc123.old-"), + ); + expect(retired).toHaveLength(2); + + yield* removeRetired; + expect((yield* fs.readDirectory(parent)).toSorted()).toEqual(["other-instance"]); + }).pipe(Effect.scoped), + ); +}); diff --git a/apps/server/src/provider/acp/AntigravitySessionFiles.ts b/apps/server/src/provider/acp/AntigravitySessionFiles.ts index 7a25c8323174d..1c8069ed2d722 100644 --- a/apps/server/src/provider/acp/AntigravitySessionFiles.ts +++ b/apps/server/src/provider/acp/AntigravitySessionFiles.ts @@ -42,13 +42,35 @@ export const removeAntigravitySessionFiles = Effect.fn("removeAntigravitySession Effect.catch(() => Effect.logWarning("Could not remove temporary Antigravity session files.")), ); -/** Sweep only private runtimes before launch; Windows does not protect live extracted data files from deletion. */ -export const removeAntigravityRuntimeTempDirs = Effect.fn("removeAntigravityRuntimeTempDirs")( +const warnRuntimeTempLeftovers = Effect.logWarning( + "Could not remove leftover Antigravity runtime temp files.", +); + +/** + * Sweep only private runtimes before launch; Windows does not protect live extracted data files from deletion. + * Moves leftovers aside and returns their slow deletion (seconds on Windows) to run off the startup path. + */ +export const retireAntigravityRuntimeTempDirs = Effect.fn("retireAntigravityRuntimeTempDirs")( function* (tempDirectory: string) { const fs = yield* FileSystem.FileSystem; - yield* fs.remove(tempDirectory, { recursive: true, force: true }); + const path = yield* Path.Path; + const parent = path.dirname(tempDirectory); + const prefix = `${path.basename(tempDirectory)}.old-`; + if (yield* fs.exists(tempDirectory)) { + const retired = yield* fs.makeTempDirectory({ directory: parent, prefix }); + yield* fs + .rename(tempDirectory, path.join(retired, "runtime")) + .pipe(Effect.catch(() => fs.remove(tempDirectory, { recursive: true, force: true }))); + } + const entries = yield* fs.readDirectory(parent).pipe(Effect.orElseSucceed(() => [])); + return Effect.forEach( + entries.filter((entry) => entry.startsWith(prefix)), + (entry) => + fs + .remove(path.join(parent, entry), { recursive: true, force: true }) + .pipe(Effect.catch(() => warnRuntimeTempLeftovers)), + { discard: true }, + ); }, - Effect.catch(() => - Effect.logWarning("Could not remove leftover Antigravity runtime temp files."), - ), + Effect.catch(() => warnRuntimeTempLeftovers.pipe(Effect.as(Effect.void))), ); From b679dd227c124d0207a86c03a3cfce001399812e Mon Sep 17 00:00:00 2001 From: Lukas <340913497+LukeCommits@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:03:11 +0200 Subject: [PATCH 2/2] fix(server): delete Antigravity leftovers inline when they cannot be moved aside If creating the retiree directory failed, for example on a full disk, the leftover runtime stayed in place. Treat that failure like a failed rename and fall back to the inline delete, which can free the space. Co-Authored-By: Claude Opus 5.5 --- .../acp/AntigravitySessionFiles.test.ts | 29 +++++++++++++++++++ .../provider/acp/AntigravitySessionFiles.ts | 8 ++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts b/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts index 483d292e65a0d..795e80b861e84 100644 --- a/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts +++ b/apps/server/src/provider/acp/AntigravitySessionFiles.test.ts @@ -3,6 +3,7 @@ import { expect, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; +import * as PlatformError from "effect/PlatformError"; import { retireAntigravityRuntimeTempDirs } from "./AntigravitySessionFiles.ts"; @@ -32,4 +33,32 @@ it.layer(NodeServices.layer)("retireAntigravityRuntimeTempDirs", (it) => { expect((yield* fs.readDirectory(parent)).toSorted()).toEqual(["other-instance"]); }).pipe(Effect.scoped), ); + + it.effect( + "deletes leftovers inline when they cannot be moved aside, such as on a full disk", + () => + Effect.gen(function* () { + const removed: Array = []; + const fs = FileSystem.makeNoop({ + exists: () => Effect.succeed(true), + makeTempDirectory: () => + Effect.fail( + PlatformError.systemError({ + _tag: "Unknown", + module: "FileSystem", + method: "makeTempDirectory", + description: "ENOSPC", + }), + ), + remove: (path) => Effect.sync(() => void removed.push(path)), + readDirectory: () => Effect.succeed([]), + }); + + const removeRetired = yield* retireAntigravityRuntimeTempDirs( + "/state/antigravity-tmp/abc123", + ).pipe(Effect.provideService(FileSystem.FileSystem, fs)); + yield* removeRetired; + expect(removed).toEqual(["/state/antigravity-tmp/abc123"]); + }), + ); }); diff --git a/apps/server/src/provider/acp/AntigravitySessionFiles.ts b/apps/server/src/provider/acp/AntigravitySessionFiles.ts index 1c8069ed2d722..819d79a05ed12 100644 --- a/apps/server/src/provider/acp/AntigravitySessionFiles.ts +++ b/apps/server/src/provider/acp/AntigravitySessionFiles.ts @@ -57,10 +57,10 @@ export const retireAntigravityRuntimeTempDirs = Effect.fn("retireAntigravityRunt const parent = path.dirname(tempDirectory); const prefix = `${path.basename(tempDirectory)}.old-`; if (yield* fs.exists(tempDirectory)) { - const retired = yield* fs.makeTempDirectory({ directory: parent, prefix }); - yield* fs - .rename(tempDirectory, path.join(retired, "runtime")) - .pipe(Effect.catch(() => fs.remove(tempDirectory, { recursive: true, force: true }))); + yield* fs.makeTempDirectory({ directory: parent, prefix }).pipe( + Effect.flatMap((retired) => fs.rename(tempDirectory, path.join(retired, "runtime"))), + Effect.catch(() => fs.remove(tempDirectory, { recursive: true, force: true })), + ); } const entries = yield* fs.readDirectory(parent).pipe(Effect.orElseSucceed(() => [])); return Effect.forEach(