Browse documentation
CanLang handbook

Stored state machines

A stored scalar enum field may opt into a flat lifecycle machine. Its constant default is the initial state. Operations declare edges with transition record.field source -> target; each edge is owned by its declaring scenario or trusted handler. Ordinary set, CRUD patches, create inputs, and hook rewrites cannot assign the managed field.

On this page

Declare an operation-owned transition

Given
 Job { status:enum(queued,running,ready,failed)=queued machine }
When
 # Start processing a queued job.
 scenario start(job:Job) by=members
  do
   transition job.status queued -> running

This is a fragment requiring membership, model policy, and a usable page or caller. A transition validates the operation-owned edge and current provisional source. Mismatch is a business-rule failure; malformed edge data is validation failure. A later failed guard/effect rolls back the transition. Fixture and migration snapshots may use any declared state without implying an edge was taken.

V1 is a flat stored lifecycle, not a general statechart: no hierarchy, parallel/history states, callbacks, or implicit cascades. State name alone does not protect delayed completions after leaving and reentering; use an explicit version or occurrence guard. See scenario effects and transactions.

The declared edge

This fragment declares only the start edge below. The other enum cases name possible stored values; they do not imply completion, failure, or retry edges.

The Job start edgeA new Job starts queued. The declared start scenario can transition it from queued to running; no other lifecycle edges are declared in this fragment.startqueuedrunning