1
Declare schemas
The checkout pipeline uses request, event, and response schemas. The
completeness: complete tells the verifier these declarations describe the full logical shape.event_id field on schema.OrderPlaced is what enables keyed message identity on the outbox. Without it the verifier cannot establish that duplicate deliveries carry the same logical message.2
Declare the data model and outbox
Persistent state lives in a data model. The checkout flow has one data model with an object and a typed outbox.The outbox is a typed logical message collection belonging to the same transactional state boundary as the data model’s objects. A transaction declaring this data model may atomically mutate objects and admit messages to the outbox in one commit.
An outbox is not a topic. The outbox belongs to a data model and is written transactionally. A topic is an L0 logical channel that is produced by effect execution outside any transaction. The two are intentionally separate semantic concepts.
3
Model op.place_order
The placement operation receives a request, inserts the order inside a deduplicated transaction, stages the outbox message atomically, launches an async payment charge, awaits the charge with Several things work together here:
join_all, then branches on the result.tx.place_orderisdeduplicated_bythe request’sidempotency_key. A second attempt under the same key resolves the prior commit instead of inserting a duplicate order and staging a second outbox message.- The
write_outboxstep stagesOrderPlacedinto the outbox atomically with the insert. Both commit together or abort together. execute_effect_asynclaunches the payment charge without blocking. Thejoin_allstep establishescomplete(charge) < continuationso the program waits for the charge before returning.- The external payment effect declares
deduplicated_byon the sameidempotency_key, so duplicate executions collapse. Bothokanderrdispositions areterminal, so the result is replay-stable.
4
Model op.dispatch_receipt
The downstream operation consumes the outbox and sends a receipt. It uses an The
OutboxInput, which is the honest way to consume a data model outbox: each committed message becomes one logical invocation.acknowledge_on_success: true declaration says that when an invocation reaches successful completion, the triggering outbox message is acknowledged for this consumer. This does not close an idempotency proof and does not prove duplicate collapse: under at_least_once delivery, failure or uncertainty before acknowledgement may still admit another attempt, so the consumer’s own idempotency requirement is still needed.5
Add the runtime layer
The optional Each runtime declaration serves a specific purpose:
runtime block declares how the L0 machine is realized. Here it declares execution pools, a router for the request, and an OutboxRuntime for the downstream consumer.execution_pools:member_concurrency: bounded(1)declares that each pool member executes at most one invocation at a time. This is the key fact that serialization and ordering proofs build on.routers: The router keys request invocations byidempotency_keyand assigns them to one member ofpool.checkoutvia consistent hash. Equal keys map to the same routing domain, and bounded(1) concurrency means same-key invocations cannot overlap.outboxes: The outbox runtime declares keyed partitioning ontenant_id, partition-level ordering, and dispatch onto a serial pool with order-preserving batching. This is the precedence source and execution topology that the ordering proof consumes.
A declared batching stage stops the serialization route because batch-internal overlap is intentionally unmodeled. For ordering,
batching.ordering: preserved lets the established precedence pass through.6
Run the verifier
With your model file complete, run:You will see a summary of obligations and verdicts. With the declarations above, the obligations should prove as follows:
op.place_orderidempotency: proven — the transaction’sdeduplicated_bysuppresses duplicate commits, the outbox write shares that commit, and the external charge deduplicates by the same key.op.place_orderrecoverability: proven — resumable by same-path continuation.op.dispatch_receiptidempotency: proven — the external email boundary deduplicates byevent_id, which is the outbox message identity field.op.dispatch_receiptordering: proven — the outbox runtime declares keyed partitioning ontenant_id, partition ordering, consistent-hash assignment to a serial pool, and order-preserving batching. This is an outbox-routed discharge.op.dispatch_receiptrecoverability: proven —guaranteedbecause the outbox runtime declaresat_least_oncedelivery.
disposition: terminal from the external payment effect’s err variant and rerun. The verifier will report op.place_order idempotency as unknown because the match_result on the charge result is no longer established to replay: a retry might observe a different result variant.7
Generate the visualization
Produce the proof overlay in one command:Open
checkout.html in a browser. The system view shows services, topics, and external systems with obligation status rings. Double-click op.place_order to see its program diagram: transaction steps expanded in place, the async launch, join_all, and the match_result arms. Click op.dispatch_receipt to inspect the outbox input and runtime topology that the ordering proof consumed.Understanding the proof structure
Idempotency via producer-suppressed outbox discharge
Theop.place_order idempotency proof is discharged through the producer-suppressed route: the transaction declares deduplicated_by on idempotency_key, which is replay-stable because the request input declares a keyed identity on that field. At most one logical commit per class is admitted, so at most one order insert and one outbox write occur. The outbox boundary does not terminate the causal graph: the verifier continues through outbox.order_events to op.dispatch_receipt and checks that the consumer also collapses duplicates, which it does because the email boundary deduplicates by event_id.
Ordering via outbox-routed discharge
Theop.dispatch_receipt ordering proof is discharged through the outbox-routed route: the outbox runtime declares keyed partitioning, which establishes that same-tenant_id messages share a partition. The ordering: partition declaration supplies the precedence source. Dispatch with member_assignment: consistent_hash gives each partition one active owning member, and member_concurrency: bounded(1) on the target pool means later invocations cannot overtake earlier ones. The batching.ordering: preserved declaration lets the precedence pass through the batching stage.
Closing the unknown verdict
The deliberate gap mentioned above (removingdisposition: terminal) produces an unknown verdict because the control decision no longer replays. To close it, restore the terminal disposition on both variants of the external effect’s result contract. This makes the match_result replay-stable: every attempt observes the same variant and proceeds down the same arm.
Proving Idempotency
Deep dive into the three-leg proof and greatest-fixpoint reasoning.
CI Integration
Automate verification in your CI pipeline and track obligations over time.