fix(FN-7224): notify workflow graph task flows
Fusion-Task-Id: FN-7224
This commit is contained in:
7
.changeset/fn-7224-workflow-notifications.md
Normal file
7
.changeset/fn-7224-workflow-notifications.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Send ntfy notifications from workflow graph lifecycle and notify-node flows.
|
||||
category: fix
|
||||
dev: Workflow column transitions now use moveTask events; workflow-notify is enabled in default ntfy events.
|
||||
@@ -314,6 +314,47 @@ describe("CE workflow-step executor integration", () => {
|
||||
expect(live.mergeDetails?.mergeConfirmed).toBe(true);
|
||||
});
|
||||
|
||||
it("uses moveTask for workflow graph column transitions so lifecycle notifications fire", async () => {
|
||||
const store = createMockStore();
|
||||
store.getTask.mockResolvedValue(baseStepTask({ column: "todo" }) as any);
|
||||
const { executor } = makeExecutor(store);
|
||||
const settings = await store.getSettings();
|
||||
const primitives = (executor as any).createAuthoritativeWorkflowPrimitives(settings);
|
||||
|
||||
const result = await primitives.transitionTask(
|
||||
{
|
||||
run: { runId: "run-1", taskId: "FN-CE-1", workflowId: "builtin:coding" },
|
||||
node: { node: { id: "schedule", kind: "prompt", column: "todo", config: {} }, context: {} },
|
||||
},
|
||||
baseStepTask({ column: "todo" }),
|
||||
{
|
||||
column: "in-progress",
|
||||
status: "queued",
|
||||
reason: "workflow-schedule",
|
||||
preserveProgress: true,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual({ outcome: "success", value: "workflow-schedule" });
|
||||
expect(store.moveTask).toHaveBeenCalledWith(
|
||||
"FN-CE-1",
|
||||
"in-progress",
|
||||
expect.objectContaining({
|
||||
moveSource: "engine",
|
||||
preserveProgress: true,
|
||||
workflowMoveSource: "workflow-graph",
|
||||
workflowMoveMetadata: expect.objectContaining({
|
||||
reason: "workflow-schedule",
|
||||
nodeId: "schedule",
|
||||
workflowId: "builtin:coding",
|
||||
runId: "run-1",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-CE-1", { status: "queued" });
|
||||
expect(store.updateTask).not.toHaveBeenCalledWith("FN-CE-1", expect.objectContaining({ column: "in-progress" }));
|
||||
});
|
||||
|
||||
it("treats terminal graph step projection as success when the legacy pass rejects", async () => {
|
||||
const store = createMockStore();
|
||||
store.getTask.mockResolvedValue(baseStepTask({
|
||||
|
||||
@@ -26,6 +26,7 @@ describe("Ntfy notifier helpers", () => {
|
||||
expect(DEFAULT_NTFY_EVENTS).toContain("message:agent-to-user");
|
||||
expect(DEFAULT_NTFY_EVENTS).toContain("message:agent-to-agent");
|
||||
expect(DEFAULT_NTFY_EVENTS).toContain("message:room");
|
||||
expect(DEFAULT_NTFY_EVENTS).toContain("workflow-notify");
|
||||
});
|
||||
|
||||
it("checks awaiting-input event enablement", () => {
|
||||
|
||||
@@ -5727,11 +5727,36 @@ export class TaskExecutor {
|
||||
return { outcome: "success", value: "steps-updated", data: { count: steps.length } };
|
||||
},
|
||||
transitionTask: async (_ctx, task, input) => {
|
||||
const taskStore = this.store;
|
||||
const patch: Partial<TaskDetail> = {};
|
||||
if (input.column !== undefined) patch.column = input.column;
|
||||
/*
|
||||
FNXC:WorkflowNotifications 2026-06-29-08:50:
|
||||
Workflow graph lifecycle transitions must use TaskStore move semantics, not raw `updateTask({ column })`, because ntfy/webhook notification delivery is subscribed to `task:moved`. Direct column writes make graph-owned tasks invisible to in-review/done lifecycle notifications and bypass column hooks.
|
||||
*/
|
||||
if (input.column !== undefined) {
|
||||
const moveOptions = {
|
||||
preserveProgress: input.preserveProgress,
|
||||
moveSource: "engine" as const,
|
||||
workflowMoveSource: "workflow-graph",
|
||||
workflowMoveMetadata: {
|
||||
reason: input.reason,
|
||||
nodeId: _ctx.node.node.id,
|
||||
workflowId: _ctx.run.workflowId,
|
||||
runId: _ctx.run.runId,
|
||||
},
|
||||
};
|
||||
const storeWithMove = taskStore as typeof taskStore & {
|
||||
moveTask?: typeof taskStore.moveTask;
|
||||
};
|
||||
if (typeof storeWithMove.moveTask === "function") {
|
||||
await storeWithMove.moveTask(task.id, input.column, moveOptions);
|
||||
} else {
|
||||
patch.column = input.column;
|
||||
}
|
||||
}
|
||||
if (input.status !== undefined && input.status !== null) patch.status = input.status;
|
||||
if (Object.keys(patch).length > 0) {
|
||||
await this.store.updateTask(task.id, patch);
|
||||
await taskStore.updateTask(task.id, patch);
|
||||
}
|
||||
return { outcome: "success", value: input.reason };
|
||||
},
|
||||
|
||||
@@ -36,6 +36,7 @@ export const DEFAULT_NTFY_EVENTS: readonly NtfyNotificationEvent[] = [
|
||||
"db-corruption-detected",
|
||||
"fallback-used",
|
||||
"token-budget",
|
||||
"workflow-notify",
|
||||
"message:agent-to-user",
|
||||
"message:agent-to-agent",
|
||||
"message:room",
|
||||
|
||||
Reference in New Issue
Block a user