schematex 0.7.0 → 0.8.1

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.
Files changed (147) hide show
  1. package/dist/ai/ai-sdk.cjs +19 -19
  2. package/dist/ai/ai-sdk.d.cts +4 -4
  3. package/dist/ai/ai-sdk.d.ts +4 -4
  4. package/dist/ai/ai-sdk.js +14 -14
  5. package/dist/ai/index.cjs +25 -25
  6. package/dist/ai/index.d.cts +3 -3
  7. package/dist/ai/index.d.ts +3 -3
  8. package/dist/ai/index.js +14 -14
  9. package/dist/{api-JaBtsUT8.d.cts → api-BDMaX1cT.d.cts} +1 -1
  10. package/dist/{api-DIJM_mqp.d.ts → api-OED2jUVj.d.ts} +1 -1
  11. package/dist/browser.cjs +20 -20
  12. package/dist/browser.d.cts +3 -3
  13. package/dist/browser.d.ts +3 -3
  14. package/dist/browser.js +14 -14
  15. package/dist/{chunk-SHMG7BVF.cjs → chunk-6L46VIXI.cjs} +4 -4
  16. package/dist/{chunk-SHMG7BVF.cjs.map → chunk-6L46VIXI.cjs.map} +1 -1
  17. package/dist/{chunk-OFKRELZK.js → chunk-ABCMTAOZ.js} +3 -3
  18. package/dist/{chunk-OFKRELZK.js.map → chunk-ABCMTAOZ.js.map} +1 -1
  19. package/dist/{chunk-34O3C6OC.cjs → chunk-AJJYWXZB.cjs} +4 -4
  20. package/dist/chunk-AJJYWXZB.cjs.map +1 -0
  21. package/dist/{chunk-5FYPSIGD.cjs → chunk-B5AQ3CG3.cjs} +4 -4
  22. package/dist/{chunk-5FYPSIGD.cjs.map → chunk-B5AQ3CG3.cjs.map} +1 -1
  23. package/dist/{chunk-3GAPHXCE.js → chunk-CDK7KDIW.js} +3 -3
  24. package/dist/{chunk-3GAPHXCE.js.map → chunk-CDK7KDIW.js.map} +1 -1
  25. package/dist/{chunk-Z3A2UNK2.cjs → chunk-D34VGLSE.cjs} +4 -4
  26. package/dist/{chunk-Z3A2UNK2.cjs.map → chunk-D34VGLSE.cjs.map} +1 -1
  27. package/dist/{chunk-JAYJ2G4R.cjs → chunk-DHI7YAQJ.cjs} +4 -4
  28. package/dist/{chunk-JAYJ2G4R.cjs.map → chunk-DHI7YAQJ.cjs.map} +1 -1
  29. package/dist/{chunk-C7V57V6O.js → chunk-DX44TBFZ.js} +3 -3
  30. package/dist/{chunk-C7V57V6O.js.map → chunk-DX44TBFZ.js.map} +1 -1
  31. package/dist/{chunk-MPCSWRZC.js → chunk-E3CAJGJM.js} +3 -3
  32. package/dist/{chunk-MPCSWRZC.js.map → chunk-E3CAJGJM.js.map} +1 -1
  33. package/dist/{chunk-EENA7KNU.js → chunk-E7LXMEKX.js} +3 -3
  34. package/dist/{chunk-EENA7KNU.js.map → chunk-E7LXMEKX.js.map} +1 -1
  35. package/dist/{chunk-HFATQXFN.cjs → chunk-HBQFUZBD.cjs} +8663 -546
  36. package/dist/chunk-HBQFUZBD.cjs.map +1 -0
  37. package/dist/{chunk-P26FCZP3.js → chunk-IXRPRMHI.js} +3 -3
  38. package/dist/{chunk-P26FCZP3.js.map → chunk-IXRPRMHI.js.map} +1 -1
  39. package/dist/{chunk-IFNNV54X.js → chunk-J6AIKEVV.js} +8634 -526
  40. package/dist/chunk-J6AIKEVV.js.map +1 -0
  41. package/dist/{chunk-WU2N6ZVM.js → chunk-JGCKW5RS.js} +3 -3
  42. package/dist/{chunk-WU2N6ZVM.js.map → chunk-JGCKW5RS.js.map} +1 -1
  43. package/dist/{chunk-J3EPFZPX.cjs → chunk-JIJWGHRN.cjs} +5 -5
  44. package/dist/{chunk-J3EPFZPX.cjs.map → chunk-JIJWGHRN.cjs.map} +1 -1
  45. package/dist/{chunk-TACTEF2N.cjs → chunk-JIUC4DRS.cjs} +4 -4
  46. package/dist/{chunk-TACTEF2N.cjs.map → chunk-JIUC4DRS.cjs.map} +1 -1
  47. package/dist/{chunk-JTGTWBAD.js → chunk-KF3DFASA.js} +3 -3
  48. package/dist/chunk-KF3DFASA.js.map +1 -0
  49. package/dist/{chunk-77GPD4YQ.cjs → chunk-KUITLG4N.cjs} +4 -4
  50. package/dist/{chunk-77GPD4YQ.cjs.map → chunk-KUITLG4N.cjs.map} +1 -1
  51. package/dist/{chunk-2F45Y2ON.cjs → chunk-NXU4XKLY.cjs} +4 -4
  52. package/dist/{chunk-2F45Y2ON.cjs.map → chunk-NXU4XKLY.cjs.map} +1 -1
  53. package/dist/{chunk-UHPGWO77.cjs → chunk-Q2YRJHFB.cjs} +4 -4
  54. package/dist/{chunk-UHPGWO77.cjs.map → chunk-Q2YRJHFB.cjs.map} +1 -1
  55. package/dist/{chunk-CTMJ3XP2.cjs → chunk-Q3SJB7H3.cjs} +708 -7
  56. package/dist/chunk-Q3SJB7H3.cjs.map +1 -0
  57. package/dist/{chunk-2TUZ3QJA.js → chunk-T3GV7OVF.js} +3 -3
  58. package/dist/{chunk-2TUZ3QJA.js.map → chunk-T3GV7OVF.js.map} +1 -1
  59. package/dist/{chunk-5IKOLUWK.js → chunk-TO6PNBT3.js} +3 -3
  60. package/dist/chunk-TO6PNBT3.js.map +1 -0
  61. package/dist/{chunk-T3FV73LM.js → chunk-UOM3EDE6.js} +3 -3
  62. package/dist/{chunk-T3FV73LM.js.map → chunk-UOM3EDE6.js.map} +1 -1
  63. package/dist/{chunk-NAGUZFXX.cjs → chunk-VHDSPI6A.cjs} +3 -2
  64. package/dist/chunk-VHDSPI6A.cjs.map +1 -0
  65. package/dist/{chunk-YMZTXOUG.js → chunk-WNGOG4CI.js} +3 -3
  66. package/dist/{chunk-YMZTXOUG.js.map → chunk-WNGOG4CI.js.map} +1 -1
  67. package/dist/{chunk-LPAWZYDU.cjs → chunk-YMFYPB5Y.cjs} +14 -14
  68. package/dist/{chunk-LPAWZYDU.cjs.map → chunk-YMFYPB5Y.cjs.map} +1 -1
  69. package/dist/{chunk-IFXHIYZE.js → chunk-YWYWVKRB.js} +706 -5
  70. package/dist/chunk-YWYWVKRB.js.map +1 -0
  71. package/dist/{diagnostics-BKRow1ur.d.ts → diagnostics-hObcaaFC.d.cts} +1 -1
  72. package/dist/{diagnostics-BKRow1ur.d.cts → diagnostics-hObcaaFC.d.ts} +1 -1
  73. package/dist/diagrams/blockdiagram/index.cjs +6 -6
  74. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  75. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  76. package/dist/diagrams/blockdiagram/index.js +2 -2
  77. package/dist/diagrams/circuit/index.cjs +9 -9
  78. package/dist/diagrams/circuit/index.d.cts +1 -1
  79. package/dist/diagrams/circuit/index.d.ts +1 -1
  80. package/dist/diagrams/circuit/index.js +2 -2
  81. package/dist/diagrams/ecomap/index.d.cts +1 -1
  82. package/dist/diagrams/ecomap/index.d.ts +1 -1
  83. package/dist/diagrams/entity/index.cjs +6 -6
  84. package/dist/diagrams/entity/index.d.cts +1 -1
  85. package/dist/diagrams/entity/index.d.ts +1 -1
  86. package/dist/diagrams/entity/index.js +2 -2
  87. package/dist/diagrams/fishbone/index.d.cts +1 -1
  88. package/dist/diagrams/fishbone/index.d.ts +1 -1
  89. package/dist/diagrams/flowchart/index.d.cts +2 -2
  90. package/dist/diagrams/flowchart/index.d.ts +2 -2
  91. package/dist/diagrams/genogram/index.d.cts +1 -1
  92. package/dist/diagrams/genogram/index.d.ts +1 -1
  93. package/dist/diagrams/ladder/index.cjs +6 -6
  94. package/dist/diagrams/ladder/index.d.cts +1 -1
  95. package/dist/diagrams/ladder/index.d.ts +1 -1
  96. package/dist/diagrams/ladder/index.js +2 -2
  97. package/dist/diagrams/logic/index.cjs +8 -8
  98. package/dist/diagrams/logic/index.d.cts +1 -1
  99. package/dist/diagrams/logic/index.d.ts +1 -1
  100. package/dist/diagrams/logic/index.js +2 -2
  101. package/dist/diagrams/orgchart/index.cjs +8 -8
  102. package/dist/diagrams/orgchart/index.d.cts +1 -1
  103. package/dist/diagrams/orgchart/index.d.ts +1 -1
  104. package/dist/diagrams/orgchart/index.js +2 -2
  105. package/dist/diagrams/pedigree/index.d.cts +1 -1
  106. package/dist/diagrams/pedigree/index.d.ts +1 -1
  107. package/dist/diagrams/phylo/index.cjs +7 -7
  108. package/dist/diagrams/phylo/index.d.cts +1 -1
  109. package/dist/diagrams/phylo/index.d.ts +1 -1
  110. package/dist/diagrams/phylo/index.js +2 -2
  111. package/dist/diagrams/sld/index.cjs +8 -8
  112. package/dist/diagrams/sld/index.d.cts +1 -1
  113. package/dist/diagrams/sld/index.d.ts +1 -1
  114. package/dist/diagrams/sld/index.js +2 -2
  115. package/dist/diagrams/sociogram/index.cjs +6 -6
  116. package/dist/diagrams/sociogram/index.d.cts +1 -1
  117. package/dist/diagrams/sociogram/index.d.ts +1 -1
  118. package/dist/diagrams/sociogram/index.js +2 -2
  119. package/dist/diagrams/timing/index.cjs +5 -5
  120. package/dist/diagrams/timing/index.d.cts +1 -1
  121. package/dist/diagrams/timing/index.d.ts +1 -1
  122. package/dist/diagrams/timing/index.js +2 -2
  123. package/dist/diagrams/venn/index.cjs +9 -9
  124. package/dist/diagrams/venn/index.d.cts +1 -1
  125. package/dist/diagrams/venn/index.d.ts +1 -1
  126. package/dist/diagrams/venn/index.js +2 -2
  127. package/dist/{index-DU2h1Y-a.d.cts → index-B0YO7rx8.d.cts} +1 -1
  128. package/dist/{index-Dq3Mfxay.d.ts → index-u3KZBdas.d.ts} +1 -1
  129. package/dist/index.cjs +87 -51
  130. package/dist/index.d.cts +35 -5
  131. package/dist/index.d.ts +35 -5
  132. package/dist/index.js +17 -17
  133. package/dist/react.cjs +14 -14
  134. package/dist/react.d.cts +2 -2
  135. package/dist/react.d.ts +2 -2
  136. package/dist/react.js +13 -13
  137. package/dist/{tools-OzOQnlnV.d.cts → tools-CVgUmiGP.d.cts} +2 -2
  138. package/dist/{tools-A0HRZ8jj.d.ts → tools-CbBjmZVr.d.ts} +2 -2
  139. package/package.json +1 -1
  140. package/dist/chunk-34O3C6OC.cjs.map +0 -1
  141. package/dist/chunk-5IKOLUWK.js.map +0 -1
  142. package/dist/chunk-CTMJ3XP2.cjs.map +0 -1
  143. package/dist/chunk-HFATQXFN.cjs.map +0 -1
  144. package/dist/chunk-IFNNV54X.js.map +0 -1
  145. package/dist/chunk-IFXHIYZE.js.map +0 -1
  146. package/dist/chunk-JTGTWBAD.js.map +0 -1
  147. package/dist/chunk-NAGUZFXX.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkHFATQXFN_cjs = require('./chunk-HFATQXFN.cjs');
3
+ var chunkHBQFUZBD_cjs = require('./chunk-HBQFUZBD.cjs');
4
4
 
5
5
  // src/ai/registry.ts
6
6
  var DIAGRAM_REGISTRY = [
@@ -340,6 +340,90 @@ var DIAGRAM_REGISTRY = [
340
340
  cluster: "generic",
341
341
  standard: "Timeline convention with era bands",
342
342
  syntaxKey: "timeline"
343
+ },
344
+ // ── Risk & reliability (Bucket B) ────────────────────────────
345
+ {
346
+ type: "eventtree",
347
+ name: "Event Tree Analysis",
348
+ tagline: "Forward-looking risk: one initiating event branches through each safety function into outcome sequences, with computed path frequencies.",
349
+ useWhen: "Use to propagate the consequences of an initiating event through a chain of barriers/safety functions and quantify each outcome's frequency \u2014 the inductive complement to a fault tree and the right wing of a bowtie. Header `eventtree`/`eta`; declare the initiating event with a frequency, the ordered functions with success/failure branch probabilities, and outcome rows with `s`/`f`/`*` patterns. The engine computes path frequency = f_initiating \xD7 \u03A0 branch-probabilities and flags the dominant sequence.",
350
+ cluster: "risk-reliability",
351
+ standard: "IEC 62502:2010 \xB7 NUREG/CR-2300 (PRA) \xB7 ISO 31010 Annex B; see 39-EVENT-TREE-STANDARD.md",
352
+ syntaxKey: "eventtree"
353
+ },
354
+ {
355
+ type: "fmea",
356
+ name: "FMEA (Failure Mode and Effects Analysis)",
357
+ tagline: "The reliability worksheet that computes its own risk \u2014 RPN = S\xD7O\xD7D plus AIAG-VDA Action Priority, ranked and colour-coded.",
358
+ useWhen: "Use to score and prioritise how each component/process step can fail \u2014 severity, occurrence, detection \u2014 and decide what to fix first. Header `fmea`; declare item/function \u2192 failure mode \u2192 effect (with `sev`) \u2192 cause (with `occ`) \u2192 controls (with `det`). The engine computes RPN = S\xD7O\xD7D and the AIAG-VDA Action Priority (High/Medium/Low), sorts the sheet, and colour-fills the RPN/AP cells by risk. Schematex's first table-shaped diagram.",
359
+ cluster: "risk-reliability",
360
+ standard: "AIAG-VDA FMEA Handbook (2019) \xB7 IEC 60812:2018 \xB7 SAE J1739 \xB7 MIL-STD-1629A; see 40-FMEA-STANDARD.md",
361
+ syntaxKey: "fmea"
362
+ },
363
+ // ── Systems thinking / stochastic ────────────────────────────
364
+ {
365
+ type: "causalloop",
366
+ name: "Causal Loop Diagram",
367
+ tagline: "System-dynamics feedback map \u2014 signed causal links the engine reads to classify each loop as reinforcing (R) or balancing (B).",
368
+ useWhen: 'Use for systems thinking / system dynamics: variables connected by `+`/`\u2212` causal links, where the engine detects feedback loops and labels each R (even number of negative links) or B (odd). Header `causalloop`/`cld`; write `A -> B : +` links and optional `loop R1 "name"` annotations and `delay` marks. Distinct from `sociogram` (social ties, no polarity) and `flowchart` (process steps).',
369
+ cluster: "causality-analysis",
370
+ standard: "Sterman, Business Dynamics (2000) \xB7 Meadows, Thinking in Systems; see 41-CAUSAL-LOOP-STANDARD.md",
371
+ syntaxKey: "causalloop"
372
+ },
373
+ {
374
+ type: "markov",
375
+ name: "Markov chain",
376
+ tagline: "Discrete-time Markov chain \u2014 circles + probability arcs, with the stationary distribution and recurrent/transient/absorbing classification computed for you.",
377
+ useWhen: "Use to model a probabilistic state process (reliability/availability, queueing, regime models) where you want the long-run distribution or absorption answer, not just the picture. Header `markov`/`markovchain`; write `S1 -> S2 : 0.3` transitions (each state's out-edges sum to 1). The engine computes the stationary distribution, classifies states, and for absorbing chains the fundamental matrix. Sibling of `state` and `petri`.",
378
+ cluster: "behavior-modeling",
379
+ standard: "Norris, Markov Chains (1997) \xB7 Kemeny & Snell, Finite Markov Chains; see 42-MARKOV-CHAIN-STANDARD.md",
380
+ syntaxKey: "markov"
381
+ },
382
+ // ── Software / process engineering ───────────────────────────
383
+ {
384
+ type: "gitgraph",
385
+ name: "Git commit graph",
386
+ tagline: "Branch-and-merge commit history on per-branch swimlanes \u2014 Mermaid gitGraph compatible.",
387
+ useWhen: 'Use to visualise a git branching/merging history. Header `gitGraph`; ordered `commit`, `branch <name>`, `checkout <name>`, `merge <name>`, `cherry-pick id: "\u2026"`, with `commit id:/tag:/type: HIGHLIGHT|REVERSE`. Mermaid `gitGraph` syntax parity so LLM output is drop-in compatible. Commits sit on per-branch lanes ordered chronologically; merges join lanes.',
388
+ cluster: "software-uml",
389
+ standard: "Mermaid gitGraph syntax \xB7 git DAG model; see 43-GIT-GRAPH-STANDARD.md",
390
+ syntaxKey: "gitgraph"
391
+ },
392
+ {
393
+ type: "epc",
394
+ name: "Event-driven Process Chain (EPC)",
395
+ tagline: "ARIS business-process notation \u2014 alternating events (red hexagons) and functions (green rounded rects) joined by AND/OR/XOR connectors, with the alternation rule validated.",
396
+ useWhen: "Use for ARIS-style business process modelling (SAP / enterprise BPM). Header `epc`; declare `event`, `function`, connectors `and`/`or`/`xor`, and the control flow between them. The engine validates strict event\u2194function alternation and connector legality (an event cannot be the source of an OR/XOR split). Distinct from `bpmn` and `flowchart` \u2014 a separate published standard with stricter rules.",
397
+ cluster: "corporate-legal",
398
+ standard: "ARIS / Keller, N\xFCttgens & Scheer (1992); see 44-EPC-STANDARD.md",
399
+ syntaxKey: "epc"
400
+ },
401
+ {
402
+ type: "idef0",
403
+ name: "IDEF0 function model",
404
+ tagline: "Federal function-modelling standard \u2014 boxes are activities, arrows are positional (the ICOM rule: Input-left, Control-top, Output-right, Mechanism-bottom).",
405
+ useWhen: "Use to model what a system/process does and its inputs/controls/outputs/mechanisms \u2014 systems engineering, defence/government process docs, enterprise architecture. Header `idef0`; declare `function` boxes and ICOM arrows (`input`/`control`/`output`/`mechanism`) plus box\u2192box flows that name the target ICOM side. The engine enforces ICOM placement and assigns node numbers, in a diagonal box staircase.",
406
+ cluster: "project-management",
407
+ standard: "FIPS PUB 183 (1993) \xB7 SADT (Ross); see 45-IDEF0-STANDARD.md",
408
+ syntaxKey: "idef0"
409
+ },
410
+ {
411
+ type: "threatmodel",
412
+ name: "Threat model (DFD + STRIDE)",
413
+ tagline: "Security data-flow diagram where the engine annotates each element with its applicable STRIDE threats and flags every flow that crosses a trust boundary.",
414
+ useWhen: "Use for security threat modelling (Microsoft SDL / OWASP Threat Dragon workflow): DFD shapes (external entity, process, data store), labelled data flows, and `boundary` trust zones. Header `threatmodel`/`stride`. The engine maps each element type to its STRIDE categories (external = S,R; process = all six; store = T,I,D + conditional R for logs; flow = T,I,D) and accents flows crossing a trust boundary. Includes the DFD base notation (no separate `dfd` engine).",
415
+ cluster: "network-infrastructure",
416
+ standard: "Shostack, Threat Modeling (2014) STRIDE-per-element \xB7 Microsoft SDL \xB7 base DFD DeMarco/Yourdon; see 46-THREAT-MODEL-STRIDE-STANDARD.md + 31-DFD-STANDARD.md",
417
+ syntaxKey: "threatmodel"
418
+ },
419
+ {
420
+ type: "welding",
421
+ name: "Welding symbols",
422
+ tagline: "AWS A2.4 / ISO 2553 welding callouts \u2014 the reference-line skeleton with weld glyphs, dimensions, and supplementary symbols placed correct-by-construction.",
423
+ useWhen: 'Use to annotate a welded joint on an engineering drawing: a horizontal reference line, a leader arrow to the joint, and a weld-symbol glyph above (other side) / below (arrow side) with size, length-pitch, groove angle, root opening, contour + finish. Header `welding [standard: aws|iso-a|iso-b]`; one `joint "label" { arrow: \u2026 other: \u2026 around field tail: \u2026 }` block per joint. Full glyph catalog (fillet, all groove types, plug/slot, spot/seam, back/backing, surfacing, edge) + weld-all-around, field flag, and tail process note. Validates illegal type/side/dimension combinations.',
424
+ cluster: "electrical-industrial",
425
+ standard: "AWS A2.4:2020 \xB7 ISO 2553:2019; see 47-WELDING-SYMBOL-STANDARD.md",
426
+ syntaxKey: "welding"
343
427
  }
344
428
  ];
345
429
  var DIAGRAM_SINCE = {
@@ -390,7 +474,18 @@ var DIAGRAM_SINCE = {
390
474
  // 0.6.5
391
475
  faulttree: "0.6.5",
392
476
  // 0.6.6
393
- bowtie: "0.6.6"
477
+ bowtie: "0.6.6",
478
+ // 0.8.0 — Bucket B (event tree, FMEA, causal loop, Markov, git graph, EPC, IDEF0, threat model)
479
+ eventtree: "0.8.0",
480
+ fmea: "0.8.0",
481
+ causalloop: "0.8.0",
482
+ markov: "0.8.0",
483
+ gitgraph: "0.8.0",
484
+ epc: "0.8.0",
485
+ idef0: "0.8.0",
486
+ threatmodel: "0.8.0",
487
+ // 0.8.1 — welding symbols (AWS A2.4 / ISO 2553)
488
+ welding: "0.8.1"
394
489
  };
395
490
  function getDiagramSince(type) {
396
491
  const resolved = resolveDiagramType(type);
@@ -672,6 +767,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
672
767
  "dsl": 'breadboard\nboard: half\ntitle: "HC-SR04 distance sensor + Arduino Uno"\n\nparts\n uno: mcu uno @beside-left\n s1: sensor hcsr04 @8a\n\nwires\n s1:VCC --red-- @+t1\n s1:GND --black-- @-t1\n s1:TRIG --yellow-- @9c\n s1:ECHO --green-- @10c\n uno:5V --red-- @+t1\n uno:GND --black-- @-t1\n uno:D9 --yellow-- @9c\n uno:D10 --green-- @10c',
673
768
  "notes": "The HC-SR04 is the most common ultrasonic distance sensor in beginner Arduino kits. Four pins, four wires, no driver IC needed. The sensor module is rendered as a blue PCB tile with its four pin labels (VCC / TRIG / ECHO / GND) sitting above the breadboard rows where they plug in.\n\nThe TRIG line is a digital output from the Arduino \u2014 pulse it high for 10 \xB5s and the sensor fires an ultrasonic chirp. The ECHO line is a digital input \u2014 its high-time, in microseconds, is twice the round-trip distance divided by the speed of sound. Conventional wiring uses yellow for TRIG and green for ECHO so the two signals are visually distinguishable, though the colors carry no electrical meaning."
674
769
  },
770
+ {
771
+ "slug": "causalloop-growth-engine",
772
+ "diagram": "causalloop",
773
+ "title": "Startup growth engine (R and B loops)",
774
+ "description": "A system-dynamics model of a viral product whose growth is capped by scaling strain. The engine enumerates every feedback loop and classifies each reinforcing (R) or balancing (B) by Sterman's even/odd rule.",
775
+ "standard": "Sterman, Business Dynamics (2000)",
776
+ "tags": [
777
+ "causalloop",
778
+ "cld",
779
+ "system-dynamics",
780
+ "feedback",
781
+ "reinforcing",
782
+ "balancing"
783
+ ],
784
+ "complexity": 3,
785
+ "featured": false,
786
+ "dsl": 'causalloop "Startup growth engine"\n "Active users" -> "Word of mouth" : +\n "Word of mouth" -> "New signups" : +\n "New signups" -> "Active users" : +\n "Active users" -> "Server load" : +\n "Server load" -> "App performance" : -\n "App performance" -> "Active users" : + delay\n "Active users" -> Revenue : +\n Revenue -> "Infra investment" : +\n "Infra investment" -> "App performance" : + delay\n loop R1 "Viral flywheel"\n loop B1 "Scaling strain"',
787
+ "notes": '## What this shows\n\nThe two-sided story of every viral product. One reinforcing loop drives growth: more active users produce more word of mouth, which drives signups, which adds users \u2014 an amplifying cycle. A second, balancing loop pushes back: more users raise server load, which degrades app performance, which (after a delay) loses users. The `delay` markers put the system-dynamics hash mark on the slow legs, where the lag between cause and effect is what makes the system overshoot.\n\nThe engine finds and labels the loops for you, which a drawing tool cannot. It enumerates every elementary cycle in the signed graph with Johnson\'s algorithm, then applies Sterman\'s rule \u2014 count the negative links, even means reinforcing (R), odd means balancing (B) \u2014 and numbers them R1, B1\u2026 The growth cycle comes back R (zero negatives), the load cycle B (one negative). The named `loop` declarations attach the human phrasing \u2014 "Viral flywheel", "Scaling strain" \u2014 onto the loops the engine detects.'
788
+ },
789
+ {
790
+ "slug": "causalloop-traffic-congestion",
791
+ "diagram": "causalloop",
792
+ "title": "Urban traffic congestion (two balancing loops)",
793
+ "description": "The classic system-dynamics counterintuitive result \u2014 widening roads induces demand. The engine enumerates the feedback loops and classifies each reinforcing or balancing by link polarity.",
794
+ "standard": "Sterman, Business Dynamics (2000)",
795
+ "tags": [
796
+ "causalloop",
797
+ "cld",
798
+ "system-dynamics",
799
+ "induced-demand",
800
+ "balancing",
801
+ "transit"
802
+ ],
803
+ "complexity": 2,
804
+ "featured": false,
805
+ "dsl": 'causalloop "Urban traffic congestion"\n "Road capacity" -> "Travel speed" : +\n "Travel speed" -> "Driving attractiveness" : +\n "Driving attractiveness" -> "Number of cars" : +\n "Number of cars" -> Congestion : +\n Congestion -> "Travel speed" : -\n Congestion -> "Public transit use" : +\n "Public transit use" -> "Number of cars" : -\n loop B1 "Congestion brake"\n loop B2 "Transit substitution"',
806
+ "notes": "## What this shows\n\nThe system-dynamics model behind one of the most counterintuitive results in transport policy: widening a road does not durably cut congestion, because faster travel makes driving more attractive, which pulls more cars onto the road until it clogs again. Two balancing loops fight the build-up \u2014 congestion itself slows travel and dampens demand, and congestion pushes commuters onto public transit, which removes cars.\n\nThe engine classifies the loops by polarity rather than asking you to. It walks the signed graph, enumerates each elementary cycle, and counts the negative links: both feedback loops here carry an odd number of negative links, so both come back **balancing (B)** and are numbered B1, B2. That even/odd classification \u2014 Sterman's textbook rule \u2014 is the analysis a drawing tool skips, and it is what explains why the system settles back to congestion no matter how much capacity is added."
807
+ },
675
808
  {
676
809
  "slug": "circuit-bridge-rectifier-supply",
677
810
  "diagram": "circuit",
@@ -961,6 +1094,25 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
961
1094
  "dsl": 'entity-structure "Acme Inc. \u2014 post Series A"\nentity acme "Acme Inc." corp@DE\nentity founders "Founders (2)" individual\nentity seed "Seed Fund I" lp@DE\nentity lead "Sequoia Series A" lp@DE\nentity angels "Angel group" individual\nentity esop "Employee Option Pool" trust@DE\nfounders -> acme : 45%\nseed -> acme : 12%\nlead -> acme : 22%\nangels -> acme : 6%\nesop -> acme : 15%',
962
1095
  "notes": "## Scenario\n\nA startup attorney or CFO documents the post-Series A ownership table for a 409A valuation, board consent, or investor report. The cap table diagram makes the dilution story visual \u2014 founders can immediately see their post-money percentage, and the VC can verify their ownership stake before signing the term sheet.\n\n## Annotation key\n\n- `-> acme : 45%` \u2014 ownership arrow with percentage label; all percentages should sum to 100%\n- `individual` \u2014 natural person (founder, angel)\n- `lp` \u2014 institutional investor entity (fund/LP)\n- `trust` \u2014 the ESOP/option pool (typically a Delaware trust or reserved pool)\n- `corp@DE` \u2014 the issuer (Delaware C-corp)\n\n## How to read\n\nAcme Inc. (Delaware C-corp) sits at the bottom as the issuer. Five shareholder classes flow down with their ownership arrows: founders at 45%, the lead Series A investor at 22%, the employee option pool at 15%, the seed fund at 12%, and angels at 6%. Percentages sum to 100%, representing a clean fully-diluted cap table on the day of Series A close."
963
1096
  },
1097
+ {
1098
+ "slug": "epc-procurement",
1099
+ "diagram": "epc",
1100
+ "title": "Procurement EPC with XOR split",
1101
+ "description": "An ARIS event-driven process chain for purchase requisitions, branching on an XOR connector and re-joining on an AND. The engine validates EPC well-formedness \u2014 event/function alternation and the event-cannot-decide rule.",
1102
+ "standard": "ARIS EPC (Scheer)",
1103
+ "tags": [
1104
+ "epc",
1105
+ "aris",
1106
+ "business-process",
1107
+ "xor",
1108
+ "and",
1109
+ "control-flow"
1110
+ ],
1111
+ "complexity": 3,
1112
+ "featured": false,
1113
+ "dsl": 'epc "Purchase requisition"\n layout: tb\n event E1 "Material need identified"\n function F1 "Create requisition"\n function F2 "Determine source of supply"\n xor X1\n event E2 "Stock supplier exists"\n event E3 "New supplier needed"\n function F3 "Issue purchase order"\n function F4 "Run tender process"\n and A1\n event E4 "Order dispatched"\n event E5 "Supplier qualified"\n E1 -> F1 -> F2 -> X1\n X1 -> E2\n X1 -> E3\n E2 -> F3 -> A1\n E3 -> F4 -> A1\n A1 -> E4\n A1 -> E5',
1114
+ "notes": '## What this shows\n\nA SAP-style procurement process modelled as an ARIS event-driven process chain (EPC), the notation that strictly alternates passive **events** ("Material need identified") and active **functions** ("Create requisition"). After sourcing, an **XOR** connector splits the flow on a real decision \u2014 an existing stock supplier versus a new supplier needing a tender \u2014 and the two paths re-converge on an **AND** connector that fans back out to the dispatched order and the qualified-supplier outcome.\n\nEPC\'s value here is structural validation, not a computed number. The engine checks the well-formedness rules and flags violations rather than silently drawing a broken model: event/function alternation along every path, single-in/single-out multiplicity carried by the connectors, reachability from start to end, and the **event-cannot-decide signature rule** \u2014 a passive event may not be the source of an XOR/OR split. That\'s why the decision routes *through* function `F2` before reaching the XOR, which is the correct ARIS form.'
1115
+ },
964
1116
  {
965
1117
  "slug": "erd-billing-ledger-audit-trail",
966
1118
  "diagram": "erd",
@@ -1035,6 +1187,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1035
1187
  "dsl": 'erd\ntitle: "University Schema"\n\ntable Student {\n student_id int PK\n name varchar\n email varchar UK\n major_id int FK -> Major.major_id\n}\n\ntable Major {\n major_id int PK\n name varchar\n}\n\ntable Course {\n course_id int PK\n title varchar\n credits int\n}\n\ntable Enrollment {\n student_id int PK FK -> Student.student_id\n course_id int PK FK -> Course.course_id\n grade char\n}\n\nref Student.major_id many-mandatory -- one-mandatory Major.major_id : "majors in"\nref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id\nref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id',
1036
1188
  "notes": "The university schema is the canonical introduction to associative entities. The M:N relationship between Student and Course is resolved through Enrollment, which carries the relationship attribute `grade`. Both Student and Course join Enrollment via composite primary keys (each FK column doubles as part of the PK)."
1037
1189
  },
1190
+ {
1191
+ "slug": "eventtree-flammable-release",
1192
+ "diagram": "eventtree",
1193
+ "title": "Flammable release event tree (process QRA)",
1194
+ "description": "A loss-of-containment release branched through detection, isolation, and ignition. The engine computes each outcome frequency \u2014 safe shutdown, jet fire, dispersion, vapour-cloud explosion \u2014 and flags the dominant one.",
1195
+ "standard": "IEC 62502 / process QRA",
1196
+ "tags": [
1197
+ "eventtree",
1198
+ "eta",
1199
+ "qra",
1200
+ "loss-of-containment",
1201
+ "ignition",
1202
+ "explosion"
1203
+ ],
1204
+ "complexity": 2,
1205
+ "featured": false,
1206
+ "dsl": 'eventtree "Flammable release"\n initiating REL "Loss of containment" freq: 0.02\n function DET "Gas detection alarms" p: 0.05\n function ISO "Emergency isolation" p: 0.1\n function IGN "No ignition source" p: 0.3\n outcome s s s -> "Safe shutdown"\n outcome s s f -> "Jet fire"\n outcome s f * -> "Large dispersion"\n outcome f * * -> "Vapour cloud explosion"',
1207
+ "notes": "## What this shows\n\nThe quantitative risk assessment (QRA) event tree a process-safety study runs after a HAZOP flags a credible leak. The initiating event \u2014 a flammable release at `0.02` per year \u2014 is challenged by three safety functions in order: gas detection, emergency isolation, and the chance of no ignition source. The `IGN` function reads as a *success* when there is no ignition (`s`), so its failure leg `f` is where the fire or explosion lives.\n\nThe value is the computed end-state frequencies. The engine propagates the release frequency through each branch's success/failure probability to size each consequence \u2014 safe shutdown, jet fire, large dispersion, vapour-cloud explosion \u2014 and accents the dominant sequence in red, the same role the single point of failure plays in a fault tree. That ranked, numeric reading is what turns a forking ladder into an analysis."
1208
+ },
1209
+ {
1210
+ "slug": "eventtree-loca",
1211
+ "diagram": "eventtree",
1212
+ "title": "Large LOCA event tree (reactor PRA)",
1213
+ "description": "A large-break loss-of-coolant accident branched through four reactor safety functions. The engine computes every sequence frequency from the initiating rate and per-function failure probabilities, then highlights the dominant accident sequence.",
1214
+ "standard": "IEC 62502 / NUREG (WASH-1400)",
1215
+ "tags": [
1216
+ "eventtree",
1217
+ "eta",
1218
+ "pra",
1219
+ "nuclear",
1220
+ "sequence-frequency",
1221
+ "core-damage"
1222
+ ],
1223
+ "complexity": 3,
1224
+ "featured": false,
1225
+ "dsl": 'eventtree "Large LOCA"\n initiating LOCA "Large-break LOCA" freq: 1e-4\n function RT "Reactor trips" p: 0.001\n function ECCS "ECCS injection" p: 0.01\n function CHR "Containment heat removal" p: 0.02\n function CI "Containment integrity" p: 0.005\n outcome s s s s -> "OK"\n outcome s s s f -> "Late release"\n outcome s s f * -> "Late release"\n outcome s f * * -> "Early release"\n outcome f * * * -> "Core damage"',
1226
+ "notes": '## What this shows\n\nThe textbook nuclear probabilistic-risk-assessment (PRA) tree. One initiating event \u2014 a large pipe break draining the coolant at a frequency of `1e-4` per year \u2014 is asked, left to right, whether each safety function holds: reactor trip, emergency core cooling (ECCS), containment heat removal, then containment integrity. Each `*` prunes a path that has already terminated, so the tree stays compact instead of ballooning to a full 2\u2074 ladder.\n\nThe engine does the arithmetic an event tree exists to give. It multiplies the initiating frequency by the success leg (`1 \u2212 p`) or failure leg (`p`) at each branch to get every sequence frequency, sums the two `"Late release"` leaves into one rolled-up outcome, and paints the largest-frequency path \u2014 the dominant accident sequence \u2014 in red. That computed answer, not the forking picture, is the deliverable a drawing tool can\'t produce.'
1227
+ },
1038
1228
  {
1039
1229
  "slug": "faulttree-pump-redundancy",
1040
1230
  "diagram": "faulttree",
@@ -1260,6 +1450,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1260
1450
  "dsl": "flowchart LR\n start([New order received])\n start --> validate{Inventory available?}\n validate -->|Yes| reserve[Reserve items]\n validate -->|No| notify[Notify customer]\n notify --> done([End])\n reserve --> payment{Payment authorized?}\n payment -->|Yes| ship[Ship order]\n payment -->|No| cancel[Cancel & release]\n ship --> confirm[Send confirmation email]\n confirm --> done\n cancel --> done",
1261
1451
  "notes": '## Scenario\n\nA product ops lead circulates this flowchart during an ops-review meeting to align engineering, customer support, and fulfillment on the single source of truth for what happens when a new order comes in. It surfaces the two decision gates (inventory, payment) and the three exception paths (out-of-stock notification, payment failure with released hold, successful ship with confirmation).\n\n## Annotation key\n\n- `([\u2026])` \u2014 stadium / terminal; used for Start and End\n- `{\u2026}` \u2014 diamond; decision node\n- `[\u2026]` \u2014 rectangle; process step\n- `-->|label|` \u2014 edge with a branch label (`Yes` / `No`)\n\n## How to read\n\nStart at the top-left terminal. Inventory check gates the first branch \u2014 a "No" routes straight to the End after notification. A "Yes" reserves stock then hits the payment gate. Payment failure releases the reservation and goes to End; success ships and emails the customer. Every path terminates at the same End node, so nothing dangles.'
1262
1452
  },
1453
+ {
1454
+ "slug": "fmea-ev-battery-dfmea",
1455
+ "diagram": "fmea",
1456
+ "title": "EV battery pack DFMEA",
1457
+ "description": "A design FMEA for an electric-vehicle battery pack. The engine flattens the failure chain, computes RPN = S \xD7 O \xD7 D, and derives the AIAG-VDA Action Priority \u2014 keeping a S10 safety failure High even when its RPN looks low.",
1458
+ "standard": "AIAG-VDA FMEA Handbook (2019) / IEC 60812",
1459
+ "tags": [
1460
+ "fmea",
1461
+ "dfmea",
1462
+ "rpn",
1463
+ "action-priority",
1464
+ "severity",
1465
+ "automotive"
1466
+ ],
1467
+ "complexity": 3,
1468
+ "featured": false,
1469
+ "dsl": 'fmea "EV battery pack DFMEA"\n type: design\n rank: ap\n flag: ap >= High\n number: DFMEA-2026-014\n item "Cell module" fn "Store and deliver energy"\n mode "Thermal runaway"\n effect "Pack fire / occupant injury" sev: 10\n cause "Internal short from dendrite growth" occ: 3\n controls prevention: "Cell qualification", detection: "In-line CT scan" det: 4\n cause "Overcharge past cutoff" occ: 2\n controls prevention: "BMS voltage clamp", detection: "Redundant voltage sense" det: 3\n mode "Capacity fade"\n effect "Reduced range" sev: 6\n cause "Electrolyte depletion" occ: 5\n controls detection: "Periodic SOH estimate" det: 6\n item "Busbar joint" fn "Conduct current between modules"\n mode "High-resistance connection"\n effect "Local overheating" sev: 8\n cause "Loose torque on weld" occ: 4\n controls detection: "End-of-line resistance test" det: 4',
1470
+ "notes": "## What this shows\n\nA design FMEA (DFMEA) on the highest-stakes subsystem in an electric vehicle. The nested chain reads item \u2192 mode \u2192 effect/cause/controls: a cell module that can suffer thermal runaway or capacity fade, and a busbar joint that can go high-resistance. Each effect carries a Severity, each cause an Occurrence, each control the Detection it earns \u2014 the three AIAG-VDA 1\u201310 scales.\n\nThe engine computes the priority rather than just tabling it. It flattens to one worksheet row per (item, mode, cause), multiplies RPN = S \xD7 O \xD7 D, and \u2014 the part that matters \u2014 derives the AIAG-VDA Action Priority, which is severity-primary. The thermal-runaway row sits at S10\xB7O3\xB7D4 with an RPN of 120; a naive RPN sort would rank it below a noisier low-severity defect, but Action Priority keeps every safety failure (S = 9\u201310) at **High** regardless. The `flag: ap >= High` directive highlights exactly those rows so the safety-critical work rises to the top."
1471
+ },
1472
+ {
1473
+ "slug": "fmea-injection-molding-pfmea",
1474
+ "diagram": "fmea",
1475
+ "title": "Injection-moulding PFMEA",
1476
+ "description": "A process FMEA for an injection-moulding line, ranked the legacy way by RPN. The engine computes RPN = S \xD7 O \xD7 D for every (step, mode, cause) row and flags the ones that breach the 100 threshold.",
1477
+ "standard": "AIAG-VDA FMEA Handbook (2019) / IEC 60812",
1478
+ "tags": [
1479
+ "fmea",
1480
+ "pfmea",
1481
+ "rpn",
1482
+ "process",
1483
+ "manufacturing",
1484
+ "scrap"
1485
+ ],
1486
+ "complexity": 2,
1487
+ "featured": false,
1488
+ "dsl": 'fmea "Injection moulding PFMEA"\n type: process\n rank: rpn\n flag: rpn > 100\n item "Mould fill step" fn "Fill cavity with melt"\n mode "Short shot"\n effect "Incomplete part scrapped" sev: 7\n cause "Injection pressure too low" occ: 5\n controls prevention: "Pressure setpoint lock", detection: "Vision check" det: 4\n cause "Blocked gate" occ: 3\n controls detection: "Cycle-time monitor" det: 6\n item "Cooling step" fn "Solidify part to spec"\n mode "Warpage"\n effect "Out-of-tolerance dimension" sev: 6\n cause "Uneven cooling channel flow" occ: 6\n controls detection: "CMM sampling" det: 7',
1489
+ "notes": "## What this shows\n\nA process FMEA (PFMEA) following the steps of an injection-moulding line \u2014 the fill step and the cooling step \u2014 rather than the parts of a product. Each process step gets its failure modes (short shot, warpage), the effect each has on the part, the process causes behind it, and the in-line controls that prevent or detect it.\n\nThis worksheet is ranked the legacy way with `rank: rpn`, so the engine sorts purely on RPN = S \xD7 O \xD7 D and the `flag: rpn > 100` directive lights up every row above 100 \u2014 here the uneven-cooling warpage row at S6\xB7O6\xB7D7 (RPN 252) and the short-shot pressure row at S7\xB7O5\xB7D4 (RPN 140). Each rendered row carries `data-rpn` and `data-ap`, so even on a legacy RPN worksheet the AIAG-VDA Action Priority is still computed and inspectable underneath."
1490
+ },
1263
1491
  {
1264
1492
  "slug": "genogram-brca-cancer",
1265
1493
  "diagram": "genogram",
@@ -1349,6 +1577,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1349
1577
  "dsl": 'genogram "The Potter Family"\n fleamont [male, 1909, 1979, deceased]\n euphemia [female, 1920, 1979, deceased]\n fleamont -- euphemia\n james [male, 1960, 1981, deceased]\n mr_evans [male, 1925, deceased]\n mrs_evans [female, 1928, deceased]\n mr_evans -- mrs_evans\n lily [female, 1960, 1981, deceased]\n petunia [female, 1958]\n james -- lily "m. 1978"\n harry [male, 1980, index]\n petunia -- vernon [male, 1951]\n dudley [male, 1980]\n harry -cutoff- petunia\n harry -hostile- dudley\n harry -close- lily',
1350
1578
  "notes": '## Scenario\n\nA teaching example for social work students learning genogram notation. The Potter family is fictional but emotionally rich \u2014 death years, a marriage date, cross-family emotional relationships, and three distinct relational patterns (cutoff, hostile, close) all in one diagram.\n\n## Annotation key\n\n- `[male/female, birth_year, death_year, deceased]` \u2014 person with death marker\n- `"m. 1978"` \u2014 marriage date label on the union line\n- `index` \u2014 marks Harry as the identified patient (proband)\n- `-cutoff-` \u2014 estrangement; drawn as two parallel bars across the relationship line\n- `-hostile-` \u2014 conflict; drawn as zigzag line\n- `-close-` \u2014 enmeshment/closeness; drawn as double parallel line\n\n## How to read\n\nRead each indented block as a family unit. James and Lily (index generation) both died in 1981. Harry\'s emotional world is defined by three relational lines: cutoff from Aunt Petunia, hostility toward cousin Dudley, and closeness to his deceased mother.'
1351
1579
  },
1580
+ {
1581
+ "slug": "gitgraph-release-flow",
1582
+ "diagram": "gitgraph",
1583
+ "title": "Git Flow release history",
1584
+ "description": "A realistic Git Flow branch/merge/tag history \u2014 develop, a feature branch, a tagged release, and a hotfix merged back to both lines. Mermaid gitGraph syntax, rendered through Schematex's zero-dependency engine.",
1585
+ "standard": "Mermaid gitGraph dialect",
1586
+ "tags": [
1587
+ "gitgraph",
1588
+ "git-flow",
1589
+ "branch",
1590
+ "merge",
1591
+ "tag",
1592
+ "hotfix"
1593
+ ],
1594
+ "complexity": 3,
1595
+ "featured": false,
1596
+ "dsl": 'gitGraph\n commit id: "init"\n commit id: "scaffold"\n branch develop\n checkout develop\n commit id: "feature scaffold"\n branch feature/login\n checkout feature/login\n commit id: "login UI"\n commit id: "login API"\n checkout develop\n merge feature/login\n commit id: "polish"\n checkout main\n merge develop tag: "v1.0"\n branch hotfix\n checkout hotfix\n commit id: "patch CVE" type: HIGHLIGHT\n checkout main\n merge hotfix tag: "v1.0.1"\n checkout develop\n merge main',
1597
+ "notes": "## What this shows\n\nA full Git Flow release cycle, the branching strategy teams adopt to keep a clean trunk. Work forks off `main` into a long-lived `develop`, then into a short-lived `feature/login` branch; the feature merges back to develop, develop merges to `main` as the tagged `v1.0` release. Then a security `hotfix` branches straight off `main`, lands a HIGHLIGHT commit, ships as `v1.0.1`, and is merged back down into develop so the fix isn't lost \u2014 the discipline that keeps the two lines from diverging.\n\nThe DSL is the Mermaid `gitGraph` dialect verbatim, so existing Mermaid sources port directly. The engine replays the operation list in order to assign each commit to a lane, routes the merge connectors from each branch tip without crossings, renders the patch commit emphasised as a HIGHLIGHT, and draws the version tags as flags \u2014 all from a KB-scale, dependency-free bundle."
1598
+ },
1599
+ {
1600
+ "slug": "idef0-order-fulfilment",
1601
+ "diagram": "idef0",
1602
+ "title": "IDEF0 A0 \u2014 fulfil customer order",
1603
+ "description": "An IDEF0 A0 function decomposition with all four ICOM arrow types. Inputs, controls, mechanisms and outputs each pin to a fixed box edge, so the model is correct by construction per FIPS PUB 183.",
1604
+ "standard": "FIPS PUB 183 (IDEF0)",
1605
+ "tags": [
1606
+ "idef0",
1607
+ "icom",
1608
+ "function-model",
1609
+ "decomposition",
1610
+ "fips-183",
1611
+ "structured-analysis"
1612
+ ],
1613
+ "complexity": 3,
1614
+ "featured": false,
1615
+ "dsl": 'idef0 "Fulfil customer order"\nnode A0\nfunction A1 "Validate order"\nfunction A2 "Pick and pack"\nfunction A3 "Ship and invoice"\ninput A1 "Customer order"\ncontrol A1 "Credit policy"\nmechanism A1 "Order management system"\nA1 -> A2 "Validated order"\ninput A2 "Inventory"\ncontrol A2 "Picking rules"\nmechanism A2 "Warehouse staff"\nA2 -> A3 "Packed shipment"\ncontrol A3 "Carrier contract"\nmechanism A3 "Shipping carrier"\noutput A3 "Delivered order"\noutput A3 "Invoice"',
1616
+ "notes": '## What this shows\n\nAn IDEF0 A0 diagram \u2014 the top-level functional decomposition of an order-fulfilment system \u2014 showing all four ICOM arrow roles. Reading the box edges by convention: **I**nputs enter on the left (the customer order, inventory), **C**ontrols govern from the top (credit policy, picking rules, carrier contract), **M**echanisms supply resources from the bottom (the order system, warehouse staff, carrier), and **O**utputs leave on the right (delivered order, invoice). The box-to-box flows (`A1 -> A2 "Validated order"`) chain the three activities.\n\nThe differentiator is that the model is correct by construction, not just drawn. Because the arrow keyword *is* the box edge it attaches to, the engine enforces ICOM placement \u2014 you cannot accidentally draw a control as an input. It resolves every box reference, assigns decomposition numbers (A0 \u2192 A1..A3), codes the boundary arrows down each edge (I1/C1/O1/M1\u2026), and applies the FIPS 3-to-6-box guideline. A flow that tried to *enter* a box via its `.output` edge would be rejected, because an output leaves a box \u2014 the standard is enforced, not suggested.'
1617
+ },
1352
1618
  {
1353
1619
  "slug": "ladder-conveyor-interlock",
1354
1620
  "diagram": "ladder",
@@ -1459,6 +1725,58 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1459
1725
  "dsl": 'logic "1-bit Full Adder"\ninput A, B, Cin\noutput Sum, Cout\ns1 = XOR(A, B)\nSum = XOR(s1, Cin)\nc1 = AND(A, B)\nc2 = AND(s1, Cin)\nCout = OR(c1, c2)',
1460
1726
  "notes": "## Scenario\n\nThe 1-bit full adder is the foundational building block of every arithmetic logic unit. Digital logic students derive it in lecture; FPGA engineers instantiate it in RTL. Schematex renders it from a purely functional description \u2014 no manual gate placement, no wire routing \u2014 making it easy to embed in textbooks, datasheets, or AI-generated hardware documentation.\n\n## Annotation key\n\n- `input A, B, Cin` \u2014 declare named input ports\n- `output Sum, Cout` \u2014 declare named output ports\n- `s1 = XOR(A, B)` \u2014 intermediate signal `s1` is the XOR of inputs A and B\n- `Sum = XOR(s1, Cin)` \u2014 the sum bit is the XOR of the partial sum and carry-in\n- `c1 = AND(A, B)` \u2014 carry generated when both A and B are 1\n- `c2 = AND(s1, Cin)` \u2014 carry propagated when partial sum is 1 and Cin is 1\n- `Cout = OR(c1, c2)` \u2014 carry-out is 1 if either generate or propagate carry is active\n\n## How to read\n\nThe diagram renders two XOR gates for the sum path (A\u2295B, then \u2295Cin) and two AND gates feeding an OR for the carry-out (the standard generate/propagate structure). The layout is automatically ranked so data flows left to right, inputs on the left edge, outputs on the right. Every 4-bit or 8-bit ripple-carry adder in textbooks is just this circuit chained together."
1461
1727
  },
1728
+ {
1729
+ "slug": "markov-customer-lifecycle",
1730
+ "diagram": "markov",
1731
+ "title": "Customer lifecycle (ergodic chain, stationary \u03C0)",
1732
+ "description": "A SaaS trial \u2192 active \u2192 churned chain. The engine validates the row-stochastic matrix, confirms the chain is ergodic, and computes the long-run stationary distribution \u03C0 \u2014 the steady-state share of customers in each state.",
1733
+ "standard": "Kemeny & Snell, Finite Markov Chains",
1734
+ "tags": [
1735
+ "markov",
1736
+ "stationary",
1737
+ "ergodic",
1738
+ "lifecycle",
1739
+ "churn",
1740
+ "steady-state"
1741
+ ],
1742
+ "complexity": 2,
1743
+ "featured": false,
1744
+ "dsl": 'markov "Customer lifecycle"\n analysis: classify, stationary\n Trial -> Trial : 0.4\n Trial -> Active : 0.5\n Trial -> Churned : 0.1\n Active -> Active : 0.8\n Active -> Trial : 0.05\n Active -> Churned : 0.15\n Churned -> Churned : 0.7\n Churned -> Trial : 0.3',
1745
+ "notes": "## What this shows\n\nA SaaS customer modelled as a memoryless hop between three states \u2014 Trial, Active, Churned \u2014 each month. Every row of probabilities leaving a state sums to 1 (the row-stochastic rule), and because churned customers can re-enter via Trial (`Churned -> Trial : 0.3`), no state is a dead end: the chain is **ergodic**, every state reachable from every other.\n\nThe engine does the linear algebra. It validates the matrix, runs SCC analysis to confirm a single recurrent class with no absorbing sink, then computes the **stationary distribution \u03C0** \u2014 the long-run share of your customer base sitting in each state once the system settles, independent of where it started. That steady-state answer (and the per-state classification) is carried in `data-*`, and it is the number a churn model exists to produce, not the circles-and-arrows."
1746
+ },
1747
+ {
1748
+ "slug": "markov-gamblers-ruin",
1749
+ "diagram": "markov",
1750
+ "title": "Gambler's ruin (absorbing chain)",
1751
+ "description": "The classic absorbing Markov chain with two sinks. The engine classifies the transient and absorbing states and computes the fundamental matrix \u2014 the probability of ending broke vs rich and the expected number of steps to absorption.",
1752
+ "standard": "Kemeny & Snell, Finite Markov Chains",
1753
+ "tags": [
1754
+ "markov",
1755
+ "absorbing",
1756
+ "fundamental-matrix",
1757
+ "gamblers-ruin",
1758
+ "transient",
1759
+ "expected-steps"
1760
+ ],
1761
+ "complexity": 3,
1762
+ "featured": false,
1763
+ "dsl": `markov "Gambler's ruin"
1764
+ analysis: classify, absorbing
1765
+ state Broke "$0" absorbing
1766
+ state Rich "$4" absorbing
1767
+ state S1
1768
+ state S2
1769
+ state S3
1770
+ Broke -> Broke : 1
1771
+ Rich -> Rich : 1
1772
+ S1 -> Broke : 0.5
1773
+ S1 -> S2 : 0.5
1774
+ S2 -> S1 : 0.5
1775
+ S2 -> S3 : 0.5
1776
+ S3 -> S2 : 0.5
1777
+ S3 -> Rich : 0.5`,
1778
+ "notes": "## What this shows\n\nThe canonical absorbing Markov chain. A gambler with \\$1, \\$2, or \\$3 (states S1\u2013S3) bets on a fair coin, moving up or down \\$1 each round, and stops only at ruin (\\$0) or the target (\\$4). The two endpoints are declared `absorbing` \u2014 once entered, never left (a self-loop of probability 1) \u2014 and the engine cross-checks that assertion against the matrix.\n\nThis is where the engine's linear algebra pays off. It classifies S1\u2013S3 as **transient** and Broke/Rich as **absorbing**, then forms the fundamental matrix N = (I \u2212 Q)\u207B\xB9 to compute the two answers an absorbing chain is built for: the **absorption probabilities** (starting from each state, the chance of ending broke vs rich \u2014 for a fair game, proportional to the distance to each barrier) and the **expected number of steps to absorption**. Those computed values, carried in `data-*`, are the result no drawing tool can give."
1779
+ },
1462
1780
  {
1463
1781
  "slug": "matrix-9-box-talent",
1464
1782
  "diagram": "matrix",
@@ -1554,6 +1872,63 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1554
1872
  "dsl": 'matrix johari "Self vs. Team \u2014 Q2 Reflection"\nstyle: table\nQ2: "Strong technical instincts"\nQ2: "Direct in code review"\nQ2: "Patient with juniors"\nQ1: "Interrupts in meetings"\nQ1: "Hard to read when stressed"\nQ3: "Imposter syndrome about leadership"\nQ3: "Anxiety about cross-team politics"\nQ4: "Capacity for difficult conversations under pressure"',
1555
1873
  "notes": "## Scenario\n\nA newly-promoted engineering manager runs a Johari exercise with her team during a 1:1 retro. She populates the **Open** cell (things both she and the team see); the team adds to **Blind** (things they see that she doesn't); she fills **Hidden** privately; **Unknown** is the open hypothesis space \u2014 capabilities and limitations that haven't surfaced yet.\n\nThe table form is the canonical Johari output. Coaches print it on a single page and walk through it with the coachee \u2014 a scatter plot of dots would defeat the entire purpose.\n\n## Annotation key\n\n- `matrix johari` \u2014 preset axes (Known to Self \xD7 Known to Others) with the four window panes\n- `style: table` \u2014 flips off axes/grid, places each pane title as a cell header, lists items as bullets\n- `Q1` = Blind (top-right: not known to self, known to others)\n- `Q2` = Open / Arena (top-left: known to self + others)\n- `Q3` = Hidden / Fa\xE7ade (bottom-left: known to self, not to others)\n- `Q4` = Unknown (bottom-right: not known to either \u2014 the growth hypothesis space)\n\n## How to read\n\nThe classic Johari coaching prompt: **how do you move items from Hidden \u2192 Open** (vulnerability work) **and from Blind \u2192 Open** (feedback-acceptance work)? An overstuffed Hidden pane signals psychological-safety debt; an empty Blind pane usually means the team hasn't been asked."
1556
1874
  },
1875
+ {
1876
+ "slug": "matrix-punnett-dihybrid",
1877
+ "diagram": "matrix",
1878
+ "title": "Dihybrid cross Punnett square (9:3:3:1)",
1879
+ "description": "A two-gene Punnett square where the engine computes the gametes, the 4\xD74 offspring grid, and the canonical 9:3:3:1 phenotype ratio \u2014 Mendel's pea seed shape \xD7 colour cross \u2014 with each phenotype class auto-tinted.",
1880
+ "standard": "Mendelian genetics (Punnett square)",
1881
+ "tags": [
1882
+ "matrix",
1883
+ "punnett",
1884
+ "genetics",
1885
+ "mendel",
1886
+ "dihybrid",
1887
+ "biology"
1888
+ ],
1889
+ "complexity": 3,
1890
+ "featured": false,
1891
+ "dsl": 'matrix punnett "Seed shape & colour (RrYy \xD7 RrYy)"\ncross: RrYy x RrYy\ntrait R: "Round" / "Wrinkled"\ntrait Y: "Yellow" / "Green"',
1892
+ "notes": "## What this shows\n\nA **Punnett square** predicts the offspring of a genetic cross. You write only the two parental genotypes \u2014 the engine does the Mendelian bookkeeping: it enumerates each parent's **gametes** (one allele per gene locus), fills the grid with every gamete combination, and counts the resulting **genotype and phenotype ratios**.\n\nThis is a **dihybrid** cross (two genes at once) \u2014 Mendel's classic pea experiment crossing seed shape (`R` round dominant / `r` wrinkled) with seed colour (`Y` yellow dominant / `y` green). Two heterozygous parents each produce four gametes (RY, Ry, rY, ry), so the grid is 4\xD74 = 16 boxes. The engine computes the famous **9:3:3:1** phenotype ratio \u2014 9 round-yellow : 3 wrinkled-yellow : 3 round-green : 1 wrinkled-green \u2014 and tints each box by its phenotype class so the ratio is visible at a glance. Case sets dominance (uppercase = dominant), and the optional `trait` lines name each phenotype so the legend reads in plain English."
1893
+ },
1894
+ {
1895
+ "slug": "matrix-punnett-monohybrid",
1896
+ "diagram": "matrix",
1897
+ "title": "Monohybrid cross Punnett square (3:1)",
1898
+ "description": "The classic single-gene Punnett square \u2014 two heterozygous parents (Bb \xD7 Bb) crossed for eye colour. The engine computes the gametes, the 2\xD72 grid, and the canonical 3:1 dominant-to-recessive phenotype ratio with a 1:2:1 genotype ratio.",
1899
+ "standard": "Mendelian genetics (Punnett square)",
1900
+ "tags": [
1901
+ "matrix",
1902
+ "punnett",
1903
+ "genetics",
1904
+ "mendel",
1905
+ "monohybrid",
1906
+ "biology"
1907
+ ],
1908
+ "complexity": 1,
1909
+ "featured": false,
1910
+ "dsl": 'matrix punnett "Eye color (Bb \xD7 Bb)"\ncross: Bb x Bb\ntrait B: "Brown eyes" / "Blue eyes"',
1911
+ "notes": "## What this shows\n\nThe **monohybrid cross** is where every genetics course starts: one gene, two heterozygous parents. Here both parents are `Bb` for eye colour \u2014 `B` (brown) is dominant, `b` (blue) recessive. You write only the cross; the engine does the Mendelian bookkeeping.\n\nEach `Bb` parent makes two gametes, `B` and `b`, so the grid is 2\xD72. The engine fills it \u2014 `BB`, `Bb`, `Bb`, `bb` \u2014 and computes the two ratios every student memorises: a **3:1 phenotype ratio** (3 brown-eyed : 1 blue-eyed) and a **1:2:1 genotype ratio** (1 `BB` : 2 `Bb` : 1 `bb`). The single recessive `bb` box is tinted apart from the three dominant boxes, and the `trait` line names the phenotypes so the legend reads in plain English. Allele case sets dominance \u2014 uppercase is dominant \u2014 so the notation is exactly what a textbook uses."
1912
+ },
1913
+ {
1914
+ "slug": "matrix-punnett-testcross",
1915
+ "diagram": "matrix",
1916
+ "title": "Test cross Punnett square (1:1)",
1917
+ "description": "A genetic test cross \u2014 crossing an organism of unknown genotype against a homozygous-recessive parent (Bb \xD7 bb) to reveal whether it is heterozygous. The engine computes the 1:1 phenotype ratio that signals a heterozygous parent.",
1918
+ "standard": "Mendelian genetics (Punnett square)",
1919
+ "tags": [
1920
+ "matrix",
1921
+ "punnett",
1922
+ "genetics",
1923
+ "mendel",
1924
+ "test-cross",
1925
+ "biology"
1926
+ ],
1927
+ "complexity": 2,
1928
+ "featured": false,
1929
+ "dsl": 'matrix punnett "Test cross (Bb \xD7 bb)"\ncross: Bb x bb\ntrait B: "Brown" / "Blue"',
1930
+ "notes": "## What this shows\n\nA **test cross** answers a practical genetics question: an organism shows the dominant trait, but is it `BB` or `Bb`? Cross it against a **homozygous-recessive** partner (`bb`) and read the offspring. You write only the cross; the engine computes the outcome that gives the answer.\n\nThe recessive `bb` parent contributes only `b` gametes, so the offspring genotypes come straight from the unknown parent's gametes. Crossing `Bb \xD7 bb` yields a **1:1 phenotype ratio** \u2014 half `Bb` (dominant phenotype), half `bb` (recessive) \u2014 the tell-tale signature of a heterozygous parent. (A `BB` parent would instead give all dominant offspring.) The engine fills the 2\xD71 grid, tints the two phenotype classes apart, and reports the 1:1 ratio, so the diagnostic result is immediate."
1931
+ },
1557
1932
  {
1558
1933
  "slug": "matrix-qfd-coffee-maker",
1559
1934
  "diagram": "matrix",
@@ -2711,6 +3086,25 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
2711
3086
  "dsl": 'state "Traffic Light"\n\ninitial i\nfinal f\n\ni -> Red\nRed -> Green : timer\nGreen -> Yellow : timer\nYellow -> Red : timer\nRed -> f : power_off',
2712
3087
  "notes": "The traffic light is to state diagrams what the pump loop is to P&IDs: every textbook uses it, every engineer has drawn it, and if you understand it you understand the grammar of every more complex model.\n\n**Why a state diagram and not a flowchart.** A flowchart for a traffic light would show a loop: start \u2192 Red \u2192 (timer fires?) \u2192 Green \u2192 (timer fires?) \u2192 Yellow \u2192 back to Red. That works for describing an algorithm, but it misses the essential question: *what is the system doing right now?* A state machine makes the current state a first-class concept. The traffic light is not executing a loop \u2014 it *is* Red, or it *is* Green. Transitions are events that change what it is. This distinction matters the moment you add complexity: \"what happens if a pedestrian button is pressed while we're in Green?\" You answer that by looking at the transitions out of Green, not by tracing a flowchart path.\n\n**UML pseudo-states.** The filled black circle (`initial i`) and the bull's-eye circle (`final f`) are pseudo-states \u2014 they are not real states the system can dwell in, just notational entry and exit points. The initial pseudo-state shows where the machine starts; the arrow from `i` to Red tells you Red is the first real state. The final pseudo-state shows where the machine terminates. In the traffic light, termination is the `power_off` event from Red \u2014 the system shuts down from the Red state only (it wouldn't be safe to power off mid-Green).\n\n**Transition labels.** Each arrow is labeled with the trigger event that causes the transition. `timer` means \"the countdown for this phase has elapsed.\" In a real embedded implementation, this would be a hardware timer interrupt or a software watchdog expiry. Schematex doesn't execute the state machine \u2014 it renders the model. The labels are free text; you write exactly what your system calls the event.\n\n**Cyclic structure.** The three main states form a cycle: Red \u2192 Green \u2192 Yellow \u2192 Red. Most state diagrams describing continuous systems have cycles \u2014 the system runs until something external stops it. The `power_off` transition is the only way to reach the final state, and it is only modeled on Red because that is the safe state to stop in. If you wanted to model an emergency override (traffic officer stops the light mid-cycle), you would add `power_off` transitions from Green and Yellow too.\n\n**From model to code.** A UML state diagram maps directly to an enum + switch statement, a state table, or a state-machine framework (XState, Boost.MSM, Qt State Machine). The diagram is the specification; the implementation strategy is separate. Generating this diagram from a DSL means you can keep the spec in version control alongside the code, diff it in PRs, and regenerate it from an LLM prompt when requirements change."
2713
3088
  },
3089
+ {
3090
+ "slug": "threatmodel-ecommerce-checkout",
3091
+ "diagram": "threatmodel",
3092
+ "title": "E-commerce checkout threat model (STRIDE)",
3093
+ "description": "A security data-flow diagram for a checkout flow with three trust boundaries. The engine applies the STRIDE-per-element mapping and flags every flow that crosses a trust boundary \u2014 where spoofing and tampering concentrate.",
3094
+ "standard": "Microsoft STRIDE / Shostack (2014)",
3095
+ "tags": [
3096
+ "threatmodel",
3097
+ "stride",
3098
+ "dfd",
3099
+ "trust-boundary",
3100
+ "security",
3101
+ "data-flow"
3102
+ ],
3103
+ "complexity": 3,
3104
+ "featured": false,
3105
+ "dsl": 'threatmodel "E-commerce checkout"\nexternal: Customer\nexternal: Payment Gateway\nprocess 1.0: Web App\nprocess 2.0: Order Service\ndatastore D1: Orders DB\ndatastore D2: Audit Log\nCustomer -> 1.0 : "HTTPS Checkout"\n1.0 -> 2.0 : "Place order"\n2.0 -> D1 : "Write order"\n2.0 -> Payment Gateway : "Charge card"\n2.0 -> D2 : "Order event"\nboundary "Internet" { Customer, Payment_Gateway }\nboundary "DMZ" { 1.0 }\nboundary "Internal" { 2.0, D1, D2 }',
3106
+ "notes": "## What this shows\n\nA STRIDE threat model of an e-commerce checkout drawn as a data-flow diagram (DFD): two external entities (the customer and a third-party payment gateway), two processes (the web app in the DMZ, the order service internally), and two data stores (the orders database and an audit log). Three trust boundaries partition the system \u2014 Internet, DMZ, Internal \u2014 and the labelled flows carry the data crossing between them.\n\nThe engine does the STRIDE-per-element analysis, not just the boxes. It applies the canonical mapping \u2014 externals get Spoofing/Repudiation, processes get the full S-T-R-I-D-E, stores get Tampering/Information-disclosure/DoS, and the **audit log additionally gets Repudiation** because it matches the log/journal pattern. Most usefully, it **flags every flow that crosses a trust boundary** \u2014 the customer\u2192web-app HTTPS request (Internet\u2192DMZ), the order-service\u2192payment-gateway charge (Internal\u2192Internet) \u2014 because boundary crossings are where spoofing, tampering, and information disclosure concentrate. Each element and flow carries its applicable STRIDE categories in `data-*`."
3107
+ },
2714
3108
  {
2715
3109
  "slug": "timeline-company-milestones",
2716
3110
  "diagram": "timeline",
@@ -2992,6 +3386,25 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
2992
3386
  "featured": false,
2993
3387
  "dsl": 'venn "Programming Paradigms"\nset oop "Object-Oriented" [color: "#1E88E5"]\nset fp "Functional" [color: "#E53935"]\nset logic "Logic" [color: "#43A047"]\noop & fp : 180\noop & logic : 45\nfp & logic : 90\noop & fp & logic : 12\noop only : 620\nfp only : 340\nlogic only : 95',
2994
3388
  "notes": '## Scenario\n\nA CS instructor opens a lecture on programming paradigms with this Venn. Counts come from a language survey \u2014 how many languages offer strong OOP, FP, and logic support. The tiny triple-overlap (12) tells students that languages supporting all three well are rare; the large OOP-only region shows industry gravity; the FP \u2229 logic overlap (90) is where languages like Prolog-with-lambdas sit.\n\n## Annotation key\n\n- `set ID "Label"` \u2014 paradigm circle\n- `A & B : n` \u2014 number of languages supporting both paradigms strongly\n- `A only : n` \u2014 languages committed to a single paradigm\n\n## How to read\n\nEach circle represents a paradigm. Overlapping regions count multi-paradigm languages. The `oop & fp` intersection (180) is where modern mainstream languages \u2014 Scala, Kotlin, Swift, TypeScript \u2014 sit. The tiny triple intersection (12) is a reminder that truly multi-paradigm language design is expensive; the larger exclusive regions show how few languages commit to logic programming at all.'
3389
+ },
3390
+ {
3391
+ "slug": "welding-bracket-fillet",
3392
+ "diagram": "welding",
3393
+ "title": "Welded bracket \u2014 fillet + V-groove callouts",
3394
+ "description": "AWS A2.4 welding callouts on a fabrication drawing \u2014 an intermittent double-fillet bracket with a weld-all-around flag, and a full-penetration V-groove butt weld with backing, root opening, and the welding process in the tail.",
3395
+ "standard": "AWS A2.4 / ISO 2553 welding symbols",
3396
+ "tags": [
3397
+ "welding",
3398
+ "aws-a2.4",
3399
+ "iso-2553",
3400
+ "fillet",
3401
+ "groove",
3402
+ "fabrication"
3403
+ ],
3404
+ "complexity": 3,
3405
+ "featured": false,
3406
+ "dsl": 'welding "Bracket assembly"\njoint "gusset to column" {\n arrow: fillet size=8 len=50 pitch=150\n other: fillet size=6\n around\n field\n tail: "GMAW"\n}\njoint "splice plate (butt)" {\n arrow: vgroove angle=60 root=3 throat=12 contour=flush finish=G\n other: backing\n tail: "SMAW; E7018"\n}',
3407
+ "notes": "## What this shows\n\nA **welding symbol** is the standard way a drawing tells a fabricator how to weld a joint \u2014 codified by **AWS A2.4** (US) and **ISO 2553** (international). It is a *reference-line skeleton*: a horizontal line, a leader arrow to the joint, and a weld glyph snapped above (other side) or below (arrow side) the line, with dimensions in fixed slots.\n\nThe first joint is an **intermittent double fillet** \u2014 a `size=8` arrow-side fillet welded `50` long on a `150` pitch, a `size=6` fillet on the other side \u2014 carried **all around** the gusset (the open circle at the leader junction) as a **field weld** (the flag), with the **GMAW** process noted in the tail. The second joint is a full-penetration **V-groove butt weld**: a `60\xB0` included angle with a `3 mm` root opening and a `12 mm` effective throat, a **backing** weld on the far side, ground **flush**, welded **SMAW** with **E7018** electrode. Each callout is placed correct-by-construction \u2014 the engine owns the skeleton, you describe the weld."
2995
3408
  }
2996
3409
  ];
2997
3410
  var SYNTAX = {
@@ -3061,7 +3474,7 @@ var SYNTAX = {
3061
3474
  },
3062
3475
  "matrix": {
3063
3476
  "title": "Matrix / Quadrant diagram",
3064
- "content": '## 1. Your first matrix\n\nThe smallest useful matrix: a custom 2\xD72 with two labeled axes and three points.\n\n```\nmatrix "Feature Prioritization"\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n\n"Add search" at (0.3, 0.8)\n"Rebuild pipeline" at (0.85, 0.7)\n"Update footer" at (0.2, 0.2)\n```\n\nFour rules cover 80% of usage:\n\n1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title.\n2. Set the axes with `x-axis:` and `y-axis:` \u2014 or use a built-in template and skip this step entirely.\n3. Each point is `"Label" at (x, y)` where `x` and `y` are decimal fractions from 0.0 (low/left/bottom) to 1.0 (high/right/top).\n4. Add optional properties \u2014 `size:`, `category:`, `color:`, `shape:`, `highlight:` \u2014 after the coordinates.\n\n> Comments must start with `#` anywhere on a line (outside quoted strings).\n\n---\n\n## 2. Built-in templates\n\nA template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line.\n\n| Template | Grid | Use case |\n|---|---|---|\n| `eisenhower` | 2\xD72 | Urgency / Importance task prioritization |\n| `impact-effort` | 2\xD72 | Feature prioritization by impact vs. effort |\n| `rice` | 2\xD72 | RICE scoring \u2014 Reach \xD7 Impact vs. Effort |\n| `bcg` | 2\xD72 | Portfolio \u2014 Market Share vs. Growth rate |\n| `ansoff` | 2\xD72 | Product/market growth strategy |\n| `johari` | 2\xD72 | Self-awareness \u2014 known-to-self vs. known-to-others |\n| `9-box` | 3\xD73 | HR talent review \u2014 Performance vs. Potential |\n| `risk-matrix` | 5\xD75 | Risk assessment \u2014 Likelihood vs. Severity (heatmap) |\n\n```\nmatrix eisenhower "This Week"\n"Ship hotfix" at (0.1, 0.9) size: 5 highlight: true\n"Team 1:1s" at (0.1, 0.7) size: 3\n"Write Q3 OKRs" at (0.8, 0.85) size: 4\n"Inbox zero" at (0.1, 0.3) size: 2\n"Refactor auth" at (0.75, 0.4) size: 3\n```\n\nAxes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives.\n\n---\n\n## 3. Axes\n\nAxis lines declare the semantic poles of each dimension.\n\n```\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n```\n\nThe arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent:\n\n| Separator | Example |\n|---|---|\n| `\u2192` (Unicode) | `x-axis: Rare \u2192 Certain` |\n| `->` (ASCII) | `x-axis: Rare -> Certain` |\n| `\u2191` | `y-axis: Cheap \u2191 Expensive` |\n| `\u2190` / `<-` / `<` | Reversed axis \u2014 high label is on the left |\n\nA **reversed axis** is for conventions where the "high" value sits at the left or bottom:\n\n```\nx-axis: High Market Share \u2190 Low Market Share\n```\n\n```\nmatrix "Product Portfolio"\nx-axis: High Market Share \u2190 Low Market Share\ny-axis: Low Growth \u2192 High Growth\n\nquadrant Q1 "Question Marks"\nquadrant Q2 "Stars"\nquadrant Q3 "Cash Cows"\nquadrant Q4 "Dogs"\n\n"Analytics Suite" at (0.25, 0.35) size: 5\n"ChatBot Pro" at (0.2, 0.8) size: 4 highlight: true\n"Legacy CRM" at (0.75, 0.25) size: 6\n"Mobile App" at (0.65, 0.75) size: 3\n```\n\n---\n\n## 4. Points\n\nEach point is a bubble positioned by a normalized (x, y) coordinate pair.\n\n```\n"Label" at (x, y)\n"Label" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: "clarify spec"\n```\n\n| Property | Values | Meaning |\n|---|---|---|\n| `size:` | positive number | Bubble area weight (default: 3) |\n| `category:` | bareword | Color group; drives the legend |\n| `color:` | hex string | Override bubble color for this point |\n| `shape:` | `circle` \\| `square` \\| `triangle` \\| `diamond` | Bubble shape (default: `circle`) |\n| `highlight:` | `true` | Draws an emphasis ring around the bubble |\n| `note:` | quoted string | Tooltip annotation |\n| `label:` | quoted string | Replaces the display label (different from the ID) |\n\nCoordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge \u2014 the original value is stored for tooltip display.\n\n```\nmatrix "Risk Register"\nx-axis: Low Impact \u2192 High Impact\ny-axis: Rare \u2192 Certain\n\n"Vendor delay" at (0.45, 0.7) size: 4 category: schedule highlight: true\n"Security breach" at (0.9, 0.3) size: 5 category: security shape: diamond\n"Budget overrun" at (0.5, 0.65) size: 3 category: finance\n"Key hire falls through" at (0.6, 0.55) size: 3 category: people\n"Scope creep" at (0.4, 0.8) size: 4 category: schedule\n```\n\n---\n\n## 5. Quadrant labels\n\nLabel each quadrant with a name and an optional subtitle.\n\n```\nquadrant Q1 "Do First"\nquadrant Q2 "Schedule"\nquadrant Q3 "Delete"\nquadrant Q4 "Delegate"\n\n# With an optional subtitle:\nquadrant Q1 "Do First" description: "High urgency, high importance"\n```\n\nQuadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional \u2014 `quadrant 1 "Label"` is equally valid.\n\n---\n\n## 6. Heatmap mode\n\nHeatmap mode fills N\xD7M cells with color intensity instead of plotting bubble positions.\n\n```\nmatrix heatmap 4x3 "Skill Matrix"\nrows: [Strategy, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (0,1) value: 7\ncell (1,2) label: "Top 10%"\n```\n\n- `matrix heatmap COLxROW` \u2014 header sets the grid dimensions.\n- `rows:` and `cols:` \u2014 comma-separated or bracket-list of axis labels.\n- `cell (col, row)` \u2014 zero-indexed, column first, row second (row 0 = bottom).\n- `level:` \u2014 `strong` (3), `medium` (2), or `weak` (1) \u2014 shorthand for heat intensity.\n- `value:` \u2014 explicit numeric value (overrides `level:`).\n- `label:` \u2014 quoted text placed inside the cell.\n\n```\nmatrix heatmap 4x4 "Competency Heat Map"\nrows: [Leadership, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior, Staff]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (3,0) level: strong\ncell (0,1) level: medium\ncell (1,1) level: medium\ncell (2,1) level: strong\ncell (3,1) level: strong\ncell (0,2) level: weak\ncell (1,2) level: medium\ncell (2,2) level: medium\ncell (3,2) level: strong\ncell (0,3) level: weak\ncell (1,3) level: weak\ncell (2,3) level: medium\ncell (3,3) level: strong\n```\n\n---\n\n## 7. Correlation mode\n\nCorrelation mode renders an N\xD7M dot matrix where intensity represents the relationship strength between row and column variables.\n\n```\nmatrix correlation 4x4 "Product Metrics"\nrows: [DAU, Retention, Revenue, NPS]\ncols: [DAU, Retention, Revenue, NPS]\n\ncell (0,0) value: 1\ncell (1,0) value: 0.82\ncell (2,0) value: 0.54\ncell (3,0) value: 0.71\n```\n\nThe same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode.\n\n---\n\n## 8. SIPOC mode\n\nA **SIPOC** is the one-page scoping table that opens the *Define* phase of a Six Sigma DMAIC project. It names, in five fixed columns left to right, everyone and everything the process touches: **S**uppliers \xB7 **I**nputs \xB7 **P**rocess \xB7 **O**utputs \xB7 **C**ustomers. Before a team measures or improves anything, SIPOC pins down the boundary \u2014 "where does this process start, where does it end, and who hands work in and out of it."\n\n```\nmatrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"\n```\n\n- Start with `matrix sipoc`, optionally followed by a quoted title.\n- Each of the five columns is its own directive: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`.\n- After the colon, list the entries as **comma-separated quoted strings**. A column may have any number of entries; the rows simply stack top-down inside that column.\n- The `process:` column is the high-level step sequence (typically 4\u20137 steps) \u2014 keep it to the major stages, not a detailed flowchart.\n\nThe five columns always render in the canonical S-I-P-O-C order regardless of the order you declare them, so the diagram reads correctly even if an LLM emits the blocks out of sequence.\n\n```\nmatrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"\n```\n\n---\n\n## 9. QFD mode (House of Quality)\n\n**Quality Function Deployment (QFD)** \u2014 the *House of Quality*, introduced by Yoji Akao \u2014 translates what customers want into the engineering characteristics that deliver it. Rows are the **WHATs** (customer requirements, each with an importance weight); columns are the **HOWs** (the measurable engineering characteristics the team controls). The body of the grid records how strongly each HOW serves each WHAT.\n\nThe differentiator: the engine **computes** the bottom row for you. Each HOW\'s *technical importance* is the sum down its column of `weight \xD7 relationship strength` \u2014 a ranked answer to "which engineering characteristic moves the most customer value, and is therefore worth the most effort." And the roof of the house \u2014 a half-matrix of diamond cells above the columns \u2014 records whether two HOWs help or fight each other.\n\n```\nmatrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +\n```\n\n### WHATs and HOWs\n\n| Directive | Form | Meaning |\n|---|---|---|\n| `what:` | `what: "Label" weight: N` | A customer requirement (one row). `weight:` is its importance, conventionally 1\u20135. Declaration order is the row order, indexed from 0. |\n| `how:` | `how: "Label" dir: up\\|down` | An engineering characteristic (one column). Declaration order is the column order, indexed from 0. `dir:` is the optimization target \u2014 `up` = more is better, `down` = less is better. |\n\n### Relationship cells\n\n`rel (i, j): strength` records how strongly column-`j` HOW serves row-`i` WHAT. The index is **(row, column)**, both zero-based.\n\n| Strength | Meaning |\n|---|---|\n| `9` | Strong relationship |\n| `3` | Medium relationship |\n| `1` | Weak relationship |\n| *(omitted)* | No relationship \u2014 leave the cell out |\n\nThis 9 / 3 / 1 scale is the QFD convention: it is deliberately non-linear so that one strong link outweighs several weak ones when the importance row is summed.\n\n### Computed technical-importance row\n\nThe engine sums each column to produce the technical-importance row at the foot of the house:\n\n```\nimportance(j) = \u03A3 over rows i ( weight(i) \xD7 strength(i, j) )\n```\n\nFor the coffee-maker example above the row computes to **45 / 39 / 51** \u2014 Insulation (51) is the highest-leverage characteristic, Heater watts (39) the lowest. This ranking is the deliverable: it tells the team where to spend engineering effort.\n\nAdd `normalize: true` (its own line, anywhere in the block) to show each column as a **percentage of the total** instead of a raw sum \u2014 for this example, **33% / 29% / 38%**. Percentages make the relative priorities easier to read across very different weight scales.\n\n### The roof \u2014 HOW \xD7 HOW correlations\n\nThe **roof** is the triangular half-matrix sitting above the columns. `roof (i, j): glyph` records whether HOW `i` and HOW `j` reinforce or conflict with each other \u2014 the synergies and trade-offs a team must reconcile.\n\n| Glyph | Correlation |\n|---|---|\n| `++` | Strong positive \u2014 improving one strongly helps the other |\n| `+` | Positive |\n| `-` | Negative |\n| `--` | Strong negative \u2014 improving one hurts the other (a trade-off) |\n| *(omitted)* | No correlation \u2014 leave the cell out |\n\nEach roof entry renders as a diamond cell in the standard QFD pitched-roof grid. In the example, `roof (0,1): --` flags that pushing Fan RPM down while pushing Heater watts up is a trade-off, and `roof (1,2): +` flags that Heater watts and Insulation reinforce each other.\n\n```\nmatrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +\n```\n\n---\n\n## 10. Config options\n\nA `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header.\n\n```\nconfig:\n quadrantBg: true\n gridLines: true\n axisArrows: true\n bubbleScale: area\n legendPosition: bottom-right\n```\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `quadrantBg` | `true` \\| `false` | `true` | Colored quadrant background fills |\n| `gridLines` | `true` \\| `false` | `true` | Grid lines overlay |\n| `axisArrows` | `true` \\| `false` | `true` | Arrows at axis ends |\n| `bubbleScale` | `area` \\| `radius` | `area` | Whether `size:` scales bubble area or radius |\n| `quadrantAnnotations` | `true` \\| `false` | `true` | Show quadrant label text in corners |\n| `legendPosition` | `bottom-right` \\| `right` \\| `bottom-center` \\| `none` | `bottom-right` | Category legend placement |\n| `labelCollision` | `auto` \\| `offset-only` \\| `leader-only` \\| `off` | `auto` | Overlap avoidance strategy for point labels |\n| `offChartPolicy` | `clamp-badge` \\| `drop` | `clamp-badge` | What to do with points outside [0,1] |\n\nTwo shorthand directives also work at the top level (not inside the `config:` block):\n\n```\naxis: off # off | on | auto \u2014 show or hide the axis lines\nmargins: true # true | false \u2014 show Score + Rank margins (correlation mode)\n```\n\n---\n\n## 11. Labels & comments\n\n- **Title:** `matrix "My Title"` or `title: My Title` as a standalone line.\n- **Point label:** the quoted string before `at (\u2026)`.\n- **Axis labels:** `x-axis:` and `y-axis:` directives.\n- **Quadrant labels:** `quadrant Q1 "Name"` directive.\n- **Comments:** `#` anywhere on a line, outside quoted strings.\n\n```\nmatrix "Prioritization"\n# This is a comment\nx-axis: Low Cost \u2192 High Cost # inline comment after a directive\n"Fix bug" at (0.1, 0.9) size: 3 # comment after a point\n```\n\n---\n\n## 12. Table mode (`style: table`)\n\nThe default matrix rendering is a **scatter / bubble chart** \u2014 points float at (x, y) coordinates. For frameworks where the output is a list of items grouped by quadrant (Eisenhower, Johari, Impact-Effort, 9-box), use `style: table` to switch to a **text-in-cell layout** instead.\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n`style: table` applies these changes automatically:\n\n| Effect | Detail |\n|---|---|\n| Axes and arrows hidden | No axis lines, labels, or arrowheads |\n| Grid lines hidden | Only the outer border and cell dividers remain |\n| Quadrant titles move inside cells | Each title becomes a cell header instead of a corner overlay |\n| Items stack as a bullet list | Multiple entries for the same quadrant stack top-down |\n\n### `Q1` \u2026 `Q4` shorthand (2\xD72 only)\n\nFor 2\xD72 templates, use `Qn: "item"` instead of the longer `cell (col, row) label: "item"` form. Mapping:\n\n| Shorthand | Cell | Eisenhower | Johari |\n|---|---|---|---|\n| `Q1:` | top-right | Schedule | Blind |\n| `Q2:` | top-left | Do First | Open / Arena |\n| `Q3:` | bottom-left | Delete | Hidden / Fa\xE7ade |\n| `Q4:` | bottom-right | Delegate | Unknown |\n\nRepeat a shorthand key to add multiple items to the same cell:\n\n```\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\n```\n\nFor 3\xD73 grids (9-box), use `cell (col, row) label: "\u2026"` directly \u2014 the `Q` shorthand is 2\xD72 only.\n\n### When to use table vs scatter\n\n| Use `style: table` for | Use scatter (default) for |\n|---|---|\n| Eisenhower with task lists | Eisenhower with `size:` effort weights |\n| Johari window coaching | Impact-Effort with bubble = revenue |\n| Backlog grouping (no numeric third dimension) | RICE / BCG portfolio (third dimension IS the bubble size) |\n| 9-box talent review | Risk heatmap (5\xD75 with numeric severity) |\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n---\n\n## 13. Reserved words & escaping\n\n**Reserved at line start:** `matrix` (header), `x-axis:`, `y-axis:`, `quadrant`, `config:`, `title:`, `rows:`, `cols:`, `grid:`, `axis:`, `margins:`, `cell`. In **SIPOC** mode: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`. In **QFD** mode: `what:`, `how:`, `rel`, `roof`, `normalize:`.\n\n**Point lines must start with a quote character** (`"` or `\'`). A line that does not start with a quote is not treated as a point.\n\n**Strings with spaces** in axis labels do not need quoting \u2014 the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes.\n\n---\n\n## 14. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `"Fix bug" at (1, 2)` | Point parsed; x=1 clamped, y=1 clamped; off-chart badge shown | Keep coordinates in [0.0, 1.0] or accept the clamp-badge |\n| `quadrant 1 "Quick Wins"` (no Q prefix) | Accepted \u2014 `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work |\n| `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` |\n| `x-axis: "Low" \u2192 "High"` (quoted labels) | Arrow not found inside quotes \u2014 treated as plain text | Remove quotes: `x-axis: Low \u2192 High` |\n| `matrix heatmap` without dimensions | Defaults to 2\xD72; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` |\n| `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive \u2014 accepted | Both `strong` and `Strong` work |\n| `shape: oval` | Unknown shape value \u2014 silently ignored | Use `circle`, `square`, `triangle`, or `diamond` |\n| `"Fix bug" at (0.1, 0.9)` on an Eisenhower with a task list | Valid scatter point \u2014 but you probably wanted a list in a cell | Add `style: table` and use `Q2: "Fix bug"` instead |\n| `Q1: "item"` on a 3\xD73 template | `Q` shorthand is parsed as a point line \u2014 silently dropped | Use `cell (col, row) label: "item"` for 3\xD73 grids |\n\n---\n\n## 15. Grammar (EBNF)\n\n```text\ndocument = header directive*\n\nheader = "matrix" ( template-name | mode-header | title )? NEWLINE\ntemplate-name = "eisenhower"|"impact-effort"|"rice"|"bcg"|"ansoff"|"johari"|"9-box"|"risk-matrix"\nmode-header = ( "heatmap" | "correlation" ) ( number "x" number )? title?\n | ( "sipoc" | "qfd" ) title?\ntitle = quoted-string | bare-text\n\ndirective = x-axis | y-axis | quadrant-dir | config-block\n | point | cell | q-short | rows-dir | cols-dir | grid-dir\n | style-dir | title-dir | axis-dir | margins-dir\n | sipoc-col | qfd-what | qfd-how | qfd-rel | qfd-roof | normalize-dir\n | comment | blank\n\n# SIPOC mode\nsipoc-col = ( "suppliers:" | "inputs:" | "process:" | "outputs:" | "customers:" )\n WS quoted-string ( "," quoted-string )* NEWLINE\n\n# QFD / House of Quality mode\nqfd-what = "what:" WS quoted-string WS "weight:" number NEWLINE\nqfd-how = "how:" WS quoted-string ( WS "dir:" ( "up" | "down" ) )? NEWLINE\nqfd-rel = "rel" WS "(" number "," number ")" ":" WS ( "9" | "3" | "1" ) NEWLINE # (row, col)\nqfd-roof = "roof" WS "(" number "," number ")" ":" WS ( "++" | "+" | "-" | "--" ) NEWLINE # (how, how)\nnormalize-dir = "normalize:" WS "true" NEWLINE\n\nx-axis = "x-axis:" WS axis-spec NEWLINE\ny-axis = "y-axis:" WS axis-spec NEWLINE\naxis-spec = text arrow text | text # plain text \u2192 high label only\narrow = "\u2192" | "->" | "\u2191" | "\u2190" | "<-" | "<" | "\u2193"\n\nquadrant-dir = "quadrant" WS "Q"? digit WS quoted-string ( WS "description:" quoted-string )? NEWLINE\n\nconfig-block = "config:" NEWLINE ( INDENT key ":" WS value NEWLINE )*\n\npoint = quoted-string WS "at" WS "(" number "," number ")" ( WS point-prop )* NEWLINE\npoint-prop = "size:" number\n | "category:" bareword\n | "color:" hex-color\n | "shape:" ( "circle"|"square"|"triangle"|"diamond" )\n | "highlight:" "true"\n | "note:" quoted-string\n | "label:" quoted-string\n\ncell = "cell" WS "(" digit "," digit ")" ( WS cell-prop )* NEWLINE\ncell-prop = "value:" number\n | "label:" quoted-string\n | "level:" ( "strong" | "medium" | "weak" )\n\nstyle-dir = "style:" WS "table" NEWLINE\nq-short = "Q" ( "1" | "2" | "3" | "4" ) ":" WS quoted-string NEWLINE # 2\xD72 only\n\nrows-dir = "rows:" WS label-list NEWLINE\ncols-dir = "cols:" WS label-list NEWLINE\ngrid-dir = "grid:" WS number "x" number NEWLINE\naxis-dir = "axis:" WS ( "off" | "on" | "auto" ) NEWLINE\nmargins-dir = "margins:" WS ( "true" | "false" | "on" | "1" ) NEWLINE\n\nlabel-list = "[" text ("," text)* "]" | text ("," text)*\nquoted-string = \'"\' any-char-but-quote* \'"\' | "\'" any-char-but-quote* "\'"\ncomment = "#" any NEWLINE\n```\n\nAuthoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
3477
+ "content": '## 1. Your first matrix\n\nThe smallest useful matrix: a custom 2\xD72 with two labeled axes and three points.\n\n```\nmatrix "Feature Prioritization"\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n\n"Add search" at (0.3, 0.8)\n"Rebuild pipeline" at (0.85, 0.7)\n"Update footer" at (0.2, 0.2)\n```\n\nFour rules cover 80% of usage:\n\n1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title.\n2. Set the axes with `x-axis:` and `y-axis:` \u2014 or use a built-in template and skip this step entirely.\n3. Each point is `"Label" at (x, y)` where `x` and `y` are decimal fractions from 0.0 (low/left/bottom) to 1.0 (high/right/top).\n4. Add optional properties \u2014 `size:`, `category:`, `color:`, `shape:`, `highlight:` \u2014 after the coordinates.\n\n> Comments must start with `#` anywhere on a line (outside quoted strings).\n\n---\n\n## 2. Built-in templates\n\nA template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line.\n\n| Template | Grid | Use case |\n|---|---|---|\n| `eisenhower` | 2\xD72 | Urgency / Importance task prioritization |\n| `impact-effort` | 2\xD72 | Feature prioritization by impact vs. effort |\n| `rice` | 2\xD72 | RICE scoring \u2014 Reach \xD7 Impact vs. Effort |\n| `bcg` | 2\xD72 | Portfolio \u2014 Market Share vs. Growth rate |\n| `ansoff` | 2\xD72 | Product/market growth strategy |\n| `johari` | 2\xD72 | Self-awareness \u2014 known-to-self vs. known-to-others |\n| `9-box` | 3\xD73 | HR talent review \u2014 Performance vs. Potential |\n| `risk-matrix` | 5\xD75 | Risk assessment \u2014 Likelihood vs. Severity (heatmap) |\n\n```\nmatrix eisenhower "This Week"\n"Ship hotfix" at (0.1, 0.9) size: 5 highlight: true\n"Team 1:1s" at (0.1, 0.7) size: 3\n"Write Q3 OKRs" at (0.8, 0.85) size: 4\n"Inbox zero" at (0.1, 0.3) size: 2\n"Refactor auth" at (0.75, 0.4) size: 3\n```\n\nAxes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives.\n\n---\n\n## 3. Axes\n\nAxis lines declare the semantic poles of each dimension.\n\n```\nx-axis: Low Effort \u2192 High Effort\ny-axis: Low Value \u2192 High Value\n```\n\nThe arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent:\n\n| Separator | Example |\n|---|---|\n| `\u2192` (Unicode) | `x-axis: Rare \u2192 Certain` |\n| `->` (ASCII) | `x-axis: Rare -> Certain` |\n| `\u2191` | `y-axis: Cheap \u2191 Expensive` |\n| `\u2190` / `<-` / `<` | Reversed axis \u2014 high label is on the left |\n\nA **reversed axis** is for conventions where the "high" value sits at the left or bottom:\n\n```\nx-axis: High Market Share \u2190 Low Market Share\n```\n\n```\nmatrix "Product Portfolio"\nx-axis: High Market Share \u2190 Low Market Share\ny-axis: Low Growth \u2192 High Growth\n\nquadrant Q1 "Question Marks"\nquadrant Q2 "Stars"\nquadrant Q3 "Cash Cows"\nquadrant Q4 "Dogs"\n\n"Analytics Suite" at (0.25, 0.35) size: 5\n"ChatBot Pro" at (0.2, 0.8) size: 4 highlight: true\n"Legacy CRM" at (0.75, 0.25) size: 6\n"Mobile App" at (0.65, 0.75) size: 3\n```\n\n---\n\n## 4. Points\n\nEach point is a bubble positioned by a normalized (x, y) coordinate pair.\n\n```\n"Label" at (x, y)\n"Label" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: "clarify spec"\n```\n\n| Property | Values | Meaning |\n|---|---|---|\n| `size:` | positive number | Bubble area weight (default: 3) |\n| `category:` | bareword | Color group; drives the legend |\n| `color:` | hex string | Override bubble color for this point |\n| `shape:` | `circle` \\| `square` \\| `triangle` \\| `diamond` | Bubble shape (default: `circle`) |\n| `highlight:` | `true` | Draws an emphasis ring around the bubble |\n| `note:` | quoted string | Tooltip annotation |\n| `label:` | quoted string | Replaces the display label (different from the ID) |\n\nCoordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge \u2014 the original value is stored for tooltip display.\n\n```\nmatrix "Risk Register"\nx-axis: Low Impact \u2192 High Impact\ny-axis: Rare \u2192 Certain\n\n"Vendor delay" at (0.45, 0.7) size: 4 category: schedule highlight: true\n"Security breach" at (0.9, 0.3) size: 5 category: security shape: diamond\n"Budget overrun" at (0.5, 0.65) size: 3 category: finance\n"Key hire falls through" at (0.6, 0.55) size: 3 category: people\n"Scope creep" at (0.4, 0.8) size: 4 category: schedule\n```\n\n---\n\n## 5. Quadrant labels\n\nLabel each quadrant with a name and an optional subtitle.\n\n```\nquadrant Q1 "Do First"\nquadrant Q2 "Schedule"\nquadrant Q3 "Delete"\nquadrant Q4 "Delegate"\n\n# With an optional subtitle:\nquadrant Q1 "Do First" description: "High urgency, high importance"\n```\n\nQuadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional \u2014 `quadrant 1 "Label"` is equally valid.\n\n---\n\n## 6. Heatmap mode\n\nHeatmap mode fills N\xD7M cells with color intensity instead of plotting bubble positions.\n\n```\nmatrix heatmap 4x3 "Skill Matrix"\nrows: [Strategy, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (0,1) value: 7\ncell (1,2) label: "Top 10%"\n```\n\n- `matrix heatmap COLxROW` \u2014 header sets the grid dimensions.\n- `rows:` and `cols:` \u2014 comma-separated or bracket-list of axis labels.\n- `cell (col, row)` \u2014 zero-indexed, column first, row second (row 0 = bottom).\n- `level:` \u2014 `strong` (3), `medium` (2), or `weak` (1) \u2014 shorthand for heat intensity.\n- `value:` \u2014 explicit numeric value (overrides `level:`).\n- `label:` \u2014 quoted text placed inside the cell.\n\n```\nmatrix heatmap 4x4 "Competency Heat Map"\nrows: [Leadership, Execution, Communication, Technical]\ncols: [Junior, Mid, Senior, Staff]\n\ncell (0,0) level: weak\ncell (1,0) level: medium\ncell (2,0) level: strong\ncell (3,0) level: strong\ncell (0,1) level: medium\ncell (1,1) level: medium\ncell (2,1) level: strong\ncell (3,1) level: strong\ncell (0,2) level: weak\ncell (1,2) level: medium\ncell (2,2) level: medium\ncell (3,2) level: strong\ncell (0,3) level: weak\ncell (1,3) level: weak\ncell (2,3) level: medium\ncell (3,3) level: strong\n```\n\n---\n\n## 7. Correlation mode\n\nCorrelation mode renders an N\xD7M dot matrix where intensity represents the relationship strength between row and column variables.\n\n```\nmatrix correlation 4x4 "Product Metrics"\nrows: [DAU, Retention, Revenue, NPS]\ncols: [DAU, Retention, Revenue, NPS]\n\ncell (0,0) value: 1\ncell (1,0) value: 0.82\ncell (2,0) value: 0.54\ncell (3,0) value: 0.71\n```\n\nThe same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode.\n\n---\n\n## 8. SIPOC mode\n\nA **SIPOC** is the one-page scoping table that opens the *Define* phase of a Six Sigma DMAIC project. It names, in five fixed columns left to right, everyone and everything the process touches: **S**uppliers \xB7 **I**nputs \xB7 **P**rocess \xB7 **O**utputs \xB7 **C**ustomers. Before a team measures or improves anything, SIPOC pins down the boundary \u2014 "where does this process start, where does it end, and who hands work in and out of it."\n\n```\nmatrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"\n```\n\n- Start with `matrix sipoc`, optionally followed by a quoted title.\n- Each of the five columns is its own directive: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`.\n- After the colon, list the entries as **comma-separated quoted strings**. A column may have any number of entries; the rows simply stack top-down inside that column.\n- The `process:` column is the high-level step sequence (typically 4\u20137 steps) \u2014 keep it to the major stages, not a detailed flowchart.\n\nThe five columns always render in the canonical S-I-P-O-C order regardless of the order you declare them, so the diagram reads correctly even if an LLM emits the blocks out of sequence.\n\n```\nmatrix sipoc "Order fulfilment"\nsuppliers: "Vendor", "Warehouse"\ninputs: "PO", "Stock levels"\nprocess: "Receive order", "Pick", "Pack", "Ship"\noutputs: "Shipped package", "Invoice"\ncustomers: "End customer", "Finance"\n```\n\n---\n\n## 9. QFD mode (House of Quality)\n\n**Quality Function Deployment (QFD)** \u2014 the *House of Quality*, introduced by Yoji Akao \u2014 translates what customers want into the engineering characteristics that deliver it. Rows are the **WHATs** (customer requirements, each with an importance weight); columns are the **HOWs** (the measurable engineering characteristics the team controls). The body of the grid records how strongly each HOW serves each WHAT.\n\nThe differentiator: the engine **computes** the bottom row for you. Each HOW\'s *technical importance* is the sum down its column of `weight \xD7 relationship strength` \u2014 a ranked answer to "which engineering characteristic moves the most customer value, and is therefore worth the most effort." And the roof of the house \u2014 a half-matrix of diamond cells above the columns \u2014 records whether two HOWs help or fight each other.\n\n```\nmatrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +\n```\n\n### WHATs and HOWs\n\n| Directive | Form | Meaning |\n|---|---|---|\n| `what:` | `what: "Label" weight: N` | A customer requirement (one row). `weight:` is its importance, conventionally 1\u20135. Declaration order is the row order, indexed from 0. |\n| `how:` | `how: "Label" dir: up\\|down` | An engineering characteristic (one column). Declaration order is the column order, indexed from 0. `dir:` is the optimization target \u2014 `up` = more is better, `down` = less is better. |\n\n### Relationship cells\n\n`rel (i, j): strength` records how strongly column-`j` HOW serves row-`i` WHAT. The index is **(row, column)**, both zero-based.\n\n| Strength | Meaning |\n|---|---|\n| `9` | Strong relationship |\n| `3` | Medium relationship |\n| `1` | Weak relationship |\n| *(omitted)* | No relationship \u2014 leave the cell out |\n\nThis 9 / 3 / 1 scale is the QFD convention: it is deliberately non-linear so that one strong link outweighs several weak ones when the importance row is summed.\n\n### Computed technical-importance row\n\nThe engine sums each column to produce the technical-importance row at the foot of the house:\n\n```\nimportance(j) = \u03A3 over rows i ( weight(i) \xD7 strength(i, j) )\n```\n\nFor the coffee-maker example above the row computes to **45 / 39 / 51** \u2014 Insulation (51) is the highest-leverage characteristic, Heater watts (39) the lowest. This ranking is the deliverable: it tells the team where to spend engineering effort.\n\nAdd `normalize: true` (its own line, anywhere in the block) to show each column as a **percentage of the total** instead of a raw sum \u2014 for this example, **33% / 29% / 38%**. Percentages make the relative priorities easier to read across very different weight scales.\n\n### The roof \u2014 HOW \xD7 HOW correlations\n\nThe **roof** is the triangular half-matrix sitting above the columns. `roof (i, j): glyph` records whether HOW `i` and HOW `j` reinforce or conflict with each other \u2014 the synergies and trade-offs a team must reconcile.\n\n| Glyph | Correlation |\n|---|---|\n| `++` | Strong positive \u2014 improving one strongly helps the other |\n| `+` | Positive |\n| `-` | Negative |\n| `--` | Strong negative \u2014 improving one hurts the other (a trade-off) |\n| *(omitted)* | No correlation \u2014 leave the cell out |\n\nEach roof entry renders as a diamond cell in the standard QFD pitched-roof grid. In the example, `roof (0,1): --` flags that pushing Fan RPM down while pushing Heater watts up is a trade-off, and `roof (1,2): +` flags that Heater watts and Insulation reinforce each other.\n\n```\nmatrix qfd "Coffee maker"\nwhat: "Quiet operation" weight: 5\nwhat: "Brews fast" weight: 3\nwhat: "Energy efficient" weight: 4\nhow: "Fan RPM" dir: down\nhow: "Heater watts" dir: up\nhow: "Insulation" dir: up\nrel (0,0): 9\nrel (0,2): 3\nrel (1,1): 9\nrel (2,1): 3\nrel (2,2): 9\nroof (0,1): --\nroof (1,2): +\n```\n\n---\n\n## 10. Punnett mode (Mendelian genetics)\n\nA **Punnett square** predicts the offspring of a genetic cross. You write only the two parental genotypes; the engine does the Mendelian bookkeeping \u2014 it enumerates each parent\'s **gametes** (one allele per gene locus), fills the grid with every gamete combination, and **computes the genotype and phenotype ratios**. The user never fills the grid.\n\n```\nmatrix punnett "Eye color (Bb \xD7 Bb)"\ncross: Bb x Bb\ntrait B: "Brown eyes" / "Blue eyes"\n```\n\n### The cross\n\n| Directive | Form | Meaning |\n|---|---|---|\n| `cross:` | `cross: Bb x Bb` | The two parental genotypes, separated by `x`, `\xD7`, or `*`. `parents:` is an accepted alias. |\n| `trait:` | `trait B: "Dominant" / "Recessive"` | *(optional)* Names the two phenotypes for gene `B`, so the legend reads in plain English instead of `B_` / `bb`. |\n\n**Allele case sets dominance** \u2014 this is the standard genetics convention. An uppercase letter is the **dominant** allele, the matching lowercase letter is **recessive**. A genotype groups alleles by letter: `RrYy` is two loci, `R/r` (round/wrinkled) and `Y/y` (yellow/green). One gene is a **monohybrid** cross (2\xD72 grid), two genes a **dihybrid** (4\xD74), three a trihybrid (8\xD78).\n\n### Computed ratios (the differentiator)\n\nThe engine derives, from the genotypes alone:\n\n- the **gametes** of each parent \u2014 the column and row headers \u2014 by taking one allele per locus (so a heterozygote `Bb` yields `B` and `b`);\n- the **offspring grid** \u2014 every gamete pairing, written dominant-allele-first (`Bb`, never `bB`);\n- the **phenotype ratio** \u2014 boxes grouped by which phenotype they express, reduced to lowest terms. A monohybrid `Bb \xD7 Bb` gives the classic **3:1**; a dihybrid `RrYy \xD7 RrYy` gives the famous **9:3:3:1**;\n- the **genotype ratio** \u2014 e.g. `1:2:1` (1 BB, 2 Bb, 1 bb) for the monohybrid.\n\nEach box is tinted by its phenotype class, and the footer lists the phenotype ratio with a legend plus the genotype ratio.\n\n```\nmatrix punnett "Seed shape & colour (RrYy \xD7 RrYy)"\ncross: RrYy x RrYy\ntrait R: "Round" / "Wrinkled"\ntrait Y: "Yellow" / "Green"\n```\n\n---\n\n## 11. Config options\n\nA `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header.\n\n```\nconfig:\n quadrantBg: true\n gridLines: true\n axisArrows: true\n bubbleScale: area\n legendPosition: bottom-right\n```\n\n| Key | Values | Default | Effect |\n|---|---|---|---|\n| `quadrantBg` | `true` \\| `false` | `true` | Colored quadrant background fills |\n| `gridLines` | `true` \\| `false` | `true` | Grid lines overlay |\n| `axisArrows` | `true` \\| `false` | `true` | Arrows at axis ends |\n| `bubbleScale` | `area` \\| `radius` | `area` | Whether `size:` scales bubble area or radius |\n| `quadrantAnnotations` | `true` \\| `false` | `true` | Show quadrant label text in corners |\n| `legendPosition` | `bottom-right` \\| `right` \\| `bottom-center` \\| `none` | `bottom-right` | Category legend placement |\n| `labelCollision` | `auto` \\| `offset-only` \\| `leader-only` \\| `off` | `auto` | Overlap avoidance strategy for point labels |\n| `offChartPolicy` | `clamp-badge` \\| `drop` | `clamp-badge` | What to do with points outside [0,1] |\n\nTwo shorthand directives also work at the top level (not inside the `config:` block):\n\n```\naxis: off # off | on | auto \u2014 show or hide the axis lines\nmargins: true # true | false \u2014 show Score + Rank margins (correlation mode)\n```\n\n---\n\n## 12. Labels & comments\n\n- **Title:** `matrix "My Title"` or `title: My Title` as a standalone line.\n- **Point label:** the quoted string before `at (\u2026)`.\n- **Axis labels:** `x-axis:` and `y-axis:` directives.\n- **Quadrant labels:** `quadrant Q1 "Name"` directive.\n- **Comments:** `#` anywhere on a line, outside quoted strings.\n\n```\nmatrix "Prioritization"\n# This is a comment\nx-axis: Low Cost \u2192 High Cost # inline comment after a directive\n"Fix bug" at (0.1, 0.9) size: 3 # comment after a point\n```\n\n---\n\n## 13. Table mode (`style: table`)\n\nThe default matrix rendering is a **scatter / bubble chart** \u2014 points float at (x, y) coordinates. For frameworks where the output is a list of items grouped by quadrant (Eisenhower, Johari, Impact-Effort, 9-box), use `style: table` to switch to a **text-in-cell layout** instead.\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n`style: table` applies these changes automatically:\n\n| Effect | Detail |\n|---|---|\n| Axes and arrows hidden | No axis lines, labels, or arrowheads |\n| Grid lines hidden | Only the outer border and cell dividers remain |\n| Quadrant titles move inside cells | Each title becomes a cell header instead of a corner overlay |\n| Items stack as a bullet list | Multiple entries for the same quadrant stack top-down |\n\n### `Q1` \u2026 `Q4` shorthand (2\xD72 only)\n\nFor 2\xD72 templates, use `Qn: "item"` instead of the longer `cell (col, row) label: "item"` form. Mapping:\n\n| Shorthand | Cell | Eisenhower | Johari |\n|---|---|---|---|\n| `Q1:` | top-right | Schedule | Blind |\n| `Q2:` | top-left | Do First | Open / Arena |\n| `Q3:` | bottom-left | Delete | Hidden / Fa\xE7ade |\n| `Q4:` | bottom-right | Delegate | Unknown |\n\nRepeat a shorthand key to add multiple items to the same cell:\n\n```\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\n```\n\nFor 3\xD73 grids (9-box), use `cell (col, row) label: "\u2026"` directly \u2014 the `Q` shorthand is 2\xD72 only.\n\n### When to use table vs scatter\n\n| Use `style: table` for | Use scatter (default) for |\n|---|---|\n| Eisenhower with task lists | Eisenhower with `size:` effort weights |\n| Johari window coaching | Impact-Effort with bubble = revenue |\n| Backlog grouping (no numeric third dimension) | RICE / BCG portfolio (third dimension IS the bubble size) |\n| 9-box talent review | Risk heatmap (5\xD75 with numeric severity) |\n\n```\nmatrix eisenhower "This Week"\nstyle: table\nQ2: "Ship hotfix"\nQ2: "Customer demo prep"\nQ1: "Write Q3 OKRs"\nQ1: "Refactor auth layer"\nQ4: "LinkedIn updates"\nQ3: "Reorganize Slack channels"\n```\n\n---\n\n## 14. Reserved words & escaping\n\n**Reserved at line start:** `matrix` (header), `x-axis:`, `y-axis:`, `quadrant`, `config:`, `title:`, `rows:`, `cols:`, `grid:`, `axis:`, `margins:`, `cell`. In **SIPOC** mode: `suppliers:`, `inputs:`, `process:`, `outputs:`, `customers:`. In **QFD** mode: `what:`, `how:`, `rel`, `roof`, `normalize:`.\n\n**Point lines must start with a quote character** (`"` or `\'`). A line that does not start with a quote is not treated as a point.\n\n**Strings with spaces** in axis labels do not need quoting \u2014 the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes.\n\n---\n\n## 15. Common mistakes\n\n| You wrote | Parser says | Fix |\n|---|---|---|\n| `"Fix bug" at (1, 2)` | Point parsed; x=1 clamped, y=1 clamped; off-chart badge shown | Keep coordinates in [0.0, 1.0] or accept the clamp-badge |\n| `quadrant 1 "Quick Wins"` (no Q prefix) | Accepted \u2014 `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work |\n| `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` |\n| `x-axis: "Low" \u2192 "High"` (quoted labels) | Arrow not found inside quotes \u2014 treated as plain text | Remove quotes: `x-axis: Low \u2192 High` |\n| `matrix heatmap` without dimensions | Defaults to 2\xD72; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` |\n| `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive \u2014 accepted | Both `strong` and `Strong` work |\n| `shape: oval` | Unknown shape value \u2014 silently ignored | Use `circle`, `square`, `triangle`, or `diamond` |\n| `"Fix bug" at (0.1, 0.9)` on an Eisenhower with a task list | Valid scatter point \u2014 but you probably wanted a list in a cell | Add `style: table` and use `Q2: "Fix bug"` instead |\n| `Q1: "item"` on a 3\xD73 template | `Q` shorthand is parsed as a point line \u2014 silently dropped | Use `cell (col, row) label: "item"` for 3\xD73 grids |\n\n---\n\n## 16. Grammar (EBNF)\n\n```text\ndocument = header directive*\n\nheader = "matrix" ( template-name | mode-header | title )? NEWLINE\ntemplate-name = "eisenhower"|"impact-effort"|"rice"|"bcg"|"ansoff"|"johari"|"9-box"|"risk-matrix"\nmode-header = ( "heatmap" | "correlation" ) ( number "x" number )? title?\n | ( "sipoc" | "qfd" | "punnett" ) title?\ntitle = quoted-string | bare-text\n\ndirective = x-axis | y-axis | quadrant-dir | config-block\n | point | cell | q-short | rows-dir | cols-dir | grid-dir\n | style-dir | title-dir | axis-dir | margins-dir\n | sipoc-col | qfd-what | qfd-how | qfd-rel | qfd-roof | normalize-dir\n | punnett-cross | punnett-trait\n | comment | blank\n\n# SIPOC mode\nsipoc-col = ( "suppliers:" | "inputs:" | "process:" | "outputs:" | "customers:" )\n WS quoted-string ( "," quoted-string )* NEWLINE\n\n# QFD / House of Quality mode\nqfd-what = "what:" WS quoted-string WS "weight:" number NEWLINE\nqfd-how = "how:" WS quoted-string ( WS "dir:" ( "up" | "down" ) )? NEWLINE\nqfd-rel = "rel" WS "(" number "," number ")" ":" WS ( "9" | "3" | "1" ) NEWLINE # (row, col)\nqfd-roof = "roof" WS "(" number "," number ")" ":" WS ( "++" | "+" | "-" | "--" ) NEWLINE # (how, how)\nnormalize-dir = "normalize:" WS "true" NEWLINE\n\n# Punnett (Mendelian genetics) mode\npunnett-cross = ( "cross:" | "parents:" ) WS genotype WS ( "x" | "\xD7" | "*" ) WS genotype NEWLINE\npunnett-trait = "trait" WS letter ":" WS quoted-string WS "/" WS quoted-string NEWLINE # dominant / recessive\ngenotype = ( letter letter )+ # allele pairs grouped by letter; case = dominance, e.g. "RrYy"\n\nx-axis = "x-axis:" WS axis-spec NEWLINE\ny-axis = "y-axis:" WS axis-spec NEWLINE\naxis-spec = text arrow text | text # plain text \u2192 high label only\narrow = "\u2192" | "->" | "\u2191" | "\u2190" | "<-" | "<" | "\u2193"\n\nquadrant-dir = "quadrant" WS "Q"? digit WS quoted-string ( WS "description:" quoted-string )? NEWLINE\n\nconfig-block = "config:" NEWLINE ( INDENT key ":" WS value NEWLINE )*\n\npoint = quoted-string WS "at" WS "(" number "," number ")" ( WS point-prop )* NEWLINE\npoint-prop = "size:" number\n | "category:" bareword\n | "color:" hex-color\n | "shape:" ( "circle"|"square"|"triangle"|"diamond" )\n | "highlight:" "true"\n | "note:" quoted-string\n | "label:" quoted-string\n\ncell = "cell" WS "(" digit "," digit ")" ( WS cell-prop )* NEWLINE\ncell-prop = "value:" number\n | "label:" quoted-string\n | "level:" ( "strong" | "medium" | "weak" )\n\nstyle-dir = "style:" WS "table" NEWLINE\nq-short = "Q" ( "1" | "2" | "3" | "4" ) ":" WS quoted-string NEWLINE # 2\xD72 only\n\nrows-dir = "rows:" WS label-list NEWLINE\ncols-dir = "cols:" WS label-list NEWLINE\ngrid-dir = "grid:" WS number "x" number NEWLINE\naxis-dir = "axis:" WS ( "off" | "on" | "auto" ) NEWLINE\nmargins-dir = "margins:" WS ( "true" | "false" | "on" | "1" ) NEWLINE\n\nlabel-list = "[" text ("," text)* "]" | text ("," text)*\nquoted-string = \'"\' any-char-but-quote* \'"\' | "\'" any-char-but-quote* "\'"\ncomment = "#" any NEWLINE\n```\n\nAuthoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins \u2014 please open an issue.\n\n---'
3065
3478
  },
3066
3479
  "orgchart": {
3067
3480
  "title": "Org chart",
@@ -3138,6 +3551,42 @@ var SYNTAX = {
3138
3551
  "bowtie": {
3139
3552
  "title": "Bowtie Risk Diagram",
3140
3553
  "content": '## 1. Your first diagram\n\nEvery document starts with the `bowtie` keyword, an optional title, then the hazard, the top event, and the two wings:\n\n```\nbowtie\ntopevent "Loss of containment"\nthreat "Corrosion"\n prevent "Inspection programme"\nconsequence "Release to atmosphere"\n mitigate "Gas detection + ESD"\n```\n\n`topevent` declares the single knot at the centre (mandatory, exactly one). Each `threat` starts a left-wing line; each `consequence` ends a right-wing line. A barrier under a `threat` is **preventative** (`prevent`); under a `consequence` it is **mitigative** (`mitigate`). The diagram is laid out symmetrically about the knot \u2014 threats fan in from the left, consequences fan out to the right.\n\nThe DSL is **indentation-structured** and mirrors the CCPS 7-step build methodology: identify the hazard \u2192 the top event \u2192 the threats \u2192 the consequences \u2192 the preventative barriers \u2192 the mitigative barriers \u2192 the escalation factors.\n\nHeader directives (any order):\n\n- `layout: symmetric | compact` \u2014 band model (default `symmetric`).\n- `legend: on | off | bottom | top` \u2014 the auto-derived colour legend (default on).\n\n---\n\n## 2. Hazard and top event\n\n```\nhazard "Working at height"\ntopevent "Person falls from height"\n```\n\n- **`hazard`** \u2014 the operation or material with the potential to cause harm: the *context* the bowtie is about (e.g. "Working at height", "Hydrocarbon under pressure"). Optional; renders as a header box above the knot with a tie-line down to it. At most one.\n- **`topevent`** \u2014 the moment control of the hazard is **lost** (e.g. "Loss of containment", "Person falls from height"). The knot of the bowtie, drawn as a green circle. **Mandatory, exactly one.**\n\nA hazard is a *thing/activity*, not a failure; the top event is the precise moment of *loss of control* \u2014 not a cause, and not yet a consequence.\n\n---\n\n## 3. Threats and consequences\n\n```\nthreat "Guardrail removed for access"\n prevent "Permit-to-work system"\n\nconsequence "Fatality"\n mitigate "Fall-arrest harness + lanyard"\n```\n\n- A **threat** is a credible cause that, *on its own*, could trigger the top event. Each threat is the start of one left-wing line, drawn as an orange box on the left edge.\n- A **consequence** is a credible outcome *of the top event* (not of the threat), drawn as a red box on the right edge.\n\nThreats and consequences may be declared in any interleaved order \u2014 the parser groups all left-wing blocks and all right-wing blocks regardless of sequence. A bowtie needs **at least one of each**: a one-wing diagram is a fault tree (see `faulttree`) or an event tree, not a bowtie.\n\n---\n\n## 4. Barriers (the controls in between)\n\n```\nthreat "Guardrail removed for access"\n prevent "Permit-to-work system"\n prevent "Temporary edge protection"\n prevent "Spotter / banksman"\n```\n\nA **barrier** is a control that interrupts the threat \u2192 top-event path (preventative) or reduces the consequence after the top event (mitigative). Each is a grey box *on the line*. Chains are free length (1..n) \u2014 the wing simply extends.\n\n**Barrier order is declaration order**: the first declared is the **outermost** (closest to the threat/consequence, the first line of defence); the last declared is the **innermost** (closest to the knot). This matches the left-to-right reading of a real bowtie. Each barrier carries `data-order` (0 = outermost) and `data-side` (`prevent` / `mitigate`) for downstream interactivity.\n\nWhen chains differ in length, barriers are **centre-anchored**: the innermost barriers align in a neat column near the knot, and the threat/consequence boxes are ragged by chain depth \u2014 reading as defence-in-depth.\n\n---\n\n## 5. Escalation factors\n\n```\nthreat "Corrosion"\n prevent "UT thickness inspection"\n escalation "Inspection interval too long"\n barrier "Risk-based inspection scheme"\n```\n\nAn **escalation factor** (or degradation factor) is a condition that *degrades a specific barrier\'s effectiveness* \u2014 e.g. "Edge protection not inspected", "Operator fatigue". It attaches to **one** barrier (not to the line) and drops vertically below it as an amber box, joined by a muted "degrades" connector.\n\nAn **escalation-factor barrier** is a control placed on the escalation factor itself \u2014 it protects the barrier from being degraded (e.g. a pre-use inspection regime). It nests one level deeper, under the escalation factor, and renders as a grey box below it.\n\nIndentation binds the nesting: `prevent`/`mitigate` at 2 spaces, `escalation` at 4, `barrier` at 6.\n\n---\n\n## 6. Correct by construction (the barrier rule set)\n\nBefore it draws a single shape, the engine validates the structural half of the CCPS/EI barrier rule set and **refuses to render** on failure \u2014 exactly as `prisma` refuses missing counts:\n\n- **Exactly one top event** \u2014 zero or several is an error.\n- **Every threat has \u2265 1 preventative barrier** \u2014 a bare threat is a Swiss-cheese cartoon, not a bowtie.\n- **Every consequence has \u2265 1 mitigative barrier** \u2014 the mirror rule.\n- **Every escalation factor is attached to a barrier** \u2014 it cannot float on a line or on the top event.\n- **At least one threat and one consequence** \u2014 a one-wing diagram is an FTA or an ETA.\n\nMessages name the offending element and the rule in plain English, e.g. *"Threat \'Corrosion\' has no preventative barrier \u2014 every threat must reach the top event through at least one barrier (CCPS/EI barrier rule). Add a `prevent` line under it."* This is what separates a real bowtie from a doodle. The engine does **not** judge whether a barrier is truly *effective* or *independent* \u2014 that is the analyst\'s qualitative judgement.\n\n---\n\n## 7. Bowtie vs fault tree\n\nA fully-developed bowtie *is* a fault tree glued to an event tree at the top event: the left wing read backwards is the fault tree whose top event is the bowtie\'s knot, and the right wing is the event tree that propagates it into consequences. Schematex keeps them as two engines because their use differs:\n\n- **`bowtie`** is qualitative and symmetric \u2014 the barrier inventory and the at-a-glance defence-in-depth story; no probability arithmetic.\n- **`faulttree`** is quantitative and Boolean \u2014 AND/OR gates, basic-event probabilities, minimal cut sets, a probability rollup.\n\nWhere you want the gate-level detail behind a single threat, draw a separate `faulttree`.\n\n---\n\n## 8. Theming\n\n`default` uses the recognised BowTieXP / bowtiemaster palette \u2014 **orange threats** (left), **grey barriers** on the line, a **green top-event disc** (centre knot), **red consequences** (right), **amber escalation factors** dropping below \u2014 mapped onto Schematex\'s semantic slots so it stays coherent with `prisma` / `pert` / `petri`. `monochrome` reproduces the regulator-print black-and-white look, where element distinction rides on shape/border + position (escalation factors get a dashed border, the knot a doubled ring) rather than colour. `dark` follows Catppuccin Mocha. All strokes/fills come from `BowtieTokens`; every element carries `data-*` (`data-role`, `data-side`, `data-line`, `data-order`, `data-barrier`) so the structure is inspectable downstream.'
3554
+ },
3555
+ "eventtree": {
3556
+ "title": "Event Tree Analysis",
3557
+ "content": '## 1. Your first event tree\n\nEvery document starts with the `eventtree` keyword (alias `eta`), an optional title, then a flat list of declarations:\n\n```\neventtree "Smoke detector demand"\n initiating FIRE "Fire starts" freq: 0.01\n function D "Detector actuates" p: 0.02\n function S "Suppression works" p: 0.05\n outcome s s -> "Controlled"\n outcome s f -> "Damage, contained"\n outcome f * -> "Uncontrolled fire"\n```\n\n- **`initiating ID "label" freq: N`** \u2014 exactly one. The challenge frequency, accepting decimals or scientific notation (`freq: 0.01` or `freq: 1e-4`).\n- **`function ID "label" p: N`** \u2014 one per branch column, **declared left\u2192right in query order**. `p:` is the **failure** probability; the engine derives the success leg as its complement `1 \u2212 p` (you never state both).\n- **`outcome <pattern> -> "end state"`** \u2014 one realised leaf each.\n\n---\n\n## 2. The s / f / * outcome pattern\n\nEach `outcome` row reads left\u2192right over the function columns:\n\n```\noutcome s s s -> "OK" # every function succeeds\noutcome s s f -> "Late release" # C fails on the last query\noutcome s f * -> "Early release"# B fails; C is never queried (pruned)\noutcome f * * -> "Core damage" # A fails; path terminates immediately\n```\n\n- **`s`** \u2014 success leg (upper branch).\n- **`f`** \u2014 failure leg (lower branch).\n- **`*`** \u2014 pruned: the path is not queried here, it runs flat to its leaf.\n\nThis is how an event tree avoids being a full balanced 2\u207F tree: once a function failure makes later questions moot, you write `*` and the sequence terminates early. Two hard rules: a pattern may not be **longer** than the column count, and **once a column is pruned (`*`) every later column must also be `*`** \u2014 a path that has terminated cannot resume querying.\n\n---\n\n## 3. Computed path frequencies & outcomes\n\nThis is the differentiator. With the failure probabilities and `freq`, the engine computes:\n\n- **Each path frequency** = `f\u2080 \xB7 \u220F branch-probabilities` along its `s`/`f` legs (success legs contribute `1 \u2212 p`, failure legs `p`).\n- **Outcome roll-up**: outcomes with the same end-state label are summed across every path that reaches them (every `"Core damage"` leaf adds up).\n- **The dominant sequence** \u2014 the largest-frequency path \u2014 gets the reserved-red accent, the ETA analogue of the fault tree\'s single point of failure.\n\nEvery leaf carries `data-*` (`data-freq`, `data-outcome`) so the computed numbers are inspectable downstream.\n\n---\n\n## 4. Common mistakes\n\n```\n# WRONG \u2014 function with no failure probability\nfunction A "ECCS"\n\n# WRONG \u2014 querying after a pruned column (path already terminated)\noutcome * s -> "bad"\n\n# WRONG \u2014 more tokens than declared columns\nfunction A p: 0.1\noutcome s s -> "ok"\n\n# WRONG \u2014 initiating event with no frequency\ninitiating LOCA "Large LOCA"\n```\n\nEach is rejected with a plain-English message naming the line. State `p:` as a **failure** probability (small), give the initiating event a `freq:`, keep prunes trailing, and you are correct by construction.\n\n---'
3558
+ },
3559
+ "fmea": {
3560
+ "title": "FMEA Worksheet",
3561
+ "content": '## 1. Your first worksheet\n\nEvery document starts with `fmea`, an optional title, optional directives, then a **nested** failure chain. Structure comes from the keyword, not the indent depth:\n\n```\nfmea "Pump DFMEA"\n item "Impeller" fn "Move fluid"\n mode "No flow"\n effect "Process stops" sev: 8\n cause "Impeller wear" occ: 4\n controls detection: "Flow sensor" det: 5\n```\n\n- **`item "name" fn "function"`** \u2014 a part and the function it delivers. `fn` is optional.\n- **`mode "failure mode"`** \u2014 how that function fails. An item may hold several.\n- **`effect "consequence" sev: 1..10`** \u2014 what the failure causes; carries its own **Severity**. A mode may have several (the engine uses the *worst* for every row of that mode).\n- **`cause "root cause" occ: 1..10`** \u2014 why it happens; carries **Occurrence**.\n- **`controls prevention: "\u2026", detection: "\u2026" det: 1..10`** \u2014 current controls and the **Detection** rating they earn. With no control, Detection defaults to **10** (undetectable).\n\nAll three ratings are integers 1\u201310; anything outside that range is rejected.\n\n---\n\n## 2. Header directives\n\n```\ntype: design # design (dfmea) | process (pfmea) | msr\nrank: ap # ap (default) | rpn \u2014 the sort/priority key\nflag: ap >= High # or `rpn > 100` \u2014 highlight rows over a threshold\nnumber: FMEA-2026-014 # free metadata: number/team/author/date/revision/dept/process/product\n```\n\n`rank` chooses how rows are prioritised; `flag` highlights the rows that breach a threshold (`ap >= High` / `ap == High`, or `rpn > 100` / `rpn >= 120`).\n\n---\n\n## 3. Computed RPN & Action Priority\n\nThis is the differentiator. From the nested AST the engine:\n\n1. **Flattens** to one row per (item, mode, cause); the mode\'s worst effect severity governs every row of that mode.\n2. **RPN = S \xD7 O \xD7 D** (1\u20131000).\n3. **Action Priority** by the AIAG-VDA band structure \u2014 **Severity is the primary axis**, Occurrence second, Detection third. S = 9\u201310 (safety/regulatory) is High for *every* O and D; mid-severity degrades High\u2192Medium\u2192Low as Occurrence falls.\n4. **Sorts** by the chosen key. For `ap`: High > Medium > Low, tie-broken by Severity then RPN \u2014 *never* RPN alone, which would re-introduce the bug AP was designed to fix.\n5. **Flags** rows over the threshold.\n\nEach row carries `data-rpn` and `data-ap` so the computed priority is inspectable.\n\n---\n\n## 4. After-action follow-up\n\nRecord corrective actions and the revised ratings to show the before/after delta:\n\n```\nfmea "Brake DFMEA"\n item "MC"\n mode "Seal leak"\n effect "Loss of braking" sev: 9\n cause "Degradation" occ: 3 det: 4\n action "Seal leak" / "Degradation"\n do: "Upgrade seal to EPDM" owner: "J. Lee" target: 2026-Q3\n revised sev: 9 occ: 1 det: 4\n```\n\n`action "Mode" / "Cause"` targets a chain by its quoted mode and (optional) cause; `do:` is the recommendation; `revised sev/occ/det` recomputes the after-action RPN and AP so the engine reports the risk reduction.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 rating out of the 1..10 band\neffect "Leak" sev: 11\n\n# WRONG \u2014 mode before any item\nmode "orphan mode"\n\n# WRONG \u2014 effect before any mode\nitem "x"\n effect "e" sev: 3\n```\n\nRatings must be integers 1\u201310; the failure chain must nest `item \u2192 mode \u2192 effect/cause`; out-of-order keywords are rejected by name. Detection silently defaults to 10 if no control rates it \u2014 state `det:` (on the cause or its `controls`) when you have a detection control.\n\n---'
3562
+ },
3563
+ "causalloop": {
3564
+ "title": "Causal Loop Diagram",
3565
+ "content": '## 1. Your first causal loop\n\nStart with the `causalloop` keyword (alias `cld`), an optional title, then **signed links**. Variables are auto-created from the links \u2014 you rarely declare them:\n\n```\ncausalloop "Adoption model"\n"Adoption rate" -> Adopters : +\nAdopters -> "Adoption rate" : +\nloop R1 "Word of mouth"\n```\n\nA link is `SOURCE -> TARGET : POLARITY`. Multi-word variable names are quoted (`"Adoption rate"`); single words need no quotes (`Adopters`). At least one link is required.\n\n---\n\n## 2. Link polarity\n\nPolarity is the sign of the causal influence and is **mandatory** on every link:\n\n```\nA -> B : + # same direction (more A \u2192 more B)\nB -> C : - # opposite direction (more B \u2192 less C)\nA -> B : s # alias for + (same)\nB -> C : o # alias for \u2212 (opposite)\nC -> D : same # alias for +\nD -> E : opposite # alias for \u2212\nA -> B + # the colon is optional\n```\n\n`+` / `s` / `same` mean *same direction*; `\u2212` / `o` / `opposite` mean *opposite*. A link with no polarity is rejected.\n\n---\n\n## 3. Delays and explicit variables\n\n```\n"Training quality" -> "Salesperson skills" : + delay # marked delay (\u2225 hash on the arrow)\nA -> B : + ~delay # the ~delay form also works\nvar "Adoption rate" # pin a variable so it isn\'t auto-created\nloop R1 "Word of mouth" # name/annotate a loop\n```\n\n- **`delay`** / **`~delay`** marks a link as delayed (the system-dynamics hash mark).\n- **`var "name"`** declares a variable explicitly (fixes its label; not auto-created).\n- **`loop ID "phrase"`** attaches a human-readable name to a loop the engine detects.\n\n---\n\n## 4. Computed feedback loops\n\nThis is the differentiator. The engine:\n\n1. Builds the **signed directed graph** (nodes = variables, edges = signed links).\n2. Enumerates **every elementary feedback loop** (simple directed cycle) with Johnson\'s algorithm \u2014 deterministically, in declaration order.\n3. **Classifies** each loop by counting negative links:\n - **even** count (including 0) \u2192 **R** (reinforcing); product of signs = +1\n - **odd** count \u2192 **B** (balancing); product of signs = \u22121\n\nThis is exactly Sterman\'s even/odd rule. Loops are numbered in detection order by kind (R1, B1, R2\u2026) and drawn with their `R`/`B` rotation glyph at the loop centre. Each loop carries `data-loop` and `data-kind`.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 link with no polarity\nA -> B\n\n# WRONG \u2014 a diagram with no links at all\ncld\n```\n\nEvery link needs a polarity (`: +` or `: -`); a CLD with no links is rejected. Remember polarity is about *direction of change*, not desirability \u2014 a link from "deaths" to "population" is still `-` (more deaths \u2192 less population) even though deaths are bad.\n\n---'
3566
+ },
3567
+ "markov": {
3568
+ "title": "Markov Chain",
3569
+ "content": '## 1. Your first chain\n\nStart with the `markov` keyword (alias `markovchain`), an optional title, then **probability-weighted transitions**. States are auto-created from the first arc that mentions them:\n\n```\nmarkov "Weather"\n Sunny -> Sunny : 0.9\n Sunny -> Rainy : 0.1\n Rainy -> Sunny : 0.5\n Rainy -> Rainy : 0.5\n```\n\nA transition is `FROM -> TO : PROBABILITY`. A self-loop (`Sunny -> Sunny`) is the probability of staying put. Every probability must be in `[0, 1]`, and **the probabilities leaving each state must sum to 1**.\n\n---\n\n## 2. Declaring states\n\nYou can declare states explicitly to fix label, order, or mark them absorbing:\n\n```\nmarkov "Gambler\'s ruin"\n state Broke "$0" absorbing\n state Rich "$4" absorbing\n state S1\n state S2\n Broke -> Broke : 1\n Rich -> Rich : 1\n S1 -> Broke : 0.5\n S1 -> S2 : 0.5\n S2 -> S1 : 0.5\n S2 -> Rich : 0.5\n```\n\n`state ID "label" absorbing` \u2014 the label is optional; `absorbing` asserts the state is a sink (a self-loop of probability 1). The engine cross-checks the assertion against the matrix.\n\n---\n\n## 3. Directives\n\n```\nlayout: layered # layout mode\nnormalize: true # scale each row to sum to 1 instead of hard-erroring\nanalysis: classify, absorbing # what to compute: classify | absorbing | stationary\n```\n\n- **`normalize: true`** rescales each row to sum to 1 (handy for unnormalised weights like `A -> B : 1`, `A -> C : 1`). Without it, a row that does not sum to 1 is a hard error.\n- **`analysis:`** selects which computations to run and surface.\n\n---\n\n## 4. The computed answer\n\nThis is the differentiator. Three hand-written, dependency-free linear-algebra passes:\n\n1. **Matrix assembly + validation** \u2014 build P and enforce the row-sum policy (hard error, or rescale under `normalize`).\n2. **State classification** \u2014 Tarjan SCC \u2192 communicating classes; a class is **recurrent** iff closed, **transient** otherwise; a closed singleton with a probability-1 self-loop is **absorbing**.\n3. **The quantitative answer**:\n - **Stationary distribution** \u03C0 (\u03C0P = \u03C0, \u03A3\u03C0 = 1) by power iteration with an exact Gaussian-elimination fallback for periodic chains.\n - For **absorbing chains**: the fundamental matrix N = (I\u2212Q)\u207B\xB9, absorption probabilities B = N\xB7R, and expected steps to absorption t = N\xB71.\n\nTransient states, recurrent classes, and absorbing states are rendered distinctly; computed values are carried in `data-*`.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 probability outside [0,1]\nA -> B : 1.5\n\n# WRONG \u2014 transition with no probability\nA -> B\n\n# WRONG \u2014 rows that don\'t sum to 1 (without `normalize: true`)\nA -> B : 0.3\nA -> C : 0.3\n```\n\nEvery transition needs a probability in `[0, 1]`; each state\'s outgoing probabilities must sum to 1, or you must set `normalize: true`. An empty chain (no transitions) is rejected.\n\n---'
3570
+ },
3571
+ "gitgraph": {
3572
+ "title": "Git Graph",
3573
+ "content": '## 1. Your first git graph\n\nStart with the `gitGraph` keyword (case-insensitive; a trailing colon is allowed), then one operation per line. The first commit lands on the main branch:\n\n```\ngitGraph\n commit\n branch develop\n checkout develop\n commit id: "feature work"\n checkout main\n merge develop tag: "v1.0"\n```\n\nEvery operation runs against the **currently checked-out branch**. `commit` appends to it; `branch` creates a new line from the current commit; `checkout` (alias `switch`) moves the cursor; `merge` brings another branch into the current one.\n\n---\n\n## 2. Operations\n\n```\ncommit # plain commit on the current branch\ncommit id: "init" # give the commit an explicit id\ncommit tag: "v0.1" # attach a release tag\ncommit type: HIGHLIGHT # NORMAL (default) | HIGHLIGHT | REVERSE\nbranch develop # fork a branch from the current commit\nbranch hotfix order: 3 # pin its lane order\ncheckout develop # switch the cursor (alias: switch)\nmerge develop tag: "v1.0" # merge a branch into the current one\ncherry-pick id: "abc" # copy a commit onto the current branch\n```\n\n- **`commit`** takes optional `id:`, `tag:`, and `type:` (`NORMAL` / `HIGHLIGHT` / `REVERSE`).\n- **`branch NAME`** takes an optional `order:` to control lane placement.\n- **`checkout` / `switch`** are interchangeable.\n- **`cherry-pick id:`** copies a commit; an optional `parent:` disambiguates a merge commit.\n\n---\n\n## 3. Orientation\n\nThe graph defaults to left-to-right. Set the direction inline on the header:\n\n```\ngitGraph TB:\n commit\n branch feature\n checkout feature\n commit\n checkout main\n merge feature\n```\n\n`LR` (default), `TB`, and `BT` are accepted. Config can also be supplied via a leading `%%{init: {\'gitGraph\': {...}}}%%` directive or a YAML frontmatter `config:` block for Mermaid compatibility (`mainBranchName`, `showCommitLabel`, `rotateCommitLabel`).\n\n---\n\n## 4. How the engine lays it out\n\nMermaid-compatibility is the differentiator, but the layout is the work:\n\n- The operation list is **replayed in order** to build the commit DAG and assign each commit to its branch lane.\n- Branch lanes are ordered by appearance (overridable with `order:`); merge connectors are routed from the merged branch\'s tip to the new merge commit.\n- Commit nodes are styled by `type:` \u2014 a HIGHLIGHT commit is emphasised, a REVERSE commit marked \u2014 and tags render as flags. Cherry-picks draw a dashed copy edge to the source.\n\nEvery commit carries `data-*` (branch, id, type) for downstream interaction.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 no gitGraph header\ncommit\ncommit\n\n# WRONG \u2014 unknown operation\ngitGraph\n rebase main\n\n# WRONG \u2014 unknown commit type\ngitGraph\n commit type: SQUASH\n```\n\nThe document must start with `gitGraph`; only `commit` / `branch` / `checkout` / `switch` / `merge` / `cherry-pick` are valid operations; `type:` must be `NORMAL`, `HIGHLIGHT`, or `REVERSE`. `%%` starts a comment, matching Mermaid.\n\n---'
3574
+ },
3575
+ "epc": {
3576
+ "title": "EPC (Event-driven Process Chain)",
3577
+ "content": '## 1. Your first EPC\n\nStart with the `epc` keyword, an optional title, then declare **nodes by id** and **wire them with arrows**:\n\n```\nepc "Order fulfilment"\n event E1 "Order received"\n function F1 "Check credit"\n event E2 "Credit OK"\n E1 -> F1 -> E2\n```\n\nNodes carry an id and an optional quoted label; arrows reference ids. The canonical form declares connectors as nodes (`xor X1`) and wires everything by id \u2014 closest to how ARIS stores an EPC.\n\n---\n\n## 2. Node kinds\n\n```\nevent E1 "Order received" # passive state (rounded hexagon)\nfunction F1 "Check credit" # active task (rounded rectangle); alias: func\nfunc F2 "Send invoice" # `func` is shorthand for `function`\nand A1 # AND connector (\u2227)\nor O1 # OR connector (\u2228)\nxor X1 # exclusive-OR connector (\xD7)\n```\n\nA connector fans either way (split or join) depending on its incoming/outgoing arcs, so the same glyph serves both. Connector labels are optional.\n\n---\n\n## 3. Wiring control flow\n\n```\nE1 -> F1 -> X1 # a chain is sugar for the pairwise arcs E1\u2192F1, F1\u2192X1\nX1 -> E2 # connector split into two branches\nX1 -> E3\nF2 -> E2 : sent # a single arc may carry a \': label\'\n```\n\n- A `->` **chain** expands to pairwise edges.\n- A trailing **`: label`** annotates a single arc.\n- An arrow endpoint not yet declared is **auto-created** (and flagged by the validator), so you can sketch fast and clean up later.\n\n```\nepc "Procure-to-pay"\n layout: tb\n event E1 "Need identified"\n function F1 "Create PO"\n and A1\n function F2 "Notify supplier"\n function F3 "Update budget"\n E1 -> F1 -> A1\n A1 -> F2\n A1 -> F3\n```\n\n`layout: tb` lays the chain top-to-bottom (default is also vertical-friendly).\n\n---\n\n## 4. Computed well-formedness\n\nThis is the differentiator. The engine validates (flags, doesn\'t throw):\n\n1. **Bipartite alternation** \u2014 events and functions strictly alternate along any path; connectors don\'t break it.\n2. **Start/end must be events** \u2014 a function may not be a start or end node.\n3. **Signature rule** \u2014 an *event must not be the source of an OR/XOR split* (a passive event cannot decide); an AND-split after an event is allowed.\n4. **Split/join balancing** \u2014 a split of type T should be closed by a join of type T; mismatches are warnings (real EPCs are sometimes unbalanced).\n5. **Single-in / single-out** per event and function \u2014 connectors carry the multiplicity.\n6. **Reachability** \u2014 every node reachable from a start and reaching an end.\n\nOffending nodes are highlighted with `data-*` flags; the diagram still renders.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 invalid id (must start with a letter)\nevent 9bad\n\n# AVOID \u2014 redeclaring a node (the first wins, a warning is raised)\nevent E1 "first"\nevent E1 "second"\n```\n\nIds must start with a letter; a redeclared node keeps the first definition and warns. Because event\u2192XOR/OR splits are flagged, route a decision through a **function** that produces the decision, then split.\n\n---'
3578
+ },
3579
+ "idef0": {
3580
+ "title": "IDEF0 Function Model",
3581
+ "content": '## 1. Your first IDEF0 diagram\n\nStart with the `idef0` keyword, an optional title, an optional `node` (the diagram\'s node number), then **function boxes** and their **ICOM arrows**:\n\n```\nidef0 "Fill order"\nfunction A1 "Receive order"\ninput A1 "Customer request"\ncontrol A1 "Order policy"\nmechanism A1 "Order clerk"\noutput A1 "Confirmed order"\n```\n\n`function ID "name"` declares a box (in declaration order). `node A0` sets the parent node number used to derive child node numbers (A0 \u2192 A1..An). FIPS guidance is 3\u20136 boxes per diagram; outside that range the engine warns.\n\n---\n\n## 2. ICOM boundary arrows\n\nEach keyword pins an arrow to a specific side of a box, and that *is* its role:\n\n```\ninput A1 "Sales orders" # enters the LEFT edge\ncontrol A1 "Production schedule" # enters the TOP edge (governs the activity)\noutput A1 "Product" # leaves the RIGHT edge\nmechanism A1 "CNC machines" # enters the BOTTOM edge (the resource)\n```\n\n`input`, `control`, and `mechanism` route from the diagram frame **into** the box; `output` routes from the box **out** to the frame. The engine codes these boundary arrows down each edge (I1, I2 / C1 / O1 / M1).\n\n---\n\n## 3. Flow arrows between boxes\n\nA `->` arrow connects two boxes; by default it lands on the target\'s **input**, but you can name the target\'s ICOM side:\n\n```\nA1 -> A2 "Work plan" # box\u2192box; defaults to A2\'s input\nA2 -> A3.control "Parts spec" # land on A3\'s control (top) edge\ninput A2 "Raw material" (tunnel) # (tunnel) hides the arrow at this level\n```\n\n- `target.control` / `target.input` / `target.mechanism` picks the landing side.\n- A flow **cannot land on the target\'s `.output`** \u2014 an output leaves a box, it does not enter one.\n- `(tunnel)` marks a tunnelled arrow (suppressed on the parent/child diagram per FIPS).\n\n---\n\n## 4. Computed structural enforcement\n\nThis is the differentiator \u2014 what makes the model correct where a drawing tool is not:\n\n1. **ICOM placement enforcement** \u2014 the role is resolved against the box\'s geometry side; a malformed role, or a flow asked to *enter* a box via `.output`, is rejected.\n2. **Reference resolution** \u2014 every box id named by an arrow must be declared.\n3. **Decomposition numbering** \u2014 boxes get contiguous box numbers 1..n (lower-right corner) and node numbers (A0 \u2192 A1..An); explicit `#N` numbers are checked for contiguity, range, and duplicates.\n4. **Boundary coding** \u2014 boundary arrows are coded I1/C1/O1/M1 down each edge.\n5. **Box-count guideline** \u2014 fewer than 3 or more than 6 boxes raises a FIPS-183 warning.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 no idef0 header\nfunction A1 "x"\n\n# WRONG \u2014 a flow landing on the target\'s output\nA1 -> A2.output "bad"\n\n# WRONG \u2014 an unknown ICOM side word\nA1 -> A2.sideways "bad"\n```\n\nThe document must start with `idef0`; arrows may target `.input` / `.control` / `.mechanism` only; every referenced box id must be declared. Because the keyword encodes the side, you cannot accidentally draw a control as an input \u2014 the standard is enforced, not suggested.\n\n---'
3582
+ },
3583
+ "threatmodel": {
3584
+ "title": "Threat Model (STRIDE DFD)",
3585
+ "content": '## 1. Your first threat model\n\nStart with the `threatmodel` keyword (alias `stride`), an optional title, then **declare elements** and **wire flows**:\n\n```\nthreatmodel "Login flow"\nexternal: User\nprocess 1.1: Web Server\ndatastore D1: User DB\nUser -> 1.1 : "Login request"\n1.1 -> D1 : Lookup\n```\n\nEach element is `kind: ID: Label` (or `kind: Label`, where the id is slugged from the label \u2014 `external: Mobile App` becomes id `Mobile_App`). A flow is `SOURCE -> TARGET : label`, and the **label is mandatory** (it names the data crossing).\n\n---\n\n## 2. Element kinds and flows\n\n```\nexternal: User # external entity (the attacker\'s side)\nprocess 1.1: Web Server # a process / service\ndatastore D1: User DB # a data store\ndatastore D2: Audit log # a log/audit store (gets conditional Repudiation)\nUser -> 1.1 : "HTTPS Request" # directed flow, quoted or bare label\n1.1 <-> D1 : Read/Write # <-> expands into two directed flows\n```\n\n- Process ids are often dotted DFD numbers (`1.1`, `2.3`); external/store ids are usually short slugs (`User`, `D1`).\n- A `<->` flow expands into **two** directed flows.\n- A store whose name/id matches `log|audit|journal` (or carries an explicit hint) is treated as a log store.\n\n**Flow rules** the engine enforces: no store\u2192store flows (data stores are passive), no external\u2192external flows, and every endpoint must be a declared element.\n\n---\n\n## 3. Trust boundaries\n\n```\nboundary "Internet" { User }\nboundary "DMZ" { 1.1 }\nboundary "Internal" { D1, D2 }\n```\n\n`boundary "name" { id, id, \u2026 }` groups elements into a trust zone. An element may belong to **at most one** boundary; members must be declared. Elements in no boundary share an implicit untrusted zone.\n\n---\n\n## 4. Computed STRIDE analysis\n\nThis is the differentiator. The engine applies the **STRIDE-per-element** mapping:\n\n| DFD element | Threats applied |\n|-----------------|------------------------|\n| External entity | S, R |\n| Process | S, T, R, I, D, E |\n| Data store | T, I, D (+ R if log) |\n| Data flow | T, I, D |\n\n- The data-store **Repudiation** is conditional \u2014 added for log / audit / journal stores (the Shostack green "?").\n- **Boundary crossing**: a flow whose endpoints sit in different trust zones is flagged, because that is where Spoofing / Tampering / Information-disclosure concentrate. Two elements in the same (or implicit) zone do not cross.\n\nEach element and flow carries its applicable STRIDE categories in `data-*` so the analysis is inspectable.\n\n---\n\n## 5. Common mistakes\n\n```\n# WRONG \u2014 flow with no label\nUser -> 1.1\n\n# WRONG \u2014 store to store (data stores are passive)\nD1 -> D2 : x\n\n# WRONG \u2014 external to external\nA -> B : x\n\n# WRONG \u2014 unknown flow endpoint\nP -> Ghost : x\n\n# WRONG \u2014 an element in two boundaries\nboundary "A" { P }\nboundary "B" { P }\n```\n\nEvery flow needs a label; stores and externals cannot be flow partners with their own kind; endpoints must be declared; an element belongs to at most one trust boundary. Duplicate ids are rejected.\n\n---'
3586
+ },
3587
+ "welding": {
3588
+ "title": "Welding symbol diagram",
3589
+ "content": '## 1. Your first weld\n\nThe smallest useful callout: a header, one joint, one arrow-side weld.\n\n```\nwelding "Bracket"\njoint "bracket to plate" {\n arrow: fillet size=8\n}\n```\n\nThree rules cover most usage:\n\n1. Start with `welding`, optionally `standard: aws | iso-a | iso-b` (default `aws`) and a quoted title.\n2. Each joint is a `joint "label" { \u2026 }` block. Put a weld on `arrow:` (arrow side) and/or `other:` (other side).\n3. A weld spec is a type followed by `key=value` dimensions \u2014 `fillet size=8`, `vgroove angle=60 root=3`.\n\n---\n\n## 2. Sides \u2014 arrow, other, both\n\nA joint is welded on the arrow side, the other side, or both.\n\n```\njoint "double fillet" {\n both: fillet size=6 # same weld on both sides\n}\njoint "asymmetric" {\n arrow: fillet size=8 # arrow side only\n other: vgroove angle=60 # different weld on the other side\n}\n```\n\n- **AWS** (default): the arrow-side glyph draws **below** the reference line, the other-side glyph **above**.\n- **ISO-A** (`standard: iso-a`): a **dashed companion line** appears; the arrow-side weld attaches to the solid line, the other-side weld to the dashed line. A symmetric `both:` weld suppresses the dashed line.\n\n---\n\n## 3. Weld types\n\n| Type | Glyph | Type | Glyph |\n|---|---|---|---|\n| `fillet` | triangle | `plug` / `slot` | rectangle |\n| `square` | parallel verticals | `spot` | circle on the line |\n| `vgroove` | V | `seam` | circle + line |\n| `bevel` | half-V | `back` / `backing` | semicircle |\n| `ugroove` | U | `surfacing` | build-up bumps |\n| `jgroove` | half-U | `edge` | tall verticals |\n| `flarev` / `flarebevel` | curved groove | | |\n\nAliases: `v`\u2192`vgroove`, `u`\u2192`ugroove`, `j`\u2192`jgroove`, `flare-v`\u2192`flarev`.\n\n---\n\n## 4. Dimensions\n\nDimensions read along the reference line in fixed slots.\n\n```\njoint "groove" {\n arrow: vgroove angle=60 root=3 throat=12 len=50 pitch=150\n}\n```\n\n| Key | Slot | Meaning |\n|---|---|---|\n| `size=` | left of symbol | fillet leg / groove depth / plug diameter |\n| `throat=` | left, in parentheses | effective throat `(E)` |\n| `len=` | right of symbol | weld length |\n| `pitch=` | right (with `len`) | intermittent centre-to-centre pitch \u2192 `len-pitch` |\n| `count=` | right (ISO) | number of increments \u2192 `count\xD7len (pitch)` |\n| `angle=` | at the opening | groove included angle (groove types only) |\n| `root=` | between symbol and line | root opening / gap |\n| `contour=` | above the symbol | `flush` (bar), `convex`, `concave` (arc) |\n| `finish=` | above the contour | finish method letter `G`/`M`/`C`/`R`/`H`/`U` |\n\n---\n\n## 5. Supplementary symbols\n\n```\njoint "post base" {\n arrow: fillet size=10\n around # weld-all-around \u2014 open circle at the junction\n field # field / site weld \u2014 filled flag pointing to the tail\n tail: "GTAW; WPS-12" # process / spec / NDE method\n}\n```\n\n```\nwelding\njoint "butt weld" {\n arrow: vgroove angle=60 root=3 throat=12\n other: backing\n tail: "SMAW; E7018"\n}\n```\n\n---'
3141
3590
  }
3142
3591
  };
3143
3592
 
@@ -3703,6 +4152,258 @@ var PROFILES = {
3703
4152
  "'needs at least one threat/consequence' \u2014 a one-wing diagram is a fault tree or event tree; add the missing wing.",
3704
4153
  "'exactly one top event' \u2014 keep a single `topevent` line."
3705
4154
  ]
4155
+ },
4156
+ eventtree: {
4157
+ type: "eventtree",
4158
+ header: 'eventtree "Title"',
4159
+ mode: "initiating event + ordered safety functions, then outcome rows with s/f/* branch patterns",
4160
+ forms: [
4161
+ 'initiating LOCA "Large LOCA" freq: 1e-4',
4162
+ 'function A "ECCS injects" p: 0.001',
4163
+ 'function B "Containment spray" p: 0.01',
4164
+ 'outcome s s -> "OK"',
4165
+ 'outcome s f -> "Late release"',
4166
+ 'outcome f * -> "Core damage"'
4167
+ ],
4168
+ prefer: [
4169
+ "Keyword `eventtree` (or `eta`). One `initiating` line with a `freq:`; then `function` lines in left-to-right order, each with its failure probability `p:` (success is auto-complemented to 1\u2212p).",
4170
+ 'Each `outcome` row gives a `s`/`f`/`*` pattern (one symbol per function) then `-> "label"`. `*` = path already terminated (pruned) \u2014 runs flat to its leaf, no further branching.',
4171
+ "Let the engine compute path frequencies (f_initiating \xD7 \u03A0 branch-probs) \u2014 don't hand-write them."
4172
+ ],
4173
+ avoid: [
4174
+ "Don't try to make a balanced 2\u207F tree \u2014 prune with `*` where a sequence ends early.",
4175
+ "Don't put probabilities on outcome rows; `p:` belongs on `function` lines.",
4176
+ "Don't reverse the success/failure convention \u2014 success branches up, failure down."
4177
+ ],
4178
+ repair: [
4179
+ "'outcome pattern length' \u2014 give exactly one s/f/* per declared function.",
4180
+ "'no initiating event' \u2014 add one `initiating <id> \"\u2026\" freq: \u2026` line.",
4181
+ "'once pruned stays pruned' \u2014 after a `*` in a pattern, the rest must also be `*`."
4182
+ ]
4183
+ },
4184
+ fmea: {
4185
+ type: "fmea",
4186
+ header: 'fmea "Title"',
4187
+ mode: "indentation-structured worksheet: item \u2192 failure mode \u2192 effect/cause/controls with S/O/D scores",
4188
+ forms: [
4189
+ 'fmea "Brake System DFMEA"',
4190
+ ' item "Master cylinder" fn "Generate pressure"',
4191
+ ' mode "Internal seal leak"',
4192
+ ' effect "Loss of braking" sev: 9',
4193
+ ' cause "Seal degradation" occ: 3',
4194
+ ' controls detection: "Bench test" det: 4'
4195
+ ],
4196
+ prefer: [
4197
+ "Keyword `fmea`. Nest `item` \u2192 `mode` \u2192 `effect`/`cause` \u2192 `controls`. Put Severity on the effect (`sev:`), Occurrence on the cause (`occ:`), Detection on the controls (`det:`) \u2014 each 1\u201310.",
4198
+ "Let the engine compute RPN = S\xD7O\xD7D and the Action Priority (High/Medium/Low) and rank the sheet \u2014 don't hand-write RPN.",
4199
+ "Optional `rank: ap|rpn` and `flag: ap >= High` / `flag: rpn > 100` to control sorting/highlighting."
4200
+ ],
4201
+ avoid: [
4202
+ "Don't write the RPN or AP yourself \u2014 they are computed.",
4203
+ "Don't use scores outside 1\u201310.",
4204
+ "Don't put `occ:` on an effect or `sev:` on a cause \u2014 they bind to the wrong column."
4205
+ ],
4206
+ repair: [
4207
+ "'score out of range' \u2014 keep S/O/D in 1\u201310.",
4208
+ "'mode has no effect/cause' \u2014 every failure mode needs at least one effect and one cause."
4209
+ ]
4210
+ },
4211
+ causalloop: {
4212
+ type: "causalloop",
4213
+ header: 'causalloop "Title"',
4214
+ mode: "signed causal links between variables; engine detects R/B loops",
4215
+ forms: [
4216
+ 'causalloop "Adoption model"',
4217
+ '"Adoption rate" -> Adopters : +',
4218
+ 'Adopters -> "Adoption rate" : +',
4219
+ '"Adoption rate" -> "Potential adopters" : -',
4220
+ 'loop R1 "Word of mouth"',
4221
+ 'loop B1 "Market saturation"'
4222
+ ],
4223
+ prefer: [
4224
+ "Keyword `causalloop` (or `cld`). Write `A -> B : +` or `A -> B : -` (you may use `s`/`o` aliases). Multi-word variables go in quotes.",
4225
+ 'Let the engine classify loops R (reinforcing) / B (balancing) by counting negative links; optionally name them with `loop R1 "\u2026"`.',
4226
+ "Put the `+`/`\u2212` polarity at the end of each link."
4227
+ ],
4228
+ avoid: [
4229
+ "Don't draw boxes around variables \u2014 CLD variables are boxless text.",
4230
+ "Don't omit polarity \u2014 every causal link is signed."
4231
+ ],
4232
+ repair: [
4233
+ "'link needs a polarity' \u2014 append `: +` or `: -`.",
4234
+ "'unknown variable' \u2014 variables are created on first use; check quoting/spelling."
4235
+ ]
4236
+ },
4237
+ markov: {
4238
+ type: "markov",
4239
+ header: 'markov "Title"',
4240
+ mode: "probability-labelled state transitions; engine computes stationary distribution + classification",
4241
+ forms: [
4242
+ 'markov "Weather"',
4243
+ "Sunny -> Sunny : 0.9",
4244
+ "Sunny -> Rainy : 0.1",
4245
+ "Rainy -> Sunny : 0.5",
4246
+ "Rainy -> Rainy : 0.5"
4247
+ ],
4248
+ prefer: [
4249
+ "Keyword `markov` (or `markovchain`). Write `S1 -> S2 : 0.3` transitions; each state's outgoing probabilities must sum to 1.",
4250
+ "Mark absorbing states by a 1.0 self-loop (or the `absorbing` keyword); the engine computes the stationary distribution and classifies states.",
4251
+ "For an absorbing chain add `analysis: classify, absorbing` to get absorption probabilities + expected steps."
4252
+ ],
4253
+ avoid: [
4254
+ "Don't let a state's out-probabilities sum to \u2260 1 (the engine errors); use `normalize: true` only if you intend rescaling.",
4255
+ "Don't hand-compute the stationary distribution \u2014 it is derived."
4256
+ ],
4257
+ repair: [
4258
+ "'row does not sum to 1' \u2014 fix the probabilities or add `normalize: true`.",
4259
+ "'probability out of range' \u2014 keep each on (0,1]."
4260
+ ]
4261
+ },
4262
+ gitgraph: {
4263
+ type: "gitgraph",
4264
+ header: "gitGraph",
4265
+ mode: "Mermaid-compatible ordered git operations (commit/branch/checkout/merge)",
4266
+ forms: [
4267
+ "gitGraph",
4268
+ ' commit id: "init"',
4269
+ " branch develop",
4270
+ " checkout develop",
4271
+ ' commit tag: "v0.1"',
4272
+ " checkout main",
4273
+ " merge develop"
4274
+ ],
4275
+ prefer: [
4276
+ 'Header `gitGraph` (Mermaid syntax). Operations in order: `commit`, `branch <name>`, `checkout <name>`, `merge <name>`, `cherry-pick id: "\u2026"`.',
4277
+ 'Annotate commits with `id: "\u2026"`, `tag: "\u2026"`, `type: HIGHLIGHT|REVERSE|NORMAL`.',
4278
+ "`checkout` (or `switch`) before committing onto a branch; `merge <name>` merges that branch into the current one."
4279
+ ],
4280
+ avoid: [
4281
+ "Don't `merge` or `checkout` a branch you never created with `branch`.",
4282
+ "Don't merge a branch into itself."
4283
+ ],
4284
+ repair: [
4285
+ "'unknown branch' \u2014 add a `branch <name>` before `checkout`/`merge`.",
4286
+ "'cannot merge current branch' \u2014 checkout the target lane first."
4287
+ ]
4288
+ },
4289
+ epc: {
4290
+ type: "epc",
4291
+ header: 'epc "Title"',
4292
+ mode: "alternating events/functions joined by AND/OR/XOR connectors; alternation validated",
4293
+ forms: [
4294
+ 'epc "Order fulfilment"',
4295
+ ' event E1 "Order received"',
4296
+ ' function F1 "Check credit"',
4297
+ " xor X1",
4298
+ ' event E2 "Credit OK"',
4299
+ ' event E3 "Credit rejected"',
4300
+ " E1 -> F1 -> X1",
4301
+ " X1 -> E2",
4302
+ " X1 -> E3"
4303
+ ],
4304
+ prefer: [
4305
+ "Keyword `epc`. Declare `event`, `function`, and connectors `and`/`or`/`xor` with ids; then the control flow `A -> B -> C`.",
4306
+ "Strictly alternate events and functions (a connector may sit between them); start and end with events.",
4307
+ "Use a connector node to split/join \u2014 e.g. a `xor` after a function fans out to alternative events."
4308
+ ],
4309
+ avoid: [
4310
+ "Don't make an event the source of an OR/XOR split (only functions may decide) \u2014 the engine rejects it.",
4311
+ "Don't connect event\u2192event or function\u2192function directly."
4312
+ ],
4313
+ repair: [
4314
+ "'event cannot be an OR/XOR split source' \u2014 put a function (decision) before the connector.",
4315
+ "'alternation violated' \u2014 insert the missing event/function between two same-kind nodes."
4316
+ ]
4317
+ },
4318
+ idef0: {
4319
+ type: "idef0",
4320
+ header: 'idef0 "Title"',
4321
+ mode: "function boxes + positional ICOM arrows (input-left, control-top, output-right, mechanism-bottom)",
4322
+ forms: [
4323
+ 'idef0 "Manufacture product"',
4324
+ ' function A1 "Plan production"',
4325
+ ' function A2 "Make parts"',
4326
+ ' input A1 "Sales orders"',
4327
+ ' control A1 "Production schedule"',
4328
+ ' A1 -> A2 "Work plan"',
4329
+ ' mechanism A2 "CNC machines"',
4330
+ ' output A2 "Product"'
4331
+ ],
4332
+ prefer: [
4333
+ "Keyword `idef0`. Declare `function` boxes; attach boundary arrows with `input`/`control`/`output`/`mechanism`.",
4334
+ 'Box\u2192box flow `A1 -> A2 "label"` lands on A2\'s input by default; use `A1 -> A2.control "\u2026"` to land on another ICOM side.',
4335
+ "Remember the ICOM rule: Inputs enter left, Controls enter top, Outputs exit right, Mechanisms enter bottom \u2014 the engine enforces it."
4336
+ ],
4337
+ avoid: [
4338
+ "Don't route an output anywhere but the right edge, or a control anywhere but the top \u2014 the engine rejects it.",
4339
+ "Don't hand-number the boxes \u2014 node numbers are assigned."
4340
+ ],
4341
+ repair: [
4342
+ "'output must exit the right edge' \u2014 declare it with `output` / land flows on the correct side.",
4343
+ "'unknown box' \u2014 declare the `function` before referencing it."
4344
+ ]
4345
+ },
4346
+ threatmodel: {
4347
+ type: "threatmodel",
4348
+ header: 'threatmodel "Title"',
4349
+ mode: "DFD elements + data flows + trust boundaries; engine maps STRIDE + flags boundary crossings",
4350
+ forms: [
4351
+ 'threatmodel "Web App \u2014 STRIDE"',
4352
+ "external: User",
4353
+ "process 1.1: Web Server",
4354
+ "datastore D1: User DB",
4355
+ "User -> 1.1 : HTTPS Request",
4356
+ "1.1 -> D1 : Lookup",
4357
+ 'boundary "Internet" { User }',
4358
+ 'boundary "DMZ" { 1.1 }'
4359
+ ],
4360
+ prefer: [
4361
+ 'Keyword `threatmodel` (or `stride`). Declare `external:`, `process N:`, `datastore D:`; connect with `A -> B : "label"` flows; group ids into `boundary "name" { \u2026 }` trust zones.',
4362
+ "Let the engine annotate each element's STRIDE letters and flag flows that cross a trust boundary \u2014 don't list threats by hand.",
4363
+ "Name audit/log stores with `log`/`audit`/`journal` so they pick up the conditional Repudiation threat."
4364
+ ],
4365
+ avoid: [
4366
+ "Don't connect store\u2192store or external\u2192external (not valid DFD edges).",
4367
+ "Don't leave data flows unlabelled."
4368
+ ],
4369
+ repair: [
4370
+ "'unknown element' \u2014 declare the external/process/datastore before referencing it.",
4371
+ "'flow needs a label' \u2014 add `: \"\u2026\"` to the flow."
4372
+ ]
4373
+ },
4374
+ welding: {
4375
+ type: "welding",
4376
+ header: "welding [standard: aws|iso-a|iso-b]",
4377
+ mode: "reference-line callouts; one joint block per joint, weld glyphs above/below the line",
4378
+ forms: [
4379
+ 'welding "Bracket welds"',
4380
+ 'joint "bracket to plate" {',
4381
+ " arrow: fillet size=8 len=50 pitch=150",
4382
+ " other: fillet size=6",
4383
+ " around",
4384
+ " field",
4385
+ ' tail: "GTAW"',
4386
+ "}",
4387
+ 'joint "butt weld" {',
4388
+ " arrow: vgroove angle=60 root=3 throat=12",
4389
+ " other: backing",
4390
+ "}"
4391
+ ],
4392
+ prefer: [
4393
+ 'Keyword `welding`; one `joint "label" { \u2026 }` block per joint. Put a weld on `arrow:` (arrow side) and/or `other:` (other side); `both:` is shorthand for the same weld on both sides.',
4394
+ "A weldspec is `<type> key=value \u2026`: `size=` (left of symbol), `len=`/`pitch=` (length-pitch, right), `angle=`/`root=` (groove only), `throat=`, `contour=flush|convex|concave`, `finish=G|M|C|R|H|U`.",
4395
+ 'Flags on their own line: `around` (weld-all-around), `field` (site weld). Process/spec/NDE goes in `tail: "GTAW; WPS-12"`.',
4396
+ "Types: fillet \xB7 square \xB7 vgroove \xB7 bevel \xB7 ugroove \xB7 jgroove \xB7 flarev \xB7 flarebevel \xB7 plug \xB7 slot \xB7 spot \xB7 seam \xB7 back \xB7 backing \xB7 surfacing \xB7 edge."
4397
+ ],
4398
+ avoid: [
4399
+ "Don't put `angle=` on a fillet/plug/spot \u2014 angle is groove-only (the engine warns).",
4400
+ "Don't use `both:` for plug/slot/surfacing \u2014 they are single-side; surfacing is arrow-side only.",
4401
+ "Don't give a fillet without `size=`, or a `pitch=` without `len=`."
4402
+ ],
4403
+ repair: [
4404
+ "'a fillet weld needs a leg size' \u2014 add `size=\u2026` to the fillet spec.",
4405
+ "'angle= only applies to groove welds' \u2014 drop `angle=`, or change the type to a groove."
4406
+ ]
3706
4407
  }
3707
4408
  };
3708
4409
  function getGenerationProfile(type) {
@@ -3786,7 +4487,7 @@ function getExamples(type, opts = {}) {
3786
4487
  function validateDsl(type, dsl) {
3787
4488
  const resolvedType = type ? resolveDiagramType(type) : void 0;
3788
4489
  const config = type ? { type: resolvedType ?? type } : void 0;
3789
- const result = chunkHFATQXFN_cjs.parseResult(dsl, config);
4490
+ const result = chunkHBQFUZBD_cjs.parseResult(dsl, config);
3790
4491
  if (result.ok) {
3791
4492
  return {
3792
4493
  ok: true,
@@ -3812,7 +4513,7 @@ function renderDsl(type, dsl, options = {}) {
3812
4513
  ...options,
3813
4514
  ...type ? { type: resolvedType ?? type } : {}
3814
4515
  };
3815
- const result = chunkHFATQXFN_cjs.renderResult(dsl, config);
4516
+ const result = chunkHBQFUZBD_cjs.renderResult(dsl, config);
3816
4517
  if (result.ok) {
3817
4518
  return {
3818
4519
  ok: true,
@@ -3865,5 +4566,5 @@ exports.listDiagrams = listDiagrams;
3865
4566
  exports.renderDsl = renderDsl;
3866
4567
  exports.resolveDiagramType = resolveDiagramType;
3867
4568
  exports.validateDsl = validateDsl;
3868
- //# sourceMappingURL=chunk-CTMJ3XP2.cjs.map
3869
- //# sourceMappingURL=chunk-CTMJ3XP2.cjs.map
4569
+ //# sourceMappingURL=chunk-Q3SJB7H3.cjs.map
4570
+ //# sourceMappingURL=chunk-Q3SJB7H3.cjs.map