diff --git a/apps/server/execution/orchestrator.ts b/apps/server/execution/orchestrator.ts index 83f9194..5d29142 100644 --- a/apps/server/execution/orchestrator.ts +++ b/apps/server/execution/orchestrator.ts @@ -631,6 +631,15 @@ export class ExecutionOrchestrator { } } } + + // Re-read tasks after recovery updates and check if phase is now fully done + const updatedTasks = await this.taskRepository.findByPhaseId(phase.id); + const allDone = updatedTasks.every((t) => t.status === 'completed'); + if (allDone && updatedTasks.length > 0) { + log.info({ phaseId: phase.id }, 'all tasks completed in in_progress phase, triggering phase completion'); + await this.handlePhaseAllTasksDone(phase.id); + phasesRecovered++; + } } } } diff --git a/docs/dispatch-events.md b/docs/dispatch-events.md index 0b3be50..5d1b4e9 100644 --- a/docs/dispatch-events.md +++ b/docs/dispatch-events.md @@ -136,7 +136,8 @@ When an agent crashes (`agent:crashed` event), the orchestrator automatically re On server restart, `recoverDispatchQueues()` also recovers: - Stuck `in_progress` tasks whose agents are dead (status is not `running` or `waiting_for_input`) — reset to `pending` and re-queued -- Erroneously `blocked` tasks whose agents completed successfully (status is `idle` or `completed`) — marked `completed` so the phase can progress. This handles the legacy case where conflict resolution incorrectly blocked already-completed tasks. +- Erroneously `blocked` tasks whose agents completed successfully (status is `idle` or `stopped`) — marked `completed` so the phase can progress. This handles the legacy case where conflict resolution incorrectly blocked already-completed tasks. +- Fully-completed `in_progress` phases — after task recovery, if all tasks in an `in_progress` phase are completed, triggers `handlePhaseAllTasksDone` to complete/review the phase Manual retry via `retryBlockedTask()` resets `retryCount` to 0, giving the task a fresh set of automatic retries.