FN-6031: add workflow notification nodes

Add workflow notify nodes with templated notification dispatch support.

- add notify node support to workflow IR, engine handlers, and executor wiring
- expose notify node configuration and summaries in the dashboard editor and node metadata
- add regression tests and a published changeset, plus workflow/settings documentation updates

Files changed:
 .changeset/fn-6031-notification-node.md            |   5 +
 docs/settings-reference.md                         |   6 +-
 docs/workflow-steps.md                             |  10 +-
 packages/core/src/__tests__/workflow-ir.test.ts    |  59 ++++++++++
 packages/core/src/types.ts                         |   4 +-
 packages/core/src/workflow-ir-types.ts             |   4 +-
 packages/core/src/workflow-ir.ts                   |  19 +++
 packages/dashboard/app/components/WorkflowNodeEditor.tsx          |  76 +++++++++++-
 packages/dashboard/app/components/__tests__/WorkflowNodeEditor.test.tsx          |  61 ++++++++++
 packages/dashboard/app/components/__tests__/node-summary.test.ts  |  29 +++++
 packages/dashboard/app/components/__tests__/workflow-flow-mapping.test.ts        |  37 ++++++
 packages/dashboard/app/components/nodes/WorkflowNodeTypes.tsx     |   7 +-
 packages/dashboard/app/components/nodes/node-summary.ts |   5 +
 packages/engine/src/__tests__/workflow-node-handlers-notify.test.ts          | 131 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |   2 +
 packages/engine/src/notification/ntfy-provider.ts  |  11 ++
 packages/engine/src/notification/webhook-provider.ts    |   6 +-
 packages/engine/src/workflow-graph-executor.ts     |   4 +
 packages/engine/src/workflow-graph-task-runner.ts  |   4 +
 packages/engine/src/workflow-node-handlers.ts      |  93 ++++++++++++++-
 20 files changed, 560 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-6031

Fusion-Task-Lineage: ff5c91e2-3872-4264-9c18-5d9e11628f13
This commit is contained in:
gsxdsm
2026-06-09 01:57:16 -07:00
parent 59c613cf38
commit 13c6d96fe1
20 changed files with 560 additions and 13 deletions

View File

@@ -91,7 +91,7 @@ A v2 column can optionally name a **permanent agent** from the agent registry, s
**Write-time validation.** Saving a workflow validates agent references: an unknown `agentId` is rejected with a typed 4xx naming the column. Binding an agent whose permission policy is broader than the project default requires an explicit policy-escalation confirmation (`confirmPolicyEscalation`) at save time, so override cannot silently re-key action gates to a more-privileged agent.
### Workflow IR v2 — step inversion (foreach, loop, step-review, parse-steps, code)
### Workflow IR v2 — step inversion (foreach, loop, step-review, parse-steps, code, notify)
The **step-inversion** track makes task *steps* themselves workflow-modelable. Today the engine owns step policy end-to-end (PROMPT.md parsing, per-step review verdicts, RETHINK/REVISE control flow, merge blocking). Step inversion extracts exactly one new substrate capability — *run one step inside a task's session, and reset one step to its baseline* — and exposes everything else as authored graph structure. It is additive to IR v2 and gated by `experimentalFeatures.workflowGraphExecutor`. The default coding workflow is untouched and byte-identical (it keeps its monolithic `execute` seam and is the parity oracle); inversion is opt-in via custom workflows and a new built-in **stepwise coding workflow**.
@@ -155,6 +155,12 @@ Parallelism is opt-in *per step by the planner*, not asserted by the workflow au
`code` (`{ source, timeoutMs? }`, default 30s, cap 300s) runs inline TypeScript (compiled with esbuild, executed in a timeout-bounded child process with cwd = the task worktree) for logic no built-in node covers. The script default-exports `async (ctx) => result` where `ctx = { task, steps, customFields, context, artifacts: { read(key) }, instance? }` (`instance` present inside a foreach template). The returned `{ outcome?, value?, contextPatch?, customFields? }` routes `outcome:<value>` edges, merges `contextPatch` into walk context, and writes `customFields` through the validated field authority. It gets **no store handle**, cannot write the step list, and a throw/timeout/non-zero exit becomes an audited `failure`. Source compile errors are rejected at save time (a dashboard 400 listing the failing node ids). It runs at the same trust tier as existing project-local script steps.
#### `notify` node — workflow-authored notifications
`notify` (`{ event, title?, message? }`) dispatches a notification through Fusion's active notification service and then always continues on the normal success path. `event` may be one of the standard notification events (for example `in-review`, `merged`, or `failed`), the built-in workflow-authored event `workflow-notify`, or a provider-specific custom event string. `title` and `message` are optional templates; the engine interpolates `{{taskTitle}}`, `{{taskId}}`, `{{workflowName}}`, and `{{context:key}}` from the workflow walk context.
Notification delivery is intentionally best-effort: a missing/unconfigured notification service, an empty `event`, or a provider delivery failure is logged/audited but does not fail the workflow node. Providers receive the rendered title/message in notification metadata so ntfy and webhook notifications can show workflow-specific copy. `workflow-notify` is **not** part of the default ntfy event allowlist; add it to `ntfyEvents` or the provider `events` filter when you want workflow-authored notifications delivered.
#### Workflow-defined custom task fields
Workflows declare typed task fields via IR `fields: [{ id, name, type, required?, default?, options?, render? }]` (`type ∈ string | text | number | boolean | enum | multi-enum | date | url`; `options` for enum kinds; `render.placement ∈ card | detail | detail-section`, `render.widget`, `render.badge`). Values live in `tasks.customFields` and are validated through a single store authority (`updateTaskCustomFields`) with typed rejections (offending `fieldId` + `code`). Editing or switching a workflow **orphans** (never destroys) values for removed/incompatible fields — orphans are retained and shown under a detail disclosure. The task UI renders the schema dynamically (detail-form widgets by type, up to 3 card badges by placement). Agents read/write fields via `fn_task_update`'s `custom_fields` patch; authors set them via `fn_workflow_create/update`. Field values are surfaced in task/session context.
@@ -489,7 +495,7 @@ Workflow graph execution is the task lifecycle runtime. `TaskExecutor` pins `wor
Default node dispatch:
- `prompt` / `script` nodes with `config.seam` dispatch through workflow runtime primitives (`planning`, `execute`, `workflow-step`, `review`, `merge`, `schedule`, `step-execute`)
- `step-review`, `parse-steps`, `code`, and PR nodes use their dedicated primitive/dependency adapters
- `step-review`, `parse-steps`, `code`, `notify`, and PR nodes use their dedicated primitive/dependency adapters
- `gate` nodes evaluate context-key expectations or run configured executable checks
Traversal semantics: