Execution Model
This section defines the behavioral semantics of executing a Workflow. It specifies how context flows between Nodes, how routing decisions are made, and what observable events and traces a conforming executor MUST produce.
Context Accumulation
Section titled “Context Accumulation”At each node, a conforming executor MUST provide a context object containing:
input— the original workflow input passed to the executor.- One key per previously completed node, where the key is the node ID and the value is that node’s result data.
context = { input: <original workflow input>, nodeA: <nodeA result data>, nodeB: <nodeB result data>, ...}This context is available to the node’s instruction execution and to edge condition evaluation.
Context is append-only: each completed node adds its result to the context. If a node executes multiple times (via a bounded cycle), the context contains the most recent result for that node ID.
Node Execution Sequence
Section titled “Node Execution Sequence”For each node, a conforming executor MUST perform the following steps in order:
- Resolve Sources (once, before any node executes). Every Source in the workflow — node instructions, input rules/context, templates — is resolved. Files are read; URLs are fetched. Results are recorded in
trace.sources. - Resolve skills from the node’s
skillslist (see Skills). - Collect tools from resolved skills that have
tools. - Collect MCP servers from resolved skills that have
mcp. Wire them for this node’s execution session only (node-scoped, not global). - Build context — the accumulated context object (input + all prior node results).
- Build instruction — the node’s base instruction, augmented with input
rulesandcontextfields (see Nodes — Input Augmentation), then skill instructions from resolved skills that haveinstruction. - Invoke the AI model with: instruction, context, tools (from step 2 + MCP tools from step 3), and output schema (if present). The model is resolved per Model Selection (
node.model→workflow.model→ executor default); when none is specified the executor uses its implementation-defined default. - Capture the result as a
NodeResult. - Emit execution events (see Execution Events below).
- Resolve the next node via edge routing.
NodeResult
Section titled “NodeResult”The result of executing a single node.
| Field | Type | Description |
|---|---|---|
status | "success" | "skipped" | "failed" | Outcome of this node’s execution. |
data | Record<string, unknown> | Output data. When the node has an output schema, this conforms to it. Available to downstream nodes via context. |
toolCalls | ToolCall[] | Record of tools invoked during execution. |
ToolCall
Section titled “ToolCall”| Field | Type | Description |
|---|---|---|
tool | string | Tool name. |
input | unknown | Input passed to the tool. |
output | unknown | Output returned by the tool. Absent on error. |
Dry-Run Semantics
Section titled “Dry-Run Semantics”When the workflow input contains dryRun: true, a conforming executor MUST:
- Execute nodes normally through unconditional edges.
- Stop at the first node that has conditional outgoing edges (edges with
whenclauses). - Return the execution result with all nodes completed up to that point.
This enables “analysis without action” — a workflow runs through gathering and analysis nodes but stops before making action decisions that require conditional routing.
Unconditional edges are deterministic transitions (e.g., gather → investigate). Conditional edges represent decision points (e.g., investigate → create_issue vs skip). Dry-run halts at decisions.
Execution Events
Section titled “Execution Events”A conforming executor MUST emit the following events during execution. Events are delivered to an observer callback. If the observer throws, the executor MUST NOT crash — observer errors are silently swallowed.
| Event Type | Payload | Emitted When |
|---|---|---|
workflow:start | { workflow: string } | Before the first node executes. |
sources:resolved | { sources: Record<string, ResolvedSource> } | Once, after the Source resolution phase completes, before the first node executes. |
node:enter | { node: string, instruction: string } | Before a node begins execution. |
tool:call | { node: string, tool: string, input: unknown } | Before a tool is invoked. |
tool:result | { node: string, tool: string, output: unknown } | After a tool returns. |
node:progress | { node: string, message: string } | During node execution (streaming status). |
node:exit | { node: string, result: NodeResult } | After a node completes. |
route | { from: string, to: string, reason: string } | After an edge is selected for routing. |
workflow:end | { results: Record<string, NodeResult> } | After all execution is complete. |
Event Ordering
Section titled “Event Ordering”For each node, events MUST be emitted in this order:
node:enter- Zero or more
tool:call/tool:resultpairs andnode:progressevents (interleaved). node:exitroute(if a next node exists)
The first event of any execution is workflow:start, followed by sources:resolved, then the first node:enter. The last is workflow:end.
Execution Trace
Section titled “Execution Trace”A conforming executor MUST produce an ExecutionTrace after execution completes. The trace records the complete ordered path through the workflow, including loop iterations.
TraceStep
Section titled “TraceStep”| Field | Type | Description |
|---|---|---|
node | string | Node ID. |
status | "success" | "failed" | "skipped" | Outcome. |
iteration | number | 1-based iteration count. 2 means this node executed a second time (via bounded cycle). |
TraceEdge
Section titled “TraceEdge”| Field | Type | Description |
|---|---|---|
from | string | Source node ID. |
to | string | Target node ID. |
reason | string | Why this edge was followed — the when condition text, or "only path" for unconditional edges. |
ExecutionResult
Section titled “ExecutionResult”The return value of a workflow execution.
| Field | Type | Description |
|---|---|---|
results | Map<string, NodeResult> | Final result per node. If a node executed multiple times (bounded cycle), this contains the last result. |
trace | ExecutionTrace | Ordered record of all steps and routing decisions. |
trace.sources
Section titled “trace.sources”Every resolved Source is recorded under trace.sources, keyed by field path (e.g. nodes.investigate.instruction, input.rules[0]). The value is a ResolvedSource containing content, kind, hash, and provenance.
This enables diffing runs to detect drift in externally-hosted content.
Error Handling
Section titled “Error Handling”- A node that fails MUST produce a
NodeResultwithstatus: "failed". - Whether the executor continues to subsequent nodes or terminates is implementation-defined.
- The trace MUST record the failure.
- Tool handler errors are surfaced to the AI model as tool results; they do not terminate execution.
AI Model Interface (Informative)
Section titled “AI Model Interface (Informative)”This section is informative, not normative. It describes the abstract interface the reference implementation uses. Alternative implementations MAY use different interfaces as long as observable behavior (context accumulation, routing, events, trace) conforms to this specification.
The reference implementation defines two AI model operations:
Execute a node. Takes:
instruction— the built instruction string.context— the accumulated context object.tools— resolved tool list from the node’s skills.outputSchema— optional JSON Schema for structured output.onProgress— optional callback for streaming status messages.
Returns a NodeResult.
evaluate
Section titled “evaluate”Route at a conditional edge. Takes:
question— a routing prompt (e.g., “Based on the results so far, which condition is true?”).context— the accumulated context object.choices— an array of{ id: string, description: string }whereidis the target node ID anddescriptionis thewhenclause text.
Returns the selected choice id (target node ID).