@w6w/ui 0.1.1 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +7 -5
- package/dist/AppPicker.d.ts +48 -2
- package/dist/CanvasLayer.d.ts +38 -0
- package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-BqXDA35P.js} +2 -2
- package/dist/{CodeBlock-Dot3Pd7P.js.map → CodeBlock-BqXDA35P.js.map} +1 -1
- package/dist/{CodeBlock-CZu4TFSi.cjs → CodeBlock-CulfbnZD.cjs} +2 -2
- package/dist/{CodeBlock-CZu4TFSi.cjs.map → CodeBlock-CulfbnZD.cjs.map} +1 -1
- package/dist/ConfirmModal-Ln99S9NT.js +7919 -0
- package/dist/ConfirmModal-Ln99S9NT.js.map +1 -0
- package/dist/ConfirmModal-fjHW5rN-.cjs +14 -0
- package/dist/ConfirmModal-fjHW5rN-.cjs.map +1 -0
- package/dist/ConnectionHeader.d.ts +19 -0
- package/dist/Copyable-DA6eiU7w.cjs +2 -0
- package/dist/Copyable-DA6eiU7w.cjs.map +1 -0
- package/dist/{Copyable-1niV9gUa.js → Copyable-F4NTaG77.js} +91 -15
- package/dist/Copyable-F4NTaG77.js.map +1 -0
- package/dist/EditConnectionModal.d.ts +26 -0
- package/dist/JsonEditor.d.ts +8 -0
- package/dist/Markdown.d.ts +25 -0
- package/dist/ParamsForm.d.ts +7 -1
- package/dist/StepBuilderModal.d.ts +23 -5
- package/dist/WorkflowFlowEditor.d.ts +14 -3
- package/dist/app-pages.d.ts +5 -0
- package/dist/canvas-types.d.ts +36 -0
- package/dist/canvas-utils.d.ts +44 -0
- package/dist/code.cjs +1 -1
- package/dist/code.mjs +2 -2
- package/dist/components/AuthFieldsForm.d.ts +19 -1
- package/dist/components/BudgetMeter.d.ts +15 -0
- package/dist/components/CallableList.d.ts +58 -0
- package/dist/components/Combobox.d.ts +8 -1
- package/dist/components/ConnectionList.d.ts +43 -0
- package/dist/components/CredentialPicker.d.ts +35 -0
- package/dist/components/DataTable.d.ts +27 -0
- package/dist/components/EmptyState.d.ts +9 -0
- package/dist/components/ListItem.d.ts +16 -6
- package/dist/components/ReliabilityChart.d.ts +10 -0
- package/dist/components/ReliabilityList.d.ts +47 -0
- package/dist/components/icons.d.ts +1 -1
- package/dist/connection-app-meta.d.ts +32 -0
- package/dist/dynamic-fields.d.ts +60 -0
- package/dist/editor-theme.d.ts +24 -0
- package/dist/flow-fanout.d.ts +2 -2
- package/dist/flow-types.d.ts +18 -1
- package/dist/flow-utils.d.ts +10 -3
- package/dist/flow.cjs +2 -2
- package/dist/flow.cjs.map +1 -1
- package/dist/flow.d.ts +9 -0
- package/dist/flow.mjs +1519 -1138
- package/dist/flow.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -2
- package/dist/index.mjs +2053 -1005
- package/dist/index.mjs.map +1 -1
- package/dist/note-markdown.d.ts +22 -0
- package/dist/provider.d.ts +73 -5
- package/dist/reliability-board-layout.d.ts +121 -0
- package/dist/resolve-params.d.ts +5 -0
- package/dist/run-visuals.d.ts +4 -1
- package/dist/step-problems.d.ts +34 -0
- package/dist/types.d.ts +117 -3
- package/dist/use-dynamic-fields.d.ts +23 -0
- package/package.json +5 -3
- package/src/styles/_budget-meter.scss +46 -0
- package/src/styles/_callable-list.scss +69 -0
- package/src/styles/_canvas.scss +318 -0
- package/src/styles/_connections.scss +88 -0
- package/src/styles/_data-table.scss +55 -0
- package/src/styles/_empty-state.scss +21 -0
- package/src/styles/_flow.scss +11 -0
- package/src/styles/_fx.scss +23 -0
- package/src/styles/_layout.scss +10 -0
- package/src/styles/_lists.scss +4 -0
- package/src/styles/_markdown.scss +20 -0
- package/src/styles/_reliability.scss +339 -0
- package/src/styles/_tester.scss +1 -14
- package/src/styles.css +1142 -17
- package/src/styles.scss +8 -0
- package/dist/ConfirmModal-8avl9Nv8.cjs +0 -11
- package/dist/ConfirmModal-8avl9Nv8.cjs.map +0 -1
- package/dist/ConfirmModal-CD-P8mhN.js +0 -6335
- package/dist/ConfirmModal-CD-P8mhN.js.map +0 -1
- package/dist/Copyable-1niV9gUa.js.map +0 -1
- package/dist/Copyable-CYZU1o4X.cjs +0 -2
- package/dist/Copyable-CYZU1o4X.cjs.map +0 -1
package/dist/flow.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow.cjs","sources":["../src/components/resolved-clip.ts","../src/components/ResolvedParams.tsx","../src/flow-connect.ts","../src/flow-fanout.ts","../src/run-visuals.ts","../src/use-app-action-defs.ts","../src/WorkflowFlowEditor.tsx"],"sourcesContent":["/**\n * Clipping for ONE resolved value's display text (Test tab, read-only rows).\n *\n * A resolved row is a SUMMARY of what the run will be sent, not a viewer for\n * it. A value that resolves from a document (`documents.*.body`) or a rendered\n * template is routinely tens of kilobytes of HTML, and pasting all of it inline\n * pushed the rest of the params — and the run's own result — off the screen\n * (human report, 2026-09-01). So a long value is clipped here and offered\n * behind a toggle; the complete value is always one click away in the `<>`\n * code view above, which is COMPLETE over every visible param by construction\n * (`resolved-json.ts`, D-2) and so must never be clipped itself.\n *\n * Plain `.ts`, no React — same pure-module-beside-the-component precedent\n * `history-scale.ts` sets for `HistoryTimeline.tsx`, so the rule is testable\n * without a JSX transpile.\n */\n\n/**\n * Characters shown before a value is clipped. Sized to a few lines at the\n * row's font — enough to recognize WHICH value resolved (the opening of an\n * HTML document, a subject line, a URL) without becoming the page.\n */\nexport const RESOLVED_VALUE_CLIP = 220;\n\n/** A clip decision: the text to render, and whether anything was withheld. */\nexport interface ClippedValue {\n /** What to show when collapsed — never longer than {@link RESOLVED_VALUE_CLIP}. */\n text: string;\n /** True when `text` is shorter than the input, i.e. a toggle is warranted. */\n clipped: boolean;\n /** The full value's length, for the toggle's own label. */\n length: number;\n}\n\n/**\n * Clip `text` for the collapsed row. At or under the budget the input is\n * returned byte-identically and `clipped` is false — a short value must never\n * grow a toggle, an ellipsis, or a trailing-whitespace trim it didn't ask for.\n *\n * Over the budget, the cut is a plain `slice` at the boundary with trailing\n * whitespace trimmed (so an ellipsis never floats a space away from the text).\n * No word-boundary search: these values are markup and JSON as often as prose,\n * where \"the last space\" is not a meaningful place to stop.\n */\nexport function clipResolvedValue(text: string): ClippedValue {\n if (text.length <= RESOLVED_VALUE_CLIP) {\n return { text, clipped: false, length: text.length };\n }\n return {\n text: text.slice(0, RESOLVED_VALUE_CLIP).trimEnd(),\n clipped: true,\n length: text.length,\n };\n}\n","import { useMemo, useState } from \"react\";\nimport { JsonEditor } from \"../JsonEditor.tsx\";\nimport { type DataVar, flattenParams, isParamVisible } from \"../ParamsForm.tsx\";\nimport type { StepStartState } from \"../provider.tsx\";\nimport {\n type ResolveScope,\n type ResolvedSegment,\n buildResolveScope,\n deepEqual,\n resolveParamValue,\n} from \"../resolve-params.ts\";\nimport { resolveParamJson, resolveVarsJson } from \"../resolved-json.ts\";\nimport type { ActionParam } from \"../types.ts\";\nimport { useExpressionOptions } from \"./ExpressionOptions.tsx\";\nimport { varLabel } from \"./expression-dom.ts\";\nimport { clipResolvedValue } from \"./resolved-clip.ts\";\n\nexport interface ResolvedParamsProps {\n /** The action's declared params — same list `ParamsForm` renders on Configure. */\n params: ActionParam[];\n /** What will actually be submitted: `{ ...step.with, ...override }` — `testValues`, not `step.with`. */\n values: Record<string, unknown>;\n /** The exact object the test invoke sends as `state` — `steps.*`/`trigger.*` resolve against this. */\n testStartState?: StepStartState;\n /**\n * `\"props\"` (default) renders the filtered row list; `\"code\"` renders the\n * COMPLETE resolved-values JSON (A4/A5/D-2) — every visible param, defaults\n * included. Driven by the host's own toggle state; this component never\n * mounts a toggle of its own (D-7 — `PropertyEntryForm` already owns one on\n * the trigger arm, and two side by side is a defect, not a feature).\n */\n view?: \"props\" | \"code\";\n}\n\n/**\n * Read-only sibling of `ParamsForm`: for every configured param, shows the\n * value it resolves to against the current test scope instead of an editable\n * widget. Mounts no `contentEditable`, no `ƒx` button, no input, and never\n * calls an `onChange` — this is the Test tab's \"what will actually be sent\"\n * view, not a second place to edit params.\n *\n * Reuses `flattenParams`/`isParamVisible` from `ParamsForm.tsx` (same order +\n * visibility the Configure tab uses) rather than re-flattening sections, and\n * the static chip vocabulary (`varLabel`, `.w6w-expr-chip-*`) rather than\n * `expression-dom.ts`'s DOM-painting, which is for the editable field.\n */\nexport function ResolvedParams({\n params,\n values,\n testStartState,\n view = \"props\",\n}: ResolvedParamsProps) {\n const { sampleValues } = useExpressionOptions();\n const scope = useMemo(\n () => buildResolveScope(sampleValues, testStartState),\n [sampleValues, testStartState],\n );\n const flat = flattenParams(params);\n const effective = (key: string) =>\n values[key] !== undefined ? values[key] : flat.find((p) => p.key === key)?.default;\n // Sections are layout-only containers with no value of their own (their\n // children, already flattened, carry the values) — skip rendering a row for\n // the section param itself, exactly as `ParamsForm`'s own `renderOne` does.\n // This is the \"visible\" row set D-2's code view is complete OVER — before\n // A1's unchanged-value filter, so the code view still carries a param the\n // list hides.\n const visibleRows = flat.filter((p) => p.type !== \"section\" && isParamVisible(p, effective));\n\n if (view === \"code\") {\n // Derived fresh on every render from `values` — no draft state — so\n // editing the incoming-state override above updates this text live,\n // exactly as it already updates the row list (A5).\n const json = buildResolvedJson(visibleRows, values, scope);\n return (\n <div className=\"w6w-resolved-params\">\n <JsonEditor\n value={JSON.stringify(json, null, 2)}\n onChange={() => {}}\n readOnly\n minHeight=\"260px\"\n height=\"100%\"\n aria-label=\"Resolved test values (JSON)\"\n />\n </div>\n );\n }\n\n // A1/D-1: drop a row whose effective value is deep-equal to its declared\n // default — noise the human named explicitly (an unset `Sender Name \"\"`, an\n // explicitly-false `required` boolean, an empty `{}`/array). Uniform: no\n // `required` exemption, and a key EXPLICITLY present in `with` that happens\n // to equal the default is hidden too (equality, not presence — D-1).\n const rows = visibleRows.filter((p) => !deepEqual(effective(p.key), p.default));\n\n if (visibleRows.length === 0) {\n return (\n <div className=\"w6w-resolved-params\">\n <p className=\"w6w-muted w6w-small\">This action takes no parameters.</p>\n </div>\n );\n }\n\n if (rows.length === 0) {\n return (\n <div className=\"w6w-resolved-params\">\n <p className=\"w6w-muted w6w-small\">\n Every configured value matches its default — nothing to show here. Switch to JSON to see\n the full set.\n </p>\n </div>\n );\n }\n\n return (\n // `.w6w-resolved-table` is the ONE grid this whole row list shares — each\n // `.w6w-resolved-row` below is `display: contents` and contributes its\n // two children straight into these column tracks, so every row's key\n // column lines up with every other row's.\n <div className=\"w6w-resolved-params w6w-resolved-table\">\n {rows.map((param) =>\n param.type === \"vars\" ? (\n <ResolvedVarsRow key={param.key} param={param} value={values[param.key]} scope={scope} />\n ) : (\n <ResolvedParamRow\n key={param.key}\n label={param.label ?? param.key}\n segments={resolveParamValue(\n values[param.key] !== undefined ? values[param.key] : param.default,\n scope,\n )}\n />\n ),\n )}\n </div>\n );\n}\n\n/**\n * D-2: the code view is COMPLETE over `rows` (every visible param — see the\n * caller — defaults included, the row set BEFORE A1's filter), one key per\n * `param.key`, each folded through {@link resolveParamJson}/{@link\n * resolveVarsJson} (never the schema, never raw `step.with`, never the\n * `{type:\"expr\",…}` envelope — A6's pinned serialization).\n */\nfunction buildResolvedJson(\n rows: ActionParam[],\n values: Record<string, unknown>,\n scope: ResolveScope,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const param of rows) {\n if (param.type === \"vars\") {\n const raw = Array.isArray(values[param.key])\n ? (values[param.key] as DataVar[])\n : Array.isArray(param.default)\n ? (param.default as DataVar[])\n : [];\n out[param.key] = resolveVarsJson(raw, scope);\n } else {\n out[param.key] = resolveParamJson(\n values[param.key] !== undefined ? values[param.key] : param.default,\n scope,\n );\n }\n }\n return out;\n}\n\nfunction ResolvedParamRow({ label, segments }: { label: string; segments: ResolvedSegment[] }) {\n return (\n <div className=\"w6w-resolved-row\">\n <span className=\"w6w-resolved-label\">{label}</span>\n <div className=\"w6w-resolved-value\">\n <ResolvedSegments segments={segments} />\n </div>\n </div>\n );\n}\n\n/** A `vars`-typed param: one row per configured key, each resolved independently (A5). */\nfunction ResolvedVarsRow({\n param,\n value,\n scope,\n}: {\n param: ActionParam;\n value: unknown;\n scope: ResolveScope;\n}) {\n const rows: DataVar[] = Array.isArray(value)\n ? (value as DataVar[])\n : Array.isArray(param.default)\n ? (param.default as DataVar[])\n : [];\n return (\n <div className=\"w6w-resolved-row\">\n <span className=\"w6w-resolved-label\">{param.label ?? param.key}</span>\n {rows.length === 0 ? (\n <p className=\"w6w-muted w6w-small\">None configured.</p>\n ) : (\n <div className=\"w6w-stack\">\n {rows.map((row, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: rows have no stable id (mirrors ParamsForm's VarsField)\n <div className=\"w6w-resolved-value\" key={i}>\n <code>{row.key || \"(no key)\"}</code>\n {\" → \"}\n <ResolvedSegments segments={resolveParamValue(row.value, scope)} />\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction ResolvedSegments({ segments }: { segments: ResolvedSegment[] }) {\n return (\n <>\n {segments.map((seg, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: segments are a positional, immutable render of one value's parts\n <ResolvedSegmentView key={i} segment={seg} />\n ))}\n </>\n );\n}\n\n/** One part's worth of chrome: a static chip (for anything ref-shaped) plus its outcome. */\nfunction ResolvedSegmentView({ segment }: { segment: ResolvedSegment }) {\n switch (segment.status) {\n case \"resolved\":\n return segment.ref ? (\n <>\n <ExprChip kind=\"var\" refName={segment.ref} />\n <ResolvedValueText value={segment.value} />\n </>\n ) : (\n <ResolvedValueText value={segment.value} />\n );\n case \"unresolved\":\n // Never the ref/template text as if it were the value: the chip shows\n // only the short display LABEL (never the full ref string — the same\n // \"label ≠ ref\" precedent `expression-dom.ts` documents), and the\n // outcome is a dedicated, always-non-blank badge.\n return (\n <>\n <ExprChip kind=\"var\" refName={segment.ref} />\n <span className=\"w6w-param-unresolved\">unresolved</span>\n </>\n );\n case \"not-evaluated\":\n return (\n <>\n <ExprChip kind=\"expr\" />\n <span className=\"w6w-param-unevaluated\">not evaluated</span>\n </>\n );\n case \"masked\":\n // The chip's 🔒 sigil already says \"masked\" — no separate badge needed,\n // and no ciphertext or plaintext is ever available to show here.\n return <ExprChip kind=\"secret\" refName={segment.ref} />;\n }\n}\n\n/**\n * One resolved value's text, clipped when it is long enough to take over the\n * row (`resolved-clip.ts`). A document body or a rendered template resolves to\n * tens of kilobytes of HTML, and a row that pastes all of it inline buries the\n * other params and the run's result below it (human report, 2026-09-01).\n *\n * Expanding does NOT restore the unbounded paste: the full text goes into its\n * own scrollable block (`.w6w-resolved-text-full`), so the row can never grow\n * past a few lines of the panel either way. A value inside the budget renders\n * exactly as it always did — one bare `<span>`, no toggle, no wrapper.\n */\nfunction ResolvedValueText({ value }: { value: unknown }) {\n const [expanded, setExpanded] = useState(false);\n const full = formatResolvedValue(value);\n const clip = clipResolvedValue(full);\n if (!clip.clipped) return <span>{full}</span>;\n return (\n <>\n <span className={expanded ? \"w6w-resolved-text-full\" : undefined}>\n {expanded ? full : `${clip.text}…`}\n </span>\n <button\n type=\"button\"\n className=\"w6w-resolved-more\"\n aria-expanded={expanded}\n onClick={() => setExpanded((e) => !e)}\n >\n {expanded ? \"Show less\" : `Show all (${clip.length.toLocaleString()} characters)`}\n </button>\n </>\n );\n}\n\nconst CHIP_SIGIL: Record<\"var\" | \"secret\" | \"expr\", string> = { var: \"◆\", secret: \"🔒\", expr: \"ƒ\" };\n\n/**\n * A read-only chip, visually consistent with `expression-dom.ts`'s\n * `makeChip` (same classes/sigils, same `varLabel` shortening) but built on a\n * plain `<span>` — no `contentEditable`, no remove button, no DOM painting.\n */\nfunction ExprChip({ kind, refName }: { kind: \"var\" | \"secret\" | \"expr\"; refName?: string }) {\n const label = kind === \"expr\" ? \"expr\" : varLabel(refName ?? \"\");\n return (\n <span className={`w6w-expr-chip w6w-expr-chip-${kind}`}>\n <span className=\"w6w-expr-chip-sigil\">{CHIP_SIGIL[kind]}</span>\n <span className=\"w6w-expr-chip-label\">{label}</span>\n </span>\n );\n}\n\n/**\n * A resolved value, formatted for display — never blank, even for the falsy\n * values `resolved` explicitly covers (`null`/`\"\"`/`false`/`0`): each renders\n * visibly distinct text so it never reads as \"nothing here\" the way an actual\n * blank would.\n */\nfunction formatResolvedValue(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"(not set)\";\n if (value === \"\") return '\"\"';\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n","/**\n * Connection rules for the flow canvas: which edges may be drawn, what the edge\n * set becomes once one is, and how edge ids are rewritten when a step is renamed.\n *\n * These are total, pure functions of `(source, target, nodes, edges)` and they\n * live in a **`.ts`** module on purpose. They used to sit inside\n * `WorkflowFlowEditor.tsx`, where Node's type-stripping test runner cannot reach\n * them (it cannot parse JSX), so the exit-port capacity rule — the highest-risk\n * decision in the editor's wiring — was covered only by inspection. Here it is\n * covered by `flow-connect.test.ts` (`node --test`, no new dependency). Keep this\n * file JSX-free.\n *\n * Internal to `@w6w/ui`: imported by relative path, deliberately NOT re-exported\n * from `index.ts` / `flow.ts`.\n */\nimport { type Edge, addEdge } from \"@xyflow/react\";\nimport {\n type PortsLookup,\n edgeVisuals,\n nodePortsForStep,\n sourceHandleForLane,\n} from \"./flow-types.ts\";\nimport { type StepNode, flowEdgeId } from \"./flow-utils.ts\";\n\n/**\n * Which lane a React Flow edge belongs to (core `rfcs/workflow.md` · `Edge.when`\n * amendment). **Omitted ⇒ `\"success\"`**, and read defensively: an edge that\n * predates the `data.when` stamp — or one React Flow synthesised — may carry no\n * `data` at all. The single accessor: every consumer calls this rather than\n * re-deriving the fallback, so \"absent means success\" is decided once.\n */\nexport function edgeLane(e: Edge): \"success\" | \"error\" {\n return (e.data as { when?: string } | undefined)?.when === \"error\" ? \"error\" : \"success\";\n}\n\n/**\n * A refused connection: the source's exit port is full **in this lane** and the cap\n * is finite. Drawing past a finite `out` cap is refused, never satisfied by evicting\n * an existing edge (an unbounded `\"many\"` out never refuses).\n */\nexport type OutCapRefusal = { refused: \"out_cap\"; cap: number };\n\n/**\n * The id of the edge in `survivors` that `id` would duplicate, or `null` when it is\n * free — **the one id-uniqueness test in this module**, shared by the creation path\n * ({@link planConnect}) and the re-lane path ({@link planRelane}).\n *\n * Both paths must answer the same question because `flowEdgeId` is not injective\n * over free-text step ids: a step literally named `b:error` makes `a → b` in the\n * error lane and `a → \"b:error\"` in the success lane mint ONE id. React Flow's store\n * is id-keyed, so a duplicate id is not an error — it is one of the two wires\n * silently vanishing from the canvas, which reads as the editor losing the author's\n * work. Two independent copies of this test is how one order (mark, then draw) got\n * through while the other (draw, then mark) was refused.\n *\n * Always called with the edges that **survive** eviction, never the whole set: a\n * collider the same call evicts is not a collision (see `flow-connect.test.ts`\n * case 11).\n */\nfunction collidingEdgeId(survivors: Edge[], id: string): string | null {\n return survivors.find((e) => e.id === id)?.id ?? null;\n}\n\n/**\n * The hard rules for an edge `source → target` — the ones no amount of\n * replacement can satisfy: a real, *distinct* pair (no self-loops), no duplicate\n * edge, the target accepts an entry port (blocks connecting *into* a trigger,\n * which declares `in: 0`), and the source has an exit port. Port **capacity** is\n * deliberately NOT checked here: a full single-slot port is freed by replacement\n * (see `applyConnect`), so dragging a new wire from an already-connected node\n * re-points it rather than being rejected. Used as the live `isValidConnection`.\n *\n * The duplicate-pair check is **lane-blind by design**, and that is a v1 *editor*\n * limit, not a model limit: the model permits a success edge and an error edge\n * between the same two steps (D-T1-8 widened the engine's `skippedEdges` key so\n * they cannot collide), but v1 declines to author that pair because there are no\n * id'd source handles to pick the lane from (D-T1-7).\n *\n * A colliding minted **id** is deliberately NOT rejected here, even though\n * {@link applyConnect} refuses it: this predicate is the live `isValidConnection`,\n * and a `false` here means React Flow never fires `onConnect`, so the editor never\n * gets to *say why*. That refusal is invisible (and, worse, lands in\n * `onConnectEnd`'s \"dropped on empty canvas\" branch, which opens the step builder).\n * Letting the drop through and refusing in `applyConnect` is what lets the reason\n * paint — see {@link connectConflict}.\n */\nexport function canConnect(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n lookup?: PortsLookup,\n): boolean {\n if (!source || !target || source === target) return false;\n if (edges.some((e) => e.source === source && e.target === target)) return false;\n const srcStep = nodes.find((n) => n.id === source)?.data.step;\n const tgtStep = nodes.find((n) => n.id === target)?.data.step;\n if (!srcStep || !tgtStep) return false;\n // Per-step ports (T2.3.1): a persisted `ports.in > 1` lets multiple edges land.\n const srcPorts = nodePortsForStep(srcStep, lookup);\n const tgtPorts = nodePortsForStep(tgtStep, lookup);\n return srcPorts.out >= 1 && tgtPorts.in >= 1;\n}\n\n/**\n * The plan for a new `source → target` connection: the resulting edge set, plus the\n * id of an existing edge the minted id would **collide** with (`null` when it is\n * safe). The creation-path twin of {@link planRelane}, deliberately the same shape —\n * both public entry points read off this one plan, so the eviction arithmetic and\n * the id-uniqueness test are computed once, in the right order (the collision is\n * tested against the edges that SURVIVE eviction).\n */\nfunction planConnect(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\",\n lookup?: PortsLookup,\n): { next: Edge[]; conflict: string | null; refusal: OutCapRefusal | null } | null {\n if (!canConnect(source, target, nodes, edges, lookup) || !source || !target) return null;\n const srcStep = nodes.find((n) => n.id === source)?.data.step;\n const tgtStep = nodes.find((n) => n.id === target)?.data.step;\n if (!srcStep || !tgtStep) return null;\n // Per-step ports (T2.3.1): capacity honors a persisted `ports.in`/`ports.out`,\n // so a fan-in node with `ports.in > 1` keeps prior edges instead of dropping them.\n const srcPorts = nodePortsForStep(srcStep, lookup);\n const tgtPorts = nodePortsForStep(tgtStep, lookup);\n let next = edges;\n // The source's exit port IN THIS LANE is full at a finite cap: REFUSE. Nothing is\n // evicted — `next` stays the input edge set. The other lane is untouched.\n const fromSrc = next.filter((e) => e.source === source && edgeLane(e) === when);\n if (fromSrc.length >= srcPorts.out) {\n return { next: edges, conflict: null, refusal: { refused: \"out_cap\", cap: srcPorts.out } };\n }\n // Free the target's entry port likewise — lane-blind, see the docstring.\n const toTgt = next.filter((e) => e.target === target);\n if (toTgt.length >= tgtPorts.in) {\n const drop = new Set(toTgt.slice(0, toTgt.length - tgtPorts.in + 1).map((e) => e.id));\n next = next.filter((e) => !drop.has(e.id));\n }\n // Stamp the lane at creation: a freshly drawn wire is a success edge unless the\n // caller says otherwise, and flowToWorkflow reads `data.when` back off the edge\n // (it would see `undefined` otherwise). \"success\" is emitted as ABSENT on the\n // way out. The id comes from `flowEdgeId`, so an error edge is qualified and\n // cannot collide with its success sibling. `sourceHandle` is stamped by\n // EXPLICIT assignment (T1.1.1) so the anchor follows the lane the same way\n // `id`/`data` do. `edgeVisuals(when)` (className/label) is spread here too —\n // 2026-08-30 fix: a freshly DRAWN error edge previously carried the right\n // `data.when`/`sourceHandle` (so it persisted and reloaded correctly) but\n // rendered with no red dash / \"on error\" label until the next page load,\n // because only `planRelane` (re-laning an EXISTING edge) applied it — the\n // create path never did. Same trap `planRelane`'s own comment warns about:\n // this must be an explicit spread here, not left for a later re-render.\n const id = flowEdgeId(source, target, when);\n return {\n next: addEdge(\n {\n source,\n target,\n id,\n sourceHandle: sourceHandleForLane(when),\n data: { when },\n // No run state: this mints a freshly DRAWN edge, never a run-taken\n // one — the live run-aware repaint is a separate effect\n // (WorkflowFlowEditor.tsx, T1.1.1), not this creation path.\n ...edgeVisuals(when, undefined),\n },\n next,\n ),\n conflict: collidingEdgeId(next, id),\n refusal: null,\n };\n}\n\n/**\n * Build the next edge set for a new `source → target` connection. **Additive at the\n * source**: a source under its `out` cap (or unbounded) keeps every existing edge and\n * gains one; at a finite cap the connection is **refused** (see {@link connectRefusal}),\n * never satisfied by evicting. The target's entry port is still single-slot-replacing:\n * the oldest inbound edge(s) are dropped to make room. Returns `null` when the\n * connection is disallowed by {@link canConnect}, refused at the out cap — **or when the minted id would\n * duplicate a surviving edge's id**, the same refusal {@link setEdgeWhen} performs\n * through the same {@link collidingEdgeId} test.\n *\n * That last one closes the reverse order of the `:error` collision: marking `a → b`\n * as the error lane is refused when `a → \"b:error\"` already exists, but *first*\n * marking `a → b` Error and *then* drawing `a → \"b:error\"` used to be allowed and\n * minted a duplicate id — React Flow's id-keyed store then held one wire for two\n * edges (measured `storeSize 1 / 2`). Refusing here is narrow on purpose; rejecting\n * the step id itself is the durable fix and would also have to reject ids inside\n * workflows that already exist (FOLLOWUPS.md). The caller must surface the refusal —\n * see {@link connectConflict}.\n *\n * The source's exit capacity is **per lane** (T3.2.2): success edges compete only\n * with success edges, error edges only with error edges. So a finite `out: 1`\n * step refuses a second success edge *within* a lane,\n * while additionally being able to hold one error edge — which is what makes a\n * `send → (error) → fallback` shape authorable at all. No `ports.out` bump is\n * needed, so every persisted definition and palette entry stays valid.\n *\n * The target's entry capacity stays **lane-blind**, and that asymmetry is the\n * point: a converging tail step receiving one success and one error edge is\n * governed by `ports.in` alone. Partitioning the target rule by lane too — the\n * plausible \"symmetry\" cleanup — would let a `ports.in: 1` node silently accept\n * two inbound edges.\n */\nexport function applyConnect(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\" = \"success\",\n lookup?: PortsLookup,\n): Edge[] | null {\n const plan = planConnect(source, target, nodes, edges, when, lookup);\n if (!plan || plan.conflict || plan.refusal) return null;\n return plan.next;\n}\n\n/**\n * The id of the existing edge that drawing `source → target` would collide with, or\n * `null` when the connection is safe (or refused for an ordinary reason — a\n * duplicate pair, a self-loop, a portless step — which the drag already showed the\n * user by marking the drop invalid). The creation-path twin of\n * {@link edgeWhenConflict}, and the same channel: it lets the caller *name* the\n * offending edge in an inline refusal instead of losing a wire silently. The editor\n * selects that edge and renders the message in the edge-lane panel (never a browser\n * dialog; see `.ai/conventions.md`).\n */\nexport function connectConflict(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\" = \"success\",\n lookup?: PortsLookup,\n): string | null {\n return planConnect(source, target, nodes, edges, when, lookup)?.conflict ?? null;\n}\n\n/**\n * The out-cap refusal for drawing `source → target`, or `null` when the source has\n * room (or is unbounded). The editor renders `cap` in an inline message on the same\n * channel as {@link connectConflict}; {@link applyConnect} returns `null` for it.\n */\nexport function connectRefusal(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\" = \"success\",\n lookup?: PortsLookup,\n): OutCapRefusal | null {\n return planConnect(source, target, nodes, edges, when, lookup)?.refusal ?? null;\n}\n\n/**\n * The plan for re-laning one edge: the resulting edge set, plus the id of an\n * existing edge it would **collide** with (`null` when it is safe).\n *\n * Both public entry points below read off this, so the eviction arithmetic and the\n * collision test are computed once, in the right order — the collision is checked\n * against the edges that SURVIVE eviction. Checking it against all edges instead\n * would refuse a legitimate switch: re-laning the error half of a same-target\n * pair back to success mints the success sibling's id, but that sibling is\n * evicted by the very same call (see `flow-connect.test.ts` case 11).\n */\nfunction planRelane(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): { next: Edge[]; conflict: string | null; refusal: OutCapRefusal | null } | null {\n // EXACTLY ONE edge must own `edgeId`. None ⇒ nothing to re-lane. Two or more ⇒ the\n // edge set is already corrupt (a step id containing `->` mints a duplicate id —\n // FOLLOWUPS.md), and both the lookup here and the replacement below match BY ID,\n // so proceeding would rewrite EVERY match: the second edge's endpoints silently\n // overwritten with the first's and then persisted through the host's onChange.\n // Refuse instead — `null` is the refusal channel, and the caller already has a\n // generic message for \"this edge can't be switched right now\".\n const matches = edges.filter((e) => e.id === edgeId);\n if (matches.length !== 1) return null;\n const me = matches[0];\n const srcStep = nodes.find((n) => n.id === me.source)?.data.step;\n if (!srcStep) return null;\n const out = nodePortsForStep(srcStep, lookup).out;\n // The DESTINATION lane at the source is full at a finite cap: REFUSE, evicting\n // nothing (exactly the rule `applyConnect` applies on creation). `me` is excluded —\n // it is the edge moving in.\n //\n // ...but ONLY when the edge is actually MOVING. An edge already in `when`\n // occupies that lane already, so a no-op re-lane is never refused: a definition\n // loaded with more edges out of one step than its cap is over capacity but valid.\n //\n // `e.source === me.source` is a CORRECTNESS barrier: capacity belongs to one\n // step's exit port, so other steps' edges must not be counted (pinned by\n // `flow-connect.test.ts` \"the lane census is per-SOURCE\").\n const others = edges.filter((e) => e.id !== edgeId);\n const sameLane =\n edgeLane(me) === when\n ? []\n : others.filter((e) => e.source === me.source && edgeLane(e) === when);\n if (sameLane.length >= out) {\n return { next: edges, conflict: null, refusal: { refused: \"out_cap\", cap: out } };\n }\n // The lane rides on `data.when`, the visuals come from the shared\n // `edgeVisuals`, and the id is re-minted because it ENCODES the lane\n // (`flowEdgeId`). `className`/`label`/`sourceHandle` are assigned rather than\n // spread: for \"success\" `edgeVisuals` returns `{}` and `sourceHandleForLane`\n // returns `undefined`, and spreading `...me` over either would leave a stale\n // error class / a stale error-port anchor on an edge being moved back to the\n // success lane (T1.1.1 — `sourceHandle` has exactly the same hazard as\n // `className`).\n // No run state: an edge is only re-laned during live authoring — there is\n // no run in progress while an author re-lanes an edge (T1.1.1).\n const visuals = edgeVisuals(when, undefined);\n const relaned: Edge = {\n ...me,\n id: flowEdgeId(me.source, me.target, when),\n data: { ...me.data, when },\n className: visuals.className,\n label: visuals.label,\n sourceHandle: sourceHandleForLane(when),\n };\n // The same id-uniqueness test the creation path runs, against the survivors only.\n const conflict = collidingEdgeId(others, relaned.id);\n return {\n // `edgeId` is single-owner (guarded above), so this replaces exactly one edge.\n next: edges.map((e) => (e.id === edgeId ? relaned : e)),\n conflict,\n refusal: null,\n };\n}\n\n/**\n * Re-lane one edge. Updates its `data.when`, its visuals and its id, and frees\n * nothing: when the destination lane is at a finite out cap the switch is refused\n * (see {@link relaneRefusal}) — the same rule `applyConnect` applies on creation.\n *\n * Returns a **new** array and mutates nothing (React Flow's state handle compares\n * by identity), or **`null` when the switch must be refused** — the `applyConnect`\n * idiom. It is refused when the re-minted id would duplicate a surviving edge's\n * id, which happens for a step id ending in `:error`: `flowEdgeId` qualifies the\n * error lane with that same suffix, so `a → b` in the error lane and `a → \"b:error\"`\n * in the success lane mint one id, and React Flow's id-keyed store would collapse\n * them — one wire silently vanishing from the canvas, which reads as the editor\n * losing the author's work. The caller must surface the refusal (see\n * {@link edgeWhenConflict}); rejecting the step id itself is the durable fix and\n * is deliberately NOT done here, because it would also have to reject ids inside\n * workflows that already exist (FOLLOWUPS.md).\n *\n * It is refused for one more reason: an `edgeId` owned by more than one edge. That\n * edge set is already corrupt, and re-laning by id would rewrite **every** match,\n * overwriting the other edge's endpoints (`edgeWhenConflict` returns `null` there —\n * there is no *other* edge to name, so the caller's generic refusal applies).\n */\nexport function setEdgeWhen(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): Edge[] | null {\n const plan = planRelane(edges, edgeId, when, nodes, lookup);\n if (!plan || plan.conflict || plan.refusal) return null;\n return plan.next;\n}\n\n/**\n * The id of the existing edge that {@link setEdgeWhen} would collide with, or\n * `null` when the switch is safe. Lets the caller *name* the offending edge in an\n * inline refusal instead of failing silently — the editor renders it in the\n * edge-lane panel (never a browser dialog; see `.ai/conventions.md`).\n */\nexport function edgeWhenConflict(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): string | null {\n return planRelane(edges, edgeId, when, nodes, lookup)?.conflict ?? null;\n}\n\n/** The out-cap refusal for re-laning `edgeId`, or `null` — the re-lane twin of {@link connectRefusal}. */\nexport function relaneRefusal(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): OutCapRefusal | null {\n return planRelane(edges, edgeId, when, nodes, lookup)?.refusal ?? null;\n}\n\n/**\n * Rewrite the edge set after a step's id changed from `prevId` to `nextId`:\n * re-point every endpoint and **re-mint the id in the edge's own lane**.\n *\n * Extracted from `updateStep` so it is testable, and fixing a real defect: the\n * rewrite used to rebuild every id as `` `${source}->${target}` `` unconditionally,\n * dropping an error edge's `:error` qualifier — so renaming a step collapsed a\n * same-target success+error pair into one edge in React Flow's id-keyed store.\n * That was unreachable while a second outgoing edge deleted the first; per-lane\n * exit capacity is what makes it reachable, hence the fix ships alongside it.\n */\nexport function renameStepInEdges(edges: Edge[], prevId: string, nextId: string): Edge[] {\n return edges.map((e) => {\n const source = e.source === prevId ? nextId : e.source;\n const target = e.target === prevId ? nextId : e.target;\n return { ...e, source, target, id: flowEdgeId(source, target, edgeLane(e)) };\n });\n}\n","// Pure fan-out decisions for the \"Run next steps\" control and card badge.\nimport type { Edge } from \"@xyflow/react\";\nimport { edgeLane } from \"./flow-connect.ts\";\nimport type { FlowStep } from \"./flow-types.ts\";\n\nexport type FanOutMode = \"sequential\" | \"parallel\";\n\n/**\n * `count` is the largest number of outgoing edges the step has in any ONE lane\n * (success / error); fan-out only means something at `count >= 2`.\n */\nexport function fanOutState(\n stepId: string,\n edges: readonly Edge[],\n): { enabled: boolean; count: number } {\n const per = { success: 0, error: 0 };\n for (const e of edges) if (e.source === stepId) per[edgeLane(e)]++;\n const count = Math.max(per.success, per.error);\n return { enabled: count >= 2, count };\n}\n\n/** Sequential is the default, so it is expressed by the key's ABSENCE. */\nexport function setFanOut(step: FlowStep, mode: FanOutMode): FlowStep {\n const { fanOut: _drop, ...rest } = step;\n return mode === \"parallel\" ? { ...rest, fanOut: \"parallel\" } : rest;\n}\n\n/** Card-badge wording: how many next steps run, and in which mode. */\nexport function fanOutLabel(count: number, mode: FanOutMode): string {\n return `Runs these ${count} steps in ${mode === \"parallel\" ? \"parallel\" : \"sequence\"}`;\n}\n","/**\n * Pure derivation of a live run's canvas visuals (T1.1.1) — step node styling\n * and main-graph edge taken/skipped/untouched state. Plain functions of (run\n * state, step/edge identity) → visual state, with **no React import**, so\n * `node --test` exercises them directly (A7) the same way `flow-connect.ts`'s\n * connection rules do.\n *\n * `RunStatus`/`StepStatus`/`RunState`/`StepExecution` below are thin LOCAL\n * copies of the shapes `@w6w/workflow-types` declares\n * (`packages/w6w-workflow/packages/types/mod.ts`) — the same house idiom\n * `components/HealthStatusPill.tsx`'s `HealthPillState` uses, and the same\n * design note `flow-types.ts`'s own header already states for\n * `FlowWorkflow`/`FlowStep`: `packages/ui/package.json` has no dependency on\n * `@w6w/workflow-types` and must not gain one, so this module stays a\n * pure-presentation leaf with no transport/spec coupling. A caller who\n * already has the real `RunState`/`StepExecution` is structurally compatible\n * for free.\n */\n\n/** Mirrors `RunStatus` in `@w6w/workflow-types`. */\nexport type RunStatus = \"queued\" | \"running\" | \"succeeded\" | \"failed\" | \"canceled\";\n/** Mirrors `StepStatus` in `@w6w/workflow-types`. */\nexport type StepStatus = \"pending\" | \"running\" | \"succeeded\" | \"failed\" | \"skipped\";\n\n/** The slice of `StepExecution` this module reads. */\nexport interface StepExecution {\n status: StepStatus;\n}\n\n/** The slice of `RunState` this module reads — steps keyed by step id, same\n * as the engine's own `RunState.steps`. */\nexport interface RunState {\n status: RunStatus;\n steps: Record<string, StepExecution>;\n}\n\nconst RUN_ENDED: ReadonlySet<RunStatus> = new Set([\"succeeded\", \"failed\", \"canceled\"]);\n\n/**\n * A step node's rendered state — exactly the five {@link StepStatus} values\n * plus `\"no-run\"` (A2): `runState` omitted ⇒ `\"no-run\"` (this canvas isn't\n * showing any run). A step present in `runState.steps` renders its own\n * recorded status; a step the run hasn't reached yet has no entry there at\n * all (only steps that were actually started/skipped get a `StepExecution` —\n * see the engine's `recordSkipped`), so absence reads as `\"pending\"`.\n */\nexport type StepVisualState = \"no-run\" | StepStatus;\n\n/**\n * The rendered state for step `stepId` under `runState` — the StepStatus\n * half of A3's RunStatus × StepStatus mapping. Pure function of the run's own\n * step records; no additional data needed (`GET /runs/:id` already returns\n * everything this reads).\n */\nexport function stepVisualState(runState: RunState | undefined, stepId: string): StepVisualState {\n if (!runState) return \"no-run\";\n return runState.steps[stepId]?.status ?? \"pending\";\n}\n\n/** One node's non-persisted, run-aware look. Colour is never the only signal\n * (the house rule `HealthStatusPill`'s own doc comment states) — every\n * non-default state also differs from the default (`\"no-run\"`) in\n * `borderStyle`, `borderWidth`, or `opacity`. */\nexport interface StepNodeVisual {\n borderColor: string;\n borderStyle: \"solid\" | \"dashed\";\n borderWidth: number;\n opacity: number;\n}\n\nconst NO_RUN_VISUAL: StepNodeVisual = {\n borderColor: \"var(--w6w-border)\",\n borderStyle: \"solid\",\n borderWidth: 1,\n opacity: 1,\n};\n\n/**\n * The node visual for a step's {@link StepVisualState}, `runStatus`\n * (the owning run's OWN status) additionally distinguishing the `\"pending\"`\n * case — A3: a `canceled`/`failed` run's not-yet-reached steps render\n * dimmer/muted (they never will run) than a `queued`/`running` run's\n * not-yet-reached steps (they still might), even though both share the same\n * `StepVisualState`. Every token used is one of the five named in\n * `context.notes` (`--w6w-accent`/`--w6w-success`/`--w6w-danger`/\n * `--w6w-muted`/`--w6w-border`) — never a literal hex, never a different\n * token family.\n */\nexport function stepNodeVisual(state: StepVisualState, runStatus?: RunStatus): StepNodeVisual {\n const runEnded = runStatus !== undefined && RUN_ENDED.has(runStatus);\n switch (state) {\n case \"no-run\":\n return NO_RUN_VISUAL;\n case \"pending\":\n return runEnded\n ? { borderColor: \"var(--w6w-muted)\", borderStyle: \"dashed\", borderWidth: 1, opacity: 0.5 }\n : { borderColor: \"var(--w6w-border)\", borderStyle: \"dashed\", borderWidth: 1, opacity: 1 };\n case \"running\":\n return { borderColor: \"var(--w6w-accent)\", borderStyle: \"solid\", borderWidth: 2, opacity: 1 };\n case \"succeeded\":\n return {\n borderColor: \"var(--w6w-success)\",\n borderStyle: \"solid\",\n borderWidth: 2,\n opacity: 1,\n };\n case \"failed\":\n return { borderColor: \"var(--w6w-danger)\", borderStyle: \"solid\", borderWidth: 2, opacity: 1 };\n case \"skipped\":\n return {\n borderColor: \"var(--w6w-muted)\",\n borderStyle: \"dashed\",\n borderWidth: 1,\n opacity: 0.7,\n };\n default:\n return NO_RUN_VISUAL;\n }\n}\n\n// ── Edge inference (main-graph `when` edges only — no `if`/then-else\n// sub-block inference: the canvas has no rendering or authoring path for\n// that construct today, see FOLLOWUPS.md) ───────────────────────────────\n\n/** Whether a main-graph edge was taken, skipped, or left untouched by the\n * run (`undefined` — the source step hasn't been decided yet, or there is\n * no run). */\nexport type EdgeRunState = \"taken\" | \"skipped\";\n\n/**\n * Whether the edge `from --(when)--> …` was taken or skipped in `runState` —\n * computable from the run's step records alone (no engine data beyond what\n * `GET /runs/:id` already returns):\n * - `from` **succeeded**: its `\"success\"`/omitted-`when` edges are taken,\n * its `\"error\"` edges are skipped.\n * - `from` **failed**: a declared `\"error\"` edge is taken, every other\n * outgoing edge (the `\"success\"` lane) is skipped.\n * - `from` was itself **skipped** (reached only through a skipped edge —\n * the engine's own `recordSkipped`/`markOutgoingEdgesSkipped`): every\n * outgoing edge is skipped too, regardless of lane — a step that never\n * ran cannot have taken an exit.\n * - `from` is **running**, or has no record at all (not yet reached): the\n * edge's fate isn't decided yet ⇒ `undefined`.\n */\nexport function edgeRunState(\n runState: RunState | undefined,\n from: string,\n when: \"success\" | \"error\" | undefined,\n): EdgeRunState | undefined {\n if (!runState) return undefined;\n const exec = runState.steps[from];\n if (!exec) return undefined;\n const lane = when ?? \"success\";\n if (exec.status === \"succeeded\") return lane === \"success\" ? \"taken\" : \"skipped\";\n if (exec.status === \"failed\") return lane === \"error\" ? \"taken\" : \"skipped\";\n if (exec.status === \"skipped\") return \"skipped\";\n return undefined;\n}\n","/**\n * The action definitions of every app a workflow's steps use, so the editor\n * can read each upstream step's declared output shape (`output-shape.ts`)\n * without running it.\n *\n * Fetched once per app id (`getAppActions`, i.e. `GET /apps/:id`) and kept for\n * the editor's lifetime. Keyed on the sorted SET of app ids rather than\n * `nodes`' identity — `nodes` is rebuilt on every drag and keystroke. Internal\n * `@w6w/*` apps are skipped: their shapes are static (`internalNodeDef`).\n * Best-effort per app: a failed fetch contributes nothing and never breaks\n * the picker.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport { type PortsLookup, isInternalApp } from \"./flow-types.ts\";\nimport type { StepNode } from \"./flow-utils.ts\";\nimport type { W6WApi } from \"./provider.tsx\";\nimport type { ActionDef } from \"./types.ts\";\n\nexport function useAppActionDefs(api: W6WApi, nodes: StepNode[]): Record<string, ActionDef[]> {\n const [defs, setDefs] = useState<Record<string, ActionDef[]>>({});\n // Apps already requested (loaded or in flight), so a re-run fetches only new ones.\n const requested = useRef(new Set<string>());\n const appIdsKey = JSON.stringify(\n [...new Set(nodes.map((n) => n.data.step.uses.app))]\n .filter((app) => app && !isInternalApp(app))\n .sort(),\n );\n useEffect(() => {\n const missing = (JSON.parse(appIdsKey) as string[]).filter(\n (app) => !requested.current.has(app),\n );\n if (missing.length === 0) return;\n for (const app of missing) requested.current.add(app);\n // Never canceled: an app's actions don't depend on which run of this effect\n // asked for them, so a result landing after a re-key is still right, and\n // dropping it would leave that app unloaded (it is already `requested`).\n Promise.all(\n missing.map(async (app) => {\n try {\n return [app, await api.getAppActions(app)] as const;\n } catch {\n requested.current.delete(app); // let a later change retry it\n return null;\n }\n }),\n ).then((res) => {\n const loaded = res.filter((r): r is readonly [string, ActionDef[]] => r !== null);\n if (loaded.length === 0) return;\n setDefs((prev) => ({ ...prev, ...Object.fromEntries(loaded) }));\n });\n }, [api, appIdsKey]);\n return defs;\n}\n\n/** Build a `PortsLookup` over the fetched catalog defs (`useAppActionDefs`' result). */\nexport function portsLookupFromDefs(defs: Record<string, ActionDef[]>): PortsLookup {\n return (app, action) => defs[app]?.find((d) => d.key === action)?.ports;\n}\n","import {\n Background,\n type Connection,\n // Rendered as a `<Controls>` CHILD, which the library appends after its three\n // built-ins — so the auto-layout button lands in the same stack and inherits\n // T3.1.1's `--xy-*` → `--w6w-*` bridge with no chrome of its own.\n ConnectionLineType,\n ControlButton,\n Controls,\n type Edge,\n MiniMap,\n type NodeProps,\n NodeToolbar,\n Panel,\n Position,\n ReactFlow,\n ReactFlowProvider,\n // Type-only now: `addEdge` itself is called inside `flow-connect.ts`; this file\n // only borrows its parameter type for the `onConnect` handler.\n type addEdge,\n useEdgesState,\n useNodesState,\n useReactFlow,\n} from \"@xyflow/react\";\nimport type { FinalConnectionState } from \"@xyflow/react\";\nimport { Handle } from \"@xyflow/react\";\nimport {\n type ReactNode,\n type RefObject,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { AddConnectionModal } from \"./AddConnectionModal.tsx\";\nimport { AppPicker } from \"./AppPicker.tsx\";\nimport { JsonEditor } from \"./JsonEditor.tsx\";\nimport { NodeConfigForm } from \"./NodeConfigForm.tsx\";\nimport { ParamsForm } from \"./ParamsForm.tsx\";\nimport { PropertyEntryForm } from \"./PropertyEntryForm.tsx\";\nimport {\n type BuiltStep,\n type ConfigView,\n ConfigViewToggle,\n StepBuilderModal,\n StepTestRun,\n type StepTestRunHandle,\n requiredParamsFilled,\n} from \"./StepBuilderModal.tsx\";\nimport { TriggerFillForm } from \"./TriggerFillForm.tsx\";\nimport { WaitEditor } from \"./WaitEditor.tsx\";\nimport { AppIcon } from \"./components/AppIcon.tsx\";\nimport { Combobox } from \"./components/Combobox.tsx\";\nimport { ConfirmModal } from \"./components/ConfirmModal.tsx\";\nimport { Copyable } from \"./components/Copyable.tsx\";\nimport {\n type ExpressionOptions,\n ExpressionOptionsProvider,\n type ExpressionStepSource,\n excludeSelfStep,\n useExpressionOptions,\n} from \"./components/ExpressionOptions.tsx\";\nimport { Icon } from \"./components/Icon.tsx\";\nimport { InternalIcon } from \"./components/InternalIcon.tsx\";\nimport { Modal } from \"./components/Modal.tsx\";\nimport { ResolvedParams } from \"./components/ResolvedParams.tsx\";\nimport {\n applyConnect,\n canConnect,\n connectConflict,\n connectRefusal,\n edgeLane,\n edgeWhenConflict,\n relaneRefusal,\n renameStepInEdges,\n setEdgeWhen,\n} from \"./flow-connect.ts\";\n// The connection rules live in a JSX-free `.ts` module so `node --test` can run\n// them (see `flow-connect.ts`). This file is their only production caller.\nimport { fanOutLabel, fanOutState, setFanOut } from \"./flow-fanout.ts\";\nimport {\n CALL_APP,\n ERROR_SOURCE_HANDLE,\n type FlowStep,\n type FlowWorkflow,\n type PortsLookup,\n WEBHOOK_APP,\n edgeVisuals,\n internalNodeIcon,\n internalNodeLabel,\n internalNodeParams,\n isControlApp,\n isInternalApp,\n isTriggerApp,\n laneForSourceHandle,\n nodePortsForStep,\n} from \"./flow-types.ts\";\nimport {\n type StepNode,\n findChainRoot,\n findChainTail,\n flowToWorkflow,\n idClashMessage,\n paramsToJson,\n relayoutNodes,\n slugifyLabel,\n stepIdError,\n stepToJson,\n storedViewport,\n suggestStepId,\n withViewport,\n workflowToFlow,\n} from \"./flow-utils.ts\";\n// One implementation of the incoming-state pipeline, shared by every surface\n// that offers upstream seed chips (this file's step editor + ▶ Run collect\n// form, and StepBuilderModal's add-step Test tab) — see `step-preview-state.ts`'s\n// header comment for why this lives outside WorkflowFlowEditor.tsx.\nimport { valueAtPath } from \"./output-shape.ts\";\nimport {\n type StepMeta,\n type StepStartState,\n type StepTest,\n WorkflowProjectProvider,\n useW6WApi,\n} from \"./provider.tsx\";\n// T1.1.1 — the pure run-visual derivation, no React import (`run-visuals.ts`'s\n// own header). This file is the one production caller that turns it into\n// pixels: a live-run repaint effect (in `Inner`) feeds the results into\n// `data`, and `StepNodeCard`/`ControlNodeCard` read them back off `data`.\nimport { type RunState, edgeRunState, stepNodeVisual, stepVisualState } from \"./run-visuals.ts\";\nimport {\n type SeedSource,\n type StepShapeContext,\n startStateFromSeeds,\n stepBuilderUpstreamSteps,\n stepMetaSampleValues,\n upstreamStateSources,\n} from \"./step-preview-state.ts\";\nimport { useEffectiveTheme } from \"./theme.ts\";\nimport { asFieldDefs, fieldsToParams, seedValues } from \"./trigger-fields.ts\";\nimport type {\n ActionDef,\n ActionParam,\n AppSummary,\n ConnectionSummary,\n SubscriptionSummary,\n} from \"./types.ts\";\nimport { portsLookupFromDefs, useAppActionDefs } from \"./use-app-action-defs.ts\";\nimport { useSeedSources } from \"./use-seed-sources.ts\";\n\n/**\n * What the \"Run on\" control does, shown on the lane panel (T1.1.1).\n *\n * An error edge is authored by DRAGGING from the step's error exit port (the\n * second, red-tinted source handle every step/control node renders below its\n * ordinary exit) — that's what stamps `sourceHandle`/`data.when` at creation\n * time (`onConnect` → `laneForSourceHandle`). This panel is not how an error\n * edge is first drawn; it is the RE-LANE affordance for an edge already drawn,\n * for when it was dragged from the wrong port. Each lane still holds one edge\n * out of an `out: 1` step, so switching an edge into a lane that's already\n * occupied re-points the incumbent (the deliberate single-slot UX).\n */\nconst LANE_HINT =\n \"Which outcome of the source step this edge carries. An error edge is drawn by dragging \" +\n \"from the step's error exit port; use this control to re-lane an edge already drawn. \" +\n \"An error edge overrides the step’s “On error” policy.\";\n\n/**\n * The step ids the refused edge id is minted from: the endpoints of the edge being\n * drawn/moved plus those of the edge already holding the id. {@link idClashMessage}\n * needs them to *determine* the cause instead of asserting one — the wording used to\n * blame a `:error` step id unconditionally, and a collision from an embedded `->`\n * therefore told the author to rename a step that did not exist (ADDENDUM Y).\n *\n * Both call sites look the sitting edge up by the id `connectConflict` /\n * `edgeWhenConflict` returned; a corrupt edge set with no such edge simply\n * contributes nothing, and the message falls back to naming no cause.\n */\nfunction clashInvolvedIds(\n edges: Edge[],\n conflictId: string,\n source: string | null | undefined,\n target: string | null | undefined,\n): (string | null | undefined)[] {\n const sitting = edges.find((e) => e.id === conflictId);\n return [source, target, sitting?.source, sitting?.target];\n}\n\nexport interface WorkflowFlowEditorProps {\n /** The workflow being edited. The editor re-derives layout when this changes identity. */\n value: FlowWorkflow;\n /** Fired whenever the user changes the graph — new nodes, edges, or step edits. */\n onChange: (next: FlowWorkflow) => void;\n /** Disable all interactions — pans/zooms are still enabled. */\n readOnly?: boolean;\n /** Height of the editor viewport. Defaults to 480px. */\n height?: string | number;\n /**\n * Registered apps, used to render each action node with its owning app's icon,\n * display name, and version. A step only carries `uses.app` (an id), so the\n * card joins that id against this list. Optional — unknown/absent apps degrade\n * to an initials tile with no version. Metadata is looked up at render time and\n * never stored on nodes, so it can't corrupt round-tripping back to a workflow.\n */\n apps?: AppSummary[];\n /**\n * EXTRA scope for each step field's expression picker, on top of whatever\n * this editor is already mounted under.\n *\n * Optional, and normally omitted: `ExpressionOptionsProvider` layers, so a\n * host that provides the project's vars/secrets/documents above this editor\n * (studio does, once, in its app shell) needs to pass nothing here. Use it\n * only to add or override something at this level.\n *\n * Names only — secret plaintext never reaches the client.\n */\n exprOptions?: ExpressionOptions;\n /**\n * The workflow's currently-selected project id. Threaded into ad-hoc\n * test-invokes so document expressions resolve against that project's docs\n * (not the scope's default/starter project). The host (studio) passes the\n * active project here; omitted → server default-project behavior. See T2.1.2 /\n * HITL-4(b): the invoke path's ambient scope is already project-aware.\n */\n project?: string;\n /**\n * The live run's per-step state, when the canvas is showing one (T1.1.1).\n * Omitted ⇒ the canvas renders exactly as it does today: every node in its\n * default look, every edge unstyled beyond its own `when` lane. This\n * editor does no fetching of its own — the host (`studio`) owns polling\n * `GET /runs/:id` and passes each successive result here, the same\n * \"host owns the query, editor is presentational\" split `apps`/`readOnly`\n * already establish for every other external data source.\n *\n * A structural subset of `RunState` in `@w6w/workflow-types` (mirrors this\n * file's own `FlowWorkflow`/`FlowStep` — see `flow-types.ts`'s header\n * note): `packages/ui` takes no dependency on the engine's types, so a\n * caller who already has the real `RunState` is compatible for free.\n *\n * Never persisted: {@link flowToWorkflow} never reads it — a run is not\n * part of the workflow that ran, and `data.stepStatus`/`data.runStatus`\n * (where this ends up on the canvas) are never among the fields it reads\n * back off a node.\n */\n runState?: RunState;\n /**\n * Fired the instant a step test starts — the ▶ collect form's Run button, or\n * `StepEditModal`'s footer Test button — before the invoke goes out, so a host\n * can drop whatever run its canvas was showing (T1.1.1) instead of leaving a\n * test to run underneath a stale run's highlighting.\n *\n * On the click, never on the result: only one of those two surfaces has a\n * completion to report at all, and the click is the one moment both share.\n */\n onTestRun?: () => void;\n /**\n * Re-frames the canvas when the host changes the space around it. When this\n * value changes after mount (compared with `Object.is`), the editor calls\n * `fitView({ duration: 300 })` once. It never fires on mount, and omitting it\n * or passing the same value again adds no fit.\n *\n * For a host whose page layout resizes the canvas without the graph changing,\n * e.g. a side panel docking beside it (pass the panel's open state). React Flow\n * keeps its pan/zoom transform when its pane narrows, so nodes near the edge\n * would otherwise end up behind the panel. The editor does not watch its own\n * size, so the host decides which layout changes deserve a re-frame.\n *\n * A fit replaces whatever camera is showing, including a saved viewport the\n * editor restored on open. The fit is not user-initiated, so it never emits\n * `onChange` and never overwrites the stored viewport.\n */\n fitViewKey?: string | number | boolean;\n}\n\n/**\n * App metadata by id, shared with the node cards so `StepNodeCard` can show the\n * app's icon/name/version without threading it through each node's `data`.\n */\nconst PortsLookupCtx = createContext<PortsLookup | undefined>(undefined);\n/** Per-step max outgoing edges in one lane (`fanOutState().count`); drives the card badge. */\nconst FanOutCountsCtx = createContext<ReadonlyMap<string, number>>(new Map());\n\n/** The inline refusal when a source is already at its finite `out` cap (no eviction). */\nfunction outCapMessage(cap: number): string {\n return `Can’t draw that edge: this node allows at most ${cap} outgoing edge${cap === 1 ? \"\" : \"s\"} per lane.`;\n}\n\nconst AppsCtx = createContext<Map<string, AppSummary>>(new Map());\n\n/** Which view the step edit modal opens in: the tabbed form, raw JSON, or node settings. */\ntype EditView = \"props\" | \"json\" | \"settings\";\n\n/** Per-node control handlers, provided to the node cards via context. */\ninterface StepControls {\n /** Open the edit modal on the form view. */\n onEdit: (id: string) => void;\n /** Open the edit modal straight on the JSON view. */\n onEditJson: (id: string) => void;\n /** Test-run a single step and show its result. */\n onRun: (id: string) => void;\n onDuplicate: (id: string) => void;\n onDelete: (id: string) => void;\n readOnly?: boolean;\n}\nconst StepControlsCtx = createContext<StepControls | null>(null);\n\n/**\n * Visual workflow editor. Renders a Workflow's DAG as a React Flow graph:\n *\n * - Auto-layouted on load (topological columns + sibling rows).\n * - Nodes render differently for action steps vs. control steps\n * (`uses.app === \"@w6w/control\"`).\n * - `+ Step` opens a guided builder (pick app → connection → action → params,\n * or a flow control). Drag nodes to reposition; connect handles to add edges.\n * - Selecting a node reveals a toolbar above it: Edit / Duplicate / Delete.\n * - Edit opens a modal with a Form ⇄ JSON toggle for that step.\n * - Every meaningful change fires `onChange` with an updated Workflow.\n */\nexport function WorkflowFlowEditor(props: WorkflowFlowEditorProps) {\n return (\n <ReactFlowProvider>\n <Inner {...props} />\n </ReactFlowProvider>\n );\n}\n\n/**\n * Live state of a single-step test run, shown in **one** modal that walks three\n * phases against this one state slot: `collect` (fill the step's properties),\n * `running`, then `done` / `error`. ▶ opens `collect` — it no longer invokes on\n * click, so a trigger's declared fields are filled before the run instead of the\n * run coming back `{}`.\n */\ninterface StepRunState {\n stepId: string;\n status: \"collect\" | \"running\" | \"done\" | \"error\";\n value?: unknown;\n /** Which app/version/action/connection the run used; absent when the host sent none. */\n meta?: StepMeta;\n error?: string;\n errorCode?: string;\n /** console.* output captured from a script node run, if any. */\n logs?: string[];\n}\n\nfunction Inner({\n value,\n onChange,\n readOnly,\n height = 480,\n apps,\n exprOptions,\n project,\n runState,\n onTestRun,\n fitViewKey,\n}: WorkflowFlowEditorProps) {\n const api = useW6WApi();\n const appsById = useMemo(() => new Map((apps ?? []).map((a) => [a.id, a])), [apps]);\n const [runResult, setRunResult] = useState<StepRunState | null>(null);\n // Re-hydrate nodes+edges only when the workflow id changes identity. Local\n // edits (drag, connect, delete) go through the useNodesState / useEdgesState\n // handles so React Flow's own state stays authoritative during interaction.\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-derive layout only on workflow identity change\n const initial = useMemo(() => workflowToFlow(value), [value.id]);\n // The camera to open at, read at mount for the same reason `initial` is: React\n // Flow consumes `defaultViewport` in a mount-only effect (ZoomPane's `XYPanZoom`\n // init), so a fresh object per render would be inert but misleading — and this\n // is what makes a save round-trip not yank the view back.\n // biome-ignore lint/correctness/useExhaustiveDependencies: read at mount only, like `initial`\n const savedViewport = useMemo(() => storedViewport(value), [value.id]);\n // Gates BOTH position writers below (the drag and the pan/zoom). ⚠️ Omitted ⇒\n // **true** (core rfcs/workflow.md · authoring-presentation amendment), so it is\n // read as `!== false` — never `?? false` and never `=== true`. Derived from\n // `value`, deliberately NOT a prop: the flag is a field of the workflow the\n // editor already receives, and a prop would be a second source of truth that\n // can disagree with the persisted document.\n const savePosition = value.settings?.savePosition !== false;\n const [nodes, setNodes, onNodesChange] = useNodesState<StepNode>(initial.nodes);\n // Catalog ports reach resolution through this lookup, never by stamping\n // `step.ports` (which would persist into every saved workflow).\n const actionDefs = useAppActionDefs(api, nodes);\n const portsLookup = useMemo(() => portsLookupFromDefs(actionDefs), [actionDefs]);\n const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(initial.edges);\n // T1.1.1 — live run-state repaint. `initial` above deliberately re-derives\n // nodes/edges ONLY on workflow identity change (Trap 1 — its own lint\n // suppression comment a few lines up says so), so a poll tick that\n // changes `runState` while `value.id` stays put would be invisible to the\n // cards if it went through `workflowToFlow` — that memo simply never fires\n // again. This effect instead patches the already-authoritative React Flow\n // state directly, the same mechanism a drag/connect/delete already uses to\n // update `nodes`/`edges` without re-deriving `initial`.\n //\n // Only new `data` keys are stamped — `stepStatus`/`runStatus` on nodes, a\n // run-aware `className`/`label` on edges. `data.step` and `data.when` (the\n // fields `flowToWorkflow` reads back to build the persisted document) are\n // never touched here, so nothing run-related can round-trip into a saved\n // workflow (A6).\n //\n // Functional `setNodes`/`setEdges` updaters read the PREVIOUS state\n // directly, so `nodes`/`edges` need not be dependencies — keeping this\n // effect keyed on `runState` alone (plus the setters, stable across\n // renders) is what makes it fire on every run-state change and nothing\n // else, including a plain drag or edit that doesn't touch `runState`.\n useEffect(() => {\n setNodes((nds) =>\n nds.map((n) => {\n const stepStatus = stepVisualState(runState, n.id);\n if (n.data.stepStatus === stepStatus && n.data.runStatus === runState?.status) return n;\n return { ...n, data: { ...n.data, stepStatus, runStatus: runState?.status } };\n }),\n );\n setEdges((eds) =>\n eds.map((e) => {\n const lane = edgeLane(e);\n const run = edgeRunState(runState, e.source, lane);\n const visuals = edgeVisuals(lane, run);\n if (e.className === visuals.className && e.label === visuals.label) return e;\n return { ...e, className: visuals.className, label: visuals.label };\n }),\n );\n }, [runState, setNodes, setEdges]);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null);\n const [builderOpen, setBuilderOpen] = useState(false);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editView, setEditView] = useState<EditView>(\"props\");\n // The step id awaiting delete confirmation — one pending slot driving one modal.\n const [pendingDelete, setPendingDelete] = useState<string | null>(null);\n // Set right after `setNodes(next)` in `performRelayout`, cleared by the\n // `[nodes, fitView]` effect below, which then calls `fitView()`. A ref (not\n // state) because it drives no render of its own — it only needs to survive\n // from the relayout click to the next commit of `nodes`.\n const relayoutPendingFitRef = useRef(false);\n // Carries the flow container div so the capture-phase dblclick listener below\n // can be attached/removed on it directly — see that effect for why a native\n // listener is required instead of React Flow's own node-double-click prop.\n const flowContainerRef = useRef<HTMLDivElement>(null);\n // When a connection drag is released on empty canvas, we open the builder to\n // create a new node and auto-wire it to the handle it was dragged from.\n const [pendingConnect, setPendingConnect] = useState<{\n nodeId: string;\n handleType: \"source\" | \"target\";\n position: { x: number; y: number };\n /**\n * The lane the drag originated from, derived via {@link laneForSourceHandle}\n * at CAPTURE time in `onConnectEnd` (T1.1.1) — a target-handle drag reports a\n * handle id that is never `ERROR_SOURCE_HANDLE`, so this naturally reads\n * \"success\" for that gesture without a separate branch.\n */\n lane: \"success\" | \"error\";\n } | null>(null);\n const { screenToFlowPosition, fitView } = useReactFlow();\n // React Flow's `colorMode` defaults to \"light\", so without this its chrome\n // (controls, minimap mask, handles, edge strokes, background dots) stays in the\n // light palette on a dark canvas. Read the mode from the house hook — the same\n // `data-theme` signal `styles.css` and AppIcon/CodeEditor/JsonEditor already\n // follow — rather than adding a prop the host would have to keep in sync.\n const colorMode = useEffectiveTheme();\n\n // If the caller swaps in a different workflow, reset local graph state.\n useEffect(() => {\n setNodes(initial.nodes);\n setEdges(initial.edges);\n setSelectedId(null);\n setEditingId(null);\n }, [initial, setNodes, setEdges]);\n\n const emitChange = useCallback(\n (nextNodes: StepNode[], nextEdges: Edge[]) => {\n onChange(flowToWorkflow(value, nextNodes, nextEdges));\n },\n [value, onChange],\n );\n\n // Live gate while dragging a connection — React Flow marks the drop invalid\n // (and won't fire onConnect) when this returns false.\n const isValidConnection = useCallback(\n (c: Connection | Edge) => canConnect(c.source, c.target, nodes, edges, portsLookup),\n [nodes, edges, portsLookup],\n );\n\n const onConnect = useCallback(\n (params: Parameters<typeof addEdge>[0]) => {\n if (readOnly) return;\n // The lane rides on the handle the wire was dragged from (T1.1.1) — the\n // one canonicalisation point, so a handle-to-handle drag and the\n // drag-to-empty-canvas gesture in `onConnectEnd` cannot disagree on what\n // counts as the error port. applyConnect replaces a full single-slot port\n // rather than ignoring the drop.\n const next = applyConnect(\n params.source,\n params.target,\n nodes,\n edges,\n laneForSourceHandle(params.sourceHandle),\n portsLookup,\n );\n if (!next) {\n // FAIL LOUDLY when the refusal is one the drag could not have shown. Every\n // ordinary rule already ran in `isValidConnection` (React Flow marks the\n // drop invalid and never fires this handler), so the only refusal that\n // reaches here is a duplicate minted id — and that one is *invisible*:\n // React Flow's id-keyed store would have held ONE wire for two edges, which\n // reads as the editor losing the author's work.\n //\n // No new channel: `connectConflict` names the edge already holding the id,\n // and the refusal renders in the edge-lane panel's `role=\"alert\"` slot. The\n // clashing edge is SELECTED — in React Flow's own store, not just our mirror\n // state, so `onSelectionChange` (re-invoked on any re-render) confirms it\n // instead of wiping it before paint — which both reveals that panel and\n // highlights the wire the author has to rename.\n // A finite `out` cap is refused, never evicted: name the cap inline, on an\n // edge already leaving this source in this lane (the panel needs one selected).\n const lane = laneForSourceHandle(params.sourceHandle);\n const refusal = connectRefusal(\n params.source,\n params.target,\n nodes,\n edges,\n lane,\n portsLookup,\n );\n if (refusal) {\n const holder = edges.find((e) => e.source === params.source && edgeLane(e) === lane);\n if (holder) {\n setEdges(edges.map((e) => ({ ...e, selected: e.id === holder.id })));\n setSelectedEdgeId(holder.id);\n setLaneError({ edgeId: holder.id, message: outCapMessage(refusal.cap) });\n }\n return;\n }\n const conflict = connectConflict(\n params.source,\n params.target,\n nodes,\n edges,\n laneForSourceHandle(params.sourceHandle),\n );\n if (!conflict) return;\n setEdges(edges.map((e) => ({ ...e, selected: e.id === conflict })));\n setSelectedEdgeId(conflict);\n setLaneError({\n edgeId: conflict,\n message: idClashMessage(\n \"Can’t draw that edge\",\n conflict,\n clashInvolvedIds(edges, conflict, params.source, params.target),\n ),\n });\n return;\n }\n setLaneError(null);\n setEdges(next);\n emitChange(nodes, next);\n },\n [edges, nodes, setEdges, emitChange, readOnly, portsLookup],\n );\n\n // A connection dropped on empty canvas (no valid target) means \"add a new node\n // here and connect to it\" — open the builder and remember where it came from.\n const onConnectEnd = useCallback(\n (event: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => {\n if (readOnly) return;\n // A valid drop onto a handle is already handled by onConnect.\n if (connectionState.isValid) return;\n const fromNode = connectionState.fromNode;\n if (!fromNode) return;\n const handleType = connectionState.fromHandle?.type === \"target\" ? \"target\" : \"source\";\n // Only spawn if the origin handle can participate at all — a source handle\n // needs an exit port, a target handle an entry port. (A full single-slot\n // port is fine: the auto-wire below replaces its existing edge.)\n const originStep = nodes.find((n) => n.id === fromNode.id)?.data.step;\n if (originStep) {\n const p = nodePortsForStep(originStep, portsLookup);\n if ((handleType === \"source\" ? p.out : p.in) < 1) return;\n }\n const point = \"changedTouches\" in event ? event.changedTouches[0] : event;\n const position = screenToFlowPosition({ x: point.clientX, y: point.clientY });\n // The lane is derived HERE, not re-derived at `addBuiltStep` time\n // (T1.1.1): the dragged handle is only available on `connectionState`,\n // which does not survive past this callback — the step builder opens in\n // between. A target-handle drag reports a handle id that is never\n // `ERROR_SOURCE_HANDLE`, so this naturally reads \"success\" for that\n // gesture with no separate branch.\n setPendingConnect({\n nodeId: fromNode.id,\n handleType,\n position,\n lane: laneForSourceHandle(connectionState.fromHandle?.id),\n });\n setBuilderOpen(true);\n },\n [readOnly, screenToFlowPosition, nodes, portsLookup],\n );\n\n // Deleting a step is confirmed by an in-app modal, never a blocking browser\n // dialog: `deleteStep` is also reached from the canvas keydown handler\n // below, so the confirmation has to be state + a sibling modal.\n // `deleteStep` only *requests* the delete; `performDeleteStep` does it, and\n // is invoked solely from the ConfirmModal's onConfirm.\n const deleteStep = useCallback(\n (id: string) => {\n if (readOnly) return;\n setPendingDelete(id);\n },\n [readOnly],\n );\n\n const performDeleteStep = useCallback(\n (id: string) => {\n if (readOnly) return;\n const nextNodes = nodes.filter((n) => n.id !== id);\n const nextEdges = edges.filter((e) => e.source !== id && e.target !== id);\n setNodes(nextNodes);\n setEdges(nextEdges);\n if (selectedId === id) setSelectedId(null);\n if (editingId === id) setEditingId(null);\n emitChange(nextNodes, nextEdges);\n },\n [nodes, edges, setNodes, setEdges, emitChange, readOnly, selectedId, editingId],\n );\n\n // ── Auto-layout (the fourth control button) ─────────────────────────────────\n //\n // Re-flows the CURRENT canvas — including steps added and wires drawn since the\n // last save — through `relayoutNodes`, which is the same layering + the same\n // placement arithmetic `workflowToFlow` runs on first open. Not a second\n // algorithm: \"arrange\" disagreeing with \"how it opened\" is the one thing an\n // author notices immediately (D-I0-6 also pins that no layout dependency is\n // added — the layerer already exists).\n //\n // `setNodes` then `emitChange` is what makes the result PERSIST: `flowToWorkflow`\n // stamps each step with its node's rounded coordinate and the host's auto-save\n // writes it, so a reload reopens on the re-flowed graph.\n const performRelayout = useCallback(() => {\n if (readOnly) return;\n const next = relayoutNodes(nodes, edges);\n setNodes(next);\n relayoutPendingFitRef.current = true;\n emitChange(next, edges);\n }, [nodes, edges, setNodes, emitChange, readOnly]);\n\n // No confirmation (D-T2b): re-flowing is instant, deliberately with no\n // replacement gate — the intake is explicit this click should never warn. See\n // `.ai/conventions.md`'s \"No browser dialogs\" for the house pattern this is a\n // recorded exception to, not an oversight.\n const requestRelayout = useCallback(() => {\n if (readOnly) return;\n performRelayout();\n }, [readOnly, performRelayout]);\n\n // Frame the re-flowed graph once React Flow's own node-sync effect (a child of\n // this component) has committed the new positions — that child-before-parent\n // effect ordering is what makes `fitView()` here read POST-relayout bounds\n // instead of the stale pre-relayout ones. Keyed on `[nodes, fitView]` so it\n // re-checks the ref on every nodes commit, not just relayout's — `nodes`\n // itself is unread in the body, only its commit timing matters, so the\n // exhaustive-deps rule can't see why it's there.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `nodes` drives ordering, not the body\n useEffect(() => {\n if (!relayoutPendingFitRef.current) return;\n relayoutPendingFitRef.current = false;\n void fitView({ duration: 300 });\n }, [nodes, fitView]);\n\n // `fitViewKey`: one fit per change of the host's key, never on mount. The ref\n // starts at the mount value, so the first run (and StrictMode's re-run) sees\n // no change; a new `fitView` identity alone fails the same check.\n const lastFitViewKeyRef = useRef(fitViewKey);\n useEffect(() => {\n if (Object.is(lastFitViewKeyRef.current, fitViewKey)) return;\n lastFitViewKeyRef.current = fitViewKey;\n void fitView({ duration: 300 });\n }, [fitViewKey, fitView]);\n\n // ── The edge-level lane control (`Run on: Success / Error`) ─────────────────\n // Reuses the selection state that already exists; nothing new is tracked but the\n // refusal message, which renders INLINE in the panel. Never a browser dialog —\n // same rule as the delete confirm above (.ai/conventions.md · No browser dialogs).\n // Scoped to the edge it is about, NOT cleared from `onSelectionChange`: React\n // Flow re-invokes that callback after any re-render (it is an inline lambda, so\n // it re-subscribes), which clears the message before it is ever painted — a\n // refusal that renders for zero frames is the same as failing silently.\n const [laneError, setLaneError] = useState<{ edgeId: string; message: string } | null>(null);\n const selectedEdge = useMemo(\n () => edges.find((e) => e.id === selectedEdgeId) ?? null,\n [edges, selectedEdgeId],\n );\n\n const setEdgeLane = useCallback(\n (when: \"success\" | \"error\") => {\n if (readOnly || !selectedEdgeId) return;\n const me = edges.find((e) => e.id === selectedEdgeId);\n if (!me) return;\n // Choosing the lane it is already on is a no-op — don't emit a change (which\n // would mark the host's workflow dirty for nothing). `setEdgeWhen` is\n // separately safe for this, so neither layer relies on the other.\n if (edgeLane(me) === when) return;\n const conflict = edgeWhenConflict(edges, selectedEdgeId, when, nodes);\n const refusal = relaneRefusal(edges, selectedEdgeId, when, nodes, portsLookup);\n const next =\n conflict || refusal ? null : setEdgeWhen(edges, selectedEdgeId, when, nodes, portsLookup);\n if (!next) {\n if (refusal) {\n setLaneError({ edgeId: selectedEdgeId, message: outCapMessage(refusal.cap) });\n return;\n }\n // FAIL LOUDLY. The alternative is minting a duplicate edge id, which React\n // Flow's id-keyed store silently collapses — one wire disappearing from the\n // canvas with no error, indistinguishable from the editor losing the work.\n setLaneError({\n edgeId: selectedEdgeId,\n message: conflict\n ? idClashMessage(\n \"Can’t switch this edge\",\n conflict,\n clashInvolvedIds(edges, conflict, me.source, me.target),\n )\n : \"This edge can’t be switched right now.\",\n });\n return;\n }\n setLaneError(null);\n setEdges(next);\n // The id encodes the lane, so re-laning re-mints it — move the selection (and\n // therefore this panel) onto the same wire under its new id.\n const relaned = next.find(\n (e) => e.source === me.source && e.target === me.target && edgeLane(e) === when,\n );\n setSelectedEdgeId(relaned?.id ?? null);\n emitChange(nodes, next);\n },\n [edges, nodes, setEdges, emitChange, readOnly, selectedEdgeId, portsLookup],\n );\n\n const deleteEdge = useCallback(\n (id: string) => {\n if (readOnly) return;\n const nextEdges = edges.filter((e) => e.id !== id);\n if (nextEdges.length === edges.length) return;\n setEdges(nextEdges);\n if (selectedEdgeId === id) setSelectedEdgeId(null);\n emitChange(nodes, nextEdges);\n },\n [nodes, edges, setEdges, emitChange, readOnly, selectedEdgeId],\n );\n\n const duplicateStep = useCallback(\n (id: string) => {\n if (readOnly) return;\n const src = nodes.find((n) => n.id === id);\n if (!src) return;\n const newId = suggestStepId(\n nodes.map((n) => n.id),\n `${src.data.step.id}_copy`,\n );\n const cloned: FlowStep = { ...structuredClone(src.data.step), id: newId };\n const newNode: StepNode = {\n id: newId,\n type: src.type,\n position: { x: src.position.x + 40, y: src.position.y + 60 },\n data: { step: cloned, isInternal: src.data.isInternal },\n };\n const nextNodes = [...nodes, newNode];\n setNodes(nextNodes);\n setSelectedId(newId);\n emitChange(nextNodes, edges);\n },\n [nodes, edges, setNodes, emitChange, readOnly],\n );\n\n const addBuiltStep = useCallback(\n (built: BuiltStep): string | undefined => {\n if (readOnly) return undefined;\n const isInternal = isInternalApp(built.uses.app);\n // Derived from the action itself (\"data_1\", \"render_template_1\"), not\n // a generic \"gate_1\"/\"step_1\" — human override, 2026-08-30: a reader\n // who never renames a step can still orient by its id.\n // `internalNodeLabel` falls back to the bare `action` key for a\n // non-internal (registry) app, since only internal nodes carry a\n // `label` this lookup can find.\n const id = suggestStepId(\n nodes.map((n) => n.id),\n slugifyLabel(internalNodeLabel(built.uses.app, built.uses.action)),\n );\n const step: FlowStep = {\n id,\n uses: built.uses,\n ...(built.with && Object.keys(built.with).length > 0 ? { with: built.with } : {}),\n };\n const newNode: StepNode = {\n id,\n type: isInternal ? \"control\" : \"step\",\n // Drop point when spawned from a dragged connection; else a light cascade.\n position: pendingConnect?.position ?? { x: 80, y: 80 + nodes.length * 24 },\n data: { step, isInternal },\n };\n const nextNodes = [...nodes, newNode];\n\n // Auto-wire the edge back to the handle the drag started from. Dragging a\n // source handle points the edge at the new node; a target handle reverses it.\n // Gated + replacing per the port rules (applyConnect) against the new node.\n let nextEdges = edges;\n if (pendingConnect) {\n const [source, target] =\n pendingConnect.handleType === \"target\"\n ? [id, pendingConnect.nodeId]\n : [pendingConnect.nodeId, id];\n // The lane captured at drag-release time (T1.1.1) — a step spawned by\n // dragging the error port onto empty canvas is wired error, one spawned\n // from the ordinary exit is wired success.\n nextEdges =\n applyConnect(source, target, nextNodes, edges, pendingConnect.lane, portsLookup) ?? edges;\n } else {\n // \"+ Step\" (no drag) — human override, 2026-08-30. A trigger\n // auto-wires to the CURRENT chain root, but ONLY when no trigger\n // exists yet anywhere in the graph; a second trigger is left\n // floating on purpose (nothing to sensibly guess — the author\n // decides if and where it feeds in). A non-trigger step prefers\n // wiring an existing UNCONNECTED trigger forward (the likely intent\n // right after adding one) over extending the current tail.\n const existingTrigger = nodes.find((n) => isTriggerApp(n.data.step.uses.app));\n if (isTriggerApp(built.uses.app)) {\n if (!existingTrigger) {\n const root = findChainRoot(nodes, edges);\n if (root)\n nextEdges =\n applyConnect(id, root.id, nextNodes, edges, \"success\", portsLookup) ?? edges;\n }\n } else {\n const unconnectedTrigger =\n existingTrigger && !edges.some((e) => e.source === existingTrigger.id)\n ? existingTrigger\n : undefined;\n const from = unconnectedTrigger ?? findChainTail(nodes, edges);\n if (from)\n nextEdges =\n applyConnect(from.id, id, nextNodes, edges, \"success\", portsLookup) ?? edges;\n }\n }\n\n setNodes(nextNodes);\n if (nextEdges !== edges) setEdges(nextEdges);\n setSelectedId(id);\n // Progressive commit (T4.1.1): the builder stays open past this first\n // commit to keep taking edits via `onDraftChange`, so closing is\n // exclusively `onClose`'s job now (wired at the render site below).\n emitChange(nextNodes, nextEdges);\n return id;\n },\n [nodes, edges, setNodes, setEdges, emitChange, readOnly, pendingConnect, portsLookup],\n );\n\n // Apply an edit to a step, rewiring edges if its id changed.\n const updateStep = useCallback(\n (prevId: string, next: FlowStep) => {\n const idChanged = next.id !== prevId;\n if (idChanged && nodes.some((n) => n.id === next.id)) return; // reject dup id\n const nextNodes = nodes.map((n) =>\n n.id === prevId\n ? {\n ...n,\n id: next.id,\n data: { step: next, isInternal: isInternalApp(next.uses.app) },\n }\n : n,\n );\n if (idChanged) {\n // Re-point the endpoints AND re-mint each id in the edge's own lane. This\n // used to rebuild every id unqualified, dropping an error edge's `:error`\n // suffix so a same-target success+error pair collapsed into one edge in\n // React Flow's id-keyed store. The lane-aware rewrite lives in\n // `flow-connect.ts` and mints ids through the one shared `flowEdgeId`\n // helper — never a second copy of the template.\n const nextEdges = renameStepInEdges(edges, prevId, next.id);\n setNodes(nextNodes);\n setEdges(nextEdges);\n if (selectedId === prevId) setSelectedId(next.id);\n if (editingId === prevId) setEditingId(next.id);\n emitChange(nextNodes, nextEdges);\n return;\n }\n setNodes(nextNodes);\n emitChange(nextNodes, edges);\n },\n [nodes, edges, setNodes, setEdges, selectedId, editingId, emitChange],\n );\n\n // Remember the values a Run was made with, then record the run AGAINST that\n // fixture — the same two-call sequence the Test path uses (`StepTestRun` in\n // StepBuilderModal), so a Run and a Test write the same kind of `step_tests`\n // row for the same step and each surface pre-fills from whichever ran last.\n //\n // The `stepTestId` is the load-bearing half: `recordStepTestRun` only updates\n // the fixture's `last_run_*` columns when it is present, and this call used to\n // post without one — so a canvas run was logged but never written back onto\n // the fixture. Best-effort throughout: a persist failure is logged and never\n // surfaces as a failed run.\n //\n // The two calls fail INDEPENDENTLY, and deliberately so: remembering the\n // values and recording that the run happened are different obligations, and\n // only the first can fail on its own (`POST …/tests` 404s on an unknown\n // workflow, `POST …/test-runs` does not). Folding both into one `try` would\n // make a fixture-save failure swallow the run's `step_test_runs` row *and* the\n // `run_log` ledger row the server writes per POST — i.e. silently lose run\n // history over a persistence hiccup. So a failed save degrades to\n // `stepTestId: null`: a run recorded with no fixture attached, which is\n // exactly what the route already accepts (step-test-runs.ts).\n const persistStepRun = useCallback(\n (\n stepId: string,\n fixture: { input: Record<string, unknown>; with: Record<string, unknown> },\n outcome: { status: string; output?: unknown; error?: unknown; meta?: StepMeta },\n ) => {\n void (async () => {\n const saved = await api.saveStepTest(value.id, stepId, fixture).catch((err) => {\n console.error(\"step run fixture save failed\", err);\n return null;\n });\n await api.recordStepTestRun(value.id, stepId, {\n stepTestId: saved?.id ?? null,\n status: outcome.status,\n input: fixture.input,\n output: outcome.output,\n error: outcome.error,\n ...(outcome.meta ? { meta: outcome.meta } : {}),\n });\n })().catch((err) => {\n console.error(\"step run persist failed\", err);\n });\n },\n [api, value.id],\n );\n\n // Test-running one step is two-phase, mirroring delete's request/perform split:\n // `runStep` (the ▶ control) only *opens* the collect phase, and\n // `performRunStep` — reached solely from the collect form's Run button — does\n // the invoke with the values the user just filled in. Control steps aren't\n // invocable. Signature is unchanged: `onRun` is still `(id: string) => void`.\n const runStep = useCallback(\n (id: string) => {\n const node = nodes.find((n) => n.id === id);\n // Flow-control nodes can't run standalone; app + compute/trigger nodes can.\n if (!node || isControlApp(node.data.step.uses.app)) return;\n setRunResult({ stepId: id, status: \"collect\" });\n },\n [nodes],\n );\n\n // Invoke one step through the invoke API with the properties collected in the\n // modal's collect phase, and (for action steps) its stored connection.\n //\n // `state` is the run's START STATE — what the upstream steps last produced —\n // and it is NOT params: it goes out as the invoke body's own `state` field so\n // the server can resolve `{{ steps.<id>.output.<field> }}` in the step's\n // `with`. Sending it as params would do nothing: the runtime copies only\n // DECLARED params and drops the rest (`core/runtime/src/resolve.ts`).\n const performRunStep = useCallback(\n async (id: string, values: Record<string, unknown>, state?: StepStartState) => {\n // T1.1.1: a step test starts NOW, so the host drops the run it was\n // showing before the invoke goes out — the canvas returns to editable on\n // the user's action, not on this test's eventual result.\n onTestRun?.();\n const node = nodes.find((n) => n.id === id);\n if (!node) return;\n const step = node.data.step;\n const isTrigger = isTriggerApp(step.uses.app);\n // A trigger's filled fields ARE the run's starting state: the handler\n // returns `params.input` verbatim, so anything else comes back `{}`. Every\n // other step is ALREADY CONFIGURED — `values` is the incoming state the\n // collect form asked for, merged over the step's STORED `with`, which is\n // exactly the payload the Test tab sends (`testValues`). The stored `with`\n // is read, never written: a run does not rewrite the step's configuration.\n const payload = isTrigger ? { input: values } : { ...(step.with ?? {}), ...values };\n // The fixture saved alongside the run, in the same two slots the Test tab\n // writes: `input` is the run's incoming state — for a trigger its filled\n // values *are* it, for every other step it is what the incoming-state box\n // held — and `with` is the full set of params the call was made with.\n const fixture = isTrigger\n ? { input: values, with: values }\n : { input: values, with: payload };\n setRunResult({ stepId: id, status: \"running\" });\n try {\n const result = await api.invokeAction(step.uses.app, step.uses.action, payload, {\n ...(step.uses.connection ? { connectionId: step.uses.connection } : {}),\n // Scope document expressions to the workflow's selected project (T2.1.2).\n project,\n // Omitted when nothing upstream has a saved output, so a run with no\n // seed sends exactly the request it sent before.\n ...(state ? { state } : {}),\n });\n // Script nodes may return captured console output alongside the value.\n const logs = (result as { logs?: string[] }).logs;\n const meta = result.meta;\n setRunResult({\n stepId: id,\n status: \"done\",\n value: result.value,\n logs,\n ...(meta ? { meta } : {}),\n });\n // Remember the values and log the run against the fixture they were\n // saved as (best-effort — never fail the run over it).\n persistStepRun(id, fixture, {\n status: \"succeeded\",\n output: result.value,\n ...(meta ? { meta } : {}),\n });\n } catch (e) {\n // The api client wraps network/parse failures with context; duck-type the\n // code so the modal can show it next to the message.\n const err = e as { message?: string; code?: string };\n setRunResult({\n stepId: id,\n status: \"error\",\n error: err.message ?? String(e),\n errorCode: err.code,\n });\n // A failed run still remembers what was entered — the operator's next\n // attempt starts from the values that failed, not from blank.\n persistStepRun(id, fixture, { status: \"failed\", error: err.message ?? String(e) });\n }\n },\n [nodes, api, persistStepRun, project, onTestRun],\n );\n\n const controls = useMemo<StepControls>(\n () => ({\n onEdit: (id) => {\n setEditView(\"props\");\n setEditingId(id);\n },\n onEditJson: (id) => {\n setEditView(\"json\");\n setEditingId(id);\n },\n onRun: runStep,\n onDuplicate: duplicateStep,\n onDelete: deleteStep,\n readOnly,\n }),\n [runStep, duplicateStep, deleteStep, readOnly],\n );\n\n // Opens the step editor on a canvas node double-click — deliberately NOT\n // React Flow's own node-double-click prop on `<ReactFlow>` below. That prop\n // fires from React's root container, an ANCESTOR of `.react-flow__pane` under\n // React 18's event delegation, so it always loses the race to the pane's own\n // native `dblclick.zoom` listener and can't stop it. A capture-phase listener\n // on this container fires first instead.\n //\n // The guard matters specifically in `readOnly`: `nodesDraggable={!readOnly}`\n // below drops `nopan` from the node wrapper's class list for a non-draggable\n // node, and for THIS scenario — a non-wheel double-click with no active\n // selection or connection in progress — `@xyflow/system`'s zoom filter's\n // remaining check is `.nopan` — so without this listener winning the race,\n // a viewer's double-click silently zooms the canvas instead of opening the\n // modal.\n useEffect(() => {\n const div = flowContainerRef.current;\n if (!div) return;\n const handler = (e: MouseEvent) => {\n const card = (e.target as HTMLElement).closest(\".react-flow__node\");\n if (!card) return; // not over a node — let the pane's own dblclick-zoom run\n const id = card.getAttribute(\"data-id\");\n if (!id) return;\n // CAPTURE phase (registered below) is what makes this run before the\n // pane's bubble-phase `dblclick.zoom` listener; stopping it here is what\n // actually suppresses the zoom.\n e.stopPropagation();\n // Same transition the node toolbar's pencil button performs — no new\n // plumbing, `controls` already carries `onEdit`.\n controls.onEdit(id);\n };\n div.addEventListener(\"dblclick\", handler, true);\n return () => div.removeEventListener(\"dblclick\", handler, true);\n }, [controls]);\n\n const editingStep = nodes.find((n) => n.id === editingId)?.data.step ?? null;\n // The step the run modal is open on, resolved live so the collect phase reads\n // the current `with` (a trigger's declared `fields`) rather than a snapshot.\n const runningStep = runResult\n ? (nodes.find((n) => n.id === runResult.stepId)?.data.step ?? null)\n : null;\n // Phase 1 of the run modal: collect the run's incoming state, nothing invoked\n // yet. The collect form offers the same upstream seed chips the Test tab does,\n // so it needs the RUNNING step's graph ancestors — the step editor's own\n // `upstreamState` below is computed for `editingId`, a different step.\n const collecting = runResult?.status === \"collect\" && !!runningStep;\n const runUpstreamSteps = useMemo(\n () => (runResult ? upstreamStateSources(runResult.stepId, nodes, edges).steps : []),\n [runResult, nodes, edges],\n );\n\n const fanOutCounts = useMemo(() => {\n const m = new Map<string, number>();\n for (const n of nodes) m.set(n.id, fanOutState(n.id, edges).count);\n return m;\n }, [nodes, edges]);\n\n const nodeTypes = useMemo(\n () => ({\n step: StepNodeCard,\n control: ControlNodeCard,\n }),\n [],\n );\n\n // The workflow state in scope for the step being edited: its upstream steps'\n // outputs (`steps.<id>.output`) and, if a trigger precedes it, `trigger.event`.\n //\n // Each upstream step also carries its pickable output FIELDS when a shape is\n // known (`output-shape.ts`): an app action's declared `output`, else its\n // `sample`, else that step's last test run — so a later step can reference\n // `steps.<id>.output.<field>` without running anything first.\n const [stepTestOutputs, setStepTestOutputs] = useState<Record<string, unknown>>({});\n const [stepTestMetas, setStepTestMetas] = useState<Record<string, StepMeta>>({});\n const stepShapes = useMemo<StepShapeContext>(\n () => ({\n actionDef: (app, action) => actionDefs[app]?.find((a) => a.key === action),\n testOutputs: stepTestOutputs,\n }),\n [actionDefs, stepTestOutputs],\n );\n // While the add-step builder is open `editingId` is null, which makes\n // `upstreamStateSources` offer EVERY node — including the step being drafted\n // (progressively committed as a node). Exclude it from its own rail sources.\n const [draftStepId, setDraftStepId] = useState<string | null>(null);\n const upstreamState = useMemo(() => {\n const st = upstreamStateSources(editingId, nodes, edges, stepShapes);\n return { ...st, steps: excludeSelfStep(st.steps, editingId ?? draftStepId) };\n }, [editingId, draftStepId, nodes, edges, stepShapes]);\n // A stable identity for *which* steps are upstream. The memo above rebuilds on\n // every node drag and every field edit; this string changes only when the SET\n // does, so the fetch below doesn't re-run on each keystroke.\n const upstreamIdsKey = JSON.stringify(upstreamState.steps.map((s) => s.id));\n\n // Design-time sample values for the expression editor's Result pane: what each\n // upstream step's LAST TEST RUN actually captured, flattened onto the very refs\n // the picker inserts (`steps.<id>.output` and `steps.<id>.output.<field>`). Read\n // from the saved fixtures (`StepTest.lastRunOutput`), so it survives a reload.\n //\n // ⚠️ EDITOR-SIDE ONLY, like `upstreamStateSources` above: this is a PREVIEW of\n // values a past test produced. It is not what a full run resolves — nothing here\n // is sent to the engine.\n useEffect(() => {\n const ids = JSON.parse(upstreamIdsKey) as string[];\n if (!editingId || ids.length === 0) {\n setStepTestOutputs({});\n setStepTestMetas({});\n return;\n }\n let canceled = false;\n // Best-effort, per step: a failed or empty list contributes no entry and never\n // breaks the picker — the same shape as the step editor's seed effect.\n Promise.all(\n ids.map(async (id) => {\n try {\n const tests = await api.listStepTests(value.id, id);\n // `listStepTests` is oldest-first, so the LAST entry is the most recent\n // fixture — the \"latest\" convention this file already uses twice.\n const latest = tests.length ? tests[tests.length - 1] : null;\n return latest ? { id, output: latest.lastRunOutput, meta: latest.lastRunMeta } : null;\n } catch {\n return null;\n }\n }),\n ).then((res) => {\n // LOAD-BEARING, and its failure mode is silent: without this guard a slow\n // list that resolves after the editor was closed and reopened overwrites\n // the fresh samples with the ones it read before — the pane then previews a\n // POPULATED, STALE value, which is worse than showing none. Pinned by\n // `artifacts/T5.1.3-r2-checks.sh` §X (mutant M11).\n if (canceled) return;\n const next: Record<string, unknown> = {};\n const nextMetas: Record<string, StepMeta> = {};\n for (const r of res) {\n if (r?.meta) nextMetas[r.id] = r.meta;\n if (!r || r.output === undefined || r.output === null) continue;\n next[r.id] = r.output;\n }\n setStepTestOutputs(next);\n setStepTestMetas(nextMetas);\n });\n return () => {\n canceled = true;\n };\n }, [api, value.id, editingId, upstreamIdsKey]);\n // The last test output, flattened onto the very refs the picker inserts: the\n // whole output, and each offered field path (nested ones included). Values\n // pass through unstringified — the modal's `effectiveSamples` stringifies.\n const stepSampleValues = useMemo(() => {\n const next: Record<string, unknown> = {};\n for (const st of upstreamState.steps) {\n Object.assign(next, stepMetaSampleValues(st.id, stepTestMetas[st.id]));\n const output = stepTestOutputs[st.id];\n if (output === undefined) continue;\n next[`steps.${st.id}.output`] = output;\n // Every own key of a plain object, as before…\n if (typeof output === \"object\" && output !== null && !Array.isArray(output)) {\n for (const [k, v] of Object.entries(output as Record<string, unknown>)) {\n next[`steps.${st.id}.output.${k}`] = v;\n }\n }\n // …plus each offered nested field path.\n for (const o of st.outputs ?? []) {\n if (!o.path || !o.key.includes(\".\")) continue;\n const v = valueAtPath(output, o.key);\n if (v !== undefined) next[`steps.${st.id}.output.${o.key}`] = v;\n }\n }\n return next;\n }, [upstreamState.steps, stepTestOutputs, stepTestMetas]);\n\n // …merged with the host-supplied vars/secrets/sealSecret so the expression\n // editor's left panel shows every source at once.\n //\n // `ExpressionOptionsProvider` below LAYERS over whatever this editor is\n // mounted under, so the project's vars/secrets/documents arrive on their own\n // and need not be restated here — the `exprOptions` PROP is now only for a\n // host that wants to override or add something at this level.\n //\n // `sampleValues` is the one key that must UNION rather than replace, and that\n // is why the inherited scope is read explicitly: three disjoint ref\n // namespaces contribute to it (`vars.*`/`documents.*` from the app shell,\n // anything the prop adds, and `steps.*` computed here), and layering alone\n // would let the object built below shadow the inherited one wholesale.\n const inheritedExprOptions = useExpressionOptions();\n const mergedExprOptions = useMemo<ExpressionOptions>(\n () => ({\n ...(exprOptions ?? {}),\n steps: upstreamState.steps,\n hasTrigger: upstreamState.hasTrigger,\n sampleValues: {\n ...(inheritedExprOptions.sampleValues ?? {}),\n ...(exprOptions?.sampleValues ?? {}),\n ...stepSampleValues,\n },\n }),\n [exprOptions, inheritedExprOptions.sampleValues, upstreamState, stepSampleValues],\n );\n\n return (\n <StepControlsCtx.Provider value={controls}>\n <PortsLookupCtx.Provider value={portsLookup}>\n <FanOutCountsCtx.Provider value={fanOutCounts}>\n <AppsCtx.Provider value={appsById}>\n <WorkflowProjectProvider project={project}>\n <ExpressionOptionsProvider value={mergedExprOptions}>\n <div\n ref={flowContainerRef}\n className=\"w6w-flow\"\n style={{ width: \"100%\", height, position: \"relative\" }}\n onKeyDown={(e) => {\n if (e.key !== \"Backspace\" && e.key !== \"Delete\") return;\n // Only delete the selected node/edge when the key is aimed at the\n // canvas — never while a modal is open or the user is editing a field.\n // The modal <dialog> is a DOM descendant here, so its keystrokes\n // bubble up; without this guard, backspacing a typo deletes a node.\n if (editingId || builderOpen || (!selectedId && !selectedEdgeId)) return;\n const t = e.target as HTMLElement;\n if (\n t.isContentEditable ||\n t.tagName === \"INPUT\" ||\n t.tagName === \"TEXTAREA\" ||\n t.tagName === \"SELECT\" ||\n t.closest(\"dialog, .w6w-modal\") !== null\n ) {\n return;\n }\n e.preventDefault();\n // A selected node takes precedence (its confirm); else drop the edge.\n if (selectedId) deleteStep(selectedId);\n else if (selectedEdgeId) deleteEdge(selectedEdgeId);\n }}\n >\n <ReactFlow\n nodes={nodes}\n edges={edges}\n // Straight segments with right-angle corners, not the library's\n // default bezier curve — reads clearer on a wide, branchy graph\n // than a sweeping curve does. One place: no edge object sets its\n // own `type`, so this alone governs every edge on the canvas.\n // `connectionLineType` matches it for the in-progress drag line,\n // so the preview doesn't curve while the settled edge won't.\n defaultEdgeOptions={{ type: \"smoothstep\" }}\n connectionLineType={ConnectionLineType.SmoothStep}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onConnect={onConnect}\n onConnectEnd={onConnectEnd}\n isValidConnection={isValidConnection}\n // A finished drag is what makes a new coordinate stick: until this\n // existed, dragging went only through `onNodesChange` and never\n // marked the workflow changed, so the position was lost on reload.\n // `flowToWorkflow` (inside `emitChange`) does the conversion and the\n // rounding — nothing is converted here.\n //\n // Drag STOP, never `onNodeDrag`: the per-frame variant fires on every\n // animation frame of the gesture, and the host's auto-save is a\n // trailing debounce over emitted changes, so a per-frame emit is a\n // write storm by construction.\n //\n // No `readOnly` term is needed: `nodesDraggable={!readOnly}` below\n // means React Flow never starts the drag, so this cannot fire at all\n // in a read-only editor. Measured, not assumed — the probe's readOnly\n // fixture asserts the node does not move (F4-V1).\n onNodeDragStop={() => {\n if (!savePosition) return;\n emitChange(nodes, edges);\n }}\n onMoveEnd={(event, viewport) => {\n // FIRST statement, and load-bearing. React Flow passes\n // `event === null` when the move was NOT user-initiated\n // (@xyflow/react 12.11.1, `types/component-props.d.ts` on\n // `onMoveEnd`: \"If the movement is not user-initiated, the event\n // parameter will be `null`.\"). The `fitView` on open is exactly\n // such a move — without this guard every open would emit a change,\n // and with the host's auto-save every page load would be a write.\n if (!event) return;\n // `readOnly` is checked here and NOT delegated, because — unlike a\n // drag — panning and zooming stay enabled in a read-only editor (see\n // the prop's docstring), so this handler really does fire and a\n // viewer must not write to what they are only looking at.\n if (readOnly) return;\n // `savePosition` is deliberately NOT re-checked here: `withViewport`\n // is the single place that reads the flag for this path and returns\n // its argument unchanged when it is off, so a duplicate term would be\n // unobservable — measured as a surviving mutant (T3.3.2-mutants.sh\n // C5) and removed rather than shipped as an unpinnable branch.\n //\n // Emit ONLY when `withViewport` hands back a different reference:\n // it returns `value`'s base unchanged when the rounded viewport\n // already equals the stored one, so a pan that lands back where it\n // started emits nothing. Rate is bounded by the gesture, not the\n // frame — `onMoveEnd` is d3-zoom's terminal event, and\n // `@xyflow/system` additionally coalesces scroll-driven pans behind\n // a 150ms trailing timer of its own (no timer belongs here).\n const base = flowToWorkflow(value, nodes, edges);\n const next = withViewport(base, viewport);\n if (next !== base) onChange(next);\n }}\n onSelectionChange={({ nodes: sel, edges: edgeSel }) => {\n setSelectedId(sel[0]?.id ?? null);\n setSelectedEdgeId(edgeSel[0]?.id ?? null);\n // Deliberately NOT clearing `laneError` here — see its declaration.\n }}\n nodeTypes={nodeTypes}\n nodesDraggable={!readOnly}\n nodesConnectable={!readOnly}\n elementsSelectable\n // Reopen at the camera the author left, when the workflow stores one;\n // otherwise fit the graph exactly as before. The two props are spread\n // in so only ONE of them is ever passed: they are mutually exclusive\n // by the library's own rule — \"If a default viewport is provided but\n // `fitView` is enabled, the default viewport will be ignored\"\n // (`types/component-props.d.ts` on `defaultViewport`) — and a stray\n // `fitView` would silently discard the restored view.\n {...(savedViewport ? { defaultViewport: savedViewport } : { fitView: true })}\n // Deletion is owned solely by the guarded onKeyDown handler above\n // (canvas-only, with a confirm). Disable React Flow's built-in\n // Backspace/Delete so it can't silently remove a node — e.g. while a\n // modal is open or the user is editing a field.\n deleteKeyCode={null}\n colorMode={colorMode}\n proOptions={{ hideAttribution: true }}\n >\n <Background gap={16} />\n {/* `showInteractive={false}` still suppresses the lock button; the\n child is appended AFTER the three built-ins (zoom in / zoom out /\n fit view), so auto-layout reads as the fourth control in the same\n stack — \"beside the zoom controls\", not a floating panel. It\n carries NO colour, background or border of its own: the\n `--xy-controls-button-*` → `--w6w-*` bridge in `styles.css` sets\n inherited custom properties on the `.w6w-flow` wrapper, so any\n control button added later is themed for free (measured in both\n stylesheet orders — T3.1.1's evaluation built this exact shape). */}\n <Controls showInteractive={false}>\n {!readOnly && (\n <ControlButton\n onClick={requestRelayout}\n title=\"Auto-layout — re-flow the graph into columns\"\n aria-label=\"Auto-layout the graph\"\n >\n {/* `icons.tsx`'s `layout` — the three-box hierarchy. It must NOT be a\n `fill=\"none\" stroke=…` glyph: the library styles a control button's\n svg as `svg { width: 100%; max-width: 12px; max-height: 12px;\n fill: currentColor }` (`@xyflow/react/dist/style.css`), so a stroked\n root would render as nothing at all in here — hence `layout`'s bare\n root (see its entry). */}\n <Icon name=\"layout\" />\n </ControlButton>\n )}\n </Controls>\n <MiniMap pannable zoomable style={{ background: \"var(--w6w-panel-2)\" }} />\n {!readOnly && (\n <Panel position=\"top-left\">\n <button\n type=\"button\"\n className=\"w6w-btn\"\n onClick={() => setBuilderOpen(true)}\n >\n + Step\n </button>\n </Panel>\n )}\n {/* Which outcome the selected edge carries (core rfcs/workflow.md ·\n `Edge.when`). Revealed only when exactly ONE edge is selected —\n `selectedEdgeId` is already `edgeSel[0]`, so selecting a node or\n nothing hides it. top-left is `+ Step`, Controls are bottom-left,\n the MiniMap bottom-right. D-T1-7 SUPERSEDED (T1.1.1): id'd source\n handles now exist (the error exit port), so a fresh edge's lane IS\n chosen by which handle it was dragged from — this panel keeps its\n job as the RE-LANE affordance for an edge already drawn on the\n wrong port, not the only way to author an error edge. */}\n {!readOnly && selectedEdge && (\n <Panel position=\"top-right\">\n <div className=\"w6w-edge-lane\">\n <span className=\"w6w-muted w6w-small\">Run on</span>\n {([\"success\", \"error\"] as const).map((lane) => (\n <button\n key={lane}\n type=\"button\"\n className={`w6w-btn w6w-btn-sm w6w-btn-ghost${\n edgeLane(selectedEdge) === lane ? \" active\" : \"\"\n }`}\n aria-pressed={edgeLane(selectedEdge) === lane}\n title={LANE_HINT}\n onClick={() => setEdgeLane(lane)}\n >\n {lane === \"success\" ? \"Success\" : \"Error\"}\n </button>\n ))}\n {laneError?.edgeId === selectedEdge.id && (\n <span className=\"w6w-edge-lane-err w6w-small\" role=\"alert\">\n {laneError.message}\n </span>\n )}\n </div>\n </Panel>\n )}\n </ReactFlow>\n\n {builderOpen && (\n <StepBuilderModal\n onClose={() => {\n setBuilderOpen(false);\n setPendingConnect(null);\n setDraftStepId(null);\n }}\n onAdd={(built) => {\n const id = addBuiltStep(built);\n if (id) setDraftStepId(id);\n return id;\n }}\n // Progressive commit (T4.1.1): once `addBuiltStep` has minted\n // the step's id, every subsequent field change updates that\n // same node instead of waiting for a final \"Add step\" click.\n onDraftChange={(id, next) => updateStep(id, { id, ...next })}\n workflowId={value.id}\n // The new step's known upstream ancestors, from the handle a\n // connection drag was released from (T1.1.1) — so the builder's\n // own Test tab can seed `{{ steps.<id>.output.<field> }}` the\n // same way the step editor's Test tab already does.\n upstreamSteps={stepBuilderUpstreamSteps(pendingConnect, nodes, edges)}\n />\n )}\n\n {editingStep && editingId && (\n // No `key` on purpose: renaming a step updates `editingId`, and a keyed\n // remount would drop focus mid-keystroke. The modal seeds its own state\n // once and unmounts (editingId → null) between edits of different nodes.\n <StepEditModal\n workflowId={value.id}\n step={editingStep}\n // Graph ancestors of the editing step, from `upstreamStateSources`\n // (via `mergedExprOptions`) — the incoming-state picker seeds from\n // each ancestor's latest saved step-test rather than re-walking the graph.\n upstreamSteps={mergedExprOptions.steps ?? []}\n readOnly={readOnly}\n fanOutCount={fanOutCounts.get(editingId) ?? 0}\n initialView={editView}\n onChange={(next) => updateStep(editingId, next)}\n onClose={() => setEditingId(null)}\n onTestRun={onTestRun}\n />\n )}\n\n {runResult && (\n <Modal\n title={`${collecting ? \"Run step\" : \"Test run\"}: ${runResult.stepId}`}\n onClose={() => setRunResult(null)}\n >\n {collecting && runningStep && (\n <StepRunCollect\n // Keyed on the step so switching nodes re-seeds the form.\n key={runResult.stepId}\n workflowId={value.id}\n step={runningStep}\n upstreamSteps={runUpstreamSteps}\n onCancel={() => setRunResult(null)}\n onRun={(values, state) => performRunStep(runResult.stepId, values, state)}\n />\n )}\n {/* The collect phase renders its own actions (Run + Cancel),\n gated on the required fields being filled. The status/result/log\n blocks scroll inside the body; Close stays pinned below it. */}\n {!collecting && (\n <>\n <div className=\"w6w-modal-body\">\n {runResult.status === \"running\" && (\n <p className=\"w6w-muted w6w-small\">Running…</p>\n )}\n {runResult.status === \"error\" && (\n <div className=\"w6w-result w6w-error\">\n {runResult.errorCode && (\n <div\n className=\"w6w-small\"\n style={{ opacity: 0.75, marginBottom: 4 }}\n >\n <code>{runResult.errorCode}</code>\n </div>\n )}\n {runResult.error || \"The step failed with no error message.\"}\n </div>\n )}\n {runResult.status === \"done\" && (\n <div>\n <div className=\"w6w-muted w6w-small\" style={{ marginBottom: 6 }}>\n Result\n </div>\n <pre\n className=\"w6w-result\"\n style={{\n whiteSpace: \"pre-wrap\",\n maxHeight: 360,\n overflow: \"auto\",\n margin: 0,\n }}\n >\n {JSON.stringify(runResult.value, null, 2)}\n </pre>\n {runResult.meta && (\n <>\n <div\n className=\"w6w-muted w6w-small\"\n style={{ margin: \"10px 0 6px\" }}\n >\n Metadata\n </div>\n <pre\n className=\"w6w-result\"\n style={{\n whiteSpace: \"pre-wrap\",\n maxHeight: 360,\n overflow: \"auto\",\n margin: 0,\n }}\n >\n {JSON.stringify(runResult.meta, null, 2)}\n </pre>\n </>\n )}\n </div>\n )}\n {runResult.logs && runResult.logs.length > 0 && (\n <div>\n <div\n className=\"w6w-muted w6w-small\"\n style={{ margin: \"10px 0 6px\" }}\n >\n Console output\n </div>\n <pre\n className=\"w6w-result\"\n style={{\n whiteSpace: \"pre-wrap\",\n maxHeight: 200,\n overflow: \"auto\",\n margin: 0,\n }}\n >\n {runResult.logs.join(\"\\n\")}\n </pre>\n </div>\n )}\n </div>\n <div className=\"w6w-modal-actions\">\n <button\n type=\"button\"\n className=\"w6w-btn\"\n onClick={() => setRunResult(null)}\n >\n Close\n </button>\n </div>\n </>\n )}\n </Modal>\n )}\n\n {pendingDelete !== null && (\n <ConfirmModal\n title=\"Delete step\"\n message={`Delete step \"${pendingDelete}\"? Its connections are removed too.`}\n confirmLabel=\"Delete\"\n onConfirm={() => {\n performDeleteStep(pendingDelete);\n setPendingDelete(null);\n }}\n onClose={() => setPendingDelete(null)}\n />\n )}\n </div>\n </ExpressionOptionsProvider>\n </WorkflowProjectProvider>\n </AppsCtx.Provider>\n </FanOutCountsCtx.Provider>\n </PortsLookupCtx.Provider>\n </StepControlsCtx.Provider>\n );\n}\n\n/**\n * Parse the incoming-state box into the object it stands for. Anything that is\n * not a JSON **object** — unparseable, an array, a scalar, `null` — is not a\n * state at all and reads as `null`, which the callers turn into \"merge nothing\"\n * (Test) or \"don't run\" (▶ Run, a real execution).\n */\nfunction parseStateOverride(text: string): Record<string, unknown> | null {\n try {\n const v = JSON.parse(text);\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The **incoming state** control — the one implementation behind both the step\n * editor's Test tab and the canvas ▶ Run collect form for a non-trigger step.\n * What varies between two runs of an already-configured step is the data\n * arriving at it, not its parameters, so this is what both surfaces collect.\n *\n * The raw JSON box is an **override** and is titled as one: it sits behind a\n * closed disclosure so neither surface opens on a JSON textarea, while the\n * upstream seed chips stay in plain sight above it. The disclosure opens by\n * itself whenever an override actually exists — seeded from a chip, remembered\n * from the last run, or typed — so an active override is never hidden.\n *\n * ⚠️ This component says nothing about what a **production** run resolves; it\n * collects what the test/run call is given.\n */\nfunction IncomingStateField({\n text,\n onChange,\n seeds,\n readOnly,\n}: {\n /** Raw JSON text of the override. `{}` (the default) means \"no override\". */\n text: string;\n onChange: (next: string) => void;\n seeds: SeedSource[];\n readOnly?: boolean;\n}) {\n const parsed = parseStateOverride(text);\n const keyCount = parsed ? Object.keys(parsed).length : 0;\n const hasOverride = keyCount > 0;\n const [open, setOpen] = useState(hasOverride);\n // An override usually arrives AFTER this field has mounted — the Test tab's\n // remembered-state effect (`StepEditModal`) sets the text a network hop later\n // — and `useState`'s argument is a MOUNT-TIME value that never re-derives. Do\n // not collapse this back into the initial value: with `listStepTests` delayed\n // 900 ms, that left a remembered override IN EFFECT behind a CLOSED\n // disclosure (measured in T6.1.1's evaluation; ▶ Run escaped it only because\n // it withholds the whole form until its fixture has loaded). Only the\n // \"nothing → something\" transition reopens it, so a disclosure the operator\n // closed themselves stays closed while they keep typing in it.\n useEffect(() => {\n if (hasOverride) setOpen(true);\n }, [hasOverride]);\n // Seeding from an ancestor uses the output its last run captured; only a\n // fixture that never ran falls back to its own captured incoming state.\n const seedFrom = (test: StepTest) => {\n onChange(JSON.stringify(test.lastRunOutput ?? test.input ?? {}, null, 2));\n setOpen(true);\n };\n return (\n <div className=\"w6w-field w6w-incoming-state\">\n <span>Incoming state</span>\n {seeds.length > 0 && (\n <div className=\"w6w-seed-picker\">\n <div className=\"w6w-seed-chips\">\n {seeds.map((s) => (\n <button\n key={s.stepId}\n type=\"button\"\n className=\"w6w-chip w6w-seed-chip\"\n title={`Use ${s.label}'s saved test as the incoming state`}\n disabled={readOnly}\n onClick={() => seedFrom(s.test)}\n >\n <code>{s.label}</code>\n {s.test.lastRunStatus ? (\n <span className=\"w6w-muted w6w-small\">\n {\" · \"}\n {s.test.lastRunStatus}\n </span>\n ) : null}\n </button>\n ))}\n </div>\n </div>\n )}\n <details\n className=\"w6w-state-override\"\n open={open}\n onToggle={(e) => setOpen(e.currentTarget.open)}\n >\n <summary>\n Override the incoming state\n {keyCount > 0 ? ` · ${keyCount} key${keyCount === 1 ? \"\" : \"s\"} set` : \"\"}\n </summary>\n <textarea\n rows={3}\n value={text}\n readOnly={readOnly}\n spellCheck={false}\n aria-label=\"Incoming state override (JSON)\"\n onChange={(e) => onChange(e.target.value)}\n />\n <span className=\"w6w-hint\">\n JSON merged into the call (e.g. a script's <code>input</code>). Reach for this to test\n against a state the flow cannot currently produce — a field no upstream step returns yet,\n or a case you can't reproduce by running the flow.\n </span>\n </details>\n </div>\n );\n}\n\n/**\n * Phase 1 of the canvas ▶ run: **collect the run's incoming state**, then hand\n * it to the host to invoke. Rendered inside the run modal, which is the chrome.\n *\n * Two shapes, because the two kinds of step start from different things:\n * - a **trigger** has no incoming state — it *is* the start — so it projects\n * its configured `fields` *definitions* into params (`fieldsToParams`), seeds\n * from their declared defaults (`seedValues`) and renders them through\n * {@link PropertyEntryForm} (chrome-less by contract, so it is mounted bare\n * here). The filled values become the run's starting state (`{ input }`), and\n * the step's **last saved values** come back pre-filled on top (T4.1.3).\n * - **everything else** is already configured, and what changes between two\n * runs is the data arriving at it. So it collects exactly what the Test tab\n * collects — {@link IncomingStateField}, the same component — and runs the\n * step with its **stored** `with` underneath. The configuration is not\n * re-collected here and is never rewritten by a run (R-I-1: the ▶ modal used\n * to render the Configure form).\n *\n * The trigger check comes first on purpose: a trigger *is* an internal node, and\n * its internal schema is the `fields` **editor**, not the fields themselves.\n *\n * `params` is fetched on **both** paths — a trigger renders them, and a\n * non-trigger needs them for the same required-params gate the Test tab applies\n * to `{...step.with, ...state}`. An app action fetches its action definition\n * exactly as `StepEditModal` does, showing \"Loading parameters…\" until it lands.\n */\nfunction StepRunCollect({\n workflowId,\n step,\n upstreamSteps,\n onRun,\n onCancel,\n}: {\n /** Fixture key, with `step.id` — where the remembered values are read from. */\n workflowId: string;\n step: FlowStep;\n /** Graph ancestors of this step; their saved tests are offered as seeds. */\n upstreamSteps: ExpressionStepSource[];\n /**\n * Fired when the user presses Run — with the trigger's filled values, or (for\n * every other step) the incoming-state override alone, plus the start state\n * seeded from the upstream steps' saved outputs (so the step's own\n * `{{ steps.<id>.output.<field> }}` references resolve). The two are\n * different things and stay separate: `values` are params, `state` is scope.\n */\n onRun: (values: Record<string, unknown>, state?: StepStartState) => void;\n onCancel: () => void;\n}) {\n const api = useW6WApi();\n const isTrigger = isTriggerApp(step.uses.app);\n const isInternal = isInternalApp(step.uses.app);\n const defs = useMemo(\n () => (isTrigger ? asFieldDefs(step.with?.fields) : []),\n [isTrigger, step.with],\n );\n\n // Known synchronously for triggers and internal nodes; `null` means \"an app\n // action, still to be fetched\" so only that path shows a loading line.\n const localParams = useMemo<ActionParam[] | null>(() => {\n if (isTrigger) return fieldsToParams(defs);\n if (isInternal) return internalNodeParams(step.uses.app, step.uses.action);\n return null;\n }, [isTrigger, isInternal, defs, step.uses.app, step.uses.action]);\n const [fetched, setFetched] = useState<ActionParam[] | null>(null);\n const params = localParams ?? fetched;\n\n // Mirrors StepEditModal's \"refetch actions + params\" effect rather than\n // inventing a second fetch pattern.\n useEffect(() => {\n if (localParams) return;\n if (!step.uses.app || !step.uses.action) {\n setFetched([]);\n return;\n }\n let canceled = false;\n setFetched(null);\n api\n .getAppActions(step.uses.app)\n .then((acts) => {\n if (canceled) return;\n setFetched(acts.find((a) => a.key === step.uses.action)?.params ?? []);\n })\n .catch(() => !canceled && setFetched([]));\n return () => {\n canceled = true;\n };\n }, [api, localParams, step.uses.app, step.uses.action]);\n\n // THE TRIGGER PATH's value bag, seeded once from its declared defaults.\n const [values, setValues] = useState<Record<string, unknown>>(() =>\n isTrigger ? seedValues(asFieldDefs(step.with?.fields)) : {},\n );\n // EVERY OTHER STEP's collect surface: the incoming-state override, as raw\n // JSON text — the same thing the Test tab collects, in the same component.\n const [stateText, setStateText] = useState(\"{}\");\n const seeds = useSeedSources(workflowId, upstreamSteps, !isTrigger);\n // The same fixtures the chips offer, projected into the run's start state —\n // one source, so what the chips SAY is upstream is what the run is GIVEN.\n const startState = startStateFromSeeds(seeds);\n\n // …then layered with what was entered the last time this step was tested or\n // run. `listStepTests` is oldest-first, so the LAST entry is the most recent\n // fixture — the same convention the step editor's upstream-seed effect uses.\n // Which slot is remembered follows what the surface collects: a trigger's\n // filled fields live in the fixture's `with`, an ordinary step's incoming\n // state in its `input` (that is what the Test tab writes there too).\n // An `ExprValue` (`{type:\"expr\",…}`) round-trips untouched: `FxField` derives\n // expression mode from the value, so it comes back as a chip, not as text.\n const [seeded, setSeeded] = useState(false);\n useEffect(() => {\n let canceled = false;\n api\n .listStepTests(workflowId, step.id)\n .then((tests) => {\n if (canceled) return;\n const latest = tests.length ? tests[tests.length - 1] : null;\n if (isTrigger) {\n if (latest?.with) setValues((v) => ({ ...v, ...latest.with }));\n } else if (latest?.input && Object.keys(latest.input).length > 0) {\n setStateText(JSON.stringify(latest.input, null, 2));\n }\n setSeeded(true);\n })\n // Never block or break the form: no fixture (or a failed list) just leaves\n // the declared defaults in place, silently.\n .catch(() => {\n if (!canceled) setSeeded(true);\n });\n return () => {\n canceled = true;\n };\n }, [api, workflowId, step.id, isTrigger]);\n\n // A raw-JSON draft that is not a values map (unparseable, or an array/scalar/\n // `null`) never reaches `values` — the entry form holds it back and says so\n // here. Run is a REAL execution, so it must not silently run the *previous*\n // payload while the operator is looking at a different one. The same applies\n // to the incoming-state box, which is parsed by the same rule.\n const [draftValid, setDraftValid] = useState(true);\n const state = parseStateOverride(stateText);\n const validDraft = isTrigger ? draftValid : state !== null;\n // What the run will actually be given. A non-trigger runs on its STORED\n // configuration with the incoming state merged over it — byte-for-byte what\n // the Test tab sends — so the required-params gate is applied to that, not to\n // the override alone. Never a second required-check.\n const effective = isTrigger ? values : { ...(step.with ?? {}), ...(state ?? {}) };\n const canRun = !!params && seeded && validDraft && requiredParamsFilled(params, effective);\n\n return (\n // `.w6w-stepconfig` frame + body + pinned footer (D-3): this component is a\n // direct <Modal> child, so the frame's `height:100%` resolves against the\n // dialog and only the form scrolls. The `.w6w-stack` keeps its own spacing.\n <div className=\"w6w-stepconfig\">\n <div className=\"w6w-modal-body\">\n <div className=\"w6w-stack\">\n {params === null || !seeded ? (\n <p className=\"w6w-muted w6w-small\">\n {params === null ? \"Loading parameters…\" : \"Loading saved values…\"}\n </p>\n ) : isTrigger ? (\n <>\n <PropertyEntryForm\n params={params}\n values={values}\n onChange={setValues}\n onValidityChange={setDraftValid}\n />\n {params.length === 0 && (\n <span className=\"w6w-hint\">\n This trigger declares no fields — provide a sample payload to run with. It becomes\n the trigger's output state (<code>input</code>).\n </span>\n )}\n </>\n ) : (\n <>\n <p className=\"w6w-muted w6w-small\">\n Runs <code>{step.uses.action}</code> with the configuration saved on this step —\n only the incoming state changes per run. Edit the step to change how it is\n configured.\n </p>\n <IncomingStateField text={stateText} onChange={setStateText} seeds={seeds} />\n </>\n )}\n </div>\n </div>\n <div className=\"w6w-modal-actions\">\n <button type=\"button\" className=\"w6w-btn w6w-btn-ghost\" onClick={onCancel}>\n Cancel\n </button>\n {!canRun && params !== null && seeded && (\n <span className=\"w6w-muted w6w-small\">\n {!validDraft\n ? isTrigger\n ? \"The payload must be a JSON object to run.\"\n : \"The incoming state must be a JSON object to run.\"\n : isTrigger\n ? \"Fill the required fields to test.\"\n : \"This step is missing required configuration — set it on the Configure tab.\"}\n </span>\n )}\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={!canRun}\n // A trigger IS the start state, so it seeds nothing from upstream —\n // its filled fields go out as params (`{ input }`) exactly as before.\n onClick={() =>\n onRun(isTrigger ? values : (state ?? {}), isTrigger ? undefined : startState)\n }\n >\n ▶ Run\n </button>\n </div>\n </div>\n );\n}\n\n// ── Node renderers ────────────────────────────────────────────────────────\n\n/**\n * A single connection handle, drawn to distinguish a single port from a\n * **multiple**-connection (fan-in / fan-out) port: a lone port keeps React\n * Flow's default dot, while `multiple` (cardinality > 1) renders a taller,\n * segmented bar so a node that accepts several inbound edges reads differently\n * from a single-in node (see core rfcs/node-types.md · Ports & cardinality).\n *\n * `variant === \"error\"` (T1.1.1) renders the second, visually subordinate\n * source handle every step/control node's error exit uses — `id` is what\n * lets React Flow (and `laneForSourceHandle`) tell it apart from the node's\n * ordinary, unnamed exit. The ONE shared handle component for both — no\n * second handle component and no raw inline `<Handle>` at either node card's\n * call site (`building-blocks.md`'s anti-duplication callout).\n */\nfunction PortHandle({\n type,\n position,\n multiple,\n id,\n variant,\n}: {\n type: \"source\" | \"target\";\n position: Position;\n multiple: boolean;\n id?: string;\n variant?: \"error\";\n}) {\n return (\n <Handle\n type={type}\n position={position}\n id={id}\n className={\n variant === \"error\" ? \"w6w-handle-error\" : multiple ? \"w6w-handle-multi\" : undefined\n }\n style={multiple ? { height: 22, borderRadius: 3, width: 8 } : undefined}\n title={\n variant === \"error\"\n ? \"On error — drag to route this step's failure separately\"\n : multiple\n ? type === \"target\"\n ? \"Accepts multiple incoming connections\"\n : \"Multiple outgoing connections\"\n : undefined\n }\n />\n );\n}\n\nfunction NodeControls({ id, runnable }: { id: string; runnable?: boolean }) {\n const ctrl = useContext(StepControlsCtx);\n if (!ctrl || ctrl.readOnly) return null;\n return (\n <NodeToolbar position={Position.Top} className=\"w6w-node-toolbar\">\n {runnable && (\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn\"\n title=\"Test-run this step\"\n aria-label=\"Test-run this step\"\n onClick={() => ctrl.onRun(id)}\n >\n <Icon name=\"run\" size={15} />\n </button>\n )}\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn\"\n title=\"Edit\"\n aria-label=\"Edit step\"\n onClick={() => ctrl.onEdit(id)}\n >\n <Icon name=\"edit\" size={15} />\n </button>\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn\"\n title=\"Duplicate\"\n aria-label=\"Duplicate step\"\n onClick={() => ctrl.onDuplicate(id)}\n >\n <Icon name=\"copy\" size={15} />\n </button>\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn danger\"\n title=\"Delete\"\n aria-label=\"Delete step\"\n onClick={() => ctrl.onDelete(id)}\n >\n <Icon name=\"trash\" size={15} />\n </button>\n </NodeToolbar>\n );\n}\n\n/** Card badge: a text glyph per mode (not colour), only with 2+ outgoing edges in a lane. */\nfunction FanOutBadge({ step, count }: { step: FlowStep; count: number }) {\n if (count < 2) return null;\n const parallel = step.fanOut === \"parallel\";\n const label = fanOutLabel(count, parallel ? \"parallel\" : \"sequential\");\n return (\n <div className=\"w6w-fanout-badge\" role=\"img\" aria-label={label} title={label}>\n <span aria-hidden=\"true\">{parallel ? \"⇉\" : \"→\"}</span> {count}\n </div>\n );\n}\n\nfunction StepNodeCard({ id, data, selected }: NodeProps<StepNode>) {\n const step = data.step;\n const apps = useContext(AppsCtx);\n const app = apps.get(step.uses.app);\n // The human app name (fall back to the raw id when the app isn't in the list).\n const appName = app?.displayName || step.uses.app || \"—\";\n // Per-step ports (T2.3.1): a persisted `ports.in > 1` renders a multi-input handle.\n const ports = nodePortsForStep(step, useContext(PortsLookupCtx));\n // T1.1.1 — run-execution visual, RunStatus × StepStatus (A3). `data.stepStatus`\n // is stamped by `Inner`'s live-run repaint effect, never by `workflowToFlow`.\n const visual = stepNodeVisual(data.stepStatus ?? \"no-run\", data.runStatus);\n const fanBadge = <FanOutBadge step={step} count={useContext(FanOutCountsCtx).get(id) ?? 0} />;\n return (\n <div>\n <NodeControls id={id} runnable />\n <div\n data-run-status={data.stepStatus ?? \"no-run\"}\n style={{\n // `relative` so the Handles center on the CARD, not the whole node\n // (which also spans the meta line below) — keeps ports vertically centered.\n position: \"relative\",\n // Selection always wins the BORDER COLOUR (unchanged from before\n // T1.1.1 — the ternary this extends); width/style/opacity keep\n // reflecting the run status underneath so a selected running/failed\n // step is still distinguishable from a selected idle one.\n border: `${visual.borderWidth}px ${visual.borderStyle} ${\n selected ? \"var(--w6w-accent)\" : visual.borderColor\n }`,\n opacity: selected ? 1 : visual.opacity,\n background: \"var(--w6w-panel)\",\n color: \"var(--w6w-text)\",\n borderRadius: 4,\n padding: \"8px 12px\",\n minWidth: 180,\n fontSize: 13,\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n }}\n >\n {ports.in > 0 && (\n <PortHandle type=\"target\" position={Position.Left} multiple={ports.in > 1} />\n )}\n <AppIcon\n src={app?.iconSvg}\n srcDark={app?.iconSvgDark}\n brandColor={app?.brandColor}\n name={appName}\n size={28}\n />\n <div style={{ minWidth: 0 }}>\n <div style={{ fontWeight: 600 }}>{appName}</div>\n <div className=\"w6w-muted w6w-small\" style={{ marginTop: 2 }}>\n <code>{step.uses.action || \"—\"}</code>\n </div>\n </div>\n {ports.out > 0 && (\n <>\n <PortHandle type=\"source\" position={Position.Right} multiple={ports.out > 1} />\n <PortHandle\n type=\"source\"\n position={Position.Bottom}\n multiple={false}\n id={ERROR_SOURCE_HANDLE}\n variant=\"error\"\n />\n </>\n )}\n </div>\n {fanBadge}\n {/* Meta line under the card: the step id and (when known) the app version. */}\n <div\n className=\"w6w-muted\"\n style={{ marginTop: 3, fontSize: 10, opacity: 0.75, paddingLeft: 2 }}\n >\n {step.id}\n {app?.version ? ` - v${app.version}` : \"\"}\n </div>\n {/* The step's own Notes (settings → gear icon), shown here so a reader\n can associate it with the step without opening it — human override,\n 2026-08-30. Full text in `title=` for a hover, one visual line on\n the canvas itself; `.w6w-node-notes` (styles.css) truncates. */}\n {step.notes && (\n <div className=\"w6w-muted w6w-node-notes\" title={step.notes}>\n {step.notes}\n </div>\n )}\n </div>\n );\n}\n\nfunction ControlNodeCard({ id, data, selected }: NodeProps<StepNode>) {\n const step = data.step;\n const label = internalNodeLabel(step.uses.app, step.uses.action);\n const icon = internalNodeIcon(step.uses.app, step.uses.action);\n // Per-step ports (T2.3.1): triggers have no entry (0 in, 1 out); a fan-in node\n // (e.g. aggregate, or `ports.in > 1`) renders a multi-input handle.\n const ports = nodePortsForStep(step, useContext(PortsLookupCtx));\n // A trigger is the run's ENTRY, not a step that acts: it emits the start\n // payload and has no vendor call to fail, so there is no failure of its own\n // to route — no error exit port (human report, 2026-09-01). Triggers on the\n // canvas are always internal nodes (an app's own trigger binds as a\n // subscription, never as a node), so this card is the only one that needs\n // the guard.\n const isTrigger = isTriggerApp(step.uses.app);\n // T1.1.1 — same run-execution visual StepNodeCard derives; see its comment.\n const visual = stepNodeVisual(data.stepStatus ?? \"no-run\", data.runStatus);\n const fanBadge = <FanOutBadge step={step} count={useContext(FanOutCountsCtx).get(id) ?? 0} />;\n return (\n <div>\n {/* Compute/trigger nodes can be test-run; flow-control nodes cannot. */}\n <NodeControls id={id} runnable={!isControlApp(step.uses.app)} />\n <div\n data-run-status={data.stepStatus ?? \"no-run\"}\n style={{\n // `relative` so the Handles center on the CARD, not the whole node\n // (which also spans the meta line below) — keeps ports vertically centered.\n position: \"relative\",\n border: `${visual.borderWidth}px ${visual.borderStyle} ${\n selected ? \"var(--w6w-accent)\" : visual.borderColor\n }`,\n opacity: selected ? 1 : visual.opacity,\n background: \"var(--w6w-panel-2)\",\n color: \"var(--w6w-text)\",\n borderRadius: 4,\n padding: \"6px 14px 6px 8px\",\n minWidth: 140,\n fontSize: 13,\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n }}\n >\n {ports.in > 0 && (\n <PortHandle type=\"target\" position={Position.Left} multiple={ports.in > 1} />\n )}\n {icon && <InternalIcon icon={icon} />}\n <div style={{ minWidth: 0, textAlign: \"left\" }}>\n <div style={{ fontWeight: 600 }}>{label}</div>\n <div className=\"w6w-muted w6w-small\" style={{ marginTop: 2 }}>\n <code>{step.uses.action || \"—\"}</code>\n </div>\n </div>\n {ports.out > 0 && (\n <>\n <PortHandle type=\"source\" position={Position.Right} multiple={ports.out > 1} />\n {!isTrigger && (\n <PortHandle\n type=\"source\"\n position={Position.Bottom}\n multiple={false}\n id={ERROR_SOURCE_HANDLE}\n variant=\"error\"\n />\n )}\n </>\n )}\n </div>\n {fanBadge}\n {/* Meta line under the card: the step id (internal nodes carry no version). */}\n <div\n className=\"w6w-muted\"\n style={{ marginTop: 3, fontSize: 10, opacity: 0.75, paddingLeft: 2 }}\n >\n {step.id}\n </div>\n {/* The step's own Notes — see StepNodeCard's own copy for the rationale. */}\n {step.notes && (\n <div className=\"w6w-muted w6w-node-notes\" title={step.notes}>\n {step.notes}\n </div>\n )}\n </div>\n );\n}\n\n// ── Step edit modal (Form ⇄ JSON) ─────────────────────────────────────────\n\nexport function StepEditModal({\n workflowId,\n step: initialStep,\n upstreamSteps,\n onChange,\n onClose,\n readOnly,\n initialView = \"props\",\n fanOutCount = 0,\n onTestRun,\n}: {\n workflowId: string;\n step: FlowStep;\n /** Graph ancestors of this step (from `upstreamStateSources`); the incoming-state picker seeds from their saved tests. */\n upstreamSteps: ExpressionStepSource[];\n onChange: (next: FlowStep) => void;\n onClose: () => void;\n readOnly?: boolean;\n initialView?: EditView;\n /** Most outgoing edges this step has in one lane; \"Run next steps\" needs >= 2. */\n fanOutCount?: number;\n /** Fired the instant the footer Test button is clicked, before the test runs (T1.1.1). */\n onTestRun?: () => void;\n}) {\n const api = useW6WApi();\n const apps = useContext(AppsCtx);\n const [step, setStep] = useState<FlowStep>(initialStep);\n // Same shape as the add modal: Setup/Configure/Test tabs with the Configure\n // tab showing form (props) / full-step JSON (code) / params JSON\n // (params-code) / node settings (config).\n const [tab, setTab] = useState<\"setup\" | \"configure\" | \"test\">(\n initialView === \"json\" ? \"configure\" : initialView === \"settings\" ? \"configure\" : \"configure\",\n );\n const [configView, setConfigView] = useState<ConfigView>(\n initialView === \"json\" ? \"code\" : initialView === \"settings\" ? \"config\" : \"props\",\n );\n // The Test tab's own props/JSON toggle state (A4/D-7) — deliberately NOT\n // `configView`, which ranges over `\"params-code\"`/`\"config\"` too, neither of\n // which is in the Test tab's narrowed `[\"props\",\"code\"]` set. Always starts\n // on the row list.\n const [testView, setTestView] = useState<\"props\" | \"code\">(\"props\");\n // Draft text backing the \"code\" (full-step, read-only) view.\n const [codeText, setCodeText] = useState(() => stepToJson(initialStep));\n // Draft text backing the \"params-code\" (params-only, writable) view.\n const [paramsCodeText, setParamsCodeText] = useState(() => paramsToJson(initialStep));\n const [testState, setTestState] = useState(\"{}\");\n // Drives the footer \"Test\" button, which triggers the body's <StepTestRun> so\n // the run + persist logic isn't duplicated across two affordances.\n const testRunRef = useRef<StepTestRunHandle>(null);\n const [testBusy, setTestBusy] = useState(false);\n // Inline step rename (pencil next to the name). `updateStep` fixes up edges.\n const [renaming, setRenaming] = useState(false);\n const [draftId, setDraftId] = useState(step.id);\n const [renameErr, setRenameErr] = useState<string | null>(null);\n\n const [params, setParams] = useState<ActionParam[] | null>(null);\n const [actions, setActions] = useState<ActionDef[] | null>(null);\n const [conns, setConns] = useState<ConnectionSummary[] | null>(null);\n const isInternal = isInternalApp(step.uses.app);\n\n // Refetch actions + params whenever the app/action identity changes. P-3:\n // this must refetch on an APP change alone (action `\"\"` included) — an\n // early return here on a missing action left `actions` holding the\n // PREVIOUS app's list, stale-ing the Action <select> after Change (D-1).\n useEffect(() => {\n if (isInternal) {\n setParams(internalNodeParams(step.uses.app, step.uses.action));\n setActions(null);\n return;\n }\n if (!step.uses.app) {\n setActions(null);\n setParams([]);\n return;\n }\n let canceled = false;\n setParams(null);\n api\n .getAppActions(step.uses.app)\n .then((acts) => {\n if (canceled) return;\n setActions(acts);\n setParams(\n step.uses.action ? (acts.find((a) => a.key === step.uses.action)?.params ?? []) : [],\n );\n })\n .catch(() => !canceled && setParams([]));\n return () => {\n canceled = true;\n };\n }, [api, step.uses.app, step.uses.action, isInternal]);\n\n const refetchConns = useCallback(() => {\n if (isInternal || !step.uses.app) return;\n api\n .listConnectionsForApp(step.uses.app)\n .then((c) => setConns(c))\n .catch(() => setConns([]));\n }, [api, step.uses.app, isInternal]);\n\n useEffect(() => {\n if (isInternal || !step.uses.app) return;\n let canceled = false;\n api\n .listConnectionsForApp(step.uses.app)\n .then((c) => !canceled && setConns(c))\n .catch(() => !canceled && setConns([]));\n return () => {\n canceled = true;\n };\n }, [api, step.uses.app, isInternal]);\n\n const commit = useCallback(\n (next: FlowStep) => {\n setStep(next);\n onChange(next);\n },\n [onChange],\n );\n\n const changeConfigView = (v: ConfigView) => {\n if (v === \"code\") setCodeText(stepToJson(step));\n else if (v === \"params-code\") setParamsCodeText(paramsToJson(step));\n setConfigView(v);\n };\n const commitRename = () => {\n const id = draftId.trim();\n const err = id && id !== step.id ? stepIdError(id) : null;\n if (err) {\n setRenameErr(err);\n return;\n }\n if (id && id !== step.id) commit({ ...step, id });\n setRenameErr(null);\n setRenaming(false);\n };\n\n const testable = !!step.uses.app && !!step.uses.action && !isControlApp(step.uses.app);\n // A manual/webhook trigger's Test tab fills its configured `fields` into\n // `{ input }` (via TriggerFillForm) rather than running the raw config.\n const isTrigger = isTriggerApp(step.uses.app);\n // A4/D-7: the tabs-bar toggle is enabled and narrowed to props/code ONLY on\n // this arm — the non-trigger, testable Test tab, the one that mounts\n // `<ResolvedParams>`. The trigger arm keeps the dim four (`PropertyEntryForm`\n // renders its own toggle inside `TriggerFillForm`'s body); Setup/Configure\n // are unchanged.\n const isTestPropsCode = tab === \"test\" && testable && !isTrigger;\n\n // The editing step's graph ancestors that carry a saved step-test, offered as\n // one-click seeds for the incoming state. The SAME hook the canvas ▶ Run\n // collect form uses — one implementation, so the two surfaces cannot drift.\n const seedSources = useSeedSources(workflowId, upstreamSteps, testable && !isTrigger);\n // …and the start state the test is SENT with, from those same fixtures. This\n // is what makes `{{ steps.<id>.output.<field> }}` in this step's `with`\n // resolve to what that step last produced instead of to \"\".\n const testStartState = startStateFromSeeds(seedSources);\n\n // The incoming state this step was last tested or run with comes back (T4.1.3\n // on this surface's own noun): the fixture's `input` slot is where both\n // surfaces write it. Applied only while the box is still untouched, so a slow\n // list can never overwrite what the operator is typing.\n useEffect(() => {\n if (!testable || isTrigger) return;\n let canceled = false;\n api\n .listStepTests(workflowId, step.id)\n .then((tests) => {\n if (canceled) return;\n const latest = tests.length ? tests[tests.length - 1] : null;\n if (!latest?.input || Object.keys(latest.input).length === 0) return;\n const next = JSON.stringify(latest.input, null, 2);\n setTestState((cur) => (cur.trim() === \"{}\" ? next : cur));\n })\n // Best-effort, exactly like the seed effect: no fixture (or a failed list)\n // just leaves the box empty.\n .catch(() => {});\n return () => {\n canceled = true;\n };\n }, [api, workflowId, step.id, testable, isTrigger]);\n\n // The incoming-state override, and the payload it produces. The step's STORED\n // configuration is what runs; the override is merged over it. A box that is\n // not a JSON object overrides nothing (see `parseStateOverride`).\n const testOverride = parseStateOverride(testState);\n const testValues = { ...(step.with ?? {}), ...(testOverride ?? {}) };\n // The resolved incoming state saved alongside the fixture — the slot the ▶ Run\n // collect form reads back, and what the upstream seed chips write into.\n const testInput = testOverride ?? {};\n const canTest = !!params && requiredParamsFilled(params, testValues);\n\n // Header icon mirrors the canvas node: the app's icon for app steps, the\n // internal glyph for triggers/actions/control nodes (same as the node cards).\n const app = apps.get(step.uses.app);\n const internalIcon = isInternal ? internalNodeIcon(step.uses.app, step.uses.action) : null;\n const titleIcon = isInternal ? (\n internalIcon ? (\n <InternalIcon icon={internalIcon} />\n ) : null\n ) : (\n <AppIcon\n src={app?.iconSvg}\n srcDark={app?.iconSvgDark}\n brandColor={app?.brandColor}\n name={app?.displayName}\n />\n );\n\n return (\n <Modal\n ariaLabel=\"Edit step\"\n titleIcon={titleIcon}\n title={\n <span className=\"w6w-step-rename\">\n {renaming && !readOnly ? (\n <input\n // biome-ignore lint/a11y/noAutofocus: rename input opened on demand\n autoFocus\n value={draftId}\n onChange={(e) => {\n setDraftId(e.target.value);\n setRenameErr(null);\n }}\n onBlur={commitRename}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") commitRename();\n if (e.key === \"Escape\") {\n setDraftId(step.id);\n setRenameErr(null);\n setRenaming(false);\n }\n }}\n />\n ) : (\n <>\n <code>{step.id}</code>\n {!readOnly && (\n <button\n type=\"button\"\n className=\"w6w-icon-btn w6w-btn-sm\"\n title=\"Rename step\"\n aria-label=\"Rename step\"\n onClick={() => {\n setDraftId(step.id);\n setRenaming(true);\n }}\n >\n <Icon name=\"edit\" size={15} />\n </button>\n )}\n </>\n )}\n {renameErr && (\n <span className=\"w6w-hint\" role=\"alert\" style={{ color: \"var(--w6w-danger)\" }}>\n {renameErr}\n </span>\n )}\n </span>\n }\n onClose={onClose}\n size=\"wide\"\n >\n <div className=\"w6w-stepconfig\">\n <div className=\"w6w-tabsbar\">\n <div className=\"w6w-subtabs\">\n {([\"setup\", \"configure\", \"test\"] as const).map((t) => (\n <button\n key={t}\n type=\"button\"\n className={`w6w-subtab${tab === t ? \" active\" : \"\"}`}\n onClick={() => setTab(t)}\n >\n {t === \"setup\" ? \"Setup\" : t === \"configure\" ? \"Configure\" : \"Test\"}\n </button>\n ))}\n </div>\n {isTestPropsCode ? (\n <ConfigViewToggle\n view={testView}\n views={[\"props\", \"code\"]}\n onChange={(v) => setTestView(v as \"props\" | \"code\")}\n />\n ) : (\n <ConfigViewToggle\n view={configView}\n onChange={changeConfigView}\n disabled={tab !== \"configure\"}\n />\n )}\n </div>\n\n <div className=\"w6w-stepconfig-body\">\n {tab === \"setup\" && (\n <SetupTab\n step={step}\n app={app}\n actions={actions}\n conns={conns}\n isInternal={isInternal}\n readOnly={readOnly}\n onChangeAction={(action) =>\n commit({ ...step, uses: { ...step.uses, action }, with: {} })\n }\n onChangeConnection={(connection) =>\n commit({ ...step, uses: { ...step.uses, connection } })\n }\n onConnectionCreated={refetchConns}\n onChangeApp={(appId) =>\n // D-1: a genuinely different app clears `uses.action`/`with` and\n // drops the connection; reselecting the SAME app is a no-op —\n // still committed (so the field always collapses cleanly), but\n // with `step` itself, unchanged.\n commit(\n appId === step.uses.app\n ? step\n : {\n ...step,\n uses: { app: appId, action: \"\", connection: undefined },\n with: {},\n },\n )\n }\n />\n )}\n\n {tab === \"configure\" && (\n <>\n {step.uses.app === WEBHOOK_APP &&\n api.listSubscriptionsForWorkflow &&\n api.createSubscription && (\n <WebhookUrlPanel workflowId={workflowId} step={step} readOnly={readOnly} />\n )}\n {params === null ? (\n <p className=\"w6w-muted w6w-small\">Loading parameters…</p>\n ) : configView === \"props\" ? (\n step.uses.action === \"wait\" && isControlApp(step.uses.app) ? (\n <WaitEditor\n values={step.with ?? {}}\n readOnly={readOnly}\n onChange={(w) => commit({ ...step, with: w })}\n />\n ) : (\n <ParamsForm\n params={params}\n values={step.with ?? {}}\n readOnly={readOnly}\n onChange={(w) => commit({ ...step, with: w })}\n />\n )\n ) : configView === \"code\" ? (\n // Full step, read-only (D-3) — `stepToJson` is the ONE serializer,\n // shared with the two other code-view hosts.\n <JsonEditor\n value={codeText}\n onChange={() => {}}\n readOnly\n minHeight=\"260px\"\n height=\"100%\"\n aria-label={`Step ${step.id} JSON`}\n copyable\n />\n ) : configView === \"params-code\" ? (\n <JsonEditor\n value={paramsCodeText}\n onChange={setParamsCodeText}\n readOnly={readOnly}\n minHeight=\"260px\"\n height=\"100%\"\n aria-label={`Step ${step.id} params JSON`}\n copyable\n onValidChange={(p) =>\n p &&\n typeof p === \"object\" &&\n !Array.isArray(p) &&\n commit({ ...step, with: p as Record<string, unknown> })\n }\n />\n ) : (\n <div className=\"w6w-stack\">\n <NodeConfigForm\n config={{ retry: step.retry, onError: step.onError, notes: step.notes }}\n onChange={(c) => commit({ ...step, ...c })}\n readOnly={readOnly}\n // A trigger has no failure of its own to retry or police —\n // same reason its card renders no error exit port.\n failureHandling={!isTrigger}\n // A `@w6w/call`/`@w6w/control` step runs no retry loop of\n // its own (core rfcs/workflow.md \"Retries come first\"; D-3).\n retryControls={!isControlApp(step.uses.app) && step.uses.app !== CALL_APP}\n />\n {SHOW_STEP_PORTS && (\n <StepPortsControl step={step} readOnly={readOnly} onChange={commit} />\n )}\n <StepFanOutControl\n step={step}\n count={fanOutCount}\n readOnly={readOnly}\n onChange={commit}\n />\n </div>\n )}\n </>\n )}\n\n {tab === \"test\" && (\n <div className=\"w6w-stack\">\n {testable ? (\n isTrigger ? (\n <TriggerFillForm\n app={step.uses.app}\n action={step.uses.action}\n fields={step.with?.fields}\n // Same fixture key the non-trigger Test path passes to\n // <StepTestRun> below, so the values entered here are\n // remembered and come back on the canvas ▶ Run form.\n persist={{ workflowId, stepId: step.id, input: testInput }}\n />\n ) : (\n <>\n <p className=\"w6w-muted w6w-small\">\n Tests <code>{step.uses.action}</code> with the configuration saved on this\n step — only the incoming state changes per run.\n </p>\n <IncomingStateField\n text={testState}\n onChange={setTestState}\n seeds={seedSources}\n readOnly={readOnly}\n />\n {/* What will actually be submitted, resolved against the\n incoming state above — `testValues` (post-override),\n never `step.with` alone, so overriding the incoming\n state visibly updates these rows. */}\n {params === null ? (\n <p className=\"w6w-muted w6w-small\">Loading parameters…</p>\n ) : (\n <ResolvedParams\n params={params}\n values={testValues}\n testStartState={testStartState}\n view={testView}\n />\n )}\n <StepTestRun\n ref={testRunRef}\n app={step.uses.app}\n action={step.uses.action}\n connectionId={step.uses.connection ?? undefined}\n values={testValues}\n state={testStartState}\n canRun={canTest}\n hideRunButton\n persist={{ workflowId, stepId: step.id, input: testInput }}\n onBusyChange={setTestBusy}\n />\n </>\n )\n ) : (\n <p className=\"w6w-muted w6w-small\">\n Flow-control nodes can't be tested on their own.\n </p>\n )}\n </div>\n )}\n </div>\n\n <div className=\"w6w-modal-actions w6w-stepconfig-footer\">\n {tab !== \"test\" ? (\n <button\n type=\"button\"\n className=\"w6w-btn\"\n onClick={() => setTab(tab === \"setup\" ? \"configure\" : \"test\")}\n >\n Next →\n </button>\n ) : (\n <>\n <button type=\"button\" className=\"w6w-btn w6w-btn-ghost\" onClick={onClose}>\n Done\n </button>\n {testable && !isTrigger && (\n // Runs the step and persists the outcome (records a run + saves\n // the fixture) via the body's <StepTestRun>, so a step test is\n // saved and re-runnable. `readOnly` viewers can't write tests.\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={readOnly || !canTest || testBusy}\n onClick={() => {\n // T1.1.1: before the run, so the host clears whatever run\n // the canvas was showing on the click itself.\n onTestRun?.();\n testRunRef.current?.run();\n }}\n >\n {testBusy ? \"Testing…\" : \"Test\"}\n </button>\n )}\n </>\n )}\n </div>\n </div>\n </Modal>\n );\n}\n\n/** Default in-cardinality applied when a step is opted into multiple inbound edges. */\nconst MULTI_IN_DEFAULT = 10;\n\n/** Hidden for now (26-08-30-00-fixes item 3). Flip to `true` to restore; nothing else changes. */\nconst SHOW_STEP_PORTS = false;\n\n/**\n * Opt a step into accepting **multiple** incoming edges by setting `step.ports.in`\n * (see core rfcs/node-types.md · Ports & cardinality). Unchecking reverts to the\n * node's default (drops the persisted `ports` so an untouched step stays `1/1`).\n * Hidden for entry nodes (triggers) that declare no inbound port.\n */\nfunction StepPortsControl({\n step,\n readOnly,\n onChange,\n}: {\n step: FlowStep;\n readOnly?: boolean;\n onChange: (next: FlowStep) => void;\n}) {\n const ports = nodePortsForStep(step, useContext(PortsLookupCtx));\n // Nothing flows into a node with no entry port (e.g. a trigger).\n if (ports.in < 1) return null;\n const multiple = ports.in > 1;\n return (\n <label className=\"w6w-field\" style={{ flexDirection: \"row\", alignItems: \"center\", gap: 8 }}>\n <input\n type=\"checkbox\"\n checked={multiple}\n disabled={readOnly}\n onChange={(e) => {\n if (e.target.checked) {\n onChange({ ...step, ports: { in: MULTI_IN_DEFAULT, out: ports.out } });\n } else {\n const { ports: _drop, ...rest } = step;\n onChange(rest);\n }\n }}\n />\n <span>Accept multiple incoming connections</span>\n </label>\n );\n}\n\n/** \"Run next steps: In sequence / In parallel\" — only meaningful with 2+ outgoing edges in a lane. */\nfunction StepFanOutControl({\n step,\n count,\n readOnly,\n onChange,\n}: {\n step: FlowStep;\n count: number;\n readOnly?: boolean;\n onChange: (next: FlowStep) => void;\n}) {\n const enabled = count >= 2;\n const mode = step.fanOut === \"parallel\" ? \"parallel\" : \"sequential\";\n return (\n <label className=\"w6w-field\">\n <span>Run next steps</span>\n <select\n aria-label=\"Run next steps\"\n value={mode}\n disabled={readOnly || !enabled}\n onChange={(e) => onChange(setFanOut(step, e.target.value as \"sequential\" | \"parallel\"))}\n >\n <option value=\"sequential\">In sequence</option>\n <option value=\"parallel\">In parallel</option>\n </select>\n {!enabled && (\n <span className=\"w6w-muted w6w-small\">\n Connect two or more steps after this one to choose how they run.\n </span>\n )}\n </label>\n );\n}\n\n/**\n * \"Webhook URL\" panel (I-2) — rendered only for a `WEBHOOK_APP` step, above\n * the ParamsForm/JsonEditor arms. Get-or-create against the Subscription\n * mechanism (rfcs/trigger.md), per DECISIONS.md HITL-1 + plan.md D-5:\n * - on open, lists this workflow's Subscriptions ONCE and takes the first\n * `appId === WEBHOOK_APP` entry — never re-derived client-side\n * (gap-analysis-I2 §1's `SubscriptionsPage.tsx:24-28` correctness bug is\n * deliberately not reproduced here; the server's own `webhookUrl` is the\n * only source).\n * - if one exists, its URL renders through `Copyable` exactly as\n * `TenantSettingsPage.tsx:219-227` (full URL, readOnly, aria-label) — no\n * create call.\n * - if none exists, a labelled button creates one with the step's own\n * current `with` as the Subscription's `params`, then renders the result.\n * `sub_…` ids are server-minted, so this never re-fires on its own — only\n * an explicit click creates, and only once a subscription is confirmed\n * absent.\n */\nfunction WebhookUrlPanel({\n workflowId,\n step,\n readOnly,\n}: {\n workflowId: string;\n step: FlowStep;\n readOnly?: boolean;\n}) {\n const api = useW6WApi();\n // `undefined` while the initial list is in flight; `null` once resolved\n // with no existing webhook subscription for this workflow.\n const [sub, setSub] = useState<SubscriptionSummary | null | undefined>(undefined);\n const [error, setError] = useState<string | null>(null);\n const [creating, setCreating] = useState(false);\n\n // Deliberately keyed on [api, workflowId] only — NOT on `step` (its `with`\n // changes on every keystroke in the panel below). Re-running this on a\n // `with` edit would defeat the \"never re-creates\" guarantee this panel\n // exists to provide; the list is a one-shot get, not a live subscription.\n useEffect(() => {\n const list = api.listSubscriptionsForWorkflow;\n if (!list) return;\n let canceled = false;\n list(workflowId)\n .then((subs) => {\n if (!canceled) setSub(subs.find((s) => s.appId === WEBHOOK_APP) ?? null);\n })\n .catch((e) => !canceled && setError((e as Error).message));\n return () => {\n canceled = true;\n };\n }, [api, workflowId]);\n\n const create = () => {\n const createSubscription = api.createSubscription;\n if (!createSubscription) return;\n setError(null);\n setCreating(true);\n createSubscription(WEBHOOK_APP, step.uses.action, {\n workflowId,\n connectionId: null,\n params: step.with ?? {},\n })\n .then((created) => {\n setCreating(false);\n setSub(created);\n })\n .catch((e) => {\n setCreating(false);\n setError(e instanceof Error ? e.message : String(e));\n });\n };\n\n return (\n <div className=\"w6w-stack\">\n <p className=\"w6w-muted w6w-small\">\n <strong>Webhook URL</strong> — the address a third party posts to.\n </p>\n {error && <div className=\"w6w-result w6w-error\">{error}</div>}\n {sub?.webhookUrl ? (\n <Copyable value={sub.webhookUrl} readOnly>\n <input type=\"text\" readOnly value={sub.webhookUrl} aria-label=\"Webhook URL\" />\n </Copyable>\n ) : sub === undefined ? (\n <p className=\"w6w-muted w6w-small\">Loading…</p>\n ) : (\n !readOnly && (\n <button type=\"button\" className=\"w6w-btn\" disabled={creating} onClick={create}>\n {creating ? \"Creating…\" : \"Create webhook URL\"}\n </button>\n )\n )}\n </div>\n );\n}\n\n/**\n * A small popover anchored to a trigger element, portaled to the trigger's\n * own `<dialog>` (never `document.body`) — a `showModal()` dialog and its\n * contents live in the browser's native top layer, so a portal straight to\n * `document.body` would render BEHIND the modal, not over it (2026-08-30:\n * \"it needs to be a flyout over the modal, not squeezed inside it\"). Plain\n * CSS-positioned `<div>`, not a nested `<dialog>` — the ask was explicit\n * (\"don't use native dialog, create css dialog\").\n *\n * Closes on Escape or a click outside both itself and the anchor. It does\n * NOT close on a click ON the anchor — that stays the trigger's own toggle\n * (click to open, click again to close), so the anchor button remains a\n * working close affordance the whole time the flyout is open, unlike an\n * inline \"replace the button with the picker\" approach where there is\n * nothing left to click to back out.\n */\nfunction Flyout({\n anchorRef,\n onClose,\n children,\n}: {\n // Anchored to the ROW (both App and Connection buttons together), not the\n // individual trigger button — two reasons, both from the same feedback\n // pass (2026-08-30): a flyout anchored to whichever button sits at the\n // row's right edge (Connection) could overflow past the modal's own right\n // border (\"fix anchor\"), and the app picker specifically was asked to\n // \"take up the entire horizontal width of the content... extend to the\n // end of the prod button\". Anchoring both to the row's own bounding box\n // solves both at once: the flyout's width always matches content already\n // known to fit inside the modal, and it never hangs off either edge.\n anchorRef: RefObject<HTMLElement | null>;\n onClose: () => void;\n children: ReactNode;\n}) {\n const popRef = useRef<HTMLDivElement>(null);\n const [pos, setPos] = useState<{ top: number; left: number; width: number } | null>(null);\n const [container, setContainer] = useState<HTMLElement | null>(null);\n\n useEffect(() => {\n const el = anchorRef.current;\n if (!el) return;\n const r = el.getBoundingClientRect();\n setPos({ top: r.bottom + 6, left: r.left, width: r.width });\n setContainer(el.closest(\"dialog\") ?? document.body);\n }, [anchorRef]);\n\n useEffect(() => {\n function onDocPointerDown(e: MouseEvent) {\n const target = e.target as Node;\n if (popRef.current?.contains(target)) return;\n // A click ON the anchor is the trigger's own toggle — never close here\n // AND reopen there in the same gesture.\n if (anchorRef.current?.contains(target)) return;\n onClose();\n }\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === \"Escape\") onClose();\n }\n document.addEventListener(\"mousedown\", onDocPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onDocPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [onClose, anchorRef]);\n\n if (!pos || !container) return null;\n return createPortal(\n <div\n ref={popRef}\n className=\"w6w-flyout\"\n style={{ top: pos.top, left: pos.left, width: pos.width }}\n >\n {children}\n </div>,\n container,\n );\n}\n\n/**\n * The Setup tab — an App button and a Connection button, spaced apart, then\n * the step's action (a dropdown for app steps). Two independent pickers\n * (2026-08-30, superseding D-1's single combined field — see intake for the\n * exact spec): App opens a searchable app picker; Connection opens a plain\n * list of connections FOR THE CURRENT APP plus \"+ New connection\" — no\n * search, per the ask (\"just list the connections for the same app\"). Both\n * render as an anchored `Flyout`, not inline content that pushes the rest\n * of the tab down.\n */\nfunction SetupTab({\n step,\n app,\n actions,\n conns,\n isInternal,\n readOnly,\n onChangeAction,\n onChangeConnection,\n onChangeApp,\n onConnectionCreated,\n}: {\n step: FlowStep;\n app: AppSummary | undefined;\n actions: ActionDef[] | null;\n conns: ConnectionSummary[] | null;\n isInternal: boolean;\n readOnly?: boolean;\n onChangeAction: (action: string) => void;\n onChangeConnection: (connection: string | undefined) => void;\n /** Fires with whatever app is picked, same or different — the caller owns\n * the same-app-is-a-no-op / different-app-clears-action-and-with decision\n * (it already owns the equivalent decision for `onChangeAction`). */\n onChangeApp: (appId: string) => void;\n /** Fires after a new connection is created via the inline \"+ New\n * connection\" flow, so the caller can refetch its (app-scoped) connection\n * list — `conns` here is a prop, this component owns no fetch of its own. */\n onConnectionCreated: () => void;\n}) {\n // Which flyout (if either) is open. Local, ephemeral UI state: the tab\n // unmounts on tab-switch, so it always starts closed. `AppPicker` fetches\n // and searches its own catalog; the connection picker below just maps\n // `conns` (already app-scoped by the caller) — neither needs its own fetch.\n const [mode, setMode] = useState<\"app\" | \"conn\" | null>(null);\n const [showConnModal, setShowConnModal] = useState(false);\n const rowRef = useRef<HTMLDivElement>(null);\n\n const connName = step.uses.connection\n ? ((conns ?? []).find((c) => c.id === step.uses.connection)?.displayName ??\n step.uses.connection)\n : \"No connection\";\n\n return (\n <div className=\"w6w-stack\">\n <div className=\"w6w-field\">\n <span>App</span>\n {/* One field, two buttons — `[App] <-- spacer --> [Connection]`\n (2026-08-30). Always rendered, never swapped out for the picker:\n each button IS the flyout's anchor, and stays clickable the whole\n time its own flyout is open (click again to close — the toggle\n is the only way back out short of picking something or clicking\n away, since there is no third \"cancel\" control). */}\n <div className=\"w6w-app-conn-row\" ref={rowRef}>\n <button\n type=\"button\"\n className=\"w6w-app-conn-btn\"\n aria-label=\"App\"\n aria-expanded={mode === \"app\"}\n disabled={readOnly}\n onClick={() => setMode((m) => (m === \"app\" ? null : \"app\"))}\n >\n {!isInternal && app && (\n <AppIcon\n src={app.iconSvg}\n srcDark={app.iconSvgDark}\n brandColor={app.brandColor}\n name={app.displayName}\n size={20}\n />\n )}\n <span className=\"w6w-conn-label-name\">\n {isInternal ? step.uses.app : (app?.displayName ?? step.uses.app)}\n </span>\n </button>\n {!isInternal && (\n <button\n type=\"button\"\n className=\"w6w-app-conn-btn\"\n aria-label=\"Connection\"\n aria-expanded={mode === \"conn\"}\n disabled={readOnly}\n onClick={() => setMode((m) => (m === \"conn\" ? null : \"conn\"))}\n >\n <span className=\"w6w-conn-label-name\">{connName}</span>\n </button>\n )}\n </div>\n\n {mode === \"app\" && (\n <Flyout anchorRef={rowRef} onClose={() => setMode(null)}>\n <AppPicker\n onSelectApp={(a) => {\n const sameAppReselected = a.id === step.uses.app;\n onChangeApp(a.id);\n // Same app: nothing else changed (action/config untouched, per\n // the ask) — go straight to the connection picker so they can\n // still pick a different connection. Different app: the caller\n // already wiped action/connection/with; close, exactly\n // \"start over\" on the fresh (connection-less) app.\n setMode(sameAppReselected ? \"conn\" : null);\n }}\n />\n </Flyout>\n )}\n {mode === \"conn\" && (\n <Flyout anchorRef={rowRef} onClose={() => setMode(null)}>\n <div className=\"w6w-stack\">\n <div className=\"w6w-stepbuilder-list\">\n {(conns ?? []).map((c) => (\n <button\n key={c.id}\n type=\"button\"\n className=\"w6w-stepbuilder-item\"\n onClick={() => {\n onChangeConnection(c.id);\n setMode(null);\n }}\n >\n <span className=\"w6w-stepbuilder-item-main\">\n <strong>{c.displayName || c.id}</strong>\n {c.state && <span className=\"w6w-muted w6w-small\">{c.state}</span>}\n </span>\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={readOnly}\n onClick={() => {\n setShowConnModal(true);\n setMode(null);\n }}\n >\n + New connection\n </button>\n </div>\n </Flyout>\n )}\n </div>\n\n {!isInternal && showConnModal && (\n <AddConnectionModal\n initialAppId={step.uses.app}\n onClose={() => setShowConnModal(false)}\n onCreated={({ connectionId }) => {\n setShowConnModal(false);\n onChangeConnection(connectionId);\n onConnectionCreated();\n setMode(null);\n }}\n />\n )}\n\n {isInternal ? (\n <div className=\"w6w-field\">\n <span>Action</span>\n <div className=\"w6w-muted w6w-small\">\n <code>{step.uses.action}</code>\n </div>\n </div>\n ) : (\n // biome-ignore lint/a11y/noLabelWithoutControl: Combobox renders its own <select>/<input> INSIDE this label, so the field text labels the control at runtime; the rule cannot see through the component boundary.\n <label className=\"w6w-field\">\n <span>Action</span>\n {/* While the action manifest is still loading there is nothing to pick\n from: the current action renders as the single option, so the field\n keeps showing the step's action instead of going empty. */}\n <Combobox\n value={step.uses.action}\n disabled={readOnly || actions === null}\n onChange={onChangeAction}\n options={\n actions === null\n ? [{ value: step.uses.action, label: step.uses.action }]\n : actions.map((a) => ({ value: a.key, label: a.title ?? a.key }))\n }\n />\n </label>\n )}\n </div>\n );\n}\n"],"names":["RESOLVED_VALUE_CLIP","clipResolvedValue","text","ResolvedParams","params","values","testStartState","view","sampleValues","useExpressionOptions","scope","useMemo","buildResolveScope","flat","flattenParams","effective","key","_a","p","visibleRows","isParamVisible","json","buildResolvedJson","jsx","JsonEditor","rows","deepEqual","param","ResolvedVarsRow","ResolvedParamRow","resolveParamValue","out","raw","resolveVarsJson","resolveParamJson","label","segments","jsxs","ResolvedSegments","value","row","i","Fragment","seg","ResolvedSegmentView","segment","ExprChip","ResolvedValueText","expanded","setExpanded","useState","full","formatResolvedValue","clip","e","CHIP_SIGIL","kind","refName","varLabel","edgeLane","collidingEdgeId","survivors","id","canConnect","source","target","nodes","edges","lookup","srcStep","n","tgtStep","_b","srcPorts","nodePortsForStep","tgtPorts","planConnect","when","next","toTgt","drop","flowEdgeId","addEdge","sourceHandleForLane","edgeVisuals","applyConnect","plan","connectConflict","connectRefusal","planRelane","edgeId","matches","me","others","visuals","relaned","conflict","setEdgeWhen","edgeWhenConflict","relaneRefusal","renameStepInEdges","prevId","nextId","fanOutState","stepId","per","count","setFanOut","step","mode","_drop","rest","fanOutLabel","RUN_ENDED","stepVisualState","runState","NO_RUN_VISUAL","stepNodeVisual","state","runStatus","runEnded","edgeRunState","from","exec","lane","useAppActionDefs","api","defs","setDefs","requested","useRef","appIdsKey","app","isInternalApp","useEffect","missing","res","loaded","r","prev","portsLookupFromDefs","action","d","LANE_HINT","clashInvolvedIds","conflictId","sitting","PortsLookupCtx","createContext","FanOutCountsCtx","outCapMessage","cap","AppsCtx","StepControlsCtx","WorkflowFlowEditor","props","ReactFlowProvider","Inner","onChange","readOnly","height","apps","exprOptions","project","onTestRun","fitViewKey","useW6WApi","appsById","a","runResult","setRunResult","initial","workflowToFlow","savedViewport","storedViewport","savePosition","setNodes","onNodesChange","useNodesState","actionDefs","portsLookup","setEdges","onEdgesChange","useEdgesState","nds","stepStatus","eds","run","selectedId","setSelectedId","selectedEdgeId","setSelectedEdgeId","builderOpen","setBuilderOpen","editingId","setEditingId","editView","setEditView","pendingDelete","setPendingDelete","relayoutPendingFitRef","flowContainerRef","pendingConnect","setPendingConnect","screenToFlowPosition","fitView","useReactFlow","colorMode","useEffectiveTheme","emitChange","useCallback","nextNodes","nextEdges","flowToWorkflow","isValidConnection","c","onConnect","laneForSourceHandle","refusal","holder","setLaneError","idClashMessage","onConnectEnd","event","connectionState","fromNode","handleType","originStep","point","position","_c","deleteStep","performDeleteStep","performRelayout","relayoutNodes","requestRelayout","lastFitViewKeyRef","laneError","selectedEdge","setEdgeLane","deleteEdge","duplicateStep","src","newId","suggestStepId","cloned","newNode","addBuiltStep","built","isInternal","slugifyLabel","internalNodeLabel","existingTrigger","isTriggerApp","root","findChainRoot","findChainTail","updateStep","idChanged","persistStepRun","fixture","outcome","saved","err","runStep","node","isControlApp","performRunStep","isTrigger","payload","result","logs","meta","controls","div","handler","card","editingStep","runningStep","collecting","runUpstreamSteps","upstreamStateSources","fanOutCounts","m","nodeTypes","StepNodeCard","ControlNodeCard","stepTestOutputs","setStepTestOutputs","stepTestMetas","setStepTestMetas","stepShapes","draftStepId","setDraftStepId","upstreamState","st","excludeSelfStep","upstreamIdsKey","s","ids","canceled","tests","latest","nextMetas","stepSampleValues","stepMetaSampleValues","output","k","v","o","valueAtPath","inheritedExprOptions","mergedExprOptions","WorkflowProjectProvider","ExpressionOptionsProvider","t","ReactFlow","ConnectionLineType","viewport","base","withViewport","sel","edgeSel","Background","Controls","ControlButton","Icon","MiniMap","Panel","StepBuilderModal","stepBuilderUpstreamSteps","StepEditModal","Modal","StepRunCollect","ConfirmModal","parseStateOverride","IncomingStateField","seeds","parsed","keyCount","hasOverride","open","setOpen","seedFrom","test","workflowId","upstreamSteps","onRun","onCancel","asFieldDefs","localParams","fieldsToParams","internalNodeParams","fetched","setFetched","acts","setValues","seedValues","stateText","setStateText","useSeedSources","startState","startStateFromSeeds","seeded","setSeeded","draftValid","setDraftValid","validDraft","canRun","requiredParamsFilled","PropertyEntryForm","PortHandle","type","multiple","variant","Handle","NodeControls","runnable","ctrl","useContext","NodeToolbar","Position","FanOutBadge","parallel","data","selected","appName","ports","visual","fanBadge","AppIcon","ERROR_SOURCE_HANDLE","icon","internalNodeIcon","InternalIcon","initialStep","onClose","initialView","fanOutCount","setStep","tab","setTab","configView","setConfigView","testView","setTestView","codeText","setCodeText","stepToJson","paramsCodeText","setParamsCodeText","paramsToJson","testState","setTestState","testRunRef","testBusy","setTestBusy","renaming","setRenaming","draftId","setDraftId","renameErr","setRenameErr","setParams","actions","setActions","conns","setConns","refetchConns","commit","changeConfigView","commitRename","stepIdError","testable","isTestPropsCode","seedSources","cur","testOverride","testValues","testInput","canTest","internalIcon","titleIcon","ConfigViewToggle","SetupTab","connection","appId","WEBHOOK_APP","WebhookUrlPanel","WaitEditor","w","ParamsForm","NodeConfigForm","CALL_APP","SHOW_STEP_PORTS","StepFanOutControl","TriggerFillForm","StepTestRun","enabled","sub","setSub","error","setError","creating","setCreating","list","subs","create","createSubscription","created","Copyable","Flyout","anchorRef","children","popRef","pos","setPos","container","setContainer","el","onDocPointerDown","onKeyDown","createPortal","onChangeAction","onChangeConnection","onChangeApp","onConnectionCreated","setMode","showConnModal","setShowConnModal","rowRef","connName","AppPicker","sameAppReselected","AddConnectionModal","connectionId","Combobox"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flow.cjs","sources":["../src/canvas-types.ts","../src/canvas-utils.ts","../src/components/resolved-clip.ts","../src/components/ResolvedParams.tsx","../src/flow-connect.ts","../src/flow-fanout.ts","../src/run-visuals.ts","../src/step-problems.ts","../src/use-app-action-defs.ts","../src/WorkflowFlowEditor.tsx"],"sourcesContent":["/** Workflow canvas (notes + sections); hand-mirror of core's `Workflow.canvas`. */\nexport interface CanvasNote {\n id: string;\n color: string;\n position: { x: number; y: number };\n size: { width: number; height: number };\n content: string;\n}\n\nexport interface CanvasSection {\n id: string;\n title: string;\n color: string;\n position: { x: number; y: number };\n size: { width: number; height: number };\n /** Member node ids; a node belongs to at most one section. */\n nodes: string[];\n}\n\nexport interface FlowCanvas {\n notes?: CanvasNote[];\n sections?: CanvasSection[];\n}\n\nexport const MAX_NOTES = 200;\nexport const MAX_SECTIONS = 200;\nexport const MAX_NOTE_BYTES = 10240;\n","import { type FlowCanvas, MAX_NOTES, MAX_NOTE_BYTES, MAX_SECTIONS } from \"./canvas-types.ts\";\n\n/** Live geometry keyed by note/section id. */\nexport interface LiveGeometry {\n [id: string]: { position?: { x: number; y: number }; size?: { width: number; height: number } };\n}\n\n/** Round every stored number to an integer. */\nexport function roundGeometry<\n T extends { x: number; y: number } | { width: number; height: number },\n>(g: T): T {\n const out: Record<string, number> = {};\n for (const [k, v] of Object.entries(g)) out[k] = Math.round(v as number);\n return out as T;\n}\n\ntype Pos = { x: number; y: number };\ntype Size = { width: number; height: number };\n\nconst isFinitePos = (p?: Pos): p is Pos => !!p && Number.isFinite(p.x) && Number.isFinite(p.y);\nconst isFiniteSize = (z?: Size): z is Size =>\n !!z && Number.isFinite(z.width) && Number.isFinite(z.height);\n\nfunction mapItems<T extends { id: string; position: Pos; size: Size }>(\n items: T[] | undefined,\n live: LiveGeometry | undefined,\n useLive: boolean,\n): T[] | undefined {\n if (!items) return items;\n return items.map((it) => {\n const l = useLive ? live?.[it.id] : undefined;\n const position = isFinitePos(l?.position) ? l.position : it.position;\n const size = isFiniteSize(l?.size) ? l.size : it.size;\n return { ...it, position: roundGeometry(position), size: roundGeometry(size) };\n });\n}\n\n/**\n * Stamp live geometry onto the stored canvas, rounded to integers. When\n * `savePosition === false` the stored canvas is returned untouched (mirrors\n * `flowToWorkflow`'s `withPosition`): not written is not erased.\n */\nexport function withCanvasGeometry(\n canvas: FlowCanvas,\n savePosition: boolean,\n live?: LiveGeometry,\n): FlowCanvas {\n if (!savePosition) return canvas;\n const out: FlowCanvas = { ...canvas };\n if (canvas.notes) out.notes = mapItems(canvas.notes, live, true);\n if (canvas.sections) out.sections = mapItems(canvas.sections, live, true);\n return out;\n}\n\n/** Remove a deleted node id from every section. */\nexport function pruneSectionMembers(canvas: FlowCanvas, deletedNodeId: string): FlowCanvas {\n if (!canvas.sections) return canvas;\n return {\n ...canvas,\n sections: canvas.sections.map((s) => ({\n ...s,\n nodes: s.nodes.filter((n) => n !== deletedNodeId),\n })),\n };\n}\n\n/** Map a renamed node id in every section. */\nexport function renameSectionMember(canvas: FlowCanvas, from: string, to: string): FlowCanvas {\n if (!canvas.sections) return canvas;\n return {\n ...canvas,\n sections: canvas.sections.map((s) => ({\n ...s,\n nodes: s.nodes.map((n) => (n === from ? to : n)),\n })),\n };\n}\n\n/** Put `nodeIds` into `sectionId`, removing them from any other section (at most one each). */\nexport function assignMembership(\n canvas: FlowCanvas,\n sectionId: string,\n nodeIds: string[],\n): FlowCanvas {\n if (!canvas.sections) return canvas;\n const moving = new Set(nodeIds);\n return {\n ...canvas,\n sections: canvas.sections.map((s) => {\n const kept = s.nodes.filter((n) => !moving.has(n));\n return s.id === sectionId\n ? { ...s, nodes: [...new Set([...kept, ...nodeIds])] }\n : { ...s, nodes: kept };\n }),\n };\n}\n\n/** Ids of every section containing the node. */\nexport function sectionsContaining(canvas: FlowCanvas, nodeId: string): string[] {\n return (canvas.sections ?? []).filter((s) => s.nodes.includes(nodeId)).map((s) => s.id);\n}\n\n/** Truncate to at most `max` UTF-8 bytes without splitting a codepoint. */\nfunction truncateBytes(text: string, max: number): string {\n const enc = new TextEncoder();\n if (enc.encode(text).length <= max) return text;\n let out = \"\";\n let used = 0;\n for (const ch of text) {\n const n = enc.encode(ch).length;\n if (used + n > max) break;\n out += ch;\n used += n;\n }\n return out;\n}\n\n/** Enforce the caps: 200 notes, 200 sections, 10240 UTF-8 bytes per note content. */\nexport function clampCanvas(canvas: FlowCanvas): FlowCanvas {\n const out: FlowCanvas = { ...canvas };\n if (canvas.notes) {\n out.notes = canvas.notes\n .slice(0, MAX_NOTES)\n .map((n) => ({ ...n, content: truncateBytes(n.content, MAX_NOTE_BYTES) }));\n }\n if (canvas.sections) out.sections = canvas.sections.slice(0, MAX_SECTIONS);\n return out;\n}\n\n/**\n * The canvas to emit. Geometry is stamped (rounded) from the live canvas when `savePosition` is\n * on; when off, every item already in `stored` keeps its STORED position/size (live edits render\n * but are not written — mirrors node positions). Items new this session keep their live geometry.\n */\nexport function persistCanvas(\n canvas: FlowCanvas,\n stored: FlowCanvas | undefined,\n savePosition: boolean,\n): FlowCanvas {\n const live: LiveGeometry = {};\n for (const it of [...(canvas.notes ?? []), ...(canvas.sections ?? [])]) {\n live[it.id] = { position: it.position, size: it.size };\n }\n const pin = <T extends { id: string; position: Pos; size: Size }>(items?: T[], old?: T[]) =>\n items?.map((it) => {\n const s = old?.find((o) => o.id === it.id);\n return s ? { ...it, position: s.position, size: s.size } : it;\n });\n const base: FlowCanvas = savePosition\n ? canvas\n : {\n ...canvas,\n ...(canvas.notes ? { notes: pin(canvas.notes, stored?.notes) } : {}),\n ...(canvas.sections ? { sections: pin(canvas.sections, stored?.sections) } : {}),\n };\n return withCanvasGeometry(base, savePosition, live);\n}\n","/**\n * Clipping for ONE resolved value's display text (Test tab, read-only rows).\n *\n * A resolved row is a SUMMARY of what the run will be sent, not a viewer for\n * it. A value that resolves from a document (`documents.*.body`) or a rendered\n * template is routinely tens of kilobytes of HTML, and pasting all of it inline\n * pushed the rest of the params — and the run's own result — off the screen\n * (human report, 2026-09-01). So a long value is clipped here and offered\n * behind a toggle; the complete value is always one click away in the `<>`\n * code view above, which is COMPLETE over every visible param by construction\n * (`resolved-json.ts`, D-2) and so must never be clipped itself.\n *\n * Plain `.ts`, no React — same pure-module-beside-the-component precedent\n * `history-scale.ts` sets for `HistoryTimeline.tsx`, so the rule is testable\n * without a JSX transpile.\n */\n\n/**\n * Characters shown before a value is clipped. Sized to a few lines at the\n * row's font — enough to recognize WHICH value resolved (the opening of an\n * HTML document, a subject line, a URL) without becoming the page.\n */\nexport const RESOLVED_VALUE_CLIP = 220;\n\n/** A clip decision: the text to render, and whether anything was withheld. */\nexport interface ClippedValue {\n /** What to show when collapsed — never longer than {@link RESOLVED_VALUE_CLIP}. */\n text: string;\n /** True when `text` is shorter than the input, i.e. a toggle is warranted. */\n clipped: boolean;\n /** The full value's length, for the toggle's own label. */\n length: number;\n}\n\n/**\n * Clip `text` for the collapsed row. At or under the budget the input is\n * returned byte-identically and `clipped` is false — a short value must never\n * grow a toggle, an ellipsis, or a trailing-whitespace trim it didn't ask for.\n *\n * Over the budget, the cut is a plain `slice` at the boundary with trailing\n * whitespace trimmed (so an ellipsis never floats a space away from the text).\n * No word-boundary search: these values are markup and JSON as often as prose,\n * where \"the last space\" is not a meaningful place to stop.\n */\nexport function clipResolvedValue(text: string): ClippedValue {\n if (text.length <= RESOLVED_VALUE_CLIP) {\n return { text, clipped: false, length: text.length };\n }\n return {\n text: text.slice(0, RESOLVED_VALUE_CLIP).trimEnd(),\n clipped: true,\n length: text.length,\n };\n}\n","import { useMemo, useState } from \"react\";\nimport { JsonEditor } from \"../JsonEditor.tsx\";\nimport { type DataVar, flattenParams, isParamVisible } from \"../ParamsForm.tsx\";\nimport type { StepStartState } from \"../provider.tsx\";\nimport {\n type ResolveScope,\n type ResolvedSegment,\n buildResolveScope,\n deepEqual,\n resolveParamValue,\n} from \"../resolve-params.ts\";\nimport { resolveParamJson, resolveVarsJson } from \"../resolved-json.ts\";\nimport type { ActionParam } from \"../types.ts\";\nimport { useExpressionOptions } from \"./ExpressionOptions.tsx\";\nimport { varLabel } from \"./expression-dom.ts\";\nimport { clipResolvedValue } from \"./resolved-clip.ts\";\n\nexport interface ResolvedParamsProps {\n /** The action's declared params — same list `ParamsForm` renders on Configure. */\n params: ActionParam[];\n /** What will actually be submitted: `{ ...step.with, ...override }` — `testValues`, not `step.with`. */\n values: Record<string, unknown>;\n /** The exact object the test invoke sends as `state` — `steps.*`/`trigger.*` resolve against this. */\n testStartState?: StepStartState;\n /**\n * `\"props\"` (default) renders the filtered row list; `\"code\"` renders the\n * COMPLETE resolved-values JSON (A4/A5/D-2) — every visible param, defaults\n * included. Driven by the host's own toggle state; this component never\n * mounts a toggle of its own (D-7 — `PropertyEntryForm` already owns one on\n * the trigger arm, and two side by side is a defect, not a feature).\n */\n view?: \"props\" | \"code\";\n}\n\n/**\n * Read-only sibling of `ParamsForm`: for every configured param, shows the\n * value it resolves to against the current test scope instead of an editable\n * widget. Mounts no `contentEditable`, no `ƒx` button, no input, and never\n * calls an `onChange` — this is the Test tab's \"what will actually be sent\"\n * view, not a second place to edit params.\n *\n * Reuses `flattenParams`/`isParamVisible` from `ParamsForm.tsx` (same order +\n * visibility the Configure tab uses) rather than re-flattening sections, and\n * the static chip vocabulary (`varLabel`, `.w6w-expr-chip-*`) rather than\n * `expression-dom.ts`'s DOM-painting, which is for the editable field.\n */\nexport function ResolvedParams({\n params,\n values,\n testStartState,\n view = \"props\",\n}: ResolvedParamsProps) {\n const { sampleValues } = useExpressionOptions();\n const scope = useMemo(\n () => buildResolveScope(sampleValues, testStartState),\n [sampleValues, testStartState],\n );\n const flat = flattenParams(params);\n const effective = (key: string) =>\n values[key] !== undefined ? values[key] : flat.find((p) => p.key === key)?.default;\n // Sections are layout-only containers with no value of their own (their\n // children, already flattened, carry the values) — skip rendering a row for\n // the section param itself, exactly as `ParamsForm`'s own `renderOne` does.\n // This is the \"visible\" row set D-2's code view is complete OVER — before\n // A1's unchanged-value filter, so the code view still carries a param the\n // list hides.\n const visibleRows = flat.filter((p) => p.type !== \"section\" && isParamVisible(p, effective));\n\n if (view === \"code\") {\n // Derived fresh on every render from `values` — no draft state — so\n // editing the incoming-state override above updates this text live,\n // exactly as it already updates the row list (A5).\n const json = buildResolvedJson(visibleRows, values, scope);\n return (\n <div className=\"w6w-resolved-params\">\n <JsonEditor\n value={JSON.stringify(json, null, 2)}\n onChange={() => {}}\n readOnly\n minHeight=\"260px\"\n height=\"100%\"\n aria-label=\"Resolved test values (JSON)\"\n />\n </div>\n );\n }\n\n // A1/D-1: drop a row whose effective value is deep-equal to its declared\n // default — noise the human named explicitly (an unset `Sender Name \"\"`, an\n // explicitly-false `required` boolean, an empty `{}`/array). Uniform: no\n // `required` exemption, and a key EXPLICITLY present in `with` that happens\n // to equal the default is hidden too (equality, not presence — D-1).\n const rows = visibleRows.filter((p) => !deepEqual(effective(p.key), p.default));\n\n if (visibleRows.length === 0) {\n return (\n <div className=\"w6w-resolved-params\">\n <p className=\"w6w-muted w6w-small\">This action takes no parameters.</p>\n </div>\n );\n }\n\n if (rows.length === 0) {\n return (\n <div className=\"w6w-resolved-params\">\n <p className=\"w6w-muted w6w-small\">\n Every configured value matches its default — nothing to show here. Switch to JSON to see\n the full set.\n </p>\n </div>\n );\n }\n\n return (\n // `.w6w-resolved-table` is the ONE grid this whole row list shares — each\n // `.w6w-resolved-row` below is `display: contents` and contributes its\n // two children straight into these column tracks, so every row's key\n // column lines up with every other row's.\n <div className=\"w6w-resolved-params w6w-resolved-table\">\n {rows.map((param) =>\n param.type === \"vars\" ? (\n <ResolvedVarsRow key={param.key} param={param} value={values[param.key]} scope={scope} />\n ) : (\n <ResolvedParamRow\n key={param.key}\n label={param.label ?? param.key}\n segments={resolveParamValue(\n values[param.key] !== undefined ? values[param.key] : param.default,\n scope,\n )}\n />\n ),\n )}\n </div>\n );\n}\n\n/**\n * D-2: the code view is COMPLETE over `rows` (every visible param — see the\n * caller — defaults included, the row set BEFORE A1's filter), one key per\n * `param.key`, each folded through {@link resolveParamJson}/{@link\n * resolveVarsJson} (never the schema, never raw `step.with`, never the\n * `{type:\"expr\",…}` envelope — A6's pinned serialization).\n */\nfunction buildResolvedJson(\n rows: ActionParam[],\n values: Record<string, unknown>,\n scope: ResolveScope,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const param of rows) {\n if (param.type === \"vars\") {\n const raw = Array.isArray(values[param.key])\n ? (values[param.key] as DataVar[])\n : Array.isArray(param.default)\n ? (param.default as DataVar[])\n : [];\n out[param.key] = resolveVarsJson(raw, scope);\n } else {\n out[param.key] = resolveParamJson(\n values[param.key] !== undefined ? values[param.key] : param.default,\n scope,\n );\n }\n }\n return out;\n}\n\nfunction ResolvedParamRow({ label, segments }: { label: string; segments: ResolvedSegment[] }) {\n return (\n <div className=\"w6w-resolved-row\">\n <span className=\"w6w-resolved-label\">{label}</span>\n <div className=\"w6w-resolved-value\">\n <ResolvedSegments segments={segments} />\n </div>\n </div>\n );\n}\n\n/** A `vars`-typed param: one row per configured key, each resolved independently (A5). */\nfunction ResolvedVarsRow({\n param,\n value,\n scope,\n}: {\n param: ActionParam;\n value: unknown;\n scope: ResolveScope;\n}) {\n const rows: DataVar[] = Array.isArray(value)\n ? (value as DataVar[])\n : Array.isArray(param.default)\n ? (param.default as DataVar[])\n : [];\n return (\n <div className=\"w6w-resolved-row\">\n <span className=\"w6w-resolved-label\">{param.label ?? param.key}</span>\n {rows.length === 0 ? (\n <p className=\"w6w-muted w6w-small\">None configured.</p>\n ) : (\n <div className=\"w6w-stack\">\n {rows.map((row, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: rows have no stable id (mirrors ParamsForm's VarsField)\n <div className=\"w6w-resolved-value\" key={i}>\n <code>{row.key || \"(no key)\"}</code>\n {\" → \"}\n <ResolvedSegments segments={resolveParamValue(row.value, scope)} />\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction ResolvedSegments({ segments }: { segments: ResolvedSegment[] }) {\n return (\n <>\n {segments.map((seg, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: segments are a positional, immutable render of one value's parts\n <ResolvedSegmentView key={i} segment={seg} />\n ))}\n </>\n );\n}\n\n/** One part's worth of chrome: a static chip (for anything ref-shaped) plus its outcome. */\nfunction ResolvedSegmentView({ segment }: { segment: ResolvedSegment }) {\n switch (segment.status) {\n case \"resolved\":\n return segment.ref ? (\n <>\n <ExprChip kind=\"var\" refName={segment.ref} />\n <ResolvedValueText value={segment.value} />\n </>\n ) : (\n <ResolvedValueText value={segment.value} />\n );\n case \"unresolved\":\n // Never the ref/template text as if it were the value: the chip shows\n // only the short display LABEL (never the full ref string — the same\n // \"label ≠ ref\" precedent `expression-dom.ts` documents), and the\n // outcome is a dedicated, always-non-blank badge.\n return (\n <>\n <ExprChip kind=\"var\" refName={segment.ref} />\n <span className=\"w6w-param-unresolved\">unresolved</span>\n </>\n );\n case \"not-evaluated\":\n return (\n <>\n <ExprChip kind=\"expr\" />\n <span className=\"w6w-param-unevaluated\">not evaluated</span>\n </>\n );\n case \"masked\":\n // The chip's 🔒 sigil already says \"masked\" — no separate badge needed,\n // and no ciphertext or plaintext is ever available to show here.\n return <ExprChip kind=\"secret\" refName={segment.ref} />;\n }\n}\n\n/**\n * One resolved value's text, clipped when it is long enough to take over the\n * row (`resolved-clip.ts`). A document body or a rendered template resolves to\n * tens of kilobytes of HTML, and a row that pastes all of it inline buries the\n * other params and the run's result below it (human report, 2026-09-01).\n *\n * Expanding does NOT restore the unbounded paste: the full text goes into its\n * own scrollable block (`.w6w-resolved-text-full`), so the row can never grow\n * past a few lines of the panel either way. A value inside the budget renders\n * exactly as it always did — one bare `<span>`, no toggle, no wrapper.\n */\nfunction ResolvedValueText({ value }: { value: unknown }) {\n const [expanded, setExpanded] = useState(false);\n const full = formatResolvedValue(value);\n const clip = clipResolvedValue(full);\n if (!clip.clipped) return <span>{full}</span>;\n return (\n <>\n <span className={expanded ? \"w6w-resolved-text-full\" : undefined}>\n {expanded ? full : `${clip.text}…`}\n </span>\n <button\n type=\"button\"\n className=\"w6w-resolved-more\"\n aria-expanded={expanded}\n onClick={() => setExpanded((e) => !e)}\n >\n {expanded ? \"Show less\" : `Show all (${clip.length.toLocaleString()} characters)`}\n </button>\n </>\n );\n}\n\nconst CHIP_SIGIL: Record<\"var\" | \"secret\" | \"expr\", string> = { var: \"◆\", secret: \"🔒\", expr: \"ƒ\" };\n\n/**\n * A read-only chip, visually consistent with `expression-dom.ts`'s\n * `makeChip` (same classes/sigils, same `varLabel` shortening) but built on a\n * plain `<span>` — no `contentEditable`, no remove button, no DOM painting.\n */\nfunction ExprChip({ kind, refName }: { kind: \"var\" | \"secret\" | \"expr\"; refName?: string }) {\n const label = kind === \"expr\" ? \"expr\" : varLabel(refName ?? \"\");\n return (\n <span className={`w6w-expr-chip w6w-expr-chip-${kind}`}>\n <span className=\"w6w-expr-chip-sigil\">{CHIP_SIGIL[kind]}</span>\n <span className=\"w6w-expr-chip-label\">{label}</span>\n </span>\n );\n}\n\n/**\n * A resolved value, formatted for display — never blank, even for the falsy\n * values `resolved` explicitly covers (`null`/`\"\"`/`false`/`0`): each renders\n * visibly distinct text so it never reads as \"nothing here\" the way an actual\n * blank would.\n */\nfunction formatResolvedValue(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"(not set)\";\n if (value === \"\") return '\"\"';\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n","/**\n * Connection rules for the flow canvas: which edges may be drawn, what the edge\n * set becomes once one is, and how edge ids are rewritten when a step is renamed.\n *\n * These are total, pure functions of `(source, target, nodes, edges)` and they\n * live in a **`.ts`** module on purpose. They used to sit inside\n * `WorkflowFlowEditor.tsx`, where Node's type-stripping test runner cannot reach\n * them (it cannot parse JSX), so the exit-port capacity rule — the highest-risk\n * decision in the editor's wiring — was covered only by inspection. Here it is\n * covered by `flow-connect.test.ts` (`node --test`, no new dependency). Keep this\n * file JSX-free.\n *\n * Internal to `@w6w/ui`: imported by relative path, deliberately NOT re-exported\n * from `index.ts` / `flow.ts`.\n */\nimport { type Edge, addEdge } from \"@xyflow/react\";\nimport {\n type PortsLookup,\n edgeVisuals,\n nodePortsForStep,\n sourceHandleForLane,\n} from \"./flow-types.ts\";\nimport { type StepNode, flowEdgeId } from \"./flow-utils.ts\";\n\n/**\n * Which lane a React Flow edge belongs to (core `rfcs/workflow.md` · `Edge.when`\n * amendment). **Omitted ⇒ `\"success\"`**, and read defensively: an edge that\n * predates the `data.when` stamp — or one React Flow synthesised — may carry no\n * `data` at all. The single accessor: every consumer calls this rather than\n * re-deriving the fallback, so \"absent means success\" is decided once.\n */\nexport function edgeLane(e: Edge): \"success\" | \"error\" {\n return (e.data as { when?: string } | undefined)?.when === \"error\" ? \"error\" : \"success\";\n}\n\n/**\n * A refused connection: the source's exit port is full **in this lane** and the cap\n * is finite. Drawing past a finite `out` cap is refused, never satisfied by evicting\n * an existing edge (an unbounded `\"many\"` out never refuses).\n */\nexport type OutCapRefusal = { refused: \"out_cap\"; cap: number };\n\n/**\n * The id of the edge in `survivors` that `id` would duplicate, or `null` when it is\n * free — **the one id-uniqueness test in this module**, shared by the creation path\n * ({@link planConnect}) and the re-lane path ({@link planRelane}).\n *\n * Both paths must answer the same question because `flowEdgeId` is not injective\n * over free-text step ids: a step literally named `b:error` makes `a → b` in the\n * error lane and `a → \"b:error\"` in the success lane mint ONE id. React Flow's store\n * is id-keyed, so a duplicate id is not an error — it is one of the two wires\n * silently vanishing from the canvas, which reads as the editor losing the author's\n * work. Two independent copies of this test is how one order (mark, then draw) got\n * through while the other (draw, then mark) was refused.\n *\n * Always called with the edges that **survive** eviction, never the whole set: a\n * collider the same call evicts is not a collision (see `flow-connect.test.ts`\n * case 11).\n */\nfunction collidingEdgeId(survivors: Edge[], id: string): string | null {\n return survivors.find((e) => e.id === id)?.id ?? null;\n}\n\n/**\n * The hard rules for an edge `source → target` — the ones no amount of\n * replacement can satisfy: a real, *distinct* pair (no self-loops), no duplicate\n * edge, the target accepts an entry port (blocks connecting *into* a trigger,\n * which declares `in: 0`), and the source has an exit port. Port **capacity** is\n * deliberately NOT checked here: a full single-slot port is freed by replacement\n * (see `applyConnect`), so dragging a new wire from an already-connected node\n * re-points it rather than being rejected. Used as the live `isValidConnection`.\n *\n * The duplicate-pair check is **lane-blind by design**, and that is a v1 *editor*\n * limit, not a model limit: the model permits a success edge and an error edge\n * between the same two steps (D-T1-8 widened the engine's `skippedEdges` key so\n * they cannot collide), but v1 declines to author that pair because there are no\n * id'd source handles to pick the lane from (D-T1-7).\n *\n * A colliding minted **id** is deliberately NOT rejected here, even though\n * {@link applyConnect} refuses it: this predicate is the live `isValidConnection`,\n * and a `false` here means React Flow never fires `onConnect`, so the editor never\n * gets to *say why*. That refusal is invisible (and, worse, lands in\n * `onConnectEnd`'s \"dropped on empty canvas\" branch, which opens the step builder).\n * Letting the drop through and refusing in `applyConnect` is what lets the reason\n * paint — see {@link connectConflict}.\n */\nexport function canConnect(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n lookup?: PortsLookup,\n): boolean {\n if (!source || !target || source === target) return false;\n if (edges.some((e) => e.source === source && e.target === target)) return false;\n const srcStep = nodes.find((n) => n.id === source)?.data.step;\n const tgtStep = nodes.find((n) => n.id === target)?.data.step;\n if (!srcStep || !tgtStep) return false;\n // Per-step ports (T2.3.1): a persisted `ports.in > 1` lets multiple edges land.\n const srcPorts = nodePortsForStep(srcStep, lookup);\n const tgtPorts = nodePortsForStep(tgtStep, lookup);\n return srcPorts.out >= 1 && tgtPorts.in >= 1;\n}\n\n/**\n * The plan for a new `source → target` connection: the resulting edge set, plus the\n * id of an existing edge the minted id would **collide** with (`null` when it is\n * safe). The creation-path twin of {@link planRelane}, deliberately the same shape —\n * both public entry points read off this one plan, so the eviction arithmetic and\n * the id-uniqueness test are computed once, in the right order (the collision is\n * tested against the edges that SURVIVE eviction).\n */\nfunction planConnect(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\",\n lookup?: PortsLookup,\n): { next: Edge[]; conflict: string | null; refusal: OutCapRefusal | null } | null {\n if (!canConnect(source, target, nodes, edges, lookup) || !source || !target) return null;\n const srcStep = nodes.find((n) => n.id === source)?.data.step;\n const tgtStep = nodes.find((n) => n.id === target)?.data.step;\n if (!srcStep || !tgtStep) return null;\n // Per-step ports (T2.3.1): capacity honors a persisted `ports.in`/`ports.out`,\n // so a fan-in node with `ports.in > 1` keeps prior edges instead of dropping them.\n const srcPorts = nodePortsForStep(srcStep, lookup);\n const tgtPorts = nodePortsForStep(tgtStep, lookup);\n let next = edges;\n // The source's exit port IN THIS LANE is full at a finite cap: REFUSE. Nothing is\n // evicted — `next` stays the input edge set. The other lane is untouched.\n const fromSrc = next.filter((e) => e.source === source && edgeLane(e) === when);\n if (fromSrc.length >= srcPorts.out) {\n return { next: edges, conflict: null, refusal: { refused: \"out_cap\", cap: srcPorts.out } };\n }\n // Free the target's entry port likewise — lane-blind, see the docstring.\n const toTgt = next.filter((e) => e.target === target);\n if (toTgt.length >= tgtPorts.in) {\n const drop = new Set(toTgt.slice(0, toTgt.length - tgtPorts.in + 1).map((e) => e.id));\n next = next.filter((e) => !drop.has(e.id));\n }\n // Stamp the lane at creation: a freshly drawn wire is a success edge unless the\n // caller says otherwise, and flowToWorkflow reads `data.when` back off the edge\n // (it would see `undefined` otherwise). \"success\" is emitted as ABSENT on the\n // way out. The id comes from `flowEdgeId`, so an error edge is qualified and\n // cannot collide with its success sibling. `sourceHandle` is stamped by\n // EXPLICIT assignment (T1.1.1) so the anchor follows the lane the same way\n // `id`/`data` do. `edgeVisuals(when)` (className/label) is spread here too —\n // 2026-08-30 fix: a freshly DRAWN error edge previously carried the right\n // `data.when`/`sourceHandle` (so it persisted and reloaded correctly) but\n // rendered with no red dash / \"on error\" label until the next page load,\n // because only `planRelane` (re-laning an EXISTING edge) applied it — the\n // create path never did. Same trap `planRelane`'s own comment warns about:\n // this must be an explicit spread here, not left for a later re-render.\n const id = flowEdgeId(source, target, when);\n return {\n next: addEdge(\n {\n source,\n target,\n id,\n sourceHandle: sourceHandleForLane(when),\n data: { when },\n // No run state: this mints a freshly DRAWN edge, never a run-taken\n // one — the live run-aware repaint is a separate effect\n // (WorkflowFlowEditor.tsx, T1.1.1), not this creation path.\n ...edgeVisuals(when, undefined),\n },\n next,\n ),\n conflict: collidingEdgeId(next, id),\n refusal: null,\n };\n}\n\n/**\n * Build the next edge set for a new `source → target` connection. **Additive at the\n * source**: a source under its `out` cap (or unbounded) keeps every existing edge and\n * gains one; at a finite cap the connection is **refused** (see {@link connectRefusal}),\n * never satisfied by evicting. The target's entry port is still single-slot-replacing:\n * the oldest inbound edge(s) are dropped to make room. Returns `null` when the\n * connection is disallowed by {@link canConnect}, refused at the out cap — **or when the minted id would\n * duplicate a surviving edge's id**, the same refusal {@link setEdgeWhen} performs\n * through the same {@link collidingEdgeId} test.\n *\n * That last one closes the reverse order of the `:error` collision: marking `a → b`\n * as the error lane is refused when `a → \"b:error\"` already exists, but *first*\n * marking `a → b` Error and *then* drawing `a → \"b:error\"` used to be allowed and\n * minted a duplicate id — React Flow's id-keyed store then held one wire for two\n * edges (measured `storeSize 1 / 2`). Refusing here is narrow on purpose; rejecting\n * the step id itself is the durable fix and would also have to reject ids inside\n * workflows that already exist (FOLLOWUPS.md). The caller must surface the refusal —\n * see {@link connectConflict}.\n *\n * The source's exit capacity is **per lane** (T3.2.2): success edges compete only\n * with success edges, error edges only with error edges. So a finite `out: 1`\n * step refuses a second success edge *within* a lane,\n * while additionally being able to hold one error edge — which is what makes a\n * `send → (error) → fallback` shape authorable at all. No `ports.out` bump is\n * needed, so every persisted definition and palette entry stays valid.\n *\n * The target's entry capacity stays **lane-blind**, and that asymmetry is the\n * point: a converging tail step receiving one success and one error edge is\n * governed by `ports.in` alone. Partitioning the target rule by lane too — the\n * plausible \"symmetry\" cleanup — would let a `ports.in: 1` node silently accept\n * two inbound edges.\n */\nexport function applyConnect(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\" = \"success\",\n lookup?: PortsLookup,\n): Edge[] | null {\n const plan = planConnect(source, target, nodes, edges, when, lookup);\n if (!plan || plan.conflict || plan.refusal) return null;\n return plan.next;\n}\n\n/**\n * The id of the existing edge that drawing `source → target` would collide with, or\n * `null` when the connection is safe (or refused for an ordinary reason — a\n * duplicate pair, a self-loop, a portless step — which the drag already showed the\n * user by marking the drop invalid). The creation-path twin of\n * {@link edgeWhenConflict}, and the same channel: it lets the caller *name* the\n * offending edge in an inline refusal instead of losing a wire silently. The editor\n * selects that edge and renders the message in the edge-lane panel (never a browser\n * dialog; see `.ai/conventions.md`).\n */\nexport function connectConflict(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\" = \"success\",\n lookup?: PortsLookup,\n): string | null {\n return planConnect(source, target, nodes, edges, when, lookup)?.conflict ?? null;\n}\n\n/**\n * The out-cap refusal for drawing `source → target`, or `null` when the source has\n * room (or is unbounded). The editor renders `cap` in an inline message on the same\n * channel as {@link connectConflict}; {@link applyConnect} returns `null` for it.\n */\nexport function connectRefusal(\n source: string | null | undefined,\n target: string | null | undefined,\n nodes: StepNode[],\n edges: Edge[],\n when: \"success\" | \"error\" = \"success\",\n lookup?: PortsLookup,\n): OutCapRefusal | null {\n return planConnect(source, target, nodes, edges, when, lookup)?.refusal ?? null;\n}\n\n/**\n * The plan for re-laning one edge: the resulting edge set, plus the id of an\n * existing edge it would **collide** with (`null` when it is safe).\n *\n * Both public entry points below read off this, so the eviction arithmetic and the\n * collision test are computed once, in the right order — the collision is checked\n * against the edges that SURVIVE eviction. Checking it against all edges instead\n * would refuse a legitimate switch: re-laning the error half of a same-target\n * pair back to success mints the success sibling's id, but that sibling is\n * evicted by the very same call (see `flow-connect.test.ts` case 11).\n */\nfunction planRelane(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): { next: Edge[]; conflict: string | null; refusal: OutCapRefusal | null } | null {\n // EXACTLY ONE edge must own `edgeId`. None ⇒ nothing to re-lane. Two or more ⇒ the\n // edge set is already corrupt (a step id containing `->` mints a duplicate id —\n // FOLLOWUPS.md), and both the lookup here and the replacement below match BY ID,\n // so proceeding would rewrite EVERY match: the second edge's endpoints silently\n // overwritten with the first's and then persisted through the host's onChange.\n // Refuse instead — `null` is the refusal channel, and the caller already has a\n // generic message for \"this edge can't be switched right now\".\n const matches = edges.filter((e) => e.id === edgeId);\n if (matches.length !== 1) return null;\n const me = matches[0];\n const srcStep = nodes.find((n) => n.id === me.source)?.data.step;\n if (!srcStep) return null;\n const out = nodePortsForStep(srcStep, lookup).out;\n // The DESTINATION lane at the source is full at a finite cap: REFUSE, evicting\n // nothing (exactly the rule `applyConnect` applies on creation). `me` is excluded —\n // it is the edge moving in.\n //\n // ...but ONLY when the edge is actually MOVING. An edge already in `when`\n // occupies that lane already, so a no-op re-lane is never refused: a definition\n // loaded with more edges out of one step than its cap is over capacity but valid.\n //\n // `e.source === me.source` is a CORRECTNESS barrier: capacity belongs to one\n // step's exit port, so other steps' edges must not be counted (pinned by\n // `flow-connect.test.ts` \"the lane census is per-SOURCE\").\n const others = edges.filter((e) => e.id !== edgeId);\n const sameLane =\n edgeLane(me) === when\n ? []\n : others.filter((e) => e.source === me.source && edgeLane(e) === when);\n if (sameLane.length >= out) {\n return { next: edges, conflict: null, refusal: { refused: \"out_cap\", cap: out } };\n }\n // The lane rides on `data.when`, the visuals come from the shared\n // `edgeVisuals`, and the id is re-minted because it ENCODES the lane\n // (`flowEdgeId`). `className`/`label`/`sourceHandle` are assigned rather than\n // spread: for \"success\" `edgeVisuals` returns `{}` and `sourceHandleForLane`\n // returns `undefined`, and spreading `...me` over either would leave a stale\n // error class / a stale error-port anchor on an edge being moved back to the\n // success lane (T1.1.1 — `sourceHandle` has exactly the same hazard as\n // `className`).\n // No run state: an edge is only re-laned during live authoring — there is\n // no run in progress while an author re-lanes an edge (T1.1.1).\n const visuals = edgeVisuals(when, undefined);\n const relaned: Edge = {\n ...me,\n id: flowEdgeId(me.source, me.target, when),\n data: { ...me.data, when },\n className: visuals.className,\n label: visuals.label,\n sourceHandle: sourceHandleForLane(when),\n };\n // The same id-uniqueness test the creation path runs, against the survivors only.\n const conflict = collidingEdgeId(others, relaned.id);\n return {\n // `edgeId` is single-owner (guarded above), so this replaces exactly one edge.\n next: edges.map((e) => (e.id === edgeId ? relaned : e)),\n conflict,\n refusal: null,\n };\n}\n\n/**\n * Re-lane one edge. Updates its `data.when`, its visuals and its id, and frees\n * nothing: when the destination lane is at a finite out cap the switch is refused\n * (see {@link relaneRefusal}) — the same rule `applyConnect` applies on creation.\n *\n * Returns a **new** array and mutates nothing (React Flow's state handle compares\n * by identity), or **`null` when the switch must be refused** — the `applyConnect`\n * idiom. It is refused when the re-minted id would duplicate a surviving edge's\n * id, which happens for a step id ending in `:error`: `flowEdgeId` qualifies the\n * error lane with that same suffix, so `a → b` in the error lane and `a → \"b:error\"`\n * in the success lane mint one id, and React Flow's id-keyed store would collapse\n * them — one wire silently vanishing from the canvas, which reads as the editor\n * losing the author's work. The caller must surface the refusal (see\n * {@link edgeWhenConflict}); rejecting the step id itself is the durable fix and\n * is deliberately NOT done here, because it would also have to reject ids inside\n * workflows that already exist (FOLLOWUPS.md).\n *\n * It is refused for one more reason: an `edgeId` owned by more than one edge. That\n * edge set is already corrupt, and re-laning by id would rewrite **every** match,\n * overwriting the other edge's endpoints (`edgeWhenConflict` returns `null` there —\n * there is no *other* edge to name, so the caller's generic refusal applies).\n */\nexport function setEdgeWhen(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): Edge[] | null {\n const plan = planRelane(edges, edgeId, when, nodes, lookup);\n if (!plan || plan.conflict || plan.refusal) return null;\n return plan.next;\n}\n\n/**\n * The id of the existing edge that {@link setEdgeWhen} would collide with, or\n * `null` when the switch is safe. Lets the caller *name* the offending edge in an\n * inline refusal instead of failing silently — the editor renders it in the\n * edge-lane panel (never a browser dialog; see `.ai/conventions.md`).\n */\nexport function edgeWhenConflict(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): string | null {\n return planRelane(edges, edgeId, when, nodes, lookup)?.conflict ?? null;\n}\n\n/** The out-cap refusal for re-laning `edgeId`, or `null` — the re-lane twin of {@link connectRefusal}. */\nexport function relaneRefusal(\n edges: Edge[],\n edgeId: string,\n when: \"success\" | \"error\",\n nodes: StepNode[],\n lookup?: PortsLookup,\n): OutCapRefusal | null {\n return planRelane(edges, edgeId, when, nodes, lookup)?.refusal ?? null;\n}\n\n/**\n * Rewrite the edge set after a step's id changed from `prevId` to `nextId`:\n * re-point every endpoint and **re-mint the id in the edge's own lane**.\n *\n * Extracted from `updateStep` so it is testable, and fixing a real defect: the\n * rewrite used to rebuild every id as `` `${source}->${target}` `` unconditionally,\n * dropping an error edge's `:error` qualifier — so renaming a step collapsed a\n * same-target success+error pair into one edge in React Flow's id-keyed store.\n * That was unreachable while a second outgoing edge deleted the first; per-lane\n * exit capacity is what makes it reachable, hence the fix ships alongside it.\n */\nexport function renameStepInEdges(edges: Edge[], prevId: string, nextId: string): Edge[] {\n return edges.map((e) => {\n const source = e.source === prevId ? nextId : e.source;\n const target = e.target === prevId ? nextId : e.target;\n return { ...e, source, target, id: flowEdgeId(source, target, edgeLane(e)) };\n });\n}\n","// Pure fan-out decisions for the \"Run next nodes\" control and card badge.\nimport type { Edge } from \"@xyflow/react\";\nimport { edgeLane } from \"./flow-connect.ts\";\nimport type { FlowStep } from \"./flow-types.ts\";\n\nexport type FanOutMode = \"sequential\" | \"parallel\";\n\n/**\n * `count` is the largest number of outgoing edges the node has in any ONE lane\n * (success / error); fan-out only means something at `count >= 2`.\n */\nexport function fanOutState(\n stepId: string,\n edges: readonly Edge[],\n): { enabled: boolean; count: number } {\n const per = { success: 0, error: 0 };\n for (const e of edges) if (e.source === stepId) per[edgeLane(e)]++;\n const count = Math.max(per.success, per.error);\n return { enabled: count >= 2, count };\n}\n\n/** Sequential is the default, so it is expressed by the key's ABSENCE. */\nexport function setFanOut(step: FlowStep, mode: FanOutMode): FlowStep {\n const { fanOut: _drop, ...rest } = step;\n return mode === \"parallel\" ? { ...rest, fanOut: \"parallel\" } : rest;\n}\n\n/** Card-badge wording: how many next nodes run, and in which mode. */\nexport function fanOutLabel(count: number, mode: FanOutMode): string {\n return `Runs these ${count} nodes in ${mode === \"parallel\" ? \"parallel\" : \"sequence\"}`;\n}\n","/**\n * Pure derivation of a live run's canvas visuals (T1.1.1) — step node styling\n * and main-graph edge taken/skipped/untouched state. Plain functions of (run\n * state, step/edge identity) → visual state, with **no React import**, so\n * `node --test` exercises them directly (A7) the same way `flow-connect.ts`'s\n * connection rules do.\n *\n * `RunStatus`/`StepStatus`/`RunState`/`StepExecution` below are thin LOCAL\n * copies of the shapes `@w6w/workflow-types` declares\n * (`packages/w6w-workflow/packages/types/mod.ts`) — the same house idiom\n * `components/HealthStatusPill.tsx`'s `HealthPillState` uses, and the same\n * design note `flow-types.ts`'s own header already states for\n * `FlowWorkflow`/`FlowStep`: `packages/ui/package.json` has no dependency on\n * `@w6w/workflow-types` and must not gain one, so this module stays a\n * pure-presentation leaf with no transport/spec coupling. A caller who\n * already has the real `RunState`/`StepExecution` is structurally compatible\n * for free.\n */\n\n/** Mirrors `RunStatus` in `@w6w/workflow-types`. */\nexport type RunStatus = \"queued\" | \"running\" | \"succeeded\" | \"failed\" | \"canceled\";\n/** Mirrors `StepStatus` in `@w6w/workflow-types`. */\nexport type StepStatus = \"pending\" | \"running\" | \"succeeded\" | \"failed\" | \"skipped\";\n\n/** The slice of `StepExecution` this module reads. */\nexport interface StepExecution {\n status: StepStatus;\n}\n\n/** The slice of `RunState` this module reads — steps keyed by step id, same\n * as the engine's own `RunState.steps`. */\nexport interface RunState {\n status: RunStatus;\n /** Canonical per-node records (D2). */\n nodes?: Record<string, StepExecution>;\n /** Legacy alias of `nodes`; read as `nodes ?? steps` (un-upgraded server). */\n steps?: Record<string, StepExecution>;\n}\n\nfunction execOf(runState: RunState, id: string): StepExecution | undefined {\n return (runState.nodes ?? runState.steps)?.[id];\n}\n\nconst RUN_ENDED: ReadonlySet<RunStatus> = new Set([\"succeeded\", \"failed\", \"canceled\"]);\n\n/**\n * A step node's rendered state — exactly the five {@link StepStatus} values\n * plus `\"no-run\"` (A2): `runState` omitted ⇒ `\"no-run\"` (this canvas isn't\n * showing any run). A step present in `runState.steps` renders its own\n * recorded status; a step the run hasn't reached yet has no entry there at\n * all (only steps that were actually started/skipped get a `StepExecution` —\n * see the engine's `recordSkipped`), so absence reads as `\"pending\"`.\n */\nexport type StepVisualState = \"no-run\" | StepStatus;\n\n/**\n * The rendered state for step `stepId` under `runState` — the StepStatus\n * half of A3's RunStatus × StepStatus mapping. Pure function of the run's own\n * step records; no additional data needed (`GET /runs/:id` already returns\n * everything this reads).\n */\nexport function stepVisualState(runState: RunState | undefined, stepId: string): StepVisualState {\n if (!runState) return \"no-run\";\n return execOf(runState, stepId)?.status ?? \"pending\";\n}\n\n/** One node's non-persisted, run-aware look. Colour is never the only signal\n * (the house rule `HealthStatusPill`'s own doc comment states) — every\n * non-default state also differs from the default (`\"no-run\"`) in\n * `borderStyle`, `borderWidth`, or `opacity`. */\nexport interface StepNodeVisual {\n borderColor: string;\n borderStyle: \"solid\" | \"dashed\";\n borderWidth: number;\n opacity: number;\n}\n\nconst NO_RUN_VISUAL: StepNodeVisual = {\n borderColor: \"var(--w6w-border)\",\n borderStyle: \"solid\",\n borderWidth: 1,\n opacity: 1,\n};\n\n/**\n * The node visual for a step's {@link StepVisualState}, `runStatus`\n * (the owning run's OWN status) additionally distinguishing the `\"pending\"`\n * case — A3: a `canceled`/`failed` run's not-yet-reached steps render\n * dimmer/muted (they never will run) than a `queued`/`running` run's\n * not-yet-reached steps (they still might), even though both share the same\n * `StepVisualState`. Every token used is one of the five named in\n * `context.notes` (`--w6w-accent`/`--w6w-success`/`--w6w-danger`/\n * `--w6w-muted`/`--w6w-border`) — never a literal hex, never a different\n * token family.\n */\nexport function stepNodeVisual(state: StepVisualState, runStatus?: RunStatus): StepNodeVisual {\n const runEnded = runStatus !== undefined && RUN_ENDED.has(runStatus);\n switch (state) {\n case \"no-run\":\n return NO_RUN_VISUAL;\n case \"pending\":\n return runEnded\n ? { borderColor: \"var(--w6w-muted)\", borderStyle: \"dashed\", borderWidth: 1, opacity: 0.5 }\n : { borderColor: \"var(--w6w-border)\", borderStyle: \"dashed\", borderWidth: 1, opacity: 1 };\n case \"running\":\n return { borderColor: \"var(--w6w-accent)\", borderStyle: \"solid\", borderWidth: 2, opacity: 1 };\n case \"succeeded\":\n return {\n borderColor: \"var(--w6w-success)\",\n borderStyle: \"solid\",\n borderWidth: 2,\n opacity: 1,\n };\n case \"failed\":\n return { borderColor: \"var(--w6w-danger)\", borderStyle: \"solid\", borderWidth: 2, opacity: 1 };\n case \"skipped\":\n return {\n borderColor: \"var(--w6w-muted)\",\n borderStyle: \"dashed\",\n borderWidth: 1,\n opacity: 0.7,\n };\n default:\n return NO_RUN_VISUAL;\n }\n}\n\n// ── Edge inference (main-graph `when` edges only — no `if`/then-else\n// sub-block inference: the canvas has no rendering or authoring path for\n// that construct today, see FOLLOWUPS.md) ───────────────────────────────\n\n/** Whether a main-graph edge was taken, skipped, or left untouched by the\n * run (`undefined` — the source step hasn't been decided yet, or there is\n * no run). */\nexport type EdgeRunState = \"taken\" | \"skipped\";\n\n/**\n * Whether the edge `from --(when)--> …` was taken or skipped in `runState` —\n * computable from the run's step records alone (no engine data beyond what\n * `GET /runs/:id` already returns):\n * - `from` **succeeded**: its `\"success\"`/omitted-`when` edges are taken,\n * its `\"error\"` edges are skipped.\n * - `from` **failed**: a declared `\"error\"` edge is taken, every other\n * outgoing edge (the `\"success\"` lane) is skipped.\n * - `from` was itself **skipped** (reached only through a skipped edge —\n * the engine's own `recordSkipped`/`markOutgoingEdgesSkipped`): every\n * outgoing edge is skipped too, regardless of lane — a step that never\n * ran cannot have taken an exit.\n * - `from` is **running**, or has no record at all (not yet reached): the\n * edge's fate isn't decided yet ⇒ `undefined`.\n */\nexport function edgeRunState(\n runState: RunState | undefined,\n from: string,\n when: \"success\" | \"error\" | undefined,\n): EdgeRunState | undefined {\n if (!runState) return undefined;\n const exec = execOf(runState, from);\n if (!exec) return undefined;\n const lane = when ?? \"success\";\n if (exec.status === \"succeeded\") return lane === \"success\" ? \"taken\" : \"skipped\";\n if (exec.status === \"failed\") return lane === \"error\" ? \"taken\" : \"skipped\";\n if (exec.status === \"skipped\") return \"skipped\";\n return undefined;\n}\n","/**\n * Host-supplied per-step problems painted on the canvas. Pure — no React or\n * @xyflow imports. The host owns detection; ui never infers a problem itself.\n */\nexport interface StepProblem {\n code: \"unknown_connection\";\n connection: string;\n}\n\n/** Keyed by step id. */\nexport type StepProblems = Record<string, StepProblem>;\n\ninterface ProblemNode {\n id: string;\n data: { stepProblem?: StepProblem };\n}\n\n/**\n * Stamp `data.stepProblem` from `problems`, clearing it for steps no longer in\n * the map. Returns the SAME node object when nothing changed.\n */\nexport function applyStepProblems<N extends ProblemNode>(\n nodes: N[],\n problems: StepProblems | undefined,\n): N[] {\n return nodes.map((n) => {\n const next = problems?.[n.id];\n const cur = n.data.stepProblem;\n if (next === undefined) {\n if (cur === undefined && !(\"stepProblem\" in n.data)) return n;\n const { stepProblem: _drop, ...rest } = n.data;\n return { ...n, data: rest } as N;\n }\n if (cur && cur.code === next.code && cur.connection === next.connection) return n;\n return { ...n, data: { ...n.data, stepProblem: { ...next } } };\n });\n}\n\nexport function stepProblemTooltip(p: StepProblem): string {\n return `Connection ${p.connection} no longer exists — open the step and choose another on the Setup tab`;\n}\n\n/** True while the step still points at the connection the host reports missing. */\nexport function isConnectionBlocked(\n step: { uses: { connection?: string | null } },\n problem: StepProblem | undefined,\n): boolean {\n return problem?.code === \"unknown_connection\" && step.uses.connection === problem.connection;\n}\n\n/** The Setup tab's Connection button label — exactly five outcomes. */\nexport function connectionButtonLabel(\n connection: string | undefined | null,\n conns: { id: string; displayName?: string }[] | null,\n failed: boolean,\n): string {\n if (!connection) return \"No connection\";\n if (failed) return \"Couldn't load connections\";\n if (conns === null) return connection;\n const found = conns.find((c) => c.id === connection);\n if (found) return found.displayName ?? connection;\n return \"Missing connection — choose another\";\n}\n","/**\n * The action definitions of every app a workflow's steps use, so the editor\n * can read each upstream step's declared output shape (`output-shape.ts`)\n * without running it.\n *\n * Fetched once per app id (`getAppActions`, i.e. `GET /apps/:id`) and kept for\n * the editor's lifetime. Keyed on the sorted SET of app ids rather than\n * `nodes`' identity — `nodes` is rebuilt on every drag and keystroke. Internal\n * `@w6w/*` apps are skipped: their shapes are static (`internalNodeDef`).\n * Best-effort per app: a failed fetch contributes nothing and never breaks\n * the picker.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport { type PortsLookup, isInternalApp } from \"./flow-types.ts\";\nimport type { StepNode } from \"./flow-utils.ts\";\nimport type { W6WApi } from \"./provider.tsx\";\nimport type { ActionDef } from \"./types.ts\";\n\nexport function useAppActionDefs(api: W6WApi, nodes: StepNode[]): Record<string, ActionDef[]> {\n const [defs, setDefs] = useState<Record<string, ActionDef[]>>({});\n // Apps already requested (loaded or in flight), so a re-run fetches only new ones.\n const requested = useRef(new Set<string>());\n const appIdsKey = JSON.stringify(\n [...new Set(nodes.map((n) => n.data.step.uses.app))]\n .filter((app) => app && !isInternalApp(app))\n .sort(),\n );\n useEffect(() => {\n const missing = (JSON.parse(appIdsKey) as string[]).filter(\n (app) => !requested.current.has(app),\n );\n if (missing.length === 0) return;\n for (const app of missing) requested.current.add(app);\n // Never canceled: an app's actions don't depend on which run of this effect\n // asked for them, so a result landing after a re-key is still right, and\n // dropping it would leave that app unloaded (it is already `requested`).\n Promise.all(\n missing.map(async (app) => {\n try {\n return [app, await api.getAppActions(app)] as const;\n } catch {\n requested.current.delete(app); // let a later change retry it\n return null;\n }\n }),\n ).then((res) => {\n const loaded = res.filter((r): r is readonly [string, ActionDef[]] => r !== null);\n if (loaded.length === 0) return;\n setDefs((prev) => ({ ...prev, ...Object.fromEntries(loaded) }));\n });\n }, [api, appIdsKey]);\n return defs;\n}\n\n/** Build a `PortsLookup` over the fetched catalog defs (`useAppActionDefs`' result). */\nexport function portsLookupFromDefs(defs: Record<string, ActionDef[]>): PortsLookup {\n return (app, action) => defs[app]?.find((d) => d.key === action)?.ports;\n}\n","import {\n Background,\n type Connection,\n // Rendered as a `<Controls>` CHILD, which the library appends after its three\n // built-ins — so the auto-layout button lands in the same stack and inherits\n // T3.1.1's `--xy-*` → `--w6w-*` bridge with no chrome of its own.\n ConnectionLineType,\n ControlButton,\n Controls,\n type Edge,\n MiniMap,\n type NodeProps,\n NodeToolbar,\n Panel,\n Position,\n ReactFlow,\n ReactFlowProvider,\n // Type-only now: `addEdge` itself is called inside `flow-connect.ts`; this file\n // only borrows its parameter type for the `onConnect` handler.\n type addEdge,\n useEdgesState,\n useNodeId,\n useNodesState,\n useReactFlow,\n} from \"@xyflow/react\";\nimport type { FinalConnectionState } from \"@xyflow/react\";\nimport { Handle } from \"@xyflow/react\";\nimport {\n type ReactNode,\n type RefObject,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { AddConnectionModal } from \"./AddConnectionModal.tsx\";\nimport { AppPicker } from \"./AppPicker.tsx\";\nimport { CanvasLayer } from \"./CanvasLayer.tsx\";\nimport { JsonEditor } from \"./JsonEditor.tsx\";\nimport { NodeConfigForm } from \"./NodeConfigForm.tsx\";\nimport { ParamsForm } from \"./ParamsForm.tsx\";\nimport { PropertyEntryForm } from \"./PropertyEntryForm.tsx\";\nimport {\n type BuiltStep,\n type ConfigView,\n ConfigViewToggle,\n StepBuilderModal,\n StepTestRun,\n type StepTestRunHandle,\n requiredParamsFilled,\n} from \"./StepBuilderModal.tsx\";\nimport { TriggerFillForm } from \"./TriggerFillForm.tsx\";\nimport { WaitEditor } from \"./WaitEditor.tsx\";\nimport type { FlowCanvas } from \"./canvas-types.ts\";\nimport {\n assignMembership,\n clampCanvas,\n persistCanvas,\n pruneSectionMembers,\n renameSectionMember,\n sectionsContaining,\n} from \"./canvas-utils.ts\";\nimport { AppIcon } from \"./components/AppIcon.tsx\";\nimport { Combobox } from \"./components/Combobox.tsx\";\nimport { ConfirmModal } from \"./components/ConfirmModal.tsx\";\nimport { Copyable } from \"./components/Copyable.tsx\";\nimport {\n type ExpressionOptions,\n ExpressionOptionsProvider,\n type ExpressionStepSource,\n excludeSelfStep,\n useExpressionOptions,\n} from \"./components/ExpressionOptions.tsx\";\nimport { Icon } from \"./components/Icon.tsx\";\nimport { IconButton } from \"./components/IconButton.tsx\";\nimport { InternalIcon } from \"./components/InternalIcon.tsx\";\nimport { Modal } from \"./components/Modal.tsx\";\nimport { ResolvedParams } from \"./components/ResolvedParams.tsx\";\nimport {\n applyConnect,\n canConnect,\n connectConflict,\n connectRefusal,\n edgeLane,\n edgeWhenConflict,\n relaneRefusal,\n renameStepInEdges,\n setEdgeWhen,\n} from \"./flow-connect.ts\";\n// The connection rules live in a JSX-free `.ts` module so `node --test` can run\n// them (see `flow-connect.ts`). This file is their only production caller.\nimport { fanOutLabel, fanOutState, setFanOut } from \"./flow-fanout.ts\";\nimport {\n CALL_APP,\n ERROR_SOURCE_HANDLE,\n type FlowStep,\n type FlowWorkflow,\n type PortsLookup,\n WEBHOOK_APP,\n edgeVisuals,\n internalNodeIcon,\n internalNodeLabel,\n internalNodeParams,\n isControlApp,\n isInternalApp,\n isTriggerApp,\n laneForSourceHandle,\n nodePortsForStep,\n} from \"./flow-types.ts\";\nimport {\n type StepNode,\n findChainRoot,\n findChainTail,\n flowToWorkflow,\n idClashMessage,\n paramsToJson,\n relayoutNodes,\n slugifyLabel,\n stepIdError,\n stepToJson,\n storedViewport,\n suggestStepId,\n withViewport,\n workflowToFlow,\n} from \"./flow-utils.ts\";\n// One implementation of the incoming-state pipeline, shared by every surface\n// that offers upstream seed chips (this file's step editor + ▶ Run collect\n// form, and StepBuilderModal's add-step Test tab) — see `step-preview-state.ts`'s\n// header comment for why this lives outside WorkflowFlowEditor.tsx.\nimport { valueAtPath } from \"./output-shape.ts\";\nimport {\n type StepMeta,\n type StepStartState,\n type StepTest,\n WorkflowProjectProvider,\n useW6WApi,\n} from \"./provider.tsx\";\n// T1.1.1 — the pure run-visual derivation, no React import (`run-visuals.ts`'s\n// own header). This file is the one production caller that turns it into\n// pixels: a live-run repaint effect (in `Inner`) feeds the results into\n// `data`, and `StepNodeCard`/`ControlNodeCard` read them back off `data`.\nimport { type RunState, edgeRunState, stepNodeVisual, stepVisualState } from \"./run-visuals.ts\";\nimport {\n type SeedSource,\n type StepShapeContext,\n startStateFromSeeds,\n stepBuilderUpstreamSteps,\n stepMetaSampleValues,\n upstreamStateSources,\n} from \"./step-preview-state.ts\";\nimport {\n type StepProblem,\n type StepProblems,\n applyStepProblems,\n connectionButtonLabel,\n isConnectionBlocked,\n stepProblemTooltip,\n} from \"./step-problems.ts\";\nimport { useEffectiveTheme } from \"./theme.ts\";\nimport { asFieldDefs, fieldsToParams, seedValues } from \"./trigger-fields.ts\";\nimport type {\n ActionDef,\n ActionParam,\n AppSummary,\n ConnectionSummary,\n SubscriptionSummary,\n} from \"./types.ts\";\nimport { portsLookupFromDefs, useAppActionDefs } from \"./use-app-action-defs.ts\";\nimport { useSeedSources } from \"./use-seed-sources.ts\";\n\n/**\n * What the \"Run on\" control does, shown on the lane panel (T1.1.1).\n *\n * An error edge is authored by DRAGGING from the step's error exit port (the\n * second, red-tinted source handle every step/control node renders below its\n * ordinary exit) — that's what stamps `sourceHandle`/`data.when` at creation\n * time (`onConnect` → `laneForSourceHandle`). This panel is not how an error\n * edge is first drawn; it is the RE-LANE affordance for an edge already drawn,\n * for when it was dragged from the wrong port. Each lane still holds one edge\n * out of an `out: 1` step, so switching an edge into a lane that's already\n * occupied re-points the incumbent (the deliberate single-slot UX).\n */\nconst LANE_HINT =\n \"Which outcome of the source node this edge carries. An error edge is drawn by dragging \" +\n \"from the node's error exit port; use this control to re-lane an edge already drawn. \" +\n \"An error edge overrides the node’s “On error” policy.\";\n\n/**\n * The step ids the refused edge id is minted from: the endpoints of the edge being\n * drawn/moved plus those of the edge already holding the id. {@link idClashMessage}\n * needs them to *determine* the cause instead of asserting one — the wording used to\n * blame a `:error` step id unconditionally, and a collision from an embedded `->`\n * therefore told the author to rename a step that did not exist (ADDENDUM Y).\n *\n * Both call sites look the sitting edge up by the id `connectConflict` /\n * `edgeWhenConflict` returned; a corrupt edge set with no such edge simply\n * contributes nothing, and the message falls back to naming no cause.\n */\nfunction clashInvolvedIds(\n edges: Edge[],\n conflictId: string,\n source: string | null | undefined,\n target: string | null | undefined,\n): (string | null | undefined)[] {\n const sitting = edges.find((e) => e.id === conflictId);\n return [source, target, sitting?.source, sitting?.target];\n}\n\nexport interface WorkflowFlowEditorProps {\n /** The workflow being edited. The editor re-derives layout when this changes identity. */\n value: FlowWorkflow;\n /** Fired whenever the user changes the graph — new nodes, edges, or step edits. */\n onChange: (next: FlowWorkflow) => void;\n /** Disable all interactions — pans/zooms are still enabled. */\n readOnly?: boolean;\n /** Height of the editor viewport. Defaults to 480px. */\n height?: string | number;\n /**\n * Registered apps, used to render each action node with its owning app's icon,\n * display name, and version. A step only carries `uses.app` (an id), so the\n * card joins that id against this list. Optional — unknown/absent apps degrade\n * to an initials tile with no version. Metadata is looked up at render time and\n * never stored on nodes, so it can't corrupt round-tripping back to a workflow.\n */\n apps?: AppSummary[];\n /**\n * EXTRA scope for each step field's expression picker, on top of whatever\n * this editor is already mounted under.\n *\n * Optional, and normally omitted: `ExpressionOptionsProvider` layers, so a\n * host that provides the project's vars/secrets/documents above this editor\n * (studio does, once, in its app shell) needs to pass nothing here. Use it\n * only to add or override something at this level.\n *\n * Names only — secret plaintext never reaches the client.\n */\n exprOptions?: ExpressionOptions;\n /**\n * The workflow's currently-selected project id. Threaded into ad-hoc\n * test-invokes so document expressions resolve against that project's docs\n * (not the scope's default/starter project). The host (studio) passes the\n * active project here; omitted → server default-project behavior. See T2.1.2 /\n * HITL-4(b): the invoke path's ambient scope is already project-aware.\n */\n project?: string;\n /**\n * The live run's per-step state, when the canvas is showing one (T1.1.1).\n * Omitted ⇒ the canvas renders exactly as it does today: every node in its\n * default look, every edge unstyled beyond its own `when` lane. This\n * editor does no fetching of its own — the host (`studio`) owns polling\n * `GET /runs/:id` and passes each successive result here, the same\n * \"host owns the query, editor is presentational\" split `apps`/`readOnly`\n * already establish for every other external data source.\n *\n * A structural subset of `RunState` in `@w6w/workflow-types` (mirrors this\n * file's own `FlowWorkflow`/`FlowStep` — see `flow-types.ts`'s header\n * note): `packages/ui` takes no dependency on the engine's types, so a\n * caller who already has the real `RunState` is compatible for free.\n *\n * Never persisted: {@link flowToWorkflow} never reads it — a run is not\n * part of the workflow that ran, and `data.stepStatus`/`data.runStatus`\n * (where this ends up on the canvas) are never among the fields it reads\n * back off a node.\n */\n runState?: RunState;\n /**\n * Host-detected per-step problems, keyed by step id — an app step whose bound\n * connection no longer exists gets an error ring + tooltip. Omitted ⇒ the\n * canvas renders exactly as today. The host owns detection; this editor never\n * infers a problem itself. Repaints live (same `value.id`) and is never\n * persisted: `flowToWorkflow` never reads `data.stepProblem`.\n */\n stepProblems?: StepProblems;\n /**\n * Fired the instant a step test starts — the ▶ collect form's Run button, or\n * `StepEditModal`'s footer Test button — before the invoke goes out, so a host\n * can drop whatever run its canvas was showing (T1.1.1) instead of leaving a\n * test to run underneath a stale run's highlighting.\n *\n * On the click, never on the result: only one of those two surfaces has a\n * completion to report at all, and the click is the one moment both share.\n */\n onTestRun?: () => void;\n /**\n * Re-frames the canvas when the host changes the space around it. When this\n * value changes after mount (compared with `Object.is`), the editor calls\n * `fitView({ duration: 300 })` once. It never fires on mount, and omitting it\n * or passing the same value again adds no fit.\n *\n * For a host whose page layout resizes the canvas without the graph changing,\n * e.g. a side panel docking beside it (pass the panel's open state). React Flow\n * keeps its pan/zoom transform when its pane narrows, so nodes near the edge\n * would otherwise end up behind the panel. The editor does not watch its own\n * size, so the host decides which layout changes deserve a re-frame.\n *\n * A fit replaces whatever camera is showing, including a saved viewport the\n * editor restored on open. The fit is not user-initiated, so it never emits\n * `onChange` and never overwrites the stored viewport.\n */\n fitViewKey?: string | number | boolean;\n}\n\n/**\n * App metadata by id, shared with the node cards so `StepNodeCard` can show the\n * app's icon/name/version without threading it through each node's `data`.\n */\nconst PortsLookupCtx = createContext<PortsLookup | undefined>(undefined);\n/** Per-step max outgoing edges in one lane (`fanOutState().count`); drives the card badge. */\nconst FanOutCountsCtx = createContext<ReadonlyMap<string, number>>(new Map());\n\n/** The inline refusal when a source is already at its finite `out` cap (no eviction). */\nfunction outCapMessage(cap: number): string {\n return `Can’t draw that edge: this node allows at most ${cap} outgoing edge${cap === 1 ? \"\" : \"s\"} per lane.`;\n}\n\nconst AppsCtx = createContext<Map<string, AppSummary>>(new Map());\n\n/** Which view the step edit modal opens in: the tabbed form, raw JSON, or node settings. */\ntype EditView = \"props\" | \"json\" | \"settings\";\n\n/** Per-node control handlers, provided to the node cards via context. */\ninterface StepControls {\n /** Open the edit modal on the form view. */\n onEdit: (id: string) => void;\n /** Open the edit modal straight on the JSON view. */\n onEditJson: (id: string) => void;\n /** Test-run a single step and show its result. */\n onRun: (id: string) => void;\n onDuplicate: (id: string) => void;\n onDelete: (id: string) => void;\n /**\n * Open the step builder for a new step wired to this port — a plain click on the\n * port. Same path as dropping a dragged connection on empty canvas.\n */\n onAddFromPort: (id: string, handleType: \"source\" | \"target\", handleId?: string) => void;\n readOnly?: boolean;\n}\nconst StepControlsCtx = createContext<StepControls | null>(null);\n\n/**\n * Visual workflow editor. Renders a Workflow's DAG as a React Flow graph:\n *\n * - Auto-layouted on load (topological columns + sibling rows).\n * - Nodes render differently for action steps vs. control steps\n * (`uses.app === \"@w6w/control\"`).\n * - `+ Node` opens a guided builder (pick app → connection → action → params,\n * or a flow control). Drag nodes to reposition; connect handles to add edges.\n * - Selecting a node reveals a toolbar above it: Edit / Duplicate / Delete.\n * - Edit opens a modal with a Form ⇄ JSON toggle for that step.\n * - Every meaningful change fires `onChange` with an updated Workflow.\n */\nexport function WorkflowFlowEditor(props: WorkflowFlowEditorProps) {\n return (\n <ReactFlowProvider>\n <Inner {...props} />\n </ReactFlowProvider>\n );\n}\n\n/**\n * Live state of a single-step test run, shown in **one** modal that walks three\n * phases against this one state slot: `collect` (fill the step's properties),\n * `running`, then `done` / `error`. ▶ opens `collect` — it no longer invokes on\n * click, so a trigger's declared fields are filled before the run instead of the\n * run coming back `{}`.\n */\ninterface StepRunState {\n stepId: string;\n status: \"collect\" | \"running\" | \"done\" | \"error\";\n value?: unknown;\n /** Which app/version/action/connection the run used; absent when the host sent none. */\n meta?: StepMeta;\n error?: string;\n errorCode?: string;\n /** console.* output captured from a script node run, if any. */\n logs?: string[];\n}\n\nfunction nextCanvasId(prefix: string, taken: string[]) {\n let i = taken.length + 1;\n while (taken.includes(`${prefix}-${i}`)) i++;\n return `${prefix}-${i}`;\n}\nfunction nodeRect(n: StepNode) {\n return {\n x: n.position.x,\n y: n.position.y,\n w: n.measured?.width ?? n.width ?? 200,\n h: n.measured?.height ?? n.height ?? 64,\n };\n}\n\nfunction Inner({\n value,\n onChange,\n readOnly,\n height = 480,\n apps,\n exprOptions,\n project,\n runState,\n stepProblems,\n onTestRun,\n fitViewKey,\n}: WorkflowFlowEditorProps) {\n const api = useW6WApi();\n const appsById = useMemo(() => new Map((apps ?? []).map((a) => [a.id, a])), [apps]);\n const [runResult, setRunResult] = useState<StepRunState | null>(null);\n // Re-hydrate nodes+edges only when the workflow id changes identity. Local\n // edits (drag, connect, delete) go through the useNodesState / useEdgesState\n // handles so React Flow's own state stays authoritative during interaction.\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-derive layout only on workflow identity change\n const initial = useMemo(() => workflowToFlow(value), [value.id]);\n // The camera to open at, read at mount for the same reason `initial` is: React\n // Flow consumes `defaultViewport` in a mount-only effect (ZoomPane's `XYPanZoom`\n // init), so a fresh object per render would be inert but misleading — and this\n // is what makes a save round-trip not yank the view back.\n // biome-ignore lint/correctness/useExhaustiveDependencies: read at mount only, like `initial`\n const savedViewport = useMemo(() => storedViewport(value), [value.id]);\n // Gates BOTH position writers below (the drag and the pan/zoom). ⚠️ Omitted ⇒\n // **true** (core rfcs/workflow.md · authoring-presentation amendment), so it is\n // read as `!== false` — never `?? false` and never `=== true`. Derived from\n // `value`, deliberately NOT a prop: the flag is a field of the workflow the\n // editor already receives, and a prop would be a second source of truth that\n // can disagree with the persisted document.\n const savePosition = value.settings?.savePosition !== false;\n const [nodes, setNodes, onNodesChange] = useNodesState<StepNode>(initial.nodes);\n // Catalog ports reach resolution through this lookup, never by stamping\n // `step.ports` (which would persist into every saved workflow).\n const actionDefs = useAppActionDefs(api, nodes);\n const portsLookup = useMemo(() => portsLookupFromDefs(actionDefs), [actionDefs]);\n const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(initial.edges);\n // T1.1.1 — live run-state repaint. `initial` above deliberately re-derives\n // nodes/edges ONLY on workflow identity change (Trap 1 — its own lint\n // suppression comment a few lines up says so), so a poll tick that\n // changes `runState` while `value.id` stays put would be invisible to the\n // cards if it went through `workflowToFlow` — that memo simply never fires\n // again. This effect instead patches the already-authoritative React Flow\n // state directly, the same mechanism a drag/connect/delete already uses to\n // update `nodes`/`edges` without re-deriving `initial`.\n //\n // Only new `data` keys are stamped — `stepStatus`/`runStatus` on nodes, a\n // run-aware `className`/`label` on edges. `data.step` and `data.when` (the\n // fields `flowToWorkflow` reads back to build the persisted document) are\n // never touched here, so nothing run-related can round-trip into a saved\n // workflow (A6).\n //\n // Functional `setNodes`/`setEdges` updaters read the PREVIOUS state\n // directly, so `nodes`/`edges` need not be dependencies — keeping this\n // effect keyed on `runState` alone (plus the setters, stable across\n // renders) is what makes it fire on every run-state change and nothing\n // else, including a plain drag or edit that doesn't touch `runState`.\n useEffect(() => {\n setNodes((nds) =>\n nds.map((n) => {\n const stepStatus = stepVisualState(runState, n.id);\n if (n.data.stepStatus === stepStatus && n.data.runStatus === runState?.status) return n;\n return { ...n, data: { ...n.data, stepStatus, runStatus: runState?.status } };\n }),\n );\n setEdges((eds) =>\n eds.map((e) => {\n const lane = edgeLane(e);\n const run = edgeRunState(runState, e.source, lane);\n const visuals = edgeVisuals(lane, run);\n if (e.className === visuals.className && e.label === visuals.label) return e;\n return { ...e, className: visuals.className, label: visuals.label };\n }),\n );\n }, [runState, setNodes, setEdges]);\n // Host-supplied step problems — same live-repaint mechanism as runState above:\n // stamps/clears `data.stepProblem` only, never `data.step`.\n useEffect(() => {\n setNodes((nds) => applyStepProblems(nds, stepProblems));\n }, [stepProblems, setNodes]);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n // Notes + sections: live canvas state (geometry renders live; persistCanvas decides what is\n // written). Re-seeded only on workflow identity change, like nodes/edges.\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-seed on workflow identity change only\n const initialCanvas = useMemo<FlowCanvas>(() => value.canvas ?? {}, [value.id]);\n const [canvas, setCanvas] = useState<FlowCanvas>(initialCanvas);\n // The draw-section tool, and the rectangle being dragged (client px) while it is armed.\n const [drawingSection, setDrawingSection] = useState(false);\n const [sectionDraft, setSectionDraft] = useState<{\n sx: number;\n sy: number;\n ex: number;\n ey: number;\n } | null>(null);\n const [pendingCanvasDelete, setPendingCanvasDelete] = useState<{\n kind: \"note\" | \"section\";\n id: string;\n } | null>(null);\n const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null);\n const [builderOpen, setBuilderOpen] = useState(false);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editView, setEditView] = useState<EditView>(\"props\");\n // The step id awaiting delete confirmation — one pending slot driving one modal.\n const [pendingDelete, setPendingDelete] = useState<string | null>(null);\n // Set right after `setNodes(next)` in `performRelayout`, cleared by the\n // `[nodes, fitView]` effect below, which then calls `fitView()`. A ref (not\n // state) because it drives no render of its own — it only needs to survive\n // from the relayout click to the next commit of `nodes`.\n const relayoutPendingFitRef = useRef(false);\n // Carries the flow container div so the capture-phase dblclick listener below\n // can be attached/removed on it directly — see that effect for why a native\n // listener is required instead of React Flow's own node-double-click prop.\n const flowContainerRef = useRef<HTMLDivElement>(null);\n // When a connection drag is released on empty canvas, we open the builder to\n // create a new node and auto-wire it to the handle it was dragged from.\n const [pendingConnect, setPendingConnect] = useState<{\n nodeId: string;\n handleType: \"source\" | \"target\";\n position: { x: number; y: number };\n /**\n * The lane the drag originated from, derived via {@link laneForSourceHandle}\n * at CAPTURE time in `onConnectEnd` (T1.1.1) — a target-handle drag reports a\n * handle id that is never `ERROR_SOURCE_HANDLE`, so this naturally reads\n * \"success\" for that gesture without a separate branch.\n */\n lane: \"success\" | \"error\";\n } | null>(null);\n const { screenToFlowPosition, fitView } = useReactFlow();\n // React Flow's `colorMode` defaults to \"light\", so without this its chrome\n // (controls, minimap mask, handles, edge strokes, background dots) stays in the\n // light palette on a dark canvas. Read the mode from the house hook — the same\n // `data-theme` signal `styles.css` and AppIcon/CodeEditor/JsonEditor already\n // follow — rather than adding a prop the host would have to keep in sync.\n const colorMode = useEffectiveTheme();\n\n // If the caller swaps in a different workflow, reset local graph state.\n useEffect(() => {\n setNodes(initial.nodes);\n setEdges(initial.edges);\n setSelectedId(null);\n setEditingId(null);\n setCanvas(initialCanvas);\n }, [initial, initialCanvas, setNodes, setEdges]);\n\n // Stamp the `canvas` key onto an outgoing workflow. Omitted entirely when there is nothing to\n // keep (no notes, no sections), so a workflow that never used the feature round-trips unchanged.\n const withCanvas = useCallback(\n (wf: FlowWorkflow, c: FlowCanvas): FlowWorkflow => {\n const { canvas: _drop, ...rest } = wf;\n if (!c.notes?.length && !c.sections?.length) return rest as FlowWorkflow;\n return { ...rest, canvas: persistCanvas(c, value.canvas, savePosition) };\n },\n [value.canvas, savePosition],\n );\n\n const emitChange = useCallback(\n (nextNodes: StepNode[], nextEdges: Edge[], canvasOverride?: FlowCanvas) => {\n onChange(withCanvas(flowToWorkflow(value, nextNodes, nextEdges), canvasOverride ?? canvas));\n },\n [value, onChange, canvas, withCanvas],\n );\n\n // Live gate while dragging a connection — React Flow marks the drop invalid\n // (and won't fire onConnect) when this returns false.\n const isValidConnection = useCallback(\n (c: Connection | Edge) => canConnect(c.source, c.target, nodes, edges, portsLookup),\n [nodes, edges, portsLookup],\n );\n\n const onConnect = useCallback(\n (params: Parameters<typeof addEdge>[0]) => {\n if (readOnly) return;\n // The lane rides on the handle the wire was dragged from (T1.1.1) — the\n // one canonicalisation point, so a handle-to-handle drag and the\n // drag-to-empty-canvas gesture in `onConnectEnd` cannot disagree on what\n // counts as the error port. applyConnect replaces a full single-slot port\n // rather than ignoring the drop.\n const next = applyConnect(\n params.source,\n params.target,\n nodes,\n edges,\n laneForSourceHandle(params.sourceHandle),\n portsLookup,\n );\n if (!next) {\n // FAIL LOUDLY when the refusal is one the drag could not have shown. Every\n // ordinary rule already ran in `isValidConnection` (React Flow marks the\n // drop invalid and never fires this handler), so the only refusal that\n // reaches here is a duplicate minted id — and that one is *invisible*:\n // React Flow's id-keyed store would have held ONE wire for two edges, which\n // reads as the editor losing the author's work.\n //\n // No new channel: `connectConflict` names the edge already holding the id,\n // and the refusal renders in the edge-lane panel's `role=\"alert\"` slot. The\n // clashing edge is SELECTED — in React Flow's own store, not just our mirror\n // state, so `onSelectionChange` (re-invoked on any re-render) confirms it\n // instead of wiping it before paint — which both reveals that panel and\n // highlights the wire the author has to rename.\n // A finite `out` cap is refused, never evicted: name the cap inline, on an\n // edge already leaving this source in this lane (the panel needs one selected).\n const lane = laneForSourceHandle(params.sourceHandle);\n const refusal = connectRefusal(\n params.source,\n params.target,\n nodes,\n edges,\n lane,\n portsLookup,\n );\n if (refusal) {\n const holder = edges.find((e) => e.source === params.source && edgeLane(e) === lane);\n if (holder) {\n setEdges(edges.map((e) => ({ ...e, selected: e.id === holder.id })));\n setSelectedEdgeId(holder.id);\n setLaneError({ edgeId: holder.id, message: outCapMessage(refusal.cap) });\n }\n return;\n }\n const conflict = connectConflict(\n params.source,\n params.target,\n nodes,\n edges,\n laneForSourceHandle(params.sourceHandle),\n );\n if (!conflict) return;\n setEdges(edges.map((e) => ({ ...e, selected: e.id === conflict })));\n setSelectedEdgeId(conflict);\n setLaneError({\n edgeId: conflict,\n message: idClashMessage(\n \"Can’t draw that edge\",\n conflict,\n clashInvolvedIds(edges, conflict, params.source, params.target),\n ),\n });\n return;\n }\n setLaneError(null);\n setEdges(next);\n emitChange(nodes, next);\n },\n [edges, nodes, setEdges, emitChange, readOnly, portsLookup],\n );\n\n // A connection dropped on empty canvas (no valid target) means \"add a new node\n // here and connect to it\" — open the builder and remember where it came from.\n const onConnectEnd = useCallback(\n (event: MouseEvent | TouchEvent, connectionState: FinalConnectionState) => {\n if (readOnly) return;\n // A valid drop onto a handle is already handled by onConnect.\n if (connectionState.isValid) return;\n const fromNode = connectionState.fromNode;\n if (!fromNode) return;\n const handleType = connectionState.fromHandle?.type === \"target\" ? \"target\" : \"source\";\n // Only spawn if the origin handle can participate at all — a source handle\n // needs an exit port, a target handle an entry port. (A full single-slot\n // port is fine: the auto-wire below replaces its existing edge.)\n const originStep = nodes.find((n) => n.id === fromNode.id)?.data.step;\n if (originStep) {\n const p = nodePortsForStep(originStep, portsLookup);\n if ((handleType === \"source\" ? p.out : p.in) < 1) return;\n }\n const point = \"changedTouches\" in event ? event.changedTouches[0] : event;\n const position = screenToFlowPosition({ x: point.clientX, y: point.clientY });\n // The lane is derived HERE, not re-derived at `addBuiltStep` time\n // (T1.1.1): the dragged handle is only available on `connectionState`,\n // which does not survive past this callback — the step builder opens in\n // between. A target-handle drag reports a handle id that is never\n // `ERROR_SOURCE_HANDLE`, so this naturally reads \"success\" for that\n // gesture with no separate branch.\n setPendingConnect({\n nodeId: fromNode.id,\n handleType,\n position,\n lane: laneForSourceHandle(connectionState.fromHandle?.id),\n });\n setBuilderOpen(true);\n },\n [readOnly, screenToFlowPosition, nodes, portsLookup],\n );\n\n // A plain click on a port: the same \"new step wired to this port\" as a drag\n // dropped on empty canvas, with no drop point — the new step is placed one\n // step-width out from the port's side of the node instead.\n const addFromPort = useCallback(\n (nodeId: string, handleType: \"source\" | \"target\", handleId?: string) => {\n if (readOnly) return;\n const n = nodes.find((x) => x.id === nodeId);\n if (!n) return;\n const r = nodeRect(n);\n const lane = laneForSourceHandle(handleId);\n const gap = 120;\n const position =\n handleType === \"target\"\n ? { x: r.x - r.w - gap, y: r.y }\n : lane === \"error\"\n ? { x: r.x, y: r.y + r.h + gap }\n : { x: r.x + r.w + gap, y: r.y };\n setPendingConnect({ nodeId, handleType, position, lane });\n setBuilderOpen(true);\n },\n [readOnly, nodes],\n );\n\n // Deleting a step is confirmed by an in-app modal, never a blocking browser\n // dialog: `deleteStep` is also reached from the canvas keydown handler\n // below, so the confirmation has to be state + a sibling modal.\n // `deleteStep` only *requests* the delete; `performDeleteStep` does it, and\n // is invoked solely from the ConfirmModal's onConfirm.\n const deleteStep = useCallback(\n (id: string) => {\n if (readOnly) return;\n setPendingDelete(id);\n },\n [readOnly],\n );\n\n const performDeleteStep = useCallback(\n (id: string) => {\n if (readOnly) return;\n const nextNodes = nodes.filter((n) => n.id !== id);\n const nextEdges = edges.filter((e) => e.source !== id && e.target !== id);\n const nextCanvas = pruneSectionMembers(canvas, id);\n setNodes(nextNodes);\n setEdges(nextEdges);\n setCanvas(nextCanvas);\n if (selectedId === id) setSelectedId(null);\n if (editingId === id) setEditingId(null);\n emitChange(nextNodes, nextEdges, nextCanvas);\n },\n [nodes, edges, canvas, setNodes, setEdges, emitChange, readOnly, selectedId, editingId],\n );\n\n // ── Auto-layout (the fourth control button) ─────────────────────────────────\n //\n // Re-flows the CURRENT canvas — including steps added and wires drawn since the\n // last save — through `relayoutNodes`, which is the same layering + the same\n // placement arithmetic `workflowToFlow` runs on first open. Not a second\n // algorithm: \"arrange\" disagreeing with \"how it opened\" is the one thing an\n // author notices immediately (D-I0-6 also pins that no layout dependency is\n // added — the layerer already exists).\n //\n // `setNodes` then `emitChange` is what makes the result PERSIST: `flowToWorkflow`\n // stamps each step with its node's rounded coordinate and the host's auto-save\n // writes it, so a reload reopens on the re-flowed graph.\n const performRelayout = useCallback(() => {\n if (readOnly) return;\n const next = relayoutNodes(nodes, edges);\n setNodes(next);\n relayoutPendingFitRef.current = true;\n emitChange(next, edges);\n }, [nodes, edges, setNodes, emitChange, readOnly]);\n\n // No confirmation (D-T2b): re-flowing is instant, deliberately with no\n // replacement gate — the intake is explicit this click should never warn. See\n // `.ai/conventions.md`'s \"No browser dialogs\" for the house pattern this is a\n // recorded exception to, not an oversight.\n const requestRelayout = useCallback(() => {\n if (readOnly) return;\n performRelayout();\n }, [readOnly, performRelayout]);\n\n // Frame the re-flowed graph once React Flow's own node-sync effect (a child of\n // this component) has committed the new positions — that child-before-parent\n // effect ordering is what makes `fitView()` here read POST-relayout bounds\n // instead of the stale pre-relayout ones. Keyed on `[nodes, fitView]` so it\n // re-checks the ref on every nodes commit, not just relayout's — `nodes`\n // itself is unread in the body, only its commit timing matters, so the\n // exhaustive-deps rule can't see why it's there.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `nodes` drives ordering, not the body\n useEffect(() => {\n if (!relayoutPendingFitRef.current) return;\n relayoutPendingFitRef.current = false;\n void fitView({ duration: 300 });\n }, [nodes, fitView]);\n\n // `fitViewKey`: one fit per change of the host's key, never on mount. The ref\n // starts at the mount value, so the first run (and StrictMode's re-run) sees\n // no change; a new `fitView` identity alone fails the same check.\n const lastFitViewKeyRef = useRef(fitViewKey);\n useEffect(() => {\n if (Object.is(lastFitViewKeyRef.current, fitViewKey)) return;\n lastFitViewKeyRef.current = fitViewKey;\n void fitView({ duration: 300 });\n }, [fitViewKey, fitView]);\n\n // ── The edge-level lane control (`Run on: Success / Error`) ─────────────────\n // Reuses the selection state that already exists; nothing new is tracked but the\n // refusal message, which renders INLINE in the panel. Never a browser dialog —\n // same rule as the delete confirm above (.ai/conventions.md · No browser dialogs).\n // Scoped to the edge it is about, NOT cleared from `onSelectionChange`: React\n // Flow re-invokes that callback after any re-render (it is an inline lambda, so\n // it re-subscribes), which clears the message before it is ever painted — a\n // refusal that renders for zero frames is the same as failing silently.\n const [laneError, setLaneError] = useState<{ edgeId: string; message: string } | null>(null);\n const selectedEdge = useMemo(\n () => edges.find((e) => e.id === selectedEdgeId) ?? null,\n [edges, selectedEdgeId],\n );\n\n const setEdgeLane = useCallback(\n (when: \"success\" | \"error\") => {\n if (readOnly || !selectedEdgeId) return;\n const me = edges.find((e) => e.id === selectedEdgeId);\n if (!me) return;\n // Choosing the lane it is already on is a no-op — don't emit a change (which\n // would mark the host's workflow dirty for nothing). `setEdgeWhen` is\n // separately safe for this, so neither layer relies on the other.\n if (edgeLane(me) === when) return;\n const conflict = edgeWhenConflict(edges, selectedEdgeId, when, nodes);\n const refusal = relaneRefusal(edges, selectedEdgeId, when, nodes, portsLookup);\n const next =\n conflict || refusal ? null : setEdgeWhen(edges, selectedEdgeId, when, nodes, portsLookup);\n if (!next) {\n if (refusal) {\n setLaneError({ edgeId: selectedEdgeId, message: outCapMessage(refusal.cap) });\n return;\n }\n // FAIL LOUDLY. The alternative is minting a duplicate edge id, which React\n // Flow's id-keyed store silently collapses — one wire disappearing from the\n // canvas with no error, indistinguishable from the editor losing the work.\n setLaneError({\n edgeId: selectedEdgeId,\n message: conflict\n ? idClashMessage(\n \"Can’t switch this edge\",\n conflict,\n clashInvolvedIds(edges, conflict, me.source, me.target),\n )\n : \"This edge can’t be switched right now.\",\n });\n return;\n }\n setLaneError(null);\n setEdges(next);\n // The id encodes the lane, so re-laning re-mints it — move the selection (and\n // therefore this panel) onto the same wire under its new id.\n const relaned = next.find(\n (e) => e.source === me.source && e.target === me.target && edgeLane(e) === when,\n );\n setSelectedEdgeId(relaned?.id ?? null);\n emitChange(nodes, next);\n },\n [edges, nodes, setEdges, emitChange, readOnly, selectedEdgeId, portsLookup],\n );\n\n const deleteEdge = useCallback(\n (id: string) => {\n if (readOnly) return;\n const nextEdges = edges.filter((e) => e.id !== id);\n if (nextEdges.length === edges.length) return;\n setEdges(nextEdges);\n if (selectedEdgeId === id) setSelectedEdgeId(null);\n emitChange(nodes, nextEdges);\n },\n [nodes, edges, setEdges, emitChange, readOnly, selectedEdgeId],\n );\n\n const duplicateStep = useCallback(\n (id: string) => {\n if (readOnly) return;\n const src = nodes.find((n) => n.id === id);\n if (!src) return;\n const newId = suggestStepId(\n nodes.map((n) => n.id),\n `${src.data.step.id}_copy`,\n );\n const cloned: FlowStep = { ...structuredClone(src.data.step), id: newId };\n const newNode: StepNode = {\n id: newId,\n type: src.type,\n position: { x: src.position.x + 40, y: src.position.y + 60 },\n data: { step: cloned, isInternal: src.data.isInternal },\n };\n const nextNodes = [...nodes, newNode];\n setNodes(nextNodes);\n setSelectedId(newId);\n emitChange(nextNodes, edges);\n },\n [nodes, edges, setNodes, emitChange, readOnly],\n );\n\n const addBuiltStep = useCallback(\n (built: BuiltStep): string | undefined => {\n if (readOnly) return undefined;\n const isInternal = isInternalApp(built.uses.app);\n // Derived from the action itself (\"data_1\", \"render_template_1\"), not\n // a generic \"gate_1\"/\"step_1\" — human override, 2026-08-30: a reader\n // who never renames a step can still orient by its id.\n // `internalNodeLabel` falls back to the bare `action` key for a\n // non-internal (registry) app, since only internal nodes carry a\n // `label` this lookup can find.\n const id = suggestStepId(\n nodes.map((n) => n.id),\n slugifyLabel(internalNodeLabel(built.uses.app, built.uses.action)),\n );\n const step: FlowStep = {\n id,\n uses: built.uses,\n ...(built.with && Object.keys(built.with).length > 0 ? { with: built.with } : {}),\n };\n const newNode: StepNode = {\n id,\n type: isInternal ? \"control\" : \"step\",\n // Drop point when spawned from a dragged connection; else a light cascade.\n position: pendingConnect?.position ?? { x: 80, y: 80 + nodes.length * 24 },\n data: { step, isInternal },\n };\n const nextNodes = [...nodes, newNode];\n\n // Auto-wire the edge back to the handle the drag started from. Dragging a\n // source handle points the edge at the new node; a target handle reverses it.\n // Gated + replacing per the port rules (applyConnect) against the new node.\n let nextEdges = edges;\n if (pendingConnect) {\n const [source, target] =\n pendingConnect.handleType === \"target\"\n ? [id, pendingConnect.nodeId]\n : [pendingConnect.nodeId, id];\n // The lane captured at drag-release time (T1.1.1) — a step spawned by\n // dragging the error port onto empty canvas is wired error, one spawned\n // from the ordinary exit is wired success.\n nextEdges =\n applyConnect(source, target, nextNodes, edges, pendingConnect.lane, portsLookup) ?? edges;\n } else {\n // \"+ Node\" (no drag) — human override, 2026-08-30. A trigger\n // auto-wires to the CURRENT chain root, but ONLY when no trigger\n // exists yet anywhere in the graph; a second trigger is left\n // floating on purpose (nothing to sensibly guess — the author\n // decides if and where it feeds in). A non-trigger step prefers\n // wiring an existing UNCONNECTED trigger forward (the likely intent\n // right after adding one) over extending the current tail.\n const existingTrigger = nodes.find((n) => isTriggerApp(n.data.step.uses.app));\n if (isTriggerApp(built.uses.app)) {\n if (!existingTrigger) {\n const root = findChainRoot(nodes, edges);\n if (root)\n nextEdges =\n applyConnect(id, root.id, nextNodes, edges, \"success\", portsLookup) ?? edges;\n }\n } else {\n const unconnectedTrigger =\n existingTrigger && !edges.some((e) => e.source === existingTrigger.id)\n ? existingTrigger\n : undefined;\n const from = unconnectedTrigger ?? findChainTail(nodes, edges);\n if (from)\n nextEdges =\n applyConnect(from.id, id, nextNodes, edges, \"success\", portsLookup) ?? edges;\n }\n }\n\n setNodes(nextNodes);\n if (nextEdges !== edges) setEdges(nextEdges);\n setSelectedId(id);\n // Progressive commit (T4.1.1): the builder stays open past this first\n // commit to keep taking edits via `onDraftChange`, so closing is\n // exclusively `onClose`'s job now (wired at the render site below).\n emitChange(nextNodes, nextEdges);\n return id;\n },\n [nodes, edges, setNodes, setEdges, emitChange, readOnly, pendingConnect, portsLookup],\n );\n\n // Apply an edit to a step, rewiring edges if its id changed.\n const updateStep = useCallback(\n (prevId: string, next: FlowStep) => {\n const idChanged = next.id !== prevId;\n if (idChanged && nodes.some((n) => n.id === next.id)) return; // reject dup id\n const nextNodes = nodes.map((n) =>\n n.id === prevId\n ? {\n ...n,\n id: next.id,\n data: { step: next, isInternal: isInternalApp(next.uses.app) },\n }\n : n,\n );\n if (idChanged) {\n // Re-point the endpoints AND re-mint each id in the edge's own lane. This\n // used to rebuild every id unqualified, dropping an error edge's `:error`\n // suffix so a same-target success+error pair collapsed into one edge in\n // React Flow's id-keyed store. The lane-aware rewrite lives in\n // `flow-connect.ts` and mints ids through the one shared `flowEdgeId`\n // helper — never a second copy of the template.\n const nextEdges = renameStepInEdges(edges, prevId, next.id);\n const nextCanvas = renameSectionMember(canvas, prevId, next.id);\n setNodes(nextNodes);\n setEdges(nextEdges);\n setCanvas(nextCanvas);\n if (selectedId === prevId) setSelectedId(next.id);\n if (editingId === prevId) setEditingId(next.id);\n emitChange(nextNodes, nextEdges, nextCanvas);\n return;\n }\n setNodes(nextNodes);\n emitChange(nextNodes, edges);\n },\n [nodes, edges, canvas, setNodes, setEdges, selectedId, editingId, emitChange],\n );\n\n // ── Notes & sections ────────────────────────────────────────────────────────\n const onCanvasChange = useCallback(\n (next: FlowCanvas) => {\n if (readOnly) return;\n setCanvas(next);\n emitChange(nodes, edges, next);\n },\n [readOnly, nodes, edges, emitChange],\n );\n\n const addNote = useCallback(() => {\n if (readOnly) return;\n const box = flowContainerRef.current?.getBoundingClientRect();\n const at = box\n ? screenToFlowPosition({ x: box.left + box.width / 2, y: box.top + box.height / 2 })\n : { x: 0, y: 0 };\n const notes = canvas.notes ?? [];\n const note = {\n id: nextCanvasId(\n \"note\",\n notes.map((n) => n.id),\n ),\n color: \"#facc15\",\n position: { x: Math.round(at.x - 100), y: Math.round(at.y - 60) },\n size: { width: 200, height: 120 },\n content: \"\",\n };\n onCanvasChange(clampCanvas({ ...canvas, notes: [...notes, note] }));\n }, [readOnly, canvas, screenToFlowPosition, onCanvasChange]);\n\n // \"Draw section\" tool: the author drags a rectangle anywhere on the canvas (corners in flow\n // units). The frame owns the nodes whose centre falls inside it — the same rule a node drag\n // uses — so dragging the section carries them. A click without a drag drops a default frame.\n const addSectionAt = useCallback(\n (a: { x: number; y: number }, b: { x: number; y: number } | null) => {\n if (readOnly) return;\n const x0 = b ? Math.min(a.x, b.x) : a.x;\n const y0 = b ? Math.min(a.y, b.y) : a.y;\n const w = b ? Math.abs(b.x - a.x) : 360;\n const h = b ? Math.abs(b.y - a.y) : 240;\n const members = nodes\n .filter((n) => {\n const r = nodeRect(n);\n const cx = r.x + r.w / 2;\n const cy = r.y + r.h / 2;\n return cx >= x0 && cx <= x0 + w && cy >= y0 && cy <= y0 + h;\n })\n .map((n) => n.id);\n const sections = canvas.sections ?? [];\n const id = nextCanvasId(\n \"section\",\n sections.map((x) => x.id),\n );\n const section = {\n id,\n title: \"Section\",\n color: \"#6366f1\",\n position: { x: Math.round(x0), y: Math.round(y0) },\n size: { width: Math.round(w), height: Math.round(h) },\n nodes: [] as string[],\n };\n const withNew = { ...canvas, sections: [...sections, section] };\n onCanvasChange(clampCanvas(assignMembership(withNew, id, members)));\n },\n [readOnly, nodes, canvas, onCanvasChange],\n );\n\n // Esc leaves the draw-section tool without drawing.\n useEffect(() => {\n if (!drawingSection) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n setDrawingSection(false);\n setSectionDraft(null);\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [drawingSection]);\n\n // Dropping a section moves its members by the same delta: ONE emission, on drop.\n const onSectionDrop = useCallback(\n (id: string, d: { dx: number; dy: number }) => {\n if (readOnly) return;\n const sec = canvas.sections?.find((x) => x.id === id);\n if (!sec) return;\n const members = new Set(sec.nodes);\n const nextNodes = nodes.map((n) =>\n members.has(n.id)\n ? { ...n, position: { x: n.position.x + d.dx, y: n.position.y + d.dy } }\n : n,\n );\n const nextCanvas: FlowCanvas = {\n ...canvas,\n sections: (canvas.sections ?? []).map((x) =>\n x.id === id ? { ...x, position: { x: x.position.x + d.dx, y: x.position.y + d.dy } } : x,\n ),\n };\n setNodes(nextNodes);\n setCanvas(nextCanvas);\n emitChange(nextNodes, edges, nextCanvas);\n },\n [readOnly, canvas, nodes, edges, setNodes, emitChange],\n );\n\n const performCanvasDelete = useCallback(\n (kind: \"note\" | \"section\", id: string) => {\n if (readOnly) return;\n // Deleting a section deletes the frame, never its nodes.\n const next: FlowCanvas =\n kind === \"note\"\n ? { ...canvas, notes: (canvas.notes ?? []).filter((n) => n.id !== id) }\n : { ...canvas, sections: (canvas.sections ?? []).filter((x) => x.id !== id) };\n onCanvasChange(next);\n },\n [readOnly, canvas, onCanvasChange],\n );\n\n // After a node drag: membership follows the node CENTRE (inside a section ⇒ joins it,\n // leaving any other; outside every section ⇒ leaves). Returns the canvas to emit.\n const membershipAfterDrag = (dragged: StepNode[]): FlowCanvas => {\n let next = canvas;\n for (const d of dragged) {\n const r = nodeRect(d);\n const cx = r.x + r.w / 2;\n const cy = r.y + r.h / 2;\n const target = (next.sections ?? []).find(\n (sec) =>\n cx >= sec.position.x &&\n cx <= sec.position.x + sec.size.width &&\n cy >= sec.position.y &&\n cy <= sec.position.y + sec.size.height,\n );\n const current = sectionsContaining(next, d.id);\n if (target) {\n if (current.length !== 1 || current[0] !== target.id) {\n next = assignMembership(next, target.id, [d.id]);\n }\n } else if (current.length > 0) {\n next = pruneSectionMembers(next, d.id);\n }\n }\n return next;\n };\n\n // Remember the values a Run was made with, then record the run AGAINST that\n // fixture — the same two-call sequence the Test path uses (`StepTestRun` in\n // StepBuilderModal), so a Run and a Test write the same kind of `step_tests`\n // row for the same step and each surface pre-fills from whichever ran last.\n //\n // The `stepTestId` is the load-bearing half: `recordStepTestRun` only updates\n // the fixture's `last_run_*` columns when it is present, and this call used to\n // post without one — so a canvas run was logged but never written back onto\n // the fixture. Best-effort throughout: a persist failure is logged and never\n // surfaces as a failed run.\n //\n // The two calls fail INDEPENDENTLY, and deliberately so: remembering the\n // values and recording that the run happened are different obligations, and\n // only the first can fail on its own (`POST …/tests` 404s on an unknown\n // workflow, `POST …/test-runs` does not). Folding both into one `try` would\n // make a fixture-save failure swallow the run's `step_test_runs` row *and* the\n // `run_log` ledger row the server writes per POST — i.e. silently lose run\n // history over a persistence hiccup. So a failed save degrades to\n // `stepTestId: null`: a run recorded with no fixture attached, which is\n // exactly what the route already accepts (step-test-runs.ts).\n const persistStepRun = useCallback(\n (\n stepId: string,\n fixture: { input: Record<string, unknown>; with: Record<string, unknown> },\n outcome: { status: string; output?: unknown; error?: unknown; meta?: StepMeta },\n ) => {\n void (async () => {\n const saved = await api.saveStepTest(value.id, stepId, fixture).catch((err) => {\n console.error(\"step run fixture save failed\", err);\n return null;\n });\n await api.recordStepTestRun(value.id, stepId, {\n stepTestId: saved?.id ?? null,\n status: outcome.status,\n input: fixture.input,\n output: outcome.output,\n error: outcome.error,\n ...(outcome.meta ? { meta: outcome.meta } : {}),\n });\n })().catch((err) => {\n console.error(\"step run persist failed\", err);\n });\n },\n [api, value.id],\n );\n\n // Test-running one step is two-phase, mirroring delete's request/perform split:\n // `runStep` (the ▶ control) only *opens* the collect phase, and\n // `performRunStep` — reached solely from the collect form's Run button — does\n // the invoke with the values the user just filled in. Control steps aren't\n // invocable. Signature is unchanged: `onRun` is still `(id: string) => void`.\n const runStep = useCallback(\n (id: string) => {\n const node = nodes.find((n) => n.id === id);\n // Flow-control nodes can't run standalone; app + compute/trigger nodes can.\n if (!node || isControlApp(node.data.step.uses.app)) return;\n setRunResult({ stepId: id, status: \"collect\" });\n },\n [nodes],\n );\n\n // Invoke one step through the invoke API with the properties collected in the\n // modal's collect phase, and (for action steps) its stored connection.\n //\n // `state` is the run's START STATE — what the upstream steps last produced —\n // and it is NOT params: it goes out as the invoke body's own `state` field so\n // the server can resolve `{{ steps.<id>.output.<field> }}` in the step's\n // `with`. Sending it as params would do nothing: the runtime copies only\n // DECLARED params and drops the rest (`core/runtime/src/resolve.ts`).\n const performRunStep = useCallback(\n async (id: string, values: Record<string, unknown>, state?: StepStartState) => {\n // T1.1.1: a step test starts NOW, so the host drops the run it was\n // showing before the invoke goes out — the canvas returns to editable on\n // the user's action, not on this test's eventual result.\n onTestRun?.();\n const node = nodes.find((n) => n.id === id);\n if (!node) return;\n const step = node.data.step;\n const isTrigger = isTriggerApp(step.uses.app);\n // A trigger's filled fields ARE the run's starting state: the handler\n // returns `params.input` verbatim, so anything else comes back `{}`. Every\n // other step is ALREADY CONFIGURED — `values` is the incoming state the\n // collect form asked for, merged over the step's STORED `with`, which is\n // exactly the payload the Test tab sends (`testValues`). The stored `with`\n // is read, never written: a run does not rewrite the step's configuration.\n const payload = isTrigger ? { input: values } : { ...(step.with ?? {}), ...values };\n // The fixture saved alongside the run, in the same two slots the Test tab\n // writes: `input` is the run's incoming state — for a trigger its filled\n // values *are* it, for every other step it is what the incoming-state box\n // held — and `with` is the full set of params the call was made with.\n const fixture = isTrigger\n ? { input: values, with: values }\n : { input: values, with: payload };\n setRunResult({ stepId: id, status: \"running\" });\n try {\n const result = await api.invokeAction(step.uses.app, step.uses.action, payload, {\n ...(step.uses.connection ? { connectionId: step.uses.connection } : {}),\n // Scope document expressions to the workflow's selected project (T2.1.2).\n project,\n // Omitted when nothing upstream has a saved output, so a run with no\n // seed sends exactly the request it sent before.\n ...(state ? { state } : {}),\n });\n // Script nodes may return captured console output alongside the value.\n const logs = (result as { logs?: string[] }).logs;\n const meta = result.meta;\n setRunResult({\n stepId: id,\n status: \"done\",\n value: result.value,\n logs,\n ...(meta ? { meta } : {}),\n });\n // Remember the values and log the run against the fixture they were\n // saved as (best-effort — never fail the run over it).\n persistStepRun(id, fixture, {\n status: \"succeeded\",\n output: result.value,\n ...(meta ? { meta } : {}),\n });\n } catch (e) {\n // The api client wraps network/parse failures with context; duck-type the\n // code so the modal can show it next to the message.\n const err = e as { message?: string; code?: string };\n setRunResult({\n stepId: id,\n status: \"error\",\n error: err.message ?? String(e),\n errorCode: err.code,\n });\n // A failed run still remembers what was entered — the operator's next\n // attempt starts from the values that failed, not from blank.\n persistStepRun(id, fixture, { status: \"failed\", error: err.message ?? String(e) });\n }\n },\n [nodes, api, persistStepRun, project, onTestRun],\n );\n\n const controls = useMemo<StepControls>(\n () => ({\n onEdit: (id) => {\n setEditView(\"props\");\n setEditingId(id);\n },\n onEditJson: (id) => {\n setEditView(\"json\");\n setEditingId(id);\n },\n onRun: runStep,\n onDuplicate: duplicateStep,\n onDelete: deleteStep,\n onAddFromPort: addFromPort,\n readOnly,\n }),\n [runStep, duplicateStep, deleteStep, addFromPort, readOnly],\n );\n\n // Opens the step editor on a canvas node double-click — deliberately NOT\n // React Flow's own node-double-click prop on `<ReactFlow>` below. That prop\n // fires from React's root container, an ANCESTOR of `.react-flow__pane` under\n // React 18's event delegation, so it always loses the race to the pane's own\n // native `dblclick.zoom` listener and can't stop it. A capture-phase listener\n // on this container fires first instead.\n //\n // The guard matters specifically in `readOnly`: `nodesDraggable={!readOnly}`\n // below drops `nopan` from the node wrapper's class list for a non-draggable\n // node, and for THIS scenario — a non-wheel double-click with no active\n // selection or connection in progress — `@xyflow/system`'s zoom filter's\n // remaining check is `.nopan` — so without this listener winning the race,\n // a viewer's double-click silently zooms the canvas instead of opening the\n // modal.\n useEffect(() => {\n const div = flowContainerRef.current;\n if (!div) return;\n const handler = (e: MouseEvent) => {\n const card = (e.target as HTMLElement).closest(\".react-flow__node\");\n if (!card) return; // not over a node — let the pane's own dblclick-zoom run\n const id = card.getAttribute(\"data-id\");\n if (!id) return;\n // CAPTURE phase (registered below) is what makes this run before the\n // pane's bubble-phase `dblclick.zoom` listener; stopping it here is what\n // actually suppresses the zoom.\n e.stopPropagation();\n // Same transition the node toolbar's pencil button performs — no new\n // plumbing, `controls` already carries `onEdit`.\n controls.onEdit(id);\n };\n div.addEventListener(\"dblclick\", handler, true);\n return () => div.removeEventListener(\"dblclick\", handler, true);\n }, [controls]);\n\n const editingNode = nodes.find((n) => n.id === editingId);\n const editingStep = editingNode?.data.step ?? null;\n // The step the run modal is open on, resolved live so the collect phase reads\n // the current `with` (a trigger's declared `fields`) rather than a snapshot.\n const runningStep = runResult\n ? (nodes.find((n) => n.id === runResult.stepId)?.data.step ?? null)\n : null;\n // Phase 1 of the run modal: collect the run's incoming state, nothing invoked\n // yet. The collect form offers the same upstream seed chips the Test tab does,\n // so it needs the RUNNING step's graph ancestors — the step editor's own\n // `upstreamState` below is computed for `editingId`, a different step.\n const collecting = runResult?.status === \"collect\" && !!runningStep;\n const runUpstreamSteps = useMemo(\n () => (runResult ? upstreamStateSources(runResult.stepId, nodes, edges).steps : []),\n [runResult, nodes, edges],\n );\n\n const fanOutCounts = useMemo(() => {\n const m = new Map<string, number>();\n for (const n of nodes) m.set(n.id, fanOutState(n.id, edges).count);\n return m;\n }, [nodes, edges]);\n\n const nodeTypes = useMemo(\n () => ({\n step: StepNodeCard,\n control: ControlNodeCard,\n }),\n [],\n );\n\n // The workflow state in scope for the step being edited: its upstream steps'\n // outputs (`steps.<id>.output`) and, if a trigger precedes it, `trigger.event`.\n //\n // Each upstream step also carries its pickable output FIELDS when a shape is\n // known (`output-shape.ts`): an app action's declared `output`, else its\n // `sample`, else that step's last test run — so a later step can reference\n // `steps.<id>.output.<field>` without running anything first.\n const [stepTestOutputs, setStepTestOutputs] = useState<Record<string, unknown>>({});\n const [stepTestMetas, setStepTestMetas] = useState<Record<string, StepMeta>>({});\n const stepShapes = useMemo<StepShapeContext>(\n () => ({\n actionDef: (app, action) => actionDefs[app]?.find((a) => a.key === action),\n testOutputs: stepTestOutputs,\n }),\n [actionDefs, stepTestOutputs],\n );\n // While the add-step builder is open `editingId` is null, which makes\n // `upstreamStateSources` offer EVERY node — including the step being drafted\n // (progressively committed as a node). Exclude it from its own rail sources.\n const [draftStepId, setDraftStepId] = useState<string | null>(null);\n const upstreamState = useMemo(() => {\n const st = upstreamStateSources(editingId, nodes, edges, stepShapes);\n return { ...st, steps: excludeSelfStep(st.steps, editingId ?? draftStepId) };\n }, [editingId, draftStepId, nodes, edges, stepShapes]);\n // A stable identity for *which* steps are upstream. The memo above rebuilds on\n // every node drag and every field edit; this string changes only when the SET\n // does, so the fetch below doesn't re-run on each keystroke.\n const upstreamIdsKey = JSON.stringify(upstreamState.steps.map((s) => s.id));\n\n // Design-time sample values for the expression editor's Result pane: what each\n // upstream step's LAST TEST RUN actually captured, flattened onto the very refs\n // the picker inserts (`steps.<id>.output` and `steps.<id>.output.<field>`). Read\n // from the saved fixtures (`StepTest.lastRunOutput`), so it survives a reload.\n //\n // ⚠️ EDITOR-SIDE ONLY, like `upstreamStateSources` above: this is a PREVIEW of\n // values a past test produced. It is not what a full run resolves — nothing here\n // is sent to the engine.\n useEffect(() => {\n const ids = JSON.parse(upstreamIdsKey) as string[];\n if (!editingId || ids.length === 0) {\n setStepTestOutputs({});\n setStepTestMetas({});\n return;\n }\n let canceled = false;\n // Best-effort, per step: a failed or empty list contributes no entry and never\n // breaks the picker — the same shape as the step editor's seed effect.\n Promise.all(\n ids.map(async (id) => {\n try {\n const tests = await api.listStepTests(value.id, id);\n // `listStepTests` is oldest-first, so the LAST entry is the most recent\n // fixture — the \"latest\" convention this file already uses twice.\n const latest = tests.length ? tests[tests.length - 1] : null;\n return latest ? { id, output: latest.lastRunOutput, meta: latest.lastRunMeta } : null;\n } catch {\n return null;\n }\n }),\n ).then((res) => {\n // LOAD-BEARING, and its failure mode is silent: without this guard a slow\n // list that resolves after the editor was closed and reopened overwrites\n // the fresh samples with the ones it read before — the pane then previews a\n // POPULATED, STALE value, which is worse than showing none. Pinned by\n // `artifacts/T5.1.3-r2-checks.sh` §X (mutant M11).\n if (canceled) return;\n const next: Record<string, unknown> = {};\n const nextMetas: Record<string, StepMeta> = {};\n for (const r of res) {\n if (r?.meta) nextMetas[r.id] = r.meta;\n if (!r || r.output === undefined || r.output === null) continue;\n next[r.id] = r.output;\n }\n setStepTestOutputs(next);\n setStepTestMetas(nextMetas);\n });\n return () => {\n canceled = true;\n };\n }, [api, value.id, editingId, upstreamIdsKey]);\n // The last test output, flattened onto the very refs the picker inserts: the\n // whole output, and each offered field path (nested ones included). Values\n // pass through unstringified — the modal's `effectiveSamples` stringifies.\n const stepSampleValues = useMemo(() => {\n const next: Record<string, unknown> = {};\n for (const st of upstreamState.steps) {\n Object.assign(next, stepMetaSampleValues(st.id, stepTestMetas[st.id]));\n const output = stepTestOutputs[st.id];\n if (output === undefined) continue;\n next[`steps.${st.id}.output`] = output;\n // Every own key of a plain object, as before…\n if (typeof output === \"object\" && output !== null && !Array.isArray(output)) {\n for (const [k, v] of Object.entries(output as Record<string, unknown>)) {\n next[`steps.${st.id}.output.${k}`] = v;\n }\n }\n // …plus each offered nested field path.\n for (const o of st.outputs ?? []) {\n if (!o.path || !o.key.includes(\".\")) continue;\n const v = valueAtPath(output, o.key);\n if (v !== undefined) next[`steps.${st.id}.output.${o.key}`] = v;\n }\n }\n return next;\n }, [upstreamState.steps, stepTestOutputs, stepTestMetas]);\n\n // …merged with the host-supplied vars/secrets/sealSecret so the expression\n // editor's left panel shows every source at once.\n //\n // `ExpressionOptionsProvider` below LAYERS over whatever this editor is\n // mounted under, so the project's vars/secrets/documents arrive on their own\n // and need not be restated here — the `exprOptions` PROP is now only for a\n // host that wants to override or add something at this level.\n //\n // `sampleValues` is the one key that must UNION rather than replace, and that\n // is why the inherited scope is read explicitly: three disjoint ref\n // namespaces contribute to it (`vars.*`/`documents.*` from the app shell,\n // anything the prop adds, and `steps.*` computed here), and layering alone\n // would let the object built below shadow the inherited one wholesale.\n const inheritedExprOptions = useExpressionOptions();\n const mergedExprOptions = useMemo<ExpressionOptions>(\n () => ({\n ...(exprOptions ?? {}),\n steps: upstreamState.steps,\n hasTrigger: upstreamState.hasTrigger,\n sampleValues: {\n ...(inheritedExprOptions.sampleValues ?? {}),\n ...(exprOptions?.sampleValues ?? {}),\n ...stepSampleValues,\n },\n }),\n [exprOptions, inheritedExprOptions.sampleValues, upstreamState, stepSampleValues],\n );\n\n return (\n <StepControlsCtx.Provider value={controls}>\n <PortsLookupCtx.Provider value={portsLookup}>\n <FanOutCountsCtx.Provider value={fanOutCounts}>\n <AppsCtx.Provider value={appsById}>\n <WorkflowProjectProvider project={project}>\n <ExpressionOptionsProvider value={mergedExprOptions}>\n <div\n ref={flowContainerRef}\n className=\"w6w-flow\"\n style={{ width: \"100%\", height, position: \"relative\" }}\n onKeyDown={(e) => {\n if (e.key !== \"Backspace\" && e.key !== \"Delete\") return;\n // Only delete the selected node/edge when the key is aimed at the\n // canvas — never while a modal is open or the user is editing a field.\n // The modal <dialog> is a DOM descendant here, so its keystrokes\n // bubble up; without this guard, backspacing a typo deletes a node.\n if (editingId || builderOpen || (!selectedId && !selectedEdgeId)) return;\n const t = e.target as HTMLElement;\n if (\n t.isContentEditable ||\n t.tagName === \"INPUT\" ||\n t.tagName === \"TEXTAREA\" ||\n t.tagName === \"SELECT\" ||\n t.closest(\"dialog, .w6w-modal\") !== null\n ) {\n return;\n }\n e.preventDefault();\n // A selected node takes precedence (its confirm); else drop the edge.\n if (selectedId) deleteStep(selectedId);\n else if (selectedEdgeId) deleteEdge(selectedEdgeId);\n }}\n >\n <ReactFlow\n nodes={nodes}\n edges={edges}\n // Straight segments with right-angle corners, not the library's\n // default bezier curve — reads clearer on a wide, branchy graph\n // than a sweeping curve does. One place: no edge object sets its\n // own `type`, so this alone governs every edge on the canvas.\n // `connectionLineType` matches it for the in-progress drag line,\n // so the preview doesn't curve while the settled edge won't.\n defaultEdgeOptions={{ type: \"smoothstep\" }}\n connectionLineType={ConnectionLineType.SmoothStep}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onConnect={onConnect}\n onConnectEnd={onConnectEnd}\n isValidConnection={isValidConnection}\n // A finished drag is what makes a new coordinate stick: until this\n // existed, dragging went only through `onNodesChange` and never\n // marked the workflow changed, so the position was lost on reload.\n // `flowToWorkflow` (inside `emitChange`) does the conversion and the\n // rounding — nothing is converted here.\n //\n // Drag STOP, never `onNodeDrag`: the per-frame variant fires on every\n // animation frame of the gesture, and the host's auto-save is a\n // trailing debounce over emitted changes, so a per-frame emit is a\n // write storm by construction.\n //\n // No `readOnly` term is needed: `nodesDraggable={!readOnly}` below\n // means React Flow never starts the drag, so this cannot fire at all\n // in a read-only editor. Measured, not assumed — the probe's readOnly\n // fixture asserts the node does not move (F4-V1).\n onNodeDragStop={(_e, _n, dragged) => {\n const nextCanvas = membershipAfterDrag(dragged as StepNode[]);\n const membershipChanged = nextCanvas !== canvas;\n if (membershipChanged) setCanvas(nextCanvas);\n if (!savePosition && !membershipChanged) return;\n emitChange(nodes, edges, nextCanvas);\n }}\n onMoveEnd={(event, viewport) => {\n // FIRST statement, and load-bearing. React Flow passes\n // `event === null` when the move was NOT user-initiated\n // (@xyflow/react 12.11.1, `types/component-props.d.ts` on\n // `onMoveEnd`: \"If the movement is not user-initiated, the event\n // parameter will be `null`.\"). The `fitView` on open is exactly\n // such a move — without this guard every open would emit a change,\n // and with the host's auto-save every page load would be a write.\n if (!event) return;\n // `readOnly` is checked here and NOT delegated, because — unlike a\n // drag — panning and zooming stay enabled in a read-only editor (see\n // the prop's docstring), so this handler really does fire and a\n // viewer must not write to what they are only looking at.\n if (readOnly) return;\n // `savePosition` is deliberately NOT re-checked here: `withViewport`\n // is the single place that reads the flag for this path and returns\n // its argument unchanged when it is off, so a duplicate term would be\n // unobservable — measured as a surviving mutant (T3.3.2-mutants.sh\n // C5) and removed rather than shipped as an unpinnable branch.\n //\n // Emit ONLY when `withViewport` hands back a different reference:\n // it returns `value`'s base unchanged when the rounded viewport\n // already equals the stored one, so a pan that lands back where it\n // started emits nothing. Rate is bounded by the gesture, not the\n // frame — `onMoveEnd` is d3-zoom's terminal event, and\n // `@xyflow/system` additionally coalesces scroll-driven pans behind\n // a 150ms trailing timer of its own (no timer belongs here).\n const base = flowToWorkflow(value, nodes, edges);\n const next = withViewport(base, viewport);\n if (next !== base) onChange(next);\n }}\n onSelectionChange={({ nodes: sel, edges: edgeSel }) => {\n // ⚠️ Only primitives are stored from this handler: React Flow re-invokes\n // it (inline) after every re-render of this component, so storing a\n // fresh array here defeats React's `Object.is` bail-out and loops —\n // a silent CPU burn in a page, a frozen main thread under Storybook.\n setSelectedId(sel[0]?.id ?? null);\n setSelectedEdgeId(edgeSel[0]?.id ?? null);\n // Deliberately NOT clearing `laneError` here — see its declaration.\n }}\n nodeTypes={nodeTypes}\n nodesDraggable={!readOnly}\n nodesConnectable={!readOnly}\n // A click on a port adds a step from it (PortHandle) — not the library's\n // click-a-port-then-click-another connect mode, which shows nothing\n // after the first click and reads as a dead port.\n connectOnClick={false}\n elementsSelectable\n // Reopen at the camera the author left, when the workflow stores one;\n // otherwise fit the graph exactly as before. The two props are spread\n // in so only ONE of them is ever passed: they are mutually exclusive\n // by the library's own rule — \"If a default viewport is provided but\n // `fitView` is enabled, the default viewport will be ignored\"\n // (`types/component-props.d.ts` on `defaultViewport`) — and a stray\n // `fitView` would silently discard the restored view.\n {...(savedViewport ? { defaultViewport: savedViewport } : { fitView: true })}\n // Deletion is owned solely by the guarded onKeyDown handler above\n // (canvas-only, with a confirm). Disable React Flow's built-in\n // Backspace/Delete so it can't silently remove a node — e.g. while a\n // modal is open or the user is editing a field.\n deleteKeyCode={null}\n colorMode={colorMode}\n proOptions={{ hideAttribution: true }}\n >\n <Background gap={16} />\n {/* `showInteractive={false}` still suppresses the lock button; the\n child is appended AFTER the three built-ins (zoom in / zoom out /\n fit view), so auto-layout reads as the fourth control in the same\n stack — \"beside the zoom controls\", not a floating panel. It\n carries NO colour, background or border of its own: the\n `--xy-controls-button-*` → `--w6w-*` bridge in `styles.css` sets\n inherited custom properties on the `.w6w-flow` wrapper, so any\n control button added later is themed for free (measured in both\n stylesheet orders — T3.1.1's evaluation built this exact shape). */}\n <Controls showInteractive={false}>\n {!readOnly && (\n <ControlButton\n onClick={requestRelayout}\n title=\"Auto-layout — re-flow the graph into columns\"\n aria-label=\"Auto-layout the graph\"\n >\n {/* `icons.tsx`'s `layout` — the three-box hierarchy. It must NOT be a\n `fill=\"none\" stroke=…` glyph: the library styles a control button's\n svg as `svg { width: 100%; max-width: 12px; max-height: 12px;\n fill: currentColor }` (`@xyflow/react/dist/style.css`), so a stroked\n root would render as nothing at all in here — hence `layout`'s bare\n root (see its entry). */}\n <Icon name=\"layout\" />\n </ControlButton>\n )}\n </Controls>\n <MiniMap pannable zoomable style={{ background: \"var(--w6w-panel-2)\" }} />\n {!readOnly && (\n <Panel position=\"top-left\" className=\"w6w-flow-tools\">\n <button\n type=\"button\"\n className=\"w6w-btn\"\n onClick={() => setBuilderOpen(true)}\n >\n + Node\n </button>\n <IconButton\n label=\"Add note\"\n icon=\"sticky-note\"\n className=\"w6w-flow-tool\"\n data-testid=\"add-note\"\n onClick={addNote}\n />\n <IconButton\n label=\"Draw section\"\n title=\"Draw section — drag a frame on the canvas (Esc cancels)\"\n icon=\"frame\"\n className={`w6w-flow-tool${drawingSection ? \" active\" : \"\"}`}\n aria-pressed={drawingSection}\n data-testid=\"draw-section\"\n onClick={() => {\n setDrawingSection(!drawingSection);\n setSectionDraft(null);\n }}\n />\n </Panel>\n )}\n {/* The draw-section surface: above the graph (z 4, after the renderer)\n and below the panels (z 5), so the tool button stays clickable. */}\n {!readOnly && drawingSection && (\n <div\n className=\"w6w-section-draw nopan nodrag nowheel\"\n data-testid=\"section-draw-surface\"\n onPointerDown={(e) => {\n if (e.button !== 0) return;\n e.currentTarget.setPointerCapture(e.pointerId);\n const { clientX: x, clientY: y } = e;\n setSectionDraft({ sx: x, sy: y, ex: x, ey: y });\n }}\n onPointerMove={(e) => {\n const { clientX: x, clientY: y } = e;\n setSectionDraft((d) => d && { ...d, ex: x, ey: y });\n }}\n onPointerUp={() => {\n const d = sectionDraft;\n setSectionDraft(null);\n setDrawingSection(false);\n if (!d) return;\n const a = screenToFlowPosition({ x: d.sx, y: d.sy });\n const dragged = Math.abs(d.ex - d.sx) > 6 || Math.abs(d.ey - d.sy) > 6;\n addSectionAt(\n a,\n dragged ? screenToFlowPosition({ x: d.ex, y: d.ey }) : null,\n );\n }}\n >\n {sectionDraft &&\n (() => {\n const box = flowContainerRef.current?.getBoundingClientRect();\n const ox = box?.left ?? 0;\n const oy = box?.top ?? 0;\n return (\n <div\n className=\"w6w-section-draw-rect\"\n style={{\n left: Math.min(sectionDraft.sx, sectionDraft.ex) - ox,\n top: Math.min(sectionDraft.sy, sectionDraft.ey) - oy,\n width: Math.abs(sectionDraft.ex - sectionDraft.sx),\n height: Math.abs(sectionDraft.ey - sectionDraft.sy),\n }}\n />\n );\n })()}\n </div>\n )}\n {/* Which outcome the selected edge carries (core rfcs/workflow.md ·\n `Edge.when`). Revealed only when exactly ONE edge is selected —\n `selectedEdgeId` is already `edgeSel[0]`, so selecting a node or\n nothing hides it. top-left is `+ Node`, Controls are bottom-left,\n the MiniMap bottom-right. D-T1-7 SUPERSEDED (T1.1.1): id'd source\n handles now exist (the error exit port), so a fresh edge's lane IS\n chosen by which handle it was dragged from — this panel keeps its\n job as the RE-LANE affordance for an edge already drawn on the\n wrong port, not the only way to author an error edge. */}\n {!readOnly && selectedEdge && (\n <Panel position=\"top-right\">\n <div className=\"w6w-edge-lane\">\n <span className=\"w6w-muted w6w-small\">Run on</span>\n {([\"success\", \"error\"] as const).map((lane) => (\n <button\n key={lane}\n type=\"button\"\n className={`w6w-btn w6w-btn-sm w6w-btn-ghost${\n edgeLane(selectedEdge) === lane ? \" active\" : \"\"\n }`}\n aria-pressed={edgeLane(selectedEdge) === lane}\n title={LANE_HINT}\n onClick={() => setEdgeLane(lane)}\n >\n {lane === \"success\" ? \"Success\" : \"Error\"}\n </button>\n ))}\n {laneError?.edgeId === selectedEdge.id && (\n <span className=\"w6w-edge-lane-err w6w-small\" role=\"alert\">\n {laneError.message}\n </span>\n )}\n </div>\n </Panel>\n )}\n <CanvasLayer\n canvas={canvas}\n readOnly={readOnly}\n savePosition={savePosition}\n onChange={onCanvasChange}\n onSectionDrop={onSectionDrop}\n onRequestDelete={(kind, id) =>\n !readOnly && setPendingCanvasDelete({ kind, id })\n }\n />\n </ReactFlow>\n {pendingCanvasDelete && (\n <ConfirmModal\n title={pendingCanvasDelete.kind === \"note\" ? \"Delete note\" : \"Delete section\"}\n message={\n pendingCanvasDelete.kind === \"note\"\n ? \"Delete this note?\"\n : \"Delete this section? Its nodes are kept.\"\n }\n confirmLabel=\"Delete\"\n onConfirm={() => {\n performCanvasDelete(pendingCanvasDelete.kind, pendingCanvasDelete.id);\n setPendingCanvasDelete(null);\n }}\n onClose={() => setPendingCanvasDelete(null)}\n />\n )}\n\n {builderOpen && (\n <StepBuilderModal\n onClose={() => {\n setBuilderOpen(false);\n setPendingConnect(null);\n setDraftStepId(null);\n }}\n onAdd={(built) => {\n const id = addBuiltStep(built);\n if (id) setDraftStepId(id);\n return id;\n }}\n // Progressive commit (T4.1.1): once `addBuiltStep` has minted\n // the step's id, every subsequent field change updates that\n // same node instead of waiting for a final \"Add node\" click.\n onDraftChange={(id, next) => updateStep(id, { id, ...next })}\n workflowId={value.id}\n // The new step's known upstream ancestors, from the handle a\n // connection drag was released from (T1.1.1) — so the builder's\n // own Test tab can seed `{{ steps.<id>.output.<field> }}` the\n // same way the step editor's Test tab already does.\n upstreamSteps={stepBuilderUpstreamSteps(pendingConnect, nodes, edges)}\n />\n )}\n\n {editingStep && editingId && (\n // No `key` on purpose: renaming a step updates `editingId`, and a keyed\n // remount would drop focus mid-keystroke. The modal seeds its own state\n // once and unmounts (editingId → null) between edits of different nodes.\n <StepEditModal\n workflowId={value.id}\n step={editingStep}\n // Graph ancestors of the editing step, from `upstreamStateSources`\n // (via `mergedExprOptions`) — the incoming-state picker seeds from\n // each ancestor's latest saved step-test rather than re-walking the graph.\n upstreamSteps={mergedExprOptions.steps ?? []}\n readOnly={readOnly}\n fanOutCount={fanOutCounts.get(editingId) ?? 0}\n initialView={editView}\n stepProblem={editingNode?.data.stepProblem}\n onChange={(next) => updateStep(editingId, next)}\n onClose={() => setEditingId(null)}\n onTestRun={onTestRun}\n />\n )}\n\n {runResult && (\n <Modal\n title={`${collecting ? \"Run node\" : \"Test run\"}: ${runResult.stepId}`}\n onClose={() => setRunResult(null)}\n >\n {collecting && runningStep && (\n <StepRunCollect\n // Keyed on the step so switching nodes re-seeds the form.\n key={runResult.stepId}\n workflowId={value.id}\n step={runningStep}\n upstreamSteps={runUpstreamSteps}\n onCancel={() => setRunResult(null)}\n onRun={(values, state) => performRunStep(runResult.stepId, values, state)}\n />\n )}\n {/* The collect phase renders its own actions (Run + Cancel),\n gated on the required fields being filled. The status/result/log\n blocks scroll inside the body; Close stays pinned below it. */}\n {!collecting && (\n <>\n <div className=\"w6w-modal-body\">\n {runResult.status === \"running\" && (\n <p className=\"w6w-muted w6w-small\">Running…</p>\n )}\n {runResult.status === \"error\" && (\n <div className=\"w6w-result w6w-error\">\n {runResult.errorCode && (\n <div\n className=\"w6w-small\"\n style={{ opacity: 0.75, marginBottom: 4 }}\n >\n <code>{runResult.errorCode}</code>\n </div>\n )}\n {runResult.error || \"The node failed with no error message.\"}\n </div>\n )}\n {runResult.status === \"done\" && (\n <div>\n <div className=\"w6w-muted w6w-small\" style={{ marginBottom: 6 }}>\n Result\n </div>\n <pre\n className=\"w6w-result\"\n style={{\n whiteSpace: \"pre-wrap\",\n maxHeight: 360,\n overflow: \"auto\",\n margin: 0,\n }}\n >\n {JSON.stringify(runResult.value, null, 2)}\n </pre>\n {runResult.meta && (\n <>\n <div\n className=\"w6w-muted w6w-small\"\n style={{ margin: \"10px 0 6px\" }}\n >\n Metadata\n </div>\n <pre\n className=\"w6w-result\"\n style={{\n whiteSpace: \"pre-wrap\",\n maxHeight: 360,\n overflow: \"auto\",\n margin: 0,\n }}\n >\n {JSON.stringify(runResult.meta, null, 2)}\n </pre>\n </>\n )}\n </div>\n )}\n {runResult.logs && runResult.logs.length > 0 && (\n <div>\n <div\n className=\"w6w-muted w6w-small\"\n style={{ margin: \"10px 0 6px\" }}\n >\n Console output\n </div>\n <pre\n className=\"w6w-result\"\n style={{\n whiteSpace: \"pre-wrap\",\n maxHeight: 200,\n overflow: \"auto\",\n margin: 0,\n }}\n >\n {runResult.logs.join(\"\\n\")}\n </pre>\n </div>\n )}\n </div>\n <div className=\"w6w-modal-actions\">\n <button\n type=\"button\"\n className=\"w6w-btn\"\n onClick={() => setRunResult(null)}\n >\n Close\n </button>\n </div>\n </>\n )}\n </Modal>\n )}\n\n {pendingDelete !== null && (\n <ConfirmModal\n title=\"Delete node\"\n message={`Delete node \"${pendingDelete}\"? Its connections are removed too.`}\n confirmLabel=\"Delete\"\n onConfirm={() => {\n performDeleteStep(pendingDelete);\n setPendingDelete(null);\n }}\n onClose={() => setPendingDelete(null)}\n />\n )}\n </div>\n </ExpressionOptionsProvider>\n </WorkflowProjectProvider>\n </AppsCtx.Provider>\n </FanOutCountsCtx.Provider>\n </PortsLookupCtx.Provider>\n </StepControlsCtx.Provider>\n );\n}\n\n/**\n * Parse the incoming-state box into the object it stands for. Anything that is\n * not a JSON **object** — unparseable, an array, a scalar, `null` — is not a\n * state at all and reads as `null`, which the callers turn into \"merge nothing\"\n * (Test) or \"don't run\" (▶ Run, a real execution).\n */\nfunction parseStateOverride(text: string): Record<string, unknown> | null {\n try {\n const v = JSON.parse(text);\n return v && typeof v === \"object\" && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The **incoming state** control — the one implementation behind both the step\n * editor's Test tab and the canvas ▶ Run collect form for a non-trigger step.\n * What varies between two runs of an already-configured step is the data\n * arriving at it, not its parameters, so this is what both surfaces collect.\n *\n * The raw JSON box is an **override** and is titled as one: it sits behind a\n * closed disclosure so neither surface opens on a JSON textarea, while the\n * upstream seed chips stay in plain sight above it. The disclosure opens by\n * itself whenever an override actually exists — seeded from a chip, remembered\n * from the last run, or typed — so an active override is never hidden.\n *\n * ⚠️ This component says nothing about what a **production** run resolves; it\n * collects what the test/run call is given.\n */\nfunction IncomingStateField({\n text,\n onChange,\n seeds,\n readOnly,\n}: {\n /** Raw JSON text of the override. `{}` (the default) means \"no override\". */\n text: string;\n onChange: (next: string) => void;\n seeds: SeedSource[];\n readOnly?: boolean;\n}) {\n const parsed = parseStateOverride(text);\n const keyCount = parsed ? Object.keys(parsed).length : 0;\n const hasOverride = keyCount > 0;\n const [open, setOpen] = useState(hasOverride);\n // An override usually arrives AFTER this field has mounted — the Test tab's\n // remembered-state effect (`StepEditModal`) sets the text a network hop later\n // — and `useState`'s argument is a MOUNT-TIME value that never re-derives. Do\n // not collapse this back into the initial value: with `listStepTests` delayed\n // 900 ms, that left a remembered override IN EFFECT behind a CLOSED\n // disclosure (measured in T6.1.1's evaluation; ▶ Run escaped it only because\n // it withholds the whole form until its fixture has loaded). Only the\n // \"nothing → something\" transition reopens it, so a disclosure the operator\n // closed themselves stays closed while they keep typing in it.\n useEffect(() => {\n if (hasOverride) setOpen(true);\n }, [hasOverride]);\n // Seeding from an ancestor uses the output its last run captured; only a\n // fixture that never ran falls back to its own captured incoming state.\n const seedFrom = (test: StepTest) => {\n onChange(JSON.stringify(test.lastRunOutput ?? test.input ?? {}, null, 2));\n setOpen(true);\n };\n return (\n <div className=\"w6w-field w6w-incoming-state\">\n <span>Incoming state</span>\n {seeds.length > 0 && (\n <div className=\"w6w-seed-picker\">\n <div className=\"w6w-seed-chips\">\n {seeds.map((s) => (\n <button\n key={s.stepId}\n type=\"button\"\n className=\"w6w-chip w6w-seed-chip\"\n title={`Use ${s.label}'s saved test as the incoming state`}\n disabled={readOnly}\n onClick={() => seedFrom(s.test)}\n >\n <code>{s.label}</code>\n {s.test.lastRunStatus ? (\n <span className=\"w6w-muted w6w-small\">\n {\" · \"}\n {s.test.lastRunStatus}\n </span>\n ) : null}\n </button>\n ))}\n </div>\n </div>\n )}\n <details\n className=\"w6w-state-override\"\n open={open}\n onToggle={(e) => setOpen(e.currentTarget.open)}\n >\n <summary>\n Override the incoming state\n {keyCount > 0 ? ` · ${keyCount} key${keyCount === 1 ? \"\" : \"s\"} set` : \"\"}\n </summary>\n <textarea\n rows={3}\n value={text}\n readOnly={readOnly}\n spellCheck={false}\n aria-label=\"Incoming state override (JSON)\"\n onChange={(e) => onChange(e.target.value)}\n />\n <span className=\"w6w-hint\">\n JSON merged into the call (e.g. a script's <code>input</code>). Reach for this to test\n against a state the flow cannot currently produce — a field no upstream step returns yet,\n or a case you can't reproduce by running the flow.\n </span>\n </details>\n </div>\n );\n}\n\n/**\n * Phase 1 of the canvas ▶ run: **collect the run's incoming state**, then hand\n * it to the host to invoke. Rendered inside the run modal, which is the chrome.\n *\n * Two shapes, because the two kinds of step start from different things:\n * - a **trigger** has no incoming state — it *is* the start — so it projects\n * its configured `fields` *definitions* into params (`fieldsToParams`), seeds\n * from their declared defaults (`seedValues`) and renders them through\n * {@link PropertyEntryForm} (chrome-less by contract, so it is mounted bare\n * here). The filled values become the run's starting state (`{ input }`), and\n * the step's **last saved values** come back pre-filled on top (T4.1.3).\n * - **everything else** is already configured, and what changes between two\n * runs is the data arriving at it. So it collects exactly what the Test tab\n * collects — {@link IncomingStateField}, the same component — and runs the\n * step with its **stored** `with` underneath. The configuration is not\n * re-collected here and is never rewritten by a run (R-I-1: the ▶ modal used\n * to render the Configure form).\n *\n * The trigger check comes first on purpose: a trigger *is* an internal node, and\n * its internal schema is the `fields` **editor**, not the fields themselves.\n *\n * `params` is fetched on **both** paths — a trigger renders them, and a\n * non-trigger needs them for the same required-params gate the Test tab applies\n * to `{...step.with, ...state}`. An app action fetches its action definition\n * exactly as `StepEditModal` does, showing \"Loading parameters…\" until it lands.\n */\nfunction StepRunCollect({\n workflowId,\n step,\n upstreamSteps,\n onRun,\n onCancel,\n}: {\n /** Fixture key, with `step.id` — where the remembered values are read from. */\n workflowId: string;\n step: FlowStep;\n /** Graph ancestors of this step; their saved tests are offered as seeds. */\n upstreamSteps: ExpressionStepSource[];\n /**\n * Fired when the user presses Run — with the trigger's filled values, or (for\n * every other step) the incoming-state override alone, plus the start state\n * seeded from the upstream steps' saved outputs (so the step's own\n * `{{ steps.<id>.output.<field> }}` references resolve). The two are\n * different things and stay separate: `values` are params, `state` is scope.\n */\n onRun: (values: Record<string, unknown>, state?: StepStartState) => void;\n onCancel: () => void;\n}) {\n const api = useW6WApi();\n const isTrigger = isTriggerApp(step.uses.app);\n const isInternal = isInternalApp(step.uses.app);\n const defs = useMemo(\n () => (isTrigger ? asFieldDefs(step.with?.fields) : []),\n [isTrigger, step.with],\n );\n\n // Known synchronously for triggers and internal nodes; `null` means \"an app\n // action, still to be fetched\" so only that path shows a loading line.\n const localParams = useMemo<ActionParam[] | null>(() => {\n if (isTrigger) return fieldsToParams(defs);\n if (isInternal) return internalNodeParams(step.uses.app, step.uses.action);\n return null;\n }, [isTrigger, isInternal, defs, step.uses.app, step.uses.action]);\n const [fetched, setFetched] = useState<ActionParam[] | null>(null);\n const params = localParams ?? fetched;\n\n // Mirrors StepEditModal's \"refetch actions + params\" effect rather than\n // inventing a second fetch pattern.\n useEffect(() => {\n if (localParams) return;\n if (!step.uses.app || !step.uses.action) {\n setFetched([]);\n return;\n }\n let canceled = false;\n setFetched(null);\n api\n .getAppActions(step.uses.app)\n .then((acts) => {\n if (canceled) return;\n setFetched(acts.find((a) => a.key === step.uses.action)?.params ?? []);\n })\n .catch(() => !canceled && setFetched([]));\n return () => {\n canceled = true;\n };\n }, [api, localParams, step.uses.app, step.uses.action]);\n\n // THE TRIGGER PATH's value bag, seeded once from its declared defaults.\n const [values, setValues] = useState<Record<string, unknown>>(() =>\n isTrigger ? seedValues(asFieldDefs(step.with?.fields)) : {},\n );\n // EVERY OTHER STEP's collect surface: the incoming-state override, as raw\n // JSON text — the same thing the Test tab collects, in the same component.\n const [stateText, setStateText] = useState(\"{}\");\n const seeds = useSeedSources(workflowId, upstreamSteps, !isTrigger);\n // The same fixtures the chips offer, projected into the run's start state —\n // one source, so what the chips SAY is upstream is what the run is GIVEN.\n const startState = startStateFromSeeds(seeds);\n\n // …then layered with what was entered the last time this step was tested or\n // run. `listStepTests` is oldest-first, so the LAST entry is the most recent\n // fixture — the same convention the step editor's upstream-seed effect uses.\n // Which slot is remembered follows what the surface collects: a trigger's\n // filled fields live in the fixture's `with`, an ordinary step's incoming\n // state in its `input` (that is what the Test tab writes there too).\n // An `ExprValue` (`{type:\"expr\",…}`) round-trips untouched: `FxField` derives\n // expression mode from the value, so it comes back as a chip, not as text.\n const [seeded, setSeeded] = useState(false);\n useEffect(() => {\n let canceled = false;\n api\n .listStepTests(workflowId, step.id)\n .then((tests) => {\n if (canceled) return;\n const latest = tests.length ? tests[tests.length - 1] : null;\n if (isTrigger) {\n if (latest?.with) setValues((v) => ({ ...v, ...latest.with }));\n } else if (latest?.input && Object.keys(latest.input).length > 0) {\n setStateText(JSON.stringify(latest.input, null, 2));\n }\n setSeeded(true);\n })\n // Never block or break the form: no fixture (or a failed list) just leaves\n // the declared defaults in place, silently.\n .catch(() => {\n if (!canceled) setSeeded(true);\n });\n return () => {\n canceled = true;\n };\n }, [api, workflowId, step.id, isTrigger]);\n\n // A raw-JSON draft that is not a values map (unparseable, or an array/scalar/\n // `null`) never reaches `values` — the entry form holds it back and says so\n // here. Run is a REAL execution, so it must not silently run the *previous*\n // payload while the operator is looking at a different one. The same applies\n // to the incoming-state box, which is parsed by the same rule.\n const [draftValid, setDraftValid] = useState(true);\n const state = parseStateOverride(stateText);\n const validDraft = isTrigger ? draftValid : state !== null;\n // What the run will actually be given. A non-trigger runs on its STORED\n // configuration with the incoming state merged over it — byte-for-byte what\n // the Test tab sends — so the required-params gate is applied to that, not to\n // the override alone. Never a second required-check.\n const effective = isTrigger ? values : { ...(step.with ?? {}), ...(state ?? {}) };\n const canRun = !!params && seeded && validDraft && requiredParamsFilled(params, effective);\n\n return (\n // `.w6w-stepconfig` frame + body + pinned footer (D-3): this component is a\n // direct <Modal> child, so the frame's `height:100%` resolves against the\n // dialog and only the form scrolls. The `.w6w-stack` keeps its own spacing.\n <div className=\"w6w-stepconfig\">\n <div className=\"w6w-modal-body\">\n <div className=\"w6w-stack\">\n {params === null || !seeded ? (\n <p className=\"w6w-muted w6w-small\">\n {params === null ? \"Loading parameters…\" : \"Loading saved values…\"}\n </p>\n ) : isTrigger ? (\n <>\n <PropertyEntryForm\n params={params}\n values={values}\n onChange={setValues}\n onValidityChange={setDraftValid}\n />\n {params.length === 0 && (\n <span className=\"w6w-hint\">\n This trigger declares no fields — provide a sample payload to run with. It becomes\n the trigger's output state (<code>input</code>).\n </span>\n )}\n </>\n ) : (\n <>\n <p className=\"w6w-muted w6w-small\">\n Runs <code>{step.uses.action}</code> with the configuration saved on this node —\n only the incoming state changes per run. Edit the node to change how it is\n configured.\n </p>\n <IncomingStateField text={stateText} onChange={setStateText} seeds={seeds} />\n </>\n )}\n </div>\n </div>\n <div className=\"w6w-modal-actions\">\n <button type=\"button\" className=\"w6w-btn w6w-btn-ghost\" onClick={onCancel}>\n Cancel\n </button>\n {!canRun && params !== null && seeded && (\n <span className=\"w6w-muted w6w-small\">\n {!validDraft\n ? isTrigger\n ? \"The payload must be a JSON object to run.\"\n : \"The incoming state must be a JSON object to run.\"\n : isTrigger\n ? \"Fill the required fields to test.\"\n : \"This step is missing required configuration — set it on the Configure tab.\"}\n </span>\n )}\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={!canRun}\n // A trigger IS the start state, so it seeds nothing from upstream —\n // its filled fields go out as params (`{ input }`) exactly as before.\n onClick={() =>\n onRun(isTrigger ? values : (state ?? {}), isTrigger ? undefined : startState)\n }\n >\n ▶ Run\n </button>\n </div>\n </div>\n );\n}\n\n// ── Node renderers ────────────────────────────────────────────────────────\n\n/**\n * A single connection handle, drawn to distinguish a single port from a\n * **multiple**-connection (fan-in / fan-out) port: a lone port keeps React\n * Flow's default dot, while `multiple` (cardinality > 1) renders a taller,\n * segmented bar so a node that accepts several inbound edges reads differently\n * from a single-in node (see core rfcs/node-types.md · Ports & cardinality).\n *\n * `variant === \"error\"` (T1.1.1) renders the second, visually subordinate\n * source handle every step/control node's error exit uses — `id` is what\n * lets React Flow (and `laneForSourceHandle`) tell it apart from the node's\n * ordinary, unnamed exit. The ONE shared handle component for both — no\n * second handle component and no raw inline `<Handle>` at either node card's\n * call site (`building-blocks.md`'s anti-duplication callout).\n */\nfunction PortHandle({\n type,\n position,\n multiple,\n id,\n variant,\n}: {\n type: \"source\" | \"target\";\n position: Position;\n multiple: boolean;\n id?: string;\n variant?: \"error\";\n}) {\n const nodeId = useNodeId();\n const ctrl = useContext(StepControlsCtx);\n const canAdd = !!nodeId && !!ctrl && !ctrl.readOnly;\n const add = () => {\n if (canAdd && nodeId) ctrl.onAddFromPort(nodeId, type, id);\n };\n const what =\n variant === \"error\"\n ? \"a step that handles this step's failure\"\n : type === \"target\"\n ? \"a step before this one\"\n : \"the next step\";\n return (\n <Handle\n type={type}\n position={position}\n id={id}\n className={\n variant === \"error\" ? \"w6w-handle-error\" : multiple ? \"w6w-handle-multi\" : undefined\n }\n style={multiple ? { height: 22, borderRadius: 3, width: 8 } : undefined}\n // A click (no drag) adds a step from this port; a drag still draws a connection.\n onClick={canAdd ? add : undefined}\n title={\n canAdd\n ? `Drag to empty canvas (or click) to add ${what} · drag to a port to connect`\n : variant === \"error\"\n ? \"On error — drag to route this step's failure separately\"\n : multiple\n ? type === \"target\"\n ? \"Accepts multiple incoming connections\"\n : \"Multiple outgoing connections\"\n : undefined\n }\n />\n );\n}\n\nfunction NodeControls({ id, runnable }: { id: string; runnable?: boolean }) {\n const ctrl = useContext(StepControlsCtx);\n if (!ctrl || ctrl.readOnly) return null;\n return (\n <NodeToolbar position={Position.Top} className=\"w6w-node-toolbar\">\n {runnable && (\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn\"\n title=\"Test-run this node\"\n aria-label=\"Test-run this node\"\n onClick={() => ctrl.onRun(id)}\n >\n <Icon name=\"run\" size={15} />\n </button>\n )}\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn\"\n title=\"Edit\"\n aria-label=\"Edit node\"\n onClick={() => ctrl.onEdit(id)}\n >\n <Icon name=\"edit\" size={15} />\n </button>\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn\"\n title=\"Duplicate\"\n aria-label=\"Duplicate node\"\n onClick={() => ctrl.onDuplicate(id)}\n >\n <Icon name=\"copy\" size={15} />\n </button>\n <button\n type=\"button\"\n className=\"w6w-node-toolbar-btn danger\"\n title=\"Delete\"\n aria-label=\"Delete node\"\n onClick={() => ctrl.onDelete(id)}\n >\n <Icon name=\"trash\" size={15} />\n </button>\n </NodeToolbar>\n );\n}\n\n/** Card badge: a text glyph per mode (not colour), only with 2+ outgoing edges in a lane. */\nfunction FanOutBadge({ step, count }: { step: FlowStep; count: number }) {\n if (count < 2) return null;\n const parallel = step.fanOut === \"parallel\";\n const label = fanOutLabel(count, parallel ? \"parallel\" : \"sequential\");\n return (\n <div className=\"w6w-fanout-badge\" role=\"img\" aria-label={label} title={label}>\n <span aria-hidden=\"true\">{parallel ? \"⇉\" : \"→\"}</span> {count}\n </div>\n );\n}\n\nfunction StepNodeCard({ id, data, selected }: NodeProps<StepNode>) {\n const step = data.step;\n const apps = useContext(AppsCtx);\n const app = apps.get(step.uses.app);\n // The human app name (fall back to the raw id when the app isn't in the list).\n const appName = app?.displayName || step.uses.app || \"—\";\n // Per-step ports (T2.3.1): a persisted `ports.in > 1` renders a multi-input handle.\n const ports = nodePortsForStep(step, useContext(PortsLookupCtx));\n // T1.1.1 — run-execution visual, RunStatus × StepStatus (A3). `data.stepStatus`\n // is stamped by `Inner`'s live-run repaint effect, never by `workflowToFlow`.\n const visual = stepNodeVisual(data.stepStatus ?? \"no-run\", data.runStatus);\n const fanBadge = <FanOutBadge step={step} count={useContext(FanOutCountsCtx).get(id) ?? 0} />;\n return (\n <div>\n <NodeControls id={id} runnable />\n <div\n data-run-status={data.stepStatus ?? \"no-run\"}\n data-step-problem={data.stepProblem?.code}\n title={data.stepProblem ? stepProblemTooltip(data.stepProblem) : undefined}\n style={{\n // `relative` so the Handles center on the CARD, not the whole node\n // (which also spans the meta line below) — keeps ports vertically centered.\n position: \"relative\",\n // Missing-connection ring; the border stays the run-status visual.\n ...(data.stepProblem ? { boxShadow: \"0 0 0 2px var(--w6w-danger)\" } : {}),\n // Selection always wins the BORDER COLOUR (unchanged from before\n // T1.1.1 — the ternary this extends); width/style/opacity keep\n // reflecting the run status underneath so a selected running/failed\n // step is still distinguishable from a selected idle one.\n border: `${visual.borderWidth}px ${visual.borderStyle} ${\n selected ? \"var(--w6w-accent)\" : visual.borderColor\n }`,\n opacity: selected ? 1 : visual.opacity,\n background: \"var(--w6w-panel)\",\n color: \"var(--w6w-text)\",\n borderRadius: 4,\n padding: \"8px 12px\",\n minWidth: 180,\n fontSize: 13,\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n }}\n >\n {data.stepProblem && (\n <span\n data-step-problem-badge=\"\"\n role=\"img\"\n aria-label={stepProblemTooltip(data.stepProblem)}\n title={stepProblemTooltip(data.stepProblem)}\n style={{\n position: \"absolute\",\n top: -9,\n right: -9,\n width: 18,\n height: 18,\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"var(--w6w-danger)\",\n color: \"#fff\",\n }}\n >\n <Icon name=\"warning\" size={12} />\n </span>\n )}\n {ports.in > 0 && (\n <PortHandle type=\"target\" position={Position.Left} multiple={ports.in > 1} />\n )}\n <AppIcon\n src={app?.iconSvg}\n srcDark={app?.iconSvgDark}\n brandColor={app?.brandColor}\n name={appName}\n size={28}\n />\n <div style={{ minWidth: 0 }}>\n <div style={{ fontWeight: 600 }}>{appName}</div>\n <div className=\"w6w-muted w6w-small\" style={{ marginTop: 2 }}>\n <code>{step.uses.action || \"—\"}</code>\n </div>\n </div>\n {ports.out > 0 && (\n <>\n <PortHandle type=\"source\" position={Position.Right} multiple={ports.out > 1} />\n <PortHandle\n type=\"source\"\n position={Position.Bottom}\n multiple={false}\n id={ERROR_SOURCE_HANDLE}\n variant=\"error\"\n />\n </>\n )}\n </div>\n {fanBadge}\n {/* Meta line under the card: the step id and (when known) the app version. */}\n <div\n className=\"w6w-muted\"\n style={{ marginTop: 3, fontSize: 10, opacity: 0.75, paddingLeft: 2 }}\n >\n {step.id}\n {app?.version ? ` - v${app.version}` : \"\"}\n </div>\n {/* The step's own Notes (settings → gear icon), shown here so a reader\n can associate it with the step without opening it — human override,\n 2026-08-30. Full text in `title=` for a hover, one visual line on\n the canvas itself; `.w6w-node-notes` (styles.css) truncates. */}\n {step.notes && (\n <div className=\"w6w-muted w6w-node-notes\" title={step.notes}>\n {step.notes}\n </div>\n )}\n </div>\n );\n}\n\nfunction ControlNodeCard({ id, data, selected }: NodeProps<StepNode>) {\n const step = data.step;\n const label = internalNodeLabel(step.uses.app, step.uses.action);\n const icon = internalNodeIcon(step.uses.app, step.uses.action);\n // Per-step ports (T2.3.1): triggers have no entry (0 in, 1 out); a fan-in node\n // (e.g. aggregate, or `ports.in > 1`) renders a multi-input handle.\n const ports = nodePortsForStep(step, useContext(PortsLookupCtx));\n // A trigger is the run's ENTRY, not a step that acts: it emits the start\n // payload and has no vendor call to fail, so there is no failure of its own\n // to route — no error exit port (human report, 2026-09-01). Triggers on the\n // canvas are always internal nodes (an app's own trigger binds as a\n // subscription, never as a node), so this card is the only one that needs\n // the guard.\n const isTrigger = isTriggerApp(step.uses.app);\n // T1.1.1 — same run-execution visual StepNodeCard derives; see its comment.\n const visual = stepNodeVisual(data.stepStatus ?? \"no-run\", data.runStatus);\n const fanBadge = <FanOutBadge step={step} count={useContext(FanOutCountsCtx).get(id) ?? 0} />;\n return (\n <div>\n {/* Compute/trigger nodes can be test-run; flow-control nodes cannot. */}\n <NodeControls id={id} runnable={!isControlApp(step.uses.app)} />\n <div\n className={isTrigger ? \"w6w-node-trigger\" : undefined}\n data-node-kind={isTrigger ? \"trigger\" : undefined}\n data-run-status={data.stepStatus ?? \"no-run\"}\n style={{\n // `relative` so the Handles center on the CARD, not the whole node\n // (which also spans the meta line below) — keeps ports vertically centered.\n position: \"relative\",\n border: `${visual.borderWidth}px ${visual.borderStyle} ${\n selected ? \"var(--w6w-accent)\" : visual.borderColor\n }`,\n opacity: selected ? 1 : visual.opacity,\n background: \"var(--w6w-panel-2)\",\n color: \"var(--w6w-text)\",\n // A trigger's half-pill radius + padding live in `.w6w-node-trigger`\n // (styles/_flow.scss): an inline value here would beat the class.\n ...(isTrigger ? {} : { borderRadius: 4, padding: \"6px 14px 6px 8px\" }),\n minWidth: 140,\n fontSize: 13,\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n }}\n >\n {ports.in > 0 && (\n <PortHandle type=\"target\" position={Position.Left} multiple={ports.in > 1} />\n )}\n {icon && <InternalIcon icon={icon} />}\n <div style={{ minWidth: 0, textAlign: \"left\" }}>\n <div style={{ fontWeight: 600 }}>{label}</div>\n <div className=\"w6w-muted w6w-small\" style={{ marginTop: 2 }}>\n <code>{step.uses.action || \"—\"}</code>\n </div>\n </div>\n {ports.out > 0 && (\n <>\n <PortHandle type=\"source\" position={Position.Right} multiple={ports.out > 1} />\n {!isTrigger && (\n <PortHandle\n type=\"source\"\n position={Position.Bottom}\n multiple={false}\n id={ERROR_SOURCE_HANDLE}\n variant=\"error\"\n />\n )}\n </>\n )}\n </div>\n {fanBadge}\n {/* Meta line under the card: the step id (internal nodes carry no version). */}\n <div\n className=\"w6w-muted\"\n style={{ marginTop: 3, fontSize: 10, opacity: 0.75, paddingLeft: 2 }}\n >\n {step.id}\n </div>\n {/* The step's own Notes — see StepNodeCard's own copy for the rationale. */}\n {step.notes && (\n <div className=\"w6w-muted w6w-node-notes\" title={step.notes}>\n {step.notes}\n </div>\n )}\n </div>\n );\n}\n\n// ── Step edit modal (Form ⇄ JSON) ─────────────────────────────────────────\n\nexport function StepEditModal({\n workflowId,\n step: initialStep,\n upstreamSteps,\n onChange,\n onClose,\n readOnly,\n initialView = \"props\",\n fanOutCount = 0,\n onTestRun,\n stepProblem,\n}: {\n workflowId: string;\n step: FlowStep;\n /** Graph ancestors of this step (from `upstreamStateSources`); the incoming-state picker seeds from their saved tests. */\n upstreamSteps: ExpressionStepSource[];\n onChange: (next: FlowStep) => void;\n onClose: () => void;\n readOnly?: boolean;\n initialView?: EditView;\n /** Most outgoing edges this node has in one lane; \"Run next nodes\" needs >= 2. */\n fanOutCount?: number;\n /** Fired the instant the footer Test button is clicked, before the test runs (T1.1.1). */\n onTestRun?: () => void;\n /** Host-supplied problem for this step; a dangling connection locks Configure/Test. */\n stepProblem?: StepProblem;\n}) {\n const api = useW6WApi();\n const apps = useContext(AppsCtx);\n const [step, setStep] = useState<FlowStep>(initialStep);\n // Checked against the LOCAL step so choosing another connection unblocks at once.\n const blocked = isConnectionBlocked(step, stepProblem);\n // Same shape as the add modal: Setup/Configure/Test tabs with the Configure\n // tab showing form (props) / full-step JSON (code) / params JSON\n // (params-code) / node settings (config).\n const [tab, setTab] = useState<\"setup\" | \"configure\" | \"test\">(\n isConnectionBlocked(initialStep, stepProblem) ? \"setup\" : \"configure\",\n );\n const [configView, setConfigView] = useState<ConfigView>(\n initialView === \"json\" ? \"code\" : initialView === \"settings\" ? \"config\" : \"props\",\n );\n // The Test tab's own props/JSON toggle state (A4/D-7) — deliberately NOT\n // `configView`, which ranges over `\"params-code\"`/`\"config\"` too, neither of\n // which is in the Test tab's narrowed `[\"props\",\"code\"]` set. Always starts\n // on the row list.\n const [testView, setTestView] = useState<\"props\" | \"code\">(\"props\");\n // Draft text backing the \"code\" (full-step, read-only) view.\n const [codeText, setCodeText] = useState(() => stepToJson(initialStep));\n // Draft text backing the \"params-code\" (params-only, writable) view.\n const [paramsCodeText, setParamsCodeText] = useState(() => paramsToJson(initialStep));\n const [testState, setTestState] = useState(\"{}\");\n // Drives the footer \"Test\" button, which triggers the body's <StepTestRun> so\n // the run + persist logic isn't duplicated across two affordances.\n const testRunRef = useRef<StepTestRunHandle>(null);\n const [testBusy, setTestBusy] = useState(false);\n // Inline step rename (pencil next to the name). `updateStep` fixes up edges.\n const [renaming, setRenaming] = useState(false);\n const [draftId, setDraftId] = useState(step.id);\n const [renameErr, setRenameErr] = useState<string | null>(null);\n\n const [params, setParams] = useState<ActionParam[] | null>(null);\n const [actions, setActions] = useState<ActionDef[] | null>(null);\n const [conns, setConns] = useState<ConnectionSummary[] | null>(null);\n const [connsFailed, setConnsFailed] = useState(false);\n const isInternal = isInternalApp(step.uses.app);\n\n // Refetch actions + params whenever the app/action identity changes. P-3:\n // this must refetch on an APP change alone (action `\"\"` included) — an\n // early return here on a missing action left `actions` holding the\n // PREVIOUS app's list, stale-ing the Action <select> after Change (D-1).\n useEffect(() => {\n if (isInternal) {\n setParams(internalNodeParams(step.uses.app, step.uses.action));\n setActions(null);\n return;\n }\n if (!step.uses.app) {\n setActions(null);\n setParams([]);\n return;\n }\n let canceled = false;\n setParams(null);\n api\n .getAppActions(step.uses.app)\n .then((acts) => {\n if (canceled) return;\n setActions(acts);\n setParams(\n step.uses.action ? (acts.find((a) => a.key === step.uses.action)?.params ?? []) : [],\n );\n })\n .catch(() => !canceled && setParams([]));\n return () => {\n canceled = true;\n };\n }, [api, step.uses.app, step.uses.action, isInternal]);\n\n const refetchConns = useCallback(() => {\n if (isInternal || !step.uses.app) return;\n api\n .listConnectionsForApp(step.uses.app)\n .then((c) => {\n setConns(c);\n setConnsFailed(false);\n })\n .catch(() => setConnsFailed(true));\n }, [api, step.uses.app, isInternal]);\n\n useEffect(() => {\n if (isInternal || !step.uses.app) return;\n let canceled = false;\n api\n .listConnectionsForApp(step.uses.app)\n .then((c) => {\n if (canceled) return;\n setConns(c);\n setConnsFailed(false);\n })\n .catch(() => !canceled && setConnsFailed(true));\n return () => {\n canceled = true;\n };\n }, [api, step.uses.app, isInternal]);\n\n const commit = useCallback(\n (next: FlowStep) => {\n setStep(next);\n onChange(next);\n },\n [onChange],\n );\n\n const changeConfigView = (v: ConfigView) => {\n if (v === \"code\") setCodeText(stepToJson(step));\n else if (v === \"params-code\") setParamsCodeText(paramsToJson(step));\n setConfigView(v);\n };\n const commitRename = () => {\n const id = draftId.trim();\n const err = id && id !== step.id ? stepIdError(id) : null;\n if (err) {\n setRenameErr(err);\n return;\n }\n if (id && id !== step.id) commit({ ...step, id });\n setRenameErr(null);\n setRenaming(false);\n };\n\n const testable = !!step.uses.app && !!step.uses.action && !isControlApp(step.uses.app);\n // A manual/webhook trigger's Test tab fills its configured `fields` into\n // `{ input }` (via TriggerFillForm) rather than running the raw config.\n const isTrigger = isTriggerApp(step.uses.app);\n // A4/D-7: the tabs-bar toggle is enabled and narrowed to props/code ONLY on\n // this arm — the non-trigger, testable Test tab, the one that mounts\n // `<ResolvedParams>`. The trigger arm keeps the dim four (`PropertyEntryForm`\n // renders its own toggle inside `TriggerFillForm`'s body); Setup/Configure\n // are unchanged.\n const isTestPropsCode = tab === \"test\" && testable && !isTrigger;\n\n // The editing step's graph ancestors that carry a saved step-test, offered as\n // one-click seeds for the incoming state. The SAME hook the canvas ▶ Run\n // collect form uses — one implementation, so the two surfaces cannot drift.\n const seedSources = useSeedSources(workflowId, upstreamSteps, testable && !isTrigger);\n // …and the start state the test is SENT with, from those same fixtures. This\n // is what makes `{{ steps.<id>.output.<field> }}` in this step's `with`\n // resolve to what that step last produced instead of to \"\".\n const testStartState = startStateFromSeeds(seedSources);\n\n // The incoming state this step was last tested or run with comes back (T4.1.3\n // on this surface's own noun): the fixture's `input` slot is where both\n // surfaces write it. Applied only while the box is still untouched, so a slow\n // list can never overwrite what the operator is typing.\n useEffect(() => {\n if (!testable || isTrigger) return;\n let canceled = false;\n api\n .listStepTests(workflowId, step.id)\n .then((tests) => {\n if (canceled) return;\n const latest = tests.length ? tests[tests.length - 1] : null;\n if (!latest?.input || Object.keys(latest.input).length === 0) return;\n const next = JSON.stringify(latest.input, null, 2);\n setTestState((cur) => (cur.trim() === \"{}\" ? next : cur));\n })\n // Best-effort, exactly like the seed effect: no fixture (or a failed list)\n // just leaves the box empty.\n .catch(() => {});\n return () => {\n canceled = true;\n };\n }, [api, workflowId, step.id, testable, isTrigger]);\n\n // The incoming-state override, and the payload it produces. The step's STORED\n // configuration is what runs; the override is merged over it. A box that is\n // not a JSON object overrides nothing (see `parseStateOverride`).\n const testOverride = parseStateOverride(testState);\n const testValues = { ...(step.with ?? {}), ...(testOverride ?? {}) };\n // The resolved incoming state saved alongside the fixture — the slot the ▶ Run\n // collect form reads back, and what the upstream seed chips write into.\n const testInput = testOverride ?? {};\n const canTest = !!params && requiredParamsFilled(params, testValues);\n\n // Header icon mirrors the canvas node: the app's icon for app steps, the\n // internal glyph for triggers/actions/control nodes (same as the node cards).\n const app = apps.get(step.uses.app);\n const internalIcon = isInternal ? internalNodeIcon(step.uses.app, step.uses.action) : null;\n const titleIcon = isInternal ? (\n internalIcon ? (\n <InternalIcon icon={internalIcon} />\n ) : null\n ) : (\n <AppIcon\n src={app?.iconSvg}\n srcDark={app?.iconSvgDark}\n brandColor={app?.brandColor}\n name={app?.displayName}\n />\n );\n\n return (\n <Modal\n ariaLabel=\"Edit step\"\n titleIcon={titleIcon}\n title={\n <span className=\"w6w-step-rename\">\n {renaming && !readOnly ? (\n <input\n // biome-ignore lint/a11y/noAutofocus: rename input opened on demand\n autoFocus\n value={draftId}\n onChange={(e) => {\n setDraftId(e.target.value);\n setRenameErr(null);\n }}\n onBlur={commitRename}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") commitRename();\n if (e.key === \"Escape\") {\n setDraftId(step.id);\n setRenameErr(null);\n setRenaming(false);\n }\n }}\n />\n ) : (\n <>\n <code>{step.id}</code>\n {!readOnly && (\n <button\n type=\"button\"\n className=\"w6w-icon-btn w6w-btn-sm\"\n title=\"Rename node\"\n aria-label=\"Rename node\"\n onClick={() => {\n setDraftId(step.id);\n setRenaming(true);\n }}\n >\n <Icon name=\"edit\" size={15} />\n </button>\n )}\n </>\n )}\n {renameErr && (\n <span className=\"w6w-hint\" role=\"alert\" style={{ color: \"var(--w6w-danger)\" }}>\n {renameErr}\n </span>\n )}\n </span>\n }\n onClose={onClose}\n size=\"wide\"\n >\n <div className=\"w6w-stepconfig\">\n <div className=\"w6w-tabsbar\">\n <div className=\"w6w-subtabs\">\n {([\"setup\", \"configure\", \"test\"] as const).map((t) => (\n <button\n key={t}\n type=\"button\"\n className={`w6w-subtab${tab === t ? \" active\" : \"\"}`}\n disabled={blocked && t !== \"setup\"}\n onClick={() => setTab(t)}\n >\n {t === \"setup\" ? \"Setup\" : t === \"configure\" ? \"Configure\" : \"Test\"}\n </button>\n ))}\n </div>\n {isTestPropsCode ? (\n <ConfigViewToggle\n view={testView}\n views={[\"props\", \"code\"]}\n onChange={(v) => setTestView(v as \"props\" | \"code\")}\n />\n ) : (\n <ConfigViewToggle\n view={configView}\n onChange={changeConfigView}\n disabled={tab !== \"configure\"}\n />\n )}\n </div>\n\n {blocked && stepProblem && (\n <div role=\"alert\" className=\"w6w-field-error\">\n Connection {stepProblem.connection} no longer exists — choose another to continue.\n </div>\n )}\n <div className=\"w6w-stepconfig-body\">\n {tab === \"setup\" && (\n <SetupTab\n step={step}\n app={app}\n actions={actions}\n conns={conns}\n connsFailed={connsFailed}\n isInternal={isInternal}\n readOnly={readOnly}\n onChangeAction={(action) =>\n commit({ ...step, uses: { ...step.uses, action }, with: {} })\n }\n onChangeConnection={(connection) =>\n commit({ ...step, uses: { ...step.uses, connection } })\n }\n onConnectionCreated={refetchConns}\n onChangeApp={(appId) =>\n // D-1: a genuinely different app clears `uses.action`/`with` and\n // drops the connection; reselecting the SAME app is a no-op —\n // still committed (so the field always collapses cleanly), but\n // with `step` itself, unchanged.\n commit(\n appId === step.uses.app\n ? step\n : {\n ...step,\n uses: { app: appId, action: \"\", connection: undefined },\n with: {},\n },\n )\n }\n />\n )}\n\n {tab === \"configure\" && (\n <>\n {step.uses.app === WEBHOOK_APP &&\n api.listSubscriptionsForWorkflow &&\n api.createSubscription && (\n <WebhookUrlPanel workflowId={workflowId} step={step} readOnly={readOnly} />\n )}\n {params === null ? (\n <p className=\"w6w-muted w6w-small\">Loading parameters…</p>\n ) : configView === \"props\" ? (\n step.uses.action === \"wait\" && isControlApp(step.uses.app) ? (\n <WaitEditor\n values={step.with ?? {}}\n readOnly={readOnly}\n onChange={(w) => commit({ ...step, with: w })}\n />\n ) : (\n <ParamsForm\n params={params}\n values={step.with ?? {}}\n readOnly={readOnly}\n onChange={(w) => commit({ ...step, with: w })}\n dynamic={\n isInternal || !step.uses.action\n ? undefined\n : {\n appId: step.uses.app,\n actionKey: step.uses.action,\n connectionId: blocked ? undefined : (step.uses.connection ?? undefined),\n appName: app?.displayName,\n }\n }\n />\n )\n ) : configView === \"code\" ? (\n // Full step, read-only (D-3) — `stepToJson` is the ONE serializer,\n // shared with the two other code-view hosts.\n <JsonEditor\n value={codeText}\n onChange={() => {}}\n readOnly\n minHeight=\"260px\"\n height=\"100%\"\n aria-label={`Node ${step.id} JSON`}\n copyable\n />\n ) : configView === \"params-code\" ? (\n <JsonEditor\n value={paramsCodeText}\n onChange={setParamsCodeText}\n readOnly={readOnly}\n minHeight=\"260px\"\n height=\"100%\"\n aria-label={`Node ${step.id} params JSON`}\n copyable\n onValidChange={(p) =>\n p &&\n typeof p === \"object\" &&\n !Array.isArray(p) &&\n commit({ ...step, with: p as Record<string, unknown> })\n }\n />\n ) : (\n <div className=\"w6w-stack\">\n <NodeConfigForm\n config={{ retry: step.retry, onError: step.onError, notes: step.notes }}\n onChange={(c) => commit({ ...step, ...c })}\n readOnly={readOnly}\n // A trigger has no failure of its own to retry or police —\n // same reason its card renders no error exit port.\n failureHandling={!isTrigger}\n // A `@w6w/call`/`@w6w/control` step runs no retry loop of\n // its own (core rfcs/workflow.md \"Retries come first\"; D-3).\n retryControls={!isControlApp(step.uses.app) && step.uses.app !== CALL_APP}\n />\n {SHOW_STEP_PORTS && (\n <StepPortsControl step={step} readOnly={readOnly} onChange={commit} />\n )}\n <StepFanOutControl\n step={step}\n count={fanOutCount}\n readOnly={readOnly}\n onChange={commit}\n />\n </div>\n )}\n </>\n )}\n\n {tab === \"test\" && (\n <div className=\"w6w-stack\">\n {testable ? (\n isTrigger ? (\n <TriggerFillForm\n app={step.uses.app}\n action={step.uses.action}\n fields={step.with?.fields}\n // Same fixture key the non-trigger Test path passes to\n // <StepTestRun> below, so the values entered here are\n // remembered and come back on the canvas ▶ Run form.\n persist={{ workflowId, stepId: step.id, input: testInput }}\n />\n ) : (\n <>\n <p className=\"w6w-muted w6w-small\">\n Tests <code>{step.uses.action}</code> with the configuration saved on this\n step — only the incoming state changes per run.\n </p>\n <IncomingStateField\n text={testState}\n onChange={setTestState}\n seeds={seedSources}\n readOnly={readOnly}\n />\n {/* What will actually be submitted, resolved against the\n incoming state above — `testValues` (post-override),\n never `step.with` alone, so overriding the incoming\n state visibly updates these rows. */}\n {params === null ? (\n <p className=\"w6w-muted w6w-small\">Loading parameters…</p>\n ) : (\n <ResolvedParams\n params={params}\n values={testValues}\n testStartState={testStartState}\n view={testView}\n />\n )}\n <StepTestRun\n ref={testRunRef}\n app={step.uses.app}\n action={step.uses.action}\n connectionId={step.uses.connection ?? undefined}\n values={testValues}\n state={testStartState}\n canRun={canTest}\n hideRunButton\n persist={{ workflowId, stepId: step.id, input: testInput }}\n onBusyChange={setTestBusy}\n />\n </>\n )\n ) : (\n <p className=\"w6w-muted w6w-small\">\n Flow-control nodes can't be tested on their own.\n </p>\n )}\n </div>\n )}\n </div>\n\n <div className=\"w6w-modal-actions w6w-stepconfig-footer\">\n {tab !== \"test\" ? (\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={blocked}\n onClick={() => setTab(tab === \"setup\" ? \"configure\" : \"test\")}\n >\n Next →\n </button>\n ) : (\n <>\n <button type=\"button\" className=\"w6w-btn w6w-btn-ghost\" onClick={onClose}>\n Done\n </button>\n {testable && !isTrigger && (\n // Runs the step and persists the outcome (records a run + saves\n // the fixture) via the body's <StepTestRun>, so a step test is\n // saved and re-runnable. `readOnly` viewers can't write tests.\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={readOnly || !canTest || testBusy}\n onClick={() => {\n // T1.1.1: before the run, so the host clears whatever run\n // the canvas was showing on the click itself.\n onTestRun?.();\n testRunRef.current?.run();\n }}\n >\n {testBusy ? \"Testing…\" : \"Test\"}\n </button>\n )}\n </>\n )}\n </div>\n </div>\n </Modal>\n );\n}\n\n/** Default in-cardinality applied when a step is opted into multiple inbound edges. */\nconst MULTI_IN_DEFAULT = 10;\n\n/** Hidden for now (26-08-30-00-fixes item 3). Flip to `true` to restore; nothing else changes. */\nconst SHOW_STEP_PORTS = false;\n\n/**\n * Opt a step into accepting **multiple** incoming edges by setting `step.ports.in`\n * (see core rfcs/node-types.md · Ports & cardinality). Unchecking reverts to the\n * node's default (drops the persisted `ports` so an untouched step stays `1/1`).\n * Hidden for entry nodes (triggers) that declare no inbound port.\n */\nfunction StepPortsControl({\n step,\n readOnly,\n onChange,\n}: {\n step: FlowStep;\n readOnly?: boolean;\n onChange: (next: FlowStep) => void;\n}) {\n const ports = nodePortsForStep(step, useContext(PortsLookupCtx));\n // Nothing flows into a node with no entry port (e.g. a trigger).\n if (ports.in < 1) return null;\n const multiple = ports.in > 1;\n return (\n <label className=\"w6w-field\" style={{ flexDirection: \"row\", alignItems: \"center\", gap: 8 }}>\n <input\n type=\"checkbox\"\n checked={multiple}\n disabled={readOnly}\n onChange={(e) => {\n if (e.target.checked) {\n onChange({ ...step, ports: { in: MULTI_IN_DEFAULT, out: ports.out } });\n } else {\n const { ports: _drop, ...rest } = step;\n onChange(rest);\n }\n }}\n />\n <span>Accept multiple incoming connections</span>\n </label>\n );\n}\n\n/** \"Run next nodes: In sequence / In parallel\" — only meaningful with 2+ outgoing edges in a lane. */\nfunction StepFanOutControl({\n step,\n count,\n readOnly,\n onChange,\n}: {\n step: FlowStep;\n count: number;\n readOnly?: boolean;\n onChange: (next: FlowStep) => void;\n}) {\n const enabled = count >= 2;\n const mode = step.fanOut === \"parallel\" ? \"parallel\" : \"sequential\";\n return (\n <label className=\"w6w-field\">\n <span>Run next nodes</span>\n <select\n aria-label=\"Run next nodes\"\n value={mode}\n disabled={readOnly || !enabled}\n onChange={(e) => onChange(setFanOut(step, e.target.value as \"sequential\" | \"parallel\"))}\n >\n <option value=\"sequential\">In sequence</option>\n <option value=\"parallel\">In parallel</option>\n </select>\n {!enabled && (\n <span className=\"w6w-muted w6w-small\">\n Connect two or more steps after this one to choose how they run.\n </span>\n )}\n </label>\n );\n}\n\n/**\n * \"Webhook URL\" panel (I-2) — rendered only for a `WEBHOOK_APP` step, above\n * the ParamsForm/JsonEditor arms. Get-or-create against the Subscription\n * mechanism (rfcs/trigger.md), per DECISIONS.md HITL-1 + plan.md D-5:\n * - on open, lists this workflow's Subscriptions ONCE and takes the first\n * `appId === WEBHOOK_APP` entry — never re-derived client-side\n * (gap-analysis-I2 §1's `SubscriptionsPage.tsx:24-28` correctness bug is\n * deliberately not reproduced here; the server's own `webhookUrl` is the\n * only source).\n * - if one exists, its URL renders through `Copyable` exactly as\n * `TenantSettingsPage.tsx:219-227` (full URL, readOnly, aria-label) — no\n * create call.\n * - if none exists, a labelled button creates one with the step's own\n * current `with` as the Subscription's `params`, then renders the result.\n * `sub_…` ids are server-minted, so this never re-fires on its own — only\n * an explicit click creates, and only once a subscription is confirmed\n * absent.\n */\nfunction WebhookUrlPanel({\n workflowId,\n step,\n readOnly,\n}: {\n workflowId: string;\n step: FlowStep;\n readOnly?: boolean;\n}) {\n const api = useW6WApi();\n // `undefined` while the initial list is in flight; `null` once resolved\n // with no existing webhook subscription for this workflow.\n const [sub, setSub] = useState<SubscriptionSummary | null | undefined>(undefined);\n const [error, setError] = useState<string | null>(null);\n const [creating, setCreating] = useState(false);\n\n // Deliberately keyed on [api, workflowId] only — NOT on `step` (its `with`\n // changes on every keystroke in the panel below). Re-running this on a\n // `with` edit would defeat the \"never re-creates\" guarantee this panel\n // exists to provide; the list is a one-shot get, not a live subscription.\n useEffect(() => {\n const list = api.listSubscriptionsForWorkflow;\n if (!list) return;\n let canceled = false;\n list(workflowId)\n .then((subs) => {\n if (!canceled) setSub(subs.find((s) => s.appId === WEBHOOK_APP) ?? null);\n })\n .catch((e) => !canceled && setError((e as Error).message));\n return () => {\n canceled = true;\n };\n }, [api, workflowId]);\n\n const create = () => {\n const createSubscription = api.createSubscription;\n if (!createSubscription) return;\n setError(null);\n setCreating(true);\n createSubscription(WEBHOOK_APP, step.uses.action, {\n workflowId,\n connectionId: null,\n params: step.with ?? {},\n })\n .then((created) => {\n setCreating(false);\n setSub(created);\n })\n .catch((e) => {\n setCreating(false);\n setError(e instanceof Error ? e.message : String(e));\n });\n };\n\n return (\n <div className=\"w6w-stack\">\n <p className=\"w6w-muted w6w-small\">\n <strong>Webhook URL</strong> — the address a third party posts to.\n </p>\n {error && <div className=\"w6w-result w6w-error\">{error}</div>}\n {sub?.webhookUrl ? (\n <Copyable value={sub.webhookUrl} readOnly>\n <input type=\"text\" readOnly value={sub.webhookUrl} aria-label=\"Webhook URL\" />\n </Copyable>\n ) : sub === undefined ? (\n <p className=\"w6w-muted w6w-small\">Loading…</p>\n ) : (\n !readOnly && (\n <button type=\"button\" className=\"w6w-btn\" disabled={creating} onClick={create}>\n {creating ? \"Creating…\" : \"Create webhook URL\"}\n </button>\n )\n )}\n </div>\n );\n}\n\n/**\n * A small popover anchored to a trigger element, portaled to the trigger's\n * own `<dialog>` (never `document.body`) — a `showModal()` dialog and its\n * contents live in the browser's native top layer, so a portal straight to\n * `document.body` would render BEHIND the modal, not over it (2026-08-30:\n * \"it needs to be a flyout over the modal, not squeezed inside it\"). Plain\n * CSS-positioned `<div>`, not a nested `<dialog>` — the ask was explicit\n * (\"don't use native dialog, create css dialog\").\n *\n * Closes on Escape or a click outside both itself and the anchor. It does\n * NOT close on a click ON the anchor — that stays the trigger's own toggle\n * (click to open, click again to close), so the anchor button remains a\n * working close affordance the whole time the flyout is open, unlike an\n * inline \"replace the button with the picker\" approach where there is\n * nothing left to click to back out.\n */\nfunction Flyout({\n anchorRef,\n onClose,\n children,\n}: {\n // Anchored to the ROW (both App and Connection buttons together), not the\n // individual trigger button — two reasons, both from the same feedback\n // pass (2026-08-30): a flyout anchored to whichever button sits at the\n // row's right edge (Connection) could overflow past the modal's own right\n // border (\"fix anchor\"), and the app picker specifically was asked to\n // \"take up the entire horizontal width of the content... extend to the\n // end of the prod button\". Anchoring both to the row's own bounding box\n // solves both at once: the flyout's width always matches content already\n // known to fit inside the modal, and it never hangs off either edge.\n anchorRef: RefObject<HTMLElement | null>;\n onClose: () => void;\n children: ReactNode;\n}) {\n const popRef = useRef<HTMLDivElement>(null);\n const [pos, setPos] = useState<{ top: number; left: number; width: number } | null>(null);\n const [container, setContainer] = useState<HTMLElement | null>(null);\n\n useEffect(() => {\n const el = anchorRef.current;\n if (!el) return;\n const r = el.getBoundingClientRect();\n setPos({ top: r.bottom + 6, left: r.left, width: r.width });\n setContainer(el.closest(\"dialog\") ?? document.body);\n }, [anchorRef]);\n\n useEffect(() => {\n function onDocPointerDown(e: MouseEvent) {\n const target = e.target as Node;\n if (popRef.current?.contains(target)) return;\n // A click ON the anchor is the trigger's own toggle — never close here\n // AND reopen there in the same gesture.\n if (anchorRef.current?.contains(target)) return;\n onClose();\n }\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === \"Escape\") onClose();\n }\n document.addEventListener(\"mousedown\", onDocPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onDocPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [onClose, anchorRef]);\n\n if (!pos || !container) return null;\n return createPortal(\n <div\n ref={popRef}\n className=\"w6w-flyout\"\n style={{ top: pos.top, left: pos.left, width: pos.width }}\n >\n {children}\n </div>,\n container,\n );\n}\n\n/**\n * The Setup tab — an App button and a Connection button, spaced apart, then\n * the step's action (a dropdown for app steps). Two independent pickers\n * (2026-08-30, superseding D-1's single combined field — see intake for the\n * exact spec): App opens a searchable app picker; Connection opens a plain\n * list of connections FOR THE CURRENT APP plus \"+ New connection\" — no\n * search, per the ask (\"just list the connections for the same app\"). Both\n * render as an anchored `Flyout`, not inline content that pushes the rest\n * of the tab down.\n */\nfunction SetupTab({\n step,\n app,\n actions,\n conns,\n connsFailed,\n isInternal,\n readOnly,\n onChangeAction,\n onChangeConnection,\n onChangeApp,\n onConnectionCreated,\n}: {\n step: FlowStep;\n app: AppSummary | undefined;\n actions: ActionDef[] | null;\n conns: ConnectionSummary[] | null;\n /** The connections fetch rejected — a distinct state, never \"missing\". */\n connsFailed: boolean;\n isInternal: boolean;\n readOnly?: boolean;\n onChangeAction: (action: string) => void;\n onChangeConnection: (connection: string | undefined) => void;\n /** Fires with whatever app is picked, same or different — the caller owns\n * the same-app-is-a-no-op / different-app-clears-action-and-with decision\n * (it already owns the equivalent decision for `onChangeAction`). */\n onChangeApp: (appId: string) => void;\n /** Fires after a new connection is created via the inline \"+ New\n * connection\" flow, so the caller can refetch its (app-scoped) connection\n * list — `conns` here is a prop, this component owns no fetch of its own. */\n onConnectionCreated: () => void;\n}) {\n // Which flyout (if either) is open. Local, ephemeral UI state: the tab\n // unmounts on tab-switch, so it always starts closed. `AppPicker` fetches\n // and searches its own catalog; the connection picker below just maps\n // `conns` (already app-scoped by the caller) — neither needs its own fetch.\n const [mode, setMode] = useState<\"app\" | \"conn\" | null>(null);\n const [showConnModal, setShowConnModal] = useState(false);\n const rowRef = useRef<HTMLDivElement>(null);\n\n const connName = connectionButtonLabel(step.uses.connection, conns, connsFailed);\n\n return (\n <div className=\"w6w-stack\">\n <div className=\"w6w-field\">\n <span>App</span>\n {/* One field, two buttons — `[App] <-- spacer --> [Connection]`\n (2026-08-30). Always rendered, never swapped out for the picker:\n each button IS the flyout's anchor, and stays clickable the whole\n time its own flyout is open (click again to close — the toggle\n is the only way back out short of picking something or clicking\n away, since there is no third \"cancel\" control). */}\n <div className=\"w6w-app-conn-row\" ref={rowRef}>\n <button\n type=\"button\"\n className=\"w6w-app-conn-btn\"\n aria-label=\"App\"\n aria-expanded={mode === \"app\"}\n disabled={readOnly}\n onClick={() => setMode((m) => (m === \"app\" ? null : \"app\"))}\n >\n {!isInternal && app && (\n <AppIcon\n src={app.iconSvg}\n srcDark={app.iconSvgDark}\n brandColor={app.brandColor}\n name={app.displayName}\n size={20}\n />\n )}\n <span className=\"w6w-conn-label-name\">\n {isInternal ? step.uses.app : (app?.displayName ?? step.uses.app)}\n </span>\n </button>\n {!isInternal && (\n <button\n type=\"button\"\n className=\"w6w-app-conn-btn\"\n aria-label=\"Connection\"\n aria-expanded={mode === \"conn\"}\n disabled={readOnly}\n onClick={() => setMode((m) => (m === \"conn\" ? null : \"conn\"))}\n >\n <span className=\"w6w-conn-label-name\">{connName}</span>\n </button>\n )}\n </div>\n\n {mode === \"app\" && (\n <Flyout anchorRef={rowRef} onClose={() => setMode(null)}>\n <AppPicker\n onSelectApp={(a) => {\n const sameAppReselected = a.id === step.uses.app;\n onChangeApp(a.id);\n // Same app: nothing else changed (action/config untouched, per\n // the ask) — go straight to the connection picker so they can\n // still pick a different connection. Different app: the caller\n // already wiped action/connection/with; close, exactly\n // \"start over\" on the fresh (connection-less) app.\n setMode(sameAppReselected ? \"conn\" : null);\n }}\n />\n </Flyout>\n )}\n {mode === \"conn\" && (\n <Flyout anchorRef={rowRef} onClose={() => setMode(null)}>\n <div className=\"w6w-stack\">\n <div className=\"w6w-stepbuilder-list\">\n {(conns ?? []).map((c) => (\n <button\n key={c.id}\n type=\"button\"\n className=\"w6w-stepbuilder-item\"\n onClick={() => {\n onChangeConnection(c.id);\n setMode(null);\n }}\n >\n <span className=\"w6w-stepbuilder-item-main\">\n <strong>{c.displayName || c.id}</strong>\n {c.state && <span className=\"w6w-muted w6w-small\">{c.state}</span>}\n </span>\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n className=\"w6w-btn\"\n disabled={readOnly}\n onClick={() => {\n setShowConnModal(true);\n setMode(null);\n }}\n >\n + New connection\n </button>\n </div>\n </Flyout>\n )}\n </div>\n\n {!isInternal && showConnModal && (\n <AddConnectionModal\n initialAppId={step.uses.app}\n onClose={() => setShowConnModal(false)}\n onCreated={({ connectionId }) => {\n setShowConnModal(false);\n onChangeConnection(connectionId);\n onConnectionCreated();\n setMode(null);\n }}\n />\n )}\n\n {isInternal ? (\n <div className=\"w6w-field\">\n <span>Action</span>\n <div className=\"w6w-muted w6w-small\">\n <code>{step.uses.action}</code>\n </div>\n </div>\n ) : (\n // biome-ignore lint/a11y/noLabelWithoutControl: Combobox renders its own <select>/<input> INSIDE this label, so the field text labels the control at runtime; the rule cannot see through the component boundary.\n <label className=\"w6w-field\">\n <span>Action</span>\n {/* While the action manifest is still loading there is nothing to pick\n from: the current action renders as the single option, so the field\n keeps showing the step's action instead of going empty. */}\n <Combobox\n value={step.uses.action}\n disabled={readOnly || actions === null}\n onChange={onChangeAction}\n options={\n actions === null\n ? [{ value: step.uses.action, label: step.uses.action }]\n : actions.map((a) => ({ value: a.key, label: a.title ?? a.key }))\n }\n />\n </label>\n )}\n </div>\n );\n}\n"],"names":["MAX_NOTES","MAX_SECTIONS","MAX_NOTE_BYTES","roundGeometry","g","out","k","v","isFinitePos","p","isFiniteSize","z","mapItems","items","live","useLive","it","position","size","withCanvasGeometry","canvas","savePosition","pruneSectionMembers","deletedNodeId","s","n","renameSectionMember","from","to","assignMembership","sectionId","nodeIds","moving","kept","sectionsContaining","nodeId","truncateBytes","text","max","enc","used","ch","clampCanvas","persistCanvas","stored","pin","old","o","base","RESOLVED_VALUE_CLIP","clipResolvedValue","ResolvedParams","params","values","testStartState","view","sampleValues","useExpressionOptions","scope","useMemo","buildResolveScope","flat","flattenParams","effective","key","_a","visibleRows","isParamVisible","json","buildResolvedJson","jsx","JsonEditor","rows","deepEqual","param","ResolvedVarsRow","ResolvedParamRow","resolveParamValue","raw","resolveVarsJson","resolveParamJson","label","segments","jsxs","ResolvedSegments","value","row","i","Fragment","seg","ResolvedSegmentView","segment","ExprChip","ResolvedValueText","expanded","setExpanded","useState","full","formatResolvedValue","clip","e","CHIP_SIGIL","kind","refName","varLabel","edgeLane","collidingEdgeId","survivors","id","canConnect","source","target","nodes","edges","lookup","srcStep","tgtStep","_b","srcPorts","nodePortsForStep","tgtPorts","planConnect","when","next","toTgt","drop","flowEdgeId","addEdge","sourceHandleForLane","edgeVisuals","applyConnect","plan","connectConflict","connectRefusal","planRelane","edgeId","matches","me","others","visuals","relaned","conflict","setEdgeWhen","edgeWhenConflict","relaneRefusal","renameStepInEdges","prevId","nextId","fanOutState","stepId","per","count","setFanOut","step","mode","_drop","rest","fanOutLabel","execOf","runState","RUN_ENDED","stepVisualState","NO_RUN_VISUAL","stepNodeVisual","state","runStatus","runEnded","edgeRunState","exec","lane","applyStepProblems","problems","cur","stepProblemTooltip","isConnectionBlocked","problem","connectionButtonLabel","connection","conns","failed","found","c","useAppActionDefs","api","defs","setDefs","requested","useRef","appIdsKey","app","isInternalApp","useEffect","missing","res","loaded","r","prev","portsLookupFromDefs","action","d","LANE_HINT","clashInvolvedIds","conflictId","sitting","PortsLookupCtx","createContext","FanOutCountsCtx","outCapMessage","cap","AppsCtx","StepControlsCtx","WorkflowFlowEditor","props","ReactFlowProvider","Inner","nextCanvasId","prefix","taken","nodeRect","onChange","readOnly","height","apps","exprOptions","project","stepProblems","onTestRun","fitViewKey","useW6WApi","appsById","a","runResult","setRunResult","initial","workflowToFlow","savedViewport","storedViewport","setNodes","onNodesChange","useNodesState","actionDefs","portsLookup","setEdges","onEdgesChange","useEdgesState","nds","stepStatus","eds","run","selectedId","setSelectedId","initialCanvas","setCanvas","drawingSection","setDrawingSection","sectionDraft","setSectionDraft","pendingCanvasDelete","setPendingCanvasDelete","selectedEdgeId","setSelectedEdgeId","builderOpen","setBuilderOpen","editingId","setEditingId","editView","setEditView","pendingDelete","setPendingDelete","relayoutPendingFitRef","flowContainerRef","pendingConnect","setPendingConnect","screenToFlowPosition","fitView","useReactFlow","colorMode","useEffectiveTheme","withCanvas","useCallback","wf","emitChange","nextNodes","nextEdges","canvasOverride","flowToWorkflow","isValidConnection","onConnect","laneForSourceHandle","refusal","holder","setLaneError","idClashMessage","onConnectEnd","event","connectionState","fromNode","handleType","originStep","point","_c","addFromPort","handleId","x","gap","deleteStep","performDeleteStep","nextCanvas","performRelayout","relayoutNodes","requestRelayout","lastFitViewKeyRef","laneError","selectedEdge","setEdgeLane","deleteEdge","duplicateStep","src","newId","suggestStepId","cloned","newNode","addBuiltStep","built","isInternal","slugifyLabel","internalNodeLabel","existingTrigger","isTriggerApp","root","findChainRoot","findChainTail","updateStep","idChanged","onCanvasChange","addNote","box","at","notes","note","addSectionAt","b","x0","y0","h","members","cx","cy","sections","section","withNew","onKey","onSectionDrop","sec","performCanvasDelete","membershipAfterDrag","dragged","current","persistStepRun","fixture","outcome","saved","err","runStep","node","isControlApp","performRunStep","isTrigger","payload","result","logs","meta","controls","div","handler","card","editingNode","editingStep","runningStep","collecting","runUpstreamSteps","upstreamStateSources","fanOutCounts","m","nodeTypes","StepNodeCard","ControlNodeCard","stepTestOutputs","setStepTestOutputs","stepTestMetas","setStepTestMetas","stepShapes","draftStepId","setDraftStepId","upstreamState","st","excludeSelfStep","upstreamIdsKey","ids","canceled","tests","latest","nextMetas","stepSampleValues","stepMetaSampleValues","output","valueAtPath","inheritedExprOptions","mergedExprOptions","WorkflowProjectProvider","ExpressionOptionsProvider","t","ReactFlow","ConnectionLineType","_e","_n","membershipChanged","viewport","withViewport","sel","edgeSel","Background","Controls","ControlButton","Icon","MiniMap","Panel","IconButton","y","ox","oy","CanvasLayer","ConfirmModal","StepBuilderModal","stepBuilderUpstreamSteps","StepEditModal","Modal","StepRunCollect","parseStateOverride","IncomingStateField","seeds","parsed","keyCount","hasOverride","open","setOpen","seedFrom","test","workflowId","upstreamSteps","onRun","onCancel","asFieldDefs","localParams","fieldsToParams","internalNodeParams","fetched","setFetched","acts","setValues","seedValues","stateText","setStateText","useSeedSources","startState","startStateFromSeeds","seeded","setSeeded","draftValid","setDraftValid","validDraft","canRun","requiredParamsFilled","PropertyEntryForm","PortHandle","type","multiple","variant","useNodeId","ctrl","useContext","canAdd","add","what","Handle","NodeControls","runnable","NodeToolbar","Position","FanOutBadge","parallel","data","selected","appName","ports","visual","fanBadge","AppIcon","ERROR_SOURCE_HANDLE","icon","internalNodeIcon","InternalIcon","initialStep","onClose","initialView","fanOutCount","stepProblem","setStep","blocked","tab","setTab","configView","setConfigView","testView","setTestView","codeText","setCodeText","stepToJson","paramsCodeText","setParamsCodeText","paramsToJson","testState","setTestState","testRunRef","testBusy","setTestBusy","renaming","setRenaming","draftId","setDraftId","renameErr","setRenameErr","setParams","actions","setActions","setConns","connsFailed","setConnsFailed","refetchConns","commit","changeConfigView","commitRename","stepIdError","testable","isTestPropsCode","seedSources","testOverride","testValues","testInput","canTest","internalIcon","titleIcon","ConfigViewToggle","SetupTab","appId","WEBHOOK_APP","WebhookUrlPanel","WaitEditor","w","ParamsForm","NodeConfigForm","CALL_APP","SHOW_STEP_PORTS","StepFanOutControl","TriggerFillForm","StepTestRun","enabled","sub","setSub","error","setError","creating","setCreating","list","subs","create","createSubscription","created","Copyable","Flyout","anchorRef","children","popRef","pos","setPos","container","setContainer","el","onDocPointerDown","onKeyDown","createPortal","onChangeAction","onChangeConnection","onChangeApp","onConnectionCreated","setMode","showConnModal","setShowConnModal","rowRef","connName","AppPicker","sameAppReselected","AddConnectionModal","connectionId","Combobox"],"mappings":"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"}
|