# State diagrams

> Page: https://docs.plotwork.dev/state-diagrams. Index of every page: https://docs.plotwork.dev/llms.txt

A state diagram shows the states of something and the events that move it from one to another: an order, a ticket, an agent run, a connection, a download, a release. Use it for a lifecycle or a state machine. It is for anyone, human or AI agent, who needs the answer to "what can happen next from here, and why".

## TL;DR

| Question | Answer |
|---|---|
| What is it for? | A lifecycle or a state machine: states, the transitions between them, and the events, conditions and actions on each transition. |
| How does it start? | `state "Title"`, then `state id "Label"` for a state and `a -> b : "event [guard] / action"` for a transition. The words `start` and `end` in a transition draw the start and end markers. |
| How do I make it readable? | One line per state: `state paid "Paid" sub="Money received" tone=green icon=lucide:credit-card`. Green is for success, amber for waiting, red for failure, and blue or violet for work in progress. See [Make it readable](#make-it-readable). |
| What else can it hold? | Composite states with states inside, regions that run side by side, a choice, a fork and a join, a history marker, and notes. |
| Can I convert Mermaid? | Yes. `stateDiagram` and `stateDiagram-v2` become a state diagram. They used to become a flow diagram. |
| How big may it be? | 300 nodes, 600 transitions and 6 levels of nesting. Past a limit the diagram is refused with error `E140`, before anything is drawn. |
| How is it drawn? | A rounded card for each state with an icon and a tint, a frame with a corner tab for a composite state, a dot and a ring for start and end, and a pill with a branch icon for a choice. Layout is automatic, and the theme chooses every color. |

## Ask your agent

Say what the states are and what moves the thing from one to the next, then ask for an icon on every state and a color for what each state means. The agent writes the rest.

```text
Use Plotwork to draw the lifecycle of an online order as a state diagram. An order starts as Pending, waiting for payment. A valid card payment moves it to a stock check, and nothing within 24 hours cancels it. If the item is in stock, it is picked and packed in a Fulfilment state and then delivered. If it is sold out, the order is cancelled and the money refunded. Give every state an icon, color the states by what they mean, and add a note that refunds stay open for 30 days.
```

```text
Use Plotwork to draw the run of an AI agent as a state diagram. The agent plans a step, then runs it with a tool, or waits for a person to approve it when the step is risky. A tool error is retried after a backoff while retries are left, and the run fails when they run out or when a person denies a step. The run is Done when the goal is met. Put an icon on every state, and use green for success, amber for waiting, and red for failure.
```

```text
Use Plotwork to draw the lifecycle of a subscription as a state diagram. A customer starts a 14 day trial, and is charged when it ends if a card is on file, or cancelled if not. An active subscription renews each period. A failed payment makes it Past due and is retried on days 3, 5, and 7, and after 21 days the subscription is cancelled and access is revoked. Give each state an icon and a tone that shows how healthy it is.
```

## Example

This is the order lifecycle from the first prompt. It shows a composite state with an icon of its own (Fulfilment, with its own start and end), a choice, transition labels that carry an event, a guard and an action, and a note.

**Diagram source**

```diagram
state "Order lifecycle" desc="An order is paid, checked against stock, packed and delivered, or cancelled."

state pending "Pending" sub="Waiting for payment" tone=amber icon=status:pending
choice stock "In stock?"
state fulfilment "Fulfilment" icon=lucide:package {
  start -> picking
  state picking "Picking" tone=blue icon=lucide:clipboard-list
  state packed "Packed" tone=blue icon=lucide:package-check
  picking -> packed : "items scanned"
  packed -> end
}
state delivered "Delivered" tone=green icon=lucide:map-pin
state cancelled "Cancelled" sub="Money refunded" tone=red icon=status:error

start -> pending
pending -> stock : "pay [card valid] / charge card"
pending -> cancelled : "timeout [after 24 hours]"
stock -> fulfilment : "[in stock] / reserve items"
stock -> cancelled : "[sold out] / refund"
fulfilment -> delivered : "arrived"
delivered -> end
cancelled -> end

note cancelled : "Refunds stay open for 30 days"
```

*Picture: Order lifecycle: 6 states (1 composite), 11 transitions. An order is paid, checked against stock, packed and delivered, or cancelled.*

**Text description**

**States:**

- Start
- Pending (Waiting for payment), state
- In stock?, choice
- Fulfilment, composite state
  - Start of Fulfilment
  - Picking, state
  - Packed, state
  - End of Fulfilment
- Delivered, state
- Cancelled (Money refunded), state
- End

**Transitions:**

- Start of Fulfilment to Picking
- Picking to Packed on items scanned
- Packed to End of Fulfilment
- Start to Pending
- Pending to In stock? on pay, if card valid, then charge card
- Pending to Cancelled on timeout, if after 24 hours
- In stock? to Fulfilment, if in stock, then reserve items
- In stock? to Cancelled, if sold out, then refund
- Fulfilment to Delivered on arrived
- Delivered to End
- Cancelled to End

**Notes:**

- On Cancelled: Refunds stay open for 30 days

### Make it readable

A state diagram reads fastest when the picture says how things stand before anyone reads a word. Four habits do that, and an agent can write all of them in one line per state, as the order lifecycle above does.

| Habit | How | Example |
|---|---|---|
| Color shows how it is going | `tone=green` for success, `tone=amber` for waiting or needing attention, `tone=red` for failure, `tone=blue` or `tone=violet` for work in progress, and no tone for a state at rest. | `state failed "Failed" tone=red` |
| One icon on every state | `icon=` names what the state is, so a card is clear at a glance. A `status:` icon has a tone of its own, such as `status:success` in green and `status:error` in red, and a `simple:` icon is the mark of a tool. | `icon=lucide:credit-card`, `icon=status:error`, `icon=simple:docker` |
| One fact in `sub=` | The one detail that decides what happens next, such as a limit, a duration, or who acts. | `sub="Waiting for payment"` |
| The guard and the action in the label | The event says what happened, the guard in brackets says when the transition may run, and the action after a slash says what the system does. | `"pay [card valid] / charge card"` |

Keep a state name to a few words and move detail into `sub=` or a note: text wider than a card is cut with warning `W203`. Do not rely on color alone. The name and the icon say the same thing, so the diagram still reads for someone who cannot tell two tones apart. An icon name must exist, so ask `search_icons` or `map_icons` for it, and never invent one.

### An AI agent run

An agent run shows tones that say how each step is going, a loop back to planning, and guards that choose between two transitions out of one state.

**Diagram source**

```diagram
state "Agent run" desc="An agent plans, runs tools, waits for approval on risky steps, retries on errors, and finishes or gives up."

state planning "Planning" sub="Picks the next step" tone=violet icon=lucide:brain
state approval "Waiting for approval" sub="A person reviews the step" tone=amber icon=lucide:user-check
state calling "Calling a tool" sub="Runs the step" tone=blue icon=lucide:square-terminal
state retrying "Retrying" sub="Backs off, then runs again" tone=amber icon=status:retry
state done "Done" sub="Goal met" tone=green icon=status:success
state failed "Failed" sub="Denied or out of retries" tone=red icon=status:error

start -> planning : "task received"
planning -> approval : "step chosen [risky]"
planning -> calling : "step chosen [safe]"
approval -> calling : "approved"
approval -> failed : "denied"
calling -> planning : "result [goal not met]"
calling -> done : "result [goal met]"
calling -> retrying : "error [retries left] / wait"
retrying -> calling : "backoff over"
calling -> failed : "error [no retries left]"
done -> end
failed -> end

note approval : "Times out after 10 minutes" side=top
```

*Picture: Agent run: 6 states, 12 transitions. An agent plans, runs tools, waits for approval on risky steps, retries on errors, and finishes or gives up.*

**Text description**

**States:**

- Start
- Planning (Picks the next step), state
- Waiting for approval (A person reviews the step), state
- Calling a tool (Runs the step), state
- Retrying (Backs off, then runs again), state
- Done (Goal met), state
- Failed (Denied or out of retries), state
- End

**Transitions:**

- Start to Planning on task received
- Planning to Waiting for approval on step chosen, if risky
- Planning to Calling a tool on step chosen, if safe
- Waiting for approval to Calling a tool on approved
- Waiting for approval to Failed on denied
- Calling a tool to Planning on result, if goal not met
- Calling a tool to Done on result, if goal met
- Calling a tool to Retrying on error, if retries left, then wait
- Retrying to Calling a tool on backoff over
- Calling a tool to Failed on error, if no retries left
- Done to End
- Failed to End

**Notes:**

- On Waiting for approval: Times out after 10 minutes

### A subscription lifecycle

A subscription shows a transition from a state to itself (the renewal), several ways into one state (Cancelled), and labels that carry an event, a guard and an action.

**Diagram source**

```diagram
state "Subscription lifecycle" desc="A customer starts a trial, pays every period, may fall behind on payment, and cancels or lapses."

state trial "Trial" sub="14 days, then the first charge" tone=blue icon=lucide:timer
state active "Active" sub="Paid and in good standing" tone=green icon=lucide:badge-check
state pastdue "Past due" sub="Payment failed, retrying" tone=amber icon=lucide:clock-alert
state cancelled "Cancelled" sub="Access ends with the period" icon=lucide:ban

start -> trial : "sign up"
trial -> active : "trial ends [card on file] / charge"
trial -> cancelled : "trial ends [no card]"
active -> active : "period ends / renew"
active -> pastdue : "payment failed / email customer"
pastdue -> active : "payment succeeds"
pastdue -> cancelled : "[after 21 days] / revoke access"
active -> cancelled : "customer cancels"
cancelled -> end

note pastdue : "Retries on days 3, 5, and 7" side=bottom
```

*Picture: Subscription lifecycle: 4 states, 9 transitions. A customer starts a trial, pays every period, may fall behind on payment, and cancels or lapses.*

**Text description**

**States:**

- Start
- Trial (14 days, then the first charge), state
- Active (Paid and in good standing), state
- Past due (Payment failed, retrying), state
- Cancelled (Access ends with the period), state
- End

**Transitions:**

- Start to Trial on sign up
- Trial to Active on trial ends, if card on file, then charge
- Trial to Cancelled on trial ends, if no card
- Active to Active on period ends, then renew
- Active to Past due on payment failed, then email customer
- Past due to Active on payment succeeds
- Past due to Cancelled, if after 21 days, then revoke access
- Active to Cancelled on customer cancels
- Cancelled to End

**Notes:**

- On Past due: Retries on days 3, 5, and 7

### A release pipeline

A release pipeline shows a fork and a join around checks that run in parallel, a choice on the result, and the brand icons of the tools from the `simple` set.

**Diagram source**

```diagram
state "Release pipeline" desc="A push runs three checks in parallel, joins them, and ships only when all are green."

fork checks
join gate
choice green "All green?"
state push "Push" sub="To the main branch" icon=simple:github
state lint "Lint" tone=blue icon=simple:eslint
state types "Type check" tone=blue icon=simple:typescript
state test "Unit tests" tone=blue icon=simple:vitest
state image "Build image" sub="Tagged with the commit" tone=violet icon=simple:docker
state publish "Publish" sub="To the npm registry" tone=green icon=simple:npm
state fix "Fix" sub="Author is notified" tone=red icon=simple:git

start -> push
push -> checks : "workflow starts"
checks -> lint
checks -> types
checks -> test
lint -> gate
types -> gate
test -> gate
gate -> green
green -> image : "[all pass]"
green -> fix : "[any fails] / notify author"
fix -> push : "new commit"
image -> publish : "image built"
publish -> end
```

*Picture: Release pipeline: 7 states, 14 transitions. A push runs three checks in parallel, joins them, and ships only when all are green.*

**Text description**

**States:**

- Start
- Fork checks, fork into parallel paths
- Join gate, join of parallel paths
- All green?, choice
- Push (To the main branch), state
- Lint, state
- Type check, state
- Unit tests, state
- Build image (Tagged with the commit), state
- Publish (To the npm registry), state
- Fix (Author is notified), state
- End

**Transitions:**

- Start to Push
- Push to Fork checks on workflow starts
- Fork checks to Lint
- Fork checks to Type check
- Fork checks to Unit tests
- Lint to Join gate
- Type check to Join gate
- Unit tests to Join gate
- Join gate to All green?
- All green? to Build image, if all pass
- All green? to Fix, if any fails, then notify author
- Fix to Push on new commit
- Build image to Publish on image built
- Publish to End

### A connection that remembers its state

A live connection shows a composite state with a history marker, so a reconnect returns to Idle or Streaming, whichever was active when the network dropped.

**Diagram source**

```diagram
state "Live connection" desc="A connection opens, drops, and comes back to the state it was in."

state connecting "Connecting" sub="Opens the socket" tone=blue icon=lucide:plug
state online "Online" tone=green icon=lucide:wifi {
  history last
  start -> idle
  state idle "Idle" sub="Heartbeats only" icon=lucide:radio
  state streaming "Streaming" sub="Receiving events" tone=aqua icon=lucide:zap
  idle -> streaming : "subscribe"
  streaming -> idle : "unsubscribe"
}
state reconnecting "Reconnecting" sub="Backs off up to 30 s" tone=amber icon=status:retry
state closed "Closed" icon=lucide:unplug

start -> connecting
connecting -> online : "socket open"
online -> reconnecting : "network lost"
reconnecting -> last : "reconnected / resubscribe"
reconnecting -> closed : "[after 5 tries]"
online -> closed : "close / send FIN"
closed -> end
```

*Picture: Live connection: 6 states (1 composite), 10 transitions. A connection opens, drops, and comes back to the state it was in.*

**Text description**

**States:**

- Start
- Connecting (Opens the socket), state
- Online, composite state
  - Start of Online
  - History of Online
  - Idle (Heartbeats only), state
  - Streaming (Receiving events), state
- Reconnecting (Backs off up to 30 s), state
- Closed, state
- End

**Transitions:**

- Start of Online to Idle
- Idle to Streaming on subscribe
- Streaming to Idle on unsubscribe
- Start to Connecting
- Connecting to Online on socket open
- Online to Reconnecting on network lost
- Reconnecting to History of Online on reconnected, then resubscribe
- Reconnecting to Closed, if after 5 tries
- Online to Closed on close, then send FIN
- Closed to End

### A download with two regions

A download shows regions, two parts of one state that run side by side, and a transition from a state to itself.

**Diagram source**

```diagram
state "Download manager" direction=down desc="A download runs its transfer and its progress display side by side, each as a region of one state."

state active "Downloading" icon=lucide:cloud-download {
  region "Transfer" {
    start -> connecting
    state connecting "Connecting" icon=lucide:plug
    state transferring "Transferring" sub="Reads blocks of 64 KB" tone=blue icon=lucide:download
    state verifying "Verifying" sub="Checks the SHA-256" tone=violet icon=lucide:shield-check
    connecting -> transferring : "connected"
    transferring -> transferring : "block / write to disk"
    transferring -> verifying : "last block"
    verifying -> end : "checksum matches"
  }
  region "Display" {
    start -> hidden
    state hidden "Hidden" icon=lucide:eye-off
    state shown "Progress shown" tone=blue icon=lucide:eye
    hidden -> shown : "window opened"
    shown -> hidden : "window closed"
  }
}
state failed "Failed" sub="Network lost" tone=red icon=status:error
state finished "Finished" tone=green icon=status:success

start -> active
active -> finished
active -> failed : "network lost"
failed -> active : "retry"
finished -> end
```

*Picture: Download manager: 8 states (1 composite), 13 transitions. A download runs its transfer and its progress display side by side, each as a region of one state.*

**Text description**

**States:**

- Start
- Downloading, composite state
  - Region Transfer
    - Start of Downloading, region Transfer
    - Connecting, state
    - Transferring (Reads blocks of 64 KB), state
    - Verifying (Checks the SHA-256), state
    - End of Downloading, region Transfer
  - Region Display
    - Start of Downloading, region Display
    - Hidden, state
    - Progress shown, state
- Failed (Network lost), state
- Finished, state
- End

**Transitions:**

- Start of Downloading, region Transfer to Connecting
- Connecting to Transferring on connected
- Transferring to Transferring on block, then write to disk
- Transferring to Verifying on last block
- Verifying to End of Downloading, region Transfer on checksum matches
- Start of Downloading, region Display to Hidden
- Hidden to Progress shown on window opened
- Progress shown to Hidden on window closed
- Start to Downloading
- Downloading to Finished
- Downloading to Failed on network lost
- Failed to Downloading on retry
- Finished to End

## The language

A line is one statement. A `#` starts a comment. Ids are words such as `pending`, `in-stock` or `_x`, labels are in double quotes, and options are written `key=value`. The first line is the header.

### The header

```plotwork
state "Order lifecycle" direction=right desc="How an order moves from payment to delivery."
```

| Part | Meaning |
|---|---|
| `state` | The kind. |
| `"Title"` | The title. It is the accessible name of the picture and heads the text description. Optional. |
| `direction=` | `auto` (the default), `right`, `down`, `left`, or `up`. `auto` draws the diagram left to right and, when that is wider than a Markdown column, also top to bottom, and keeps the narrower one. |
| `desc=` | A plain-language description for the text description. Optional. A `desc "text"` statement does the same. |

### Statements

| Statement | Meaning | Example |
|---|---|---|
| `state <id> "Label" sub="Detail" tone=<tone> icon=<set:name>` | A state, drawn as a rounded card. `sub=` is the description under the name, and a `\n` in it starts a new line. A tone other than `neutral` tints the card. `icon=` puts an icon before the name. | `state paid "Paid" sub="Money received" tone=green icon=lucide:credit-card` |
| `state <id> "Label" icon=<set:name> {`, the states inside, then `}` | A composite state: a state that holds other states, drawn as a frame with its name, and its icon if it has one, in a corner tab. `sub=` goes in the tab as one line. The `}` is on a line of its own. | `state shipping "Shipping" icon=lucide:package {` |
| `<from> -> <to> : "event [guard] / action"` | A transition. Every part of the label is optional. Only `->` draws a transition. | `pending -> paid : "payment received [card valid] / charge card"` |
| `start -> <id>` | The start marker of the scope the line is in: a filled dot. | `start -> pending` |
| `<id> -> end` | The end marker of the scope the line is in: a ring with a dot. | `delivered -> end` |
| `choice <id> "Question?" tone=<tone> icon=<set:name>` | A pill with a branch icon where one path splits on conditions. The question is optional: with none it is a small round chip holding only the icon. The default tone is amber, `icon=` swaps the icon, and `icon=none` drops the chip. | `choice stock "In stock?"` |
| `fork <id>` | A bar where one path splits into parallel paths. | `fork split` |
| `join <id>` | A bar where parallel paths merge. | `join merge` |
| `history <id> deep=on` | A circle holding `H`, or `H*` with `deep=on`, inside a composite state. A transition into it returns to the state that was active last. | `history last deep=on` |
| `region "Name" {`, the states inside, then `}` | One of the parts of a composite state that run side by side. The name is optional. | `region "Reader" {` |
| `note <id> : "text" side=<side>` | A note on a state, a choice, a bar, or a composite state. `side=` is `left`, `right`, `top`, `bottom`, or left out. | `note paid : "Refunds stay open for 30 days" side=right` |
| `desc "text"` | The description of the diagram. | `desc "How an order moves."` |

`tone=` takes `neutral`, `blue`, `violet`, `aqua`, `orange`, `green`, `amber`, or `red`, and `icon=` takes a name from the closed list of icons, such as `lucide:play`, `status:success` or `simple:github`. A diagram cannot set a color: the theme chooses every color, in light and dark. [Make it readable](#make-it-readable) says which tone and icon to choose.

### The transition label

The label of a transition reads `event [guard] / action`, as in UML. The parser splits it, so the picture sets the event in the primary ink and the guard and the action in the secondary ink, and the text description says "on the event, if the guard, then the action".

| Written | Event | Guard | Action |
|---|---|---|---|
| `payment received` | payment received | none | none |
| `[in stock]` | none | in stock | none |
| `pay [card valid] / charge card` | pay | card valid | charge card |
| `close / send FIN` | close | none | send FIN |
| `/ notify support` | none | none | notify support |
| `GET /orders` | `GET /orders` | none | none |
| `array[0] access` | `array[0] access` | none | none |

A slash starts the action only when a space follows it, and brackets are a guard only when they are the last thing before the action or the end of the label. So `GET /orders` and `array[0] access` are events. Text wider than 300 px is cut with an ellipsis and warning `W203`; the full text stays in the text description.

### Start, end, and scopes

A scope is the top level of the diagram, a composite state, or one region.

| Rule | Detail |
|---|---|
| `start` and `end` are words, not ids | `state start "Start"` is error `E360`. A transition into `start` or out of `end` is error `E361`. |
| A scope has one start and one end | The marker is made the first time a transition in that scope names it, however many transitions name it. |
| The marker belongs to where the line is written | `start -> a` inside `state shipping "Shipping" {` is the start of Shipping. |
| A transition may cross scopes | Ids are global, so a line inside one composite state may name a state in another. |
| A transition may enter or leave a composite state by its id | `order -> shipping` enters at the start marker of Shipping, and `shipping -> delivered` leaves from its frame. A transition between a composite state and a state inside it is error `E365`. |
| A state may loop to itself | A composite state may too. A choice, a bar, a marker, or a history marker may not (`E364`). |

### Regions

A `region` goes directly inside a composite state, and a composite state holds either states or regions, never both (`E362`). A region outside a composite state, or inside another region, is `E363`. Each region has its own start and end markers. A diagram that reads sideways stacks its regions, a diagram that reads up or down puts them in a row, and a dashed line crosses the whole frame between two of them. [A download with two regions](#a-download-with-two-regions) draws this.

```plotwork
state sync "Syncing" {
  region "Upload" {
    start -> sending
    state sending "Sending" icon=lucide:cloud-upload
  }
  region "Download" {
    start -> receiving
    state receiving "Receiving" icon=lucide:cloud-download
  }
}
```

### Forks, joins, and choices

A fork splits one path into parallel paths and a join brings them together, so use them when several things must all happen. A choice splits one path on conditions, so give every branch out of it a guard. [A release pipeline](#a-release-pipeline) uses both.

```plotwork
fork split
join merge
choice green "All green?"

split -> lint
split -> test
lint -> merge
test -> merge
merge -> green
green -> deploy : "[yes]"
green -> fix : "[no]"
```

### History

A history marker sits inside a composite state. A transition into it returns to the state that was active last, and `deep=on` makes it remember the state of nested composite states too. It is an error outside a composite state (`E366`). [A connection that remembers its state](#a-connection-that-remembers-its-state) draws this.

```plotwork
state player "Player" {
  history last deep=on
  start -> stopped
  state stopped "Stopped"
  state playing "Playing" icon=lucide:play
  stopped -> playing : "play"
  playing -> stopped : "stop"
}
state off "Off"

start -> player
player -> off : "power"
off -> last : "power"
```

### Notes

A note sits across the reading direction, so a transition, which runs along it, never meets one. `side=` is a request: a side along the reading direction moves to the nearest side across it.

| The diagram reads | `left` | `right` | `top` | `bottom` | No side |
|---|---|---|---|---|---|
| Right or left | above | below | above | below | below |
| Down or up | left | right | left | right | right |

Notes on one side stack in the order they are written, nearest first, and a dotted line joins each note to its state. A note on a composite state has no free side, so it sits inside the frame, under what the frame holds, with no connector. A note on a start or end marker is error `E367`.

## Limits and warnings

Warnings never stop a diagram from being drawn. Errors do.

| Limit | Value | What happens past it |
|---|---|---|
| Nodes of any kind | 300 | `E140` |
| Transitions | 600 | `E140` |
| Layers skipped by transitions | 400 | `E140` |
| Nesting of composite states | 6 | `E140` |
| Regions in one composite state | 8 | `E140` |
| Notes | 100 | `E140` |
| States before the density warning | 15, plus 1 for each composite state, up to 25 | `W200` |
| Nesting before the warning | 3 | `W334` |

Layers skipped counts how far transitions jump over the rest of the diagram: a transition that passes over many layers makes the layout work much harder than the number of states suggests, so that number is limited, not only the number of states. The limits are checked from the text alone, so a diagram that is too big is refused at once with a hint on how to split it.

| Code | Severity | When | Fix |
|---|---|---|---|
| `E360` | error | `start` or `end` is used as an id. | Pick another id, and write the marker as a transition. |
| `E361` | error | A transition goes into `start` or out of `end`. | The marker only begins or finishes a transition. |
| `E362` | error | A composite state holds both states and regions. | Put the state into a region. |
| `E363` | error | A `region` is outside a composite state, or inside a region. | Open it inside a composite state. |
| `E364` | error | A choice, a bar, a marker, or a history marker loops to itself. | Loop through a state. |
| `E365` | error | A transition joins a composite state and a state inside it. | Start the transition from inside. |
| `E366` | error | A `history` is outside a composite state. | Put it inside the composite state it belongs to. |
| `E367` | error | A note is on a start or end marker. | Attach it to the state next to the marker. |
| `E140` | error | The diagram is over a limit in the table above. | Split the diagram. |
| `E100`, `E101`, `E110`, `E111`, `E121` to `E123`, `E130`, `E131`, `E133` to `E136` | error | The shared codes: a syntax error, an unterminated string, an unknown statement or header, an unknown tone, icon, or direction, a duplicate id, an unknown state, an unclosed or unmatched block, an unknown option, or a bad option value. | The message names the line and says what to write. For an unknown state it gives the nearest id. |
| `W200` | warning | There are more states than the density limit. | Group states into composite states, or split the diagram. |
| `W201` | warning | A state has no transition at all. | Connect it or remove it. |
| `W202` | warning | The diagram is wider than a column would keep readable. | Use `direction=down`, shorten the labels, or split it. |
| `W203` | warning | Text was cut with an ellipsis. | Shorten the text, or move the detail into `sub=` or a note. |
| `W330` | warning | No path from the top-level start reaches a state. | Add a transition into it. |
| `W331` | warning | A state has no way out, in a part of the diagram that has an end. | Add a transition out, such as `name -> end`. |
| `W332` | warning | A choice has fewer than two ways out, or a branch has no condition. | Give every branch a guard. |
| `W333` | warning | A fork has fewer than two transitions out, or a join has fewer than two in. | Give it two, or use a state. |
| `W334` | warning | Composite states are nested more than 3 deep. | Flatten them, or draw the inner machine as its own diagram. |

`W330` is checked only when the top level has a start marker, and a composite state with no start of its own begins wherever nothing leads into it. `W331` looks at one scope at a time: a state is a dead end only when its own scope has an end marker and neither it nor a composite state around it has a transition out.

## From Mermaid

`stateDiagram` and `stateDiagram-v2` convert into a state diagram. Before the state kind existed they were converted into a flow diagram, with the start and end as nodes and each composite state as a group. The converter now writes a state diagram, checks it, and returns nothing for one that would not validate. Call `convert_mermaid` on one diagram, or `sync_markdown` with `convertMermaid` for a whole file, as the [migration guide](https://docs.plotwork.dev/migrating-from-mermaid.md) describes. The conversion adds no icons and keeps no colors, apart from a tone taken from a class name such as `error`, so choose a tone and an icon for each state afterwards, as [Make it readable](#make-it-readable) describes.

```mermaid-source
---
title: Print job
---
stateDiagram-v2
  [*] --> Queued
  Queued --> Printing : printer free
  Printing --> Done
  Printing --> Failed : jam
  Failed --> Queued : retry
  Done --> [*]
  state Printing {
    [*] --> Warming
    Warming --> Spooling
    Spooling --> [*]
  }
  note right of Failed : Paper jams are the usual cause
  classDef bad fill:#f96
  class Failed bad
```

The converter writes this, and reports two warnings `M200` for the dropped `classDef` and `class` lines:

```plotwork
state "Print job"

state Queued "Queued"
state Printing "Printing" {
  state Warming "Warming"
  state Spooling "Spooling"

  start -> Warming
  Warming -> Spooling
  Spooling -> end
}
state Done "Done"
state Failed "Failed"

start -> Queued
Queued -> Printing : "printer free"
Printing -> Done
Printing -> Failed : "jam"
Failed -> Queued : "retry"
Done -> end

note Failed : "Paper jams are the usual cause" side=right
```

### What is kept

| Mermaid | Plotwork | Note |
|---|---|---|
| `A --> B : event` | `a -> b : "event"` | The label is kept as written. A chain such as `A --> B --> C : x` is two transitions, with the label on the last. |
| `state A`, `state "Long name" as A` | `state a "Long name"` | The id stays. A state first mentioned in a transition is declared when the diagram is written. |
| `A : description` | `sub="line one\nline two"` | Each description line stays its own line. |
| `[*] --> A`, `A --> [*]` | `start -> a`, `a -> end` | The marker of the scope the line is in. Inside a composite state it is that state's own. |
| `state A {` and its states | `state a "A" {` and its states | Nesting is kept up to 24 levels (`M102` past that). A transition into or out of the composite state is kept as written. |
| `--` inside a composite state | `region` blocks | What comes before the first `--` is the first region. One region alone is no split. |
| `<<choice>>`, `[[choice]]` | `choice id` | |
| `<<fork>>`, `<<join>>` | `fork id`, `join id` | They are bars now, so there is no warning. |
| `note right of A : text`, or a note with `end note` | `note a : "text" side=right` | A multi-line note joins its lines with a space. A note on a state that was never mentioned declares it. |
| `direction LR` | `direction=right` in the header | `TB` and `TD` give down, `BT` up, and `RL` left. |
| `accTitle`, `accDescr`, a front matter `title:` | The title and `desc=` | A front matter title wins. |
| `%% comment`, on a line of its own or at the end of one | Ignored | A `%%` inside quotes is text. |

### What is dropped or changed

| Mermaid | What happens | Code |
|---|---|---|
| `classDef`, `class`, `:::name`, `style`, `%%{init}%%`, front matter `config:` | Dropped. A class name that means a tone (`error`, `danger` and `fail` give red, `warning` gives amber, and `success`, `ok`, `done` and `pass` give green) gives the state that `tone=`, and the class itself is dropped. | `M200`, and `M207` for the tone |
| `click` | Dropped. A diagram is a static picture with no links inside it. | `M201` |
| `scale`, `hide empty description`, and a `direction` inside a composite state | Dropped. Plotwork chooses the scale and has one reading direction for the whole diagram. | `M202` |
| A `--` outside a composite state, a transition that loops a choice or a bar to itself, and a transition between a composite state and a state inside it | Dropped, because the meaning has no Plotwork form. | `M203` |
| A stereotype Plotwork has no shape for, such as `<<expandable>>` | Drawn as a plain state. | `M204` |
| HTML tags, Markdown marks, line breaks and entities such as `&quot;` in a label | Decoded or removed. | `M205` |
| An id that is not a valid Plotwork id | Renamed. The label keeps the original name. | `M206` |
| An exact repeat of a transition | Dropped. | `M209` |
| An empty composite state, an empty region, or an empty description | The composite state becomes a plain state, and the empty region is dropped. | `M216` |
| A composite state or a note that is never closed, or a `}` that closes nothing | Repaired. | `M217` |
| A line that cannot be read | An error, or with `lenient` a warning that skips the line. | `M110`, `M218` |
| A note on a composite state | Drawn inside its frame, under the states it holds, because the frame has no free side. | `M250` |
| A state named `start` or `end` | Renamed (`start_state`), because the markers use those words. | `M251` |

The old conversion differed in ways that matter if you kept its output. A state was `node id step "Label"` and is now `state id "Label"`, with no node types. The start and end were terminal nodes with ids such as `start_Active` and are now markers written as transitions. A note and a `--` were dropped and are now kept, as a `note` and as regions. A transition into a composite state was redrawn to its start node, and is now kept as written. `<<fork>>` and `<<join>>` were plain steps and are now bars.

## See also

| Doc | Why |
|---|---|
| [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md) | The flow, sequence, and schema kinds, and the options every kind shares |
| [Examples to copy](https://docs.plotwork.dev/examples.md) | Finished diagrams with the prompt that gets an agent to write each one |
| [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md) | The converter, its diagnostic codes, and what carries over |
| [Icon reference](https://docs.plotwork.dev/reference/icons.md) | Every icon name you can put in `icon=` |
| [Git graphs](https://docs.plotwork.dev/git-graphs.md) | A repository history drawn as lanes |
| [Gantt charts](https://docs.plotwork.dev/gantt-charts.md) | A plan drawn on a calendar |
| [Class diagrams](https://docs.plotwork.dev/class-diagrams.md) | Classes, their members, and how they relate |
| [Timelines](https://docs.plotwork.dev/timelines.md) | Events in time order |
| [User journeys](https://docs.plotwork.dev/user-journeys.md) | The steps of a person, scored by how they feel |
| [Mind maps](https://docs.plotwork.dev/mind-maps.md) | An idea and its branches |
| [Language reference](https://docs.plotwork.dev/reference/language.md) | Every word of the language, one page for each topic |
| [Diagram kinds](https://docs.plotwork.dev/reference/language/kinds.md) | The kinds, and what each one draws |
