Diagrams
LifecycleFigure
A state machine: what something can be, and what moves it between states.
Usage
When to reach for it
Ships from@misoto22/design/diagrams
import { LifecycleFigure } from '@misoto22/design/diagrams'Notes
A state machine: what something can BE, and what moves it.
The one figure here that spends colour, and it spends exactly the two tokens this system reserves for state: --success on a terminal success, --danger on a terminal failure. Nothing else in the package is allowed those two, and a lifecycle diagram is the case they were reserved for — the whole point of the picture is which end a run came out of.
The other six kinds are carried by shape, so a greyscale print loses the two outcomes' hue and keeps every other distinction: a start is a filled cap, a decision is a diamond, a wait is a dashed frame, an external is a plate with a cut corner.
A RECOVERABLE FAILURE IS A FAILURE WITH A WAY BACK. type: "failure" says how a state is drawn; a transition OUT of it is what says whether the run is over. A retryable error and a terminal one look identical until you follow the arrows, which is correct — the difference between them is a real edge in the machine, not an adjective on a box.
Anatomy
| Element | Description |
|---|---|
| Figure shellrequired | The frame around the picture. What a machine hands it is a transition list with each note folded in after an em dash, so the condition on an arrow — the timeout, the retry count — is in the text copy and not only on the line. |
| State platerequired | One shape per kind: a filled cap for a start, a diamond for a decision, a dashed frame for a wait, a cut corner for an external, and the two washes the system reserves for the terminals. The name is centred, step sits in the leading corner and tag along the bottom edge. |
| Main rail | The first lane’s states, sorted by col and joined consecutively at the emphasis weight — one line per neighbouring pair the transitions do not already declare, so a spine written out in full is drawn from the transitions and a spine left implied is drawn anyway. Without it the top row reads as five unconnected boxes, because the spine of a machine is the part nobody writes down. |
| Transition | A routed line with its label on a mask and its note in a quieter line beneath. A declared transition between two rail neighbours replaces the implicit one rather than doubling it. |
| Lane | A rule with a mono caption above it, drawn only as far as that lane’s own states reach. A lane past the first has its columns shifted along the main rail, which is what keeps a drop out of the spine vertical rather than a dogleg. |
Best practices
Do
- Keep the first lane for the ordered spine and nothing else. Consecutive states there are joined by the implicit rail even when no transition declares the pair, so a state parked in lane 0 for spacing invents an edge the machine does not have. A state whose lane id nothing declares is kept out of that rail and reported as DIAGRAM_LANE_UNKNOWN instead, because a typo should not be able to add an arrow.
- Count a secondary lane’s columns from the rail. Every col in a lane past the first is shifted two columns along, so col: 0 in the second lane sits under col: 2 in the first — the convention archify’s lifecycle contract already implies, restated here because it is what makes a drop land on the state it came from.
- Say whether a failure is recoverable with an edge, not with the type. type: "failure" only paints the plate; a retryable error and a terminal one are identical until a reader follows the arrows out, and the one with none is where the run ended.
Don’t
- yOffset moves the plate and moves nothing out of its way. The lane’s rule is drawn from what that lane holds, so a negative nudge drags the rule up with the state and a large positive one drops the plate into the band below — a state needing that much room wants a column of its own rather than a push.
- The figure claims a state can be left only the ways its arrows say. A cancel that can happen from anywhere and is drawn from nowhere reads as impossible, which is a reading the picture makes and the specification never wrote — draw it, or say in the subtitle that it is not drawn.
Examples
default
The rail across the top is not in the spec. A lifecycle diagram gives its first lane to the ordered phases, and the arrows between consecutive columns there are drawn for you at the emphasis weight, so an author declares only the edges that leave the spine — here, the three that pass through Blocked. This is also the one figure allowed colour, and it spends exactly two tokens: one on the completed state, one on the failed one. Every other kind is carried by shape, so a greyscale print loses the two outcomes and keeps every other distinction.
A job run
6 elements and 6 relationships. The only figure that spends colour, on the two outcomes.
- Lane: Phases
- Queued (start) — accepted — step 01
- Running (active) — tool calls — step 02
- Review (decision) — quality gate — step 03
- Completed (success) — final answer — step 04
- Lane: Interruptions
- Blocked (waiting) — missing input
- Lane: Terminal
- Failed (failure) — timed out
- Queued → Running
- Running → Review
- Review → Completed
- Review → Blocked: needs input
- Blocked → Failed: expired — after 24h
- Blocked → Running: answered
a decision with both ends
A decision with both of its ends drawn. Approved sits on the rail, so its arrow is implied; Declined sits in a second lane, which is the only way to fan out of a decision here — a fourth state added to the first lane would be joined to its neighbour by the implied rail whether that transition exists or not. The two terminal states are the only marks in the package allowed colour, and this is the figure the tokens were reserved for: the question a reader brings to a state machine is which end a run came out of.
Card authorisation
4 elements and 3 relationships. One decision, and the two ends a run can come out of.
- Lane: Authorisation
- Submitted (start) — card token — step 01
- Risk score (decision) — model and rules — step 02
- Approved (success) — funds held — step 03
- Lane: Refusals
- Declined (failure) — no retry
- Submitted → Risk score
- Risk score → Approved
- Risk score → Declined: score above 90
- A run leaves through Approved or through Declined and nowhere else.
- Submitted to Risk score to Approved is drawn without being declared.
a failure with a way back
Two states of type failure, drawn identically, and only one of them ends the run. Nothing on a plate says whether a failure is terminal — the difference is a real edge in the machine, so Timed out has an arrow back to Sending and Rejected has none, and a reader tells them apart by following arrows rather than by reading an adjective. The retry edge carries a note under its wording, which is where a count or a condition goes when folding it into the label would make the label too long to sit in the gap it has.
A delivery attempt
5 elements and 5 relationships. Two failures on the same plate; one of them is not the end.
- Lane: Delivery
- Queued (start) — accepted — step 01
- Sending (active) — attempt n — step 02
- Delivered (success) — 2xx — step 03
- Lane: Faults
- Timed out (failure) — no response
- Rejected (failure) — no retry
- Queued → Sending
- Sending → Delivered
- Sending → Timed out: no reply in 10s
- Timed out → Sending: retry — up to 5 times
- Sending → Rejected: 400 invalid body
- Timed out and Rejected are drawn identically.
- Only the arrow leaving Timed out says the run continues.
the implicit rail
Three facts about placement that only bite once. Declaring one of the spine's own pairs is not a duplicate — it replaces the implied edge and takes your label — so the way to name a step is to write that transition, not to hope the rail carries it. A secondary lane's column N sits under the main lane's column N + 2, which is why Withdrawn at column 0 lands beneath Accept rather than beneath In review. And yOffset is accepted on a state and deliberately ignored: the nudge is measured in archify's lane depth, not this one, so honouring it would drop a plate onto the lane below.
An article, from draft to live
6 elements and 6 relationships. The spine is drawn; only the departures are written.
- Lane: Review
- Draft (start) — author only — step 01
- In review (active) — two readers — step 02
- Accept? (decision) — editor — step 03
- Published (success) — on the site — step 04
- Lane: Off the rail
- Withdrawn (neutral) — by the author
- Held (waiting) — awaiting legal
- Draft → In review
- In review → Accept?
- Accept? → Published
- In review → Withdrawn: author pulled it
- Accept? → Held: legal question — blocks publication
- Held → Accept?: cleared
Accessibility
- success and failure are the only coloured marks in the package’s diagrams, and each is also a distinct plate shape — colour is never the only carrier.
- The main rail is drawn between consecutive states in the first lane even when the specification does not list those transitions, because they are the diagram’s spine rather than its exceptions.