Board DSL — Syntax Reference
The Board DSL describes a Flight-Levels Board: the columns a work item
flows through, the lanes it flows in, and the groups that band columns together.
A board carries header metadata (its title:, flight level, timebox, working
agreements:, interactions:, descriptive tags:), a columns: structure,
and an optional lanes: / groups: structure.
The title: + tags: half of that header is shared with the flight-route and
topology DSLs — same keys, same meaning, same shape in all three. See
The document header.
The DSL is plain YAML. dwsd-board is the VS Code language id for
standalone .dwsd-board files. In Markdown, the fence tag that the extension’s
preview renders live is dwsd.board (the dot form):
```dwsd.boardtitle: Team Boardcolumns: - ToDo - Doing - Done```In plain Markdown / GitHub the same example reads as a YAML document either way.
Live example · Charger ART Board — Voltfeld example →
title: Charger ART flightlevel: FL2 timebox: 10w agreements: - "Features enter the PI at PI planning; mid-PI changes need the Business Owners" lanes: - name: Feature groups: - name: Preparation columns: - name: Program Backlog status-category: to-do - name: Refined status-category: to-do check: "Acceptance criteria, team assignment and platform dependencies named" - name: In PI interactions: - "[[ART Sync]]" columns: - name: Implementing wip: 8 status-category: in-progress - name: Integrating status-category: in-progress agreements: - "Integration dates are set at PI planning; a slipped date is raised at the ART Sync, not discovered on the rig" - name: Release interactions: - "[[System Demo]]" columns: - name: Demoed status-category: assess - name: Certified status-category: waiting - name: Released status-category: done - name: Field Issue type: expedite columns: - name: Triage status-category: assess - name: Fix-in-Progress wip: 2 status-category: in-progress agreements: - "One hotfix in progress per team; a second one waits or becomes a feature" - name: Hotfix-Released status-category: done
Fence-tag caveat — the examples on the topic pages use
dwsd-board. Syntax highlighting accepts four spellings (wsd-board,dwsd-board,wsd.board,dwsd.board— the markdown-injection grammar), but the preview extractor currently accepts onlydwsd.board. So a```dwsd-boardfence highlights but does not render. The topology DSL already resolved this asymmetry by accepting all four spellings in both places; board and route have not yet. Until they do, use the dot form for a fence you want to see rendered.
The parser is tolerant: it never throws on author input. Unknown or
malformed fields produce a board.* diagnostic (a warning or info) and the
offending value is clamped to a default or skipped — the rest of the document
still parses. The diagnostic codes are listed per field on each topic page and
summarized under Diagnostics (board.*) below.
Note on field names that do not exist. The Board DSL has no inline sugar — there is no
#statustag-suffix, no[wip]/[2..4]bracket modifier, and no bare-modifier line. Every modifier is a spelled-out YAML key (status-category:,tags:,wip:,aging:,max-returns:,agreements:,interactions:,split:).The following are not keys — do not write them:
policy:/refs:— working agreements (both linked entities and inline rules) are the singleagreements:key.labels:— descriptive chips aretags:.focus-fit:(aliasfocus_fit) — the header carries no FIT-type label.statuses:— there is nostatuses:Jira-status key (no Jira coupling is surfaced).board:— retired. The document title istitle:(see The document header). Unlike the other entries in this list,board:is not silently ignored: it is a hardboard.retired-keyerror that names its own fix.The tolerant parser silently ignores any non-key (no diagnostic, no field), so an example that writes one would parse “clean” while dropping the value — which is exactly why
board:was given a real error instead of joining that list. Two repo gates hold the line on the examples below:pnpm check:dsl-corpus-sweepparses every tracked unit and live fence and requires 0 diagnostics, andpnpm check:retired-key-auditfails on any surviving retired spelling.
Topic pages
The reference is split across per-topic pages:
- The document header —
title:+tags:, the header shared by all three DSLs, the missing-value convention, and the retired keys. - Columns — columns + modifiers (
wip,aging,max-returns,status-categorytint,tagschips) and the status-tint palette. - Splits — a column’s
split:block and per-section modifiers. - Lanes — lanes + lane modifiers and lane-local structure.
- Groups — group blocks, the three-scope
groups:semantics, and depth bands. - Anchors, agreements & interactions — the locator, the grouped bucket + shared drawer + disclosure tiers, inline markdown-lite, and overflow affordances.
- Examples — full worked
dwsd-boardexamples.
Design notes — YAML, humans, and machines
The Board DSL is YAML by design. Every example in this reference is also a
valid YAML document — block style (columns: as an indented list) and flow
style ({ name: doing, status-category: waiting }) both parse — so any YAML-aware
editor, linter, or formatter can read and write it. The package ships its own
tolerant parser (src/board/parser.ts) that accepts the same shape, modeled
verbatim on the topology parser (src/topology/parser.ts).
The syntax optimizes for readability and round-trippability, not brevity.
Field names are spelled out (max-returns, flightlevel — not abbreviations),
values are explicit, and the parser never throws: unknown or malformed input
yields a board.* diagnostic and a sensible default rather than a hard error.
That verbosity is deliberate — the format is meant to be read and written by
humans, but above all by machines.
Increasingly, that machine is an AI. The raw DSL is a good target for generation
and repair by coding agents: the YAML shape, the closed and documented set of
fields, and the per-field diagnostics give an agent everything it needs to
author or fix a board without a UI. The board format deliberately dropped its
former inline string-sugar (#status, [wip]) precisely because a mini-grammar
hidden inside a string is the one thing YAML removes — a closed, explicit
schema is validatable, autocompletable, and agent-repairable; string-sugar is
not. The live fence preview is then the way a person checks what the machine
produced.
Board header fields
Every field below is implemented by the parser (src/board/parser.ts,
normalizeBoardAst) and produces the frozen BoardAst contract
(src/board/types.ts). The Diagnostic column names the board.* code an
author hits on misuse.
| Field | Values / shape | Default | Diagnostic on misuse |
|---|---|---|---|
title | string (the board name) | (none — omitted when absent) | (none — a non-string or empty value is silently ignored) |
flightlevel | FL1 | FL2 | FL3 | (none) | board.invalid-flightlevel (warning; the value is omitted) |
timebox | string (e.g. 5d, 12w) | (none) | (none — a non-string or empty value is ignored) |
aging | string (board-level aging duration, e.g. 3d) | (none) | (none — non-string ignored) |
tags | string list (descriptive category chips, rendered visibly; a scalar string is accepted as a one-element list) | (none) | tolerant — non-string entries dropped, empties trimmed |
agreements | string OR string list — working agreements (a [[wikilink]] entry = a linked entity; a plain string = an inline working agreement) | (none) | tolerant — non-string entries dropped |
interactions | string OR string list — meeting / interaction pointers | (none) | tolerant — non-string entries dropped |
columns | list of column entries (see Columns) | [] | per-entry board.invalid-column |
lanes | list of lane entries (see Lanes) | [] | per-entry board.invalid-lane |
groups | list of board-level group blocks (see Groups) | (none) | per-entry board.invalid-group |
title is the optional board name — the header text. It is the same key
the flight-route and topology DSLs use; when it is absent the field is
omitted from the AST (never ''). The type-named board: key it replaced is
retired and now errors — see The document header.
flightlevel places the board in its band; only the canonical
FL1/FL2/FL3 strings are accepted (anything else is dropped with
board.invalid-flightlevel). tags are descriptive category chips,
rendered visibly (with … ellipsis overflow when they do not fit) — the same
cross-DSL convention the topology DSL uses for tags:. They carry no color
tint (the tint is the separate status-category: concern, which is not a
board-level key). agreements carry the working agreements: a [[Target]] /
[[Target|Alias]] wikilink entry points at a linked entity (kept verbatim;
bare-target extraction is a render/resolve concern), while a plain string is an
inline working agreement. interactions point at meetings / interactions.
Both render as a single grouped chip → shared drawer (see
Anchors, agreements & interactions).
Themes
The theme is selected via data-theme on the board root; theme CSS travels as
TS-string exports (board-theme-css.ts).
| Theme id | Aesthetic | Character |
|---|---|---|
jira-default | Atlassian / Jira | white background, the 5-color status tints (see Status tint), blue chips, uppercase column names |
One theme ships today. A second one — a training-slide aesthetic from the board’s earliest days — was removed rather than parked: it had been written against a much smaller board and had no rules for most of what a board renders now, so selecting it produced a largely unstyled board. A workspace still configured to it falls back to
jira-default; nothing errors and no authored file is affected, because the theme never touched authored content. The theme mechanism is unchanged and a second theme can be added again.
Zoom
A board zooms like the other diagrams. Nothing in the board text controls it; it is part of how a board is shown.
- A board is laid out at its natural size. Every column has a minimum readable width (220 px by default), so a board with many columns is wide rather than squeezed: its cards keep their size in a narrow panel and you scroll, instead of reading text that has been crushed.
- A board opens at Fit. Fit shows the visible board in the available space, and a board starts out that way. A collapsed lane counts with its header row only, so collapsing lanes and pressing Fit again makes the rest larger. Fit may enlarge a small board, but never beyond 1.25 times its natural size, the same limit as every other diagram.
- + and − scale the board as a picture. + and − enlarge and shrink it step by step. The columns keep their proportions at every size. Collapsing or expanding a lane keeps the current zoom; press Fit to fit the new height.
- The header stays the same size. The title and the chips above the board do not grow or shrink with the picture, as in every other zooming view.
- The mouse wheel still scrolls. A board scrolls with the wheel as before; zooming is done with the toolbar’s −, Fit and +.
- Export is the natural-size board. An exported PNG or SVG shows the board at its natural size whatever the zoom was when you exported it.
Diagnostics (board.*)
The parser is tolerant and never throws; every malformed input yields a board.*
diagnostic and a clamp/skip. The full diagnostic family is:
board.syntax-error— the YAML itself does not parse (aYAMLParseError, including any attempt at an executable!!-tag, which is rejected, never run).board.retired-key— error (the one non-tolerant code in this family): the retired type-named document-title key was authored. The message names the fix verbatim —`board:` is retired — rename it to `title:` to name this board— and the retired key’s value is dropped rather than read. Only a root-levelboard:fires it; a nestedboard:(a route’s board binding) is a different, legitimate key. See The document header.board.internal-error— a belt-and-suspenders catch for an unexpected throw.board.invalid-column/board.invalid-lane/board.invalid-split/board.invalid-group— a malformed entry of that kind is skipped.board.invalid-wip— awip:value that is neither a number nor a[min, max]pair; omitted.board.invalid-flightlevel— aflightlevel:other thanFL1/FL2/FL3; omitted.board.unknown-lane-type— a lanetype:other thannamed/assignee/expedite; defaults tonamed.board.invalid-status-category— astatus-category:value outside the closed setto-do | in-progress | done | waiting | assess; the value is dropped and the unit gets no tint (tolerant — never throws).board.nested-too-deep— a group path nested deeper than 3; deeper levels are ignored.
Security: the parser uses the default core YAML schema — no executable tags (no RCE), ReDoS-safe regexes, and a hard nesting cap of 3.
Like what you see? → Start — build your own org folder in ~2 min