fix(FN-6035): address workflow projection review feedback
This commit is contained in:
@@ -297,6 +297,43 @@ describe("WorkflowGraphExecutor traversal", () => {
|
||||
expect(result.context["node:a:projectionError"]).toBe("store unavailable");
|
||||
});
|
||||
|
||||
it("does not fail the node when the deprecated touched-files hook fails", async () => {
|
||||
const ir: WorkflowIr = {
|
||||
version: "v1",
|
||||
name: "legacy-touched-files-failure",
|
||||
nodes: [
|
||||
{ id: "start", kind: "start" },
|
||||
{ id: "a", kind: "prompt" },
|
||||
{ id: "end", kind: "end" },
|
||||
],
|
||||
edges: [
|
||||
{ from: "start", to: "a" },
|
||||
{ from: "a", to: "end", condition: "success" },
|
||||
],
|
||||
};
|
||||
const publishTaskProjection = vi.fn();
|
||||
const publishTouchedFiles = vi.fn(async () => {
|
||||
throw new Error("legacy sink unavailable");
|
||||
});
|
||||
const executor = new WorkflowGraphExecutor({
|
||||
handlers: {
|
||||
prompt: async () => ({
|
||||
outcome: "success",
|
||||
contextPatch: { modifiedFiles: ["src/projected.ts"] },
|
||||
}),
|
||||
},
|
||||
publishTaskProjection,
|
||||
publishTouchedFiles,
|
||||
});
|
||||
|
||||
const result = await executor.run(task, settingsOn(), ir);
|
||||
|
||||
expect(publishTaskProjection).toHaveBeenCalledTimes(1);
|
||||
expect(publishTouchedFiles).toHaveBeenCalledTimes(1);
|
||||
expect(result.outcome).toBe("success");
|
||||
expect(result.context["node:a:projectionError"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("caps retries and converts exceptions to failure", async () => {
|
||||
const ir: WorkflowIr = {
|
||||
version: "v1",
|
||||
|
||||
@@ -3752,19 +3752,18 @@ export class TaskExecutor {
|
||||
runCustomNode: (node, nodeTask) =>
|
||||
this.runGraphCustomNode(node, nodeTask, settings, resolveBindingForNode(node.id)),
|
||||
publishTaskProjection: async (taskId, patch) => {
|
||||
const liveTask = await this.store.getTask(taskId);
|
||||
const update: Parameters<TaskStore["updateTask"]>[1] = {};
|
||||
if (patch.modifiedFiles) {
|
||||
const merged = [...new Set([...(liveTask?.modifiedFiles ?? []), ...patch.modifiedFiles])].sort();
|
||||
if (merged.length > 0) update.modifiedFiles = merged;
|
||||
}
|
||||
if (patch.mergeDetails) {
|
||||
update.mergeDetails = { ...(liveTask?.mergeDetails ?? {}), ...patch.mergeDetails };
|
||||
}
|
||||
if (patch.summary !== undefined) update.summary = patch.summary;
|
||||
if (Object.keys(update).length > 0) {
|
||||
await this.store.updateTask(taskId, update);
|
||||
}
|
||||
await this.store.updateTaskAtomic(taskId, (liveTask) => {
|
||||
const update: Parameters<TaskStore["updateTask"]>[1] = {};
|
||||
if (patch.modifiedFiles) {
|
||||
const merged = [...new Set([...(liveTask.modifiedFiles ?? []), ...patch.modifiedFiles])].sort();
|
||||
if (merged.length > 0) update.modifiedFiles = merged;
|
||||
}
|
||||
if (patch.mergeDetails) {
|
||||
update.mergeDetails = { ...(liveTask.mergeDetails ?? {}), ...patch.mergeDetails };
|
||||
}
|
||||
if (patch.summary !== undefined) update.summary = patch.summary;
|
||||
return update;
|
||||
});
|
||||
},
|
||||
onEvent: (event) => executorLog.log(`[workflow-graph] ${event.type} ${event.taskId}: ${event.detail}`),
|
||||
// Wire SQLite-backed per-branch persistence in production (#1407): the
|
||||
|
||||
@@ -717,9 +717,6 @@ export class WorkflowGraphExecutor {
|
||||
const source = { nodeId: node.id, nodeKind: node.kind };
|
||||
try {
|
||||
await this.deps.publishTaskProjection?.(taskId, patch, source);
|
||||
if (patch.modifiedFiles && patch.modifiedFiles.length > 0) {
|
||||
await this.deps.publishTouchedFiles?.(taskId, patch.modifiedFiles, source);
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
outcome: "failure",
|
||||
@@ -730,6 +727,13 @@ export class WorkflowGraphExecutor {
|
||||
},
|
||||
};
|
||||
}
|
||||
if (patch.modifiedFiles && patch.modifiedFiles.length > 0) {
|
||||
try {
|
||||
await this.deps.publishTouchedFiles?.(taskId, patch.modifiedFiles, source);
|
||||
} catch {
|
||||
// Deprecated compatibility hook; primary projection persistence owns node outcome.
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user