agentic-mermaid 0.1.2 → 0.3.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 (54) hide show
  1. package/AGENT_NATIVE.md +671 -0
  2. package/CHANGELOG.md +36 -0
  3. package/Instructions_for_agents.md +89 -0
  4. package/README.md +8 -8
  5. package/assets/hero.png +0 -0
  6. package/dist/agent-core.d.ts +2 -2
  7. package/dist/agent-core.js +2 -2
  8. package/dist/agent.d.ts +2 -2
  9. package/dist/agent.js +4 -4
  10. package/dist/agentic-mermaid-mcp.js +5 -5
  11. package/dist/am.js +8 -15
  12. package/dist/browser.global.js +777 -0
  13. package/dist/{chunk-WN3YDOBG.js → chunk-3QCXO4V6.js} +4 -4
  14. package/dist/{chunk-4QECACIA.js → chunk-AVCWKYII.js} +19 -19
  15. package/dist/{chunk-WI35L7IR.js → chunk-GILQUGFK.js} +2 -2
  16. package/dist/{chunk-QKDLMOJJ.js → chunk-HFD4MALA.js} +7 -9
  17. package/dist/{chunk-CDPKJ2BQ.js → chunk-UJNY65QS.js} +2 -2
  18. package/dist/{chunk-ZYMMKQ3I.js → chunk-WHYJLDYG.js} +299 -973
  19. package/dist/{index-D54dqSdC.d.ts → index-CLwYn6Im.d.ts} +7 -4
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/mcp-cli-SJVOGRA6.js +17 -0
  23. package/docs/README.md +35 -34
  24. package/docs/agent-api-cookbook.md +317 -0
  25. package/docs/agent-mutation-policy.md +104 -0
  26. package/docs/agent-workflow-examples.md +46 -0
  27. package/docs/api.md +418 -0
  28. package/docs/ascii.md +113 -0
  29. package/docs/browser.md +183 -0
  30. package/docs/code-mode-impact.md +214 -0
  31. package/docs/comparison.md +106 -0
  32. package/docs/computational-aesthetics-prototype-plan.md +252 -0
  33. package/docs/config.md +121 -0
  34. package/docs/custom-fonts.md +265 -0
  35. package/docs/custom-style-cookbook.md +275 -0
  36. package/docs/diagram-families.md +218 -0
  37. package/docs/features.md +154 -0
  38. package/docs/fork-differences.md +166 -0
  39. package/docs/getting-started.md +182 -0
  40. package/docs/issue-derived-test-cases.md +163 -0
  41. package/docs/mcp-code-mode-rationale.md +108 -0
  42. package/docs/mcp-http-transport.md +230 -0
  43. package/docs/mermaid-layout-complaints.md +851 -0
  44. package/docs/mutation-testing.md +285 -0
  45. package/docs/quality.md +224 -0
  46. package/docs/react.md +97 -0
  47. package/docs/style-authoring.md +336 -0
  48. package/docs/svg-semantic-contract.md +49 -0
  49. package/docs/testing-strategy.md +443 -0
  50. package/docs/theming.md +149 -0
  51. package/package.json +28 -9
  52. package/server.json +2 -2
  53. package/skills/agentic-mermaid-live-editor/SKILL.md +1 -1
  54. package/dist/mcp-cli-SBQSTJFC.js +0 -17
@@ -0,0 +1,851 @@
1
+ # Mermaid layout complaints: catalog, root causes, and coverage
2
+
3
+ Status: evidence and capability-coverage scorecard, not a roadmap or backlog.
4
+ `TODO.md` is the sole authority for scheduled work. Historical issues and specs
5
+ below retain candidate contract evidence only; they do not schedule
6
+ implementation unless a stable root TODO item promotes them.
7
+
8
+ This document maps what users actually complain about in Mermaid's layout →
9
+ why those failures happen → which Agentic Mermaid work addresses each one
10
+ (landed, retained as candidate evidence, or deliberately out of scope). It is
11
+ the durable version of a June 2026 research pass and should be updated when
12
+ complaint-relevant work lands.
13
+
14
+ Companions:
15
+
16
+ - [`issue-derived-test-cases.md`](./issue-derived-test-cases.md) — fixture-level
17
+ coverage map for specific upstream issues.
18
+ - [`quality.md`](./quality.md) — the project's definition of "good looking" and
19
+ its metric gates.
20
+ - [Issue #25](https://github.com/adewale/agentic-mermaid/issues/25) and
21
+ [issue #26](https://github.com/adewale/agentic-mermaid/issues/26) — the
22
+ principled routing/contract specs that several entries below point into.
23
+ - [PR #24](https://github.com/adewale/agentic-mermaid/pull/24) — the Gantt
24
+ spec and implementation, the model for chart-family layout contracts.
25
+ - [Issue #41](https://github.com/adewale/agentic-mermaid/issues/41) — the
26
+ diagram-family citizenship matrix that now keeps supported families honest
27
+ about parser, serializer, renderer, verifier, and test obligations.
28
+
29
+ Method: complaint evidence was gathered in June 2026 from the
30
+ `mermaid-js/mermaid` tracker (reaction counts "R" are as of that date), Hacker
31
+ News, Reddit, comparison blogs, and Mermaid's own documentation. Root-cause
32
+ evidence comes from the graph-drawing literature, competing products'
33
+ documentation, diagram interchange standards, and the recorded history of the
34
+ three codebases this project descends from (Mermaid, `mermaid-ascii`,
35
+ `beautiful-mermaid`).
36
+
37
+ Status legend used below:
38
+
39
+ | Status | Meaning |
40
+ |---|---|
41
+ | **landed** | shipped in this repo with tests |
42
+ | **partial** | some of the complaint class is covered, the rest is not |
43
+ | **candidate evidence** | described in a historical issue/spec, but unscheduled unless promoted to root `TODO.md` |
44
+ | **out of scope** | deliberately not addressed; the reason is stated |
45
+
46
+ ---
47
+
48
+ ## Part 1 — The complaint catalog
49
+
50
+ Thirteen recurring complaint classes, ordered roughly by loudness. Each entry
51
+ names its root causes (`R1`–`R5`, defined in Part 2) and its fixture/test
52
+ coverage in this repo.
53
+
54
+ ### C1. No manual positioning
55
+
56
+ Mermaid is auto-layout only: no coordinates, no pinning, no post-hoc nudge.
57
+ When the engine makes a bad call, the sanctioned workaround is invisible
58
+ `~~~` links — Mermaid's own docs describe them as the way "to alter the
59
+ default positioning of a node," and GitLab maintains a handbook page of such
60
+ tricks. This is the loudest and oldest theme.
61
+
62
+ - Evidence: [mermaid#270](https://github.com/mermaid-js/mermaid/issues/270)
63
+ (11R, open since 2016),
64
+ [mermaid#2483](https://github.com/mermaid-js/mermaid/issues/2483) (12R),
65
+ [mermaid#5420 "Add positioning for elk layout renderer"](https://github.com/mermaid-js/mermaid/issues/5420)
66
+ (47R, zero comments — pure demand). HN: "if you don't like the way the
67
+ diagram is visually laid out there's not really an easy way to change it"
68
+ ([41262756](https://news.ycombinator.com/item?id=41262756)).
69
+ - Root cause: R3 (missing control surface).
70
+ - Status here: **manual coordinates and post-hoc nudging remain out of scope.**
71
+ `BUILD-24` now owns a narrower response: typed, deterministic structural
72
+ constraints for rank/layer, adjacency, and edge-length preference. They are
73
+ render/mutation metadata, not Agentic-only Mermaid syntax, and therefore do
74
+ not contradict #25's rule against inventing route syntax. Deterministic
75
+ quality measurement and route certificates remain the feedback loop; the
76
+ hints let an agent state topology-preserving intent without deleting a real
77
+ edge. This still does not provide arbitrary node coordinates or dragging.
78
+
79
+ ### C2. Layout collapses at scale
80
+
81
+ Past roughly 20–50 nodes: whitespace oceans, extreme aspect ratios, nodes
82
+ unreadably small once the diagram is fit to screen.
83
+
84
+ - Evidence: [mermaid#1984 "Massive whitespace"](https://github.com/mermaid-js/mermaid/issues/1984)
85
+ (39R, open since 2021),
86
+ [mermaid#3262](https://github.com/mermaid-js/mermaid/issues/3262),
87
+ [mermaid#2807 "ER graphs tend to get REALLY wide"](https://github.com/mermaid-js/mermaid/issues/2807)
88
+ (19R). HN: "more and more acres of white space"
89
+ ([30781954](https://news.ycombinator.com/item?id=30781954)).
90
+ - Root cause: R1 (layered-algorithm sprawl), aggravated in Mermaid by R4
91
+ (dagre, fixed spacing constants).
92
+ - Fixture/test: `whitespaceBalance` (5–55% band) and `aspectRatio` (0.2–5.0
93
+ band) in `src/agent/quality.ts` flag both failure directions;
94
+ `eval/layout-compare/` reports deltas corpus-wide. Generated 20/50/100-node
95
+ flowchart corpora now assert finite aspect ratios and non-degenerate
96
+ whitespace in `src/__tests__/agent-quality.test.ts`.
97
+ - Status here: **partial — detection only.** Nothing in the backlog improves
98
+ large-graph compaction, and the source-order priority can *worsen* width:
99
+ `docs/quality.md` documents the Auth Flow trade, and
100
+ `src/__tests__/agent-auth-flow.test.ts` pins quality with a widened aspect
101
+ band (`[0.2, 7]`) for exactly that reason. One candidate mitigation that
102
+ needs no syntax invention: the standards-era answer to oversized charts
103
+ was never better compaction but **decomposition** — ANSI X3.5/ISO 5807
104
+ prescribe named on-page/off-page connectors to split a chart when it
105
+ outgrows a page or lines would cross. The agent-loop analogue: when
106
+ `checkQuality` flags an oversized diagram, propose or perform splitting
107
+ into multiple linked diagrams through the existing mutation API. Mermaid
108
+ has no continuation concept (users fake it with duplicated nodes), so
109
+ this would be a differentiator rather than a compatibility surface.
110
+
111
+ ### C3. Subgraphs: direction ignored, titles overlapped, clusters colliding
112
+
113
+ The most-reacted pure layout bug in Mermaid's tracker. `direction` inside a
114
+ subgraph is silently ignored the moment any inner node links outside —
115
+ documented as a limitation, unfixed since 2021. Titles get covered by nodes;
116
+ sibling clusters overlap.
117
+
118
+ - Evidence: [mermaid#2509](https://github.com/mermaid-js/mermaid/issues/2509)
119
+ (189R, 50 comments),
120
+ [mermaid#3806 title overlap](https://github.com/mermaid-js/mermaid/issues/3806)
121
+ (54R), [mermaid#4990 cluster overlap](https://github.com/mermaid-js/mermaid/issues/4990)
122
+ (32R), [mermaid#287](https://github.com/mermaid-js/mermaid/issues/287) (29R).
123
+ - Root cause: R1 (compound layout is genuinely hard), R4 (dagre's cluster
124
+ support is weak; title boxes not reserved during layout).
125
+ - Fixture/test: `src/__tests__/subgraph-direction.test.ts` pins SVG geometry
126
+ for `direction TB` inside an LR flowchart *with an external link to an
127
+ inner node* — the exact mermaid#2509 case — plus ASCII one-row inner LR
128
+ layout, and container edges attaching to the subgraph border without
129
+ phantom node boxes (BUILD-14), in both Unicode and 7-bit ASCII, nested,
130
+ labeled/styled, and multi-edge variants. Group titles reserve layout space
131
+ via the header-aware `elk.padding` in `src/layout-engine.ts`; breaches are
132
+ a Tier 1 `GROUP_BREACH` warning.
133
+ - Status here: **landed** (PRs #17, #21, #22). Residual: cross-hierarchy and
134
+ container-edge *route classes* — making the SVG router treat boundary
135
+ edges semantically rather than via representative children — remain candidate
136
+ contract evidence in #25 §8.3 and #26 workstream 2, not scheduled work.
137
+
138
+ ### C4. Erratic edge routing: avoidable crossings, overlaps, doglegs
139
+
140
+ Here and in the scorecard, **C4 means complaint category 4**, not Mermaid's C4
141
+ diagram family.
142
+
143
+ Edges cross when they obviously need not, overlap each other or pass through
144
+ nodes, and acquire small unexplained doglegs ("hitches"). Users untangle by
145
+ trial-and-error reordering of source lines.
146
+
147
+ - Evidence: [mermaid#6476 "Unnecessary crossing"](https://github.com/mermaid-js/mermaid/issues/6476),
148
+ [mermaid#5601 planar graphs rendered non-planar](https://github.com/mermaid-js/mermaid/issues/5601),
149
+ [mermaid#1006](https://github.com/mermaid-js/mermaid/issues/1006) ("it often
150
+ really handles edges in an erradic way" — closed *Wont Fix*),
151
+ [mermaid#5060](https://github.com/mermaid-js/mermaid/issues/5060),
152
+ [mermaid#2792 edges overlap boxes](https://github.com/mermaid-js/mermaid/issues/2792).
153
+ - Root cause: R1 (crossing minimization is NP-hard, solved per-layer by
154
+ heuristic; routing is a late phase that cannot revisit placement), R4
155
+ (dagre routes splines without obstacle avoidance), R5 (post-layout passes
156
+ move geometry without rerouting).
157
+ - Fixture/test: the worked example is issue #25's login/MFA flow. Rendered
158
+ with the current engine (June 2026, ASCII):
159
+
160
+ ```
161
+ | User |---->| Login Page |<No->| Valid Credentials? |-Yes>| MFA Enabled? |----No-->| Create Session | Yes-+--| Code Valid? |
162
+ ... | +-----No-------+ |
163
+ ... +------Yes------>| Enter MFA Code |----------------+
164
+ ```
165
+
166
+ `F --> G` (`Code Valid?` → `Create Session`) runs backwards; the `No`
167
+ feedback loops read ambiguously inline. Yet `verifyMermaid` returns
168
+ `ok: true` with zero warnings, and every perceptual metric passes except
169
+ aspect ratio (6.23 vs the 5.0 band): `edgeCrossings: 0`,
170
+ `labelLegibility: 1`. That is the measured gap between today's generic
171
+ metrics and the visible failure — the gap #25/#26 exist to close.
172
+ Current coverage: `edgeCrossings` gate (≤5% of edge pairs),
173
+ `ROUTE_SELF_CROSS` Tier 2 warning, edge-vs-node collision heuristics in
174
+ `src/__tests__/layout-quality-heuristics.test.ts`, deterministic FIFO A*
175
+ tie-breaking in the ASCII pathfinder
176
+ (`src/__tests__/ascii-pathfinder-determinism.test.ts`).
177
+ - Status here: **partial today, with the route-contract core landed and the
178
+ broader program retained as candidate evidence.** #25 defines route classes
179
+ (`primary-forward`/`feedback`/fan trunks/…), the straight-primary and
180
+ hitch invariants (§11.1–11.2), the certifying simplifier (§12), and the
181
+ hard rule "no node-coordinate mutation after route extraction unless all
182
+ incident routes are rerouted or recertified" (§9). The current tests now
183
+ include route-certificate geometry and property coverage across dotted,
184
+ thick, bidirectional, marker, and labeled edge forms. The still-open
185
+ route/port issues (#32, #35, #38 and the #26 umbrella) retain unscheduled
186
+ evidence; any implementation or cross-renderer adoption requires promotion
187
+ to root `TODO.md`.
188
+
189
+ ### C5. Edge labels overlap edges, nodes, and each other
190
+
191
+ Mermaid places labels at edge midpoints with no collision handling: labels
192
+ sit on other edges, get struck through by their own line, or detach from the
193
+ edge they describe.
194
+
195
+ - Evidence: [mermaid#490](https://github.com/mermaid-js/mermaid/issues/490)
196
+ (2017 — labels struck through by their own edge, asking for "integrated
197
+ logic which tries to avoid lines going over the texts": literally the
198
+ integrated-labeling concept yFiles ships, see R3),
199
+ [mermaid#2131 mis-aligned state labels](https://github.com/mermaid-js/mermaid/issues/2131),
200
+ [mermaid#2525 "messages appear with line through them"](https://github.com/mermaid-js/mermaid/issues/2525),
201
+ [mermaid#1233](https://github.com/mermaid-js/mermaid/issues/1233).
202
+ - Root cause: R1 (label placement is NP-hard and classically solved *after*
203
+ layout; dagre models labels as mid-edge dummy nodes), R4/R5 (renderers do
204
+ not collision-check label boxes afterwards).
205
+ - Fixture/test: this engine passes edge labels into layout as first-class
206
+ inline ELK labels (`elk.edgeLabels.inline: 'true'`,
207
+ `src/layout-engine.ts`), so they reserve space instead of being overlaid;
208
+ `labelEdgeProximity` (≥4px to any non-attached node, other edge-label
209
+ box, or other edge path) gates regressions in the graph families, and the
210
+ 2026-07 overlap audit added a cross-family occlusion gate
211
+ (`eval/overlap-audit` + `label-overlap-gate.test.ts`: curated corpus at
212
+ zero label/box overlap findings, per-family fuzz ratchets) after finding
213
+ label collisions the proximity metric alone had missed in five families.
214
+ ASCII branch labels are pinned to sibling branch segments, not shared
215
+ trunks: `src/__tests__/ascii-fanout-trunk-labeled.test.ts`,
216
+ `ascii-pathfinder-trunk.test.ts`, plus exact goldens under
217
+ `src/__tests__/testdata/{ascii,unicode}/`.
218
+ - Status here: **partial.** Landed: label-aware layout + proximity gate +
219
+ ASCII branch-label placement. Specced: the label invariant (#25 §11.4 —
220
+ labels are obstacles *and* capacity constraints; trunks may not host
221
+ per-sibling labels) and label-capacity checks before straightening (§12).
222
+
223
+ ### C6. No rank/order control; tiny edits reshuffle the diagram
224
+
225
+ Users cannot say "these two nodes at the same level" or "keep my declared
226
+ order," and adding one edge can scramble a previously good layout — which
227
+ also makes diagrams churn across edits.
228
+
229
+ - Evidence: [mermaid#3723 same-rank request](https://github.com/mermaid-js/mermaid/issues/3723)
230
+ (86R — the most-upvoted open layout-control ask),
231
+ [mermaid#815 "Maintain the order of the nodes"](https://github.com/mermaid-js/mermaid/issues/815)
232
+ (45R), [mermaid#2834](https://github.com/mermaid-js/mermaid/issues/2834),
233
+ [mermaid#6527 "flowchart-elk … reshuffled every time you add a node"](https://github.com/mermaid-js/mermaid/issues/6527).
234
+ - Root cause: R3 (no constraint surface) over R1 (layered layouts are
235
+ input-order sensitive unless order is made an explicit objective).
236
+ - Fixture/test: source order *is* the constraint surface here:
237
+ `elk.layered.considerModelOrder.strategy: NODES_AND_EDGES`,
238
+ `cycleBreaking.strategy: MODEL_ORDER`, and
239
+ `crossingMinimization.forceNodeModelOrder: true`
240
+ (`src/layout-engine.ts`) make author order the layout's primary input.
241
+ `src/__tests__/agent-auth-flow.test.ts` asserts the primary LR chain stays
242
+ in source order (`A < B < … < H` by x-center) and feedback edges route
243
+ backwards — the mermaid#5227 "backwards arrows" class. Edit stability is
244
+ the determinism guarantee plus typed mutation: a one-op change re-lays-out
245
+ as a pure function of the new source
246
+ (`src/__tests__/agent-determinism.test.ts`). The same file now pins
247
+ edit-stability for small source mutations: label edits, inserted nodes,
248
+ appended leaves, style-only changes, and feedback-edge additions must
249
+ preserve the left-to-right order of the unchanged primary chain.
250
+ - Status here: **partial.** Landed: deterministic source-order layout (the
251
+ practical answer to #815/#6527). Not offered: an explicit same-rank
252
+ constraint (#3723's literal ask) — that is C1's policy decision again.
253
+ Graphviz has had `rank=same` since the 1990s (see R3); if Mermaid core
254
+ ever standardizes an equivalent, this fork's policy is to follow, not
255
+ invent.
256
+
257
+ ### C7. "It's just ugly": dated default aesthetics
258
+
259
+ An aesthetic judgment independent of geometric correctness: default colors,
260
+ shapes, curves, and contrast read as dated next to D2 or hand-drawn
261
+ diagrams; some embeddings are unreadable (dark-theme contrast).
262
+
263
+ - Evidence: HN "why does it have to be so ugly"
264
+ ([30339032](https://news.ycombinator.com/item?id=30339032)); "Both PlantUML
265
+ and Mermaid mostly produce (Mermaid more) ugly-looking diagrams"
266
+ ([33704875](https://news.ycombinator.com/item?id=33704875));
267
+ [Cursor forum on unreadable contrast](https://forum.cursor.com/t/mermaid-diagrams-are-hard-to-read-poor-contrast/153850).
268
+ - Root cause: R5 (renderer/theming defaults, not the layout algorithm).
269
+ Mermaid's v11 `handDrawn`/`neo` "looks" are its own remediation.
270
+ - Fixture/test: inherited from upstream Beautiful Mermaid: two-color theme
271
+ derivation, 21 built-in themes, Shiki/VS Code theme compatibility. Fork
272
+ additions: Style + Palette stacks, Tufte/Salmon theme families (PR #10),
273
+ auto-contrast on custom fills
274
+ (upstream #115 class) pinned by
275
+ `src/__tests__/renderer-contrast.test.ts`.
276
+ - Status here: **landed** (largely upstream's credit; see
277
+ [`comparison.md`](./comparison.md)).
278
+
279
+ ### C8. Text clipped, overflowing, or hand-wrapped
280
+
281
+ Node boxes don't reliably fit their text. In Mermaid the classic causes are
282
+ HTML labels in `foreignObject` (SVG exports drop them), browser font metrics
283
+ at layout time, and no width budget for CJK/long words.
284
+
285
+ - Evidence: [mermaid#58](https://github.com/mermaid-js/mermaid/issues/58)
286
+ ("Generated SVG works poorly outside web browsers" — opened December
287
+ 2014, six weeks after Mermaid's first release, still open),
288
+ [mermaid#2688 foreignObject](https://github.com/mermaid-js/mermaid/issues/2688)
289
+ (24R), [mermaid#790](https://github.com/mermaid-js/mermaid/issues/790),
290
+ [mermaid#4918](https://github.com/mermaid-js/mermaid/issues/4918),
291
+ [mermaid#7359 Korean clipped on export](https://github.com/mermaid-js/mermaid/issues/7359);
292
+ GitLab converted diagrams to tables rather than fight it
293
+ ([gitlab#583608](https://gitlab.com/gitlab-org/gitlab/-/issues/583608)).
294
+ - Root cause: R5 (browser-first text pipeline), R2 for the ASCII lineage
295
+ (`string.length` width assumptions on fullwidth text).
296
+ - Fixture/test: structurally avoided here — text is plain SVG `<text>`
297
+ measured by deterministic metrics (`src/text-metrics.ts`), never
298
+ `foreignObject`; PNG embeds pinned fonts (DejaVu default plus the built-in style faces) so rasterization cannot
299
+ font-substitute. Guards: Tier 1 `LABEL_OVERFLOW`, `labelLegibility` ≥85%
300
+ gate, CJK fullwidth handling
301
+ (`src/__tests__/ascii-cjk-width.test.ts`, upstream #119 class), ER/class
302
+ label truncation fixes with exact goldens (upstream #121 class,
303
+ `er-integration.test.ts`, `class-integration.test.ts`).
304
+ - Status here: **landed**, with an honest gap: the 7px/char SVG heuristic is
305
+ approximate (`docs/quality.md`), and the shared text-measurement contract
306
+ that would unify SVG/ASCII/quality measurement (CJK, emoji, ambiguous
307
+ width) remains candidate evidence in #26 workstream 8. Regression coverage
308
+ now includes
309
+ emoji-presentation selectors and East Asian ambiguous-width symbols, so
310
+ common Unicode labels cannot silently drift between SVG metrics and
311
+ terminal-width assumptions.
312
+
313
+ ### C9. Stuck on dagre; ELK bolted on as an escape hatch
314
+
315
+ The meta-complaint: Mermaid's default engine is dagre — widely described as
316
+ abandoned — and the community's fix (ELK) is opt-in, a separate package,
317
+ unavailable in GitHub's native rendering, and not itself a cure-all.
318
+
319
+ - Evidence: "Dagre is pretty good but I think ELK is better and Dagre has
320
+ been abandoned"
321
+ ([r/programming](https://www.reddit.com/r/programming/comments/z1qoxc/d2_is_now_open_source_a_new_modern_language_that/ixlwqce/));
322
+ Mermaid docs: "The elk renderer is better for larger and/or more complex
323
+ diagrams" ([flowchart docs](https://mermaid.js.org/syntax/flowchart.html));
324
+ HN: "the layout renderer is pretty bad … the layout algorithm is so bad"
325
+ ([41277795](https://news.ycombinator.com/item?id=41277795)).
326
+ - Root cause: R4.
327
+ - Status here: **landed structurally.** ELK layered is this engine's only
328
+ graph layout (synchronous bundled `elkjs`, `src/elk-instance.ts`), with
329
+ pinned options (`src/layout-engine.ts`) and #25 §10 as the documented
330
+ option policy — node placement is Brandes–Köpf, the same default
331
+ Mermaid's own ELK package exposes via `nodePlacementStrategy`, so
332
+ opting into `flowchart-elk` in Mermaid lands on the placement family this
333
+ engine uses by default. Two honest caveats: (1) ELK is not a panacea — upstream
334
+ #83 (TD flowcharts flipping horizontal) was *ELK cycle-breaking* behavior,
335
+ countered here by model-order options; (2) none of this changes what
336
+ GitHub/GitLab render natively (see Part 4).
337
+
338
+ ### C10. Self-loops and back-edges render awkwardly
339
+
340
+ A node linking to itself draws a kinked doubled-back path; loops in flows
341
+ take long unexplained detours.
342
+
343
+ - Evidence: [mermaid#6336](https://github.com/mermaid-js/mermaid/issues/6336)
344
+ (31R; the issue contrasts Graphviz doing it "So much more natural"),
345
+ [mermaid#6049](https://github.com/mermaid-js/mermaid/issues/6049) (11R —
346
+ also a version regression: "pretty" in 11.0.2, "Ugly since 11.1.1").
347
+ - Root cause: R1/R4 (layered frameworks have no native self-loop concept;
348
+ the route is synthesized late).
349
+ - Fixture/test: ASCII self-loops currently render compactly (verified June
350
+ 2026: `B --> B` draws a tight two-cell side loop). `ROUTE_SELF_CROSS`
351
+ (Tier 2) and self-loop clearance heuristics
352
+ (`layout-quality-heuristics.test.ts`) guard regressions. SVG self-loop
353
+ port policy (N/S side by clearance) is #25 §8.1; mermaid#6049 is listed as
354
+ candidate fixture evidence in [`issue-derived-test-cases.md`](./issue-derived-test-cases.md).
355
+ - Status here: **partial** (ASCII decent + guarded; SVG semantics retained as
356
+ candidate evidence, not scheduled).
357
+
358
+ ### C11. Unstable output across versions and renderers
359
+
360
+ The same source renders differently in the live editor, CLI, GitHub, and
361
+ Obsidian, and upgrades silently regress committed diagrams.
362
+
363
+ - Evidence: [mermaid#5813 v11 regression](https://github.com/mermaid-js/mermaid/issues/5813)
364
+ (28R), [mermaid#1485 live editor vs CLI](https://github.com/mermaid-js/mermaid/issues/1485),
365
+ [mermaid#5969 editor silently uses ELK](https://github.com/mermaid-js/mermaid/issues/5969),
366
+ [gitlab#583608](https://gitlab.com/gitlab-org/gitlab/-/issues/583608),
367
+ [Obsidian forum "Mermaid arrows are a mess"](https://forum.obsidian.md/t/mermaid-arrows-are-a-mess/104633).
368
+ - Root cause: R5 (browser/CSS/version-coupled rendering) plus R1 (layout
369
+ sensitivity amplifies small engine changes into big visual diffs).
370
+ - Fixture/test: this is the fork's categorical answer rather than a
371
+ mitigation. Byte-identical output across runs, processes, and runtimes is
372
+ CI-gated (`agent-determinism.test.ts`); ASCII output is hash-stable across
373
+ the full 271-entry docs corpus (`ascii-determinism.test.ts`); PNG bytes
374
+ are pinned via exact-version resvg + bundled fonts
375
+ (`agent-png-determinism.test.ts`). Intentional layout changes must pass
376
+ the before/after comparison harness (`eval/layout-compare/`,
377
+ regressions-first verdicts) and update exact goldens — so every visual
378
+ change is deliberate, evidenced, and reviewed.
379
+ - Status here: **landed.** No Mermaid-syntax renderer we know of makes this
380
+ guarantee.
381
+
382
+ ### C12. Sequence-diagram alignment and overlap
383
+
384
+ Sequence diagrams (bespoke layouter in Mermaid, not dagre) have their own
385
+ class: uncontrollable/overlapping activations, off-center `alt`/`loop`
386
+ titles, notes colliding with lifelines.
387
+
388
+ - Evidence: [mermaid#1765 activations](https://github.com/mermaid-js/mermaid/issues/1765)
389
+ (28R), [mermaid#3216](https://github.com/mermaid-js/mermaid/issues/3216),
390
+ [mermaid#7651](https://github.com/mermaid-js/mermaid/issues/7651),
391
+ [mermaid#7682](https://github.com/mermaid-js/mermaid/issues/7682).
392
+ - Root cause: R5 (hand-rolled per-family geometry, no shared invariants) +
393
+ R3 (no spacing overrides).
394
+ - Fixture/test: layout geometry tests exist
395
+ (`src/__tests__/sequence-layout.test.ts`, `ascii-sequence-blocks.test.ts`)
396
+ and the sequence `RenderedLayout` adapter feeds the generic metrics.
397
+ Sequence property coverage now checks activation spans, note stacking and
398
+ bounds, and block/divider containment/non-overlap geometry over generated
399
+ activation/note/block combinations. The broader productized verifier set
400
+ — lifeline x-order, message y-order, activation span containment, and
401
+ note/block containment as user-facing warnings — remains #25 §14.2 and
402
+ #26 workstream 11.
403
+ - Status here: **partial, with first family-specific properties landed.**
404
+
405
+ ### C13. Chart families: hard-coded geometry breaks on real data
406
+
407
+ Gantt axis dates overlap on long ranges, pie titles/labels clip, and radar axis
408
+ labels compress. These are
409
+ not dagre problems — each chart type has bespoke geometry with no collision
410
+ handling.
411
+
412
+ - Evidence: [mermaid#1301 gantt axis overlap](https://github.com/mermaid-js/mermaid/issues/1301)
413
+ (23R), [mermaid#6232 pie title cut off](https://github.com/mermaid-js/mermaid/issues/6232),
414
+ and [mermaid#7683 radar labels clipped](https://github.com/mermaid-js/mermaid/issues/7683).
415
+ - Root cause: R5 (per-family renderer-first geometry; semantics inferred at
416
+ draw time — the exact anti-pattern #26's guiding principle names).
417
+ - Fixture/test: pie and quadrant landed source-level with geometry tests
418
+ (PR #22; quadrant point placement verified against the upstream Mermaid
419
+ reference). Gantt is now implemented from PR #24's principled model — a
420
+ pure, clock-free schedule resolver *before* layout, family validators
421
+ (deterministic task intervals, milestone zero-width markers,
422
+ non-overlapping compact rows) designed against precisely the mermaid#1301
423
+ class. Mindmap now has deterministic tree geometry; Radar has deterministic
424
+ radial layout, bounded labels, ring ticks, and cross-format render contracts.
425
+ - Status here: **covered for every registered chart family**; remaining work is
426
+ tracked as family-specific quality improvement, not missing Radar support.
427
+
428
+ ---
429
+
430
+ ## Part 2 — Root causes
431
+
432
+ Five root causes explain the catalog. R1–R3 apply to every auto-layout
433
+ text-to-diagram tool, including this one; R4–R5 are specific lineages.
434
+
435
+ ### R1. The layered framework solves NP-hard problems in irreversible phases
436
+
437
+ Every engine in this story — dagre, ELK, dot — descends from the layered
438
+ method of
439
+ [Sugiyama, Tagawa & Toda 1981](https://doi.org/10.1109/TSMC.1981.4308636)
440
+ (IEEE Trans. SMC-11(2)), extended since into the standard pipeline: break a
441
+ directed graph drawing into sequential phases (cycle removal → layer
442
+ assignment → crossing minimization → coordinate assignment → edge
443
+ routing). The framework is powerful and is the right baseline for flow-like
444
+ diagrams, but it has three structural consequences users experience
445
+ directly:
446
+
447
+ 1. **The subproblems are intractable, so every phase is heuristic.**
448
+ Crossing number is NP-complete
449
+ ([Garey & Johnson 1983](https://dl.acm.org/doi/10.1137/0604033), SIAM
450
+ J. Alg. Disc. Meth. 4(3)); crossing minimization stays NP-complete even
451
+ for two layers with one layer's order already fixed
452
+ ([Eades & Wormald 1994](https://link.springer.com/article/10.1007/BF01187020),
453
+ Algorithmica 11(4)) — the exact subproblem inside every layered engine;
454
+ label placement is NP-complete even in simple variants
455
+ ([Marks & Shieber 1991](https://dash.harvard.edu/entities/publication/73120379-247a-6bd4-e053-0100007fdf3b),
456
+ Harvard TR-05-91), and edge-label placement specifically is NP-hard
457
+ ([Kakoulis & Tollis 2001](https://www.sciencedirect.com/science/article/pii/S0925772100000250),
458
+ Comput. Geom. 18(1)). No engine "chooses" the avoidable crossing in
459
+ complaint C4 — it fails to find the better optimum it cannot afford to
460
+ search for.
461
+ 2. **Phases cannot revisit earlier decisions.** Layering fixes ranks before
462
+ crossing minimization sees them; coordinates are assigned before routing.
463
+ A route that needs a node nudged one lane over cannot get it. This is
464
+ the literature's own characterization of the framework's failure mode —
465
+ "decisions made at previous steps influence later steps and yet cannot
466
+ be undone"
467
+ ([Healy & Nikolov, *Handbook of Graph Drawing and Visualization* ch. 13](https://cs.brown.edu/people/rtamassi/gdhandbook/chapters/hierarchical.pdf));
468
+ "since layer assignment and crossing reduction are realized as
469
+ independent steps, the resulting drawing might have many unnecessary
470
+ crossings caused by an unfortunate layer assignment"
471
+ ([Chimani, Gutwenger, Mutzel & Wong, JGAA 15(1), 2011](https://jgaa.info/index.php/jgaa/article/view/paper220/2748)).
472
+ It is why locally-reasonable pipelines emit globally wrong results — the
473
+ "hitch" of issue #25 is a phase-boundary artifact, and #25 §2 names it a
474
+ *contract failure*: nothing in the pipeline encodes "this edge should be
475
+ straight when its lane is clear" as an invariant any phase must preserve.
476
+ 3. **Labels and shapes are afterthoughts in the classic formulation.**
477
+ Labels enter as dummy nodes or post-hoc annotations (C5); non-rectangular
478
+ shapes are approximated by their bounding boxes until a final clipping
479
+ pass (#25 principle 6).
480
+
481
+ Empirical work confirms users are right to care:
482
+ [Purchase 1997](https://link.springer.com/chapter/10.1007/3-540-63938-1_67)
483
+ (Graph Drawing, LNCS 1353) found reducing edge crossings "by far the most
484
+ important" aesthetic for human comprehension, with bend minimization also
485
+ significant — exactly the two artifacts the phase structure produces.
486
+ Compound (subgraph) layout multiplies the difficulty: nested clusters with
487
+ cross-cluster edges constrain layering and crossing minimization
488
+ simultaneously
489
+ ([Sander 1996](https://publikationen.sulb.uni-saarland.de/bitstream/20.500.11880/25862/1/tr-A03-96.pdf),
490
+ TR A/03/96, Universität des Saarlandes), which is why C3 is hard for
491
+ everyone — and weak cluster handling is dagre's most visible gap (open for
492
+ years in its own tracker:
493
+ [dagre#125](https://github.com/dagrejs/dagre/issues/125),
494
+ [dagre#238](https://github.com/dagrejs/dagre/issues/238)).
495
+
496
+ ### R2. Greedy per-edge grid routing in the ASCII lineage
497
+
498
+ [`mermaid-ascii`](https://github.com/AlexanderGrooff/mermaid-ascii)
499
+ (Alexander Grooff, Go) works in three stages — parse, map components onto a
500
+ character grid, route each edge independently by pathfinding — and this
501
+ repo's ASCII engine is a TypeScript port of it
502
+ (`src/ascii/edge-routing.ts` is "ported from
503
+ AlexanderGrooff/mermaid-ascii cmd/direction.go + cmd/mapping_edge.go").
504
+ Per-edge greedy routing with no shared intent produces the ASCII-specific
505
+ complaint classes visible in both trackers: in the original,
506
+ single-edge-per-node-pair limits, floating arrowheads on labeled
507
+ back-edges, and East Asian width breakage
508
+ ([mermaid-ascii#56](https://github.com/AlexanderGrooff/mermaid-ascii/issues/56),
509
+ [#63](https://github.com/AlexanderGrooff/mermaid-ascii/issues/63),
510
+ [#59](https://github.com/AlexanderGrooff/mermaid-ascii/issues/59)); in
511
+ this lineage, equal-cost path nondeterminism, labels landing on shared
512
+ detours instead of their own branch (upstream #111/#112), fan-in groups
513
+ merging into ambiguous trunks (#68/#69), connectors floating off node
514
+ borders, phantom boxes for subgraph-id endpoints, and fullwidth/CJK text
515
+ breaking column math (#119, #121) — inherited and then fixed here
516
+ case-by-case. The durable fix is the same as for R1: route intent shared
517
+ across edges before geometry (#25 §13 — ASCII consumes route classes;
518
+ trunks, reserved feedback channels, label capacity).
519
+
520
+ ### R3. Layout is authored content everywhere except text-to-diagram tools
521
+
522
+ The standards world treats positions as part of the artifact:
523
+ [OMG Diagram Definition](https://www.omg.org/spec/DD/) distinguishes
524
+ "graphical information … users have control over, such as position of
525
+ nodes and line routing points," which is "captured for interchange between
526
+ tools," from styling fixed by the notation — and its predecessor UML DI
527
+ spec states the failure mode plainly: without diagram interchange, "all
528
+ diagram information is lost" when a model moves between tools. BPMN DI is a
529
+ normative part of BPMN 2.0
530
+ ([ISO/IEC 19510](https://www.iso.org/standard/62652.html)): "the
531
+ interchange of laid out shapes and edges," carrying shape bounds and edge
532
+ waypoints. UML 2.5 normatively defines notation but is silent on layout,
533
+ delegating persistence to the DD-based UML DI annex. Even drafting
534
+ conventions were standardized:
535
+ [ANSI X3.5-1970 §4.4.1](https://www.bitsavers.org/pdf/ansi/X3/X3.005-1970_Flowchart_Symbols_and_Their_Usage_in_Information_Processing.pdf)
536
+ is normative that "normal direction of flow is from left to right and top
537
+ to bottom," with arrowheads *required* only on reverse-direction lines —
538
+ and its successor [ISO 5807:1985](https://www.iso.org/standard/11955.html)
539
+ keeps the conditional-arrowhead rule (§9.3.1: "solid or open arrowheads
540
+ shall be added to indicate direction of flow where necessary (see
541
+ 10.2.1.2)"; clause 10 "Conventions" carries the direction rules). Issue
542
+ #25's primary-forward/feedback route classes are that half-century-old
543
+ convention, restated as a checkable invariant — and the same convention
544
+ family yields further validator candidates (unlabeled decision branches;
545
+ single-entry/labeled-exit analysis), tracked in
546
+ [`issue-derived-test-cases.md`](./issue-derived-test-cases.md).
547
+
548
+ Mature tools likewise expose constraint surfaces. Graphviz dot has had
549
+ [`rank=same`/`min`/`max`](https://graphviz.org/docs/attrs/rank/),
550
+ [compass-point ports](https://graphviz.org/docs/attr-types/portPos/)
551
+ (`a:port:nw -> b`), per-edge
552
+ [`constraint`](https://graphviz.org/docs/attrs/constraint/)/[`weight`](https://graphviz.org/docs/attrs/weight/)
553
+ knobs, and obstacle-avoiding spline routing
554
+ ([`splines=true`: "edges are drawn as splines routed around nodes"](https://graphviz.org/docs/attrs/splines/))
555
+ since the 1990s. D2 ships [`near`](https://d2lang.com/tour/positions/)
556
+ (anchor a shape near a named other shape),
557
+ [grid diagrams](https://d2lang.com/tour/grid-diagrams/) for exact
558
+ row/column placement, and the architecture-tuned
559
+ [TALA engine](https://d2lang.com/tour/tala/) — "first-class consideration
560
+ for containers," position locking via `top`/`left`, per-container
561
+ direction, dynamic label positioning (and, honestly documented, its own
562
+ instability: a small label change "can cascade into an entirely different
563
+ layout"). yFiles distinguishes post-hoc "generic" labeling from
564
+ [*integrated* labeling computed during layout](https://docs.yworks.com/yfiles-html/dguide/layout/label_placement.html),
565
+ which "prevent[s] label overlaps completely."
566
+
567
+ Text-to-diagram tools deliberately drop that half of the artifact: source
568
+ in, geometry recomputed every render, nothing persisted, no constraints.
569
+ That is the actual root of C1 and C6 — not dagre. It is also a real trade
570
+ (zero-friction authoring, diffable sources, no stale coordinates), which is
571
+ why this project keeps the contract but changes who absorbs the cost: the
572
+ layout is a *deterministic function* of source (C6, C11), its quality is
573
+ *measurable* (`checkQuality`), and its decisions are to become
574
+ *explainable* (#25 certificates) — so an editing program gets back the
575
+ agency a human loses by not being able to drag nodes.
576
+
577
+ ### R4. dagre: a partial dot port, frozen
578
+
579
+ Mermaid (2014, Knut Sveidqvist) was built browser-first on d3 + dagre-d3;
580
+ layout was delegated wholesale to dagre, Chris Pettitt's JavaScript
581
+ implementation of the Sugiyama/dot pipeline — a delegation Mermaid's
582
+ maintainer named himself when opening
583
+ [mermaid#867 "Support multiple layout algorithms"](https://github.com/mermaid-js/mermaid/issues/867)
584
+ in 2019: "Currently the layout mechanism mermaid is using is a dagre
585
+ algorithm."
586
+
587
+ What dagre ports — and what it omits — maps directly onto the catalog. The
588
+ dot paper
589
+ ([Gansner, Koutsofios, North & Vo 1993](https://www.graphviz.org/documentation/TSE93.pdf),
590
+ IEEE TSE 19(3)) is a *four*-pass algorithm: ranking, ordering, coordinates,
591
+ then a spline-routing pass that computes piecewise-Bézier edges inside a
592
+ feasible region so they avoid node obstacles — the paper explicitly calls
593
+ out tools that "make no attempt to avoid situations where line segments
594
+ overlap unrelated nodes." dot also guarantees that edge labels, modeled as
595
+ virtual nodes, "never overlap other nodes, edges or labels," and exposes
596
+ rank constraints. dagre implements the first three passes (its coordinate
597
+ assignment is
598
+ [Brandes & Köpf 2001](https://link.springer.com/chapter/10.1007/3-540-45848-4_3),
599
+ in `lib/position/bk.js`) and then stops: there is no routing phase at all —
600
+ edges are interpolated through dummy-node points.
601
+ [dagre-d3#291](https://github.com/dagrejs/dagre-d3/issues/291) is the
602
+ user-visible result, and complaint categories C4/C5 are the Mermaid-visible
603
+ results. Cluster
604
+ support follows Sander/Forster on paper (per
605
+ [dagre's wiki](https://github.com/dagrejs/dagre/wiki)) but fails on nesting
606
+ and compound edges in practice
607
+ ([dagre#125](https://github.com/dagrejs/dagre/issues/125),
608
+ [dagre#238](https://github.com/dagrejs/dagre/issues/238)) — C3. dot-style
609
+ rank constraints existed in early dagre, were dropped in the 0.7 rewrite,
610
+ and the restoration request —
611
+ [dagre#159 "Add back support for rank constraints"](https://github.com/dagrejs/dagre/issues/159)
612
+ — has been open since November 2014.
613
+
614
+ The "abandoned" framing needs one nuance. D2's engine-comparison page
615
+ states ["Unmaintained. Development stopped in 2018"](https://d2lang.com/tour/dagre/)
616
+ ("battle tested (thanks to MermaidJS, which exclusively uses Dagre for its
617
+ flowcharts)"; cons include "makes some inexplicable edge routing decisions
618
+ occasionally"), and that was accurate for the era it described — but the
619
+ `@dagrejs` org resumed releases in 2023 (1.0.0 May 2023; 3.0.0 March 2026).
620
+ What has *not* changed is the feature picture: no routing phase, no ports,
621
+ rank constraints still unrestored after a decade, and Mermaid ships the
622
+ dormant `dagre-d3` line via the `dagre-d3-es` fork.
623
+
624
+ Mermaid's own remediations concede the diagnosis. The migration ask —
625
+ [mermaid#1969 "Migrate flowchart from dagre to elkjs"](https://github.com/mermaid-js/mermaid/issues/1969)
626
+ (2021: dagre draws "edges … through other blocks instead of around them")
627
+ — was answered by the maintainer himself with the experimental
628
+ `flowchart-elk` renderer in v9.4.0 (February 2023), then v11.0.0 (August
629
+ 2024) made layout pluggable with ELK as the opt-in
630
+ [`@mermaid-js/layout-elk`](https://www.npmjs.com/package/@mermaid-js/layout-elk)
631
+ package ("better for larger and/or more complex diagrams," per its own
632
+ docs) — selected via frontmatter `config.layout`, and per the
633
+ [syntax reference](https://mermaid.js.org/intro/syntax-reference.html)
634
+ "currently … supported for flowcharts and state diagrams" only. The
635
+ edge-overlap ask
636
+ [mermaid#1006](https://github.com/mermaid-js/mermaid/issues/1006) was
637
+ closed *Wont Fix*. GitHub's native rendering still runs the dagre default —
638
+ so most readers see the unfixed engine (complaint categories C2, C3, C4, C9),
639
+ and even opted-in users get the better engine for two of ~25 families. PlantUML made the
640
+ opposite bet decades earlier: it
641
+ [delegates class/state/component/usecase layout to Graphviz dot](https://plantuml.com/graphviz-dot),
642
+ inheriting dot's router and rank constraints along with its weaknesses.
643
+
644
+ This engine's answer is architectural: ELK layered
645
+ ([Schulze, Spönemann & von Hanxleden 2014](https://www.sciencedirect.com/science/article/abs/pii/S1045926X13000943),
646
+ JVLC 25(2) — the port-constraint extension of Sugiyama) as the only graph
647
+ engine, bundled synchronously, with model-order options pinned so source
648
+ order drives cycle breaking and crossing minimization
649
+ ([`considerModelOrder`](https://eclipse.dev/elk/reference/options/org-eclipse-elk-layered-considerModelOrder-strategy.html):
650
+ "preserves the order of nodes and edges in the model file if this does not
651
+ lead to additional edge crossings"; the backing paper is Domrös &
652
+ von Hanxleden, IVAPP 2022). ELK brings the port constraints
653
+ (`FIXED_SIDE`/`FIXED_ORDER`/`FIXED_POS`) that #25 §8 builds on and dagre
654
+ never had, plus compound layout and an optional libavoid-based
655
+ obstacle-avoiding router. It is not sufficient by itself: upstream #83
656
+ showed ELK's default cycle breaking flipping TD diagrams horizontal until
657
+ model order was enforced; ELK's own docs restrict what composes with
658
+ `INCLUDE_CHILDREN` hierarchy handling (which is why
659
+ `src/layout-engine.ts` falls back to `SEPARATE` when a subgraph overrides
660
+ direction); and ELK still knows nothing about *semantic* edge roles, which
661
+ is exactly the intent layer #25 adds.
662
+
663
+ ### R5. Renderer-first semantics, in three generations
664
+
665
+ The recurring design flaw across all three codebases is the one #26's
666
+ guiding principle names: *the renderer is the first place semantics are
667
+ guessed.*
668
+
669
+ - **Mermaid (November 2014–):** born, per its own 0.1.0 README, to "avoid
670
+ heavy tools like Visio" with "a simple markdown-like script language" —
671
+ the design optimized authoring friction, not layout quality. Per-family
672
+ Jison grammars feed per-family bespoke renderers (the sequence renderer
673
+ uses no layout engine at all — hand-maintained vertical-position
674
+ bookkeeping, hence C12); text is measured via the browser DOM with HTML
675
+ labels in `foreignObject` — the C8 export-clipping family, first reported
676
+ six weeks after the first release
677
+ ([mermaid#58](https://github.com/mermaid-js/mermaid/issues/58), December
678
+ 2014, still open) — styling is coupled to page CSS, chart geometry is
679
+ hard-coded per type with no collision handling (C13), and there is no IR
680
+ contract between parse and render, so every embedder/version pair renders
681
+ differently (C11).
682
+ - **`mermaid-ascii`:** grid geometry computed during drawing; labels and
683
+ containers handled by local painter logic (R2).
684
+ - **`beautiful-mermaid` (Craft/Luki Labs, January 2026):** re-implemented
685
+ rendering as synchronous zero-DOM TypeScript with bundled elkjs (run
686
+ synchronously via a worker bypass, `src/elk-instance.ts`) — fixing C8 and
687
+ the browser half of C11 — but kept lenient line-by-line regex parsers
688
+ (the ER cardinality parser silently dropped valid relationships — upstream
689
+ #124, fixed here in PR #17) and accumulated post-ELK geometry passes:
690
+ layer alignment introduced in upstream v1.0.1 ("Layout quality
691
+ improvements and layer alignment"), edge-point orthogonalization, and
692
+ post-routing shape clipping. Each pass is locally reasonable; together
693
+ they move geometry after routes are extracted, which is #25's diagnosed
694
+ source of hitches and stale bends (§2, §9).
695
+
696
+ This fork's trajectory is the inversion of that flaw: parse to a typed
697
+ model, derive layout intent, compute geometry, certify, then render —
698
+ with renderers as projections of shared intent rather than the place where
699
+ intent is invented.
700
+
701
+ ### Root causes → historical issue #26 workstream evidence
702
+
703
+ This crosswalk preserves the research decomposition only. It does not make the
704
+ issue workstreams an active schedule; root `TODO.md` must own any promoted work.
705
+
706
+ | Root cause | #26 workstream(s) |
707
+ |---|---|
708
+ | R1 phase separation, no invariants | WS1 layout-intent/certificates; WS4 certifying simplifier |
709
+ | R1 labels as afterthoughts | WS8 text-measurement contract; #25 §11.4 label invariant |
710
+ | R1 compound difficulty | WS2 route classes (`cross-hierarchy`, `container-edge`) |
711
+ | R2 greedy grid routing | WS6 ASCII/Unicode as first-class projections of route intent |
712
+ | R3 no control surface | WS14 analysis outputs (explain, don't invent syntax); #25 certificates |
713
+ | R4 rectangular/port-less model | WS3 semantic ports and shape-aware anchors |
714
+ | R5 renderer-first semantics | WS5 Gantt resolver model (landed); WS7 class/ER contracts; WS10 region tree; WS13 preservation ladder |
715
+ | Unreviewable visual change (meta) | WS12 reproducible before/after evidence |
716
+ | Generic metrics miss failures (meta, complaint C4 worked example) | WS11 family-specific validators |
717
+
718
+ ---
719
+
720
+ ## Part 3 — Coverage scorecard
721
+
722
+ | # | Complaint | Loudest evidence | Status here | Key references |
723
+ |---|---|---|---|---|
724
+ | C1 | Manual positioning | mermaid#5420 (47R), #270 (2016–) | **out of scope** by policy | #25 OQ2; agent loop as substitute |
725
+ | C2 | Scale collapse | mermaid#1984 (39R) | **partial** (detection only) | `quality.ts` bands; `eval/layout-compare/` |
726
+ | C3 | Subgraphs | mermaid#2509 (189R) | **landed** | `subgraph-direction.test.ts`; PRs #17/#21/#22 |
727
+ | C4 | Erratic routing | mermaid#6476, #5601 | **partial**; candidate fix evidence only | #25 §11–12; MFA worked example above |
728
+ | C5 | Edge-label overlap | mermaid#2131 | **partial** | inline ELK labels; `labelEdgeProximity`; #25 §11.4 |
729
+ | C6 | Order control / edit stability | mermaid#3723 (86R), #815 (45R) | **partial** | model-order ELK opts; `agent-auth-flow.test.ts`; edit-stability tests |
730
+ | C7 | Default aesthetics | HN 30339032 | **landed** (upstream + fork) | theming; Style + Palette; contrast tests |
731
+ | C8 | Text clipping | mermaid#2688 (24R) | **landed** | zero-DOM text; `LABEL_OVERFLOW`; CJK tests; WS8 candidate evidence |
732
+ | C9 | dagre stagnation | Mermaid docs' own ELK advice | **landed** structurally | `layout-engine.ts`; `elk-instance.ts`; #25 §10 |
733
+ | C10 | Self-loops | mermaid#6336 (31R) | **partial** | clearance heuristics; #25 §8.1 candidate evidence |
734
+ | C11 | Version/renderer instability | mermaid#5813 (28R) | **landed** | determinism suite; `eval/layout-compare/`; goldens |
735
+ | C12 | Sequence alignment | mermaid#1765 (28R) | **partial**; first properties landed | #25 §14.2; #26 WS11; `sequence-layout.test.ts` |
736
+ | C13 | Chart-family geometry | mermaid#1301 (23R) | **partial** | PR #24 (gantt); pie/quadrant/gantt geometry tests |
737
+
738
+ ---
739
+
740
+ ## Part 4 — What this project deliberately does not address
741
+
742
+ Stated plainly so the scorecard cannot oversell:
743
+
744
+ 1. **Manual positioning (C1).** Arbitrary coordinates and post-layout nudging
745
+ remain deliberately unsupported. `BUILD-24` schedules only typed structural
746
+ auto-layout constraints (rank, adjacency, short-edge preference), without
747
+ inventing Mermaid source syntax. If Mermaid core standardizes compatible
748
+ source hints, this fork can map them into the same typed contract.
749
+ 2. **Large-diagram compaction (C2).** Detection exists; no compaction work
750
+ is scheduled. Source-order priority can make wide diagrams wider — the
751
+ Auth Flow aspect band in `agent-auth-flow.test.ts` is widened to 7
752
+ because of it. The only candidate direction on record is the
753
+ decomposition idea noted in C2, which sidesteps compaction rather than
754
+ solving it.
755
+ 3. **Reach.** Most complainers experience Mermaid through GitHub/GitLab/
756
+ Notion native rendering, which no fork renderer can change. Work here
757
+ applies to diagrams rendered through this stack (agent pipelines, CI,
758
+ terminals, the editor) plus whatever is upstreamed per the
759
+ [upstreaming strategy](./fork-differences.md#upstreaming-strategy).
760
+ 4. **Family coverage.** The checked runtime registry is the source of truth for
761
+ supported families. Every registered family, including Radar, is rendered and
762
+ characterized; Mermaid families outside that registry remain unsupported.
763
+ 5. **Metric honesty.** Today's perceptual metrics pass diagrams whose
764
+ routing is visibly wrong (complaint C4 worked example). Unless and until the
765
+ #25 certificate and #26 family-validator candidates are promoted in root
766
+ `TODO.md` and land, `verify.ok` + `checkQuality` must not be read as "looks
767
+ right" — `docs/quality.md` already says this; the MFA fixture proves it.
768
+
769
+ ---
770
+
771
+ ## Sources
772
+
773
+ Complaint evidence (June 2026): the `mermaid-js/mermaid` issues linked
774
+ inline above; Mermaid flowchart docs
775
+ (<https://mermaid.js.org/syntax/flowchart.html>); Hacker News items linked
776
+ inline; the GitLab handbook Mermaid-layout page
777
+ (<https://handbook.gitlab.com/handbook/tools-and-tips/mermaid/>);
778
+ diagrams.so's diagram-as-code comparison
779
+ (<https://diagrams.so/learn/diagram-as-code-comparison>).
780
+
781
+ Literature (all verified June 2026; venue/page details via dblp or
782
+ publisher pages):
783
+
784
+ - Sugiyama, Tagawa & Toda, "Methods for Visual Understanding of
785
+ Hierarchical System Structures," IEEE Trans. SMC-11(2), 1981.
786
+ <https://doi.org/10.1109/TSMC.1981.4308636>
787
+ - Garey & Johnson, "Crossing Number is NP-Complete," SIAM J. Algebraic
788
+ Discrete Methods 4(3), 1983. <https://dl.acm.org/doi/10.1137/0604033>
789
+ - Eades & Wormald, "Edge crossings in drawings of bipartite graphs,"
790
+ Algorithmica 11(4), 1994.
791
+ <https://link.springer.com/article/10.1007/BF01187020>
792
+ - Marks & Shieber, "The Computational Complexity of Cartographic Label
793
+ Placement," Harvard TR-05-91, 1991.
794
+ <https://dash.harvard.edu/entities/publication/73120379-247a-6bd4-e053-0100007fdf3b>
795
+ - Kakoulis & Tollis, "On the complexity of the Edge Label Placement
796
+ problem," Comput. Geom. 18(1), 2001.
797
+ <https://www.sciencedirect.com/science/article/pii/S0925772100000250>
798
+ - Gansner, Koutsofios, North & Vo, "A Technique for Drawing Directed
799
+ Graphs," IEEE TSE 19(3), 1993.
800
+ <https://www.graphviz.org/documentation/TSE93.pdf>
801
+ - Brandes & Köpf, "Fast and Simple Horizontal Coordinate Assignment,"
802
+ GD 2001, LNCS 2265.
803
+ <https://link.springer.com/chapter/10.1007/3-540-45848-4_3> (authors'
804
+ 2020 erratum: <https://arxiv.org/abs/2008.01252>)
805
+ - Schulze, Spönemann & von Hanxleden, "Drawing layered graphs with port
806
+ constraints," JVLC 25(2), 2014.
807
+ <https://www.sciencedirect.com/science/article/abs/pii/S1045926X13000943>
808
+ - Domrös & von Hanxleden, "Preserving Order during Crossing Minimization
809
+ in Sugiyama Layouts," IVAPP 2022.
810
+ <https://www.scitepress.org/Papers/2022/108338/108338.pdf>
811
+ - Purchase, "Which aesthetic has the greatest effect on human
812
+ understanding?", GD 1997, LNCS 1353.
813
+ <https://link.springer.com/chapter/10.1007/3-540-63938-1_67>
814
+ - Sander, "Layout of Compound Directed Graphs," TR A/03/96, Universität
815
+ des Saarlandes, 1996.
816
+ <https://publikationen.sulb.uni-saarland.de/bitstream/20.500.11880/25862/1/tr-A03-96.pdf>
817
+ - Wybrow, Marriott & Stuckey, "Orthogonal Connector Routing," GD 2009
818
+ (libavoid). <https://users.monash.edu/~mwybrow/papers/wybrow-gd-2009.pdf>
819
+
820
+ Products: Graphviz attribute docs (`rank`, `portPos`, `splines`,
821
+ `constraint`, `weight`, linked inline); D2/Terrastruct docs (TALA, `near`,
822
+ grid diagrams, dagre/ELK engine pages, linked inline); yFiles automatic
823
+ label placement
824
+ (<https://docs.yworks.com/yfiles-html/dguide/layout/label_placement.html>);
825
+ PlantUML–Graphviz delegation (<https://plantuml.com/graphviz-dot>);
826
+ Mermaid v11.0.0 release
827
+ (<https://github.com/mermaid-js/mermaid/releases/tag/v11.0.0>) and
828
+ `@mermaid-js/layout-elk`
829
+ (<https://www.npmjs.com/package/@mermaid-js/layout-elk>); ELK option
830
+ reference (<https://eclipse.dev/elk/reference.html>) and the ELK overview
831
+ paper (<https://arxiv.org/abs/2311.00533>).
832
+
833
+ Standards: OMG Diagram Definition v1.1 (<https://www.omg.org/spec/DD/>);
834
+ BPMN 2.0.2 (<https://www.omg.org/spec/BPMN/2.0.2/>) / ISO/IEC 19510:2013
835
+ (<https://www.iso.org/standard/62652.html>); UML 2.5.1
836
+ (<https://www.omg.org/spec/UML/2.5.1/About-UML>); ISO 5807:1985
837
+ (<https://www.iso.org/standard/11955.html>); ANSI X3.5-1970 (scan:
838
+ <https://www.bitsavers.org/pdf/ansi/X3/X3.005-1970_Flowchart_Symbols_and_Their_Usage_in_Information_Processing.pdf>).
839
+
840
+ Origins: Mermaid 0.1.0 (November 2014, Knut Sveidqvist;
841
+ <https://github.com/mermaid-js/mermaid/releases/tag/0.1.0> — the 0.1.0
842
+ README credits d3 and dagre-d3 and states the Visio-avoidance motive);
843
+ dagre (Chris Pettitt; wiki reading list
844
+ <https://github.com/dagrejs/dagre/wiki>; rank-constraint regression
845
+ dagre#159; `@dagrejs` revival releases 2023–2026); `mermaid-ascii`
846
+ (Alexander Grooff, <https://github.com/AlexanderGrooff/mermaid-ascii>;
847
+ Show HN October 2024 <https://news.ycombinator.com/item?id=41847407>);
848
+ `beautiful-mermaid` (Luki Labs/Craft, npm January 2026; HN thread
849
+ <https://news.ycombinator.com/item?id=46804828>; upstream v1.0.1
850
+ layout/layer-alignment commit; upstream issues
851
+ #63/#68/#69/#83/#89/#98/#111/#112/#113/#115/#119/#121/#124).