Skip to main content
Fabro workflows are written in a subset of the Graphviz DOT language with extensions for agent orchestration. This page is the complete syntax reference. For conceptual introductions, see Workflows and Nodes & Stages.

File structure

Every workflow is a digraph (directed graph) with a name and a body of statements:
my-workflow.fabro
Only digraph is supported — graph (undirected) and strict are not. The graph name is required. Semicolons after statements are optional.

Comments

Comments inside quoted strings are preserved as literal text.

Value types

Attribute values in [key=value] blocks can be: Escape sequences in quoted strings: \", \\, \n, \t. Duration units: ms (milliseconds), s (seconds), m (minutes), h (hours), d (days).

Statements

The body of a digraph can contain these statement types:

Graph attributes

Set workflow-level configuration:

Node defaults

Apply default attributes to all subsequently declared nodes:
Defaults are scoped to their enclosing subgraph. Explicit attributes on individual nodes override defaults.

Edge defaults

Apply default attributes to all subsequently declared edges:

Node declarations

Declare a node with optional attributes:
Node identifiers must start with a letter or underscore, followed by letters, digits, or underscores (e.g. run_tests, gate_1, _private). Nodes referenced in edges are auto-created if not explicitly declared.

Edge declarations

Connect nodes with directed edges:
Chained edges like A -> B -> C expand to individual edges A -> B and B -> C, all sharing the same attributes. Edges can have attributes:

Subgraphs

Group nodes visually and apply scoped defaults:
When a subgraph has a label, it is converted to a CSS class name and applied to all nodes within the subgraph (e.g. "Implementation" becomes class implementation, "Loop A" becomes loop-a). This enables stylesheet targeting. Node and edge defaults declared inside a subgraph are scoped — they don’t leak to the outer graph. Edges can cross subgraph boundaries.

Node types

Each node’s shape attribute determines its execution behavior. See Nodes & Stages for detailed documentation of each type. The type attribute can also be set explicitly to override the shape-based mapping. Start nodes can also be identified by ID (start or Start). Exit nodes can be identified by ID (exit, Exit, end, or End).

Node attributes

All nodes

Agent and prompt nodes

Structured output validation

output_schema opts an agent, prompt, or command node into strict JSON validation:
  • output_schema="routing" requires a JSON object with at least one recognized routing field: preferred_next_label, outcome, failure_reason, suggested_next_ids, or context_updates.
  • output_schema="@schemas/audit-result.schema.json" loads a JSON Schema file using workflow file-reference rules and validates the final JSON object in the response text. Inline JSON Schema object strings are also accepted, but file references are usually easier to read.
  • On validation failure, Fabro sends validation feedback to the same active context before failing: prompt nodes keep the prior assistant response in the message list, and API-backed agent nodes repair in the same live session.
  • output_retries defaults to 2 and controls only these corrective structured-output turns. Negative values are treated as 0. It is not the same as max_retries and does not consume workflow retry attempts.
  • Custom schema output is stored in context at output.{node_id}. Routing schema output updates routing fields and any context_updates.
  • Agent routing fallbacks still apply to output_schema="routing": response text first, then status.json, then the last file touched by the agent. The last-file fallback only accepts .json and .md files (case-insensitive) whose final JSON object contains the routing directive; only whitespace may follow it. Custom schemas and prompt nodes validate response text only.
  • Command nodes validate merged stdout and stderr only after the script exits with code 0, using the same object selection as agents and prompts: custom schemas validate the last JSON object, and routing validates the last JSON object containing a recognized routing field. Print the intended JSON object last. A validation error is a deterministic, non-retryable failure with no repair turn or status.json fallback. output_retries, retry_policy, and max_retries do not retry it. Nonzero exits retain normal command failure behavior without schema validation.
  • For commands, custom schema output is stored at output.{node_id}; edge conditions cannot traverse into its fields. The routing schema applies routing fields and merges context_updates into flat context keys, which conditions can read (for example, context.kept_count).
  • backend="acp" with output_schema is unsupported in this release.

Command nodes

Parallel (fan-out) nodes

For the first node in each branch, fidelity resolves from the fork-to-branch edge, then the branch node; without either, the fork preamble is inherited unchanged. Branch-specific preambles are rendered before fan-out from the fork’s context snapshot. Concurrent branches cannot share sessions, so explicit branch full becomes summary:high, and branch-level thread_id is inert.

Wait nodes

Human nodes

Manager loop (sub-workflow) nodes

Edge attributes

Condition expressions

Edge conditions are boolean expressions evaluated against the stage outcome and run context. See Transitions for the full routing logic.

Grammar

Keys

Operators

A bare key with no operator is a truthiness check — it passes if the value is non-empty, not "false", and not "0".

Examples

Prompt and schema file references

Instead of inlining long prompts or JSON Schemas, reference an external file:
The @ prefix tells Fabro to load the referenced file relative to the workflow file. Paths support ~ (home directory) and .. (parent directory):
Untracked @file references (files not committed to git) are inlined into the Graphviz source at prepare time, so they work even inside sandboxes that only see the git tree. Fabro validates @file references at parse time — if the referenced file does not exist, validation fails with a clear error pointing to the bad reference.

Validation

Fabro validates workflows at parse time and reports diagnostics. Key rules:
  • Exactly one start node and one exit node
  • All nodes reachable from start
  • No incoming edges to start, no outgoing edges from exit
  • Edge targets reference existing nodes
  • Condition expressions parse correctly
  • Stylesheet syntax is valid
  • LLM nodes (agent, prompt) have a prompt attribute
  • @file references point to existing files
  • Conditional nodes have multiple outgoing edges with conditions
  • Retry targets reference existing nodes
  • Goal gates have retry configuration
  • thread_id requires fidelity="full" (session reuse depends on full fidelity)
  • Known handler types only

Complete example

implement-feature.fabro