Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

choose — readable branching

choose runs the first branch whose if: is true (or the trailing else), and nothing else. It replaces long chains of negative if: conditions with one readable block.

rflow_version: 1
name: risk-router
 
config:
  port: 3940
  db_connection: ${DATABASE_URL}
 
networks:
  - name: ethereum
    chain_id: 1
    rpc: ${ETH_RPC}
 
# DEV ONLY raw mnemonic - swap in a production signer before real funds
signer:
  raw:
    mnemonic: ${RAW_DANGEROUS_MNEMONIC}
 
relayers:
  ops:
    networks: [ethereum]
 
contracts:
  Vault:
    abi: ./abis/vault.json
    addresses:
      ethereum: "0x0000000000000000000000000000000000000000" # replace
 
notifications:
  channels:
    pager:
      pagerduty:
        routing_key: ${PAGERDUTY_ROUTING_KEY}
    ops:
      console: {}
 
workflows:
  route-by-risk:
    trigger:
      event:
        contract: Vault
        name: Deposit
        network: ethereum
    steps:
      - id: risk
        command: { run: "node ./score.js" }
      - id: route
        choose: 
          - if: "${{ steps.risk.output.level == 'high' }}"
            steps: 
              - notify: { channel: pager, message: "high risk ${{ trigger.tx_hash }}" } 
          - if: "${{ steps.risk.output.level == 'medium' }}"
            steps: 
              - send_transaction: 
                  network: ethereum
                  relayer: ops
                  contract: Vault
                  function: "flag(bytes32)"
                  args: ["${{ steps.risk.output.id }}"] 
                  approval: { via: [cli] } 
          - else: 
              steps: 
                - notify: { channel: ops, message: "low risk" } 

Rules

  • Branches evaluate in order; the first true if: wins. Only one branch runs.
  • At most one else, and it must be last (validation enforces both).
  • Nested branch steps use the same journal and recovery rules as top-level steps. A send_transaction inside a branch keeps its persisted external_id; HTTP, notifications and commands have the same external-effect idempotency requirements.
  • Nested steps are journaled under the stable id <chooseId>/<branchIdx>/<stepId> (visible in rflow runs show); they reference siblings and outer steps by their declared id (steps.<id>).

Crash-safety

The branch decision is journaled before any nested step runs, so a crash mid-branch resumes into the same branch, even if a mutable state.* / lists.* value the if: read has since changed. Already-settled nested steps are never re-run on resume.

Limits

To keep the branch a plain journaled sequence (no goto, no fan-out), a choose branch may not contain wait_for, foreach, or a nested choose (validation rejects them). Every other step kind is allowed.