Order lifecycle as a typed state machine
Demonstrates: a sum-typed `state` machine (states ARE variants, so a typo in transition() is a compile error), guarded transitions, and assert_fails on illegal moves. `soma verify` proves the machine always reaches a terminal (delivered or cancelled).
A Soma program from the state_machines part of the verified corpus: 58 lines, cells OrderStatus, OrderFlow, OrderFlowTests. It passes soma check with soma 3.2.4.
How it is built
The cell OrderFlow declares 7 signals in its face: open(id: String) -> String, place(id: String) -> String, pay(id: String) -> String, ship(id: String) -> String, deliver(id: String) -> String, cancel(id: String) -> String and locate(id: String) -> String. Its state machine order starts in Draft and allows 5 transitions: Draft -> Placed, Placed -> Paid, Paid -> Shipped, Shipped -> Delivered and * -> Cancelled. Cancelled and Delivered are terminal. Handlers: open (line 32), place (line 33), pay (line 34), ship (line 35), deliver (line 36), cancel (line 37) and locate (line 38).
The file also holds 1 test cell (OrderFlowTests) with 8 assertions, run by soma test.
The source
order_lifecycle.cell
// Order lifecycle as a typed state machine.
// Demonstrates: a sum-typed `state` machine (states ARE variants, so a
// typo in transition() is a compile error), guarded transitions, and
// assert_fails on illegal moves. `soma verify` proves the machine always
// reaches a terminal (delivered or cancelled).
cell type OrderStatus {
variants { Draft Placed Paid Shipped Delivered Cancelled }
}
cell OrderFlow {
face {
signal open(id: String) -> String
signal place(id: String) -> String
signal pay(id: String) -> String
signal ship(id: String) -> String
signal deliver(id: String) -> String
signal cancel(id: String) -> String
signal locate(id: String) -> String
promise "every order eventually reaches Delivered or Cancelled"
}
state order: OrderStatus {
initial: Draft
Draft -> Placed
Placed -> Paid
Paid -> Shipped
Shipped -> Delivered
* -> Cancelled
}
on open(id: String) { return get_status(id) }
on place(id: String) { transition(id, Placed) return get_status(id) }
on pay(id: String) { transition(id, Paid) return get_status(id) }
on ship(id: String) { transition(id, Shipped) return get_status(id) }
on deliver(id: String) { transition(id, Delivered) return get_status(id) }
on cancel(id: String) { transition(id, Cancelled) return get_status(id) }
on locate(id: String) { return get_status(id) }
}
cell test OrderFlowTests {
rules {
// happy path: Draft -> Placed -> Paid -> Shipped -> Delivered
assert open("a") == "Draft"
assert place("a") == "Placed"
assert pay("a") == "Paid"
assert ship("a") == "Shipped"
assert deliver("a") == "Delivered"
// a fresh order can be cancelled from any state (the * arrow)
assert cancel("b") == "Cancelled"
// illegal skips are rejected by the machine
assert_fails ship("c") // c is still Draft, cannot jump to Shipped
assert_fails deliver("d") // d is still Draft, cannot jump to Delivered
}
}What the toolchain says
$ soma check order_lifecycle.cell
note: promise on 'OrderFlow' is documentation (not machine-verifiable): "every order eventually reaches Delivered or Cancelled"
--> order_lifecycle.cell:20:17
|
20 | promise "every order eventually reaches Delivered or Cancelled"
| ^
✓ All checks passed (1 note above).$ soma test order_lifecycle.cell
test OrderFlowTests ...
✓ assert open("a") == "Draft"
✓ assert place("a") == "Placed"
✓ assert pay("a") == "Paid"
✓ assert ship("a") == "Shipped"
✓ assert deliver("a") == "Delivered"
✓ assert cancel("b") == "Cancelled"
✓ assert_fails ship("c") — raised invalid transition: Draft → Shipped. Current state: 'Draft'. Valid targets: [Placed, Cancelled]
✓ assert_fails deliver("d") — raised invalid transition: Draft → Delivered. Current state: 'Draft'. Valid targets: [Placed, Cancelled]
8 tests: 8 passed, 0 failed$ soma verify order_lifecycle.cell
Verifying order_lifecycle.cell...
State machine 'order': 6 states, initial 'Draft'
States: [Cancelled, Delivered, Draft, Paid, Placed, Shipped]
✓ 6 states, 9 transitions
✓ all states reachable from 'Draft'
✓ terminal states: [Cancelled]
✓ no deadlocks
✓ liveness: every state can eventually reach a terminal state
✓ wildcard transitions: * -> [Cancelled]
⚠ `* -> Cancelled` also adds Delivered -> Cancelled — so Delivered is not final. If it should stay final, write `* -> Cancelled except [Delivered]`
✓ think-isolated: CTL safety properties hold regardless of LLM output (5 handlers, 5 literal transitions, 0 dynamic)
✓ termination: all 7 handlers structurally terminate
✓ refinement: handler `place` ⟶ {Placed}
✓ refinement: handler `pay` ⟶ {Paid}
✓ refinement: handler `ship` ⟶ {Shipped}
✓ refinement: handler `deliver` ⟶ {Delivered}
✓ refinement: handler `cancel` ⟶ {Cancelled}
· transition sources: `place`, `pay`, `ship`, `deliver`, `cancel` call transition(id, target) without declaring the source — the runtime refuses a move the graph forbids, but the proof does not see where the handler starts; `transition(id, from, to)` names the edge (checked by `soma check`, refused at run time from any other state)
13 passed, 1 warnings, 0 failures
Temporal properties for 'order':
✓ deadlock_free — no deadlocks in any reachable state
Temporal: 1 passed, 0 failed
VERIFY OK