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_transactioninside a branch keeps its persistedexternal_id; HTTP, notifications and commands have the same external-effect idempotency requirements. - Nested steps are journaled under the stable id
<chooseId>/<branchIdx>/<stepId>(visible inrflow 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.