{"title":"Meta Museum Roadmap","path":"docs/roadmap.md","generatedAt":"2026-07-12T00:09:43.643Z","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 July 7, 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.9` |\n| React | `19.2.4` |\n| App page files | root homepage + `33` non-root page files (`34` total) |\n| API route handlers | `146` `app/api` route handlers |\n<!-- END:PROJECT_STATS -->\n\n- [ ] ⚠️ **Strict 10/10 readiness is not green yet**: `pnpm review:goals:check` still reports `status: external-evidence-required`, `local gate status: passed`, and `strict 10/10 gate status: failed`, but stale deployment proof is no longer the story. GitHub CI is green, and the July 4 production preflight passes `20/20` when the non-secret rotation timestamp and rotated-key list are supplied: Postgres storage, `sslmode=verify-full`, live Neon authentication, active Vercel deployment freshness after secret rotation, auth routes, public-read reachability, validation/reconciliation Render `/health` probes, IIIF tile reachability, DR drill freshness, no production smoke override token, `security.secretRotation`, and `security.secretHistory` all pass. Live probes against `https://www.metamuseum.org` return `200` for `/api/health`, `/api/records`, and `/api/auth/signin`, while `/agents` redirects public users to sign-in. The refreshed production launch-evidence packet passes `8/9`, launch review passes `7/8`, and the remaining launch-review failure is the Era C real-world evidence row. The latest review-goals handoff reports `4` deployment-environment blockers plus `19` real-world-evidence blockers for deployed Render service probes, clean 30-day SLO/uptime windows, ActivityStreams `Delete` type coverage after `3/3` real external consumers and restored `3/3` durable callback rows, partner-confirmed `Update`, paid-pilot entitlement/activation/support-load/KPI proof, retention, and gross-margin evidence; Te Papa first outreach is now recorded, but no reply or paid-pilot proof exists yet. The refreshed 10/10 tracker is [`docs/roadmap-to-10.md`](roadmap-to-10.md).\n- [ ] ⚠️ **Strict real-world lane:** the real-world lane still has `19` blockers, and the highest-leverage remaining ActivityStreams unblocker is real `Delete` evidence. The first reviewed metadata `Update` row is live in production from a real Met object `437133` metadata delta, and MetaHistoryBook re-read it as `metahistorybook-harvester-prod` at `2026-07-05T19:50:30Z` with `totalItems: 1`, real non-synthetic `source.kind: reviewed-update`, conformant projections, and the shared `Q432253` reconciliation. `pnpm providers:coverage:seed` now brings the provider tombstone-watch corpus to all `14/14` source-provider lanes by seeding Harvard from a public object page and Europeana from its read-only Record API status surface, with `0` skipped and `0` failed rows. MoMA is tracked separately as an open-data snapshot provider: `pnpm moma:dataset:diff` compares explicit Artworks snapshots and writes review-required removal candidates without creating ActivityStreams `Delete` readiness. The `Delete` side remains intentionally blocked because the latest provider-aware `pnpm activity:tombstone:scan` run considered `126` stored records at `2026-07-10T01:07:06.186Z`, scanned `68` unique upstream targets across all `14` source providers, skipped `5` unknown/local records, collapsed `53` duplicate Met targets, and found `0` real upstream `404`/`410` tombstones. Next strict step: send a separate `Delete` deploy-note only after a genuine tombstone appears. The same lane still needs the invoice-backed pilot, 30-day SLO/uptime, KPI, retention, and gross-margin proof.\n- [x] ✅ **Latest local verification:** ActivityStreams syndication evidence, provider tombstone-watch evidence, review-goals local gate, and focused review/readiness tests pass for this evidence refresh; the remaining strict blockers are real-world proof, not stale callback, dataset, or local-readiness artifacts. The 10/10 tracker has a drift guard against the latest `artifacts/review-goals/review-goals-latest.json` strict-handoff counts so stale blocker totals are caught in tests.\n- [x] ✅ **B6.1 equivalent-URI reconciliation proof:** exhibition/literature candidates now count Linked Art `equivalent[]` identity hints on works and embedded participants as identifier/participant overlap while preserving full opaque URI evidence. Compact Wikidata, OpenLibrary, and Getty keys are derived only from recognized authority URI/CURIE shapes, and `tests/quality/reconciliation-exhibitions-literature.test.ts` guards arbitrary URI paths from being promoted to authority identifiers.\n- [x] ✅ **Durable callback proof guard:** `pnpm activity:subscriptions:guard` now runs inside `pnpm activity:syndication:evidence`, verifies the three accepted partner callback rows plus their non-placeholder callback proof artifacts, preserves the current zero rejected subscriptions state, and is called explicitly by the nightly Era C evidence workflow. The durable consumer/subscription ledgers and callback proof JSONs are intentionally trackable while the rest of the generated ActivityStreams artifact directory stays ignored.\n- [x] ✅ **Strict handoff public-doc drift guard:** documentation drift tests now compare the latest review-goals strict handoff counts against the README, roadmap, and roadmap-to-10 summaries, so total production-proof blockers and deployment/real-world lane counts cannot quietly diverge after evidence refreshes.\n- [x] ✅ **Deterministic local test gate:** `pnpm test` and `pnpm test:coverage` now pass Node's test runner `--test-concurrency=1` after repeated full-suite runs exposed order-sensitive conformance files that passed in isolation and under the serial quality subset. This preserves the canonical green gate while the route-level conformance suites continue to exercise process-local env/fetch/storage behavior.\n- [x] ✅ **Core stack and runtime**: generated project stats above provide the current Next.js and React pins; TypeScript strict + custom CSS remain the runtime baseline. Latest pilot outreach reply/evidence guard passed focused evidence/outreach/activation/support operator/service/offer/page/storage/docs/exporter checks (`70` tests / `13` suites), final `pnpm test` (`1103` tests / `310` suites), `pnpm lint`, and `pnpm build`; `/pilot` now renders a typed zero-complete activation evidence ledger with the six canonical seven-day pilot milestones, while `src/services/pilot-outreach-events.ts`, `src/services/pilot-activation-events.ts`, `src/services/pilot-support-issues.ts`, and `src/services/pilot-evidence-packet.ts` record sequence-guarded exact-account outreach, ordered exact-tenant activation events, chronology/status/identity/severity/resolution-evidence-guarded support load, explicit blocked/ready pilot packet artifacts, blocker-first Markdown packet summaries, open-blocking-support readiness blockers, and overdue open support-response blockers, `pnpm pilot:outreach`, `pnpm pilot:activation`, `pnpm pilot:support`, and `pnpm pilot:evidence --markdown` let operators append, package, and share real dated evidence without hand-editing JSON, and the runbook forbids marking outreach, activation, support, or packet readiness complete from demo, fixture, smoke evidence, reply claims without prior sent evidence, replies dated before `sentAt`, sent follow-up dates that predate `sentAt`, tenant-mismatched activation evidence, later activation milestones without prior tenant evidence, support response deadlines before `openedAt`, support issue `requester`, `summary`, `openedAt`, or `severity` rewrites, resolved support issue `resolvedAt` or `resolutionSummary` rewrites, support resolutions before `openedAt`, or open support issues carrying resolution evidence. Tenant RBAC evidence still proves signed-in members cannot use sibling active-org cookies to read sibling record lists/details, write records into sibling/default storage, read/review/publish/write sibling wiki draft state, read/review/triage/write sibling annotation queues, read/write sibling AgentTask history, or steer scoped AI/editorial record-read outputs away from their authenticated org; proving signed-in non-members cannot use a cookie-selected org to read or mutate records, annotation, wiki draft, or scoped AI/editorial record-read outputs; and proving signed-in non-admin roles cannot use org administration or support-adjacent routes to list orgs, add memberships, create/revoke invites, or export org audit packets. Gated pilot API routes return stable `429`/`Retry-After` denials before parsing or work execution without changing monthly quota denial semantics. The membership-validated `metamuseum.activeOrgId` cookie drives shared preview, request-storage, and pilot route-gate scope without weakening test override or explicit `?tenantId=...` compatibility precedence. The workspace shell renders sanitized active-org status, org id, accessible-org count, storage scope, membership role, selector, stale-selection warning, and an `/orgs` switch/manage affordance without exposing invite token material. Workspace records, explore, entities, entity detail, artwork detail, graph, and patterns pages derive preview storage from authenticated org session scope when no compatibility `?tenantId=...` is supplied, while explicit tenant preview links still win for controlled pilot URLs. Manual pilot entitlements can bind to authenticated org ids while route usage gates prefer the selected active org for entitlement lookup and post-success counter writes before compatibility tenant headers. `/orgs` can render the operator console while admin-only org routes create/list orgs, memberships, sanitized invites, invite revocation, invite acceptance, form posts, org-scoped audit rows, and org audit export packets while Auth.js org scope continues to resolve from managed org memberships/invites before compatibility env mappings or pilot tenant headers. Wiki draft create/list/detail/review/publish access, live-publish sync-map artifacts, reverse-ETL state, and tenant preview pages remain scoped while unscoped public requests cannot see scoped tenant records or artifacts. Provider facade/direct provider/explorer/Linked Art import writes, public browse reads, records, jobs, AgentTask, annotation, audit, activity route storage, wiki draft storage/access routes, wiki sync-map routes, and tenant-scoped export/DR managed documents remain isolated. The temporary `metagenauto/` AG2 reference repo has been distilled into `docs/agents/ag2-extraction-notes.md` before removal from the app tree.\n- [x] ✅ **Product surface**: generated project stats above provide the current page and API handler counts; the live surface includes 14 provider integrations, ARK resolver, provider facade/readiness/capabilities, standards APIs, ActivityStreams, OpenAPI/docs, worker status API, AI query/chat/evals, org admin/invite APIs, org audit export, `/api/orgs/active` active-org selection, the `/orgs` operator console for org provisioning, memberships, invites, and revocation, `/workers` operator health for projection/publish drain lag, next cron wakeup, effective drain time, and disabled modes, workspace-shell active-org status with selector, stale-selection warning, and switch/manage affordance, persisted AgentTask review history, wiki publish/sync, annotations, public trust, IIIF routes, a larger rotating midnight/navy rain-blue home hero with bright lemon-yellow Meta Museum highlights, top-padded full-color unfiltered contained artwork, a verified Met/CMA/AIC clean-deploy fallback when local image-backed imports are absent, a centered non-overlapping navy artwork info panel, a white hero label, three source-backed real-artwork pathway cards with provider/maker/date/rights/link metadata instead of AI placeholder images, a more readable artwork detail facts panel with metadata-forward desktop proportions and full-width long source fields, a HAL-powered “Related” panel on entity and artwork detail pages for followable Linked Art relation searches, a full-width “Live source network” homepage band that frames the current project as a source-backed workbench with numbered source metrics, provenance, rights/attribution context, citation checks, and editor-gated review signals, a `/projects` product case-study surface with a short “what matters in 90 seconds” evaluator brief, hidden technical proof disclosures, walkthrough video, proof strip, live route links, architecture evidence links, and an honest strict-readiness note, and the `/pilot` Managed Linked Art Launch Pilot offer page with shared primary/footer navigation access, named outreach status ledger, zero-complete activation evidence ledger, and validated operator flows for outreach and activation evidence.\n- [x] ✅ **External evidence ledger**: `/evidence` and `/api/evidence/ledger` now give evaluators a compact public proof surface over partner-confirmed `Update`, durable callback proof, MetaHistoryBook's FAIR/LOUD dataset reuse confirmation, the provider tombstone watch, 30-day SLO/uptime progress, and invoice-backed pilot blockers. `/api/evidence` is a compatibility alias and `/en/evidence` is an explicit locale route for external readers following locale redirects. It deliberately renders `Delete` as pending genuine upstream `404`/`410` evidence rather than treating an empty `type=Delete` feed as a defect to paper over, the production API falls back to public docs when generated artifact JSON is not bundled, and production responses include live probes for the current `Update` feed, Update `linkedArt.id` dereference into `/api/events/...`, intentionally empty `Delete` feed, dataset profile headers, and NDJSON export integrity. The ledger also publishes a tombstone-watch history from retained scan-run artifacts so repeated 14-provider scans are visible over time, not just the latest no-tombstone snapshot. `pnpm evidence:ledger:probe:check` now retains the live ledger result as `artifacts/evidence-ledger/evidence-ledger-probe-latest.json` plus timestamped run artifacts, the nightly Era C workflow captures/uploads those artifacts, and `/readiness` surfaces the latest probe as an operator source.\n- [x] ✅ **Museums Victoria provider slice (2026-07-04)**: added an item-first Museums Victoria Collections API provider with bounded `recordtype=item` search, object fetch, import, `/explore` source toggle, provider facade routes, media-rights preservation, First Peoples/cultural-context review notes, rights-map coverage, and generated pass/fail fixtures. Articles, species, and specimens remain intentionally excluded until separate semantic mapping exists. Provider notes: [docs/providers/museumsvictoria-collections-api.md](providers/museumsvictoria-collections-api.md).\n- [x] ✅ **Portfolio product polish controls**: [`src/services/portfolio-product-polish-controls.ts`](../src/services/portfolio-product-polish-controls.ts) and [`tests/services/portfolio-product-polish-controls.test.ts`](../tests/services/portfolio-product-polish-controls.test.ts) now keep the `/projects` case study, product walkthrough video, standalone overview HTML, public docs freshness metadata, shared navigation, and strict-readiness copy boundary in one executable report.\n- [x] ✅ **Operator readiness page**: `/readiness` now condenses existing ignored readiness artifacts into one editor-gated status view with local-vs-strict claim boundaries, source freshness, blocker rows, and next remediation commands. It also renders the generated acceptance ledger from long-window maintenance, ActivityStreams community-outreach, ActivityStreams partner-pack, paid-pilot buyer-pack, FAIR/LOUD dataset reuse, evidence-ledger live probes, and SOTA KPI artifacts, plus a dataset reuse source card for MetaHistoryBook's external confirmation of dataset discovery, checksum integrity, export pagination, object-level `equivalent[]`, Wikidata reconciliation, and license-scope clarity. The strict readiness audit source card now shows the `strictGateHandoff` blocker, next-action, command, artifact, and requirement counts, while the page also exposes the `review-goals.nextActions[]` strict handoff as an action queue grouped by execution scope with deployment-environment and real-world proof lane totals before row-level commands, so local gates, optional evidence rows, outreach context, repair hints, executable strict actions, and strict production proof remain visibly distinct.\n- [x] ✅ **Operator stale-preflight guard**: `/readiness` now fails production deployment-preflight source rows when the latest artifact is missing current selected secret evidence checks (`security.secretRotation`, `security.secretHistory`), even if the stale artifact summary says `pass`, and adds `pnpm launch:preflight:production` to the next-command list.\n- [x] ✅ **Readiness mismatch guard**: `/readiness` preserves local-gate and strict-gate proof booleans from review-goals artifacts, renders strict production proof as “not proven” whenever external evidence is still required, and downgrades any global strict-gate pass when the five real-world evidence contracts are not independently satisfied.\n- [x] ✅ **Strict evidence loop visibility**: `/readiness` now surfaces the review-goals handoff as three distinct evidence lanes — local refresh, deployment-environment proof, and real-world production proof — with blocker counts, commands, requirements, and artifact targets kept separate so local gate confidence cannot be presented as strict readiness. It also renders five strict external evidence contracts for 30-day SLO/uptime, ActivityStreams adoption, invoice-backed paid pilot, pilot retention, and gross-margin proof; each contract rejects local substitutes, names the source and acceptance-ledger rows it depends on, and remains failed until its own real-world evidence lands. Production launch-packet a11y remediation now points operators at `BASE_URL=https://<deploy-host> pnpm a11y:check` in the deployment-environment lane, so a stale embedded artifact command cannot look like a local-only fix.\n- [x] ✅ **Evidence script ownership**: high-churn launch, review-goals, long-term, ActivityStreams, pilot, continuity, AI-eval, worker, and governance commands now have a typed owner registry plus a regression guard, with the human handoff at [`docs/ops/evidence-script-ownership.md`](ops/evidence-script-ownership.md). The ActivityStreams owner row explicitly covers `pnpm activity:subscriptions:guard` so durable callback verification cannot drift outside the accountable evidence lane. Package-script namespaces now also have owner, preferred-entry-point, and pruning-rule controls so new `namespace:*` command groups cannot appear unclassified.\n- [x] ✅ **Operations risk controls**: [`src/services/operations-risk-controls.ts`](../src/services/operations-risk-controls.ts) and [`tests/services/operations-risk-controls.test.ts`](../tests/services/operations-risk-controls.test.ts) now summarize and regression-check the four recurring operations risks: the large API route surface with first-level route-family owner/review-lane classification, package-script namespace classification plus high-churn script ownership, mixed Vercel/Neon/Render/Redis/Solr/GraphDB topology, and schema/freshness/retention controls for readiness artifacts.\n- [x] ✅ **Turbopack runtime-file warning cleanup**: runtime-only artifact, storage, SHACL, schedule, and queue file reads now use [`src/utils/runtime-fs.ts`](../src/utils/runtime-fs.ts), so `pnpm build` keeps those dynamic paths out of static file tracing and finishes without broad file-pattern warnings.\n- [x] ✅ **Public docs metadata freshness**: `/api/docs/manifest` now reports response `generatedAt` separately from declared source `sourceUpdatedAt` plus `sourceUpdatedDoc` latest-source checksum metadata, keeping Vercel bundle mtimes or response timing from being mistaken for source-document currency.\n- [x] ✅ **OKF / LLM wiki seed**: [docs/knowledge](knowledge/index.md) now starts a project-local Open Knowledge Format-style bundle with a root index, append-only log, source summaries for the OKF announcement and LLM Wiki pattern note, concept pages for OKF, the LLM Wiki pattern, and the Meta Museum knowledge system, plus a maintainer schema that tells agents how to ingest, query, and lint the wiki. `pnpm okf:check` validates that reserved `index.md`/`log.md` files stay separate from typed concept documents with non-empty `type` frontmatter.\n- [x] ✅ **Linked Art HAL reference capture**: [linked-art/api/hal.md](linked-art/api/hal.md) now records the `_links` boundary, CURIE/version/alternate/collection rules, the detailed high-priority relation-search notes captured so far, and the complete 95-name upstream relation inventory extracted from the cloned `linked.art/scripts/hal_links.tsv` mirror. The doc keeps the current implementation boundary explicit: semantic activity/concept/object-part/object-to-work/production-influence/group-formation/custody/encounter/ownership/production/set-creation/work-aboutness/representation/work-creation/work-publication/place-activity/active-place data is preserved; entity-role responses now expose followable usage-indexed named HAL relation links for `la:objectProducedByAgent`, `la:objectOwnedByAgent`, `la:objectCuratedByAgent`, `la:objectMemberOfSet`, `la:objectPartOfObject`, `la:objectCarriesWork`, `la:objectShowsWork`, `la:workAboutAgent`, `la:workRepresentsAgent`, `la:workAboutOrRepresentsAgent`, `la:activityCarriedOutByAgent`, `la:activityUsedObject`, `la:activityTookPlaceAtPlace`, `la:groupFoundedByAgent`, `la:objectEncounteredByAgent`, `la:agentActiveAtPlace`, `la:objectProductionInfluencedByAgent`, `la:objectProductionInfluencedByObject`, `la:objectProductionInfluencedByPlace`, `la:objectProductionInfluencedByWork`, `la:setCreatedByAgent`, `la:conceptInfluencedByObject`, `la:workCreatedByAgent`, and `la:workPublishedByAgent`; `/api/relations` now exposes the supported relation definition catalog with domain/range/search-template metadata; and `/api/relations/{relation}` pages carry relation/current/return-class/conformance/partOf metadata rather than a bare result list. Full relation breadth for work/object/set/place/concept variants beyond the currently indexed subset and other upstream relation families remains next implementation work with failing-first relation tests. The Linked Art inspector also warns when dereferenceable embedded activity/shared-structure IDs omit `_complete: false`.\n- [x] ✅ **HAL relation explorer UI**: entity and artwork detail pages now reuse those followable `la:*` relation links in a visible “Related” panel, exposing produced-object, work-aboutness/representation, owner/curator, object-part, and place-activity searches as clickable `OrderedCollectionPage` API affordances instead of keeping relation-search value hidden in JSON only.\n- [x] ✅ **Linked Art relation-search expansion slices**: `activityCarriedOutByAgent`, `activityUsedObject`, `activityTookPlaceAtPlace`, `groupFoundedByAgent`, `objectEncounteredByAgent`, `agentActiveAtPlace`, `objectCarriesWork`, `objectShowsWork`, `objectProductionInfluencedByAgent`, `objectProductionInfluencedByObject`, `objectProductionInfluencedByPlace`, `objectProductionInfluencedByWork`, `setCreatedByAgent`, `conceptInfluencedByObject`, `workAboutOrRepresentsAgent`, `workCreatedByAgent`, and `workPublishedByAgent` now have registry metadata, sparse POST preservation where needed, entity-index extraction for top-level and embedded activities, formation actors, encounter actors, agent active-place references, physical object `carries`/`shows` object-to-work evidence, production influences, set creation, concept influence, and work aboutness/representation/creation/publication evidence, `/api/relations` discovery, followable entity `_links`, and `OrderedCollectionPage` tests proving Activity/Event, Group, HumanMadeObject, Agent, Place, Set, Type, and Work return-class behavior.\n- [x] ✅ **Linked Art provenance search surface**: `/api/provenance` now provides a provenance-specialized Linked Art Search API page over stored provenance wrappers, with `kind=acquisition`, `kind=custody`, `kind=transfer`, `kind=move`, and `kind=right-acquisition` filters plus optional `object=` filtering. Responses include `provenanceKind`, `conforms_to`, `partOf`, `la:provenanceProfile`, source object summaries, wrapper activities, and matching `part[]` entries so acquisition, custody, indeterminate transfer, movement, and rights-acquisition evidence stays distinct at discovery time. Evidence: [`tests/api/provenance.test.ts`](../tests/api/provenance.test.ts).\n- [x] ✅ **HAL entity envelope upgrade**: shared entity/record HAL responses now emit `self`, a templated Linked Art `la` CURIE, `la:activityFeed`, and link-object `la:apiVersion`/`la:modelVersion` entries with `href` and `name`, replacing the older string-only version metadata while keeping HAL out of the semantic graph payload.\n- [x] ✅ **FAIR/LOUD dataset publication surface**: `/api/datasets` now publishes a machine-readable DCAT/DataCite/VoID-style JSON-LD dataset profile with dataset id, version, checksum, license, update cadence, distributions, and canonical identifier policy. `/api/datasets/exports/records?format=jsonld|ndjson` provides downloadable versioned record envelopes with per-record rights/provenance packaging and object-level `equivalent[]` links projected from stored equivalents, provider URLs, public object pages, and Wikidata entity URLs; `/api/providers/capabilities` advertises those dataset distributions for follow-your-nose discovery; dataset responses expose id/version/distribution/checksum/license headers for integrity checks without body parsing; `/api/datasets` states that CC BY 4.0 governs export packaging/metadata while per-record rights govern object/content/media reuse; `/datasets` links the machine-readable profile and exports; and [linked-art/canonical-identifier-policy.md](linked-art/canonical-identifier-policy.md) documents persistence, redirects, content negotiation, ARK/DOI strategy, reconciliation equivalents, and license scope. MetaHistoryBook re-read dataset version `2026.07.06+sha256.43eb7f0049c9` and confirmed `53/54` non-empty `record.equivalent[]` rows, `47/54` Wikidata rows, checksum validity, pagination/discovery, and license-scope clarity, converting the dataset reuse package from self-verified to partner-verified evidence. This improves reuse packaging without treating the remaining real-world Delete or 30-day SLO/uptime evidence as complete.\n- [x] ✅ **Performance scale controls**: [`src/services/performance-scalability-controls.ts`](../src/services/performance-scalability-controls.ts) and [`tests/services/performance-scalability-controls.test.ts`](../tests/services/performance-scalability-controls.test.ts) now exercise cold-record SLO miss/depth behavior, Solr/GraphDB projection enablement thresholds, bounded cron backlog escalation, and provider cache/rate-limit/validation-drift safeguards.\n- [x] ✅ **Calendar-auditable long-window evidence**: `pnpm longterm:evidence` and `pnpm longterm:evidence:maintenance` now include observed and missing UTC days for SLO and public-read uptime windows, and the maintenance Markdown names missing days so 30-day readiness cannot be inferred from clustered or opaque sample totals. The maintenance report also carries SLO trend-intake diagnostics, grouped repair-queue rows for retained SLO metric failures and public-read uptime path failures, row-level and per-failed-sample age-out dates, acceptance rows for SLO sample depth, SLO failure-free status, uptime observation depth, uptime availability, ActivityStreams consumer depth, activity-type coverage, and strict long-term packet refresh; `/readiness` shows the repair-queue count without treating it as proof. The latest July 6 long-window maintenance artifact has `20/30` retained deployed SLO samples across `7/30` observed UTC days, `13/30` passing SLO samples across `6/30` passing days, `7` failed/incomplete retained SLO rows, and SLO trend intake of `23` raw timestamped rows with `20` inside the report window and `3` older than the window. Public-read uptime has `117` retained probe snapshots with `0.9744` availability across `9/30` observed days, and the last retained failed SLO/uptime sample ages out by `2026-07-28T20:28:03.851Z`; daily passing-depth evidence is ready no earlier than `2026-07-29T09:15:00.000Z` if every future sample passes and adoption evidence lands. Review-goals now derives long-term blocker detail from retained sample counts, failed/incomplete sample counts, distinct-day coverage, uptime availability, and missing ActivityStreams activity types, so the strict handoff cannot hide behind aggregate sample totals.\n- [x] ✅ **Scheduled public-read probe contract**: `.github/workflows/era-c-exit-gate-evidence.yml` now defaults `METAMUSEUM_PUBLIC_READ_BASE_URL` to `https://www.metamuseum.org` and runs `pnpm era-c:exit-gate:public`, so nightly Era C packets require production probe-backed uptime instead of inheriting local or variable-only telemetry.\n- [x] ✅ **CI readiness workflow repair**: GitHub build/smoke jobs now set a CI-only dummy `AUTH_SECRET` so the production auth-secret throw remains intact while clean runners can compile and start Next, env-loader tests isolate inherited CI secrets before checking local file assignment, the standalone public-trust smoke filter watches auth/workflow changes, review-goals tests assert evidence-loop invariants from generated reports and tolerate both local-present and clean-runner-missing artifact blocker shapes, ActivityStreams adoption controls keep ignored generated partner-pack outputs as handoff targets without requiring them to exist in a clean checkout, the a11y smoke treats expected `/agents` and `/automation` Auth.js redirects as protected-route passes, and the Era C evidence workflow force-adds only its explicit rolling-evidence allowlist.\n- [x] ✅ **SOTA §26 KPI export path**: `pnpm monitoring:kpi-evidence` writes current-environment `monitoring/kpi-evidence.json` from record-enrichment and reconciliation decision counts, `pnpm monitoring:kpi-evidence:production` now loads repo env files before requiring Postgres storage and production source labels, real reviewed/accepted auto-link counts must be supplied together, and reviewed precision now also requires `--reviewed-precision-source` naming production review evidence before it enters the KPI snapshot. The KPI enrichment counter now counts unique recognized authority references rather than authority source families; the July 4 local export writes `1/5` ready acceptance rows, `0/4` ready production capture rows, `2` KPI metric blockers, and `diagnostics.handoffSummary.status: needs-production-evidence`; the record denominator is present, local enrichment is `52/57` (`0.9123`) against the `46/57` threshold, reconciliation candidates remain `0`, and reviewed precision still needs real reviewed/accepted auto-link counts plus a production review source. The handoff now separates strict source-shape work from metric work: `nextCommand` asks for the production KPI export, while `nextMetricCommand` points directly at production reconciliation distribution capture with `--auto-link`, `--weekly-digest`, `--human-review`, and `--drop-candidate`; `/readiness` shows both the metric-specific command and the required metric evidence text in the SOTA KPI source card. The export includes acceptance rows for production record export, enrichment denominator, production reconciliation export, reviewed precision counts/source, and strict Era C refresh readiness, plus KPI diagnostics with metric previews, `diagnostics.evidenceNeeds`, a capped `sampleRecordGaps[]` enrichment repair sample for under-authoritied record IDs, each sample's existing recognized authority URIs and suggested Linked Art fields, production-only `diagnostics.capturePlan` rows, and `diagnostics.handoffSummary` so the source-shape and metric next evidence are visible without inferring them from the failed gate.\n- [x] ✅ **ActivityStreams onboarding ledger**: `pnpm activity:partner-pack` now gives each requested consumer copy-ready outreach text, a partner response template, an evidence checklist, and structured acceptance rows for declared-feed-read, activity-type coverage, durable-subscription, callback-verification, and strict-gate-refresh proof. The placeholder pack keeps `consumerIds: []` and `pending-consumer-id` rows, so outreach scaffolding cannot count as strict adoption evidence. `pnpm activity:syndication:evidence` emits `consumerOnboarding.rows` with feed-read, durable-subscription, callback-verification, blocker, next-action, and evidence-ref fields so real external adoption can be tracked consumer by consumer.\n- [x] ✅ **Linked Art-native activity stream lift (2026-07-04, activity-class/provenance coverage lifted 2026-07-06)**: `/api/activity` now embeds a `linkedArt` JSON-LD projection on every ActivityStreams item, extracts semantic record events from `produced_by`, `created_by`, `modified_by`, `destroyed_by`, `removed_by`, `encountered_by`, `formed_by`, `dissolved_by`, `born`, `died`, title/custody transfer, and `used_for` properties, emits conservative `record-backfill` `Create` activities for otherwise uncovered stored records, supports `type`, `source`, `provider`, `object`, `since`, `until`, and cursor sync filters, keeps offset `nextPage` compatibility, defaults absent `limit` to a 25-item page, falls back to offset `next` links instead of emitting invalid cursors, and exposes object-scoped feeds at `/api/activity/object/{encodedObjectId}`. Linked Art `Destruction` and `Dissolution` stay semantic `Update` feed rows, so the strict ActivityStreams `Delete` lane remains reserved for real upstream `404`/`410` tombstones; dereferenceable activity IDs preserve source-supplied `_complete: false`; `Set` records emit `Creation`/`created` rows instead of physical `Production` rows, while record detail keeps `member_of` links on member records instead of inventing inverse `member[]` lists on the Set; `Birth`/`Death` projections suppress disallowed `carried_out_by` and `used_specific_object` fields; and provenance wrapper activities preserve names, classifications, descriptions, relative `before`/`after` ordering, and bundled `part[]` entries such as `Encounter`, `Acquisition`, indeterminate `Transfer`, `RightAcquisition`, `Move`, and `Payment`, including find/rediscovery encounters, explicit rights establishment/invalidation, percentage ownership shares, copyright rights, unknown-transfer evidence without title/custody assertions, multiple-owner, agent-carried acquisition, exchange, custody-transfer loans, movement origin/destination places, theft/loss custody changes, commission/service payment details, auction event classifications, auction-of-lot identifiers, lot `Set` `used_specific_object` values, `part_of` auction links, provenance purchases `caused_by` the lot auction, exhibition venue/organizer metadata, exhibition-object `Set` links, concept influence, and travelling-exhibition `part_of` links. After MetaHistoryBook partner feedback, `/api/activity/collection` and `/api/activity/page/{page}` now add a first IIIF Change Discovery-compatible walk-back surface with a mandatory collection `last` link, fixed ascending `OrderedCollectionPage` resources, thin default activities, valid UTC page `endTime` values, dereferenceable MetaMuseum `object.id` URLs for record-backed rows, opt-in `embed=linked-art`, omitted absent `prev`/`next` links, `object.equivalent[]` reconciliation hooks synthesized from stored equivalents, provider URLs, and object Wikidata URLs, and declared-consumer telemetry for walk-back harvesters while preserving the cursor API as an extension. The partner test-ID rerun structurally passed; the first production-ID run then passed ordering, real timestamps, and dedupe but exposed the source-equivalent gap, so record detail responses now project both bare and HTTP stored IDs as canonical `/api/records/{encodedSourceRecordId}` URLs with `_links.self.href` alignment and source/Wikidata equivalents for strict Linked Art consumers. The July 5 `metahistorybook-harvester-prod` rerun passed all four validation points and is filed as first production-grade external-consumer harvest evidence; Daily's `daily-metahistorybook-prod` app read is filed as a second distinct product consumer and its callback is verified as the first durable subscription row; Wikidata Explorer's `wikidataexplorer-metamuseum-prod` walk-back read is filed as the third distinct external consumer with `53/53` Met `Create` activities and `47/53` QID mappings, and its callback is now verified by a real Met `Create` POST returning `202` plus a GET ledger showing QID `Q29385853`. The adoption matrix and partner onboarding pack now require a broad read plus explicit `type=Create`, `type=Update`, and `type=Delete` reads for each partner ID, keeping future partner evidence aligned with the strict activity-type coverage gate. Reviewed record metadata now emits real `reviewed-update` rows, with Met object `437133` live in production and partner-confirmed by MetaHistoryBook at `2026-07-05T19:50:30Z`; `pnpm activity:tombstone:scan` keeps a latest/run artifact for real upstream `404`/`410` monitoring and `pnpm activity:tombstone:evidence` still creates deletion rows only from real upstream `404`/`410` evidence refs. MetaHistoryBook signed callback proof is accepted with a real production `Create` Activity POST returning `204`, bringing durable callback evidence to `3/3`; strict proof now needs real `Delete` feed-read coverage. `Refresh` handling, stronger delivery guarantees, and real `Delete` observations remain follow-up compatibility/adoption work. Profile doc: [linked-art/activity-stream-profile.md](linked-art/activity-stream-profile.md). Evidence: `tests/api/activity.test.ts`, `tests/api/activity-as2.test.ts`, `tests/api/records/by-id.test.ts`, [linked-art/wikidataexplorer-consumer-evidence.md](linked-art/wikidataexplorer-consumer-evidence.md).\n- [x] ✅ **Linked Art conservation activity proof (2026-07-06)**: `/api/activity` now treats object-level `attributed_by` condition assessments as semantic `AttributeAssignment` `Update` rows, preserving `assigned_property`, `assigned` condition types, descriptions, timespan, actors, and conservation-project `part_of` links separately from `modified_by` conservation `Modification` rows with technique and intervention metadata. Evidence: `src/services/activity-feed.ts`, `tests/api/activity.test.ts`, [linked-art/activity-stream-profile.md](linked-art/activity-stream-profile.md).\n- [x] ✅ **Linked Art actor identity proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for rich `Person` and `Group` records from the People/Organizations pattern: name parts, authority `equivalent[]`, group membership, `contact_point` addresses/phone/email, residence `Place`, birth/death, formation, professional `carried_out` activity, burial `participated_in`, biography statements, and typed nationality/gender/occupation classifications stay as first-class Linked Art structures. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art place identity proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the Places pattern: Place classifications, names, descriptions, `part_of` spatial hierarchy, authority `equivalent[]`, WKT `defined_by` geometry, approximate place nesting, and `HumanMadeObject.current_location` references to Places stay distinct so buildings/immovable objects are not coerced into Place records. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art concept identity proof (2026-07-06)**: `/api/records/{id}` and `/api/concepts/{id}` now have regression coverage for the Concepts pattern: `Type`, `Material`, `Language`, `Currency`, and `MeasurementUnit` remain accepted concept classes; local concept records preserve primary names, AAT `equivalent[]`, `classified_as` meta-types, `broader` hierarchy, concept-scheme `member_of` Sets, coordinated concept `created_by.influenced_by`, and concept references with embedded equivalents without collapsing hierarchy, classification, and grouping. Evidence: `tests/api/records/by-id.test.ts`, `tests/api/entity-roles.test.ts`.\n- [x] ✅ **Linked Art vocabulary-term proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the vocabulary-term rule that classification terms may cite required/recommended/optional vocabulary URIs directly in `classified_as.id` or through an intermediary local term with the authority URI in `equivalent[]`. The fixture preserves both shapes exactly, so consumers can compare across institutions without Meta Museum flattening local terms or discarding Getty/AAT reconciliation anchors. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art required-term proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the full Required Terms family when those concepts are modeled: primary/display/sort names, sort values, statement/type-of-work/style/shape/nationality/occupation/color meta-types, exhibition/provenance/professional/publication/promise activities, and collection-item/artwork flags all preserve the canonical full Getty AAT URIs instead of local substitutes. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art recommended-term proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for recommended vocabulary families as interoperability preferences rather than hard conformance failures: alternate/person-name parts, accession/local/system/call identifiers, statement categories with the Statement meta-type, object/place/group/digital/set/dimension classifications, language/unit/material/currency instances, nationality and occupation refinements with their meta-types, and shape terms with the Shape meta-type all preserve full Getty AAT URIs when modeled. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art optional-term proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for optional vocabulary families as mapping aids rather than requirements: translated titles, subtitles, aliases, pseudonyms, auction/ISBN/ISSN/DOI/stock identifiers, email/street/phone/fax contact points, optional statement and document classifications, optional place/group/object/object-part categories, and diameter/length/thickness dimensions all preserve full Getty AAT URIs when present. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art textual document proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the Textual Documents pattern: physical book `HumanMadeObject` carriers preserve `carries -> LinguisticObject` text links, textual content preserves monograph/chapter type classifications, primary and system identifiers, language, `content`, authorship `Creation`, publishing `Activity`, object `about` references, abstract-work `part_of` links, pagination statements, computable page-count dimensions, and `digitally_carried_by -> DigitalObject` web-page carriers without collapsing copy, text, and digital surrogate layers. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art archival hierarchy proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the Archival Hierarchies pattern: archive, archival grouping, and archival sub-grouping `Set` records preserve conceptual `member_of` hierarchy; archival `HumanMadeObject` letters and `DigitalObject` scans preserve their own `member_of` links; archival Sets use `members_contained_by` to align conceptual groups with physical boxes; item records use `held_or_supported_by` for actual physical containment; and `members_exemplified_by` preserves collective description without inventing inverse `member[]` lists. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art specific assertion proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the Specific Assertions pattern: `AttributeAssignment` records preserve `assigned_property`, `assigned`, timespan, source/citation `used_specific_object`, context `caused_by`, uncertain production assignment details, embedded `Identifier`/`Name` `assigned_by` provenance, AI statement creation technique/tool metadata, statement-level rights, and generic related-entity display labels without replacing current canonical values. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art profile-boundary proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the Profile design principle itself: baseline JSON-LD fields stay simple and predictable, including context, type, names, classifications, descriptions, equivalents, representations, and current production, while optional complexity is preserved as explicit `AttributeAssignment` expansion evidence with `assigned_property`, `assigned`, timespan, source, and review context. The fixture also keeps `_complete: false` and proves historical assertions do not overwrite current profile values. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art class-analysis proof (2026-07-06)**: `/api/records/{id}` now has regression coverage for the Class Analysis guidance: public records use practical Linked Art classes such as `HumanMadeObject`, `LinguisticObject`, `VisualItem`, `DigitalObject`, `Name`, `Identifier`, `Set`, `Dimension`, `MeasurementUnit`, `Material`, `Right`, and `Group`, with collection/document/image/identifier specificity carried by `classified_as` terms where needed instead of raw CIDOC-CRM utility classes. A traversal assertion rejects leaked `E##` class names in the returned record graph. Evidence: `tests/api/records/by-id.test.ts`.\n- [x] ✅ **Linked Art API reference docs section (2026-07-06)**: [linked-art/api/index.md](linked-art/api/index.md) now anchors a local Linked Art API 1.0 reference section for implementers and agents, with CC BY attribution, source URLs, endpoint summaries, field checklists, incoming relationship notes, Meta Museum coverage notes, and test ideas. The captured core endpoint notes cover [Abstract Works](linked-art/api/abstract-works.md), [Concepts](linked-art/api/concepts.md), [Digital Objects](linked-art/api/digital-objects.md), [Events](linked-art/api/events.md), [Groups](linked-art/api/groups.md), [People](linked-art/api/people.md), [Physical Objects](linked-art/api/physical-objects.md), [Places](linked-art/api/places.md), [Provenance Activities](linked-art/api/provenance-activities.md), [Sets](linked-art/api/sets.md), [Textual Works](linked-art/api/textual-works.md), and [Visual Works](linked-art/api/visual-works.md); [JSON Schemas](linked-art/api/json-schemas.md) records the official endpoint schema catalog, Abstract Work required fields, and no-additional-properties validation target; [Abstract Work Schema](linked-art/api/schema-abstract-work.md) records expanded `abstract.json` constraints for `PropositionalObject`, permitted top-level fields, embedded names/references/rights/visual/text structures, conceptual parent links, and `created_by` activity shape; [Concept Schema](linked-art/api/schema-concept.md) records expanded `concept.json` constraints for `crm:E55_Type`, concept subclasses, embedded statements/representations, `created_by` activity shape, and `broader` hierarchy; [Digital Object Schema](linked-art/api/schema-digital-object.md) records expanded `digital.json` constraints for `dig:D1_Digital_Object`, access points, media formats, conformance, digital services, carried/shown content, and `used_for`/`created_by` activity evidence; [Event Schema](linked-art/api/schema-event.md) records expanded `event.json` constraints for `Period`/`Event`/`Activity`, timespans, places, temporal ordering, causation, actor responsibility, participants, techniques, and `part_of` references; [Group Schema](linked-art/api/schema-group.md) records expanded `group.json` constraints for `crm:E74_Group`, group membership, performed/participated activities, contact points, residences, formation, and dissolution; [Person Schema](linked-art/api/schema-person.md) records expanded `person.json` constraints for `crm:E21_Person`, group membership, performed/participated activities, contact points, residences, birth, and death; [Physical Object Schema](linked-art/api/schema-physical-object.md) records expanded `object.json` constraints for `crm:E22_Human-Made_Object`, current ownership/custody/location, materials, parts, carrier/content/surrogate relationships, lifecycle activities, and provenance activities; [Shared Structures](linked-art/api/shared-structures.md) records the embedded data-structure families, inherited-context behavior, and `_complete: false` dereference rule; [Shared Activities](linked-art/api/shared-activities.md) records embedded activity classes, incoming relationship names, Birth/Death constraints, and activity `_complete: false` URI behavior; [Shared Digital Links](linked-art/api/shared-digital-links.md) records nested work/digital-object links, `access_point`, service, format, and `conforms_to` behavior; [Shared Dimensions](linked-art/api/shared-dimensions.md) records `Dimension` value/unit/type, uncertainty bounds, duration use, and `assigned_by` measurement evidence; [Shared Concept References](linked-art/api/shared-concept-references.md) records allowed concept classes, notation, equivalent links, and concept meta-classification; [Shared Identifiers](linked-art/api/shared-identifiers.md) records `Identifier` content, classification, notes, contact points, and assignment provenance; [Shared Monetary Amounts](linked-art/api/shared-monetary-amounts.md) records `MonetaryAmount` value/currency/classification, uncertainty bounds, notes, `paid_amount`, and auction-lot dimension usage; [Shared Names](linked-art/api/shared-names.md) records `Name` content, language tagging, nested parts, notes, and assignment provenance; [Shared Rights](linked-art/api/shared-rights.md) records `Right` structures, license/right classifications, rights holders, statements, and `subject_to` usage; [Shared References](linked-art/api/shared-references.md) records compact references, `equivalent`, `notation`, and the no-`_complete` reference rule; [Shared Statements](linked-art/api/shared-statements.md) records embedded `LinguisticObject` content, language, labels, format, rights, creation evidence, and nested statements; [Shared TimeSpans](linked-art/api/shared-timespans.md) records fuzzy boundary dates, duration dimensions, notes, and minimum content requirements; [Shared Relationships](linked-art/api/shared-relationships.md) records `AttributeAssignment` relationship assertions, `assigned`, `assigned_property`, assignment provenance, and arbitrary related-entity links. `/api/docs/manifest` plus `/api/docs/content` have regression coverage proving the section is discoverable through the public docs surfaces. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Endpoint-family schema enforcement slice**: local Linked Art schema-profile validation now rejects unexpected top-level fields against endpoint-family allowlists for implemented families while permitting documented Meta Museum response metadata (`_links`, `schemaVersion`, `_source`), and a vendored official Linked Art schema JSON executor now checks the same aggregate endpoint-family fixtures for required fields, allowed fields, and `type` const/enum rules. Physical Object, Digital Object, Person, Group, Place, Concept subclasses (`Type`, `Material`, `Language`, `Currency`, `MeasurementUnit`), Event/Activity, Provenance Activity, Set, Textual Work, Visual Work, and Abstract Work pass/extra-field fail fixtures prove no-additional-properties behavior with local and official schema evidence. Recursive checks now cover endpoint-local Provenance Activity `part[]` structures and shared `core.json` structures, including nested Acquisition extra-field rejection, required `transferred_title_of` enforcement, embedded Name extra-field rejection, and required Name `content` enforcement. Evidence: [`tests/quality/validation-architecture-depth.test.ts`](../tests/quality/validation-architecture-depth.test.ts).\n- [x] ✅ **Linked Art graph partition planner**: `src/utils/linked-art-partitioner.ts` now turns arbitrarily shaped but valid Linked Art graph input into discrete endpoint-family documents with first-class endpoint nodes emitted separately, compact references across document boundaries, owned embedded activity/shared structures preserved, `_complete: false` projected onto dereferenceable embedded summaries, recursion bounds for object parts, archival/set hierarchy, concept `broader`/`member_of`, and provenance `part[]` graphs, richer duplicate-node selection, and optional official schema validation on generated partitions. `/api/events/{id}` now uses the planner for ActivityStreams `linkedArt.id` dereferences, including activity subclasses such as `Production`, so event dereference responses keep object/agent relationships partition-bounded instead of recursively embedding record graphs. `/api/objects/{id}` now uses the planner for stored physical object records, bounding object `part` recursion while keeping owned lifecycle summaries such as `produced_by` embedded and their actor/place relationships compact. `/api/sets/{id}` and `/api/concepts/{id}` now use the same planner for stored Set and Concept hierarchy records, bounding `member`, `member_of`, and `broader` recursion while preserving the existing entity-index fallback for nested-only referenced sets/concepts. `/api/records/{id}` now runs record-detail serialization through the planner after canonical MetaMuseum id/equivalent projection, then adopts only recursive/root-activity planner fields so `_links.self`, `_source`, source equivalents, and rich record-detail embeddings stay stable. Evidence: [`tests/utils/linked-art-partitioner.test.ts`](../tests/utils/linked-art-partitioner.test.ts), [`tests/api/activity.test.ts`](../tests/api/activity.test.ts), [`tests/api/entity-roles.test.ts`](../tests/api/entity-roles.test.ts), [`tests/api/records/by-id.test.ts`](../tests/api/records/by-id.test.ts), [linked-art/partitioning.md](linked-art/partitioning.md).\n- [x] ✅ **Linked Art embedded completeness projection slice**: `_complete: false` is now emitted automatically for partial dereferenceable embedded shared structures and activity nodes during record normalization and semantic ActivityStreams `linkedArt` projection, while compact references such as `equivalent`, `member_of`, and actor references remain reference-only without `_complete`. Evidence: [`tests/utils/linked-art.test.ts`](../tests/utils/linked-art.test.ts), [`tests/api/activity.test.ts`](../tests/api/activity.test.ts).\n- [x] ✅ **Linked Art API design-principles reference (2026-07-06)**: [Design Principles](linked-art/api/design-principles.md) records the IIIF-derived API rules for shared use cases, internationalization, simplicity, REST/cacheability, JSON-LD, standards alignment, extensibility, networked retrieval, right-layer problem solving, one-direction relationship assertions, embedding, and opaque URI handling. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art API JSON-LD considerations reference (2026-07-06)**: [JSON-LD Considerations](linked-art/api/json-ld-considerations.md) records the canonical Linked Art context, profile media type, case-sensitive terms, `id`/`type` aliases, always-array property discipline, scoped context naming, plain-JSON usability, and RDF compatibility checks for records, exports, and embedded `linkedArt` projections. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art API protocol reference (2026-07-06)**: [Protocol](linked-art/api/protocol.md) records the HTTP(S) retrieval contract, required `GET`/`OPTIONS`, optional non-required `HEAD`, Linked Art JSON-LD content negotiation, wildcard CORS, future-version profile URI strategy, persistent URI practice, preferred endpoint names, and opaque URI handling. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art API Search and Discovery captures (2026-07-07)**: [Search](linked-art/api/search.md) now records the HAL-driven inverse-discovery pattern, official `OrderedCollectionPage`/embedded `OrderedCollection` shape, `orderedItems`, `next`/`prev`, `startIndex`, and the current Meta Museum refinement target for normalizing local `nextPage`/`prevPage` compatibility fields toward official Search API pagination. [Discovery](linked-art/api/discovery.md) now records HTML and HTTP `describedby` signposting, the exactly-one Linked Art record link rule, FAIR Signposting alignment, IIIF Change Discovery harvesting, Linked Art record-types context usage, and the current route-level refinement target for `Link: rel=\"describedby\"` headers. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`, `tests/quality/data-discovery-signposting.test.ts`, `tests/quality/hal-search-relations-conformance.test.ts`.\n- [x] ✅ **Linked Art canonical Search API pagination refinement (2026-07-07)**: `/api/search` and `/api/relations/{relation}` now emit official `next`/`prev` `OrderedCollectionPage` link objects, `startIndex`, and `partOf.first`/`partOf.last` while preserving existing `nextPage`/`prevPage` compatibility fields. The relation fixture now proves a real next-page boundary with `objectOwnedByAgent&limit=1`, and the shared HAL/search conformance helper checks official link-object shape without forcing every provider-specific legacy search route to migrate at once. Evidence: `tests/api/search.test.ts`, `tests/api/hal-relations.test.ts`, `tests/quality/hal-search-relations-conformance.test.ts`.\n- [x] ✅ **Linked Art code-and-tools reference (2026-07-06)**: [Code And Tools](linked-art/api/code-and-tools.md) records implementation libraries, platforms, documentation/modeling aids, validators, visualization tools, and data-cleaning resources including Crom, LinkedArt.js, Linked.Art.Net, LUX, Arches, Ogee, Zellij, the Linked Art JSON Validator, Simple Dynamic Modelling, Mermaid, and OpenRefine. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art bibliography reference (2026-07-06)**: [Bibliography](linked-art/api/bibliography.md) records scholarly and technical sources for Linked Art, LOUD, provenance, semantic annotation, image archives, community practice, and cross-collection discovery, with version-context guidance for using 2024-2025 sources for current claims and older entries for lineage/history. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art CDWA mapping reference (2026-07-06)**: [CDWA Mapping](linked-art/api/cdwa-mapping.md) records the Getty CDWA-to-Linked-Art crosswalk for object, title, creation, measurement, materials, statement, activity, provenance, exhibition, visual documentation, textual reference, person/group, place, and concept authority fields, including explicit non-mappings for meta-metadata, Phase-like facts, and unsupported source fields. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art Schema.org mapping reference (2026-07-06)**: [Schema.org Mapping](linked-art/api/schema-org-mapping.md) records derivative structured-data projection rules from canonical Linked Art into Schema.org for shared properties, people, organizations, places, concepts, human-made objects, digital objects, visual/textual works, events, and sets while keeping Linked Art as the source of truth. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art Place schema reference (2026-07-06)**: [Place Schema](linked-art/api/schema-place.md) records expanded `place.json` constraints for `crm:E53_Place`, required identity fields, no-additional-properties validation, `defined_by` WKT/GeoJSON geometry, `part_of` spatial hierarchy, and incoming object/activity/actor place references. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art Provenance Activity schema reference (2026-07-06)**: [Provenance Activity Schema](linked-art/api/schema-provenance-activity.md) records expanded `provenance.json` constraints for `crm:E7_Activity`, required identity fields, required provenance `classified_as`, no-additional-properties validation, temporal/activity context, and `part[]` evidence for acquisition, custody transfer, payment, encounter, movement, rights acquisition, and generic classified provenance activities. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art Set schema reference (2026-07-06)**: [Set Schema](linked-art/api/schema-set.md) records expanded `set.json` constraints for `la:Set`, required identity fields, no-additional-properties validation, parent set hierarchy, physical member containers, exemplar member templates, topics, dimensions, and `used_for`/`created_by` activity evidence. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art Textual Work schema reference (2026-07-06)**: [Textual Work Schema](linked-art/api/schema-textual-work.md) records expanded `text.json` constraints for `crm:E33_Linguistic_Object`, required identity fields, no-additional-properties validation, language, content, format, rights, aboutness, part relationships, and creation/use activity evidence while preserving carrier boundaries. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art Visual Work schema reference (2026-07-06)**: [Visual Work Schema](linked-art/api/schema-visual-work.md) records expanded `image.json` constraints for `crm:E36_Visual_Item`, required identity fields, no-additional-properties validation, rights, dimensions, aboutness, represented entities, represented entity types, part/conceptual relationships, and creation/use activity evidence while preserving carrier boundaries. Evidence: `tests/utils/docs.test.ts`, `tests/api/docs.test.ts`.\n- [x] ✅ **Linked Art event identity proof (2026-07-06)**: `/api/records/{id}` and `/api/events/{id}` now have regression coverage for the Events pattern: `Period`, `Event`, and `Activity` records preserve names, timespans, places, actor responsibility, `caused_by`, strict `part_of` partitioning, contextual `during`, and relative `before`/`after` ordering, while object production/destruction references keep their temporal links instead of flattening them into dates. Evidence: `tests/api/records/by-id.test.ts`, `tests/api/entity-roles.test.ts`, `tests/api/activity.test.ts`.\n- [x] ✅ **MetaHistoryBook Update/Delete alignment (2026-07-05)**: after the accepted `204` callback proof, MetaHistoryBook first rechecked `type=Update` and `type=Delete` as explicit zeroes, then approved a two-ping plan. MetaMuseum deployed the real reviewed Met `437133` `Update` row, and MetaHistoryBook re-read `/api/activity?type=Update&limit=100` as `metahistorybook-harvester-prod` at `2026-07-05T19:50:30Z`: `totalItems: 1`, `1` item, real non-synthetic, conformant `object.id`/`object.sourceId`/`equivalent[]`/UTC `endTime`, and cross-reconciliation through `Q432253`. `type=Delete` remains explicit zero by agreement until a genuine upstream `404`/`410` tombstone exists. Evidence: [linked-art/metahistorybook-prod-harvest-evidence.md](linked-art/metahistorybook-prod-harvest-evidence.md).\n- [x] ✅ **ActivityStreams partner quickstart (2026-07-04)**: [linked-art/activity-stream-partner-quickstart.md](linked-art/activity-stream-partner-quickstart.md) now gives external partners copy/paste feed-read, cursor sync, object-feed, subscription, verification, and response-template steps while explicitly stating the quickstart is not adoption evidence. `pnpm activity:partner-pack` links the quickstart and still emits `consumerIds: []`, placeholder rows, and `needs-consumer-ids` until real partner-owned IDs exist. Evidence: `tests/docs/activity-stream-partner-quickstart.test.ts`, `tests/scripts/activity-partner-onboarding-pack-script.test.ts`.\n- [x] ✅ **MetaHistoryBook reciprocal harvest probe and rehearsal path (2026-07-05)**: MetaHistoryBook now exposes a live IIIF Change Discovery stream, and Meta Museum has a bounded read-only probe at `pnpm activity:metahistorybook:harvest` that checks collection/page shape, fixed page size, oldest-first UTC `Update` activities, last-page walk-back start, dereferenceable Linked Art `object.id`, Wikidata `equivalent` reconciliation hooks, and the CC0 metadata `Link rel=license` header on both the collection and a sample record before any full-corpus import is considered. `pnpm activity:metahistorybook:import-rehearsal` adds the next rehearsal-only gate: capped page walk, capped record dereference sample, CC0 metadata-only rights mapping, QID mapping, and media-rights separation without adding a provider claim; after the partner approved moving the schedule forward, the latest larger rehearsal walked `562/562` pages, saw `56,196` activities, sampled `250/250` records, and mapped `250/250` metadata rights plus `250/250` QIDs. `pnpm activity:provider-backfill:evidence` now proves Meta Museum's own Met `record-backfill` publication rows separately from historic semantic events; the local gate covers `53` backfill rows, `53/53` real non-epoch timestamps, `53/53` equivalents, canonical MetaMuseum activity object IDs, and source/Wikidata equivalents synthesized for dereferenced records. MetaHistoryBook's July 5 production run as `metahistorybook-harvester-prod` passed multi-page ordering, real timestamps, `equivalent[]` reconciliation, and dedupe; it also proved the first concrete Met-to-MetaHistoryBook join on `Q432253` (`Garden at Sainte-Adresse`). Daily (`daily-metahistorybook-prod`) is filed as a second distinct external product consumer after walking the same 53 Met activities into an artwork-of-the-day candidate pool with 15 paintings and cursor `2026-05-31T10:40:54.290Z`; its public callback at `https://daily.metahistorybook.com/api/consumer/met/callback` now verifies for `Create`/`Update`/`Delete` and is filed as the first durable callback row. The rights mapping boundary is explicit: CC0 covers MetaHistoryBook's derived JSON metadata, while referenced image/media licenses remain separate. Checklist: [linked-art/metahistorybook-reciprocal-harvest-checklist.md](linked-art/metahistorybook-reciprocal-harvest-checklist.md). Evidence: `docs/linked-art/metahistorybook-prod-harvest-evidence.md`, `artifacts/activity-syndication/daily-metahistorybook-prod-callback-2026-07-05.json`, `src/services/metahistorybook-harvest.ts`, `src/services/metahistorybook-import-rehearsal.ts`, `src/services/activity-provider-backfill-evidence.ts`, `tests/services/metahistorybook-harvest.test.ts`, `tests/services/metahistorybook-import-rehearsal.test.ts`, `tests/services/activity-provider-backfill-evidence.test.ts`, `tests/scripts/metahistorybook-harvest-probe-script.test.ts`.\n- [x] ✅ **Activity discovery duplicate collapse (2026-07-05)**: default `/api/activity/collection` and `/api/activity/page/{page}` responses now collapse duplicate rows for the same `object.id`, preferring deletion, audit, and `record-backfill` publication signals over historic `record-semantic` rows so walk-back harvesters see one latest-change item per object by default. Explicit `source=record-semantic` and `source=record-backfill` filters still expose source-specific rows for diagnostics and evidence checks. Evidence: `src/services/activity-feed.ts`, `app/api/activity/collection/route.ts`, `app/api/activity/page/[page]/route.ts`, `tests/api/activity-as2.test.ts`.\n- [x] ✅ **Production activity backfill seed (2026-07-05)**: external checks showed `www.metamuseum.org` had the latest activity code but only the single clean-deploy `record-003` seed, so `provider=met` and `source=record-backfill` returned `0` rows despite the local gate being green. `data/sample-records.json` now carries the 53 Met records needed for the public `record-backfill` corpus, and `tests/fixtures/production-seed-records.test.ts` guards the seed for 53 Met rows, real non-epoch `importedAt` values, and reconciliation/source hooks.\n- [x] ✅ **External activity stream candidate registry (2026-07-05)**: `/api/providers/capabilities` now exposes read-only standards-stream candidates separately from provider ingest claims: Getty IIIF Change Discovery is `probe-ready`, Rijksmuseum LDES is `route-available` via `/api/rijks/ldes`, and Europeana/CoGent LDES remain watchlist references. Candidate notes: [linked-art/external-activity-streams.md](linked-art/external-activity-streams.md). Evidence: `src/services/external-activity-sources.ts`, `tests/services/external-activity-sources.test.ts`, `tests/api/providers/capabilities.test.ts`.\n- [x] ✅ **ActivityStreams adoption controls**: [`src/services/activitystreams-adoption-controls.ts`](../src/services/activitystreams-adoption-controls.ts) and [`tests/services/activitystreams-adoption-controls.test.ts`](../tests/services/activitystreams-adoption-controls.test.ts) now summarize and regression-check feed-read telemetry, partner-pack acceptance rows, durable HTTPS callback verification, strict review-goals selected checks, and placeholder/derived/local evidence rejection for the `0/3` real-consumer lane.\n- [x] ✅ **Commercial readiness controls**: [`src/services/commercial-readiness-controls.ts`](../src/services/commercial-readiness-controls.ts) and [`tests/services/commercial-readiness-controls.test.ts`](../tests/services/commercial-readiness-controls.test.ts) now summarize and regression-check the pre-revenue `/pilot` claim boundary, buyer-pack acceptance ledger, invoice-backed entitlement guard, monthly KPI/retention/gross-margin packet, and manual concierge versus repeatable subscription packaging gate.\n- [x] ✅ **Security reliability controls**: [`src/services/security-reliability-controls.ts`](../src/services/security-reliability-controls.ts) and [`tests/services/security-reliability-controls.test.ts`](../tests/services/security-reliability-controls.test.ts) now keep rotated-secret hygiene, production test-token rejection, membership-validated org-scope route coverage, and cron `CRON_SECRET` fail-closed behavior tied to one executable regression report.\n- [x] ✅ **Testing gap controls**: [`src/services/testing-gap-controls.ts`](../src/services/testing-gap-controls.ts) and [`tests/services/testing-gap-controls.test.ts`](../tests/services/testing-gap-controls.test.ts) now preserve complete onboarding coverage, scheduled disabled-feature drills, the `pnpm typecheck` release-command contract, storage-scope matrix breadth, and local-vs-strict evidence separation in one executable regression report, including acceptance-ledger links for every strict external contract and the no-global-bypass readiness downgrade.\n- [x] ✅ **Complete onboarding e2e regression**: [`tests/e2e/onboarding-flow.test.ts`](../tests/e2e/onboarding-flow.test.ts) now walks the high-value launch path as one executable journey: public users hit the sign-in gate, a signed-in editor selects an active org, imports a provider record into org-scoped storage, creates an approval-required AgentTask review, approves a wiki draft, proves dry-run publication stays non-live, and exercises the publish-queue daily-cap boundary.\n- [x] ✅ **Secret rotation preflight guard**: production `pnpm launch:preflight:production` now requires non-secret rotation evidence and a git tracking/history probe for sensitive `.env*` files through `security.secretRotation` and `security.secretHistory`, and review-goals selects both rows as launch-readiness evidence.\n- [x] ✅ **Secret evidence stale-artifact handoff**: when an older `artifacts/launch/deployment-preflight-latest.json` is missing `security.secretRotation` or `security.secretHistory`, `pnpm review:goals` now reports the artifact as stale or missing current selected-check coverage, points to `pnpm launch:preflight:production`, and keeps the exact rotation/history acceptance criteria visible.\n- [x] ✅ **Production preflight latest-artifact protection**: `pnpm launch:preflight:production` now treats localhost/private production-target runs as diagnostics by keeping the timestamped run artifact while refusing to replace `deployment-preflight-latest.json`, so local `.env` defaults cannot accidentally become the review-goals launch evidence packet.\n- [x] ✅ **Production launch-review public Era C handoff**: production `pnpm launch:review:production` now points red or stale Era C evidence at `pnpm era-c:exit-gate:public` so operators refresh probe-backed public evidence instead of local telemetry, while staging review keeps `pnpm era-c:exit-gate:evidence` for rehearsal.\n- [x] ✅ **Explicit-env production preflight mode**: `METAMUSEUM_SKIP_ENV_FILES=1` now lets production evidence runs ignore local `.env*` files so staging-only values such as `METAMUSEUM_TEST_ROLE_OVERRIDE_TOKEN` cannot contaminate the public launch artifact. The latest explicit-env dry run proved the new `security.secretRotation` and `security.secretHistory` checks pass when non-secret rotation evidence and git history probes are supplied; strict `/api/validate` is now production-default closed unless `METAMUSEUM_STRICT_VALIDATION_PUBLIC=1` is set.\n- [x] ✅ **Render strict-validation cold-start guard**: `VALIDATION_TIMEOUT_MS` now bounds strict `/api/validate`, while production preflight keeps the long Render `/health` cold-start probe separate from user-facing readiness by treating Render validation as operator-only unless public strict validation is explicitly enabled. If `METAMUSEUM_STRICT_VALIDATION_PUBLIC=1` is set, preflight still fails without paid/no-sleep capacity evidence or within-budget health duration.\n- [x] ✅ **TypeScript command drift retired**: `pnpm typecheck` remains the CI-aligned production signal, while `pnpm typecheck:diagnostic` now converges with direct `tsc --noEmit`; `pnpm typecheck:diagnostic:report` writes JSON/Markdown parity artifacts with `status: converged`, `0` errors, `0` buckets, and `0` unclassified diagnostics. The command contract is in [`docs/development/typescript-command-contract.md`](development/typescript-command-contract.md).\n- [x] ✅ **Era A + Era B**: legacy lift, parity, provider hardening, authority caching, B6.1 reconciliation, B7 gateway readiness/facade, B8 protocol conformance, B9 modeling guardrails, B10 ARK behavior, and pre-Era-C operational sign-off are complete.\n- [x] ✅ **Era C implementation surface**: C1-C5 core features are implemented, including multi-modal storage scaffolding, HAL/search/activity endpoints, C2 ETL/reconciliation/mapper, C3 IIIF + visualization surfaces, C4 AI query/chat/evals/mapping assist, specialized review agents, and C5 syndication/wiki/security/privacy hardening.\n- [x] ✅ **AI agent review layer**: five single-word field-aligned agents are live behind human approval: Clio (research signals), Mercator (Linked Art mapping review), Janus (reconciliation review), Themis (rights/provenance review), and Calliope (citation-backed curatorial drafts). Localhost smoke completed all five, avatar assets are verified, each returned AgentTask persists to editor-gated review history at `/api/agents/tasks`, and Mercator/Janus now have a disabled-by-default AG2 bridge boundary plus local review-only FastAPI worker with trace propagation, contract validation, timeout/refusal fallback, local fallback, safe enablement docs, and live-worker eval artifacts. AgentTask outputs now include reviewer-facing evidence diagnostics: Clio sparse-scope missing signals and next evidence, Mercator unmapped sensitive-column explanations, Janus reconciliation candidate-readiness plus zero-candidate reasons, Themis structured-rights/provenance evidence checks, and Calliope source-note citation-review snippets. `src/services/ai-agent-safety-controls.ts` and `tests/services/ai-agent-safety-controls.test.ts` now keep the safety posture executable across agent/model-spend RBAC, anonymous model downgrade, cite-or-refuse gates, approval-required AgentTask defaults, and opt-in AG2 bridge drills.\n- [x] ✅ **Disabled-system staging drill**: `pnpm staging:disabled-drill:check` now writes `artifacts/staging-disabled-systems/latest.json` and rehearses off-by-default AG2 fallback, enabled AG2 contract delegation with a mocked worker, Solr/GraphDB projection skip/no-network behavior, mocked enabled Solr/GraphDB request dispatch, projection readiness scale-pressure blockers, disabled cron no-ops, publication disabled-channel handling, and enabled publication webhook dispatch before those systems are enabled for real staging or production use; `.github/workflows/staging-disabled-systems-drill.yml` runs the same staging check weekly and uploads the latest/timestamped artifacts, with service, script, workflow, and testing-gap regressions asserting the expanded 9-check schedule contract.\n- [x] ✅ **SEO/content publishing policy**: generated article, book, object-label, and collection-brief outputs now include source-derived SEO metadata (`seoTitle`, meta description, primary/secondary keywords, H1, subheadings, slug, rationale) on both accepted and refused content paths; WikiDrafts validate the same SEO envelope while preserving canonical source titles and citation/originality gates.\n- [x] ✅ **Security/privacy posture**: PII/sensitivity scan, review holds, audited human disposition, rights/reuse UI warnings, and public projection controls are active before Solr/GraphDB syndication.\n- [x] ✅ **Governance + docs contract**: markdown under `docs/` remains the canonical source of truth, surfaced by `/docs`, `/api/docs/manifest`, and `/api/docs/content`; Linked Art reference mapping, AIDD/TDD, and closeout evidence remain mandatory. `pnpm product:constraints:check` now makes Linked Art conformance and rights-first publication non-negotiable by checking the generated conformance command/docs/tests, `Right` entity modeling, WikiDraft rights-warning publication guards, cite-or-refuse coverage, and human approval policy. `pnpm review:goals` now gives the 10/10 review-goal audit a blocker-preserving JSON and Markdown artifact, with each JSON artifact carrying a `$schema` pointer to `docs/schemas/review-goals.schema.json`, per-goal `evidenceArtifacts` paths, observed `evidenceArtifactStatuses`, selected `evidenceArtifactCheckStatuses`, structured per-goal `externalEvidenceBlockers`, and a flattened report-level blocker list for launch evidence/preflight/review checks, Render service probes, managed-pilot entitlement/outreach/activation/support/KPI evidence, long-term runway checks, Era C exit-gate `sota204P95`/`publicReadUptime`/`activityFeedAdoption`/`sota26Kpis` checks, ActivityStreams consumer/subscription checks, and paid-pilot proof. Those selected checks are readiness gates: local gates can be complete while real SLO, uptime, ActivityStreams consumer, KPI, and paid-pilot evidence remain explicitly external, no high-level flag can produce a 10/10 report while a selected artifact check is failing or missing, and bounded or fail-fast launch evidence still leaves a full packet behind with skipped remainder steps plus structured remediation, next evidence, command, and artifact fields preserved in the top-level audit. `SUPPORT.md` now gives contributors and operators an explicit issue, PR, security advisory, pilot evidence, launch blocker, and conformance escalation path without inventing an undocumented chat channel. The normal `pnpm session:closeout` path now refuses to append unless `README.md` and this roadmap were updated since the previous closeout.\n- [x] ✅ **Deployment (LIVE)**: the Next.js app is deployed to **production on Vercel against Neon Postgres** (`storageMode=postgres`), verified serving real records and public pages (2026-06-23). `vercel.json` pins `next build` so the close-out guard cannot break builds; the guard also self-skips when `VERCEL` is set. The `render.yaml` FastAPI validation and reconciliation services plus Redis cache are now deployed on Render, and both `/health` endpoints are included in launch readiness probes when their URLs are passed to `pnpm launch:evidence:production`. Secured Vercel Cron drains for outbox projection and publish queue processing are now configured at `/api/cron/outbox` and `/api/cron/publish`; both require `CRON_SECRET` and remain opt-in behind worker env flags until Solr/GraphDB projection or wiki/publication publishing is intentionally enabled. `/workers` and `/api/workers/status` now make scheduled-drain state explicit with worker lag, backlog, next cron wakeup, effective next drain, disabled projection/publish modes, and blocked/dead-letter signals. Direct Solr/GraphDB projection now also skips when target flags are unset, matching the read-only deploy contract. Deployment preflight now fails production when `BASE_URL` and `METAMUSEUM_PUBLIC_READ_BASE_URL` are missing, divergent, localhost, non-HTTPS, or otherwise not the same shareable public HTTPS URL, and runtime social metadata resolves against the public-read base before Vercel preview fallbacks. Documentation drift guards now keep Vercel/Neon launch examples aligned on the canonical public URL pairing and Neon `sslmode=verify-full`, including `.env.example`. Preflight now also probes live Auth.js `/api/auth/signin` and `/api/auth/session` routes so auth readiness is route evidence, not only secret presence, and fetches `IIIF_TILE_URL` so k6 tile evidence cannot point at a broken or protected target. Probe-based uptime history now keeps per-route URL, status, duration, and error/protection detail for public-read checks, making failed uptime samples and deployment-protection regressions diagnosable from the evidence artifact while the 30-sample window fills. `pnpm longterm:evidence` now records the daily 09:15 UTC collection cadence, next scheduled run, missing distinct SLO/uptime days, missing external-consumer count, and earliest possible ready date in the long-term evidence `collectionPlan`, so the 30-day blocker has an operator-visible runway instead of a vague wait state. Optional Render validation/reconciliation dependencies are now explicit in deployment preflight: unset URLs record the local fallback/in-process mode, while configured service URLs get `/health` status and duration evidence so cold starts and timeouts are visible in launch readiness artifacts. `pnpm activity:syndication:evidence` now ties real declared external ActivityStreams consumers to active external HTTPS subscriptions for the same IDs, keeps accepted/rejected callback rows in the artifact, and counts only public non-placeholder callback hosts that match their HTTPS callback URL, were updated inside the evidence window, cover Create/Update/Delete, and carry recent 2xx callback verification proof, keeping durable syndication blocked until partner callback workflows are fresh, complete, and verified. `pnpm smoke:crawler-preview` now records Facebook, Slack, Googlebot, LinkedIn, and X/Twitter-facing Open Graph/Twitter/canonical checks plus preview image fetchability in launch artifacts. `pnpm launch:evidence` and `pnpm launch:evidence:production` now run hardening security/DR, preflight, public trust, crawler preview, a11y, explore, k6, Era C exit-gate, and launch review as one blocker-preserving evidence packet, and failed/skipped steps name the remediation command plus artifact path operators should turn green next. The route-storage guard now walks production API import paths and blocks unmanaged local JSON writes; activity subscriptions, AI query usage logs, issue cache, and publish queue persistence are Postgres-managed documents instead of Vercel filesystem writes. See [deployment.md](deployment.md).\n  Review-goal deployment evidence now distinguishes deployed Render service probes from documented local fallback mode: `pnpm review:goals` only counts the validation/reconciliation service goal when both selected checks show passing public HTTPS Render `/health` probes, rejects HTTP, non-health, placeholder, local, or private-network probe text, and launch readiness also selects those same deployed service checks. Launch readiness additionally selects worker scheduler readiness, Auth.js secret, GitHub OAuth credential, live signin/session route, production public-base URL, social-preview base URL, database authentication, deployment activation freshness, and public-read uptime source checks from deployment preflight, plus crawler-preview checks from both launch evidence and launch review, so missing cron/secret wiring, localhost, non-HTTPS, protected, mismatched share URLs, missing auth credentials, stale database credentials, stale active Vercel deployments, missing uptime-source setup, or missing Render service probes stay explicit deployment blockers before Open Graph/Twitter/canonical, preview-image, public-read, a11y, and probe-based uptime evidence can be trusted; the `launchEvidenceReady` coarse flag now requires those selected launch-evidence, launch-review, and deployment-preflight rows before it can pass. The long-term goal now also selects the Era C exit-gate `sota204P95`, `publicReadUptime`, `activityFeedAdoption`, and `sota26Kpis` rows, so measured p95 misses, insufficient uptime depth, missing external consumers, and KPI export gaps are real-world blockers with `pnpm era-c:exit-gate:public` remediation rather than a single opaque exit-gate failure; its coarse readiness flag requires both the selected runway rows and Era C rows before counting 30-day evidence ready. Manual public evidence refreshes should use `pnpm monitoring:telemetry:public`, `pnpm era-c:exit-gate:public`, or `pnpm longterm:evidence:public`, which force probe-backed uptime against `https://www.metamuseum.org` and avoid inheriting localhost from `.env.local`. The ActivityStreams coarse readiness flag now requires both selected rows, `activity.declaredConsumers` and `activity.durableSubscriptions`, before counting syndication ready, so a top-level ready artifact cannot hide missing or unverified durable subscriptions. The version-45 audit also scopes production launch-packet a11y failures to the deployment-environment lane instead of treating them as local-refresh debt, and includes `analytics-consent-posture` as a local governance goal backed by `pnpm privacy:consent:check` and `artifacts/privacy/analytics-consent-latest.json`. Coarse external blocker rows and selected artifact-check rows now include their own next-evidence text, remediation commands, and artifact targets for launch, Render probes, 30-day SLO/uptime, Era C exit-gate, ActivityStreams syndication, paid-pilot evidence, and consent-posture checks, with a command-and-scope `nextActions` list that unions every artifact path into machine-readable `artifactTargets`, preserves each blocker as a goal-scoped action `blockerIds` reference whose length matches `blockerCount`, preserves every distinct evidence requirement as `nextEvidenceRequirements`, keeps local/deploy/real-world queueing as `executionScope`, exposes top-level lane counts as `nextActionSummary.actionCount`, includes blocker-lane `externalEvidenceBlockerSummary.nextActionIds`, `actionCount`, `nextEvidenceRequirements`, and `requirementCount`, includes per-goal `blockedByExternalEvidenceGoals.nextEvidenceRequirements`, `requirementCount`, `actionCount`, `commandCount`, `artifactCount`, and `scopeCount`, reports the local-only CI result as top-level `localGateStatus`, reports the final strict gate as top-level `strictGateStatus`, lists the strict-gate blocker lanes in `strictGateFailureReasons`, adds structured strict-gate rows in `strictGateFailureSummary`, adds consolidated strict-gate commands/artifacts/goals in `strictGateHandoff`, includes exact blocker IDs in `strictGateHandoff.blockerIds` and `strictGateHandoff.scopeBreakdown[].blockerIds`, includes exact action IDs in `strictGateHandoff.nextActionIds` and `strictGateHandoff.scopeBreakdown[].nextActionIds`, includes explicit strict next-action totals in `strictGateHandoff.nextActionCount` and `strictGateHandoff.scopeBreakdown[].nextActionCount`, includes explicit affected-goal totals in `strictGateHandoff.goalCount` and `strictGateHandoff.scopeBreakdown[].goalCount`, includes distinct remediation-command totals in `strictGateHandoff.commandCount` and `strictGateHandoff.scopeBreakdown[].commandCount`, includes distinct evidence-artifact target totals in `strictGateHandoff.artifactCount` and `strictGateHandoff.scopeBreakdown[].artifactCount`, includes distinct next-evidence requirement totals in `strictGateHandoff.requirementCount` and `strictGateHandoff.scopeBreakdown[].requirementCount`, includes consolidated strict-gate next-evidence sentences in `strictGateHandoff.nextEvidenceRequirements`, splits those strict handoffs by scope in `strictGateHandoff.scopeBreakdown`, and exposes both `score.blockedByExternalEvidence` and `blockedByExternalEvidenceGoals` so local-gate-complete goals do not hide the true external-blocked total or the named goal handoffs. Each blocked-goal row now carries exact blocker IDs, action IDs, action-level blocker/artifact/requirement counts, action counts, scopes, scope counts, remediation commands, command counts, artifact targets, artifact counts, distinct top-level evidence requirements and counts, an `actionBreakdown` mapping each command to the blockers it remediates with goal-local evidence requirements, and a `scopeBreakdown` that splits that goal's action, next-action ID, blocker, command, artifact, and requirement arrays and totals across local-refresh, deployment-environment, and real-world-evidence lanes; the schema now requires those blocked-goal handoff arrays to be populated and duplicate-free before the artifact contract is considered valid. The latest local audit reports `complete=7/12`, with launch readiness, Render service probes, managed pilot, 30-day SLO/uptime, and ActivityStreams syndication named in `blockedByExternalEvidenceGoals`. The local-refresh lane has `0` blockers, deployment-environment has `4` blockers driven by launch-evidence, launch-review, Era C proof, and Render service probes, and the real-world lane still has `19` blockers for long-window, ActivityStreams `Delete` type coverage, KPI, and pilot proof. `pnpm review:goals:local` now lets CI fail only on missing local commands/docs/tests while `pnpm review:goals:check` stays reserved for the final real-evidence 10/10 gate; documentation drift tests scan public docs for stale demo-script-only, launch-preflight-green, strict-handoff count drift, and broad-public-SaaS readiness claims that bypass that strict gate, and the Era C evidence workflow enforces the local gate before uploading the review-goals packet.\n- [x] ✅ **Review-goals version 44 consent posture gate**: `pnpm privacy:consent:check` now writes `artifacts/privacy/analytics-consent-latest.json`, and review goals include the GA4 consent posture as a governance goal with local script, docs, tests, and artifact checks.\n- [x] ✅ **Review-goals version 42 scoped blocked-goal actions**: `blockedByExternalEvidenceGoals[].scopeBreakdown[].nextActionIds` now lets each blocked-goal scope join directly to grouped remediation actions.\n- [x] ✅ **Review-goals version 41 blocked-goal scope counts**: `blockedByExternalEvidenceGoals[].scopeCount` now exposes how many execution lanes remain for each named blocked goal.\n- [x] ✅ **Review-goals version 40 blocked-goal totals**: `blockedByExternalEvidenceGoals[].actionCount`, `commandCount`, and `artifactCount` now expose dashboard-ready blocked-goal handoff totals directly.\n- [x] ✅ **Review-goals version 39 blocked-goal requirements**: `blockedByExternalEvidenceGoals[].nextEvidenceRequirements` and `requirementCount` now make each named blocked goal self-contained with exact acceptance criteria.\n- [x] ✅ **Review-goals version 38 external blocker actions**: `externalEvidenceBlockerSummary[].nextActionIds` and `actionCount` now point each blocker lane directly at grouped remediation actions.\n- [x] ✅ **Review-goals version 37 external blocker requirements**: `externalEvidenceBlockerSummary[].nextEvidenceRequirements` and `requirementCount` now expose acceptance criteria directly per blocker lane.\n- [x] ✅ **Review-goals version 36 action-summary counts**: `nextActionSummary[].actionCount` now exposes action totals per execution-scope lane while legacy `count` remains for compatibility.\n- [x] ✅ **Review-goals version 35 strict action counts**: `strictGateHandoff.nextActionCount` and scoped `strictGateHandoff.scopeBreakdown[].nextActionCount` now expose executable action totals directly, so dashboards do not have to count action ID arrays.\n- [x] ✅ **Review-goals version 34 blocked-goal scoped payloads**: `blockedByExternalEvidenceGoals[].scopeBreakdown[]` now includes scoped remediation commands, artifact targets, and next-evidence requirements, so each named blocked goal can be handed to an operator without joining through action rows.\n- [x] ✅ **Review-goals version 33 blocked-goal scope counts**: `blockedByExternalEvidenceGoals[].scopeBreakdown[]` now exposes action, command, artifact, and requirement counts per scope, so each named blocked goal carries dashboard-ready handoff totals without parsing nested arrays.\n- [x] ✅ **Review-goals version 32 strict requirement counts**: `strictGateHandoff.requirementCount` and scoped `strictGateHandoff.scopeBreakdown[].requirementCount` now expose distinct next-evidence acceptance-criteria totals directly, so dashboards can show how many criteria remain without parsing long requirement text.\n- [x] ✅ **Review-goals version 31 strict artifact counts**: `strictGateHandoff.artifactCount` and scoped `strictGateHandoff.scopeBreakdown[].artifactCount` now expose distinct evidence-artifact target totals directly, so dashboards do not have to count artifact arrays before showing the remaining evidence packet footprint.\n- [x] ✅ **Review-goals version 30 strict command counts**: `strictGateHandoff.commandCount` and scoped `strictGateHandoff.scopeBreakdown[].commandCount` now expose distinct remediation-command totals directly, so dashboards do not have to count command arrays or confuse distinct commands with scoped action rows.\n- [x] ✅ **Review-goals version 29 strict goal counts**: `strictGateHandoff.goalCount` and scoped `strictGateHandoff.scopeBreakdown[].goalCount` now expose affected-goal totals directly, so dashboards do not need to count goal arrays.\n- [x] ✅ **Review-goals version 28 strict action IDs**: `strictGateHandoff.nextActionIds[]` and scoped `strictGateHandoff.scopeBreakdown[].nextActionIds[]` now point from the strict-gate handoff directly to the executable `nextActions[]` rows, so operators do not have to reconstruct command/scope keys.\n- [x] ✅ **Review-goals version 27 strict blocker IDs**: `strictGateHandoff.blockerIds[]` and scoped `strictGateHandoff.scopeBreakdown[].blockerIds[]` now name the exact failing checks behind the strict gate, so operators can trace a blocker directly to the artifact/check row without joining through goals.\n- [x] ✅ **Review-goals version 26 strict scope breakdown**: `strictGateHandoff.scopeBreakdown[]` now splits strict-gate commands, artifacts, affected goals, blocker counts, and next-evidence requirements by execution scope, so deployment setup and real-world proof cannot be merged in the final operator handoff.\n- [x] ✅ **Review-goals version 25 strict evidence requirements**: `strictGateHandoff.nextEvidenceRequirements[]` now unions the exact next-evidence sentences from scoped action rows, so the strict-gate handoff carries the commands, artifact paths, and evidence acceptance criteria together.\n- [x] ✅ **Review-goals version 24 strict handoff**: `strictGateHandoff` now consolidates strict-gate summary IDs, execution scopes, blocker count, affected goals, remediation commands, and artifact targets into one schema-locked operator payload, so launch dashboards can show remaining deployment and real-world proof without rejoining summary rows.\n- [x] ✅ **Review-goals version 23 strict summary invariants**: runtime invariants now validate every structured `strictGateFailureSummary[]` field against the derived blocker summaries, so strict-gate dashboard rows cannot silently drift in counts, goals, commands, artifacts, scope, kind, or reason text.\n- [x] ✅ **Review-goals version 22 structured strict failure summary**: `strictGateFailureSummary[]` now gives dashboards closed rows with kind, scope, blocker counts, affected goals, commands, artifacts, and the matching reason, so deployment-environment and real-world evidence lanes can be consumed without parsing prose.\n- [x] ✅ **Review-goals version 21 strict failure reasons**: `strictGateFailureReasons[]` now lists the non-empty blocker lanes and remediation commands when the strict 10/10 gate fails, so local-green reports explain the remaining deployment and real-world evidence work directly in JSON and Markdown.\n- [x] ✅ **Review-goals version 20 strict gate status**: `strictGateStatus` now records the final `pnpm review:goals:check` pass/fail result separately from `localGateStatus`, so local-green reports with remaining production, partner, customer, or 30-day evidence blockers cannot be misread as 10/10-ready.\n- [x] ✅ **Review-goals version 16 handoff shape**: `nextActionSummary[]` now carries distinct commands, artifact targets, goal IDs, titles, categories, and counts per execution scope, so the deployment-environment and real-world-evidence lanes show which 10/10 goals they affect and which outputs they refresh without requiring dashboards or operators to join through `nextActions`.\n- [x] ✅ **Review-goals version 17 blocker scopes**: goal-level and report-level external-evidence blocker rows now carry `executionScope` directly, so each blocker is visibly `deployment-environment` or `real-world-evidence` at the row level instead of requiring a join through `nextActions`.\n- [x] ✅ **Review-goals version 19 scoped actions**: `nextActions[]` now groups by remediation command plus execution scope, so shared commands such as `pnpm era-c:exit-gate:evidence` can produce separate deployment-environment and real-world-evidence action rows instead of mixing blocker lanes.\n- [x] ✅ **Review-goals version 18 blocker summaries**: `externalEvidenceBlockerSummary[]` now groups raw blocker rows by execution scope with blocker IDs, goals, remediation commands, and artifact targets, so deployment and real-world blocker debt remains visible even before operators inspect grouped actions.\n- [x] ✅ **Review-goals invariant guard**: the CLI now refuses to write malformed review-goals artifacts when score totals, blocked-goal counts, blocker counts, unknown action references, scope summaries, commands, artifact targets, or goal lists drift from the generated report body.\n- [x] ✅ **Review-goals version alignment**: `REVIEW_GOALS_REPORT_VERSION`, emitted report `version`, and JSON schema `version.const` are test-locked together so artifact contract bumps cannot split the service and schema.\n- [x] ✅ **Review-goals schema parity guard**: generated JSON artifacts are runtime-validated against the schema-required keys and closed-object sections for report, score, goal, evidence, blocked-goal, action, and action-summary rows before the CLI prints or writes them, so schema/report drift fails before operators receive a malformed 10/10 evidence handoff.\n- [x] ✅ **Portfolio README + docs (2026-06-24)**: README trimmed from ~1,300 lines to a lean hero + highlights + run-it + honest \"what's real vs. in progress\" (detailed status stays here in the roadmap). New deep-dives added: [responsible-ai.md](responsible-ai.md) (key handling, denial-of-wallet auth-gate, citation/refusal gates, eval harness, cost control) and [linked-art/conformance-matrix.md](linked-art/conformance-matrix.md) (protocol MUSTs verified live + per-provider matrix + honest gaps). A single-file architecture/reviewer HTML handoff is maintained at [metamuseum-project-overview.html](metamuseum-project-overview.html) for browser-openable reviewer context.\n- [x] ✅ **README density refresh (2026-06-28)**: the README no longer carries the long review-goals artifact-version history or operator-level schema prose; it now gives fast reviewers a short strict-readiness summary and links to [docs/ops/review-goals.md](ops/review-goals.md) for the detailed artifact contract.\n- [x] ✅ **Linked Art rights as `Right` entities — all providers (2026-06-24)**: started the [roadmap to 10/10](roadmap-to-10.md) with milestone **B1**. `src/utils/linked-art-rights.ts` synthesizes a conformant `subject_to` `Right` (classified by CC0 / rightsstatements.org URIs) for every object record that lacks one, wired into both `normalizeIncomingRecord` and the read-path `migrateToCurrentSchema`; Getty's are preserved. Closes the \"rights as labels outside Getty\" gap in the conformance matrix.\n- [x] ✅ **Reliable, badged CI — roadmap-to-10 A1 (2026-06-24)**: the session close-out guard no longer fails CI — `scripts/session-closeout.ts` skips the `--check` guard when `CI` is set (it stays enforced locally), so a stale local close-out log can't turn a green build red (the PR #16 failure mode). README header now carries CI / License / Linked Art / tests badges, and the tests badge intentionally uses a guarded `1,100+` label rather than an exact stale-prone count.\n- [x] ✅ **Supply-chain hygiene — roadmap-to-10 A2 (2026-06-24)**: resolved all 3 moderate `pnpm audit --prod` advisories via `pnpm.overrides` (postcss XSS → `>=8.5.10`; OpenTelemetry memory-DoS → core/resources/sdk-trace-base `^2.8.0`, which also fixes `@vercel/otel`'s mis-resolved 1.30.1 peers). Added `.github/dependabot.yml` (npm + github-actions + 4 pip services) and a `pnpm audit --prod --audit-level high` CI gate. Audit clean; tests 1,114; build green.\n- [x] ✅ **Per-provider conformance matrix generated — roadmap-to-10 B3 (2026-06-24)**: the 14×2 per-provider pass/fail fixtures (asserted in CI by `validation-architecture-depth.test.ts`) now drive a **generated** conformance matrix via `scripts/generate-conformance-matrix.ts` (`pnpm conformance:matrix`); `conformance-matrix-generated.test.ts` gates drift. The published `conformance-matrix.md` table is no longer hand-maintained. 14/14 providers pass both directions.\n- [x] ✅ **SHACL conformance gate in CI — roadmap-to-10 B2 (2026-06-24)**: `services/validation-service/shacl_gate.py` + `.github/workflows/shacl-conformance.yml` validate every provider's pass fixture against the Linked Art SHACL shapes with pyshacl (JSON-LD → CIDOC-CRM RDF). Path-filtered job; `pnpm shacl:gate` locally. All 14 pass fixtures conform; a CRM-expansion regression now blocks the build.\n- [x] ✅ **Measured + gated test coverage — roadmap-to-10 A3 (2026-06-24)**: `pnpm test:coverage` runs the suite under c8 with a `--check-coverage` gate (lines 85 / funcs 85 / branches 70); CI's test step now enforces it. Current 89.4% lines / 92.1% funcs (core `src/services` 91.9%). README coverage badge added; also fixed CRLF-fragility in the B3 drift test so coverage runs clean cross-platform.\n- [x] ✅ **Published quality scores — roadmap-to-10 A4 (2026-06-24)**: [`docs/quality.md`](quality.md) publishes CI-measured numbers — Lighthouse a11y **100/100** on key pages, axe **0 severe** WCAG 2A/2AA violations across 18 routes, and the k6 p95 performance budget **met** (cached 73.5 ms, cold 56.1 ms, facet 55.1 ms, 0% errors). README a11y badge added.\n- [x] ✅ **Faceted / relevance search — roadmap-to-10 B4 (2026-06-24)**: `src/services/search.ts` ranks `/api/search` results by hit quality (exact label > prefix > substring > name) and returns `type`/`provider` facet counts + `q`/`type`/`provider`/`limit`/`offset` params in the `ld+json` `OrderedCollectionPage`. Tested at the service + API level; conformance-matrix \"basic, not faceted\" gap closed (Solr 9 documented as the env-gated scale backend).\n- [x] ✅ **Fixed flaky annotations test / CI reliability (2026-06-24)**: annotation ids were `annotation-${Date.now()}`, so two creates in the same millisecond shared an id — letting annotations in different org scopes collide and intermittently breaking the cross-org isolation assertion in CI. Centralized id minting in `mintAnnotationId()` (timestamp + `randomUUID`); added a deterministic 1,000-mint uniqueness test. Completes the A1 \"reliable CI\" goal.\n- [x] ✅ **HEAD + HTTP/2 conformance — roadmap-to-10 B6 (2026-06-24)**: the canonical Linked Art entity/collection routes now export `HEAD` (via a `bodilessResponse(await GET(...))` helper in `src/utils/protocol.ts`) — same headers as GET, no body, mirrors 200/404; `OPTIONS` advertises `GET,HEAD,OPTIONS`. HTTP/2 verified live (`HTTP/2.0 200` via Vercel). `tests/api/head-methods.test.ts`; suite 1,128.\n- [x] ✅ **Roadmap trimmed — roadmap-to-10 A5 (2026-06-24)**: this roadmap went from ~1,510 lines to ~420 by archiving the slice-by-slice Era A/B/C history to [`progress/era-history.md`](progress/era-history.md) (see \"Era delivery history\" below). `getStructuredRoadmap` aggregates both files so `/api/roadmap` still exposes full phases/milestones.\n- [x] ✅ **Activity Streams change feed — roadmap-to-10 B5 (2026-06-24, lifted 2026-07-04)**: aligned `/api/activity` to Activity Streams 2.0 — AS2 `@context`, cursor-first `next`/`prev` page links (kept offset `nextPage`/`prevPage` aliases), a fuller `partOf` `OrderedCollection` with `first`/`last`/`totalItems`, `application/activity+json`, embedded Linked Art projections, semantic record-event extraction, filters, cursor sync, and object-scoped feeds. `tests/api/activity-as2.test.ts`, `tests/api/activity.test.ts`.\n- [x] ✅ **Product walkthrough + evaluator brief — roadmap-to-10 A6 (2026-06-29)**: the README now links a recorded no-narration public-site walkthrough at [`public/media/metamuseum-product-walkthrough.webm`](../public/media/metamuseum-product-walkthrough.webm), `/projects` presents it as a professional Linked Art SaaS product case study, and the shot-by-shot replacement script remains at [`demo-script.md`](demo-script.md). The current 10/10 gate is no longer a demo-media checklist; `pnpm review:goals:check` remains blocked on the launch-review Era C dependency, paid-pilot proof, 30-day SLO/uptime evidence, KPI exports, and durable ActivityStreams syndication.\n- [x] ✅ **Product case-study layout pass (2026-06-29)**: `/projects` now gives the CTA row, proof strip, case-study cards, architecture cards, and walkthrough media page-specific spacing and wrapping so buttons and cards do not touch or overlap across mobile, tablet, and desktop checks.\n- [x] ✅ **Standalone project overview HTML (2026-06-29)**: [`metamuseum-project-overview.html`](metamuseum-project-overview.html) now provides a single-file, browser-openable project overview with inline CSS/SVG only, covering essence, architecture, components, engineering discipline, state, risks, and evidence-based next improvements.\n- [x] ✅ **Dependency batch verified (2026-06-24)**: applied all 14 open Dependabot updates in one verified PR (#48) — fastapi 0.138, pyld 3.1, redis 8, pydantic 2.13, dagster 1.13.10, `actions/checkout` 6→7 — each installed in a clean venv and run against the relevant service's tests (validation `validate_record` + SHACL gate, reconciliation 9 tests, ag2-worker 6 tests, pipeline 3 tests). SHACL CI pins synced; queue cleared. Keeps A2 supply-chain hygiene current.\n- [ ] ⚠️ **Era C exit gate is not green yet**: latest evidence is still `failed`. The current strict handoff reports `20/30` retained deployed SLO samples across `7/30` observed days, with `13/30` passing samples across `6/30` passing days and `7` failed/incomplete retained rows. Public-read uptime has `126/129` passing retained checks across `10/30` observed days but only `0.9767` availability against the `0.999` target, ActivityStreams has `3/3` declared external consumers, restored `3/3` durable callback rows, and observed `Create, Update` type coverage while still missing real `Delete` read coverage, and production KPI exports still miss reconciliation thresholds. The project is strong for controlled beta/demo use, but not yet ready to claim full public-production completion.\n\n- [x] ✅ **Worker scale scheduler guard (2026-06-27)**: production preflight now fails if Solr/GraphDB projection targets are enabled without `CRON_SECRET` plus `METAMUSEUM_OUTBOX_CRON_ENABLED=1`, or if live MediaWiki/Wikibase endpoints or bot tokens are configured without `CRON_SECRET` plus `METAMUSEUM_PUBLISH_QUEUE_CRON_ENABLED=1`. A static Vercel config test also keeps `/api/cron/outbox` and `/api/cron/publish` scheduled before those scale flags can be treated as launch-ready.\n- [x] ✅ **Vercel Observability cron-error fix (2026-06-28)**: live probes showed `/api/cron/outbox` and `/api/cron/publish` returning `503 cron_secret_missing` while `/api/workers/status` showed both drains intentionally disabled. Disabled scheduled drains now return `200` no-op JSON without `CRON_SECRET`, while enabled drains still require bearer auth before any outbox or publish work can run; route-level tests cover the disabled no-op and enabled-secret cases.\n- [x] ✅ **Projection scale-readiness gate (2026-06-27)**: `pnpm projection:readiness` now writes `artifacts/launch/projection-readiness-latest.json` and classifies Solr/GraphDB projection as `portable`, `watch`, `enable`, or `enabled` based on current record count, active discovery/graph workflow counts, and search/graph p95 metrics. `pnpm projection:readiness:check` exits non-zero when the scale path is required but target/scheduler readiness is incomplete, so Solr/GraphDB projection is enabled only when volume and discovery workflows justify it."},{"level":3,"heading":"Current Launch Readiness","body":"| Launch lane | Score | Current decision | Required next evidence |\n|---|---:|---|---|\n| Internal/dev demo | 9/10 | Safe to keep using and iterating locally. | Keep `pnpm test`, `pnpm lint`, `pnpm build`, and closeout guard green. |\n| Controlled public beta | 8.4/10 | App is now **live on Vercel + Neon** at `https://www.metamuseum.org` (2026-06-23), clearing the deployed-base-URL blocker. The July 3 production preflight passes `20/20`: OAuth, Postgres storage shape, `sslmode=verify-full`, live Neon authentication, active deployment freshness after secret rotation, auth/IIIF reachability, validation/reconciliation Render `/health` probes, public-read reachability, social-preview base URL parity, no production smoke override token, and secret-rotation/history evidence all pass. Strict `/api/validate` is operator-only by default in production unless `METAMUSEUM_STRICT_VALIDATION_PUBLIC=1` is set. The refreshed launch evidence packet passes `8/9`, launch review passes `7/8`, and controlled beta remains blocked only because the Era C row still needs 30-day SLO/uptime, ActivityStreams, and KPI proof. | Keep `pnpm launch:evidence:production`, `pnpm launch:review:production`, and `pnpm launch:beta:readiness` fresh with production env present; controlled beta can advance further once Era C has enough SLO, uptime, adoption, and KPI evidence or an explicit narrower beta acceptance policy is recorded. |\n| General public production | 6.5/10 | Not yet; product is feature-rich but evidence gates are red. | Passing 30-day SLO/uptime evidence, real KPI telemetry, and external activity-feed adoption proof. |\n| Institution-grade / 10/10 | 5.5-6/10 | Blocked by time-based evidence and real-world adoption. | At least 30 days of green SLO + uptime samples, 3 declared external feed consumers, and SOTA §26 KPI targets. |"},{"level":3,"heading":"Current SaaS Readiness","body":"| SaaS lane | Score | Current decision | Required next evidence |\n|---|---:|---|---|\n| Technical SaaS foundation | 7/10 | Strong enough to begin SaaS packaging: auth, roles, Postgres storage, provider ingestion, validation, docs, launch review, and trust/syndication tooling are real. | Complete staging secrets, production-like deployment, usage limits, tenant-aware data boundaries, and supportable onboarding. |\n| Paid pilot readiness | 8.2/10 | Close for 1-3 concierge pilots where Sun & Rain Works can manually onboard collections and invoice outside the app; `/pilot` is shared-nav reachable and publishes the offer, commercial-readiness ledger showing `0` paid pilots, manual invoice entitlement path, and in-app billing not built, named initial outreach queue, derived status ledger showing 13 researched accounts, 1 sent message, and 0 replies, and a zero-complete activation evidence ledger backed by managed outreach and activation events with the next required evidence. The queue includes Museum of New Zealand Te Papa Tongarewa, Museums Victoria, and Art Gallery of Ontario; Te Papa web-form outreach is recorded at `2026-07-04T18:30:00.000Z` from the user's reported July 4, 2026 11:30 AM Pacific submission, while no reply, invoice, or buyer activation is claimed. `pnpm pilot:buyer-pack` gives buyer-ready packs acceptance rows for outreach, invoice-backed entitlement, activation, support load, required KPIs, retention signal, gross-margin proof, and strict packet refresh, with the support-load row now verifying through `pnpm pilot:support -- --tenant <tenant-id> --summary`; `pnpm pilot:outreach`, `pnpm pilot:activation`, `pnpm pilot:support`, and `pnpm pilot:evidence --markdown` give operators validated no-JSON-edit commands for recording real first-outreach/milestone/support evidence and packaging explicit blocked/ready JSON plus Markdown evidence artifacts. `pnpm pilot:evidence --check` exits non-zero for blocked packets while still writing artifacts, so missing real entitlement, activation, support load after real pilot activity, KPI, retention, or gross-margin proof cannot pass automated readiness silently. Outreach replies require prior sent evidence, sent follow-up dates are barred from predating `sentAt`, activation milestones require prior same-tenant evidence, tenant-mismatched activation evidence is rejected, support response and resolution timestamps are barred from predating `openedAt`, existing support issue updates are barred from rewriting the original `requester`, `summary`, `openedAt`, or `severity`, already-resolved support issue updates are barred from rewriting `resolvedAt` or `resolutionSummary`, open support issues are barred from carrying resolution evidence, and support-load evidence remains blocked until an invoice-backed pilot has `pilot.support_minutes` evidence plus no open blocking or overdue support responses. `docs/ops/managed-linked-art-pilot-runbook.md` defines the concierge setup, tenant/source namespace, activation events, support intake, monthly evidence packet, and counter-backed route usage gate; `src/services/pilot-entitlements.ts`, `src/services/pilot-usage-counters.ts`, `src/services/pilot-support-issues.ts`, org storage scope, tenant preview scope, and route gates keep the manual pilot lane durable, org-aware, quota-gated, and exportable without implying self-serve billing readiness. | Follow up on Te Papa if they reply, sign one manual pilot scope, create an invoice-referenced entitlement, onboard one real pilot dataset using the runbook, attach deployment-specific security/legal evidence to the buyer packet, complete the buyer-pack acceptance rows with real tenant evidence, add route-level support-access implementation tests only if a support-as-customer feature ships, and complete activation, support, KPI, retention, and gross-margin ledgers with real tenant evidence. |\n| Self-serve SaaS readiness | 3/10 | Not ready; the app lacks pricing pages, tenant signup, billing, plan gates, org invites, usage dashboards, and support workflows. | Add account/org onboarding, billing/manual-plan entitlements, quotas, usage analytics, and customer success runbooks. |\n| Profitable SaaS business | 4/10 | The product has a credible technical wedge, but revenue operations and repeatable sales motion are not proven yet. | Convert paid pilots into recurring subscriptions with gross-margin, retention, support-load, and acquisition-channel evidence. |\n\nPilot packets now expose `commercial.billing`, `commercial.retention`, and `commercial.grossMargin` evidence directly. `pnpm review:goals` treats invoice-backed billing, `pilot.retention_signal_count`, and `pilot.gross_margin_percent` as selected checks, so managed-pilot readiness cannot pass from activation/support/KPI proof alone. `src/services/commercial-readiness-controls.ts` keeps the public pre-revenue copy, buyer-pack rows, invoice guard, monthly packet, and packaging gate in one executable report while real paid-pilot evidence remains missing."},{"level":3,"heading":"SaaS Commercialization Strategy","body":"**Primary wedge:** managed Linked Art API + data-quality cockpit for small/mid-size museums, archives, galleries, digital humanities labs, and artist estates that want standards-compliant publication without hiring a semantic-web team. This wedge matches the current product surface best: provider ingestion, Linked Art normalization, API/docs, validation, public trust, AI query, reconciliation, IIIF, ActivityStreams, and Neon-backed storage.\n\n**Secondary wedge, defer until the B2B pilot loop works:** creator-side provenance and authorship tools. This may scale further, but it needs simpler onboarding, consumer-grade billing, evidence storage, and marketplace/export integrations that are not yet core to the current app.\n\n**Initial paid offer:** \"Managed Linked Art Launch Pilot\" — fixed-scope onboarding of one collection export into a hosted workspace, including data-quality report, Linked Art API, public browse pages, provenance/rights review flags, and a monthly evidence packet. Manual invoicing is acceptable for the first 1-3 concierge pilots; `pnpm pilot:packaging` now makes that support limit executable and blocks repeatable SaaS until subscription checkout, webhook entitlement sync, customer billing portal, and plan-change audit evidence exist."},{"level":3,"heading":"SaaS Roadmap Track","body":"| Phase | Goal | Build / decide | Exit criteria |\n|---|---|---|---|\n| SaaS-0: Positioning + offer | Turn the technical platform into a sellable pilot. | ✅ `/pilot` now publishes the ICP, pilot promise, pricing hypothesis, deliverables, data prerequisites, support boundaries, success metrics, commercial-readiness ledger (`0` paid pilots, manual invoice path, in-app billing deferred), ten qualified prospect profiles, and a 13-account outreach queue with structured stages, status evidence, derived counts, three non-US prospects, and one recorded Te Papa web-form outreach. | Offer page is published, success metrics are explicit, named accounts are listed, and status tracking is visible; full exit now needs a reply or disqualification plus invoice-backed pilot proof. |\n| SaaS-1: Concierge paid pilot | Earn first non-demo revenue without overbuilding self-serve. | ✅ `docs/ops/managed-linked-art-pilot-runbook.md` now defines the pilot workspace setup runbook, tenant namespace convention, manual plan entitlement config, activation events, support intake process, customer evidence packet template, and `pnpm pilot:packaging` supportability check. Next: follow up only if Te Papa replies or the follow-up date arrives, then execute one invoice-backed pilot dataset with completed real-tenant activation milestones. | 1-3 invoice-backed paid pilots onboarded; each reaches first value within 7 days; pilot users can view/import/query/export without engineer intervention for routine tasks; packaging check remains supportable. |\n| SaaS-2: Multi-tenant product core | Make the app safe for multiple paying organizations. | ✅ Service-layer org-scoped storage isolation is in place for records, jobs, persisted AgentTask artifacts, researcher annotations, audit logs and org audit exports, ActivityStreams feed reads, activity readiness metrics, Postgres storage export, DR restore rehearsal, scoped public browse/derived reads, scoped AI/editorial read dependencies, wiki draft storage/access routes, wiki sync-map/reverse-ETL artifacts, authenticated org-session preview fallback for workspace pages, first-class managed `org-tenants.json` org/membership/invite backing, admin/team invite APIs, the `/orgs` operator UI, membership-validated `/api/orgs/active` cookie selection, workspace active-org status/selector/switch affordance plus stale-selection feedback, selected-org propagation through shared preview/storage/gate resolvers, records, wiki draft, annotation, AgentTask, and scoped AI/editorial route tenant RBAC read/write evidence, representative records/annotation/wiki draft/scoped AI-editorial non-member route RBAC evidence, non-admin org administration denial evidence, org-bound pilot entitlement/counter gates, and stable pilot API rate-limit denials, with Auth.js resolving active org memberships before compatibility env mappings. Support impersonation policy is now defined and test-locked in the procurement packet; next support-access evidence is route-level implementation proof only if the feature ships. | Tests prove tenant isolation at service and route boundaries; launch review includes tenant security checks; one hosted deployment supports multiple orgs without data bleed. |\n| SaaS-3: Billing + growth loop | Move from manual pilots to repeatable subscriptions. | ✅ Interim plan gates and durable manual pilot entitlement validation are executable in `src/services/pilot-entitlements.ts` and stored in managed `storage/pilot-entitlements.json`; `src/services/pilot-saas-packaging.ts` now decides when manual invoice billing is still enough and when subscription billing is required. Next: add pricing page, checkout or invoice-backed subscriptions, billing webhooks, usage enforcement, metered usage, trial/activation emails, onboarding checklist UI, churn/cancel reasons, and product analytics dashboard. | New org can sign up or be provisioned in under 15 minutes; MRR, activation, retention, support tickets, and usage are visible weekly; `pnpm pilot:packaging -- --target=repeatable-saas --check` passes. |\n| SaaS-4: Reliability + compliance for institutions | Make paid deployments procurement-friendly. | ✅ First procurement readiness packet is landed with security overview, data-flow diagram, hosting/subprocessor assumptions, backup/restore proof path, incident response summary, and checklist. Next: add customer-facing status page, SLA/SLO reporting, DPA/legal packet, access-review reports, deployment-specific backup evidence exports, incident drill evidence, and data-retention controls. | Controlled beta evidence is green enough for pilots; `pnpm review:goals:check` must be green before broad public SaaS claims. |\n| SaaS-5: Profitability gate | Prove the business model, not just the software. | Track gross-margin percentage, cloud cost per tenant, support minutes per account, onboarding cost, conversion rate, retention, expansion, and CAC/payback by channel. | Positive gross-margin per tenant, repeatable acquisition channel, retention evidence, and at least one pricing tier that remains profitable after support + infra cost. |"},{"level":3,"heading":"SaaS Product Backlog","body":"| Capability | Current state | SaaS-grade next step |\n|---|---|---|\n| Tenant/account model | Auth roles and Postgres storage exist; pilot entitlement/counter tests prove exact-tenant and org-bound gate isolation; `src/services/org-tenants.ts` stores first-class orgs, active/inactive memberships, and hashed-token invites in managed storage; `src/services/active-org-selection.ts` validates and persists selected active orgs for signed-in members and now shares that membership-validated resolver with request storage, preview, and route-gate scope; `src/services/org-session-status.ts` resolves sanitized active-org display state and generic stale-selection warnings; admin-only org APIs create/list orgs, memberships, sanitized invites, revocations, public invite acceptance, form posts, and org-scoped audit rows; `/orgs` provides the current operator UI for org creation, membership adds, invite creation, sanitized invite review, and pending-invite revocation; the workspace shell shows active org status, storage scope, membership role, accessible-org count, selector, stale-selection warning, and a switch/manage affordance without token material; Auth.js resolves active memberships into session org ids before compatibility env mappings; records, jobs, and persisted AgentTask artifacts support org-scoped service-layer storage under a shared root; tenant-tagged records, jobs, AgentTask, annotation, activity, audit, import, public browse/derived read routes, AI/editorial read routes, wiki draft access routes, wiki sync-map/reverse-ETL routes, and preview pages now propagate exact tenant or authenticated org scope into backing stores or read models. | Add route-level support-access implementation safeguards only if support-as-customer ships, plus broader tenant RBAC evidence before self-serve hosting. |\n| Plans and entitlements | `src/services/pilot-entitlements.ts` defines `free`, `pilot`, `institution`, and `enterprise` plan gates for imports, AI calls, storage, users, exports, API rate limits, and feature access; it also validates and persists interim manual pilot entitlement records by exact `tenantId` with optional `orgId` binding in managed `storage/pilot-entitlements.json`, evaluates requested usage against the active plan, and `src/services/pilot-route-gates.ts` reads tenant or selected authenticated-org usage from managed `storage/pilot-usage-counters.json` before provider facade import/search/profile, AI, content generation, records API, and records export work runs, returns stable `429`/`Retry-After` responses for API-per-minute overages, then records successful 2xx work back into the same counter ledger under the selected scope. `src/services/org-tenants.ts` now gives plan gates a first-class org/membership/invite backing store, `src/services/active-org-selection.ts` persists membership-validated org choice and feeds shared selected-org resolution into route gates, `src/services/org-session-status.ts` makes active-org scope visible in the workspace shell, while `proxy.ts` blocks tenant-tagged direct legacy provider routes, `src/auth/roles.ts` derives provider import write gates from the capability registry, scoped service storage covers records/jobs/AgentTask artifacts, records/jobs/AgentTask routes pass tenant identity into scoped stores, browse plus AI/editorial read APIs select scoped record stores when request context is scoped, wiki draft access routes select scoped draft stores, wiki sync-map/reverse-ETL routes select scoped stores, and preview pages select authenticated org records when query tenant scope is absent. | Add richer plan surfaces, production limiter metadata, and usage dashboards before supporting self-serve multi-org hosting. |\n| Billing | No in-app billing; `/pilot` now visibly says `0` paid pilots, manual invoice entitlement only, and in-app billing not built. First pilots have a durable manual invoice-backed entitlement contract with required invoice reference, namespace, owner, publication boundary, monthly evidence cadence, and Postgres export coverage, plus `pnpm pilot:buyer-pack` for generating the buyer-specific capture checklist and timestamped run artifact before packet creation; `/readiness` now surfaces that buyer handoff as its own waiting source until real buyer fields and invoice evidence arrive. | Use manual invoice entitlement records for pilots; graduate to Stripe or equivalent checkout/webhooks only after signed pilot pricing and activation evidence are validated. |\n| Onboarding | Developer-led setup works; the managed pilot runbook now defines concierge workspace setup, source-data requirements, namespace rules, and a seven-day activation checklist, but self-serve setup does not exist. | Execute the runbook on one real dataset, then add guided org setup, sample dataset path, first-value dashboard, and onboarding email flow. |\n| Usage analytics | Launch/exit evidence exists; GA4 page analytics is wired from the root layout when `NEXT_PUBLIC_GA_MEASUREMENT_ID` is set, with the current web stream ID `G-WPGJX5H0S7` documented for Vercel. The site now ships a browser-persisted analytics consent banner and privacy-choices control; Consent Mode v2 defaults deny ad storage, ad user data, ad personalization, and analytics storage until an analytics choice exists, advertising consent remains denied even when analytics is accepted, and `pnpm privacy:consent:check` writes `artifacts/privacy/analytics-consent-latest.json` proving GA4 ID validation, denied-by-default Consent Mode v2, persisted banner controls, `/privacy` disclosure, root-layout tag wiring, and the no-ad-personalization expansion guard. `src/services/pilot-outreach-events.ts`, `src/services/pilot-activation-events.ts`, `src/services/pilot-support-issues.ts`, `src/services/pilot-kpi-events.ts`, and `src/services/pilot-evidence-packet.ts` now record, summarize, and package required outreach/activation/support/KPI evidence, `pnpm pilot:outreach`, `pnpm pilot:activation`, `pnpm pilot:support`, `pnpm pilot:kpi`, and `pnpm pilot:evidence --markdown` give operators validated write/export paths for real JSON and customer-readable Markdown evidence, `/pilot` renders honest zero-sent and zero-complete ledgers until real records exist, and route-level entitlement gates read exact-tenant month/minute counters from managed storage and record successful gated route work, but broader tenant-aware activation analytics remain thin. | Implement tenant-aware tracking for activation milestones, validation improvements, customer views, weekly active users, usage cost, monthly evidence exports, and legal/CMP review before enabling ads, remarketing, or region-specific marketing workflows. |\n| Support operations | Technical docs are strong; the managed pilot runbook now defines support intake fields, severity levels, response rules, and evidence cadence for concierge pilots. | Formalize intake tooling, known-issues page, escalation policy, and pilot feedback cadence once the first pilot is active. |\n| Sales/marketing surface | `/pilot` now publishes the buyer-facing Managed Linked Art Launch Pilot page with problem, buyer, offer, pricing hypothesis, success metrics, support boundaries, source-network CTA, contact CTA, shared primary/footer nav access, named outreach queue, an outreach status ledger with non-clipped status cells, and a zero-complete activation evidence ledger backed by managed outreach/activation events plus operator commands needed to record and package real evidence. | Send/record the first real outreach, then add proof screenshots, completed activation milestones, and a customer evidence packet once the first pilot is active. |\n| Procurement readiness | `docs/ops/procurement-readiness-packet.md` now packages a buyer-reviewable security overview, Mermaid data-flow diagram, hosting/subprocessor assumptions, backup/restore evidence path, incident response summary, checklist, and explicit non-SOC-2 / incomplete-tenant-isolation limits. | Attach actual deployment-specific evidence, legal/DPA artifacts, access-review exports, incident drill evidence, and customer-specific subprocessors once the first pilot is active. |"},{"level":3,"heading":"Current Evidence Blockers","body":"| Era C exit check | Current evidence | Required to clear |\n|---|---|---|\n| SOTA §20.4 p95 SLOs | The current strict handoff reports `20/30` retained deployed SLO samples across `7/30` distinct UTC days, with `13/30` passing samples across `6/30` passing days and `7` failed/incomplete retained rows. `pnpm longterm:evidence:public` now reports SLO trend intake (`23` raw timestamped rows, `20` inside the report window, `3` older) plus distinct UTC observation days and acceptance rows so stale, clustered, old, or failed retained samples cannot masquerade as 30-day evidence. | Keep complete five-scenario `pnpm k6:slo` samples passing against the deployed target, then retain 30 days of passing samples; keep all p95s under policy thresholds until the SLO depth and failure-free acceptance rows are ready. |\n| Public-read uptime | Live public-read URL is known (`https://www.metamuseum.org`) and deployment preflight probes `/`, `/api/health`, and `/api/records` for launch-critical read availability; retained public-read uptime now has `117` probe snapshots across `9/30` observed UTC days, but availability is only `0.9744`, still below the 99.9% threshold because one retained failed sample remains. `pnpm monitoring:telemetry:public` forces a public probe snapshot, and `pnpm longterm:evidence:public` turns the uptime snapshot/history into both accumulation runway and maintenance repair artifacts with uptime depth, availability, and failed-sample age-out acceptance rows. | Keep scheduled public probes running via `METAMUSEUM_PUBLIC_READ_BASE_URL=https://www.metamuseum.org` / uptime envs; retain a clean 30-day window with >= 99.9% availability until the uptime observation-depth and availability rows are ready. |\n| Activity feed adoption | `3/3` declared external consumers in the latest local syndication artifact, with `metahistorybook-harvester-prod`, `daily-metahistorybook-prod`, and `wikidataexplorer-metamuseum-prod` filed from production reads; the restored `storage/activity-subscriptions.json` ledger now reports `3/3` matched durable external callbacks and `3` accepted callback rows. Single-consumer and three-consumer matrix proof tooling are available, `pnpm activity:partner-pack` writes the partner-specific `limit=100` read/subscription handoff plus per-consumer acceptance rows and a timestamped run artifact, now including the optional `type=Update -> linkedArt.id -> /api/events/...` dereference check; `/readiness` surfaces both broad community outreach and the waiting partner handoff as generated sources, and the no-ID pack includes three `pending-consumer-id` outreach slots while keeping `consumerIds: []` so placeholders cannot satisfy strict proof. `pnpm activity:community-outreach` captures broad Linked Art/Slack asks as outreach context with `strictEvidence: false`, prints the latest JSON, Markdown, and timestamped run artifact paths for attachment, and `/readiness` shows when its channel, message reference, timestamp, or owner still need to be captured. `/api/activity`, `/api/activity/collection`, and `/api/activity/page/{page}` record each declared consumer's observed `Create`/`Update`/`Delete` feed activity types, embedded `linkedArt.id` values now dereference through `/api/events/{encodedSourceActivityId}` with `_complete: false` plus source-preserving `equivalent[]`, `/api/records/{id}` now emits activity autodiscovery `Link` headers, `/api/providers/capabilities` advertises activity endpoint templates/filter/signing metadata, callback subscriptions can declare HMAC-SHA256 signing via secret references, `pnpm activity:adoption:matrix` plus the long-term runway/maintenance reports expose observed/missing type coverage, `pnpm activity:subscription:verify` records recent 2xx callback proof without hand-editing `storage/activity-subscriptions.json`, `pnpm activity:syndication:evidence` reports durable callback row counts, and `pnpm longterm:evidence:public` ignores placeholder-looking declared IDs in the long-term adoption runway. The first reviewed `Update` row is now live and partner-confirmed in production from a real Met object `437133` metadata delta; MetaHistoryBook also confirmed on `2026-07-10T00:18:38Z` that `/evidence`, `/api/evidence`, `/api/evidence/ledger`, and the Update row's hosted `/api/events/...` Linked Art dereference are externally live and conformant. `pnpm providers:coverage:seed` now verifies all `14/14` source-provider lanes before the tombstone scan, `pnpm activity:tombstone:scan` keeps the upstream tombstone watch fresh with provider-aware support/skipped/duplicate reporting, and `pnpm moma:dataset:diff` now provides a separate MoMA open-data snapshot comparison lane whose removals stay review-required and never satisfy ActivityStreams `Delete`. The latest run considered `126` stored records at `2026-07-10T01:07:06.186Z`, scanned `68` unique upstream targets across all `14` source providers, and found `0` upstream `404`/`410` tombstones. | Collect a real upstream `404`/`410` tombstone before emitting `Delete`, then send the second deploy-note for MetaHistoryBook to re-read `type=Delete`; keep all three callback verification rows fresh with `pnpm activity:subscription:verify`, publish retry/delivery guarantee windows, and capture refreshed `pnpm activity:adoption:matrix`, `pnpm providers:coverage:seed`, `pnpm activity:tombstone:scan`, `pnpm moma:dataset:diff` when a new MoMA snapshot is available, plus `pnpm activity:syndication:evidence` artifacts. |\n| SOTA §26 KPIs | Failing `reconciliationAutoApproveRate` and `reconciliationPrecisionReviewed`; local record-enrichment preview now passes after counting unique recognized authority references (`52/57`, `0.9123` against the `0.8` target), but it still must be regenerated from a production/postgres/warehouse records export before strict proof. AI query cost telemetry is sourced and within policy in the latest artifact. `pnpm monitoring:kpi-evidence` now creates the `monitoring/kpi-evidence.json` input from record-enrichment and reconciliation distribution counts, while `pnpm monitoring:kpi-evidence:production` requires Postgres storage, reviewed precision requires real reviewed/accepted auto-link counts plus a named production review source, acceptance rows distinguish production-shaped KPI inputs from local/current-environment exports, `diagnostics.blockers[]` names the exact observed value, Era C target, next evidence, command, and artifact for each KPI gap, `diagnostics.evidenceNeeds.enrichment.sampleRecordGaps[]` lists a capped repair sample of under-enriched record IDs plus existing recognized authority URIs and suggested Linked Art fields, `diagnostics.evidenceNeeds.reconciliation` now includes the raw candidate distribution plus a production `sourceReady` flag, `diagnostics.evidenceNeeds.reviewedPrecision` now includes a named-source readiness flag and source requirement, `diagnostics.capturePlan.rows[]` gives the production field/source checklist for enrichment, reconciliation distribution, reviewed precision, and strict refresh, and `diagnostics.handoffSummary` now reports both source-shape next evidence and `nextMetricCommand`/`nextMetricEvidenceNeeded` for the first KPI threshold blocker. `/readiness` and the `pnpm monitoring:kpi-evidence` console summary now lift the first repair target, suggested field, production source labels, raw candidate counts, and reviewed-source readiness so operators can triage the gap without opening raw JSON. | Export real production record-enrichment + reconciliation review counts to `monitoring/kpi-evidence.json`, make every KPI acceptance and capture row ready, clear `diagnostics.blockers[]`, confirm `diagnostics.handoffSummary.status` is `ready-to-refresh`, then rerun telemetry sync and Era C gates. |"},{"level":3,"heading":"Next Operating Plan","body":"1. **Deployment foundation** — ✅ preflight automation and runbook are landed (`pnpm launch:preflight`, `pnpm launch:preflight:production`, `docs/ops/deployment-preflight.md`); Neon-backed `DATABASE_URL` is seeded with all present managed storage documents, `DATABASE_URL` now verifies with `sslmode=verify-full`, `pnpm launch:smoke-token` now generates/rotates the staging researcher smoke token in `.env` without printing it, and the Next.js app is live at `https://www.metamuseum.org` on Vercel against Neon (`vercel.json`, `render.yaml`, `docs/deployment.md` landed; the close-out guard self-skips on Vercel). **Update 2026-07-03:** the rotated production `DATABASE_URL` is active, `/api/records` returns `200`, production preflight records no effective `METAMUSEUM_TEST_ROLE_OVERRIDE_TOKEN`, public base/SLO/IIIF metadata are set, validation and reconciliation Render `/health` probes pass, the active deployment is newer than the recorded secret rotation, and `pnpm launch:preflight:production` passes `20/20`. The full launch-evidence packet has been refreshed; strict readiness now waits on Era C real-world proof, not stale preflight data.\n2. **Evidence pipeline** — ✅ nightly workflow now prefers deployed-target `pnpm k6:slo`, seeds `/api/ai/query` telemetry, probes declared activity adoption, captures the public evidence-ledger live-probe packet, preserves local `pnpm k6:slo:ci` fallback, runs `pnpm longterm:evidence`, commits compact rolling k6/uptime/adoption/evidence-ledger inputs back to `main`, and uploads performance/activity/monitoring plus long-term runway artifacts; public-read uptime probe evidence is active but still needs 30 clean observation days.\n3. **Telemetry completeness** — ✅ AI query runs emit per-query usage/cost logs, and `pnpm monitoring:kpi-evidence:production` can now generate `monitoring/kpi-evidence.json` with aggregate production record-enrichment + reconciliation counts only when Postgres storage is active; still generate the real production export before the next exit-gate run.\n4. **External adoption proof** — ✅ partner/bot proof commands and runbook are landed (`pnpm activity:adoption:probe`, `pnpm activity:adoption:matrix`, `pnpm activity:community-outreach`, `pnpm activity:syndication:evidence`, `docs/ops/activity-adoption-proof.md`); the community outreach command records broad asks without turning them into strict proof, `/readiness` surfaces those rows as outreach context, and the syndication evidence command initializes an empty subscription ledger and reports durable callback rows without treating missing rows as proof. July 8 verification passed the provider tombstone-watch and local review-goals refresh, with strict ActivityStreams proof now blocked only on live `Delete` read coverage after real partner-confirmed `Update` and restored `3/3` durable callback rows. Keep validating `class: \"declared\"`, `declaredId`, `isExternal`, and recent `lastSeenAt` in `storage/activity-consumers.json`, then keep callback verification rows fresh for each counted consumer.\n5. **Launch review** — ✅ `pnpm launch:review` / `pnpm launch:review:production` aggregate latest preflight, exit-gate, security, DR, public-trust, a11y, and explore-smoke evidence into a packet, and `pnpm launch:beta:readiness` now summarizes controlled-beta go/no-go status from launch-review plus deployment-preflight artifacts. **Update 2026-07-04:** production is live on Vercel + Neon at `https://www.metamuseum.org`, the rotated production `DATABASE_URL` is active, `/api/records` returns `200`, `pnpm launch:preflight:production` passes `20/20` with explicit non-secret rotation metadata, full `pnpm launch:evidence:production` passes `8/9`, and `pnpm launch:review:production` passes `7/8`. Current launch status is governed by `pnpm review:goals:check`, which still reports external evidence required until Era C, long-term SLO/uptime, ActivityStreams, KPI, and pilot real-world blockers are green.\n6. **SaaS packaging** — ⚠️ `/pilot` is shared-nav reachable and publishes the Managed Linked Art Launch Pilot offer for concierge paid pilots, including pricing hypothesis, scope, prerequisites, support boundaries, success metrics, a commercial-readiness ledger that honestly shows 0 paid pilots, manual invoice entitlements, and in-app billing not built. The named outreach ledger now has 13 researched accounts, 1 sent message, and 0 replies: Te Papa web-form outreach was recorded at `2026-07-04T18:30:00.000Z` from the user's reported July 4, 2026 11:30 AM Pacific submission, while Museums Victoria and Art Gallery of Ontario remain researched prospects. `pnpm pilot:buyer-pack -- --submission-mode=form --omit-pricing` creates form-safe no-pricing outreach and a reply-ready packet with data checklist, seven-day timeline, sample outputs, and privacy/security notes, but the latest Te Papa pack still blocks until a real invoice reference exists. `pnpm pilot:outreach`, `pnpm pilot:activation`, `pnpm pilot:support`, `pnpm pilot:kpi`, and `pnpm pilot:evidence --markdown` convert real manual outreach, activation, support, KPI, entitlement, usage, retention, and gross-margin ledgers into explicit `blocked` or `ready` JSON plus Markdown packets while rejecting chronology errors, placeholder billing/KPI references, unsupported replies, and incomplete support states. The next SaaS evidence target is a Te Papa reply/follow-up or disqualification, then a signed invoice-referenced pilot and real tenant dataset with dated activation, support, KPI, retention, and gross-margin evidence; do not claim profitable SaaS readiness until recurring revenue, support load, retention, and gross-margin evidence are real.\n7. **Documentation currency** — ✅ every iteration must update `README.md` and `docs/roadmap.md` before `pnpm session:closeout`; `scripts/session-closeout.ts` enforces this on the normal closeout path by comparing both files to the previous closeout timestamp. Latest CI hardening keeps workflow JavaScript Actions on Node 24-native major versions while preserving the project’s Node 20 app execution path; latest UI polish keeps the home hero carousel in this current surface because clear full-color artwork imagery, attribution, reuse context, and a centered non-overlapping info panel are part of the public Linked Art trust contract.\n8. **Agent productionization** — ✅ persistent AgentTask review history is landed (`agent-tasks.json` managed storage + `/api/agents/tasks`), the internal AG2 bridge boundary is wired for Mercator/Janus behind `METAMUSEUM_AG2_BRIDGE_ENABLED`, and the local Python AG2 worker endpoint is available at `services/ag2-worker` with review-only contract tests, route-to-worker trace propagation, timeout/refusal fallback coverage, safe enablement docs, and live-worker eval artifacts via `pnpm ag2:worker:eval`. ⚠️ next value is collecting operator sign-off for any production bridge enablement; A2A/AG-UI remain deferred.\n\n---"},{"level":2,"heading":"Linked Art adherence uplift (current -> high)","body":"This section turns the current medium/medium-high areas into explicit completion criteria."},{"level":3,"heading":"A. Validation architecture depth (B2 follow-through)","body":"Current: validation architecture is in place and standards-linked.  \nTarget: high adherence through continuous standards-backed enforcement.\n\nAdherence upgrade target:\n- [x] ✅ Validation depth moved from \"in place\" to continuous fixture-backed drift enforcement.\n\nStatus:\n- [x] ✅ Complete.\n- [x] ✅ Evidence: `/explore` includes `vanda` source toggle and `/artwork/[id]` now exposes digital/IIIF manifest-image links when present in imported records.\n- [x] ✅ Provider/import transform policy is now executable via fixture manifest + tests:\n  - `tests/fixtures/validation/provider-fixture-manifest.json`\n  - `tests/quality/validation-architecture-depth.test.ts`\n- [x] ✅ Scheduled revalidation pass landed:\n  - `.github/workflows/validation-drift.yml` (weekly + manual dispatch)\n  - `scripts/validation-drift.ts`\n- [x] ✅ CI drift visibility + regression blocking landed:\n  - `.github/workflows/ci.yml` runs `pnpm validation:drift:check`\n  - net-new critical violations fail the job\n\nDefinition of done:\n- [x] ✅ Validation coverage includes object, digital, provenance, shared structures, and endpoint-shape fixtures from the reference rounds used in active slices.\n- [x] ✅ CI shows stable/no-regression validation trend for two consecutive release cycles.\n  - `config/validation-drift-cycles.json` tracks release-cycle snapshots.\n  - `pnpm validation:drift:trend` performs executable two-cycle no-regression gating."},{"level":3,"heading":"B. Provider rollout completeness (B5)","body":"Current: all planned expansion providers are landed.  \nTarget: high adherence with repeatable, standards-mapped provider slices.\n\nAdherence upgrade target:\n- [x] ✅ Provider rollout discipline is locked to keep all landed B5 providers green with standards-mapped tests (fixtures + protocol/profile checks + parity checklist).\n\n- [x] ✅ Execute remaining providers as independent slices (Louvre, Harvard, Smithsonian, V&A, Princeton, Europeana, AIC, CMA), each with:\n  - [x] ✅ adapter isolation conformance\n  - [x] ✅ fixture-anchored standards mapping\n  - [x] ✅ protocol/profile checks from B8\n- [x] ✅ Track per-provider readiness in this roadmap with explicit `not started / in progress / done` status and standards round coverage.\n\nProvider readiness matrix:\n\n| Provider | Status | Standards round coverage | B8 protocol/profile checks | Notes |\n|---|---|---|---|---|\n| Rijks | done | object + digital + provenance + shared structures + endpoint-shape fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests are landed and included in provider protocol conformance suite. |\n| NGA | done | object + digital + provenance + shared structures + endpoint-shape fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | CSV ingest/provider slice landed with adapter + profile/search/import routes + tests. |\n| Louvre | done | object + shared structures + references fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| Harvard | done | object + shared structures + endpoint-shape fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| Smithsonian | done | object + shared structures + data-discovery fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| V&A | done | object + digital + data-discovery fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| Princeton | done | object + digital + shared structures fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| Europeana | done | object + shared structures + data-discovery fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| AIC | done | object + digital + shared structures fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n| CMA | done | object + digital + shared structures fixture anchors mapped to `LinkedArtModel1.0-Reference.md` | complete | Routes + adapter + tests landed with provider facade wiring. |\n\nProvider parity checklist (all must be complete per provider before status can be set to `done`):\n- [x] ✅ identity mapping (stable URI + `equivalent` handling)\n- [x] ✅ activity/event modeling preserved (no object-person shortcut regressions)\n- [x] ✅ rights/reuse + attribution semantics preserved and surfaced\n- [x] ✅ IIIF/link-layer handling preserved when source provides it\n- [x] ✅ source provenance metadata preserved end-to-end (`_source.provider`, source URL, ingest time)\n\nDefinition of done:\n- [x] ✅ All planned B5 providers shipped with route + adapter + tests + standards mapping notes.\n- [x] ✅ Provider parity checklist complete for identity, activity modeling, rights, IIIF, and source provenance.\n- [x] ✅ Local upstream metadata mirrors are organized under `data/source-repos/` with `moma-collection` and `tate-collection`\n      kept ignored from the main app repository except for the tracked pointer README; both are documented as\n      snapshot/open-data provider candidates rather than live public API or tombstone sources.\n- [x] ✅ MoMA is now wired as a snapshot/open-data provider: `src/adapters/moma-open-data.ts` reads local JSON/CSV,\n      maps `ObjectID` to `https://www.moma.org/collection/works/{ObjectID}`, maps artists by `ConstituentID` with\n      Wikidata/ULAN `equivalent[]`, preserves `Cataloged` quality status and conservative image policy in `_source`,\n      exposes `/api/moma/*` plus `/api/providers/moma/*`, and is explicitly excluded from live `404`/`410`\n      tombstone scans pending a separate dataset-diff deletion workflow. Provider note: [providers/moma-open-data.md](providers/moma-open-data.md)."},{"level":3,"heading":"C. HAL + Search relations conformance (rounds 71-79)","body":"Current: documented in the standards reference, partially deferred in platform slices.  \nTarget: high adherence with enforceable API behavior.\n\n- [x] ✅ Add conformance tests for OrderedCollection/OrderedCollectionPage search response shapes.\n- [x] ✅ Enforce stable relation naming and discoverability contracts via search relation fields (`nextPage` / `prevPage`) and HAL-aware protocol assertions.\n- [x] ✅ Prevent inverse-relationship duplication drift by asserting search-driven inverse discovery patterns.\n\nDefinition of done:\n- [x] ✅ Representative search endpoints pass relation + pagination + shape conformance tests.\n- [x] ✅ HAL link contract tests pass for versioning, related search links, and format/profile discoverability.\n\nStatus:\n- [x] ✅ `tests/quality/hal-search-relations-conformance.test.ts` enforces response-shape + relation-contract behavior on representative direct and provider-facade search routes.\n- [x] ✅ `tests/quality/protocol-conformance.test.ts` continues to enforce HAL separation + media-type/profile behavior on public API payloads."},{"level":3,"heading":"D. Era B exit-gate closure readiness","body":"Current: Era B gate closed for the current scope.  \nTarget: keep it closed as new providers land.\n\n- [x] ✅ B6 authority-cache request-path policy enforced.\n- [x] ✅ B8/B9 conformance suites in CI.\n- [x] ✅ Postgres mode is now the default storage-of-record when `DATABASE_URL` is present.\n- [x] ✅ `storage/*.json` removed from version control.\n- [x] ✅ Write audit-log verification in CI for all primary write routes.\n\nDefinition of done:\n- [x] ✅ Era B exit gate lines are green with evidence links to tests and commands (`tests/quality/era-b-exit-gate.test.ts`, `tests/quality/protocol-conformance.test.ts`, `tests/quality/provider-protocol-conformance.test.ts`, `tests/quality/linked-art-b9-guardrails.test.ts`).\n\nAdherence upgrade target:\n- [x] ✅ Era B sustainment is continuously enforced: provider-slice conformance, authority-cache policy, and write-audit checks remain green as new sources land.\n\nExecution policy:\n- [x] ✅ No provider/validation PR merges without round + fixture-anchor standards mapping.\n- [x] ✅ No protocol-affecting merges without conformance test coverage for headers/shape/negotiation touched.\n- [x] ✅ Enforcement evidence:\n  - `.github/pull_request_template.md`\n  - `tests/quality/execution-policy-gates.test.ts`\n\n---"},{"level":2,"heading":"Stack decisions — locked in","body":"| Layer | Decision | Locked because |\n|---|---|---|\n| Framework | Next.js 16 App Router + RSC | already scaffolded; matches SOTA §11 |\n| UI lang | TypeScript 5, `strict: true` | scaffolded |\n| Styling | **Custom CSS** — design tokens + BEM-lite component classes in `app/globals.css`; no utility framework | user decision (reversed earlier Tailwind choice); resolves SOTA §30 Q3 |\n| Forms | React Hook Form + Zod | SOTA §3.2 |\n| Data fetching | RSC `fetch` first; TanStack Query for interactive client state | SOTA §11.3 + Next 16 cache-components model |\n| Server state mutations | Server Actions over fetch-based POSTs where possible | Next 16 idiom |\n| Tests | `node:test` + `node:assert` via `tsx`, Playwright for e2e, axe-core for a11y | SOTA §23 + legacy convention |\n| Pkg manager | pnpm | scaffolded |\n| Persistence (this era) | Postgres JSONB is the storage-of-record behind `src/utils/storage.ts` (`postgres` default with compatibility `file`/`double-write` modes) | preserves stable call sites during/after B3 migration |\n| Triple store (SOTA era) | **GraphDB (Ontotext)** — Community Edition for OSS path; SE/EE if SPARQL p95 demands | user decision; resolves SOTA §30 Q1 |\n| Search index (SOTA era) | **Solr 9** (LUX-aligned) | architecture decision (May 30, 2026); replaces Solr/OpenSearch fork |\n| Persistence (SOTA era) | Postgres 16 + JSONB · Solr 9 · GraphDB · pgvector | SOTA §3.4 / §8 with finalized search choice |\n| Curator backend (SOTA era) | **In-house curator console** (custom, Linked Art-native) | architecture decision (May 30, 2026); draws lessons from Arches/Ogee without adopting platform lock-in |\n| Developer/ops backend | **In-house ops console** (pipeline/debug/automation focused) | architecture decision (May 30, 2026); complements curator console |\n| Canonical ID scheme | **`https://lod.metamuseum.org/{type}/{ulid}`** | architecture decision (May 30, 2026); opaque, sortable, federation-ready |\n| Publication bridge (SOTA era) | **MediaWiki + custom Wikibase** for Meta Wiki Art publishing | aligns Linked Art/SPARQL/citation goals; see `docs/meta-wiki-art-bridge.md` |\n\n**Previously deferred architecture decisions (now finalized, May 30, 2026):**\n- [x] ✅ Search engine: **Solr 9** (LUX-aligned).\n- [x] ✅ Curator backend: **in-house custom curator console**.\n- [x] ✅ Developer backend: **in-house ops console**.\n- [x] ✅ Canonical ID scheme: **`https://lod.metamuseum.org/{type}/{ulid}`**.\n\n---"},{"level":2,"heading":"Era delivery history","body":"All three delivery eras are complete (see Status above):\n\n- **Era A — The Lift** (10 PR-sized slices): TDD foundations, Met + Getty verticals, records/artworks/entities, Linked Art inspector, patterns/graph, issues/SSE, agents/jobs/content, workspace chrome.\n- **Era B — Hardening** (B1–B10): Zod contracts + schema versioning, formal validation, Postgres, auth + roles, 14-provider expansion, authority caching, exhibition/literature reconciliation, protocol + modeling guardrails, ARK conformance, gateway readiness.\n- **Era C — SOTA** (C1–C5): multi-modal storage + HAL, ETL + reconciliation + mapper, IIIF + visualizations, the AI layer, syndication + Meta Wiki Art + security/privacy hardening.\n\nThe full slice-by-slice and B-/C-series implementation detail is archived in **[progress/era-history.md](progress/era-history.md)**. The active forward plan is **[roadmap-to-10.md](roadmap-to-10.md)**.\n\n---"},{"level":2,"heading":"Cross-cutting standards (apply from Slice 1 onward)","body":"These are not phases — they are continuous quality gates. Borrowed from `_legacy/AGENTS.md` and SOTA §23.\n\n- [x] ✅ **AIDD + TDD is the default.** Define behavior in natural language and map standards rounds/fixture anchors first, then write the failing test (red), pass with minimum code (green), and refactor with the suite green. Tests are the spec; reviewers read tests before reading implementation. A failing test stops the line. See [CLAUDE.md](../CLAUDE.md) §\"We lead with AIDD + TDD\".\n- [x] ✅ **Adapters do not import each other.** Bridge via `src/utils/artwork-builder.ts`.\n- [x] ✅ **Contracts are leaf modules.** No upstream deps.\n- [x] ✅ **`_source.raw` is immutable.** Transform at read time.\n- [x] ✅ **Rights-aware by default.** Every UI surface showing an image carries reuse status + attribution.\n- [x] ✅ **Linked Art JSON-LD is the canonical data layer.** UI DTOs (`Artwork`) are separate; map at the boundary.\n- [x] ✅ **Loading / empty / error / success states** on every interactive UI.\n- [x] ✅ **Keyboard navigation + visible focus** on every interactive surface.\n- [x] ✅ **At least one test for any risky transform.**\n- [x] ✅ **Cite or refuse.** Generated content always carries citations + rights + review state.\n- [x] ✅ **Next 16 specifics**: `cookies()`, `headers()`, dynamic `params` are async — always `await` them.\n- [x] ✅ **No `unknown` swallowed silently.** A record with unknown rights gets an explicit \"Rights unknown — do not reuse\" badge.\n- [x] ✅ **Reference-driven conformance.** Any provider/API/schema/search/protocol PR must cite relevant [linked-art/LinkedArtModel1.0-Reference.md](linked-art/LinkedArtModel1.0-Reference.md) rounds and include failing-first tests mapped to the referenced fixture anchors.\n- [x] ✅ **Reference maintenance loop.** If a PR depends on newly published Linked Art guidance not yet captured in `LinkedArtModel1.0-Reference.md`, that round/addendum must be appended before (or in the same change as) the implementation PR.\n- [x] ✅ **Standards Mapping is required in provider/validation PRs.** Include: referenced round numbers, fixture anchors exercised, and failing-first test files proving red→green conformance.\n- [x] ✅ **PR template completion is required.** Every PR must complete [.github/pull_request_template.md](../.github/pull_request_template.md), including AIDD checklist gates, standards mapping, and protocol assertions touched.\n- [x] ✅ **AI-generated tests/refactors require human semantic verification.** AI can accelerate drafting, but authors/reviewers remain accountable for Linked Art correctness, provider semantics, and protocol behavior.\n- [x] ✅ **Provider/pipeline boundary drift is actively bounded.** `docs/risk-register.md` tracks risk posture and `tests/contracts/provider-boundary-contracts.test.ts` enforces adapter import boundaries.\n- [x] ✅ **Protocol conformance is mandatory.** Public API behavior must preserve JSON-LD context/profile correctness, support `GET` + `OPTIONS`, and provide baseline CORS + media-type negotiation.\n- [x] ✅ **AI-RSI compounding loop is mandatory.** Each merge requires: 72h review check, evidence capture in session log, and roadmap/README/CLAUDE updates before the next RSI expansion scope.\n- [x] ✅ **HAL/data separation is mandatory.** API navigation metadata lives in `_links` (non-semantic) and must not pollute semantic graph payloads.\n- [x] ✅ **URI opacity is mandatory.** Never infer semantics from URI path structure in router logic or client helpers.\n- [x] ✅ **Inverse discovery via Search API.** Prefer standardized search relations and OrderedCollection/OrderedCollectionPage responses rather than duplicating inverse relationship fields.\n- [x] ✅ **Carrier/content separation is non-negotiable.** `HumanMadeObject`/`DigitalObject` must remain distinct from `VisualItem`/`LinguisticObject`.\n- [x] ✅ **Authority-backed classification UX.** Curatorial/classification input paths must use controlled authority sources (AAT/ULAN/Wikidata equivalents), not free-text categories by default.\n- [x] ✅ **Data discovery signposting.** Public record HTML pages expose a single canonical `describedby` link to the Linked Art JSON-LD record.\n\nVerification note (May 31, 2026):\n- The first nine cross-cutting gates above are marked complete based on current enforcement in CI/tests and live implementation patterns (provider boundary checks, contract leaf structure, `_source.raw` invariants, rights surfaces, Linked Art boundary mapping, interactive state handling, and keyboard/focus coverage).\n- Additional governance/protocol gates are marked complete where enforced by executable tests (`protocol-conformance`, `provider-protocol-conformance`, `hal-search-relations-conformance`, `provider-digital-content-gates`) and PR governance checks (`execution-policy-gates`, PR template standards mapping requirements).\n- Remaining open gates in this section are intentionally left unchecked only where future era scope is intentionally deferred; cross-cutting gate set above is now fully enforced in current Era A/B surfaces.\n\n---"},{"level":2,"heading":"What this roadmap deliberately does NOT do (yet)","body":"To stay honest about scope:\n\n- [x] ✅ **No microservice split during Era A.** All routes lived in the single Next 16 app; Python services begin in Era B (validation) and Era C (reconciliation, AI).\n- [x] ✅ **No triple store or vector store in Era A or B.** Postgres + JSONB remains sufficient until Era C search/graph patterns are activated.\n- [x] ✅ **No module-federation for the Era A app.** The app remains a single deployable Next.js build.\n- [x] ✅ **No Arches / Ogee / Zelge adoption planned.** Lessons are reused, but curator and ops surfaces remain in-house.\n- [x] ✅ **No fancy IIIF in Era A.** Current Era A/B UI uses provider image URLs; OpenSeadragon remains planned for C3.\n- [x] ✅ **No NL→SPARQL until the SHACL gate exists** (B2 → C4).\n- [x] ✅ **No Meta Wiki Art write path** until contracts, validation, auth, audit log, and Postgres cutover are stable (C5).\n\nVerification note (May 31, 2026):\n- Constraints above are verified against current code/routes/dependencies and remain in force for pre-Era-C scope control.\n\n---"},{"level":2,"heading":"What I'd build next, concretely","body":"Era C1 prep while sustaining Era B quality gates:\n- [x] ✅ **RSI-5: AI evidence drift + citation freshness** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - Owner: Platform + AI Reliability\n  - **Action 1 complete (2026-06-09):** `/api/ai/query` now returns citation metadata, coverage, and explicit refusal state, locked by `tests/api/ai-query.test.ts` and `tests/quality/cite-or-refuse-conformance.test.ts`.\n  - **Action 2 complete (2026-06-09):** `/api/ai/query` and `/api/ai/chat` now emit `retrievedAt` + `citationFreshness` diagnostics and refuse stale evidence via policy-backed route tests.\n  - Scope:\n    - Enforce same cite-or-refuse behavior beyond `/api/ai/chat` so `/api/ai/query` emits stable evidence metadata and refuses under coverage/freshness thresholds.\n    - Keep `/api/ai/chat` grounded response semantics while adding the shared freshness guard.\n  - Acceptance:\n    - `/api/ai/query` returns `{ answer, citations, coverage, citationFreshness, refusalReason? }` with `entityId`, `propertyPath`, `sourceUrl`, and `retrievedAt` in cited outputs.\n    - Under-cited or stale-evidence answers return explicit refusal and reason.\n    - Regression test coverage proves chat/query parity and stale-evidence failure modes.\n  - Proof packet:\n    - `tests/api/ai-query.test.ts`, `tests/api/ai-chat.test.ts`, and `tests/quality/cite-or-refuse-conformance.test.ts` cover cited success, coverage refusal, and freshness refusal behavior.\n    - Close-out packet synchronizes `docs/risk-register.md`, `CLAUDE.md`, `README.md`, and this roadmap with evidence proofs.\n- [x] ✅ **RSI-6: AI eval drift baselines include citation freshness** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/services/ai-eval-harness.ts` now scores `citationFreshness` from actual citation/source timestamps instead of treating retrieval time as always fresh.\n  - `src/services/ai-eval-regression.ts`, `scripts/ai-eval-gate.ts`, and `config/ai-eval-regression-policy.json` now baseline, persist, print, and fail-fast on `citationFreshnessDrop`.\n  - `evals/golden-museum-questions.v1.json` and `docs/evals/golden-museum-questions.md` now declare `citationFreshnessThreshold = 0.95`.\n  - Proof packet: `tests/services/ai-eval-harness.test.ts`, `tests/services/ai-eval-regression.test.ts`, `tests/services/ai-eval-artifacts.test.ts`, `tests/quality/ai-eval-golden-dataset.test.ts`, plus direct AI eval gate output with `citationFreshness=1` and `citationFreshnessDrop=0`.\n- [x] ✅ **RSI-7: AI eval summary badges + aging-pressure alerting** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/services/ai-eval-artifacts.ts` now renders `artifacts/evals/summary.md` with status, faithfulness, relevance, citation accuracy, `citationFreshness`, pass-rate badges, artifact links, and alerts.\n  - `src/services/ai-eval-harness.ts` now persists citation freshness aging (`oldestAgeDays`, `oldestAgeRatio`, `maxAgeDays`) for trend-aware pressure detection.\n  - `.github/workflows/ai-eval-gate.yml` now appends the summary to `$GITHUB_STEP_SUMMARY` and uploads `artifacts/evals/` for CI inspection.\n  - Proof packet: `tests/services/ai-eval-artifacts.test.ts`, plus direct AI eval gate output with `summary=artifacts/evals/summary.md`, `citationFreshness=1`, and `oldestAgeRatio=0`.\n- [x] ✅ **RSI-8: AI eval artifact dashboard visibility** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/services/ai-eval-dashboard.ts` now loads ignored local eval artifacts, latest metrics, trend runs, artifact timestamps, and freshness-aging warnings with explicit empty/partial states.\n  - `app/(workspace)/ai-evals/page.tsx` now provides a read-only app dashboard for latest eval summary, trend index, freshness-aging state, and active warnings.\n  - Navigation now exposes the dashboard from the workspace sidebar, primary Workspace menu, and footer.\n  - Proof packet: `tests/services/ai-eval-dashboard.test.ts` and `tests/pages/ai-evals-page.test.ts`, plus focused system Node test output passing 3 tests.\n- [x] ✅ **RSI-9: latest-vs-previous AI eval artifact diff** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/services/ai-eval-dashboard.ts` now computes latest-vs-previous metric deltas, freshness-aging pressure deltas, status changes, prompt-count changes, identity changes, and compact “what changed” notes.\n  - `app/(workspace)/ai-evals/page.tsx` now renders a “Latest vs previous run” review section with metric arrows, freshness pressure movement, and fast-review notes.\n  - Proof packet: `tests/services/ai-eval-dashboard.test.ts` and `tests/pages/ai-evals-page.test.ts`, plus focused system Node test output passing 3 tests.\n- [x] ✅ **RSI-10: severity-labeled AI eval diff triage** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/services/ai-eval-dashboard.ts` now classifies metric deltas, freshness-aging pressure movement, and overall latest-vs-previous diff priority as `regression`, `watch`, `stable`, or `improved`.\n  - `app/(workspace)/ai-evals/page.tsx` now renders priority labels and visible metric/aging threshold pills so review starts with severity instead of raw deltas only.\n  - Proof packet: `tests/services/ai-eval-dashboard.test.ts` and `tests/pages/ai-evals-page.test.ts`, plus focused system Node output passing 3 tests, `pnpm test` passing 787/249, `pnpm lint` passing with one existing warning, and production Next build passing via system Node.\n- [x] ✅ **RSI-11: policy-driven AI eval priority visibility** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `config/ai-eval-regression-policy.json` now owns diff severity thresholds consumed by dashboard and CI summary generation.\n  - `app/(workspace)/ai-evals/page.tsx` now shows last-N severity distribution across `regression`, `watch`, `stable`, and `improved` comparisons.\n  - `artifacts/evals/summary.md` now includes CI-visible review priority when at least two retained eval runs exist.\n  - Proof packet: `tests/services/ai-eval-dashboard.test.ts`, `tests/services/ai-eval-artifacts.test.ts`, and `tests/pages/ai-evals-page.test.ts`, plus focused system Node output passing 8 tests, `pnpm test` passing 788/249, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build passing via system Node.\n- [x] ✅ **RSI-12: AI eval agent-summary reliability** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/services/ai-eval-severity.ts` now validates `diffSeverityPolicy` shape/order and fails malformed policy with explicit errors.\n  - `/api/ai-evals/summary` now exposes agent-ready JSON with latest run, review priority, severity distribution, severity history, alerts, and artifact links.\n  - `app/(workspace)/ai-evals/page.tsx` now renders compact severity sparkline and latest-vs-previous comparison history for fast trend review.\n  - Proof packet: `tests/services/ai-eval-severity.test.ts`, `tests/api/ai-evals-summary.test.ts`, `tests/services/ai-eval-dashboard.test.ts`, `tests/services/ai-eval-artifacts.test.ts`, and `tests/pages/ai-evals-page.test.ts`, plus focused system Node output passing 13 tests, `pnpm test` passing 793/251, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai-evals/summary`.\n- [x] ✅ **RSI-13: AI eval contract + CI annotation reliability** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/contracts/zod/ai-eval-summary.ts` now defines the reusable agent JSON contract and OpenAPI schema for `/api/ai-evals/summary`.\n  - `config/ai-eval-regression-policy.json` now owns `severityHistoryPolicy.maxComparisons`, which drives dashboard distribution/history and agent JSON window metadata.\n  - `scripts/ai-eval-gate.ts` now emits a GitHub PR warning annotation when review priority is `watch` or `regression`, with workflow path filters covering `/api/ai-evals`, policy, and contract changes.\n  - Proof packet: `tests/api/ai-evals-summary.test.ts`, `tests/api/openapi.test.ts`, `tests/services/ai-eval-severity.test.ts`, `tests/services/ai-eval-dashboard.test.ts`, `tests/services/ai-eval-artifacts.test.ts`, and `tests/pages/ai-evals-page.test.ts`, plus focused system Node output passing 18 tests, `pnpm test` passing 796/251, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai-evals/summary`.\n- [x] ✅ **RSI-14: AI eval version/pruning hygiene** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `/api/ai-evals/summary` now returns `schemaVersion: 1`, and `aiEvalSummaryResponseSchema` rejects unsupported versions before agents consume the payload.\n  - `docs/evals/golden-museum-questions.md` publishes exact GitHub CI annotation examples for `regression` and `watch`, with snapshot assertions keeping docs and formatter output aligned.\n  - `src/services/ai-eval-artifacts.ts` now prunes orphaned run JSON outside the retained trend window while preserving retained run files and non-JSON notes.\n  - Proof packet: `tests/api/ai-evals-summary.test.ts`, `tests/api/openapi.test.ts`, and `tests/services/ai-eval-artifacts.test.ts`, plus focused system Node output passing 22 tests, `pnpm test` passing 800/251, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai-evals/summary`.\n- [x] ✅ **RSI-15: AI eval migration/reporting visibility** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `src/contracts/zod/ai-eval-summary.ts` now includes future `schemaVersion: 2` migration notes, with v1 accepted and planned v2 rejected through fixture compatibility tests.\n  - `src/services/ai-eval-artifacts.ts` now returns a retention pruning report with `delete`/`dry-run` mode and retained/orphaned/deleted/preserved file counts.\n  - `artifacts/evals/summary.md` now surfaces latest CI annotation status and retention pruning status for PR/build reviewers.\n  - Proof packet: `tests/api/ai-evals-summary.test.ts`, `tests/services/ai-eval-artifacts.test.ts`, and `tests/fixtures/ai-eval-summary/*`, plus focused system Node output passing 24 tests, `pnpm test` passing 802/251, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai-evals/summary`.\n- [x] ✅ **RSI-16: AI eval summary artifact/schema visibility** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `tests/fixtures/ai-eval-summary/summary-snapshot.md` now locks the generated CI summary markdown, proving `summary.md` stays reviewable and stable.\n  - `/api/ai-evals/summary` now exposes the latest `retentionPruneReport` for agents, including delete/dry-run mode and retained/orphaned/deleted/preserved counts.\n  - `/api/openapi` now includes the AI eval summary schema migration compatibility table so future `schemaVersion` upgrades are discoverable from the contract surface.\n  - Proof packet: `tests/api/ai-evals-summary.test.ts`, `tests/api/openapi.test.ts`, `tests/services/ai-eval-artifacts.test.ts`, `tests/services/ai-eval-dashboard.test.ts`, and `tests/fixtures/ai-eval-summary/summary-snapshot.md`, plus focused system Node output passing 25 tests, `pnpm test` passing 803/251, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai-evals/summary`.\n- [x] ✅ **RSI-17: Visual ETL Mapper AI-assist safety** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `/api/ai/mapping-assist` now returns review-ready, contract-valid `MappingTemplate` drafts from source columns with confidence, rationale, standards anchors, and unmapped-column diagnostics.\n  - `/etl/mapper` now exposes a \"Suggest mapping with AI\" action while keeping generated mappings review-only before any ingestion activation.\n  - Unknown columns are surfaced as diagnostics instead of invented mappings.\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`, plus focused system Node output passing 7 mapper-assist tests and 2 OpenAPI tests, `pnpm test` passing 809/253, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai/mapping-assist`.\n- [x] ✅ **RSI-18: mapper-assist fixture/schema/importability hardening** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `tests/fixtures/mapping-assist/tricky-columns.json` now locks tricky rights/credit/sensitive columns so mapper assist cannot invent unsafe Linked Art target paths.\n  - `src/utils/etl-mapper-assist.ts` and `/etl/mapper` now support importing returned suggestions as reviewable ReactFlow draft nodes/edges.\n  - `/api/openapi` now exposes `MappingAssistResponse` and references it from `/api/ai/mapping-assist`.\n  - Proof packet: `tests/services/mapping-assist.test.ts`, `tests/utils/etl-mapper-assist.test.ts`, `tests/components/etl-mapper-config.test.ts`, `tests/api/openapi.test.ts`, and `tests/api/ai-mapping-assist.test.ts`, plus focused system Node output passing 11 tests, `pnpm test` passing 811/254, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai/mapping-assist`.\n- [x] ✅ **RSI-19: provider-family/browser/request-schema mapper hardening** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `tests/fixtures/mapping-assist/provider-families.json` now covers Met, Getty, and Rijks-style mapper columns with allowed-path and must-stay-unmapped assertions.\n  - `scripts/smoke-etl-mapper-assist.ts` and `pnpm smoke:etl:mapper-assist` now provide a Playwright browser smoke for `/etl/mapper` assist generation plus draft import.\n  - `/api/openapi` now exposes `MappingAssistRequest` and attaches it to the `/api/ai/mapping-assist` POST request body.\n  - Proof packet: `tests/services/mapping-assist.test.ts`, `tests/scripts/etl-mapper-smoke-script.test.ts`, and `tests/api/openapi.test.ts`, plus focused system Node output passing 7 tests, `pnpm smoke:etl:mapper-assist` passing against `http://localhost:3001/en`, `pnpm test` passing 813/255, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build confirming `/api/ai/mapping-assist`.\n- [x] ✅ **RSI-20: negative mapper fixtures, visual screenshot, and API-doc examples** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `tests/fixtures/mapping-assist/negative-provider-families.json` now locks near-miss Met/Getty/Rijks columns so rights, credit, restriction, sensitivity, donor, and flag wording cannot trigger unsafe suggestions.\n  - `scripts/smoke-etl-mapper-assist.ts` now waits on the mapping-assist POST, imports the draft, and writes `artifacts/smoke/etl-mapper-assist-imported.png` with animations disabled.\n  - `/api/docs` now includes concrete mapping-assist request and response examples next to the Swagger UI entry point.\n  - Proof packet: `tests/services/mapping-assist.test.ts`, `tests/scripts/etl-mapper-smoke-script.test.ts`, and `tests/api/docs.test.ts`, plus focused system Node output passing 11 tests, `pnpm smoke:etl:mapper-assist` passing against `http://localhost:3001/en`, `pnpm test` passing 814/255, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build passing.\n- [x] ✅ **RSI-21: mapper layout, OpenAPI-sourced docs examples, and confidence policy** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `app/globals.css` now gives mapper actions a full-width wrapped row with no button overlap in the refreshed screenshot.\n  - `app/api/docs/route.ts` now renders mapping-assist request/response examples from `/api/openapi` instead of duplicating static JSON.\n  - `src/services/mapping-assist.ts` now applies a minimum confidence policy so lower-confidence accession/place/description patterns stay diagnostics-only.\n  - Proof packet: `tests/components/etl-mapper-config.test.ts`, `tests/api/docs.test.ts`, and `tests/services/mapping-assist.test.ts`, plus focused system Node output passing 15 tests, `pnpm smoke:etl:mapper-assist` refreshing `artifacts/smoke/etl-mapper-assist-imported.png`, `pnpm test` passing 817/255, `pnpm lint` passing with one existing warning, AI eval gate printing `reviewPriority: stable`, and production Next build passing.\n- [x] ✅ **RSI-22: public source narrative and trust uplift** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `app/datasets/page.tsx` now presents a public source-network narrative backed by `getProviderCapabilities`, not copied prototype constants.\n  - `app/about/page.tsx` and `app/projects/page.tsx` now migrate the sibling `meta-museum-art` mission/project surfaces into native Next pages while replacing coming-soon/static project cards with live source-network, Linked Art workbench, and Meta Wiki Art workflow links.\n  - `src/services/site-metadata.ts` centralizes OpenGraph/Twitter metadata with a reviewed local image, and footer navigation exposes Contact/Privacy/Terms plus asset provenance.\n  - `docs/asset-provenance.md` tracks all preserved sibling visual assets with SHA-256 hashes while marking placeholder thumbnails as excluded from public use and keeping copied legal text out.\n  - Proof packet: `tests/services/public-source-narrative.test.ts`, `tests/pages/public-source-pages.test.ts`, focused RSI-22 test output passing 6/2 plus follow-up `pnpm test -- tests/pages/public-source-pages.test.ts` passing 851/267 on 2026-06-09, `pnpm lint` passing with one existing warning, `pnpm build` passing, and screenshot proof at `artifacts/smoke/datasets-page.png`.\n- [x] ✅ **RSI-23: public-source agent API and trust smoke hardening** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `/api/public-sources/summary` now exposes schema-versioned agent JSON for source stats, provider capability flags, and imported asset provenance.\n  - `docs/asset-provenance.md` and `src/contracts/zod/public-sources-summary.ts` now enforce explicit license-review statuses so unknown asset rows fail.\n  - `pnpm smoke:public-trust` captures browser screenshots for `/datasets`, `/contact`, `/privacy`, and `/terms`; the nested `meta-museum-art` prototype copy was removed after all images were preserved and inventoried.\n  - Proof packet: `tests/api/public-sources-summary.test.ts`, `tests/services/public-source-narrative.test.ts`, `tests/scripts/public-trust-smoke-script.test.ts`, focused RSI-23 output passing 6/3, `pnpm smoke:public-trust` passing against `http://localhost:3001`, `pnpm test` passing 827/259, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-24: OpenAPI, checksum drift, and screenshot retention hardening** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `/api/openapi` now includes `PublicSourcesSummaryResponse` and references it from `/api/public-sources/summary`.\n  - Imported public-source assets now fail tests when their SHA-256 hashes drift from `docs/asset-provenance.md`.\n  - `pnpm smoke:public-trust` now writes timestamped screenshot runs, latest copies, previous-run links, a summary JSON, and prunes old runs outside the retention window.\n  - Proof packet: `tests/api/openapi.test.ts`, `tests/services/public-source-narrative.test.ts`, `tests/services/public-trust-smoke-artifacts.test.ts`, `tests/scripts/public-trust-smoke-script.test.ts`, focused RSI-24 output passing 6/5, `pnpm smoke:public-trust` passing against `http://localhost:3001`, `pnpm test` passing 828/260, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-25: public trust docs, diff metadata, and CI artifact visibility** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `/api/docs` now renders the `/api/public-sources/summary` response example from `/api/openapi`, keeping examples single-source for humans and agents.\n  - Public trust smoke artifacts now include latest-vs-previous checksum/byte diff metadata per screenshot plus CI-ready summary markdown.\n  - `.github/workflows/public-trust-smoke.yml` now builds, runs `pnpm smoke:public-trust`, appends public trust summary links to `$GITHUB_STEP_SUMMARY`, and uploads `public-trust-smoke-artifacts`.\n  - Proof packet: `tests/api/docs.test.ts`, `tests/api/openapi.test.ts`, `tests/services/public-trust-smoke-artifacts.test.ts`, `tests/services/public-trust-smoke-ci-summary.test.ts`, `tests/scripts/public-trust-smoke-script.test.ts`, `tests/scripts/public-trust-ci-workflow.test.ts`, focused RSI-25 output passing 12/10, `pnpm smoke:public-trust` passing against temporary `http://localhost:3001`, `pnpm test` passing 830/262, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-26: pixel-diff thresholds, public trust summary API, and main CI artifact links** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `pnpm smoke:public-trust` now decodes PNG screenshots, computes changed-pixel ratios, and fails when `PUBLIC_TRUST_SCREENSHOT_PIXEL_DIFF_THRESHOLD` is exceeded.\n  - `/api/public-trust/summary` now exposes schema-versioned latest public trust smoke artifacts and pixel-diff status for agents.\n  - `.github/workflows/ci.yml` now runs public trust smoke, appends summary links to `$GITHUB_STEP_SUMMARY`, and uploads `public-trust-smoke-artifacts`.\n  - Proof packet: `tests/services/public-trust-smoke-artifacts.test.ts`, `tests/api/public-trust-summary.test.ts`, `tests/api/openapi.test.ts`, `tests/scripts/public-trust-smoke-script.test.ts`, `tests/scripts/public-trust-ci-workflow.test.ts`, focused RSI-26 output passing 10/7, `pnpm smoke:public-trust` passing with `unchanged=4` and `pixel failures=0`, `pnpm test` passing 834/263, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-27: public trust per-page thresholds, OpenAPI docs example, and retention badge** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - Public trust smoke now applies stricter `/datasets` pixel drift policy (`0.005`) than Contact/Privacy/Terms legal pages (`0.02`).\n  - `/api/docs` now renders the `/api/public-trust/summary` response example from `/api/openapi`.\n  - Public trust CI summary now includes a retention badge snapshot-locked by `tests/fixtures/public-trust-summary/summary-snapshot.md`.\n  - Proof packet: `tests/services/public-trust-smoke-artifacts.test.ts`, `tests/scripts/public-trust-smoke-script.test.ts`, `tests/services/public-trust-smoke-ci-summary.test.ts`, `tests/fixtures/public-trust-summary/summary-snapshot.md`, `tests/api/docs.test.ts`, `tests/api/openapi.test.ts`, focused RSI-27 output passing 13/9, `pnpm smoke:public-trust` passing with per-page thresholds, `unchanged=4`, and `pixel failures=0`, `pnpm test` passing 835/263, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-28: JSON public trust threshold policy, agent-visible policy, and CI drift annotations** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - Public trust smoke policy is now persisted in `config/public-trust-smoke-policy.json` and consumed by `scripts/smoke-public-trust-pages.ts`.\n  - `/api/public-trust/summary` now exposes the applied threshold policy for agents alongside latest smoke artifacts.\n  - CI summary tooling now emits warning annotations when screenshots change but remain under their configured threshold.\n  - Proof packet: `config/public-trust-smoke-policy.json`, `src/services/public-trust-smoke-policy.ts`, `scripts/smoke-public-trust-pages.ts`, `src/services/public-trust-summary.ts`, `src/contracts/zod/public-trust-summary.ts`, `src/services/public-trust-smoke-ci-summary.ts`, `tests/services/public-trust-smoke-policy.test.ts`, `tests/scripts/public-trust-smoke-script.test.ts`, `tests/api/public-trust-summary.test.ts`, `tests/services/public-trust-smoke-ci-summary.test.ts`, `tests/scripts/public-trust-ci-workflow.test.ts`, focused RSI-28 output passing 20/12, `pnpm smoke:public-trust` passing with JSON policy thresholds, `unchanged=4`, and `pixel failures=0`, `pnpm test` passing 838/264, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-29: public trust reviewer rationale, schema rejection, and severity-grouped PR summaries** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - Public trust policy pages now carry `reviewSeverity`, `reviewerNote`, and `reasonCodes`.\n  - `/api/public-trust/summary` now exposes reviewer rationale metadata in the applied threshold policy for agents.\n  - Public trust CI summaries and warning annotations now group under-threshold visual drift by high/medium/low route severity.\n  - Proof packet: `config/public-trust-smoke-policy.json`, `src/services/public-trust-smoke-policy.ts`, `src/services/public-trust-summary.ts`, `src/contracts/zod/public-trust-summary.ts`, `scripts/smoke-public-trust-pages.ts`, `src/services/public-trust-smoke-ci-summary.ts`, `tests/services/public-trust-smoke-policy.test.ts`, `tests/api/public-trust-summary.test.ts`, `tests/services/public-trust-smoke-ci-summary.test.ts`, `tests/fixtures/public-trust-summary/summary-snapshot.md`, focused RSI-29 output passing 18/11 plus CI-summary focused retest 2/1, `pnpm exec start-server-and-test \"pnpm dev\" http://localhost:3000 \"pnpm smoke:public-trust\"` passing with JSON policy metadata and `pixel failures=0`, `pnpm test` passing 839/264, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-30: owner/reviewer initials, schema v2 fixture, and grouped annotation snapshot** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - Public trust policy pages now carry `ownerInitials` and `reviewerInitials` alongside severity, notes, and reason codes.\n  - `/api/public-trust/summary` and CI drift summary rows now expose owner/reviewer initials for fast review routing.\n  - Planned `schemaVersion: 2` policy fixture and grouped warning annotation snapshot are now committed as executable contract evidence.\n  - Proof packet: `config/public-trust-smoke-policy.json`, `src/services/public-trust-smoke-policy.ts`, `src/services/public-trust-summary.ts`, `src/contracts/zod/public-trust-summary.ts`, `src/services/public-trust-smoke-ci-summary.ts`, `tests/fixtures/public-trust-policy/schema-v2-planned.json`, `tests/fixtures/public-trust-summary/grouped-annotations-snapshot.txt`, `tests/services/public-trust-smoke-policy.test.ts`, `tests/api/public-trust-summary.test.ts`, `tests/services/public-trust-smoke-ci-summary.test.ts`, focused RSI-30 tests passing 15/10, `pnpm exec start-server-and-test \"pnpm dev\" http://localhost:3000 \"pnpm smoke:public-trust\"` passing with `unchanged=4` and `pixel failures=0`, isolated transient `tests/api/artworks/by-id.test.ts` retest passing, final `pnpm test` passing 839/264, `pnpm lint` passing with one existing warning, and `pnpm build` passing.\n- [x] ✅ **RSI-1: Provider/pipeline boundary drift hardening** (High-severity remediation) is closed and proven:\n  - boundary contract test passes with allowed shared imports only,\n  - full `pnpm test` + `pnpm lint` + `pnpm build` cycle passed in-cycle,\n  - close-out evidence synchronized in `CLAUDE.md`, `README.md`, and this roadmap.\n- [x] ✅ **RSI-2: UI journey automation scope** (Medium-severity remediation) is closed and proven:\n  - matrix automation now spans role/provider combinations (`pnpm smoke:explore:matrix`), and `/api/objects`, `/api/works`, `/api/agents`, `/api/places`, `/api/sets` route assertions verify positive + negative paths for imported records,\n  - smoke probe evidence and status updates are synchronized in `CLAUDE.md`, this roadmap, and `README.md`.\n- [x] ✅ **RSI-3: Single-file and process-complexity reduction** (Low-severity remediation) is complete (proven 2026-06-09):\n  - Owner map and top-complexity target inventory are now finalized in `docs/risk-register.md` (Action 1 complete).\n  - Action 2 is complete: `src/services/publish-queue-worker.ts` split into helper modules with existing tests preserved.\n  - Action 3 is complete: `src/services/issues.ts` split into focused modules under `src/services/issues/` with behavior preserved.\n  - Action 4 is complete: `src/services/outbox.ts` split into focused modules under `src/services/outbox/` with behavior preserved.\n  - Action 5 is complete: `src/services/reconciliation.ts` split into focused modules under `src/services/reconciliation/` with behavior preserved.\n  - Action 6 is complete: `src/services/wiki-publish.ts` split into focused modules under `src/services/wiki-publish/` with behavior preserved.\n  - Action 7 is complete: `src/services/monitoring-telemetry.ts` split into focused modules under `src/services/monitoring-telemetry/` with behavior preserved.\n  - Proof packet: `pnpm test`, `pnpm lint`, and `pnpm build` for the closeout cycle, plus synchronized updates in `CLAUDE.md`, `docs/roadmap.md`, and `README.md` with this evidence path.\n  - Action 8 is complete: `scripts/authority-cache-refresh.ts` split into focused modules under `scripts/authority-cache-refresh/` with behavior preserved.\n  - Proof packet: `pnpm test`, `pnpm lint`, and `pnpm build` with synchronized updates in `CLAUDE.md`, `docs/roadmap.md`, and `README.md`.\n  - Action 9 is complete: `src/services/ai-layer.ts` split into focused modules under `src/services/ai-layer/` with API preserved in the facade.\n  - Proof packet: `pnpm test`, `pnpm lint`, and `pnpm build` all passed, with close-out updates synchronized in `CLAUDE.md`, `README.md`, and `docs/risk-register.md`.\n- [x] ✅ **RSI-4: Chat grounding and citation enforcement** (Medium-severity remediation) is complete (proven 2026-06-09):\n  - `/api/ai/chat` now returns sentence-level grounded citations (`[entityId, propertyPath]`) and refuses output when citation coverage is incomplete.\n  - Evidence is implemented in `app/api/ai/chat/route.ts` and `src/services/ai-chat.ts`.\n  - Proof packet: `tests/api/ai-chat.test.ts`, `pnpm test` (full suite), `pnpm lint`, and `pnpm build`; close-out updates synchronized in `CLAUDE.md`, `README.md`, and `docs/risk-register.md`.\n- [x] ✅ Expand HAL `_links` discoverability coverage from representative routes to all public entity-role routes as they land (enforced through protocol + provider conformance suites and `hal-entity-discoverability-conformance` quality checks).\n- [x] ✅ Add search-relation conformance breadth tests across additional provider search endpoints (beyond representative NGA/RKD/facade checks) with pagination drift assertions.\n  - Evidence: [`tests/quality/hal-search-relations-conformance.test.ts`](/C:/Projects/metamuseum/tests/quality/hal-search-relations-conformance.test.ts) now validates relation and pagination behavior for Louvre, Harvard, Smithsonian, V&A, Princeton, Europeana, AIC, CMA, and Rijks routes in addition to existing representative checks.\n- [x] ✅ Keep execution-policy gates strict: no provider/validation merges without standards mapping + fixture anchors, and no protocol-affecting merges without conformance coverage.\n  - Evidence: [`tests/quality/execution-policy-gates.test.ts`](/C:/Projects/metamuseum/tests/quality/execution-policy-gates.test.ts), [`.github/pull_request_template.md`](/C:/Projects/metamuseum/.github/pull_request_template.md).\n\nSuggested branch name (used): `codex/era-c1-hal-search-conformance-breadth`.\n\n<!-- 2026-07-01T18:13:00Z fix: TS6 compat -->\n\n<!-- 2026-07-01T18:28:55Z style: homepage color refresh -->\n<!-- last-session: 2026-07-01T19:07:54Z -->\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---"}]}