Public data dictionary for EN 18xxx digital product passports. Every entry is one immutable
fact at https://material-identity.eu/def/<uuid> — JSON for machines, HTML for humans, same
URI. Source of truth is this repository: YAML under published/ (add-only, forever), built
into a static site and served through a thin Cloudflare Worker doing content negotiation.
There is no versioning and no status field anywhere. An entry is either current or it has
been superseded by a newer entry that names it via replaces — that link is the entire
lifecycle model. "Current" and "superseded" are never stored; they're derived at build time
by scanning for whichever entry (if any) replaces a given one, and shown only as presentation
(a banner, an index filter) — never written back into any file.
Because /def/<uuid> is served Cache-Control: immutable, a superseded entry never gains a
supersession signal of its own — discovery lives at mutable surfaces instead: the index (lists
only current entries), /feed.xml (announces new supersessions), and
/superseded.json — a build-generated
{ old id → successor id } map for any consumer holding an id, however old, that wants to
check whether a newer version exists.
Content license: CC0 1.0. Agent contract: CLAUDE.md. Reviewer contract: REVIEW.md.
This dictionary syndicates definitions in machine-readable form — it is not the authority over what a definition means, and not the canonical host of that meaning.
- Dictionary element id — an entry's
id. Scoped to this dictionary, not to the concept's authority. Replaced only when the meaning it syndicates changes — never per release, never per editorial touch. - Global definition — the authoritative meaning itself: a standard's clause, a regulation's
article. It lives elsewhere, in a source with no obligation to be machine-readable at all.
definitionStandard(andlegalBasis, for a legal rather than technical authority) is an entry's reference to its global definition. That reference must be version-stable — a dated edition for a standard, a specific expression for legislation — so that whoever follows it finds the same content the entry was syndicated against (seeschema/dictionary-entry.schema.jsonand REVIEW.md).
Three layers of addressing fall out of this: (1) a content specification id — reissued only when its shape changes or a referenced meaning changes; (2) a dictionary release — versioned per publication, re-versions nothing else; (3) the dictionary element id — replaced only when a new meaning is required.
An entry is the product; everything else is derived from published/ at build time and
served with a short cache, so no derived view can ever contradict an entry.
/def/<uuid> |
the entry — JSON for machines, HTML for people, same URI; immutable, cached for a year. Also /def/<uuid>.csl.json for reference managers, and a Link: …; rel="cite-as" header |
/ |
paginated index of current entries |
/tree |
the containment hierarchy — collections, their members, enumerations — fold/unfold, no JavaScript |
/graph |
the cross-links the tree omits (unit, quantityKind, accessCategory, replaces) as a static SVG, plus the same edges as a table |
/schema |
generated field reference, and the raw JSON Schema next to it |
/superseded.json |
old id → successor id, for a consumer holding any id however old |
/dictionary.ttl |
the whole dictionary as RDF; semantics declared in /context.jsonld |
/feed.xml |
new and superseded entries |
/about |
what this is, what it promises, what it is not |
RDF is a second serialization, never a mutation of the first: /def/<uuid>.json carries no
@context and never will.
Node 24 (.nvmrc), then:
npm ci # install (exact-pinned; wires the pre-push hook)
npm run validate # checks 1–6: immutability, schema, identity, replaces integrity,
# pinning, move purity
npm test # node:test suite; the run fails below 85% line coverage
npm run build # YAML → site/ (entries + every derived view above; deterministic)validate and build accept -- --root <dir> to run against a fixture tree;
validate accepts -- --base <ref> for the diff-based checks (default main).
npm run build
npx serve site/Then open e.g. http://localhost:3000/def/<uuid>.html. In production the Worker serves
the same files on the extension-free canonical URI with content negotiation; the .json /
.html origin files stay directly reachable for debugging but are never published as
references.
Open a dictionary request — the form
mirrors the entry envelope (shortName, objectType, preferredName, definition, and
whatever optional fields apply). A maintainer triages it (Yes #1); everything after that
is the author workflow below.
Every step is a documented, executable procedure under .claude/skills/ —
not tribal knowledge. A contributor (human or agent) with no other context can follow this
end to end.
- Draft (
new-entry) — from an accepted request, authordrafts/<shortName>.yaml: the full envelope perschema/dictionary-entry.schema.json,ida placeholder, everything else — includingreplaces, if this supersedes something — already final. Nothing here is promised to anyone;npm run validateshould be green. - Publish (
publish-entry) — mint the UUID (scripts/mint.ts, which rewrites only the draft'sid:line), move it topublished/<uuid>.yaml, and open a PR withCloses #<n>for the request. There is no separate "supersede" step and no concept file to update — if the draft hasreplacesset, publishing it is the supersession. CI enforces the whole integrity chain: schema, identity, replaces integrity (resolves, no forks), pinning, immutability, move purity, and the two-yes gate (the PR must close an issue labeledtype:dictionary-request+state:accepted— Yes #1, made mechanical). Merge requires a CODEOWNERS approval from someone who is never the author (Yes #2 — GitHub structurally forbids self-approval). The superseded entry (if any) keeps resolving forever, byte-identical to its own publication — nothing about it needed touching. - Release (M6) — a GPG-signed tag (
git tag -s vYYYY.MM.DD) onmain, with a GitHub Release carrying the generated changelog, the full dictionary tarball (published/+schema/), and the CycloneDX SBOM.
A passport can carry
https://material-identity.eu/def/<uuid>asdictionaryReference; acurlwithAccept: application/jsonreturns the schema-valid entry withCache-Control: immutable; the same URI in a browser shows the human page — and no process exists by which the JSON response can ever change.
(Project-Plan-and-Architecture.md §8, verbatim.)