fix(FN-000): keep workflow processor cleanup best effort
Address PR #1581 feedback by ensuring cleanup transition failures do not hide claimed work identity after runtime dispatch errors.
This commit is contained in:
@@ -270,4 +270,42 @@ describe("workflow work processor", () => {
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns claimed identity when runtime and cleanup transition both fail", async () => {
|
||||
const task = await store.createTask({ description: "processor double failure task" });
|
||||
await store.moveTask(task.id, "todo");
|
||||
await store.moveTask(task.id, "in-progress");
|
||||
await store.handoffToReview(task.id, {
|
||||
ownerAgentId: "agent-test",
|
||||
evidence: { reason: "fn_task_done", runId: "run-processor-double-failure", agentId: "agent-test" },
|
||||
now: "2026-06-09T00:00:00.000Z",
|
||||
});
|
||||
const runtime = new WorkflowTaskRuntime({
|
||||
store,
|
||||
primitives: primitives(),
|
||||
runCustomNode: async () => ({ outcome: "success" }),
|
||||
});
|
||||
vi.spyOn(runtime, "runWorkItem").mockRejectedValue(new Error("sqlite busy"));
|
||||
vi.spyOn(store, "transitionWorkflowWorkItem").mockImplementation(() => {
|
||||
throw new Error("cleanup busy");
|
||||
});
|
||||
|
||||
const result = await processDueWorkflowWorkItem(store, runtime, { experimentalFeatures: {} } as any, {
|
||||
now: "2026-06-09T00:00:00.000Z",
|
||||
leaseOwner: "processor-a",
|
||||
leaseDurationMs: 60_000,
|
||||
kinds: workflowMergeWorkKinds(),
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
claimed: true,
|
||||
taskId: task.id,
|
||||
runtime: {
|
||||
disposition: "failed",
|
||||
outcome: "failure",
|
||||
reason: "workflow-work-item-runtime-error:sqlite busy",
|
||||
},
|
||||
});
|
||||
expect(result.workItemId).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,12 +43,16 @@ export async function processDueWorkflowWorkItem(
|
||||
runtimeResult = await runtime.runWorkItem(dispatch.workItem, settings);
|
||||
} catch (err) {
|
||||
const reason = `workflow-work-item-runtime-error:${err instanceof Error ? err.message : String(err)}`;
|
||||
store.transitionWorkflowWorkItem?.(dispatch.workItem.id, "failed", {
|
||||
now: opts.now,
|
||||
leaseOwner: null,
|
||||
leaseExpiresAt: null,
|
||||
lastError: reason,
|
||||
});
|
||||
try {
|
||||
store.transitionWorkflowWorkItem?.(dispatch.workItem.id, "failed", {
|
||||
now: opts.now,
|
||||
leaseOwner: null,
|
||||
leaseExpiresAt: null,
|
||||
lastError: reason,
|
||||
});
|
||||
} catch {
|
||||
// Best-effort cleanup; callers still need the claimed work identity on double-failure.
|
||||
}
|
||||
runtimeResult = {
|
||||
disposition: "failed",
|
||||
outcome: "failure",
|
||||
|
||||
Reference in New Issue
Block a user