agentic-mermaid 0.5.1 → 0.5.3

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 (155) hide show
  1. package/AGENT_NATIVE.md +63 -375
  2. package/CHANGELOG.md +39 -0
  3. package/README.md +87 -50
  4. package/assets/hero.png +0 -0
  5. package/dist/agent-core.d.ts +7 -7
  6. package/dist/agent-core.js +1 -1
  7. package/dist/agent.d.ts +7 -7
  8. package/dist/agent.js +1 -1
  9. package/dist/agentic-mermaid-mcp.js +1 -1
  10. package/dist/am.js +1 -1
  11. package/dist/browser-lazy/chunks/agentic-producer-core-CC6OOLDW.js +1 -0
  12. package/dist/browser-lazy/chunks/architecture-QUFVKYJR.js +13 -0
  13. package/dist/browser-lazy/chunks/block-6J3Q63BO.js +11 -0
  14. package/dist/browser-lazy/chunks/builtin-backends-7SOZZHPF.js +12 -0
  15. package/dist/browser-lazy/chunks/c4-SQVBZAKJ.js +10 -0
  16. package/dist/browser-lazy/chunks/{chunk-MSP3S2VS.js → chunk-2GPK4VFF.js} +1 -1
  17. package/dist/browser-lazy/chunks/chunk-2SH76OTH.js +1 -0
  18. package/dist/browser-lazy/chunks/chunk-2UIA2IME.js +3 -0
  19. package/dist/browser-lazy/chunks/chunk-4LHYF3BS.js +1 -0
  20. package/dist/browser-lazy/chunks/{chunk-HR5TE7TE.js → chunk-5B2Z7575.js} +1 -1
  21. package/dist/browser-lazy/chunks/{chunk-REBVEXI2.js → chunk-5X6MJX3H.js} +1 -1
  22. package/dist/browser-lazy/chunks/chunk-AMJLVPUB.js +4 -0
  23. package/dist/browser-lazy/chunks/chunk-BU5LHXNJ.js +3 -0
  24. package/dist/browser-lazy/chunks/chunk-BY2CPG6B.js +1 -0
  25. package/dist/browser-lazy/chunks/{chunk-HDKHAM2V.js → chunk-CN7WTBWB.js} +1 -1
  26. package/dist/browser-lazy/chunks/chunk-CQE4HQEW.js +1 -0
  27. package/dist/browser-lazy/chunks/{chunk-KXPF5KIU.js → chunk-DF6765YX.js} +3 -3
  28. package/dist/browser-lazy/chunks/chunk-E2WMJZNM.js +1 -0
  29. package/dist/browser-lazy/chunks/chunk-FAIMVOX5.js +6 -0
  30. package/dist/browser-lazy/chunks/chunk-GVOEAY52.js +1 -0
  31. package/dist/browser-lazy/chunks/chunk-HLWUNUCM.js +1 -0
  32. package/dist/browser-lazy/chunks/chunk-ICC7HBJX.js +1 -0
  33. package/dist/browser-lazy/chunks/{chunk-FBL4ONAL.js → chunk-J35HAQQF.js} +15 -15
  34. package/dist/browser-lazy/chunks/chunk-J7FFAFQF.js +1 -0
  35. package/dist/browser-lazy/chunks/chunk-NO7ZIVRU.js +1 -0
  36. package/dist/browser-lazy/chunks/chunk-OZ443IVJ.js +16 -0
  37. package/dist/browser-lazy/chunks/{chunk-2J5K6NIP.js → chunk-PQGPS2YV.js} +1 -1
  38. package/dist/browser-lazy/chunks/{chunk-SBXYR7Z3.js → chunk-QA6GJQ4X.js} +1 -1
  39. package/dist/browser-lazy/chunks/chunk-RIVTHZAQ.js +1 -0
  40. package/dist/browser-lazy/chunks/chunk-SZELX2EJ.js +1 -0
  41. package/dist/browser-lazy/chunks/chunk-TYZOZXUJ.js +22 -0
  42. package/dist/browser-lazy/chunks/{chunk-E7UIB4SZ.js → chunk-UL6NDWLL.js} +1 -1
  43. package/dist/browser-lazy/chunks/chunk-VQYYGAXP.js +1 -0
  44. package/dist/browser-lazy/chunks/chunk-VWVZGQE2.js +1 -0
  45. package/dist/browser-lazy/chunks/{chunk-GMTB5UTY.js → chunk-X2UYKWKM.js} +3 -3
  46. package/dist/browser-lazy/chunks/{chunk-LLPBKBIG.js → chunk-YCEICNM6.js} +1 -1
  47. package/dist/browser-lazy/chunks/chunk-Z3FDIVLN.js +20 -0
  48. package/dist/browser-lazy/chunks/class-5QA4SCTS.js +13 -0
  49. package/dist/browser-lazy/chunks/er-7ZSKGN7Y.js +11 -0
  50. package/dist/browser-lazy/chunks/eventmodeling-2BPLHWRN.js +19 -0
  51. package/dist/browser-lazy/chunks/flowchart-NEIMZTB5.js +4 -0
  52. package/dist/browser-lazy/chunks/gantt-RDPIFYAF.js +11 -0
  53. package/dist/browser-lazy/chunks/{gitgraph-W26QIMTY.js → gitgraph-N4Y5RYB7.js} +4 -4
  54. package/dist/browser-lazy/chunks/ishikawa-LD3YBDJN.js +6 -0
  55. package/dist/browser-lazy/chunks/journey-6YQR5DJX.js +16 -0
  56. package/dist/browser-lazy/chunks/kanban-PLMJ7NBW.js +20 -0
  57. package/dist/browser-lazy/chunks/{mindmap-5A3MVHNH.js → mindmap-K2UBFO3Y.js} +6 -6
  58. package/dist/browser-lazy/chunks/{packet-AL6VDOMY.js → packet-CUW4QY6Y.js} +5 -5
  59. package/dist/browser-lazy/chunks/pie-7F7UUCG3.js +15 -0
  60. package/dist/browser-lazy/chunks/{quadrant-RJ56XFBR.js → quadrant-TOONO2WC.js} +5 -5
  61. package/dist/browser-lazy/chunks/radar-J6JVWVAX.js +16 -0
  62. package/dist/browser-lazy/chunks/render-core-BZOM3K7D.js +3 -0
  63. package/dist/browser-lazy/chunks/requirement-CMAKDJPO.js +19 -0
  64. package/dist/browser-lazy/chunks/{sankey-WKTBEZSS.js → sankey-AWXQEP3F.js} +8 -8
  65. package/dist/browser-lazy/chunks/sequence-Q2RJRB4L.js +5 -0
  66. package/dist/browser-lazy/chunks/state-WRBFQEDX.js +4 -0
  67. package/dist/browser-lazy/chunks/{timeline-PRV5PWGP.js → timeline-D2KILFXM.js} +4 -4
  68. package/dist/browser-lazy/chunks/{treemap-BVME3JLI.js → treemap-N3VYX5D4.js} +4 -4
  69. package/dist/browser-lazy/chunks/{xychart-XBI2RNHD.js → xychart-G73OGK66.js} +5 -5
  70. package/dist/browser-lazy/chunks/{zenuml-GQKPRQFJ.js → zenuml-H4BXNYXR.js} +8 -8
  71. package/dist/browser-lazy/index.d.ts +10 -0
  72. package/dist/browser-lazy/index.js +1 -1
  73. package/dist/browser.global.js +539 -539
  74. package/dist/chunk-7CRDX2QR.js +2 -0
  75. package/dist/{chunk-AVS4T4NO.js → chunk-DQDKA5ND.js} +2 -2
  76. package/dist/chunk-EHCMJUMD.js +52 -0
  77. package/dist/{chunk-AWZBTOV6.js → chunk-GVCHLSHH.js} +2 -2
  78. package/dist/{chunk-6JBQYHWB.js → chunk-H3NCS2XZ.js} +2 -2
  79. package/dist/{chunk-ORYXYGDV.js → chunk-JZOJ4YGS.js} +2 -2
  80. package/dist/{chunk-AIWXP4OF.js → chunk-NKNFOREZ.js} +2 -2
  81. package/dist/{chunk-PNL435CH.js → chunk-OMY6NRQD.js} +17 -17
  82. package/dist/{chunk-YISEN5SO.js → chunk-UA234O4M.js} +2 -2
  83. package/dist/{cli-BOEN5YAF.js → cli-YGGTJZH7.js} +2 -2
  84. package/dist/{index-Cr3xjRw9.d.ts → index-EeXqJXr5.d.ts} +3 -3
  85. package/dist/index.d.ts +4 -4
  86. package/dist/index.js +1 -1
  87. package/dist/{mcp-cli-NM3P4VPC.js → mcp-cli-63DYXTVD.js} +2 -2
  88. package/dist/{paint-certification-C33LRvWT.d.ts → paint-certification-6rwQjIv_.d.ts} +22 -2
  89. package/dist/{server-FY5Z7F32.js → server-5NEOHOFS.js} +2 -2
  90. package/dist/{svg-styles-C_v2tnxG.d.ts → svg-styles-BdnoM7AT.d.ts} +1 -1
  91. package/dist/svg-styles.d.ts +1 -1
  92. package/dist/svg-styles.js +1 -1
  93. package/dist/svg.d.ts +4 -4
  94. package/dist/svg.js +1 -1
  95. package/dist/{types-DxOTIgwg.d.ts → types-BcS52Mjt.d.ts} +11 -1
  96. package/dist/zenuml/chunks/{appearance-FTQ2722O.js → appearance-UOQ5KA6O.js} +1 -1
  97. package/dist/zenuml/chunks/{builtin-backends-QLY75JCL.js → builtin-backends-KDKE7YHO.js} +3 -3
  98. package/dist/zenuml/chunks/chunk-42DXZ5C6.js +4 -0
  99. package/dist/zenuml/chunks/{chunk-GDLMM3YO.js → chunk-7TVYPTDW.js} +1 -1
  100. package/dist/zenuml/chunks/{chunk-6OONHTSD.js → chunk-OXQWRSBM.js} +10 -10
  101. package/dist/zenuml/chunks/{chunk-LYIRUTCM.js → chunk-QWX77U7G.js} +1 -1
  102. package/dist/zenuml/chunks/{chunk-4VRUBLG4.js → chunk-RLJEI7YN.js} +10 -10
  103. package/dist/zenuml/chunks/{chunk-YFCDP4ZS.js → chunk-RZ6XK5CD.js} +1 -1
  104. package/dist/zenuml/chunks/{render-core-DNCYYOIB.js → render-core-V5RQ737O.js} +3 -3
  105. package/dist/zenuml/chunks/{zenuml-URQ4HIMD.js → zenuml-EA2ZFRUX.js} +9 -9
  106. package/dist/zenuml.d.ts +2 -2
  107. package/dist/zenuml.js +1 -1
  108. package/docs/MCP-DIRECTORY-LISTINGS.md +1 -1
  109. package/docs/README.md +59 -110
  110. package/docs/api.md +3 -1
  111. package/docs/config.md +37 -0
  112. package/docs/getting-started.md +27 -44
  113. package/examples/styles/readme-hero.md +25 -0
  114. package/examples/styles/readme-hero.mmd +13 -0
  115. package/examples/styles/readme-hero.style.json +7 -0
  116. package/package.json +1 -1
  117. package/server.json +2 -2
  118. package/dist/browser-lazy/chunks/agentic-producer-core-MGU4FD6J.js +0 -1
  119. package/dist/browser-lazy/chunks/architecture-KZTC3672.js +0 -13
  120. package/dist/browser-lazy/chunks/block-J5PD6HQM.js +0 -11
  121. package/dist/browser-lazy/chunks/builtin-backends-GSGJPT5A.js +0 -12
  122. package/dist/browser-lazy/chunks/c4-VNQSIA5M.js +0 -10
  123. package/dist/browser-lazy/chunks/chunk-4C2BVM5Y.js +0 -1
  124. package/dist/browser-lazy/chunks/chunk-5GE2SFLQ.js +0 -4
  125. package/dist/browser-lazy/chunks/chunk-6YGQR6R4.js +0 -16
  126. package/dist/browser-lazy/chunks/chunk-7AHX3HH7.js +0 -1
  127. package/dist/browser-lazy/chunks/chunk-B5PDJUJG.js +0 -3
  128. package/dist/browser-lazy/chunks/chunk-CXQWSLJV.js +0 -1
  129. package/dist/browser-lazy/chunks/chunk-DWDYWUAS.js +0 -1
  130. package/dist/browser-lazy/chunks/chunk-EUBVLDYV.js +0 -29
  131. package/dist/browser-lazy/chunks/chunk-NQF75XGM.js +0 -3
  132. package/dist/browser-lazy/chunks/chunk-P7DO3MRP.js +0 -13
  133. package/dist/browser-lazy/chunks/chunk-QQ2U2KAY.js +0 -1
  134. package/dist/browser-lazy/chunks/chunk-SJYDRC6G.js +0 -1
  135. package/dist/browser-lazy/chunks/chunk-UUTB6MDK.js +0 -1
  136. package/dist/browser-lazy/chunks/chunk-WEREIH5P.js +0 -1
  137. package/dist/browser-lazy/chunks/chunk-WHNTP27R.js +0 -6
  138. package/dist/browser-lazy/chunks/chunk-YXGB25MZ.js +0 -1
  139. package/dist/browser-lazy/chunks/class-FNDNJGYH.js +0 -13
  140. package/dist/browser-lazy/chunks/er-P263BDFG.js +0 -11
  141. package/dist/browser-lazy/chunks/eventmodeling-XX3VCGSC.js +0 -19
  142. package/dist/browser-lazy/chunks/flowchart-GTLWJA3P.js +0 -4
  143. package/dist/browser-lazy/chunks/gantt-SRIHFAIC.js +0 -11
  144. package/dist/browser-lazy/chunks/ishikawa-NTHJ7BUO.js +0 -6
  145. package/dist/browser-lazy/chunks/journey-3T62IEHN.js +0 -16
  146. package/dist/browser-lazy/chunks/kanban-U6DQ7LWQ.js +0 -20
  147. package/dist/browser-lazy/chunks/pie-4QYMTQ7W.js +0 -15
  148. package/dist/browser-lazy/chunks/radar-L5B4CNEL.js +0 -16
  149. package/dist/browser-lazy/chunks/render-core-CIGKBI2T.js +0 -3
  150. package/dist/browser-lazy/chunks/requirement-5QVLRI6Z.js +0 -19
  151. package/dist/browser-lazy/chunks/sequence-G6DQHPR7.js +0 -5
  152. package/dist/browser-lazy/chunks/state-6DEA5BXE.js +0 -4
  153. package/dist/chunk-XCEEF3NJ.js +0 -2
  154. package/dist/chunk-YQKNJNU4.js +0 -52
  155. package/dist/zenuml/chunks/chunk-7QDAZ2HL.js +0 -4
package/AGENT_NATIVE.md CHANGED
@@ -1,100 +1,49 @@
1
- # Agentic Mermaid — agent-native architecture
1
+ # Agent architecture
2
2
 
3
- **Status.** Architecture/spec rationale for the merged agent-native surface on `main`. Current capabilities live in [`docs/features.md`](./docs/features.md); active work lives only in [`TODO.md`](./TODO.md). Not intended for upstream.
3
+ Agentic Mermaid provides typed operations for editing Mermaid source while
4
+ preserving the parts an operation does not own. For runnable examples, use the
5
+ [editing cookbook](docs/agent-api-cookbook.md). Agents should start with
6
+ [Instructions_for_agents.md](Instructions_for_agents.md); contributors should
7
+ read the [renderer architecture](https://github.com/adewale/agentic-mermaid/blob/main/docs/design/system/README.md).
4
8
 
5
- **System architecture overview.** For the how-the-engine-works entry point — the rendered three-stacks diagram and the routing/layout design docs — start at [`docs/design/system/README.md`](./docs/design/system/README.md).
9
+ ## The edit workflow
6
10
 
7
- **Vocabulary.** Use the [library definitions](./docs/api.md#vocabulary) for
8
- family, source object, Scene part and resolved primitive. Narrowing selects
9
- the typed family; it does not select one object or keep other objects' positions
10
- fixed. Existing `SceneNode` and `*Mark` type names describe Scene parts.
11
+ For an existing diagram, parse it with `parseRegisteredMermaid`, narrow it with
12
+ its `as*` helper, and apply the smallest supported `mutate` operation. Inspect
13
+ `verifyMermaid`, then save `serializeMermaid` output. A new diagram can be
14
+ written directly as Mermaid source before parsing and verification.
11
15
 
12
- **Thesis.** Agents authoring Mermaid diagrams today regenerate the whole source on every edit, or render to PNG and read it back with vision. The Beautiful Mermaid renderer foundation already fixed the worst of the rendering side (sync + DOM-free + ASCII). Agentic Mermaid adds the editing surface — structured verification, typed mutation, round-trippable IR — so an agent can edit one node and trust the result without ever opening an image.
16
+ A successful edit must also match the request. Verification does not check
17
+ whether an agent changed the intended label, retained a requested value or
18
+ picked the right diagram family; read back those details before returning.
13
19
 
14
- ---
20
+ ## (1) Deterministic layout
15
21
 
16
- ## Why fork Beautiful Mermaid
22
+ Within the supported runtime and dependency configuration, the same input and
23
+ options produce the same layout. Cross-process and cross-runtime tests exercise
24
+ this behavior, and the geometry corpus detects unintended layout changes.
25
+ This is not a promise of identical output across dependency upgrades or every
26
+ host font environment.
17
27
 
18
- The stack is three layers, each contributing something the others can't:
19
-
20
- | Layer | Contribution |
21
- |---|---|
22
- | **Mermaid (grammar)** | 20+ diagram families. Rendered inline by GitHub, GitLab, Obsidian, Notion. Frontmatter + init + runtime config plane. `accTitle`/`accDescr` directives. **The corpus moat.** |
23
- | **Beautiful Mermaid (renderer foundation)** | Synchronous, zero DOM, pure TypeScript. ASCII output. Two-color theming. Semantic role styling. Multi-family layout/render coverage. Property + mutation + e2e test scaffold already in place. **The AI-era renderer Craft built for Craft Agents.** |
24
- | **Agentic Mermaid (this product/workflow)** | `ValidDiagram` IR. Deterministic layout. `verify()`. `mutate()` + round-trip `serializeMermaid`. Agent-agnostic skills, CLI, `--agent-instructions`. **The editing surface.** |
25
-
26
- D2 has a better language than Mermaid. The Beautiful Mermaid renderer foundation has a better fit than D2 for agent contexts. Mermaid has a corpus neither of them has. The bet: stacking the three wins.
27
-
28
- ---
29
-
30
- ## The three properties
31
-
32
- 1. **Deterministic layout** — same input → structurally identical layout JSON across runs with the same ELK version. (Not "byte-identical SVG across versions" — that's a stronger claim that needs a forked ELK to actually deliver.)
33
- 2. **Verifiable rendering** — structured "did this render cleanly?" check. Structural warnings (anchors, bounds, emptiness, group containment) are reliable; metric warnings (label fit, overlap) are best-effort because they depend on font-measurement parity with ELK.
34
- 3. **Round-trippable** — `parseRegisteredMermaid` produces a `ValidDiagram` that carries source needed to re-emit the diagram. For every family whose registry entry declares a narrower, `mutate` operates on structured bodies; for opaque-fallback bodies, preserved source is the round-trip mechanism.
35
-
36
- (Composition — `@include`, templates, layered scenarios — was the fourth in earlier drafts and is deferred. Agents do not currently reach for composition; they paste and edit. Add when evidence demands.)
37
-
38
- (1) is a prerequisite for (2) and (3). Skip it and the others check moving targets.
39
-
40
- ## Honest scope
41
-
42
- The checked family roster, capability states, edit policies, narrowers, and mutation operations are generated from `FamilyDescriptor`: read `am capabilities --json`, and use `describeOps(family)` for exact operation schemas. This rationale deliberately does not copy that live inventory.
43
-
44
- The generated edit policy decides where typed mutation is available. For an opaque fallback, the agent's tool surface is *parse → verify → render → serialize*, not *parse → mutate → verify → serialize*. Cross-cutting edits on those bodies happen at the preserved source level (`body.source` for opaque bodies), not at the typed mutation layer. Code Mode opportunity #1 covers this for the cases where it matters.
45
-
46
- ---
47
-
48
- ## (1) Deterministic layout — structural, not seeded
49
-
50
- **Empirically established (v4):** ELK output in this configuration is already deterministic, byte-identical across separate processes. The determinism comes from ELK's `considerModelOrder.strategy: NODES_AND_EDGES` setting plus the absence of any `randomSeed` option — layout order is a pure function of model order, not of randomness. Verified by a cross-process test (three separate `bun` invocations on the same source produce identical layout JSON) and a determinism grid.
51
-
52
- This is the honest, stronger position. Earlier drafts wrapped ELK in a `withSeededRandom(rng, fn)` helper and exposed a `LayoutContext.rng` seed, claiming the seed "drove" layout. **It did not.** Seed 1 and seed 999999 produced byte-identical output because ELK never consults `Math.random` on this path. That apparatus was theater and is removed. There is no layout seed because none is needed; determinism is a property of the engine configuration, guarded by test. (The render option `seed` that exists today is a *style* seed — it re-rolls the ink of styled looks and never touches layout.)
53
-
54
- Enforcement that determinism stays true:
55
- - A **grep-based lint test** (runs under `bun test`, not aspirational ESLint) fails if `Math.random`, `Date.now`, or `performance.now` appear in `src/agent/**`, `src/gantt/**`, `src/mindmap/**`, `src/gitgraph/**`, or `src/layout-engine.ts` (the Gantt scheduler must never read the wall clock; Mindmap/GitGraph ids and geometry must be deterministic). Introducing ambient nondeterminism breaks the build.
56
- - A **cross-process determinism test** spawns child processes and asserts byte-identical layout.
57
- - A **drift sentinel** (`src/__tests__/layout-equivalence.test.ts`) pins integer-rounded layout geometry for the mermaid-docs corpus plus the layout-compare fixtures in `src/__tests__/testdata/layout-geometry-baseline.json`; any intentional re-baseline must be reviewed, with each changed golden and why it changed listed in the PR description.
58
-
59
- The canonical artifact is the **layout JSON**, not the SVG:
60
-
61
- ```json
62
- {
63
- "version": 1,
64
- "kind": "flowchart",
65
- "nodes": [{ "id": "A", "x": 12, "y": 36, "w": 80, "h": 40, "shape": "rect" }, ...],
66
- "edges": [{ "id": "A->B", "from": "A", "to": "B", "path": [[..],[..]], "label": {...} }, ...],
67
- "groups": [{ "id": "g1", "x": 0, "y": 0, "w": 200, "h": 120, "members": ["A","B"] }, ...],
68
- "bounds": { "w": 320, "h": 180 }
69
- }
70
- ```
71
-
72
- There is no `seed` field (it was always `0` — a placeholder masquerading as state). SVG is the visual projection. Two render results are equal iff their layout JSON is structurally equal.
73
-
74
- ---
28
+ The `seed` option controls drawing variation in styles; it does not drive node
29
+ placement. Rendering, terminal layout and rasterization have separate output
30
+ contracts. See [quality](docs/quality.md) and [fonts](docs/custom-fonts.md).
75
31
 
76
32
  ## (2) Verifiable rendering
77
33
 
78
- ```ts
79
- verifyMermaid(source: string | ValidDiagram, opts?: VerifyOptions): VerifyResult
80
-
81
- interface VerifyOptions {
82
- suppress?: WarningCode[] // codes to omit, e.g. ['UNKNOWN_SHAPE']
83
- labelCharCap?: number // default cap 40
84
- }
85
-
86
- interface VerifyResult {
87
- ok: boolean
88
- warnings: LayoutWarning[]
89
- layout: RenderedLayout
90
- }
91
- ```
34
+ `verifyMermaid` reports structural diagnostics, measured geometry and advisory
35
+ warnings. Inspect warning severity and scope along with `ok`.
92
36
 
93
- Warnings split into three tiers by how reliable/actionable the underlying check is:
37
+ Rendering receipts separately report whether final painted output is `passed`,
38
+ `unverified` or failed. A successful render can still be unverified when a
39
+ backend lacks a final-paint proof or an ink intersection remains uncertain.
40
+ These statuses do not establish visual quality or task correctness.
94
41
 
95
- ### Tier 1 — Source-and-structure (reliable)
42
+ The warning tables below are generated from `src/agent/warning-catalog.ts`.
43
+ The [warning reference](https://agentic-mermaid.dev/warnings/) explains individual
44
+ codes, and [quality](docs/quality.md) describes layout priorities and limits.
96
45
 
97
- Derived from parsed structure or character-level source properties. Deterministic, no font measurement dependency.
46
+ ### Structural diagnostics
98
47
 
99
48
  <!-- BEGIN GENERATED: warning-table:structural -->
100
49
  | Code | Severity | Description |
@@ -110,9 +59,7 @@ Derived from parsed structure or character-level source properties. Deterministi
110
59
  | `INTERNAL_ERROR` | error | Agentic Mermaid hit an internal defect while drawing this source; it is not caused by the source. `defaultRender` says whether an ordinary render still produces output: `succeeds-unchecked` — it does, but the extra consistency checks verify runs failed, so its layout evidence cannot be trusted; `fails` — rendering throws. The payload's `reason` carries the internal error. Do not rewrite the diagram to work around it; report it with the source. |
111
60
  <!-- END GENERATED: warning-table:structural -->
112
61
 
113
- ### Tier 2 — Geometric
114
-
115
- Measured content occlusion is an error. Other geometric warnings can be intentional and remain advisory; inspect their severity and the rendered artifact before suppressing them.
62
+ ### Geometric diagnostics
116
63
 
117
64
  <!-- BEGIN GENERATED: warning-table:geometric -->
118
65
  | Code | Severity | Description |
@@ -130,11 +77,7 @@ Measured content occlusion is an error. Other geometric warnings can be intentio
130
77
  | `ROUTE_STALE_AFTER_NODE_MOVE` | warning | An endpoint detached from its node entirely, or another node sits on the edge's route (route-contract tripwire) |
131
78
  <!-- END GENERATED: warning-table:geometric -->
132
79
 
133
- Codes are the contract surface agents reason about. These tables are generated from `src/agent/warning-catalog.ts`, which must describe every `WarningCode` and nothing else or typecheck fails, so a code cannot ship undocumented. Agents omit known-irrelevant codes via `VerifyOptions.suppress`.
134
-
135
- ### Tier 3 — Lint (advisory)
136
-
137
- Tier 3 warnings are family-specific quality hints for "common LLM mistakes" that parse and render but are probably not what the agent intended. Advisory lint never flips `verify.ok`; an explicitly authored Brand constraint with `action: "error"` emits `BRAND_CONSTRAINT_ERROR` and does flip it.
80
+ ### Advisory lint
138
81
 
139
82
  <!-- BEGIN GENERATED: warning-table:lint -->
140
83
  | Code | Severity | Description |
@@ -157,294 +100,39 @@ Tier 3 warnings are family-specific quality hints for "common LLM mistakes" that
157
100
  | `BRAND_CONSTRAINT_ERROR` | error | The same inspect-only Brand constraint contract with `action: "error"`; it preserves authored output but flips `verify.ok` so a caller can enforce its declared brand policy. |
158
101
  <!-- END GENERATED: warning-table:lint -->
159
102
 
160
- `FamilyDescriptor.verify` hooks are wired and run today; built-ins use them for Tier 1 structural warnings for class/ER and the central flowchart verifier emits the initial Tier 3 lint catalogue. Future lint codes should be added deliberately to `WARNING_TIER` and described in `src/agent/warning-catalog.ts`; `bun run doc-blocks` regenerates the tables here.
161
-
162
- **Branded coordinate types** (`Finite`) prevent NaN / Infinity from reaching the renderer. `toFinite()` is the only constructor; it throws on invalid input.
163
-
164
- **Model-gap property test**: for every generated `D` that parses successfully, `verify(D).warnings` filtered to Tier-1 `error` codes must be empty. Counterexamples are renderer bugs. Tier-2 warnings are excluded from this property because a `NODE_OVERLAP` or `ROUTE_SELF_CROSS` can be a legitimate property of a valid diagram, not a bug. Tier-3 lint warnings are excluded because they are maintainability hints, not render failures.
165
-
166
- ---
167
-
168
- ## (3) Round-trippable
169
-
170
- A sealed `ValidDiagram` that preserves everything the source had — and carries the canonical source verbatim as the round-trip pillar:
171
-
172
- ```ts
173
- interface ValidDiagram {
174
- readonly kind: DiagramKind
175
- readonly meta: {
176
- frontmatter?: Frontmatter
177
- initDirectives: InitDirective[]
178
- comments: Comment[]
179
- accessibility: { title?: string, descr?: string }
180
- }
181
- readonly body: DiagramBody
182
- readonly source: SourceMap
183
- /**
184
- * The canonical preprocessed source — frontmatter, init directives, and
185
- * comments stripped; line breaks normalized. Structured renderers use this as
186
- * input. Opaque/source-level fidelity relies on `body.source`, not this field.
187
- */
188
- readonly canonicalSource: string
189
- }
190
-
191
- type DiagramBody =
192
- | { kind: 'flowchart'; graph: MermaidGraph }
193
- | SequenceBody | GanttBody | … // one structured body per registered family; the SDK declaration lists them
194
- | { kind: 'opaque'; family: DiagramKind; source: string }
195
-
196
- type MutableValidDiagram = import('agentic-mermaid/agent').MutableValidDiagram
197
- ```
198
-
199
- ```ts
200
- parseRegisteredMermaid(source: string): Result<ValidDiagram, ParseError[]>
201
- serializeMermaid(d: ValidDiagram): string
202
- synthesizeFromGraph(payload): Result<ValidDiagram, ParseError[]>
203
- createMermaid(kind, opts?): MutableValidDiagram // empty structured diagram; overloads narrow per kind
204
- buildMermaid(kind, ops, opts?): Result<MutableValidDiagram, MutationError & { opIndex: number }>
205
- mutate(d, op): Result<MutableValidDiagram, MutationError>
206
- // Concrete overloads preserve the narrowed family. Overloads and narrowers are
207
- // registry-derived; discover them with am capabilities --json and describeOps(family).
208
- ```
209
-
210
- **`mutate` is overloaded by family.** A registry-declared narrower returns the family-specific mutable type or `null`; the generated capability entry and `describeOps(family)` own the live operation contract. Opaque-fallback diagrams are not typed for mutation, so agents get a compile-time/null-narrower stop rather than a lossy edit path.
211
-
212
- Two contracts:
213
-
214
- - `serializeMermaid(parseRegisteredMermaid(s)) ≡ normalize(s)` for canonical input. For structured families this emits a fresh canonical form; for opaque families it emits preserved source with `meta` re-attached.
215
- - `parseRegisteredMermaid(serializeMermaid(d)) ≡ d` for every `d` produced by `parseRegisteredMermaid` or `mutate`.
216
-
217
- **Flowchart MutationOp kinds** (the authoritative menu is generated from the registry; `shape` fields accept geometry names and documented v11 names/aliases, and edge targets accept authored edge IDs or endpoint forms):
218
-
219
- | Kind | Required | Optional | Inverse |
220
- |---|---|---|---|
221
- | `add_node` | `id`, `label` | `shape`, `parent` | `remove_node(id)` |
222
- | `remove_node` | `id` | — | `add_node(id, label, shape, parent)` |
223
- | `rename_node` | `from`, `to` | — | `rename_node(to, from)` |
224
- | `set_label` | `target`, `label` | — | `set_label(target, prev_label)` |
225
- | `add_edge` | `from`, `to` | `label`, `style` | `remove_edge(id)` |
226
- | `remove_edge` | `id` | — | `add_edge(from, to, label, style)` |
227
- | `set_shape` | `id`, `shape` | — | `set_shape(id, prev_shape)` |
228
- | `set_direction` | `direction` | `subgraph` (sets that subgraph's override; omit = diagram) | `set_direction(prev_direction, subgraph)` |
229
- | `add_subgraph` | `id` | `label`, `parent`, `members` (existing nodes MOVED in — the state `make_composite` precedent) | `remove_subgraph(id)` |
230
- | `remove_subgraph` | `id` | `removeMembers` (default dissolves the box — members move to the parent scope; `true` deletes member nodes and their edges) | `add_subgraph(...)` |
231
- | `move_node` | `id`, `subgraph \| null` (null = top level) | — | `move_node(id, prev_subgraph)` |
232
- | `define_class` | `name`, `style` (CSS-like pairs, parsed by the parser's own style grammar) | — | `define_class(name, prev_style)` |
233
- | `set_node_class` | `id`, `className \| null` | — | `set_node_class(id, prev_class)` |
234
- | `set_node_style` | `id`, `style \| null` | — | `set_node_style(id, prev_style)` |
235
-
236
- **Other families.** The flowchart table is the worked example, not the menu. Every other family's op kinds, field shapes, requiredness, enums, and mutator-enforced constraints are generated from the registry: `describeOps(family)` / `opSignatures(family)` in the library and Code Mode, `families[].opFields` in `am capabilities --json`, and the generated SDK declaration. Each family's modeled grammar and fallbacks live in its design doc under [`docs/design/families/`](./docs/design/families/README.md). What stays here are the conventions the families share:
237
-
238
- - **Ops are invertible.** A removal carries what re-adding needs and a setter can restore the previous value, so an edit can be undone with typed ops. (Keeping the previous immutable `ValidDiagram` is the cheaper undo — see *Why totality matters*.)
239
- - **Address by identity where the family has one, by index where order is the meaning.** Nodes, states, classes, entities, services, and radar axes/curves are addressed by id; pie slices, quadrant points, and sankey nodes by their unique label, and a rename that would merge two identities is rejected. Sections, tasks, periods, events, and messages are addressed by index because source order is the timeline; sequence top-level message indices stay separate from fragment-message indices.
240
- - **Order-bearing families make reordering first-class.** Timeline, Journey, Sequence, and Gantt expose move ops. `index?` on an add op is an insert position (omit = append), a move op takes the insert position after removal, and errors name the legal range.
241
- - **Ops refuse rather than silently change meaning.** Gantt moves reject when they would change an implicit-start task's predecessor (materialize an explicit start and retry), and Gantt validates every value it writes by rendering and re-parsing that line. Sankey rejects self-loops and cycles. State composite and Architecture group removal refuse a non-empty container (State accepts `recursive: true`).
242
- - **Numbers are finite and canonical.** Values must be finite and inside the family's range (quadrant coordinates in `[0, 1]`, journey scores 1..5), and XY chart serializes numbers as `String(n)`, the shortest round-tripping decimal.
243
-
244
- **Structured-or-opaque rule (v4): never lossy.** The parser only produces a structured body when it fully understands every non-blank, non-comment line for most structured families. If the source contains *any* construct the parser doesn't model — `direction TB` in class, out-of-range scores in journey, duplicate Architecture title declarations, a `;`-joined multi-statement line in xychart, a malformed entry in pie, or out-of-range coordinates in quadrant, etc. — parsing **falls back to an opaque body**. The diagram still parses, renders, verifies (structurally), and round-trips losslessly via preserved `body.source`; it simply isn't offered for structured mutation (structured-family narrowers return `null` on opaque fallbacks). This guarantees the parser never silently drops information. Earlier drafts dropped unrecognized lines on the floor; v4 does not.
245
-
246
- **Segment-preserving bodies (BUILD-18) — sequence ends the all-or-nothing cliff.** Sequence carries an ordered `statements: SequenceStatement[]` list. `alt|opt|loop|par` are typed fragments: their branches/messages participate in describe/facts/verify and have dedicated mutation ops. Other block constructs (`critical|break|rect|box … end`) and `Note`/`activate`/`deactivate`/`create`/`destroy`/`autonumber`/`title` remain VERBATIM opaque segments. Top-level message indices stay backward-compatible and distinct from fragment indices. Only an un-segmentable body (a stray `end`, an unclosed block) falls back to whole-body opaque. Gantt adopts the same pattern for calendar directives/click/comments. ER also uses ordered typed and opaque-block segments; Class and Timeline are the remaining follow-up work.
247
-
248
- For any opaque fallback, cross-cutting edits are source-level only: operate against preserved source intentionally, then re-parse and verify before returning. Every renderable family now ships structured mutation; a new family follows the same pattern as its definition of done: narrowed type + body parser + serializer + per-family ops + verify hook + round-trip property tests + doc sync (most recently Mindmap and GitGraph). See `docs/contributing/adding-diagram-types.md`.
249
-
250
- Convention bans constructing `ValidDiagram` outside `parseRegisteredMermaid`, `mutate`, `synthesizeFromGraph`, and `createMermaid`/`buildMermaid`.
251
-
252
- ---
253
-
254
- ## Public API
255
-
256
- ```ts
257
- parseRegisteredMermaid(source: string): Result<ValidDiagram, ParseError[]>
258
- layoutMermaid(d: ValidDiagram): RenderedLayout
259
- renderMermaidASCII(input: ValidDiagram | string, opts?): string
260
- renderMermaidPNG(input: ValidDiagram | string, opts?): Uint8Array
261
- renderMermaidSVG(input: ValidDiagram | string, opts?): string
262
- verifyMermaid(input: ValidDiagram | string, opts?: VerifyOptions): VerifyResult
263
- serializeMermaid(d: ValidDiagram): string
264
- // ASCII opts.targetWidth is a hard display-cell bound; impossible bounds throw
265
- // AsciiWidthError { code, requestedWidth, requiredWidth, family, reason }.
266
-
267
- // Mutation is overloaded by family: one overload per mutable family, for example
268
- mutate(d: FlowchartValidDiagram, op: FlowchartMutationOp): Result<FlowchartValidDiagram, MutationError>
269
- mutate(d: GanttValidDiagram, op: GanttMutationOp): Result<GanttValidDiagram, MutationError>
270
- // Opaque/source-only bodies don't typecheck.
271
-
272
- // One narrowing helper per family (the registry names it: families[].narrower
273
- // in am capabilities --json); null when the diagram isn't of that family or is
274
- // opaque/source-level.
275
- asFlowchart(d: ValidDiagram): FlowchartValidDiagram | null
276
- asGantt(d: ValidDiagram): GanttValidDiagram | null
277
-
278
- // Build a ValidDiagram from a JSON-safe graph payload without re-parsing
279
- // source. Used by `am parse | am serialize` shell pipelines.
280
- synthesizeFromGraph(payload: ValidDiagramPayload): Result<ValidDiagram, ParseError[]>
281
-
282
- // Blank-slate authoring on the typed path: an empty structured diagram for
283
- // any built-in family (overloads narrow the return per kind), and a fold of
284
- // typed ops over that empty diagram. Errors carry the failing op's index.
285
- createMermaid(kind: DiagramKind, opts?: { direction?: Direction }): MutableValidDiagram
286
- buildMermaid(kind: DiagramKind, ops: AnyMutationOp[], opts?: { direction?: Direction }): Result<MutableValidDiagram, MutationError & { opIndex: number }>
287
-
288
- // VerifyOptions carries the only real knob: the label character cap.
289
- interface VerifyOptions { suppress?: WarningCode[]; labelCharCap?: number } // default cap 40
290
- ```
291
-
292
- There is no `LayoutContext`, no `SeededRNG`, no `Clock`, no font-metric table in the public surface. Those existed to support a seed apparatus that did nothing (see § (1)). The only verification knob is `labelCharCap`.
293
-
294
- **CLI** (`am <verb>`) with JSON where useful: <!-- BEGIN GENERATED: cli-verbs -->`render`, `verify`, `parse`, `serialize`, `mutate`, `preview`, `format`, `describe`, `capabilities`, `styles`, `llms-txt`, `init-agent`, `batch`, `render-markdown`<!-- END GENERATED: cli-verbs --> (`am --help` is authoritative). `am capabilities --json` reports each family's `editPolicy` (`structured-when-narrowed` vs `source-level-only`) plus `mutationOps`, so agents can route without trial-and-error. Plus `am --agent-instructions` printing the canonical agent-use guide embedded in the binary at build time — agents read the doc that ships with the tool, not whatever their training set indexed. The CLI's role is one-shot operations, shell-only contexts (CI, Bash-tool agents), and human inspection; multi-step editing belongs in the library or Code Mode, not in shell pipelines.
295
-
296
- ---
297
-
298
- ## Compatibility choices from the Beautiful Mermaid foundation
299
-
300
- - **Agent surface exposed via the `./agent` subpath export** on the `agentic-mermaid` package. Agentic Mermaid is the product/docs name, npm identity, and repository path (`adewale/agentic-mermaid`). The subpath export keeps renderer and agent surfaces side by side.
301
- - **Deterministic layout, verified.** Layout JSON is byte-identical across processes because ELK is configured for model-order layout with no random seed (see § (1)). No seed parameter is exposed because none affects output. Cross-machine byte equality across different CPU float behavior is not claimed; structural determinism within an ELK version is guaranteed and tested cross-process.
302
- - **IDs are content-hashed and stable** across runs (within an ELK version).
303
- - **`MermaidGraph` is kept** as an exported type — `ValidDiagram` wraps it in `body.graph` for flowchart, rather than replacing it. The original spec called for removal; characterization showed it would break existing tests and the Craft Agents consumer. The wrapping shape costs nothing.
304
- - **`renderMermaidSVGAsync` is kept**. Removing it was a v1-aspiration that turned out to break consumers for no win at the agent surface.
305
- - **`mermaidConfig` precedence** is source frontmatter < init < explicit render options for supported family fields. Normalization merges source wrappers; family resolvers then reapply the original explicit section last. State has direct public-path precedence and geometry probes in `state-config.test.ts`.
306
-
307
- ---
308
-
309
- ## Distribution
310
-
311
- Five artifacts, all derived from this doc:
312
-
313
- - **npm package** `agentic-mermaid` with the `agentic-mermaid/agent` subpath. The full TypeScript API, including ASCII, PNG, and SVG output helpers. Agents with shell access import the library directly and compose verbs in their own JS/TS runtime; no MCP wrapper required.
314
- - **`skills/agentic-mermaid-diagram-workflow/`** agent-agnostic skill bundle. Master `SKILL.md` routes by *both* diagram family and composition channel: it picks Code Mode when the MCP is connected, library import when the agent can run JS/TS with imports, the CLI for shell-only contexts. Per-family references (`flowchart.md`, `sequence.md`, etc.) describe syntax. Two channel references — `code-mode.md` (the canonical multi-step pattern) and `cli.md` (shell-only) — describe composition. Progressive disclosure means the LLM loads only what it needs. Family references sync from upstream Mermaid docs weekly via the shipped GitHub Action at `.github/workflows/sync-mermaid-docs.yml`, alongside our additions (LayoutWarning codes, MutationOp taxonomy).
315
- - **Determinism is tested on output**: `agent-determinism.test.ts` and `ascii-determinism.test.ts` render the same input repeatedly (and under different locales) and require byte-identical results, so ambient randomness or clocks in the agent, layout, or terminal code fail CI.
316
- - **`agentic-mermaid-mcp`** Code Mode-style MCP server. The primary tool is `execute(code: string)`: the model writes JavaScript against the typed `mermaid.*` SDK declaration embedded in the system prompt. Local stdio/HTTP runs synchronous code in a `node:vm` sandbox. The hosted `/mcp` endpoint runs the same hardened facade in per-request Cloudflare Dynamic Worker isolates with bounded CPU/subrequests and no outbound network. Pure render/verify/describe helpers share transport-neutral behavior. Differential tests pin the common contract and explicitly name strict-module and CPU-time divergences; neither runtime depends on `@cloudflare/codemode`.
317
- - **`Instructions_for_agents.md`** at repo root, kept under 100 lines. `am --agent-instructions` prints the same content at runtime; a doc-sync test asserts the two are byte-identical.
318
-
319
- Beyond stdio, the MCP server also speaks Streamable HTTP/SSE locally (`agentic-mermaid-mcp --transport http`, see `docs/mcp-http-transport.md`), and the hosted `/mcp` endpoint runs the same facade; there is no REST render API and no editor WebSocket watch. The skill teaches Code Mode for both paths: agents-with-shell write JS/TS against the imported library; agents-without-shell write JavaScript against the MCP's `mermaid.*` SDK. Same surface in both cases.
320
-
321
- ---
322
-
323
- ## Agent-contract verbs (CLI)
324
-
325
- Agent-contract CLI verbs for explicit self-discovery, summaries, and batch operation:
326
-
327
- - `am capabilities [--json]` — emit `{ sdkVersion, families, warningCodes, outputFormats }`, where each family entry carries its `editPolicy`, `mutationOps`, `opFields`, narrower, headers, and example. Sourced from the family registry and the `WARNING_SEVERITY` / `WARNING_TIER` tables — so the contract is self-describing, not hand-maintained. A JSON Schema is committed at `src/__tests__/__fixtures__/capabilities.schema.json`; any shape drift fails the test loudly.
328
- - `am batch --jsonl` — read JSONL from stdin, dispatch per-line to render/verify/parse/serialize/mutate handlers, emit one JSON envelope per result. Malformed lines surface `{ ok: false, error: { code: 'INVALID_JSON' } }` and do **not** abort the stream.
329
- - `am preview <file|-> [--output file.html] [--open] [--json] [--security strict]` — write a standalone strict-mode HTML preview for human inspection without hand-building wrapper files.
330
- - `am mutate <file|-> (--op '<json>' | --ops '<json array|file>') [--json]` — apply typed mutations, verify once at the commit point, and omit source on verify failure.
331
- - `am describe <file|-> [--format text|json|facts] [--json]` — emit a prose summary, structured AX tree (`{kind,nodes,edges,entryPoints,sinks}`), or semantic fact lines for screen readers, doc generation, and agent context compaction.
332
- - **Exit codes** are widened to 4: `EXIT_OK=0`, `EXIT_ARG_ERROR=2`, `EXIT_VERIFY_FAILED=3`, `EXIT_INTERNAL=4` (in `src/cli/exit-codes.ts`). The CLI was previously `0` or `2` only. `EXIT_VERIFY_FAILED=3` is the new code for "valid args, but the diagram failed verify" — important for agents wrapping `am verify` in batch.
333
-
334
- **Counter-example, documented.** manuareraa PR #42 on `lukilabs/beautiful-mermaid` ships an MCP server with 4 render-only tools (`render_svg` / `render_ascii` / `list_themes` / `parse`). We rejected this design: a render-tool-per-format MCP forces the agent to chain calls and loses ValidDiagram context. Our Code Mode design keeps one primary `execute()` surface for multi-step edits. The hosted endpoint does expose narrow `describe_sdk`, `render_svg`/`render_ascii`/`render_png`, `verify`, `describe`, and declarative `mutate`/`build` helpers for clients that cannot run code, but they are conveniences over the same facade, not the primary editing API. PR #42 is preserved here as the documented counter-example so future contributors understand why we chose Code Mode. <!-- complete: hosted-mcp-tools -->
335
-
336
- ---
337
-
338
- ## Agent workflow
339
-
340
- The canonical runtime guide lives in `Instructions_for_agents.md` and is emitted byte-for-byte by `am --agent-instructions`; this spec intentionally does not duplicate the full snippet. The stable contract is:
341
-
342
- 1. <!-- BEGIN GENERATED: new-diagram-policy -->New diagrams: author Mermaid source directly, then parse → verify → render or return it. `buildMermaid(kind, ops)` / `createMermaid(kind)` build a diagram from typed ops when you are generating one programmatically (for example, from data).<!-- END GENERATED: new-diagram-policy -->
343
- 2. For existing diagrams, `parseRegisteredMermaid(source)` → `ValidDiagram`.
344
- 3. Use the family entry's registry-advertised narrower; `null` means no structured mutation for that body.
345
- 4. Apply typed `mutate` ops only to narrowed mutable bodies; Code Mode SDK-returned diagrams are read-only to block direct IR edits.
346
- 5. Run `verifyMermaid(d)` at every commit point and inspect `ok` / `warnings` / `layout`.
347
- 6. Only then `serializeMermaid(d)`.
348
-
349
- Anti-patterns: whole-source regeneration of an existing parsed diagram, string concatenation to edit an existing structured diagram where a typed op exists, serializing before inspecting verify, and calling `mutate` on opaque/source-only bodies.
350
-
351
- ---
352
-
353
- ## Release discipline
354
-
355
- This section records the design discipline for the branch, not an active roadmap. Active work lives only in `TODO.md`.
356
-
357
- | Principle | Meaning |
358
- |---|---|
359
- | **Ship** | Keep the minimum lethal surface together: substrate + `verify` + `mutate` + `serialize` + typed MutationOps + CLI + skill + Code Mode MCP + `Instructions_for_agents.md`. |
360
- | **Learn** | Use MermaidSeqBench, stored Code Mode evals, live model transcripts, and real consumers to decide what is missing. |
361
- | **Expand by evidence** | Promote more MutationOps, composition primitives, or additional structured families only when evidence justifies them. |
362
-
363
- ---
364
-
365
- ## Measurement
366
-
367
- | What | How | Target |
368
- |---|---|---|
369
- | Layout JSON byte-equality across runs | Representative direction/density cases in `agent-determinism.test.ts`, plus corpus and cross-process keepers | Identical within one ELK version on one machine |
370
- | Drift sentinel | `layout-equivalence.test.ts`: integer-rounded geometry for the docs corpus plus layout-compare fixtures, byte-compared to a committed baseline; the PR description lists each changed golden and why | 0 diffs |
371
- | Cross-process determinism | Test spawns child `bun` processes; layout JSON byte-identical across them | 100% |
372
- | Tier-1 verifier recall on broken-fixture cases | Inline tests per Tier-1 code | high |
373
- | Round-trip identity | Golden corpus + property test | 100% on canonical input |
374
- | Round-trip property | Property test (fast-check) | 100% on parseable input |
375
- | Sequence fidelity | Property test (BUILD-18): any sequence source is segments-or-opaque and always lossless — interleaved structured + opaque lines round-trip in order; `remove_message` never touches opaque-block bytes | 100% lossless |
376
- | Sad-path coverage | CLI mutate on opaque family; malformed JSON-RPC; broken Code Mode script; N-round format idempotence | explicit tests |
377
- | Targeted fault controls | Introduce a credible regression at an audited boundary, confirm the owning test fails, restore | Evidence for the recorded scope; no whole-core mutation-coverage claim |
378
- | Code Mode sandbox isolation | Tests assert `execute()` cannot reach `process`, `require`, `fetch`, `eval`, `Function`, or host-constructor escape paths; dynamic code generation is disabled | explicit tests |
379
-
380
- Cross-cutting:
381
-
382
- - **Doc-sync** (both directions): every `LayoutWarning` code must appear in this spec, and the `MutationOp` menu is generated rather than copied here. `am --agent-instructions` output must equal `Instructions_for_agents.md` byte-for-byte.
383
- - **Test honesty:** each claimed behavior needs a credible failing regression and an independent oracle. Packaging parity, determinism and semantic correctness protect different contracts; see [`docs/testing-strategy.md`](./docs/testing-strategy.md).
384
-
385
- MermaidSeqBench is wired as an external corpus signal; live model transcript evaluation remains periodic / pre-release rather than PR CI.
386
-
387
- ---
388
-
389
- ## Risks (honest, v4)
390
-
391
- - **Determinism is empirical, not proven.** It's established by cross-process test over a corpus + the drift sentinel, plus reading ELK's config (`considerModelOrder: NODES_AND_EDGES`, no `randomSeed`). An ELK upgrade could in principle change this; the cross-process test and sentinel would catch it. There is no layout seed to fall back on because seeding never affected geometry. (The render option `seed` is a *style* seed: it re-rolls the ink wobble of styled looks and never moves layout.)
392
- - **Determinism claim, precisely.** Layout JSON is byte-identical (after structural parse) across processes AND across JS runtimes (bun, node) on the same machine and same ELK version; this is verified on same-machine x86_64 and ARM64 when Node + built `dist/` are present. Direct cross-architecture byte equality (x86_64 output compared to ARM64 output) is still not a separate claim.
393
- - **Sequence structured coverage is segment-preserving (BUILD-18).** Participant declarations, top-level messages, and direct-message `alt`/`opt`/`loop`/`par` fragments are typed; fragment messages participate in describe/facts/verify and dedicated mutation ops. Note/critical/break/rect/box/activate/autonumber/title and nested or otherwise unmodeled fragment content ride along as verbatim opaque-block *segments*, so the structured ops survive instead of going whole-body opaque. Messages inside those remaining opaque blocks are deliberately not modeled. Only un-segmentable input (unbalanced `end`, unclosed block) falls back to whole-body opaque. The honest tradeoff is unchanged — never lossy — it just no longer sacrifices the structured ops at the first unmodeled line. ER also has ordered typed and opaque-block segments; Class and Timeline are the remaining follow-up work.
394
- - **Every registered renderable family ships structured mutation; opaque fallbacks stay source-level only.** A typed narrower exists for every family kind; only opaque-fallback bodies for unmodeled syntax remain deliberate, lossless source-level paths.
395
- - **Live-model agent-usage eval is periodic, not PR CI.** Stored Code Mode scripts, sandbox traces, and task oracles run in CI; committed pi-subagent transcripts are archival evidence; API-backed release-model transcripts remain in `TODO.md` because they need model access and selected release tasks.
396
- - **Bloat in agent-facing docs.** `Instructions_for_agents.md` is kept under 100 lines by review, not by a test.
397
-
398
- ## What v4 delivers (vs. earlier drafts)
399
-
400
- - **Determinism is structural and verified cross-process** — not a seed apparatus. The seed/RNG/clock machinery and the font-metric table are *removed* (they did nothing).
401
- - **Mutation for every registered renderable family**, with family-narrowed overloads and compile-time rejection of opaque/source-only fallback bodies.
402
- - **Sequence parsing is lossless** — segment-preserving structured body (BUILD-18): Note/alt/loop/etc. ride along verbatim as opaque-block segments while the structured ops stay live; only un-segmentable input falls back to whole-body opaque; never silently drops constructs.
403
- - **Substrate enforcement is a real grep test** that runs under `bun test`, not an ESLint config that was never installed.
404
- - **`synthesizeFromGraph`** lets `am parse | am serialize` round-trip without `canonicalSource` on the wire.
405
- - **`LABEL_OVERFLOW` is a rendered-line char-count check** (Tier 1, reliable), not a font-metric heuristic: the cap applies to the longest displayed line (XML entities decode, `<br>` splits lines, formatting tags strip), not raw source chars. Families that draw text literally (Pie, Timeline, Gantt, XYChart, GitGraph, Radar) count `<br>` and tags as characters, because that is what they draw.
406
- - **`Finite` branded type** enforced at every coordinate emission.
407
- - **Deliverable completeness:** CHANGELOG entry, README section, an `examples/` script, per-verb CLI `--help`, and a [`docs/fork-differences.md`](./docs/fork-differences.md) mention all ship with the code.
408
-
409
- ---
410
-
411
- ## What Code Mode unlocks
412
-
413
- The Code Mode MCP raises the ceiling on what an agent can do with the library in one call. We initially framed the agent surface as a fixed set of verbs (parse, verify, mutate, serialize) where each verb is one round-trip. Code Mode reframes the surface as a typed library that the agent composes against — and the only thing we ship is the small set of typed operations plus a sandbox. The agent supplies the algorithm.
414
-
415
- Concrete consequences, in roughly descending impact:
416
-
417
- 1. **Composition without shipping composition.** `@include`, `@template`, vars/`${}` are deferred indefinitely. An agent can implement its own splice or template flavor in JavaScript over source strings and SDK-returned parsed diagrams — parse trusted inputs, use supported `mutate` operations, verify, serialize — in one round-trip. Code Mode does not bless hand-fabricated `ValidDiagram` clones; structured edits stay on SDK lineage.
418
- 2. **Multi-diagram repo operations.** "Rename `AuthService` across every architecture diagram in this repo, verify each, and report which now have warnings." With verb-per-tool MCP that's `N × 3` round-trips. With Code Mode: host/agent code supplies the file list and source strings, and one `execute()` can process them together. The sandbox itself does not expose filesystem access.
419
- 3. **Auto-fix loops in one round-trip.** `verify` → identify mechanically fixable warnings → apply fixes → `verify` again, as a `while (!result.ok)` block. The agent only sees the final state plus a structured audit trail.
420
- 4. **Diagram-as-tests / CI gate.** A repo installs `agentic-mermaid`, runs the `agentic-mermaid-mcp` binary, and writes a Code Mode snippet that verifies every `.mmd` on push. Diagrams become test artifacts that fail CI when they break.
421
- 5. **No need to ship `diffDiagrams` or `explainDiagram`.** Already cut. Code Mode confirms the cut: an agent that wants structural diff writes it from `parse` + `ValidDiagram` inspection in JavaScript. Every "would be nice to have a verb for" becomes "write the code for it in `execute()`."
422
- 6. **Library as the cross-tool agent interface for diagrams.** A Mermaid linter, a `mermaid → d2` converter, a `graphviz → mermaid` importer can each expose the same Code Mode shape. Any agent then writes one JavaScript snippet that composes across libraries. We've effectively defined the agent interface for diagrams in this language.
423
- 7. **Benchmark eval at speed.** MermaidSeqBench (and any future eval) runs as one `execute()` per case rather than N round-trips. Internal velocity multiplier.
424
- 8. **Hosted Worker path.** The shipped `/mcp` endpoint uses Cloudflare Dynamic Worker isolates through the platform loader binding, not `@cloudflare/codemode`. The runtime has explicit security/resource limits and differential parity with local Code Mode; remaining WAF and abuse-control operations are tracked separately in `TODO.md`.
425
- 9. **The skill becomes runnable, not just descriptive.** `references/code-mode.md` ships canonical executable JavaScript snippets the agent copy-pastes into `execute()`. Skill stops being prose; starts being a library of executable patterns.
426
- 10. **A future diagram REPL would be a thin transport.** A `repl` verb on the `am` CLI could become an interactive Code Mode shell — paste JavaScript, get structured results, iterate. Same sandbox, different transport. It is not shipped and would need promotion to `TODO.md` before implementation.
427
-
428
- The biggest single consequence is #1: it gives us permission to never grow the spec for composition, queries, diffing, explaining, or any "we should probably have a verb for that" feature. **The verb set is intentionally small; Code Mode makes it sufficient.**
429
-
430
- ### Counter-example: why we did not ship a `render-tool-per-format` MCP
103
+ ## (3) Round-trippable source
431
104
 
432
- Upstream [manuareraa PR #42](https://github.com/lukilabs/beautiful-mermaid/pull/42) takes the opposite design: an MCP server with one tool per output format (`render_svg`, `render_ascii`, `render_png`, etc.), each pinned to a specific renderer call. We considered cloning that surface and decided against it for two reasons. First, the verb explosion is unbounded — every new output format means a new tool, every option flag means a new tool variant, and every cross-cutting workflow (parse → verify → render → write back) requires the agent to glue tools together via the host's tool-call protocol, multiplying round-trips. Second, the per-tool typing pushes structure into the tool schema where the agent can only see what the schema declares; it can't introspect `ValidDiagram`, can't compose `mutate` with `verify`, and can't write the algorithm that uses the answer. Code Mode inverts both: one `execute` tool with a typed `mermaid.*` SDK declaration in scope. Read-only inspection, cross-diagram lookups, and custom transforms are ordinary JavaScript inside that execution context rather than additional MCP tools. Anything PR #42's surface could do — render-to-format with options — is one line of JavaScript inside `execute`; anything our surface can do that PR #42's can't includes every multi-step workflow that touches more than one verb. The asymmetry justifies the cut.
105
+ Every registered renderable family exposes typed edits for the syntax its
106
+ narrower can model. Unsupported statements can be retained as verbatim segments;
107
+ other inputs remain opaque source and refuse ordinary typed mutation. Preserving
108
+ source is preferable to silently dropping meaning to fit a smaller model.
433
109
 
434
- ---
110
+ Canonical serialization can normalize source spelling. Where exact retained
111
+ bytes matter, the explicit source-preserving mode supports a bounded set of
112
+ Class and Flowchart edits. It returns the edited source together with its parsed
113
+ diagram and refuses ambiguous ownership or unsupported dependencies. See the
114
+ [mutation policy](docs/agent-mutation-policy.md) for the operations and limits.
435
115
 
436
- ## Why totality matters
116
+ ## Library, CLI and MCP
437
117
 
438
- Each property alone leaves a gap. Determinism alone — agents can diff outputs but can't edit without regenerating. Round-trip alone — every edit shuffles the layout, drowning the signal. Verify alone — outputs aren't reproducible across runs, so a warning fixed in one run silently returns in the next.
118
+ For Node/Bun applications, `agentic-mermaid/agent` provides rendering and editing.
119
+ Browser/Worker editing uses `agentic-mermaid/agent/core` to avoid the native PNG
120
+ dependency; browser rendering uses `agentic-mermaid/browser/lazy`.
439
121
 
440
- Together they close a loop:
122
+ The CLI and MCP tools use the same operations. Run `am capabilities --json` for
123
+ version-matched families and edit schemas; the registry is their source of truth.
124
+ The [MCP guide](docs/mcp-http-transport.md) distinguishes the local and hosted
125
+ tool sets. Code Mode composes SDK calls in JavaScript; it grants no filesystem
126
+ access. Local VM containment and hosted Worker isolation have different security
127
+ boundaries, documented in [SECURITY.md](SECURITY.md).
441
128
 
442
- 1. Parse the source into `ValidDiagram`.
443
- 2. `mutate` one node — the IR guarantees the edit produces valid Mermaid.
444
- 3. `verify` — structured warnings tell the agent whether anything broke.
445
- 4. If broken, back up to the previous `ValidDiagram` and try a different op.
446
- 5. `serializeMermaid` only after verify passes.
129
+ ## Where to read next
447
130
 
448
- That loop is the agent-native claim. Replacing several rounds of vision-on-PNG with one structured verification pass is what makes a coding agent reach for this fork before any other Mermaid library.
131
+ - [API reference](docs/api.md): signatures, options and result shapes.
132
+ - [Mutation policy](docs/agent-mutation-policy.md): structured and source-level edits.
133
+ - [Testing strategy](docs/testing-strategy.md): behavior checks and their limits.
134
+ - [Fork differences](docs/fork-differences.md): additions to Beautiful Mermaid.
135
+ - [TODO.md](https://github.com/adewale/agentic-mermaid/blob/main/TODO.md): active work.
449
136
 
450
- The API makes the loop possible. `Instructions_for_agents.md` and `am --agent-instructions` make it a habit. Both ship together.
137
+ Earlier implementation narratives and removed approaches remain in Git history.
138
+ Current behavior belongs in the owning code, tests and reference guides above.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,45 @@ This changelog tracks user-facing changes for **Agentic Mermaid**, a fork of `lu
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.5.3 — 2026-10-29
8
+
9
+ Includes the 0.5.2 changes below. Its registry publication was blocked when
10
+ GitHub could not allocate a macOS arm64 runner; its source tag is retained.
11
+
12
+ ### Fixed
13
+
14
+ - Keep styled arrowheads clear of connector bends in Flowchart, Architecture,
15
+ Block, Class, Requirement, C4, Event Modeling and Gantt dependencies by
16
+ reserving the painted marker depth plus 4 px of straight shaft through
17
+ routing and rounding.
18
+ - Run the required macOS release build and rendering contracts on a standard
19
+ Intel macOS 26 runner. Windows coverage, the rendering suite, build heap
20
+ allowance and verified-artifact publication gates are unchanged.
21
+
22
+ ## 0.5.2 — 2026-10-29
23
+
24
+ ### Fixed
25
+
26
+ - Parse long same-line Flowchart chains without rescanning the remaining
27
+ chain for each node's edge-ID diagnostic. Preserve the existing syntax
28
+ warnings and source-map spans.
29
+ - Align ELK routes whose straight runs differ only by floating-point roundoff
30
+ as they are read back, so wide Class diagrams with padded cards keep their
31
+ card contacts instead of refusing to render, and Flowchart, State, ER and
32
+ Requirement routes start exactly orthogonal.
33
+ - Render Class diagrams with rounded relationship corners, such as the
34
+ `publication-figure` style, when a relationship must bypass a namespace
35
+ header, including nested namespaces.
36
+ - Render publication-styled Flowcharts with compound long links when joined
37
+ route sections differ only by floating-point roundoff, retaining their
38
+ endpoint contacts and existing route-safety checks.
39
+ - Keep Flowchart relationship ends readable: clear endpoint labels and styled
40
+ arrowheads, symmetric grouped fan-outs with safe caption seats, and distinct
41
+ terminals for differently painted or separately captioned relationships.
42
+ - Reject shared buses that imply invisible or differently painted relationships,
43
+ including replacements that introduce a new misleading overlap without
44
+ increasing the overlap count.
45
+
7
46
  ## 0.5.1 — 2026-10-29
8
47
 
9
48
  Includes the 0.5.0 changes below. That version's npm and MCP Registry