# DGMO Language Specification > **Authoritative reference** for the DGMO diagram language. This document describes what is valid syntax. If it is not in this document, it is not valid DGMO. > **1.0 freeze:** DGMO syntax freezes at the **1.0 release tag** — after 1.0, any change to valid syntax is a major-version bump. Pre-1.0 the language was deliberately tightened toward one canonical form per construct; the last cleanups shipped through 0.44 (channel-named value ramps in 0.42, chart-type consolidation 50→44 + alias removal in 0.43 per #23–28, `chord` folded into `arc` + `layout chord` in 0.44). Retired legacy/ambiguous forms include: bare `description`, PERT `analysis`, C4 bare same-line tail, timeline positional duration (→ `duration:`), gantt legacy scheduling, and the standalone `chord` / chart-type-alias keywords; bare tag values now auto-assign a palette color. Each removed form **fails loudly** — a generic parse error, or a targeted hint where one survives (e.g. a C4 bare tail names the `description:` fix and the diagram does not render). See `dgmo-language-spec-decisions.md` § "1.0 Freeze Decisions". > > **Diagnostic codes (0.43.0, decision #28):** the dedicated `E_*_REMOVED` family was **deleted** for minimal surface — removed forms fail however they naturally fail, not via a named code. Treat every `E_*_REMOVED` reference below as **historical**; re-verify behavior against the parser. Note two forms are deliberately **accepted, not errors**: data-row numeric values may use thousands-grouping commas (`1,000` → 1000) or underscores (`1_000` → 1000), and separator commas between values are tolerated (values are canonically space-separated). _(2026-07-05: the C4 bare-tail case, which regressed to a silent drop after #28, was restored to a hard error — see §"C4".)_ ## Table of Contents 1. [Universal Constructs](#1-universal-constructs) 2. [Universal Name Handling](#2-universal-name-handling) 3. [Sequence Diagrams](#3-sequence-diagrams) 4. [Infrastructure Diagrams](#4-infrastructure-diagrams) 5. [Flowchart Diagrams](#5-flowchart-diagrams) 6. [State Diagrams](#6-state-diagrams) 7. [Org Charts](#7-org-charts) 8. [C4 Architecture Diagrams](#8-c4-architecture-diagrams) 9. [Entity-Relationship Diagrams](#9-entity-relationship-diagrams) 10. [Class Diagrams](#10-class-diagrams) 11. [Kanban Boards](#11-kanban-boards) 12. [Sitemap Diagrams](#12-sitemap-diagrams) 13. [Gantt Charts](#13-gantt-charts) 14. [Boxes and Lines Diagrams](#14-boxes-and-lines-diagrams) 15. [Timeline Diagrams](#15-timeline-diagrams) 16. [Data Charts](#16-data-charts) 17. [Visualizations](#17-visualizations) 18. [Mindmap Diagrams](#18-mindmap-diagrams) 19. [Wireframe Diagrams](#19-wireframe-diagrams) 20. [Tech Radar Diagrams](#20-tech-radar-diagrams) 21. [Cycle Diagrams](#21-cycle-diagrams) 22. [Journey Map Diagrams](#22-journey-map-diagrams) 23. [Pyramid Diagrams](#23-pyramid-diagrams) 24. [Ring Diagrams](#24-ring-diagrams) - [RACI Matrices](#24a-raci-matrices-raci--rasci--daci) - [Map Diagrams](#map-diagrams) 25. [Colon Usage Summary](#25-colon-usage-summary) 26. [Authoring Rules (Generators Read This First)](#authoring-rules) 27. [Swimlane Diagrams](#27-swimlane-diagrams) 28. [Event Line Diagrams](#28-event-line-diagrams) 29. [Version-Control Diagrams](#29-version-control-diagrams) 30. [Block Diagrams](#30-block-diagrams) 31. [Sketch Diagrams](#31-sketch-diagrams) 32. [Family Diagrams](#32-family-diagrams) --- ## 1. Universal Constructs These patterns are shared across all or most diagram types. ### 1.1 Chart Type Declaration (First Line) ``` [Title] ``` - Space-separated, NO colon - Title is optional - Examples: `bar Treasure Hauls`, `sequence Auth Flow`, `gantt Product Launch` ### 1.2 Comments ``` // comment text ``` - Full-line only (no inline comments after code) - `#` is NOT a comment character - One documented carve-out: **PERT (§13A)** accepts both `#` and `//`, full-line and trailing-after-whitespace — the only chart type with inline comments. Everywhere else `#` is content. ### 1.3 Tag Declarations ``` tag GroupName as Value1 color Value2 color ``` - `tag` keyword, NO colon - **Name**: a single identifier (`[A-Za-z_][A-Za-z0-9_-]*` — letters, digits, underscore, hyphen), e.g. `Team`, `Trust-Zone`. For a multi-word display name with **spaces**, quote it: `tag "Trust Zone" as tz`. The quoted text becomes the legend label; internally it slugs to a DOM-safe key (`Trust Zone` → `trust-zone`) used for assignment, matching, and `active-tag`. Always pair a quoted name with an `as ` so values stay terse (`Node tz: client`). An *unquoted* multi-word name is ambiguous (the trailing word is read as an alias) and is not a spaced name. - Alias: optional postfix `as ` per §2A (universal alias syntax — `[A-Za-z][A-Za-z0-9_]{0,11}`) - Inline values also supported: `tag Priority as p Low green, High red` - Color follows the value as a bare trailing token (see §1.5). Capitalize the color word (`Red`, `Yellow`) to keep it as a literal value with no color. - **Color is optional — a bare value auto-assigns a palette color.** A value declared without an explicit color (`High`, not `High red`) is not an error: at parse time it is given a deterministic categorical color drawn from the recognized color names (`red`, `orange`, `yellow`, `green`, `blue`, `purple`, `teal`, `cyan` — neutrals excluded), cycling in that order. Auto-assignment **skips any color already used by an explicit entry in the same group** (including explicit entries that appear *after* the bare one), so `High` / `Low red` gives `High` a non-red color. An explicit `Value color` always wins. This lets you sketch a group as a plain value list (`tag Priority as p High, Med, Low`) and let the palette pick distinct colors. This is the language's single canonical categorical rotation — chart sections that rotate colors (e.g. ring layers, §24.6) cite this order; pyramid's primary-mix ramp (§23.6) is the one documented exception. - **First entry is the default** — applied to nodes that don't carry an explicit value for this group. Reorder the entries to change the default. Three charts opt out of first-value defaulting: **sketch** and **body** render untagged shapes neutral gray (meaning is absent until assigned — decision #33), and **swimlane** untagged nodes fall back to the lane shade (tag → symbol → lane cascade, §27.7) - Must appear before diagram content - `none` is reserved — cannot be used as a tag group name - **The first declared group is active by default** — declaring a tag group colours nodes by that group immediately; the legend renders it expanded/active with the other groups as collapsed pills. `active-tag ` is therefore **only needed when ≥2 colouring dimensions exist** (multiple tag groups, or a group plus a score ramp) and you want one *other than the first-declared* active. With a single tag group, `active-tag` is redundant. In the app a reader can click any legend pill to flip the active group (live preview only — it does not edit the source) - **Opt-out**: `active-tag none` **suppresses all tag colouring** — every node renders neutral and all groups stay collapsed. This is the *opposite* of omitting `active-tag` (which activates the first group); use it to author the quiet, no-colour default deliberately (case-insensitive: `none`, `None`, `NONE`) - **Legacy bare shorthand** (`tag Priority p`) and **explicit `alias` keyword** (`tag Priority alias p`) emit `E_TAG_SHORTHAND_REMOVED` per TD-18 **Diagram types that support tags**: sequence, infra, state, org, mindmap, c4, er, kanban, gantt, sitemap, timeline, boxes-and-lines, swimlane, pert, journey-map, treemap, map, event-line, block, sketch, family, body, bracket > This list is enforced, not hand-maintained: it must match `TAG_SUPPORTING_TYPES` in `dgmo/src/completion.ts` (derived from `STRUCTURAL_KEYWORDS`, the parser-validated source of truth) and is checked by the completion-conformance suite. #### Strict ordering: tag declarations must precede content Tag declarations (and their aliases) feed the chart's reserved-key registry (§1.4.3) — that registry must be **finalized before the parser enters content-line mode**. DGMO is a single-pass parser with no lookahead, so a tag alias declared after the first content line is unreachable: any earlier line that *could* have used the alias was already classified under the pre-declaration registry. A `tag` block appearing after the first non-tag content line emits `E_TAG_DECLARED_AFTER_CONTENT`. The declaration is still parsed (so the legend renders), but no upstream content lines retroactively pick up the alias. ``` sequence Voyage tag Concern as c // ✅ before content Alice -login-> Bob c: Auth [Backend] tag Concern as c // ❌ E_TAG_DECLARED_AFTER_CONTENT ``` This applies uniformly across all chart types that support tags. ### 1.4 Metadata Grammar DGMO has **one** metadata grammar that every chart type uses. Entity metadata may be written same-line after the name, or as indented lines beneath the entity. Both forms are valid wherever metadata is supported; per-chart-type preferred form is listed in §26. ``` // Same-line form (canonical for most chart types) EntityName k: v, k2: v2 API Gateway c: Auth // name = "API Gateway"; meta = { c: Auth } Alice -login-> Bob c: Auth // edge metadata, no delimiter 20bd Database Schema progress: 100 // gantt: leading-duration prefix preserved // Indented form (canonical for org, c4; available everywhere) API Gateway c: Auth // attribute — `c` is a declared tag alias description: Main gateway ``` The `|` operator that delimited metadata in pre-0.18.0 DGMO is **removed** as a metadata delimiter. Before 0.43.0 a stray `|` emitted `E_PIPE_OPERATOR_REMOVED`; as of 0.43.0 (#28) that guard is gone, so a `|` outside its surviving uses (§1.4.5) is no longer a dedicated error — it falls through to normal parsing (treated as a literal / ignored). #### 1.4.1 Same-line form The parser scans the line left-to-right and flips into **metadata mode** the first time it encounters a whitespace-delimited token whose shape is `:` (a key followed by a colon, with the key matching the chart type's reserved-key registry or a declared tag alias). Everything to the left of that token is the entity's **name region**; everything to the right is comma-separated `key: value` pairs. ``` Foo k: v // name = "Foo", meta = { k: v } Foo k: v, k2: v2 // name = "Foo", meta = { k: v, k2: v2 } Foo green k: v // name = "Foo", color = green, meta = { k: v } (color peeled from name region — §1.5) Foo as f k: v // name = "Foo", alias = f, meta = { k: v } ``` **Whitespace around `:` is tolerated** — tokenization treats `c: Auth`, `c :Auth`, `c : Auth`, and `c:Auth` identically. The value start is after `:`, with leading whitespace trimmed. **Quoted values escape the comma terminator.** Wrap a value in `"..."` when it contains commas. ``` Foo description: "My, complicated, value" // meta = { description: "My, complicated, value" } Foo description: "with spaces", color: red // meta = { description: "with spaces", color: red } ``` **No line continuation.** Same-line metadata is single-line. A trailing comma on a metadata line is a parse error. For long metadata sets, use the indented form. **Empty values are dropped.** `Foo c:` (no value) emits `W_EMPTY_METADATA_VALUE` and the key/value pair is discarded from the entity's metadata. **Quoted names tokenize as one unit before the first-`:`-token scan.** A colon inside a quoted name does not trigger the metadata cut. ``` "Auth: Service" // name = "Auth: Service" (single token) "Order | Items" k: v // name = "Order | Items", meta = { k: v } ``` #### 1.4.2 Indented form (reserved-key dispatch) An indented line of shape `key: value` attaches as metadata to the parent entity **only when `key` matches the chart type's reserved-key registry or a declared tag alias**. Otherwise, the line falls through to the chart type's own grammar (class members, RACI role assignments, ER columns, function entries, sequence notes, etc.). ``` API Gateway c: Auth // attribute — `c` is a declared tag alias latency-ms: 50 // known-schema property (colon required — see §4.3) Ship // class + name: string // member — `+` prefix; not a reserved-key match description: Capital ship // attribute — `description` is reserved [Departure] // RACI phase Plot the course // task Cap: A // role assignment — `Cap` is a declared role Nav: R ``` Indented lines without a colon remain **structural children** (sub-nodes, columns, dependencies, cards, etc.). ER columns (`id int pk`) are space-separated known-schema properties that don't carry colons; the dispatch is unambiguous. Infra node properties require colons (`latency-ms: 50`) — see §4.3. **Drift warning.** When an indented `key: value` line where `key` is in the reserved-key registry appears at the same indent level as preceding structural children, the parser emits `W_ATTRIBUTE_AT_PARENT_INDENT` — the attribute attaches to the parent above, but a reader could plausibly intend it to attach to the preceding structural child. The hint: indent further if you meant it on the preceding child. #### 1.4.3 Reserved-key registry Each chart type publishes a closed set of reserved attribute keys. These keys, plus any declared tag aliases (§1.3), trigger metadata dispatch on indented lines and the same-line first-`:`-token cut. Everything else falls through to chart-specific grammar. | Chart type | Reserved keys (in addition to declared tag aliases) | | --- | --- | | sequence | `color`, `description`, `role`, `collapsed`, `position` | | infra | `color`, `description`, `collapsed`, `icon` | | flowchart | *(no metadata)* | | state | *(tag-group aliases — tags newly granted, decision #48)* | | org | `color`, `description`, `role`, `location`, `email`, `phone`, plus per-chart custom keys (free-form indented attributes) | | c4 | `color`, `description`, `tech`, `type`, `collapsed` | | er | `color`, `description`, `domain` | | class | `color`, `description` | | kanban | `color`, `description`, `wip`, `assignee`, `due`, `priority` | | sitemap | `color`, `description`, `status` | | gantt | `color`, `description`, `duration`, `offset`, `progress`, `start` | | pert | `color`, `description`, `confidence`, `collapsed` | | boxes-and-lines | `color`, `description`, `heat` | | timeline | `color`, `description`, `duration` | | mindmap | `color`, `description`, `collapsed`, plus per-chart priorities/status keys declared as tag aliases | | tech-radar | `color`, `description`, `quadrant`, `ring`, `trend` | | cycle | `color`, `span`, `description` (`width` is edge-only) | | journey-map | `score`, `emotion`, `description`, `pain`, `opportunity`, `thought`, `color` (decision #13, persona long form) | | pyramid | `color`, `description` | | ring | `color`, `description` | | raci | `color`, `description`, plus declared roles (which dispatch as role assignments, not attributes) | | wireframe | *(uses trailing-keyword flag list — see §19.5)* | | treemap | `heat`, plus tag-group aliases | | swimlane | *(tag-group aliases; `note:` deferred)* | | event-line | *(tag aliases; `collapsed` via era lines)* | | version-control | `id`, `tag`, `type`, `order` | | block | `span` | | sketch | `shape`, `at` | | family | closed GEDCOM set — see §32.6 | | body | *(tag aliases)* | | bracket | *(tag aliases; seed/score keys per §35)* | | map | registry in §24B.9 (`heat`, `size`, `width`, `label`, `style`, `clock`) | | goal | *(no metadata)* | | countdown | *(no metadata)* | | clock | *(no metadata)* | Keys not in a chart type's registry are not metadata. Tag aliases declared via `tag Group as x` are added to that chart's effective registry for the duration of the parse. #### 1.4.4 Diagnostic catalog > The `*_REMOVED` rows below were **deleted in 0.43.0 (#28)** and no longer fire — see the 0.43.0 update note at the top of this spec. They are kept here, marked, for historical reference. | Code | Severity | Fires when | | --- | --- | --- | | ~~`E_PIPE_OPERATOR_REMOVED`~~ | *removed 0.43.0* | (Historical) A `|` appeared outside its surviving uses (wireframe option braces `{A | B}`, in-arrow label characters per §1.10, quoted-string content). Now: a stray `|` falls through to normal parsing (literal / ignored), no dedicated error. | | `W_EMPTY_METADATA_VALUE` | warning | A metadata pair has an empty value (`Foo c:`). The pair is dropped from the entity's metadata. | | `W_ATTRIBUTE_AT_PARENT_INDENT` | warning | An indented attribute line (`key: value` where `key` is reserved) appears at the same indent level as preceding structural children, ambiguating which entity it attaches to. | | ~~`E_SEQUENCE_BARE_POSITION_REMOVED`~~ | *removed 0.43.0* | (Historical) A sequence participant used the retired bare-keyword `position N` ordering shorthand; use colon-keyed `position: N` (§2.2). Now: no dedicated error — falls through to normal parsing. | | ~~`E_DATA_COMMA_REMOVED`~~ | *removed 0.43.0* | (Historical) The short-lived 0.30.0 freeze hard-errored commas in data-chart VALUE position. Reversed by #28: commas are now **accepted** — thousands grouping (`Revenue 1,000` → 1000) and separator commas (`Q1 400, 700`) both parse. Values are canonically space-separated; `1_000` also works. Not an error. | #### 1.4.5 Surviving uses of `|` The `|` character is **not reserved** in DGMO 0.18.0; it survives as a literal in three specific contexts: - **Wireframe dropdown options:** `{Option A | Option B | Option C}` — inside `{...}` braces, `|` separates dropdown options (§19.3). - **In-arrow label characters:** `A -file|name-> B` — `|` inside an arrow label is preserved verbatim (§1.10). - **Quoted name characters:** `"Order | Items"` — inside `"..."` quoted strings, `|` is a literal name character. Outside these three contexts, `|` used to trigger `E_PIPE_OPERATOR_REMOVED`; as of 0.43.0 (#28) that guard is gone, so a stray `|` is simply parsed as a literal / ignored rather than flagged. `dgmo migrate` still mechanically converts legacy pipe content. ### 1.5 Color Suffixes Color is set by typing the color name at the end of a label, lowercase. Example: `Done green` colors Done green. Eleven colors exist: `red`, `orange`, `yellow`, `green`, `blue`, `purple`, `teal`, `cyan`, `gray`, `black`, `white`. To use a color word as a literal label, capitalize it: `Red` stays as the word `Red`. > **These eleven names are the ONLY valid colors. Hex (`#e6194b`), `rgb(...)`/`hsl(...)`, and CSS keyword colors (`crimson`, `pink`, …) are NOT supported anywhere in DGMO** — not on groups, series, nodes, edges, tags, or any other element. A hex or CSS value is rejected with an **error** diagnostic and the element falls back to its default / auto-assigned palette color. The only way to specify a color is one of the eleven names above. This is deliberate: a named color resolves to the right hex per active palette and light/dark theme, so the same source stays palette-portable — a hardcoded hex would not. ``` Label color // bare trailing color token Done green // value=Done, color=green Senior Engineer red // value="Senior Engineer", color=red Red // value=Red, no color (capitalized → escape hatch) ``` **The universal rule** — color trails the label: > Color is the trailing whitespace-delimited token of a label region, when that token (case-sensitive, lowercase) is one of the 11 names above. Otherwise the label region has no color. The "label region" is everything left after the parser strips off structural terminators it owns: same-line metadata (§1.4.1), `as ` postfix, numeric values, date ranges, structural brackets, arrow constructs. The color rule operates only on the label region — never on a raw line — so `Tortuga Distillery orange 3000` is `{ label: "Tortuga Distillery", color: "orange", value: 3000 }`: the parser splits off `3000` as the flow value first, then `orange` peels off as the trailing color. **Cut order is pinned.** On any line that could carry all of (a) metadata, (b) an `as ` postfix, and (c) a trailing-token color, the parser strips them in this order: 1. **Metadata cut (§1.4.1)** — the first `:` token where `key` is in the chart's reserved-key registry or a declared tag alias splits the line into name-region + metadata (left-to-right). Color tokens that appear *inside* a metadata value are NOT extracted as the entity's color — they belong to the value. 2. **Trailing-token color (§1.5)** — applied to the name region. The last whitespace-delimited token, when it is exactly a lowercase color name from the 11-name palette, is peeled as color. 3. **`as ` postfix** — stripped from the right-hand side of what remains of the name region. The alias is registered against the canonical name. Step 2 runs before step 3 because in source order the alias sits *between* the label and the trailing color (`