Names, Scope, and Single Assignment
A step is a map with exactly one key, and that key is the step’s id. There is no separate id field, and no step is anonymous.
- classify: # the id is `classify` llm.categorize: { ... }Ids MUST be unique within their enclosing scope and MUST match ^[a-z][a-z0-9_]*$.
Reserved names
Section titled “Reserved names”The names run, inputs, trigger, current, and error are reserved for the bindings that this specification defines. A file MUST NOT use a reserved name as a step id, a trigger as binding, an into binding, an outputs name, or a state’s accepts name.
Note: run is also the key of a step body. A step body key is not an expression path, so the two uses do not conflict. {{ run.id }} always names the run metadata.
Single assignment
Section titled “Single assignment”An id names the binding that holds the step’s result. Bindings are created once and are never reassigned. No syntax mutates a binding, and one id resolves to one value for the life of a run.
A step binds either a value or a scope, according to its body kind. The table of step bindings states which body kinds bind which.
- A binding that holds a value is read directly:
{{ classify.category }}reads a field of an action result. - A binding that holds a scope is read by the ids inside it:
{{ triage.lead.id }}reads theleadstep of the branch that arouteexecuted.
Scopes
Section titled “Scopes”Scopes nest. A step MAY reference a binding in its own scope and in any enclosing scope. The following constructs create a scope: route cases, choose arms, for_each iterations, parallel branches, repeat iterations, and states entries. A binding created inside such a construct is visible within it, and from outside through the construct’s own id.
steps: - classify: { ... } # binds `classify` in the root scope - triage: route: "{{ classify.category }}" cases: sales: - lead: { ... } # binds `lead` inside the `sales` case # visible outside as `triage.lead`An inner binding does not shadow an enclosing one. Ids are unique within a scope, and a construct’s bindings are reached through its own id.
A trigger binds its payload in the root scope. That binding follows the rules of this section. A root-scope step id that repeats the binding’s name is a collision, and as MUST resolve it.