Flight Route DSL — Edges
The abstract path: is a list of edges — directed transitions between stages.
Every edge carries a kind that is encoded by its arrow glyph and drives
how the renderer paints it. There are three canonical edge kinds — generate,
copy, feedback — plus a reserved delivery edge that terminates at the
outside-band ()/⊙ sink. This is the SEED-004 / Klaus-#20 edge vocabulary. See
the reference index for the header and diagnostics family, and
Path & bands for stage identity and bands:.
When prose and code disagree, the code wins. The edge glyphs, kinds, and band constraints on this page were authored from
src/route/yaml-parser.ts(parsePathLine/parseExpandedEdge) and the band-constraint rulesrc/route/rules/r14-edge-band-constraint.ts.
The edge vocabulary
Each path: line is LEFT <glyph> RIGHT, where both endpoints are stages in the
itemType @ Band form — except the delivery sink (). The glyph selects the kind:
| Kind | Glyph | Bands it connects | Render style | Diagnostic on misuse |
|---|---|---|---|---|
generate | -> (also Unicode →) | the same band | solid line | route.edge-band-constraint (info) when it crosses bands |
copy | --> | different bands; fan-out capable (many edges may share one from) | dashed line | route.edge-band-constraint (info) when both stages are in the same band |
feedback | ..> | band-unconstrained (a backward info-flow to an earlier stage) | dotted line, bidirectional arrowheads | (none — feedback is not band-checked) |
delivery | ..> () | terminates at the reserved outside-band ()/⊙ sink (always counts as “different”) | fine-dotted line ending at the ⊙ ring | route.invalid-edge (error) when () is reached via a non-dotted glyph (-> () / --> ()) |
An unparseable edge line (no glyph, or an endpoint that is not a valid
itemType @ Band) is skipped with a route.invalid-edge warning; the rest of the
path: still parses. A multi-hop line (a @ X -> b @ Y -> c @ Z) is not
supported — it is a route.invalid-edge error that names the one-hop-per-line
fix.
Expanded object form
Every glyph line has an equivalent expanded object form — { from, to, kind }
with kind one of generate / copy / feedback / delivery — and both forms
funnel through the same construction, so a sugar line and its expanded twin produce
an identical edge. Use whichever reads better; they mix freely within one path:.
Band constraints
The band constraint (route.edge-band-constraint, r14) is a teaching nudge, an
info-level diagnostic — never a hard block. It has been downgraded twice, and the
preview always still renders: from an error to a warning at the Phase-25 EDH
walk, then from a warning to info at the Phase-38.1 re-walk, so that its level
agrees with what its own message says (the message names the type-changing cross-band
case as legitimate and ignorable, and a warning would tell you to act on it anyway):
generateconnects stages in the SAME band. A cross-bandgenerateis flagged: in the Klaus-#20 vocabulary a cross-band transition is usually acopyinto the receiving band’s pool, or you widen this band’sfl:span so the transformation stays within one band.copyconnects DIFFERENT bands. A same-bandcopyis flagged — a same-band forward flow is agenerateedge.feedbackis band-unconstrained — its identity is the backward information flow to an earlier stage, not a band relation, so it is never band-checked.deliveryalways passes — the()/⊙ sink always counts as “different”, so a delivery edge never reaches a band comparison.
Edge shapes
The renderer picks an edge’s shape from where its two stages sit; nothing in the DSL selects it. Every edge leaves a stage and arrives at a stage sideways: a forward edge leaves the source’s right side and enters the target’s left side, a backward edge (a feedback to an earlier stage) leaves the source’s left side and enters the target’s right side. The last stretch before the target is flat, so the arrowhead points straight into the box.
- Same band — a straight horizontal line between the two stages. When a fan-out puts the two stages at slightly different heights, the line steps over with a short eased slant.
- One band change — a flat 18 px stub out of the source, one diagonal at 60°, and a flat 28 px lead-in into the target. Corners are rounded.
- Two or more band changes — the same 60° diagonal split in two halves with a vertical run between them. The angle stays 60° whatever the number of bands.
- A there-and-back pair across bands — a forward edge and a feedback between the same two stages run as two parallel lines 14 px apart: both are exact offsets of one centre line, and their corners are concentric (the inner corner tighter, the outer one wider). Where the two run horizontally, the forward edge is the upper one.
- A there-and-back pair in one band — the two edges bow apart, one above and one below the line between the stages.
A 60° diagonal needs room: the column gap it runs through is 120 px instead of 70 px. Only those gaps widen. A route without band changes keeps every gap at 70 px and looks exactly as before; a route with band changes gets 50 px wider for each gap that carries a diagonal.
Where the diagonal sits. Next to the source, by default: the edge leaves the source on its 18 px stub, turns into the 60° diagonal in the first gap and arrives on its flat lead-in. An edge that spans two or more columns may find that a flat run at the target’s level would pass a stage of the target’s band, while the run at the source’s level is free. Then it runs flat out of the source at its own level and turns into its diagonal next to the target, in the gap in front of it. This is decided per edge from which columns the bands occupy, never from a stage name or an edge kind. The bound-board badges in a band’s label row are labels, not obstacles: an edge passes behind a badge (the opaque badge is painted over the line) and keeps its 60° angle instead of giving the shape up.
When the 60° shape is not used. Two cases keep another shape:
- The same band, or a target in the source’s own column. A same-band edge is straight (or a short eased slant); a target in the source’s own column is crossed at the level halfway between the two stages (see “A stage always sits at least one column right” below).
- A stage of each band in between. When a stage of the source’s band and a stage of the target’s band both stand between the two columns, neither flat run is free. The edge then takes the general obstacle routing: a slanted step that arches over or under whatever is in the way. If the drop is taller than one band pitch (a band that grew for a feedback lane, or a fan-out stagger), the one-band diagonal gets a little steeper rather than leaving its gap.
Where edges meet a stage side. Several edges can arrive at or leave the same side of a stage. They are ordered by where each line goes next to the stage: up on top, level in the middle, down at the bottom, so an arrival and a departure on one side do not cross there. The flat end of a 60° edge counts as level. An edge that has to pass a stage of its own band leaves on the side it passes: over the stage, on top; a feedback inside the band, at the bottom. The lines of one side spread over the stage’s height, starting 4 px from its corners and at most 14 px apart. The most level line (typically the done line) stays at the centre so it leaves dead level, and a there-and-back pair takes the middle of two slots and keeps its 14 px.
A stage always sits at least one column right of the stage whose forward edge (-> or -->)
it follows, even when two stages of one band compete for a column. An edge that still meets a
target in its own column (a feedback straight up or down) crosses at the level halfway between
the two stages and reaches the target from the side its arrowhead points in.
In the picture above, story --> task changes one band (one 60° diagonal) and task ..> epic
is a feedback that leaves task to the left. It runs flat at task’s own level and turns into its
60° diagonal in the gap right of epic, next to the target, because a flat run at epic’s level
would pass story.
Delivery — the retired deliver edge and the ()/⊙ sink
The legacy fourth edge deliver is retired (a hard cut, no alias). It belonged
to the old compat route form, which now gets a route.legacy-syntax error (see
Legacy keys). Delivery
is its own edge kind: a dotted ..> () terminating at the single reserved
outside-band ()/⊙ delivery sink — write stage ..> ().
The canonical delivery pattern is a copy into the delivery band, then a dotted
..> () to the sink (copy → ()): the work is copied into its final band and
then delivered out of the route. The () sink is delivery-only and reachable
only via the dotted ..> glyph — a -> () or --> () (a () target via a
non-dotted glyph) is a wrong-glyph delivery and a route.invalid-edge error
naming the dotted fix.
Worked example — all three canonical edges
The edges.dwsd-flightroute corpus fixture — a generate within a band, a copy
between bands, and a feedback backward:
# edges — all three canonical edges in one route: generate (solid), copy (dashed), feedback (dotted). title: Edge Vocabulary for: feature bands: - name: Strategy fl: 3 - name: Delivery fl: 1 path: - epic @ Strategy -> story @ Strategy - story @ Strategy --> task @ Delivery - task @ Delivery ..> epic @ Strategy
epic @ Strategy -> story @ Strategy is a same-band generate (solid);
story @ Strategy --> task @ Delivery is a cross-band copy (dashed);
task @ Delivery ..> epic @ Strategy is a feedback (dotted, bidirectional).
Worked example — delivery to the ()/⊙ sink
The delivery.dwsd-flightroute corpus fixture — a copy into the ship band, then
the dotted ..> () delivery to the reserved outside-band sink:
# delivery — a copy into the delivery band, then a dotted `..> ()` to the reserved outside-band sink. title: Delivery Route for: feature bands: - name: Build fl: 2 - name: Ship fl: 1 path: - account @ Build --> account @ Ship - account @ Ship ..> ()
account @ Build --> account @ Ship copies the work into its final band;
account @ Ship ..> () delivers it out of the route to the ⊙ sink. To pin the
Ship band onto a concrete board, add a bound-to: layer — see
Binding.
Like what you see? → Start — build your own org folder in ~2 min