Summary
Component fences are validated at publish time and degrade to inline error boxes. Mermaid
and chart specs are not validated at all: they are inlined verbatim and fail in the
browser, after the page has been published and handed to a reader.
The result is that a publish can report success, and a chart-error scan of the output
HTML can come back clean, while the page is broken.
What happened
An agent session published an architecture page whose sequence diagram contained:
Note over S: builder holds AccountState; cut it at boundarySeq
Mermaid treats ; as a statement terminator in sequence diagrams, so the note ends early
and the remainder parses as a statement with no arrow. The reader saw:
Syntax error in text
mermaid version 11.16.1
The publish itself succeeded. Scanning the rendered HTML for class="chart-error" returned
zero, because the only occurrences are the CSS rule and the two runtime handlers that inject
the box client-side.
Why this is worth fixing here
skills/artifact-pages/SKILL.md lists as a pre-publish check:
- No error boxes survive to publish — fix and re-render first
For component fences that check is enforceable, because dist/components.js returns
errorBox(...) at publish time. For mermaid, vega-lite, vega, and echarts it cannot
be performed at all, since nothing evaluates those specs until a browser does. An agent
following the checklist literally will still ship a broken page.
It is checkable with what already ships
The package already depends on mermaid. Parsing a diagram headlessly needs a DOM, but works:
const dom = new JSDOM("<!doctype html><html><body></body></html>");
globalThis.window = dom.window; globalThis.document = dom.window.document;
globalThis.Element = dom.window.Element; globalThis.SVGElement = dom.window.SVGElement;
globalThis.DOMParser = dom.window.DOMParser; globalThis.Node = dom.window.Node;
const m = (await import("mermaid/dist/mermaid.core.mjs")).default;
m.initialize({ startOnLoad: false });
await m.parse(src); // throws with the offending line and column
On the failing source above this reports:
Parse error on line 12 ... Expecting 'SOLID_ARROW', ... got 'NEWLINE'
Note that without a DOM the flowchart parser fails earlier with
DOMPurify.addHook is not a function, before its grammar is reached — so a naive headless
check silently validates only some diagram types. That trap is worth encoding once, here,
rather than in every consumer.
Vega-Lite has a cheaper path: vega-lite's compile() is already a build-time operation and
throws on an invalid spec without needing a DOM.
Suggested behaviour
- Validate
mermaid and chart specs during publish, and emit the same inline error box the
component fences already produce, so one rule covers every fence.
- If a jsdom dependency is unwanted in the default path, make it opt-in — a
--validate-diagrams
flag on render, or a validate: true argument on artifact_publish — and state in the
skill's checklist that unvalidated diagrams are a known blind spot.
- Failing both, a cheap lint would have caught this specific case: a
; inside a sequence
diagram Note line, and similar known-hazardous punctuation, are pure string checks.
Option 1 is the one that makes the existing checklist item true.
Environment
opencode-artifacts 0.14.4, mermaid 11.16.1, Node v24.19.0, OpenCode 1.18.9, macOS 25.5.0.
Summary
Component fences are validated at publish time and degrade to inline error boxes. Mermaid
and chart specs are not validated at all: they are inlined verbatim and fail in the
browser, after the page has been published and handed to a reader.
The result is that a publish can report success, and a
chart-errorscan of the outputHTML can come back clean, while the page is broken.
What happened
An agent session published an architecture page whose sequence diagram contained:
Mermaid treats
;as a statement terminator in sequence diagrams, so the note ends earlyand the remainder parses as a statement with no arrow. The reader saw:
The publish itself succeeded. Scanning the rendered HTML for
class="chart-error"returnedzero, because the only occurrences are the CSS rule and the two runtime handlers that inject
the box client-side.
Why this is worth fixing here
skills/artifact-pages/SKILL.mdlists as a pre-publish check:For component fences that check is enforceable, because
dist/components.jsreturnserrorBox(...)at publish time. Formermaid,vega-lite,vega, andechartsit cannotbe performed at all, since nothing evaluates those specs until a browser does. An agent
following the checklist literally will still ship a broken page.
It is checkable with what already ships
The package already depends on mermaid. Parsing a diagram headlessly needs a DOM, but works:
On the failing source above this reports:
Note that without a DOM the flowchart parser fails earlier with
DOMPurify.addHook is not a function, before its grammar is reached — so a naive headlesscheck silently validates only some diagram types. That trap is worth encoding once, here,
rather than in every consumer.
Vega-Lite has a cheaper path:
vega-lite'scompile()is already a build-time operation andthrows on an invalid spec without needing a DOM.
Suggested behaviour
mermaidand chart specs during publish, and emit the same inline error box thecomponent fences already produce, so one rule covers every fence.
--validate-diagramsflag on
render, or avalidate: trueargument onartifact_publish— and state in theskill's checklist that unvalidated diagrams are a known blind spot.
;inside a sequencediagram
Noteline, and similar known-hazardous punctuation, are pure string checks.Option 1 is the one that makes the existing checklist item true.
Environment
opencode-artifacts 0.14.4, mermaid 11.16.1, Node v24.19.0, OpenCode 1.18.9, macOS 25.5.0.