schematex 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/dist/ai/ai-sdk.cjs +19 -19
  2. package/dist/ai/ai-sdk.d.cts +3 -3
  3. package/dist/ai/ai-sdk.d.ts +3 -3
  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-BnJAPCwC.d.cts} +1 -1
  10. package/dist/{api-DIJM_mqp.d.ts → api-DAZJGq9r.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-34O3C6OC.cjs → chunk-2YBBDPOW.cjs} +4 -4
  16. package/dist/chunk-2YBBDPOW.cjs.map +1 -0
  17. package/dist/{chunk-HFATQXFN.cjs → chunk-3CSST5GZ.cjs} +7725 -487
  18. package/dist/chunk-3CSST5GZ.cjs.map +1 -0
  19. package/dist/{chunk-JTGTWBAD.js → chunk-6DVK5M2J.js} +3 -3
  20. package/dist/chunk-6DVK5M2J.js.map +1 -0
  21. package/dist/{chunk-SHMG7BVF.cjs → chunk-6L46VIXI.cjs} +4 -4
  22. package/dist/{chunk-SHMG7BVF.cjs.map → chunk-6L46VIXI.cjs.map} +1 -1
  23. package/dist/{chunk-OFKRELZK.js → chunk-ABCMTAOZ.js} +3 -3
  24. package/dist/{chunk-OFKRELZK.js.map → chunk-ABCMTAOZ.js.map} +1 -1
  25. package/dist/{chunk-5FYPSIGD.cjs → chunk-B5AQ3CG3.cjs} +4 -4
  26. package/dist/{chunk-5FYPSIGD.cjs.map → chunk-B5AQ3CG3.cjs.map} +1 -1
  27. package/dist/{chunk-3GAPHXCE.js → chunk-CDK7KDIW.js} +3 -3
  28. package/dist/{chunk-3GAPHXCE.js.map → chunk-CDK7KDIW.js.map} +1 -1
  29. package/dist/{chunk-Z3A2UNK2.cjs → chunk-D34VGLSE.cjs} +4 -4
  30. package/dist/{chunk-Z3A2UNK2.cjs.map → chunk-D34VGLSE.cjs.map} +1 -1
  31. package/dist/{chunk-JAYJ2G4R.cjs → chunk-DHI7YAQJ.cjs} +4 -4
  32. package/dist/{chunk-JAYJ2G4R.cjs.map → chunk-DHI7YAQJ.cjs.map} +1 -1
  33. package/dist/{chunk-C7V57V6O.js → chunk-DX44TBFZ.js} +3 -3
  34. package/dist/{chunk-C7V57V6O.js.map → chunk-DX44TBFZ.js.map} +1 -1
  35. package/dist/{chunk-MPCSWRZC.js → chunk-E3CAJGJM.js} +3 -3
  36. package/dist/{chunk-MPCSWRZC.js.map → chunk-E3CAJGJM.js.map} +1 -1
  37. package/dist/{chunk-EENA7KNU.js → chunk-E7LXMEKX.js} +3 -3
  38. package/dist/{chunk-EENA7KNU.js.map → chunk-E7LXMEKX.js.map} +1 -1
  39. package/dist/{chunk-P26FCZP3.js → chunk-IXRPRMHI.js} +3 -3
  40. package/dist/{chunk-P26FCZP3.js.map → chunk-IXRPRMHI.js.map} +1 -1
  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-77GPD4YQ.cjs → chunk-KUITLG4N.cjs} +4 -4
  48. package/dist/{chunk-77GPD4YQ.cjs.map → chunk-KUITLG4N.cjs.map} +1 -1
  49. package/dist/{chunk-2F45Y2ON.cjs → chunk-NXU4XKLY.cjs} +4 -4
  50. package/dist/{chunk-2F45Y2ON.cjs.map → chunk-NXU4XKLY.cjs.map} +1 -1
  51. package/dist/{chunk-UHPGWO77.cjs → chunk-Q2YRJHFB.cjs} +4 -4
  52. package/dist/{chunk-UHPGWO77.cjs.map → chunk-Q2YRJHFB.cjs.map} +1 -1
  53. package/dist/{chunk-CTMJ3XP2.cjs → chunk-RKN6QJ7K.cjs} +582 -6
  54. package/dist/chunk-RKN6QJ7K.cjs.map +1 -0
  55. package/dist/{chunk-2TUZ3QJA.js → chunk-T3GV7OVF.js} +3 -3
  56. package/dist/{chunk-2TUZ3QJA.js.map → chunk-T3GV7OVF.js.map} +1 -1
  57. package/dist/{chunk-5IKOLUWK.js → chunk-TO6PNBT3.js} +3 -3
  58. package/dist/chunk-TO6PNBT3.js.map +1 -0
  59. package/dist/{chunk-IFXHIYZE.js → chunk-TXMT4XLE.js} +580 -4
  60. package/dist/chunk-TXMT4XLE.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-IFNNV54X.js → chunk-XI5QP7LM.js} +7697 -467
  68. package/dist/chunk-XI5QP7LM.js.map +1 -0
  69. package/dist/{chunk-LPAWZYDU.cjs → chunk-YMFYPB5Y.cjs} +14 -14
  70. package/dist/{chunk-LPAWZYDU.cjs.map → chunk-YMFYPB5Y.cjs.map} +1 -1
  71. package/dist/{diagnostics-BKRow1ur.d.ts → diagnostics-B-jOt6Ua.d.cts} +1 -1
  72. package/dist/{diagnostics-BKRow1ur.d.cts → diagnostics-B-jOt6Ua.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-Dq3Mfxay.d.ts → index-8MaUFTMN.d.ts} +1 -1
  128. package/dist/{index-DU2h1Y-a.d.cts → index-D-MAB6CH.d.cts} +1 -1
  129. package/dist/index.cjs +83 -51
  130. package/dist/index.d.cts +33 -5
  131. package/dist/index.d.ts +33 -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-CV1JZ75-.d.cts} +2 -2
  138. package/dist/{tools-A0HRZ8jj.d.ts → tools-CcPysgAD.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 chunk3CSST5GZ_cjs = require('./chunk-3CSST5GZ.cjs');
4
4
 
5
5
  // src/ai/registry.ts
6
6
  var DIAGRAM_REGISTRY = [
@@ -340,6 +340,81 @@ 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"
343
418
  }
344
419
  ];
345
420
  var DIAGRAM_SINCE = {
@@ -390,7 +465,16 @@ var DIAGRAM_SINCE = {
390
465
  // 0.6.5
391
466
  faulttree: "0.6.5",
392
467
  // 0.6.6
393
- bowtie: "0.6.6"
468
+ bowtie: "0.6.6",
469
+ // 0.8.0 — Bucket B (event tree, FMEA, causal loop, Markov, git graph, EPC, IDEF0, threat model)
470
+ eventtree: "0.8.0",
471
+ fmea: "0.8.0",
472
+ causalloop: "0.8.0",
473
+ markov: "0.8.0",
474
+ gitgraph: "0.8.0",
475
+ epc: "0.8.0",
476
+ idef0: "0.8.0",
477
+ threatmodel: "0.8.0"
394
478
  };
395
479
  function getDiagramSince(type) {
396
480
  const resolved = resolveDiagramType(type);
@@ -672,6 +756,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
672
756
  "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
757
  "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
758
  },
759
+ {
760
+ "slug": "causalloop-growth-engine",
761
+ "diagram": "causalloop",
762
+ "title": "Startup growth engine (R and B loops)",
763
+ "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.",
764
+ "standard": "Sterman, Business Dynamics (2000)",
765
+ "tags": [
766
+ "causalloop",
767
+ "cld",
768
+ "system-dynamics",
769
+ "feedback",
770
+ "reinforcing",
771
+ "balancing"
772
+ ],
773
+ "complexity": 3,
774
+ "featured": false,
775
+ "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"',
776
+ "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.'
777
+ },
778
+ {
779
+ "slug": "causalloop-traffic-congestion",
780
+ "diagram": "causalloop",
781
+ "title": "Urban traffic congestion (two balancing loops)",
782
+ "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.",
783
+ "standard": "Sterman, Business Dynamics (2000)",
784
+ "tags": [
785
+ "causalloop",
786
+ "cld",
787
+ "system-dynamics",
788
+ "induced-demand",
789
+ "balancing",
790
+ "transit"
791
+ ],
792
+ "complexity": 2,
793
+ "featured": false,
794
+ "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"',
795
+ "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."
796
+ },
675
797
  {
676
798
  "slug": "circuit-bridge-rectifier-supply",
677
799
  "diagram": "circuit",
@@ -961,6 +1083,25 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
961
1083
  "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
1084
  "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
1085
  },
1086
+ {
1087
+ "slug": "epc-procurement",
1088
+ "diagram": "epc",
1089
+ "title": "Procurement EPC with XOR split",
1090
+ "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.",
1091
+ "standard": "ARIS EPC (Scheer)",
1092
+ "tags": [
1093
+ "epc",
1094
+ "aris",
1095
+ "business-process",
1096
+ "xor",
1097
+ "and",
1098
+ "control-flow"
1099
+ ],
1100
+ "complexity": 3,
1101
+ "featured": false,
1102
+ "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',
1103
+ "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.'
1104
+ },
964
1105
  {
965
1106
  "slug": "erd-billing-ledger-audit-trail",
966
1107
  "diagram": "erd",
@@ -1035,6 +1176,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1035
1176
  "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
1177
  "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
1178
  },
1179
+ {
1180
+ "slug": "eventtree-flammable-release",
1181
+ "diagram": "eventtree",
1182
+ "title": "Flammable release event tree (process QRA)",
1183
+ "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.",
1184
+ "standard": "IEC 62502 / process QRA",
1185
+ "tags": [
1186
+ "eventtree",
1187
+ "eta",
1188
+ "qra",
1189
+ "loss-of-containment",
1190
+ "ignition",
1191
+ "explosion"
1192
+ ],
1193
+ "complexity": 2,
1194
+ "featured": false,
1195
+ "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"',
1196
+ "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."
1197
+ },
1198
+ {
1199
+ "slug": "eventtree-loca",
1200
+ "diagram": "eventtree",
1201
+ "title": "Large LOCA event tree (reactor PRA)",
1202
+ "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.",
1203
+ "standard": "IEC 62502 / NUREG (WASH-1400)",
1204
+ "tags": [
1205
+ "eventtree",
1206
+ "eta",
1207
+ "pra",
1208
+ "nuclear",
1209
+ "sequence-frequency",
1210
+ "core-damage"
1211
+ ],
1212
+ "complexity": 3,
1213
+ "featured": false,
1214
+ "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"',
1215
+ "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.'
1216
+ },
1038
1217
  {
1039
1218
  "slug": "faulttree-pump-redundancy",
1040
1219
  "diagram": "faulttree",
@@ -1260,6 +1439,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1260
1439
  "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
1440
  "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
1441
  },
1442
+ {
1443
+ "slug": "fmea-ev-battery-dfmea",
1444
+ "diagram": "fmea",
1445
+ "title": "EV battery pack DFMEA",
1446
+ "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.",
1447
+ "standard": "AIAG-VDA FMEA Handbook (2019) / IEC 60812",
1448
+ "tags": [
1449
+ "fmea",
1450
+ "dfmea",
1451
+ "rpn",
1452
+ "action-priority",
1453
+ "severity",
1454
+ "automotive"
1455
+ ],
1456
+ "complexity": 3,
1457
+ "featured": false,
1458
+ "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',
1459
+ "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."
1460
+ },
1461
+ {
1462
+ "slug": "fmea-injection-molding-pfmea",
1463
+ "diagram": "fmea",
1464
+ "title": "Injection-moulding PFMEA",
1465
+ "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.",
1466
+ "standard": "AIAG-VDA FMEA Handbook (2019) / IEC 60812",
1467
+ "tags": [
1468
+ "fmea",
1469
+ "pfmea",
1470
+ "rpn",
1471
+ "process",
1472
+ "manufacturing",
1473
+ "scrap"
1474
+ ],
1475
+ "complexity": 2,
1476
+ "featured": false,
1477
+ "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',
1478
+ "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."
1479
+ },
1263
1480
  {
1264
1481
  "slug": "genogram-brca-cancer",
1265
1482
  "diagram": "genogram",
@@ -1349,6 +1566,44 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1349
1566
  "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
1567
  "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
1568
  },
1569
+ {
1570
+ "slug": "gitgraph-release-flow",
1571
+ "diagram": "gitgraph",
1572
+ "title": "Git Flow release history",
1573
+ "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.",
1574
+ "standard": "Mermaid gitGraph dialect",
1575
+ "tags": [
1576
+ "gitgraph",
1577
+ "git-flow",
1578
+ "branch",
1579
+ "merge",
1580
+ "tag",
1581
+ "hotfix"
1582
+ ],
1583
+ "complexity": 3,
1584
+ "featured": false,
1585
+ "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',
1586
+ "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."
1587
+ },
1588
+ {
1589
+ "slug": "idef0-order-fulfilment",
1590
+ "diagram": "idef0",
1591
+ "title": "IDEF0 A0 \u2014 fulfil customer order",
1592
+ "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.",
1593
+ "standard": "FIPS PUB 183 (IDEF0)",
1594
+ "tags": [
1595
+ "idef0",
1596
+ "icom",
1597
+ "function-model",
1598
+ "decomposition",
1599
+ "fips-183",
1600
+ "structured-analysis"
1601
+ ],
1602
+ "complexity": 3,
1603
+ "featured": false,
1604
+ "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"',
1605
+ "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.'
1606
+ },
1352
1607
  {
1353
1608
  "slug": "ladder-conveyor-interlock",
1354
1609
  "diagram": "ladder",
@@ -1459,6 +1714,58 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
1459
1714
  "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
1715
  "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
1716
  },
1717
+ {
1718
+ "slug": "markov-customer-lifecycle",
1719
+ "diagram": "markov",
1720
+ "title": "Customer lifecycle (ergodic chain, stationary \u03C0)",
1721
+ "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.",
1722
+ "standard": "Kemeny & Snell, Finite Markov Chains",
1723
+ "tags": [
1724
+ "markov",
1725
+ "stationary",
1726
+ "ergodic",
1727
+ "lifecycle",
1728
+ "churn",
1729
+ "steady-state"
1730
+ ],
1731
+ "complexity": 2,
1732
+ "featured": false,
1733
+ "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',
1734
+ "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."
1735
+ },
1736
+ {
1737
+ "slug": "markov-gamblers-ruin",
1738
+ "diagram": "markov",
1739
+ "title": "Gambler's ruin (absorbing chain)",
1740
+ "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.",
1741
+ "standard": "Kemeny & Snell, Finite Markov Chains",
1742
+ "tags": [
1743
+ "markov",
1744
+ "absorbing",
1745
+ "fundamental-matrix",
1746
+ "gamblers-ruin",
1747
+ "transient",
1748
+ "expected-steps"
1749
+ ],
1750
+ "complexity": 3,
1751
+ "featured": false,
1752
+ "dsl": `markov "Gambler's ruin"
1753
+ analysis: classify, absorbing
1754
+ state Broke "$0" absorbing
1755
+ state Rich "$4" absorbing
1756
+ state S1
1757
+ state S2
1758
+ state S3
1759
+ Broke -> Broke : 1
1760
+ Rich -> Rich : 1
1761
+ S1 -> Broke : 0.5
1762
+ S1 -> S2 : 0.5
1763
+ S2 -> S1 : 0.5
1764
+ S2 -> S3 : 0.5
1765
+ S3 -> S2 : 0.5
1766
+ S3 -> Rich : 0.5`,
1767
+ "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."
1768
+ },
1462
1769
  {
1463
1770
  "slug": "matrix-9-box-talent",
1464
1771
  "diagram": "matrix",
@@ -2711,6 +3018,25 @@ If the LED doesn't light up, three things to check, in order: LED polarity (the
2711
3018
  "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
3019
  "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
3020
  },
3021
+ {
3022
+ "slug": "threatmodel-ecommerce-checkout",
3023
+ "diagram": "threatmodel",
3024
+ "title": "E-commerce checkout threat model (STRIDE)",
3025
+ "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.",
3026
+ "standard": "Microsoft STRIDE / Shostack (2014)",
3027
+ "tags": [
3028
+ "threatmodel",
3029
+ "stride",
3030
+ "dfd",
3031
+ "trust-boundary",
3032
+ "security",
3033
+ "data-flow"
3034
+ ],
3035
+ "complexity": 3,
3036
+ "featured": false,
3037
+ "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 }',
3038
+ "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-*`."
3039
+ },
2714
3040
  {
2715
3041
  "slug": "timeline-company-milestones",
2716
3042
  "diagram": "timeline",
@@ -3138,6 +3464,38 @@ var SYNTAX = {
3138
3464
  "bowtie": {
3139
3465
  "title": "Bowtie Risk Diagram",
3140
3466
  "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.'
3467
+ },
3468
+ "eventtree": {
3469
+ "title": "Event Tree Analysis",
3470
+ "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---'
3471
+ },
3472
+ "fmea": {
3473
+ "title": "FMEA Worksheet",
3474
+ "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---'
3475
+ },
3476
+ "causalloop": {
3477
+ "title": "Causal Loop Diagram",
3478
+ "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---'
3479
+ },
3480
+ "markov": {
3481
+ "title": "Markov Chain",
3482
+ "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---'
3483
+ },
3484
+ "gitgraph": {
3485
+ "title": "Git Graph",
3486
+ "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---'
3487
+ },
3488
+ "epc": {
3489
+ "title": "EPC (Event-driven Process Chain)",
3490
+ "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---'
3491
+ },
3492
+ "idef0": {
3493
+ "title": "IDEF0 Function Model",
3494
+ "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---'
3495
+ },
3496
+ "threatmodel": {
3497
+ "title": "Threat Model (STRIDE DFD)",
3498
+ "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---'
3141
3499
  }
3142
3500
  };
3143
3501
 
@@ -3703,6 +4061,224 @@ var PROFILES = {
3703
4061
  "'needs at least one threat/consequence' \u2014 a one-wing diagram is a fault tree or event tree; add the missing wing.",
3704
4062
  "'exactly one top event' \u2014 keep a single `topevent` line."
3705
4063
  ]
4064
+ },
4065
+ eventtree: {
4066
+ type: "eventtree",
4067
+ header: 'eventtree "Title"',
4068
+ mode: "initiating event + ordered safety functions, then outcome rows with s/f/* branch patterns",
4069
+ forms: [
4070
+ 'initiating LOCA "Large LOCA" freq: 1e-4',
4071
+ 'function A "ECCS injects" p: 0.001',
4072
+ 'function B "Containment spray" p: 0.01',
4073
+ 'outcome s s -> "OK"',
4074
+ 'outcome s f -> "Late release"',
4075
+ 'outcome f * -> "Core damage"'
4076
+ ],
4077
+ prefer: [
4078
+ "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).",
4079
+ '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.',
4080
+ "Let the engine compute path frequencies (f_initiating \xD7 \u03A0 branch-probs) \u2014 don't hand-write them."
4081
+ ],
4082
+ avoid: [
4083
+ "Don't try to make a balanced 2\u207F tree \u2014 prune with `*` where a sequence ends early.",
4084
+ "Don't put probabilities on outcome rows; `p:` belongs on `function` lines.",
4085
+ "Don't reverse the success/failure convention \u2014 success branches up, failure down."
4086
+ ],
4087
+ repair: [
4088
+ "'outcome pattern length' \u2014 give exactly one s/f/* per declared function.",
4089
+ "'no initiating event' \u2014 add one `initiating <id> \"\u2026\" freq: \u2026` line.",
4090
+ "'once pruned stays pruned' \u2014 after a `*` in a pattern, the rest must also be `*`."
4091
+ ]
4092
+ },
4093
+ fmea: {
4094
+ type: "fmea",
4095
+ header: 'fmea "Title"',
4096
+ mode: "indentation-structured worksheet: item \u2192 failure mode \u2192 effect/cause/controls with S/O/D scores",
4097
+ forms: [
4098
+ 'fmea "Brake System DFMEA"',
4099
+ ' item "Master cylinder" fn "Generate pressure"',
4100
+ ' mode "Internal seal leak"',
4101
+ ' effect "Loss of braking" sev: 9',
4102
+ ' cause "Seal degradation" occ: 3',
4103
+ ' controls detection: "Bench test" det: 4'
4104
+ ],
4105
+ prefer: [
4106
+ "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.",
4107
+ "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.",
4108
+ "Optional `rank: ap|rpn` and `flag: ap >= High` / `flag: rpn > 100` to control sorting/highlighting."
4109
+ ],
4110
+ avoid: [
4111
+ "Don't write the RPN or AP yourself \u2014 they are computed.",
4112
+ "Don't use scores outside 1\u201310.",
4113
+ "Don't put `occ:` on an effect or `sev:` on a cause \u2014 they bind to the wrong column."
4114
+ ],
4115
+ repair: [
4116
+ "'score out of range' \u2014 keep S/O/D in 1\u201310.",
4117
+ "'mode has no effect/cause' \u2014 every failure mode needs at least one effect and one cause."
4118
+ ]
4119
+ },
4120
+ causalloop: {
4121
+ type: "causalloop",
4122
+ header: 'causalloop "Title"',
4123
+ mode: "signed causal links between variables; engine detects R/B loops",
4124
+ forms: [
4125
+ 'causalloop "Adoption model"',
4126
+ '"Adoption rate" -> Adopters : +',
4127
+ 'Adopters -> "Adoption rate" : +',
4128
+ '"Adoption rate" -> "Potential adopters" : -',
4129
+ 'loop R1 "Word of mouth"',
4130
+ 'loop B1 "Market saturation"'
4131
+ ],
4132
+ prefer: [
4133
+ "Keyword `causalloop` (or `cld`). Write `A -> B : +` or `A -> B : -` (you may use `s`/`o` aliases). Multi-word variables go in quotes.",
4134
+ 'Let the engine classify loops R (reinforcing) / B (balancing) by counting negative links; optionally name them with `loop R1 "\u2026"`.',
4135
+ "Put the `+`/`\u2212` polarity at the end of each link."
4136
+ ],
4137
+ avoid: [
4138
+ "Don't draw boxes around variables \u2014 CLD variables are boxless text.",
4139
+ "Don't omit polarity \u2014 every causal link is signed."
4140
+ ],
4141
+ repair: [
4142
+ "'link needs a polarity' \u2014 append `: +` or `: -`.",
4143
+ "'unknown variable' \u2014 variables are created on first use; check quoting/spelling."
4144
+ ]
4145
+ },
4146
+ markov: {
4147
+ type: "markov",
4148
+ header: 'markov "Title"',
4149
+ mode: "probability-labelled state transitions; engine computes stationary distribution + classification",
4150
+ forms: [
4151
+ 'markov "Weather"',
4152
+ "Sunny -> Sunny : 0.9",
4153
+ "Sunny -> Rainy : 0.1",
4154
+ "Rainy -> Sunny : 0.5",
4155
+ "Rainy -> Rainy : 0.5"
4156
+ ],
4157
+ prefer: [
4158
+ "Keyword `markov` (or `markovchain`). Write `S1 -> S2 : 0.3` transitions; each state's outgoing probabilities must sum to 1.",
4159
+ "Mark absorbing states by a 1.0 self-loop (or the `absorbing` keyword); the engine computes the stationary distribution and classifies states.",
4160
+ "For an absorbing chain add `analysis: classify, absorbing` to get absorption probabilities + expected steps."
4161
+ ],
4162
+ avoid: [
4163
+ "Don't let a state's out-probabilities sum to \u2260 1 (the engine errors); use `normalize: true` only if you intend rescaling.",
4164
+ "Don't hand-compute the stationary distribution \u2014 it is derived."
4165
+ ],
4166
+ repair: [
4167
+ "'row does not sum to 1' \u2014 fix the probabilities or add `normalize: true`.",
4168
+ "'probability out of range' \u2014 keep each on (0,1]."
4169
+ ]
4170
+ },
4171
+ gitgraph: {
4172
+ type: "gitgraph",
4173
+ header: "gitGraph",
4174
+ mode: "Mermaid-compatible ordered git operations (commit/branch/checkout/merge)",
4175
+ forms: [
4176
+ "gitGraph",
4177
+ ' commit id: "init"',
4178
+ " branch develop",
4179
+ " checkout develop",
4180
+ ' commit tag: "v0.1"',
4181
+ " checkout main",
4182
+ " merge develop"
4183
+ ],
4184
+ prefer: [
4185
+ 'Header `gitGraph` (Mermaid syntax). Operations in order: `commit`, `branch <name>`, `checkout <name>`, `merge <name>`, `cherry-pick id: "\u2026"`.',
4186
+ 'Annotate commits with `id: "\u2026"`, `tag: "\u2026"`, `type: HIGHLIGHT|REVERSE|NORMAL`.',
4187
+ "`checkout` (or `switch`) before committing onto a branch; `merge <name>` merges that branch into the current one."
4188
+ ],
4189
+ avoid: [
4190
+ "Don't `merge` or `checkout` a branch you never created with `branch`.",
4191
+ "Don't merge a branch into itself."
4192
+ ],
4193
+ repair: [
4194
+ "'unknown branch' \u2014 add a `branch <name>` before `checkout`/`merge`.",
4195
+ "'cannot merge current branch' \u2014 checkout the target lane first."
4196
+ ]
4197
+ },
4198
+ epc: {
4199
+ type: "epc",
4200
+ header: 'epc "Title"',
4201
+ mode: "alternating events/functions joined by AND/OR/XOR connectors; alternation validated",
4202
+ forms: [
4203
+ 'epc "Order fulfilment"',
4204
+ ' event E1 "Order received"',
4205
+ ' function F1 "Check credit"',
4206
+ " xor X1",
4207
+ ' event E2 "Credit OK"',
4208
+ ' event E3 "Credit rejected"',
4209
+ " E1 -> F1 -> X1",
4210
+ " X1 -> E2",
4211
+ " X1 -> E3"
4212
+ ],
4213
+ prefer: [
4214
+ "Keyword `epc`. Declare `event`, `function`, and connectors `and`/`or`/`xor` with ids; then the control flow `A -> B -> C`.",
4215
+ "Strictly alternate events and functions (a connector may sit between them); start and end with events.",
4216
+ "Use a connector node to split/join \u2014 e.g. a `xor` after a function fans out to alternative events."
4217
+ ],
4218
+ avoid: [
4219
+ "Don't make an event the source of an OR/XOR split (only functions may decide) \u2014 the engine rejects it.",
4220
+ "Don't connect event\u2192event or function\u2192function directly."
4221
+ ],
4222
+ repair: [
4223
+ "'event cannot be an OR/XOR split source' \u2014 put a function (decision) before the connector.",
4224
+ "'alternation violated' \u2014 insert the missing event/function between two same-kind nodes."
4225
+ ]
4226
+ },
4227
+ idef0: {
4228
+ type: "idef0",
4229
+ header: 'idef0 "Title"',
4230
+ mode: "function boxes + positional ICOM arrows (input-left, control-top, output-right, mechanism-bottom)",
4231
+ forms: [
4232
+ 'idef0 "Manufacture product"',
4233
+ ' function A1 "Plan production"',
4234
+ ' function A2 "Make parts"',
4235
+ ' input A1 "Sales orders"',
4236
+ ' control A1 "Production schedule"',
4237
+ ' A1 -> A2 "Work plan"',
4238
+ ' mechanism A2 "CNC machines"',
4239
+ ' output A2 "Product"'
4240
+ ],
4241
+ prefer: [
4242
+ "Keyword `idef0`. Declare `function` boxes; attach boundary arrows with `input`/`control`/`output`/`mechanism`.",
4243
+ 'Box\u2192box flow `A1 -> A2 "label"` lands on A2\'s input by default; use `A1 -> A2.control "\u2026"` to land on another ICOM side.',
4244
+ "Remember the ICOM rule: Inputs enter left, Controls enter top, Outputs exit right, Mechanisms enter bottom \u2014 the engine enforces it."
4245
+ ],
4246
+ avoid: [
4247
+ "Don't route an output anywhere but the right edge, or a control anywhere but the top \u2014 the engine rejects it.",
4248
+ "Don't hand-number the boxes \u2014 node numbers are assigned."
4249
+ ],
4250
+ repair: [
4251
+ "'output must exit the right edge' \u2014 declare it with `output` / land flows on the correct side.",
4252
+ "'unknown box' \u2014 declare the `function` before referencing it."
4253
+ ]
4254
+ },
4255
+ threatmodel: {
4256
+ type: "threatmodel",
4257
+ header: 'threatmodel "Title"',
4258
+ mode: "DFD elements + data flows + trust boundaries; engine maps STRIDE + flags boundary crossings",
4259
+ forms: [
4260
+ 'threatmodel "Web App \u2014 STRIDE"',
4261
+ "external: User",
4262
+ "process 1.1: Web Server",
4263
+ "datastore D1: User DB",
4264
+ "User -> 1.1 : HTTPS Request",
4265
+ "1.1 -> D1 : Lookup",
4266
+ 'boundary "Internet" { User }',
4267
+ 'boundary "DMZ" { 1.1 }'
4268
+ ],
4269
+ prefer: [
4270
+ 'Keyword `threatmodel` (or `stride`). Declare `external:`, `process N:`, `datastore D:`; connect with `A -> B : "label"` flows; group ids into `boundary "name" { \u2026 }` trust zones.',
4271
+ "Let the engine annotate each element's STRIDE letters and flag flows that cross a trust boundary \u2014 don't list threats by hand.",
4272
+ "Name audit/log stores with `log`/`audit`/`journal` so they pick up the conditional Repudiation threat."
4273
+ ],
4274
+ avoid: [
4275
+ "Don't connect store\u2192store or external\u2192external (not valid DFD edges).",
4276
+ "Don't leave data flows unlabelled."
4277
+ ],
4278
+ repair: [
4279
+ "'unknown element' \u2014 declare the external/process/datastore before referencing it.",
4280
+ "'flow needs a label' \u2014 add `: \"\u2026\"` to the flow."
4281
+ ]
3706
4282
  }
3707
4283
  };
3708
4284
  function getGenerationProfile(type) {
@@ -3786,7 +4362,7 @@ function getExamples(type, opts = {}) {
3786
4362
  function validateDsl(type, dsl) {
3787
4363
  const resolvedType = type ? resolveDiagramType(type) : void 0;
3788
4364
  const config = type ? { type: resolvedType ?? type } : void 0;
3789
- const result = chunkHFATQXFN_cjs.parseResult(dsl, config);
4365
+ const result = chunk3CSST5GZ_cjs.parseResult(dsl, config);
3790
4366
  if (result.ok) {
3791
4367
  return {
3792
4368
  ok: true,
@@ -3812,7 +4388,7 @@ function renderDsl(type, dsl, options = {}) {
3812
4388
  ...options,
3813
4389
  ...type ? { type: resolvedType ?? type } : {}
3814
4390
  };
3815
- const result = chunkHFATQXFN_cjs.renderResult(dsl, config);
4391
+ const result = chunk3CSST5GZ_cjs.renderResult(dsl, config);
3816
4392
  if (result.ok) {
3817
4393
  return {
3818
4394
  ok: true,
@@ -3865,5 +4441,5 @@ exports.listDiagrams = listDiagrams;
3865
4441
  exports.renderDsl = renderDsl;
3866
4442
  exports.resolveDiagramType = resolveDiagramType;
3867
4443
  exports.validateDsl = validateDsl;
3868
- //# sourceMappingURL=chunk-CTMJ3XP2.cjs.map
3869
- //# sourceMappingURL=chunk-CTMJ3XP2.cjs.map
4444
+ //# sourceMappingURL=chunk-RKN6QJ7K.cjs.map
4445
+ //# sourceMappingURL=chunk-RKN6QJ7K.cjs.map