{"title":"Meta Museum Roadmap","path":"docs/roadmap.md","generatedAt":"2026-08-09T09:04:37.701Z","sourceUpdatedAt":"2018-10-20T01:46:40.000Z","milestones":[{"label":"Slice 0","title":"Staging","complete":true,"status":"complete"},{"label":"Slice 1","title":"Foundations (TDD infra first)","complete":true,"status":"complete"},{"label":"Slice 2","title":"Met vertical (canary)","complete":true,"status":"complete"},{"label":"Slice 3","title":"Getty vertical","complete":true,"status":"complete"},{"label":"Slice 4","title":"Records + Artworks + Entities","complete":true,"status":"complete"},{"label":"Slice 5","title":"Linked Art Inspector + Roadmap + Best-Practices","complete":true,"status":"complete"},{"label":"Slice 6","title":"Patterns + Graph","complete":true,"status":"complete"},{"label":"Slice 7","title":"Issues + SSE","complete":true,"status":"complete"},{"label":"Slice 8","title":"Agents + Jobs + Content Generation + Automation","complete":true,"status":"complete"},{"label":"Slice 9","title":"Workspace chrome + design-system pass (Custom CSS)","complete":true,"status":"complete"},{"label":"Slice 10","title":"Lift cleanup","complete":true,"status":"complete"}],"sections":[{"level":2,"heading":"Status (as of August 6, 2026)","body":"<!-- BEGIN:PROJECT_STATS -->\n<!-- Generated by `pnpm docs:stats`; do not edit by hand. -->\n\n| Generated project stats | Current value |\n|---|---|\n| Next.js | `16.2.12` |\n| React | `19.2.4` |\n| App page files | root homepage + `41` non-root page files (`42` total) |\n| API route handlers | `149` `app/api` route handlers |\n<!-- END:PROJECT_STATS -->"},{"level":3,"heading":"Executive Assessment","body":"Meta Museum is a strong, unusually complete Linked Art product and a credible\ncontrolled-beta system. It is not yet defensible as institution-grade or\nrepeatable SaaS. The gap is mostly evidence quality, operational history, and\ncommercial proof rather than missing core product breadth.\n\n| Category | Score | Evidence-based assessment | Immediate implication |\n|---|---:|---|---|\n| Product experience | 6.5/10 for the public; 8.2/10 for museum and research users | The source-backed professional journeys are coherent, but public discovery still exposes workspace language, operational controls, and specialist navigation before establishing a reason to explore or return. | Separate the public museum from the professional workspace and build one curiosity-led discovery loop before adding more platform breadth. |\n| Linked Art and data semantics | 9.0/10 | Canonical JSON-LD, event-centric modeling, rights, provenance, equivalent identities, HAL relations, IIIF, and provider boundaries are deeply implemented and test-backed. | Sustain conformance; do not add another provider until readiness work clears. |\n| Evaluator and trust story | 8.4/10 | `/projects`, `/evidence`, `/datasets`, `/docs`, and machine-readable APIs make the work inspectable. The public evidence ledger currently mixes contradictory artifact baselines. | Repair proof consistency before adding more evaluator copy. |\n| Accessibility | 9.3/10 | `pnpm a11y:check` passed 18 routes with 0 violations on July 12. Auth redirects for protected routes were handled as expected. | Keep zero severe violations and add accessibility checks to any hero or navigation polish. |\n| Engineering architecture | 8.0/10 | Boundaries, contracts, strict TypeScript, storage abstractions, tests, and evidence automation are mature. The route and script surface is large and operationally expensive. | Prefer consolidation and owner clarity over new surface area. |\n| Reproducible local quality gate | 6.5/10 | Direct ESLint passed and the local review-goals gate passed, but the audit began with a stale closeout guard, direct TypeScript failed in generated `.next/dev/types/validator.ts`, and a direct full test run did not finish within 10 minutes. | Re-establish one clean, repeatable canonical gate before feature work. |\n| Documentation and governance | 8.5/10 | The docs surface is rich, agent-readable, and guarded for drift. The previous roadmap had become a completion ledger rather than a decision document. | Keep this file current and concise; send completed detail to history. |\n| Operational readiness | 6.0/10 | Production preflight is 20/20 and launch review is 7/8, but long-window SLO, uptime, KPI, and artifact-coherence proof remain red. | Clear deploy-environment proof, then let time-based evidence accumulate without manufacturing results. |\n| SaaS and business readiness | 5.5/10 | The managed pilot offer and tenant-aware technical foundation are credible. There is no invoice-backed pilot, repeatable onboarding, retention, or margin proof. | Sell and execute one bounded concierge pilot before building self-serve billing. |\n\nOverall decision: **8.1/10 as a local product and portfolio case study; 5.8/10\nfor strict public-production readiness.** The project is safe to keep demoing and\niterating. Broad institution-grade or profitable-SaaS claims remain blocked.\n\nThe July 21 live review confirmed that the primary public journeys render without\nbrowser console errors. The same review found that `pnpm typecheck:diagnostic`\nfailed in test contracts and fixtures. Those type errors are now repaired:\n`pnpm typecheck:diagnostic`, 59 focused tests, `pnpm lint`, and `pnpm build` pass.\nThe full serial `pnpm test` run completed `1,677/1,677` tests in `157.5s` on\nJuly 28 after stale deployment-preflight and documentation-contract expectations\nwere aligned with canonical evidence. `pnpm review:goals:check`\nnow reports `21` production-proof blockers (`0` local-refresh, `0`\ndeployment-environment, and `21` real-world-evidence). Completing the canonical\nlocal test run is no longer a P0 blocker.\nThe July 28 timeout investigation confirmed `1,704/1,704` tests pass in 154\nseconds with a compact reporter. The canonical test script now uses that\nreporter to prevent captured verbose output from stalling the command channel.\n\nAugust 5 quality-gate refresh: P0.2 is green again after the 24-hour closeout\nguard expired. Lint passed in `19.3s`, diagnostic TypeScript in `5.4s`, the full\nserial suite in `150.4s`, and the Next.js production build in `50s`. The repair\nkeeps intentionally untracked `data/source-repos` mirrors out of tracked-text\ndrift scans and anchors synthetic SLO samples to each evaluation timestamp so\nthe gate remains reproducible after the original June fixture window. The three\nnew standalone evaluator briefings are preserved in the same clean worktree.\nThe architecture evaluation briefing now marks the canonical-gate risk closed\nand carries these measured results instead of its earlier stale warning."},{"level":3,"heading":"Readiness Boundary","body":"- [ ] **Strict 10/10 readiness is not green yet**: `pnpm review:goals:check`\n  reports `status: external-evidence-required`, `local gate status: passed`, and\n  `strict 10/10 gate status: failed`.\n- [x] The July 10 production deployment preflight passes `20/20`; the latest\n  launch review passes `7/8`, with the launch-review Era C dependency still red.\n- [x] The latest strict handoff reports `0` local-refresh, `0` deployment-environment, and `21` real-world-evidence blockers, or `21` production-proof blockers in total.\n- [x] The evidence ledger, readiness dashboard, Era C, long-term, launch-review, and review-goals artifacts now share the same canonical blocker, SLO, uptime, and ActivityStreams values.\n- [ ] Remaining strict proof includes 30-day SLO/uptime evidence, production KPI exports, durable ActivityStreams syndication including a genuine `Delete`,\n  paid-pilot proof, retention, and gross-margin evidence.\n- [x] Current launch status is governed by `pnpm review:goals:check`.\n- [x] `pnpm review:goals:check` must be green before broad public SaaS claims.\n\nThe allowed claim is: **local gates pass and strong external proof exists; strict\n10/10 production readiness remains blocked by external evidence.**"},{"level":3,"heading":"Evaluation Findings That Change Priority","body":"1. **Public evidence consistency is repaired and regression-guarded.** The\n   canonical handoff, README, roadmap, and live ledger now agree on `21 = 0 + 3\n   + 18` strict blockers. The ledger also reconciles partner confirmations with\n   generated ActivityStreams evidence, so its pending Delete row cannot repeat\n   confirmed `Create` or `Update` types as missing.\n2. **The local gate is not yet reproducible from one clean command sequence.**\n   The closeout guard, a malformed generated Next dev validator, and a long-running\n   direct test invocation obscure whether a fresh checkout is truly green.\n3. **The product breadth is sufficient.** Fourteen provider lanes, 34 pages, 146\n   API routes, public docs, validation, reconciliation, ActivityStreams, IIIF,\n   agent review, and tenant-aware pilot controls are enough for the next learning\n   cycle. More breadth would dilute the evidence and customer work.\n4. **The next product-polish gain is quality, not more sections.** The current\n   hero can enlarge a low-resolution source image, while Core Web Vitals and\n   primary-journey performance do not yet have a concise public baseline.\n5. **The public product needs a distinct reason to return.** Cross-provider search\n   is useful, but it does not yet turn the underlying data advantage into stories,\n   surprising discoveries, personal collections, or connections that no single\n   museum site can show.\n\n---"},{"level":2,"heading":"Priority Plan","body":"Priority is determined by trust and dependency, not by implementation novelty.\nDo not start a lower tier while an actionable higher-tier exit condition is red.\nTime-bound external evidence may continue accumulating in parallel."},{"level":3,"heading":"P0 - Restore A Trustworthy Baseline (Now, 0-7 Days)","body":"| ID | Outcome | Owner | Exit criteria | Verification |\n|---|---|---|---|---|\n| P0.1 | Keep public readiness evidence internally consistent. | Platform + Evidence | `/api/evidence/ledger`, `/evidence`, `/readiness`, review-goals, launch review, and the long-term artifacts agree on SLO sample/day counts, uptime, ActivityStreams observed/missing types, and current blocker scope. Fallbacks disclose missing artifacts instead of substituting incompatible values. | `tests/services/readiness-evidence-consistency.test.ts`; focused evidence-ledger, readiness, review-goals, and documentation-drift tests; `pnpm evidence:ledger:probe:check`. |\n| P0.2 | Restore one clean local quality gate. | Platform | From a fresh generated state, `pnpm session:closeout:check`, `pnpm lint`, `pnpm test`, `pnpm build`, and `pnpm typecheck:diagnostic` all complete successfully. The generated `app/api/vanda/search/route.js` validator fragment is valid after regeneration, and test duration is recorded. | Run the five canonical commands and attach elapsed time plus the first failing test if any. |\n| P0.3 | Clear deployment-environment proof drift. | Operations | Rerun production preflight, launch evidence, launch review, and public Era C evidence with production environment present; reduce the deployment-environment lane from `4` blockers to `0` without changing the real-world claim boundary. | `pnpm launch:preflight:production`; `pnpm launch:evidence:production`; `pnpm launch:review:production`; `pnpm era-c:exit-gate:public`; `pnpm review:goals:check`. |\n| P0.4 | Preserve nightly k6 evidence artifacts. | Platform + Evidence | The Docker fallback writes `artifacts/performance/k6-slo-summary.json` as the host runner user, deletes stale summaries before each run, and fails when no fresh summary is produced. | `pnpm exec tsx --test tests/scripts/k6-slo-runner.test.ts`; confirm the next Era C workflow ingests one fresh SLO sample. |\n\nProduction database SSL drift was repaired on July 27: Vercel now uses the\nexisting `sslmode=verify-full` connection value, the production artifact was\nredeployed, and `/api/ai/query` returned `200` through the public alias.\n\nP0 exit gate: all local commands are reproducibly green, public evidence has no\ncross-surface contradictions, and deployment-environment blockers are zero."},{"level":3,"heading":"P1 - Convert Reliability And Demand Into Proof (Next, 1-6 Weeks)","body":"| ID | Outcome | Owner | Exit criteria | Verification |\n|---|---|---|---|---|\n| P1.1 | Complete the 30-day reliability window. | Operations | One canonical source reports 30 distinct UTC days of complete passing deployed SLO samples, including the cold-record scenario, and at least 99.9% public-read uptime with failed rows aged out of the retained window. | Scheduled probes plus `pnpm longterm:evidence:public` and `pnpm era-c:exit-gate:public`. |\n| P1.2 | Produce real SOTA KPI evidence. | Data + Curation | Production/Postgres or warehouse exports meet the reconciliation auto-approve and reviewed-precision thresholds; every capture row identifies its production source. | `pnpm monitoring:kpi-evidence:production`; `pnpm era-c:exit-gate:public`. |\n| P1.3 | Close one invoice-backed managed pilot. | Founder + Product | One real buyer has a signed scope and invoice reference, one collection is activated within seven days, and support, required KPI, retention, and gross-margin rows are captured without placeholders. | `pnpm pilot:buyer-pack`; `pnpm pilot:activation`; `pnpm pilot:support`; `pnpm pilot:kpi`; `pnpm pilot:evidence --check`. |\n\nThe architecture evaluator now participates in the commercial pre-revenue claim control: it must disclose the absent invoice-backed pilot, remain `External evidence required`, and point to `pnpm pilot:buyer-pack` plus the tenant-scoped `pnpm pilot:evidence --check` acceptance gate. This improves the handoff but does not count outreach or local tooling as revenue proof.\n\nAugust 5 ownership and operator-experience pass: the compact public mobile header and product-specific agent sign-in context are implemented and regression-tested. The operations-risk report now covers `35/35` page routes across `7` ownership/consolidation lanes alongside `61/61` API families and fully classified package-script namespaces. `pnpm ops:profile` makes the Next.js + Postgres portable baseline explicit and keeps Python services, AG2, Solr, GraphDB, and publication workers optional until their readiness gates justify enablement. The evaluator marks mobile and authentication closed while keeping broad surface area and specialist topology honestly managed rather than eliminated.\n| P1.4 | Preserve honest ActivityStreams adoption. | Platform + Partnerships | Keep `3/3` real external consumers, `3/3` verified durable callbacks, and zero rejected subscriptions fresh. Add `Delete` only after a genuine upstream `404`/`410` tombstone and partner read; never synthesize it to satisfy the gate. | `pnpm providers:coverage:seed`; `pnpm activity:tombstone:scan`; `pnpm activity:subscriptions:guard`; `pnpm activity:syndication:evidence`. |\n| P1.5 | Raise visible product quality and establish a performance baseline. | Product + Frontend | Home, Explore, one artwork detail, Projects, and Pilot pass mobile/desktop visual review; no hero image is rendered above a defensible intrinsic size; no text overlaps; LCP <= 2.5 s, CLS <= 0.1, and INP <= 200 ms on the agreed production profile. | Refresh public-trust screenshots, run a production performance audit, rerun `pnpm a11y:check`, and retain the metrics artifact. |\n\nP1.5 performance checkpoint (July 28): all ten production cold-load traces pass\nthe agreed lab budgets. Explore mobile is the limiting LCP at `2,286 ms`, Pilot\ndesktop is the largest CLS at `0.0665`, and the representative interaction trace\nis `28 ms`. The route matrix and profile are retained in\n[`docs/ops/frontend-performance-baseline.md`](ops/frontend-performance-baseline.md).\nP1.5 remains open pending the mobile/desktop visual review and fresh axe run.\n\nStep 6 remediation (July 28): the fresh axe run passes `18/18`. The two Home\nblockers are fixed locally: the source band is now a normal full-width sibling\nof the padded content container, and V&A IIIF services are promoted to a\n1,200 px image derivative before thumbnail fallbacks. The repeated local\nproduction-build matrix passes all ten mobile/desktop traces with 0.00 CLS, a\nmaximum 1,628 ms LCP, and 29 ms INP; the 18-route axe gate also remains green.\nRepeat this matrix against the deployed revision to close P1.5.\n\nLinked Art 1.1 watch checkpoint (July 28): the standalone\n[`linked-art-1-1-agenda-impact-tracker.html`](../public/linked-art-1-1-agenda-impact-tracker.html)\nmaps all 26 August 5 agenda issues to current support, expected impact, required\nfixtures or schema changes, and pending community decisions. Update its decision\ncolumn and the canonical Linked Art reference ledger after the meeting before\nchanging validators or production mappings. Issue #637 now has an internal,\nprovenance-bearing\nconfirmed-negative reconciliation contract. It keeps curator-confirmed\nnon-matches separate from unresolved candidates and withholds Linked Art\nprojection until the community settles the property name and assertion pattern.\nIssue #362 now has a provisional internal response-profile contract. Its\nserver-defined brief projection preserves canonical identity, marks itself\nincomplete, and links deterministically to the full record; no new public\nprofile parameter is enabled before the community decision.\nThe pre-meeting implementation evidence packet now combines issues #362, #637,\nand #780 with executable references and decision questions. Use it during the\nAugust 5 discussion, then replace its pending questions with resolution links\nbefore promoting any candidate behavior.\nThe machine-readable meeting decision ledger covers all 26 agenda issues.\nAgenda proposals are recorded separately from outcomes; resolved rows require a\nmatching Linked Art issue URL, target release, and explicit local action before\nthey can drive post-meeting changes.\n\nThe Step 3 conformance pass now covers nine focused patterns under\n`tests/fixtures/linked-art-1.1/`: qualified `AttributeAssignment` ambiguity,\ninscribed `Name` evidence, `Name.created_by`, prototype-level provenance for an\nunenumerated `Set`, member-side Addition and Removal, Person Joining and\nLeaving, and auction selling/purchase separation.\nEndpoint-family inspection now preserves the active terms-ontology inverse links\n`added_member_by` and `removed_member_by`. The lifecycle fixture declares its\nextension context explicitly and stays non-normative until the 1.1 meeting\ndecision is recorded.\n\nThe compatibility pass is now executable through\n`LINKED_ART_1_1_COMPATIBILITY_BOUNDARY` and\n`tests/quality/linked-art-1-1-compatibility-boundary.test.ts`. The audit keeps\nsix representative pending property placements rejected, leaves proposed and\ndeferred classes outside the endpoint map, and documents the post-meeting\npromotion procedure in\n[`docs/linked-art/1.1-compatibility-audit.md`](linked-art/1.1-compatibility-audit.md).\n\nP1 exit gate: 30-day reliability and production KPI rows pass, one real paid\npilot reaches first value, and strict ActivityStreams evidence is either complete\nor explicitly waiting on a genuine upstream tombstone with all other rows fresh.\n\nJuly 27 checkpoint: the three accepted durable callback rows are restored and\n`pnpm activity:subscriptions:guard` passes `3/3`. Syndication remains honestly\nblocked only on a real `Delete` activity read.\n\nThe nightly Actions environment now supplies all three production consumer IDs\nto `activity:adoption:matrix`. Its production verification passed `12/12` feed\nprobes and resolved `3/3` declared consumers; `Delete` remains the sole missing\nobserved activity type.\n\nA write-enabled July 27 tombstone scan checked 68 canonical upstream targets\nacross 14 provider lanes with zero errors and found no genuine `404`/`410`.\nAccordingly, no `Delete` was minted; the scheduled scan must continue until a\nreal upstream removal can be observed and read by the three consumers.\n\nThe nightly workflow now runs the durable callback guard exactly once through\n`activity:syndication:evidence`; the redundant standalone guard step was removed\nwithout weakening its failure behavior.\n\nCollection and readiness now have separate Actions semantics. The nightly\nevidence workflow succeeds when probes and artifact generation work even if the\nrecorded status is red. The following `Era C Readiness Gate` workflow reports\nthose known external-evidence blockers without producing a failed scheduled job;\na manual dispatch remains fail-fast and owns strict Era C thresholds, durable\ncallback enforcement, and production launch review.\nThe Actions matrix now uses `actions/setup-node@v7`; execution-policy tests own\nthat major consistently, and runtime file metadata no longer depends on an\noverload-derived Node type that can become optional in newer type packages."},{"level":3,"heading":"Public Discovery Product Track (Next, staged behind the P0 gate)","body":"Goal: turn Meta Museum's cross-provider data advantage into a welcoming public\nmuseum built around curiosity, storytelling, and repeat visits. The professional\nworkspace remains available, but it must no longer dominate the anonymous public\njourney. The defining promise is **connections no single museum website can\nshow**.\n\nThis track does not authorize a new provider, runtime dependency, or autonomous\npublishing path. Each phase ships behind the existing rights, provenance,\naccessibility, performance, citation, and human-review controls. Do not advance\nwhen the preceding phase's measured exit condition is red.\n\n| Phase | Outcome | Initial scope | Exit criteria | Verification |\n|---|---|---|---|---|\n| PD0 | Establish the public baseline and content boundary. | Record first-time task completion, artwork-to-artwork continuation, return visits, public-domain downloads, and share events. Define the minimum quality bar for public records: usable image, intelligible title/date, source, attribution, and resolved reuse message. | Baseline artifact exists; public and professional audiences, routes, vocabulary, and analytics events are explicitly separated; records below the quality bar cannot enter featured feeds. | Public-route inventory, analytics event contract, quality-filter fixtures, privacy review, and five non-specialist usability sessions. |\n| PD1 | Separate the public museum from the professional workspace. | Public navigation becomes Home, Explore, Stories, Connections, My Collection, and About. Evidence, APIs, agents, imports, annotations, org status, and operational controls move behind one clearly labeled “For museums and researchers” entry point. Rewrite the homepage in plain language around art and discovery. | A first-time visitor can explain the product and reach an artwork without encountering workspace status or specialist implementation language; professional routes remain directly reachable and unchanged in capability. | Mobile and desktop visual review, keyboard pass, `pnpm a11y:check`, public-navigation tests, and moderated five-second comprehension checks. |\n| PD2 | Ship the minimum delightful discovery loop. | Add `Surprise me`, a rights-safe Artwork of the Day with a stable dated URL, and public artwork pages led by image, essential facts, “Why this is interesting,” related works, and previous/next discovery. Collapse technical metadata and researcher feedback below the public story. | The complete loop works: entry point → artwork → short story → related discovery → another artwork. Featured records never have broken media or ambiguous reuse messaging. | Deterministic selection tests, record-quality tests, mobile/desktop E2E, share-preview checks, and measured artwork-to-artwork continuation. |\n| PD3 | Launch Connections as the signature feature. | Start with three evidence-backed types: the same subject across cultures, works made in the same period on different continents, and recurring symbols or materials across institutions. Label documented relationships separately from algorithmic suggestions and expose sources plus confidence. | At least three curated connection journeys span two or more providers, contain no unsupported causal claims, and pass human editorial review. Visitors can move from a work to a connection and into another institution's work. | Connection contract tests, citation completeness, confidence-label checks, curator review checklist, and journey E2E. |\n| PD4 | Add source-backed Stories and guided exploration. | Publish short image-led stories using reusable formats such as “One artwork, three details,” “Same year, different worlds,” “A disputed identity,” and “How an object changed hands.” Add exploration by subject, place, century, and color; add mood only as clearly labeled interpretation. Hide empty maps and timelines. | A minimum viable editorial cadence is sustainable; every factual claim resolves to a source; guided filters return useful results; empty analytical surfaces do not appear publicly. | Story-schema and citation tests, filter-quality samples, editorial review log, structured-data/share-card validation, and completion-rate measurement. |\n| PD5 | Make trustworthy reuse and participation useful. | Add public-domain image download with source, rights, attribution, and metadata. Add “What changed?” with plain-language record version comparisons. Allow local-first saved collections, then optional account sync and read-only sharing after demand is observed. | Downloads package correct rights context; record changes identify source, time, and change origin; a visitor can save and share a coherent collection without being forced to sign in first. | Rights/download fixtures, version-diff tests, local-storage and account-migration tests, privacy review, and collection share E2E. |\n| PD6 | Prove retention before expanding. | Evaluate artwork continuation, Surprise Me use, story completion, connection opens, downloads, collections created/shared, and 7-day return visits. Improve the strongest loop; retire or revise weak entry points. | Two consecutive measurement windows show a credible repeat-use signal and no regression in accessibility, performance, rights, or citation quality. Any further personalization or recommendation work has a measured hypothesis. | Analytics review, usability replay, public performance/a11y matrix, editorial quality audit, and a written continue/change/stop decision. |\n\nRecommended first release: **PD0 + PD1 + PD2 + three PD3 journeys**. It must\ndemonstrate one complete public loop:\n\n> Interesting entry point → beautiful artwork → understandable source-backed\n> story → unexpected cross-museum connection → another discovery → save or share.\n\nPublic discovery stop conditions: pause expansion if featured-record quality\ncannot be guaranteed, if connection evidence cannot support the displayed claim,\nif rights context is separated from a download, if public pages regress the\nagreed accessibility or performance budgets, or if measured use shows no\nimprovement after two iterations.\n\nPD1 implementation checkpoint (August 6): the primary public navigation is now\nHome, Explore, Stories, Connections, My Collection, About, and one quiet “For\nmuseums and researchers” entry. Anonymous Explore and artwork journeys no longer\nrender organization status or professional workspace chrome, and public Explore\nsuppresses import prompts, roadmap language, and provider implementation notes.\nThe homepage now leads with cross-museum discovery in plain language; dedicated\nStories, Connections, My Collection, and professional-workspace landing pages\nmake every navigation destination intentional. Automated navigation, homepage,\nworkspace-boundary, and Explore acceptance tests are green. The local production\nbuild passes, the 18-route accessibility matrix reports zero severe violations,\nand a 375 px browser review finds no horizontal overflow. A fresh deployed visual\nreview and non-specialist comprehension sessions remain PD1 evidence tasks rather\nthan reasons to reopen its implementation scope.\n\nPD2 implementation checkpoint (August 6): `/surprise` selects from the same\nimage-backed, publication-eligible local artwork pool as the homepage and sends\nvisitors directly into a public artwork journey. `/today` resolves to a stable\nUTC-dated `/today/YYYY-MM-DD` page whose selection is deterministic for that date.\nThe homepage exposes both entry points. Anonymous artwork pages now lead with\n“Why this is interesting,” keep facts and source detail in an expandable section,\noffer previous, next, Surprise Me, and related-artwork paths, and withhold\nresearcher annotations and operational relationship tools. Signed-in researchers\nretain the complete professional view. Selection and surface acceptance tests are\ngreen. The production build passes, the 18-route accessibility matrix reports\nzero severe violations, Surprise Me resolves into an eligible local artwork, and\nhomepage, daily, and artwork routes show no horizontal overflow at 375 px. A\ndeployed review remains necessary before promoting this local checkpoint to\nproduction proof.\n\nAugust 7 deployed checkpoint: PD1/PD2 is live on the production alias. The full\nserial test suite, lint, diagnostic typecheck, local and Vercel builds,\nproduction 18-route axe audit, 66-check crawler preview, 20/20 deployment\npreflight, public Explore smoke, repeated public-trust screenshot baseline, and\nzero-advisory dependency audit pass. Ten retained Lighthouse captures report\n100 accessibility and 0 CLS; the desktop routes pass the LCP budget, while the\nstricter Lighthouse mobile profile reports 3.7-5.9 second LCP and keeps P1.5\nopen for remediation and an agreed-profile recapture. The refreshed adoption\nmatrix passes 12/12 operator-run endpoint probes for all three named consumer\nIDs and remains blocked on genuine `Delete`. Those probes do not substitute for\nfresh reads made by the external consumers themselves: the retained declared\nconsumer reads are outside the 30-day adoption window, so Era C correctly\nreports `0/3`. Production preflight is 20/20 with zero deployment-environment\nfailures; the remaining launch-review blockers are time-bound SLO samples and\nreal-world adoption/KPI evidence.\n\nThe concrete production preflight is zero-failure on deployment\n`dpl_2WH2w1hrM4zftM84kn3ouU3xu4N4`. The broader `review:goals:local` roll-up now\nalso reports zero deployment-environment blockers: aggregate launch-review and\nEra C wrappers inherit real-world-evidence scope, while concrete preflight,\nauth, smoke, IIIF, and k6 failures remain deployment-scoped when present. The\nremaining 21 strict blockers are explicitly time-bound or human/external\nevidence rather than deployment configuration failures.\n\nAugust 7 PD0/PD3 checkpoint: the five public outcome events now have a typed,\nconsent-gated contract that excludes direct identifiers and professional\nroutes. First artwork completion, artwork continuation, 24-hour return,\nrights-qualified download selection, and successful share are instrumented.\nA dated baseline artifact and five-session non-specialist\nprotocol are present, while production observation and the five human sessions\nremain open. PD3 has exactly one curated journey—Flowers across two centuries—\nspanning Getty and Met records with citations, a high-confidence metadata\nlabel, an explicit no-influence boundary, contract tests, and an editorial\ndecision packet awaiting attributable human sign-off. Do not expand to three until this first\njourney is deployed and the measurement/editorial evidence is reviewed.\nAugust 8 cultural-intelligence checkpoint: the first journey now produces three\nsynchronized representations from one typed contract: a public visual story, a\nresearch dossier exposing fact/inference labels, method, uncertainties, rejected\nhypotheses, novelty status, and rights boundary, plus an evaluation-only JSON\nrecord with the same claim/citation graph and human-review state. Consent-gated\nstory-completion and reuse-interest signals are instrumented outside the five PD0\noutcomes. Institutional usefulness, agent/editorial minutes, independent novelty\nverification, five usability sessions, and attributable editorial approval remain\nexternal evidence gates; the one-journey expansion stop is unchanged.\nThe expansion gate is now executable through `pnpm connections:evidence`: its\nprivacy-safe artifact requires observed completion plus sharing/reuse, qualified\neditorial sign-off, institutional usefulness, agent/editorial labor and cost,\nindependent novelty review, a real price response, and the valid synchronized\nmachine record. It reports tested gross value before labor separately from labor\nminutes and cannot call that result profit. No real evidence has been imported,\nso expansion remains unauthorized.\nInternal hidden-pattern work can now proceed without violating that public gate:\n`pnpm connections:patterns` emits a review-only collection-intelligence report\nfrom normalized records plus explicit source rows. Deterministic candidates cover\nequivalent-record conflicts, possible entity reconciliation, shared materials,\nowner/custodian or set references, structured-provenance coverage gaps, and\ngeographic contrasts. Every lead carries citations, confidence, and a refusal\nboundary; uncited records are rejected and unsupported demographic or market\nconclusions are listed as refused analyses. No second public journey was added.\nThe review-only report now also consumes explicit Linked Art event evidence:\nmatching exhibition identifiers, `used_specific_object` groupings, structured\nacquisition/transfer parts, dated event places, and shared activity actors. Its\nevent graph preserves source-record IDs on every edge. Geographic sequences are\nmovement candidates rather than transport claims; ownership histories do not\nassert completeness, authenticity, custody, or legal title.\nAdditional explicit-evidence candidates now cover alternative maker assignments,\nreversed event timespans, repeated technique identifiers, separate `represents`\nand `about` iconographic concepts, and unidentified depicted people. Boundaries\nprevent authorship resolution, invented corrected dates, workshop/influence\nclaims, collapsed depiction semantics, or demographic/underrepresentation\ninference from these candidates.\nThe machine layer now also exposes `/api/cultural-intelligence` as a lifecycle-\naware collection feed. Each item preserves revision, production provenance,\nreview history, corrections, and separate editorial/licensing decisions. The\nfeed currently reports one evaluation item and zero licensable items; approval\nmetadata must be complete and all corrections resolved before eligibility can\nchange. Underlying source-record and media rights remain explicitly separate.\nFive derivative formats now compile from the same versioned claim graph:\nnewsletter, daily feed, narrated visual essay, classroom package, and licensed\narticle. Evidence sections preserve claim/citation IDs and all formats repeat the\nrights boundary. The derivative endpoint returns only an HTTP 409 release\nmanifest—not internal copy—while the first record lacks editorial and licensing\napproval. Format availability is therefore implemented but audience demand,\nquality, labor, accessibility, and price remain unproven external evidence.\nValue-based pricing now has an executable evidence ladder through\n`pnpm connections:pricing`: hypothesis, tested-no-signal, market signal, one\ninvoice-validated delivery, and repeatable price evidence. Repeatability requires\nthree scoped offers and two paid, accepted, value-confirmed, positive-contribution\ndeliveries across buyer segments. Labor is fully costed at an attributable rate;\ninterest is never revenue. No real offer artifact exists yet, so pricing remains\nunvalidated.\nThe `pd0:evidence` intake command now validates a real GA4 export and moderated\nsession records, rejects direct identifiers or invented counts, and derives\ncompletion only from five sessions plus attributable product-owner approval.\nIts package namespace, script, artifact directory, and product-governance owner\nare registered in the executable evidence-ownership and operations-risk controls.\nResponsive browser review found the initial action block below the full image on\nmobile; it now precedes the image and remains overflow-free at 390 px and 1440 px.\nThe Vercel ignore contract excludes local provider source mirrors, the `.tools`\nbinary cache, and the upstream `linked.art` checkout except its required schema\nsubtree. However, deployment `dpl_GiNFmNpo2UZs4uGEm4Y3B54Bya3b` still archived\n79,077 files (669.1 MB), so CLI archive filtering remains an open packaging\noptimization; the deploy itself completed and passed its runtime build."},{"level":3,"heading":"P2 - Productize Only After The Pilot Loop Works (Later, 6-12 Weeks)","body":"| ID | Outcome | Trigger | Exit criteria |\n|---|---|---|---|\n| P2.1 | Guided organization onboarding and first-value dashboard. | One invoice-backed pilot completes activation and its friction is documented. | A new managed org can be provisioned with a sample or customer dataset in under 15 minutes; progress and first value are visible without engineering inspection. |\n| P2.2 | Repeatable subscriptions and usage visibility. | Pricing, support load, and gross margin are validated on at least one pilot. | Checkout or invoice-backed subscription sync, webhook/audit evidence, quotas, usage, billing state, cancellation reason, and customer portal are supportable. |\n| P2.3 | Institution procurement package. | A buyer starts security/legal review. | Deployment-specific subprocessors, DPA/legal artifacts, access review, incident drill, retention controls, backup/restore proof, status reporting, and SLA/SLO packet are buyer-reviewable. |\n| P2.4 | Production agent bridge decision. | A named operator accepts the review workload and risk boundary. | AG2 bridge has explicit sign-off, eval evidence, rollback, auditability, and human-publication approval. A2A/AG-UI remain deferred. |\n\n---"},{"level":2,"heading":"Readiness Scorecards","body":""},{"level":3,"heading":"Launch Readiness","body":"| Lane | Score | Decision | Next evidence |\n|---|---:|---|---|\n| Internal development and portfolio demo | 9.0/10 | Safe to use and present with the strict-readiness caveat. | Reproduce the canonical local gate and resolve the public evidence inconsistencies. |\n| Controlled public beta | 8.4/10 | Technically credible on Vercel + Neon, but the formal beta gate remains evidence-red. | Clear P0, then maintain narrow acceptance criteria while the 30-day window accumulates. |\n| General public production | 6.5/10 | Do not claim complete readiness. | Passing long-window SLO/uptime, production KPI, and coherent launch evidence. |\n| Institution-grade / strict 10/10 | 5.5-6.0/10 | Blocked by external and time-based proof. | `pnpm review:goals:check` passes with no production-proof blockers. |"},{"level":3,"heading":"SaaS Readiness","body":"| Lane | Score | Decision | Next evidence |\n|---|---:|---|---|\n| Technical SaaS foundation | 7.0/10 | Strong enough for concierge pilots. | Prove onboarding, support load, usage, and tenant operations with one buyer. |\n| Paid pilot readiness | 8.2/10 | The offer and operator path are ready; revenue proof is not. | Reply or qualified follow-up, signed scope, invoice-backed entitlement, real activation. |\n| Self-serve SaaS readiness | 3.0/10 | Deferred. | Start only after the pilot validates pricing and activation friction. |\n| Profitable SaaS business | 4/10 | Credible wedge, but repeatable revenue is not proven yet. | Retention, support minutes, infrastructure cost, conversion, and gross-margin evidence. |\n\nThe primary wedge remains the **Managed Linked Art Launch Pilot** for small and\nmid-size museums, archives, galleries, digital-humanities labs, and artist estates\nthat need standards-compliant collection publication without a semantic-web team.\nManual invoicing is correct for the first 1-3 pilots. Creator-side provenance and\nself-serve billing stay deferred until the B2B pilot loop produces evidence.\n\n---"},{"level":2,"heading":"Evidence Workstreams","body":"| Workstream | Current state | Completion condition |\n|---|---|---|\n| Local quality | Review-goals local status passes and direct ESLint passes; full canonical reproducibility was not demonstrated in the July 12 audit. | P0.2 is green from a clean generated state. |\n| Deployment | Preflight `20/20`, both Render probes, all public smokes, and deployed k6 pass; launch review is `7/8`. The strict handoff has zero deployment-environment blockers because its remaining launch and Era C wrappers depend only on real-world evidence. | Preserve the green concrete deployment matrix while the real-world evidence windows mature. |\n| Long-window SLO and uptime | The latest strict handoff reports `11` retained deployed SLO samples across `10/30` distinct UTC days, with `11` passing samples and no failed or incomplete rows in the active report window; the Era C artifact still reports only `15/30` samples toward its exit gate. | Continue distinct-day collection until the complete 30-day threshold is genuinely met. |\n| ActivityStreams | Operator endpoint probes pass for three named IDs, but the retained real external consumer evidence is stale and therefore counts as `0/3`; genuine `Delete` evidence is pending. | Collect three fresh real external consumers covering `Create`, `Update`, and `Delete`, with verified callbacks. |\n| Production KPI | Local enrichment is promising; production reconciliation distribution and reviewed precision are incomplete. | P1.2 passes the SOTA KPI acceptance rows from named production sources. |\n| Managed pilot | The offer, runbook, entitlement, activation, support, and evidence tooling exist. The latest no-pricing buyer pack is specific to the recorded Te Papa outreach and has a real account, organization, and owner, but no paid-pilot tenant or invoice-backed entitlement exists. | Obtain a real buyer reply plus signed scope or invoice reference, provision the tenant, then use P1.3 tooling to record activation, retention, support load, and margin evidence. |\n\nThe buyer-review surface now includes a standalone ten-record demonstration at\n`/museum-linked-art-pilot-demonstration.html`. It uses traceable public API records\nto show source preservation, event-centric Linked Art JSON-LD, rights review\nboundaries, validation findings, and museum questions. It proves a review pattern,\nnot a completed customer engagement or permission to reuse source images.\n\nA one-page buyer brief at `/managed-linked-art-pilot-brief.html` now packages the\nproblem, five-day process, required inputs, deliverables, privacy and security\nboundaries, and post-pilot decision into a printable pre-call handout. It links to\nthe ten-record demonstration and preserves the same evaluation-only claim boundary.\n\nThe first-call workflow now has a timed guide at\n`/museum-pilot-discovery-call-guide.html`: a one-minute permission-based opening,\nfive fit questions, a boundary recap, three explicit decision paths, and a\nfollow-up record. The call qualifies a bounded pilot before any product tour and\nlinks directly to the buyer brief and demonstration when supporting proof is useful.\n\nThe post-call handoff now has a public-data request at\n`/museum-pilot-data-request-template.html`. It supplies a copy-ready museum message,\naccepts CSV, JSON, XML, LIDO, or a public API, distinguishes minimum from optional\nfields, excludes credentials and restricted material, and records the reviewer,\npublication boundary, transfer method, receipt evidence, and agreed deletion date.\n\nThe conversion and delivery packet now adds a counsel-review sample agreement,\nfour-level introductory pricing, and a reusable results report. The public pilot\noffer uses the same `$0` evaluation, `$3,500` fixed paid pilot, implementation from\n`$12,000`, and ongoing service from `$1,250` monthly hypothesis, so buyer surfaces\nno longer conflict. These remain unvalidated prices until invoice-backed delivery,\nacceptance, retention, and gross-margin evidence exists.\n\nThe refined demonstration presents one primary path on desktop and mobile:\ncollection record, Linked Art mapping, validation, then reviewable result. Source\nevidence is collapsed beneath the interaction, and the final state names open\nmuseum decisions and the human publication gate.\n\nThe current outreach ledger records the eight user-confirmed August 5 submissions,\ntheir real recipient or form channel, zero assumed replies, and August 12 follow-up\ndates. `docs/sales/museum-outreach-pipeline.md` contains eight unsent follow-up\ndrafts plus a second official-source-researched group of eight prospects that must\nremain `research_only` until first-round feedback is reviewed and sending is\nauthorized. `docs/ops/paid-pilot-commercial-evidence-process.md` closes the\ninvoice-to-margin capture design without treating placeholders as proof."},{"level":3,"heading":"Standing Evidence Controls","body":"- **Public docs metadata freshness:** `/api/docs/manifest` must keep response\n  `generatedAt` separate from source `sourceUpdatedAt` and `sourceUpdatedDoc`\n  checksum metadata.\n- **ActivityStreams onboarding ledger:** partner rows must keep\n  `wikidataexplorer-metamuseum-prod` and the other real consumers distinct,\n  retain durable callback evidence, and preserve the zero rejected subscriptions\n  state without allowing placeholders to satisfy strict proof.\n- **Performance evidence:** the cold-record budget, 30-day SLO depth, and\n  `pnpm longterm:evidence:public` output remain strict gates; a frontend Core Web\n  Vitals baseline is added in P1.5 rather than inferred from API SLO evidence.\n- **Claim boundary:** local success, deployment success, and real-world success\n  remain separate scopes. No aggregate badge may silently promote one scope into\n  another.\n\n---"},{"level":2,"heading":"Product And Engineering Guardrails","body":"1. Linked Art JSON-LD remains canonical; UI DTOs are projections at boundaries.\n2. Preserve rights, source attribution, provenance, multi-value arrays, event\n   semantics, carrier/content/surrogate separation, and opaque URI handling.\n3. Adapters do not import each other; provider parsing stays in adapters;\n   cross-provider mapping stays in `src/utils/artwork-builder.ts`; contracts remain\n   leaf modules.\n4. AIDD + TDD remains mandatory for behavior changes. Standards-critical work\n   cites reference rounds and fixture anchors before implementation.\n5. Public publication and agent-generated claims require citations, refusal paths,\n   audit evidence, and human approval.\n6. Keep Next.js, React, TypeScript, custom CSS, Postgres/JSONB, Solr, GraphDB, and\n   canonical ID decisions locked as documented in [CLAUDE.md](../CLAUDE.md).\n7. No new runtime dependency, provider, service, database, or architecture era is\n   started while P0 is red without explicit approval."},{"level":3,"heading":"Deliberately Deferred","body":"- New provider integrations beyond the current 14 production lanes.\n- Self-serve signup, checkout, billing portal, and growth automation before pilot\n  economics and activation are real.\n- Synthetic ActivityStreams `Delete` evidence.\n- Broad production agent autonomy or public publishing without operator sign-off.\n- A microservice, triple-store, vector-store, or framework expansion not justified\n  by measured scale or customer evidence.\n\n---"},{"level":2,"heading":"Cadence And Ownership","body":"| Cadence | Required action |\n|---|---|\n| Every behavior change | Red-green-refactor tests, focused smoke/evidence, README + roadmap update, and `pnpm session:closeout`. |\n| Every 72 hours during active shipping | Canonical local gate plus fresh P0 evidence checks. Stop expansion when a required gate is missing. |\n| Weekly | Refresh long-window evidence, inspect failed-sample age-out dates, review pilot pipeline, and update only changed roadmap decisions. |\n| Monthly or before a buyer review | Refresh production preflight, launch evidence/review, procurement packet, access/DR evidence, and the strict handoff. |\n\nThe roadmap records current decisions and measurable outcomes, not every merged\nchange. Completed implementation detail belongs in\n[progress/era-history.md](progress/era-history.md), specialized docs, generated\nartifacts, and git history.\n\n---"},{"level":2,"heading":"History And References","body":"- [progress/era-history.md](progress/era-history.md): full Era A, B, and C slice\n  history, including the milestones consumed by `/api/roadmap`.\n- [roadmap-to-10.md](roadmap-to-10.md): executable strict-readiness checklist and\n  artifact handoff.\n- [risk-register.md](risk-register.md): open engineering and operating risks.\n- [ops/review-goals.md](ops/review-goals.md): review-goals policy and command\n  contract.\n- [ops/evidence-script-ownership.md](ops/evidence-script-ownership.md): evidence\n  command ownership and preferred entry points.\n- [linked-art/LinkedArtModel1.0-Reference.md](linked-art/LinkedArtModel1.0-Reference.md):\n  standards rounds and fixture anchors.\n- [linked-art/LinkedArtSOTAWebApp.md](linked-art/LinkedArtSOTAWebApp.md): target\n  architecture and SOTA acceptance criteria.\n\n\n# Meta Museum — Era Delivery History\n\nArchived detailed record of the completed delivery eras (Lift / Hardening / SOTA), moved out of the active [roadmap](../roadmap.md) to keep it current. This is the slice-by-slice and B-/C-series implementation log; the roadmap holds the live status and the forward plan ([roadmap-to-10.md](../roadmap-to-10.md)).\n\n---"},{"level":2,"heading":"Three eras","body":"Scope honestly. The SOTA spec is a 20-week, multi-service, multi-store target. We get there in three eras with hard exit gates between them.\n\n| Era | Theme | Outcome | Tier |\n|---|---|---|---|\n| **A. Lift** | Move what exists into Next.js 16 + custom CSS | Public site + research workspace at parity with legacy prototype, on a modern stack | Slices 1-10 |\n| **B. Hardening** | Make the data layer trustworthy | Zod-mirrored contracts, SHACL validation, ValidationReport, Postgres swap, real auth | Quarters 2-3 post-lift |\n| **C. SOTA** | The full Yale-LUX-pattern platform | Multi-modal store, HAL hypermedia, Visual ETL Mapper, NL→SPARQL, Graph-RAG, IIIF deep-zoom, Meta Wiki Art bridge | Quarter 4+ |\n\n**Cardinal rule:** do not start a later era until the prior era's exit gate is green. Don't ship a SHACL validator on top of a JSON-file store, and don't ship Graph-RAG on top of unvalidated records.\n\n---"},{"level":2,"heading":"Era A — The Lift (10 slices, PR-sized each)","body":"Goal: end the era with the legacy prototype's full feature set running natively on Next.js 16, custom CSS, App Router + RSC, with the dependency rules from `_legacy/AGENTS.md` preserved (adapters don't cross-import; contracts are leaves; `_source.raw` is immutable)."},{"level":3,"heading":"Slice 0 — Staging (DONE)","body":"See §Status above."},{"level":3,"heading":"Slice 1 — Foundations (TDD infra first) (DONE)","body":"Port the dep-free leaves so everything else can be built on them. **Test infra lands before any port.**\n\nOrder within the slice:\n- [x] ✅ **Test infra**: `tsx` dev dependency + `\"test\": \"node --import tsx --test tests/**/*.test.ts\"` + smoke test (`tests/smoke.test.ts`) landed; `pnpm test` green.\n- [x] ✅ **Port test-first.** Legacy tests were ported and implementations landed for the specified dep-free leaves.\n   - [x] ✅ `src/constants.ts` (port of `_legacy/src/constants.js`)\n   - [x] ✅ `src/contracts/*.ts` — all 8 (`artwork`, `source-record`, `rights-report`, `import-job`, `agent-task`, `citation`, `shared-structures`, `wiki-draft`)\n   - [x] ✅ `src/utils/{text,rights,http,storage,linked-art}.ts` (leaf utils)\n- [x] ✅ No routes, no UI changes in this foundation slice scope.\n\n**Acceptance**: `pnpm test` green with full coverage of legacy `tests/contracts/*` and `tests/utils/{linked-art,validation-report}.test.ts`. `pnpm build` green. No new client-side bundle weight. Every implementation file has at least one corresponding test file."},{"level":3,"heading":"Slice 2 — Met vertical (canary) (DONE)","body":"Prove the route-handler pattern end-to-end with the simpler of the two adapters.\n\n- [x] ✅ `src/adapters/{adapter-utils,provider-interface,met}.ts` + tests\n- [x] ✅ Route handlers (all `app/api/.../route.ts`): `/health`, `/met/profile`, `/met/departments`, `/met/search` (POST), `/met/object` (POST), `/met/import` (POST)\n- [x] ✅ `app/explore/page.tsx` — minimal custom-CSS UI calling `/api/met/search`, showing image cards\n- [x] ✅ `app/layout.tsx` — `Create Next App` metadata replaced; shell landed and evolved in later slices\n\n**Acceptance**: User can search Met for \"flowers\", click a result, see object detail JSON. Loading/empty/error states present. No Linked Art shortcuts taken — Met objects normalize through the `Artwork` contract before display."},{"level":3,"heading":"Slice 3 — Getty vertical (DONE)","body":"Same shape as Slice 2 for Getty (more endpoints).\n\n- [x] ✅ `src/adapters/getty.ts` + tests\n- [x] ✅ Routes: `/getty/profile`, `/getty/entity` (POST), `/getty/import` (POST), `/getty/activity` (GET), `/getty/sparql` (POST)\n- [x] ✅ `app/explore/page.tsx` extended with provider toggle (Getty / Met / both; later expanded further)\n- [x] ✅ `app/getty/page.tsx` — SPARQL playground + ActivityStream peek\n\n**Acceptance**: Both providers reachable from `/explore`. Getty SPARQL playground returns rows. Rights and source attribution rendered on every card."},{"level":3,"heading":"Slice 4 — Records + Artworks + Entities (DONE)","body":"The persistence-touching slice. Storage stays JSON files in `storage/`.\n\n- [x] ✅ `src/utils/{artwork-builder,artwork-facets,entities,relationships}.ts` + tests\n- [x] ✅ Routes: `/records` (GET/POST), `/records/[id]` (GET), `/artworks/[id]` (GET), `/entities` (GET), `/entities/[id]` (GET), `/explorer/artworks` (GET), `/explorer/import` (POST)\n- [x] ✅ Pages: `app/records/page.tsx`, `app/artwork/[id]/page.tsx`, `app/entity/[id]/page.tsx`\n- [x] ✅ Async-`params` patterns applied per Next 16 requirements.\n\n**Acceptance**: Import a Met or Getty record from `/explore`; it appears on `/records`; clicking it opens `/artwork/[id]` with maker, date, materials, rights, citation, and a list of pivotable entities. Each entity link opens `/entity/[id]`."},{"level":3,"heading":"Slice 5 — Linked Art Inspector + Roadmap + Best-Practices (DONE)","body":"Port the JSON-LD inspect/import workflow plus the two reflective endpoints.\n\n- [x] ✅ `src/utils/best-practices-audit.ts` + tests\n- [x] ✅ Routes: `/linked-art/profile`, `/linked-art/inspect` (POST), `/linked-art/import` (POST), `/roadmap`, `/best-practices`\n- [x] ✅ Pages: `app/linked-art/page.tsx`, `app/roadmap/page.tsx`\n- [x] ✅ `/api/roadmap` returns this document as structured JSON; `/api/best-practices` preserves legacy audit semantics.\n\nStarted in this pass:\n- [x] ✅ `src/utils/best-practices-audit.ts` + tests.\n- [x] ✅ `/api/roadmap` route returning structured JSON.\n- [x] ✅ `/api/best-practices` route running the audit against stored records.\n- [x] ✅ `app/roadmap/page.tsx` initial UI shell.\n- [x] ✅ `/linked-art/profile`, `/linked-art/inspect`, `/linked-art/import` routes.\n- [x] ✅ `app/linked-art/page.tsx` full inspect/import workflow UI.\n\n**Acceptance**: Paste a Linked Art JSON-LD blob → see the inspection report. The roadmap page renders this file's phases and exit gates."},{"level":3,"heading":"Slice 6 — Patterns + Graph (DONE)","body":"Port pattern discovery and the graph view.\n\n- [x] ✅ `src/utils/patterns.ts` + tests\n- [x] ✅ Routes: `/patterns` (POST), `/graph` (GET)\n- [x] ✅ Pages: `app/patterns/page.tsx`, `app/graph/page.tsx` (Cytoscape force-directed — first external runtime dep; `cytoscape` + `cytoscape-cose-bilkent` per SOTA §3.2)\n\n**Acceptance**: Pattern scan produces buckets for unknown makers, missing dates, shared concepts. Graph page renders nodes (artworks + entities) with click-to-pivot.\n\nStarted in this pass:\n- [x] ✅ `src/utils/patterns.ts` + tests.\n- [x] ✅ `/api/patterns` route returning unknown + shared buckets.\n- [x] ✅ `/api/graph` route returning artwork/entity nodes + edges with pivot hrefs.\n- [x] ✅ `app/patterns/page.tsx` pattern scan UI.\n- [x] ✅ `app/graph/page.tsx` + `src/components/graph-viewer.tsx` Cytoscape force-directed graph UI.\n- [x] ✅ Runtime deps: `cytoscape`, `cytoscape-cose-bilkent`."},{"level":3,"heading":"Slice 7 — Issues + SSE (DONE)","body":"The streaming case.\n\n- [x] ✅ Route: `/issues` (GET), `/issues/webhook` (POST), `/issues/stream` (GET — `ReadableStream`, `export const dynamic = 'force-dynamic'`)\n- [x] ✅ Page: `app/issues/page.tsx` with the live-updating issue inventory (re-uses `_legacy/storage/linked-art-issues.json` as a fallback cache, refreshes from GitHub on a `revalidate` cadence)\n\n**Acceptance**: Issues view loads from cache instantly, hot-updates from SSE without a refresh, and respects the same `GITHUB_OWNER`/`GITHUB_REPO`/`ISSUE_POLL_MS` env vars as the legacy server.\n\nStarted in this pass:\n- [x] ✅ `src/services/issues.ts` service with legacy-compatible env vars, live GitHub fetch, local runtime cache, and `_legacy/storage/linked-art-issues.json` fallback.\n- [x] ✅ `/api/issues` route with optional `?refresh=1` force refresh.\n- [x] ✅ `/api/issues/webhook` route with optional signature verification and issue-event refresh hooks.\n- [x] ✅ `/api/issues/stream` route using `ReadableStream` SSE (`dynamic = \"force-dynamic\"`).\n- [x] ✅ `/issues/webhook` + `/issues/stream` direct route aliases for legacy-compatible pathing.\n- [x] ✅ `app/issues/page.tsx` + `src/components/issues-workbench.tsx` live issue inventory UI with SSE updates.\n- [x] ✅ New tests for service + routes: `tests/services/issues.test.ts`, `tests/api/issues.test.ts`, `tests/api/issues-webhook.test.ts`, `tests/api/issues-stream.test.ts`."},{"level":3,"heading":"Slice 8 — Agents + Jobs + Content Generation + Automation (DONE)","body":"Port the agent/job stubs as-is. No new agent logic this slice — just parity.\n\n- [x] ✅ Routes: `/agents/run` (POST), `/content/generate` (POST), `/jobs` (GET), `/jobs/run` (POST)\n- [x] ✅ Pages: `app/agents/page.tsx`, `app/automation/page.tsx`\n\n**Acceptance**: Manual pattern scan + collection brief jobs runnable from the UI; output appears in `app/automation/page.tsx`.\n\nStarted in this pass:\n- [x] ✅ `src/services/agents.ts` with legacy-parity agent/content stubs (`runAgent`, `generateContent`) and local fallback drafting.\n- [x] ✅ `src/services/jobs.ts` JSON-backed manual jobs service with seeded defaults and `lastRun` updates.\n- [x] ✅ `/api/agents/run`, `/api/content/generate`, `/api/jobs`, `/api/jobs/run` routes.\n- [x] ✅ Legacy-compatible direct route aliases: `/agents/run`, `/content/generate`, `/jobs`, `/jobs/run`.\n- [x] ✅ `app/agents/page.tsx` + `src/components/agents-workbench.tsx` for manual agent and content runs.\n- [x] ✅ `app/automation/page.tsx` + `src/components/automation-workbench.tsx` for job execution and output review.\n- [x] ✅ Route tests: `tests/api/agents-run.test.ts`, `tests/api/content-generate.test.ts`, `tests/api/jobs.test.ts`, `tests/api/jobs-run.test.ts`."},{"level":3,"heading":"Slice 9 — Workspace chrome + design-system pass (Custom CSS) (DONE)","body":"Now everything works route-by-route. Unify the visual layer.\n\n- [x] ✅ `app/(workspace)/layout.tsx` route group — sidebar nav + topbar, all custom CSS\n- [x] ✅ Expand `app/globals.css` design tokens + component classes to cover every recurring pattern; keep BEM-lite naming consistent\n- [x] ✅ Implement the design-system atomics from SOTA §12.1 in `src/components/` with a `data-la-entity-id` attribute on every entity-derived element: `<LinkedDate>`, `<LinkedDimensions>`, `<LinkedLabel>`, `<UriBadge>`, `<RightsBadge>` (already in Slice 2), `<SourceBadge>`, `<CitationBlock>`\n- [x] ✅ Entity cards (SOTA §12.2): `<ObjectCard>`, `<ActorCard>`, `<PlaceCard>`, `<ConceptCard>`\n- [x] ✅ a11y pass: axe-core in CI; keyboard nav on all interactive surfaces; WCAG 2.1 AA on public pages\n\n**Acceptance**: Lighthouse a11y ≥ 95 on `/`, `/explore`, `/artwork/[id]`. Storybook scaffold (vite-based, no Next coupling) for the atomic components.\n\nStarted in this pass:\n- [x] ✅ `app/(workspace)/layout.tsx` route-group shell with keyboard-first skip link, sidebar navigation, and topbar.\n- [x] ✅ Workspace pages moved under `app/(workspace)/...` so URLs stay unchanged while sharing common chrome.\n- [x] ✅ Expanded `app/globals.css` with workspace shell classes and reusable design-system component classes.\n- [x] ✅ Added atomics in `src/components/`: `LinkedDate`, `LinkedDimensions`, `LinkedLabel`, `UriBadge`, `SourceBadge`, `CitationBlock`; extended `RightsBadge` with `data-la-entity-id`.\n- [x] ✅ Added entity cards in `src/components/`: `ObjectCard`, `ActorCard`, `PlaceCard`, `ConceptCard`.\n- [x] ✅ Wired new components into `/explore`, `/artwork/[id]`, `/entity/[id]`, and `/records`.\n- [x] ✅ Added component tests: `tests/components/linked-atomics.test.ts`, `tests/components/entity-cards.test.ts`.\n- [x] ✅ Added CI workflow at `.github/workflows/ci.yml` running lint, tests, build, Playwright install, axe accessibility checks, and Lighthouse CI assertions.\n- [x] ✅ Added Vite-based Storybook scaffold (`.storybook/*`) and atomic stories (`src/components/atomics.stories.tsx`), validated with `pnpm storybook:build`."},{"level":3,"heading":"Slice 10 — Lift cleanup (DONE)","body":"- [x] ✅ Delete `_legacy/` (empty by now or only contains files we deliberately chose not to port)\n- [x] ✅ Rewrite `README.md` from the Next.js side; preserve the legacy product narrative\n- [x] ✅ Confirm `metamuseum-legacy/` can be archived/deleted (verified absent at `C:\\Projects\\metamuseum-legacy`).\n- [x] ✅ Security credential rotation moved to Era B operational preflight tracking (see `Pre-Era-C Operational Sign-Off` under Era B).\n- [x] ✅ Document the env-var surface (`PORT`, `GITHUB_OWNER`, `GITHUB_REPO`, `ISSUE_POLL_MS`, future `DATABASE_URL`) in `docs/env.md`\n\nStarted in this pass:\n- [x] ✅ Added automated parity gate test: `tests/quality/era-a-exit-gate.test.ts` (legacy route/view equivalents + route-test coverage checks).\n- [x] ✅ Lighthouse a11y gate now runs reliably in local Windows env via `scripts/lighthouse-a11y.mjs` (no `chrome-launcher` temp-dir cleanup failure path).\n- [x] ✅ `_legacy/` removed from workspace.\n- [x] ✅ Verified `C:\\Projects\\metamuseum-legacy` is absent as of **May 30, 2026** (already archived/deleted outside this repo).\n\n**Era A exit gate (must all be green):**\n- [x] ✅ Legacy API routes have Next 16 equivalents, tested (`tests/quality/era-a-exit-gate.test.ts`; current legacy snapshot evaluates to 33 route handlers).\n- [x] ✅ Legacy SPA views have Next 16 page equivalents (`tests/quality/era-a-exit-gate.test.ts`; current legacy snapshot evaluates to 13 named views).\n- [x] ✅ `pnpm build && pnpm test && pnpm lint` clean (validated in `metamuseum` conda env on May 30, 2026).\n- [x] ✅ Lighthouse a11y ≥ 95 on the public pages (`/`, `/explore`, `/artwork/[id]`) via `pnpm lighthouse:ci`.\n- [x] ✅ `_legacy/` deleted.\n- [x] ✅ `metamuseum-legacy/` archived/deleted (path not present at `C:\\Projects\\metamuseum-legacy` on May 30, 2026).\n\n---"},{"level":2,"heading":"Era B — Hardening (quarters, not weeks)","body":"Goal: make the data layer trustworthy enough that AI agents and external consumers can rely on it. The Era A app keeps shipping during this era; we add validation and durable storage *under* it.\n\nSlices in suggested order, but each is independently shippable:"},{"level":3,"heading":"B1 — Zod contracts + schema versioning","body":"- [x] ✅ Mirror every `src/contracts/*.ts` as a Zod schema in `src/contracts/zod/*.ts`\n- [x] ✅ Add `schemaVersion` field + a `src/utils/migrations/` registry\n- [x] ✅ Server Actions and Route Handlers validate at the boundary with the Zod schemas\n\nStatus:\n- [x] ✅ Completed."},{"level":3,"heading":"B2 — Formal validation","body":"- [x] ✅ Build a Python validation microservice (FastAPI + PySHACL + PyLD) — first non-Node service\n- [x] ✅ SHACL shapes in `shapes/linked-art/*.shacl.ttl`, fixtures in `fixtures/linked-art/{pass,fail}/`\n- [x] ✅ New route `app/api/validate/route.ts` proxies to it\n- [x] ✅ New contract `ValidationReport` (SOTA §5.1) wired into inspect/import flows\n- [x] ✅ Validation fixtures and route assertions are checked against [linked-art/LinkedArtModel1.0-Reference.md](linked-art/LinkedArtModel1.0-Reference.md) before merge.\n\nStatus:\n- [x] ✅ Completed."},{"level":3,"heading":"B3 — Postgres migration","body":"- [x] ✅ Postgres 16 via Docker Compose for dev (`ops/docker-compose.yml`)\n- [x] ✅ Migrate `storage/{records,jobs}.json` → Postgres JSONB tables via a one-time exporter that double-writes for one release, then cuts over\n- [x] ✅ Replace `src/utils/storage.ts` JSON-file impl with a Postgres impl behind the same interface; no call sites change\n- [x] ✅ Add `next-env.d.ts` env validation with Zod for `DATABASE_URL`\n\nStatus:\n- [x] ✅ Completed.\n- [x] ✅ Added `ops/docker-compose.yml` + `ops/postgres/init/01-storage.sql` (Postgres 16 dev runtime + table bootstrap).\n- [x] ✅ Added `scripts/export-storage-to-postgres.ts` one-time exporter and `pnpm storage:export:postgres`.\n- [x] ✅ `src/utils/storage.ts` now supports `file`, `double-write`, and `postgres` modes for the centralized managed-document contract behind unchanged `readJson/writeJson`.\n- [x] ✅ Added Zod-backed runtime env parsing for `DATABASE_URL` + storage mode in `src/utils/env.ts` and typed env keys in `next-env.d.ts`.\n- [x] ✅ Updated `records` and `jobs` services to avoid file-`stat` assumptions so Postgres cutover does not require call-site changes."},{"level":3,"heading":"B4 — Auth + roles","body":"- [x] ✅ Verify and document production credential rotation for `AUTH_SECRET` + `AUTH_GITHUB_SECRET` (operational sign-off completed; see [`docs/ops/auth-credential-rotation.md`](docs/ops/auth-credential-rotation.md) and `Pre-Era-C Operational Sign-Off`).\n- [x] ✅ Add Auth.js v5 with GitHub provider for write paths (`/api/records` POST, `/api/explorer/import`, `/api/getty/import`, `/api/met/import`, `/api/linked-art/import`, `/api/jobs/run`)\n- [x] ✅ Roles: `public` (read only), `researcher` (read + import), `editor` (import + agent jobs), `admin` (all)\n- [x] ✅ Middleware gates write routes; UI conditionally renders import/run buttons\n\nStatus:\n- [x] ✅ Completed.\n- [x] ✅ Added Auth.js v5 (`next-auth@5.0.0-beta.31`) with GitHub provider in root `auth.ts`.\n- [x] ✅ Added `/api/auth/[...nextauth]` handlers.\n- [x] ✅ Added centralized role mapping in `src/auth/roles.ts` (public/researcher/editor/admin with allowlist env vars).\n- [x] ✅ Added Next 16 `proxy.ts` route gates for write paths (`/api/records` POST, `/api/explorer/import`, `/api/getty/import`, `/api/met/import`, `/api/linked-art/import`, `/api/jobs/run`) plus editor-only agent endpoints.\n- [x] ✅ UI now conditionally enables import/run controls in linked-art, agents, and automation workbenches based on resolved role.\n- [x] ✅ Rotate production GitHub OAuth credentials if any legacy values are still active (operational follow-up reminder remains until verified)."},{"level":3,"heading":"B5 — Provider expansion","body":"- [x] ✅ New adapters for Harvard, Smithsonian Open Access, Rijks, RKD Knowledge Graph, National Gallery of Art Open Data, Louvre Collections JSON, V&A Collections API, Princeton University Art Museum API, Europeana, AIC, CMA (SOTA §27.1) — each shipped with route + adapter + tests.\n- [x] ✅ Each adapter implements the `provider-interface` contract; cross-adapter imports remain forbidden.\n- [x] ✅ `/explore` import flow now accepts all landed provider source IDs (`met`, `getty`, `rijks`, `nga`, `louvre`, `harvard`, `smithsonian`, `vanda`, `princeton`, `europeana`, `aic`, `cma`).\n- [x] ✅ Provider slices include executable conformance tests mapped to [linked-art/LinkedArtModel1.0-Reference.md](linked-art/LinkedArtModel1.0-Reference.md) fixture anchors.\n\n**Acceptance for each upcoming provider slice (object-specific AIDD + TDD gates):**\n- [x] ✅ Failing-first contract tests verify culturally valued physical objects normalize as `HumanMadeObject` unless explicit evidence requires another canonical class.\n- [x] ✅ Failing-first tests verify production/destruction remain structured activity/event nodes when present (not flattened to display-only strings).\n- [x] ✅ Failing-first tests verify physical characteristics (dimensions/materials/parts) are preserved as structured data when available.\n- [x] ✅ Failing-first tests verify ownership/location assertions remain distinct from non-ownership rights/reuse assertions.\n- [x] ✅ Failing-first tests verify physical object identity remains distinct from digital surrogates/representations while preserving linkage.\n- [x] ✅ Failing-first regression tests verify immovable/place-centric records are not coerced into moveable-object assumptions.\n- [x] ✅ Route-level tests verify inspect/import outputs preserve the above structures without mutating canonical source fields.\n\n**Acceptance for each upcoming provider slice (digital-content AIDD + TDD gates):**\n- [x] ✅ Failing-first contract tests verify `DigitalObject` records preserve `access_point`, `format`, and `conforms_to` when provided.\n- [x] ✅ Failing-first tests verify digital object creation events use `Creation` semantics where present, without coercion to physical-object production semantics.\n- [x] ✅ Failing-first tests verify content/carrier separation is preserved (`DigitalObject` versus `VisualItem`/`LinguisticObject`) without collapsing layers.\n- [x] ✅ Failing-first tests verify surrogate linkage can preserve shared visual content (`shows` and `digitally_shows`) when provider data supports it.\n- [x] ✅ Failing-first tests verify web-page and document references preserve the `subject_of` → `LinguisticObject` → `digitally_carried_by` pattern when present.\n- [x] ✅ Failing-first tests verify IIIF structures are preserved, including Presentation manifest `conforms_to`/`format` and Image API `DigitalService` via `digitally_available_via`.\n- [x] ✅ Route-level tests verify inspect/import outputs retain digital metadata structures (including IIIF fields) and do not mutate canonical `_source.raw`.\n- [x] ✅ Provider-slice test PRs must include or update fixture-backed tests mapped to `Round 3 Addendum — Digital Content` → `Fixture Anchors — Digital Content Examples` in [linked-art/LinkedArtModel1.0-Reference.md](linked-art/LinkedArtModel1.0-Reference.md) (web publication, surrogate parity, embedded representation image, subject_of web page, IIIF Presentation manifest, IIIF Image service).\n- [x] ✅ Provider-slice test PRs must also include a short \"Standards Mapping\" note listing the specific round addenda used (for example endpoint schema rounds, shared-structure rounds, and relevant search-relation rounds) and the fixture anchors exercised.\n\nStatus:\n- [x] ✅ Complete (Rijks, NGA, RKD, Louvre, Harvard, Smithsonian, V&A, Princeton, Europeana, AIC, CMA slices landed).\n- [x] ✅ Object-specific acceptance gates are executable and passing in `tests/quality/provider-object-specific-gates.test.ts`.\n- [x] ✅ Digital-content acceptance gates are executable and passing in `tests/quality/provider-digital-content-gates.test.ts`.\n- [x] ✅ Route-level digital inspect/import preservation is executable and passing in `tests/quality/provider-digital-content-gates.test.ts`.\n- [x] ✅ Provider manifest enforcement now requires active import providers to include `Round 3 Addendum - Digital Content` in `tests/fixtures/validation/provider-fixture-manifest.json` (`tests/quality/validation-architecture-depth.test.ts`).\n- [x] ✅ PR template now requires explicit provider digital-content fixture-anchor mapping + short Standards Mapping notes (`.github/pull_request_template.md`).\n- [x] ✅ Added `src/adapters/rijks.ts` with Search + Resolver + LDES + Change Discovery helpers and IIIF URL normalization.\n- [x] ✅ Added Rijks API routes: `/api/rijks/profile`, `/api/rijks/search`, `/api/rijks/resolve`, `/api/rijks/import`, `/api/rijks/ldes`, `/api/rijks/cd`.\n- [x] ✅ `/explore` source toggle now supports `both` / `met` / `getty` / `rijks`; `/api/explorer/import` supports Rijks URLs.\n- [x] ✅ Added test coverage for adapter + routes + provider inference (`tests/adapters/rijks.test.ts`, `tests/api/rijks/*`, updated explorer/provider tests).\n- [x] ✅ Added `src/adapters/nga.ts` plus routes `/api/nga/profile`, `/api/nga/search`, `/api/nga/import` with failing-first tests and explore/import wiring.\n- [x] ✅ Remaining provider slices are now landed: RKD Knowledge Graph, Louvre Collections JSON, Harvard, Smithsonian Open Access, V&A Collections API, Princeton University Art Museum API, Europeana, AIC, CMA.\n- [x] ✅ Rijks incremental-ingest hooks are executable: `extractRijksLdesHookData()` and `extractRijksChangeDiscoveryHookData()` with route coverage in `tests/api/rijks/ldes.test.ts` and `tests/api/rijks/cd.test.ts`.\n- [x] ✅ Rijks profile now exposes a bibliographic SRU extension-point base (`bibliographicSruBase`) and SRU URL builder coverage (`buildRijksSruSearchUrl()`), while UI flows remain unchanged.\n\nRijksmuseum integration scope now includes:\n- [x] ✅ Object metadata search and dereference pipeline (Search API + PID Resolver with content negotiation to Linked Art).\n- [x] ✅ Linked Data Event Streams ingest hooks for incremental refreshes.\n- [x] ✅ IIIF Change Discovery ingest hooks for change tracking.\n- [x] ✅ IIIF image/presentation compatibility via Micrio endpoints.\n- [x] ✅ Future bibliographic extension point via SRU (planned, not yet wired into UI flows)."},{"level":4,"heading":"B5.1 — RKD Knowledge Graph provider slice (done)","body":"Goal: integrate RKD Linked Data (CIDOC-CRM + Linked Art oriented) as a standards-first provider without bypassing current adapter boundaries.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/rkd.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/rkd/profile` (GET)\n  - [x] ✅ `/api/rkd/search` (POST, paged candidate retrieval)\n  - [x] ✅ `/api/rkd/entity` (POST, URI-based fetch/enrichment)\n  - [x] ✅ `/api/rkd/import` (POST, normalize + persist)\n  - [x] ✅ optional `/api/rkd/sparql` (POST, read-only, allowlisted query templates only)\n- [x] ✅ UI:\n  - [x] ✅ added `rkd` source toggle support in `/explore`\n  - [x] ✅ provider attribution + ODC-By 1.0 license/reuse guidance rendered in RKD card/detail data.\n\nData-source constraints:\n- [x] ✅ Dataset scale is 600M+ statements and is consumed via bounded queries/pagination (`limit`/`offset` clamps and bounded search defaults).\n- [x] ✅ SPARQL endpoint details are deploy-time config (env vars) via `getRkdProfile()`/`buildRkdSparqlEndpoint()`.\n- [x] ✅ Graph scoping is supported via optional `graph` input on search/entity/template flows.\n- [x] ✅ Raw SPARQL usage is constrained to controlled, allowlisted query templates (`entitySummary`/`labelSearch`) for route-level access.\n- [x] ✅ Triply protocol-compatible SPARQL request behavior is implemented with explicit `Accept` negotiation and read-only request handling.\n\nLicense and attribution:\n- [x] ✅ RKD dataset license is Open Data Commons Attribution License 1.0; provider output carries source URL + provider attribution + license metadata.\n- [x] ✅ Rights/reuse output remains conservative when image-level rights are not explicit in source payload.\n\nAcceptance:\n- [x] ✅ Failing-first tests for adapter + routes are landed (`tests/adapters/rkd.test.ts`, `tests/api/rkd/*.test.ts`).\n- [x] ✅ Standards mapping coverage includes object/digital/shared-structure/data-discovery anchors in `tests/fixtures/validation/provider-fixture-manifest.json` (`rkd` entry).\n- [x] ✅ B8 protocol conformance coverage includes RKD routes in `tests/quality/provider-protocol-conformance.test.ts`.\n- [x] ✅ Token security checks included (`Authorization: Bearer` from env-only `RKD_TRIPLY_TOKEN`, no token persistence/logging in adapter/route flows)."},{"level":4,"heading":"B5.2 — Smithsonian Open Access provider slice (done)","body":"Goal: integrate Smithsonian Open Access search/content APIs with secure API-key handling and standards-first normalization.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/smithsonian.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/smithsonian/profile` (GET)\n  - [x] ✅ `/api/smithsonian/search` (POST)\n  - [x] ✅ `/api/smithsonian/content` (POST)\n  - [x] ✅ `/api/smithsonian/import` (POST)\n- [x] ✅ UI:\n  - [x] ✅ added `smithsonian` source toggle in `/explore`\n  - [x] ✅ source attribution and conservative rights/reuse indicators are preserved in Smithsonian discovery/import card flows.\n\nOfficial API constraints:\n- [x] ✅ API key required (`api_key`) via data.gov registration (`SMITHSONIAN_API_KEY` enforced for Smithsonian search/content and URL-import retrieval).\n- [x] ✅ Search pagination uses `start` + `rows`; route schema enforces integer bounds (`start >= 0`, `rows` in `1..1000`) with compatibility aliases for legacy callers.\n- [x] ✅ Core category filters and row-group inputs are explicit enum validation at route boundary (`smithsonianSearchInputSchema` for category + `rowGroup: objects|archives`).\n\nAcceptance:\n- [x] ✅ Failing-first adapter + route tests with mocked Smithsonian responses.\n- [x] ✅ API-key security checks included (env-only secrets, no key in logs/errors/client, API key stripped from exposed source URLs).\n- [x] ✅ Standards Mapping note coverage includes fixture anchors for Smithsonian in `tests/fixtures/validation/provider-fixture-manifest.json`.\n- [x] ✅ B8 protocol checks include Smithsonian routes (`profile`, `search`, `content`, `import`) in `tests/quality/provider-protocol-conformance.test.ts`."},{"level":4,"heading":"B5.3 — Harvard Art Museums provider slice","body":"Goal: integrate Harvard Art Museums API with strong conformance to official usage constraints and Linked Art normalization boundaries.\n\nStatus:\n- [x] ✅ Planned deliverables in this slice scope (adapter + routes) are complete.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/harvard.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/harvard/profile` (GET)\n  - [x] ✅ `/api/harvard/search` (POST)\n  - [x] ✅ `/api/harvard/object` (POST)\n  - [x] ✅ `/api/harvard/import` (POST)\n- [x] ✅ UI:\n  - [x] ✅ add `harvard` source toggle in `/explore`\n  - [x] ✅ preserve attribution/link-back + rights/reuse indicators on all surfaces.\n\nOfficial API constraints:\n- [x] ✅ API key required on all calls (`apikey` parameter).\n- [x] ✅ Paging uses `size` (max 100) + `page`; adapter honors `info.next/info.prev` flows.\n- [x] ✅ Respect call budget guidance (2500/day) and non-commercial + attribution terms.\n- [x] ✅ Cache/storage policy enforces a two-week max retention guidance (`<=14 days`) without explicit permission.\n- [x] ✅ Use provider image URLs directly (no local copies).\n\nAcceptance:\n- [x] ✅ Failing-first adapter + route tests with mocked Harvard responses.\n- [x] ✅ API key handling checks included (env-only key, no key in logs/errors/client surfaces).\n- [x] ✅ Rate-budget and cache-TTL policy checks included (`<=14 days` cache window).\n- [x] ✅ Standards Mapping note references applicable Linked Art rounds and fixture anchors.\n- [x] ✅ B8 protocol checks included for all Harvard routes."},{"level":4,"heading":"B5.4 — V&A Collections API provider slice","body":"Goal: integrate V&A Collections API v2 with strong support for identifier/keyword filters and IIIF image/presentation link preservation.\n\nStatus:\n- [x] ✅ Complete.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/vanda.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/vanda/profile` (GET)\n  - [x] ✅ `/api/vanda/search` (POST)\n  - [x] ✅ `/api/vanda/object` (POST)\n  - [x] ✅ `/api/vanda/import` (POST)\n- [x] ✅ UI:\n  - [x] ✅ add `vanda` source toggle in `/explore`\n  - [x] ✅ expose IIIF manifest/image links in artwork/detail surfaces where available.\n\nOfficial API constraints:\n- [x] ✅ API base is `https://api.vam.ac.uk/v2`.\n- [x] ✅ Search result pages should honor official paging constraints (`size` cap 100).\n- [x] ✅ API is suitable for dynamic subsets; bulk export flows should avoid naive high-volume API crawling.\n- [x] ✅ Terms/licensing constraints and citation requirements must be preserved in downstream usage.\n\nAcceptance:\n- [x] ✅ Failing-first adapter + route tests with mocked V&A responses.\n- [x] ✅ Identifier/keyword filter behavior tests included.\n- [x] ✅ IIIF image/presentation field extraction tests included.\n- [x] ✅ Standards Mapping note references applicable Linked Art rounds and fixture anchors.\n  - [x] ✅ Standards Mapping note: Linked Art Model 1.0 references include Digital Content + Shared Structures + Data Discovery/API endpoint-shape rounds; fixture anchors include `tests/fixtures/validation/providers/vanda/pass.json` and `tests/fixtures/validation/providers/vanda/fail.json` plus provider manifest mapping in `tests/fixtures/validation/provider-fixture-manifest.json`.\n- [x] ✅ B8 protocol checks included for all V&A routes."},{"level":4,"heading":"B5.5 — Princeton University Art Museum provider slice","body":"Goal: integrate Princeton API object/search resources with strong preservation of nested research context and IIIF media references.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/princeton.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/princeton/profile` (GET)\n  - [x] ✅ `/api/princeton/search` (POST)\n  - [x] ✅ `/api/princeton/object` (POST)\n  - [x] ✅ `/api/princeton/import` (POST)\n- [x] ✅ UI:\n  - [x] ✅ add `princeton` source toggle in `/explore`\n  - [x] ✅ preserve source attribution and media link visibility.\n\nOfficial API constraints:\n- [x] ✅ Base endpoint `https://data.artmuseum.princeton.edu`.\n- [x] ✅ No auth currently required, and adapter/profile surface explicit `authMode: none` + future-auth compatibility without interface breakage.\n- [x] ✅ Static weekly full datasets are reflected in import guidance with anti-crawl guardrails on large interactive API imports.\n\nAcceptance:\n- [x] ✅ Failing-first adapter + route tests with mocked Princeton responses.\n- [x] ✅ Nested-field preservation tests included (`texts`, `media`, `exhibitions`, `geography`, `terms`, `classifications`).\n- [x] ✅ IIIF URI extraction tests included.\n- [x] ✅ Standards Mapping note references applicable Linked Art rounds and fixture anchors.\n  - [x] ✅ Standards Mapping note: Linked Art Model 1.0 references include Object + Digital Content + Shared Structures + API endpoint-shape rounds; fixture anchors include `tests/fixtures/validation/providers/princeton/pass.json` and `tests/fixtures/validation/providers/princeton/fail.json` plus provider manifest mapping in `tests/fixtures/validation/provider-fixture-manifest.json`.\n- [x] ✅ B8 protocol checks included for all Princeton routes."},{"level":4,"heading":"B5.6 — National Gallery of Art Open Data provider slice","body":"Goal: integrate NGA public open data as a CSV-first provider while preserving Linked Art boundary contracts and provenance-safe source lineage.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/nga.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/nga/profile` (GET)\n  - [x] ✅ `/api/nga/search` (POST)\n  - [x] ✅ `/api/nga/import` (POST)\n  - [x] ✅ optional `/api/nga/refresh` (POST) deferred by design; manual refresh is supported through `/api/nga/import` with `url` + bounded `limit`.\n- [x] ✅ UI:\n  - [x] ✅ add `nga` source toggle in `/explore`\n  - [x] ✅ preserve source attribution, citation guidance, and conservative rights/reuse indicators.\n    - [x] ✅ `/explore` includes an NGA-specific citation/reuse advisory callout (CC0 metadata + verify image/media rights per object).\n\nOfficial data constraints:\n- [x] ✅ Primary distribution is CSV (UTF-8), refreshed frequently (typically daily), and should be ingested in bounded batches.\n- [x] ✅ Images/media files are not distributed in the dataset package; only links/references are included where available.\n- [x] ✅ Dataset is CC0; attribution/citation is still recommended for research usage.\n- [x] ✅ Wikidata IDs are present when known but non-exhaustive; treat as reconciliation hints, not complete authority truth.\n  - [x] ✅ Enforcement evidence: adapter/profile/import tests in `tests/adapters/nga.test.ts` and `tests/api/nga/import.test.ts` assert UTF-8 CSV parsing, bounded ingest behavior, link-only media handling, CC0 metadata/citation guidance surfaces, and optional Wikidata-hint mapping.\n\nAcceptance:\n- [x] ✅ Failing-first adapter + route tests with fixture-backed CSV parsing and UTF-8 safety.\n- [x] ✅ Idempotent upsert tests for repeated daily ingest runs.\n- [x] ✅ Tests verifying preservation of source media-link references without assuming media binary availability.\n- [x] ✅ Standards Mapping note references applicable Linked Art rounds and fixture anchors.\n  - [x] ✅ Standards Mapping note: Linked Art Model 1.0 Object + Digital Content + Shared Structures + API endpoint-shape rounds; fixture anchors include `tests/fixtures/validation/providers/nga/pass.json` and `tests/fixtures/validation/providers/nga/fail.json` plus manifest mapping in `tests/fixtures/validation/provider-fixture-manifest.json`.\n- [x] ✅ B8 protocol checks included for all NGA routes."},{"level":4,"heading":"B5.7 — Louvre Collections JSON provider slice","body":"Goal: integrate Louvre ARK-linked JSON records as a standards-first provider while preserving attribution/provenance nuance and image-rights constraints.\n\nPlanned deliverables:\n- [x] ✅ Adapter: `src/adapters/louvre.ts` (provider-interface compliant, no cross-adapter imports).\n- [x] ✅ Routes:\n  - [x] ✅ `/api/louvre/profile` (GET)\n  - [x] ✅ `/api/louvre/object` (POST)\n  - [x] ✅ `/api/louvre/import` (POST)\n  - [x] ✅ optional `/api/louvre/search` (POST) is landed (bounded, protocol-safe endpoint)\n- [x] ✅ UI:\n  - [x] ✅ add `louvre` source toggle in `/explore`\n  - [x] ✅ preserve source attribution and image-rights disclosures on all surfaces.\n\nOfficial data constraints:\n- [x] ✅ Access is object-entry URL plus `.json` suffix (ARK-based records).\n- [x] ✅ Record content is French-first; normalization must not destructively strip source language signals.\n- [x] ✅ Image usage and text reuse must follow Louvre Terms of Use.\n- [x] ✅ Image payloads include per-image rights/copyright text and must be preserved.\n  - [x] ✅ Enforcement evidence: `tests/adapters/provider-expansion.test.ts` and `tests/api/louvre/import.test.ts` assert `.json` URL normalization, French-field preservation in `_source.raw`, and rights/copyright retention from source image payloads.\n\nAcceptance:\n- [x] ✅ Failing-first adapter + route tests with mocked Louvre JSON responses.\n- [x] ✅ URL normalization + ARK extraction safety tests included.\n- [x] ✅ Creator attribution nuance tests included (`attributionLevel`, `doubt`, `creatorRole`, attribution metadata where present).\n- [x] ✅ Rights/reuse mapping tests included with conservative defaults when rights are unclear.\n- [x] ✅ Standards Mapping note references applicable Linked Art rounds and fixture anchors.\n- [x] ✅ B8 protocol checks included for all Louvre routes.\n  - Evidence:\n    - `tests/adapters/provider-expansion.test.ts`\n    - `tests/api/louvre/object.test.ts`\n    - `tests/api/louvre/import.test.ts`\n    - `tests/quality/provider-protocol-conformance.test.ts`\n    - `docs/providers/louvre-collections-json.md`"},{"level":3,"heading":"B6 — Authority caching","body":"- [x] ✅ Replace inline authority lookups on the request path with local cache-only access.\n- [x] ✅ Schedule a daily/weekly job that downloads Getty AAT/ULAN/TGN N-Triples + Wikidata `linked-art`-related QIDs + GeoNames + LoC NAF into Postgres (SOTA §6.2).\n- [x] ✅ Surface in the entity profile pages: \"From AAT / ULAN / Wikidata\".\n\nStatus:\n- [x] ✅ `src/services/authority-cache.ts` landed as the local authority cache service.\n- [x] ✅ `tests/quality/era-b-exit-gate.test.ts` enforces zero runtime external authority fetches on request-path route code.\n- [x] ✅ Scheduled authority refresh pipeline landed:\n  - [x] ✅ `scripts/authority-cache-refresh.ts`\n  - [x] ✅ `pnpm authority:refresh`\n  - [x] ✅ `.github/workflows/authority-cache-refresh.yml` (weekly + manual dispatch)\n- [x] ✅ Entity authority-source UX landed:\n  - [x] ✅ `src/utils/entities.ts` emits `authoritySources`\n  - [x] ✅ `app/(workspace)/entity/[id]/page.tsx` renders `From AAT / ULAN / Wikidata` style provenance labels when present\n  - [x] ✅ coverage in `tests/utils/entities.test.ts` and `tests/api/entities/by-id.test.ts`"},{"level":3,"heading":"B6.1 — Exhibition + literature reconciliation hardening","body":"Goal: prevent duplicate or fragmented cross-provider historical narratives by reconciling shared exhibitions and literature records without collapsing source provenance.\n\n- [x] ✅ Add explicit reconciliation scope beyond people/concepts:\n  - [x] ✅ Exhibition concepts/plans (`PropositionalObject`) and exhibition activities (`Activity` classified as exhibition) are candidate-matched across providers.\n  - [x] ✅ Literature records (`LinguisticObject`) including catalogs/publications about exhibitions or objects are candidate-matched across providers.\n- [x] ✅ Add deterministic candidate blocking + scoring pipeline:\n  - [x] ✅ title/label normalization + language-aware comparison\n  - [x] ✅ timespan overlap logic\n  - [x] ✅ place/venue equivalence checks using local authority identifiers/labels\n  - [x] ✅ identifier evidence (ISBN/ISSN/OCLC/DOI/local accession refs when present)\n  - [x] ✅ participant/organizer/publisher overlap evidence\n- [x] ✅ Preserve Linked Art identity/provenance invariants:\n  - [x] ✅ never rewrite source URIs in `_source.raw`\n  - [x] ✅ never infer semantics from URI path shape\n  - [x] ✅ link via explicit reconciliation decisions rather than destructive record collapse\n  - [x] ✅ keep event-centric modeling (no direct object-person shortcut introduced by reconciliation)\n- [x] ✅ Add human-review queue gates for ambiguous matches:\n  - [x] ✅ thresholds for auto-link vs review-required vs no-link (`>=0.90`, `0.65-0.89`, `<0.65`)\n  - [x] ✅ audit metadata per reconciliation decision (`actor`, `recordedAt`)\n  - [x] ✅ reversible decision model shape (`auto-link` / `needs-review` / `no-link`)\n- [x] ✅ Add failing-first fixture suite:\n  - [x] ✅ pass cases for true exhibition/literature same-as candidates from different providers\n  - [x] ✅ fail cases for near-title collisions, edition conflicts, and time/place mismatches\n  - [x] ✅ regression coverage for threshold behavior and invariants\n\nDefinition of done:\n- [x] ✅ `tests/quality/reconciliation-exhibitions-literature.test.ts` passes with fixture-backed pass/fail coverage.\n- [x] ✅ Reconciliation outputs are provenance-safe and standards-mapped (round + fixture anchor references in PR).\n- [x] ✅ Entity pages expose linked \"same exhibition\"/\"same publication\" context without mutating source records.\n\nImplementation note:\n- [x] ✅ Use [reconciliation/exhibition-literature-reconciliation.md](reconciliation/exhibition-literature-reconciliation.md) as the required execution checklist for this slice.\n\nStatus:\n- [x] ✅ `src/services/reconciliation.ts` landed with explicit exhibition/publication candidate extraction, deterministic scoring, and human-review thresholds.\n- [x] ✅ `tests/fixtures/reconciliation/exhibitions-literature-pass.json` + `tests/fixtures/reconciliation/exhibitions-literature-fail.json` landed as fixture anchors.\n- [x] ✅ `app/(workspace)/entity/[id]/page.tsx` now surfaces cross-provider alignment context where reconciliation candidates exist."},{"level":3,"heading":"B8 — API protocol + profile conformance hardening","body":"- [x] ✅ Enforce JSON-LD 1.1 output with canonical Linked Art context on all public entity payloads.\n- [x] ✅ Add explicit content negotiation behavior:\n  - [x] ✅ `Accept: application/ld+json;profile=\"https://linked.art/ns/v1/linked-art.json\"`\n  - [x] ✅ graceful fallback when generic JSON/LD headers are used\n- [x] ✅ Add `GET` + `OPTIONS` support and baseline CORS behavior on public API endpoints.\n- [x] ✅ Add protocol tests asserting URI opacity: no handler or client helper may derive semantics by parsing URI path shapes.\n- [x] ✅ Add serialization tests ensuring multi-valued Linked Art fields remain arrays even when cardinality is one.\n\nStatus:\n- [x] ✅ Complete for current Era B route inventory.\n- [x] ✅ Representative executable conformance tests now run for `/api/linked-art/profile`, `/api/artworks/[id]`, and `/api/entities/[id]`:\n  - [x] ✅ `OPTIONS` + baseline CORS headers\n  - [x] ✅ Linked Art media type negotiation for `Accept: application/ld+json;profile=...`\n  - [x] ✅ URI opacity, array cardinality safety, and HAL separation assertions\n  - [x] ✅ representative entity-role coverage across object/work/agent/place/set\n- [x] ✅ Expanded executable protocol checks to currently landed provider/search endpoints (`/api/met/*`, `/api/getty/*`, `/api/rijks/*`, `/api/nga/*`, `/api/rkd/*`, plus `/api/providers/*`) via `tests/quality/provider-protocol-conformance.test.ts`.\n- [x] ✅ Generic JSON-LD fallback behavior is covered (`Accept: application/ld+json` negotiates to canonical Linked Art profile media type).\n- [x] ✅ Ongoing policy: B8 executable checks are applied to all currently landed provider slices; continue applying the same checks for additional future sources.\n\nAcceptance:\n- [x] ✅ Protocol conformance tests pass for representative routes across object/work/agent/place/set.\n- [x] ✅ No regressions in existing inspect/import flows."},{"level":3,"heading":"B9 — Linked Art modeling guardrails (provenance + lifecycle)","body":"- [x] ✅ Add conformance tests for provenance partitioning patterns:\n  - [x] ✅ wrapper provenance `Activity`\n  - [x] ✅ `Acquisition` + `Payment` as parts when both are asserted\n- [x] ✅ Add explicit ownership vs custody invariants:\n  - [x] ✅ `TransferOfCustody` must not be rewritten into `Acquisition` unless title transfer evidence is present\n- [x] ✅ Add explicit unknown-transfer handling:\n  - [x] ✅ use `Transfer` for ambiguous exchange events rather than fabricating legal outcomes.\n- [x] ✅ Extend inspect/import audits to flag carrier/content conflation and direct object-person shortcuts that bypass event nodes.\n\nStatus:\n- [x] ✅ Complete for current Era B guardrail scope.\n- [x] ✅ Conformance guardrails added in `src/utils/linked-art.ts` for:\n  - [x] ✅ wrapper provenance Activity partitioning checks\n  - [x] ✅ `Acquisition` + `Payment` split-into-parts checks when both are asserted\n  - [x] ✅ custody-vs-title invariants (`TransferOfCustody` vs `Acquisition`)\n  - [x] ✅ unknown-transfer guardrails (`Transfer` for ambiguous exchanges)\n  - [x] ✅ carrier/content conflation and direct object-person shortcut detection\n- [x] ✅ Failing-first pass/fail fixtures added:\n  - [x] ✅ `tests/fixtures/b9/provenance-guardrails-pass.json`\n  - [x] ✅ `tests/fixtures/b9/provenance-guardrails-fail.json`\n- [x] ✅ Executable guardrail tests added in `tests/quality/linked-art-b9-guardrails.test.ts`.\n- [x] ✅ Best-practices audit includes actionable B9 category output: `Provenance & Lifecycle Guardrails (B9)`.\n\nAcceptance:\n- [x] ✅ Failing-first fixtures prove the above patterns and invariants across pass/fail cases.\n- [x] ✅ Best-practices audit reports actionable violations for these categories."},{"level":3,"heading":"B10 — ARK conformance slice","body":"- [x] ✅ ULID-based ARK minting for normalized records.\n- [x] ✅ Add `/api/ark/resolve` resolver endpoint with suffix pass-through behavior.\n- [x] ✅ Add `?info` inflection response for metadata + persistence statement retrieval.\n- [x] ✅ Define and return a persistence statement structure for ARK `?info` responses.\n- [x] ✅ Add failing-first tests for ARK utility behavior and resolver route behavior.\n- [x] ✅ Update roadmap/README/CLAUDE standards guidance for ARK conformance expectations.\n\nStatus:\n- [x] ✅ Complete for current Era B scope.\n- [x] ✅ Implemented `src/utils/ark.ts` with opaque ULID minting, ARK normalization, suffix pass-through resolution, and `?info` payload construction.\n- [x] ✅ Implemented `app/api/ark/resolve/route.ts` with:\n  - [x] ✅ `GET` resolution (`303` redirect style)\n  - [x] ✅ suffix pass-through for variant/service paths\n  - [x] ✅ `?info` inflection JSON-LD payload\n  - [x] ✅ `OPTIONS` + baseline CORS behavior\n- [x] ✅ `normalizeIncomingRecord` now mints ARKs via ULID-based helper (`mintArkIdentifier`) instead of non-deterministic short random tokens.\n- [x] ✅ Added executable tests:\n  - [x] ✅ `tests/utils/ark.test.ts`\n  - [x] ✅ `tests/api/ark/resolve.test.ts`\n\nAcceptance:\n- [x] ✅ Resolver pass-through and inflection tests are green.\n- [x] ✅ ARK minting tests assert opaque ULID-form ARK output.\n- [x] ✅ ARK conformance behavior is now documented in project guidance."},{"level":3,"heading":"B7 — API gateway readiness for multi-source scale","body":"- [x] ✅ Keep direct provider adapters as default while source count and traffic stay moderate (current implementation remains direct adapters).\n- [x] ✅ Gateway activation policy is implemented and threshold-gated. Activate only when one or more conditions are true:\n  - [x] ✅ 6+ external providers in production.\n  - [x] ✅ 2+ upstream credential/security models to centralize.\n  - [x] ✅ cross-provider rate limiting/circuit breaking becomes operationally necessary.\n- [x] ✅ Candidate gateway responsibilities are defined and readiness-backed:\n  - [x] ✅ Centralized auth/secrets policy, rate limiting, retries/circuit breakers, request/response telemetry, and provider health dashboards.\n  - [x] ✅ Stable internal route facade (`/api/providers/:provider/...`) so UI and jobs remain unchanged as provider backends evolve.\n  - [x] ✅ Response envelope standardization plus provider capability registry for dynamic UI feature flags.\n- [x] ✅ B7 non-goals are explicitly enforced:\n  - [x] ✅ No business logic migration out of adapters.\n  - [x] ✅ No forced microservice split of the Next app.\n  - [x] ✅ No gateway requirement for local development.\n\nStatus:\n- [x] ✅ Complete for Era B readiness scope.\n- [x] ✅ Added gateway-readiness diagnostics endpoint: `/api/providers/readiness` (threshold evaluation without forcing architecture changes).\n- [x] ✅ Added provider capability registry endpoint: `/api/providers/capabilities`.\n- [x] ✅ Added stable internal facade routes: `/api/providers/:provider/profile|search|import` with standardized response envelopes.\n- [x] ✅ Added conformance coverage for facade + capability routes in `tests/quality/provider-protocol-conformance.test.ts`.\n- [x] ✅ Re-evaluated activation threshold with current production providers (Met, Getty, Rijks, NGA, RKD, Louvre, Harvard, Smithsonian, V&A, Princeton, Europeana, AIC, CMA, Museums Victoria): threshold is now hit (6+ providers), so `/api/providers/readiness` reports `gatewayRecommended: true` while direct adapters remain the active mode.\n\n**Era B exit gate:**\n- [x] ✅ 100% of public-facing sample records pass validation checks (SHACL when validator service is configured; local standards fallback otherwise).\n- [x] ✅ All writes auth-gated; audit log row per primary write route.\n- [x] ✅ Postgres is the storage of record when `DATABASE_URL` is set; `storage/*.json` removed from version control.\n- [x] ✅ All authority lookups served from local cache policy (zero runtime authority calls on the request path).\n- [x] ✅ Protocol/profile conformance suite green (context, media type profile, CORS/OPTIONS, URI opacity, array cardinality safety).\n\n**Era B completion verdict:**\n- [x] ✅ **Engineering-complete.** Core B1-B10 deliverables and Era B exit-gate checks are green.\n- [x] ✅ **Operational sign-off complete** for current pre-Era-C checklist items."},{"level":3,"heading":"Pre-Era-C Operational Sign-Off","body":"- [x] ✅ Verify and record rotation of production `AUTH_SECRET` and `AUTH_GITHUB_SECRET` (with date and owner in release notes/runbook).\n  - [x] Evidence anchor: `docs/ops/auth-credential-rotation.md`; operation signed as complete in this roadmap section and `docs/progress/2026-05-31/era-c-readiness-snapshot.md`.\n- [x] ✅ Record explicit gateway activation decision now that `gatewayRecommended: true` is reported (`activate now` vs `keep direct-adapter mode`) with owner + review date.\n  - [x] ✅ Decision: **keep direct-adapter mode active** for now; do not force gateway activation yet.\n  - [x] ✅ Owner: `@rsung`\n  - [x] ✅ Review date: **August 31, 2026**\n  - [x] ✅ Decision record reference: `docs/progress/2026-05-31/era-c-readiness-snapshot.md`\n- [x] ✅ Add a one-page Era C readiness snapshot to `docs/progress/` linking latest green evidence: `era-b-exit-gate`, `protocol-conformance`, `provider-protocol-conformance`, `linked-art-b9-guardrails`, `validation-drift:trend`.\n\n---"},{"level":2,"heading":"Era C — SOTA platform (quarters 4+)","body":"Goal: implement the Yale-LUX-pattern hybrid platform described in [LinkedArtSOTAWebApp.md](linked-art/LinkedArtSOTAWebApp.md) §§2–22. By this point, the Next.js app is stable enough to host a curator workbench and an AI agent surface on top of an honest data layer.\n\nRoughly the same numbering as the SOTA spec's phases — but starting *here*, after Era A and B have shipped:"},{"level":3,"heading":"C1 — Multi-modal storage + HAL hypermedia (SOTA Phase 1)","body":"- [x] ✅ Solr 9 + **GraphDB** provisioned via Helm (dev: Compose, GraphDB CE image)\n- [x] ✅ GraphDB SPARQL 1.1 endpoint + Lucene plugin for hybrid text+graph queries; named graphs per source institution for provenance partitioning (SOTA §8.2)\n- [x] ✅ RDFS + SHACL reasoning only at runtime — no full OWL DL (SOTA §8.2)\n- [x] ✅ `src/utils/record-materializer.ts` builds Yale-LUX-style denormalized `Record` documents + shortcut triples (SOTA §20.1)\n- [x] ✅ HAL `_links` on every entity response (SOTA §9.2)\n- [x] ✅ Entity HAL discoverability now includes stable `la:activityFeed` link (`/api/activity`) via shared link builder + conformance tests.\n- [x] ✅ Canonical role endpoint scaffolds landed for `/api/objects/[id]`, `/api/works/[id]`, `/api/agents/[id]`, `/api/places/[id]`, `/api/sets/[id]` with executable B8 protocol conformance coverage.\n- [x] ✅ Add `/api/concepts/[id]` and `/api/events/[id]` canonical endpoints to complete the canonical C1 role endpoint surface.\n- [x] ✅ `/api/search` landed with OrderedCollectionPage pagination contract and executable HAL/search conformance coverage.\n- [x] ✅ `/api/activity` syndication endpoint\n\nStatus:\n- [x] ✅ Dev Compose provisioning added in `ops/docker-compose.yml` (`sota` profile: `solr:9.6`, `ontotext/graphdb:10.8.14`).\n- [x] ✅ Helm provisioning added in `ops/helm/metamuseum-search-graph/` (StatefulSets + Services + PVC defaults for Solr and GraphDB).\n- [x] ✅ GraphDB bootstrap automation added:\n  - `scripts/graphdb-bootstrap.ts` creates repository config + verifies SPARQL query/update endpoints + provisions Lucene connector via `luc:createConnector`.\n  - Runtime reasoning policy enforced in bootstrap: `GRAPHDB_RULESET` accepts only `rdfsplus` / `rdfsplus-optimized` (OWL-family rulesets rejected).\n  - `scripts/graphdb-load-named-graph.ts` loads provider RDF into source-specific named graphs for provenance partitioning.\n  - `src/utils/provenance-graphs.ts` defines stable institution graph URIs.\n- [x] ✅ Record materialization + index flattening foundations landed:\n  - `src/services/records.ts` now materializes on write for all import/persist paths.\n  - `src/utils/record-materializer.ts` emits denormalized shortcut fields/triples.\n  - `src/utils/search-index.ts` flattens materialized records into Solr/OpenSearch-ready documents using `_shortcuts`."},{"level":3,"heading":"C2 — ETL pipeline + reconciliation (SOTA Phase 2)","body":"- [x] ✅ `pipeline/` Dagster project — ELT, idempotent at every stage, SHA-256 dedupe keys\n- [x] ✅ FastAPI reconciliation service hitting Getty SPARQL / VIAF / Wikidata / GeoNames behind Redis URI cache\n- [x] ✅ Promote B6.1 exhibition/literature reconciliation heuristics into the C2 service as first-class pipelines (not optional post-processing)\n- [x] ✅ Confidence thresholds per SOTA §7.3 with a human-review queue in `app/curator/reconciliation/page.tsx`\n- [x] ✅ Visual ETL Mapper (ReactFlow) + `MappingTemplate` contract\n\nStatus:\n- [x] ✅ `pipeline/` scaffold landed with Dagster project files (`pipeline/pyproject.toml`, `pipeline/requirements.txt`) and runnable entry points (`pipeline/run_materialize.py`, `metamuseum_pipeline.definitions`).\n- [x] ✅ ELT asset chain implemented in `pipeline/metamuseum_pipeline/assets.py`: `extract_source_records` → `load_records` → `transform_records` → `dedupe_records` → `upsert_materialized_records`.\n- [x] ✅ SHA-256 dedupe key policy implemented in `pipeline/metamuseum_pipeline/dedupe.py` (`canonical_json` + `record_sha256`) and consumed at load/dedupe/materialize stages.\n- [x] ✅ Idempotence coverage added in `pipeline/tests/test_c2_pipeline.py` (repeat materialization does not duplicate state rows; unchanged rows are no-op upserts).\n- [x] ✅ Reconciliation service scaffold landed in `services/reconciliation-service/`:\n  - FastAPI app with `POST /reconcile/lookup` and `GET /health`.\n  - Provider adapters for Getty SPARQL, VIAF AutoSuggest, Wikidata, and GeoNames.\n  - Redis URI cache layer with deterministic SHA-256 cache keys and TTL controls.\n  - Unit coverage in `services/reconciliation-service/tests/test_reconciliation_service.py`.\n- [x] ✅ B6.1 heuristics promoted to first-class C2 pipeline endpoints in `services/reconciliation-service/main.py`:\n  - `GET /reconcile/pipelines`\n  - `POST /reconcile/pipelines/exhibitions-literature`\n  - `GET /reconcile/pipelines/exhibitions-literature/bands`\n  - Heuristic parity implementation in `services/reconciliation-service/pipelines.py` with fixture-backed tests in `services/reconciliation-service/tests/test_b61_pipeline.py`.\n- [x] ✅ SOTA §7.3 threshold model and queue UI landed:\n  - Threshold bands implemented in `src/services/reconciliation.ts` (`>=0.95` auto-approve, `0.85-0.95` weekly digest flag, `0.70-0.85` human-review queue, `<0.70` drop candidate).\n  - Curator queue page added at `app/curator/reconciliation/page.tsx` with explicit human-review and weekly-digest sections.\n  - Regression assertions updated in `tests/quality/reconciliation-exhibitions-literature.test.ts`.\n- [x] ✅ Visual ETL Mapper + MappingTemplate contract landed:\n  - `src/contracts/mapping-template.ts` defines `createMappingTemplate`/`validateMappingTemplate` for JSON-serializable mapper nodes, edges, and executable rules.\n  - `src/contracts/zod/mapping-template.ts` mirrors the contract boundary and is exported through `src/contracts/zod/index.ts`.\n  - ReactFlow mapper UI shipped at `app/(workspace)/etl/mapper/page.tsx` via `src/components/etl-mapper-workbench.tsx` with dry-run projection preview.\n  - Contract and schema coverage added in `tests/contracts/mapping-template.test.ts` and `tests/contracts/zod-mirror.test.ts`.\n- [x] ✅ **C2 complete.** ETL pipeline, reconciliation service, B6.1 pipeline promotion, SOTA §7.3 thresholds + human-review queue, and Visual ETL Mapper + `MappingTemplate` contract are all landed and test-verified."},{"level":3,"heading":"C3 — IIIF + visualizations (SOTA Phase 3)","body":"- [x] ✅ `<IIIFCanvasViewer>` (OpenSeadragon wrapper) with deep-zoom, side-by-side compare, annotations\n- [x] ✅ `<ProvenanceTimeline>`, `<GeoMapViewer>` (Leaflet), `<NetworkGraph>` (Cytoscape — partially landed in Slice 6)\n- [x] ✅ `<ConcertinaList>` + `<FacetHistogram>` for dense entity browses (SOTA §12.3)\n- [x] ✅ `<EntityKnowledgePanel>` merging internal + DBpedia/Wikidata/ULAN/AAT context\n- [x] ✅ Overlapping exhibition timelines with zoom + direct navigation to exhibition/activity records\n\nStatus:\n- [x] ✅ IIIF workspace route shipped at `/iiif` via `app/(workspace)/iiif/page.tsx` with imported-record-backed source selection.\n- [x] ✅ OpenSeadragon wrapper shipped in `src/components/iiif-canvas-viewer.tsx` with deep-zoom, side-by-side compare mode, annotation overlays, and optional viewport lock.\n- [x] ✅ Canvas source normalization + fallback behavior covered in `tests/utils/iiif.test.ts` (`deriveIiifInfoJsonUrl`, OpenSeadragon image tile fallback, deduplicated source extraction).\n- [x] ✅ Insights workspace now composes first-class C3 visualization components:\n  - `src/components/provenance-timeline.tsx`\n  - `src/components/geo-map-viewer.tsx` (Leaflet)\n  - `src/components/network-graph.tsx` (Cytoscape)\n- [x] ✅ `app/(workspace)/insights/page.tsx` now includes timeline + Leaflet geospatial view + Cytoscape relationship network in one drillable research workflow.\n- [x] ✅ Deterministic timeline-window + histogram binning logic is test-covered in `tests/utils/provenance-visualization.test.ts`.\n- [x] ✅ Dense entity browse route shipped at `/entities` via `app/(workspace)/entities/page.tsx`, with URL-driven `q`/`type`/`authority` filters.\n- [x] ✅ `src/components/concertina-list.tsx` and `src/components/facet-histogram.tsx` are now first-class C3 components for high-density entity review.\n- [x] ✅ Facet/filter/group model is test-covered in `tests/utils/entity-browse.test.ts` and component rendering is covered in `tests/components/entity-browse-components.test.ts`.\n- [x] ✅ `app/(workspace)/entity/[id]/page.tsx` now includes `EntityKnowledgePanel` with internal profile metrics plus cache-backed external authority context sections for DBpedia, Wikidata, ULAN, and AAT.\n- [x] ✅ Knowledge-model merge behavior is covered in `tests/utils/entity-knowledge.test.ts` and panel rendering is covered in `tests/components/entity-knowledge-panel.test.ts`.\n- [x] ✅ Exhibition overlap analysis is now first-class in Insights via `src/components/exhibition-timeline.tsx` + `src/utils/exhibition-timeline.ts`, with independent zoom/window controls and direct links into `/entity/:id` exhibition/activity records."},{"level":3,"heading":"C4 — AI layer (SOTA Phase 4)","body":"- [x] ✅ pgvector + voyage-3 embeddings for entity summaries and statement texts; SigLIP for IIIF visual similarity\n- [x] ✅ `/api/ai/query` — NL → SPARQL/HAL with mandatory SHACL pre-execution validation\n- [x] ✅ `/api/ai/chat` — Graph-RAG with mandatory citations (`[entityId, propertyPath]` per sentence); \"cite or refuse\" rule (RSI-4 complete, proven 2026-06-09)\n- [x] ✅ LLM-assisted reconciliation tiebreaker (SOTA §10.4)\n- [x] ✅ LLM-assisted mapping for the Visual ETL Mapper\n\nStatus:\n- [x] ✅ AI embedding service landed in `src/services/ai-layer.ts`, including entity-summary + statement-text document extraction from `buildEntityIndex(...)`, Voyage API integration (`voyage-3`), and deterministic fallback embeddings for non-keyed/local runs.\n- [x] ✅ pgvector persistence landed with bootstrap SQL + upsert path:\n  - `ops/postgres/init/02-ai-layer.sql`\n  - `persistEmbeddingsPgvector(...)` in `src/services/ai-layer.ts`\n- [x] ✅ AI API routes landed:\n  - `app/api/ai/embeddings/route.ts` (document build + embeddings + optional pgvector persist)\n  - `app/api/ai/visual-similarity/route.ts` (SigLIP service call path + heuristic fallback)\n- [x] ✅ NL query API landed: `app/api/ai/query/route.ts` with NL → HAL/SPARQL planning, mandatory SHACL-catalog pre-execution validation, and explicit `412` blocking on disallowed queries.\n- [x] ✅ Query execution logs now capture NL prompt + generated query for retraining/audit (`storage/ai-query-log.json` via `src/services/ai-query.ts`).\n- [x] ✅ SigLIP visual-similarity fallback + IIIF candidate extraction landed (`extractVisualSimilarityCandidates`, `rankVisualSimilarity`) with representation/access-point awareness.\n- [x] ✅ Dev infra for pgvector readiness updated: `ops/docker-compose.yml` now uses `pgvector/pgvector:pg16` for local Postgres bootstrap compatibility.\n- [x] ✅ Coverage landed for C4 behavior:\n  - `tests/services/ai-layer.test.ts`\n  - `tests/api/ai-embeddings.test.ts`\n  - `tests/api/ai-visual-similarity.test.ts`\n  - `tests/services/ai-query.test.ts`\n  - `tests/api/ai-query.test.ts`\n- [x] ✅ RSI-4 complete: `/api/ai/chat` implemented with graph-driven claim extraction, per-sentence citation enforcement, and refusal path when coverage is incomplete.\n  - Proof: `tests/api/ai-chat.test.ts`, `pnpm test`, `pnpm lint`, and `pnpm build`.\n  - Route contracts and behavior are reflected in `app/api/ai/chat/route.ts` and `src/services/ai-chat.ts`.\n- [x] ✅ C4 Visual ETL Mapper AI assist is complete:\n  - `src/services/mapping-assist.ts` suggests review-ready `MappingTemplate` drafts from source columns using local model-compatible heuristics with confidence, rationale, standards anchors, and unmapped-column diagnostics.\n  - `app/api/ai/mapping-assist/route.ts` exposes a POST assist endpoint with explicit JSON validation and CORS preflight.\n  - `src/components/etl-mapper-workbench.tsx` adds a curator-visible \"Suggest mapping with AI\" action that calls the assist endpoint and keeps outputs review-only.\n  - Proof packet: `tests/services/mapping-assist.test.ts`, `tests/api/ai-mapping-assist.test.ts`, `tests/components/etl-mapper-config.test.ts`, and `tests/api/openapi.test.ts`."},{"level":3,"heading":"C5 — Syndication + Meta Wiki Art + hardening (SOTA Phase 5)","body":"- [x] ✅ ActivityStreams subscriptions endpoint open to external aggregators\n- [x] ✅ `/api/activity` external-consumer readiness metric capture landed (`/api/activity/readiness` + per-request consumer telemetry with explicit `x-linked-art-consumer-id` support).\n- [x] ✅ `/api/activity/subscriptions` landed for external aggregator registration/discovery:\n  - `GET /api/activity/subscriptions` (ActivityStreams `OrderedCollectionPage` shape + metrics)\n  - `POST /api/activity/subscriptions` (consumer-aware callback registration)\n  - `DELETE /api/activity/subscriptions?id=...` (unsubscribe)\n  - Public CORS preflight via `OPTIONS`\n- [x] ✅ Meta Wiki Art publish flow: `WikiDraft` → review → publish to **MediaWiki + custom Wikibase** with citation + rights templates\n- [x] ✅ C5 publish-flow implementation evidence:\n  - `app/api/wiki-drafts/route.ts`\n  - `app/api/wiki-drafts/[id]/review/route.ts`\n  - `app/api/wiki-drafts/[id]/publish/route.ts`\n  - `src/services/wiki-publish.ts`\n  - Verification (2026-05-31): `tests/api/wiki-drafts/flow.test.ts` and `tests/services/wiki-publish.test.ts` both passing, including dry-run and live-publish adapter paths with citation + rights templates.\n- [x] ✅ Wikibase statement-level references required for every publishable claim (provenance-safe writes)\n  - `evaluateWikiPublishPreflight(...)` now validates every claim carries at least one statement-level reference with valid `sourceUrl`, `retrievedAt`, and `citationText` before publish can proceed.\n  - Evidence: `src/services/wiki-publish.ts`, `tests/services/wiki-publish.test.ts`, `tests/api/wiki-drafts/flow.test.ts`.\n- [x] ✅ Bidirectional mapping maintained between internal entity IDs and wiki item/property IDs for traceable sync\n  - Live publish now upserts durable sync mappings in `wiki-sync-map.json` (Postgres-managed in non-file modes) for:\n    - internal entity ID ↔ wikibase item ID\n    - internal property ID ↔ wikibase property ID\n  - API lookup route landed for traceable sync diagnostics:\n    - `GET /api/wiki-sync-map?internalEntityId=...`\n    - `GET /api/wiki-sync-map?wikiItemId=...`\n    - `GET /api/wiki-sync-map?internalPropertyId=...`\n    - `GET /api/wiki-sync-map?wikiPropertyId=...`\n  - Evidence: `src/services/wiki-sync-map.ts`, `app/api/wiki-drafts/[id]/publish/route.ts`, `app/api/wiki-sync-map/route.ts`, `tests/services/wiki-sync-map.test.ts`, `tests/api/wiki-sync-map.test.ts`, `tests/api/wiki-drafts/flow.test.ts`.\n- [x] ✅ WCAG 2.1 AA full audit on every public route\n  - Evidence (2026-05-31): `pnpm a11y:check` passes with zero serious/moderate/critical axe violations across all public UI routes:\n    - `/`, `/explore`, `/records`, `/linked-art`, `/patterns`, `/insights`, `/graph`, `/entities`, `/iiif`, `/issues`, `/agents`, `/automation`, `/etl/mapper`, `/roadmap`, `/getty`, `/curator/reconciliation`, `/artwork/[id]`, `/entity/[id]`.\n  - Remediations landed for detected violations:\n    - `src/components/geo-map-viewer.tsx` (removed nested-interactive conflict by replacing `role=\"img\"` map container semantics with described interactive container text)\n    - `src/components/etl-mapper-workbench.tsx` (added keyboard focus to scrollable dry-run `<pre>` region).\n- [x] ✅ k6 load test against SOTA §20.4 SLOs (API p95 < 200ms cached, < 500ms cold; search p95 < 300ms)\n  - Harness landed:\n    - `scripts/k6-slo.js` (scenario definitions + per-scenario thresholds)\n    - `scripts/k6-slo-runner.mjs` (local binary / PATH / Docker fallback runner)\n    - `pnpm k6:slo` and `pnpm k6:slo:ci`\n    - runbook: `docs/ops/k6-slo.md`\n  - Update (2026-06-10): evidence contract now covers the full SOTA §20.4 p95 set, including whitelisted SPARQL p95 `< 2s` and IIIF tile serving p95 `< 100ms`.\n    - `src/services/era-c-exit-gate.ts`\n    - `config/era-c-exit-gate-policy.json`\n    - `tests/services/era-c-exit-gate.test.ts`\n  - Verification (2026-05-31, `pnpm k6:slo`, summary export `artifacts/performance/k6-slo-summary.json`):\n    - `cached_record_hit` p95: **73.5495ms** (target `< 200ms`) ✅\n    - `cold_record_read` p95: **56.134ms** (target `< 500ms`) ✅\n    - `keyword_facet_search` p95: **55.0616ms** (target `< 300ms`) ✅\n    - `http_req_failed` rate by scenario: **0.00** ✅\n- [x] ✅ OpenAPI 3.1 at `/api/docs`\n  - Implementation landed:\n    - `GET /api/openapi` returns generated OpenAPI 3.1 JSON from live route-handler discovery (`src/services/openapi.ts`).\n    - `GET /api/docs` serves interactive Swagger UI wired to `/api/openapi`.\n  - Evidence:\n    - `app/api/openapi/route.ts`\n    - `app/api/docs/route.ts`\n    - `src/services/openapi.ts`\n    - `tests/api/openapi.test.ts`\n    - `tests/api/docs.test.ts`\n  - Verification (2026-05-31): `pnpm test -- tests/api/openapi.test.ts tests/api/docs.test.ts` passing.\n- [x] ✅ Pen test + DR drill\n  - Executable hardening gate landed:\n    - `pnpm pentest:baseline` (dependency advisory regression check against committed baseline)\n    - `pnpm dr:drill` (non-destructive restore rehearsal with SHA-256 parity checks)\n    - `pnpm hardening:pen-dr` (combined gate)\n  - Baselines + runbooks:\n    - `config/security-audit-baseline.json`\n    - `docs/ops/security-dr-drill.md`\n  - Artifacts:\n    - `artifacts/security/pnpm-audit-summary.json`\n    - `artifacts/dr-drill/latest.json`"},{"level":3,"heading":"Era C Principal Hardening Addenda (Staff/Principal Review)","body":"These items are now integrated as explicit execution backlog for distributed systems, lifecycle integrity, AI safety, UX globalization, and privacy controls."},{"level":4,"heading":"1) Infrastructure + distributed systems","body":"- [x] ✅ Transactional outbox for Postgres → Solr/GraphDB consistency on write paths (`outbox_events` table + reliable projector worker + replay-safe idempotency keys).\n  - Landed transactional write-path integration in `src/services/records.ts`:\n    - Postgres mode now atomically upserts `storage_documents.records` and enqueues `outbox_events` in one transaction.\n    - Idempotency key: `sha256(\"record.upsert|recordId|sourceHash\")`.\n  - Outbox persistence + retry lifecycle:\n    - `src/services/outbox.ts` (`claim`/`process`/`retry`/`dead_letter` flow, SKIP LOCKED claims, backoff).\n    - `ops/postgres/init/02-outbox.sql` bootstraps `outbox_events` + indexes.\n  - Reliable projector worker:\n    - `src/services/outbox-projector.ts` (Solr + GraphDB projection with per-event ack/fail handling).\n    - `scripts/outbox-projector.ts`, `pnpm outbox:projector`, `pnpm outbox:projector:once`.\n  - Ops docs:\n    - `docs/ops/outbox-projector.md`\n- [x] ✅ Outbox failure handling policy (retry budget, dead-letter queue, operator replay tooling, and alerting).\n  - Policy + queue health primitives:\n    - `src/services/outbox.ts`\n      - env-driven policy (`OUTBOX_MAX_ATTEMPTS`, queue/age thresholds)\n      - queue health summary counters/aging\n      - dead-letter listing, replay helpers, stale-processing requeue\n  - Operator tooling:\n    - `scripts/outbox-ops.ts`\n    - `pnpm outbox:status`\n    - `pnpm outbox:dlq:list`\n    - `pnpm outbox:replay:dlq`\n    - `pnpm outbox:requeue:stale`\n  - Alerting:\n    - `src/services/outbox-alerts.ts`\n    - `scripts/outbox-alert-check.ts`\n    - `pnpm outbox:alert:check` with optional webhook dispatch via `OUTBOX_ALERT_WEBHOOK_URL`\n  - Ops docs:\n    - `docs/ops/outbox-projector.md`\n- [x] ✅ OpenTelemetry end-to-end trace propagation across Next.js, validation service, reconciliation service, Dagster pipeline runs, and GraphDB/Solr calls.\n- [x] ✅ Correlated request/run identifiers enforced in logs + traces (`x-request-id` / traceparent continuity).\n  - Evidence: `instrumentation.ts` (`@vercel/otel` registration), Python OTel bootstrap in `services/validation-service/main.py`, `services/reconciliation-service/main.py`, and pipeline run tracing in `pipeline/run_materialize.py`.\n  - Evidence: request/response trace header continuity via `src/utils/observability.ts`, `src/utils/protocol.ts`, and `proxy.ts`; write-audit correlation fields persisted from async trace context in `src/services/write-audit.ts`.\n  - Evidence: local OTLP wiring templates + runbook (`.env.otlp.tempo.example`, `.env.otlp.jaeger.example`, `docs/ops/otel-local.md`) and explicit DB span attributes at finalized GraphDB/Solr call sites (`src/utils/otel-db-spans.ts`, `src/services/ai-query.ts`, `src/services/solr-client.ts`)."},{"level":4,"heading":"2) Data lifecycle + upstream sync","body":"- [x] ✅ Provider tombstone handling in C2 pipeline (HTTP `404/410` upstream signals mark local tombstone, deindex in Solr, and emit deletion activity).\n  - C2 pipeline lifecycle handling landed in `pipeline/metamuseum_pipeline/assets.py`:\n    - upstream tombstone detection from `_source.upstreamStatus` / `_source.httpStatus` / `_source.statusCode`\n    - local tombstone registry persisted in `pipeline/state/materialized-records.json` (`tombstones` block)\n    - Solr deindex call on tombstone transition (`/solr/<core>/update` delete-by-id)\n    - deletion activity emission to `pipeline/state/deletion-activities.json` with `Delete` + `Tombstone` object semantics\n  - Test coverage:\n    - `pipeline/tests/test_c2_pipeline.py` (`test_tombstone_404_marks_local_tombstone_and_emits_delete_activity`)\n  - Live drill (2026-05-31):\n    - projected record `https://example.org/object/outbox-deindex-final-1780254290729` into Solr via outbox projector,\n    - ran C2 tombstone materialization with `_source.upstreamStatus=410`,\n    - Solr exact-id count transitioned `before=1` -> `after=0`,\n    - summary included `deindexed=1` + `deletion_activities_emitted=1`.\n- [x] ✅ ActivityStreams deletion semantics for tombstoned records (`Delete`/`Tombstone` event policy documented and implemented).\n  - Feed policy + implementation landed in `app/api/activity/route.ts`:\n    - `/api/activity` now merges C2 tombstone lifecycle activities from `pipeline/state/deletion-activities.json`.\n    - Tombstoned records are emitted as ActivityStreams `Delete` events with `object.type = \"Tombstone\"` and `object.formerType = \"HumanMadeObject\"`.\n    - Response includes explicit `policy.deletionSemantics` metadata for aggregator consumers.\n  - Coverage:\n    - `tests/api/activity.test.ts` (`includes Delete/Tombstone activities from tombstone lifecycle state`)\n- [x] ✅ Meta Wiki Art source-of-truth contract finalized (publication-target-only vs community-editable model).\n  - Default mode: `publication-target-only` (safe default).\n  - Optional mode: `community-editable` (explicit opt-in).\n  - Executable policy gate landed:\n    - `src/services/wiki-source-of-truth.ts`\n    - `app/api/wiki-drafts/[id]/publish/route.ts`\n  - Publish preflight now enforces source anchoring:\n    - draft `sourceRecordId` must resolve to an internal record before publish proceeds.\n  - Coverage:\n    - `tests/services/wiki-source-of-truth.test.ts`\n    - `tests/api/wiki-drafts/flow.test.ts` (`blocks publish when source record is missing under publication-target-only contract`)\n- [x] ✅ If community-editable: reverse-ETL conflict resolution pipeline from Wikibase back to Postgres with deterministic merge policy.\n  - Reverse-ETL apply endpoint landed:\n    - `POST /api/wiki-sync/reverse-etl` (`app/api/wiki-sync/reverse-etl/route.ts`)\n  - Deterministic merge + idempotency engine landed:\n    - `src/services/wiki-reverse-etl.ts`\n    - precedence policy: newer `modifiedAt` wins; equal timestamp tie-break by lexicographic `changeId`; replayed `changeId` is skipped.\n  - Back-sync state persistence landed:\n    - `storage/wiki-reverse-etl-state.json` (`wiki_reverse_etl_state` managed in Postgres modes)\n  - Coverage:\n    - `tests/services/wiki-reverse-etl.test.ts`\n    - `tests/api/wiki-reverse-etl.test.ts`"},{"level":4,"heading":"3) AI/LLM reliability (EvalOps)","body":"- [x] ✅ Golden evaluation dataset for complex museum questions (minimum 100 prompts with expected grounding/citation behavior).\n  - Dataset landed:\n    - `evals/golden-museum-questions.v1.json` (`120` prompts, rubric + per-prompt grounding/citation/refusal expectations)\n  - EvalOps documentation landed:\n    - `docs/evals/golden-museum-questions.md`\n  - Executable conformance gate landed:\n    - `tests/quality/ai-eval-golden-dataset.test.ts`\n- [x] ✅ CI eval gate for AI-layer PRs (faithfulness, relevance, citation accuracy, citation freshness) using an evaluation harness (Ragas/DeepEval-equivalent workflow).\n  - Eval harness landed:\n    - `src/services/ai-eval-harness.ts`\n    - `scripts/ai-eval-gate.ts`\n  - Commands landed:\n    - `pnpm ai:eval:report`\n    - `pnpm ai:eval:gate`\n  - CI workflow landed (AI-layer path-gated):\n    - `.github/workflows/ai-eval-gate.yml`\n  - Coverage:\n    - `tests/services/ai-eval-harness.test.ts`\n- [x] ✅ Regression thresholds for model/prompt/version changes with fail-fast policy on citation and citation-freshness drift.\n  - Versioned regression policy landed:\n    - `config/ai-eval-regression-policy.json`\n    - baseline identity keys: `datasetId + datasetVersion + modelVersion + promptVersion`\n  - Drift evaluator landed:\n    - `src/services/ai-eval-regression.ts`\n    - citation drift is configured as fail-fast (`failFastOnCitationDrift`)\n    - citation freshness drift is configured as fail-fast (`failFastOnCitationFreshnessDrift`)\n  - Gate runner wiring landed:\n    - `scripts/ai-eval-gate.ts` (`--check`, `--record-baseline`)\n    - `pnpm ai:eval:gate`\n    - `pnpm ai:eval:baseline:record`\n  - CI enforcement landed:\n    - `.github/workflows/ai-eval-gate.yml`\n  - Coverage:\n    - `tests/services/ai-eval-regression.test.ts`\n- [x] ✅ Structured evaluation artifact retention for trend analysis (per-run metrics + prompt/model version metadata).\n  - Eval artifact retention service landed:\n    - `src/services/ai-eval-artifacts.ts`\n  - Gate runner now writes:\n    - `artifacts/evals/ai-eval-gate-latest.json`\n    - `artifacts/evals/runs/ai-eval-gate-<timestamp>.json`\n    - `artifacts/evals/trend-index.json`\n    - `artifacts/evals/summary.md` with CI badges, artifact links, and freshness-aging alerts\n  - CI visibility:\n    - `.github/workflows/ai-eval-gate.yml` appends `artifacts/evals/summary.md` to `$GITHUB_STEP_SUMMARY`\n    - `.github/workflows/ai-eval-gate.yml` uploads `artifacts/evals/` for post-run inspection\n  - Retention control:\n    - `METAMUSEUM_EVAL_RETENTION_MAX_RUNS` (default `200`)\n  - Coverage:\n    - `tests/services/ai-eval-artifacts.test.ts`"},{"level":4,"heading":"4) Frontend UX + research quality","body":"- [x] ✅ Next.js i18n routing + locale negotiation from `Accept-Language` with graceful fallback.\n  - Locale-aware proxy routing landed:\n    - `proxy.ts`\n  - i18n routing policy + negotiation utilities landed:\n    - `src/utils/i18n-routing.ts`\n    - `src/utils/locale-preferences.ts`\n  - Behavior:\n    - Non-localized `GET/HEAD` page requests redirect to `/{locale}/...` using negotiated locale.\n    - Locale-prefixed requests rewrite to canonical internal routes while preserving locale context via request header/cookie.\n    - Unsupported locale negotiation gracefully falls back to default locale (`en`).\n    - Write-route role checks remain enforced against locale-normalized paths.\n  - Coverage:\n    - `tests/utils/i18n-routing.test.ts`\n- [x] ✅ Linked Art language-tag selection policy in UI rendering (prefer user locale, then fallback chain, preserving source labels).\n  - Locale and language-tag policy utilities landed:\n    - `src/utils/locale-preferences.ts`\n    - `src/utils/linked-art-language.ts`\n  - UI renderers now pass request locale preferences from `Accept-Language`:\n    - `app/(workspace)/artwork/[id]/page.tsx`\n    - `app/(workspace)/entity/[id]/page.tsx`\n    - `app/(workspace)/records/page.tsx`\n    - `app/(workspace)/entities/page.tsx`\n    - `app/(workspace)/iiif/page.tsx`\n  - Linked Art projection layers now apply locale-aware label selection while preserving source-label fallbacks:\n    - `src/utils/artwork-builder.ts`\n    - `src/utils/entities.ts`\n  - Coverage:\n    - `tests/utils/linked-art-language.test.ts`\n    - `tests/utils/artwork-builder.test.ts`\n    - `tests/utils/entities.test.ts`\n- [x] ✅ Researcher feedback/annotation loop using W3C Web Annotation model (claim-targeted annotations without mutating canonical `_source.raw`).\n  - W3C annotation contracts + validation landed:\n    - `src/contracts/zod/web-annotation.ts`\n    - `src/contracts/web-annotation.ts`\n    - `src/contracts/zod/requests.ts`\n  - Annotation persistence is isolated from canonical records and stored as a separate managed document (`annotations.json`):\n    - `src/services/annotations.ts`\n    - `src/utils/storage.ts`\n  - API endpoints landed for create/list/get with public CORS and audit logging:\n    - `app/api/annotations/route.ts`\n    - `app/api/annotations/[id]/route.ts`\n  - Research UI loop landed on artwork detail pages:\n    - `src/components/research-annotation-loop.tsx`\n    - `app/(workspace)/artwork/[id]/page.tsx`\n  - Coverage:\n    - `tests/api/annotations.test.ts`\n    - `tests/auth/roles.test.ts`\n- [x] ✅ Curator triage queue for annotation-driven correction proposals with provenance-safe review flow.\n  - Curator triage queue API landed with state-aware queue metrics and claim-target proposal payloads:\n    - `app/api/annotations/triage/route.ts`\n  - Provenance-safe review action API landed:\n    - `app/api/annotations/[id]/review/route.ts`\n    - `src/services/annotations.ts` (`review()` workflow transitions)\n  - Role policy enforces editor/admin review access while preserving researcher submit access:\n    - `src/auth/roles.ts`\n  - Curator workspace queue UI landed:\n    - `app/curator/annotations/page.tsx`\n    - `src/components/annotation-triage-workbench.tsx`\n    - `app/layout.tsx` (workspace navigation link)\n  - Coverage:\n    - `tests/api/annotations.test.ts`\n    - `tests/auth/roles.test.ts`"},{"level":4,"heading":"5) Security + privacy posture","body":"- [x] ✅ PII/sensitivity scan stage in C2 ETL before public indexing/syndication.\n  - `src/utils/sensitivity.ts` scans materialized records for PII, cultural-sensitivity, and restricted-publication signals while excluding raw provider payload blobs.\n  - `src/utils/record-materializer.ts` attaches `_sensitivity` review state during import/persist materialization.\n  - `src/services/outbox-projector.ts` skips Solr + GraphDB public projections for records held by sensitivity review.\n  - Coverage:\n    - `tests/utils/sensitivity.test.ts`\n    - `tests/utils/record-materializer.test.ts`\n    - `tests/utils/search-index.test.ts`\n    - `tests/services/outbox-projector.test.ts`\n- [x] ✅ Human-review hold policy for flagged records (restricted publication until disposition).\n  - Flagged records carry `_sensitivity.status = \"review_required\"` and `_sensitivity.holdPublication = true`.\n  - Held records are excluded from Solr/GraphDB syndication and flattened with `publication_status = \"held_for_review\"` plus no public `text_all`.\n- [x] ✅ Culturally sensitive knowledge handling rules integrated with rights/reuse UI and syndication controls.\n  - Cultural-sensitivity scan rules and syndication holds now flow into explorer DTOs as `held_for_review` records with explicit rights/reuse review labels.\n  - `RightsBadge` renders sensitivity labels as review-required publication holds, keeping cultural-sensitivity warnings visible anywhere rights/reuse chips appear.\n  - Coverage:\n    - `tests/components/linked-atomics.test.ts`\n    - `tests/utils/artwork-builder.test.ts`\n- [x] ✅ Security telemetry for sensitivity decisions (who approved, why, and when).\n  - Scanner telemetry records version, scan time, signal count, highest severity, and hold policy.\n  - Human disposition telemetry now requires reviewer identity, rationale, and timestamp before approval can clear a publication hold.\n  - Materialization preserves existing disposition telemetry during record rewrites, and reviewed records only return to public indexing after an approved disposition.\n  - Coverage:\n    - `tests/utils/sensitivity.test.ts`\n    - `tests/utils/record-materializer.test.ts`\n    - `tests/utils/search-index.test.ts`"},{"level":4,"heading":"6) Content credibility engine (trust/originality/distribution/consistency)","body":"- [x] ✅ Baseline credibility-engine policy document landed:\n  - `docs/content-credibility-engine.md`\n- [x] ✅ Trust-layer storage templates landed:\n  - `provenance/ledger.json`\n  - `provenance/source-map.yaml`\n- [x] ✅ Originality-layer storage template landed:\n  - `semantic-core/originality-index.json`\n  - baseline novelty threshold documented (`cosine_distance > 0.18`)\n- [x] ✅ Distribution/consistency scaffolding landed:\n  - `distribution/schedule.yaml`\n  - runtime queue path reserved at `distribution/queue.db` (gitignored)\n  - `generation/style-profile.md`\n- [x] ✅ Monitoring scaffold landed:\n  - `monitoring/metrics.json`\n- [x] ✅ Enforce citation-coverage gates in code for generated/publishable artifacts.\n  - `POST /api/content/generate` now returns `422` when computed citation coverage falls below threshold (`METAMUSEUM_CITATION_COVERAGE_THRESHOLD`, default `0.95`), including coverage diagnostics in response.\n  - `POST /api/wiki-drafts/[id]/publish` now runs explicit publish preflight and returns `422` with preflight diagnostics when citation coverage or other publishability checks fail.\n  - Evidence: `src/utils/citation-coverage.ts`, `app/api/content/generate/route.ts`, `src/services/wiki-publish.ts`, `app/api/wiki-drafts/[id]/publish/route.ts`, `tests/api/content-generate.test.ts`, `tests/quality/cite-or-refuse-conformance.test.ts`, `tests/services/wiki-publish.test.ts`.\n- [x] ✅ Enforce originality-score gates in code for generated/publishable artifacts.\n  - Originality scoring utility landed with policy-driven threshold + minimum unique-insight checks:\n    - `src/utils/originality-score.ts`\n  - `POST /api/content/generate` now returns `422` when originality score gates fail, including originality diagnostics in output payload.\n    - `src/services/agents.ts`\n    - `app/api/content/generate/route.ts`\n  - Wiki publish preflight now enforces originality gates before publishable status:\n    - `src/services/wiki-publish.ts`\n    - `app/api/wiki-drafts/[id]/publish/route.ts`\n  - Coverage:\n    - `tests/utils/originality-score.test.ts`\n    - `tests/api/content-generate.test.ts`\n    - `tests/quality/cite-or-refuse-conformance.test.ts`\n    - `tests/services/wiki-publish.test.ts`\n    - `tests/api/wiki-drafts/flow.test.ts`\n- [x] ✅ Add weekly credibility audit automation (drift + relevance + broken-link checks).\n  - Weekly audit orchestration script landed:\n    - `scripts/credibility-audit.ts`\n    - `src/services/credibility-audit.ts`\n  - Package commands:\n    - `pnpm credibility:audit`\n    - `pnpm credibility:audit:check`\n  - Weekly GitHub Action landed:\n    - `.github/workflows/credibility-audit.yml`\n    - uploads `artifacts/credibility-audit/latest.json`\n  - Coverage:\n    - `tests/services/credibility-audit.test.ts`\n- [x] ✅ Add queue worker implementation for multi-channel publish orchestration (web/linkedin/medium/email/api).\n  - Publish queue worker service landed with:\n    - schedule parsing from `distribution/schedule.yaml`\n    - durable queue state in `distribution/queue.db`\n    - per-channel delivery states, retries/backoff, dead-letter handling\n    - per-day channel cap deferral logic from schedule policy\n    - channel adapters for `web`, `linkedin`, `medium`, `email`, `api`\n  - Files:\n    - `src/services/publish-queue-worker.ts`\n    - `scripts/publish-queue-worker.ts`\n    - `distribution/README.md`\n  - Commands:\n    - `pnpm publish:queue:worker`\n    - `pnpm publish:queue:worker:once`\n    - `pnpm publish:queue:worker:drain`\n  - Coverage:\n    - `tests/services/publish-queue-worker.test.ts`\n- [x] ✅ Add OpenTelemetry metric/span conventions for trust/originality/distribution events.\n  - Shared conventions module landed with stable span/metric names plus common layer/event/kind/outcome attributes:\n    - `src/utils/otel-credibility.ts`\n  - Trust/originality gates now emit standardized spans/metrics:\n    - `src/utils/citation-coverage.ts`\n    - `src/utils/originality-score.ts`\n  - Wiki publish preflight/execute and distribution queue orchestration emit the same conventions:\n    - `src/services/wiki-publish.ts`\n    - `src/services/publish-queue-worker.ts`\n  - Coverage:\n    - `tests/utils/otel-credibility.test.ts`\n- [x] ✅ Add eval thresholds for engagement velocity and trust/originality regression alerts.\n  - Threshold evaluator landed for engagement velocity minimum plus trust/originality minimum and baseline-drop budgets:\n    - `src/services/credibility-eval-thresholds.ts`\n    - `config/credibility-eval-thresholds.json`\n  - Weekly credibility audit now evaluates and reports these alerts:\n    - `src/services/credibility-audit.ts`\n    - `scripts/credibility-audit.ts`\n  - Coverage:\n    - `tests/services/credibility-eval-thresholds.test.ts`\n    - `tests/services/credibility-audit.test.ts`"},{"level":4,"heading":"Highest ROI priority","body":"- [x] ✅ Implement OpenTelemetry before broader C4/C5 expansion to prevent distributed-debugging bottlenecks.\n\nMeta Wiki Art bridge implementation notes:\n- [x] ✅ See [meta-wiki-art-bridge.md](meta-wiki-art-bridge.md) for sequencing constraints, boundaries, and the staged publish flow.\n  - Sequencing constraints are documented under `## Sequencing constraints`.\n  - Bridge boundaries are documented under `## Boundaries (Out Of Scope For Era A/B)`.\n  - Staged publish flow is documented under `## Planned C5 flow`.\n\n**Era C exit gate:**\n- [x] ✅ Automated evidence pack landed for all four checks (artifact schema + nightly job + dated run history).\n  - Schema: `docs/schemas/era-c-exit-gate-evidence.schema.json`\n  - Policy: `config/era-c-exit-gate-policy.json`\n  - Script + artifacts: `scripts/era-c-exit-gate.ts`, `artifacts/exit-gate/`\n  - Trend index now carries compact failed-check reasons so agents can prioritize the next blocker without opening every historical artifact.\n  - Telemetry snapshot automation: `scripts/monitoring-telemetry-sync.ts` via `pnpm monitoring:telemetry:sync` (wired into `pnpm era-c:exit-gate:evidence` / `pnpm era-c:exit-gate:check`)\n  - Nightly workflow: `.github/workflows/era-c-exit-gate-evidence.yml`\n  - Nightly workflow now supports deployed-target evidence via `METAMUSEUM_EVIDENCE_BASE_URL`, `METAMUSEUM_EVIDENCE_IIIF_TILE_URL`, optional SPARQL/query vars, and matrix-first `METAMUSEUM_ACTIVITY_CONSUMER_IDS` (`METAMUSEUM_ACTIVITY_CONSUMER_ID` fallback); when target vars are missing it keeps the local `pnpm k6:slo:ci` fallback so automation still produces artifacts.\n- [x] ✅ Deployment-foundation preflight landed for controlled beta / production launch review.\n  - Commands: `pnpm launch:preflight`, `pnpm launch:preflight:production`\n  - Script + service: `scripts/deployment-preflight.ts`, `src/services/deployment-preflight.ts`\n  - Runbook: `docs/ops/deployment-preflight.md`\n  - Scope: verifies env/secrets, Postgres mode, uptime source, SLO target URL, fresh DR restore rehearsal, and staging-vs-production smoke-token posture before collecting exit-gate evidence.\n- [x] ✅ Launch review packet landed for controlled beta / production launch decision evidence.\n  - Commands: `pnpm launch:review`, `pnpm launch:review:check`, `pnpm launch:review:production`\n  - Script + service: `scripts/launch-review.ts`, `src/services/launch-review.ts`\n  - Runbook: `docs/ops/launch-review.md`\n  - Scope: aggregates latest preflight, Era C exit-gate, security audit baseline, DR drill, public-trust smoke, a11y evidence, and explore smoke evidence; production fails on missing/stale/red evidence while staging can warn for beta-only evidence collection.\n  - Evidence producers: `pnpm a11y:check` writes `artifacts/launch/a11y-latest.json`, and `pnpm smoke:explore:matrix` writes `artifacts/launch/explore-smoke-latest.json`.\n- [ ] ⚠️ Latest exit-gate status is **failed** (`2026-06-10T11:36:30.690Z`), but the artifact is now agent-actionable:\n  - SLO failures distinguish incomplete k6 summaries from actual p95 threshold breaches via `missingMetricsInWindow` and per-sample `metricDetails`.\n  - Uptime failures include evidence `source` and `notes`.\n  - Activity adoption credits only declared external consumers with `class: \"declared\"` and `declaredId`.\n  - KPI failures include source metadata, snapshot notes, and per-failed-metric source/reason details.\n- [ ] All SLOs in SOTA §20.4 met at p95 over a 30-day window.\n  - Evidence contract now requires all five p95 SLO metrics: cached Record, cold Record, keyword+facet search, whitelisted SPARQL, and IIIF tile serving.\n  - SLO evidence artifacts now include missing-metric summaries and per-sample metric details so incomplete k6 runs are actionable separately from threshold breaches.\n  - Nightly workflow hardening can now collect complete deployed-target samples once GitHub vars provide the app base URL, IIIF tile URL, and whitelisted SPARQL inputs.\n  - Current blocker: retained k6 history has only `3/30` samples and those samples are legacy three-metric summaries missing whitelisted SPARQL and IIIF tile p95 values.\n- [ ] 99.9% uptime on public read.\n  - Uptime gate now rejects stale or undated availability snapshots; `uptime.maxSnapshotAgeHours` defaults to `48` so the 30-day proof must be continuously refreshed.\n  - Uptime evidence artifacts now surface source (`prometheus`, `probe`, `unavailable`) plus notes, making missing public-read proof actionable without opening telemetry snapshots.\n  - Current blocker: uptime source is `probe`, availability is `1`, and `sampleCount30d` is `3`; continue scheduled probes until the 30-sample window is met.\n- [ ] ≥ 3 external Linked Art systems consume the `/api/activity` feed.\n  - Exit-gate adoption evidence now credits only declared external consumers via `x-linked-art-consumer-id`; derived fingerprints remain diagnostic and cannot satisfy the gate.\n  - Evidence ingestion preserves `class` + `declaredId` from `storage/activity-consumers.json`, so declared external consumers can satisfy the gate when real adoption arrives.\n  - Activity adoption proof tooling now rejects placeholder/local IDs by default, probes `/api/activity` + `/api/activity/readiness`, and writes dated single-consumer + matrix artifacts under `artifacts/activity-adoption/`.\n  - Current blocker in the latest published artifact: declared external consumers are `0/3`; run `pnpm activity:adoption:matrix` with three partner-owned consumer IDs against the deployed target after those consumers are onboarded.\n- [ ] KPIs in SOTA §26 hit.\n  - KPI gate now rejects stale or undated KPI snapshots and requires real AI query cost telemetry when `kpis26.requireAiQueryCostTelemetry` is enabled; fallback default cost values remain diagnostic only.\n  - KPI evidence artifacts now include source metadata, snapshot notes, and per-failed-metric source/reason details so SOTA §26 blockers are actionable without opening telemetry inputs.\n  - KPI telemetry sync now accepts `monitoring/kpi-evidence.json` (or `METAMUSEUM_KPI_EVIDENCE_PATH`) for aggregate production record-enrichment and reconciliation-review counts; invalid sections are ignored instead of creating false-green metrics.\n  - AI query telemetry now logs `costUsd`, `costCurrency`, `costSource`, and usage counts per query; the deterministic local planner records `costUsd: 0` with `costSource: \"deterministic-local-planner\"` instead of relying on fallback KPI defaults.\n  - Nightly workflow now seeds one deployed `/api/ai/query` request before telemetry sync when `METAMUSEUM_EVIDENCE_BASE_URL` is configured.\n  - Current blockers in the latest published artifact: `dataQualityEnrichedShare`, `reconciliationAutoApproveRate`, and `reconciliationPrecisionReviewed`; AI query cost telemetry is now sourced and within policy, so the remaining KPI work is production enrichment/reconciliation evidence.\n\n---"}]}