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.
@@ -1,12 +1,16 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 764 284" width="764" height="284" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">TCP Header (RFC 9293)</text><text x="11.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">0</text><text x="34.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">1</text><text x="57.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">2</text><text x="80.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">3</text><text x="103.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">4</text><text x="126.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">5</text><text x="149.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">6</text><text x="172.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">7</text><text x="195.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">8</text><text x="218.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">9</text><text x="241.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">10</text><text x="264.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">11</text><text x="287.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">12</text><text x="310.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">13</text><text x="333.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">14</text><text x="356.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">15</text><text x="379.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">16</text><text x="402.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">17</text><text x="425.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">18</text><text x="448.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">19</text><text x="471.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">20</text><text x="494.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">21</text><text x="517.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">22</text><text x="540.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">23</text><text x="563.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">24</text><text x="586.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">25</text><text x="609.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">26</text><text x="632.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">27</text><text x="655.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">28</text><text x="678.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">29</text><text x="701.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">30</text><text x="724.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">31</text><rect x="0" y="34" width="368" height="30" fill="#fff" stroke="#555"/><text x="184" y="52.85" font-size="11" text-anchor="middle">Source Port</text><rect x="368" y="34" width="368" height="30" fill="#fff" stroke="#555"/><text x="552" y="52.85" font-size="11" text-anchor="middle">Destination Port</text><rect x="0" y="64" width="736" height="30" fill="#fff" stroke="#555"/><text x="368" y="82.85" font-size="11" text-anchor="middle">Sequence Number</text><rect x="0" y="94" width="736" height="30" fill="#fff" stroke="#555"/><text x="368" y="112.85" font-size="11" text-anchor="middle">Acknowledgment Number</text><rect x="0" y="124" width="92" height="30" fill="#fff" stroke="#555"/><text x="46" y="142.85" font-size="11" text-anchor="middle">Data Offset</text><title>header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)</title><rect x="92" y="124" width="92" height="30" fill="#fff" stroke="#555"/><text x="138" y="142.85" font-size="11" text-anchor="middle">Reserved</text><title>must be zero</title><rect x="184" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="195.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">CWR</text><rect x="207" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="218.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ECE</text><rect x="230" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="241.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">URG</text><rect x="253" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="264.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ACK</text><rect x="276" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="287.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">PSH</text><rect x="299" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="310.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">RST</text><rect x="322" y="124" width="23" height="30" fill="#fee2e2" stroke="#555"/><text x="333.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">SYN</text><rect x="345" y="124" width="23" height="30" fill="#fee2e2" stroke="#555"/><text x="356.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">FIN</text><rect x="368" y="124" width="368" height="30" fill="#fff" stroke="#555"/><text x="552" y="142.85" font-size="11" text-anchor="middle">Window</text><title>receive window size in bytes (subject to window scaling option)</title><rect x="0" y="154" width="368" height="30" fill="#fff" stroke="#555"/><text x="184" y="172.85" font-size="11" text-anchor="middle">Checksum</text><rect x="368" y="154" width="368" height="30" fill="#fff" stroke="#555"/><text x="552" y="172.85" font-size="11" text-anchor="middle">Urgent Pointer</text><rect x="0" y="184" width="736" height="30" fill="#fff" stroke="#555" stroke-dasharray="5 3"/><text x="368" y="202.85" font-size="11" text-anchor="middle">Options</text><title>present iff Data Offset &gt; 5; padded to a 32-bit boundary</title><rect x="0" y="214" width="736" height="30" fill="#fff" stroke="#555"/><text x="368" y="232.85" font-size="11" text-anchor="middle">Data</text><title>variable-length payload; length derived from the IP datagram, not carried in the TCP header</title></g><metadata id="figdown-source" data-sha256="f1ce5b6b0b129a760a826f9bc81e4fa8417a571929d9e3b0adc5480e3fb1445f"><![CDATA[
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 764 332" width="764" height="332" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><text x="0" y="14" font-size="13" font-weight="600">TCP Header (RFC 9293)</text><text x="11.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">0</text><text x="34.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">1</text><text x="57.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">2</text><text x="80.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">3</text><text x="103.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">4</text><text x="126.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">5</text><text x="149.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">6</text><text x="172.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">7</text><text x="195.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">8</text><text x="218.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">9</text><text x="241.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">10</text><text x="264.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">11</text><text x="287.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">12</text><text x="310.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">13</text><text x="333.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">14</text><text x="356.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">15</text><text x="379.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">16</text><text x="402.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">17</text><text x="425.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">18</text><text x="448.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">19</text><text x="471.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">20</text><text x="494.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">21</text><text x="517.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">22</text><text x="540.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">23</text><text x="563.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">24</text><text x="586.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">25</text><text x="609.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">26</text><text x="632.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">27</text><text x="655.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">28</text><text x="678.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">29</text><text x="701.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">30</text><text x="724.5" y="29" font-size="8.5" text-anchor="middle" fill="#6f6e69">31</text><rect x="0" y="34" width="368" height="30" fill="#fff" stroke="#555"/><text x="184" y="52.85" font-size="11" text-anchor="middle">Source Port</text><rect x="368" y="34" width="368" height="30" fill="#fff" stroke="#555"/><text x="552" y="52.85" font-size="11" text-anchor="middle">Destination Port</text><rect x="0" y="64" width="736" height="30" fill="#fff" stroke="#555"/><text x="368" y="82.85" font-size="11" text-anchor="middle">Sequence Number</text><rect x="0" y="94" width="736" height="30" fill="#fff" stroke="#555"/><text x="368" y="112.85" font-size="11" text-anchor="middle">Acknowledgment Number</text><rect x="0" y="124" width="92" height="30" fill="#fff" stroke="#555"><title>header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)</title></rect><text x="46" y="142.85" font-size="11" text-anchor="middle">Data Offset</text><rect x="92" y="124" width="92" height="30" fill="#fff" stroke="#555"><title>must be zero</title></rect><text x="138" y="142.85" font-size="11" text-anchor="middle">Reserved</text><rect x="184" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="195.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">CWR</text><rect x="207" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="218.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ECE</text><rect x="230" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="241.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">URG</text><rect x="253" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="264.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">ACK</text><rect x="276" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="287.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">PSH</text><rect x="299" y="124" width="23" height="30" fill="#fff" stroke="#555"/><text x="310.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">RST</text><rect x="322" y="124" width="23" height="30" fill="#fee2e2" stroke="#555"/><text x="333.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">SYN</text><rect x="345" y="124" width="23" height="30" fill="#fee2e2" stroke="#555"/><text x="356.5" y="142.5188172043011" font-size="10.053763440860214" text-anchor="middle">FIN</text><rect x="368" y="124" width="368" height="30" fill="#fff" stroke="#555"><title>receive window size in bytes (subject to window scaling option)</title></rect><text x="552" y="142.85" font-size="11" text-anchor="middle">Window</text><rect x="0" y="154" width="368" height="30" fill="#fff" stroke="#555"/><text x="184" y="172.85" font-size="11" text-anchor="middle">Checksum</text><rect x="368" y="154" width="368" height="30" fill="#fff" stroke="#555"/><text x="552" y="172.85" font-size="11" text-anchor="middle">Urgent Pointer</text><rect x="0" y="184" width="736" height="30" fill="#fff" stroke="#555" stroke-dasharray="5 3"><title>0-320 bits (0-40 octets), always a multiple of 32, padded to a 32-bit boundary — RFC 9293 §3.1</title></rect><text x="368" y="202.85" font-size="11" text-anchor="middle">Options</text><rect x="0" y="214" width="736" height="30" fill="#fff" stroke="#555"><title>variable-length payload; length derived from the IP datagram, not carried in the TCP header</title></rect><text x="368" y="232.85" font-size="11" text-anchor="middle">Data</text><text x="0" y="263" font-size="10.5" fill="#6f6e69">Options — present: Data Offset &gt; 5</text><text x="0" y="314.5" font-size="11" fill="#1d1d1b">Width is VARIABLE on the wire; the declared width is one representative word, not the field's length</text></g><metadata id="figdown-source" data-sha256="71ea53c27bd8adc9b27608ff6be740dea8ebc14e3cf8a663d5dd137f195d2b6e" data-engine-version="0.1.0"><![CDATA[
2
+ # FigDown — figures as text. Spec: https://github.com/FigDown/figdown
3
+
2
4
  figdown 0.1 bitfield
3
5
  # TCP Header — RFC 9293 §3.1, IETF MSB-0 bit numbering (bit 0 = leftmost).
4
6
  # The .fd is the machine-readable truth: field ORDER + WIDTH give every
5
7
  # bit its absolute offset, so a reader answers "which bits carry X?" from
6
8
  # text alone — no pixels, no OCR.
7
- title TCP Header
9
+ title "TCP Header"
10
+
11
+ class varword "Width is VARIABLE on the wire; the declared width is one representative word, not the field's length"
8
12
 
9
- bitfield tcp "TCP Header (RFC 9293)" unit=32 numbering=msb0
13
+ bitfield tcp "TCP Header (RFC 9293)" word=32 numbering=msb0
10
14
 
11
15
  # Row 1: Source Port (16) + Destination Port (16) = 32 bits
12
16
  field "Source Port" 16
@@ -19,29 +23,32 @@ field "Sequence Number" 32
19
23
  field "Acknowledgment Number" 32
20
24
 
21
25
  # Row 4: Data Offset (4) + Reserved (4) + 8 control flags (1 bit each) + Window (16)
22
- field "Data Offset" 4 note="header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)"
23
- field Reserved 4 note="must be zero"
26
+ field "Data Offset" 4 description="header length in 32-bit words; min 5 (=20 bytes), max 15 (=60 bytes)"
27
+ field "Reserved" 4 description="must be zero"
24
28
  # The eight control bits are declared as individual 1-bit fields so a reader
25
29
  # can address each flag's exact bit position, not just the byte that holds them.
26
- field CWR 1
27
- field ECE 1
28
- field URG 1
29
- field ACK 1
30
- field PSH 1
31
- field RST 1
32
- field SYN 1 color=#fee2e2
33
- field FIN 1 color=#fee2e2
34
- field Window 16 note="receive window size in bytes (subject to window scaling option)"
30
+ # The first six use the COMPACT form: `name:width` pairs, comma-separated on one
31
+ # `field` line, declare exactly the same six one-bit fields the long form would —
32
+ # same model, same boxes, six lines saved. The compact form takes no option keys,
33
+ # so SYN and FIN stay long-form: they carry a `fill=` that marks the two flags
34
+ # this figure is highlighting.
35
+ field CWR:1,ECE:1,URG:1,ACK:1,PSH:1,RST:1
36
+ field "SYN" 1 fill=#fee2e2
37
+ field "FIN" 1 fill=#fee2e2
38
+ field "Window" 16 description="receive window size in bytes (subject to window scaling option)"
35
39
 
36
40
  # Row 5: Checksum (16) + Urgent Pointer (16) = 32 bits
37
- field Checksum 16
41
+ field "Checksum" 16
38
42
  field "Urgent Pointer" 16
39
43
 
40
- # Row 6+: Options — present only when Data Offset > 5 (dashed border marks
41
- # conditional presence; a reader computing offsets past here MUST branch, R48)
42
- field Options 32 optional note="present iff Data Offset > 5; padded to a 32-bit boundary"
44
+ # Row 6+: Options — the dashed border marks conditional presence, and the
45
+ # condition itself is now in the model and drawn as a caption; a reader
46
+ # computing offsets past here MUST branch (`BITFIELD-CONDITIONAL-OFFSETS`). The WIDTH is 0-320 bits
47
+ # (0-40 octets) on the wire: v0.1 has no range width (`BITFIELD-RANGE-WIDTH`) and `*` is
48
+ # spent on Data, so 32 is one representative word, said in the class meaning.
49
+ field "Options" 32 present="Data Offset > 5" class=varword description="0-320 bits (0-40 octets), always a multiple of 32, padded to a 32-bit boundary — RFC 9293 §3.1"
43
50
 
44
51
  # Payload — variable length, inferred from the IP total length, not a TCP field
45
- field Data * note="variable-length payload; length derived from the IP datagram, not carried in the TCP header"
52
+ field "Data" * description="variable-length payload; length derived from the IP datagram, not carried in the TCP header"
46
53
 
47
54
  ]]></metadata></svg>
@@ -1,6 +1,8 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 843 984" width="843" height="984" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="68" x1="429.6" y1="76" x2="429.6" y2="170" stroke="#2563eb" stroke-width="1.6"/><path data-edge="69" d="M465.2 58 L780 58 Q790 58 790 68 L790 308 Q790 318 780 318 L692.6 318" fill="none" stroke="#2563eb" stroke-width="1.6"/><line data-edge="70" x1="460.08923076923077" y1="206" x2="619.3107692307692" y2="300" stroke="#2563eb" stroke-width="1.6"/><line data-edge="71" x1="400.8612326629738" y1="209.014657885179" x2="241.49507881681996" y2="303.01465788517896" stroke="#2563eb" stroke-width="1.6"/><line data-edge="72" x1="607" y1="318" x2="266.4" y2="318" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="73" x1="619.3107692307692" y1="336" x2="460.08923076923077" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="74" x1="239.71692307692308" y1="336" x2="399.08307692307693" y2="430" stroke="#2563eb" stroke-width="1.6"/><path data-edge="77" d="M394 170.694 L308.994 129.372 Q300 125 308.883 120.408 L394.782 76" fill="none" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><path data-edge="78" d="M648.809 300 L640.55 149.985 Q640 140 630.683 136.369 L465.2 71.875" fill="none" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="79" x1="237.9387673370262" y1="296.98534211482104" x2="397.30492118318006" y2="202.985342114821" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="82" x1="209.18615384615384" y1="336" x2="209.01384615384615" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="83" x1="399.05538461538464" y1="466" x2="239.54461538461538" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="84" x1="209" y1="596" x2="209" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="85" x1="228.55076923076922" y1="596" x2="330.64923076923077" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="86" x1="218.78923076923076" y1="596" x2="340.6107692307692" y2="820" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="87" x1="228.57846153846154" y1="726" x2="330.82153846153847" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="88" x1="350.2276923076923" y1="726" x2="350.3723076923077" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="89" x1="396.79999999999995" y1="838" x2="614" y2="838" stroke="#dc2626" stroke-width="1.6"/><line data-edge="92" x1="460.1169230769231" y1="466" x2="619.483076923077" y2="560" stroke="#16a34a" stroke-width="1.6"/><line data-edge="93" x1="649.9723076923077" y1="596" x2="649.8276923076922" y2="690" stroke="#16a34a" stroke-width="1.6"/><line data-edge="94" x1="649.7723076923077" y1="726" x2="649.6276923076923" y2="820" stroke="#16a34a" stroke-width="1.6"/><g data-node="closed" data-x="394" data-y="40" style="cursor:move"><rect x="394" y="40" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="62.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><g data-node="listen" data-x="394" data-y="170" style="cursor:move"><rect x="394" y="170" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="192.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LISTEN</text></g><g data-node="synsent" data-x="607" data-y="300" style="cursor:move"><rect x="607" y="300" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="649.8" y="322.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-SENT</text></g><g data-node="synrcvd" data-x="152" data-y="300" style="cursor:move"><rect x="152" y="300" width="114.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209.2" y="322.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-RECEIVED</text></g><g data-node="estab" data-x="376" data-y="430" style="cursor:move"><rect x="376" y="430" width="107.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="452.55" font-size="13" text-anchor="middle" fill="#1d1d1b">ESTABLISHED</text></g><g data-node="fw1" data-x="159" data-y="560" style="cursor:move"><rect x="159" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-1</text></g><g data-node="fw2" data-x="159" data-y="690" style="cursor:move"><rect x="159" y="690" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-2</text></g><g data-node="closing" data-x="311" data-y="690" style="cursor:move"><rect x="311" y="690" width="78.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.2" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSING</text></g><g data-node="closewait" data-x="600" data-y="560" style="cursor:move"><rect x="600" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="650" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSE-WAIT</text></g><g data-node="lastack" data-x="607" data-y="690" style="cursor:move"><rect x="607" y="690" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="649.8" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LAST-ACK</text></g><g data-node="timewait" data-x="304" data-y="820" style="cursor:move"><rect x="304" y="820" width="92.8" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.4" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">TIME-WAIT</text></g><g data-node="closed2" data-x="614" data-y="820" style="cursor:move"><rect x="614" y="820" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="649.6" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><text x="435.6" y="137.57999999999998" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">passive OPEN / create TCB</text><text x="435.6" y="137.57999999999998" font-size="11" text-anchor="start" fill="#2563eb">passive OPEN / create TCB</text><path d="M429.6 170 L424 159.92 L435.20000000000005 159.92 z" fill="#2563eb" stroke="none"/><text x="627.6" y="52.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">active OPEN / create TCB, snd SYN</text><text x="627.6" y="52.25" font-size="11" text-anchor="middle" fill="#2563eb">active OPEN / create TCB, snd SYN</text><path d="M692.5999999999999 318 L702.68 312.4 L702.68 323.6 z" fill="#2563eb" stroke="none"/><text x="539.7" y="247.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">SEND / snd SYN</text><text x="539.7" y="247.25" font-size="11" text-anchor="middle" fill="#2563eb">SEND / snd SYN</text><path d="M619.3107692307692 300 L607.783624515843 299.6977836517479 L613.4775557147959 290.05314019013707 z" fill="#2563eb" stroke="none"/><text x="321.17815573989685" y="250.26465788517896" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="321.17815573989685" y="250.26465788517896" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN / snd SYN,ACK</text><path d="M241.49507881681996 303.01465788517896 L247.33224434230044 293.0701167379896 L253.02234270997042 302.71702197056237 z" fill="#2563eb" stroke="none"/><text x="436.7" y="312.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="436.7" y="312.25" font-size="11" text-anchor="middle" fill="#9333ea">rcv SYN / snd SYN,ACK</text><path d="M266.4 318 L276.47999999999996 312.4 L276.47999999999996 323.6 z" fill="#9333ea" stroke="none"/><text x="539.7" y="377.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN,ACK / snd ACK</text><text x="539.7" y="377.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN,ACK / snd ACK</text><path d="M460.08923076923077 430 L465.9224442852041 420.05314019013707 L471.6163754841569 429.6977836517479 z" fill="#2563eb" stroke="none"/><text x="319.4" y="377.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of SYN / x</text><text x="319.4" y="377.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv ACK of SYN / x</text><path d="M399.08307692307693 430 L387.55581302992647 429.7023640853834 L393.24591139759644 420.05545885281066 z" fill="#2563eb" stroke="none"/><text x="336.01719402985077" y="100.63" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="336.01719402985077" y="100.63" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M394.7820895522388 76 L388.3996077783373 85.60366213513446 L383.25615963242194 75.65454452751315 z" fill="#9333ea" stroke="none"/><text x="552.6" y="100.18726235741445" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="552.6" y="100.18726235741445" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M465.20000000000005 71.8745247148289 L476.6254519472023 70.31713979061396 L472.5583957756856 80.75261074777387 z" fill="#9333ea" stroke="none"/><text x="336.7457827216416" y="232.955342114821" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv RST (note 1) / x</text><text x="336.7457827216416" y="232.955342114821" font-size="11" text-anchor="middle" fill="#9333ea">rcv RST (note 1) / x</text><path d="M397.30492118318006 202.985342114821 L391.46775565769957 212.92988326201035 L385.7776572900296 203.2829780294376 z" fill="#9333ea" stroke="none"/><text x="215.1" y="451.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="215.1" y="451.3" font-size="11" text-anchor="start" fill="#dc2626">CLOSE / snd FIN</text><path d="M209.01384615384615 560 L203.42160165450994 549.915695291214 L214.62159834090195 549.9243106732804 z" fill="#dc2626" stroke="none"/><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#dc2626">CLOSE / snd FIN</text><path d="M239.54461538461538 560 L245.38572668223014 550.0577759626491 L251.0719964792192 559.7069384027706 z" fill="#dc2626" stroke="none"/><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M209 690 L203.4 679.92 L214.6 679.92 z" fill="#dc2626" stroke="none"/><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.64923076923077 690 L319.4405255167678 687.2923577482596 L327.02659217035057 679.0527222752913 z" fill="#dc2626" stroke="none"/><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN,ACK / snd ACK</text><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#9333ea">rcv FIN,ACK / snd ACK</text><path d="M340.6107692307692 820 L330.875382512483 813.820303774018 L340.7144579488292 808.469360440559 z" fill="#9333ea" stroke="none"/><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.82153846153847 820 L319.61092639723944 817.3002635047485 L327.19118005561285 809.0552799101793 z" fill="#dc2626" stroke="none"/><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M350.3723076923077 820 L344.75680664555944 809.9286273033927 L355.9567933911451 809.9113965545532 z" fill="#dc2626" stroke="none"/><text x="505.4" y="832.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">Timeout=2MSL / delete TCB</text><text x="505.4" y="832.25" font-size="11" text-anchor="middle" fill="#dc2626">Timeout=2MSL / delete TCB</text><path d="M614 838 L603.92 843.6 L603.92 832.4 z" fill="#dc2626" stroke="none"/><text x="539.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="539.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#16a34a">rcv FIN / snd ACK</text><path d="M619.483076923077 560 L607.9558130299265 559.7023640853835 L613.6459113975964 550.0554588528106 z" fill="#16a34a" stroke="none"/><text x="655.9" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="655.9" y="646.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">CLOSE / snd FIN</text><path d="M649.8276923076922 690 L644.2432066088548 679.9113965545532 L655.4431933544405 679.9286273033927 z" fill="#16a34a" stroke="none"/><text x="655.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="655.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">rcv ACK of FIN / x</text><path d="M649.6276923076923 820 L644.0432066088549 809.9113965545532 L655.2431933544406 809.9286273033927 z" fill="#16a34a" stroke="none"/><rect x="0" y="877" width="16" height="11" fill="#eef2ff" stroke="#555"/><text x="21" y="886.5" font-size="11" fill="#1d1d1b">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)</text><rect x="0" y="897" width="16" height="11" fill="#2563eb" stroke="#555"/><text x="21" y="906.5" font-size="11" fill="#1d1d1b">Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)</text><rect x="0" y="917" width="16" height="11" fill="#dc2626" stroke="#555"/><text x="21" y="926.5" font-size="11" fill="#1d1d1b">Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED</text><rect x="0" y="937" width="16" height="11" fill="#16a34a" stroke="#555"/><text x="21" y="946.5" font-size="11" fill="#1d1d1b">Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED</text><rect x="0" y="957" width="16" height="11" fill="#9333ea" stroke="#555" stroke-dasharray="6 4"/><text x="21" y="966.5" font-size="11" fill="#1d1d1b">Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)</text></g><metadata id="figdown-source" data-sha256="459b9e5cc2a557ccb672ce2b25d576fd8aa07c8384f2ad347a0c2534a2373f54"><![CDATA[
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 888 984" width="888" height="984" font-family="system-ui,sans-serif"><defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#555"/></marker><pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#bbb" stroke-width="2"/></pattern></defs><g transform="translate(18,6)"><line data-edge="70" x1="433.1" y1="76" x2="433.1" y2="190" stroke="#2563eb" stroke-width="1.6"/><line data-edge="71" x1="452.7652886772431" y1="73.34906513439799" x2="724.2052886772431" y2="307.34906513439796" stroke="#2563eb" stroke-width="1.6"/><line data-edge="72" x1="465.20000000000005" y1="221.63984674329504" x2="700" y2="311.6015325670498" stroke="#2563eb" stroke-width="1.6"/><line data-edge="73" x1="395.5028227438559" y1="228.0864522267111" x2="216.56282274385592" y2="313.1609371712436" stroke="#2563eb" stroke-width="1.6"/><line data-edge="74" x1="700" y1="328" x2="234.39999999999998" y2="328" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="75" x1="700" y1="344.3984674329502" x2="476.58000000000004" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="76" x1="215.06" y1="346" x2="391.74" y2="430" stroke="#2563eb" stroke-width="1.6"/><line data-edge="79" x1="426.1" y1="190" x2="426.1" y2="76" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="80" x1="719.6347113227569" y1="312.65093486560204" x2="448.1947113227569" y2="78.65093486560201" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="81" x1="213.5571772561441" y1="306.8390628287564" x2="392.4971772561441" y2="221.76457788422394" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="84" x1="179.4896" y1="346" x2="206.7104" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="85" x1="399.05538461538464" y1="466" x2="239.54461538461538" y2="560" stroke="#dc2626" stroke-width="1.6"/><line data-edge="86" x1="209" y1="596" x2="209" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="87" x1="228.55076923076922" y1="596" x2="330.64923076923077" y2="690" stroke="#dc2626" stroke-width="1.6"/><line data-edge="88" x1="218.78923076923076" y1="596" x2="340.6107692307692" y2="820" stroke="#9333ea" stroke-width="1.6" stroke-dasharray="6 4"/><line data-edge="89" x1="228.57846153846154" y1="726" x2="330.82153846153847" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="90" x1="350.2276923076923" y1="726" x2="350.3723076923077" y2="820" stroke="#dc2626" stroke-width="1.6"/><line data-edge="91" x1="396.79999999999995" y1="838" x2="697" y2="838" stroke="#dc2626" stroke-width="1.6"/><line data-edge="94" x1="472.57846153846157" y1="466" x2="697.0215384615385" y2="560" stroke="#16a34a" stroke-width="1.6"/><line data-edge="95" x1="739.003076923077" y1="596" x2="733.796923076923" y2="690" stroke="#16a34a" stroke-width="1.6"/><line data-edge="96" x1="732.7723076923077" y1="726" x2="732.6276923076923" y2="820" stroke="#16a34a" stroke-width="1.6"/><g data-node="closed" data-x="394" data-y="40" style="cursor:move"><rect x="394" y="40" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="62.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><g data-node="listen" data-x="394" data-y="190" style="cursor:move"><rect x="394" y="190" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="212.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LISTEN</text></g><g data-node="synsent" data-x="700" data-y="310" style="cursor:move"><rect x="700" y="310" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="742.8" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-SENT</text></g><g data-node="synrcvd" data-x="120" data-y="310" style="cursor:move"><rect x="120" y="310" width="114.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="177.2" y="332.55" font-size="13" text-anchor="middle" fill="#1d1d1b">SYN-RECEIVED</text></g><g data-node="estab" data-x="376" data-y="430" style="cursor:move"><rect x="376" y="430" width="107.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="429.6" y="452.55" font-size="13" text-anchor="middle" fill="#1d1d1b">ESTABLISHED</text></g><g data-node="fw1" data-x="159" data-y="560" style="cursor:move"><rect x="159" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-1</text></g><g data-node="fw2" data-x="159" data-y="690" style="cursor:move"><rect x="159" y="690" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="209" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">FIN-WAIT-2</text></g><g data-node="closing" data-x="311" data-y="690" style="cursor:move"><rect x="311" y="690" width="78.4" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.2" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSING</text></g><g data-node="closewait" data-x="690" data-y="560" style="cursor:move"><rect x="690" y="560" width="100" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="740" y="582.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSE-WAIT</text></g><g data-node="lastack" data-x="690" data-y="690" style="cursor:move"><rect x="690" y="690" width="85.6" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.8" y="712.55" font-size="13" text-anchor="middle" fill="#1d1d1b">LAST-ACK</text></g><g data-node="timewait" data-x="304" data-y="820" style="cursor:move"><rect x="304" y="820" width="92.8" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="350.4" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">TIME-WAIT</text></g><g data-node="closed2" data-x="697" data-y="820" style="cursor:move"><rect x="697" y="820" width="71.2" height="36" rx="14" fill="#eef2ff" stroke="#8a8880" stroke-width="1.8"/><text x="732.6" y="842.55" font-size="13" text-anchor="middle" fill="#1d1d1b">CLOSED</text></g><text x="264.6" y="136.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">passive OPEN / create TCB</text><text x="264.6" y="136.3" font-size="11" text-anchor="start" fill="#2563eb">passive OPEN / create TCB</text><path d="M433.1 190 L427.5 179.92 L438.70000000000005 179.92 z" fill="#2563eb" stroke="none"/><text x="588.485288677243" y="184.59906513439796" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">active OPEN / create TCB, snd SYN</text><text x="588.485288677243" y="184.59906513439796" font-size="11" text-anchor="middle" fill="#2563eb">active OPEN / create TCB, snd SYN</text><path d="M724.2052886772431 307.34906513439796 L712.9141343807203 305.008929528901 L720.2270581478983 296.52593795897457 z" fill="#2563eb" stroke="none"/><text x="582.6" y="260.87068965517244" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">SEND / snd SYN</text><text x="582.6" y="260.87068965517244" font-size="11" text-anchor="middle" fill="#2563eb">SEND / snd SYN</text><path d="M700 311.6015325670498 L688.5836683972988 313.224422808922 L692.5908047969832 302.76579680574537 z" fill="#2563eb" stroke="none"/><text x="306.03282274385595" y="264.8736946989773" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="306.03282274385595" y="264.8736946989773" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN / snd SYN,ACK</text><path d="M216.56282274385592 313.1609371712436 L223.261805406868 303.7753081949488 L228.0708381872069 313.89030714292835 z" fill="#2563eb" stroke="none"/><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN / snd SYN,ACK</text><text x="467.2" y="322.25" font-size="11" text-anchor="middle" fill="#9333ea">rcv SYN / snd SYN,ACK</text><path d="M234.39999999999998 328 L244.48 322.4 L244.48 333.6 z" fill="#9333ea" stroke="none"/><text x="588.29" y="381.4492337164751" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv SYN,ACK / snd ACK</text><text x="588.29" y="381.4492337164751" font-size="11" text-anchor="middle" fill="#2563eb">rcv SYN,ACK / snd ACK</text><path d="M476.58000000000004 430 L483.9891952030168 421.1642642386956 L487.99633160270133 431.6228902418722 z" fill="#2563eb" stroke="none"/><text x="303.4" y="382.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of SYN / x</text><text x="303.4" y="382.25" font-size="11" text-anchor="middle" fill="#2563eb">rcv ACK of SYN / x</text><path d="M391.74 430 L380.231984556649 430.7293699716848 L385.041017336988 420.61437102370525 z" fill="#2563eb" stroke="none"/><text x="303.1" y="149.98" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="303.1" y="149.98" font-size="11" text-anchor="start" fill="#9333ea">CLOSE / delete TCB</text><path d="M426.1 76 L431.70000000000005 86.08 L420.5 86.08 z" fill="#9333ea" stroke="none"/><text x="616.4875113227569" y="217.98093486560202" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / delete TCB</text><text x="616.4875113227569" y="217.98093486560202" font-size="11" text-anchor="middle" fill="#9333ea">CLOSE / delete TCB</text><path d="M448.1947113227569 78.65093486560201 L459.4858656192797 80.99107047109896 L452.17294185210176 89.47406204102543 z" fill="#9333ea" stroke="none"/><text x="324.49997725614406" y="248.34288216314627" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv RST (note 1) / x</text><text x="324.49997725614406" y="248.34288216314627" font-size="11" text-anchor="middle" fill="#9333ea">rcv RST (note 1) / x</text><path d="M392.4971772561441 221.76457788422394 L385.79819459313205 231.1502068605187 L380.9891618127931 221.03520791253922 z" fill="#9333ea" stroke="none"/><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="199.1" y="456.3" font-size="11" text-anchor="start" fill="#dc2626">CLOSE / snd FIN</text><path d="M206.7104 560 L199.88323361107894 550.7071963811783 L210.99371139541864 549.2939436070102 z" fill="#dc2626" stroke="none"/><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="319.3" y="507.25" font-size="11" text-anchor="middle" fill="#dc2626">CLOSE / snd FIN</text><path d="M239.54461538461538 560 L245.38572668223014 550.0577759626491 L251.0719964792192 559.7069384027706 z" fill="#dc2626" stroke="none"/><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="86" y="646.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M209 690 L203.4 679.92 L214.6 679.92 z" fill="#dc2626" stroke="none"/><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.6" y="637.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.64923076923077 690 L319.4405255167678 687.2923577482596 L327.02659217035057 679.0527222752913 z" fill="#dc2626" stroke="none"/><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN,ACK / snd ACK</text><text x="300.3185846153846" y="738.1800000000001" font-size="11" text-anchor="start" fill="#9333ea">rcv FIN,ACK / snd ACK</text><path d="M340.6107692307692 820 L330.875382512483 813.820303774018 L340.7144579488292 808.469360440559 z" fill="#9333ea" stroke="none"/><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="279.7" y="767.25" font-size="11" text-anchor="middle" fill="#dc2626">rcv FIN / snd ACK</text><path d="M330.82153846153847 820 L319.61092639723944 817.3002635047485 L327.19118005561285 809.0552799101793 z" fill="#dc2626" stroke="none"/><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="356.29999999999995" y="776.3000000000001" font-size="11" text-anchor="start" fill="#dc2626">rcv ACK of FIN / x</text><path d="M350.3723076923077 820 L344.75680664555944 809.9286273033927 L355.9567933911451 809.9113965545532 z" fill="#dc2626" stroke="none"/><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">Timeout=2MSL / delete TCB</text><text x="546.9" y="832.25" font-size="11" text-anchor="middle" fill="#dc2626">Timeout=2MSL / delete TCB</text><path d="M697 838 L686.92 843.6 L686.92 832.4 z" fill="#dc2626" stroke="none"/><text x="584.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv FIN / snd ACK</text><text x="584.8000000000001" y="507.25" font-size="11" text-anchor="middle" fill="#16a34a">rcv FIN / snd ACK</text><path d="M697.0215384615385 560 L685.5607329004544 561.2713519933599 L689.8873234537733 550.9407850106658 z" fill="#16a34a" stroke="none"/><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">CLOSE / snd FIN</text><text x="742.4" y="646.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">CLOSE / snd FIN</text><path d="M733.796923076923 690 L728.7629148866224 679.6257452537551 L739.9457765533873 680.2451037460681 z" fill="#16a34a" stroke="none"/><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#fff" stroke="#fff" stroke-width="3" stroke-linejoin="round">rcv ACK of FIN / x</text><text x="738.7" y="776.3000000000001" font-size="11" text-anchor="start" fill="#16a34a">rcv ACK of FIN / x</text><path d="M732.6276923076923 820 L727.0432066088549 809.9113965545532 L738.2431933544406 809.9286273033927 z" fill="#16a34a" stroke="none"/><rect x="0" y="877" width="16" height="11" fill="#eef2ff" stroke="#555"/><text x="21" y="886.5" font-size="11" fill="#1d1d1b">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</text><rect x="0" y="897" width="16" height="11" fill="#fff" stroke="#2563eb"/><text x="21" y="906.5" font-size="11" fill="#1d1d1b">Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)</text><rect x="0" y="917" width="16" height="11" fill="#fff" stroke="#dc2626"/><text x="21" y="926.5" font-size="11" fill="#1d1d1b">Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED</text><rect x="0" y="937" width="16" height="11" fill="#fff" stroke="#16a34a"/><text x="21" y="946.5" font-size="11" fill="#1d1d1b">Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED</text><rect x="0" y="957" width="16" height="11" fill="#fff" stroke="#9333ea" stroke-dasharray="6 4"/><text x="21" y="966.5" font-size="11" fill="#1d1d1b">Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)</text></g><metadata id="figdown-source" data-sha256="8c7cba9f05b9da4dfe03b8c3fe8cc1f6f8983fe67703351f442602c4d0f7157a" data-engine-version="0.1.0"><![CDATA[
2
+ # FigDown — figures as text. Spec: https://github.com/FigDown/figdown
3
+
2
4
  figdown 0.1 flowchart
3
- title TCP Connection State Machine (RFC 9293, Figure 5)
5
+ title "TCP Connection State Machine (RFC 9293, Figure 5)"
4
6
 
5
7
  # ── What this figure is ─────────────────────────────────────────────
6
8
  # The complete TCP connection state machine from RFC 9293 §3.3.2,
@@ -14,17 +16,17 @@ title TCP Connection State Machine (RFC 9293, Figure 5)
14
16
  #
15
17
  # HONEST LIMIT — state-ness is not yet first-class. FigDown has no
16
18
  # "state" genre in which a node IS a state as a machine-declarable fact
17
- # (that genre is the R49 §6 candidate; OQ tracks it). Here each state is
19
+ # (that genre is the `GENRE-EARNING-THRESHOLD` §6 candidate; OQ tracks it). Here each state is
18
20
  # a `shape=rounded` node and the fact "this node is a protocol state" is
19
21
  # carried by the `states` class label, not by the genre. Interim, ruled.
20
22
  #
21
- # HONEST LIMIT — the CLOSED identity (OQ-S26). The canonical RFC figure
23
+ # HONEST LIMIT — the CLOSED identity (`IDENTITY-ASSERTION`). The canonical RFC figure
22
24
  # draws CLOSED TWICE — top (origin) and bottom (the terminus TIME-WAIT
23
- # and LAST-ACK return to) — and so do we, because routing every terminal
25
+ # and LAST-ACK return to) — and so do we, because taking every terminal
24
26
  # edge back to a single top node produced long lines that struck the
25
27
  # setup-abort labels around CLOSED and made the figure unreadable. But
26
28
  # FigDown CANNOT declare two nodes to be the SAME entity: there is no
27
- # node-identity / alias construct (OQ-S26). So `closed` and `closed2`
29
+ # node-identity / alias construct (`IDENTITY-ASSERTION`). So `closed` and `closed2`
28
30
  # are two nodes that a human reads as one state only because the shared
29
31
  # `states` class label SAYS "the two CLOSED nodes are one state."
30
32
  # A reading agent sees two ids; the identity is asserted
@@ -42,14 +44,14 @@ title TCP Connection State Machine (RFC 9293, Figure 5)
42
44
 
43
45
  # ── Semantic classes — the meaning behind every stroke ──────────────
44
46
  # A reading agent queries these to group transitions by role; the
45
- # colour/dash is only presentation (R37), the meaning is the label.
46
- 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
47
- class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)" color=#2563eb
48
- class client "Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED" color=#dc2626
49
- class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" color=#16a34a
50
- class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)" color=#9333ea style=dashed
47
+ # colour/dash is only presentation (`MEANING-RECOVERY-SOURCE`), the meaning is the label.
48
+ 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
49
+ class setup "Connection-setup transition — opening the connection (OPEN, SYN exchange, first ACK)" stroke=#2563eb
50
+ class client "Active-close path (typically the client) — calls CLOSE first: FIN-WAIT-1 → FIN-WAIT-2 / CLOSING → TIME-WAIT → CLOSED" stroke=#dc2626
51
+ class server "Passive-close path (typically the server) — receives the peer's FIN first: CLOSE-WAIT → LAST-ACK → CLOSED" stroke=#16a34a
52
+ class rare "Rare / simultaneous transition — simultaneous open or close, or a reset/abort (RST, close from a half-open state)" stroke=#9333ea style=dashed
51
53
 
52
- # ── The 11 states (CLOSED drawn twice; see OQ-S26 note above) ────────
54
+ # ── The 11 states (CLOSED drawn twice; see `IDENTITY-ASSERTION` note above) ────────
53
55
  node closed "CLOSED" shape=rounded class=states
54
56
  node listen "LISTEN" shape=rounded class=states
55
57
  node synsent "SYN-SENT" shape=rounded class=states
@@ -94,47 +96,58 @@ edge estab -[rcv FIN / snd ACK]-> closewait class=server
94
96
  edge closewait -[CLOSE / snd FIN]-> lastack class=server
95
97
  edge lastack -[rcv ACK of FIN / x]-> closed2 class=server
96
98
 
97
- render
98
- # ── Presentation only (R25) ─────────────────────────────────────────
99
+ layout
100
+ # ── Presentation only (`GUI-WRITEBACK-STRUCTURE`) ─────────────────────────────────────────
99
101
  # The strip test holds: delete every line below and the machine is
100
102
  # unchanged — all 11 states, all 21 transitions and their event/action
101
103
  # labels live in the `node`/`edge`/`class` lines above (closed2 is not
102
- # an orphan: it is the target of two terminal edges).
104
+ # an orphan: it is the target of two terminal edges).
105
+ # the layout namespace is `pin` alone, so "every line below" is a shorter
106
+ # list than it used to be.
103
107
  #
104
108
  # The grid reproduces the CANONICAL Figure 5 composition, mirrored about
105
109
  # one central vertical axis (x≈430, the shared centre of CLOSED, LISTEN
106
- # and ESTABLISHED) with a uniform 130 px row rhythm:
107
- # row0 CLOSED · row1 LISTEN · row2 SYN-RECEIVED | SYN-SENT (mirrored
108
- # at ±220) · row3 ESTABLISHED · row4 FIN-WAIT-1 | CLOSE-WAIT (same
109
- # ±220 mirror) · row5 FIN-WAIT-2, CLOSING, LAST-ACK · row6 TIME-WAIT,
110
- # CLOSED (terminus, under LAST-ACK as the RFC draws it).
110
+ # and ESTABLISHED):
111
+ # row0 CLOSED · row1 LISTEN · row2 SYN-RECEIVED | SYN-SENT (a mirrored
112
+ # pair) · row3 ESTABLISHED · row4 FIN-WAIT-1 | CLOSE-WAIT (same mirror)
113
+ # · row5 FIN-WAIT-2, CLOSING, LAST-ACK · row6 TIME-WAIT, CLOSED
114
+ # (terminus, under LAST-ACK as the RFC draws it).
111
115
  # Column discipline: CLOSED→LISTEN, SYN-RECEIVED→FIN-WAIT-1→FIN-WAIT-2,
112
116
  # CLOSING→TIME-WAIT, CLOSE-WAIT→LAST-ACK→CLOSED run dead vertical; the
113
117
  # simultaneous-open edge SYN-SENT→SYN-RECEIVED runs dead horizontal
114
118
  # between the mirrored pair. All meaning is in the edges; this is
115
119
  # layout, not knowledge.
116
- pin closed at=394,20
117
- pin listen at=394,150
118
- pin synrcvd at=152,280
119
- pin synsent at=607,280
120
- pin estab at=376,410
121
- pin fw1 at=159,540
122
- pin closewait at=600,540
123
- pin fw2 at=159,670
124
- pin closing at=311,670
125
- pin lastack at=607,670
126
- pin timewait at=304,800
127
- pin closed2 at=614,800
128
-
129
- # ── Edge routing — the three long transitions, deliberately calm ────
130
- # active OPEN (closed→synsent) hugs the right margin with two elbows,
131
- # outside the row-2 band; the two aborts back to the top CLOSED bow out
132
- # on the outer left (LISTEN's close) and outer right (SYN-SENT's close)
133
- # so they cross nothing. Every other edge is straight: the FIN-WAIT-1 →
134
- # TIME-WAIT diagonal threads the gap between FIN-WAIT-2 and CLOSING,
135
- # and TIME-WAIT → CLOSED is a plain same-row horizontal.
136
- route closed -> synsent via=790,38;790,298
137
- route listen -> closed via=300,105
138
- route synsent -> closed via=640,120
120
+ #
121
+ # WHAT CHANGED, AND WHY THE TOP ROWS ARE WIDER.
122
+ # Hand-routed corridors used to sit below these pins: the active
123
+ # OPEN hugged the right margin with two elbows, and the two aborts back
124
+ # to the top CLOSED bowed out on the outer left and outer right. The
125
+ # whole construct was WITHDRAWN from the language (`EDGE-GEOMETRY-CONSTRUCTS`) — see
126
+ # spec/migrations.md 0.1 and core §9 `EDGE-IDENTITY-AND-GEOMETRY` — so those three
127
+ # transitions now draw straight, which puts each of them alongside its
128
+ # own anti-parallel partner: CLOSED↔LISTEN and CLOSED↔SYN-SENT each
129
+ # became a near-parallel pair whose two labels landed on top of one
130
+ # another. The linter could not see it (0 crossings either way); a
131
+ # rendered comparison could.
132
+ # The fix is content-zone geometry, not a replacement for the waypoints:
133
+ # rows 1 and 2 gained 20 px and 10 px, and the right-hand column moved
134
+ # ~90 px further out, which pushes the two pairs' label midpoints far
135
+ # enough apart to read. Both pairs still run close together, exactly as
136
+ # the LISTEN↔SYN-RECEIVED pair always has in this figure — the ±7 px
137
+ # anti-parallel fan-out is the language's answer to a same-pair edge, and
138
+ # the class colours (blue = setup, purple = rare) say which stroke is
139
+ # which.
140
+ pin closed at=(394,20)
141
+ pin listen at=(394,170)
142
+ pin synrcvd at=(120,290)
143
+ pin synsent at=(700,290)
144
+ pin estab at=(376,410)
145
+ pin fw1 at=(159,540)
146
+ pin closewait at=(690,540)
147
+ pin fw2 at=(159,670)
148
+ pin closing at=(311,670)
149
+ pin lastack at=(690,670)
150
+ pin timewait at=(304,800)
151
+ pin closed2 at=(697,800)
139
152
 
140
153
  ]]></metadata></svg>
@@ -0,0 +1,160 @@
1
+ # Expressing intent in FigDown — author index
2
+
3
+ > **Non-normative index.** Spec is normative: [spec/core.md](../spec/core.md).
4
+ > Full authoring workflow: [authoring.md](authoring.md) (pick a genre first, then come back here).
5
+ > Layout hints: [layout.md](layout.md).
6
+ > Build and validate: `node tools/build-svg.js X.fd` — errors are `Line N: message`.
7
+
8
+ Purpose: one-line lookup — "I need to show X → use Y." One row per intent. Notes column = a caveat or scope limit, not a tutorial.
9
+
10
+ **Main standard vs EXPERIMENTAL.** Prefer **MAIN STANDARD** genres for
11
+ portable figures: `block`, `bitfield`, `table` (plus the `UNIVERSAL-CORE-KEYWORDS` core keywords
12
+ `figdown`/`title`/`layout` and the layout namespace's normative `pin`).
13
+ Constructs marked EXPERIMENTAL — `threshold`, `band`, `bundle`,
14
+ `plane`, `chart`, option keys
15
+ `plane=`/`z-index=`/`extend=`/`data=`,
16
+ and genres `topology`, `flowchart`, `timing` (`CONSTRUCT-STATUS-TIERS`, spec §10) — still parse and
17
+ are not deprecated, but they sit **outside** the v0.1 compatibility promise
18
+ and may change without a migration entry. **Do not use them when the figure
19
+ must be portable.** `path` and `routing` (with `points=`, `tailport=`,
20
+ `headport=`, `routing=`) were on that list until this release, when `EDGE-GEOMETRY-CONSTRUCTS`
21
+ **withdrew all six from the language**: removed, not renamed, so there is no
22
+ row for them below and no replacement to point at. Edge geometry is the
23
+ engine's; the need is filed as core §9 **`EDGE-IDENTITY-AND-GEOMETRY`**. The parser never warns; this index and §10 are the only
24
+ status sources. Top-level keywords are also **genre-allowlisted (`GENRE-KEYWORD-ALLOWLIST`)**:
25
+ `node` under `figdown 0.1 bitfield` is an error, not a silent hybrid.
26
+
27
+ **This file is the single intent index.** authoring.md Step 3 used to carry a
28
+ second, partly-overlapping table; every intent it held now lives here, so there
29
+ is one place to look and nothing to reconcile.
30
+
31
+ ---
32
+
33
+ ## Structure & composition
34
+
35
+ | I need to show… | Use | Notes |
36
+ |---|---|---|
37
+ | containment — node belongs inside a box | `group g "Label"` + `node n "…" in=g` | one level of nesting only (spec §2.2); for deeper, represent the inner group as a proxy node |
38
+ | set membership / category (color + legend) | `class c "meaning" fill=… style=…` + `class=c` on members | legend derives automatically; bare `fill=` carries no named meaning |
39
+ | one class used on both nodes and edges | `class c "meaning" fill=… stroke=…` — BOTH keys on the one class | Since 0.1 (`INTERIOR-LESS-ELEMENT-PAINT`) the rule is per CHANNEL: `fill=` paints members that have an interior (a node box) and is inapplicable to an edge, which has none; `stroke=` paints the edge line and a node's outline; `style=` applies to both. So one class still carries one meaning for both kinds of member — do NOT split it. A class an edge joins MUST declare `stroke=` or `style=`: `fill=`-only (`INTERIOR-LESS-ELEMENT-PAINT`) and no-paint-at-all (`CLASS-PAINT-REQUIREMENT`) are both line errors, because the edge would otherwise lose its colour silently. `fill=`, `stroke=` and `style=` are all NORMATIVE since 0.1 (`STROKE-KEY-STATUS`) |
40
+ | hierarchy / tree | directed `edge` chain; `flow down` to orient | the edges carry the tree; group is for spatial containment, not hierarchy |
41
+ | adjacency without a link | `in=` on the same `group`, no `edge` between them | the shared frame communicates co-location |
42
+ | cross-cutting category spanning groups | `class` + `class=` on elements in different groups | one class can mark nodes, edges, and fields across the whole document |
43
+ | system boundary / inside vs outside | `external ext "label"` + edges to/from it; internal nodes in a `group` | `external` is never drawn as a shape — the edge ends open; it names an external I/O *endpoint*, not the frame drawn around a system (that is `group`) |
44
+ | ownership zones / domain coloring | `class zone "Owner" fill=…` on a `group` or its members | assign to the group itself to color the frame |
45
+ | a region the source drew as a **cloud** (the internet, a transit network, an overlay fabric) | `node net "The internet" shape=ellipse` (or a `group` when other elements sit inside it), plus a `class` when the distinction is one a reader must query | `shape=cloud` was **retired at 0.1** (`SHAPE-ENUM-VOCABULARY`) — it was the only value in a geometric enum that named a domain, which `SHAPE-ENUM-VOCABULARY` forbids. `ellipse` is what preserves the drawing; the label is what preserves the meaning, and only you know which one the figure needed |
46
+ | conditional or optional element (standby link, optional component) | `class cond "Condition text" style=dashed` + `class=cond` on the element | `style=dashed` alone states nothing (`PRESENTATION-AS-MEANING-CARRIER`); write the condition in the class label |
47
+ | one-level nesting limit | `in=` on `node`; `group … in=…` is a line error | for two levels: add a proxy `node` representing the inner group |
48
+ | layer stack or spatial arrangement where order is knowledge | `edge` chain + `flow down`, or single-row `table`; state the order in prose | no spatial-arrangement construct yet — see Known limits `MEANINGFUL-ARRANGEMENT` |
49
+ | slot map / position in a spatial grid | state positions in label text + `pin` in the layout zone | `pin` is geometry only, and the layout zone is ignored by default (`GENRE-NAMESPACE`) — use a `table` for anything a reader must count or address |
50
+ | several links that are one logical thing (LAG, ES, trunk group) | `bundle b1 "LAG" a--c,b--c` — ONE comma-delimited token (the space form was retired at 0.1) | the dashed ring around the members is derived automatically; members resolve **as written** (`a--c` is not `c--a`). **EXPERIMENTAL** (`CONSTRUCT-STATUS-TIERS`) — works unchanged; it is `topology` vocabulary and every corpus use of it is in a `topology` document |
51
+ | an independent plane a reader can separate (overlay vs underlay, control vs data) | `plane overlay "VXLAN tunnels" z-index=2` + `plane=overlay` on its elements | **EXPERIMENTAL** (`CONSTRUCT-STATUS-TIERS`) — the keyword, `z-index=` and `plane=` alike; works unchanged. The plane's **label is the knowledge**; `z` is paint order (implicit `base` is model `z` = 0; an omitted `z-index=` takes the 1-based declaration index). Honest limit: `z` reorders only the annotation pass — edges, bundle rings, thresholds, bands — nodes and groups paint in document order whatever their plane says (spec §5) |
52
+ | same row / same column (peers, stages, siblings) | `rank a,b,c` — a **semantic** (content) line, before `layout`; ONE comma-delimited token (the space form was retired at 0.1) | under `flow down` a rank shares a row, so rank the lateral peers, not the mainline ([layout.md §8](layout.md#8-cautions-from-a-production-corpus)) |
53
+ | cardinality, port name, or role at each end of a relationship | endpoint labels: `a [1] -[places]-> [N] b` | three label positions on one edge (tail · mid · head); `[flags[3:0]]` nests, `["…"]` for `\n` or unbalanced brackets. `label=`/`taillabel=`/`headlabel=` are retired options, not spellings |
54
+ | a shape in the source figure that carries no text at all (a junction, an unlabelled multiplexer, a bare glyph) | an explicitly empty label: `node j "" shape=circle` | `""` is a WRITTEN value, recorded as `label: ""` and drawn blank. Omitting the label instead makes the renderer display the **id**, which is text the source does not have. Reserve the omitted form for a label you could not read, and say so in a `#` comment (spec §2.1/§12.3, `EMPTY-LABEL-STATE`) |
55
+ | two concept areas that shared one original image | one `.fd` per concept; the Markdown composes them | `X.fd ⇔ X.svg` is one-to-one — never pack two unrelated figures into one source |
56
+ | hybrid panels in **one** artifact (scene + table, or block + bitfield panel) | **multi-section `MULTI-FIGURE-DOCUMENTS`**: a second `figdown 0.1 <genre>` line starts a new section; sections stack into one SVG | **MAIN STANDARD** hybrid path. Each section has one genre and its own id space — no cross-section edges. Nested `table`/`bitfield` under a single scene header may still parse (legacy) but is not the taught pattern |
57
+ | pure protocol header / pure table / pure box diagram | one section: `figdown 0.1 bitfield` or `table` or `block` only | do not put `node` under `bitfield`/`table` (`GENRE-KEYWORD-ALLOWLIST` allowlist error) |
58
+
59
+ ---
60
+
61
+ ## Flow & behaviour
62
+
63
+ | I need to show… | Use | Notes |
64
+ |---|---|---|
65
+ | ordered steps | directed `edge` chain; order = edge direction | `flow right` or `flow down` sets reading axis |
66
+ | decision + branches | under `flowchart`: `decision q "…"` + `edge q -[yes]-> …` / `edge q -[no]-> …` | **Prefer `decision` over `node … shape=diamond`, because the next reader then has to know nothing** — a diamond has to be learned, a word is just read. And the convention is not reliable: of 216 question-labelled nodes in the production corpus, 78% are diamond, 14% ellipse, 8% carry no shape. Under `block`/`topology` the keyword does not exist, so `shape=diamond` + labelled edges remains the baseline (`SHAPE-ENUM-VOCABULARY`: geometry only) |
67
+ | a step, a start or an end | under `flowchart`: `process p "…"` · `terminator t "…"` | Same reason. The geometry is DERIVED (box / rounded); `shape=` still works and changes only the drawing, never the role. A bare `node` under `flowchart` states NO role — use it when the thing genuinely is not a step, a branch or an endpoint (a datastore, a wait, an annotation) |
68
+ | an n-way dispatch on a value | one `decision` with ≥3 outgoing edges, each `[mid]`-labelled with the value that selects it | The single most under-marked control point in real figures. Label every exit — including the fallback (`[other]`). There is no `default`/`otherwise` keyword and none is planned (0 corpus uses) |
69
+ | loop / retry | directed cycle back to an earlier node; condition in the mid-label | **the back-edge IS the loop** — there is no `loop`/`while` construct and none is planned (172 back-edges in the corpus, not one marking itself). But a cyclic graph needs explicit arrangement, not acceptance of an unreadable crossing ([layout.md §9](layout.md#9-cyclic-flows-why-the-ladder-stalls-and-what-to-do-instead)) |
70
+ | error paths | `class err "Error path" style=dashed` + `class=err` on error edges | separate semantic category from the happy path |
71
+ | state machine / lifecycle | nodes as states + labelled directed edges as transitions (`block`, or experimental `flowchart`) | planned experimental genre **`statechart`** (draft only — not in engine yet; see decisions/registry.md); order meaning still rides on labels |
72
+ | pipeline stages | `node` per stage + `external` at the mouths + `flow right` + `rank` | `external` marks where data enters/leaves the figure |
73
+ | event → action | `edge src -[event]-> tgt` | mid-label is the trigger; head-label can name the action |
74
+ | precedence / partial order | a DAG of directed edges | absence of an edge means no stated constraint |
75
+ | fan-out / fan-in | edges from/to a common node | AND-vs-XOR join discipline: declare a `class` (`FLOWCHART-GENRE-DESIGN` — no first-class gateway yet) |
76
+ | message exchange between parties | nodes as parties + labelled directed edges; ordinal labels for ordering | strict ordering: number labels consistently (`FLOWCHART-GENRE-DESIGN`) — see Known limits |
77
+ | a label too wide for its diamond / ellipse / cylinder | nothing — shapes size themselves from their **inscribed** area | do not declare an extent; see the note below |
78
+
79
+ > **Delete declared extents added to make a shape fit its text.** Non-rectangular
80
+ > shapes were once sized by their bounding box, so authors declared an extent to
81
+ > stop `diamond` and `ellipse` labels overflowing. That is fixed. A declared
82
+ > extent is rigid and must be re-tuned whenever a label changes, so removing it
83
+ > is a maintenance win. Verified on production documents and reproducible here:
84
+ > strip every declared extent from this repo's examples that carry one and
85
+ > `node tools/shape-check.js --strict` still reports zero containment failures.
86
+ > The drawing is *not* byte-identical — the canvas shifts — but it stays
87
+ > geometrically correct. A layout-motivated extent
88
+ > ([layout.md](layout.md) rung 3) is a separate question.
89
+ >
90
+ > The spelling changed: `size` was **retired** and its keys moved
91
+ > onto `pin`, so an extent is now written `pin <id> width=<px> height=<px>`
92
+ > (`at=` optional, and `width=`/`height=` apply to **nodes only**). A stale
93
+ > document that still carries a `size` line gets a named migration diagnostic,
94
+ > not `unrecognized line`.
95
+
96
+ ---
97
+
98
+ ## Data & format
99
+
100
+ | I need to show… | Use | Notes |
101
+ |---|---|---|
102
+ | bit-level register / header | `bitfield id "label" [word=32] numbering=lsb0\|msb0` + `field "name" width` | `numbering=` is REQUIRED, no default: `lsb0` for hardware registers, `msb0` for IETF RFC headers. Fields are declared **left to right** under both values (`DECLARATION-ORDER-SEMANTICS`), so under `lsb0` declare them MSB-first — the leftmost bit number is the highest (`DECLARATION-ORDER-SEMANTICS`) |
103
+ | byte-level frame with no bit ruler | single-row `table`, cell order = wire order | `bitfield` adds a bit ruler — use `table` when bits are not the unit |
104
+ | struct / record layout | `table` with `Offset \| Field \| Type \| Width \| Notes` columns | cell order is authoritative; `width` for presentation only |
105
+ | memory map as lookup | `table` with address ranges + `cell (r,c) fill=` for highlights | a row `cell r highlight` and a cell `fill=`/`class=` that resolves to a fill on that same row may not both apply (`ROW-HIGHLIGHT-CELL-FILL-COLLISION`) — tint the row or paint the cell, not both |
106
+ | lookup / translation table | `table` + `cell (r,c) fill=` for highlights | same `ROW-HIGHLIGHT-CELL-FILL-COLLISION` constraint: `highlight` (row) and `fill=` (cell) paint one channel |
107
+ | mapping between two node sets | `edge` + `class` naming the relation | the class label is the relation name; edges draw the pairs |
108
+ | pointer chains between nodes | directed `edge` chain | can't target a table cell — see Known limits `CELL-EDGE-ANCHORS` |
109
+ | conditional / variable-length fields | `field "name" width present="..."` or `field "name" *` in a `bitfield` | `present=` = fixed width, present only under a condition — a mandatory quoted, opaque-prose value: `present=""` claims conditional presence without stating the condition, `present="C = 1"` states it (`PRESENCE-CONDITION-EXPRESSION`). Do NOT repeat the condition in the label: `field "Checksum" 16 present="C = 1"`, never `field "Checksum (present if C=1)" 16 present="C = 1"`. `*` = at most one per bitfield (row remainder / trailing blob). GRE-style headers with several such fields use several `present=` fields + one trailing `*` |
110
+ | multi-line text inside a table cell | HTML `<br>` / `<br/>` / `<br />` in the cell | pipe cells are raw GFM; `\n` is **not** an escape there (two literal characters). Other HTML is literal text, not markup |
111
+ | endianness / dual ruler convention | two `bitfield` blocks with different `numbering=` | `lsb0` and `msb0` produce different rulers from the same fields, and **identical geometry**: only the numbers on the columns change (`DECLARATION-ORDER-SEMANTICS`) |
112
+ | mode-dependent field meaning | separate labelled `bitfield` blocks + `description=` per field | no first-class variant yet — see Known limits `BITFIELD-DISCRIMINATED-VARIANTS`. `description=` draws no ink beyond a tooltip, so state which mode applies in the label too if a human must see it |
113
+ | nested encapsulation / multi-tier header | `table` with <code>\|\|</code> colspan spans in header rows | <code>^^</code> for rowspan; cell order = wire order |
114
+ | a repeated element (an array of N identical fields) | `field "<name>" <width> index=<first>..<last>` — declare the element **once** | 0.1 (`BITFIELD-REPETITION-CONSTRUCT`), drawing revised 0.1 (`REPEATED-RUN-DRAWING`). The engine draws the first element, the elision and the last element, each with its index appended to your label; there is no line to write for any of it. Tri-state, like `present=`: key absent = no repetition claim, `index=""` = repeats with no index stated, a range states one. Write a LITERAL range whenever the source gives you one — a determinate run keeps every later field's offset computable. Only the LAST index may be prose; the first is always an integer |
115
+
116
+ ---
117
+
118
+ ## Quantity, time & relation
119
+
120
+ | I need to show… | Use | Notes |
121
+ |---|---|---|
122
+ | proportion of a whole / fill level | `band "Headroom" 15..35% in=g fill=…` on a `group` or `node` | the quoted label is **mandatory** and comes **first** (`BAND-LABEL-STATUS`) — a band with no name asserts nothing; `band "X" 15%` = 0..15%. The label's colour is derived from the band's fill (`LABEL-COLOUR-SOURCE`); there is no key for it. **EXPERIMENTAL** (`CONSTRUCT-STATUS-TIERS`) |
123
+ | threshold / watermark | `threshold "label" in=g offset=N%` on a `group` or `node` | spelled `guide` until 0.1 (`THRESHOLD-KEYWORD-SPELLING`). Label and the `%` are both mandatory; there is no `value=` and no `ref=` — the reference lives in the label (`THRESHOLD-VALUE-SCOPE`). `threshold` and `band` take the same two scopes (`AUTHORING-INTENT-OVER-RENDERING`). **EXPERIMENTAL** (`CONSTRUCT-STATUS-TIERS`) |
124
+ | quantity comparison | `table` with numeric columns | `▁▃▅▇` Unicode blocks as sparklines in cells (`TABLE-SPARKLINE`) |
125
+ | signal values over time | `timing id "label"` + `signal name chars` (one char = one cycle) | lane alphabet: `0 1 p n x = .` (a strict subset of WaveDrom's; `2`–`9` retired at 0.1). `timing` is an **EXPERIMENTAL** genre (`CONSTRUCT-STATUS-TIERS`, spelled `wave` until 0.1) — the alphabet is settled, the surface around it is not |
126
+ | event ordering without exact times | directed edges with ordinal mid-labels (`-[1: SYN]->`) | number labels consistently; no sequence genre yet |
127
+ | visual code / legend | `class` — legend derives automatically from declaration order | each `class` line gives swatch + meaning text |
128
+ | annotation explaining why | `node note "…" style=dashed` + dashed edge to the target | no first-class callout yet — see Known limits `ROW-INDEX-GUTTER` |
129
+ | cross-references within a scene | `edge` + `class` naming the relation | can't reference a table cell or bitfield field — see Known limits `CELL-EDGE-ANCHORS` / `CROSS-BLOCK-REFERENCES` |
130
+ | units | state them in the label or column header | no machine-readable unit type; interim: text in the label |
131
+ | trends / rates | `table` with a values column; `▁▃▅▇` for sparklines | charts are out of scope (`CHART-SCOPE-BOUNDARY`) — keep the raster + prose |
132
+
133
+ ---
134
+
135
+ ## Known limits (v0.1)
136
+
137
+ Each entry: what cannot be expressed today · OQ reference · sanctioned interim workaround.
138
+
139
+ - **same entity in two views** — no way to assert two nodes are the same participant; OQ pending; interim: shared `class` + a note stating the identity.
140
+ - **strict message ordering** — order carried only by label numbering convention; sequence genre candidate (v0.2, `FLOWCHART-GENRE-DESIGN`); interim: number labels consistently (e.g. `1: SYN`, `2: SYN-ACK`).
141
+ - **first-class callout / annotation** — a callout is structurally a node; `ROW-INDEX-GUTTER`; interim: `node note "…" style=dashed` + dashed `edge`.
142
+ - **cell anchors** — an `edge` cannot target a `table` cell or `bitfield` field; `CELL-EDGE-ANCHORS`; interim: whole-table relation + cell name in the edge label.
143
+ - **cross-block references** — no locator from one typed block to another, and no way to declare a composed region subordinate to a host element ("this table is about node X"); `CROSS-BLOCK-REFERENCES`; interim: prose note or a linking `edge` between the host nodes.
144
+ - **a repeat COUNT that names another field** — `index=` says a `bitfield` field repeats and gives the range, but the last index can only be prose when the count lives in another field (`index="0..Last Entry"`), because no value in the language may name a field; `BITFIELD-REPETITION-CONSTRUCT`'s surviving half, downstream of the locator problem `ANNOTATION-LOCATOR-SPLIT`; interim: write the prose end — the run is then honestly indeterminate, which is the correct reading, and say so in a `description=` or a `class` meaning.
145
+ - **byte-unit packet construct** — `bitfield` adds an unwanted bit ruler; `BYTE-UNIT-PACKET-BLOCKS`; interim: single-row `table`.
146
+ - **meaningful arrangement** (layer stacks, floorplans, proportional memory maps) — no declared-arrangement construct, and the layout zone is default-ignored so `pin` is not a substitute (`GENRE-NAMESPACE`); `MEANINGFUL-ARRANGEMENT`; interim: state the arrangement meaning in prose + read the layout zone.
147
+ - **timing-diagram span parameters** (setup/hold spans) — no named span between signal events in `timing`; `TIMING-MEASUREMENT-ANNOTATIONS`; interim: keep the original raster + prose.
148
+ - **mode-dependent field variants** — same bit range, different decode per mode; `BITFIELD-DISCRIMINATED-VARIANTS`; interim: separate labelled `bitfield` blocks + `description=`.
149
+ - **multiplicity / machine-readable units** — no count or unit type; pending corpus frequency; interim: state them in label text.
150
+ - **AND vs XOR fan-out** — `decision` (`flowchart` only) settles it for a decision's own exits: exactly one fires. For any OTHER fan-out — a `process` with several successors — the language still cannot say whether all branches fire or one does; `FLOWCHART-GENRE-DESIGN` sub-question; interim: declare a `class` naming the join discipline. `fork`/`join`/`merge` are recorded as excluded (0 corpus uses).
151
+ - **swimlanes / partitions in a flowchart** — no container-with-an-axis construct; recorded as the next flowchart candidate (Mermaid #2028, UML `ActivityPartition`, BPMN `Lane`); interim: `group` + a label naming the lane, which loses the axis.
152
+ - **conditional presence in scene elements** — `present=` is a `bitfield`-only option key; `node a "A" present="..."` is a line error; <!-- fence-check: skip --> v0.2 candidate; interim: `class cond "condition text" style=dashed` + `class=cond`.
153
+ - **mutually exclusive `bitfield` interpretations — a correctness trap, not a cosmetic one.** `break` is presentation-only and never reorders or skips bits, so alternative decodings of the *same* bits stacked with `break` are read by a machine as one long contiguous sequence: an 18-bit register drawn as eight alternative encodings computes as 144 bits. There is no union/case construct and nothing warns. Open question: what a variant/discriminator construct should look like (adjacent to `BITFIELD-DISCRIMINATED-VARIANTS`). Interim: **one `bitfield` block per alternative**, each labelled with the discriminator value that selects it (`bitfield ctl_a "Control — mode=0 (18 bits)" word=18`).
154
+ - **aside / non-participating annotation** — no node kind that comments on a figure without joining its graph; a free `node` parses but reads as a participant. Open question: whether an aside is a node kind or a document-level construct. Interim: prose beside the figure, or `node note "…" style=dashed` + a dashed `edge` (which does join the graph).
155
+ - **repeated-subgraph reuse** — no way to declare a sub-structure once and instantiate it *n* times. Open question: whether declare-once/instantiate-many belongs in the language at all or in the generator above it. Interim: write each instance out.
156
+ - **two-level group nesting** — `group … in=…` is a line error; nesting is one level in v0.1. Open question: whether deeper nesting needs new syntax or only a renderer change. Interim: a proxy `node` for the inner group.
157
+ - **mux / selector shape** — `shape=` is a closed geometric set (`box|rounded|circle|ellipse|diamond|cylinder`); `shape=mux` is a line error. Open question: whether the mux trapezoid is geometry (admissible under `SHAPE-ENUM-VOCABULARY`) or a domain noun (excluded). Interim: `shape=diamond` or a box, with the selector role in the label or a `class`.
158
+ - **blank / idle lane character in `timing`** — the lane alphabet is closed and has no "no activity" cycle (`_` and `z` are line errors). Authors reach for `x`, which draws as an unknown-value hatch — a different claim. Open question: whether idle is a lane value or a `gap`-like marker. Interim: `x` plus a `data=` entry or note saying it means idle.
159
+ - **charts / plots** — deliberately out of scope (`CHART-SCOPE-BOUNDARY`); keep the raster and describe in prose or a `table`.
160
+ - **closed-world readings** (anything not drawn is denied) — absence leaves no trace in the syntax; no tool can catch this. When a figure's meaning depends on what is NOT drawn, declare the closed-world reading explicitly and state every prohibition in a table row or note (`COMPLETENESS-DEFINITION` addendum).