figdown 0.1.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ # Genre `block` — markers and zones (EXPERIMENTAL)
2
+
3
+ `threshold` and `band` are **outside the v0.1 conformance surface and outside
4
+ the compatibility promise**. The engine accepts them and your document keeps
5
+ working, but they may change or be withdrawn in a later `0.x`. The parser
6
+ emits no warning, so a line that parses tells you nothing. Use them when the
7
+ figure genuinely needs them; if the figure must be portable, do not, and if
8
+ you use one anyway, say so beside the figure.
9
+
10
+ **Both belong to `block` alone.** They are declared by this genre and by no
11
+ other: writing either under `topology`, `flowchart` or `statechart` is a line
12
+ error that says so and gives the ground. `block` earns them because it is the
13
+ genre those figures are actually authored in; a state's box, a flowchart
14
+ stage's box and a device's box have no extent that means anything, so a line
15
+ drawn 60% down one asserts nothing a reader can read.
16
+
17
+ Load `../scene.md` first — this file adds two words to `block`'s vocabulary
18
+ and changes none of it.
19
+
20
+ ```figdown
21
+ figdown 0.1 block
22
+ group pool "Storage pool"
23
+ node used "In use" in=pool
24
+ threshold "High watermark = stop writes" in=pool offset=80%
25
+ threshold "Low watermark = resume writes" in=pool offset=45%
26
+ band "Reserved" 15% in=pool fill=#fee2e2
27
+ band "Shared quota" 20..70% in=pool extend=down
28
+ ```
29
+
30
+ - `threshold` needs a quoted label **and** `offset=<0..100>%`, both mandatory
31
+ — the `%` included. There is no `value=` and no `ref=`.
32
+ - `band` needs a quoted label **first**, then either one percentage (a size)
33
+ or an explicit `<a>..<b>%` range. `..` is the language's ONE range
34
+ separator; the hyphen form is a line error. `extend=up|down|left|right`, and `up`
35
+ is the default.
36
+ - Both attach to a node or a group via `in=`.
37
+ - **Attach at the scope the fact belongs to.** A limit shared by everything in
38
+ a group is one group-level marker; a fact true of one element attaches to
39
+ that element. The renderer may draw the two the same way — the difference is
40
+ in the text, and the text is the knowledge.
41
+
42
+ ## The label is the knowledge, and `offset=` is not a value
43
+
44
+ `offset=` is a fraction of the target's **rendered extent** — where to paint
45
+ the line — **not a value of any quantity**. The target declares no scale, so
46
+ there is no conversion and nothing to convert. Put the number in the label or
47
+ it is not in the document.
48
+
49
+ **This is the trap, and it is worth naming.** In the domain these figures
50
+ usually come from, a threshold is a queue depth **with a numeric value**: an
51
+ active-queue-management minimum and maximum threshold (RFC 2309, RFC 7567)
52
+ is a number of packets or bytes. FigDown borrowed the word from exactly that
53
+ domain and did **not** borrow the attribute the domain defines it by. So
54
+ `threshold "Max cap" in=q offset=80%` invites a reader to take 80% as the
55
+ threshold value. It is a paint position. Write
56
+ `threshold "Max cap = 800 KB" in=q offset=80%` and the figure says what you
57
+ mean.
58
+
59
+ The same holds for `band`: a band is a range over that same scale-free
60
+ extent. Its label is the whole of what it asserts.
@@ -0,0 +1,32 @@
1
+ # Charts from a table — `chart`, `type=` (EXPERIMENTAL)
2
+
3
+ Outside the v0.1 conformance surface and outside the compatibility promise:
4
+ the engine accepts it, but it may change or be withdrawn in a later `0.x`,
5
+ and the parser emits no warning. If the figure must be portable, keep the
6
+ table and drop the chart.
7
+
8
+ **`chart` is composition, not subject vocabulary.** It says nothing about
9
+ what the figure is *of*, so it is not one of the words a genre declares for
10
+ its own domain — it is legal wherever a `table` region is legal, which is the
11
+ `table` genre and every scene genre, and it means the same thing in all of
12
+ them.
13
+
14
+ ```figdown
15
+ figdown 0.1 table
16
+ table t "Throughput"
17
+ | Port | Gbps |
18
+ |------|------|
19
+ | 1 | 25 |
20
+ | 2 | 40 |
21
+ chart t type=bar3d
22
+ ```
23
+
24
+ - `chart <table-id>` is the whole grammar. It draws from a `table` declared
25
+ in the same document and is defined by that reference, so the id must
26
+ already exist.
27
+ - `type=` is the only option key and `bar3d` is its only value.
28
+ - A scene document can carry both halves: open a `table` region inside it for
29
+ the numbers, then `chart` that id.
30
+
31
+ For anything a chart library would do better, keep the raster and put the
32
+ numbers in a table (`../transcribe.md`).
@@ -1,16 +1,39 @@
1
- # Genre `flowchart` — EXPERIMENTAL / EXPERIMENTAL
1
+ # Genre `flowchart` — EXPERIMENTAL
2
2
 
3
3
  The genre itself is outside the v0.1 conformance surface: it may change or be
4
- withdrawn in a later `0.x`. A portable figure uses `block` instead.
4
+ withdrawn in a later `0.x` without a migration entry. A portable figure uses
5
+ `block` instead.
5
6
 
6
- Load `../scene.md` first — `flowchart` reuses the whole scene vocabulary
7
- (`node`, `group`, `external`, `edge`, `flow`, `rank`) with the same meanings,
8
- and adds the three role keywords below. `flow down` is the default here.
7
+ Use `flowchart` when the figure is a **procedure**: stages performed in
8
+ order, with branches. `flow down` is the default here.
9
9
 
10
- ## Three role keywords
10
+ **This file is `flowchart`'s whole vocabulary.** Load it and `../layout.md`
11
+ and you can author in this genre; do not load another genre's file, because
12
+ another genre's declaration of a word is not authority for this one. In
13
+ particular this genre declares **no container**: there is no `group`, and
14
+ `in=` and `gap=` are **not this genre's option keys** — `in=` named a `group`
15
+ id and nothing else, so it went out with `group` rather than stay accepted with
16
+ no value that could resolve. Writing either is a line error that names the
17
+ withdrawal. Say what a container would have said with a `class`
18
+ whose label states it — it earns a legend entry and applies to every member at
19
+ once — or split the procedure into two figures joined by `external` endpoints.
20
+
21
+ **Under `figdown 0.2` the connector is `flowline`, not `edge`** — the word the
22
+ flowchart domain commonly uses for the symbol ISO 5807 §9.3.1 names *Line*.
23
+ Same operators, same labels, same option keys, same model; only the word
24
+ changed.
25
+
26
+ **The spelling is gated by the version you declare.** Under
27
+ `figdown 0.1 flowchart`, `edge` is still the word and `flowline` is a line
28
+ error naming the version; under `figdown 0.2 flowchart` it is the other way
29
+ round. Each version accepts exactly one spelling — two in one version would be
30
+ an alias. A document that writes `flowline` must declare `figdown 0.2`, and
31
+ `edge` under 0.1 stays legal until v1.0.
32
+
33
+ ## Three role keywords, and when to write `node` instead
11
34
 
12
35
  ```figdown
13
- figdown 0.1 flowchart
36
+ figdown 0.2 flowchart
14
37
  flow down
15
38
  terminator start "Request received"
16
39
  process parse "Parse body"
@@ -19,13 +42,13 @@ process apply "Apply change"
19
42
  process reject "Reject"
20
43
  terminator done "Response sent"
21
44
  node log "Audit store" shape=cylinder
22
- edge start -> parse
23
- edge parse -> valid
24
- edge valid -[yes]-> apply
25
- edge valid -[no]-> reject
26
- edge apply -> done
27
- edge reject -> log
28
- edge log -> parse
45
+ flowline start -> parse
46
+ flowline parse -> valid
47
+ flowline valid -[yes]-> apply
48
+ flowline valid -[no]-> reject
49
+ flowline apply -> done
50
+ flowline reject -> log
51
+ flowline log -> parse
29
52
  decision kind "Change kind?" shape=ellipse
30
53
  ```
31
54
 
@@ -44,13 +67,118 @@ wrong roughly one time in five.
44
67
  model still records the role. That is the escape hatch for matching a source
45
68
  drawing without lying about what the step is.
46
69
 
47
- **A bare `node` under `flowchart` states no role, and that is often correct.**
48
- Do not "upgrade" one to `process` unless it really is a step — a datastore, a
49
- wait, or an annotation is none of the three, and claiming a role would be a
50
- lie the reader cannot detect.
70
+ **Prefer a role. `node` is the fallback, not the default.** Reach for
71
+ `process`, `decision` or `terminator` first, and write `node` only when **the
72
+ source does not state which** the stage is.
73
+
74
+ That is what a bare `node` says here, and it is all it says: *the source does
75
+ not state the role.* ISO 5807 is a standard for **drawing** flowcharts, so it
76
+ has no "unclassified" — the person with the pen must draw some symbol, and
77
+ every drawn stage therefore has a classification. FigDown separates role from
78
+ geometry, so it can record the absence instead of inventing a role. A
79
+ transcriber who genuinely cannot tell a Process from a Predefined process
80
+ writes `node`, and that is the honest line.
81
+
82
+ **`node` is NOT the spelling for "an ISO symbol FigDown has not implemented."**
83
+ Nine ISO stage symbols have no word in this genre — Data (input/output),
84
+ Stored data, Predefined process, Preparation, Manual operation, Manual input,
85
+ Document, Parallel mode, Loop limit. That is a **gap in FigDown**, not a fact
86
+ about your figure, and writing a bare `node` for one of them files the gap as
87
+ if it were your judgement. When your source states a role this genre cannot
88
+ spell:
89
+
90
+ - write `node` — `process` would be a false claim, and nothing in the figure
91
+ could catch it;
92
+ - **name the ISO symbol in a comment** on the same line, so the classification
93
+ survives as text a reader can quote (a comment is never parsed, and never a
94
+ second semantic channel);
95
+ - report the gap, so it can be counted and closed.
96
+
97
+ ```figdown
98
+ node cfg "Read config file" # ISO 5807 Data (input/output) — no FigDown role
99
+ ```
100
+
101
+ So do not "upgrade" a `node` to `process` to make it look decided — and do not
102
+ write `node` because deciding was work. Those are different acts with the same
103
+ spelling, and only you know which one you did.
51
104
 
52
105
  **Label every exit from a `decision`.** Use the on-line label form
53
- (`edge d -[yes]-> x`) or an endpoint label; an unlabelled branch is a figure
54
- the reader has to guess at.
106
+ (`flowline d -[yes]-> x`) or an endpoint label; an unlabelled branch is a
107
+ figure the reader has to guess at.
55
108
 
56
109
  **There is no loop keyword.** A plain back-edge *is* the loop.
110
+
111
+ ## `external` — the boundary, which is not the `terminator`
112
+
113
+ ```figdown
114
+ figdown 0.2 flowchart
115
+ flow down
116
+ external ingress "Frame arrives on a port"
117
+ terminator start "Begin lookup"
118
+ process lookup "Look up destination"
119
+ external egress "Frame leaves the switch"
120
+ flowline ingress -> start
121
+ flowline start -> lookup
122
+ flowline lookup -> egress
123
+ ```
124
+
125
+ `external` is an **out-of-figure endpoint**: the procedure connects to it and
126
+ this figure does not describe it. It is **never drawn** — the flowline simply
127
+ ends open — and it takes **no option key at all**. `terminator` is the
128
+ opposite: a drawn stage of this procedure, its start or its end.
129
+
130
+ So the question to ask is *whose* start it is. The first box of **this**
131
+ procedure is a `terminator`. A step that belongs to some other procedure, or
132
+ to the world outside the figure, is an `external`.
133
+
134
+ ISO 5807 §9.4.2 names this concept — its **Terminator** symbol is "an exit
135
+ to, or an entry from, the outside world" — and this genre already spells that
136
+ word `terminator` for the drawn start/end symbol, so the ISO term is taken
137
+ and `external` keeps FigDown's own spelling. Note what follows: ISO has no
138
+ word at all for a boundary that is *never drawn*, which is what `external`
139
+ is. The spelling is FigDown's, not ISO's, and this file is the only place
140
+ that says so.
141
+
142
+ ## Presentation, layout intent and quoting
143
+
144
+ `flow` takes `flow right|down|left|up` — one of the four, once per document,
145
+ before the stages. `flow down` is this genre's default. `rank a,b,c` pulls
146
+ peers onto one row or column: ONE comma-delimited token, no spaces.
147
+
148
+ Values from a fixed list are written **bare**, never quoted: `shape=box`,
149
+ `style=dashed`, `flow down`. So are ids. Labels always take quotes.
150
+
151
+ `shape=` is pure geometry — `shape=box` (the default), `shape=rounded`,
152
+ `shape=circle`, `shape=ellipse`, `shape=diamond`, `shape=cylinder`, and
153
+ nothing else. `style=` takes `style=solid`, `style=dashed` or `style=dotted`.
154
+
155
+ `class` declares what a colour **means**, and colour that means something
156
+ must be a `class`:
157
+
158
+ ```figdown
159
+ figdown 0.2 flowchart
160
+ class yes "Yes path" stroke=#16a34a
161
+ class no "No path" stroke=#dc2626
162
+ decision check "Valid?"
163
+ process accept "Accept"
164
+ process reject "Reject"
165
+ flowline check -> accept class=yes
166
+ flowline check -> reject class=no
167
+ ```
168
+
169
+ A class a flowline joins MUST declare `stroke=` or `style=`; a `fill=`-only
170
+ class joined by one is an error naming `stroke=`. `fill=` and `stroke=` are
171
+ legal on any drawn element, but a flowline has no interior, so `fill=` on one
172
+ is an error.
173
+
174
+ The connector's operators are `->`, `<-`, `--` and `<->`. In a procedure you
175
+ almost always want `->`: a step follows another step in one direction, and an
176
+ undirected or two-way flowline asserts an order the reader cannot recover.
177
+
178
+ Endpoint labels nest (`flowline a [retry 1] -- b`); quote them when they hold
179
+ a line break or an unbalanced bracket. A `#` inside a bracket label is
180
+ ordinary text and needs no quotes, because `[ ]` is a verbatim region.
181
+
182
+ Under `figdown 0.3` any of these lines may carry `note=`, a drawn aside in
183
+ prose: `process parse "Parse body" note="…"`. It is never parsable and never
184
+ where a structural fact lives.
@@ -0,0 +1,138 @@
1
+ # Genre `statechart` — EXPERIMENTAL
2
+
3
+ The genre itself is outside the conformance surface: it may change or be
4
+ **withdrawn** in a later `0.x` without a migration. A portable figure uses
5
+ `block` instead — that is what these figures were spelled as before `figdown
6
+ 0.2`, and the rewrite back is one line.
7
+
8
+ **It needs `figdown 0.2` on line 1.** `figdown 0.1 statechart` is a line
9
+ error, and a deliberately named one: `genre "statechart" requires figdown 0.2`.
10
+ An engine may not read a `0.1` document as though it were `0.2`.
11
+
12
+ ## The vocabulary, which is two words
13
+
14
+ **This file is `statechart`'s whole vocabulary.** Load it and `../layout.md`
15
+ and you can author in this genre; do not load another genre's file, because
16
+ another genre's declaration of a word is not authority for this one.
17
+
18
+ This genre declares **two** words of its own — a node is a **`state`** and a
19
+ connector is a **`transition`**, both taken from OMG UML 2.5.1 §14 — and
20
+ **that is the entire list**. It names no container, no boundary, no marker,
21
+ no band and no bundle. Writing one of those here is a line error that states
22
+ the ground rather than guessing at a spelling, and `node` or `edge` here is a
23
+ line error naming the word to write instead.
24
+
25
+ Three more words are legal because they are not about states at all:
26
+ `class` (what a colour means), `flow` and `rank` (layout intent). Nothing in
27
+ UML competes for them.
28
+
29
+ Two consequences worth stating, because an author arriving from another genre
30
+ will reach for both:
31
+
32
+ - **There is no grouping construct.** `in=` and `gap=` are **not this genre's
33
+ option keys** — writing either is a line error that names the withdrawal.
34
+ A composite state, a region and a history are things this genre cannot
35
+ express; a `class` can say two states share a mode, and that is a different
36
+ and weaker claim. Do not fake a superstate. **`in=`'s spelling is RESERVED
37
+ here**: if composite states are ever earned, they arrive as nesting on
38
+ `state` through this exact key, with a `state` id as its value (UML 2.5.1
39
+ §14.2.3.4) — which is why the key was withdrawn rather than left pointing at
40
+ a container.
41
+ - **There is no out-of-figure endpoint.** A transition to something the
42
+ figure does not describe has no spelling. Draw the state, or leave the
43
+ transition out and say so in the prose beside the figure.
44
+
45
+ **`external` in particular is not available, and it is reserved.** UML 2.5.1
46
+ §14 already defines `TransitionKind` as `external | internal | local`, where
47
+ an *external* transition is one that exits and re-enters its source state,
48
+ firing its exit and entry behaviours. That is a defined term of art in this
49
+ genre's own source standard. If `external` ever appears in `statechart` it
50
+ will mean what UML means by it, never "an endpoint outside the figure".
51
+
52
+ `flow right` is the default, as in `block`; `flowchart`'s `down` default does
53
+ **not** apply here.
54
+
55
+ **Why this genre loses `node` and `flowchart` keeps it:** a statechart has
56
+ exactly one kind of node, so there is nothing to leave unstated and `state`
57
+ gives up nothing. A flowchart stage can have a role **the source does not
58
+ state**, and there `node` is the only way to say so without asserting a role
59
+ the source never gave.
60
+
61
+ ```figdown
62
+ figdown 0.2 statechart
63
+ title "Turnstile"
64
+ class mode "A mode of the mechanism" fill=#e0e7ff
65
+ state locked "LOCKED" class=mode
66
+ state unlocked "UNLOCKED" class=mode
67
+ transition locked -[coin]-> unlocked
68
+ transition unlocked -[push]-> locked
69
+ transition locked -[push]-> locked # self-transition: the event happens
70
+ # and the machine stays
71
+ flow right
72
+ ```
73
+
74
+ `flow` takes `flow right|down|left|up` — one of the four, once per document.
75
+ `rank a,b` pulls peers onto one row or column: ONE comma-delimited token, no
76
+ spaces. Values from a fixed list are bare (`shape=box`, `style=dashed`,
77
+ `flow right`); labels always take quotes.
78
+
79
+ `shape=` is pure geometry and changes only the drawing — `shape=box` (the
80
+ default), `shape=rounded`, `shape=circle`, `shape=ellipse`, `shape=diamond`,
81
+ `shape=cylinder`. `style=` takes `style=solid`, `style=dashed` or
82
+ `style=dotted`. A class a transition joins MUST declare `stroke=` or
83
+ `style=`; `fill=` on a transition is an error, because a line has no
84
+ interior. Under `figdown 0.3` a line may carry `note=`, a drawn aside in
85
+ prose — never parsable, never where a structural fact lives.
86
+
87
+ ## What the genre adds, which is a reading and not a syntax
88
+
89
+ Everything above parses identically under `block`. The genre earns its
90
+ existence in what you are entitled to conclude from it.
91
+
92
+ **Under `statechart`, and not under `flowchart` or `block`:**
93
+
94
+ - **Every top-level `state` is a state** — a *mode the machine is in*, not a
95
+ step it performs. It has duration; the machine sits in it until something
96
+ happens.
97
+ - **Every `transition` is a transition**, and its direction is the direction of the
98
+ change. An undirected `--` between two states asserts a relationship the
99
+ genre has no reading for; do not read it as a transition in both directions.
100
+ - **A mid label is a transition inscription** — the trigger, and optionally a
101
+ guard and an effect — as free text. There is no structure in it. Quote it,
102
+ attribute it to its edge, and stop; you may not parse `event / action` into
103
+ fields, because nothing in the language promises that shape.
104
+ - **A self-loop is "the event occurs and the machine stays"**, which is a
105
+ fact about the machine. Under `flowchart` the same drawing is a *poll* —
106
+ "check again" — and that is a different claim entirely.
107
+ - **The set of states is closed.** The nodes are all the modes the figure
108
+ asserts; a state not drawn is not asserted to exist.
109
+
110
+ **Still not conclusible, and the list is the same as everywhere else:**
111
+
112
+ - **No current state.** Nothing in a figure says which state the machine is
113
+ in now. A fill colour does not, a `class` does not, position does not.
114
+ - **No initial and no final state.** The language has no vocabulary for
115
+ either at `figdown 0.2` — deliberately, pending evidence, and **no `start`
116
+ keyword was added**: ISO's terminator symbol is *both* ends distinguished by
117
+ its label, so there is no word to borrow, and a reader who lacks one answers
118
+ "unstated" rather than answering wrongly. A state with no incoming
119
+ transition is *drawn* that way; it is not *declared* initial, and reading it
120
+ as initial is inventing a fact.
121
+ - **No ordering among transitions.** Peer edges have no sequence, and an
122
+ ordinal in a label is naming, not order.
123
+ - **No hierarchy, no regions, no history.** There is no grouping construct to
124
+ misuse for one.
125
+ - **Nothing from the layout zone.** `pin` is arrangement for humans; skip it.
126
+
127
+ **Reclassifying costs more than one line now.** Changing `flowchart` to
128
+ `statechart` on line 1 no longer suffices: every `flowline` becomes a
129
+ `transition` and every `node` a `state`. Run `tools/migrate-figdown.js` — it
130
+ rewrites both, scoped by the header genre.
131
+
132
+ **Do not upgrade a `flowchart` to a `statechart` on structure.** It was
133
+ measured: cycles do not separate the two (of the state machines and the
134
+ flowcharts in this project's own corpus, both are usually cyclic), and
135
+ self-loops appear in almost none. A retry loop drawn as a flowchart is exactly
136
+ the figure that reads wrong as a statechart. **Only the declared genre says
137
+ which it is** — and a title that contains the words "state machine" does not,
138
+ because a polling flowchart is routinely titled that way.
@@ -1,4 +1,4 @@
1
- # Genre `timing` — EXPERIMENTAL / EXPERIMENTAL
1
+ # Genre `timing` — EXPERIMENTAL
2
2
 
3
3
  The genre itself is outside the v0.1 conformance surface: it may change or be
4
4
  withdrawn in a later `0.x`. There is no portable alternative — a waveform
@@ -1,38 +1,164 @@
1
- # Genre `topology` — EXPERIMENTAL / EXPERIMENTAL
1
+ # Genre `topology` — EXPERIMENTAL
2
2
 
3
3
  The genre itself is outside the v0.1 conformance surface: it may change or be
4
- withdrawn in a later `0.x`. A portable figure uses `block` instead — the
5
- vocabulary is identical, the defaults are identical, and `block` is inside
6
- the compatibility promise.
4
+ withdrawn in a later `0.x` without a migration entry. A portable figure uses
5
+ `block` instead, which is inside the compatibility promise.
7
6
 
8
- Load `../scene.md` first. `topology` adds **no keyword of its own and changes
9
- no default**: it is the scene vocabulary under a name that says the figure is
10
- a network of peers rather than a pipeline of stages. What it carries is the
11
- intent which matters to a reader and to nothing else.
7
+ Use `topology` when the figure is a **network of devices and the links
8
+ between them** fabrics, overlays, access layers, peering diagrams. The
9
+ nodes are devices or endpoints and the edges are physical or logical links,
10
+ not dataflow steps. That intent is what the genre carries; it matters to a
11
+ reader and to nothing else.
12
+
13
+ **This file is `topology`'s whole vocabulary.** Load it and `../layout.md`
14
+ and you can author in this genre; do not load another genre's file, because
15
+ another genre's declaration of a word is not authority for this one. Words
16
+ `topology` does **not** declare — and there are several a `block` author
17
+ would reach for — are line errors that say so and give the ground.
18
+
19
+ ## The vocabulary, in one figure
12
20
 
13
21
  ```figdown
14
22
  figdown 0.1 topology
15
23
  title "Leaf-spine fabric"
16
- flow right
24
+
25
+ flow right # reading direction; ONE per document,
26
+ # before the nodes
27
+
17
28
  node s1 "Spine 1"
18
29
  node s2 "Spine 2"
19
- group pod "Pod A"
20
- node l1 "Leaf 1" in=pod
21
- node l2 "Leaf 2" in=pod
22
- external wan "To WAN"
23
- edge l1 -- s1
30
+
31
+ group pod "Pod A" gap=0 # a container; gap=0 packs its members
32
+ node l1 "Leaf 1" in=pod # flush against each other. The id is
33
+ node l2 "Leaf 2" in=pod # required and exists only so other lines
34
+ # can name it
35
+
36
+ node fw "Firewall" shape=rounded
37
+ node store "Config store" shape=cylinder style=solid
38
+
39
+ external wan "To WAN" # outside the figure. NEVER drawn as a
40
+ # shape — the link simply ends open there
41
+
42
+ edge l1 -- s1 # ops: -> <- -- <->
24
43
  edge l1 -- s2
25
44
  edge l2 -- s1
26
45
  edge l2 -- s2
27
- edge s1 -- wan
28
- class uplink "40G uplink" stroke=#0284c7
46
+ edge s1 -> wan # arrow points at `wan`
47
+ edge wan <- s2
48
+ edge fw <-> store
49
+ edge l1 -[lacp]-> fw # on-line label splits the operator
50
+ edge l2 [ge-0/0/1] -- [xe-0/0/0] fw # endpoint labels: ports, roles
51
+ edge s1 -- s2 style=dotted # spines not cabled to each other
52
+
53
+ rank l1,l2 # pull peers onto one row/column. ONE
54
+ # comma-delimited token, no spaces
55
+
56
+ class uplink "Uplink, 40G" stroke=#0284c7
29
57
  edge l2 -- s2 class=uplink
58
+
59
+ bundle lag1 "LAG to the leaf pair" l1--s1,l2--s1
60
+ ```
61
+
62
+ `flow` takes `flow right|down|left|up` — one of the four, once per document.
63
+
64
+ Values from a fixed list are written **bare**, never quoted: `shape=box`,
65
+ `style=dashed`, `flow down`, `gap=0`. So are ids. Labels are the opposite
66
+ and always take quotes.
67
+
68
+ `shape=` is **pure geometry, no domain nouns** — `shape=box` (the default),
69
+ `shape=rounded`, `shape=circle`, `shape=ellipse`, `shape=diamond`,
70
+ `shape=cylinder`, and nothing else. There is no router symbol, no switch
71
+ symbol and no cloud: a cloud in a source drawing is an ellipse or a group,
72
+ with what it *is* — "the internet", "the transit provider" — written in the
73
+ label. `style=` takes `style=solid`, `style=dashed` or `style=dotted`.
74
+
75
+ `class` declares what a colour **means** and is the only way to make colour
76
+ carry meaning; a class an edge joins MUST declare `stroke=` or `style=`, and
77
+ a `fill=`-only class joined by an edge is an error naming `stroke=`. `fill=`
78
+ and `stroke=` are legal on any element, but an `edge` has no interior, so
79
+ `fill=` on one is an error.
80
+
81
+ Endpoint labels nest (`edge a [xe-0/0/0:1] -- b`); quote them when they hold
82
+ a line break or an unbalanced bracket (`edge a ["slot\n1/1"] -- b`). A `#`
83
+ inside a bracket label is ordinary text and needs no quotes —
84
+ `edge a -[hop #1]-> b` — because `[ ]` is a verbatim region.
85
+
86
+ Under `figdown 0.3` any of these lines may carry `note=`, a drawn aside in
87
+ prose: `node s1 "Spine 1" note="…"`, `edge l1 -- s1 note="…"`,
88
+ `title "Fabric" note="…"`. It is never parsable and never where a structural
89
+ fact lives.
90
+
91
+ ## `group` is not a multicast group
92
+
93
+ `group` here is a **container**: a box drawn round its members, one nesting
94
+ level deep — a pod, a rack, a site, a tenant. It is not IGMP or MLD
95
+ membership, and `group g "224.0.0.5"` does not say what a reader of that
96
+ address expects.
97
+
98
+ The spelling stays anyway, because in this domain every synonym is more
99
+ taken than the word is: `zone` belongs to DNS and to firewalls, `cluster` to
100
+ route reflection (RFC 4456), `domain` to inter-domain traffic engineering
101
+ (RFC 7926), `area` to OSPF, `site` to EVPN. Trading a soft collision for a
102
+ hard one is a bad trade — and this collision **is** soft, because the drawing
103
+ contradicts the wrong reading: a multicast group is never drawn as a box
104
+ round its members.
105
+
106
+ ## `external` is not an external route
107
+
108
+ `external` is an **out-of-figure endpoint**: something this figure talks to
109
+ but does not describe. It is never drawn, it takes **no option key at all**,
110
+ and the link to it simply ends open.
111
+
112
+ It is not an external route — not an OSPF Type 5 or Type 7 LSA, not an eBGP
113
+ peer, not "the external interface". Same soft collision, same reason for
114
+ keeping the spelling: an `external` is never drawn at all, so a reader who
115
+ starts with the routing sense finds nothing on the page to sustain it. Where
116
+ the figure means *the router that faces outside*, that router is a `node`
117
+ with a label saying so; `external` is for what the figure leaves out.
118
+
119
+ ## `bundle` — a LAG, an ECMP set, an Ethernet Segment
120
+
121
+ ```figdown
122
+ figdown 0.1 topology
123
+ node leaf "Leaf"
124
+ node s1 "Spine 1"
125
+ node s2 "Spine 2"
126
+ edge leaf -- s1
127
+ edge leaf -- s2
128
+ bundle ecmp "ECMP uplink set, 2 × 100G" leaf--s1,leaf--s2
30
129
  ```
31
130
 
32
- The constructs most topology figures reach for next link bundles for LAGs
33
- and ECMP sets, overlay planes for tunnels, markers and zone bands are in
34
- `constructs.md`, and every one of them is EXPERIMENTAL too.
131
+ A **link bundle**: a set of parallel links between the same pair of devices
132
+ operated as one logical link. Its referents are the ones this domain already
133
+ names a link aggregation group (IEEE 802.1AX), an equal-cost multipath
134
+ set, an EVPN Ethernet Segment — and the ring drawn round the member links is
135
+ the same thing the domain means. The domain reading and the drawn reading
136
+ are one reading; this is the cell where the word and the referent agree, and
137
+ it is why `topology` declares `bundle` and the general-purpose genre has no
138
+ use for it.
139
+
140
+ `bundle` is EXPERIMENTAL even inside this experimental genre. It is spelled
141
+ `bundle` and not after the standard's own noun deliberately: *aggregation*
142
+ is right for a link aggregation group and false for an equal-cost multipath
143
+ set and for an Ethernet Segment, so the standard's word cannot be taken
144
+ whole and an umbrella spelling is the honest one.
145
+
146
+ - The member list is ONE comma-delimited token, no spaces.
147
+ - Each member must name an edge that **already exists** and is unambiguous.
148
+ There is no way to address one of two parallel edges between the same
149
+ pair, so a bundle over parallel links cannot be spelled — declare the
150
+ members as distinct edges to distinct peers, as above.
151
+ - `bundle` takes `fill=`, `stroke=` and `style=`; the dashed ring is drawn
152
+ for you.
153
+
154
+ ## Two mistakes this genre invites
155
+
156
+ **A shared medium is not a star.** Flattening a bus, a broadcast segment, a
157
+ shared VLAN or a fabric's underlay into point-to-point links changes what the
158
+ figure asserts: the shared thing reaches every participant at once. Model it
159
+ as one intermediate `node` that fans out.
35
160
 
36
- The two mistakes this genre invites are both covered where they are owned:
37
- a shared medium modelled as a mesh (`../scene.md`, "A shared bus is not a
38
- star") and geography carried by the layout zone alone (`../layout.md`).
161
+ **Geography is not carried by the layout zone.** If where a device sits is
162
+ part of the claim a site, a rack, a country say it with a `group` and a
163
+ label. Delete every `pin` and the figure must still be the same figure
164
+ (`../layout.md`).