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.
- package/README.md +253 -171
- package/dist/figdown.js +2601 -580
- package/dist/figdown.mjs +2601 -580
- package/examples/evpn-fabric.svg +16 -11
- package/examples/showcase/arp-resolution.svg +18 -16
- package/examples/showcase/ethernet-frame.svg +12 -10
- package/examples/showcase/l2-forwarding-logic.svg +20 -16
- package/examples/showcase/tcp-handshake.svg +14 -13
- package/examples/showcase/tcp-header.svg +26 -19
- package/examples/showcase/tcp-state-machine.svg +57 -44
- package/guide/expressing.md +160 -0
- package/guide/layout.md +635 -0
- package/{SHOWCASE.md → guide/showcase.md} +130 -108
- package/package.json +30 -4
- package/skill/figdown/SKILL.md +173 -0
- package/skill/figdown/build-svg.js +51 -16
- package/skill/figdown/figdown.html +3005 -783
- package/skill/figdown/reference/bitfield.md +130 -0
- package/skill/figdown/reference/experimental/constructs.md +90 -0
- package/skill/figdown/reference/experimental/flowchart.md +56 -0
- package/skill/figdown/reference/experimental/timing.md +33 -0
- package/skill/figdown/reference/experimental/topology.md +38 -0
- package/skill/figdown/reference/layout.md +77 -0
- package/skill/figdown/reference/reading.md +119 -0
- package/skill/figdown/reference/scene.md +118 -0
- package/skill/figdown/reference/table.md +71 -0
- package/skill/figdown/reference/transcribe.md +46 -0
- package/EXPRESSING.md +0 -119
- package/LAYOUT-GUIDE.md +0 -367
- package/README.zh-tw.md +0 -185
|
@@ -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
|
|
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/
|
|
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
|
-
**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-

|
|
32
|
+

|
|
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)"
|
|
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
|
|
42
|
-
field Reserved 4
|
|
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
|
|
50
|
-
field FIN 1
|
|
51
|
-
field Window 16
|
|
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
|
|
55
|
-
field Data *
|
|
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
|
|
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 (`
|
|
70
|
-
|
|
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
|
-

|
|
81
|
+

|
|
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"
|
|
83
|
-
class data "Segment that also carries application data (piggybacked on the ACK)"
|
|
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 **
|
|
104
|
-
|
|
105
|
-
per-endpoint state on the lifelines; the topology
|
|
106
|
-
construct, so the companion table carries
|
|
107
|
-
|
|
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 —
|
|
126
|
-
> semantics. The v0.2 sequence genre (
|
|
127
|
-
> [
|
|
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
|
-

|
|
141
|
+

|
|
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"
|
|
140
|
-
class flood "Flood — send out every port in the VLAN except the ingress port"
|
|
141
|
-
class filter "Filter — discard; destination is out the same port it arrived on"
|
|
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
|
-
|
|
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
|
-

|
|
191
|
+

|
|
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"
|
|
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
|
-
|
|
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
|
|
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
|
|
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 —
|
|
233
|
+
## 5. ARP resolution — external + class discipline (`topology`)
|
|
226
234
|
|
|
227
|
-

|
|
235
|
+

|
|
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)"
|
|
234
|
-
class ucast "Unicast — delivered only to the requester (dst MAC = A's MAC)"
|
|
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
|
-
|
|
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 **
|
|
255
|
-
the cache *transition* is the figure's actual outcome, but
|
|
256
|
-
change on A rather than a message, so the companion table
|
|
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
|
|
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 —
|
|
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 (
|
|
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
|
-

|
|
297
|
+

|
|
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 —
|
|
294
|
-
class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)"
|
|
295
|
-
class client "Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED"
|
|
296
|
-
class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED"
|
|
297
|
-
class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)"
|
|
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
|
-
|
|
340
|
-
pin closed at=394,20
|
|
341
|
-
pin listen at=394,
|
|
342
|
-
pin synrcvd at=
|
|
343
|
-
pin synsent at=
|
|
344
|
-
pin estab at=376,410
|
|
345
|
-
pin fw1 at=159,540
|
|
346
|
-
pin closewait at=
|
|
347
|
-
pin fw2 at=159,670
|
|
348
|
-
pin closing at=311,670
|
|
349
|
-
pin lastack at=
|
|
350
|
-
pin timewait at=304,800
|
|
351
|
-
pin closed2 at=
|
|
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
|
|
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** (`
|
|
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:
|
|
376
|
-
transition, `fw1 -> timewait`)
|
|
377
|
-
`rcv FIN / snd ACK` (`fw1 -> fw2 -> timewait`).
|
|
378
|
-
|
|
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
|
|
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 (
|
|
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
|
|
403
|
-
sequence genre (
|
|
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
|
|
406
|
-
it as composition (
|
|
407
|
-
|
|
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 (
|
|
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
|
|
414
|
-
no **node-identity/alias** construct (
|
|
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
|
-
[
|
|
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
|
|
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
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
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
|
],
|