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.
package/guide/showcase.md CHANGED
@@ -141,7 +141,7 @@ endpoint's TCP state segment by segment.
141
141
  ![L2 forwarding logic](../examples/showcase/l2-forwarding-logic.svg)
142
142
 
143
143
  ```figdown
144
- figdown 0.1 flowchart
144
+ figdown 0.2 flowchart
145
145
  title "L2 Switch Forwarding Decision"
146
146
 
147
147
  class forward "Forward — send the frame out the single learned egress port" fill=#16a34a
@@ -160,13 +160,13 @@ node flt "Filter (drop):\nsame-port destination" shape=rounded class=f
160
160
 
161
161
  flow down
162
162
 
163
- edge wire -> learn
164
- edge learn -> lookup
165
- edge lookup -> hit
166
- edge hit -[no]-> fld
167
- edge hit -[yes]-> same
168
- edge same -[yes]-> flt
169
- edge same -[no]-> fwd
163
+ flowline wire -> learn
164
+ flowline learn -> lookup
165
+ flowline lookup -> hit
166
+ flowline hit -[no]-> fld
167
+ flowline hit -[yes]-> same
168
+ flowline same -[yes]-> flt
169
+ flowline same -[no]-> fwd
170
170
  ```
171
171
 
172
172
  **Human sees:** a frame from the ingress port → source-MAC learning →
@@ -292,59 +292,59 @@ prose rather than on an edge of its own.
292
292
 
293
293
  ---
294
294
 
295
- ## 6. TCP connection state machine — every state, every transition (`flowchart`)
295
+ ## 6. TCP connection state machine — every state, every transition (`statechart`)
296
296
 
297
297
  ![TCP state machine](../examples/showcase/tcp-state-machine.svg)
298
298
 
299
299
  ```figdown
300
- figdown 0.1 flowchart
300
+ figdown 0.2 statechart
301
301
  title "TCP Connection State Machine (RFC 9293, Figure 5)"
302
302
 
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
303
+ class states "A state IS a TCP connection state (RFC 9293 §3.3.2); the two CLOSED states are one state drawn twice — only this label says so" fill=#eef2ff
304
304
  class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)" stroke=#2563eb
305
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
306
  class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" stroke=#16a34a
307
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
308
308
 
309
- node closed "CLOSED" shape=rounded class=states
310
- node listen "LISTEN" shape=rounded class=states
311
- node synsent "SYN-SENT" shape=rounded class=states
312
- node synrcvd "SYN-RECEIVED" shape=rounded class=states
313
- node estab "ESTABLISHED" shape=rounded class=states
314
- node fw1 "FIN-WAIT-1" shape=rounded class=states
315
- node fw2 "FIN-WAIT-2" shape=rounded class=states
316
- node closing "CLOSING" shape=rounded class=states
317
- node closewait "CLOSE-WAIT" shape=rounded class=states
318
- node lastack "LAST-ACK" shape=rounded class=states
319
- node timewait "TIME-WAIT" shape=rounded class=states
320
- node closed2 "CLOSED" shape=rounded class=states
309
+ state closed "CLOSED" shape=rounded class=states
310
+ state listen "LISTEN" shape=rounded class=states
311
+ state synsent "SYN-SENT" shape=rounded class=states
312
+ state synrcvd "SYN-RECEIVED" shape=rounded class=states
313
+ state estab "ESTABLISHED" shape=rounded class=states
314
+ state fw1 "FIN-WAIT-1" shape=rounded class=states
315
+ state fw2 "FIN-WAIT-2" shape=rounded class=states
316
+ state closing "CLOSING" shape=rounded class=states
317
+ state closewait "CLOSE-WAIT" shape=rounded class=states
318
+ state lastack "LAST-ACK" shape=rounded class=states
319
+ state timewait "TIME-WAIT" shape=rounded class=states
320
+ state closed2 "CLOSED" shape=rounded class=states
321
321
 
322
322
  flow down
323
323
 
324
- edge closed -[passive OPEN / create TCB]-> listen class=setup
325
- edge closed -[active OPEN / create TCB, snd SYN]-> synsent class=setup
326
- edge listen -[SEND / snd SYN]-> synsent class=setup
327
- edge listen -[rcv SYN / snd SYN,ACK]-> synrcvd class=setup
328
- edge synsent -[rcv SYN / snd SYN,ACK]-> synrcvd class=rare
329
- edge synsent -[rcv SYN,ACK / snd ACK]-> estab class=setup
330
- edge synrcvd -[rcv ACK of SYN / x]-> estab class=setup
331
-
332
- edge listen -[CLOSE / delete TCB]-> closed class=rare
333
- edge synsent -[CLOSE / delete TCB]-> closed class=rare
334
- edge synrcvd -[rcv RST (note 1) / x]-> listen class=rare
335
-
336
- edge synrcvd -[CLOSE / snd FIN]-> fw1 class=client
337
- edge estab -[CLOSE / snd FIN]-> fw1 class=client
338
- edge fw1 -[rcv ACK of FIN / x]-> fw2 class=client
339
- edge fw1 -[rcv FIN / snd ACK]-> closing class=client
340
- edge fw1 -[rcv FIN,ACK / snd ACK]-> timewait class=rare
341
- edge fw2 -[rcv FIN / snd ACK]-> timewait class=client
342
- edge closing -[rcv ACK of FIN / x]-> timewait class=client
343
- edge timewait -[Timeout=2MSL / delete TCB]-> closed2 class=client
344
-
345
- edge estab -[rcv FIN / snd ACK]-> closewait class=server
346
- edge closewait -[CLOSE / snd FIN]-> lastack class=server
347
- edge lastack -[rcv ACK of FIN / x]-> closed2 class=server
324
+ transition closed -[passive OPEN / create TCB]-> listen class=setup
325
+ transition closed -[active OPEN / create TCB, snd SYN]-> synsent class=setup
326
+ transition listen -[SEND / snd SYN]-> synsent class=setup
327
+ transition listen -[rcv SYN / snd SYN,ACK]-> synrcvd class=setup
328
+ transition synsent -[rcv SYN / snd SYN,ACK]-> synrcvd class=rare
329
+ transition synsent -[rcv SYN,ACK / snd ACK]-> estab class=setup
330
+ transition synrcvd -[rcv ACK of SYN / x]-> estab class=setup
331
+
332
+ transition listen -[CLOSE / delete TCB]-> closed class=rare
333
+ transition synsent -[CLOSE / delete TCB]-> closed class=rare
334
+ transition synrcvd -[rcv RST (note 1) / x]-> listen class=rare
335
+
336
+ transition synrcvd -[CLOSE / snd FIN]-> fw1 class=client
337
+ transition estab -[CLOSE / snd FIN]-> fw1 class=client
338
+ transition fw1 -[rcv ACK of FIN / x]-> fw2 class=client
339
+ transition fw1 -[rcv FIN / snd ACK]-> closing class=client
340
+ transition fw1 -[rcv FIN,ACK / snd ACK]-> timewait class=rare
341
+ transition fw2 -[rcv FIN / snd ACK]-> timewait class=client
342
+ transition closing -[rcv ACK of FIN / x]-> timewait class=client
343
+ transition timewait -[Timeout=2MSL / delete TCB]-> closed2 class=client
344
+
345
+ transition estab -[rcv FIN / snd ACK]-> closewait class=server
346
+ transition closewait -[CLOSE / snd FIN]-> lastack class=server
347
+ transition lastack -[rcv ACK of FIN / x]-> closed2 class=server
348
348
 
349
349
  layout
350
350
  pin closed at=(394,20)
@@ -365,7 +365,8 @@ The `pin` block reproduces the canonical Figure 5 arrangement so a
365
365
  reader who knows the RFC diagram recognises it — but it is **presentation
366
366
  only** (`strip-check --strict` passes): strip every line after `layout` and all
367
367
  11 states, all 21 transitions and their `event / action` labels survive on the
368
- `node`/`edge`/`class` lines. All meaning is in the edges; the pins are layout.
368
+ `state`/`transition`/`class` lines. All meaning is in the transitions; the pins
369
+ are layout.
369
370
 
370
371
  Three `path … points=` lines used to sit under those pins, bowing the active <!-- fence-check: skip -->
371
372
  OPEN and the two aborts out to the margins. `EDGE-GEOMETRY-CONSTRUCTS` **withdrew the construct from
@@ -389,14 +390,14 @@ arrow carrying its `event / action`.
389
390
  **An agent answers from the text alone:**
390
391
 
391
392
  - *Q: What event takes ESTABLISHED to CLOSE-WAIT, and what does the endpoint
392
- send?* A: `rcv FIN / snd ACK` — one edge label
393
- (`edge estab -[rcv FIN / snd ACK]-> closewait`).
393
+ send?* A: `rcv FIN / snd ACK` — one transition label
394
+ (`transition estab -[rcv FIN / snd ACK]-> closewait`).
394
395
  - *Q: From FIN-WAIT-1, how many ways lead to TIME-WAIT and under what
395
396
  conditions?* A: three. (1) Directly: `rcv FIN,ACK / snd ACK` (the RFC Note-2
396
397
  transition, `fw1 -> timewait`). (2) Via FIN-WAIT-2: `rcv ACK of FIN / x` then
397
398
  `rcv FIN / snd ACK` (`fw1 -> fw2 -> timewait`). (3) Via CLOSING:
398
399
  `rcv FIN / snd ACK` then `rcv ACK of FIN / x` (`fw1 -> closing -> timewait`).
399
- All read off the edges.
400
+ All read off the transitions.
400
401
  - *Q: How long does TIME-WAIT last and what happens after?* A: `Timeout=2MSL`,
401
402
  then `delete TCB` → CLOSED (`timewait -[Timeout=2MSL / delete TCB]-> closed2`).
402
403
  - *Q: Which transitions belong to the typical server (passive-close) path?*
@@ -430,12 +431,17 @@ The Ethernet frame uses the **`BYTE-UNIT-PACKET-BLOCKS`** byte-unit workaround (
430
431
  not `bitfield`) so byte order rides on cell order, and one of its facts — the
431
432
  FCS **coverage span** over a contiguous run of columns (DA through Payload) —
432
433
  has no annotation construct yet (**`CONTIGUOUS-RANGE-GROUPING`**), so it rides on a source comment
433
- and prose. The TCP state machine meets two more: **state-ness is not first-class**
434
- a node *is* a state carries as the `states` class label until the state genre
435
- lands (the `GENRE-EARNING-THRESHOLD` §6 candidate) and CLOSED is drawn **twice** because FigDown has
434
+ and prose. The TCP state machine meets one more, and **has just stopped meeting
435
+ the other**: state-ness IS first-class since `STATECHART-GENRE-SCOPE` line 1 reads
436
+ `figdown 0.2 statechart`, so "this is a state" is now declared by the word
437
+ `state` itself (`GENRE-NODE-SPELLING`) rather than carried by the `states` class
438
+ label, and the label stays only because it says
439
+ something more specific about the two CLOSED nodes. The genre is EXPERIMENTAL
440
+ and may be withdrawn. What remains is that CLOSED is drawn **twice** because
441
+ FigDown has
436
442
  no **node-identity/alias** construct (**`IDENTITY-ASSERTION`**) to declare two nodes the same
437
- entity; the shared class label asserts it instead. Every state and every labelled
438
- transition still rides on an edge, so those two are the *only* facts the figure
443
+ entity; the shared class label asserts it instead. So those two are the *only*
444
+ facts the figure
439
445
  cannot carry that the canonical drawing implies. All are documented in
440
446
  [expressing.md](expressing.md)'s "Known limits".
441
447
  That a figure format states its own limits — and that a reading agent recovers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figdown",
3
- "version": "0.1.8",
3
+ "version": "0.3.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>",
@@ -32,7 +32,7 @@
32
32
  "gate:page": "node tools/page-check.js",
33
33
  "gate:shape": "node tools/shape-check.js --strict",
34
34
  "gate:strip": "node tools/strip-check.js --strict",
35
- "gate:layout": "node tools/layout-lint.js",
35
+ "gate:layout": "node tools/layout-lint.js --strict",
36
36
  "gate:artifact": "node tools/artifact-check.js --strict",
37
37
  "gate:dist": "node tools/dist-check.js --strict",
38
38
  "gate:isolation": "node tools/isolation-check.js --strict",
@@ -106,11 +106,12 @@ so you know which file you need before you need it.
106
106
 
107
107
  | Genre on line 1 | Load | Add only for EXPERIMENTAL constructs |
108
108
  |---|---|---|
109
- | `block` — architecture, dataflow, hierarchy | `reference/scene.md`, `reference/layout.md` | `reference/experimental/constructs.md` |
109
+ | `block` — architecture, dataflow, hierarchy | `reference/scene.md`, `reference/layout.md` | `reference/experimental/block.md`, `reference/experimental/chart.md` |
110
110
  | `bitfield` — packet headers, register layouts | `reference/bitfield.md` | — |
111
- | `table` — config, state, memory maps | `reference/table.md` | `reference/experimental/constructs.md` |
112
- | `topology` | `reference/scene.md`, `reference/layout.md` | `reference/experimental/topology.md`, `reference/experimental/constructs.md` |
113
- | `flowchart` | `reference/scene.md`, `reference/layout.md` | `reference/experimental/flowchart.md`, `reference/experimental/constructs.md` |
111
+ | `table` — config, state, memory maps | `reference/table.md` | `reference/experimental/chart.md` |
112
+ | `topology` | `reference/experimental/topology.md`, `reference/layout.md` | `reference/experimental/chart.md` |
113
+ | `flowchart` | `reference/experimental/flowchart.md`, `reference/layout.md` | `reference/experimental/chart.md` |
114
+ | `statechart` | `reference/experimental/statechart.md`, `reference/layout.md` | `reference/experimental/chart.md` |
114
115
  | `timing` | — | `reference/experimental/timing.md` |
115
116
 
116
117
  Two more files answer a **task** rather than a genre:
@@ -126,6 +127,26 @@ Pick the genre by what the figure IS, not by its subject: the left-hand
126
127
  column above says what each is for. Prefer the first three — they are the
127
128
  portable ones.
128
129
 
130
+ **Load the genre file BEFORE you write line 2, not only when something
131
+ fails.** A scene genre may spell the thing and the line with **its own
132
+ domain's words**, and it may rank its keywords — preferring a precise one and
133
+ keeping a general one as the honest fallback. Neither is guessable from this
134
+ file, and neither is optional: the wrong spelling is a line error, and the
135
+ lazy spelling is a claim you did not mean to make. The genre file states both
136
+ in its first screen.
137
+
138
+ **Where a genre ranks its keywords, the general one means "the source does not
139
+ state this" — and nothing else.** It is the transcriber's honest line for a
140
+ source that leaves something unsaid, not a shrug and not a shortcut. It is
141
+ **not** the spelling for something FigDown cannot express: that is a **coverage
142
+ gap in the language**, and burying it in the general keyword makes the
143
+ language's hole look like your judgement, in a spelling no reader can tell
144
+ apart from a real decision. When your source states something no keyword
145
+ carries: write the general keyword, **name the missing thing in a `#` comment
146
+ on the same line**, and report the gap. The comment is text a reader can quote
147
+ and it survives every re-render; it is never parsed and never a second
148
+ semantic channel. Each genre file names its own ranking and its own fallback.
149
+
129
150
  ## The document skeleton
130
151
 
131
152
  The grammar is **CLOSED**: an unknown line is an error, never ignored. Five
@@ -146,6 +167,11 @@ figdown 0.1 block # REQUIRED first significant line; comments and
146
167
  # A later `figdown 0.1 <genre>` starts a new
147
168
  # section with its own genre; one file still
148
169
  # renders to one SVG.
170
+ # The VERSION is `0.1` or `0.2`. Write the LOWEST
171
+ # one that carries what the figure needs — `0.2`
172
+ # only for `statechart`, which does not exist at
173
+ # `0.1`. Sections may differ; each declares its
174
+ # own.
149
175
  title "Some Title" # optional; the quotes are REQUIRED
150
176
  # comments start with '#'; inside quotes the only escapes are \n \" \\
151
177
  class hot "Congested path" stroke=#dc2626 # meaning + style, declared once