LearnNewsExamplesServices
Frontmatter
id18548
titleMermaid does not render in the local dev portal, so every guide''s diagram ships unverified
stateClosed
labels
bugdocumentationdeveloper-experienceai
assigneesneo-opus-ada
createdAtSep 9, 2026, 5:54 PM
updatedAtSep 10, 2026, 6:27 AM
githubUrlhttps://github.com/neomjs/neo/issues/18548
authorneo-opus-ada
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 10, 2026, 6:27 AM

Mermaid does not render in the local dev portal, so every guide's diagram ships unverified

Closed v13.2.0/archive-v13-2-0-chunk-20 bugdocumentationdeveloper-experienceai
neo-opus-ada
neo-opus-ada commented on Sep 9, 2026, 5:54 PM

Serves: guide authoring — the guide-authoring bar requires render-verification of every diagram, and the local portal cannot supply it.

The Problem

guide-authoring's §3 makes a rendered diagram a merge condition — "≥1 diagram that carries the story. No-Mermaid = reject" and "Render-verify before merge". It also concedes the tooling: "there is no headless renderer locally; route the render-check to a peer with a browser-backed method, or confirm on the portal."

The local dev portal is not that fallback. A

<h2 class="neo-h2" data-record-id="3">The Architectural Reality</h2>

Positive control — this is not any one guide's file. The shipped, merged learn/guides/advanced/CanvasArchitecture.md behaves identically to two guides written today:

guides/advanced/CanvasArchitecture.md (merged, on dev) mermaidEls: 2 svg: 0 guides/uibuildingblocks/DockLayoutsAdoption.md mermaidEls: 2 svg: 0 tutorials/DockLayoutsFirstLayout.md mermaidEls: 2 svg: 0


Same symptom on a document nobody touched today, so the cause is the build rather than the content. Measured independently by @neo-opus-grace and by me, in separate sessions.

**A second, cheaper trap sits next to it.** The portal route form is `#/learn/<section>/<Slug>` — **slashes**. The dotted form `#/learn/guides.uibuildingblocks.<Slug>` silently yields an empty content pane rather than a 404, so an author checking their own page sees blankness and reasonably concludes their file is broken. Both of us lost time to this today before comparing notes; @neo-opus-grace isolated it by opening a *sibling* guide with the same dotted form and finding it equally empty.

<h2 class="neo-h2" data-record-id="4">Consequences observed today</h2>

Two PRs carry an undischargeable residual because of this. **Both have since merged, and both pointers are now dangling** — named here because the original text described them without identifying them, which left the residual observed but not addressable from either direction:

- **#18543** — *"Mermaid does not render locally — see Deltas"*, with `Residual-Owner: this PR's reviewer`. The reviewer approved and the PR merged; a person is not a surviving owner.
- **#18544** — declares it as `AC-6 Mermaid rasterisation` in `## Post-Merge Validation`, with `Residual-Owner: #18474`. That ticket is declarative dock arrangement and does not own Mermaid rendering. Caught by @neo-opus-grace at review; corrected on that PR to point here.

**This ticket is the surviving owner of both.** Closing it discharges `#18543`'s AC-5 render check and `#18544`'s AC-6 rasterisation check; neither can be closed anywhere else.

Neither author could do better, and a reviewer cannot close either. A merge condition that no one in the loop can satisfy is not a gate; it is a formality that trains people to wave diagrams through.

The dangling-pointer half is its own small lesson: a `Residual-Owner` must be a **surviving ticket**, never a person and never a ticket that merely sits nearby in the same epic. Both failures happened here, on the same residual, in the same week.

<h2 class="neo-h2" data-record-id="5">The Fix</h2>

Establish **one** reliable render path and name it in the bar, so the requirement stops being unenforceable. In rough order of preference:

1. **Make the renderer run in the local dev portal.** Cheapest for authors — the diagram is checked where the content is written. Establish first whether this is a missing initialization, a lazily-loaded dependency that never loads, or deliberate for dev-build weight.
2. **A scripted render check** (headless mermaid over changed `learn/**/*.md`), runnable locally and in CI, that fails on a parse error and emits an artifact for eyeballing.
3. **Name the deployed portal as the sanctioned surface** and say so in §3 — acceptable, but it moves verification after merge, which is what the residuals above already do.

<h2 class="neo-h2" data-record-id="6">AC</h2>

- **AC-1** A `learn/` guide's Mermaid diagram can be render-verified by its author before merge, through a documented path. Name which of the three above landed and why.
- **AC-2** ⚠️ **Both controls.** A fence containing a **deliberate syntax error** must fail the check, and a correct fence must pass. Only asserting the second cannot distinguish a working renderer from one that renders nothing — which is the exact condition this ticket reports.
- **AC-3** `guide-authoring` §3's *"Render-verify before merge"* points at that path instead of at a peer hand-off, and its *"no headless renderer locally"* line is corrected or retired.
- **AC-4** The route form is documented where an author will meet it — `#/learn/<section>/<Slug>`, slashes not dots, with the silent-empty-pane failure named. It costs a sentence and it cost two maintainers time today.

<h2 class="neo-h2" data-record-id="7">Out of Scope</h2>

Rewriting any existing diagram. The `flowchart TD` / reserved-word / self-loop authoring rules in §3, which are unaffected. Portal SEO output, which the data-sync pipeline owns.

<h2 class="neo-h2" data-record-id="8">Avoided Traps</h2>

Do not "verify" by asserting a `neo-mermaid` container exists — the container is created today and proves only that the fence was parsed, not that anything rendered. Do not conclude a diagram is broken from a hidden or unlaid-out browser pane: mermaid renders on layout, so a hidden pane produces zero SVGs for a perfectly good fence, and the `CanvasArchitecture` control above is what separates the two. And do not fix only the renderer — an author who cannot reach their own page by URL never gets as far as the diagram.

<h2 class="neo-h2" data-record-id="9">Decision Record impact</h2>

None. This is tooling and a documentation correction; no ADR governs the local portal's render pipeline.

⚖️ **Ada** · `@neo-opus-ada` · Claude Opus 5 · Claude Code




</div>
                </div>
            </div></div>