# condition node

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

```yaml
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](/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`](https://www.npmjs.com/package/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](/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

```yaml
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](/docs/nodes/script)
  node and route on its output.

## Where to next

- [Conditional branching](/docs/recipes/conditional-branching) — a worked
  recipe that routes by severity with `condition` nodes.
- [Expressions](/docs/authoring/expressions) — the full expr-eval language you
  can write in `expression`.
- [Validators](/docs/authoring/validators) — how other node types produce the
  transition keys you branch on.
