Linked Art 1.0 — Conformance Matrix
A candid, evidence-based view of how Meta Museum conforms to the Linked Art 1.0 API — at the protocol layer, the model layer, and per provider. For the full standards ledger see LinkedArtModel1.0-Reference.md(LinkedArtModel1.0-Reference.md).
Protocol conformance (MUST-level) — verified against the live deploy
The Linked Art Protocol page defines three MUSTs (first three rows); the `HEAD` and HTTP/2 SHOULDs are also met. All are verified against the live deploy on the canonical entity endpoints (`/api/objects/[id]`, `/api/artworks/[id]`, `/api/works/[id]`, …):
| Requirement | Status | Evidence |
|---|---|---|
| Content-Type `application/ld+json;profile="https://linked.art/ns/v1/linked-art.json"` when requested via `Accept` | ✅ | Echoed exactly under content negotiation (`src/utils/protocol.ts`). |
| `Access-Control-Allow-Origin: *` on every response | ✅ | `createPublicCorsHeaders()`. |
| `GET` + `OPTIONS` supported | ✅ | `optionsResponse()` on every public route; auto-implemented `OPTIONS` plus explicit allow-methods. |
| `HEAD` supported (SHOULD) | ✅ | Canonical entity/collection routes export `HEAD` (same headers as GET, no body, mirrors 200/404); `Allow-Methods` advertises `GET,HEAD,OPTIONS`. `tests/api/head-methods.test.ts`. |
| HTTP/2 on the live deploy (SHOULD) | ✅ | Vercel edge negotiates HTTP/2 — verified `HTTP/2.0 200` on `www.metamuseum.org`. |
Tested by the B8 protocol-conformance suite across ~14 representative routes (media-type negotiation, CORS, URI opacity, array cardinality, HAL `_links` separation).
Core model conformance (all normalized records)
Applied uniformly in `src/utils/linked-art.ts` (`normalizeIncomingRecord`) and `src/utils/artwork-builder.ts`:
| Aspect | Status |
|---|---|
| `@context` (`linked-art.json`) always present | ✅ |
| `id` / `type` / `_label` required | ✅ |
| `identified_by` (Name/Identifier), `classified_as`, `referred_to_by` | ✅ |
| `produced_by` as a `Production` activity (maker via `carried_out_by`, date via `timespan`, place via `took_place_at`) | ✅ |
| Event-centric provenance (`Activity`-wrapped acquisition/transfer; ownership vs. custody distinct) — B9 guardrails | ✅ |
| Multi-valued properties kept as arrays (cardinality-safe) | ✅ |
| Carrier / content / surrogate distinct (HumanMadeObject ≠ VisualItem ≠ DigitalObject) | ✅ |
| HAL `_links` separated from semantic assertions; `describedby` signposting | ✅ |
| `subject_to` (Right entities with classification) | ✅ Every object record: Getty's preserved; others synthesized from rights signals, classified by CC0 / rightsstatements.org URIs (`src/utils/linked-art-rights.ts`) |
Per-provider matrix
Every production provider has committed pass + fail fixtures (`provider-fixture-manifest.json`(../../tests/fixtures/validation/provider-fixture-manifest.json)), asserted in CI by `validation-architecture-depth.test.ts`(../../tests/quality/validation-architecture-depth.test.ts): the pass fixture must conform and the fail fixture must be rejected, each mapped to explicit Linked Art reference rounds. Each pass fixture is also validated against the Linked Art SHACL shapes by a dedicated pyshacl CI job (`shacl-conformance.yml`(../../.github/workflows/shacl-conformance.yml)) — proving the JSON-LD expands to valid CIDOC-CRM RDF. Rights are modeled uniformly as `subject_to` `Right` entities for every provider (Getty's preserved; the rest synthesized — see the rights row in the core-model table). The table below is generated from those fixtures and the capability registry, so it cannot drift from what the tests verify.
<!-- GENERATED:provider-matrix:start -->
_Generated by `pnpm conformance:matrix` from `provider-fixture-manifest.json`(../../tests/fixtures/validation/provider-fixture-manifest.json) and the provider capability registry. Each ✓ is computed live by running `inspectLinkedArtRecord` over the committed pass/fail fixtures — 13/13 providers pass both directions (pass conforms, fail rejected). Do not edit by hand._
| Provider | Import | Search | Pass fixture conforms | Fail fixture rejected | Linked Art rounds |
|---|:--:|:--:|:--:|:--:|---|
| Art Institute of Chicago API | ✅ | ✅ | ✅ | ✅ | R2, R3, R11 |
| Cleveland Museum of Art Open Access API | ✅ | ✅ | ✅ | ✅ | R2, R3, R11 |
| Europeana | ✅ | ✅ | ✅ | ✅ | R2, R3, R11, R65 |
| Getty Museum Collection | ✅ | ❌ | ✅ | ✅ | R2, R3, R11, R14 |
| Harvard Art Museums | ✅ | ✅ | ✅ | ✅ | R2, R3, R11, R68 |
| Louvre Collections | ✅ | ✅ | ✅ | ✅ | R2, R3, R11, R14 |
| National Gallery of Art Open Data Program | ✅ | ✅ | ✅ | ✅ | R2, R3, R11, R14 |
| Princeton University Art Museum | ✅ | ✅ | ✅ | ✅ | R2, R3, R11 |
| Rijksmuseum Data Services | ✅ | ✅ | ✅ | ✅ | R2, R3, R65 |
| RKD Knowledge Graph | ✅ | ✅ | ✅ | ✅ | R2, R3, R11, R65 |
| Smithsonian Open Access | ✅ | ✅ | ✅ | ✅ | R2, R3, R11, R65 |
| The Metropolitan Museum of Art | ✅ | ✅ | ✅ | ✅ | R2, R3, R11 |
| Victoria and Albert Museum Collections API | ✅ | ✅ | ✅ | ✅ | R2, R3, R65 |
<!-- GENERATED:provider-matrix:end -->
Counts come from the provider capability registry (`src/gateway/readiness.ts`), also surfaced at `/api/providers/capabilities`: 13 production providers · 12 searchable · 13 importable. Access: most providers are open; RKD uses a Triply token, Harvard and Smithsonian need an API key.
Honest gaps
- Non-Getty rights are heuristically derived. Every object record now carries a `subject_to` `Right` classified by a CC0 / rightsstatements.org URI, so the graph is queryable for all providers. But outside Getty the category (public-domain / in-copyright / undetermined) is inferred from the provider's rights signals and defaults conservatively to "Copyright Undetermined" — precise per-provider license mapping (e.g. CC-BY vs. CC-BY-SA) is still future work.
- Search runs in-app, not yet on Solr. `/api/search` now returns relevance-ranked results (exact label > prefix > substring > name hit) with `type` / `provider` facet counts and `q` / `type` / `provider` / `limit` / `offset` params, as a `ld+json` `OrderedCollectionPage` (`src/services/search.ts`). It runs over the record set; Solr 9 remains the documented scale-out backend (env-gated), with this in-app implementation as the portable default and conformance reference.
- SHACL shapes are minimal (but now CI-gated). A dedicated CI job (`shacl-conformance.yml`(../../.github/workflows/shacl-conformance.yml)) validates every provider's pass fixture against the Linked Art SHACL shapes with pyshacl, so a regression that breaks CIDOC-CRM expansion blocks the build. The shapes themselves are still minimal (object `identified_by` + `label` minimums); enriching them toward full Linked Art SHACL coverage is ongoing work.
Bottom line
The spec's MUST-level HTTP requirements and the hard part — the event-centric model — are genuinely conformant and test-backed. The remaining gaps are breadth (precise per-provider license categories, Solr-scale search, richer SHACL shapes), not correctness.