feat: Add retry mechanism for blocked tasks
Blocked tasks (from spawn failures) were a dead-end with no way to recover. Add retryBlockedTask to DispatchManager that resets status to pending and re-queues, a tRPC mutation that also kicks dispatchNext, and a Retry button in the task slide-over when status is blocked.
This commit is contained in:
@@ -237,6 +237,27 @@ export class DefaultDispatchManager implements DispatchManager {
|
||||
this.eventBus.emit(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry a blocked task.
|
||||
* Resets status to pending, clears block state, and re-queues for dispatch.
|
||||
*/
|
||||
async retryBlockedTask(taskId: string): Promise<void> {
|
||||
const task = await this.taskRepository.findById(taskId);
|
||||
if (!task) throw new Error(`Task not found: ${taskId}`);
|
||||
if (task.status !== 'blocked') throw new Error(`Task ${taskId} is not blocked (status: ${task.status})`);
|
||||
|
||||
// Clear blocked state
|
||||
this.blockedTasks.delete(taskId);
|
||||
|
||||
// Reset DB status to pending
|
||||
await this.taskRepository.update(taskId, { status: 'pending' });
|
||||
|
||||
log.info({ taskId }, 'retrying blocked task');
|
||||
|
||||
// Re-queue for dispatch
|
||||
await this.queue(taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch next available task to an agent.
|
||||
*/
|
||||
|
||||
@@ -50,6 +50,7 @@ function createMockDispatchManager(): DispatchManager {
|
||||
dispatchNext: vi.fn().mockResolvedValue({ success: false, reason: 'mock' }),
|
||||
completeTask: vi.fn(),
|
||||
blockTask: vi.fn(),
|
||||
retryBlockedTask: vi.fn(),
|
||||
getQueueState: vi.fn().mockResolvedValue({ queued: [], ready: [], blocked: [] }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,6 +102,14 @@ export interface DispatchManager {
|
||||
*/
|
||||
blockTask(taskId: string, reason: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Retry a blocked task.
|
||||
* Resets status to pending, removes from blocked map, and re-queues for dispatch.
|
||||
*
|
||||
* @param taskId - ID of the blocked task to retry
|
||||
*/
|
||||
retryBlockedTask(taskId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get current queue state.
|
||||
* Returns all queued tasks with their dispatch readiness.
|
||||
|
||||
@@ -35,5 +35,15 @@ export function dispatchProcedures(publicProcedure: ProcedureBuilder) {
|
||||
await dispatchManager.completeTask(input.taskId);
|
||||
return { success: true };
|
||||
}),
|
||||
|
||||
retryBlockedTask: publicProcedure
|
||||
.input(z.object({ taskId: z.string().min(1) }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const dispatchManager = requireDispatchManager(ctx);
|
||||
await dispatchManager.retryBlockedTask(input.taskId);
|
||||
// Kick dispatch loop to pick up the re-queued task
|
||||
await dispatchManager.dispatchNext();
|
||||
return { success: true };
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user