LIFECYCLE

Settles the “can it go back to pending?” argument

Every state a thing can reach and every legal move between them. Each arrow says who makes the move, what triggers it, what has to be true first, and what happens as a result. States you can never leave, or never reach, get flagged before anyone reads the diagram.

> what happens after a request is approved?
wrote request.lifecycle.json · request.lifecycle.html
 
# the spec is written by the skill, then rendered like any other
$ vibex validate docs/request.lifecycle.json
$ vibex render docs/request.lifecycle.json --open

A long journey and a short cycle

A parcel has twelve states, with failure paths and timeouts, drawn left to right. An invoice has six and is drawn top to bottom. Both are from the Relay showcase.

parcel.lifecycle.html · 12 states · 16 transitionsFull screen ↗

Click a state to see what is true while in it, who acts next, and the moves in and out.

What it draws

Every field in the spec, and where it shows up.

STATES

kind
initial gets a start dot. waiting is drawn with a dashed border, for a state parked until something outside happens. terminal is a double-bordered pill with no way out. failure is an error or rejected state, which may be recoverable.
initialnormalwaitingterminalfailure
id · label
Use the enum value from the code as the ID, so the diagram and the column agree on what a state is called.
actor
Who acts next while the thing sits in this state, printed in the box as next: courier.
description
One sentence on what is true in this state.

TRANSITIONS

actor · event
The first line on the arrow: merchant: cancel.
guard
The condition, on its own line in brackets: [no scan has been taken]. Write it without the brackets.
action
The side effect, after a slash: / void the label.
kind
auto and timeout are moves the system makes on its own, drawn dashed. failure is an error path, drawn red.
normalautotimeoutfailure
label
Replaces the first line when the event name alone does not read well.

SUBJECT AND LAYOUT

meta.subject
What carries the state: Order.status, Subscription.state. One subject per diagram.
layout.direction
The happy path runs left to right (lr) or top to bottom (tb). States are ranked by their shortest distance from the start. Failure and terminal states settle where they are first reached.
row · col
Override a state's rank when the automatic one is not the story you want to tell.

Where it reads from

No file format describes a state machine on its own, so the skill reads the code that enforces one:

STATES

Status enums, and the column that stores them.

TRANSITIONS

switch (status) blocks, state tables, and the service methods that set status =.

GUARDS

Checks such as assertTransition(from, to), and the conditions in front of each assignment.

TIMEOUTS

Scheduled jobs and crons that move records on their own, drawn as timeout.

Check this one before you trust it. Transitions spread across services are the easiest thing to miss. The validator will tell you about a state nobody can reach. It cannot tell you about a transition the agent never found.

The spec you keep

{
  "schema_version": 1,
  "diagram_type": "lifecycle",
  "meta": { "title": "Order", "subject": "Order.status" },
  "states": [
    { "id": "draft", "label": "Draft", "kind": "initial",
      "actor": "buyer" },
    { "id": "placed", "label": "Placed", "actor": "admin" },
    { "id": "approved", "label": "Approved", "kind": "terminal" },
    { "id": "expired", "label": "Expired", "kind": "terminal" }
  ],
  "transitions": [
    { "from": "draft", "to": "placed", "event": "submit",
      "actor": "buyer", "guard": "the basket is not empty" },
    { "from": "placed", "to": "approved", "event": "approve",
      "actor": "admin", "action": "issue invoice" },
    { "from": "placed", "to": "expired", "event": "7 days elapsed",
      "kind": "timeout" }
  ]
}

Aim for exactly one initial state, and make sure every state can reach a terminal one or is marked as a failure. The validator checks both.

“Can an approved order go back to placed?” The answer is whether the arrow is in the file. The file sits in the repo and gets reviewed like code.

The full contract is schemas/lifecycle.schema.json.

What validate flags

This is where a lifecycle diagram earns its keep. The graph checks are warnings, so a render never breaks, but they point straight at the states a design forgot about.

$ vibex validate docs/order.lifecycle.json
error dangling-ref transitions[3].to "cancelled" is not a state id
warning event transitions[2] (refunded -> placed) has no event; say what triggers it
warning dead-end state "shipped" has no way out; mark it terminal or add a transition
warning unreachable state "refunded" cannot be reached from the initial state
warning terminal-exit terminal state "refunded" has 1 outgoing transition(s); use kind "failure" or "normal" if it is recoverable
1 error(s), 4 warning(s)

Errors

Exit 1. Each one names the field.

  • dangling-refa transition to or from a state that does not exist
  • duplicate-idtwo states or transitions sharing an ID

Warnings

Printed, never blocking.

  • dead-enda state with no way out that is not marked terminal
  • unreachablea state nothing leads to from the start
  • terminal-exita “terminal” state that can be left after all
  • initialno initial state, or more than one
  • eventan arrow that does not say what triggers it
  • sizemore than 20 states. Split by subject or phase

The sentences it writes

A docs section can ask for lifecycle.states and lifecycle.transitions. Guards and side effects are quoted as you wrote them, behind a label, never bent into a clause. From the Relay build:

Created moves to Cancelled on cancel. Triggered by: merchant. Only when: no scan has been taken. Side effect: void the label.lifecycle.transitions · parcel.lifecycle
Created is the state a new Parcel starts in. 2 transitions lead out of it. Booked and labelled, still on the merchant's shelf. Acts next: merchant.lifecycle.states · parcel.lifecycle#created

What it connects to

DOCS

Anchor the guard itself

The diagram says a guard exists. An anchored claim pinned to the guard's code says what it enforces, and it goes stale when someone edits that code.

Docs →
ENDPOINTS

The calls behind each move

Most transitions are an endpoint or an event. Keep both in one dashboard, and the move and the route that makes it are one click apart in the sidebar.

Endpoints →
DASHBOARD

One file with the rest

Each lifecycle is its own entry, next to the tables that store the status.

Dashboard →