From 9b0fa1f80b6fc954e5b07ef2f1d607bba7f940dd Mon Sep 17 00:00:00 2001 From: Antigravity Date: Tue, 15 Sep 2026 09:05:05 +0800 Subject: [PATCH 1/3] test: reproduce runtime bootstrap lease expiry --- src/chat-swarm-runtime.test.ts | 37 +++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/chat-swarm-runtime.test.ts b/src/chat-swarm-runtime.test.ts index fd24ec312..621d08b06 100644 --- a/src/chat-swarm-runtime.test.ts +++ b/src/chat-swarm-runtime.test.ts @@ -42,6 +42,8 @@ class FakeManagedAdapter implements ChatSwarmManagedCarrierAdapter { failProvision = false; failStop = false; recoverReady = true; + provisionDelayMs = 0; + bootstrapDelayMs = 0; onBootstrap?: (operationId: string, rawIdentity: string) => void; readonly rawIdentities = new Map(); @@ -72,6 +74,9 @@ class FakeManagedAdapter implements ChatSwarmManagedCarrierAdapter { deadlineAt: string; }): Promise { this.provisionCalls += 1; + if (this.provisionDelayMs > 0) { + await new Promise((resolvePromise) => setTimeout(resolvePromise, this.provisionDelayMs)); + } if (this.failProvision) throw new Error("response lost after possible create"); const rawIdentity = `managed-conversation-${input.runtimeSlot}`; this.rawIdentities.set(input.runtimeSlot, rawIdentity); @@ -91,6 +96,9 @@ class FakeManagedAdapter implements ChatSwarmManagedCarrierAdapter { deadlineAt: string; }) { this.bootstrapCalls += 1; + if (this.bootstrapDelayMs > 0) { + await new Promise((resolvePromise) => setTimeout(resolvePromise, this.bootstrapDelayMs)); + } const rawIdentity = this.rawIdentities.get(input.runtimeSlot)!; this.onBootstrap?.(input.operationId, rawIdentity); return { disposition: "DELIVERED" as const, remoteMayContinue: true }; @@ -139,7 +147,10 @@ class FakeManagedAdapter implements ChatSwarmManagedCarrierAdapter { } } -function fixture(workerLimit = 5) { +function fixture( + workerLimit = 5, + timing: { operationTimeoutMs?: number; bootstrapWaitMs?: number } = {}, +) { const root = mkdtempSync(join(tmpdir(), "devspace-runtime-117-")); const store = new ChatSwarmStore(root); const coordinator = new ChatSwarmCoordinator(store); @@ -151,8 +162,8 @@ function fixture(workerLimit = 5) { DEVSPACE_CHAT_SWARM_RUNTIME: "1", DEVSPACE_CHAT_SWARM_PROJECT_URL: "https://chatgpt.com/g/g-p-runtime-test/project", DEVSPACE_CHAT_SWARM_POOL_DEFAULT: "3", - DEVSPACE_CHAT_SWARM_RUNTIME_TIMEOUT_MS: "5000", - DEVSPACE_CHAT_SWARM_BOOTSTRAP_WAIT_MS: "5000", + DEVSPACE_CHAT_SWARM_RUNTIME_TIMEOUT_MS: String(timing.operationTimeoutMs ?? 5_000), + DEVSPACE_CHAT_SWARM_BOOTSTRAP_WAIT_MS: String(timing.bootstrapWaitMs ?? 5_000), }; const manager = new ChatSwarmRuntimeManager( coordinator, @@ -404,6 +415,26 @@ test("concurrent runtime ensure creates only missing managed workers and exact r } }); +test("bootstrap authority lease covers sequential bounded provisioning phases", async () => { + const f = fixture(1, { operationTimeoutMs: 1_000, bootstrapWaitMs: 1_000 }); + f.adapter.provisionDelayMs = 800; + f.adapter.bootstrapDelayMs = 300; + try { + const status = await f.manager.ensure(f.owner, f.swarm.id, 1); + assert.equal(status.slots[0]?.state, "PARKED"); + assert.equal(f.adapter.provisionCalls, 1); + assert.equal(f.adapter.bootstrapCalls, 1); + assert.equal( + f.coordinator.store + .listWorkers(f.swarm.id) + .filter((worker) => worker.lifecycleState !== "DISABLED").length, + 1, + ); + } finally { + cleanup(f); + } +}); + test("cold ensure reconciles exact existing carriers before declaring the pool healthy", async () => { const f = fixture(); try { From 76e3d1a5c9a0c039a88664f41ca76f59c9dd8ec6 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Tue, 15 Sep 2026 09:10:24 +0800 Subject: [PATCH 2/3] test: bound runtime fixture pool default --- src/chat-swarm-runtime.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chat-swarm-runtime.test.ts b/src/chat-swarm-runtime.test.ts index 621d08b06..b237bb673 100644 --- a/src/chat-swarm-runtime.test.ts +++ b/src/chat-swarm-runtime.test.ts @@ -161,7 +161,7 @@ function fixture( const env: NodeJS.ProcessEnv = { DEVSPACE_CHAT_SWARM_RUNTIME: "1", DEVSPACE_CHAT_SWARM_PROJECT_URL: "https://chatgpt.com/g/g-p-runtime-test/project", - DEVSPACE_CHAT_SWARM_POOL_DEFAULT: "3", + DEVSPACE_CHAT_SWARM_POOL_DEFAULT: String(Math.min(3, workerLimit)), DEVSPACE_CHAT_SWARM_RUNTIME_TIMEOUT_MS: String(timing.operationTimeoutMs ?? 5_000), DEVSPACE_CHAT_SWARM_BOOTSTRAP_WAIT_MS: String(timing.bootstrapWaitMs ?? 5_000), }; From 38ff87271bd9d42966c9533dd572579e802a8d4e Mon Sep 17 00:00:00 2001 From: Antigravity Date: Tue, 15 Sep 2026 09:14:22 +0800 Subject: [PATCH 3/3] fix: renew bootstrap authority lease per phase --- src/chat-swarm-runtime.test.ts | 5 ++++- src/chat-swarm-runtime.ts | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/chat-swarm-runtime.test.ts b/src/chat-swarm-runtime.test.ts index b237bb673..215e76f62 100644 --- a/src/chat-swarm-runtime.test.ts +++ b/src/chat-swarm-runtime.test.ts @@ -470,7 +470,10 @@ test("managed bootstrap binds the authenticated peer separately from the browser authenticatedPeerFingerprint: peerFingerprint, appBinding: "READY", }); - assert.equal(f.registry.claimBootstrap(prepared.operation!.operationId), true); + assert.equal( + f.registry.claimBootstrap(prepared.operation!.operationId, 5_000), + true, + ); assert.throws( () => f.manager.bootstrap( diff --git a/src/chat-swarm-runtime.ts b/src/chat-swarm-runtime.ts index 7a6ae755f..7d85a6b0a 100644 --- a/src/chat-swarm-runtime.ts +++ b/src/chat-swarm-runtime.ts @@ -136,6 +136,7 @@ interface ProvisionReceipt { conversationUrl?: string; conversationFingerprint?: string; authenticatedPeerFingerprint?: string; + bootstrapExpiresAt?: string; workerId?: string; remoteMayContinue: boolean; observedAt: string; @@ -736,8 +737,15 @@ export class ChatSwarmRuntimeStore { return tx.immediate(); } - claimBootstrap(operationId: string): boolean { + claimBootstrap(operationId: string, ttlMs: number): boolean { + if (!Number.isSafeInteger(ttlMs) || ttlMs <= 0) { + throw new ChatSwarmError( + "INVALID_STATE", + "bootstrap authority lease ttl must be a positive integer", + ); + } const observedAt = nowIso(); + const bootstrapExpiresAt = new Date(Date.parse(observedAt) + ttlMs).toISOString(); const tx = this.database.sqlite.transaction(() => { const operation = this.requireProvision(operationId); if (operation.status !== "carrier_created") return false; @@ -748,6 +756,7 @@ export class ChatSwarmRuntimeStore { conversationFingerprint: operation.receipt?.conversationFingerprint, authenticatedPeerFingerprint: operation.receipt?.authenticatedPeerFingerprint, + bootstrapExpiresAt, remoteMayContinue: true, observedAt, }; @@ -796,6 +805,7 @@ export class ChatSwarmRuntimeStore { conversationFingerprint: operation.receipt?.conversationFingerprint, authenticatedPeerFingerprint: operation.receipt?.authenticatedPeerFingerprint, + bootstrapExpiresAt: operation.receipt?.bootstrapExpiresAt, workerId: operation.receipt?.workerId, remoteMayContinue: true, observedAt, @@ -870,6 +880,7 @@ export class ChatSwarmRuntimeStore { conversationFingerprint: operation.receipt.conversationFingerprint, authenticatedPeerFingerprint: operation.receipt.authenticatedPeerFingerprint, + bootstrapExpiresAt: operation.receipt.bootstrapExpiresAt, workerId: worker.id, remoteMayContinue: false, observedAt, @@ -2340,7 +2351,10 @@ export class ChatSwarmRuntimeManager { } if (operation.status === "carrier_created") { - if (!this.registry.claimBootstrap(operation.operationId)) { + if (!this.registry.claimBootstrap( + operation.operationId, + this.runtimeConfig.operationTimeoutMs + this.runtimeConfig.bootstrapWaitMs, + )) { await this.waitForPeerInvocation(slot).catch(() => undefined); continue; } @@ -2403,7 +2417,9 @@ export class ChatSwarmRuntimeManager { "runtime provision operation not found", ); } - if (Date.parse(operation.request.expiresAt) <= Date.now()) { + const authorityExpiresAt = + operation.receipt?.bootstrapExpiresAt ?? operation.request.expiresAt; + if (Date.parse(authorityExpiresAt) <= Date.now()) { throw new ChatSwarmError( "REQUEST_EXPIRED", "runtime provision operation expired",