Move a record through states
A lifecycle gives a table a state column and the moves between its states. Use it for an
editorial workflow, an approval, or any record that is not only created and edited.
Declare the states
Section titled “Declare the states”table posts "Posts" { display: title
title text required "Title" body markdown required "Content"
lifecycle status "Status" { state draft default "Draft" state review "In review" state published public "Published" state archived gone "Archived"
transition submit draft -> review "Send for review" { access { author where author = :user.person_id editor } }
transition publish review -> published "Publish" { access: editor set published_at = now after -> logic/post-published.ts }
transition archive published -> archived "Archive" { access: editor } }}The state column is engine-owned. It never appears in a form, and a request can never set it. A state changes only through a transition.
default names the state a new row starts in. public marks a state whose rows a public page
can read, and status.public is then usable in a condition.
Each move has its own rule
Section titled “Each move has its own rule”A transition carries its own access:. The author can send a post for review; only an editor
can publish it.
A button is absent when the reader cannot use it, and also when the record is not in a state the transition applies from. There is no disabled button that explains itself when you press it.
A transition is an action of the page rather than of a region. Which row the page is about already has one answer, so the transition does not restate it.
A stale button cannot move a record twice
Section titled “A stale button cannot move a record twice”The source state and the reader’s row filter are both conditions of the update. If somebody left a page open and the record moved on in the meantime, their button does nothing and says so.
Act when a record moves
Section titled “Act when a record moves”after -> runs a script after the move is committed. It sees the record after the move and
receives the previous row as well, so it can say what changed.
transition publish review -> published "Publish" { access: editor after -> logic/post-published.ts}A transition’s set values, the table’s own checks over the moved row, and a before hook all
run in one transaction with the move. The after hook runs after the commit, so it cannot roll
the move back and cannot make a successful move look failed.
A table’s insert and update hooks do not run on a transition. A move is not an edit.
Status: the gone modifier is accepted and is not yet enforced. An archived record is
hidden today because the app’s own pages filter on status.public. The engine does not yet
return 410 for one or exclude it from listings by itself.