Quellcode durchsuchen

fix(scheduler): unref idle reconciliation timer to avoid holding event loop

Michael Henke vor 1 Monat
Ursprung
Commit
93f111b2cb

+ 2 - 70
src/hooks/task-session-manager/index.test.ts

@@ -1276,7 +1276,7 @@ describe('task-session-manager hook', () => {
       },
     });
 
-    // Fire session.deleted event — should cancel reconciliation
+    // session.deleted cancels the timer and clears the board for this parent
     await hook.event({
       event: {
         type: 'session.deleted',
@@ -1286,75 +1286,7 @@ describe('task-session-manager hook', () => {
 
     await flushIdleReconcileDelay();
 
-    // Job should NOT be reconciled (timer was cancelled)
-    expect(board.get('child-1')).toMatchObject({
-      state: 'completed',
-      terminalUnreconciled: true,
-    });
-  });
-
-  test('session.error cancels pending idle reconciliation', async () => {
-    const board = new BackgroundJobBoard();
-    const { hook } = createHook({ backgroundJobBoard: board });
-
-    setupCompletedJob(board);
-
-    const messages = createMessages('parent-1', 'continue');
-    await hook['experimental.chat.messages.transform']({}, messages);
-
-    // Fire idle event (starts timer)
-    await hook.event({
-      event: {
-        type: 'session.status',
-        properties: { sessionID: 'parent-1', status: { type: 'idle' } },
-      },
-    });
-
-    // Fire session.error event — should cancel reconciliation
-    await hook.event({
-      event: {
-        type: 'session.error',
-        properties: { sessionID: 'parent-1', error: { message: 'test error', type: 'generic' } },
-      },
-    });
-
-    await flushIdleReconcileDelay();
-
-    // Job should NOT be reconciled (timer was cancelled)
-    expect(board.get('child-1')).toMatchObject({
-      state: 'completed',
-      terminalUnreconciled: true,
-    });
-  });
-
-  test('deleted session cancels pending idle reconciliation', async () => {
-    const board = new BackgroundJobBoard();
-    const { hook } = createHook({ backgroundJobBoard: board });
-
-    setupCompletedJob(board);
-
-    const messages = createMessages('parent-1', 'continue');
-    await hook['experimental.chat.messages.transform']({}, messages);
-
-    // Fire idle event (starts timer)
-    await hook.event({
-      event: {
-        type: 'session.status',
-        properties: { sessionID: 'parent-1', status: { type: 'idle' } },
-      },
-    });
-
-    // session.deleted before timer fires — should cancel reconciliation
-    await hook.event({
-      event: {
-        type: 'session.deleted',
-        properties: { sessionID: 'parent-1' },
-      },
-    });
-
-    await flushIdleReconcileDelay();
-
-    // Job should be gone (session.deleted clears the board for this parent)
+    // Job was removed by session.deleted (clearParent), not by reconciliation
     expect(board.get('child-1')).toBeUndefined();
   });
 

+ 6 - 7
src/hooks/task-session-manager/index.ts

@@ -606,13 +606,12 @@ export function createTaskSessionManagerHook(
         });
         if (sessionId && options.shouldManageSession(sessionId)) {
           cancelPendingReconciliation(sessionId);
-          pendingReconciliations.set(
-            sessionId,
-            setTimeout(() => {
-              pendingReconciliations.delete(sessionId);
-              reconcileInjectedTerminalJobs(sessionId);
-            }, IDLE_RECONCILE_DELAY_MS),
-          );
+          const timer = setTimeout(() => {
+            pendingReconciliations.delete(sessionId);
+            reconcileInjectedTerminalJobs(sessionId);
+          }, IDLE_RECONCILE_DELAY_MS);
+          timer.unref?.();
+          pendingReconciliations.set(sessionId, timer);
         }
         return;
       }