Concepts

The graph playground edits a workflow description. Nodes and edges express intent; the showcase does not execute the represented webhook, transformation, condition, or destination. Use recipes for concrete edits and demos for focused examples.

Graph data

A FlowGraph contains nodes and edges. A node has an id, a supported type, a canvas position, and data. Its data contains a title, optional subtitle and icon, and a params record whose values are JSON-compatible. An edge identifies its source and target nodes, its source and target handles, and an edge kind in data.kind.

Node identity is separate from its display title. Changing how a node is described is not a reason to reuse another node’s identifier. Coordinates describe presentation rather than execution order; edges carry the connections.

Each demo starts from its own fixture. createDemoGraph returns a deep working copy, so editing a demo does not mutate the fixture used for a fresh copy.

Node kinds and handles

The five node kinds have different connection surfaces:

KindRoleIncoming handleOutgoing handles
inputA source of data or eventsNoneout
transformA configurable processing stepinout
branchA decision with two outcomesinyes, no
sinkA destination or terminal stepinNone
noteAn annotation, not a processing stepNoneNone

A directed wire goes from a source handle to a target handle. An ordinary edge uses out to in; a branch edge must use its yes or no output instead of inventing an out handle. A note cannot be inserted into a data path merely because it appears on the canvas.

flowchart LR
    Input["Input: out"] --> Transform["Transform: in → out"]
    Transform --> Branch["Branch: in"]
    Branch -->|"yes"| Accepted["Sink: in"]
    Branch -->|"no"| Rejected["Sink: in"]
    Note["Note: no handles"]

The labels and parameters describe the workflow. A branch condition such as ready or score >= 80 is not evaluated by drawing the branch.

Edge kinds

The model distinguishes three kinds, with consistent factory-generated presentation:

  • data: teal solid curved edge with a closed arrowhead; it is not animated.
  • control: purple dashed smooth-step edge with an open arrowhead; it is animated.
  • error: red dotted step edge with a closed arrowhead; it is not animated.

Color is not the only distinction: line pattern, geometry, and arrowhead reinforce the kind. These are graph semantics and visual conventions, not guarantees of delivery, scheduling, or exception handling. The demos > edges example isolates their presentation.

Deterministic layout

The shared layout function accepts nodes, edge endpoints, and optional settings. LR places stages left to right; TB places them top to bottom. layerGap separates stages and nodeGap separates nodes within a stage. The demos > layout controls expose direction and spacing presets.

The algorithm:

  1. Sorts identifiers and neighbor lists to make tie-breaking stable.
  2. Groups strongly connected nodes into components, so cycles do not make layout recurse forever or fail.
  3. Assigns longest-path ranks to the resulting component graph.
  4. Performs four alternating barycenter sweeps to order nodes within layers.
  5. Places nodes using their dimensions, spacing, and origin, then returns new node objects with coordinates and matching source/target orientation.

Measured dimensions take precedence over explicit dimensions and initial dimensions; missing usable dimensions fall back to the layout defaults. Determinism therefore means the same graph, dimensions, and options produce the same placement. A new measurement or a changed spacing option can legitimately change coordinates.

Layout considers connections of all edge kinds and ignores endpoints absent from the supplied nodes when calculating positions. It does not remove or repair invalid edges. Layout can arrange a cyclic graph, but that does not establish whether the connection validator permits that cycle.

Proposals and graph changes

The local assistant’s patch vocabulary is add_node, add_edge, set_layout, delete, validate_report, and explain. A proposal is not an applied graph edit. The user chooses an individual Apply control or Apply all; reports and explanations can be returned without changing nodes or edges.

The application result separates applied patches, rejected patches with reasons, reports, explanations, and whether the graph changed. A missing endpoint is not silently fabricated for an edge patch. Read assistant for application ordering and the boundary between local deterministic behavior and any optional model integration.

Structured validation

validateConnection checks a proposed connection against the current nodes and edges. validateGraph checks the portable graph shape and then the graph’s topology. Both return { ok, code, message }: a success has ok: true and code: 'ok'; a failure has ok: false and a diagnostic code.

The graph validator returns the first encountered failure, not a list of every problem:

CodeMeaning
invalid_graphA node or edge does not have the required portable shape, supported kind, finite coordinates, or JSON-compatible parameters.
duplicate_nodeMore than one node uses the same identifier.
duplicate_edgeAn edge identifier or source-handle/target-handle connection tuple is repeated.
missing_nodeAn edge endpoint does not exist.
self_loopA node connects to itself.
input_inboundAn input receives a connection.
sink_outboundA sink sends a connection.
control_cycleThe subgraph formed only by control edges contains a cycle.

Duplicate connection comparison treats omitted or null source and target handles as out and in. Distinct explicit handle pairs remain distinct connections, but changing an edge’s kind alone does not make an otherwise identical wire unique.

The cycle check is deliberately specific: a data or error cycle is not rejected simply for being cyclic. An empty graph, disconnected nodes, or an unused branch output is not automatically a validation failure. Validation does not evaluate node parameters as executable operations.

The shared validator does not currently check handle-name membership or reject an edge solely because it touches a note. The node renderers’ handles define the intended interactive connection surface; portable data passing the topology check should not be mistaken for proof that every supplied handle is meaningful.

Use recipes > validate-graph to request a report, correct the reported issue, then validate the current graph again. A report describes the graph inspected when it was requested; it is not a perpetual validity guarantee after later edits.

Source of truth

The executable model and fixture definitions live in src/lib/flow/graph.ts; deterministic layout lives in src/lib/flow/layout.ts; portable-shape and topology checks live in src/lib/flow/validate.ts. Assistant response, patch, and report types live in src/lib/assist/types.ts. These pages explain those contracts without claiming a production deployment has been verified; see operations for operational procedures.