DWSD.md

Topology DSL — Syntax Reference

The Topology DSL describes a Flight-Levels Work-System Local View: a focal Work System and the 1-hop ego-slice of Work Systems around it (the value chain that contributes up toward it, plus the Work Systems it contributes to). It is not a global org map — it is a scoped, focal slice.

The DSL is plain YAML. The fence language id for live-rendering viewers (the dwsd-vscode extension preview, the website) is dwsd-topology:

```dwsd-topology
mode: declared
work-systems:
- { name: "Squad Aurora" }
```

A viewer that recognizes the dwsd-topology id renders the block live; in plain Markdown / GitHub the same example is also shown as a static SVG (see Examples), so the reference reads correctly in either context.

Live example · Whole-company flight-level map — Voltfeld example →

Inferred from the org folder · scroll to zoom, drag to pan, click a box to open it

The parser is tolerant: it never throws on author input. Unknown or malformed fields produce a topology.* 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 below.

Note on field names that do not exist. The Topology DSL has no host field and no member-of field. Membership and hosting are not part of the grammar — do not write them. The only relationship the DSL expresses between Work Systems is contributes-to.

Design notes — YAML, humans, and machines

The Topology DSL is YAML by design. Every example in this reference is also a valid YAML document — block style (work-systems: as an indented list) and flow style ({ name: "...", flightlevel: 1 }) both parse — so any YAML-aware editor, linter, or formatter can read and write it. The package ships its own tolerant parser (src/topology/parser.ts) that accepts the same shape.

The syntax optimizes for readability and round-trippability, not brevity. Field names are spelled out (flightlevel, contributes-to — not abbreviations), values are explicit, and the parser never throws: unknown or malformed input yields a topology.* 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 topology without a UI. Expect authoring to shift toward AI tooling over time — for example, a Claude Code plugin or similar agent integration that emits and edits dwsd-topology blocks directly. The live fence preview and the static-SVG variants then serve as the way a person checks what the machine produced.

Modes

mode: selects how the Local View is assembled. Values: infer, declared, mixed. The default (when mode: is absent) is infer.

ModeMeaning
inferDerive the Local View from the indexer — the focal Work System and its 1-hop neighbours are read from the surrounding DawnSeed vault. The author lists nothing explicitly.
declaredThe author lists the work-systems and contributes-to edges explicitly. Nothing is inferred from the indexer.
mixedThe author’s declared entries overlay the inferred topology as a diff: an entry with a delta (add / change / remove) is applied on top of what the indexer found, an entry without one is drawn as it is. Inference needs an around: (in an embedded block, the containing work system); without it the declared entries are the whole picture.

An unrecognized mode: value emits topology.unknown-mode (warning) and falls back to infer.

Syntax reference

Every field below is implemented by the frozen parser (src/topology/parser.ts) and validated by src/topology/validate.ts. The Diagnostic column names the topology.* code an author hits on misuse.

Top-level fields

FieldValues / shapeDefaultDiagnostic on misuse
modeinfer | declared | mixedinfertopology.unknown-mode (warning; falls back to infer)
titlestring(none — omitted when absent)(none — a non-string or empty value is silently ignored)
tagsstring list (document-level descriptive chips; a scalar string is accepted as a one-element list)(none)tolerant — non-string entries dropped, empties trimmed
aroundself | [[Target]] | [[Target|Alias]](none)topology.invalid-focus (warning, parse); topology.unresolved-focus (warning, validate — target is not a known Work System)
radiusa number | inf | { up, down, lateral }{ up: 1, down: 1, lateral: 1 }topology.unsupported-radius (unparseable value → defaults to 1; negative → clamped to 0; lateral > 1 flagged at validate)
work-systems (alias work_systems)list of Work-System objects[]topology.invalid-work-system (a non-object or name-less entry is skipped)
contributes-to (alias contributes_to)list of edge objects[]topology.invalid-edge (a non-object or from/to-less entry is skipped)

title is an optional human-readable label for the topology as a whole. It is data-only metadata — used for export filenames and other outside-the-image uses — and is not rendered on the diagram. A non-string or empty value is silently ignored; when the key is absent the field is omitted (never '').

tags are document-level descriptive chips. Note the two scopes: this top-level tags: describes the topology document, while the tags: on a work-systems entry (below) describes that Work System. Topology gained the document-level one in Phase 27.

title: and tags: are the shared document header — the board and flight-route DSLs use the same two keys with the same meaning and the same missing-value convention. Topology did not change here; the other two converged onto its spelling. See The document header.

around centers the view. self centers on the containing Work System (used in embedded blocks); a [[wikilink]] (optionally [[Target|Alias]]) centers on a named Work System. Anything else emits topology.invalid-focus and the center is dropped. At validate time, a wikilink target that is not a known Work System emits topology.unresolved-focus (the diagnostic codes keep their old spelling: codes are stable identifiers). focus: is the retired spelling of around:: it still works and shows an INFO (topology.retired-focus) in the editor. With both keys, around: wins and the parser warns (topology.around-and-focus). Focusing a mark in the preview is a different thing from around:: it is a view-time highlight and changes nothing in the document.

radius bounds the ego-slice. A bare number applies uniformly to up, down, and lateral. The string inf (case-insensitive) means unbounded (transitive closure). The object form fills any missing key with 1. Negative values are clamped to 0 with topology.unsupported-radius. Multi-hop up / down (and inf) are honoured; lateral > 1 is not yet supported and emits topology.unsupported-radius (info) at validate — lateral / relates-to expansion is deferred.

Work-system fields (each entry of work-systems)

FieldValues / shapeDefaultDiagnostic on misuse
namestring (required)—topology.invalid-work-system (entry skipped when name is missing or empty)
flightlevel1 | 2 | 3(none)topology.invalid-flightlevel (warning; the value is omitted)
deltaadd | change | remove(none)topology.unknown-delta (warning; the value is omitted)
tagsstring list (a scalar string is accepted as a one-element list)(none)tolerant — non-string entries are dropped, empties trimmed away
anchorsmap of typeKey → string list(none)topology.invalid-anchors (the value is ignored when it is not a map)

name is the only required field. flightlevel places the Work System in its band (1 = operational, 2 = coordination, 3 = strategy). delta is the mixed-mode diff marker. tags render as one tags marker in the node’s marker row: a tag icon with the number of tags. The tags themselves are listed in the editor preview’s drawer. anchors declares type-keyed entities attached to the Work System — the key is the entity type (e.g. agreement, meeting, individual, unit, flight-route, interaction, or a visualization:board / visualization:route composite) and the value is a list of entity names. In the live indexer path these anchors come from the vault; the declared form is for authoring and the static examples.

An agreement or an AI agent that names a work system only through a bare agreement-of: or bound-to: wikilink lands on that work system’s node, and so does a signal or insight with a bare observes: wikilink. A bare [[Name]] matches the target file case-insensitively: [[bakery operations]] reaches Bakery Operations.md. See the locator grammar for the full grammar.

Edge fields (each entry of contributes-to)

FieldValues / shapeDefaultDiagnostic on misuse
fromstring (required)—topology.invalid-edge (entry skipped when missing)
tostring (required)—topology.invalid-edge (entry skipped when missing)
deltaadd | change | remove(none)topology.unknown-delta (warning; the value is omitted)

A contributes-to edge points from a contributor to the Work System it feeds. The value chain flows upward: an edge should land on the same or a higher flight level. An edge to a strictly lower flight level emits topology.downward-contribution (warning) at validate. A repeated (from, to) pair emits topology.duplicate-edge (info) and the second occurrence is ignored at layout time.

How edges are drawn

There is nothing to configure here; these are the rules the layout follows. An edge leaves the top of its source and enters the bottom of its target, vertically at both ends. Between two boxes that sit on top of each other the edge is a straight line: one of the two boxes moves a few pixels to line up, and the focused box never moves. An edge that skips a row keeps a reserved corridor beside the boxes in between, and it bends into that corridor only when its straight route would cut through one of them; otherwise it is drawn like any other edge. Ports sit 20 px apart, centered on the box edge, incoming and outgoing alike — closer only when the box edge is too short for all of them — and a single edge meets its box in the middle.

The corridor exists so that no contributes-to edge is ever drawn through a box it does not connect; the reasoning lives in the edge-aware placement decision record of the dwsd-dsl package.

Markers, drawer and click model

A Work System node carries a marker row along its bottom edge: one small glyph with a count per kind of entity attached to it. There are nine annotation layers — Interactions · Agreements · AI Agents · Contributors (key roles) · Mandates · Change conversation · Organizational decision records · Notes · Tags — and the filter menu, the export and the command line (--hide roles, …) treat all nine alike. (The filter menu row Boards, key board-bindings and the first row of the menu, also hides the board, route, topology and flight-route markers of a box; the row Contributors, key roles, hides the role, person and member-unit markers. Both re-flow like every other layer.) The row holds at most four markers and then shows an ellipsis. A meeting is an interaction: it is counted in the box’s one interaction marker (an interaction and a meeting read 2) and listed in the drawer’s one Interactions section, and hiding Interactions hides it. Hiding a layer re-flows the inside of the box (the boxes, edges and bands never move): the remaining markers close up with no gap, a marker that did not fit before may come into view, the ellipsis goes when everything visible fits, and a box with no visible marker loses its divider line and centers its title like a box without markers. The change pill on a box (change-conversation pins and decision records) gets narrower when part of it is hidden and is not painted when all of it is. With every layer hidden the picture keeps the work systems, the edges and the bands. Roles are their own marker type: a type: role file whose for-domain: names the work system puts a role marker (a badge glyph and a count) on its node, and roles rank before people and teams / units, so a crowded row keeps the role marker visible and the ellipsis hides the last-ranked kinds. A role with several work systems in a list-form for-domain: marks each of them.

Mandates are a marker type of their own (a key glyph and a count) and rank directly after roles: a type: mandate file whose for-domain: names the work system, or a mandate that hangs at the work system with mandate-of:, puts a mandate marker on its node, and the drawer lists the holders with the mandate text. The layer is called Mandates (--hide mandates). See Mandates.

The change pill. A node with change entries carries ONE change pill straddling its top edge, 14 px from the box’s right edge and drawn over the box (and over the focal ring): a level indicator of four small dots (signal, insight, driver, proposal), filled in the marker grey up to the highest stage that has an entry and faint above it, the entry count, and the organizational decision records behind a thin divider (a sprout glyph and their count). It is a quiet 16 px capsule in the node box’s own paint (its Flight Level fill, its border color and stroke width, 3 px on the focal node) and has no type colors. Hovering it shows the card “Change story here · n entries”; a click opens the node drawer, where the change conversation is listed by stage and can be folded into a long pill. See Where the pins render.

A click anywhere on a node opens the drawer — the title area, the body, the marker row — and a second click closes it. The drawer title is a link to the work system’s file: it carries the link color and a trailing ”›” at rest, is underlined only on hover and keyboard focus, and opens the file beside the preview; the drawer stays open. A host that wants the earlier model — the marker row opens the drawer and a click on the node navigates — passes openOn: 'marker-zone' (the default is 'box'), and titleAction ('navigate' by default, 'select', or 'none' for a plain title with no control) chooses what the title does.

The drawer opens with the fact row: one line of small chips with no heading. The flight level is always there, as a colored chip (Flight Level 1, 2, 3 in the Flight Levels colors, because the chip means the flight level); the unit types follow side by side, exactly as written, with work-system reading Work System. The unit types come from the workspace — the work system’s unit file — or from the optional lookupUnit host callback (which answers just { unitTypes }); the callback wins, and without either the row shows the flight level alone. The bands follow in the order Interactions · Agreements · AI Agents · Roles · Change conversation · Organizational decision records · Notes · Tags; the change conversation reads Signal, Insight, Driver, Proposal from top to bottom whatever order the pins arrive in, and the decision records are their own section, Organizational Decision Records.

Center and radius

In the VS Code side preview, and in any host that offers it (the Dawn Seed App, for example), you can look at another part of the topology without touching the fence. Click a work system: its drawer opens, and the header offers Center here, with “+N outside” when N neighbors are not in view. Click it and the picture re-centers on that box with a short glide. The toolbar then shows Back and Forward (a right click, a half-second press or Shift+Enter on Back lists every earlier center) and the center icon, which returns to the picture the fence asks for. The radius button next to them opens a pop-out with an Up and a Down row (0, 1, 2, 3 or all); “Reset” returns to the fence’s radius:.

In a hand-declared topology, re-centering only moves the focus ring: every declared work system stays. Without a radius: in the fence nothing is cut, so the radius button reads ↑∞ ↓∞ with “default” under ∞; choosing a smaller radius trims the picture to what is within reach.

Center and radius are view state: the fence is not touched, and another fence or note starts at its own picture. A picture without a focal (no around: or focus:, and no default focus from the entity that contains the fence) shows everything, so there is nothing to center on: it offers neither “Center here” nor the radius button. A fence with only focus: counts as scoped and offers both. The Markdown preview stays static; it draws the fence’s picture and has none of these controls. The radius offers Up and Down only; there is no step along a relates-to link, so a third row would change nothing.

Examples

Each example below appears in both variants: the dynamic dwsd-topology fence (the raw DSL a live viewer renders) and the generated static SVG (the fallback for plain Markdown / GitHub). The examples escalate from the smallest valid document to the full mixed-mode diff surface.

For a focused, source-⟷-rendered showcase of just the six examples (generated from the single source of truth), see the examples gallery.

1. Minimal

The smallest valid topology: a single declared work-system.

# minimal — the smallest valid topology: a single declared work-system.
mode: declared
work-systems:
  - { name: "Squad Aurora", flightlevel: 1 }

2. Flight Levels

The three Flight Level bands (1 operational, 2 coordination, 3 strategy) across three work-systems.

# flightlevels — the three Flight Level bands (1 operational, 2 coordination, 3 strategy).
mode: declared
work-systems:
  - { name: "Squad Aurora", flightlevel: 1 }
  - { name: "Reliability Mid", flightlevel: 2 }
  - { name: "Delivery Ops", flightlevel: 3 }

3. Contributes-To Network

A contributes-to network: work systems contribute upward to higher-level targets, and a mid-level system can feed more than one target — so the shape is a network, not a single-rooted tree. Edges point to the same or a higher flight level (the value chain flows upward).

# contributes-to-network — several sources contribute UP to higher-level targets; one system feeds TWO targets (a network, not a single-rooted tree).
# Edges point same-or-higher flight level (the value chain flows upward).
mode: declared
work-systems:
  - { name: "Squad Aurora", flightlevel: 1 }
  - { name: "Squad Vega", flightlevel: 1 }
  - { name: "Reliability Mid", flightlevel: 2 }
  - { name: "Delivery Ops", flightlevel: 3 }
  - { name: "Platform Strategy", flightlevel: 3 }
contributes-to:
  - { from: "Squad Aurora", to: "Reliability Mid" }
  - { from: "Squad Vega", to: "Reliability Mid" }
  - { from: "Reliability Mid", to: "Delivery Ops" }
  - { from: "Reliability Mid", to: "Platform Strategy" }

Here Reliability Mid (FL2) contributes to both Delivery Ops and Platform Strategy (FL3) — the fan-out that makes this a network.

4. Tags

String tag lists on work-systems. The Local View draws one tags marker on the node: a tag icon with the count.

# tags — string lists on work-systems (one tags marker with the count in the rendered Local View).
mode: declared
work-systems:
  - { name: "Squad Aurora", flightlevel: 1, tags: ["delivery", "core"] }
  - { name: "Delivery Ops", flightlevel: 3, tags: ["strategy"] }

5. Focus + Radius

Local-View scoping: around: centers the view on a work-system and radius: bounds how far up/down/lateral the ego-slice reaches.

# focus-radius — Local-View scoping. `around:` centers the view on a work-system;
# `radius:` bounds how far up/down/lateral the 1-hop ego-slice reaches.
# lateral stays at 1 (lateral > 1 / relates-to expansion is not yet supported).
mode: declared
around: "[[Reliability Mid]]"
radius: { up: 2, down: 1, lateral: 1 }
work-systems:
  - { name: "Squad Aurora", flightlevel: 1 }
  - { name: "Squad Vega", flightlevel: 1 }
  - { name: "Reliability Mid", flightlevel: 2 }
  - { name: "Delivery Ops", flightlevel: 3 }
contributes-to:
  - { from: "Squad Aurora", to: "Reliability Mid" }
  - { from: "Reliability Mid", to: "Delivery Ops" }

Why “Squad Vega” is not in the rendered view. It is declared in work-systems but has no contributes-to edge connecting it to the focal Work System, so the radius walk never reaches it. Only Work Systems inside the focal ego-slice appear in the Local View — declaring a Work System does not force it to render if it falls outside around + radius.

6. Mixed Delta + Anchors

Diff authoring: mode: mixed overlays add/change/remove deltas onto the current topology, and anchors: declares type-keyed entities attached to a work-system. Squad Vega and Delivery Ops carry no delta: — they are the part of that topology the change leaves as it is.

# mixed-delta-anchors — diff authoring. `mode: mixed` overlays declared deltas
# (add/change/remove) onto the current topology, and `anchors:` declares the
# type-keyed entities attached to a work-system (agreements, meetings, ...).
# Squad Vega and Delivery Ops carry no `delta:` — the part of today's topology
# the change leaves as it is. With an `around:` in a workspace, `mode: mixed`
# infers that base from the work-system files.
mode: mixed
work-systems:
- { name: "Squad Vega", flightlevel: 1 }
- { name: "Delivery Ops", flightlevel: 3 }
- name: "Squad Aurora"
flightlevel: 1
delta: add
anchors:
agreement: ["Team Working Agreement"]
meeting: ["Weekly Sync"]
- name: "Reliability Mid"
flightlevel: 2
delta: change
- name: "Legacy Ops"
flightlevel: 3
delta: remove
contributes-to:
- { from: "Squad Vega", to: "Reliability Mid" }
- { from: "Reliability Mid", to: "Delivery Ops" }
- { from: "Reliability Mid", to: "Legacy Ops", delta: remove }
- { from: "Squad Aurora", to: "Reliability Mid", delta: add }

Like what you see? → Start — build your own org folder in ~2 min

index.md
# Topology DSL — Syntax Reference
The **Topology DSL** describes a Flight-Levels **Work-System Local View**: a
focal Work System and the 1-hop ego-slice of Work Systems around it (the value
chain that contributes up toward it, plus the Work Systems it contributes to).
It is **not** a global org map — it is a scoped, focal slice.
The DSL is plain **YAML**. The fence language id for live-rendering viewers (the
`dwsd-vscode` extension preview, the website) is **`dwsd-topology`**:
````markdown
```dwsd-topology
mode: declared
work-systems:
- { name: "Squad Aurora" }
```
````
A viewer that recognizes the `dwsd-topology` id renders the block live; in plain
Markdown / GitHub the same example is also shown as a static SVG (see
[Examples](#examples)), so the reference reads correctly in either context.
The parser is **tolerant**: it never throws on author input. Unknown or
malformed fields produce a `topology.*` 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 below.
> **Note on field names that do _not_ exist.** The Topology DSL has **no
> `host` field** and **no `member-of` field**. Membership and hosting are not
> part of the grammar — do not write them. The only relationship the DSL
> expresses between Work Systems is `contributes-to`.
## Design notes — YAML, humans, and machines
The Topology DSL is **YAML by design**. Every example in this reference is also
a valid YAML document — block style (`work-systems:` as an indented list) and
flow style (`{ name: "...", flightlevel: 1 }`) both parse — so any YAML-aware
editor, linter, or formatter can read and write it. The package ships its own
tolerant parser (`src/topology/parser.ts`) that accepts the same shape.
The syntax optimizes for **readability and round-trippability, not brevity**.
Field names are spelled out (`flightlevel`, `contributes-to` — not
abbreviations), values are explicit, and the parser never throws: unknown or
malformed input yields a `topology.*` 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 topology without a UI. Expect authoring
to shift toward AI tooling over time — for example, a Claude Code plugin or
similar agent integration that emits and edits `dwsd-topology` blocks directly.
The live fence preview and the static-SVG variants then serve as the way a
person checks what the machine produced.
## Modes
`mode:` selects how the Local View is assembled. Values: **`infer`**,
**`declared`**, **`mixed`**. The default (when `mode:` is absent) is `infer`.
| Mode | Meaning |
|------|---------|
| `infer` | Derive the Local View from the indexer — the focal Work System and its 1-hop neighbours are read from the surrounding DawnSeed vault. The author lists nothing explicitly. |
| `declared` | The author lists the `work-systems` and `contributes-to` edges explicitly. Nothing is inferred from the indexer. |
| `mixed` | The author's `declared` entries **overlay** the inferred topology as a diff: an entry with a `delta` (`add` / `change` / `remove`) is applied on top of what the indexer found, an entry without one is drawn as it is. Inference needs an `around:` (in an embedded block, the containing work system); without it the declared entries are the whole picture. |
An unrecognized `mode:` value emits **`topology.unknown-mode`** (warning) and
falls back to `infer`.
## Syntax reference
Every field below is implemented by the frozen parser
(`src/topology/parser.ts`) and validated by `src/topology/validate.ts`. The
**Diagnostic** column names the `topology.*` code an author hits on misuse.
### Top-level fields
| Field | Values / shape | Default | Diagnostic on misuse |
|-------|----------------|---------|----------------------|
| `mode` | `infer` \| `declared` \| `mixed` | `infer` | `topology.unknown-mode` (warning; falls back to `infer`) |
| `title` | string | (none — omitted when absent) | (none — a non-string or empty value is silently ignored) |
| `tags` | string list (document-level descriptive chips; a scalar string is accepted as a one-element list) | (none) | tolerant — non-string entries dropped, empties trimmed |
| `around` | `self` \| `[[Target]]` \| `[[Target\|Alias]]` | (none) | `topology.invalid-focus` (warning, parse); `topology.unresolved-focus` (warning, validate — target is not a known Work System) |
| `radius` | a number \| `inf` \| `{ up, down, lateral }` | `{ up: 1, down: 1, lateral: 1 }` | `topology.unsupported-radius` (unparseable value → defaults to `1`; negative → clamped to `0`; `lateral > 1` flagged at validate) |
| `work-systems` (alias `work_systems`) | list of Work-System objects | `[]` | `topology.invalid-work-system` (a non-object or name-less entry is skipped) |
| `contributes-to` (alias `contributes_to`) | list of edge objects | `[]` | `topology.invalid-edge` (a non-object or `from`/`to`-less entry is skipped) |
**`title`** is an optional human-readable label for the topology as a whole. It
is data-only metadata — used for export filenames and other outside-the-image
uses — and is **not rendered on the diagram**. A non-string or empty value is
silently ignored; when the key is absent the field is **omitted** (never `''`).
**`tags`** are document-level descriptive chips. Note the two scopes: this
top-level `tags:` describes **the topology document**, while the `tags:` on a
`work-systems` entry (below) describes **that Work System**. Topology gained the
document-level one in Phase 27.
`title:` and `tags:` are the **shared document header** — the board and
flight-route DSLs use the same two keys with the same meaning and the same
missing-value convention. Topology did not change here; the other two converged
onto *its* spelling. See [The document header](../document-header.md).
**`around`** centers the view. `self` centers on the containing Work System (used
in embedded blocks); a `[[wikilink]]` (optionally `[[Target|Alias]]`) centers on a
named Work System. Anything else emits `topology.invalid-focus` and the center is
dropped. At validate time, a wikilink target that is not a known Work System
emits `topology.unresolved-focus` (the diagnostic codes keep their old spelling:
codes are stable identifiers). `focus:` is the retired spelling of `around:`: it still works and shows an INFO (`topology.retired-focus`) in the editor. With both keys, `around:` wins and the parser warns (`topology.around-and-focus`).
Focusing a mark in the preview is a different thing from `around:`: it is a
view-time highlight and changes nothing in the document.
**`radius`** bounds the ego-slice. A bare number applies uniformly to `up`,
`down`, and `lateral`. The string `inf` (case-insensitive) means unbounded
(transitive closure). The object form fills any missing key with `1`. Negative
values are clamped to `0` with `topology.unsupported-radius`. Multi-hop `up` /
`down` (and `inf`) are honoured; **`lateral > 1` is not yet supported** and
emits `topology.unsupported-radius` (info) at validate — lateral / `relates-to`
expansion is deferred.
### Work-system fields (each entry of `work-systems`)
| Field | Values / shape | Default | Diagnostic on misuse |
|-------|----------------|---------|----------------------|
| `name` | string (**required**) | — | `topology.invalid-work-system` (entry skipped when `name` is missing or empty) |
| `flightlevel` | `1` \| `2` \| `3` | (none) | `topology.invalid-flightlevel` (warning; the value is omitted) |
| `delta` | `add` \| `change` \| `remove` | (none) | `topology.unknown-delta` (warning; the value is omitted) |
| `tags` | string list (a scalar string is accepted as a one-element list) | (none) | tolerant — non-string entries are dropped, empties trimmed away |
| `anchors` | map of `typeKey → string list` | (none) | `topology.invalid-anchors` (the value is ignored when it is not a map) |
**`name`** is the only required field. **`flightlevel`** places the Work System
in its band (1 = operational, 2 = coordination, 3 = strategy). **`delta`** is
the mixed-mode diff marker. **`tags`** render as one tags marker in the node's
marker row: a tag icon with the number of tags. The tags themselves are listed
in the editor preview's drawer. **`anchors`** declares type-keyed entities
attached to the Work System — the key is the entity type (e.g. `agreement`, `meeting`, `individual`,
`unit`, `flight-route`, `interaction`, or a `visualization:board` /
`visualization:route` composite) and the value is a list of entity names. In
the live indexer path these anchors come from the vault; the declared form is
for authoring and the static examples.
An agreement or an AI agent that names a work system only through a bare
`agreement-of:` or `bound-to:` wikilink lands on that work system's node, and so
does a signal or insight with a bare `observes:` wikilink. A bare `[[Name]]`
matches the target file case-insensitively: `[[bakery operations]]` reaches
`Bakery Operations.md`. See the [locator grammar](../locator-grammar.md) for the
full grammar.
### Edge fields (each entry of `contributes-to`)
| Field | Values / shape | Default | Diagnostic on misuse |
|-------|----------------|---------|----------------------|
| `from` | string (**required**) | — | `topology.invalid-edge` (entry skipped when missing) |
| `to` | string (**required**) | — | `topology.invalid-edge` (entry skipped when missing) |
| `delta` | `add` \| `change` \| `remove` | (none) | `topology.unknown-delta` (warning; the value is omitted) |
A `contributes-to` edge points from a contributor to the Work System it feeds.
The value chain flows **upward**: an edge should land on the same or a higher
flight level. An edge to a strictly lower flight level emits
`topology.downward-contribution` (warning) at validate. A repeated `(from, to)`
pair emits `topology.duplicate-edge` (info) and the second occurrence is ignored
at layout time.
## How edges are drawn
There is nothing to configure here; these are the rules the layout follows. An edge
leaves the top of its source and enters the bottom of its target, vertically at both
ends. Between two boxes that sit on top of each other the edge is a straight line: one
of the two boxes moves a few pixels to line up, and the focused box never moves. An
edge that skips a row keeps a reserved corridor beside the boxes in between, and it
bends into that corridor only when its straight route would cut through one of them;
otherwise it is drawn like any other edge. Ports sit 20 px apart, centered on the box
edge, incoming and outgoing alike — closer only when the box edge is too short for all
of them — and a single edge meets its box in the middle.
The corridor exists so that no `contributes-to` edge is ever drawn through a box it
does not connect; the reasoning lives in the edge-aware placement decision record of
the `dwsd-dsl` package.
## Markers, drawer and click model
A Work System node carries a **marker row** along its bottom edge: one small glyph
with a count per kind of entity attached to it. There are **nine annotation
layers** — Interactions · Agreements · AI Agents · **Contributors** (key `roles`) · **Mandates** · Change conversation ·
Organizational decision records · Notes · Tags — and the filter menu, the export and
the command line (`--hide roles`, …) treat all nine alike. (The filter menu row **Boards**,
key `board-bindings` and the first row of the menu, also hides the board, route, topology and flight-route
markers of a box; the row **Contributors**, key `roles`, hides the role, person and member-unit markers.
Both re-flow like every other layer.) The row holds at most
four markers and then shows an ellipsis. A **meeting** is an interaction: it is counted in the box's
one interaction marker (an interaction and a meeting read `2`) and listed in the drawer's one
Interactions section, and hiding Interactions hides it. Hiding a layer re-flows the inside of the box (the boxes, edges and bands
never move): the remaining markers close up with no gap, a marker that did not fit before may come into
view, the ellipsis goes when everything visible fits, and a box with no visible marker loses its divider
line and centers its title like a box without markers. The **change pill** on a box (change-conversation pins and
decision records) gets narrower when part of it is hidden and is not painted when all of it is. With every
layer hidden the picture keeps the work systems, the edges and the bands. **Roles** are their own marker type: a
`type: role` file whose `for-domain:` names the work system puts a **role marker**
(a badge glyph and a count) on its node, and roles rank **before** people and teams /
units, so a crowded row keeps the role marker visible and the ellipsis hides the
last-ranked kinds. A role with several work systems in a list-form `for-domain:` marks
each of them.
**Mandates** are a marker type of their own (a key glyph and a count) and rank directly after roles: a
`type: mandate` file whose `for-domain:` names the work system, or a mandate that hangs at the work system
with `mandate-of:`, puts a **mandate marker** on its node, and the drawer lists the holders with the mandate
text. The layer is called **Mandates** (`--hide mandates`). See [Mandates](../mandates.md).
**The change pill.** A node with change entries carries ONE **change pill** straddling its top edge, 14 px from the box's right edge and drawn over the box (and over the focal ring): a level indicator of four small dots (signal, insight, driver, proposal), filled in the marker grey up to the highest stage that has an entry and faint above it, the entry count, and the organizational decision records behind a thin divider (a sprout glyph and their count). It is a quiet 16 px capsule in the node box's own paint (its Flight Level fill, its border color and stroke width, 3 px on the focal node) and has no type colors. Hovering it shows the card "Change story here · n entries"; a click opens the node drawer, where the change conversation is listed by stage and can be folded into a long pill. See [Where the pins render](../change/in-flux.md#where-the-pins-render).
**A click anywhere on a node opens the drawer** — the title area, the body, the marker
row — and a second click closes it. The drawer **title** is a link to the work
system's file: it carries the link color and a trailing "›" at rest, is underlined
only on hover and keyboard focus, and opens the file beside the preview; the drawer
stays open. A host that wants the earlier model — the marker row opens the drawer and a
click on the node navigates — passes **`openOn: 'marker-zone'`** (the default is
`'box'`), and **`titleAction`** (`'navigate'` by default, `'select'`, or `'none'` for a
plain title with no control) chooses what the title does.
The drawer opens with the **fact row**: one line of small chips with no heading. The
flight level is always there, as a colored chip (`Flight Level 1`, `2`, `3` in the
Flight Levels colors, because the chip means the flight level); the unit types follow
side by side, exactly as written, with `work-system` reading **Work System**. The unit
types come from the workspace — the work system's unit file — or from the optional
`lookupUnit` host callback (which answers just `{ unitTypes }`); the callback wins, and
without either the row shows the flight level alone. The bands follow in the order
Interactions · Agreements · AI Agents · **Roles** · Change conversation · Organizational
decision records · Notes · Tags; the change conversation reads Signal, Insight, Driver,
Proposal from top to bottom whatever order the pins arrive in, and the decision
records are their own section, **Organizational Decision Records**.
## Center and radius
In the VS Code side preview, and in any host that offers it (the Dawn Seed App, for example), you can look at another part of the
topology without touching the fence. Click a work system: its drawer opens, and the header offers **Center here**, with "+N outside" when N
neighbors are not in view. Click it and the picture re-centers on that box with a short glide. The toolbar then shows **Back** and
**Forward** (a right click, a half-second press or Shift+Enter on Back lists every earlier center) and the **center** icon, which
returns to the picture the fence asks for. The **radius** button next to them opens a pop-out with an **Up** and a **Down** row (0,
1, 2, 3 or all); "Reset" returns to the fence's `radius:`.
In a hand-declared topology, re-centering only moves the focus ring: every declared work system stays. Without a `radius:` in the fence nothing is cut, so the radius button reads `↑∞ ↓∞` with "default" under ∞; choosing a smaller radius trims the picture to what is within reach.
Center and radius are view state: the fence is not touched, and another fence or note starts at its own picture. A picture without
a focal (no `around:` or `focus:`, and no default focus from the entity that contains the fence) shows everything, so there is
nothing to center on: it offers neither "Center here" nor the radius button. A fence with only `focus:` counts as scoped and offers
both. The Markdown preview stays static; it draws the fence's picture and has none of these controls. The radius offers Up and Down
only; there is no step along a relates-to link, so a third row would change nothing.
## Examples
Each example below appears in **both variants**: the dynamic `dwsd-topology`
fence (the raw DSL a live viewer renders) and the generated static SVG (the
fallback for plain Markdown / GitHub). The examples escalate from the smallest
valid document to the full mixed-mode diff surface.
> For a focused, source-⟷-rendered showcase of just the six examples (generated
> from the single source of truth), see the
> [examples gallery](./topology-examples.md).
### 1. Minimal
The smallest valid topology: a single declared work-system.
```dwsd-topology
# minimal — the smallest valid topology: a single declared work-system.
mode: declared
work-systems:
- { name: "Squad Aurora", flightlevel: 1 }
```
![Minimal](./topology-examples/minimal.svg)
### 2. Flight Levels
The three Flight Level bands (1 operational, 2 coordination, 3 strategy) across
three work-systems.
```dwsd-topology
# flightlevels — the three Flight Level bands (1 operational, 2 coordination, 3 strategy).
mode: declared
work-systems:
- { name: "Squad Aurora", flightlevel: 1 }
- { name: "Reliability Mid", flightlevel: 2 }
- { name: "Delivery Ops", flightlevel: 3 }
```
![Flight Levels](./topology-examples/flightlevels.svg)
### 3. Contributes-To Network
A contributes-to network: work systems contribute upward to higher-level
targets, and a mid-level system can feed **more than one** target — so the shape
is a network, not a single-rooted tree. Edges point to the same or a higher
flight level (the value chain flows upward).
```dwsd-topology
# contributes-to-network — several sources contribute UP to higher-level targets; one system feeds TWO targets (a network, not a single-rooted tree).
# Edges point same-or-higher flight level (the value chain flows upward).
mode: declared
work-systems:
- { name: "Squad Aurora", flightlevel: 1 }
- { name: "Squad Vega", flightlevel: 1 }
- { name: "Reliability Mid", flightlevel: 2 }
- { name: "Delivery Ops", flightlevel: 3 }
- { name: "Platform Strategy", flightlevel: 3 }
contributes-to:
- { from: "Squad Aurora", to: "Reliability Mid" }
- { from: "Squad Vega", to: "Reliability Mid" }
- { from: "Reliability Mid", to: "Delivery Ops" }
- { from: "Reliability Mid", to: "Platform Strategy" }
```
Here `Reliability Mid` (FL2) contributes to **both** `Delivery Ops` and
`Platform Strategy` (FL3) — the fan-out that makes this a network.
![Contributes-To Network](./topology-examples/contributes-to-network.svg)
### 4. Tags
String tag lists on work-systems. The Local View draws one tags marker on the
node: a tag icon with the count.
```dwsd-topology
# tags — string lists on work-systems (one tags marker with the count in the rendered Local View).
mode: declared
work-systems:
- { name: "Squad Aurora", flightlevel: 1, tags: ["delivery", "core"] }
- { name: "Delivery Ops", flightlevel: 3, tags: ["strategy"] }
```
![Tags](./topology-examples/tags.svg)
### 5. Focus + Radius
Local-View scoping: `around:` centers the view on a work-system and `radius:`
bounds how far up/down/lateral the ego-slice reaches.
```dwsd-topology
# focus-radius — Local-View scoping. `around:` centers the view on a work-system;
# `radius:` bounds how far up/down/lateral the 1-hop ego-slice reaches.
# lateral stays at 1 (lateral > 1 / relates-to expansion is not yet supported).
mode: declared
around: "[[Reliability Mid]]"
radius: { up: 2, down: 1, lateral: 1 }
work-systems:
- { name: "Squad Aurora", flightlevel: 1 }
- { name: "Squad Vega", flightlevel: 1 }
- { name: "Reliability Mid", flightlevel: 2 }
- { name: "Delivery Ops", flightlevel: 3 }
contributes-to:
- { from: "Squad Aurora", to: "Reliability Mid" }
- { from: "Reliability Mid", to: "Delivery Ops" }
```
![Focus + Radius](./topology-examples/focus-radius.svg)
> **Why "Squad Vega" is not in the rendered view.** It is declared in
> `work-systems` but has no `contributes-to` edge connecting it to the focal
> Work System, so the radius walk never reaches it. Only Work Systems inside
> the focal ego-slice appear in the Local View — declaring a Work System does
> not force it to render if it falls outside `around` + `radius`.
### 6. Mixed Delta + Anchors
Diff authoring: `mode: mixed` overlays `add`/`change`/`remove` deltas onto the
current topology, and `anchors:` declares type-keyed entities attached to a
work-system. `Squad Vega` and `Delivery Ops` carry no `delta:` — they are the
part of that topology the change leaves as it is.
```dwsd-topology
# mixed-delta-anchors — diff authoring. `mode: mixed` overlays declared deltas
# (add/change/remove) onto the current topology, and `anchors:` declares the
# type-keyed entities attached to a work-system (agreements, meetings, ...).
# Squad Vega and Delivery Ops carry no `delta:` — the part of today's topology
# the change leaves as it is. With an `around:` in a workspace, `mode: mixed`
# infers that base from the work-system files.
mode: mixed
work-systems:
- { name: "Squad Vega", flightlevel: 1 }
- { name: "Delivery Ops", flightlevel: 3 }
- name: "Squad Aurora"
flightlevel: 1
delta: add
anchors:
agreement: ["Team Working Agreement"]
meeting: ["Weekly Sync"]
- name: "Reliability Mid"
flightlevel: 2
delta: change
- name: "Legacy Ops"
flightlevel: 3
delta: remove
contributes-to:
- { from: "Squad Vega", to: "Reliability Mid" }
- { from: "Reliability Mid", to: "Delivery Ops" }
- { from: "Reliability Mid", to: "Legacy Ops", delta: remove }
- { from: "Squad Aurora", to: "Reliability Mid", delta: add }
```
![Mixed Delta + Anchors](./topology-examples/mixed-delta-anchors.svg)