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
@@ -1,52 +0,0 @@
1
- # Source preservation ladder
2
-
3
- Status: implemented as the Agentic Mermaid family-adoption contract.
4
-
5
- Agentic Mermaid edits source, not just pictures. Every family must declare how
6
- much source it can model and what happens when it encounters syntax outside the
7
- modeled subset. Silent loss is never an acceptable level.
8
-
9
- ## Levels
10
-
11
- | Level | Name | Contract |
12
- |---|---|---|
13
- | L0 | reject/fail loud | The construct is not safely renderable/editable. Parsing or verification returns a named error/warning; no partial IR is serialized as if it were complete. |
14
- | L1 | whole-body opaque | The family header is recognized, but the body is preserved byte-for-byte as opaque source. Typed narrowers return `null`; render/verify may use a safe legacy/family parser. |
15
- | L2 | segment-preserving mixed body | Modeled statements are typed-editable; unmodeled blocks/directives/comments remain in ordered opaque segments and serialize without silent loss. |
16
- | L3 | canonical structured body | Accepted body syntax is modeled; parse → serialize → parse is stable. Unsupported syntax falls back to L1/L2 or fails loud. |
17
- | L4 | traceable structured model | L3 plus stable source spans, rendered regions, and action/analysis sidecars for load-bearing semantic objects. |
18
-
19
- ## Construct policy
20
-
21
- | Construct class | Required behavior |
22
- |---|---|
23
- | Frontmatter / init directives / leading comments | Preserved in `meta.wrapperSource` and re-emitted verbatim by default; canonical wrapper synthesis is opt-in. |
24
- | In-body comments | Preserved by opaque bodies; structured bodies must either preserve them or report `COMMENT_DROPPED`. |
25
- | Unknown family syntax | Use L1 opaque preservation when rendering remains safe; otherwise L0 named failure. |
26
- | Mermaid v11 `@{ ... }` metadata | Must not create phantom nodes or silently drop targets. Current safety floor preserves opaque/unsupported forms and consumes supported label metadata conservatively. Full typed-shape vocabulary remains separate (#44). |
27
- | Actions/clicks | Never execute during parse/render/analysis. Preserve source intent and expose source-only metadata where modeled. |
28
- | Render regions | Stable IDs are required for L4 claims; V1 region coverage may be partial but must be documented and tested. |
29
-
30
- ## Current family levels
31
-
32
- | Family | Body preservation level | L4 traceability status |
33
- |---|---|---|
34
- | flowchart | L3 for modeled graph syntax; L1 for unsupported syntax/metadata forms | Partial L4: nodes/edges/groups/labels/source map, route certs, action analysis, region MVP |
35
- | state | L3 structured body | Partial L4 via graph projection, source map, route certs, region MVP |
36
- | sequence | L2 segment-preserving structured body | Partial L4: participants/messages layout + lifeline certificates |
37
- | timeline | L3 structured body with opaque fallback | Partial L4: sections/periods/events layout regions/certs |
38
- | class | L3 structured body with opaque fallback | Partial L4: classes/members source map, geometry validators, orthogonal-box certs |
39
- | er | L3 structured body with opaque fallback | Partial L4: entities/attrs/cardinalities source map, geometry validators, orthogonal-box certs |
40
- | journey | L3 structured body with opaque fallback | Partial L4: sections/tasks regions/source map |
41
- | architecture | L3 structured body | Partial L4: groups/services/junctions/side-anchored certs |
42
- | xychart | L3 structured body with opaque fallback | Partial L4: plot/marks/labels source map + containment certs |
43
- | pie | L3 structured body with opaque fallback | Partial L4: slices/legend source map + containment certs |
44
- | quadrant | L3 structured body with opaque fallback | Partial L4: quadrants/points source map + containment certs |
45
- | gantt | L2/L3 structured body with schedule resolver and named schedule errors | Partial L4: sections/tasks source map, schedule analysis, containment certs |
46
-
47
- ## Reviewer rule
48
-
49
- A family may advance levels incrementally, but it may not regress silently. New
50
- syntax support must add one of: typed modeling, opaque preservation, or a named
51
- failure with docs/tests. Mutation APIs require L3 for the affected construct;
52
- agents must verify before serializing.
@@ -1,216 +0,0 @@
1
- # Styles: the composable look system — naming, model, and rollout plan
2
-
3
- Status: v2, largely executed on PR #60 — the engine, the consolidated
4
- `style` primitive, and the product surfaces (CLI `--style`/`--seed` +
5
- `am styles`, MCP style args + typed SDK declaration, editor style picker
6
- with seed re-roll, agent docs) have landed; steps below are annotated.
7
- Nothing has shipped to npm yet, so remaining decisions stay free.
8
-
9
- The engine work is done: every diagram family lowers to a SceneGraph of
10
- semantic marks, and pluggable backends serialize those marks — crisp (the
11
- unchanged default), rough.js sketch, and hybrid (pressure ribbons, washes).
12
-
13
- **The headline is what users and their agents can construct, not what we
14
- ship.** The built-in looks are proof and raw material: the original sketch
15
- set (hand-drawn, excalidraw, pen-and-ink, freehand, watercolor, blueprint,
16
- tufte) plus coverage expanders for accessibility, print, operations,
17
- physical media, architecture, and editorial/report figures. The product is
18
- a small set of primitives with one combination rule, from which looks we
19
- never anticipated can emerge.
20
-
21
- ---
22
-
23
- ## 1. One concept: style. A theme is a kind of style.
24
-
25
- The repo used to have three overlapping vocabularies: *themes* (the
26
- `THEMES` palette record + `themeVariables` + color options), early private
27
- role-face experiments, and the pre-consolidation *aesthetics*. Multiple words,
28
- shapes, and precedence stories answered one question: **how should this diagram look?**
29
-
30
- We collapse them into one primitive:
31
-
32
- > A **style** is a partial description of how diagrams look. Every field is
33
- > optional. A style that only sets colors is a palette. A style that sets
34
- > stroke character, fills, typography, and a palette is a full look.
35
-
36
- ```ts
37
- interface StyleSpec {
38
- // identity (optional — anonymous inline styles are fine)
39
- name?: string
40
- blurb?: string
41
-
42
- // palette — the seven tokens every mark references
43
- colors?: { bg?; fg?; line?; accent?; muted?; surface?; border? }
44
-
45
- // typography
46
- font?: string
47
-
48
- // mark treatment (what the sketch backends read)
49
- stroke?: 'crisp' | 'jittered' | 'freehand'
50
- fill?: 'none' | 'hachure' | 'solid' | 'wash'
51
- roughness?; bowing?; passes?; strokeWidth?; hachureAngle?; hachureGap?;
52
- fillWeight?; washOpacity?; washEdge?
53
- backdrop?: 'plain' | 'paper-ruled' | 'grid'
54
-
55
- // advisory metadata (documented, never read by the engine)
56
- intent?: 'premium' | 'draft' | 'lofi'
57
- mono?: boolean
58
- }
59
- ```
60
-
61
- So the answer to "what is the right relationship between styles and themes"
62
- is: **subsumption, not orthogonality**. A theme is a palette-only style.
63
- The existing `THEMES` entries register into the style registry at startup
64
- (their names keep working everywhere a style name is accepted), and
65
- `themeVariables`/color options remain as the *last* override layer for
66
- Mermaid compatibility. One registry, one lookup (`getStyle`), one list
67
- (`knownStyles`) — and the docs answer the theme question in a sentence
68
- instead of a precedence table.
69
-
70
- ## 2. One combination rule: the stack
71
-
72
- The public option takes a name, a spec, or a **stack** of them:
73
-
74
- ```ts
75
- interface RenderOptions {
76
- style?: StyleInput | StyleInput[] // StyleInput = string | StyleSpec
77
- seed?: number // re-rolls ink wobble, never layout
78
- }
79
-
80
- // a full look
81
- renderMermaidSVG(src, { style: 'hand-drawn' })
82
-
83
- // hand-drawn geometry × dracula palette — "any style × any theme" is just stacking
84
- renderMermaidSVG(src, { style: ['hand-drawn', 'dracula'] })
85
-
86
- // stack a shared brand fragment and a local tweak on top
87
- renderMermaidSVG(src, { style: ['hand-drawn', acmeBrand, { colors: { accent: '#e11d48' } }] })
88
- ```
89
-
90
- Merging is left→right, per field — the CSS intuition everyone already has.
91
- The full precedence story becomes one line:
92
-
93
- ```
94
- defaults < style stack (left → right) < themeVariables < explicit color options
95
- ```
96
-
97
- This one rule replaces separate theme/style/variant parameters. It is also the
98
- agent-native shape: an agent composes a look by concatenating fragments it
99
- was given, retrieved, or wrote itself.
100
-
101
- ## 3. Simplifications this unlocks (missed in plan v1)
102
-
103
- 1. **No `backend` field for authors.** Which backend a style needs is
104
- derivable from what it asks for: `stroke: 'freehand'` or `fill: 'wash'`
105
- ⇒ hybrid; any rough parameter or `stroke: 'jittered'` ⇒ rough; otherwise
106
- ⇒ default. Authors describe the *look*; the engine picks the machinery.
107
- (An explicit `backend` override remains for code-backed extensions, but
108
- it disappears from the tutorial path.)
109
- 2. **No aesthetic/style/theme triage.** One word in the API, the CLI, the
110
- MCP schema, the docs. `THEMES` stays exported for compatibility of
111
- in-repo code but is defined *as* registered palette-only styles.
112
- 3. **No compatibility shims anywhere** — pre-release, the in-repo callers
113
- of the old option just update.
114
- 4. **`intent`/`mono` demote to advisory metadata.** The engine never
115
- branches on them; they exist for pickers, galleries, and the authoring
116
- rubric. (The mono *contract* stays a test on built-ins, not an engine
117
- switch.)
118
- 5. **One registry, one gallery.** `knownStyles()` drives the CLI list, the
119
- editor picker, and the poster harness — registering a style is the only
120
- step to appear in all three.
121
- 6. **Fragments are the sharing unit.** A JSON file with three fields is a
122
- complete, valid, shareable style. Packs are just arrays of named
123
- fragments — no schemaVersion ceremony until a real need appears.
124
-
125
- ## 4. Designed for emergence
126
-
127
- Following the "design for emergence" argument (UX Mag): when user needs are
128
- heterogeneous and unpredictable — and custom looks are exactly that — ship
129
- simple elements and combination rules, not finished solutions; keep the
130
- floor low, the ceiling high, and the walls wide.
131
-
132
- - **Low floor**: `style: 'hand-drawn'` — one string.
133
- - **Wide walls**: fragments compose across axes we don't enumerate. A
134
- palette fragment × a line-character fragment × a typography fragment — we
135
- ship full looks, but the space users can reach is
136
- the product of every fragment anyone writes. Nobody designs "corporate
137
- memo hand-drawn dark"; it emerges from a stack.
138
- - **High ceiling**: `registerBackend` + the SceneGraph types for the rare
139
- code-backed capability (a new fill algorithm, a layout-aware dialect).
140
- Styles stay data; code is the escape hatch, not the path.
141
- - **Safety rails that make wild combinations viable**: the prototype WCAG
142
- contrast audit and text-halo policy showed the right contract. Production
143
- now has deterministic stacking and built-in looks that route structural
144
- ink through the active theme foreground; a full arbitrary-stack WCAG gate
145
- remains the missing guardrail.
146
- - **Reproducible sharing**: determinism means `(source, stack, seed)` is a
147
- complete, portable description of an image. A gist with a JSON fragment
148
- IS the artifact. This is the hashtag/spreadsheet property: the unit of
149
- user invention is trivially copyable.
150
- - **Agents as the emergence engine**: `docs/style-authoring.md` (spec
151
- template + quality rubric + worked good/bad examples) is linked from
152
- `llms.txt` and the agent instructions, so an agent can author a fragment
153
- from a sentence ("make it look like our brand deck"), render it, check
154
- contrast, and iterate — the loop that produced the 31-style prototype,
155
- now available to every user's agent.
156
- - **Deliberately not built** (over-specification kills emergence): no
157
- inheritance/`extends` graphs (stacking covers it), no per-diagram-family
158
- style hooks in v1, no marketplace/registry service — a JSON fragment in
159
- a repo is the distribution mechanism until the community proves a need.
160
-
161
- ## 5. npm package changes
162
-
163
- First release ships as `agentic-mermaid` **0.x minor** (crisp default
164
- byte-identical, corpus-gated):
165
-
166
- - **API**: `RenderOptions.style` (name | spec | stack), `RenderOptions.seed`,
167
- `registerStyle`, `getStyle`, `knownStyles`, `validateStyleSpec` (JSON
168
- schema included in the package), and for extensions `registerBackend`,
169
- `StyleBackend`, plus SceneGraph types (`SceneDoc`, `SceneNode`,
170
- `SemanticChannels`, `SceneRole`). `THEMES` remains, redefined as
171
- palette-only styles.
172
- - **Runtime dependencies**: `roughjs@4.6.6` and `perfect-freehand@1.2.3`,
173
- pinned exactly — seeded geometry must not shift under a bump; a bump is a
174
- golden-fixture change (`[approve-goldens]`). They are `dependencies`
175
- because the `bun` export condition resolves raw `src/*.ts`; the Node dist
176
- bundles them (~80 KB minified). A `agentic-mermaid/styles` subpath split
177
- is the escape hatch if crisp-only weight ever matters.
178
- - **Docs in the box**: README leads with the matrix image, a one-string
179
- quick start, and a *custom fragment stack* example; `docs/api.md` gets the
180
- option + precedence line; CHANGELOG states the model in three sentences.
181
- - **Deferred**: styled PNG parity (blocked on bundling the OFL faces as
182
- reviewed production assets — notices already exist); CLI/MCP surfaces
183
- land in the rollout below.
184
-
185
- ## 6. Rollout, emergence-first
186
-
187
- 1. **Collapse the primitives**: rename `aesthetic` → `style`; make all
188
- `StyleSpec` fields optional; implement the stack merge; register `THEMES`
189
- as styles; infer backends. (Everything else builds on the final shape.)
190
- *Status: DONE — resolveStyleStack / inferBackend / validateStyleSpec in
191
- src/scene/style-registry.ts; RenderOptions.style is the union; public
192
- role-key objects were removed before launch.*
193
- 2. **Agent surface**: MCP render tool + SDK declaration accept `style`
194
- (name | spec | stack) and `seed`; authoring guide linked for agents.
195
- 3. **CLI**: `am render --style <name|file.json>` (repeatable = stack),
196
- `--seed`, `am styles [--json]`; PNG threading with an explicit
197
- font-fallback warning.
198
- 4. **Editor**: style picker and palette picker for built-in looks; `seed` stays
199
- an API/CLI/share-link field rather than a persistent topbar control.
200
- 5. **README + gallery** generated from the registry.
201
- 6. **Fonts for PNG parity** (OFL faces as reviewed assets).
202
-
203
- ## 7. How we explain it
204
-
205
- - One sentence: *"A style is a small data record describing how diagrams
206
- look; stack styles to combine them — a color-only style is a theme."*
207
- - Crisp stays the default, stated with receipts (corpus-verified byte
208
- identity), and styling covers **all 12 diagram types uniformly**.
209
- - Three promises for the supported path: **readable** built-in stacks
210
- (halos, theme-friendly structural ink, and generated evidence),
211
- **deterministic** (`seed` shuffles ink, never layout — cacheable,
212
- diffable, golden-testable), **portable** (a JSON fragment + seed
213
- reproduces the image anywhere). A full WCAG gate for arbitrary user
214
- fragments is still a follow-up.
215
- - The README example is a custom fragment stack, not a built-in name —
216
- the message *is* the primary value.
@@ -1,119 +0,0 @@
1
- # What Makes a Layout Ugly
2
-
3
- This document states the working definition of an *ugly* diagram layout that
4
- this project has converged on, and maps each defect to a detector. It is the
5
- specification for `eval/ugly-detector/` (which audits **rendered** output —
6
- SVG, PNG, ASCII — not just the internal geometry the rubric checks).
7
-
8
- ## The one-line definition
9
-
10
- > **A layout is ugly when an edge's geometry isn't forced by the node
11
- > positions** — when it bends, floats, splays, crosses, or breaks symmetry in
12
- > a way a clearer alternative would avoid for the *same* layout. Every bend,
13
- > offset, and asymmetry should be the cheapest one the node positions allow,
14
- > or it shouldn't be there. Ugliness is **unexplained geometry**.
15
-
16
- ## The defects (and how we detect them)
17
-
18
- Grouped by the layer they live in. The first group are **hard** — they must
19
- never appear in a finished render. The second are **soft** — present in good
20
- layouts only when forced, and ranked below.
21
-
22
- ### Hard defects (a finished render must have zero)
23
-
24
- | Defect | What it looks like | Detector |
25
- |---|---|---|
26
- | **Diagonal segment** | an edge segment that is neither horizontal nor vertical, in an orthogonal drawing | segment with `|dx| > ε` AND `|dy| > ε` |
27
- | **Edge through a node** | an edge crossing a node's interior (occlusion) | segment intersects a non-endpoint node's body deeper than stroke width |
28
- | **Floating endpoint** | an edge ending at an arbitrary point on the outline, not a designated connection point | endpoint not within tolerance of the shape's outline / a designated port |
29
- | **Node overlap** | two node bodies overlapping | bbox/footprint intersection |
30
- | **Hitch / dogleg** | a short perpendicular jog on a run whose lane is clear | a segment much shorter than its collinear neighbours, perpendicular, with a clear straight alternative |
31
- | **Label off its route** | a label pill sitting away from the edge it names | label centroid far from its edge's polyline |
32
-
33
- ### Soft ranking (good layouts minimize these, in this priority order)
34
-
35
- The operative priority, distilled from Purchase 1997/2002 and Ware 2002 and
36
- from the choices made across this project:
37
-
38
- 1. **Straightness / continuity** — fewest bends; a multi-edge path reads as a
39
- line. (Beats everything below.)
40
- 2. **Designated-port attachment** — endpoints sit on a *named*, deterministic
41
- connection point, not a target-determined float. **Subtlety we encode:**
42
- straightness normally beats port-exactness (the strict-ports experiment was
43
- measured and rejected), *except* when a designated port is reachable
44
- **without costing a bend** — then take it. The diamond 8-port model exists
45
- precisely to make E/F/G/K port-exact *and* straight/symmetric at once.
46
- 3. **Symmetry / balance** — where the structure is symmetric (peer fan-ins,
47
- reciprocal pairs), the drawing should be too: a centered hub, two equal
48
- parallel lines, no peer privileged over another.
49
- 4. **Fewest bends and crossings** — after the above.
50
-
51
- ### Specific ugly shapes we have named
52
-
53
- - **Cramming at a sharp point** — two lines crowded onto a vertex when they
54
- could be spread (the ±6 reciprocal-on-a-diamond-tip, before facet-mids).
55
- - **Pinch-then-splay / hourglass** — edges converging to a narrow pair of ports
56
- then fanning back out to wider targets (the rejected F facet-mid+bend).
57
- - **A line through a shape's body** — routing the short way *through* a node
58
- instead of around it (the rejected G facet-mid prototype).
59
- - **Asymmetry where symmetry is natural** — a fan-in hub stuck at the top of
60
- its sources, or a reciprocal pair of unequal lengths.
61
-
62
- ## Why detect from rendered output (not just geometry)
63
-
64
- The `assessLayout` rubric (`src/layout-rubric.ts`) checks the **internal
65
- geometry** (`PositionedGraph`). The ugly-detector checks the **rendered
66
- artifact** — the SVG/PNG/ASCII a consumer actually sees — so it:
67
-
68
- 1. catches rendering bugs where clean geometry renders wrong;
69
- 2. works on *any* diagram, including external SVGs and golden fixtures not
70
- produced by our layout pass;
71
- 3. validates the ASCII pipeline, which is a separate renderer that never sees
72
- the `RouteCertificate` contracts.
73
-
74
- The three formats degrade in fidelity: **SVG** is the authoritative analysis
75
- (vector paths + shape geometry are recoverable exactly); **PNG** is a faithful
76
- raster of an SVG, so its check is "analyze the source SVG, plus a coarse
77
- pixel-orthogonality sanity pass"; **ASCII** is a glyph grid, so detection is
78
- limited to what the grid reveals (diagonal line glyphs, edge glyphs inside a
79
- box interior).
80
-
81
- ## Running the audit
82
-
83
- ```
84
- bun run audit:ugly # render every corpus to SVG/PNG/ASCII, report
85
- bun run audit:ugly -- --verbose # also list soft findings
86
- bun run audit:ugly -- --json # machine-readable report
87
- ```
88
-
89
- The runner (`eval/ugly-detector/audit.ts`) renders every diagram corpus in the
90
- project — the contact-sheet scenarios, the heuristic-tracker examples, the
91
- site samples, the layout-compare fixtures, and the ASCII/Unicode golden
92
- fixture sources — to all three formats and runs the detectors. It exits
93
- non-zero if any **hard** finding is present, so it doubles as a CI gate.
94
- `src/__tests__/ugly-detector.test.ts` pins the detector's behavior.
95
-
96
- ### What the audit found (2026-06)
97
-
98
- Current result on PR #30 after the nested-subgraph route fixes:
99
-
100
- ```text
101
- 338 diagrams / 1009 format-audits
102
- 0 hard layout defects
103
- 0 soft layout findings
104
- 6 render/corpus errors
105
- ```
106
-
107
- The non-clean entries are **render errors** (a diagram type or config header a
108
- given renderer rejects), not layout defects, and are bucketed as such.
109
-
110
- Historical note: an earlier audit found one real hard defect — a
111
- cross-hierarchy edge into a nested subgraph missing the containing subgraph's
112
- offset. That is now fixed by hosting cross-hierarchy edges at their lowest
113
- common compound and by treating subgraph IDs as container endpoints; the
114
- regressions live in `src/__tests__/subgraph-direction.test.ts`.
115
-
116
- Two findings the audit initially raised were **detector** false positives, now
117
- fixed and regression-tested: a cylinder's footprint must union its cap ellipses
118
- (not just the body rect), and a sub-pixel endpoint jog within the curved-shape
119
- clip floor (~1.5px) is not a visible dogleg.
@@ -1,245 +0,0 @@
1
- # Characterising the layout system
2
-
3
- This folder bootstraps a **characterisation-test** suite for the layout
4
- algorithm, in the sense of
5
- [adewale/testing-best-practices](https://github.com/adewale/testing-best-practices):
6
- pin what the code does today, with worked examples + property-based tests, so
7
- the algorithm can be refactored safely and improved deliberately.
8
-
9
- It contains three layers — a **deep** characterisation of the flowchart/state
10
- grid engine, a **breadth** layer spanning every renderer, and a **visual-quality**
11
- approval layer for reviewable layout drift:
12
-
13
- | File | What it is |
14
- |------|------------|
15
- | [`contact-sheet.md`](./contact-sheet.md) | **Depth** — the minimum set of worked examples that exercise every load-bearing decision in the grid engine. One phenomenon per example. (Generated by `scripts/characterization/contact-sheet.ts`.) |
16
- | [`properties.md`](./properties.md) | **Depth** — the smallest load-bearing set of properties for the grid engine, with a minimality (mutation-kill) argument and academic grounding. |
17
- | [`../../src/__tests__/characterization-layout.test.ts`](../../src/__tests__/characterization-layout.test.ts) | **Depth** — those properties as executable `fast-check` tests (13 tests). |
18
- | [`contact-sheet-families.md`](./contact-sheet-families.md) | **Breadth** — one canonical example per renderer (all 12 families) + the universal-invariant coverage matrix. (Generated by `scripts/characterization/contact-sheet-families.ts`.) |
19
- | [`../../src/__tests__/characterization-families.test.ts`](../../src/__tests__/characterization-families.test.ts) | **Breadth** — the universal invariants extended across every family and public output surface, asserting only what holds per family (17 tests). |
20
- | [`visual-quality.md`](./visual-quality.md) | **Visual approval** — SVG snapshots, SVG/PNG fingerprints, and advisory graph-drawing metrics for all 12 families. (Generated by `scripts/characterization/visual-quality.ts`.) |
21
- | [`../../src/__tests__/characterization-generated-artifacts.test.ts`](../../src/__tests__/characterization-generated-artifacts.test.ts) | **Drift check** — fails when generated contact sheets, visual report, or SVG snapshots are stale. |
22
-
23
- No implementation code was changed to produce any of this.
24
-
25
- ## Visual testing rule
26
-
27
- Snapshots and contact sheets are approval evidence, not the whole oracle. When a
28
- bug report says something **looks** wrong, add at least one executable visual
29
- contract that would fail before updating any golden file. Pick the cheapest
30
- contract that observes the thing users actually see:
31
-
32
- | Visual risk | Preferred contract | Examples |
33
- |-------------|--------------------|----------|
34
- | Route geometry, ports, label lanes, overlaps | Geometry/rubric invariant over positioned layout | `route-contracts*.test.ts`, `layout-rubric.test.ts`, `contact-sheet.test.ts` hard metrics |
35
- | Effective color, stroke thickness, blank/collapsed render, visible endpoint glyphs | Raster invariant with `@resvg/resvg-js` pixels | `visual-rendering-contracts.test.ts`, `helpers/raster.ts` |
36
- | ASCII/Unicode line style or marker placement | Row/region assertion on the rendered character grid | `ascii-edge-styles.test.ts` connector-row contracts |
37
- | Broad family drift that needs human judgement | Golden/snapshot/contact sheet after an invariant already guards the principle | `visual-quality.md`, `visual-snapshots/*.svg`, PR screenshots |
38
- | Parser, escaping, config, IDs, public API shape | Structural/string assertion | Parser/security/integration tests |
39
-
40
- The red-green loop for visual work is:
41
-
42
- 1. Reproduce the bad rendering with the smallest diagram that shows the fault.
43
- 2. Encode the visual principle as geometry, raster, or row-level evidence.
44
- 3. Prove the test is sensitive: where practical, simulate the old behaviour or
45
- run a temporary source-level sabotage before trusting the green run.
46
- 4. Fix the implementation.
47
- 5. Regenerate approval artifacts only after the invariant test passes and the
48
- visual diff has been reviewed.
49
-
50
- ## Relationship to PR 30
51
-
52
- PR 30 changed the right baseline for layout characterisation. It introduced the
53
- route-contract layer (`src/route-contracts.ts`), the deterministic graph-drawing
54
- rubric (`src/layout-rubric.ts`), route/contact-sheet gates
55
- (`src/__tests__/contact-sheet.test.ts`, `src/__tests__/layout-rubric.test.ts`),
56
- and the heuristic tracker (`eval/heuristic-tracker/`). Those tests now own the
57
- question "is this flowchart/state/architecture route geometrically valid and
58
- free of hitches, stale certificates, off-outline endpoints, and label-route
59
- ambiguity?"
60
-
61
- This directory owns a different question: "what observable layout behaviour did
62
- we approve, and did it drift deliberately?" After rebasing this PR over PR 30,
63
- `bun run characterization:check` reported only the visual-quality report plus
64
- the flowchart/state SVG snapshots as stale. That is the intended signal:
65
- flowchart/state are exactly the families PR 30's route-contract work should
66
- move, while the other renderer-family snapshots stayed unchanged. The correct
67
- course is therefore to approve the new flowchart/state baselines after the PR 30
68
- route gates pass, rather than add duplicate route-contract assertions here.
69
-
70
- For future layout changes:
71
-
72
- | If this changes | Use this gate | Review action |
73
- |-----------------|---------------|---------------|
74
- | Flowchart/state/architecture routing geometry | `bun test src/__tests__/contact-sheet.test.ts src/__tests__/layout-rubric.test.ts` and `bun run track` | Hard rubric violations must stay zero; baseline deltas must be explained as improvement or intended drift. |
75
- | Generated characterisation artifacts | `bun run characterization:check` | Regenerate the contact sheets / visual snapshots only after reviewing the visual diff. |
76
- | ASCII grid mechanics | `bun test src/__tests__/characterization-layout.test.ts` plus `bun run mutation-test:characterization` | Update P-properties only when the algorithm deliberately changes. |
77
- | Renderer-family dispatch/output surfaces | `bun test src/__tests__/characterization-families.test.ts` | Drift outside the PR's claimed layout surface is suspicious and needs a separate explanation. |
78
-
79
- ## Coverage across renderers
80
-
81
- The **depth** layer targets the flowchart + `stateDiagram-v2` family (the
82
- hand-written grid + A\* engine — see next section for why). The **breadth** layer
83
- extends the universal invariants to every diagram family and public output
84
- surface: text (`renderMermaidASCII`), SVG (`renderMermaidSVG`), and PNG
85
- (`renderMermaidPNG`, which rasterizes the SVG path). It asserts only what is
86
- empirically true for each:
87
-
88
- | Invariant | Holds for |
89
- |-----------|-----------|
90
- | Dispatch totality (non-empty text/SVG and valid PNG signature) | **every** family and public output surface |
91
- | Sentinel label conservation | **every** family on text/SVG surfaces |
92
- | Determinism (byte-identical) | **every** family on text/SVG/PNG matrix cases (also pinned corpus-wide for ASCII by `ascii-determinism.test.ts`) |
93
- | Output hygiene (no `NaN` / `Infinity` / `undefined`, no ANSI when color is disabled) | **every** family on text/SVG surfaces |
94
- | No diagonals | **every** family on text output (xychart plots with block glyphs, not `/\`) |
95
- | Rectangular canvas (all rows one width) | box/graph families only: flowchart, state, sequence, class, er |
96
- | Layered placement, non-overlap, round-trip (P5–P10) | flowchart/state grid engine only |
97
-
98
- The chart, list, schedule, and architecture families (timeline, journey, xychart,
99
- pie, quadrant, gantt, architecture) emit ragged rows by design, so rectangularity is **not**
100
- asserted there — and that boundary is pinned as a known limit. The deep
101
- structural and layering properties are specific to the grid engine and do not
102
- generalise; each non-grid family has its own layout strategy (documented in
103
- `contact-sheet-families.md`) whose deep per-family invariants are the natural
104
- next increment.
105
-
106
- The visual-quality layer deliberately sits beside, not inside, the minimal
107
- property kernel. It records SVG snapshots, PNG hashes, crossings, bends, route
108
- length, area fill, label fit, and edge-label overlap risk for one canonical case
109
- per family. Those values are approval signals: a diff should be inspected and
110
- approved, not automatically interpreted as a semantic bug. PR 30's rubric is the
111
- hard correctness oracle for route-contract families; these snapshots are the
112
- cross-family drift ledger.
113
-
114
- ---
115
-
116
- ## What "the layout algorithm" means here
117
-
118
- This project has **two** distinct layout engines:
119
-
120
- 1. **ELK-based engine** (`src/layout-engine.ts`) — wraps Eclipse Layout Kernel's
121
- *layered* (Sugiyama) algorithm to produce pixel coordinates for **SVG/PNG**.
122
- The placement is a configured third-party black box; the bespoke part is the
123
- post-processing (layer snapping, edge bundling, margin routing, shape
124
- clipping).
125
- 2. **ASCII grid engine** (`src/ascii/grid.ts` + `pathfinder.ts` +
126
- `edge-routing.ts` + `edge-bundling.ts`) — a **hand-written** grid layout with
127
- **A\* orthogonal edge routing** that renders flowcharts and state diagrams as
128
- character art. It is a TypeScript port and extension of
129
- [`AlexanderGrooff/mermaid-ascii`](https://github.com/AlexanderGrooff/mermaid-ascii).
130
-
131
- **This characterisation targets the ASCII grid engine.** It is the genuinely
132
- "ours" algorithm (the ELK one delegates placement), it is pure / synchronous /
133
- deterministic / dependency-free, and it is the surface the project already
134
- mutation-tests (`stryker.ascii.config.json`). It is therefore the ideal
135
- characterisation subject. The flowchart and `stateDiagram-v2` families share
136
- this exact pipeline (`src/ascii/index.ts`); the other families (sequence, class,
137
- ER, pie, quadrant, xychart, timeline, journey, architecture) have their own
138
- renderers and are out of scope here.
139
-
140
- ### The pipeline in one line
141
-
142
- ```
143
- parseMermaid → convertToAsciiGraph → createMapping (grid + A*) → drawGraph → canvasToString
144
- ```
145
-
146
- `createMapping` (`grid.ts`) is the heart: it places each node as a **3×3 grid
147
- block**, assigns layers along the flow axis with a **stride of 4**, routes every
148
- edge with a **bounded, deterministic A\***, and bundles fan-in/fan-out edges onto
149
- shared trunks.
150
-
151
- ---
152
-
153
- ## How the layout works (the load-bearing mechanics)
154
-
155
- - **Grid model.** Every node owns a 3×3 block of an integer grid; `gridCoord` is
156
- its top-left cell, and the eight surrounding cells are edge attachment points.
157
- Blocks are placed 4 grid units apart (3 cells + a 1-cell routing gutter).
158
- - **Layer assignment.** Roots (nodes with no incoming edge) go at level 0;
159
- each child is placed one level (stride 4) along the **flow axis** (y for
160
- TD/BT, x for LR/RL). TD and LR are the same code with x/y transposed; **BT =
161
- TD then vertical flip**; **RL is currently treated as LR**.
162
- - **A\* routing.** Edges are routed cell-by-cell with A\* over the same grid that
163
- tracks node occupancy. Moves are 4-directional only (⇒ **orthogonal**, no
164
- diagonals); the heuristic is Manhattan distance + a corner penalty (prefers
165
- straight runs); ties break FIFO (⇒ **deterministic**); the search is bounded
166
- (⇒ **terminates**, returns `null` + a straight fallback if walled off).
167
- - **Edge bundling.** Sibling fan-out / fan-in edges of the same style merge onto
168
- a single shared trunk that splits at one junction; labelled siblings (which
169
- can't bundle) share a trunk prefix instead.
170
- - **Best-effort, not a solver.** For cyclic / dense graphs the multi-pass
171
- placement can drop or overlap nodes. That limitation is real, current, and
172
- pinned by property **P10** rather than hidden.
173
-
174
- A fuller mechanical anatomy (with `file:line` citations for all 30+ invariants)
175
- informed `properties.md`; the citations are inlined there.
176
-
177
- ---
178
-
179
- ## How this connects to the testing literature
180
-
181
- - **Characterisation tests** (Feathers, *WELC* ch. 13): tests that document the
182
- system's *actual* behaviour, not its intended behaviour — the safety net for
183
- refactoring. The contact sheet is the worked-example form; the property suite
184
- is the generalised form.
185
- - **Approval / golden tests** (Falco): the contact sheet is generated and
186
- reviewed as a diff — a surprising change is a regression, an expected change
187
- is the new approved baseline. The repo's `src/__tests__/testdata/` corpus is
188
- the byte-level golden layer beneath it.
189
- - **Property-based testing** (fast-check): the kernel asserts **invariants** and
190
- **metamorphic relations** rather than exact outputs, so it generalises across
191
- thousands of generated graphs instead of a handful of fixtures.
192
- - **Mutation testing** (Stryker, already configured): the oracle for *which*
193
- properties are load-bearing — a property is kept only if it kills a mutant no
194
- other test kills. See the minimality argument in `properties.md`.
195
- - **Visual approval / metric drift:** `visual-quality.md` keeps the literature's
196
- aesthetic objectives visible (crossings, bends, area, label overlap) without
197
- turning them into brittle universal laws.
198
-
199
- Together: **goldens characterise, properties generalise, mutation proves which
200
- assertions do work, visual snapshots make quality drift reviewable.**
201
-
202
- ---
203
-
204
- ## Reproduce
205
-
206
- ```bash
207
- bun install
208
-
209
- # Executable characterisation kernels
210
- bun test src/__tests__/characterization-layout.test.ts # grid engine (depth)
211
- bun test src/__tests__/characterization-families.test.ts # all families (breadth)
212
- bun test src/__tests__/characterization-generated-artifacts.test.ts
213
-
214
- # Regenerate the contact sheets / visual report, then review the diff and commit
215
- bun run scripts/characterization/contact-sheet.ts
216
- bun run scripts/characterization/contact-sheet-families.ts
217
- bun run scripts/characterization/visual-quality.ts
218
- git diff docs/layout-characterization/
219
-
220
- # CI-friendly generated-artifact drift check
221
- bun run characterization:check
222
-
223
- # Route-contract correctness gates introduced by PR 30
224
- bun test src/__tests__/contact-sheet.test.ts src/__tests__/layout-rubric.test.ts
225
- bun run track
226
-
227
- # (optional) quick mutation evidence for the load-bearing ranges
228
- bun run mutation-test:characterization
229
-
230
- # exhaustive but slow: mutates the whole ASCII layout core
231
- bun run mutation-test:ascii
232
- ```
233
-
234
- ## Sources
235
-
236
- Layered graph drawing: Sugiyama–Tagawa–Toda (1981); Gansner–Koutsofios–North–Vo
237
- (1993, the `dot` algorithm, <https://www.graphviz.org/documentation/TSE93.pdf>);
238
- Brandes–Köpf (2002). A\*: Hart–Nilsson–Raphael (1968). Orthogonal routing:
239
- Tamassia (1987); Wybrow–Marriott–Stuckey (2009),
240
- <https://users.monash.edu/~mwybrow/papers/wybrow-gd-2009.pdf>. ELK:
241
- <https://eclipse.dev/elk/>. ASCII prior art: mermaid-ascii
242
- (<https://github.com/AlexanderGrooff/mermaid-ascii>), Graph::Easy. Testing:
243
- Feathers, *Working Effectively with Legacy Code* (2004); Falco, ApprovalTests
244
- (<https://approvaltests.com/>); Bartocci et al., property-based mutation testing
245
- (2023, <https://arxiv.org/pdf/2301.13615>).