Examples

Crucible’s flagship example is a food-delivery order saga, examples/fooddelivery:
a generic ordering lifecycle invented from scratch, coupled to no real product.
It is a single statechart that exercises the whole engine the way a real service
would: hierarchy, parallel regions, guards authored as data, actors, invoked
services with compensation, and timer-driven escalation.
The diagram below is generated at build time from the real fooddelivery.NewModel()
machine (the same machine the example’s tests exercise), so it can never drift
from the code:
stateDiagram-v2
[*] --> Placed
state Active {
[*] --> Active_Fulfillment__Cooking
Active_Fulfillment__Cooking
Active_Fulfillment__AwaitingCourier
Active_Fulfillment__EnRoute
Active_Fulfillment__AwaitingCourier --> Active_Fulfillment__EnRoute: PickedUp
Active_Fulfillment__Cooking --> Active_Fulfillment__AwaitingCourier: PlatedUp
--
[*] --> Active_Watchdog__OnTime
Active_Watchdog__OnTime
Active_Watchdog__Overdue
Active_Watchdog__Overdue --> [*]
Active_Watchdog__OnTime --> Active_Watchdog__Overdue: after(30m0s) SLABreached
}
Delivered --> [*]
Canceled --> [*]
Rejected --> [*]
Active --> Refunding: Cancel
Active --> Settling: DroppedOff
Authorizing --> Active: Authorized [or(generousOrder,and(subtotal ge 5000,priority in ["fast","express"]))]
Authorizing --> Rejected: Declined
Placed --> Authorizing: Submit
Refunding --> Canceled: Refunded
Settling --> Delivered
The happy path
Section titled “The happy path”An order rests in Placed. A Submit signal moves it to Authorizing,
which invokes a payment service to hold funds.
The authorization’s outcome routes through two edges: Authorized advances the
order, Declined sends it to the Rejected terminal.
The Authorized edge is no rubber stamp. Its guard
is an expression mixing tiers: a Core compare and membership test (subtotal >= threshold and a fast-lane priority) OR a Rich CEL guard (subtotal + tip >= 6000), so a generous order or a flagged big basket is admitted.
Active: two lanes at once
Section titled “Active: two lanes at once”Admitted orders enter Active, an orthogonal superstate running two parallel regions at once:
- Fulfillment is the work spine:
Cookingsupervises a kitchen actor, whose plated output advances toAwaitingCourier, thenEnRoutesupervises a courier actor. - Watchdog is an SLA clock. An
after(30m)delayed transition firesSLABreachedif the delivery window elapses, recording the breach in the order’s context without blocking delivery.
Each region’s actor messages the order on completion; an assign reducer, the order’s only context writer, folds the result in.
Exits: deliver, settle, compensate
Section titled “Exits: deliver, settle, compensate”The courier’s DroppedOff is a cross-cutting transition on the Active
compound: it exits the whole parallel configuration to Settling, which
captures the held payment and runs to the Delivered terminal.
Cancellation is a saga. Cancel on Active exits to Refunding, which
invokes a refund service to reverse the authorization hold, the compensating
action, before reaching the Canceled terminal.
m, err := fooddelivery.NewModel()Notably, NewModel forges the machine, then round-trips it through its IR
(ToJSON → LoadFromJSON → Provide) so
the CEL guard binds exactly as a host loading a serialized definition would:
the in-repo proof of the serialization split.
It can also snapshot and restore
across a process restart mid-order.
The same machine, under the full runtime
Section titled “The same machine, under the full runtime”This page shows the model. The companion examples/dispatch showcase runs the
identical machine under the full runtime (durable, distributed,
polyglot, and observed) so the same statechart that renders this diagram
also survives restarts, fans out across processes, binds non-Go behavior, and
emits telemetry. See integrating for the
runtime surface.