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);
|
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 () => {
|
it("treats terminal graph step projection as success when the legacy pass rejects", async () => {
|
||||||
const store = createMockStore();
|
const store = createMockStore();
|
||||||
store.getTask.mockResolvedValue(baseStepTask({
|
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-user");
|
||||||
expect(DEFAULT_NTFY_EVENTS).toContain("message:agent-to-agent");
|
expect(DEFAULT_NTFY_EVENTS).toContain("message:agent-to-agent");
|
||||||
expect(DEFAULT_NTFY_EVENTS).toContain("message:room");
|
expect(DEFAULT_NTFY_EVENTS).toContain("message:room");
|
||||||
|
expect(DEFAULT_NTFY_EVENTS).toContain("workflow-notify");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks awaiting-input event enablement", () => {
|
it("checks awaiting-input event enablement", () => {
|
||||||
|
|||||||
@@ -5727,11 +5727,36 @@ export class TaskExecutor {
|
|||||||
return { outcome: "success", value: "steps-updated", data: { count: steps.length } };
|
return { outcome: "success", value: "steps-updated", data: { count: steps.length } };
|
||||||
},
|
},
|
||||||
transitionTask: async (_ctx, task, input) => {
|
transitionTask: async (_ctx, task, input) => {
|
||||||
|
const taskStore = this.store;
|
||||||
const patch: Partial<TaskDetail> = {};
|
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 (input.status !== undefined && input.status !== null) patch.status = input.status;
|
||||||
if (Object.keys(patch).length > 0) {
|
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 };
|
return { outcome: "success", value: input.reason };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export const DEFAULT_NTFY_EVENTS: readonly NtfyNotificationEvent[] = [
|
|||||||
"db-corruption-detected",
|
"db-corruption-detected",
|
||||||
"fallback-used",
|
"fallback-used",
|
||||||
"token-budget",
|
"token-budget",
|
||||||
|
"workflow-notify",
|
||||||
"message:agent-to-user",
|
"message:agent-to-user",
|
||||||
"message:agent-to-agent",
|
"message:agent-to-agent",
|
||||||
"message:room",
|
"message:room",
|
||||||
|
|||||||
Reference in New Issue
Block a user