feat: migrate task jobs to workflow notifications + timeline

This commit is contained in:
2026-03-02 14:29:31 -05:00
parent 36c4ed2ee2
commit d81a681905
33 changed files with 2437 additions and 292 deletions

View File

@@ -1,41 +1,48 @@
import { sleep } from 'workflow';
import { start } from 'workflow/api';
import { runTaskProcessor } from '@/lib/server/task-processors';
import {
claimQueuedTask,
completeTask,
markTaskFailure
getTaskById,
markTaskFailure,
markTaskRunning
} from '@/lib/server/repos/tasks';
import type { Task } from '@/lib/types';
export async function runTaskWorkflow(taskId: string) {
'use workflow';
const task = await claimQueuedTaskStep(taskId);
const task = await loadTaskStep(taskId);
if (!task) {
return;
}
await markTaskRunningStep(task.id);
try {
const result = await processTaskStep(task);
const refreshedTask = await loadTaskStep(task.id);
if (!refreshedTask) {
return;
}
const result = await processTaskStep(refreshedTask);
await completeTaskStep(task.id, result);
} catch (error) {
const reason = error instanceof Error
? error.message
: 'Task failed unexpectedly';
const nextState = await markTaskFailureStep(task.id, reason);
if (nextState.shouldRetry) {
await sleep('1200ms');
await restartTaskWorkflowStep(task.id);
}
await markTaskFailureStep(task.id, reason);
throw error;
}
}
async function claimQueuedTaskStep(taskId: string) {
async function loadTaskStep(taskId: string) {
'use step';
return await claimQueuedTask(taskId);
return await getTaskById(taskId);
}
async function markTaskRunningStep(taskId: string) {
'use step';
await markTaskRunning(taskId);
}
async function processTaskStep(task: Task) {
@@ -43,7 +50,7 @@ async function processTaskStep(task: Task) {
return await runTaskProcessor(task);
}
// Step-level retries duplicate task-level retry handling and can create noisy AI failure loops.
// Keep retries at the projection workflow level to avoid duplicate side effects.
(
processTaskStep as ((task: Task) => Promise<Record<string, unknown>>) & { maxRetries?: number }
).maxRetries = 0;
@@ -55,10 +62,5 @@ async function completeTaskStep(taskId: string, result: Record<string, unknown>)
async function markTaskFailureStep(taskId: string, reason: string) {
'use step';
return await markTaskFailure(taskId, reason);
}
async function restartTaskWorkflowStep(taskId: string) {
'use step';
await start(runTaskWorkflow, [taskId]);
await markTaskFailure(taskId, reason);
}