figdown 0.1.0-rc.1 → 0.1.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.
@@ -10,77 +10,82 @@ that would otherwise need OCR. The sixth — the complete TCP state machine —
10
10
  is the stress test: every state and every labelled transition of RFC 9293
11
11
  Figure 5 on one canvas.
12
12
 
13
- Each entry: the rendered SVG, the full `.fd`, one line on what the human sees,
14
- and questions an agent answers **from the text alone**. Every colour and
13
+ Each entry: the rendered SVG, the `.fd` itself with its comment banners
14
+ elided, one line on what the human sees, and questions an agent answers **from the text alone**. Every colour and
15
15
  dash is carried by a `class` whose label states its *meaning*; no meaning rides
16
- on geometry (`tools/r25-check.js --strict`) or presentation.
16
+ on geometry (`tools/strip-check.js --strict`) or presentation.
17
17
 
18
18
  Two of these figures — the TCP handshake and ARP resolution — are deliberately
19
- **mixed documents**: a scene block *and* a `table` block in one `.fd`, composed
20
- in document order (spec §8). This is the project's ruled answer for content the
21
- scene genre cannot yet carry first-class (per-endpoint lifeline state; a local
22
- cache transition): a second typed block carries it machine-readably, rather than
23
- letting it ride on geometry or absence.
19
+ **multi-section hybrids (`MULTI-FIGURE-DOCUMENTS`)**: a scene section *and* a later
20
+ `figdown 0.1 table` section in one `.fd`, stacked into one SVG.
21
+ Each section has one genre and its own id space (`GENRE-KEYWORD-ALLOWLIST` allowlist). This is the
22
+ main-standard answer for content the scene genre cannot yet carry first-class
23
+ (per-endpoint lifeline state; a local cache transition): a second section
24
+ carries it machine-readably, rather than letting it ride on geometry or
25
+ absence. Nested `table` under a single scene header is legacy, not the taught
26
+ path.
24
27
 
25
28
  ---
26
29
 
27
30
  ## 1. TCP header — bit-exact machine-readable layout (`bitfield`)
28
31
 
29
- ![TCP header](examples/showcase/tcp-header.svg)
32
+ ![TCP header](../examples/showcase/tcp-header.svg)
30
33
 
31
34
  ```figdown
32
35
  figdown 0.1 bitfield
33
- title TCP Header
36
+ title "TCP Header"
34
37
 
35
- bitfield tcp "TCP Header (RFC 9293)" unit=32 numbering=msb0
38
+ bitfield tcp "TCP Header (RFC 9293)" word=32 numbering=msb0
36
39
 
37
40
  field "Source Port" 16
38
41
  field "Destination Port" 16
39
42
  field "Sequence Number" 32
40
43
  field "Acknowledgment Number" 32
41
- field "Data Offset" 4 note="header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)"
42
- field Reserved 4 note="must be zero"
43
- field CWR 1
44
- field ECE 1
45
- field URG 1
46
- field ACK 1
47
- field PSH 1
48
- field RST 1
49
- field SYN 1 color=#fee2e2
50
- field FIN 1 color=#fee2e2
51
- field Window 16 note="receive window size in bytes (subject to window scaling option)"
52
- field Checksum 16
44
+ field "Data Offset" 4 description="header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)"
45
+ field "Reserved" 4 description="must be zero"
46
+ field "CWR" 1
47
+ field "ECE" 1
48
+ field "URG" 1
49
+ field "ACK" 1
50
+ field "PSH" 1
51
+ field "RST" 1
52
+ field "SYN" 1 fill=#fee2e2
53
+ field "FIN" 1 fill=#fee2e2
54
+ field "Window" 16 description="receive window size in bytes (subject to window scaling option)"
55
+ field "Checksum" 16
53
56
  field "Urgent Pointer" 16
54
- field Options 32 optional note="present iff Data Offset > 5; padded to a 32-bit boundary"
55
- field Data * note="variable-length payload; length from the IP datagram, not the TCP header"
57
+ field "Options" 32 present="Data Offset > 5" description="padded to a 32-bit boundary"
58
+ field "Data" * description="variable-length payload; length from the IP datagram, not the TCP header"
56
59
  ```
57
60
 
58
61
  **Human sees:** the RFC-style header — a 32-bit ruler, every field to width,
59
- the eight flags as individual bit cells, Options dashed (optional).
62
+ the eight flags as individual bit cells, Options dashed with a caption stating
63
+ its condition (`present=`).
60
64
 
61
65
  **An agent answers from the text alone:**
62
66
 
63
67
  - *Q: Which bits carry the window size?* A: bits 112–127 — the sum of preceding
64
68
  widths (96 for rows 1–3, then Data Offset 4 + Reserved 4 + eight 1-bit flags)
65
69
  places `Window` next. Derived from field order/width, not the drawing.
66
- - *Q: Is SYN one bit or a byte?* A: one bit (`field SYN 1`); each of the eight
70
+ - *Q: Is SYN one bit or a byte?* A: one bit (`field "SYN" 1`); each of the eight
67
71
  flags is a distinct, individually addressable 1-bit field.
68
72
  - *Q: When are Options present; what is the minimum header length?* A: present
69
- iff Data Offset > 5 (`optional` + note); minimum header = 5 × 32-bit words =
70
- 20 bytes (Data Offset note).
73
+ iff Data Offset > 5 (`present="Data Offset > 5"`, which also draws as a
74
+ caption under the block); minimum header = 5 × 32-bit words = 20 bytes
75
+ (Data Offset field's `description=`).
71
76
 
72
77
  ---
73
78
 
74
79
  ## 2. TCP three-way handshake — labelled directed exchange (`topology`)
75
80
 
76
- ![TCP handshake](examples/showcase/tcp-handshake.svg)
81
+ ![TCP handshake](../examples/showcase/tcp-handshake.svg)
77
82
 
78
83
  ```figdown
79
84
  figdown 0.1 topology
80
- title TCP Three-Way Handshake
85
+ title "TCP Three-Way Handshake"
81
86
 
82
- class setup "Connection-setup segment — carries SYN and/or ACK control flags" color=#2563eb
83
- class data "Segment that also carries application data (piggybacked on the ACK)" color=#16a34a
87
+ class setup "Connection-setup segment — carries SYN and/or ACK control flags" stroke=#2563eb
88
+ class data "Segment that also carries application data (piggybacked on the ACK)" stroke=#16a34a
84
89
 
85
90
  node client "Client (active open)"
86
91
  node server "Server (passive open, LISTEN)"
@@ -91,6 +96,8 @@ edge client -[1: SYN seq=x]-> server class=setup
91
96
  edge server -[2: SYN,ACK seq=y ack=x+1]-> client class=setup
92
97
  edge client -[3: ACK ack=y+1 (+ data)]-> server class=data
93
98
 
99
+ # --- panel: separate genre section (multi-section `MULTI-FIGURE-DOCUMENTS`) ---
100
+ figdown 0.1 table
94
101
  table state "Endpoint state after each segment (RFC 9293 §3.3.2 state names)"
95
102
  | Segment | Client state | Server state |
96
103
  |--------------------|-----------------------|---------------------------|
@@ -100,11 +107,12 @@ table state "Endpoint state after each segment (RFC 9293 §3.3.2 state names)"
100
107
  | 3: ACK → | ESTABLISHED | ESTABLISHED on receipt |
101
108
  ```
102
109
 
103
- This is a **mixed document**: a `topology` scene *and* a `table` in one `.fd`,
104
- composed in document order (spec §8). The canonical RFC 9293 figure carries the
105
- per-endpoint state on the lifelines; the topology genre has no lifeline-state
106
- construct, so the companion table carries those states machine-readably —
107
- composition is the ruled interim until the sequence genre lands.
110
+ This is a **multi-section hybrid**: a `topology` scene section *and* a
111
+ `table` section in one `.fd`, stacked into one SVG (`MULTI-FIGURE-DOCUMENTS`). The canonical RFC
112
+ 9293 figure carries the per-endpoint state on the lifelines; the topology
113
+ genre has no lifeline-state construct, so the companion table section carries
114
+ those states machine-readably — multi-section composition is the main-standard
115
+ interim until the sequence genre lands.
108
116
 
109
117
  **Human sees:** two endpoints and three numbered segments — SYN out, SYN-ACK
110
118
  back, ACK (with piggybacked data) out — with a state table below tracking each
@@ -122,25 +130,25 @@ endpoint's TCP state segment by segment.
122
130
  the companion table carries the RFC 9293 lifeline states.
123
131
 
124
132
  > **Honest limit (stated in the source):** message *order* rides on the 1/2/3
125
- > label ordinals, not a first-class construct — R37 does not treat numbering as
126
- > semantics. The v0.2 sequence genre (OQ-S18) closes this; see
127
- > [EXPRESSING.md](EXPRESSING.md), "strict message ordering".
133
+ > label ordinals, not a first-class construct — `MEANING-RECOVERY-SOURCE` does not treat numbering as
134
+ > semantics. The v0.2 sequence genre (`FLOWCHART-GENRE-DESIGN`) closes this; see
135
+ > [expressing.md](expressing.md), "strict message ordering".
128
136
 
129
137
  ---
130
138
 
131
139
  ## 3. L2 switch forwarding decision — protocol logic, readable (`flowchart`)
132
140
 
133
- ![L2 forwarding logic](examples/showcase/l2-forwarding-logic.svg)
141
+ ![L2 forwarding logic](../examples/showcase/l2-forwarding-logic.svg)
134
142
 
135
143
  ```figdown
136
144
  figdown 0.1 flowchart
137
- title L2 Switch Forwarding Decision
145
+ title "L2 Switch Forwarding Decision"
138
146
 
139
- class forward "Forward — send the frame out the single learned egress port" color=#16a34a
140
- class flood "Flood — send out every port in the VLAN except the ingress port" color=#2563eb
141
- class filter "Filter — discard; destination is out the same port it arrived on" color=#dc2626
147
+ class forward "Forward — send the frame out the single learned egress port" fill=#16a34a
148
+ class flood "Flood — send out every port in the VLAN except the ingress port" fill=#2563eb
149
+ class filter "Filter — discard; destination is out the same port it arrived on" fill=#dc2626
142
150
 
143
- boundary wire "from ingress port"
151
+ external wire "from ingress port"
144
152
 
145
153
  node learn "Learn source MAC:\nupdate MAC table\n(SA -> ingress port)"
146
154
  node lookup "Look up destination MAC\nin MAC table"
@@ -180,13 +188,13 @@ is no unstated fourth path.
180
188
 
181
189
  ## 4. Ethernet II frame — byte order carried by syntax (`table`)
182
190
 
183
- ![Ethernet frame](examples/showcase/ethernet-frame.svg)
191
+ ![Ethernet frame](../examples/showcase/ethernet-frame.svg)
184
192
 
185
193
  ```figdown
186
194
  figdown 0.1 table
187
- title Ethernet II Frame — Untagged vs 802.1Q Tagged
195
+ title "Ethernet II Frame — Untagged vs 802.1Q Tagged"
188
196
 
189
- class tag "802.1Q VLAN tag (4 bytes), inserted after Source MAC; absent in an untagged frame" color=#dbeafe
197
+ class tag "802.1Q VLAN tag (4 bytes), inserted after Source MAC; absent in an untagged frame" fill=#dbeafe
190
198
 
191
199
  table eth "Ethernet II frame — column order = wire order (byte counts in the second header tier)"
192
200
  | Variant | Destination MAC | Source MAC | 802.1Q Tag | EtherType | Payload | FCS |
@@ -194,9 +202,9 @@ table eth "Ethernet II frame — column order = wire order (byte counts in the s
194
202
  |----------|-----------------|------------|-------------|-----------|-----------------|-----------|
195
203
  | Untagged | DA | SA | (none) | Type | Data | CRC-32 |
196
204
  | Tagged | DA | SA | TPID 0x8100 + PCP/DEI/VID | Type | Data | CRC-32 |
197
- colw 90 auto auto auto auto auto auto
198
- cell 1,4 class=tag
199
- cell 2,4 class=tag
205
+ width 90,auto,auto,auto,auto,auto,auto
206
+ cell (1,4) class=tag
207
+ cell (2,4) class=tag
200
208
  ```
201
209
 
202
210
  **Human sees:** the frame in wire order left-to-right, two variants (untagged /
@@ -211,32 +219,32 @@ cell 2,4 class=tag
211
219
  - *Q: Untagged vs tagged?* A: the tag column is `(none)` vs
212
220
  `TPID 0x8100 + PCP/DEI/VID` — absent, not zeroed (class label).
213
221
 
214
- The OQ-S24 sanctioned pattern: a byte-unit packet is a single-row `table`, so
222
+ The `BYTE-UNIT-PACKET-BLOCKS` sanctioned pattern: a byte-unit packet is a single-row `table`, so
215
223
  byte order lives in cell order (strip test survives), not a bit ruler.
216
224
 
217
225
  **One fact this figure cannot draw:** the FCS (CRC-32) is computed over
218
226
  Destination MAC through Payload inclusive — a coverage span across a
219
227
  *contiguous run of columns*. FigDown has no construct to annotate such a span
220
- (curly-brace grouping over a contiguous cell range is **OQ-S16**, v0.2), so the
228
+ (curly-brace grouping over a contiguous cell range is **`CONTIGUOUS-RANGE-GROUPING`**, v0.2), so the
221
229
  fact is stated in the `.fd` source comment and here in prose, never on geometry.
222
230
 
223
231
  ---
224
232
 
225
- ## 5. ARP resolution — boundary + class discipline (`topology`)
233
+ ## 5. ARP resolution — external + class discipline (`topology`)
226
234
 
227
- ![ARP resolution](examples/showcase/arp-resolution.svg)
235
+ ![ARP resolution](../examples/showcase/arp-resolution.svg)
228
236
 
229
237
  ```figdown
230
238
  figdown 0.1 topology
231
- title ARP Resolution
239
+ title "ARP Resolution"
232
240
 
233
- class bcast "Broadcast — delivered to every host on the LAN (dst MAC ff:ff:ff:ff:ff:ff)" color=#dc2626 style=dashed
234
- class ucast "Unicast — delivered only to the requester (dst MAC = A's MAC)" color=#16a34a
241
+ class bcast "Broadcast — delivered to every host on the LAN (dst MAC ff:ff:ff:ff:ff:ff)" stroke=#dc2626 style=dashed
242
+ class ucast "Unicast — delivered only to the requester (dst MAC = A's MAC)" stroke=#16a34a
235
243
 
236
244
  node a "Host A\nwants MAC for B's IP"
237
245
  node b "Host B\nowns the target IP"
238
246
 
239
- boundary lan "rest of the LAN\n(hosts C, D, ...)"
247
+ external lan "rest of the LAN\n(hosts C, D, ...)"
240
248
 
241
249
  flow right
242
250
 
@@ -244,6 +252,8 @@ edge a -[1: who-has B? (broadcast)]-> b class=bcast
244
252
  edge a -[1: heard, ignored]-> lan class=bcast
245
253
  edge b -[2: B is at aa:bb:cc:... A caches it]-> a class=ucast
246
254
 
255
+ # --- panel: separate genre section (multi-section `MULTI-FIGURE-DOCUMENTS`) ---
256
+ figdown 0.1 table
247
257
  table cache "Host A's ARP cache — before and after the exchange"
248
258
  | Moment | Entry |
249
259
  |--------------------|-----------------------------------------|
@@ -251,14 +261,14 @@ table cache "Host A's ARP cache — before and after the exchange"
251
261
  | after segment 2 | B's IP → aa:bb:cc:... , learned from the reply |
252
262
  ```
253
263
 
254
- This too is a **mixed document** (scene + table in one `.fd`, document order):
255
- the cache *transition* is the figure's actual outcome, but it is a local state
256
- change on A rather than a message, so the companion table carries the
257
- before/after cache state machine-readably.
264
+ This too is a **multi-section hybrid** (scene section + `table` section in one
265
+ `.fd`, stacked SVG): the cache *transition* is the figure's actual outcome, but
266
+ it is a local state change on A rather than a message, so the companion table
267
+ section carries the before/after cache state machine-readably.
258
268
 
259
269
  **Human sees:** A broadcasts who-has (reaching B; the rest of the LAN hears it
260
270
  and ignores it), B replies by unicast and A caches the binding — broadcast
261
- dashed-red, unicast green, "rest of the LAN" an open boundary — with a cache
271
+ dashed-red, unicast green, "rest of the LAN" an open `external` endpoint — with a cache
262
272
  table below showing the before/after state. The cache update is a local action
263
273
  on A, not a message, so it rides on the reply label, the cache table, and this
264
274
  prose rather than on an edge of its own.
@@ -271,30 +281,30 @@ prose rather than on an edge of its own.
271
281
  - *Q: Who receives the reply, and what does A do with it?* A: only Host A —
272
282
  reply edge `class=ucast`, directed `b -> a`; the label states A caches the
273
283
  IP → MAC binding on receipt.
274
- - *Q: Is "rest of the LAN" a participating host?* A: no — a `boundary`, stating
284
+ - *Q: Is "rest of the LAN" a participating host?* A: no — an `external`, stating
275
285
  only that the broadcast crosses the figure's edge.
276
286
  - *Q: What does A's cache hold before the exchange?* A: nothing — row 1 of the
277
287
  cache table (`before the exchange` → `(no entry for B's IP)`). Answerable only
278
288
  because the companion table carries the before-state explicitly.
279
289
 
280
290
  > **Honest limit:** step order (1/2) rides on label ordinals, pending the v0.2
281
- > sequence genre (OQ-S18).
291
+ > sequence genre (`FLOWCHART-GENRE-DESIGN`).
282
292
 
283
293
  ---
284
294
 
285
295
  ## 6. TCP connection state machine — every state, every transition (`flowchart`)
286
296
 
287
- ![TCP state machine](examples/showcase/tcp-state-machine.svg)
297
+ ![TCP state machine](../examples/showcase/tcp-state-machine.svg)
288
298
 
289
299
  ```figdown
290
300
  figdown 0.1 flowchart
291
- title TCP Connection State Machine (RFC 9293, Figure 5)
301
+ title "TCP Connection State Machine (RFC 9293, Figure 5)"
292
302
 
293
- class states "A node IS a TCP state (RFC 9293 §3.3.2); the two CLOSED nodes are one state — no node-identity construct yet (OQ-S26)" color=#eef2ff
294
- class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)" color=#2563eb
295
- class client "Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED" color=#dc2626
296
- class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" color=#16a34a
297
- class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)" color=#9333ea style=dashed
303
+ class states "A node IS a TCP state (RFC 9293 §3.3.2); the two CLOSED nodes are one state drawn twice — only this label says so" fill=#eef2ff
304
+ class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)" stroke=#2563eb
305
+ class client "Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED" stroke=#dc2626
306
+ class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" stroke=#16a34a
307
+ class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)" stroke=#9333ea style=dashed
298
308
 
299
309
  node closed "CLOSED" shape=rounded class=states
300
310
  node listen "LISTEN" shape=rounded class=states
@@ -336,30 +346,40 @@ edge estab -[rcv FIN / snd ACK]-> closewait class=server
336
346
  edge closewait -[CLOSE / snd FIN]-> lastack class=server
337
347
  edge lastack -[rcv ACK of FIN / x]-> closed2 class=server
338
348
 
339
- render
340
- pin closed at=394,20
341
- pin listen at=394,150
342
- pin synrcvd at=152,280
343
- pin synsent at=607,280
344
- pin estab at=376,410
345
- pin fw1 at=159,540
346
- pin closewait at=600,540
347
- pin fw2 at=159,670
348
- pin closing at=311,670
349
- pin lastack at=607,670
350
- pin timewait at=304,800
351
- pin closed2 at=614,800
352
- route closed -> synsent via=790,38;790,298
353
- route listen -> closed via=300,105
354
- route synsent -> closed via=640,120
349
+ layout
350
+ pin closed at=(394,20)
351
+ pin listen at=(394,170)
352
+ pin synrcvd at=(120,290)
353
+ pin synsent at=(700,290)
354
+ pin estab at=(376,410)
355
+ pin fw1 at=(159,540)
356
+ pin closewait at=(690,540)
357
+ pin fw2 at=(159,670)
358
+ pin closing at=(311,670)
359
+ pin lastack at=(690,670)
360
+ pin timewait at=(304,800)
361
+ pin closed2 at=(697,800)
355
362
  ```
356
363
 
357
- The `pin`/`route` block reproduces the canonical Figure 5 arrangement so a
364
+ The `pin` block reproduces the canonical Figure 5 arrangement so a
358
365
  reader who knows the RFC diagram recognises it — but it is **presentation
359
- only** (`r25-check --strict` passes): strip every line after `render` and all
366
+ only** (`strip-check --strict` passes): strip every line after `layout` and all
360
367
  11 states, all 21 transitions and their `event / action` labels survive on the
361
368
  `node`/`edge`/`class` lines. All meaning is in the edges; the pins are layout.
362
369
 
370
+ Three `path … points=` lines used to sit under those pins, bowing the active <!-- fence-check: skip -->
371
+ OPEN and the two aborts out to the margins. `EDGE-GEOMETRY-CONSTRUCTS` **withdrew the construct from
372
+ the language**, so those transitions now draw straight — which
373
+ put each of them beside its anti-parallel partner and stacked two pairs of
374
+ labels. The fix was content-zone geometry, not a substitute waypoint: rows 1
375
+ and 2 gained 20 px and 10 px and the right-hand column moved ~90 px out, which
376
+ separates the label midpoints. Worth noting what caught it — the linter scored
377
+ the figure **2 either way** and reported 0 crossings both times; only looking
378
+ at the render did (§7 of [layout.md](layout.md) makes the same
379
+ point). The part of the old arrangement that has no replacement — pulling a
380
+ specific edge into a channel of your choosing — is recorded as core §9
381
+ **`EDGE-IDENTITY-AND-GEOMETRY`**.
382
+
363
383
  **Human sees:** the whole RFC 9293 state diagram — CLOSED at top, the SYN pair
364
384
  flanking, ESTABLISHED centred, the active-close column falling left and the
365
385
  passive-close column right — four colour-coded transition families (blue setup,
@@ -372,10 +392,11 @@ arrow carrying its `event / action`.
372
392
  send?* A: `rcv FIN / snd ACK` — one edge label
373
393
  (`edge estab -[rcv FIN / snd ACK]-> closewait`).
374
394
  - *Q: From FIN-WAIT-1, how many ways lead to TIME-WAIT and under what
375
- conditions?* A: two. Directly, `rcv FIN,ACK / snd ACK` (the RFC Note-2
376
- transition, `fw1 -> timewait`); and via FIN-WAIT-2, `rcv ACK of FIN / x` then
377
- `rcv FIN / snd ACK` (`fw1 -> fw2 -> timewait`). A third path runs via CLOSING
378
- (`rcv FIN / snd ACK` then `rcv ACK of FIN / x`). All read off the edges.
395
+ conditions?* A: three. (1) Directly: `rcv FIN,ACK / snd ACK` (the RFC Note-2
396
+ transition, `fw1 -> timewait`). (2) Via FIN-WAIT-2: `rcv ACK of FIN / x` then
397
+ `rcv FIN / snd ACK` (`fw1 -> fw2 -> timewait`). (3) Via CLOSING:
398
+ `rcv FIN / snd ACK` then `rcv ACK of FIN / x` (`fw1 -> closing -> timewait`).
399
+ All read off the edges.
379
400
  - *Q: How long does TIME-WAIT last and what happens after?* A: `Timeout=2MSL`,
380
401
  then `delete TCB` → CLOSED (`timewait -[Timeout=2MSL / delete TCB]-> closed2`).
381
402
  - *Q: Which transitions belong to the typical server (passive-close) path?*
@@ -384,10 +405,10 @@ arrow carrying its `event / action`.
384
405
  class, whose label spells the path out.
385
406
 
386
407
  > **Honest limit (stated in the source):** a node *is* a state is not yet a
387
- > machine-declarable fact — FigDown has no state genre (the R49 §6 candidate),
408
+ > machine-declarable fact — FigDown has no state genre (the `GENRE-EARNING-THRESHOLD` §6 candidate),
388
409
  > so "this node is a protocol state" rides on the `states` class label, interim.
389
410
  > And CLOSED is drawn **twice** (top origin, bottom terminus) because FigDown
390
- > has no node-identity/alias construct (**OQ-S26**): the two `closed`/`closed2`
411
+ > has no node-identity/alias construct (**`IDENTITY-ASSERTION`**): the two `closed`/`closed2`
391
412
  > nodes are declared one state only by the shared class label, not machine-
392
413
  > readably. Everything else — all 11 states, all 21 labelled transitions
393
414
  > including the one the canonical figure omits in its own Note 2 — the edges
@@ -399,23 +420,24 @@ arrow carrying its `event / action`.
399
420
 
400
421
  Two figures depend on a convention the language does not yet make first-class:
401
422
  **message/step ordering** in the handshake and ARP is carried by numbering edge
402
- labels `1:`/`2:`/`3:`, which R37 treats as naming, not semantics — the v0.2
403
- sequence genre (OQ-S18) will fix it, and the sources say so. Relatedly, the RFC
423
+ labels `1:`/`2:`/`3:`, which `MEANING-RECOVERY-SOURCE` treats as naming, not semantics — the v0.2
424
+ sequence genre (`FLOWCHART-GENRE-DESIGN`) will fix it, and the sources say so. Relatedly, the RFC
404
425
  9293 handshake carries **per-endpoint lifeline state** (LISTEN, SYN-SENT, …)
405
- that the topology genre has no construct for; the companion state table carries
406
- it as composition (a ruled interim, again pending the sequence genre, OQ-S18).
407
- The Ethernet frame uses the **OQ-S24** byte-unit workaround (single-row `table`,
426
+ that the topology genre has no construct for; the companion state table section
427
+ carries it as multi-section composition (`MULTI-FIGURE-DOCUMENTS` main-standard interim, again
428
+ pending the sequence genre, `FLOWCHART-GENRE-DESIGN`).
429
+ The Ethernet frame uses the **`BYTE-UNIT-PACKET-BLOCKS`** byte-unit workaround (single-row `table`,
408
430
  not `bitfield`) so byte order rides on cell order, and one of its facts — the
409
431
  FCS **coverage span** over a contiguous run of columns (DA through Payload) —
410
- has no annotation construct yet (**OQ-S16**), so it rides on a source comment
432
+ has no annotation construct yet (**`CONTIGUOUS-RANGE-GROUPING`**), so it rides on a source comment
411
433
  and prose. The TCP state machine meets two more: **state-ness is not first-class**
412
434
  — a node *is* a state carries as the `states` class label until the state genre
413
- lands (the R49 §6 candidate) — and CLOSED is drawn **twice** because FigDown has
414
- no **node-identity/alias** construct (**OQ-S26**) to declare two nodes the same
435
+ lands (the `GENRE-EARNING-THRESHOLD` §6 candidate) — and CLOSED is drawn **twice** because FigDown has
436
+ no **node-identity/alias** construct (**`IDENTITY-ASSERTION`**) to declare two nodes the same
415
437
  entity; the shared class label asserts it instead. Every state and every labelled
416
438
  transition still rides on an edge, so those two are the *only* facts the figure
417
439
  cannot carry that the canonical drawing implies. All are documented in
418
- [EXPRESSING.md](EXPRESSING.md)'s "Known limits".
440
+ [expressing.md](expressing.md)'s "Known limits".
419
441
  That a figure format states its own limits — and that a reading agent recovers
420
442
  exactly what the text declares and nothing the geometry merely implies — is the
421
443
  claim these figures make.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figdown",
3
- "version": "0.1.0-rc.1",
3
+ "version": "0.1.0",
4
4
  "description": "Figures as text — one source, two readers: a closed, deterministic figure language for Markdown, readable by AI agents and humans alike",
5
5
  "license": "MIT",
6
6
  "author": "FigDown <hello@figdown.org>",
@@ -23,6 +23,30 @@
23
23
  "deterministic"
24
24
  ],
25
25
  "type": "commonjs",
26
+ "scripts": {
27
+ "gate:conformance": "node conformance/run.js",
28
+ "gate:conformance-experimental": "node conformance/run.js --experimental",
29
+ "gate:comment": "node tools/comment-check.js --strict",
30
+ "gate:editor": "node tools/editor-check.js --strict",
31
+ "gate:fence": "node tools/fence-check.js --strict",
32
+ "gate:page": "node tools/page-check.js",
33
+ "gate:shape": "node tools/shape-check.js --strict",
34
+ "gate:strip": "node tools/strip-check.js --strict",
35
+ "gate:layout": "node tools/layout-lint.js",
36
+ "gate:artifact": "node tools/artifact-check.js --strict",
37
+ "gate:dist": "node tools/dist-check.js --strict",
38
+ "gate:isolation": "node tools/isolation-check.js --strict",
39
+ "gate:boundary": "node tools/boundary-check.js --strict",
40
+ "gate:stability": "node tools/stability-check.js",
41
+ "gate:migrate": "node tools/migrate-check.js",
42
+ "gate:skill-coverage": "node tools/skill-coverage.js --strict",
43
+ "gate:legend": "node tools/legend-check.js --strict",
44
+ "gate:registry": "node tools/registry-check.js --strict",
45
+ "gate:capability": "node tools/capability-coverage.js --strict",
46
+ "gates:list": "node -e \"const s=require(process.cwd()+'/package.json').scripts||{};const g=Object.keys(s).filter(k=>k.startsWith('gate:'));if(!g.length){console.error('gates:list — no gate:* scripts found');process.exit(2);}if(process.env.GATES_JSON)console.log(JSON.stringify(g));else g.forEach(k=>console.log(k+' -> '+s[k]));\"",
47
+ "test": "node -e \"const s=require(process.cwd()+'/package.json').scripts||{};const sp=require('child_process').spawnSync;const g=Object.keys(s).filter(k=>k.startsWith('gate:'));if(!g.length){console.error('npm test — no gate:* scripts found; refusing to report success');process.exit(2);}const bad=[];g.forEach(k=>{const a=s[k].split(' ');const bin=a[0]==='node'?process.execPath:a[0];console.log('');console.log('=== GATE '+k+' ['+s[k]+'] ===');const r=sp(bin,a.slice(1),{stdio:'inherit'});if(r.error)console.error(' spawn error: '+r.error.message);const c=(r.status===null||r.status===undefined)?1:r.status;console.log('--- '+k+': '+(c===0?'PASS':'FAIL (exit '+c+')')+' ---');if(c!==0)bad.push(k+' ['+s[k]+'] exit '+c);});console.log('');console.log('=== GATE SUMMARY: '+(g.length-bad.length)+'/'+g.length+' passed ===');if(bad.length){console.log('FAILED GATES:');bad.forEach(b=>console.log(' '+b));process.exit(1);}console.log('ALL GATES GREEN');\"",
48
+ "gate:reference": "node tools/reference-gate.js"
49
+ },
26
50
  "main": "dist/figdown.js",
27
51
  "module": "dist/figdown.mjs",
28
52
  "exports": {
@@ -37,11 +61,13 @@
37
61
  "files": [
38
62
  "dist/figdown.js",
39
63
  "dist/figdown.mjs",
64
+ "skill/figdown/SKILL.md",
65
+ "skill/figdown/reference/",
40
66
  "skill/figdown/figdown.html",
41
67
  "skill/figdown/build-svg.js",
42
- "SHOWCASE.md",
43
- "EXPRESSING.md",
44
- "LAYOUT-GUIDE.md",
68
+ "guide/showcase.md",
69
+ "guide/expressing.md",
70
+ "guide/layout.md",
45
71
  "examples/evpn-fabric.svg",
46
72
  "examples/showcase/*.svg"
47
73
  ],