fabro/docs/public/workflows/variables.mdx
Bryan Helmkamp c28a102af8
docs: sync public docs to recent changes
Document Daytona Dockerfile path refs, static template includes, Slack review context, skipped LLM setup, and template validation behavior.
2026-05-18 14:42:54 -04:00

119 lines
4.5 KiB
Text

---
title: "Variables"
description: "Using templates in workflows"
---
Fabro uses `{{ ... }}` templates for workflow strings and prompts.
## Template context
Workflow and prompt templates can reference:
| Expression | Resolves to |
|---|---|
| `{{ goal }}` | The workflow goal |
| `{{ inputs.name }}` | A value from `[run.inputs]`, optionally overridden by CLI input flags |
Environment variables are **not** available in workflow or prompt templates. Use `{{ env.NAME }}` only in config strings and HTTP hook headers.
## Run config inputs
Define typed inputs in `[run.inputs]`:
```toml title="run.toml"
_version = 1
[workflow]
graph = "check.fabro"
[run]
goal = "Run repository checks"
[run.inputs]
repo_name = "fabro"
repo_url = "https://github.com/fabro-sh/fabro"
language = "rust"
```
These values are available in final string attributes such as `goal`, `prompt`, `script`, `label`, and edge labels:
```dot title="check.fabro"
digraph Check {
graph [goal="Run tests for {{ inputs.repo_name }}"]
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
clone [label="Clone", shape=parallelogram, script="git clone {{ inputs.repo_url }} repo"]
test [label="Test", prompt="Run the {{ inputs.language }} test suite in the repo/ directory."]
start -> clone -> test -> exit
}
```
Override individual inputs at run time with repeatable `-I` / `--input` flags:
```bash
fabro run .fabro/workflows/check/workflow.toml -I repo_name=fabro-2 --input language=rust
```
CLI input values use TOML scalar parsing when possible. Quoted strings, booleans, integers, and floats keep their typed values; unquoted bare text falls back to a string. Empty values such as `foo=` are accepted as empty strings. Arrays, inline tables, and datetimes are rejected.
## `goal`
Agent and prompt nodes also receive the workflow goal at runtime:
```dot title="example.fabro"
digraph Example {
graph [goal="Implement the login feature"]
plan [label="Plan", prompt="Create a plan for: {{ goal }}"]
}
```
That prompt becomes `Create a plan for: Implement the login feature`.
## Expansion timing
Fabro keeps workflow structure static and renders workflow templates once:
1. Fabro parses the root DOT and imported `.fabro` files without rendering them.
2. Literal `import`, `@file`, graph-goal file, and child-workflow references are resolved.
3. Final graph, node, and edge string attributes are rendered with the `{ goal, inputs }` context.
Templates are not supported in graph syntax, node IDs, edge structure, `import` paths, `@file` paths, child workflow paths, or other file references.
Fabro renders the graph `goal` first and stores the rendered value back onto the graph. Other attributes that use `{{ goal }}` receive that rendered value.
## Undefined variables
Fabro renders undefined workflow variables as empty text and records a `template_undefined_variable` diagnostic. `fabro validate` reports that diagnostic as a warning so you can validate workflow structure before all inputs are known. Run-style commands such as `fabro run`, `fabro create`, and preflight promote the same diagnostic to an error before proceeding.
## Template includes
Prompt and goal templates support static MiniJinja loader dependencies such as `{% include "partial.md" %}`. Includes are resolved relative to the template file being rendered and can be nested.
Fabro discovers those static dependencies while building the run manifest so sandbox providers receive every required prompt file. Dynamic loader expressions such as `{% include inputs.partial %}` are rejected; use a literal include path and choose content with normal template conditionals instead.
## Escaping
To emit literal template syntax, use MiniJinja escaping:
```dot
test [prompt="{% raw %}{{ goal }}{% endraw %}"]
```
You can also emit literal braces with expressions such as `{{ '{{' }}` when needed.
## Input merging
TOML `[run.inputs]` tables intentionally replace the inherited map wholesale rather than merging by key. Whichever TOML layer has the highest precedence and sets `[run.inputs]` wins its entire map.
CLI input flags are different: they are sparse per-key overrides applied after config resolution, so unrelated inherited inputs remain available. If a key is repeated on the CLI, the last value wins.
| Source | Priority |
|---|---|
| CLI flags (`-I key=value` / `--input key=value`, repeated; per-key merge) | Highest |
| `workflow.toml` `[run.inputs]` | |
| `.fabro/project.toml` `[run.inputs]` | |
| `~/.fabro/settings.toml` `[run.inputs]` | Lowest |