Flight Route DSL — Path & bands
A flight route’s path: is the abstract design layer of a delivery. It is
built from three things and nothing else:
- bands — the horizontal lanes work moves through, declared in
bands:; - stages — an item type at a band (
itemType @ Band), the nodes the path visits; and - edges — the glyph-typed transitions between stages, authored in
path:.
The path is authored unbound — it references no board. Mapping a band onto a
concrete board is a separate concern handled by the bound-to: layer (see
Binding). This page covers bands:, stage identity, and external
triggers:; the edge vocabulary has its own page (Edges). See the
reference index for the route header and the full diagnostics
family.
When prose and code disagree, the code wins. Every key and diagnostic on this page was authored from
src/route/yaml-parser.ts,src/route/types.ts, and the validator rulessrc/route/rules/*.
Bands (bands:)
bands: is a list of band records. Each band is an object with a required
name and an optional fl flight-level tag that drives its color.
| Field | Values / shape | AST field | Default | Diagnostic on misuse |
|---|---|---|---|---|
name | string (required) — the band’s display name and its stage-address key | RouteBand.name | — | route.invalid-band (entry skipped when missing/empty or not an object) |
fl | a single number → one flight level; OR a numeric array (e.g. [2, 3]) → a multi-FL span | RouteBand.fl? | (none — a pure custom band omits it) | route.invalid-band when neither a number nor a numeric array (the key is omitted); route.invalid-band-fl (warn) when the value is outside the canonical 1..3 |
fl drives the band’s color. A single number picks the flight-level color; a
multi-FL span (fl: [2, 3]) resolves to a single color — there is no
gradient across the span. A band with no fl at all is a pure custom band
(e.g. a “Company Wall” outside the FL model); the fl key is simply omitted from
the AST. A fl value outside the canonical 1..3 set is kept but flagged with a
route.invalid-band-fl warning, so an author sees exactly what they wrote.
Stage identity — itemType @ Band
A stage is a (itemType, band) pair — for example epic @ Strategy. That
pair is the stage’s identity (StageRef { itemType, band } in the AST). The item
type is plain text or a quoted wikilink: epic @ Strategy describes the item
locally to the route (never looked up), "[[Epic]] @ Strategy" is a real connection
to a flight-item-type entity (StageRef.link: true, itemType holds the link
target Epic), and [[Customer Order|order]] @ ERP keeps a short label
(StageRef.alias). The two forms mix freely, also on one line; a line holding a
wikilink is quoted. See A stage’s item type is text or a wikilink.
Stages are never declared separately. There is no stages: section. Instead
the parser infers the set of stages by walking the endpoints of every edge in
path: (and every generates: target of a triggers: entry) and deduping by
(itemType, band). So the same itemType @ Band written on two different edges
is the same stage, visited twice — not two stages. The author form is always
the string itemType @ Band (or "[[ItemType]] @ Band"); the @ splits on its first
occurrence, so a multi-word band name (Core Products) is preserved, and a
wikilink target is read up to its closing ]]. A stage is named by its token, or
for a wikilink by its link target (not the alias) — that name is what locators and
bound-to keys use. Two names in one band that differ only by case get a
route.ambiguous-stage-name warning.
A stage carries no
tags:— descriptive tags are document-scope only (see The document header). At a stage a tag would only repeat what the band position already says.
External triggers (triggers:)
triggers: declares external notes that sit OUTSIDE the bands — the events
that kick a route off (a “new feature idea”, a “software defect”). Each trigger is
an object with a required name and a generates target list; it is
fan-out capable — one trigger can generate more than one stage.
| Field | Values / shape | AST field | Default | Diagnostic on misuse |
|---|---|---|---|---|
name | string (required) — the note’s label | Trigger.name | — | route.invalid-trigger (entry skipped when missing/empty) |
generates | a scalar itemType @ Band, OR a list of them → one or more stages | Trigger.generates (StageRef[]) | [] | route.invalid-trigger (an unparseable target is skipped) |
A trigger’s generates uses the same itemType @ Band stage form as edge
endpoints, so a trigger’s targets participate in stage inference exactly like edge
endpoints do (generates: "[[Feature]] @ Train" is a wikilink stage). A generates
entry that is not a valid itemType @ Band or "[[ItemType]] @ Band" string is
skipped with a route.invalid-trigger warning; the rest of the trigger survives.
Worked example — multiple bands, one per flight level
The bands.dwsd-flightroute corpus fixture — three named bands, one per flight
level (3/2/1), with stages living across them and copy edges between bands:
# bands — one named band per flight level; stages live across named bands. title: Multi-Band Route for: feature bands: - name: Strategy fl: 3 - name: Coordination fl: 2 - name: Delivery fl: 1 path: - vision @ Strategy --> epic @ Coordination - epic @ Coordination --> story @ Delivery
Strategy (FL3), Coordination (FL2), and Delivery (FL1) are one band per
level; vision, epic, and story are the three inferred stages, one per band.
Both edges are copy (-->) between different bands — see Edges
for the edge vocabulary.
A band may span several flight levels
The worked example above keeps one band per level for a clean 3/2/1 picture, but
fl also accepts a numeric array — a multi-FL span (one color, no gradient
across the span, per the bands: field table above). Here a
single Product Stream band covers FL1+FL2:
title: Span Example for: feature bands: - name: Product Stream fl: [1, 2] path: - epic @ Product Stream -> story @ Product Stream
A span is a valid authoring choice; the gallery example just prefers the clean one-band-per-level form.
Worked example — a fan-out trigger
An external trigger that generates a stage, then a copy edge carries it onward:
# a route kicked off by an external trigger sticky outside the bands. title: Triggered Route for: feature bands: - name: Coordination fl: 2 - name: Delivery fl: 1 triggers: - name: new feature idea generates: - epic @ Coordination path: - epic @ Coordination --> story @ Delivery
The new feature idea note sits outside the bands and generates the
epic @ Coordination stage; the path then carries a copy into Delivery.
Like what you see? → Start — build your own org folder in ~2 min