Skip to content

compile: references_range hardcodes the locator separator, so a colon-cited system cannot use it #94

Description

@maehr

What is wrong

expandRange hard-codes one tradition's spelling for every tradition. scripts/compile.ts:68-74:

case 'chapter_verse': {
    const out: string[] = [];
    for (let ch = 1; ch <= range.counts.length; ch++) {
        const verses = range.counts[ch - 1];
        for (let v = 1; v <= verses; v++) out.push(`${ch}.${v}`);
    }

Every multi-part kind does the same: book_line at :48, book_chapter at :56, book_chapter_verse at :64.

The text defines how a citation is spelled. The schema does not. A citation system states that spelling in prose — data/systems/bible-book-chapter-verse.yaml opens with "OSIS locator: Book.Chapter.Verse" — and locator_regex validates against it. assertValidLocator (compile.ts:189-204, called from emitBlockReferences at compile.ts:366) is where the mismatch surfaces, but the regex is the guard, never the definition. A tradition whose separator is not . therefore cannot use references_range at all.

The build fails loudly and correctly. This is not silent corruption. But the author is left with no way forward except listing every locator by hand.

Reproduction

Branch feat/quran in textrefs/registry (draft PR, textrefs/registry#23). It adds a system whose canonical form is 2:255:

locator_regex: '^(?<sura>[1-9]|[1-9][0-9]|10[0-9]|11[0-4]):(?<verse>[1-9][0-9]{0,2})$'

and a work that uses references_range: - kind: chapter_verse with the 114 sura verse counts. npm run compile:data:

Error: quran-sura-verse: locator "1.1" does not match locator_regex
    at assertValidLocator (/…/scripts/compile.ts:191:9)
    at emitBlockReferences (/…/scripts/compile.ts:366:3)
    at compileRegistry (/…/scripts/compile.ts:592:16)

Why it matters

data/AGENTS.md:16 names this exact case as the one references_range is for: "references_range: when the canonical set is regular and complete". The Qurʾān's verse space is regular, complete, closed and source-defined — 114 suras, 6,236 verses. It is the textbook instance, and it is the one shape the field cannot express.

The workaround is to list all 6,236 locators under references:. That is legal — assertValidLocator only checks the regex and rejects / — but it puts a generated table in a hand-authored file and loses the reason the field exists.

The alternative is to spell the Qurʾān 2.255. Because the tradition defines the spelling, that locator is not merely unconventional. It is wrong.

Is a colon otherwise safe?

Checked end to end, and yes:

  • UUIDv5 seed — compile.ts:298-304 joins [workKey, systemKey, locator] with \n. Byte-transparent.
  • Reference IRI — https://textrefs.org/id/ref/{uuid}. The locator never appears in an IRI.
  • Alias keys — compile.ts:405,408. quran/2:255 is a fine key.
  • :// in an alias key — settled, no new guard needed. The alias table tells cite-aliases from external mapping identifiers by testing for :// (compile.ts:810-813, and the filter at src/pages/cite/[...alias].astro:7). A locator can never carry that substring, because assertValidLocator rejects any / (compile.ts:199) and :// contains one. Verified against the compiler: a separator of :// fails with locator "1://1" contains "/", which the /cite/ alias grammar cannot represent.
  • URL building — AliasList.astro:12 and src/pages/id/ref/[uuid]/index.astro:69-71 both use new URL('/cite/' + alias + '/', site). The path is absolute, so no scheme ambiguity, and : is a valid pchar under RFC 3986.
  • Build artifact — the static route writes dist/cite/quran/2:255/index.html. Fine on APFS and Linux; would break if dist/ ever has to exist on NTFS.
  • Finder — parseQuery (src/lib/find.ts:143-152) takes the last whitespace token containing a digit. 2:255 qualifies.

Proposed fix

The separator is a property of the citation system, not of one work's range entry. It belongs beside locator_regex and the description that defines it. SystemSource.chapter_sizes (source-schema.ts:236) is the precedent: structural facts about a tradition already live on the system.

# data/systems/quran-sura-verse.yaml
key: quran-sura-verse
locator_regex: '^(?<sura>[1-9]|[1-9][0-9]|10[0-9]|11[0-4]):(?<verse>[1-9][0-9]{0,2})$'
separator: ':'

The regex keeps validating. The separator only tells the expander how to join the parts it generates.

An earlier draft of this issue proposed an optional separator on each multi-part references_range kind. That is the wrong home. It lets one system be spelled 1.1 under one work and 1:1 under another, with no single place that states what the tradition does.

Touches:

  • scripts/source-schema.ts:224-237 — an optional separator on SystemSource, defaulting to ..
  • scripts/compile.ts:37-103 — expandRange takes the system. system is already destructured at :348 and in scope at the call site :353-355, beside the assertValidLocator(locator, system) at :366.
  • src/content/docs/get-started/authoring.md:246-371 — document the field. That section is the authoring contract, and it lists every range kind and its fields.

The default keeps every existing locator byte-identical, so no reference UUID moves. #98 confirmed this for the per-range design: the compiled bundle stayed byte-identical across all 86,477 records, apart from the created timestamp in datapackage.json. A system-level default of . behaves the same way.

Filing rather than implementing, since this changes the authored data contract.

Scope

Not in v0.1.0. references_range takes no changes in this release, so textrefs/registry#23 is parked rather than pending.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpost-v0.1.0Deferred past the v0.1.0 baseline. Revisit if the need arises.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions