ALLOWED
Edge committed. Tooltip on hover shows the sous-typage reason —
Force → Numeric explains “Numeric is a descendant of Float”.
The editor lives at /workflows/{slug}/edit and is reached from:
/workflows.Direct URL works for bookmarks. /workflows/{slug} redirects to
/workflows/{slug}/edit — there is no separate read-only detail page in
v1.
┌──────────┬─────────────────────────────────┬────────────────┐ │ PALETTE │ [Auto inputs] Validate|Save|Run │ PROPERTIES │ │ ├─────────────────────────────────┤ │ │ INPUTS │ │ (visible when │ │ ├ Force │ │ a node is │ │ ├ Length│ REACT FLOW CANVAS │ selected — │ │ ├ … │ │ branched by │ │ └Custom…│ │ node kind) │ │ │ │ │ │ Packages │ │ │ │ ├ func │ │ │ │ ├ func │ │ │ └──────────┴─────────────────────────────────┴────────────────┘The top of the palette is dedicated to input nodes. Two paths:
Force, Length, Moment, …). Drop on the canvas ⇒ a typed input
node appears with its label pre-filled (and editable later).Force), parameterised
(list[Force], dict[str, Length]) or inline struct as JSON:
{"d": "Length", "p": "Length", "As": "Area"}.Every input node has a single output port named value; edges from the
node use that port.
ready versions are displayed.function_name by default, with a
numeric suffix (force-calc-2) on collisions inside the same graph.The toolbar hosts an Auto inputs switch. When on, dropping a function
node also creates:
io_spec.Force, struct, list…).value to the
matching function port.The auto-created input nodes are placed on the left of the function node, stacked vertically around the function’s Y position. Left off, the user manually drags types from the palette — useful when you want to share an existing input node across two functions.
POST /types/check-connection asynchronously
before materialising the edge.ALLOWED
Edge committed. Tooltip on hover shows the sous-typage reason —
Force → Numeric explains “Numeric is a descendant of Float”.
REFUSED
Destructive toast surfaces "Force → Length · …". The edge is
not added to the canvas; no DB state, no validation noise.
The type system reference covers the exact rules.
Click a node. A right-hand panel opens. The panel branches on the node’s kind.
| Field | Meaning |
|---|---|
| Node key (read-only) | The stable identifier used in edges and run outputs. |
| Display label | Free-form override of the function name. Saved as custom_label. |
| Function version | Dropdown listing every ready version of the same function. Switching drops edges whose ports no longer exist in the new signature (FRO-wkf-05). |
| Source package | Link to /packages/{id} for the owning package. |
| Field | Meaning |
|---|---|
| Node key (read-only) | Becomes the key of the run submission payload (inputs[node_key]). |
| Label | Shown on the node card and as the title of the run-form card. |
| Type expression | Textarea accepting a canonical name, a parameterised form, or struct JSON. Validated on blur — malformed JSON surfaces an inline error. |
| Description | Optional free text rendered under the field at run time. |
| Required | When off, the run form allows leaving the field blank and the function receives its default. |
Close the panel with the × button or click the canvas background.
Validate runs a dry-run of the full graph validation against
POST /workflows/{slug}/validate. Unlike the save path, this endpoint:
200 OK with a structured report — even on errors.The dialog categorises issues:
type_expr the catalog cannot parse.io_spec.io_spec.Save version POSTs to /workflows/{slug}/versions. The backend runs
the full validation; on success a new version (monotonic integer) is
created and becomes is_latest=true. On validation failure the call
returns 409 with the same report shape as Validate.
Old versions are immutable and remain queryable — runs reference the version they were started against, not the current head.
The Run button is disabled while the canvas has no node or no version
has ever been saved. When enabled, it navigates to
/workflows/{slug}/runs/new — see
execution for the full flow.
Out of scope for the v1 editor — planned for v1.5 / v2:
if, for (FRO-wkf-15).