Quote mermaid node labels with icons/specials and add validation tooling#8646
Conversation
Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
Pull request overview
This PR hardens Mermaid diagram authoring across the repository by introducing validation tooling (local + CI) and mechanically quoting risky node labels (icons/special characters) to reduce cross-renderer fragility across GitHub/Maven Site/export consumers.
Changes:
- Added Node-based Mermaid tooling under
scripts/(full render validation, deep inventory, and an auto-quote fixer). - Added a dedicated GitHub Actions workflow to validate Mermaid diagrams on Markdown-touching PRs/pushes and on a schedule, and added
.gitignoreentries for generated artifacts. - Updated numerous Markdown documents/visualization specs to quote Mermaid node labels containing emojis/special characters, and added
MERMAID_STYLE_GUIDE.mdto document the rules and palette.
Reviewed changes
Copilot reviewed 54 out of 55 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| WORKFLOWS.md | Quotes a few Mermaid node labels to avoid special-character parsing issues. |
| THREAT_MODEL.md | Quotes many Mermaid node labels with emojis/special chars to stabilize rendering. |
| service.data.impl/README-SCHEMA-MAINTENANCE.md | Quotes a Mermaid node label containing an icon. |
| SECURITY_ARCHITECTURE.md | Quotes Mermaid node labels across multiple security diagrams for consistent rendering. |
| scripts/validate-mermaid.mjs | New script that renders all Mermaid blocks via mmdc and reports failures. |
| scripts/fix-mermaid-quotes.mjs | New script to rewrite Mermaid node labels by adding quotes when risky chars are present. |
| scripts/deep-check-mermaid.mjs | New script to inventory Mermaid usage and flag risky/unquoted labels and palette sprawl. |
| RISK_RULES_INTOP_OSINT.md | Quotes Mermaid node labels containing emojis/special chars. |
| MERMAID_STYLE_GUIDE.md | New style guide documenting required quoting rules, palette, and validator usage. |
| json-export-specs/visualizations/top10-rising-stars.md | Quotes a Mermaid label containing &. |
| json-export-specs/visualizations/top10-party-rebels.md | Quotes Mermaid node labels with emoji/status icons. |
| json-export-specs/visualizations/top10-most-controversial.md | Quotes a Mermaid label containing &. |
| json-export-specs/visualizations/top10-most-absent.md | Quotes multiple Mermaid labels containing icons/special characters. |
| json-export-specs/visualizations/top10-ethics-concerns.md | Quotes Mermaid labels containing icons/special characters. |
| json-export-specs/visualizations/top10-electoral-risk.md | Quotes Mermaid labels containing icons/special characters. |
| json-export-specs/visualizations/top10-coalition-brokers.md | Quotes Mermaid labels containing ,/& and multi-line content. |
| json-export-specs/visualizations/politician-profile.md | Quotes Mermaid labels containing , and &. |
| json-export-specs/visualizations/party-performance.md | Quotes Mermaid labels containing ,. |
| json-export-specs/visualizations/intelligence-dashboard.md | Quotes Mermaid labels containing icons and braces-content. |
| json-export-specs/visualizations/government-cabinet.md | Quotes Mermaid labels containing &. |
| json-export-specs/visualizations/committee-network.md | Quotes Mermaid labels containing multi-line content (with <br/>). |
| json-export-specs/README.md | Quotes Mermaid multi-line node labels. |
| ISMS_COMPLIANCE_MAPPING.md | Quotes Mermaid labels (including mindmap icons) to avoid parsing issues. |
| INTELLIGENCE_DATA_FLOW.md | Quotes Mermaid node labels containing emojis/special chars. |
| FUTURE_WORKFLOWS.md | Quotes a Mermaid label containing &. |
| FUTURE_SECURITY_ARCHITECTURE.md | Quotes Mermaid node labels containing &. |
| FUTURE_FLOWCHART.md | Quotes Mermaid node labels containing / and &. |
| FUTURE_DATA_MODEL.md | Quotes Mermaid node labels containing -/&. |
| FUTURE_ARCHITECTURE.md | Updates Mermaid C4 diagrams and other labels; adds extensive quoting changes. |
| DOCUMENTATION_NAMING_CONVENTION.md | Quotes a Mermaid node label containing ,/./.... |
| DATABASE_VIEW_INTELLIGENCE_CATALOG.md | Quotes a Mermaid node label containing &. |
| DATA_ANALYSIS_INTOP_OSINT.md | Quotes many Mermaid node labels containing emojis/special chars. |
| CRA-ASSESSMENT.md | Quotes Mermaid node labels containing emojis/special chars. |
| ARCHITECTURE.md | Updates Mermaid C4 diagrams and other labels; adds extensive quoting changes. |
| .gitignore | Ignores .mermaid-validate/ and .mermaid-tools/ output directories. |
| .github/workflows/validate-mermaid.yml | New CI workflow to validate Mermaid rendering and upload reports. |
| .github/skills/vulnerability-management/SKILL.md | Quotes Mermaid node labels containing emojis/special chars. |
| .github/skills/swedish-political-system/SKILL.md | Quotes Mermaid labels containing ,. |
| .github/skills/strategic-communication-analysis/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/risk-assessment-methodology/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/risk-assessment-frameworks/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/osint-methodologies/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/open-source-policy/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/legislative-monitoring/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/intelligence-analysis-techniques/SKILL.md | Quotes Mermaid node labels containing &. |
| .github/skills/information-security-strategy/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/incident-response/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/hack23-future-architecture-standards/SKILL.md | Updates Mermaid C4 examples with quoting changes. |
| .github/skills/cryptography-policy/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/compliance-checklist/SKILL.md | Quotes Mermaid mindmap root label. |
| .github/skills/classification-policy/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/behavioral-analysis/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/backup-recovery-policy/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| .github/skills/access-control-policy/SKILL.md | Quotes Mermaid node labels with emojis/special chars. |
| Person("anonymousUser, #quot;Anonymous User#quot;, #quot;Accesses public political data and metrics#quot;") | ||
| Person("registeredUser, #quot;Registered User#quot;, #quot;Creates account and accesses personalized features#quot;") | ||
| Person("adminUser, #quot;System Administrator#quot;, #quot;Manages system configuration and user accounts#quot;") | ||
|
|
||
| System(cia, "Citizen Intelligence Agency", "Provides analysis and visualization of Swedish political activities") | ||
| System("cia, #quot;Citizen Intelligence Agency#quot;, #quot;Provides analysis and visualization of Swedish political activities#quot;") |
| Person("citizen, #quot;Citizen Analyst#quot;, #quot;Uses platform for political research and transparency insights#quot;") | ||
| Person("researcher, #quot;Political Researcher#quot;, #quot;Conducts deep analysis of political patterns and trends#quot;") | ||
| Person("developer, #quot;Developer#quot;, #quot;Maintains and extends the platform with AI-assisted tooling#quot;") | ||
|
|
||
| System("cia, #quot;Citizen Intelligence Agency#quot;, #quot;AI-enhanced political intelligence platform with LLM-powered analytics, GitHub Copilot agent-assisted development, and comprehensive Swedish political data analysis#quot;") | ||
|
|
||
| System_Ext("riksdagen, #quot;Swedish Parliament API#quot;, #quot;Parliamentary data: members, votes, documents, committees#quot;") | ||
| System_Ext("election, #quot;Swedish Election Authority#quot;, #quot;Election results, party data, voter turnout#quot;") | ||
| System_Ext("worldbank, #quot;World Bank Open Data#quot;, #quot;Economic indicators and demographic data#quot;") | ||
| System_Ext("esv, #quot;Swedish Financial Authority#quot;, #quot;Government finances and agency budgets#quot;") | ||
| System_Ext("llmService, #quot;LLM Service Layer#quot;, #quot;Anthropic Opus 4.6 / competitor models for text analysis and summarization#quot;") | ||
| System_Ext("copilotAgents, #quot;GitHub Copilot Agents#quot;, #quot;AI-assisted development, code review, documentation generation#quot;") | ||
|
|
||
| Rel("citizen, cia, #quot;Explores political data, views analytics dashboards#quot;") | ||
| Rel("researcher, cia, #quot;Queries political datasets, analyzes voting patterns#quot;") | ||
| Rel("developer, cia, #quot;Develops features with AI-assisted tooling#quot;") | ||
|
|
||
| Rel("cia, riksdagen, #quot;Imports parliamentary data via REST API#quot;") | ||
| Rel("cia, election, #quot;Imports election and party data#quot;") | ||
| Rel("cia, worldbank, #quot;Imports economic indicators#quot;") | ||
| Rel("cia, esv, #quot;Imports government financial data#quot;") | ||
| Rel("cia, llmService, #quot;Sends political texts for analysis and summarization#quot;") | ||
| Rel("developer, copilotAgents, #quot;Leverages AI agents for development tasks#quot;") | ||
|
|
||
| UpdateLayoutConfig("$c4ShapeInRow=#quot;3#quot;, $c4BoundaryInRow=#quot;1#quot;") |
| Person("citizen, #quot;Citizen#quot;, #quot;Swedish citizen seeking political transparency#quot;") | ||
| Person("analyst, #quot;Political Analyst#quot;, #quot;Researches political patterns#quot;") | ||
|
|
||
| System("cia, #quot;CIA Platform#quot;, #quot;Political intelligence and analysis#quot;") | ||
|
|
||
| System_Ext("riksdag, #quot;Riksdagen API#quot;, #quot;Swedish Parliament open data#quot;") | ||
| System_Ext("val, #quot;Val.se#quot;, #quot;Swedish Election Authority#quot;") | ||
| System_Ext("worldbank, #quot;World Bank#quot;, #quot;Economic indicators#quot;") | ||
| System_Ext("esv, #quot;ESV#quot;, #quot;Swedish financial data#quot;") | ||
|
|
||
| Rel("citizen, cia, #quot;Views political data#quot;, #quot;HTTPS#quot;") | ||
| Rel("analyst, cia, #quot;Analyzes political trends#quot;, #quot;HTTPS#quot;") | ||
| Rel("cia, riksdag, #quot;Fetches parliament data#quot;, #quot;HTTPS/JSON#quot;") | ||
| Rel("cia, val, #quot;Fetches election data#quot;, #quot;HTTPS/JSON#quot;") | ||
| Rel("cia, worldbank, #quot;Fetches economic data#quot;, #quot;HTTPS/JSON#quot;") | ||
| Rel("cia, esv, #quot;Fetches financial data#quot;, #quot;HTTPS/JSON#quot;") |
| Two scripts live under [`scripts/`](./scripts/): | ||
|
|
||
| | Script | Purpose | | ||
| |---|---| | ||
| | `scripts/validate-mermaid.mjs` | Renders every ```` ```mermaid ```` block in every `*.md` file via `@mermaid-js/mermaid-cli`. Fails CI on any unrenderable diagram. | | ||
| | `scripts/deep-check-mermaid.mjs` | Inventories palette/theme usage and reports node labels with risky characters that should be quoted. Advisory — does not fail CI. | | ||
| | `scripts/fix-mermaid-quotes.mjs` | Idempotently rewrites node labels that contain emoji, `&`, parentheses, `:`, `;`, `,` or `fa:fa-` icons to be wrapped in `"…"`. Safe to re-run. | |
| # Renders every ```mermaid block in every *.md file in the repository via the | ||
| # official @mermaid-js/mermaid-cli and fails if any diagram does not render. | ||
| # Runs on every PR that touches a markdown file, on every push to master, and | ||
| # nightly to catch regressions caused by upstream Mermaid releases. |
| * Wraps node labels in ```mermaid blocks with double quotes when they contain | ||
| * characters that are safer or required to be quoted: | ||
| * - Emoji (Unicode pictographs / symbols) | ||
| * - `&` (ampersand) | ||
| * - `(` `)` parentheses | ||
| * - `:` colon | ||
| * - `;` semicolon | ||
| * - `,` comma | ||
| * - `<` `>` (outside `<br/>` which is allowed in mermaid) | ||
| * - `fa:fa-…` Font Awesome icon syntax |
| // Require the opener to be preceded by an identifier char, otherwise it's | ||
| // not a node-label opening (it's punctuation in normal text — e.g. the | ||
| // `>` in `-->`, or the `(` in a free-text phrase). If not a node open, | ||
| // advance past JUST the opener so we don't consume bracket pairs that | ||
| // legitimately belong to following nodes (that was a real bug). | ||
| const prevChar = found.idx > 0 ? code[found.idx - 1] : ''; | ||
| const isNodeOpen = isIdentChar(prevChar); | ||
| if (!isNodeOpen) { | ||
| out += code.slice(i, found.idx + 1); // include first char of opener | ||
| i = found.idx + 1; | ||
| continue; | ||
| } |
| const ampRe = /[\[(]([^"\[\]()][^\[\]()]*&[^\[\]()]*?)[\])]/g; | ||
| while ((lm = ampRe.exec(line)) !== null) { | ||
| if (!lm[1].startsWith('"') && !lm[1].includes('&')) { | ||
| issues.ampersandInLabel.push({ file: rel, line: lineNo, snippet: line.trim() }); | ||
| } |
| const REPO = path.resolve(process.argv[2] || path.join(__dirname, '..')); | ||
| const WORK = process.env.MERMAID_WORK || path.join(REPO, '.mermaid-validate'); | ||
| const IGNORE = new Set(['node_modules', 'target', 'dist', 'build', '.git', '.mermaid-validate']); | ||
|
|
||
| // Resolve mmdc binary - prefer local node_modules, then ancestor node_modules, then PATH | ||
| function resolveMmdc() { | ||
| const candidates = [ | ||
| path.join(REPO, 'node_modules', '.bin', 'mmdc'), | ||
| path.join(__dirname, '..', 'node_modules', '.bin', 'mmdc'), | ||
| '/tmp/mmd-test/node_modules/.bin/mmdc', | ||
| ]; | ||
| for (const c of candidates) if (existsSync(c)) return c; | ||
| return 'mmdc'; | ||
| } | ||
| const MMDC = process.env.MMDC || resolveMmdc(); | ||
|
|
||
| // Puppeteer needs --no-sandbox in CI / container environments | ||
| const PUPPETEER_CFG = path.join(WORK, 'puppeteer-config.json'); | ||
|
|
||
| async function* walk(dir) { | ||
| let ents; | ||
| try { ents = await readdir(dir, { withFileTypes: true }); } catch { return; } | ||
| for (const ent of ents) { | ||
| if (IGNORE.has(ent.name)) continue; | ||
| const p = path.join(dir, ent.name); | ||
| if (ent.isDirectory()) yield* walk(p); | ||
| else if (ent.isFile() && ent.name.endsWith('.md')) yield p; | ||
| } | ||
| } | ||
|
|
||
| const fence = /```mermaid\s*\n([\s\S]*?)```/g; | ||
|
|
||
| async function main() { | ||
| await rm(WORK, { recursive: true, force: true }); | ||
| await mkdir(WORK, { recursive: true }); | ||
| await writeFile(PUPPETEER_CFG, JSON.stringify({ |
| try { | ||
| await execFileP(MMDC, ['-i', tmp, '-o', out, '-p', PUPPETEER_CFG, '-q'], { | ||
| timeout: 90_000, | ||
| maxBuffer: 8 * 1024 * 1024, | ||
| }); | ||
| results.push({ file: path.relative(REPO, file), idx, line, ok: true }); | ||
| } catch (e) { | ||
| const errMsg = (e.stderr || e.stdout || e.message || '').toString(); | ||
| results.push({ | ||
| file: path.relative(REPO, file), | ||
| idx, | ||
| line, | ||
| ok: false, | ||
| err: errMsg.slice(0, 4000), | ||
| code, | ||
| }); | ||
| } |
- Restore C4 diagrams in ARCHITECTURE.md, FUTURE_ARCHITECTURE.md, and SKILL.md that were incorrectly rewritten by the quote fixer - Fix CI: use explicit package.json instead of `npm init -y` in .mermaid-tools (dot-prefixed names are invalid npm package names) - Fix workflow comment: "nightly" → "weekly" to match actual cron schedule - Fix style guide: "Two scripts" → "Three scripts" to match actual count - Fix fix-mermaid-quotes.mjs: skip C4 diagram blocks, update docstring to match actual implementation (no `:` or `<>` handling) - Fix deep-check-mermaid.mjs: remove unused `lm` assignment in faRe loop, remove `&` exemption from ampersand check - Fix validate-mermaid.mjs: add safety guard for MERMAID_WORK path, detect ENOENT from mmdc and exit with code 2 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
|
@copilot analyse and improve all faiiling mermaid failing checks |
The theme block example in MERMAID_STYLE_GUIDE.md contained only a
%%{init}%% directive with no diagram type, causing mmdc to fail with
"UnknownDiagramError: No diagram type detected". Added a minimal
`graph LR` body below the init block so the example renders while
still demonstrating the recommended theme configuration.
Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Analysed the workflow artifact ( |
|



Description
Adds automated Mermaid diagram validation tooling and enforces quoting of node labels containing special characters (emoji,
&, parentheses,;,,,fa:fa-icons). Includes a quote fixer script, deep-check inventory script, and a CI workflow that renders every mermaid block via@mermaid-js/mermaid-cli.Key fixes applied based on review feedback:
ARCHITECTURE.md,FUTURE_ARCHITECTURE.md, andSKILL.mdthat was incorrectly rewritten by the quote fixernpm init -ywith explicitpackage.jsonin.mermaid-toolsdirectory (dot-prefixed names are invalid npm package names)&exemptionMERMAID_WORKpath, addedENOENTdetection for missingmmdc(exits with code 2)graph LRbody to the%%{init}%%theme example inMERMAID_STYLE_GUIDE.mdso it has a valid diagram type and renders under mmdc (was the sole failing diagram out of 490 in the CI validation artifact)Type of Change
Primary Changes
Political Analysis
Technical Changes
Impact Analysis
Political Analysis Impact
Technical Impact
MERMAID_WORKenv varTesting
Scripts verified via:
node --checksyntax validation on all three scriptsfix-mermaid-quotes.mjs --dry-runconfirms C4 diagrams are now skippeddeep-check-mermaid.mjsruns successfully with corrected logicvalidate-mermaid.mjsCI artifact (report.json) analysed: after the theme-example fix, 0 of 490 diagrams fail to renderDocumentation
New
MERMAID_STYLE_GUIDE.mddocuments palette, quoting rules, and tooling usage.Screenshots
N/A
Related Issues
Related to #8637
Checklist
Additional Notes
The quote fixer now detects C4 diagram types (
C4Context,C4Container,C4Component,C4Dynamic,C4Deployment) and skips them entirely, since C4 macros use parentheses as function-call delimiters rather than node-label brackets.The theme-block example in
MERMAID_STYLE_GUIDE.mdpreviously consisted of only a%%{init}%%directive, which mmdc rejected withUnknownDiagramError: No diagram type detected. It now includes a minimalgraph LRbody so the example renders while still demonstrating the recommended theme configuration.Security Considerations
Added path validation for
MERMAID_WORKto prevent accidental recursive deletion of sensitive directories.Release Notes
Added Mermaid diagram validation tooling: CI workflow validates all mermaid blocks render correctly, deep-check script inventories style issues, and quote fixer automatically wraps risky node labels in quotes while preserving C4 diagram syntax.