{"id":"quality","relativePath":"quality.md","title":"Quality & Performance","markdown":"# Quality & Performance\n\nMeasured, CI-enforced quality signals. The numbers below come from CI and the evidence pipeline — they are gated, not hand-asserted: if a score regresses below its threshold, the build fails.\n\n## Accessibility — WCAG 2.1 AA\n\nTwo independent checks run on every CI build (`.github/workflows/ci.yml`).\n\n### Lighthouse accessibility — **100 / 100** (gate: ≥ 95)\n\n| Page | Score |\n|---|:--:|\n| Home `/` | **100** |\n| Explore `/explore` | **100** |\n| Artwork `/artwork/[id]` | **100** |\n\n`scripts/lighthouse-a11y.mjs` fails the build if any page scores below 95. Run: `pnpm lighthouse:ci`.\n\n### axe-core — **0 severe violations** across 18 routes\n\nZero critical / serious / moderate WCAG 2A + 2AA violations across **18 public routes**: home, explore, records, linked-art, patterns, insights, graph, entities, iiif, issues, agents, automation, etl/mapper, roadmap, getty, curator/reconciliation, plus dynamic `/artwork/[id]` and `/entity/[id]`. `scripts/a11y-check.ts` fails the build on any severe violation. Run: `pnpm a11y:ci`.\n\n## Performance budget — k6 SLOs\n\n`scripts/k6-slo.js` enforces a per-scenario latency + error-rate budget under load (16 req/s, up to 24 VUs). Latest measured run (`artifacts/performance/k6-slo-summary.json`) — all measured scenarios comfortably within budget:\n\n| Scenario | p95 budget | measured p95 | error budget | measured |\n|---|:--:|:--:|:--:|:--:|\n| Cached record read | < 200 ms | **73.5 ms** | < 1% | **0.00%** |\n| Cold record read | < 500 ms | **56.1 ms** | < 1% | **0.00%** |\n| Keyword / facet search | < 300 ms | **55.1 ms** | < 1% | **0.00%** |\n| SPARQL (whitelisted) | < 2000 ms | _needs GraphDB_ | < 1% | — |\n| IIIF tile serving | < 100 ms | _needs IIIF infra_ | < 1% | — |\n\nThe k6 thresholds abort the run on any breach. Enforced by `pnpm k6:slo:ci` in the evidence pipeline (`.github/workflows/era-c-exit-gate-evidence.yml`).\n\n## Frontend Core Web Vitals lab baseline\n\nThe July 28, 2026 production trace covers Home, Explore, one artwork detail,\nProjects, and Pilot on mobile and desktop. All ten cold-load traces pass the\nP1.5 budgets: the slowest LCP is Explore mobile at **2,286 ms**, the largest CLS\nis Pilot desktop at **0.0665**, and a representative interaction measures\n**28 ms INP**. CrUX did not return page-level field data, so these are lab\nresults. Methodology and route-level measurements:\n[frontend-performance-baseline.md](ops/frontend-performance-baseline.md).\nThe Step 6 follow-up axe run passes all 18 routes. The Home overflow and\nlow-resolution V&A thumbnail blockers are fixed locally with regression\ncoverage. The post-fix local matrix passes every mobile/desktop performance and\nvisual check, with 0.00 CLS, a maximum 1,628 ms LCP, and 29 ms representative\nINP. A deployed-revision capture remains before closing P1.5.\n\n## Test coverage\n\n`pnpm test:coverage` (c8) — **89.4% lines · 92.1% functions · 75.5% branches** across 1,116 tests, gated in CI (floor 85 / 85 / 70). Core application code: `src/services` 91.9%, `src/adapters` 91.3%, `src/gateway` 100%.\n\nThe canonical full suite stays serial for shared-state determinism and uses\nNode's compact dot reporter. On July 28, 2026, four diagnostic shards passed\n`1,704/1,704` tests in about 158 seconds combined, and the equivalent unsharded\ndot-reporter run passed in 154 seconds. The verbose reporter exceeded five\nminutes only when its captured output saturated the command channel.\n\n## Other CI gates\n\n- **Types & lint** — TypeScript `strict`, ESLint (`pnpm lint`).\n- **Linked Art conformance** — protocol MUSTs + per-provider SHACL shapes (`shacl-conformance.yml`) + a generated, drift-gated conformance matrix.\n- **Supply chain** — `pnpm audit --prod` (fails on high/critical) + Dependabot.\n- **Visual** — public-trust Playwright smoke with screenshot diffing.\n\n_Last refreshed: 2026-07-28. Accessibility and coverage numbers are reproduced on every CI run; backend performance numbers are from the latest evidence-pipeline run; frontend metrics are the dated production lab baseline._\n","sections":[{"level":2,"heading":"Accessibility — WCAG 2.1 AA","anchor":"accessibility-wcag-2-1-aa"},{"level":3,"heading":"Lighthouse accessibility — **100 / 100** (gate: ≥ 95)","anchor":"lighthouse-accessibility-100-100-gate-95"},{"level":3,"heading":"axe-core — **0 severe violations** across 18 routes","anchor":"axe-core-0-severe-violations-across-18-routes"},{"level":2,"heading":"Performance budget — k6 SLOs","anchor":"performance-budget-k6-slos"},{"level":2,"heading":"Frontend Core Web Vitals lab baseline","anchor":"frontend-core-web-vitals-lab-baseline"},{"level":2,"heading":"Test coverage","anchor":"test-coverage"},{"level":2,"heading":"Other CI gates","anchor":"other-ci-gates"}],"html":"<h1 id=\"quality-performance\">Quality &amp; Performance</h1>\n<p>Measured, CI-enforced quality signals. The numbers below come from CI and the evidence pipeline — they are gated, not hand-asserted: if a score regresses below its threshold, the build fails.</p>\n<h2 id=\"accessibility-wcag-2-1-aa\">Accessibility — WCAG 2.1 AA</h2>\n<p>Two independent checks run on every CI build (`.github/workflows/ci.yml`).</p>\n<h3 id=\"lighthouse-accessibility-100-100-gate-95\">Lighthouse accessibility — <strong>100 / 100</strong> (gate: ≥ 95)</h3>\n<p>| Page | Score |</p>\n<p>|---|:--:|</p>\n<p>| Home `/` | <strong>100</strong> |</p>\n<p>| Explore `/explore` | <strong>100</strong> |</p>\n<p>| Artwork `/artwork/[id]` | <strong>100</strong> |</p>\n<p>`scripts/lighthouse-a11y.mjs` fails the build if any page scores below 95. Run: `pnpm lighthouse:ci`.</p>\n<h3 id=\"axe-core-0-severe-violations-across-18-routes\">axe-core — <strong>0 severe violations</strong> across 18 routes</h3>\n<p>Zero critical / serious / moderate WCAG 2A + 2AA violations across <strong>18 public routes</strong>: home, explore, records, linked-art, patterns, insights, graph, entities, iiif, issues, agents, automation, etl/mapper, roadmap, getty, curator/reconciliation, plus dynamic `/artwork/[id]` and `/entity/[id]`. `scripts/a11y-check.ts` fails the build on any severe violation. Run: `pnpm a11y:ci`.</p>\n<h2 id=\"performance-budget-k6-slos\">Performance budget — k6 SLOs</h2>\n<p>`scripts/k6-slo.js` enforces a per-scenario latency + error-rate budget under load (16 req/s, up to 24 VUs). Latest measured run (`artifacts/performance/k6-slo-summary.json`) — all measured scenarios comfortably within budget:</p>\n<p>| Scenario | p95 budget | measured p95 | error budget | measured |</p>\n<p>|---|:--:|:--:|:--:|:--:|</p>\n<p>| Cached record read | &lt; 200 ms | <strong>73.5 ms</strong> | &lt; 1% | <strong>0.00%</strong> |</p>\n<p>| Cold record read | &lt; 500 ms | <strong>56.1 ms</strong> | &lt; 1% | <strong>0.00%</strong> |</p>\n<p>| Keyword / facet search | &lt; 300 ms | <strong>55.1 ms</strong> | &lt; 1% | <strong>0.00%</strong> |</p>\n<p>| SPARQL (whitelisted) | &lt; 2000 ms | _needs GraphDB_ | &lt; 1% | — |</p>\n<p>| IIIF tile serving | &lt; 100 ms | _needs IIIF infra_ | &lt; 1% | — |</p>\n<p>The k6 thresholds abort the run on any breach. Enforced by `pnpm k6:slo:ci` in the evidence pipeline (`.github/workflows/era-c-exit-gate-evidence.yml`).</p>\n<h2 id=\"frontend-core-web-vitals-lab-baseline\">Frontend Core Web Vitals lab baseline</h2>\n<p>The July 28, 2026 production trace covers Home, Explore, one artwork detail,</p>\n<p>Projects, and Pilot on mobile and desktop. All ten cold-load traces pass the</p>\n<p>P1.5 budgets: the slowest LCP is Explore mobile at <strong>2,286 ms</strong>, the largest CLS</p>\n<p>is Pilot desktop at <strong>0.0665</strong>, and a representative interaction measures</p>\n<p><strong>28 ms INP</strong>. CrUX did not return page-level field data, so these are lab</p>\n<p>results. Methodology and route-level measurements:</p>\n<p>frontend-performance-baseline.md(ops/frontend-performance-baseline.md).</p>\n<p>The Step 6 follow-up axe run passes all 18 routes. The Home overflow and</p>\n<p>low-resolution V&amp;A thumbnail blockers are fixed locally with regression</p>\n<p>coverage. The post-fix local matrix passes every mobile/desktop performance and</p>\n<p>visual check, with 0.00 CLS, a maximum 1,628 ms LCP, and 29 ms representative</p>\n<p>INP. A deployed-revision capture remains before closing P1.5.</p>\n<h2 id=\"test-coverage\">Test coverage</h2>\n<p>`pnpm test:coverage` (c8) — <strong>89.4% lines · 92.1% functions · 75.5% branches</strong> across 1,116 tests, gated in CI (floor 85 / 85 / 70). Core application code: `src/services` 91.9%, `src/adapters` 91.3%, `src/gateway` 100%.</p>\n<p>The canonical full suite stays serial for shared-state determinism and uses</p>\n<p>Node&#39;s compact dot reporter. On July 28, 2026, four diagnostic shards passed</p>\n<p>`1,704/1,704` tests in about 158 seconds combined, and the equivalent unsharded</p>\n<p>dot-reporter run passed in 154 seconds. The verbose reporter exceeded five</p>\n<p>minutes only when its captured output saturated the command channel.</p>\n<h2 id=\"other-ci-gates\">Other CI gates</h2>\n<ul><li><strong>Types &amp; lint</strong> — TypeScript `strict`, ESLint (`pnpm lint`).</li><li><strong>Linked Art conformance</strong> — protocol MUSTs + per-provider SHACL shapes (`shacl-conformance.yml`) + a generated, drift-gated conformance matrix.</li><li><strong>Supply chain</strong> — `pnpm audit --prod` (fails on high/critical) + Dependabot.</li><li><strong>Visual</strong> — public-trust Playwright smoke with screenshot diffing.</li></ul>\n<p>_Last refreshed: 2026-07-28. Accessibility and coverage numbers are reproduced on every CI run; backend performance numbers are from the latest evidence-pipeline run; frontend metrics are the dated production lab baseline._</p>","updatedAt":"2018-10-20T01:46:40.000Z","checksum":"5257104be07ee61c8d4eecd66c560321df3b146218de595f52fa4705f281c088","checksumPrefix":"5257104be07e","anchorCount":7,"lineCount":70,"rawUrl":"/api/docs/content?path=quality.md","htmlUrl":"/docs?doc=quality.md","apiUrl":"/api/docs/content?path=quality.md"}