feat: render architecture diagrams from Graphviz DOT instead of Mermaid - #240
feat: render architecture diagrams from Graphviz DOT instead of Mermaid#240JohannesRudolph wants to merge 6 commits into
Conversation
Mermaid gives no control over layout. There is no way to say "keep these three nodes on one row" and no way to say "this edge must not affect ranking", so any diagram with two related hierarchies comes out as diagonal spaghetti and every attempt to fix it fights the engine. Switch reference architecture diagrams to Graphviz DOT committed next to the markdown, with the rendered SVG committed alongside: - tools/diagrams/render-diagrams.mjs renders every *.dot to a sibling *.svg. Graphviz runs as a WASM npm package, so no system `dot` install is needed. --check re-renders in memory and diffs against what is committed. - task diagrams / task diagrams:check wrap it; a Diagrams Check workflow runs --check on PRs so a .dot edit without a re-render fails instead of shipping a stale picture. - Convert azure-kubernetes and stackit-kubernetes from Mermaid to DOT, using rank=same for rows, constraint=false for cross-cutting edges, splines=ortho for right-angle connectors and xlabel for labels that survive ortho routing. Inline HTML/SVG is not an option for these diagrams: the website renders the markdown through marked into Angular's [innerHTML], which strips <style>, style attributes and inline SVG, and GitHub's pipeline does the same. So the markdown references the SVG as a plain relative image, which renders on GitHub, and index.ts rewrites relative image links to absolute raw URLs pinned to the built commit so the same link works on the website. While here, teach getGitHubRemoteUrl to normalize scp-style SSH remotes. It only handled https, so every generated link was malformed when the script ran in an SSH clone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Scorecard Check
|
|
This pull request is automatically being deployed by Amplify Hosting (learn more). |
The DOT diagrams were correct but plain. What made the earlier HTML mockup read better was not the tool: it was a consistent emoji vocabulary and a palette where colour meant something. Those are conventions, so write them down and apply them. Add .agents/references/diagrams.md, following the existing reference-file pattern, and reduce the AGENTS.md section to a pointer. It captures: - Composition. One top-level cluster per system, with the relationships as dotted constraint=false edges across the boundary; a lifecycle split only when the story is who-does-what rather than how two systems map onto each other. Single-node clusters read as noise. - A five-role palette (definition / instance / provider / structure / muted) where colour encodes what kind of thing a box is, never decoration. - A shared emoji vocabulary, so the same concept gets the same icon across diagrams and a reader can match boxes between them. - A canonical preamble, so spacing, type and stroke weight match everywhere. - Layout control and its traps, including two found the hard way here. Restyle both diagrams accordingly: emoji on every node, palette applied by role (the starterkit BBD reads as a meshStack definition, per-team objects as instances, cloud services as provider-managed), and the single-node catalog cluster dropped in favour of a free-standing node. Two corrections to what the previous commit assumed: - Emoji need the U+FE0F variation selector to render in colour. ☸ ⚙ 🗄 🗂 🛰 default to text presentation and came out as small monochrome symbols. The earlier note said to omit the selector, which was backwards. - style=invis edges in a rank=same block only hint at left-to-right order. A constraint=false edge between two same-rank nodes is itself a flat edge and outranks them, which is what pulled SKE out of its declared position. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both diagrams mixed a lifecycle split ("Per Application Team") with a
system split ("Azure — managed by the platform team"), which the diagram
conventions forbid, and left the starterkit BBD floating outside every
cluster with no meshStack layering around it.
- one cluster per system: Azure/GitHub/meshStack and STACKIT/meshStack
- give the meshStack side its own depth: platform -> landing zone ->
project, alongside the BBD composition
- cross-boundary edges are dotted + constraint=false so they no longer
distort the ranking they describe
- collapse the dev/prod twins to x2 cardinality labels, which removes
most of the old edge spaghetti
- meshStack constructs always point at the cloud resources they manage
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why
Mermaid gives no control over layout. There is no way to express "keep these three nodes on one row" and no way to express "this edge must not affect ranking", so any diagram with two related hierarchies comes out as diagonal spaghetti — and every attempt to fix it fights the engine rather than the diagram.
Graphviz DOT has both escape hatches, which is worth a render step.
What changed
Tooling
tools/diagrams/render-diagrams.mjsrenders every*.dotin the repo to a sibling*.svg. Graphviz runs as a WASM npm package, so no systemdotinstall is required.task diagramsrenders;task diagrams:checkverifies. The new Diagrams Check workflow runs--checkon PRs, so editing a.dotwithout committing the re-render fails CI instead of silently shipping a stale picture..gitattributesmarks the diagram SVGslinguist-generatedto keep PR diffs readable.Converted diagrams
azure-kubernetesandstackit-kubernetesmove from Mermaid to DOT —rank=samefor rows,constraint=falsefor cross-cutting edges,splines=orthofor right-angle connectors, andxlabelfor labels that survive ortho routing.Why not inline HTML/SVG
The website renders these files through
markedinto Angular's[innerHTML], which strips<style>blocks,styleattributes and inline<svg>; GitHub's markdown pipeline does the same. So the markdown references the SVG as a plain relative image — which renders on GitHub — andindex.tsrewrites relative image links to absoluterawURLs pinned to the built commit so the same link resolves on the website.Drive-by fix
getGitHubRemoteUrl()only normalizedhttpsremotes, so every generated link (sourceUrl,folderUrl, …) was malformed whenindex.tsran in an SSH clone. It now handles scp-stylegit@github.com:remotes too.Conventions
AGENTS.mdgains a Diagrams section documenting the layout, the commands, the DOT attributes that matter, and the traps — includingbgcolor="white"(a transparent background renders dark-on-dark in GitHub's dark theme) and avoidingordering=out(it silently mirrors rows).Verification
task diagrams:checkpasses; verified it fails on a deliberately stale SVG.node index.tsregenerates the catalog with correctly absolutized image URLs.Follow-up
The STACKIT Landing Zone diagram in #236 is the diagram this approach was designed against — it adopts this once #236 lands.
Note on dark mode
The SVGs commit to a white background on purpose. In GitHub's dark theme they will read as a light panel; the alternative (transparent) makes dark text invisible. Shipping light/dark pairs behind
<picture>is possible later if wanted.🤖 Generated with Claude Code