fix: patch 3 production bugs found during deep audit

1. Post-phase verify timer leak — setTimeout for verifyCurrentPhase was
   never stored, so pause() couldn't cancel it. Timer now tracked in
   postPhaseTimer field and cleared in clearPhasePoll().

2. Event forwarding flag survives loop replacement — boolean
   eventForwardingAttached stayed true when a new loop was created,
   so the new loop never got SSE forwarding. Now tracks the loop
   instance reference instead of a boolean.

3. Replan stuck when no sessions — replanPhase() returned without
   setting up task handlers or polling when no idle sessions were
   available. Now starts polling so the queued task gets picked up
   when a session becomes idle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-03-21 19:23:37 +01:00
co-authored by Claude Opus 4.6
parent 9449a8f157
commit afea91b92b
3 changed files with 45 additions and 6 deletions
+26
View File
@@ -547,6 +547,32 @@ describe('OrchestratorLoop', () => {
// After pause, listener should be removed
expect(mockSessionManager.listenerCount('sessionCompletion')).toBeLessThan(listenerCount);
});
it('cancels pending verify timer on pause', async () => {
const plan = createTestPlan();
plan.phases = [plan.phases[0]]; // No verification criteria
mockPlannerInstance.generatePlan.mockResolvedValue(plan);
// Tasks complete immediately — triggers post-phase delay timer
mockTaskQueue.addTask.mockImplementation((options) => {
const task = createMockTask(options);
task.complete();
mockTaskQueue._tasks.set(task.id, task);
return task;
});
await loop.start('Goal');
await loop.approve();
// Wait for poll to detect completion (2s) but pause before verify runs (1s delay)
await new Promise((resolve) => setTimeout(resolve, 2500));
loop.pause();
expect(loop.state).toBe('paused');
// Wait past where verify would have fired — state should still be paused
await new Promise((resolve) => setTimeout(resolve, 2000));
expect(loop.state).toBe('paused');
});
});
describe('resume()', () => {