Lifecycle & Timeout
Every commit envelope moves through a strict four-phase lifecycle. The runtime drives transitions by emitting the events you declare in lifecycle:.
State diagram
start_action submitted
│
▼
┌─────────────┐
│ PENDING │
└──────┬──────┘
│ prepare_event emitted
▼
┌─────────────┐
┌─────►│ PREPARING │◄──── (waiting for all
│ └──────┬──────┘ participants to ACK)
│ │
│ all ACK │ any NACK or timeout
│ ▼ ▼
│ ┌────────────┐ ┌──────────┐
│ │ COMMITTING │ │ ABORTING │
│ └──────┬─────┘ └────┬─────┘
│ │ │
│ commit │ abort │
│ event │ event │
│ emitted │ emitted │
│ ▼ ▼
│ ┌────────────┐ ┌──────────┐
│ │ COMMITTED │ │ ABORTED │
│ └────────────┘ └──────────┘
│ (terminal) (terminal)
│
└─── (timeout fires during PREPARING → ABORTING)Lifecycle events
lifecycle:
prepare_event: TRANSFER_PREPARED
prepared_event: TRANSFER_PREPARE_CONFIRMED
commit_event: TRANSFER_COMMITTED
abort_event: TRANSFER_ABORTED| Field | Emitted by | Description |
|---|---|---|
prepare_event | start_action core rule | Signals all participants to stage their changes |
prepared_event | Runtime (when all participants ACK) | Triggers the commit phase |
commit_event | Runtime (after prepared_event) | Tells all participants to apply staged changes |
abort_event | Runtime (on NACK or timeout) | Tells all participants to roll back |
All four events must be declared in your events: section targeting the envelope_state entity type.
Timeout
If not all participants ACK within abort_after_seconds, the runtime emits the abort_event automatically.
timeout:
abort_after_seconds: 30
tick_event: CLOCK_TICK| Field | Required | Description |
|---|---|---|
abort_after_seconds | yes | Max seconds to wait for all participants to ACK |
tick_event | yes | A kind: system event that fires periodically (the runtime checks elapsed time on each tick) |
Defining the tick event
events:
CLOCK_TICK:
kind: system
state: scheduler
entity_expr: event.tick_id
payload:
tick_id: string
instant_time: integerThe tick event is usually emitted by an external scheduler (cron job, timer service) at a fixed interval such as every 5–10 seconds.
Set your tick interval shorter than abort_after_seconds. If the tick fires every 30 seconds and your timeout is 30 seconds, envelopes may run up to 2× the timeout before aborting.
Recommended: tick every 5–10 seconds, timeout 30–60 seconds.
What happens on abort
When abort_event fires:
- Each participant executes its
abort_cleanuprules (reverting any staged state changes) - The envelope entity’s status is set to
ABORTED - A projection update records the aborted transfer for audit
The abort path is deterministic and replay-safe. If the runtime crashes mid-abort, replaying the abort_event will re-run the cleanup rules idempotently.
Your abort_cleanup rules must be idempotent. If the abort_event is delivered twice (e.g., after a crash and replay), running cleanup twice should produce the same final state as running it once.
Use cap: { min: 0 } on balance subtractions and filter to de-duplicate list reversals.