---
name: diagram-builder
description: Use when the user wants to build or extend a diagram deck of nested sections and components authored in plain YAML — an architecture map, a timeline diagram, a planner board, a process-flow diagram, a slide-style presentation, a side-by-side comparison, or a mind-map. Not for charts, plots, or numeric/data visualization — route those to the dataviz skill. Triggers — "build a diagram", "architecture diagram", "diagram deck", "timeline diagram", "flow diagram", "planner board", "comparison diagram", "add a page/section/component to the diagram".
---

# Diagram Builder

Diagram-builder draws a diagram of any kind — a system architecture, a timeline,
a slide-style presentation, a process flow, a comparison, a mind-map, a planner
board — as nested boxes authored in plain YAML and rendered by a generic engine:
no framework, no server, opens under `file://`. Its whole material is two
primitives — a recursive **section** that ARRANGES and a **component** that
CARRIES — and its work is to find the form that teaches THIS idea best and lower
it into that geometry. Everything domain-specific lives in the data; nothing
about a domain lives in the engine. Everything on the canvas invites the reader
toward the centre: the layout centres its content, a click opens a bottom-centre
panel, a chip spotlights a relation.

```
idea
 └─ document        the deck: title, subtitle, version, palette, pages
     └─ page        one act/view (also the ROOT section: its columns + sections)
         └─ section     a grid zone; nests other sections freely (a grid of grids)
             └─ component   a leaf: a card, a divider, a lane label, a declared hole
   filters (page-level only) light a relation across components
```

## The governing definition (the anchor)

Internalize this before anything else. Every design decision is judged against
it, and the adversarial critique at the end of the cycle is run against it:

> A diagram is a **semantic design tool: nested boxes, one inside another**.
> Some boxes are **sections** — they group other sections or groups of
> components. Sections divide into **columns**, vertically and horizontally.
> **Components expand horizontally and vertically** — a merge on **two axes**:
> a span of columns plus a row-span of rows — and they sit in columns, or in
> cells flowing downward. The objective is **compaction and symmetry**: the
> canvas **fills** inside a **centered width cap** (max-width ≈ 1280px — a
> medium resolution, no horizontal scroll). It neither expands to arbitrary
> width nor leaves holes — **full rectangles**.

The engine implements this model: both merge axes are real, the canvas fills to
the centered cap, cells keep a readable minimum width (columns collapse before a
cell degrades), a guardrail asserts form-scoped invariants against the real
rendered geometry, and a strict schema rejects any unknown field loudly at build
time. So design, discuss, and critique against the definition knowing the engine
renders it — the exact geometry and the field-by-field schema live in
`reference.md`, the dialect's terms and value sets in `GLOSSARY.md`.

## The nine principles

Nine principles, not a menu of features. Every capability of the engine is a
consequence of one of them, so hold the principles and the possibilities open by
themselves.

### How the canvas works

**1 · Everything you see is a merged cell.** There is no other geometry: one
uniform cell, and two axes to merge it on. Width is REACH; height is MAGNITUDE.
A merge consumes rows or columns that must already exist — something has to sit
beside it creating them. Ask: what do I want bigger, in which direction, and
what holds it up?

**2 · A grid holds cells or zones, and mixing them changes what every dial
means.** One nested section among components turns the whole level into a row of
zones: `columns` stops creating tracks, `span` becomes a relative weight,
`rowspan` ceases to exist, and the cell invariants stop measuring that level.
You will mix sometimes — a timeline of sections divided by separators is a good
reason. Mix knowing what you give up.

**3 · You author a sequence, not positions.** There is no cell coordinate, only
`order`. That same order is the packing order and the stacking order when
everything collapses to one column. And filling runs forward: nothing goes back
to fill the hole a tall cell left. Whatever belongs beside something tall goes
before it.

### How you speak through it

**4 · Every field is a slot with a character, not a meaning.** The field decides
SIZE and PROMINENCE; you decide the meaning. The kicker is small — a qualifier,
a code, a step. The title is the loud one, and that is where a number goes when
the number is the message. The description is brief and clamps. The detail is
unbounded, behind a click. Read a slot's character wrong and the layout fights
you: a lane label pressed into service as a section heading grows, steals space,
and distorts the grid — a section that needs a heading has one.

**5 · Every visual channel carries one claim.** Position, size, colour, border
style, kicker — independent of each other. Double them to reinforce (a bar that
grows and turns red says magnitude twice) or split them to say two things. A
channel's meaning is PER PAGE and declaring it is obligatory — but no schema
field holds it: you declare it in the CONTENT, as a legend band, a section
heading, or a box that spells the code out. An undeclared channel is decoration.

**6 · The grid does not draw relations: it lights them.** There are no arrows.
Every relation is shared membership in a chip — a directed path, which `order`
makes readable, or a concept that cuts across sections. A relation needs two
ends; a chip with a single member dims the page and lights nothing. The same key
on two pages projects one onto the other.

### What judges it

**7 · Structure is the assertion.** Distinct things are distinct sections; parts
of one thing are components inside its section. No machine can verify this, and
it is the only thing separating a diagram from decoration.

**8 · What does not fit does not shrink: it moves.** A cell never grows by
content. Every "it doesn't fit" is answered by moving text into the detail, by
merging, or by collapsing columns through nesting — never by squeezing. An
unreadable cell is a defect even when the geometry closes.

**9 · The hole speaks.** An empty cell asserts something. If you did not mean to
say it, close it; if you did, declare it — `type: spacer` is the leaf that
declares it, occupying its cell and drawing nothing. Reach for it whenever
placement has to rest on a BASE instead of hanging from the ceiling: filling runs
forward and top-down (principle 3), so bars written first all start in row 1 and
taper downward, and magnitudes are only comparable from a shared floor. Open each
short column with spacers above its bar and every bottom edge lands in the same
row — the alignment is bought with holes you MEANT, and the rectangle closes
exactly instead of leaning on an exemption. And a shared row only means something
when every lane is the same length.

## What the guardrail can and cannot see

The guardrail is not a design judge. It measures the real rendered geometry, so
it reaches exactly the principles that ARE geometry:

| Principle | Automatic check |
|-----------|-----------------|
| 1 merged cell · 2 cells-or-zones · 3 sequence | **yes** — the merge, the compound level, and the packing order are all measured on the render |
| 8 does not shrink · 9 the hole speaks | **yes** — clamped content, the readable floor, empty tracks and orphan cells are all asserted |
| 6 relations | **half** — the chip↔component join is asserted in both directions; whether the relation is the RIGHT one is unreachable |
| 4 slot character · 5 one claim per channel · 7 structure is the assertion | **no** — nothing measurable distinguishes a meaningful section from a convenient one |

Therefore: **a green guardrail means "it is not broken", never "it is right".**
Reading the render and running the adversarial critique are not an elegant
closing ritual — they are the ONLY verification that exists for four of the nine
principles (4, 5, 7, and the half of 6 no machine can reach). A verdict that
rests on green alone has verified the geometry and asserted the meaning.

That table is also why the evidence classes under **The verdict** are three and
not two. The **yes** rows are reached by two different gates that both compute a
number and can disagree with each other: one derives the geometry from the
authored YAML, the other observes the geometry the browser actually drew. The
**no** row is reached by neither — only by looking.

## Conceptualizing the user's problem

The form is the LAST decision, not the first. Before it:

1. **Understand the problem — the kind of input decides your first read.** What
   is the idea, who reads it, what should they walk away knowing? The two kinds
   of input below split the first move. An input that already carries its
   structure — above all an existing deck, whose path the harness usually hands
   you — is read FIRST: that deck's own `data/` is the source of truth for what
   it really says rather than what you assume, so read its real pages, sections,
   and components (the knowledge, not just how the engine works) before
   proposing anything. An open idea has no deck and no `data/` to read; there the
   first read is the seed below, and the choice of form comes out of it.
2. **Help develop it.** A vague idea is not a blocker; developing it is the
   work. Name the entities, ask what is distinct from what, surface the
   relations the user has not named yet.
3. **Summarize and adapt the information to the components.** Each thing must
   survive as a qualifier, one loud line, a brief gloss that clamps, and
   whatever is unbounded behind a click. Information that cannot be compressed
   that way is not yet a component — it is a section, or it is detail.
4. **Only then choose the form.**

**Two kinds of input.** A specific, structured document (a spec, an itemized
doc) already carries its structure — MIRROR it: its parts become sections, its
items components; inventing a different shape discards the author's own
assertion. An open idea carries no structure — EXPLORE which form teaches it
before drawing anything.

**Choosing the form is a criterion, not a lookup.** Two questions settle it: does
the idea MOVE or STAND? (a process wants a timeline or a flow; a structure wants
a dashboard, a comparison, a mind-map, a planner) — and does the reading
CONVERGE on one thing or DIVERGE into many? (a mind-map and a comparison
converge; a planner and a dashboard diverge). One caveat the geometry imposes:
the engine is a GRID and cannot radiate, so a mind-map is symmetric nested
sections around a central band — never present a radial burst as something it
draws. For a wider inventory of visualization forms and what each one teaches,
<https://www.visual-literacy.org/periodic_table/periodic_table.html>. The
copyable YAML skeleton for each form is in `reference.md` ("Per-form seed
skeletons") — do not rebuild it from memory.

## The semantic doctrine (the layout mirrors the idea)

The structure of the layout IS a mirror of the structure of the idea, so the
mapping is never stylistic — it is the meaning:

- **Distinct things are distinct sections; parts of one thing are components in
  one section.** Folding two distinct things together for visual convenience
  erases the distinction the idea makes.
- **A cross-cutting relation is a CHIP, not structure.** Beyond "what are the
  sections?", ask "what should the reader be able to spotlight?".
- **A separator is a WEAK divider.** A line divides only WITHIN a section. If
  the two sides are distinct things, they are sections — reaching for a line
  where a boundary belongs understates the distinction.
- **Every element is justified by MEANING, nothing by decoration.** For each
  placement you must be able to say why: why a section, why this column count,
  why this merge, why on the right, why the base band. That "why" IS the design
  critique, run element by element.
- **The doctrine RULES over the geometry.** Compaction, symmetry, and full
  rectangles are targets, not the meaning. A hole or an asymmetry is legitimate
  only when it ENCODES an intention; when it does not, it is a defect. Never
  fold, drop, or distort a semantic distinction to make a rectangle come out
  full — the geometry serves the idea, never the reverse.

## The conversational cycle and the handoff to the builder

The cycle is generic: whoever HOLDS the idea drives it, person or agent, and it
opens by two doors — the same two kinds of input as above:

**open idea → develop · existing deck (the usual case) → read what it says and
name the DELTA, its form already chosen** → **propose → sketch → iterate →
build → validate → adjust by recalculating.**

- **Propose; do not wait to be told.** From a vague idea — a suggestion or a
  direct mandate — naming the entities, how they group, and which form teaches
  them is your move.
- **Be explanatory, not verbose.** Do not assume the other side knows the jargon
  or the app. Say what a section, a band, or a chip does FOR THEIR IDEA, one
  plain sentence, when it earns its place.
- **The sketch is spoken, not notated.** Describe the shape in the shared
  vocabulary — "two sections side by side, the left one three cells wide, a
  full-width base band beneath them with a labelled divider" — and decide NO
  component detail here: no title wording, no kicker, no positions. The sketch
  is cheap to redo; that is its whole value, and detail is what makes it
  expensive.
- **The handoff to the builder is the agreed FORM plus the VALUES, in the
  natural language of the dialect.** You hand over the shape and the meaning —
  which sections, which merges, which relations, and the content each component
  carries. The builder holds the schema (`reference.md`, `GLOSSARY.md`) and
  lowers it into fields; you do not need the field names to hand off well.
- **Adjust means RECALCULATE, never nudge.** When a datum arrives — "mount it in
  that section", "this belongs at the base" — name the dials it touches, reason
  how the rows repack and where the collapse lands, and show the before/after of
  the changed section instead of re-reading the whole deck.
- **Ask WHERE before saving anything.** Never assume a path; the scaffolding
  modes are in `reference.md`.

**The verdict.** Editing the data is the fast path — the diagram is decided in
the YAML, not in the pixels — and a change is not done until its verdict is
earned. Build, then run the engine's own guardrail (the loop, the commands, and
the invariant table are in `reference.md`), and never declare done on red. On top
of that:

- **The engine and the bundle move together.** `index.html`, `engine/engine.js`
  and `data/data.generated.js` are one coupled trio: swapping the engine or
  rewriting the YAML without re-running the build pairs a new engine with the old
  bundle, which does not fail — it renders the stale deck silently (the coupling
  is in `reference.md`, the serving-side cache-busting in `assets/README.md`).
- **Every verdict names THREE evidence classes.** **MODELLED** — arithmetic over
  the authored YAML (`npm run model`): it proves the rectangle closes, and it has
  never seen a pixel. **MEASURED** — the real render in a browser
  (`npm run render`): it proves the stylesheet actually IMPLEMENTS what the model
  assumed. **SEEN** — a human or an agent looking: the only class that reaches
  principles 4, 5, 7 and the half of 6 no machine can. Two classes obscured
  three, and that is how a computation came to be reported as an observation —
  `ALL PASS` out of the arithmetic, cited as the verdict, over a page the browser
  was drawing wrong.
- **MODELLED and MEASURED are both mandatory, always.** `npm run gate` runs the
  pair and is the only thing a verdict may cite. Requiring the browser one costs
  nothing, which is what makes this safe: `render` resolves Playwright lazily and,
  where no browser exists, prints `SKIPPED (no browser)` and exits 0 — so the
  verdict then says `MEASURED: unavailable` in words instead of resting silently
  on MODELLED. The two disagree exactly where it matters: a rule the stylesheet
  never implemented leaves the arithmetic closing a rectangle the browser draws at
  a third of its width.
- **SEEN is required on a first build, a change of form or of model, or any
  intention no invariant covers — and it names a command.**
  `DIAGRAM_SHOTS_DIR=<a readable path> npm run verify` renders every page at 1920
  and 1440 in both themes and writes the PNGs where you can open them; then load
  `visual-verify` for the looking discipline. Do NOT write a browser probe:
  `verify.mjs` already resolves a Chromium that is on disk (no download) and
  handles the capture trap — `.canvas` is `position:absolute` with
  `overflow:auto`, so a naive full-page screenshot truncates to viewport height.
  A verdict names all three classes: "MODELLED 4540 assertions / 0 not-asserted ·
  MEASURED 170 checks · SEEN p2 at 1920, both themes". `ALL PASS` is not a
  verdict.
- **The RATCHET rule.** Every defect the eye catches becomes an invariant before
  the change closes — the guardrail only grows. A defect fixed without a new
  invariant will be reintroduced by the next change the guardrail cannot see.
  Invariants are form-scoped and retirable (one can supersede another), which is
  what lets the guardrail grow without ossifying: a rule tuned to a dashboard
  must not fail a legitimate timeline. **And a new invariant is not landed until
  it has been SEEN TO FAIL.** Break the rule it guards, watch it go red, restore
  it. An invariant that has never failed is not an invariant — it is a comment
  with a pass count: one was added inside a mirror, the rule it guarded was
  deleted, and the gate still printed ALL PASS with the count moving 6019 → 6018.
  A mirrored assertion is the one shape that can fail OPEN, so: a mirror asserts a
  rule's PRESENCE as a hard failure and only its ABSENT FILE as an advisory. Never
  the reverse.
- **The adversarial critique closes the work.** Walk the rendered layout against
  the governing definition and demand the doctrine's "why" for every element,
  and that every intended relation has a chip. An element without a "why" fails
  the critique.
- **Headless changes the counterpart, not the method.** With no interactive user,
  "iterate" resolves as adversarial SELF-critique against the doctrine before
  building — the sketch is still made, then interrogated element by element. An
  ambiguity that survives the self-critique is a REPORT FINDING, never a guess.

## The seed is the showcase — open it

`assets/data/` carries a domain-free seed deck whose only job is to EXERCISE
every tool the engine offers: inline sections side by side, nesting, the
structural leaves, height-as-magnitude, a partial merge, the collapse cascade at
a wide column count, span-weighted zones, and — twice — the deliberate mixing of
cells and zones that principle 2 warns about. It is the fastest path from "is
this possible?" to seeing it rendered.

**Open it.** A capability read in a seed that renders is worth more than the same
capability described in prose, and the seed is where a claim gets falsified.

**Coherence runs both ways.** Every tool this skill names is exercised in the
seed, and every tool the seed exercises is named in this skill. A capability
present on only one side is a defect: either the seed lost its demo, or the skill
grew a claim nothing renders. Check the pair whenever either side changes.

## Where the rest lives

- `GLOSSARY.md` — the canonical dialect terms and their value sets; the shared
  vocabulary the skill and the rendered app both speak.
- `reference.md` — the field-by-field schema, the fill geometry, the per-form
  skeletons, the positioning recipes, the engine gotchas, the authoring modes,
  and the build → validate loop with the form-scoped invariant table.
- `assets/` — the portable engine, ready to scaffold into any repo: `index.html`,
  `engine/`, the seed `data/` above, and `tools/`, where the verification lives.
  Two gates, both mandatory, because they answer different questions:
  `check-layout.mjs` over `static-census.cjs` COMPUTES the layout from the
  authored YAML (`npm run model`), and `validate-layout.cjs` OBSERVES the layout
  the browser drew (`npm run render`) — a verdict needs both, and `npm run gate` is
  the pair. Beside them: `test-guards.mjs`, the negative suite that proves the
  gates detect what they claim; `contrast-audit.cjs`; and `verify.mjs`, the
  screenshot sweep SEEN runs on. Scaffold without `tools/` and the deck has no way
  to earn a verdict.
