{"id":"roadmap-to-10","relativePath":"roadmap-to-10.md","title":"Roadmap to 10/10","markdown":"# Roadmap to 10/10\n\n**Goal:** raise both scores to a defensible 10/10 — **portfolio readiness** and **Linked Art 1.0 adherence**. Current honest baseline: ~9 / ~8.5.\n\nEach milestone is one focused PR with explicit acceptance criteria. Check items off as they land.\n\n---\n\n## Track A — Portfolio readiness → 10\n\n### A1. Reliable, badged CI ✅ done (2026-06-24)\nWas: the close-out guard (wired into `pnpm test`/`lint`/`build`) turned CI **red** whenever the local close-out log was >24h old (e.g. PR #16).\n- [x] `scripts/session-closeout.ts` now skips the `--check` guard when `CI` is set (GitHub Actions) — CI tests code, not the local session ritual; the guard still enforces locally. Verified both ways (CI=true → skip; local → enforce).\n- [x] README header carries **CI**, **License: MIT**, **Linked Art 1.0**, and **tests** badges.\n- **Done when:** consecutive green CI runs on `main` (this merge produces the first); badges render. ✅ mechanism fixed; green runs accrue from here.\n\n### A2. Dependency & supply-chain hygiene ✅ done (2026-06-24)\n- [x] Resolved the 3 moderate `pnpm audit --prod` advisories via `pnpm.overrides`: postcss → `>=8.5.10` (XSS), and the OTel tree consolidated on `@opentelemetry/core`/`resources`/`sdk-trace-base` `^2.8.0` (memory-DoS fix; also corrects `@vercel/otel`'s mis-resolved 1.30.1 peers). Verified: audit clean, tests 1,114, build green.\n- [x] `.github/dependabot.yml` covers npm + github-actions + all four pip services (weekly, grouped minor/patch); `ci.yml` gains a `pnpm audit --prod --audit-level high` gate.\n- **Done:** `pnpm audit --prod` reports \"No known vulnerabilities found\"; Dependabot config active. (One pre-existing upstream `@vercel/otel` peer warning on `instrumentation@0.57.2` remains — not a vulnerability; Dependabot will surface the `@vercel/otel` bump that fixes it.)\n\n### A3. Measured test coverage ✅ done (2026-06-24)\n- [x] `pnpm test:coverage` runs the suite under **c8** with a `--check-coverage` gate (lines 85 / functions 85 / branches 70). CI's test step now runs it, so coverage is measured and **gated** on every run.\n- [x] README shows a coverage badge; current **89.4% lines / 92.1% funcs / 75.5% branch** (`src/services` 91.9%, `src/adapters` 91.3%, `src/gateway` 100% — core services well above the 80% bar).\n- [x] Also fixed a CRLF-fragility in the B3 conformance-matrix drift test/generator (normalize line endings) so coverage runs clean on Windows.\n- **Done:** coverage runs + gates in CI; badge published; core services ≥ 80%.\n\n### A4. Published quality scores ✅ done (2026-06-24)\n- [x] [`docs/quality.md`](quality.md) publishes the CI-measured numbers: Lighthouse a11y **100/100** (`/`, `/explore`, `/artwork`), axe **0 severe** WCAG 2A/2AA violations across 18 routes, and the k6 performance budget **met** (cached read 73.5 ms < 200, cold read 56.1 ms < 500, facet search 55.1 ms < 300, 0% errors). README gains an a11y badge.\n- **Done:** a11y ≥ 95 (it's 100), the stated p95 performance budget is met, all scores published + CI-gated.\n\n### A5. Coherent docs (no remaining walls) ✅ done (2026-06-24)\n- [x] Trimmed `docs/roadmap.md` **1,510 → ~420 lines**: kept the current Status, SaaS track, Linked Art uplift, Stack decisions, cross-cutting standards, and forward plan; archived the slice-by-slice Era A/B/C saga (~1,100 lines) to [`docs/progress/era-history.md`](progress/era-history.md) with a concise pointer. `getStructuredRoadmap` aggregates both so `/api/roadmap` still exposes full phases/milestones to agents.\n- **Done:** the roadmap reads as current; full suite 1,128 green.\n\n### A6. (Optional wow) 🟡 script ready — recording pending\n- [x] Shot-by-shot demo script + screenshot-gallery plan at [`docs/demo-script.md`](demo-script.md) (60–90s, 6 shots).\n- [ ] Record + embed the video — needs a human screen-record (can't be automated here).\n\n---\n\n## Track B — Linked Art 1.0 adherence → 10\n\n### B1. Rights as `Right` entities for ALL providers ✅ done (2026-06-24)\nWas: only the Getty adapter emitted full `Right` entities; others flattened rights to labels.\n- [x] `src/utils/linked-art-rights.ts` synthesizes a `subject_to` `Right` classified by a CC0 / rightsstatements.org URI, wired into **both** boundaries — `normalizeIncomingRecord` (import) and `migrateToCurrentSchema` (read, so existing stored records conform too). Getty's `Right` entities are preserved; non-object entities are never given one; default is the honest \"Copyright Undetermined\".\n- **Done:** every object record carries `Right` + classification; covered by `tests/utils/linked-art-rights.test.ts` (full suite 1,114 green).\n\n### B2. SHACL conformance in CI ✅ done (2026-06-24)\n- [x] `services/validation-service/shacl_gate.py` validates every provider's pass fixture against the Linked Art SHACL shapes with pyld + pyshacl + rdflib (the same pipeline as the validation service); the JSON-LD expands to CIDOC-CRM and the `crm:E22` targetClass applies (not vacuous — a missing `identified_by`/`label` fails).\n- [x] `.github/workflows/shacl-conformance.yml` runs it as a path-filtered CI job (shapes/fixtures/service changes); `pnpm shacl:gate` runs it locally.\n- **Done:** all 14 pass fixtures conform; a regression that breaks CRM expansion blocks the build. (Shapes are still minimal — richer SHACL coverage is follow-on work.)\n\n### B3. Systematic per-provider fixture matrix ✅ done (2026-06-24)\n- [x] All 13 production providers (+ generic `linked-art`) have pass **and** fail fixtures in `provider-fixture-manifest.json`, asserted in CI by `validation-architecture-depth.test.ts` (pass conforms, fail rejected) — this part pre-existed.\n- [x] `scripts/generate-conformance-matrix.ts` now **generates** the per-provider table in `conformance-matrix.md` from those fixtures + the capability registry, validating each fixture live with `inspectLinkedArtRecord`. `pnpm conformance:matrix` rewrites it; `conformance:matrix:check` + `conformance-matrix-generated.test.ts` gate drift in CI.\n- **Done:** 13/13 providers pass both directions; the published matrix is generated, not hand-maintained. Full suite 1,116 green.\n\n### B4. Faceted / relevance search ✅ done (2026-06-24)\n- [x] `src/services/search.ts` ranks matches by hit quality (exact label > prefix > substring > name) and computes `type`/`provider` facet counts over the full match set; `/api/search` exposes `q`/`type`/`provider`/`limit`/`offset` and returns relevance order + facets in the `ld+json` `OrderedCollectionPage`. Tested (`tests/services/search.test.ts`, plus API-level facet/score/filter assertions).\n- [x] Conformance-matrix \"basic, not faceted\" gap closed; Solr 9 documented as the env-gated scale-out backend, this in-app impl as the portable default.\n- **Done:** facets + ranking live + tested; matrix gap closed.\n\n### B5. Activity Streams change-feed conformance ✅ done (2026-06-24)\n- [x] Aligned `/api/activity` to Activity Streams 2.0: added the AS2 `@context`, AS2 `next`/`prev` page links (kept `nextPage`/`prevPage` aliases), a fuller `partOf` `OrderedCollection` with `first`/`last`/`totalItems`, and the `application/activity+json` media type. `Create`/`Update`/`Delete` items unchanged.\n- **Done:** the feed validates against the AS2 spec shape (`tests/api/activity-as2.test.ts`); suite 1,129.\n\n### B6. HTTP SHOULDs ✅ done (2026-06-24)\n- [x] Canonical Linked Art entity/collection routes (`objects`, `artworks`, `works`, `agents`, `sets`, `concepts`, `entities`, `records`, `search`) export `HEAD` via a `bodilessResponse(await GET(...))` helper — same headers as GET, no body, mirrors 200/404; `OPTIONS` advertises `GET,HEAD,OPTIONS`. Covered by `tests/api/head-methods.test.ts`.\n- [x] HTTP/2 confirmed on the live deploy (`HTTP/2.0 200` via Vercel edge).\n- **Done:** `HEAD` returns headers without a body; HTTP/2 verified live. Full suite 1,128.\n\n---\n\n## Recommended sequence\n\n1. **A1** — green, badged CI (fast, unblocks a clean portfolio surface).\n2. **A2** — vulns + Dependabot (quick, removes a real ding).\n3. **B1** — rights as `Right` entities (the biggest Linked Art win).\n4. **A3 + B3** — measurable rigor (coverage + the fixture matrix).\n5. **B2 + B4** — the heavier conformance lifts (SHACL CI, faceted search).\n6. **A4 / A5 / A6 + B5 / B6** — polish to close out both 10s.\n\nEach step ships as its own PR with the acceptance criteria above met and the full suite green.\n","sections":[{"level":2,"heading":"Track A — Portfolio readiness → 10","anchor":"track-a-portfolio-readiness-10"},{"level":3,"heading":"A1. Reliable, badged CI ✅ done (2026-06-24)","anchor":"a1-reliable-badged-ci-done-2026-06-24"},{"level":3,"heading":"A2. Dependency & supply-chain hygiene ✅ done (2026-06-24)","anchor":"a2-dependency-supply-chain-hygiene-done-2026-06-24"},{"level":3,"heading":"A3. Measured test coverage ✅ done (2026-06-24)","anchor":"a3-measured-test-coverage-done-2026-06-24"},{"level":3,"heading":"A4. Published quality scores ✅ done (2026-06-24)","anchor":"a4-published-quality-scores-done-2026-06-24"},{"level":3,"heading":"A5. Coherent docs (no remaining walls) ✅ done (2026-06-24)","anchor":"a5-coherent-docs-no-remaining-walls-done-2026-06-24"},{"level":3,"heading":"A6. (Optional wow) 🟡 script ready — recording pending","anchor":"a6-optional-wow-script-ready-recording-pending"},{"level":2,"heading":"Track B — Linked Art 1.0 adherence → 10","anchor":"track-b-linked-art-1-0-adherence-10"},{"level":3,"heading":"B1. Rights as `Right` entities for ALL providers ✅ done (2026-06-24)","anchor":"b1-rights-as-right-entities-for-all-providers-done-2026-06-24"},{"level":3,"heading":"B2. SHACL conformance in CI ✅ done (2026-06-24)","anchor":"b2-shacl-conformance-in-ci-done-2026-06-24"},{"level":3,"heading":"B3. Systematic per-provider fixture matrix ✅ done (2026-06-24)","anchor":"b3-systematic-per-provider-fixture-matrix-done-2026-06-24"},{"level":3,"heading":"B4. Faceted / relevance search ✅ done (2026-06-24)","anchor":"b4-faceted-relevance-search-done-2026-06-24"},{"level":3,"heading":"B5. Activity Streams change-feed conformance ✅ done (2026-06-24)","anchor":"b5-activity-streams-change-feed-conformance-done-2026-06-24"},{"level":3,"heading":"B6. HTTP SHOULDs ✅ done (2026-06-24)","anchor":"b6-http-shoulds-done-2026-06-24"},{"level":2,"heading":"Recommended sequence","anchor":"recommended-sequence"}],"html":"<h1 id=\"roadmap-to-10-10\">Roadmap to 10/10</h1>\n<p><strong>Goal:</strong> raise both scores to a defensible 10/10 — <strong>portfolio readiness</strong> and <strong>Linked Art 1.0 adherence</strong>. Current honest baseline: ~9 / ~8.5.</p>\n<p>Each milestone is one focused PR with explicit acceptance criteria. Check items off as they land.</p>\n<p>---</p>\n<h2 id=\"track-a-portfolio-readiness-10\">Track A — Portfolio readiness → 10</h2>\n<h3 id=\"a1-reliable-badged-ci-done-2026-06-24\">A1. Reliable, badged CI ✅ done (2026-06-24)</h3>\n<p>Was: the close-out guard (wired into `pnpm test`/`lint`/`build`) turned CI <strong>red</strong> whenever the local close-out log was &gt;24h old (e.g. PR #16).</p>\n<ul><li>[x] `scripts/session-closeout.ts` now skips the `--check` guard when `CI` is set (GitHub Actions) — CI tests code, not the local session ritual; the guard still enforces locally. Verified both ways (CI=true → skip; local → enforce).</li><li>[x] README header carries <strong>CI</strong>, <strong>License: MIT</strong>, <strong>Linked Art 1.0</strong>, and <strong>tests</strong> badges.</li><li><strong>Done when:</strong> consecutive green CI runs on `main` (this merge produces the first); badges render. ✅ mechanism fixed; green runs accrue from here.</li></ul>\n<h3 id=\"a2-dependency-supply-chain-hygiene-done-2026-06-24\">A2. Dependency &amp; supply-chain hygiene ✅ done (2026-06-24)</h3>\n<ul><li>[x] Resolved the 3 moderate `pnpm audit --prod` advisories via `pnpm.overrides`: postcss → `&gt;=8.5.10` (XSS), and the OTel tree consolidated on `@opentelemetry/core`/`resources`/`sdk-trace-base` `^2.8.0` (memory-DoS fix; also corrects `@vercel/otel`&#39;s mis-resolved 1.30.1 peers). Verified: audit clean, tests 1,114, build green.</li><li>[x] `.github/dependabot.yml` covers npm + github-actions + all four pip services (weekly, grouped minor/patch); `ci.yml` gains a `pnpm audit --prod --audit-level high` gate.</li><li><strong>Done:</strong> `pnpm audit --prod` reports &quot;No known vulnerabilities found&quot;; Dependabot config active. (One pre-existing upstream `@vercel/otel` peer warning on `instrumentation@0.57.2` remains — not a vulnerability; Dependabot will surface the `@vercel/otel` bump that fixes it.)</li></ul>\n<h3 id=\"a3-measured-test-coverage-done-2026-06-24\">A3. Measured test coverage ✅ done (2026-06-24)</h3>\n<ul><li>[x] `pnpm test:coverage` runs the suite under <strong>c8</strong> with a `--check-coverage` gate (lines 85 / functions 85 / branches 70). CI&#39;s test step now runs it, so coverage is measured and <strong>gated</strong> on every run.</li><li>[x] README shows a coverage badge; current <strong>89.4% lines / 92.1% funcs / 75.5% branch</strong> (`src/services` 91.9%, `src/adapters` 91.3%, `src/gateway` 100% — core services well above the 80% bar).</li><li>[x] Also fixed a CRLF-fragility in the B3 conformance-matrix drift test/generator (normalize line endings) so coverage runs clean on Windows.</li><li><strong>Done:</strong> coverage runs + gates in CI; badge published; core services ≥ 80%.</li></ul>\n<h3 id=\"a4-published-quality-scores-done-2026-06-24\">A4. Published quality scores ✅ done (2026-06-24)</h3>\n<ul><li>[x] `docs/quality.md`(quality.md) publishes the CI-measured numbers: Lighthouse a11y <strong>100/100</strong> (`/`, `/explore`, `/artwork`), axe <strong>0 severe</strong> WCAG 2A/2AA violations across 18 routes, and the k6 performance budget <strong>met</strong> (cached read 73.5 ms &lt; 200, cold read 56.1 ms &lt; 500, facet search 55.1 ms &lt; 300, 0% errors). README gains an a11y badge.</li><li><strong>Done:</strong> a11y ≥ 95 (it&#39;s 100), the stated p95 performance budget is met, all scores published + CI-gated.</li></ul>\n<h3 id=\"a5-coherent-docs-no-remaining-walls-done-2026-06-24\">A5. Coherent docs (no remaining walls) ✅ done (2026-06-24)</h3>\n<ul><li>[x] Trimmed `docs/roadmap.md` <strong>1,510 → ~420 lines</strong>: kept the current Status, SaaS track, Linked Art uplift, Stack decisions, cross-cutting standards, and forward plan; archived the slice-by-slice Era A/B/C saga (~1,100 lines) to `docs/progress/era-history.md`(progress/era-history.md) with a concise pointer. `getStructuredRoadmap` aggregates both so `/api/roadmap` still exposes full phases/milestones to agents.</li><li><strong>Done:</strong> the roadmap reads as current; full suite 1,128 green.</li></ul>\n<h3 id=\"a6-optional-wow-script-ready-recording-pending\">A6. (Optional wow) 🟡 script ready — recording pending</h3>\n<ul><li>[x] Shot-by-shot demo script + screenshot-gallery plan at `docs/demo-script.md`(demo-script.md) (60–90s, 6 shots).</li><li>[ ] Record + embed the video — needs a human screen-record (can&#39;t be automated here).</li></ul>\n<p>---</p>\n<h2 id=\"track-b-linked-art-1-0-adherence-10\">Track B — Linked Art 1.0 adherence → 10</h2>\n<h3 id=\"b1-rights-as-right-entities-for-all-providers-done-2026-06-24\">B1. Rights as `Right` entities for ALL providers ✅ done (2026-06-24)</h3>\n<p>Was: only the Getty adapter emitted full `Right` entities; others flattened rights to labels.</p>\n<ul><li>[x] `src/utils/linked-art-rights.ts` synthesizes a `subject_to` `Right` classified by a CC0 / rightsstatements.org URI, wired into <strong>both</strong> boundaries — `normalizeIncomingRecord` (import) and `migrateToCurrentSchema` (read, so existing stored records conform too). Getty&#39;s `Right` entities are preserved; non-object entities are never given one; default is the honest &quot;Copyright Undetermined&quot;.</li><li><strong>Done:</strong> every object record carries `Right` + classification; covered by `tests/utils/linked-art-rights.test.ts` (full suite 1,114 green).</li></ul>\n<h3 id=\"b2-shacl-conformance-in-ci-done-2026-06-24\">B2. SHACL conformance in CI ✅ done (2026-06-24)</h3>\n<ul><li>[x] `services/validation-service/shacl_gate.py` validates every provider&#39;s pass fixture against the Linked Art SHACL shapes with pyld + pyshacl + rdflib (the same pipeline as the validation service); the JSON-LD expands to CIDOC-CRM and the `crm:E22` targetClass applies (not vacuous — a missing `identified_by`/`label` fails).</li><li>[x] `.github/workflows/shacl-conformance.yml` runs it as a path-filtered CI job (shapes/fixtures/service changes); `pnpm shacl:gate` runs it locally.</li><li><strong>Done:</strong> all 14 pass fixtures conform; a regression that breaks CRM expansion blocks the build. (Shapes are still minimal — richer SHACL coverage is follow-on work.)</li></ul>\n<h3 id=\"b3-systematic-per-provider-fixture-matrix-done-2026-06-24\">B3. Systematic per-provider fixture matrix ✅ done (2026-06-24)</h3>\n<ul><li>[x] All 13 production providers (+ generic `linked-art`) have pass <strong>and</strong> fail fixtures in `provider-fixture-manifest.json`, asserted in CI by `validation-architecture-depth.test.ts` (pass conforms, fail rejected) — this part pre-existed.</li><li>[x] `scripts/generate-conformance-matrix.ts` now <strong>generates</strong> the per-provider table in `conformance-matrix.md` from those fixtures + the capability registry, validating each fixture live with `inspectLinkedArtRecord`. `pnpm conformance:matrix` rewrites it; `conformance:matrix:check` + `conformance-matrix-generated.test.ts` gate drift in CI.</li><li><strong>Done:</strong> 13/13 providers pass both directions; the published matrix is generated, not hand-maintained. Full suite 1,116 green.</li></ul>\n<h3 id=\"b4-faceted-relevance-search-done-2026-06-24\">B4. Faceted / relevance search ✅ done (2026-06-24)</h3>\n<ul><li>[x] `src/services/search.ts` ranks matches by hit quality (exact label &gt; prefix &gt; substring &gt; name) and computes `type`/`provider` facet counts over the full match set; `/api/search` exposes `q`/`type`/`provider`/`limit`/`offset` and returns relevance order + facets in the `ld+json` `OrderedCollectionPage`. Tested (`tests/services/search.test.ts`, plus API-level facet/score/filter assertions).</li><li>[x] Conformance-matrix &quot;basic, not faceted&quot; gap closed; Solr 9 documented as the env-gated scale-out backend, this in-app impl as the portable default.</li><li><strong>Done:</strong> facets + ranking live + tested; matrix gap closed.</li></ul>\n<h3 id=\"b5-activity-streams-change-feed-conformance-done-2026-06-24\">B5. Activity Streams change-feed conformance ✅ done (2026-06-24)</h3>\n<ul><li>[x] Aligned `/api/activity` to Activity Streams 2.0: added the AS2 `@context`, AS2 `next`/`prev` page links (kept `nextPage`/`prevPage` aliases), a fuller `partOf` `OrderedCollection` with `first`/`last`/`totalItems`, and the `application/activity+json` media type. `Create`/`Update`/`Delete` items unchanged.</li><li><strong>Done:</strong> the feed validates against the AS2 spec shape (`tests/api/activity-as2.test.ts`); suite 1,129.</li></ul>\n<h3 id=\"b6-http-shoulds-done-2026-06-24\">B6. HTTP SHOULDs ✅ done (2026-06-24)</h3>\n<ul><li>[x] Canonical Linked Art entity/collection routes (`objects`, `artworks`, `works`, `agents`, `sets`, `concepts`, `entities`, `records`, `search`) export `HEAD` via a `bodilessResponse(await GET(...))` helper — same headers as GET, no body, mirrors 200/404; `OPTIONS` advertises `GET,HEAD,OPTIONS`. Covered by `tests/api/head-methods.test.ts`.</li><li>[x] HTTP/2 confirmed on the live deploy (`HTTP/2.0 200` via Vercel edge).</li><li><strong>Done:</strong> `HEAD` returns headers without a body; HTTP/2 verified live. Full suite 1,128.</li></ul>\n<p>---</p>\n<h2 id=\"recommended-sequence\">Recommended sequence</h2>\n<ol><li><strong>A1</strong> — green, badged CI (fast, unblocks a clean portfolio surface).</li></ol>\n<ol><li><strong>A2</strong> — vulns + Dependabot (quick, removes a real ding).</li></ol>\n<ol><li><strong>B1</strong> — rights as `Right` entities (the biggest Linked Art win).</li></ol>\n<ol><li><strong>A3 + B3</strong> — measurable rigor (coverage + the fixture matrix).</li></ol>\n<ol><li><strong>B2 + B4</strong> — the heavier conformance lifts (SHACL CI, faceted search).</li></ol>\n<ol><li><strong>A4 / A5 / A6 + B5 / B6</strong> — polish to close out both 10s.</li></ol>\n<p>Each step ships as its own PR with the acceptance criteria above met and the full suite green.</p>","updatedAt":"2018-10-20T01:46:40.000Z","checksum":"40a11000dc7e731aa18be35d337ea3d2d73639685245428e3aa05451390dad90","checksumPrefix":"40a11000dc7e","anchorCount":15,"lineCount":85,"rawUrl":"/api/docs/content?path=roadmap-to-10.md","htmlUrl":"/docs?doc=roadmap-to-10.md","apiUrl":"/api/docs/content?path=roadmap-to-10.md"}