Static Validation
Reliant validates every workflow when it’s loaded. This catches structural problems before your workflow ever runs.What Gets Validated
Static validation checks for:- Entry point validity: Every node listed in
entrymust exist in the workflow - Node references: Edges can’t reference non-existent node IDs
- Input completeness: Every input must have either a
defaultvalue orrequired: true - Loop structure: Loop nodes must have either a
workflowreference or aninlinedefinition - Edge consistency: Edge targets must be valid node IDs within the workflow
- Reserved names: Input groups can’t use reserved names like
default - CEL expression syntax: Template expressions and conditions are checked for valid syntax
Common Validation Errors
Validation runs whenever a workflow is loaded or saved. A project workflow file with errors is listed in Workflows → Library under Failed to load, with the error, and is not offered in the workflow picker; a workflow in the builder with errors cannot be published until they are fixed. The workflow tools an agent uses (
create_workflow, edit_workflow, write_workflow) validate every write and return the problems, and get_workflow reports whether a workflow is valid.
To check a file yourself, run reliant workflow validate path/to/workflow.yaml. It follows every ref: transitively, exits non-zero on any error, and with --trigger-payload dry-runs the workflow’s triggers against a sample event (see Automations).
When Static Validation Isn’t Enough
Static validation confirms your workflow is structurally sound, but it can’t tell you whether the workflow actually does what you intend. A workflow can pass all validation checks and still route to the wrong node, loop forever, or produce unexpected outputs. That’s where scenario testing comes in.Scenario Testing
Scenario testing lets you verify workflow runtime behavior without running real LLM calls or tool executions. You define simulated events—mocked LLM responses and tool results—then assert expected outcomes like which nodes were reached, what outputs were produced, and whether the workflow completed successfully.How It Works
Scenarios run on the real workflow runtime — the same engine that executes your workflow in a chat — inside an in-memory environment. Only the activity layer (LLM calls, tool execution, worktree and daemon operations) is replaced: each activity’s result comes from your mocked events. Edge routing, conditions, loops, joins, sub-workflows, spawns, CEL evaluation andsave_message resolution are all production code, so a passing scenario proves the workflow actually runs. Any activity a scenario cannot mock fails the run by name; nothing ever executes for real.
Scenario Structure
A scenario YAML file defines:Event Types
An event without a
type must look like the real node’s output: it is checked against that node type’s output schema, so a mock cannot claim a shape the real node never produces.
Event Fields
Targeting Nodes
By default, events are consumed sequentially — the first triggered node gets the first event, and so on. Use thenode field to target a specific node:
Inner Nodes (Loops and Sub-Workflows)
For workflows with inline loops or inline sub-workflows usingtype: workflow with inline:, you can target individual nodes inside them using dot-separated qualified IDs:
How inner nodes execute
Inline loops and inline workflow nodes always execute their bodies. Each inner node’s mock is looked up by its qualified ID (loop_id.inner_node_id, workflow_id.inner_node_id), inner conditions skip nodes exactly as they do at runtime, and a loop’s while and declared outputs: are evaluated after each iteration.
Referenced loops or workflows (ref:) are black-boxed by default: an event on the node itself (node: review) supplies the node’s whole output, and the referenced workflow’s body does not run. When the scenario targets qualified inner nodes (node: review.agent_loop.call_llm), the referenced workflow is loaded and executes for real instead.
To mock a loop as a unit — its body does not run — set black_box: true on its event; the event’s output is the loop’s aggregate output (_results, _iterations, declared outputs) exactly as downstream nodes read it.
Events are always keyed by node path. Keying an event by a workflow ref (node: builtin://agent) is a validation error: one ref can back many nodes, so name the node that references it.
Expectations
Theexpect section defines assertions:
messages come from its save_message: block, or from the node itself for a save_message node, resolved by the real runtime against each mocked result. saved: false asserts it saved nothing; count asserts an exact number (a node in a loop saves once per iteration); content_not_contains rules text out.
Partial Testing
Usestart_at and state to test from a specific point in the workflow:
Loop Testing Patterns
Single Iteration (Exit Immediately)
Multi-Iteration Loop
Provide events for each iteration. Events for the same node are consumed in order, one per execution:Testing Loop Output Routing
When a loop’s inline workflow declaresoutputs:, those evaluated outputs are available for downstream edge conditions:
Using Scenario Tools
An agent manages scenarios with these workflow tools (in any chat,load_tool(name="tag:workflow") loads them):
Scenarios are stored alongside the workflow and run instantly without network calls or LLM API usage.
Scenarios in your project
A project workflow’s scenarios live in.reliant/workflows/<name>/scenarios/, one scenario per .yaml file, where <name> is the workflow’s name: as a slug — not its file name. A workflow in blog.yaml that declares name: blog-content-pipeline keeps its scenarios in blog-content-pipeline/scenarios/:
.reliant
workflows
blog.yaml
blog-content-pipeline
scenarios
drafts_from_brief.yaml
rejects_empty_brief.yaml
--include-builtins lists or runs them alongside yours from any directory. They are worked examples of every scenario feature on this page.
A scenario file’s name: defaults to its file name. A file holding several scenarios is an error — split it into one file per scenario. Scenarios in the retired layouts (scenarios/<file>/ or <file>_scenarios.yaml) are also an error, and the message names the directory each file belongs in.
A sub-workflow reference resolves the same way everywhere: ref: project://blog-content-pipeline names the workflow by its name:, in a chat, in a scenario, and in reliant workflow validate. validate follows every reference transitively, so a broken one is reported with the chain that reaches it — content-weekly.yaml → project://content-next → project://blog: no project workflow is named "blog"; blog.yaml is named "blog-content-pipeline" — and a reference cycle is an error.