feat(standard)!: preferred citation system and qualified /cite/ aliases (ADR-0005) - #63
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Implements ADR-0005’s preferred citation system model by introducing qualified /cite/{work}/{system}/{locator} aliases for every reference while minting bare /cite/{work}/{locator} only for a work’s preferred system, enabling multi-citation-system works without alias collisions. It also rolls in ADR-0004’s lifecycle simplification (draft → active) across schema, compiler invariants, fixtures, and documentation, and adds compiler-focused tests.
Changes:
- Add
Work.preferred_citation_system_keyto the standard schema + JSON-LD/OpenAPI, and update rendering to disambiguate repeated locators across citation systems. - Update the compiler to validate YAML sources with Zod, support
additional_systemsblocks, mint qualified + (preferred-only) bare/cite/aliases, reject locators containing/, and enforce updated status dependency invariants. - Add
node:test-based compiler tests and wirenpm testintoverify/verify:fast.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| standard/schema/work.ts | Adds required preferred_citation_system_key to Work. |
| standard/schema/common.ts | Removes candidate and documents the ADR-0004 lifecycle vocabulary. |
| src/pages/reg/work/[key]/refs/[page].astro | Sorts refs by system+locator and labels system when a work is multi-system. |
| src/pages/id/work/[key]/index.astro | Labels the preferred system and tags refs by system when multi-system. |
| src/pages/id/ref/[uuid]/index.astro | Disambiguates titles/citations/description for multi-system works. |
| src/lib/registry.fixture.ts | Updates fixture records to ADR-0004 statuses and adds a second citation system case. |
| src/lib/citation.ts | Extends Chicago formatting to optionally include citation system label. |
| src/content/docs/standard/versioning.md | Documents ADR-0004/0005 invariants and alias rules (qualified vs bare, retargeting). |
| src/content/docs/standard/specification.md | Updates spec examples and adds preferred_citation_system_key + multi-system guidance. |
| src/content/docs/standard/json-ld.md | Adds JSON-LD context term for preferred_citation_system_key. |
| src/content/docs/standard/identifier-syntax.md | Updates persistence language to attach at active. |
| src/content/docs/get-started/url-layout.md | Updates /cite/ URL forms and permanence expectations. |
| src/content/docs/get-started/index.md | Updates getting-started example to mention qualified /cite/ form. |
| src/content/docs/get-started/how-it-works.md | Updates examples to reflect ADR-0004 statuses. |
| src/content/docs/get-started/authoring.md | Documents additional_systems authoring and the reference_status default asymmetry. |
| src/content/docs/de/association/governance.md | Updates legally binding German governance lifecycle/persistence wording for ADR-0004. |
| src/content/docs/community/roadmap.md | Updates roadmap note to reference ADR-0004. |
| src/content/docs/community/contributing.md | Updates review-track text/diagram for ADR-0004 lifecycle and draft landing. |
| src/content/docs/association/governance.md | Updates English governance lifecycle/persistence wording for ADR-0004. |
| src/components/Tombstone.astro | Updates draft-banner copy to reference promotion to active. |
| scripts/source-schema.ts | Adds strict Zod schemas for YAML source validation and duplicate-system rejection. |
| scripts/compile.ts | Implements ADR-0005 alias minting + additional systems + invariant updates; adds locator / rejection; adds fixture-root override for tests. |
| scripts/compile.test.ts | Adds compiler tests for alias grammar and ADR-0004/0005 dependency rules. |
| ROADMAP.md | Updates roadmap note to reference ADR-0004. |
| README.md | Updates wording to reflect registry examples are draft data. |
| public/contexts/v1.jsonld | Adds context term for preferred_citation_system_key. |
| package.json | Adds npm test and integrates it into verify/verify:fast. |
| decisions/ADR-0005-preferred-citation-system.md | Adds ADR-0005 detailing preferred citation system + alias strategy. |
| decisions/ADR-0004-lean-lifecycle.md | Adds ADR-0004 detailing lifecycle collapse to draft → active. |
| decisions/ADR-0003-draft-lifecycle-ephemeral-identity.md | Marks ADR-0003 as superseded by ADR-0004 and updates links. |
| CONTRIBUTING.md | Updates contribution tracks and promotion language for ADR-0004. |
| api/openapi.yaml | Removes candidate and adds required preferred_citation_system_key on Work. |
| AGENTS.md | Updates /cite/ URL layout description (qualified always; bare preferred-only and retargetable). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Member
Author
|
@stephenhart8 are you interested in reviewing this PR? |
…es (ADR-0005) Closes textrefs#60, raised by @stephenhart8, whose preferred-citation-system proposal this implements. Aliases dropped the citation system even though it seeds the reference UUID, so `/cite/homer.odyssey/1.5` could not distinguish two systems that share a locator string. Every reference now also gets a qualified `/cite/{work}/{system}/{locator}`; the bare `/cite/{work}/{locator}` is minted only for the work's preferred system. Existing short URLs keep working and no reference UUID changes. - `Work.preferred_citation_system_key` is required and consumer-visible (JSON-LD context term, OpenAPI schema, works.jsonl). - Work sources gain `additional_systems:` for fallback systems and `reference_status:` per block. The top-level block inherits the work's status; a fallback block defaults to `draft` and never inherits `active`, so adding a system promotes nothing by accident. - YAML sources are now validated with Zod instead of an unchecked cast, which is what lets duplicate citation-system blocks fail at parse time. - Locators containing `/` are rejected before alias minting: the grammar tells the two forms apart by segment count alone. - Invariants restated per ADR-0005: an active reference needs an active work and an active system; an active work needs an active preferred system; draft fallbacks never downgrade the work. - Work pages, paginated reference lists, reference-page titles, and the rendered Chicago citation name the system when a work has more than one, so repeated locators stay distinguishable. - New `npm test` (node:test via tsx) covers the alias grammar and every new invariant; wired into `verify` and `verify:fast`. Breaking: `Work` gains a required field, so works.jsonl consumers must adapt. Bare `/cite/` aliases are explicitly retargetable — only `/id/` identifiers are permanent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
maehr
force-pushed
the
feat/preferred-citation-system
branch
from
August 4, 2026 15:03
a863491 to
9110d05
Compare
maehr
added a commit
to maehr/textrefs.org
that referenced
this pull request
Aug 12, 2026
Replaces the skos:exactMatch / skos:closeMatch relation enum with alternateOf (prov:alternateOf) and isReferencedBy (dcterms:isReferencedBy), selected by target kind rather than by confidence. closeMatch is removed rather than retained-but-unusable: removing an enum value is breaking and this change is already breaking, while re-adding one later is additive. Status: Proposed, and no code changes yet — the issues stay open until the implementation lands. Stacked on textrefs#63 (ADR-0005), which introduces the Zod authoring schema the new enum needs. Refs textrefs#58, refs textrefs#59. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #60 — raised by @stephenhart8, whose preferred-citation-system proposal this implements.
The problem
/cite/homer.odyssey/1.5/drops the citation system even though the system is one of the three fields that seed the reference UUID (ADR-0002). Today that is unreachable rather than handled: a work source carries one scalarcitation_system:, so alias keys are unique by construction andsetAliasturns any clash into a build failure. The consequence is that a work under two citation systems cannot be represented at all.That blocks the roadmap. Psalms under Masoretic vs. Vulgate versification, the Qur'an under Cairo vs. Flügel, Aristotle under Bekker (
1094a1) and book/chapter (1.1) — the same locator string denotes a different passage. A bare alias there resolves silently to the wrong text, which is worse than a 404.The decision (ADR-0005)
Every reference gets
/cite/{work}/{system}/{locator}/. The bare/cite/{work}/{locator}/is minted only for the work's preferred citation system.Collisions stop being merely detected and become structurally impossible: a qualified alias is keyed by the same tuple that seeds the UUID, and at most one system per work is preferred.
What changed
Work.preferred_citation_system_key— required and consumer-visible: JSON-LD context termtr:preferredCitationSystemKey, OpenAPIWorkschema, andworks.jsonl.citation_system:stays the preferred one; a work may addadditional_systems:blocks, each with its own citation system, resolvers, locators, andreference_status:. The status default is asymmetric: the top-level block inherits the work's status, a fallback block defaults todraftand never inheritsactive. Adding a fallback system to an active work promotes nothing.as WorkSourcecast. That is what lets duplicate citation-system blocks fail at parse time, and it catches registry typos that previously landed silently./are rejected before any alias is minted — the grammar distinguishes the two forms by segment count alone.npm test(node:testviatsx, no new dependency), wired intoverifyandverify:fast. Eight cases build a throwaway registry and compile it, so they exercise the real YAML → record path.Alias permanence — please read
/cite/aliases are presentational. Bare aliases may be retargeted: changing a work's preferred citation system changes what its bare alias resolves to, including for active works. There is no alias ledger and no freezing — this is documented in/standard/versioning/,/get-started/url-layout/, and governance §6 item 6. Qualified aliases and/id/ref/{uuid}identifiers are never retargeted.Verification
npm test— 8/8 pass.npm run build:data— 39,250/39,250 records valid. Aliases 39,228 → 78,432, exactly 2× the 39,204 references plus 24 mapping aliases. No reference UUID changed:plato.republic/514astill resolves todc799d4b-9b17-5d76-85aa-dfd001c5321d, the identifier documented in the spec.npm run verify:fast—astro checkclean, 90 pages, all internal links valid. The fixture now carries two systems sharing the locator1; the built output confirms one bare and two qualified alias pages, correct redirect targets, thepreferredtag, and distinct reference-page titles.Follow-ups
preferred_citation_system_keyfrom the existing top-levelcitation_system:.Update: #62 merged (as a squash commit,
b365cfc), so this branch was rebased onto currentstaging— it's no longer stacked and carries only its own commit. Also folded in while rebasing:Proposed→Accepted, matching the convention feat(standard)!: collapse record lifecycle to draft → active (ADR-0004) #62 itself set.scripts/compile.test.ts:expectCompileErrornow usest.mock.method(console, 'error', …)scoped to the test'sTestContextinstead of manually reassigning the global, so the mock can't leak across concurrently-run tests.All verification numbers above were re-run and confirmed against the rebased branch.
🤖 Generated with Claude Code