condition node

Branches on an expr-eval expression evaluated against context variables.

Evaluates a small expression against the workflow's accumulated variables and routes to either 'true' or 'false'. Use it to make routing decisions that depend on prior node outputs without involving a script or an LLM.

Purpose#

condition is the cheapest, most deterministic branch in Roscoe. Reach for it when you need to inspect numeric thresholds, compare strings, or combine prior results with boolean logic: anywhere a programmer would reach for an if.

YAML schema#

states:
  passed-threshold:
    type: condition # discriminator
    label: Score above 80? # required, shown in UI / logs
    expression: judge.score >= 80 # required, evaluated each run
    on: # always 'true' / 'false'
      'true': ship
      'false': revise

Configuration#

Field Type Required Default Meaning
type 'condition' yes Discriminator.
label string yes Human-readable name for the step.
expression string yes Expression evaluated against variables. See expression reference.
maxIterations integer (1-50) no Per-run iteration cap when this node sits in a feedback loop. See /docs/recipes/closed-loop-feedback.
detectStall boolean no false Fail the run early if the expression produces an identical result three times in a row.
on transition map yes (in practice) Should contain 'true' and 'false' keys.

There is no validator: routing is fixed by the truthiness of the expression's value.

Expression language#

Expressions are parsed by expr-eval, not the JavaScript engine. The most important consequences:

  • Equality is == and !=, not === or !==.
  • Logic is and / or / not (or && / || / !).
  • No access to process, require, or any Node.js globals. This is a sandbox.

Variables are referenced by node id and output key, e.g. build.result == true or judge.score >= 80. See /docs/authoring/expressions for the full grammar.

Outputs#

Key Type Notes
result unknown The raw value the expression evaluated to (often true/false, but expressions can return numbers/strings).

Transitions#

condition always emits one of two transitions:

Transition When
'true' The expression's value is truthy.
'false' The expression's value is falsy (including 0, "", etc.)

If the expression fails to parse or throws at runtime, the node fails with the parser's error message. It does not silently route to 'false'.

Worked example#

name: gated-refund
version: 1
description: Only auto-approve a refund if it's within policy and the judge gave a high score.
initial: check-policy
states:
  check-policy:
    type: script
    label: Check refund policy
    script: |
      const daysSincePurchase = 25;
      export default daysSincePurchase <= 30;
    validator:
      kind: boolean
    on:
      'true': judge
      'false': failed

  judge:
    type: ai_judge
    label: Reviewer
    model: claude-haiku-4-5
    prompt: |
      Rate how clear-cut this refund request is on a 0-100 scale.
    validator:
      kind: confidence
      threshold: 70
    on:
      'true': gate
      'false': failed

  gate:
    type: condition
    label: Within policy AND high confidence?
    expression: check-policy.result and judge.score >= 85
    on:
      'true': approve
      'false': failed

  approve:
    type: script
    label: Approve refund
    script: |
      console.log('Refund approved');
      export default true;
    validator:
      kind: boolean
    on:
      'true': done
      'false': failed

  done:
    type: end
    label: Done
    outcome: success

  failed:
    type: end
    label: Failed
    outcome: failure

Common pitfalls#

  • Using === or !==. These are JavaScript-only and will fail to parse in expr-eval. Use == / !=.
  • Referencing a variable from a node that hasn't run. If the workflow branched around the producing node, judge.score will be undefined and the comparison will fail. Pair the condition with a prior node that guarantees the variable exists.
  • Truthiness surprises. expression: somenode.stdout is truthy for any non-empty string, including "false". When in doubt, compare explicitly: somenode.stdout == "ready".
  • Trying to call functions or access globals. The sandbox forbids it by design. Move that logic into a script node and route on its output.

Where to next#

  • Conditional branching — a worked recipe that routes by severity with condition nodes.
  • Expressions — the full expr-eval language you can write in expression.
  • Validators — how other node types produce the transition keys you branch on.

View this page as Markdown

Predictable workflows from unpredictable AI