Reference: VoxDiagram
VoxDiagram is the checked diagram language for Vox documentation. This page publishes the grammar card that agents receive with the validation tool, shows a graph and a sequence example that the doc lint validates, and documents the vox_validate_diagram MCP envelope, the vox diagram command line with its exit codes, strict Mermaid import, and the shrink-only policy for legacy Mermaid fences in docs.
Grammar card
Section titled “Grammar card”This is the exact text appended to the vox_validate_diagram tool description. A test fails if this copy and the shipped constant diverge.
VoxDiagram: graph or sequence. Put it in a vox-diagram fenced block. EVERY label is "double quoted".
graph { direction: right // down | right | up | left (optional, default down)
node client: "Desktop Client" node db: "Task DB" { shape: cylinder } // rect (default) rounded diamond cylinder start end
container backend: "Orchestrator" { // containers nest, max depth 4 node gateway: "Gateway" node engine: "Agent Runtime" gateway -> engine: "dispatch" // edge label optional engine -> engine: "retry" // self-loop ok }
client -> backend.gateway: "TLS" // dotted path reaches inside a container backend.engine -> db}
sequence { actor ui: "Desktop Client" actor api: "Gateway" ui -> api: "POST /run" note over api: "validates input" reply api -> ui: "202 Accepted" // reply is the dashed return}
Rules1. Declare every node: node id: "Label". An edge never creates a node.2. The arrow is exactly ->. Endpoints are nodes, never containers (use container.node).3. Ids: letters, digits, _ and -; start with a letter or _. Reserved: graph sequence direction node container shape4. One statement per line, one arrow per statement. Comments are // only. Escapes in labels: \" \\ \n.5. Sequences: declare every actor first. a -> b: "text" is a message, reply a -> b: "text" a dashed return, note over a[, b]: "text" a note.6. Limits: 64 KiB, 500 nodes, 1000 edges, depth 4, labels 200 chars. SVG render caps: 100 nodes+containers, 200 edges (sequence: 100 actors, 200 events).
Do NOT write Mermaid (each is an error): A[Label] --> B => node a: "Label" then a -> b subgraph X ... end => container x: "X" { ... } graph TD => graph { direction: down } flowchart LR => graph { direction: right } a -->|text| b => a -> b: "text" a -- text --> b => a -> b: "text" a & b --> c => a -> c and b -> c on two lines %% comment => // comment node a: "A"; => no semicolon style a fill:red => styling is not supported
Run the validator; apply every fixes[] entry; repeat until valid.Examples
Section titled “Examples”A graph with a nested container:
Diagram source
graph { direction: right
node client: "Desktop Client" node db: "Task DB" { shape: cylinder }
container backend: "Orchestrator" { node gateway: "Gateway" node engine: "Agent Runtime" gateway -> engine: "dispatch" }
client -> backend.gateway: "TLS" backend.engine -> db}A sequence with a note and a dashed reply:
Diagram source
sequence { actor ui: "Desktop Client" actor api: "Gateway" ui -> api: "POST /run" note over api: "validates input" reply api -> ui: "202 Accepted"}Layout stress examples
Section titled “Layout stress examples”These diagrams exercise long labels, every node shape, nested containers, non-ASCII text and a wide sequence, and the site’s browser tests check them in the docs-quality workflow. The width model is close for accented Latin and CJK text but under-measures arrows, symbols and emoji, so labels that use those may overflow their boxes.
Every node shape, with labels that wrap and edge labels near the wrap width:
Diagram source
graph { direction: down
node s: "Nightly ingest of the customer export files begins" { shape: start } node r: "Validate every incoming record against the published schema before staging it" node ro: "Rounded step that normalises addresses, phone numbers and names" { shape: rounded } node d: "Is the batch complete and consistent with yesterday?" { shape: diamond } node c: "Staging warehouse for the validated customer batches" { shape: cylinder } node e: "Report published to the operations channel" { shape: end }
s -> r: "files arrive on the shared drop location before six" r -> ro: "records that pass the schema check continue to cleaning" ro -> d: "cleaned batch handed over for the consistency review" d -> c: "complete and consistent, so the batch is loaded now" d -> r: "incomplete, so the batch is sent back for another pass" c -> e: "load finished, publish the summary for the morning shift"}Nested containers, one with a long title, and edges into nested nodes:
Diagram source
graph { direction: right
node user: "Browser"
container platform: "Platform services" { node edge: "Edge proxy" container core: "Core runtime" { node api: "Public API" container store: "Persistence layer" { node log: "Write-ahead log" node tables: "Task tables" } } } container ops: "Operations and observability tooling for the whole production fleet, including the on-call dashboards" { node metrics: "Metrics" }
user -> platform.edge: "HTTPS" platform.edge -> platform.core.api: "routes the request" platform.core.api -> platform.core.store.log: "append" platform.core.store.log -> platform.core.store.tables: "apply" platform.core.api -> ops.metrics: "emit"}Accented Latin and CJK labels:
Diagram source
graph { direction: right
node a: "Café Ünïcode" node b: "注文を確認します" node c: "Élève à l'école, naïve résumé" node d: "受け取りました、ありがとうございます"
a -> b: "Zürich, São Paulo, Kraków" b -> c: "確認が終わったら次に進みます" c -> d: "très bien, merci beaucoup"}A wide sequence with multi-hop messages, a long note, a reply and a self-message:
Diagram source
sequence { actor client: "Client" actor gateway: "Gateway" actor billing: "Billing" actor stock: "Stock" actor mail: "Mailer"
client -> gateway: "Place the order and pay with the saved card" client -> stock: "Cancel the reservation as soon as the payment fails three times in a row" gateway -> mail: "Send the receipt once the whole basket is confirmed and paid for" note over gateway, stock: "The gateway keeps the reservation alive for the whole fifteen minute checkout window, then releases every line item back to the shared pool" billing -> billing: "retry the charge with exponential backoff" reply stock -> client: "reservation released"}Validate from an agent: vox_validate_diagram
Section titled “Validate from an agent: vox_validate_diagram”vox_validate_diagram checks one diagram and returns everything needed to repair it in one round trip. It is deterministic, makes no model calls, and writes nothing. It is in the lang capability profile and is eligible for the read role.
Input:
| Field | Type | Meaning |
|---|---|---|
source | string, required | The diagram text, or one Mermaid block when from_mermaid is true. |
from_mermaid | bool, default false | Convert the Mermaid block first, then validate the conversion. |
emit | "svg" or "mermaid", optional | Also render a valid diagram. |
Unknown fields are refused.
Output (schema_version is always 1):
| Field | Present | Meaning |
|---|---|---|
valid | always | No error diagnostic remains (and, for Mermaid input, the import was complete). |
diagnostics | always | Every diagnostic in source order: code, severity, message, span (line and column), and fixes[], which is omitted when no mechanical fix exists. |
normalized_source | always | The canonical fmt text once valid; null until then. |
stats | always | nodes, containers, edges, max_depth. |
emitted | when emit was requested and the diagram is valid | The SVG or Mermaid text. |
emit_error | when an SVG could not be rendered | Why (for example a render cap). Never silently dropped. |
unsupported | when from_mermaid | Mermaid lines the importer refused, as {line, construct}; empty when the import was complete. |
dropped | when non-empty | Mermaid lines deliberately dropped (styling). |
After a Mermaid import, diagnostic spans point into normalized_source, the converted text, not into the original Mermaid.
Repair loop: apply every fixes[] entry (each is a byte span plus a replacement in the text you sent), call the tool again, and stop when valid is true. Use normalized_source as the final text. From a shell, use vox --json diagram check to get fixes[].
The tool’s response wraps the table above as {"success": true, "data": {...}}; invalid arguments return {"success": false, "error": "..."} instead of data.
CLI: vox diagram
Section titled “CLI: vox diagram”The command reads a file or stdin (-). See cli.md for the full command surface.
| Subcommand | Role |
|---|---|
vox diagram check <file|-> | Print each diagnostic as file:line:col: error|warning: code: message on stderr. Fix spans are only in the --json envelope. |
vox diagram fmt <file|-> [--check | --write] | Print the canonical form; --check exits 1 when not canonical; --write rewrites atomically (exclusive temp file and rename, source mode kept), only for a valid source. It writes through a symlink to the real file and refuses a read-only file (exit 2). |
vox diagram render <file|-> [-o FILE] | Deterministic SVG. -o is written the same atomic way and may not be the input file (exit 2). |
vox diagram export <file|-> [-o FILE] | Mermaid text. |
vox diagram import <file|-> [-o FILE] | One Mermaid block to canonical VoxDiagram. |
Exit codes: 0 valid (warnings allowed), 1 at least one error, non-canonical fmt --check, unsupported import lines or a failed render, 2 usage or I/O error. The global --json prints the bare envelope, which is the MCP tool’s data field.
vox diagram check diagram.txtvox diagram fmt --check - < diagram.txtvox --json diagram check diagram.txtImporting Mermaid
Section titled “Importing Mermaid”Import is fail-closed. It converts a block only when every line maps onto VoxDiagram; otherwise source stays empty and unsupported lists each refused line with its 1-based line number and a short construct name. Lines that carry only styling are listed in dropped and do not block the import. Run vox diagram import <file|-> for a human-readable list, or call the tool with from_mermaid: true.
Legacy Mermaid fences
Section titled “Legacy Mermaid fences”Existing fences with the mermaid info string under docs/src are recorded per file in contracts/documentation/legacy-mermaid-baseline.v1.yaml. The baseline only shrinks: the doc lint rejects a new Mermaid fence. New diagrams use a vox-diagram fence, which the doc lint validates with the same checker as this tool. VoxDiagram has no Gantt kind, so Gantt charts stay Mermaid. Since the Phase 90 migration the baseline allows exactly one fence, the Gantt chart in the deep-research full-surface audit, kept because VoxDiagram has no Gantt kind.