Skip to content

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.

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
}
Rules
1. 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 shape
4. 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.

A graph with a nested container:

Examples, diagram 14 nodes, 3 edges: Gateway → Agent Runtime; Desktop Client → Gateway; Agent Runtime → Task DBOrchestratorDesktop ClientTask DBGatewayAgent RuntimedispatchTLS
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:

Examples, diagram 22 actors, 2 messages, 1 note: Desktop Client → Gateway; Gateway → Desktop ClientDesktop ClientGatewayvalidates inputPOST /run202 Accepted
Diagram source
sequence {
actor ui: "Desktop Client"
actor api: "Gateway"
ui -> api: "POST /run"
note over api: "validates input"
reply api -> ui: "202 Accepted"
}

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:

Layout stress examples, diagram 36 nodes, 6 edges: Nightly ingest of the c… → Validate every incoming…; Validate every incoming… → Rounded step that norma…; Rounded step that norma… → Is the batch complete a…; Is the batch complete a… → Staging warehouse for t…; Is the batch complete a… → Validate every incoming…; Staging warehous…Nightly ingest of thecustomer export files beginsValidate every incomingrecord against the publishedschema before staging itRounded step that normalisesaddresses, phone numbers andnamesIs the batch complete andconsistent with yesterday?Staging warehouse for thevalidated customer batchesReport published to theoperations channelfiles arrive on the shareddrop location before sixrecords that pass the schemacheck continue to cleaningcleaned batch handed overfor the consistency reviewcomplete and consistent, sothe batch is loaded nowincomplete, so the batch issent back for another passload finished, publish thesummary for the morningshift
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:

Layout stress examples, diagram 46 nodes, 5 edges: Browser → Edge proxy; Edge proxy → Public API; Public API → Write-ahead log; Write-ahead log → Task tables; Public API → MetricsPlatform servicesOperations and observabilitytooling for the wholeproduction fleet, includingthe on-call dashboardsCore runtimePersistence layerBrowserEdge proxyPublic APIWrite-ahead logTask tablesMetricsHTTPSroutes the requestappendapplyemit
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:

Layout stress examples, diagram 54 nodes, 3 edges: Café Ünïcode → 注文を確認します; 注文を確認します → Élève à l'école, naïve …; Élève à l'école, naïve … → 受け取りました、ありがとうございますCafé Ünïcode注文を確認しますÉlève à l'école, naïverésumé受け取りました、ありがとうございますZürich, São Paulo, Kraków確認が終わったら次に進みますtrès bien, merci beaucoup
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:

Layout stress examples, diagram 65 actors, 5 messages, 1 note: Client → Gateway; Client → Stock; Gateway → Mailer; Billing → Billing; Stock → ClientClientGatewayBillingStockMailerThe gateway keeps the reservation alive for the wholefifteen minute checkout window, then releases every lineitem back to the shared poolPlace the order and pay with the saved cardCancel the reservation as soon as the payment fails threetimes in a rowSend the receipt once the whole basket is confirmed and paidforretry the charge with exponential backoffreservation released
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:

FieldTypeMeaning
sourcestring, requiredThe diagram text, or one Mermaid block when from_mermaid is true.
from_mermaidbool, default falseConvert the Mermaid block first, then validate the conversion.
emit"svg" or "mermaid", optionalAlso render a valid diagram.

Unknown fields are refused.

Output (schema_version is always 1):

FieldPresentMeaning
validalwaysNo error diagnostic remains (and, for Mermaid input, the import was complete).
diagnosticsalwaysEvery diagnostic in source order: code, severity, message, span (line and column), and fixes[], which is omitted when no mechanical fix exists.
normalized_sourcealwaysThe canonical fmt text once valid; null until then.
statsalwaysnodes, containers, edges, max_depth.
emittedwhen emit was requested and the diagram is validThe SVG or Mermaid text.
emit_errorwhen an SVG could not be renderedWhy (for example a render cap). Never silently dropped.
unsupportedwhen from_mermaidMermaid lines the importer refused, as {line, construct}; empty when the import was complete.
droppedwhen non-emptyMermaid 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.

The command reads a file or stdin (-). See cli.md for the full command surface.

SubcommandRole
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.txt
vox diagram fmt --check - < diagram.txt
vox --json diagram check diagram.txt

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.

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.