diff --git a/.github/workflows/a11y-check.yml b/.github/workflows/a11y-check.yml index d135ec6..9805e08 100644 --- a/.github/workflows/a11y-check.yml +++ b/.github/workflows/a11y-check.yml @@ -32,7 +32,7 @@ jobs: - name: Diagram pipeline unit tests working-directory: site - run: node --test test/diagram.test.mjs test/replace-plantuml.test.mjs test/schema-versions.test.mjs test/build-sports.test.mjs + run: node --test test/diagram.test.mjs test/replace-plantuml.test.mjs test/schema-versions.test.mjs test/build-sports.test.mjs test/mermaid-marked.test.mjs - name: Build site working-directory: site @@ -70,6 +70,7 @@ jobs: http://localhost:4321/learn/ \ http://localhost:4321/use/ \ http://localhost:4321/build/ \ + http://localhost:4321/build/architecture/ \ http://localhost:4321/docs/ \ http://localhost:4321/docs/spec/ \ http://localhost:4321/docs/schema/ \ diff --git a/docs/superpowers/plans/2026-08-28-siteD-arc42.md b/docs/superpowers/plans/2026-08-28-siteD-arc42.md new file mode 100644 index 0000000..5cbed4c --- /dev/null +++ b/docs/superpowers/plans/2026-08-28-siteD-arc42.md @@ -0,0 +1,1173 @@ +# Plan D — arc42 Architecture Integration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to execute this plan. Work one task at a time, dispatch a subagent per task, and stop at each review checkpoint. Do not batch tasks. + +## Goal + +Ship Plan D of 4 (the final plan) of the site redesign. Pull the ocf-validator's +**arc42 architecture docs** into the Astro site at build time and publish them +under **Build → Architecture**, with their Mermaid diagrams rendered. + +The arc42 docs live in `opencoachingformat/ocf-validator` at `docs/arc42/*.md` +— 13 Markdown files: a `README.md` and twelve numbered sections +(`01-introduction-and-goals` … `12-glossary`). Exactly three of them +(`03-system-scope-context`, `06-runtime-view`, `10-quality-requirements`) +contain fenced ` ```mermaid ` code blocks; there is **no PlantUML** anywhere in +arc42. + +A build step (`site/scripts/build-arc42.mjs`) fetches those 13 files from a +**pinned validator commit** via jsDelivr's GitHub-file CDN (the same +read-remote-at-build → write-`src/generated/` pattern as `build-adoc.mjs` +already uses for `error-codes.json`), renders each Markdown file to HTML with +`marked`, and — crucially — converts each fenced ` ```mermaid ` block into a +`
…
` element (instead of the default +`
`), so that a small client-side module
+script (`mermaid@11` ESM from jsDelivr) renders them **in the browser**. This
+is the design's 2026-08-28 revision: **Mermaid renders client-side**;
+`@mermaid-js/mermaid-cli`/`mmdc` is explicitly **not** used (it downloads a
+headless Chromium and failed to install in this environment). PlantUML stays
+build-time (Plan A), but arc42 has no PlantUML so this plan touches only Mermaid.
+
+Two new Astro routes serve the docs: `build/architecture/index.astro` (an
+overview + table of contents) and `build/architecture/[section].astro` (a
+`getStaticPaths` template that renders each section's generated HTML via
+`set:html`). The section pages include the client-side mermaid module script;
+the Build hub gains an **Architecture** card linking `/build/architecture/`.
+
+Plan D changes **no schema/validator code** — it consumes the arc42 Markdown at
+a pinned commit. It adds one runtime-free dependency (`marked`) to the site.
+
+## Architecture
+
+**Verified facts (2026-08-28):**
+
+- arc42 lives in `opencoachingformat/ocf-validator` at `docs/arc42/`, 13
+  Markdown files: `README.md`, `01-introduction-and-goals.md`,
+  `02-architecture-constraints.md`, `03-system-scope-context.md`,
+  `04-solution-strategy.md`, `05-building-block-view.md`, `06-runtime-view.md`,
+  `07-deployment-view.md`, `08-crosscutting-concepts.md`,
+  `09-architecture-decisions.md`, `10-quality-requirements.md`,
+  `11-risks-and-technical-debt.md`, `12-glossary.md`. Only
+  `03-system-scope-context`, `06-runtime-view`, and `10-quality-requirements`
+  contain ` ```mermaid ` fenced blocks. No PlantUML anywhere in arc42.
+- The validator repo's `main` commit that carries the current arc42 is
+  `07361d99f2433f2c715486e0894da5594160a218`. This plan pins arc42 to that
+  commit via a **new, separate** `OCF_ARC42_COMMIT` constant in
+  `site/src/lib/validator-version.mjs` (kept distinct from
+  `OCF_VALIDATOR_COMMIT`, which coincidentally holds the same value today, so
+  that arc42 docs and the validator bundle can be re-pinned independently later).
+- jsDelivr serves individual repo files at
+  `https://cdn.jsdelivr.net/gh/opencoachingformat/ocf-validator@/docs/arc42/.md`.
+  This mirrors how `OCF_ERROR_CODES_URL` is built from `JSDELIVR_GH_BASE` in
+  `validator-version.mjs`. arc42 is **not** part of the published npm package,
+  so — like `error-codes.json` — it must come from a pinned git commit, not the
+  npm CDN.
+- `mermaid@11` ESM loads at
+  `https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs` (HTTP 200,
+  `Access-Control-Allow-Origin: *`). Client-side rendering: emit
+  `
SOURCE
` and a module ` + )} + + + +
+ + + +

← All sections

+ + + + ``` + +3. Build and assert every section page resolves, at least one carries a + `
');process.exit(1)}
+   if(scriptPages!==mermaidPages){console.error('mermaid pages without script:',mermaidPages-scriptPages);process.exit(1)}
+   console.log('arc42 sections OK:',idx.length,'pages,',mermaidPages,'with mermaid + module script');
+   "
+   ```
+   Expect success (≥1 section with `
+         
+         
+         
+         
+       
+   ```
+   Insert an **Architecture** card between the JSON Schema card and the Error
+   codes card so the grid reads:
+   ```astro
+       
+ + + + + +
+ ``` + Leave the frontmatter, hero section, and ` diff --git a/site/src/pages/build/architecture/index.astro b/site/src/pages/build/architecture/index.astro new file mode 100644 index 0000000..299915a --- /dev/null +++ b/site/src/pages/build/architecture/index.astro @@ -0,0 +1,51 @@ +--- +import Base from '../../../layouts/Base.astro'; +import Card from '../../../components/Card.astro'; +import arc42 from '../../../generated/arc42/index.json'; +--- + +
+

Architecture (arc42)

+

+ How the reference validator is built, documented with the + arc42 template. Pulled from the + ocf-validator + repository at a pinned commit and rendered here — diagrams included. +

+
+ +
+
+ {arc42.map((entry) => ( + + ))} +
+
+ +

← Build

+ + + diff --git a/site/src/pages/build/index.astro b/site/src/pages/build/index.astro index b9de147..962d3ca 100644 --- a/site/src/pages/build/index.astro +++ b/site/src/pages/build/index.astro @@ -26,6 +26,11 @@ import Card from '../../components/Card.astro'; description="The machine-readable JSON Schema you validate documents against." href="/docs/schema/" /> + ", () => { + const md = "# Title\n\n```mermaid\ngraph TD\n A --> B\n```\n"; + const html = mermaidAwareMarked(md); + assert.match(html, /
/);
+  // The diagram source is preserved verbatim inside the pre (NOT wrapped in
+  // , NOT given a language-* class).
+  assert.match(html, /graph TD/);
+  assert.match(html, /A --> B/);
+  assert.doesNotMatch(html, /language-mermaid/);
+  // The 
 block holds no nested  element.
+  const block = html.slice(html.indexOf('
'));
+  assert.doesNotMatch(block.slice(0, block.indexOf('
')), / { + // mermaid parses raw source; --> must stay literal, not >/&. + const md = "```mermaid\nA --> B\nC -->|x & y| D\n```\n"; + const html = mermaidAwareMarked(md); + assert.match(html, /A --> B/); + assert.match(html, /-->\|x & y\| D/); + assert.doesNotMatch(html, />/); + assert.doesNotMatch(html, /&/); +}); + +test("mermaidAwareMarked: renders ordinary markdown normally", () => { + const html = mermaidAwareMarked("# Heading\n\nSome **bold** text.\n"); + assert.match(html, /]/); + assert.match(html, /bold<\/strong>/); +}); + +test("mermaidAwareMarked: a non-mermaid code fence stays a normal block, escaped", () => { + const md = "```json\n{ \"a\": 1 < 2 }\n```\n"; + const html = mermaidAwareMarked(md); + assert.match(html, /]*>/); + assert.doesNotMatch(html, /
/);
+  // Ordinary code fences keep marked's default HTML escaping.
+  assert.match(html, /1 < 2/);
+});
+
+test("mermaidAwareMarked: handles multiple mermaid blocks in one document", () => {
+  const md = "```mermaid\ngraph LR\nA-->B\n```\n\ntext\n\n```mermaid\nsequenceDiagram\nA->>B: hi\n```\n";
+  const html = mermaidAwareMarked(md);
+  const count = (html.match(/
/g) || []).length;
+  assert.equal(count, 2);
+  assert.match(html, /sequenceDiagram/);
+});
+
+test("buildArc42Index: derives slug + title, README first, then numeric order", () => {
+  const files = [
+    "12-glossary.md",
+    "README.md",
+    "01-introduction-and-goals.md",
+    "06-runtime-view.md",
+  ];
+  const idx = buildArc42Index(files);
+  assert.deepEqual(idx.map((e) => e.slug), [
+    "readme",
+    "01-introduction-and-goals",
+    "06-runtime-view",
+    "12-glossary",
+  ]);
+  const readme = idx.find((e) => e.slug === "readme");
+  assert.equal(readme.title, "Overview");
+  const intro = idx.find((e) => e.slug === "01-introduction-and-goals");
+  assert.equal(intro.title, "1. Introduction and Goals");
+  const runtime = idx.find((e) => e.slug === "06-runtime-view");
+  assert.equal(runtime.title, "6. Runtime View");
+  assert.equal(runtime.file, "06-runtime-view.md");
+});
+
+test("buildArc42Index: ignores non-markdown files", () => {
+  const idx = buildArc42Index(["README.md", "images", "notes.txt"]);
+  assert.deepEqual(idx.map((e) => e.slug), ["readme"]);
+});