From c8e24eb72011f2f4ff62b912432f4e8c45f4f880 Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Sat, 21 Feb 2026 21:48:33 +0100 Subject: [PATCH] fix: sync kanban card stage with live agent phase (fixes #1885) The emitPhaseFromState method in TaskStateManager was using Date.now() as the sequenceNumber for TASK_EXECUTION_PROGRESS events. Since agent processes use small sequential integers (1, 2, 3...) for their sequence numbers, the timestamp-based value (e.g., 1708512345678) was always much larger. This caused the out-of-order drop check in updateExecutionProgress to permanently block all subsequent agent execution-progress events once XState emitted any phase update. The result: after XState fired the initial "planning" phase via emitPhaseFromState, all agent events reporting the "coding" (or later) phase would be silently dropped because their sequential numbers were less than the stored timestamp. The kanban card would remain frozen on the "planning" badge while the task detail view (which reads from task logs) correctly showed the "coding" stage. Fix: use sequenceNumber=0 in emitPhaseFromState. With value 0, the incoming sequence check (incomingSeq > 0 && currentSeq > 0) is not triggered, so the XState phase update always applies. Subsequent agent events with their small sequential numbers are then correctly accepted and can continue updating phaseProgress. Co-Authored-By: Claude Opus 4.6 --- apps/frontend/src/main/task-state-manager.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/main/task-state-manager.ts b/apps/frontend/src/main/task-state-manager.ts index fffb7bea..a879a13f 100644 --- a/apps/frontend/src/main/task-state-manager.ts +++ b/apps/frontend/src/main/task-state-manager.ts @@ -362,7 +362,12 @@ export class TaskStateManager { const phase = XSTATE_TO_PHASE[xstateState] || 'idle'; - // Emit execution progress with the phase derived from XState + // Emit execution progress with the phase derived from XState. + // IMPORTANT: Do NOT use Date.now() as sequenceNumber here — doing so would + // permanently block all subsequent agent execution-progress events, because + // the agent uses small sequential integers (1, 2, 3…) that are always less + // than a timestamp. Instead emit with sequenceNumber=0 so the agent's own + // events can continue updating phaseProgress once the phase is established. safeSendToRenderer( this.getMainWindow, IPC_CHANNELS.TASK_EXECUTION_PROGRESS, @@ -372,7 +377,7 @@ export class TaskStateManager { phaseProgress: phase === 'complete' ? 100 : 50, overallProgress: phase === 'complete' ? 100 : 50, message: `State: ${xstateState}`, - sequenceNumber: Date.now() // Use timestamp as sequence to ensure it's newer + sequenceNumber: 0 }, projectId );