How transitions work
When a node completes, it produces an outcome with a stage outcome (succeeded, failed, partially_succeeded, or skipped) and optional signals like a preferred label or suggested next node. Fabro evaluates the outgoing edges in a fixed priority order:
- Condition match — Edges with a
conditionattribute are evaluated first. If one or more conditions match, the edge with the highestweightwins (lexical tiebreak on target node ID). - Preferred label — If the node’s outcome includes a preferred label (e.g. from a human gate selection), the edge whose
labelmatches is chosen. - Suggested next — If the node suggests a specific next node ID, the edge pointing to that node is chosen.
- Unconditional fallback — Edges without conditions are considered last, again using
weightthen lexical tiebreak.
Edge attributes
Conditions
Edge conditions are boolean expressions evaluated against the stage outcome and run context. Conditions go in thecondition attribute on an edge:
Available 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":
Combining conditions
Use&& (AND), || (OR), and ! (NOT) to build compound expressions. && binds tighter than ||:
Agent transitions
Agent and prompt nodes can influence which edge is taken by including a JSON object in their response with routing directives. Fabro scans the LLM output for the last JSON object containing any of these fields:
Fabro automatically scans LLM output for these JSON objects — no special configuration is needed. However, you do need to instruct the LLM to emit the JSON in your prompt. For example:
Human gate transitions
Human gates use edge labels to present options to the user. The selected label becomes thepreferred_label in the outcome, and Fabro matches it to the corresponding edge:
[A], [R], [S] prefixes are keyboard accelerators — Fabro strips them when matching, so the user can type just the letter.
Unconditional edges
An edge without acondition attribute always matches. When a node has a single outgoing edge, it doesn’t need a condition:
Weight tiebreaking
When multiple edges match (e.g. two unconditional edges),weight determines the winner. Higher weight wins:
Random selection
By default, tiebreaking between candidate edges is deterministic (highest weight, then lexical node ID). Settingselection="random" on a node switches to weighted-random tiebreaking for its outgoing edges:
path_a is chosen ~75% of the time and path_b ~25%. Edges with weight ≤ 0 are treated as weight 1. The cascade priority (conditions → preferred label → suggested next → unconditional) is unchanged — randomness only affects the pick-one-from-candidates step within each tier.
selection="random" cannot be combined with conditional edges on the same node. Validation rejects this combination because condition evaluation order would conflict with random selection. Use unconditional edges with weights instead.