From a9ce3d2b3314388fefdfce21a6abc262e2773723 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 3 Sep 2026 22:33:15 -0400 Subject: [PATCH] =?UTF-8?q?=E2=8F=B1=EF=B8=8F=20test:=20Size=20the=20Subag?= =?UTF-8?q?ent=20Lease=20TTL=20Above=20Shard=20Jitter=20(#15568)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The child-thread heartbeat fences itself when a renewal commits at or after the deadline it was issued against, so a 500ms lease made this test's window narrower than the scheduling stalls a loaded coverage shard produces: an ordinary pause read as a lapse, the post-preparation gate refused the run, and the assertion saw the provider never invoked while the lease row was still held. Give the lease two seconds so the heartbeat, not the runner, decides. Take the slow-preparation warning by its own delay as well, instead of assuming it is the last timer scheduled before the test resumes. --- packages/api/src/agents/subagentThreads.spec.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/api/src/agents/subagentThreads.spec.ts b/packages/api/src/agents/subagentThreads.spec.ts index 27fd1eb03a..cd78101cd3 100644 --- a/packages/api/src/agents/subagentThreads.spec.ts +++ b/packages/api/src/agents/subagentThreads.spec.ts @@ -1224,7 +1224,12 @@ describe('SubagentThreadTaskStore', () => { return methods.getMessages(...args); }), }; - const options = { leaseTtlMs: 500, leaseHeartbeatMs: 50 }; + /** The heartbeat fences itself when a renewal commits after the deadline it was + * issued against, so the TTL is this test's tolerance for runner jitter, not just + * its pace. Keep it well above the scheduling stalls a loaded shard produces: a + * half-second lease turns an ordinary pause into a lapse and the prepared run is + * refused instead of executed. */ + const options = { leaseTtlMs: 2_000, leaseHeartbeatMs: 50 }; const firstWorker = new SubagentThreadTaskStore(slowMethods, options); const secondWorker = new SubagentThreadTaskStore(methods, options); const config = buildSubagentThreadTaskConfig(firstWorker, { userId, parentConversationId }); @@ -1250,8 +1255,9 @@ describe('SubagentThreadTaskStore', () => { const heartbeatIndex = heartbeatCall == null ? -1 : intervalSpy.mock.calls.indexOf(heartbeatCall); const heartbeat = intervalSpy.mock.results[heartbeatIndex]?.value as NodeJS.Timeout | undefined; - const warningIndex = timeoutSpy.mock.calls.length - 1; - expect(timeoutSpy.mock.calls[warningIndex]?.[1]).toBe(5_000); + const warningCall = [...timeoutSpy.mock.calls].reverse().find(([, delay]) => delay === 5_000); + const warningIndex = warningCall == null ? -1 : timeoutSpy.mock.calls.lastIndexOf(warningCall); + expect(warningCall?.[1]).toBe(5_000); const warning = timeoutSpy.mock.results[warningIndex]?.value as NodeJS.Timeout | undefined; expect(heartbeat?.hasRef()).toBe(true); expect(warning?.hasRef()).toBe(true);