Workflow graph
WorkflowDsl and WorkflowDag define the structural boundary for portable workflow documents. They parse nodes and edges, preserve unknown extension fields, validate graph structure, and derive a deterministic plan. The host still binds concrete node execution.
Flow does not grant model, database, or tool access from data.type. The host validates and authorizes a node before connecting its semantics to durable steps.
Full documents and bare graphs
A complete document includes version, application information, and workflow.graph. A host that only needs the canvas graph can parse WorkflowDag directly.
One UTF-8 document is limited to 10 MiB. The currently tested DSL version is 0.7.0. An older minor may import with warnings. A newer version or different major requires an explicit host decision.
Minimal graph shape
Every node needs a non-empty id and string data.type. Every edge needs a unique ID and existing source and target nodes.
Connection labels in the Playground
The Playground initially shows the source output-port name in the middle of a connection. For example, a batch node's done port is localized as “All complete”. This text helps people read the graph; it is not the port ID used for routing.
Select a connection and click the pencil beside its label, or double-click the label itself. You can also select the connection and press Enter or F2. Press Enter to save, Escape to discard, or leave the field to save it. Clearing the field restores the port's default name. Custom names are kept in the local draft and in an exported workflow file.
Renaming a label does not change sourceHandle, targetHandle, the execution plan, or runtime semantics. Condition branch names should still be edited through the node's matched_label and otherwise_label fields because those fields are part of the condition node's business copy. A regular connection name is presentation metadata, so validate the graph by its port IDs before publishing.
Derive an execution plan
When several nodes are ready in one scope, the plan sorts by stable ID to remove input-array ordering effects. A plan expresses structural topology only. It does not execute nodes or choose failure, retry, and authorization policy.
An empty canvas can parse and round-trip so an editor may retain drafts. execution_plan() rejects it because there is no executable node.
Structural validation
Planning rejects these conditions.
- Duplicate or empty node and edge IDs
- Missing string
data.type - An edge referencing a missing node
- A self-edge
- A cycle in any scope
- An edge crossing top-level and container scopes
- A
parentIdreferring to a missing or unsupported container - An iteration or loop container without its matching start child
- More than 10,000 nodes or 100,000 edges
These checks make topology deterministic. They do not validate business parameters for a particular executor. The host must apply a schema to every authorized node type.
Iteration and loop scopes
iteration and loop nodes may own a container scope. Children declare the scope through parentId, and internal edges may connect nodes within that same container only.
The matching start node has three requirements.
- Its
parentIdequals the container ID. - Iteration uses
iteration-start, while loop usesloop-start. - The container has at least one additional executable child.
plan.scope("for-each-item") returns deterministic order inside the container. The container itself remains in the top-level plan.
Semantic digest
execution_digest() produces a stable SHA-256 identity for executable semantics.
Node positions, selection state, viewport, and input-array order do not affect the digest. Node data, semantic edge data, endpoints, and scope do. A host may use it to detect whether two publications only changed canvas layout.
The digest proves matching input semantics only. It does not prove that host node bindings are identical. Executor versions still belong in host release identity or runtime_build_id.
Lossless round trips and extensions
The parser retains unknown top-level, application, workflow, node-presentation, and edge-presentation fields. to_yaml() and to_json() write those fields back.
Preservation does not imply semantic acceptance. Keep an allowlist at the security boundary.
- Parse and enforce document size first.
- Run structural plan validation.
- Apply host schemas to allowed
data.typevalues. - Resolve credential references and reject plaintext secrets in graph documents.
- Record document digest and executor versions at publication time.
Connect graphs to durable execution
The graph compiler returns node order, while the Flow engine persists run decisions. A host commonly maps a node to a stable step ID and stores branch choices or container cursors in step output or continuation input.
If graph semantics change while old runs remain active, create a new workflow definition version or a replay-safe patch path. Do not interpret existing history with a replacement graph.
See examples/workflow_dsl_import.rs for a complete import program.
