fix(compile): stop projecting retired mapping assertions onto Work (#45) - #86
Conversation
…extrefs#45) The direct `alternateOf` / `isReferencedBy` edges on `Work` excluded only `withdrawn` and `blocked` assertions. `deprecated` still projected, so a mapping the registry had taken out of use kept emitting a live edge. That contradicts the standard's own definitions. `deprecated`, `withdrawn` and `blocked` are the three states for records that have left active use (ADR-0004, `standard/schema/common.ts`), and specification §12 defines `deprecated` as "retained but no longer recommended". The edge carries no status, so it cannot say the mapping is retired. Gate the projection on the set that already means "left active use", and rename it from SUPERSEDABLE_STATUSES to RETIRED_STATUSES so it reads correctly at both call sites. TOMBSTONE_STATUSES stays: the two remaining uses are about broken resolution, and a deprecated record still resolves. The published output does not change. Every record in `data/` is `draft`, so no assertion is deprecated today and `dist/dump/works.jsonl` is byte-identical. This closes the hole before a demotion ever happens. Issue textrefs#45 also proposed an allowlist, so that `draft` assertions stop projecting too. Declined: TextRefs' guarantees attach to `CanonicalReference`, and the Work projection is enrichment that never claimed review. The docs now say so, rather than implying a promise the standard does not make. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PUm3GZBhwznL6uZEpDMKg
There was a problem hiding this comment.
Pull request overview
This PR updates the compiler and documentation so Work.alternateOf / Work.isReferencedBy projections no longer publish unqualified edges for mapping assertions that have left active use (deprecated, withdrawn, blocked). This aligns the emitted Work enrichment with the lifecycle semantics in the standard, while keeping the reified MappingAssertion records authoritative for status/provenance.
Changes:
- Update
scripts/compile.tsto excludedeprecatedmapping assertions fromWorkmapping-edge projections (and rename the status set toRETIRED_STATUSES). - Update standard docs and OpenAPI descriptions to clarify projection semantics (enrichment-only; no review claim; read
MappingAssertionfor status). - Add a unit test covering exclusion of
deprecatedmapping assertions fromWorkprojections.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
standard/schema/work.ts |
Clarifies Work schema comments for mapping projection semantics and excluded statuses. |
src/content/docs/standard/specification.md |
Updates §6 text to exclude deprecated and clarify “no review claim; read MappingAssertion”. |
src/content/docs/standard/json-ld.md |
Updates JSON-LD documentation to match the new projection gating and semantics. |
scripts/compile.ts |
Implements projection gating using RETIRED_STATUSES and updates invariant check naming. |
scripts/compile.test.ts |
Adds regression test ensuring deprecated mappings do not project onto Work edges. |
api/openapi.yaml |
Updates API schema descriptions for alternateOf/isReferencedBy projections and their meaning. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| - `isReferencedBy` → `dcterms:isReferencedBy`, when the target is a document or page about the work (e.g. a Wikipedia article). | ||
|
|
||
| Published `Work` records additionally carry direct `alternateOf` / `isReferencedBy` arrays derived from mapping assertions that are not `withdrawn` or `blocked`, published as `prov:alternateOf` / `dcterms:isReferencedBy` edges from the work IRI to the mapped identifiers without dereferencing the reified `MappingAssertion` records. The arrays are a read-only projection; the assertion stays authoritative ([Specification §6](/standard/specification/#6-work)). | ||
| Published `Work` records additionally carry direct `alternateOf` / `isReferencedBy` arrays derived from mapping assertions that are not `deprecated`, `withdrawn` or `blocked`, published as `prov:alternateOf` / `dcterms:isReferencedBy` edges from the work IRI to the mapped identifiers without dereferencing the reified `MappingAssertion` records. The arrays are a read-only projection; the assertion stays authoritative ([Specification §6](/standard/specification/#6-work)). The arrays enrich the work. They make no claim about review: a consumer that needs the status of a mapping must read the `MappingAssertion`. |
There was a problem hiding this comment.
Declining this one — the lowercase is deliberate.
json-ld.md carries no RFC 2119 keywords at all. Counting uppercase keywords across the standard:
| file | MUST | SHOULD | MAY |
|---|---|---|---|
specification.md |
70 | 12 | 10 |
versioning.md |
18 | 3 | 3 |
identifier-syntax.md |
16 | 0 | 1 |
system-profiles.md |
12 | 1 | 1 |
json-ld.md |
0 | 0 | 0 |
The comparison is across files rather than within one. json-ld.md is the descriptive companion to the context document; it explains how records map to RDF and states no requirements of its own. Introducing a single MUST here would make it the only normative sentence on an otherwise non-normative page.
The requirement itself is already stated normatively, in the place that owns it — specification.md §6, in this same PR: "a consumer that needs the status of a mapping MUST read the MappingAssertion." The json-ld.md sentence restates it descriptively and links back to §6, which is the existing relationship between the two pages.
Closes #45.
What
The direct
alternateOf/isReferencedByedges onWork(scripts/compile.ts:491) excluded onlywithdrawnandblockedassertions.deprecatedstill projected, so a mapping the registry had taken out of use kept emitting a live, unqualified edge.That contradicts the standard's own definitions:
standard/schema/common.ts:5— "The remaining three are tombstone states for records that have left active use" (deprecated,withdrawn,blocked).specification.md:301— "deprecated— retained but no longer recommended."specification.md:305— an incorrectactiverecord "MUST instead be moved todeprecated,withdrawn, orblocked".The edge carries no status, so it cannot say the mapping is retired. Now it simply is not published.
How
The set that means "left active use" already existed. Gate the projection on it, and rename it from
SUPERSEDABLE_STATUSEStoRETIRED_STATUSESso it reads correctly at both call sites.TOMBSTONE_STATUSESstays. Its two remaining uses (:651,:671) are about broken resolution, and adeprecatedwork still resolves — the two rules are genuinely different.The allowlist half of #45 is declined
#45 also proposed switching to an allowlist so
draftassertions stop projecting, on the grounds that a consumer cannot tell a reviewed equivalence from an unreviewed one.TextRefs' guarantees attach to
CanonicalReference— identity, persistence, resolution. TheWorkprojection is enrichment and never claimed review, so gating it on promotion would buy a guarantee the standard does not make.It would also cost real capability. Every one of the 24 seed mappings is
draft, and the compiledWorkhas no back-link to its assertions (src/pages/id/work/[key].json.ts:15publishes the record verbatim, and there is no/reg/mapping/index). Under an allowlist, v0.1.0 works would lose their only machine-readable path to Wikidata.So the docs now state what the arrays are for, instead of implying a promise the standard does not make.
specification.md§6 andjson-ld.mdgain: the arrays enrich the work, they make no claim about review, and a consumer that needs the status of a mapping reads theMappingAssertion.No published output changes
Every record in
data/isdraft, so no assertion is deprecated today.dist/dump/works.jsonlis byte-identical before and after:This closes the hole before a demotion ever happens. It is not a release-scope change: #68 §4 triages #45 as "fine to ship without", and that stays true. No submodule bump.
Verification
npm run verifyandnpm run validate:data, both clean:astro check0 errors, 31/31 tests, 204,350 pages built, all internal links valid.validate:data— 68005/68005 records valid (works=12, systems=10, refs=67959, mappings=24).The gate was also proved to fire, by temporarily demoting one mapping in
data/works/aristotle.nicomachean-ethics.yamltodeprecatedand recompiling:The demoted assertion survives in full at
https://textrefs.org/id/mapping/d5d6ca80-c7e7-5919-8f95-6d96cf4f90b2, still pointing athttps://www.wikidata.org/entity/Q474537and still carrying"status": "deprecated". Only the unqualified edge is gone. Thedata/edit was reverted; the submodule pointer is untouched.Not in this PR
decisions/README.mdreserves those for choices that cross PR boundaries, and this changes no published byte.!. Behaviour against current data is unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_017PUm3GZBhwznL6uZEpDMKg