Sequence Diagram Skill
Generate UML-2.x-lite sequence diagrams as self-contained SVG files, sharing the blueprint aesthetic of the sibling architecture-diagram and mindmap skills: dark #020617 background, JetBrains Mono, halo text, semantic palette, deterministic geometry-audit gate.
What triggers this skill
- "Draw a sequence diagram of X"
- "Show the [OAuth | login | checkout | webhook] flow over time"
- "Ladder diagram for protocol Y"
- "Show the message exchange between A and B"
- "Activity diagram of [request]" — users frequently conflate these; default to sequence when there's a clear temporal order of messages between named actors
- Any request whose answer is "X happens, then Y, then Z" with named actors
When to use this skill vs siblings
| You want… |
Skill |
| Static topology — what calls what, where it lives, how it's deployed |
architecture-diagram |
| Radial topic tree — one focal point, themes radiating out |
mindmap |
| Temporal sequence — who talks to whom, in what order, over time |
sequence-diagram (this one) |
Rule of thumb: if every arrow in your diagram needs a number to make sense, you want a sequence diagram.
Output contract
Same as the rest of the plugin:
- Self-contained
.svg file (or .html if delivering to a user with the export toolbar — copy from ../architecture-diagram/resources/template.html and swap the SVG body).
- Dark
#020617 background, JetBrains Mono via the inline <style> block, halo text via paint-order: stroke fill.
- Geometry-audit (
../architecture-diagram/resources/geometry-audit.py) must exit 0 before the diagram is done.
Visual conventions (UML 2.x, blueprint-adapted)
| Element |
Notation |
Implementation |
| Actor box |
rounded rect, semantic color |
<rect class="component <color>" rx="2"> with name + optional sub-label |
| Lifeline |
dashed vertical line below actor |
<line class="lifeline" stroke-dasharray="4,4"> — the lifeline class makes the audit skip it for edge-edge / edge-crosses-component checks |
| Synchronous call |
solid arrow, filled V chevron |
<path d="M src,y L tgt,y" marker-end="url(#ah-<color>)"> |
| Async call |
dashed arrow, open V chevron |
same path + stroke-dasharray="5,3" |
| Return |
dashed line, open chevron, optional label |
stroke-dasharray="4,3", lighter stroke |
| Self-loop |
right-side bump arrow |
<path d="M x,y q 32,0 32,16 T x,y+16"> with arrowhead at end |
| Activation bar (optional) |
thin rect on lifeline |
<rect width="8" fill="rgba(...)"> overlapping lifeline at activation span |
| Note |
halo-only text in clear space |
<text class="note"> — no chip rect |
| Numbering (optional) |
small badge or prefix on label |
label = "1. login" — simplest readable form |
Semantic colors per actor type (reuse from architecture-diagram)
| Actor type |
kind |
Stroke |
| Human user |
slate |
#94a3b8 |
| Frontend / SPA / mobile |
cyan |
#22d3ee |
| Backend service |
emerald |
#34d399 |
| Database / store |
violet |
#a78bfa |
| Auth / IDP / KMS |
rose |
#fb7185 |
| Cloud edge / LB / CDN |
amber |
#fbbf24 |
| External system |
slate |
#94a3b8 |
Workflow
- Extract actors from the brief. Read the user's prose; identify the distinct named participants (USER, SPA, AUTH SERVER, DB, API, …). Typical count: 3–6. Cap at 7 — beyond that, split the diagram.
- Extract messages in order. Each message = (src actor, tgt actor, label, kind ∈ {sync, async, return, self}). Keep labels short — verbs preferred ("request token", "fetch user").
- Compute layout (algorithm below).
- Emit SVG. Actor row first, then lifelines, then messages top-to-bottom.
- Run geometry-audit. Must be clean. Lifelines are now exempt from edge-edge crossings (see Audit notes below).
Layout algorithm
The diagram is two zones: an actor row at the top, then a message ladder below.
Step 1 — column widths and positions
N = number of actors
LEFT = 60 # left margin
RIGHT = 60 # right margin
COL_W_i = max(180, label_w_i + 40) # per-actor; longest of name and sub-label drives width
x_i = LEFT + sum(COL_W_j for j < i) + COL_W_i / 2 # actor center
W = LEFT + sum(COL_W) + RIGHT
Step 2 — actor row
ACTOR_TOP = 80
ACTOR_H = 56
actor_box_i = rect(x = x_i - aw_i/2, y = ACTOR_TOP, w = aw_i, h = ACTOR_H, rx = 4)
where aw_i = max(140, label_w_i + 28)
Title and subtitle live above the actor row (y = 32 and y = 52), same as every other diagram in this plugin.
Step 3 — lifeline geometry
Each lifeline runs from the bottom of its actor box down to LIFELINE_BOTTOM:
LIFELINE_TOP = ACTOR_TOP + ACTOR_H + 8 # 8 px gap below actor
LIFELINE_BOTTOM = MSG_TOP + M * ROW_H + 16 # 16 px below last message
Emit each lifeline as:
<line class="lifeline" x1="x_i" y1="LIFELINE_TOP" x2="x_i" y2="LIFELINE_BOTTOM"
stroke="#475569" stroke-width="1" stroke-dasharray="4,4"/>
The class="lifeline" is required — the geometry-audit uses this class to skip lifelines in edge-edge and edge-crosses-component checks (otherwise every horizontal message would flag).
Step 4 — message ladder
MSG_TOP = LIFELINE_TOP + 24 # first message y
ROW_H = 36 # vertical gap per message
y_k = MSG_TOP + k * ROW_H # k = 0..M-1
For each message at row k:
- Standard message (src ≠ tgt): straight horizontal segment from src lifeline to just-before tgt lifeline (leave 8 px on the marker side so the chevron sits inside the column, not crossing into the next).
<path d="M src_x,y_k L tgt_x-sign(tgt-src)*8,y_k"
stroke="<color>" stroke-width="1.4" marker-end="url(#ah-<color>)"/>
- Self-loop (src = tgt): bump 32 px to the right, down 16 px, back. Total vertical footprint = 16 px, fits inside one row.
<path d="M src_x,y_k h 32 v 16 h -32"
stroke="<color>" stroke-width="1.4" marker-end="url(#ah-<color>)"/>
Label sits to the right of the bump: <text x="src_x + 40" y="y_k + 4">label</text>.
- Sync vs async vs return — pick from the Visual conventions table for line style. Different arrowhead is not required, but you can use a smaller arrowhead for returns.
Step 5 — labels
Centered above the arrow line, with halo:
label.x = (src_x + tgt_x) / 2
label.y = y_k - 6
If |src_x − tgt_x| < label_w + 16, widen the source/target columns so the label fits. Don't shrink the label — verbosity here costs nothing visually.
Step 6 — canvas height
H = LIFELINE_BOTTOM + 40 # 40 px footer margin
viewBox = "0 0 W H"
Audit notes
The geometry-audit has been extended (in this repo) to recognize class="lifeline" on <line> and <path> elements. Lifelines are:
- Skipped in
edge-crosses-edge (every message crosses every lifeline — by design)
- Skipped in
edge-crosses-component (lifelines shouldn't cross actor boxes anyway; if they do, the actor box layout itself is wrong)
- Still parsed so the audit can read them — just exempt from those two checks
Other audit classes apply normally:
component-overlap — actor boxes must not overlap
label-overlaps-component — message labels must not sit on an actor box
edge-crosses-label — different messages' labels must not collide
If you see edge-crosses-edge findings in a sequence diagram, those are between messages (not lifelines) — typically a sign that two messages share a y-coordinate by mistake, or that a self-loop bump overlaps the next row. Fix by spacing or by re-ordering.
Worked example
"Show the OAuth 2.0 authorization code flow with PKCE: user clicks login on the SPA, SPA redirects to the IDP, IDP authenticates the user and stores a code in its DB, IDP redirects back with the code, SPA exchanges the code (with verifier) for a token at the IDP, IDP returns the token, SPA calls the API with the token, API verifies and returns the resource."
Extracted:
- Actors (5): USER (slate), SPA (cyan), IDP (rose), DB (violet), API (emerald)
- Messages (9): each line of the prose maps to one message in order
- One self-loop on IDP for "authenticate user" (its internal step)
See ../../examples/14-sequence-oauth-code-flow.svg for the rendered output.
What you must not do
- ❌ Don't draw lifelines without
class="lifeline" — the audit will flag every message as crossing a lifeline.
- ❌ Don't use curved Bezier paths for inter-actor messages. Sequence diagrams expect straight horizontal arrows. Bezier curves are reserved for self-loops.
- ❌ Don't crowd multiple messages on the same
y. Spacing is what makes sequence diagrams readable.
- ❌ Don't number messages just because UML allows it. Add a number prefix only when there are skipped beats or back-references ("see 3.").
- ❌ Don't try to encode
alt/loop/par fragments in v1. If the brief genuinely needs branching, draw two sequence diagrams (one per branch) and reference them in prose.
- ❌ Don't load external images / icons / fonts beyond the Google Fonts JetBrains Mono — same constraint as the rest of the plugin.
Limits and when to refactor
6 actors → split into two diagrams (e.g., "auth phase" + "API phase").
14 messages → consider folding consecutive same-pair calls into one message with a list label.
- Many parallel branches (
par fragment territory) → sequence diagrams aren't the right tool; use an architecture diagram with annotated edges instead.
Attribution
Original work. Follows UML 2.x sequence-diagram semantics where applicable, blueprint-adapted to match the architecture-diagram and mindmap aesthetic in this plugin.
1---2name: sequence-diagram3description: Use when the user asks for a sequence diagram, ladder diagram, message-exchange diagram, or any temporal flow showing who talks to whom in order ("draw the OAuth flow", "show the request lifecycle", "diagram the protocol over time", "sequence diagram for X"). Produces dark-themed self-contained SVG with actor lifelines, sync/async/return messages, self-loops, halo text, and a geometry-audit gate that knows about lifelines.4---56# Sequence Diagram Skill78Generate UML-2.x-lite sequence diagrams as self-contained SVG files, sharing the blueprint aesthetic of the sibling `architecture-diagram` and `mindmap` skills: dark `#020617` background, JetBrains Mono, halo text, semantic palette, deterministic geometry-audit gate.910## What triggers this skill1112- "Draw a sequence diagram of X"13- "Show the [OAuth | login | checkout | webhook] flow over time"14- "Ladder diagram for protocol Y"15- "Show the message exchange between A and B"16- "Activity diagram of [request]" — users frequently conflate these; default to sequence when there's a clear temporal order of messages between named actors17- Any request whose answer is "X happens, then Y, then Z" with named actors1819## When to use this skill vs siblings2021| You want… | Skill |22|---|---|23| Static topology — what calls what, where it lives, how it's deployed | `architecture-diagram` |24| Radial topic tree — one focal point, themes radiating out | `mindmap` |25| **Temporal sequence — who talks to whom, in what order, over time** | **`sequence-diagram`** (this one) |2627Rule of thumb: if every arrow in your diagram needs a *number* to make sense, you want a sequence diagram.2829## Output contract3031Same as the rest of the plugin:32- Self-contained `.svg` file (or `.html` if delivering to a user with the export toolbar — copy from `../architecture-diagram/resources/template.html` and swap the SVG body).33- Dark `#020617` background, JetBrains Mono via the inline `<style>` block, halo text via `paint-order: stroke fill`.34- Geometry-audit (`../architecture-diagram/resources/geometry-audit.py`) must exit 0 before the diagram is done.3536## Visual conventions (UML 2.x, blueprint-adapted)3738| Element | Notation | Implementation |39|---|---|---|40| Actor box | rounded rect, semantic color | `<rect class="component <color>" rx="2">` with name + optional sub-label |41| Lifeline | dashed vertical line below actor | **`<line class="lifeline" stroke-dasharray="4,4">`** — the `lifeline` class makes the audit skip it for edge-edge / edge-crosses-component checks |42| Synchronous call | solid arrow, filled V chevron | `<path d="M src,y L tgt,y" marker-end="url(#ah-<color>)">` |43| Async call | dashed arrow, open V chevron | same path + `stroke-dasharray="5,3"` |44| Return | dashed line, open chevron, optional label | `stroke-dasharray="4,3"`, lighter stroke |45| Self-loop | right-side bump arrow | `<path d="M x,y q 32,0 32,16 T x,y+16">` with arrowhead at end |46| Activation bar (optional) | thin rect on lifeline | `<rect width="8" fill="rgba(...)">` overlapping lifeline at activation span |47| Note | halo-only text in clear space | `<text class="note">` — no chip rect |48| Numbering (optional) | small badge or prefix on label | label = `"1. login"` — simplest readable form |4950### Semantic colors per actor type (reuse from architecture-diagram)5152| Actor type | `kind` | Stroke |53|---|---|---|54| Human user | `slate` | `#94a3b8` |55| Frontend / SPA / mobile | `cyan` | `#22d3ee` |56| Backend service | `emerald` | `#34d399` |57| Database / store | `violet` | `#a78bfa` |58| Auth / IDP / KMS | `rose` | `#fb7185` |59| Cloud edge / LB / CDN | `amber` | `#fbbf24` |60| External system | `slate` | `#94a3b8` |6162## Workflow63641. **Extract actors from the brief.** Read the user's prose; identify the distinct named participants (USER, SPA, AUTH SERVER, DB, API, …). Typical count: 3–6. Cap at 7 — beyond that, split the diagram.652. **Extract messages in order.** Each message = (src actor, tgt actor, label, kind ∈ {sync, async, return, self}). Keep labels short — verbs preferred ("request token", "fetch user").663. **Compute layout** (algorithm below).674. **Emit SVG.** Actor row first, then lifelines, then messages top-to-bottom.685. **Run geometry-audit.** Must be clean. Lifelines are now exempt from edge-edge crossings (see *Audit notes* below).6970## Layout algorithm7172The diagram is two zones: an **actor row** at the top, then a **message ladder** below.7374### Step 1 — column widths and positions7576```77N = number of actors78LEFT = 60 # left margin79RIGHT = 60 # right margin80COL_W_i = max(180, label_w_i + 40) # per-actor; longest of name and sub-label drives width81x_i = LEFT + sum(COL_W_j for j < i) + COL_W_i / 2 # actor center8283W = LEFT + sum(COL_W) + RIGHT84```8586### Step 2 — actor row8788```89ACTOR_TOP = 8090ACTOR_H = 5691actor_box_i = rect(x = x_i - aw_i/2, y = ACTOR_TOP, w = aw_i, h = ACTOR_H, rx = 4)92 where aw_i = max(140, label_w_i + 28)93```9495Title and subtitle live above the actor row (`y = 32` and `y = 52`), same as every other diagram in this plugin.9697### Step 3 — lifeline geometry9899Each lifeline runs from the bottom of its actor box down to `LIFELINE_BOTTOM`:100101```102LIFELINE_TOP = ACTOR_TOP + ACTOR_H + 8 # 8 px gap below actor103LIFELINE_BOTTOM = MSG_TOP + M * ROW_H + 16 # 16 px below last message104```105106Emit each lifeline as:107108```html109<line class="lifeline" x1="x_i" y1="LIFELINE_TOP" x2="x_i" y2="LIFELINE_BOTTOM"110 stroke="#475569" stroke-width="1" stroke-dasharray="4,4"/>111```112113**The `class="lifeline"` is required** — the geometry-audit uses this class to skip lifelines in edge-edge and edge-crosses-component checks (otherwise every horizontal message would flag).114115### Step 4 — message ladder116117```118MSG_TOP = LIFELINE_TOP + 24 # first message y119ROW_H = 36 # vertical gap per message120y_k = MSG_TOP + k * ROW_H # k = 0..M-1121```122123For each message at row k:124125- **Standard message** (src ≠ tgt): straight horizontal segment from src lifeline to just-before tgt lifeline (leave 8 px on the marker side so the chevron sits inside the column, not crossing into the next).126 ```127 <path d="M src_x,y_k L tgt_x-sign(tgt-src)*8,y_k"128 stroke="<color>" stroke-width="1.4" marker-end="url(#ah-<color>)"/>129 ```130- **Self-loop** (src = tgt): bump 32 px to the right, down 16 px, back. Total vertical footprint = 16 px, fits inside one row.131 ```132 <path d="M src_x,y_k h 32 v 16 h -32"133 stroke="<color>" stroke-width="1.4" marker-end="url(#ah-<color>)"/>134 ```135 Label sits to the right of the bump: `<text x="src_x + 40" y="y_k + 4">label</text>`.136- **Sync vs async vs return** — pick from the *Visual conventions* table for line style. Different arrowhead is not required, but you can use a smaller arrowhead for returns.137138### Step 5 — labels139140Centered above the arrow line, with halo:141142```143label.x = (src_x + tgt_x) / 2144label.y = y_k - 6145```146147If `|src_x − tgt_x| < label_w + 16`, widen the source/target columns so the label fits. Don't shrink the label — verbosity here costs nothing visually.148149### Step 6 — canvas height150151```152H = LIFELINE_BOTTOM + 40 # 40 px footer margin153viewBox = "0 0 W H"154```155156## Audit notes157158The geometry-audit has been extended (in this repo) to recognize `class="lifeline"` on `<line>` and `<path>` elements. Lifelines are:159160- **Skipped** in `edge-crosses-edge` (every message crosses every lifeline — by design)161- **Skipped** in `edge-crosses-component` (lifelines shouldn't cross actor boxes anyway; if they do, the actor box layout itself is wrong)162- **Still parsed** so the audit can read them — just exempt from those two checks163164Other audit classes apply normally:165- `component-overlap` — actor boxes must not overlap166- `label-overlaps-component` — message labels must not sit on an actor box167- `edge-crosses-label` — different messages' labels must not collide168169If you see `edge-crosses-edge` findings in a sequence diagram, those are between **messages** (not lifelines) — typically a sign that two messages share a y-coordinate by mistake, or that a self-loop bump overlaps the next row. Fix by spacing or by re-ordering.170171## Worked example172173> "Show the OAuth 2.0 authorization code flow with PKCE: user clicks login on the SPA, SPA redirects to the IDP, IDP authenticates the user and stores a code in its DB, IDP redirects back with the code, SPA exchanges the code (with verifier) for a token at the IDP, IDP returns the token, SPA calls the API with the token, API verifies and returns the resource."174175Extracted:176- **Actors** (5): USER (slate), SPA (cyan), IDP (rose), DB (violet), API (emerald)177- **Messages** (9): each line of the prose maps to one message in order178- One self-loop on IDP for "authenticate user" (its internal step)179180See [`../../examples/14-sequence-oauth-code-flow.svg`](../../examples/14-sequence-oauth-code-flow.svg) for the rendered output.181182## What you must not do183184- ❌ Don't draw lifelines without `class="lifeline"` — the audit will flag every message as crossing a lifeline.185- ❌ Don't use curved Bezier paths for inter-actor messages. Sequence diagrams expect straight horizontal arrows. Bezier curves are reserved for self-loops.186- ❌ Don't crowd multiple messages on the same `y`. Spacing is what makes sequence diagrams readable.187- ❌ Don't number messages just because UML allows it. Add a number prefix only when there are skipped beats or back-references ("see 3.").188- ❌ Don't try to encode `alt`/`loop`/`par` fragments in v1. If the brief genuinely needs branching, draw two sequence diagrams (one per branch) and reference them in prose.189- ❌ Don't load external images / icons / fonts beyond the Google Fonts JetBrains Mono — same constraint as the rest of the plugin.190191## Limits and when to refactor192193- > 6 actors → split into two diagrams (e.g., "auth phase" + "API phase").194- > 14 messages → consider folding consecutive same-pair calls into one message with a list label.195- Many parallel branches (`par` fragment territory) → sequence diagrams aren't the right tool; use an architecture diagram with annotated edges instead.196197## Attribution198199Original work. Follows UML 2.x sequence-diagram semantics where applicable, blueprint-adapted to match the architecture-diagram and mindmap aesthetic in this plugin.