ADR-0006: Draw notebook diagrams with matplotlib, not Mermaid¶
- Status: Accepted
- Date: 2026-08-31
- Deciders: Maintainers
Context¶
The teaching format is: a flowchart of what the code is about to do, then the code, then a summary diagram and a decision tree. That means ~45 diagrams inside notebooks, and they have to render wherever a student opens them.
Options considered¶
Option A — Mermaid in markdown cells¶
Concise source, renders on GitHub and in recent JupyterLab. Costs: renders in some environments and shows raw text in others — older JupyterLab, some VS Code configurations, PDF export, nbconvert without a plugin. A diagram that silently degrades to source text is worse than no diagram, and a student cannot tell whether they are seeing a bug.
Option B — pre-rendered PNGs¶
Renders everywhere. Costs: binary assets in git, a build step, and diagrams that cannot use the numbers the notebook just computed.
Option C — matplotlib, drawn by a small diagram DSL¶
Decision¶
Option C for notebooks (nanorag/viz.py), and Mermaid for the markdown docs — where
GitHub is the only rendering target and it renders reliably.
Consequences¶
Good. Diagrams are ordinary cell outputs: they survive nbconvert, PDF export and every
Jupyter frontend. They can be data-driven — the fault-isolation tree renders with the branch
a real failing query actually took highlighted, which is the single most useful diagram in the
curriculum and is impossible with static Mermaid. One definition in catalog.py produces the
figure, the table and the executable predicate, so they cannot drift apart.
Bad. viz.py is 623 lines, which is 623 lines of diagram code we own. Layout is manual:
we compute text wrapping and box heights from font metrics, and got it wrong the first time —
text overflowed its boxes until measuring and drawing were unified into one function. Changing
a diagram means changing Python, not a two-line diff.
Revisit when: Mermaid rendering becomes universal across Jupyter frontends and export paths. Even then, the data-driven diagrams stay in matplotlib.
What would change this¶
Mermaid rendering reliably in executed-notebook HTML exports without a JavaScript runtime.
The decision rests on notebooks needing to render identically in JupyterLab, in nbconvert HTML,
and on GitHub — and Mermaid not doing that. If nbconvert gains native Mermaid rendering, or
the Pages build pre-renders fences to inline SVG, the constraint is gone and Mermaid becomes
the better choice for structural diagrams.
Note this ADR is scoped to notebooks. Markdown files use Mermaid throughout and are validated
in CI by tools/validate-mermaid.mjs — the two surfaces have different constraints and this
decision does not apply to both.