agentic-mermaid 0.1.1 → 0.2.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 (340) hide show
  1. package/AGENT_NATIVE.md +155 -91
  2. package/CHANGELOG.md +163 -4
  3. package/Instructions_for_agents.md +10 -10
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/README.md +31 -38
  6. package/THIRD_PARTY_NOTICES.md +29 -0
  7. package/assets/hero.png +0 -0
  8. package/dist/agent-core.d.ts +463 -0
  9. package/dist/agent-core.js +397 -0
  10. package/dist/agent.d.ts +73 -1868
  11. package/dist/agent.js +336 -37325
  12. package/dist/agentic-mermaid-mcp.js +7 -38399
  13. package/dist/am.js +33626 -40446
  14. package/dist/chunk-2P5FKUQC.js +3485 -0
  15. package/dist/chunk-2S3VSS6I.js +149 -0
  16. package/dist/chunk-2SILJDW2.js +37 -0
  17. package/dist/chunk-BJYMBT4I.js +61963 -0
  18. package/dist/chunk-FDM43XU7.js +615 -0
  19. package/dist/chunk-MYPDVZCO.js +4702 -0
  20. package/dist/index-DWPa2MJs.d.ts +6780 -0
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +310 -33014
  23. package/dist/mcp-cli-5J47WEY3.js +17 -0
  24. package/docs/README.md +39 -31
  25. package/docs/agent-api-cookbook.md +44 -35
  26. package/docs/agent-mutation-policy.md +1 -1
  27. package/docs/agent-workflow-examples.md +2 -2
  28. package/docs/api.md +284 -77
  29. package/docs/ascii.md +26 -16
  30. package/docs/code-mode-impact.md +214 -0
  31. package/docs/comparison.md +23 -11
  32. package/docs/computational-aesthetics-prototype-plan.md +252 -0
  33. package/docs/config.md +26 -10
  34. package/docs/custom-fonts.md +265 -0
  35. package/docs/custom-style-cookbook.md +130 -9
  36. package/docs/diagram-families.md +48 -23
  37. package/docs/features.md +59 -52
  38. package/docs/fork-differences.md +6 -3
  39. package/docs/getting-started.md +23 -16
  40. package/docs/issue-derived-test-cases.md +29 -22
  41. package/docs/mcp-code-mode-rationale.md +13 -6
  42. package/docs/mcp-http-transport.md +20 -13
  43. package/docs/mermaid-layout-complaints.md +66 -52
  44. package/docs/mutation-testing.md +145 -66
  45. package/docs/quality.md +23 -14
  46. package/docs/react.md +2 -2
  47. package/docs/schemas/style-spec.schema.json +1221 -30
  48. package/docs/style-authoring.md +241 -38
  49. package/docs/svg-semantic-contract.md +49 -0
  50. package/docs/testing-strategy.md +166 -58
  51. package/docs/theming.md +19 -32
  52. package/examples/styles/catalog.json +54 -0
  53. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  54. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  55. package/examples/styles/cupertino-prototype.mmd +10 -0
  56. package/examples/styles/cupertino-prototype.style.json +63 -0
  57. package/examples/styles/mid-century-report.style.json +2 -2
  58. package/examples/styles/star-chart-atlas.style.json +2 -1
  59. package/examples/styles/transit-route-map.style.json +2 -2
  60. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  61. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  62. package/package.json +85 -45
  63. package/server.json +2 -2
  64. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  65. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  66. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  67. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  68. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  69. package/TODO.md +0 -684
  70. package/dist/index-wjRjbsP-.d.ts +0 -1102
  71. package/docs/assets/improvements/box-start-connector.png +0 -0
  72. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  73. package/docs/assets/improvements/er-content-loss.png +0 -0
  74. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  75. package/docs/assets/improvements/gantt-family.png +0 -0
  76. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  77. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  78. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  79. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  80. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  81. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  82. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  83. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  84. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  85. package/docs/contributing/adding-diagram-types.md +0 -144
  86. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  87. package/docs/contributing/diagram-family-citizenship.md +0 -82
  88. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  89. package/docs/contributing/lessons-learned.md +0 -276
  90. package/docs/contributing/releasing.md +0 -82
  91. package/docs/contributing/visual-review-evidence.md +0 -53
  92. package/docs/design/families/README.md +0 -26
  93. package/docs/design/families/architecture-beta.md +0 -48
  94. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  95. package/docs/design/families/gantt-research.md +0 -137
  96. package/docs/design/families/gantt.md +0 -329
  97. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  98. package/docs/design/families/journey-experience-curve-implementation.png +0 -0
  99. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  100. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  101. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  102. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  103. package/docs/design/families/journey-migration-parity.md +0 -439
  104. package/docs/design/families/journey-section-overlap-after.png +0 -0
  105. package/docs/design/families/journey-section-overlap-before.png +0 -0
  106. package/docs/design/families/journey-usage-research.md +0 -423
  107. package/docs/design/families/journey.md +0 -153
  108. package/docs/design/families/xychart.md +0 -85
  109. package/docs/design/system/README.md +0 -45
  110. package/docs/design/system/abstraction-audit.md +0 -224
  111. package/docs/design/system/abstraction-recommendations.md +0 -655
  112. package/docs/design/system/architecture.mmd +0 -29
  113. package/docs/design/system/architecture.svg +0 -137
  114. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  115. package/docs/design/system/layout-rubric.md +0 -138
  116. package/docs/design/system/route-contracts.md +0 -848
  117. package/docs/design/system/source-preservation-ladder.md +0 -52
  118. package/docs/design/system/styles-rollout.md +0 -216
  119. package/docs/design/system/ugly-layouts.md +0 -119
  120. package/docs/layout-characterization/README.md +0 -245
  121. package/docs/layout-characterization/contact-sheet-families.md +0 -498
  122. package/docs/layout-characterization/contact-sheet.md +0 -638
  123. package/docs/layout-characterization/properties.md +0 -281
  124. package/docs/layout-characterization/visual-quality.md +0 -160
  125. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  126. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  127. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  128. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  129. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  130. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
  131. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  132. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  133. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  134. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  135. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  136. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  137. package/docs/project/divergences.md +0 -98
  138. package/docs/project/dogfooding-docs-strategy.md +0 -66
  139. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  140. package/docs/project/lessons-learned.md +0 -1019
  141. package/docs/project/remove-role-styling-plan.md +0 -176
  142. package/examples/agent-improve-auth-flow.ts +0 -206
  143. package/examples/agent-loop.ts +0 -53
  144. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  145. package/llms.txt +0 -104
  146. package/skill-evals/README.md +0 -77
  147. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  148. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  149. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  150. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  151. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  152. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  153. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  154. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  155. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  156. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  157. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  158. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  159. package/skill-evals/shared-benchmark.json +0 -510
  160. package/src/agent/analyze.ts +0 -157
  161. package/src/agent/apply.ts +0 -146
  162. package/src/agent/architecture-body.ts +0 -458
  163. package/src/agent/certificates.ts +0 -110
  164. package/src/agent/class-body.ts +0 -319
  165. package/src/agent/core.ts +0 -207
  166. package/src/agent/create.ts +0 -106
  167. package/src/agent/describe.ts +0 -368
  168. package/src/agent/er-body.ts +0 -224
  169. package/src/agent/facts.ts +0 -360
  170. package/src/agent/families-builtin.ts +0 -920
  171. package/src/agent/families.ts +0 -232
  172. package/src/agent/family-layouts.ts +0 -832
  173. package/src/agent/flowchart-body.ts +0 -372
  174. package/src/agent/flowchart-unsupported.ts +0 -243
  175. package/src/agent/font-coverage.ts +0 -288
  176. package/src/agent/gantt-body.ts +0 -495
  177. package/src/agent/index.ts +0 -10
  178. package/src/agent/journey-body.ts +0 -374
  179. package/src/agent/label-metrics.ts +0 -28
  180. package/src/agent/layout-to-rendered.ts +0 -63
  181. package/src/agent/mutate.ts +0 -97
  182. package/src/agent/mutation-ops.ts +0 -51
  183. package/src/agent/op-schema.ts +0 -443
  184. package/src/agent/parse.ts +0 -223
  185. package/src/agent/pie-body.ts +0 -270
  186. package/src/agent/png.ts +0 -171
  187. package/src/agent/quadrant-body.ts +0 -336
  188. package/src/agent/quality.ts +0 -363
  189. package/src/agent/readability-audit.ts +0 -92
  190. package/src/agent/rendered-route-audit.ts +0 -61
  191. package/src/agent/sequence-body.ts +0 -345
  192. package/src/agent/serialize.ts +0 -214
  193. package/src/agent/state-body.ts +0 -589
  194. package/src/agent/structural-count.ts +0 -150
  195. package/src/agent/timeline-body.ts +0 -280
  196. package/src/agent/trace-log.ts +0 -43
  197. package/src/agent/types.ts +0 -995
  198. package/src/agent/verify.ts +0 -603
  199. package/src/agent/xychart-body.ts +0 -472
  200. package/src/architecture/config.ts +0 -282
  201. package/src/architecture/layout.ts +0 -496
  202. package/src/architecture/parser.ts +0 -375
  203. package/src/architecture/renderer.ts +0 -611
  204. package/src/architecture/types.ts +0 -113
  205. package/src/ascii/ansi.ts +0 -423
  206. package/src/ascii/architecture.ts +0 -210
  207. package/src/ascii/canvas.ts +0 -443
  208. package/src/ascii/class-diagram.ts +0 -697
  209. package/src/ascii/converter.ts +0 -475
  210. package/src/ascii/draw.ts +0 -1693
  211. package/src/ascii/edge-bundling.ts +0 -328
  212. package/src/ascii/edge-routing.ts +0 -330
  213. package/src/ascii/er-diagram.ts +0 -439
  214. package/src/ascii/gantt.ts +0 -276
  215. package/src/ascii/grid.ts +0 -747
  216. package/src/ascii/index.ts +0 -196
  217. package/src/ascii/journey.ts +0 -169
  218. package/src/ascii/meta.ts +0 -244
  219. package/src/ascii/multiline-utils.ts +0 -80
  220. package/src/ascii/pathfinder.ts +0 -301
  221. package/src/ascii/pie.ts +0 -67
  222. package/src/ascii/quadrant.ts +0 -158
  223. package/src/ascii/reverse.ts +0 -216
  224. package/src/ascii/sequence.ts +0 -499
  225. package/src/ascii/shapes/circle.ts +0 -27
  226. package/src/ascii/shapes/corners.ts +0 -143
  227. package/src/ascii/shapes/diamond.ts +0 -27
  228. package/src/ascii/shapes/hexagon.ts +0 -27
  229. package/src/ascii/shapes/index.ts +0 -106
  230. package/src/ascii/shapes/rectangle.ts +0 -168
  231. package/src/ascii/shapes/rounded.ts +0 -27
  232. package/src/ascii/shapes/special.ts +0 -332
  233. package/src/ascii/shapes/stadium.ts +0 -110
  234. package/src/ascii/shapes/state.ts +0 -192
  235. package/src/ascii/shapes/types.ts +0 -73
  236. package/src/ascii/timeline.ts +0 -141
  237. package/src/ascii/types.ts +0 -312
  238. package/src/ascii/validate.ts +0 -120
  239. package/src/ascii/width.ts +0 -81
  240. package/src/ascii/wrap.ts +0 -131
  241. package/src/ascii/xychart.ts +0 -919
  242. package/src/browser.ts +0 -29
  243. package/src/class/layout.ts +0 -240
  244. package/src/class/parser.ts +0 -332
  245. package/src/class/renderer.ts +0 -638
  246. package/src/class/types.ts +0 -129
  247. package/src/cli/agent-instructions.ts +0 -1
  248. package/src/cli/am-bin.ts +0 -6
  249. package/src/cli/exit-codes.ts +0 -18
  250. package/src/cli/index.ts +0 -1132
  251. package/src/cli/init-agent.ts +0 -147
  252. package/src/cli/run-entrypoint.ts +0 -10
  253. package/src/color-resolver.ts +0 -152
  254. package/src/elk-instance.ts +0 -114
  255. package/src/er/layout.ts +0 -187
  256. package/src/er/parser.ts +0 -226
  257. package/src/er/renderer.ts +0 -792
  258. package/src/er/types.ts +0 -99
  259. package/src/family-rubric.ts +0 -425
  260. package/src/font-manifest.ts +0 -31
  261. package/src/gantt/layout.ts +0 -365
  262. package/src/gantt/parser.ts +0 -282
  263. package/src/gantt/pipeline.ts +0 -27
  264. package/src/gantt/renderer.ts +0 -414
  265. package/src/gantt/schedule.ts +0 -513
  266. package/src/gantt/types.ts +0 -255
  267. package/src/index.ts +0 -368
  268. package/src/journey/layout.ts +0 -554
  269. package/src/journey/parse-core.ts +0 -251
  270. package/src/journey/parser.ts +0 -101
  271. package/src/journey/renderer.ts +0 -917
  272. package/src/journey/types.ts +0 -144
  273. package/src/layout/geometry.ts +0 -182
  274. package/src/layout/pass.ts +0 -142
  275. package/src/layout/passes/index.ts +0 -3916
  276. package/src/layout-engine.ts +0 -1565
  277. package/src/layout-rubric.ts +0 -659
  278. package/src/mcp/artifacts.ts +0 -137
  279. package/src/mcp/dynamic-harness.ts +0 -31
  280. package/src/mcp/dynamic-modules.d.ts +0 -7
  281. package/src/mcp/facade.ts +0 -608
  282. package/src/mcp/harness-runtime.ts +0 -203
  283. package/src/mcp/hosted-server.ts +0 -468
  284. package/src/mcp/mcp-bin.ts +0 -3
  285. package/src/mcp/mcp-cli.ts +0 -82
  286. package/src/mcp/protocol.ts +0 -13
  287. package/src/mcp/sandbox.ts +0 -195
  288. package/src/mcp/sdk-decl.ts +0 -391
  289. package/src/mcp/server.ts +0 -362
  290. package/src/mcp/tool-surface.ts +0 -146
  291. package/src/mermaid-source.ts +0 -655
  292. package/src/multiline-utils.ts +0 -228
  293. package/src/parser.ts +0 -1031
  294. package/src/pie/layout.ts +0 -168
  295. package/src/pie/parser.ts +0 -136
  296. package/src/pie/renderer.ts +0 -234
  297. package/src/pie/types.ts +0 -82
  298. package/src/quadrant/layout.ts +0 -191
  299. package/src/quadrant/parser.ts +0 -183
  300. package/src/quadrant/renderer.ts +0 -322
  301. package/src/quadrant/types.ts +0 -113
  302. package/src/render-family-hooks.ts +0 -244
  303. package/src/renderer.ts +0 -1099
  304. package/src/route-contracts.ts +0 -2723
  305. package/src/scene/backend.ts +0 -76
  306. package/src/scene/fidelity.ts +0 -183
  307. package/src/scene/hybrid-backend.ts +0 -174
  308. package/src/scene/ir.ts +0 -181
  309. package/src/scene/marks.ts +0 -127
  310. package/src/scene/rough-backend.ts +0 -378
  311. package/src/scene/seed.ts +0 -46
  312. package/src/scene/style-registry.ts +0 -556
  313. package/src/sequence/layout.ts +0 -456
  314. package/src/sequence/parser.ts +0 -255
  315. package/src/sequence/renderer.ts +0 -604
  316. package/src/sequence/types.ts +0 -154
  317. package/src/shape-clipping.ts +0 -351
  318. package/src/shared/accessibility-directives.ts +0 -22
  319. package/src/shared/batched.ts +0 -61
  320. package/src/shared/color-math.ts +0 -93
  321. package/src/shared/svg-a11y.ts +0 -29
  322. package/src/shared/syntax-error.ts +0 -17
  323. package/src/shared/unicode-ranges.ts +0 -135
  324. package/src/styles.ts +0 -318
  325. package/src/svg-paths.ts +0 -28
  326. package/src/text-metrics.ts +0 -248
  327. package/src/theme.ts +0 -710
  328. package/src/timeline/layout.ts +0 -381
  329. package/src/timeline/parser.ts +0 -215
  330. package/src/timeline/renderer.ts +0 -538
  331. package/src/timeline/types.ts +0 -105
  332. package/src/types.ts +0 -462
  333. package/src/website-worker-modules.d.ts +0 -18
  334. package/src/xychart/axis-utils.ts +0 -109
  335. package/src/xychart/colors.ts +0 -124
  336. package/src/xychart/config.ts +0 -88
  337. package/src/xychart/layout.ts +0 -656
  338. package/src/xychart/parser.ts +0 -504
  339. package/src/xychart/renderer.ts +0 -640
  340. package/src/xychart/types.ts +0 -282
package/AGENT_NATIVE.md CHANGED
@@ -26,7 +26,7 @@ D2 has a better language than Mermaid. The Beautiful Mermaid renderer foundation
26
26
 
27
27
  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.)
28
28
  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.
29
- 3. **Round-trippable** — `parseMermaid` produces a `ValidDiagram` that carries source needed to re-emit the diagram. For flowchart/state, sequence, timeline, class, ER, journey, architecture, xychart, pie, quadrant, and gantt, `mutate` operates on structured bodies; for opaque-fallback bodies, preserved source is the round-trip mechanism.
29
+ 3. **Round-trippable** — `parseRegisteredMermaid` produces a `ValidDiagram` that carries source needed to re-emit the diagram. For flowchart/state, sequence, timeline, class, ER, journey, architecture, xychart, pie, quadrant, gantt, mindmap, gitgraph, and radar, `mutate` operates on structured bodies; for opaque-fallback bodies, preserved source is the round-trip mechanism.
30
30
 
31
31
  (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.)
32
32
 
@@ -34,24 +34,9 @@ D2 has a better language than Mermaid. The Beautiful Mermaid renderer foundation
34
34
 
35
35
  ## Honest scope
36
36
 
37
- What the current Agentic Mermaid surface delivers and what it doesn't:
37
+ The checked family roster, capability states, edit policies, narrowers, and mutation operations are generated from `FamilyDescriptor`: read [`docs/project/section-a-capability-report.md`](./docs/project/section-a-capability-report.md) or `am capabilities --json`, and use `describeOps(family)` for exact operation schemas. This rationale deliberately does not copy that live inventory.
38
38
 
39
- | Family | Parse / verify / render / round-trip | Structured mutation |
40
- |---|---|---|
41
- | Flowchart | Full | ✅ 6 ops |
42
- | State | Structured round-trip for the modeled subset (simple states/transitions/`[*]`/composites/direction); `<<fork>>`/`<<choice>>`/notes/`--`/`classDef` fall back to opaque losslessly | 8 ops via `asState` (BUILD-19) |
43
- | Sequence | Structured-with-segments (BUILD-18): participant/message ops stay live while Note/alt/loop/par/activate/autonumber/title ride along verbatim as opaque-block segments; only un-segmentable input (unbalanced `end`) falls back to whole-body opaque | ✅ 5 ops when structured |
44
- | Timeline | Full | ✅ 10 ops |
45
- | Class | Full | ✅ 10 ops |
46
- | ER | Full | ✅ 7 ops |
47
- | Journey | Full structured round-trip (title/sections/tasks) | 10 ops via `asJourney` (BUILD-15 pilot) |
48
- | Architecture | Structured round-trip for the modeled subset (groups/services/junctions/edges); `{group}` boundary edges + accTitle/accDescr fall back to opaque losslessly | 10 ops via `asArchitecture` (BUILD-17) |
49
- | XY chart | Structured round-trip for the modeled subset (title/axes/series); quoted text, `;` lines, accTitle/accDescr fall back to opaque losslessly | 8 ops via `asXyChart` (BUILD-16) |
50
- | Pie | Structured round-trip (title/showData/slices); accTitle/accDescr + malformed entries fall back to opaque losslessly | 7 ops via `asPie` |
51
- | Quadrant | Structured round-trip (title/axes/quadrant labels/points); styling + out-of-range coords fall back to opaque losslessly | 7 ops via `asQuadrant` |
52
- | Gantt | Structured-with-segments from day one ([`docs/design/families/gantt.md`](./docs/design/families/gantt.md)): title/section/task ops stay live while calendar directives, click lines, and comments ride along verbatim as opaque-block segments; duplicate task ids / unclosed accDescr fall back to whole-body opaque losslessly. Rendering is deterministic — the today marker needs a caller-supplied clock (`ganttToday`), never the wall clock | 9 ops via `asGantt` |
53
-
54
- The implication: for any 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.
39
+ 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.
55
40
 
56
41
  ---
57
42
 
@@ -128,6 +113,7 @@ Correctly detect what they claim to detect, but the occurrence may be intentiona
128
113
  | `ROUTE_HITCH` | warning | An edge bends although a direct lane for it is provably clear (route-contract tripwire) |
129
114
  | `ROUTE_UNEXPLAINED_BEND` | warning | An edge contains a diagonal segment under orthogonal routing (route-contract tripwire) |
130
115
  | `ROUTE_LABEL_ON_SHARED_TRUNK` | warning | A label pill sits on a line segment another edge shares (route-contract tripwire) |
116
+ | `ROUTE_SELF_LOOP_OCCUPANCY` | warning | A self-loop certificate has invalid side/boundary geometry or collides with another loop/label |
131
117
  | `ROUTE_CONTAINER_MISANCHOR` | warning | A container edge does not terminate on the container border (route-contract tripwire) |
132
118
  | `ROUTE_SHAPE_MISANCHOR` | warning | An endpoint is off the rendered shape boundary (route-contract tripwire) |
133
119
  | `ROUTE_STALE_AFTER_NODE_MOVE` | warning | An endpoint detached from its node entirely (route-contract tripwire) |
@@ -136,7 +122,7 @@ Codes are the contract surface agents reason about. Emitting an undocumented cod
136
122
 
137
123
  ### Tier 3 — Lint (advisory)
138
124
 
139
- Tier 3 warnings are family-specific quality hints for "common LLM mistakes" that parse and render but are probably not what the agent intended. They never flip `verify.ok`; callers decide whether to fail a style/quality gate.
125
+ 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.
140
126
 
141
127
  | Code | Severity | Description |
142
128
  |---|---|---|
@@ -147,8 +133,11 @@ Tier 3 warnings are family-specific quality hints for "common LLM mistakes" that
147
133
  | `UNSUPPORTED_SYNTAX` | warning | The source uses Mermaid syntax that is preserved losslessly but not fully modeled by local structured mutation/render semantics (for example flowchart edge IDs, edge metadata, click/href directives, or markdown strings). Payload includes `syntax`, optional `line`, and `message`. |
148
134
  | `CONTENT_DROPPED_ON_ROUNDTRIP` | warning | The structured `{nodes, edges, groups}` tally changed across a parse → serialize → re-parse cycle, so canonical serialization is silently dropping or duplicating content even though the bytes may re-parse (payload carries `before`/`after` counts). The faithfulness analogue of `COMMENT_DROPPED` — "100% parse success is not faithfulness". Runs on every verify, for every family; opaque bodies (byte-verbatim) are exempt. |
149
135
  | `INEFFECTIVE_CONFIG` | warning | A Mermaid config field was accepted (for config-shape compatibility) but has no effect on this family's geometry or paint — e.g. Journey's sequence-era fields (`boxMargin`, `rightAngles`, …). Payload names the `field`. Accepting-and-ignoring silently misleads migrating users; this lint says so. |
136
+ | `LOW_CONTRAST` | warning | A concrete authored paint remains authoritative but misses a measurable contrast threshold against the final resolved opaque background. Payload names the `field`, `foreground`, `background`, measured `ratio`, and required `minimum`; verification diagnoses without repainting authored intent. Transparent output is not measured because its host backdrop is unknown. |
137
+ | `BRAND_CONSTRAINT_WARNING` | warning | A caller-selected inspect-only `contrast`, `accent-area`, or `mono-role` Brand constraint failed or could not be measured. Payload reports the constraint, `measurement`, applicable role/mark, and concrete evidence without repainting or relayout. |
138
+ | `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. |
150
139
 
151
- `FamilyPlugin.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`, documented here, and covered by doc-sync tests.
140
+ `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`, documented here, and covered by doc-sync tests.
152
141
 
153
142
  **Branded coordinate types** (`Finite`) prevent NaN / Infinity from reaching the renderer. `toFinite()` is the only constructor; it throws on invalid input.
154
143
 
@@ -183,44 +172,31 @@ type DiagramBody =
183
172
  | { kind: 'flowchart'; graph: MermaidGraph }
184
173
  | StateBody | SequenceBody | TimelineBody | ClassBody | ErBody
185
174
  | JourneyBody | ArchitectureBody | XyChartBody | PieBody | QuadrantBody | GanttBody
175
+ | MindmapBody | GitGraphBody | RadarBody
186
176
  | { kind: 'opaque'; family: DiagramKind; source: string }
187
177
 
188
- type MutableValidDiagram =
189
- | FlowchartValidDiagram | StateValidDiagram | SequenceValidDiagram
190
- | TimelineValidDiagram | ClassValidDiagram | ErValidDiagram
191
- | JourneyValidDiagram | ArchitectureValidDiagram | XyChartValidDiagram
192
- | PieValidDiagram | QuadrantValidDiagram | GanttValidDiagram
178
+ type MutableValidDiagram = import('agentic-mermaid/agent').MutableValidDiagram
193
179
  ```
194
180
 
195
181
  ```ts
196
- parseMermaid(source: string): Result<ValidDiagram, ParseError[]>
182
+ parseRegisteredMermaid(source: string): Result<ValidDiagram, ParseError[]>
197
183
  serializeMermaid(d: ValidDiagram): string
198
184
  synthesizeFromGraph(payload): Result<ValidDiagram, ParseError[]>
199
185
  createMermaid(kind, opts?): MutableValidDiagram // empty structured diagram; overloads narrow per kind
200
186
  buildMermaid(kind, ops, opts?): Result<MutableValidDiagram, MutationError & { opIndex: number }>
201
- mutate(d: FlowchartValidDiagram, op: FlowchartMutationOp): Result<FlowchartValidDiagram, MutationError>
202
- mutate(d: StateValidDiagram, op: StateMutationOp): Result<StateValidDiagram, MutationError>
203
- mutate(d: SequenceValidDiagram, op: SequenceMutationOp): Result<SequenceValidDiagram, MutationError>
204
- mutate(d: TimelineValidDiagram, op: TimelineMutationOp): Result<TimelineValidDiagram, MutationError>
205
- mutate(d: ClassValidDiagram, op: ClassMutationOp): Result<ClassValidDiagram, MutationError>
206
- mutate(d: ErValidDiagram, op: ErMutationOp): Result<ErValidDiagram, MutationError>
207
- mutate(d: JourneyValidDiagram, op: JourneyMutationOp): Result<JourneyValidDiagram, MutationError>
208
- mutate(d: ArchitectureValidDiagram, op: ArchitectureMutationOp): Result<ArchitectureValidDiagram, MutationError>
209
- mutate(d: XyChartValidDiagram, op: XyChartMutationOp): Result<XyChartValidDiagram, MutationError>
210
- mutate(d: PieValidDiagram, op: PieMutationOp): Result<PieValidDiagram, MutationError>
211
- mutate(d: QuadrantValidDiagram, op: QuadrantMutationOp): Result<QuadrantValidDiagram, MutationError>
212
- mutate(d: GanttValidDiagram, op: GanttMutationOp): Result<GanttValidDiagram, MutationError>
213
- asFlowchart/asState/asSequence/asTimeline/asClass/asEr/asJourney/asArchitecture/asXyChart/asPie/asQuadrant/asGantt(d): narrowed diagram | null
187
+ mutate(d, op): Result<MutableValidDiagram, MutationError>
188
+ // Concrete overloads preserve the narrowed family. Overloads and narrowers are
189
+ // registry-derived; discover them with am capabilities --json and describeOps(family).
214
190
  ```
215
191
 
216
- **`mutate` is overloaded by family.** Flowchart/state, simple sequence, timeline, class, ER, journey, architecture, xychart, pie, quadrant, and gantt diagrams have first-class structured editing. Opaque-fallback diagrams are not typed for mutation, so agents get a compile-time/null-narrower stop rather than a lossy edit path.
192
+ **`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.
217
193
 
218
194
  Two contracts:
219
195
 
220
- - `serializeMermaid(parseMermaid(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.
221
- - `parseMermaid(serializeMermaid(d)) ≡ d` for every `d` produced by `parseMermaid` or `mutate`.
196
+ - `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.
197
+ - `parseRegisteredMermaid(serializeMermaid(d)) ≡ d` for every `d` produced by `parseRegisteredMermaid` or `mutate`.
222
198
 
223
- **Flowchart MutationOp kinds** (6):
199
+ **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):
224
200
 
225
201
  | Kind | Required | Optional | Inverse |
226
202
  |---|---|---|---|
@@ -230,73 +206,111 @@ Two contracts:
230
206
  | `set_label` | `target`, `label` | — | `set_label(target, prev_label)` |
231
207
  | `add_edge` | `from`, `to` | `label`, `style` | `remove_edge(id)` |
232
208
  | `remove_edge` | `id` | — | `add_edge(from, to, label, style)` |
209
+ | `set_shape` | `id`, `shape` | — | `set_shape(id, prev_shape)` |
210
+ | `set_direction` | `direction` | `subgraph` (sets that subgraph's override; omit = diagram) | `set_direction(prev_direction, subgraph)` |
211
+ | `add_subgraph` | `id` | `label`, `parent`, `members` (existing nodes MOVED in — the state `make_composite` precedent) | `remove_subgraph(id)` |
212
+ | `remove_subgraph` | `id` | `removeMembers` (default dissolves the box — members move to the parent scope; `true` deletes member nodes and their edges) | `add_subgraph(...)` |
213
+ | `move_node` | `id`, `subgraph \| null` (null = top level) | — | `move_node(id, prev_subgraph)` |
214
+ | `define_class` | `name`, `style` (CSS-like pairs, parsed by the parser's own style grammar) | — | `define_class(name, prev_style)` |
215
+ | `set_node_class` | `id`, `className \| null` | — | `set_node_class(id, prev_class)` |
216
+ | `set_node_style` | `id`, `style \| null` | — | `set_node_style(id, prev_style)` |
233
217
 
234
- **State MutationOp kinds** (8, BUILD-19 — promoting state from a "parses AS flowchart" projection to a dedicated `StateBody` IR with state-shaped ops and a real `asState` narrower). Modeled grammar: simple states (`state "Label" as id`, `id : Label`), transitions `from --> to [: label]` where `from`/`to` may be the reserved pseudostate `[*]` (source = start, target = end, scoped per composite level), composite blocks `state X { … }` (nestable), and `direction`. Anything else (`<<fork>>`/`<<choice>>`/`<<join>>`, history states, concurrency `--`, notes, `classDef`/`class`/`:::` styling, bare `stateId` lines, hyphenated composite ids) keeps the whole body opaque and round-trips verbatim. `[*]` is contextual, not a state node:
218
+ **State MutationOp kinds** (18, BUILD-19 + the family elevation — promoting state from a "parses AS flowchart" projection to a dedicated `StateBody` IR with state-shaped ops and a real `asState` narrower). Modeled grammar: simple states (`state "Label" as id`, `id : Label`), transitions `from --> to [: label]` where `from`/`to` may be the reserved pseudostate `[*]` (source = start, target = end, scoped per composite level) or a history reference (`[H]`, `Base[H*]`), composite blocks `state X { … }` (nestable), `direction`, pseudostate stereotypes (`state id <<fork|join|choice>>` plus the history forms `<<history>>`/`<<H>>`/`<<deephistory>>`/`<<H*>>`), and notes (`note left|right of X : text`, block form included). Concurrency `--`, `classDef`/`class`/`:::` styling, bare state lines, and hyphenated composite ids are modeled structurally. `[*]` is contextual, not a state node:
235
219
 
236
220
  | Kind | Required | Optional | Inverse |
237
221
  |---|---|---|---|
238
222
  | `add_state` | `id` | `label`, `parent` (composite) | `remove_state(id)` |
239
- | `remove_state` | `id` (refused on a non-empty composite — remove children first) | — | `add_state(...)` |
223
+ | `remove_state` | `id` (refused on a non-empty composite unless `recursive`) | `recursive` (`true` removes the whole subtree; touching transitions/notes cascade, history refs `X[H]` included) | `add_state(...)` |
240
224
  | `rename_state` | `from`, `to` | — (rewrites transitions) | `rename_state(to, from)` |
241
225
  | `set_state_label` | `id`, `label \| null` | — | `set_state_label(id, prev_label)` |
242
226
  | `add_transition` | `from`, `to` (`[*]` allowed) | `label`, `parent` | `remove_transition(from->to)` |
243
227
  | `remove_transition` | `index` or `from`/`to` pair | `parent` | `add_transition(...)` |
244
228
  | `set_transition_label` | `label \| null` + (`index` or `from`/`to`) | `parent` | `set_transition_label(..., prev_label)` |
245
- | `make_composite` | `id`, `members: string[]` | `label` | (move members out, remove composite) |
246
-
247
- **Sequence MutationOp kinds** (5):
229
+ | `make_composite` | `id`, `members: string[]` | `label` | `dissolve_composite(id)` |
230
+ | `set_direction` | `direction` | `state` (composite override; omit/null = diagram) | `set_direction(prev, state)` |
231
+ | `move_state` | `id`, `parent \| null` (null = top level; a simple parent promotes to a composite) | — | `move_state(id, prev_parent)` |
232
+ | `dissolve_composite` | `id` (hoists children + inner transitions into the parent scope; rejects while transitions/notes still reference it) | — | `make_composite(id, members, label)` |
233
+ | `add_note` | `target`, `text` | `side` (`left`/`right`, default `right`) | `remove_note(index)` |
234
+ | `remove_note` | `index` | — | `add_note(target, side, text)` |
235
+ | `set_note_text` | `index`, `text` | — | `set_note_text(index, prev_text)` |
236
+ | `define_class` | `name`, `style` | — | restore previous definition |
237
+ | `set_state_class` | `id`, `className \| null` | — | restore previous class |
238
+ | `set_state_style` | `id`, `style \| null` | — | restore previous style |
239
+ | `set_transition_style` | selector + `style \| null` | `parent`, `region` | restore previous style |
240
+
241
+ **Sequence MutationOp kinds** (top-level message indices remain separate from typed fragment-message indices):
248
242
 
249
243
  | Kind | Required | Optional | Inverse |
250
244
  |---|---|---|---|
251
- | `add_participant` | `id` | `label` | `remove_participant(id)` |
245
+ | `add_participant` | `id` | `label`, `participantKind` (`participant`/`actor`) | `remove_participant(id)` |
252
246
  | `remove_participant` | `id` | — | `add_participant(id, label)` |
253
- | `add_message` | `from`, `to`, `text` | `style` (sync/async) | `remove_message(index)` |
247
+ | `add_message` | `from`, `to`, `text` | `style` (sync/async), insert `index` (omitted = append) | `remove_message(index)` |
254
248
  | `remove_message` | `index` | — | `add_message(...)` |
255
249
  | `set_message_text` | `index`, `text` | — | `set_message_text(index, prev_text)` |
250
+ | `move_message` | `from`, `to` (source order IS the interaction timeline, so reorder is a first-class edit) | — | `move_message(to, from)` |
251
+ | `set_participant_label` | `id`, `label` | — | `set_participant_label(id, prev_label)` |
252
+ | `add_fragment` / `remove_fragment` / `set_fragment_label` | fragment kind or index | label/index | matching inverse |
253
+ | `add_fragment_branch` / `set_fragment_branch_label` | fragment/branch indices | label | matching inverse |
254
+ | `add_fragment_message` / `remove_fragment_message` / `set_fragment_message_text` | fragment index + message fields/index | branch/index/style | matching inverse |
256
255
 
257
- **Timeline MutationOp kinds** (10):
256
+ **Timeline MutationOp kinds** (15 — the journey conventions from PR #141: `index?` on add ops is an insert position, move ops take the insert position after removal, and errors name the legal range):
258
257
 
259
258
  | Kind | Required | Inverse |
260
259
  |---|---|---|
261
260
  | `set_title` | `title \| null` | `set_title(prev_title)` |
262
- | `add_section` | `label` | `remove_section(index)` |
261
+ | `add_section` | `label` (+ optional insert `index`) | `remove_section(index)` |
263
262
  | `remove_section` | `index` | `add_section(label)` |
264
263
  | `set_section_label` | `index`, `label` | `set_section_label(index, prev_label)` |
265
- | `add_period` | `sectionIndex`, `label` (+ optional `events: string[]`) | `remove_period(sectionIndex, periodIndex)` |
264
+ | `add_period` | `sectionIndex`, `label` (+ optional `events: string[]`, insert `index`) | `remove_period(sectionIndex, periodIndex)` |
266
265
  | `remove_period` | `sectionIndex`, `periodIndex` | `add_period(...)` |
267
266
  | `set_period_label` | `sectionIndex`, `periodIndex`, `label` | `set_period_label(... prev_label)` |
268
- | `add_event` | `sectionIndex`, `periodIndex`, `text` | `remove_event(... eventIndex)` |
267
+ | `add_event` | `sectionIndex`, `periodIndex`, `text` (+ optional insert `index`) | `remove_event(... eventIndex)` |
269
268
  | `remove_event` | `sectionIndex`, `periodIndex`, `eventIndex` | `add_event(...)` |
270
269
  | `set_event_text` | `sectionIndex`, `periodIndex`, `eventIndex`, `text` | `set_event_text(... prev_text)` |
270
+ | `move_period` | `fromSection`, `fromIndex`, `toSection`, `toIndex` (timeline order IS the chronology, so reorder is a first-class edit) | `move_period(toSection, toIndex, fromSection, fromIndex)` |
271
+ | `move_event` | `fromSection`, `fromPeriod`, `fromIndex`, `toSection`, `toPeriod`, `toIndex` | `move_event(...)` |
272
+ | `move_section` | `from`, `to` | `move_section(to, from)` |
273
+ | `set_accessibility_title` | `title \| null` | `set_accessibility_title(prev)` |
274
+ | `set_accessibility_description` | `description \| null` | `set_accessibility_description(prev)` |
271
275
 
272
- **Class MutationOp kinds** (10):
276
+ **Class MutationOp kinds** (15):
273
277
 
274
278
  | Kind | Required | Inverse |
275
279
  |---|---|---|
276
280
  | `set_title` | `title \| null` | `set_title(prev_title)` |
277
- | `add_class` | `id` (+ optional `label`, `members: string[]`) | `remove_class(id)` |
281
+ | `add_class` | `id` (+ optional `label`, `generic`, `members: string[]`, `namespace` dot path) | `remove_class(id)` |
278
282
  | `remove_class` | `id` | `add_class(id, label, members)` |
279
283
  | `rename_class` | `from`, `to` | `rename_class(to, from)` |
284
+ | `set_class_generic` | `class`, `generic \| null` | `set_class_generic(class, prev_generic)` |
280
285
  | `add_member` | `class`, `text` | `remove_member(class, index)` |
281
286
  | `remove_member` | `class`, `index` | `add_member(class, text)` |
282
287
  | `add_relation` | `from`, `to`, `relKind` (+ optional `label`) | `remove_relation(index)` |
283
288
  | `remove_relation` | `index` | `add_relation(...)` |
284
289
  | `add_note` | `text` (+ optional `for: class`) | `remove_note(index)` |
285
290
  | `remove_note` | `index` | `add_note(text, for)` |
291
+ | `set_class_namespace` | `class`, `namespace \| null` (a dot path like `Platform.Auth`, declared on demand; null = top level) | `set_class_namespace(class, prev_namespace)` |
292
+ | `define_class` | `name`, `style` | restore previous definition |
293
+ | `set_css_class` | `class`, `className \| null` | restore previous assignment |
294
+ | `set_class_style` | `class`, `style \| null` | restore previous style |
286
295
 
287
- **ER MutationOp kinds** (7):
296
+ **ER MutationOp kinds** (12):
288
297
 
289
298
  | Kind | Required | Inverse |
290
299
  |---|---|---|
291
- | `add_entity` | `id` (+ optional `attributes: string[]`) | `remove_entity(id)` |
292
- | `remove_entity` | `id` | `add_entity(id, attributes)` |
300
+ | `add_entity` | `id` (+ optional `label`, `attributes: string[]`) | `remove_entity(id)` |
301
+ | `remove_entity` | `id` | `add_entity(id, label, attributes)` |
293
302
  | `rename_entity` | `from`, `to` | `rename_entity(to, from)` |
303
+ | `set_entity_label` | `entity`, `label \| null` | `set_entity_label(entity, previous_label)` |
294
304
  | `add_attribute` | `entity`, `text` | `remove_attribute(entity, index)` |
295
305
  | `remove_attribute` | `entity`, `index` | `add_attribute(entity, text)` |
296
306
  | `add_relation` | `from`, `to`, `leftCard`, `rightCard` (+ `dashed`, `label`) | `remove_relation(index)` |
297
307
  | `remove_relation` | `index` | `add_relation(...)` |
308
+ | `set_direction` | `direction` | restore previous direction |
309
+ | `define_class` | `name`, `style` | restore previous definition |
310
+ | `set_entity_class` | `entity`, `className \| null` | restore previous assignment |
311
+ | `set_entity_style` | `entity`, `style \| null` | restore previous style |
298
312
 
299
- **Journey MutationOp kinds** (14, BUILD-15 — the pilot promotion from opaque-only fallback semantics to structured mutation via the FamilyPlugin registry; ordering + accessibility ops added with the Mermaid-classic renderer elevation, since journey order IS the timeline):
313
+ **Journey MutationOp kinds** (promoted from opaque-only fallback semantics through the family-descriptor registry; ordering and accessibility are typed because Journey order is the timeline):
300
314
 
301
315
  | Kind | Required | Inverse |
302
316
  |---|---|---|
@@ -315,22 +329,31 @@ Two contracts:
315
329
  | `set_accessibility_title` | `title \| null` | `set_accessibility_title(prev)` |
316
330
  | `set_accessibility_description` | `description \| null` | `set_accessibility_description(prev)` |
317
331
 
318
- **Architecture MutationOp kinds** (10, BUILD-17 — promoting the architecture-beta family to structured mutation via the FamilyPlugin registry, following the BUILD-15 journey pilot):
332
+ **Architecture MutationOp kinds** (19):
319
333
 
320
334
  | Kind | Required | Inverse |
321
335
  |---|---|---|
322
- | `add_service` | `id` (+ optional `label`, `icon`, `group`) | `remove_service(id)` |
323
- | `remove_service` | `id` (cascades its edges) | `add_service(...)` + re-add edges |
324
- | `rename_service` | `from`, `to` (updates edges) | `rename_service(to, from)` |
325
- | `set_service_label` | `id`, `label` | `set_service_label(id, prev_label)` |
326
- | `set_service_icon` | `id`, `icon: string \| null` | `set_service_icon(id, prev_icon)` |
327
- | `move_service` | `id`, `group: string \| null` | `move_service(id, prev_group)` |
328
- | `add_group` | `id` (+ optional `label`, `icon`, `parent`) | `remove_group(id)` |
329
- | `remove_group` | `id` (refused if non-empty — move members first) | `add_group(...)` |
330
- | `add_edge` | `from`, `to`, `fromSide`, `toSide` (+ optional `label`, `hasArrowStart`, `hasArrowEnd`) | `remove_edge(id)` |
331
- | `remove_edge` | `index` or `id` (`from->to`) | `add_edge(...)` |
332
-
333
- **XY chart MutationOp kinds** (8, BUILD-16 — promoting the xychart family to structured mutation via the FamilyPlugin registry, following the BUILD-15 journey and BUILD-17 architecture pilots). Canonical number format is `String(n)` (shortest round-tripping decimal); all values must be finite. Modeled grammar covers bare (unquoted) titles/axis-names/series-names/categories; quoted text, multi-statement `;` lines, accTitle/accDescr, and any other unmodeled syntax fall back to opaque:
336
+ | `set_title` | `title \| null` | `set_title(prev_title)` |
337
+ | `set_accessibility_title` | `title \| null` | `set_accessibility_title(prev)` |
338
+ | `set_accessibility_description` | `description \| null` | `set_accessibility_description(prev)` |
339
+ | `add_service` | `id` (+ optional `label`, `icon`, `group`) | `remove_service(id)` |
340
+ | `remove_service` | `id` (cascades edges/alignments) | `add_service(...)` + re-add edges |
341
+ | `rename_service` | `from`, `to` (rewrites edges/alignments) | `rename_service(to, from)` |
342
+ | `set_service_label` | `id`, `label` | `set_service_label(id, prev_label)` |
343
+ | `set_service_icon` | `id`, `icon: string \| null` | `set_service_icon(id, prev_icon)` |
344
+ | `move_service` | `id`, `group: string \| null` | `move_service(id, prev_group)` |
345
+ | `add_junction` | `id` (+ optional `group`) | `remove_junction(id)` |
346
+ | `remove_junction` | `id` (cascades edges/alignments) | `add_junction(...)` + re-add edges |
347
+ | `rename_junction` | `from`, `to` (rewrites edges/alignments) | `rename_junction(to, from)` |
348
+ | `move_junction` | `id`, `group: string \| null` | `move_junction(id, prev_group)` |
349
+ | `add_group` | `id` (+ optional `label`, `icon`, `parent`) | `remove_group(id)` |
350
+ | `set_group_label` | `id`, `label` | `set_group_label(id, prev_label)` |
351
+ | `remove_group` | `id` (refused if non-empty) | `add_group(...)` |
352
+ | `add_edge` | `from`, `to`, `fromSide`, `toSide` (+ optional boundaries/label/arrows) | `remove_edge(id)` |
353
+ | `update_edge` | `index` (+ any endpoint/side/boundary/label/arrow fields) | `update_edge(index, previous fields)` |
354
+ | `remove_edge` | `index` or `id` (`from->to`) | `add_edge(...)` |
355
+
356
+ **XY chart MutationOp kinds** (10, BUILD-16 — promoting the xychart family to structured mutation via the family-descriptor registry, following the BUILD-15 journey and BUILD-17 architecture pilots). Canonical number format is `String(n)` (shortest round-tripping decimal); all values must be finite. Modeled grammar covers bare titles/axis-names/series-names/categories — quoted text whose content is bare parses and canonicalizes to unquoted form; embedded quotes/brackets, multi-statement `;` lines, accTitle/accDescr, and any other unmodeled syntax fall back to opaque:
334
357
 
335
358
  | Kind | Required | Inverse |
336
359
  |---|---|---|
@@ -342,8 +365,10 @@ Two contracts:
342
365
  | `set_series_values` | `index`, `values: number[]` | `set_series_values(index, prev_values)` |
343
366
  | `set_series_name` | `index`, `name: string \| null` | `set_series_name(index, prev_name)` |
344
367
  | `reorder_series` | `from`, `to` | `reorder_series(to, from)` |
368
+ | `set_orientation` | `horizontal: boolean` (`true` adds the `horizontal` header suffix; `false` drops it — vertical is the serialized default) | `set_orientation(prev)` |
369
+ | `set_data_point` | `seriesIndex`, `index`, `value` (finite; out-of-range indices name the valid ranges) | `set_data_point(seriesIndex, index, prev_value)` |
345
370
 
346
- **Pie MutationOp kinds** (7 — promoting the pie family to structured mutation via the FamilyPlugin registry, following the journey/architecture/xychart pilots). Slices are addressed by their (unique) label; values must be positive finite numbers. The header's `showData` flag and an optional `title` are modeled; any unmodeled line (accTitle/accDescr, malformed entry) falls back to opaque losslessly:
371
+ **Pie MutationOp kinds** (7 — promoting the pie family to structured mutation via the family-descriptor registry, following the journey/architecture/xychart pilots). Slices are addressed by their (unique) label; values must be positive finite numbers. The header's `showData` flag and an optional `title` are modeled; any unmodeled line (accTitle/accDescr, malformed entry) falls back to opaque losslessly:
347
372
 
348
373
  | Kind | Required | Inverse |
349
374
  |---|---|---|
@@ -355,7 +380,7 @@ Two contracts:
355
380
  | `set_slice_value` | `label`, `value` (> 0) | `set_slice_value(label, prev_value)` |
356
381
  | `reorder_slice` | `from`, `to` | `reorder_slice(to, from)` |
357
382
 
358
- **Quadrant MutationOp kinds** (7 — promoting the quadrantChart family to structured mutation via the FamilyPlugin registry). Points are addressed by their (unique) label; coordinates must be in `[0, 1]`. Quadrant numbering follows Mermaid core (1=top-right, 2=top-left, 3=bottom-left, 4=bottom-right). Styling (`classDef`, `:::`), out-of-range coordinates, and any unmodeled line fall back to opaque losslessly:
383
+ **Quadrant MutationOp kinds** (7 — promoting the quadrantChart family to structured mutation via the family-descriptor registry). Points are addressed by their (unique) label; coordinates must be in `[0, 1]`. Quadrant numbering follows Mermaid core (1=top-right, 2=top-left, 3=bottom-left, 4=bottom-right). Styling (`classDef`, `:::`), out-of-range coordinates, and any unmodeled line fall back to opaque losslessly:
359
384
 
360
385
  | Kind | Required | Inverse |
361
386
  |---|---|---|
@@ -367,7 +392,7 @@ Two contracts:
367
392
  | `move_point` | `label`, `x`, `y` (in `[0,1]`) | `move_point(label, prev_x, prev_y)` |
368
393
  | `rename_point` | `from`, `to` | `rename_point(to, from)` |
369
394
 
370
- **Gantt MutationOp kinds** (9 — segment-preserving from day one per [`docs/design/families/gantt.md`](./docs/design/families/gantt.md)). Sections and tasks are addressed by index (`sectionIndex`, `taskIndex`); `taskId` is the Mermaid id used by `after`/`until`/`click`. Calendar directives (`dateFormat`, `excludes`, `includes`, `weekend`, `weekday`, `todayMarker`, `tickInterval`, `inclusiveEndDates`, `topAxis`), `click` lines, accTitle/accDescr, and comments ride along VERBATIM as opaque-block segments — preserved, source-level-editable, never typed-editable in v1. Every value an op writes is validated by rendering its canonical line and re-parsing it (correctness by construction), so labels with `:` or values with `,` are rejected rather than corrupting the source:
395
+ **Gantt MutationOp kinds** (13 — segment-preserving from day one per [`docs/design/families/gantt.md`](./docs/design/families/gantt.md)). Sections and tasks are addressed by index (`sectionIndex`, `taskIndex`); `taskId` is the Mermaid id used by `after`/`until`/`click`. Calendar directives (`dateFormat`, `excludes`, `includes`, `weekend`, `weekday`, `todayMarker`, `tickInterval`, `inclusiveEndDates`, `topAxis`), `click` lines, accTitle/accDescr, and comments ride along VERBATIM as opaque-block segments — preserved, source-level-editable, never typed-editable in v1. Every value an op writes is validated by rendering its canonical line and re-parsing it (correctness by construction), so labels with `:` or values with `,` are rejected rather than corrupting the source. Gantt source order IS scheduling semantics (a task with an implicit start chains from the previous task in flat source order), so the move ops REJECT prescriptively whenever they would change an implicit-start task's predecessor — materialize an explicit start (`set_task_dates` or an `after` dependency) and retry:
371
396
 
372
397
  | Kind | Required | Inverse |
373
398
  |---|---|---|
@@ -375,32 +400,65 @@ Two contracts:
375
400
  | `add_section` | `label` | `remove_section(index)` |
376
401
  | `rename_section` | `index`, `label` | `rename_section(index, prev_label)` |
377
402
  | `remove_section` | `index` (drops its tasks too) | `add_section(...)` + `add_task(...)` |
378
- | `add_task` | `sectionIndex`, `label`, `end` (+ optional `taskId`, `tags`, `start`) | `remove_task(sectionIndex, taskIndex)` |
403
+ | `add_task` | `sectionIndex`, `label`, `end` (+ optional `taskId`, `tags`, `start`, insert `index` — a mid-chain insert deliberately re-chains the follower) | `remove_task(sectionIndex, taskIndex)` |
379
404
  | `remove_task` | `sectionIndex`, `taskIndex` | `add_task(...)` |
380
405
  | `rename_task` | `sectionIndex`, `taskIndex`, `label` | `rename_task(..., prev_label)` |
381
406
  | `set_task_status` | `sectionIndex`, `taskIndex`, `status: 'active' \| 'done' \| 'crit' \| null` (milestone/vert tags are never disturbed) | `set_task_status(..., prev_status)` |
382
407
  | `set_task_dates` | `sectionIndex`, `taskIndex`, `start?: string \| null`, `end?: string` | `set_task_dates(..., prev_start, prev_end)` |
408
+ | `set_task_flags` | `sectionIndex`, `taskIndex`, `milestone?`, `vert?` (structural-tag toggles; `set_task_status` never touches them) | `set_task_flags(..., prev_flags)` |
409
+ | `set_task_id` | `sectionIndex`, `taskIndex`, `taskId \| null` (a rename REWRITES structured `after`/`until` references; REJECTS while opaque segments reference the id, and `null` rejects while ANY reference exists) | `set_task_id(..., prev_id)` |
410
+ | `move_task` | `fromSection`, `fromIndex`, `toSection`, `toIndex` (implicit-start guard above) | `move_task(toSection, toIndex, fromSection, fromIndex)` |
411
+ | `move_section` | `from`, `to` (implicit-start guard above) | `move_section(to, from)` |
412
+
413
+ **Mindmap MutationOp kinds** (10; see [`docs/design/families/mindmap.md`](./docs/design/families/mindmap.md)):
414
+
415
+ `add_node`, `remove_node`, `rename_node`, `set_label`, `move_node`, `set_shape`, `set_icon`, `set_node_class`, `set_accessibility_title`, `set_accessibility_description`.
416
+
417
+ **GitGraph MutationOp kinds** (11; see [`docs/design/families/gitgraph.md`](./docs/design/families/gitgraph.md)):
418
+
419
+ `append_commit`, `create_branch`, `checkout_branch`, `merge_branch`, `cherry_pick`, `set_commit_message`, `set_commit_type`, `set_commit_tags`, `rename_branch`, `set_accessibility_title`, `set_accessibility_description`.
420
+
421
+ **Radar MutationOp kinds** (14 — promoting the `radar-beta` family to structured mutation via the FamilyPlugin registry). Axes and curves are addressed by unique ids; each curve carries exactly one finite value per axis, in axis order. The header keyword is `radar-beta`; order-independent and multiline `title`, `axis`/`curve`, and option declarations are modeled. Duplicate identities, vector mismatches, accessibility directives, and any unmodeled line fall back to opaque losslessly rather than weakening the typed invariant:
422
+
423
+ | Kind | Required | Inverse |
424
+ |---|---|---|
425
+ | `set_title` | `title \| null` | `set_title(prev_title)` |
426
+ | `add_axis` | `id` (+ optional `label`, insert `index`, `fill`) | `remove_axis(id)` |
427
+ | `remove_axis` | `id` | `add_axis(...)` |
428
+ | `rename_axis` | `from`, `to` | `rename_axis(to, from)` |
429
+ | `set_axis_label` | `id`, `label \| null` | `set_axis_label(id, prev_label)` |
430
+ | `reorder_axis` | `from`, `to` (permutes every curve's value vector) | `reorder_axis(to, from)` |
431
+ | `add_curve` | `id`, `values` (one per axis) (+ optional `label`, insert `index`) | `remove_curve(id)` |
432
+ | `remove_curve` | `id` | `add_curve(...)` |
433
+ | `set_curve_values` | `id`, `values` (one per axis) | `set_curve_values(id, prev_values)` |
434
+ | `set_curve_value` | `curve`, `axis`, `value` (finite) | `set_curve_value(curve, axis, prev_value)` |
435
+ | `set_curve_label` | `id`, `label \| null` | `set_curve_label(id, prev_label)` |
436
+ | `rename_curve` | `from`, `to` | `rename_curve(to, from)` |
437
+ | `reorder_curve` | `from`, `to` | `reorder_curve(to, from)` |
438
+ | `set_config` | optional `max`, `min`, `ticks` (`1..64`), `graticule`, `showLegend`; `null` resets each field | `set_config(prev_fields)` |
383
439
 
384
- **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, `accTitle` or out-of-range scores in journey, the `{group}` boundary modifier in architecture, quoted text or `curve basis` 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.
440
+ **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.
385
441
 
386
- **Segment-preserving bodies (BUILD-18) — sequence ends the all-or-nothing cliff.** Sequence is the first family that does *not* go whole-body opaque on the first unmodeled line. Its body carries an ordered `statements: SequenceStatement[]` list (`participant` / `message` refs into the existing `participants`/`messages` arrays, plus `opaque-block` segments holding unmodeled lines VERBATIM). Block constructs (`alt|opt|loop|par|critical|break|rect … end`, nesting-tracked) become one opaque-block segment; `Note`/`activate`/`deactivate`/`autonumber`/`title` lines each join an adjacent segment. The participant/message ops stay live and address the top-level `messages` array exactly as before — **messages inside an opaque block are invisible to ops and are never touched.** Only an un-segmentable body (a stray `end`, an unclosed block) still falls back to whole-body opaque. Either way the round-trip is verbatim-lossless. Gantt adopts the same pattern from its first release: typed ops on title/sections/tasks, opaque-block segments for calendar directives/click/comments, whole-opaque only for duplicate ids or unclosed accDescr blocks. Class/ER/timeline segment-preservation is follow-up work.
442
+ **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.
387
443
 
388
- 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 gantt). See `docs/contributing/adding-diagram-types.md`.
444
+ 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`.
389
445
 
390
- Convention bans constructing `ValidDiagram` outside `parseMermaid`, `mutate`, `synthesizeFromGraph`, and `createMermaid`/`buildMermaid`.
446
+ Convention bans constructing `ValidDiagram` outside `parseRegisteredMermaid`, `mutate`, `synthesizeFromGraph`, and `createMermaid`/`buildMermaid`.
391
447
 
392
448
  ---
393
449
 
394
450
  ## Public API
395
451
 
396
452
  ```ts
397
- parseMermaid(source: string): Result<ValidDiagram, ParseError[]>
453
+ parseRegisteredMermaid(source: string): Result<ValidDiagram, ParseError[]>
398
454
  layoutMermaid(d: ValidDiagram): RenderedLayout
399
455
  renderMermaidASCII(input: ValidDiagram | string, opts?): string
400
456
  renderMermaidPNG(input: ValidDiagram | string, opts?): Uint8Array
401
457
  renderMermaidSVG(input: ValidDiagram | string, opts?): string
402
458
  verifyMermaid(input: ValidDiagram | string, opts?: VerifyOptions): VerifyResult
403
459
  serializeMermaid(d: ValidDiagram): string
460
+ // ASCII opts.targetWidth is a hard display-cell bound; impossible bounds throw
461
+ // AsciiWidthError { code, requestedWidth, requiredWidth, family, reason }.
404
462
 
405
463
  // Mutation is overloaded by family. Opaque/source-only families don't typecheck.
406
464
  mutate(d: FlowchartValidDiagram, op: FlowchartMutationOp): Result<FlowchartValidDiagram, MutationError>
@@ -415,6 +473,9 @@ mutate(d: XyChartValidDiagram, op: XyChartMutationOp): Result<XyChartValidDi
415
473
  mutate(d: PieValidDiagram, op: PieMutationOp): Result<PieValidDiagram, MutationError>
416
474
  mutate(d: QuadrantValidDiagram, op: QuadrantMutationOp): Result<QuadrantValidDiagram, MutationError>
417
475
  mutate(d: GanttValidDiagram, op: GanttMutationOp): Result<GanttValidDiagram, MutationError>
476
+ mutate(d: MindmapValidDiagram, op: MindmapMutationOp): Result<MindmapValidDiagram, MutationError>
477
+ mutate(d: GitGraphValidDiagram, op: GitGraphMutationOp): Result<GitGraphValidDiagram, MutationError>
478
+ mutate(d: RadarValidDiagram, op: RadarMutationOp): Result<RadarValidDiagram, MutationError>
418
479
 
419
480
  // Narrowing helpers; null when the diagram isn't of that family or is opaque/source-level.
420
481
  asFlowchart(d: ValidDiagram): FlowchartValidDiagram | null
@@ -429,6 +490,9 @@ asXyChart(d: ValidDiagram): XyChartValidDiagram | null
429
490
  asPie(d: ValidDiagram): PieValidDiagram | null
430
491
  asQuadrant(d: ValidDiagram): QuadrantValidDiagram | null
431
492
  asGantt(d: ValidDiagram): GanttValidDiagram | null
493
+ asMindmap(d: ValidDiagram): MindmapValidDiagram | null
494
+ asGitGraph(d: ValidDiagram): GitGraphValidDiagram | null
495
+ asRadar(d: ValidDiagram): RadarValidDiagram | null
432
496
 
433
497
  // Build a ValidDiagram from a JSON-safe graph payload without re-parsing
434
498
  // source. Used by `am parse | am serialize` shell pipelines.
@@ -455,9 +519,9 @@ There is no `LayoutContext`, no `SeededRNG`, no `Clock`, no font-metric table in
455
519
  - **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.
456
520
  - **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.
457
521
  - **IDs are content-hashed and stable** across runs (within an ELK version).
458
- - **`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; the implementation showed it would break 61 test files and the Craft Agents consumer. The wrapping shape costs nothing.
522
+ - **`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.
459
523
  - **`renderMermaidSVGAsync` is kept**. Removing it was a v1-aspiration that turned out to break consumers for no win at the agent surface.
460
- - **`mermaidConfig` precedence** (frontmatter < init < render options) is enforced by tests via a single merge function — unchanged from the existing pipeline.
524
+ - **`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`.
461
525
 
462
526
  ---
463
527
 
@@ -468,7 +532,7 @@ Five artifacts, all derived from this doc:
468
532
  - **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.
469
533
  - **`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).
470
534
  - **Substrate grep-lint** runs under `bun test` (not an uninstalled ESLint): `src/__tests__/agent-substrate-lint.test.ts` fails the build if `Math.random`, `Date.now`, or `performance.now` appear in `src/agent/**` or `src/layout-engine.ts`. This is real enforcement, executed in CI, not an aspirational config file.
471
- - **`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; the server runs the code in a local `node:vm` sandbox with the library exposed as `mermaid` and the code's return value captured as the structured result. The server also exposes narrow `render_png` and `describe` helpers for binary output and summaries. The verify-before-commit loop becomes one round-trip rather than N. Hosting: local stdio launched by the MCP client (Claude Desktop, Claude Code, Cursor) — same deployment shape as filesystem-MCP, git-MCP, sqlite-MCP. No infrastructure on our side or the user's. The current MCP is not Cloudflare Codemode, not a Worker, not backed by `@cloudflare/codemode`, and not a drop-in Cloudflare integration. The `website/` Worker is a static Workers Static Assets deployment only; hosted MCP transport or a Cloudflare executor remain future options, not shipped artifacts.
535
+ - **`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`.
472
536
  - **`Instructions_for_agents.md`** at repo root, hard-capped under 100 lines. `am --agent-instructions` prints the same content at runtime; a doc-sync test asserts the two are byte-identical.
473
537
 
474
538
  No HTTP endpoint or editor WebSocket watch in v1. 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.
@@ -495,8 +559,8 @@ Agent-contract CLI verbs for explicit self-discovery, summaries, and batch opera
495
559
  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:
496
560
 
497
561
  1. For new diagrams, `buildMermaid(kind, ops)` — or `createMermaid(kind)` then typed mutations — then `verifyMermaid` / render or return it. Author Mermaid source directly only for syntax the typed ops do not model.
498
- 2. For existing diagrams, `parseMermaid(source)` → `ValidDiagram`.
499
- 3. Narrow with `asFlowchart` / `asState` / `asSequence` / `asTimeline` / `asClass` / `asEr` / `asJourney` / `asArchitecture` / `asXyChart` / `asPie` / `asQuadrant` / `asGantt`; `null` means no structured mutation for that body.
562
+ 2. For existing diagrams, `parseRegisteredMermaid(source)` → `ValidDiagram`.
563
+ 3. Use the family entry's registry-advertised narrower; `null` means no structured mutation for that body.
500
564
  4. Apply typed `mutate` ops only to narrowed mutable bodies; Code Mode SDK-returned diagrams are read-only to block direct IR edits.
501
565
  5. Run `verifyMermaid(d)` at every commit point and inspect `ok` / `warnings` / `layout`.
502
566
  6. Only then `serializeMermaid(d)`.
@@ -546,15 +610,15 @@ MermaidSeqBench is wired as an external corpus signal; live model transcript eva
546
610
 
547
611
  - **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.)
548
612
  - **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.
549
- - **Sequence structured coverage is segment-preserving (BUILD-18).** Participant declarations + simple messages are the mutable structured surface; Note/alt/loop/par/activate/autonumber/title now ride along as verbatim opaque-block *segments* in the same body, so the structured ops survive instead of going whole-body opaque. Messages inside opaque blocks are deliberately not modeled (invisible to ops). 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. (Class/ER/timeline segment work is a follow-up.)
550
- - **All twelve renderable families ship structured mutation; opaque fallbacks stay source-level only.** Pie and quadrant completed the promotion (7 ops each, via `asPie`/`asQuadrant`) and gantt ships segment-preserving structured mutation from its first release (9 ops via `asGantt`), so a narrower exists for every family kind; only opaque-fallback bodies (unmodeled syntax) remain deliberate, lossless source-level paths. (Journey was promoted by BUILD-15; architecture by BUILD-17; xychart by BUILD-16.)
613
+ - **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.
614
+ - **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.
551
615
  - **Live-model agent-usage eval is periodic, not PR CI.** Stored Code Mode scripts, sandbox traces, task oracles, and the committed pi-subagent transcript replay run in CI; API-backed release-model transcripts remain in `TODO.md` because they need model access and selected release tasks.
552
616
  - **Bloat in agent-facing docs.** `Instructions_for_agents.md` is hard-capped under 100 lines; doc-sync test enforces.
553
617
 
554
618
  ## What v4 delivers (vs. earlier drafts)
555
619
 
556
620
  - **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).
557
- - **Mutation for all twelve renderable families** (flowchart/state, sequence, timeline, class, ER, journey, architecture, xychart, pie, quadrant, gantt), family-narrowed overloads, compile-time rejection of opaque/source-only fallback bodies.
621
+ - **Mutation for every registered renderable family**, with family-narrowed overloads and compile-time rejection of opaque/source-only fallback bodies.
558
622
  - **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.
559
623
  - **Substrate enforcement is a real grep test** that runs under `bun test`, not an ESLint config that was never installed.
560
624
  - **`synthesizeFromGraph`** lets `am parse | am serialize` round-trip without `canonicalSource` on the wire.
@@ -578,7 +642,7 @@ Concrete consequences, in roughly descending impact:
578
642
  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()`."
579
643
  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.
580
644
  7. **Benchmark eval at speed.** MermaidSeqBench (and any future eval) runs as one `execute()` per case rather than N round-trips. Internal velocity multiplier.
581
- 8. **Potential future hosted-Worker path.** Cloudflare-hosted Agentic Mermaid is tracked as `TODO.md` BUILD-4. The repo now ships a static Workers Static Assets website under `website/`, but a hosted MCP/Code Mode wrapper remains separate. That wrapper could use `@cloudflare/codemode` + `DynamicWorkerExecutor` only after the security boundary, auth/rate limits, persistence, and CLI/MCP/library parity are scoped. The current repo does not ship that dependency or runtime; JavaScript snippets plus a TypeScript-shaped SDK declaration are the reusable design idea, not a current hosted execution deployment.
645
+ 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`.
582
646
  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.
583
647
  10. **A future diagram REPL would be a thin transport.** An `am repl` 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.
584
648