feat: make the precedence table data-driven (#43) #420

Merged
manlycucumber merged 1 commit from feature/43-precedence-dataset into develop 2026-07-02 16:56:11 +00:00
manlycucumber commented 2026-07-02 16:54:53 +00:00 (Migrated from github.com)

Completes #43 — the last data-for-code refactor in Epic #38, and with it the whole corpus-generator epic.

What moves to data

Rubrics1962Precedence held the entire 1962 precedence rule set as private constants. Those move into cited corpus data behind a PrecedenceTable read seam:

  • the 23 tier ordinals of the n.91 Table of Liturgical Days (greatest=1 … fourth=28, gaps preserved),
  • the 6 membership id-sets the tier derivation branches on (the great feasts of the Lord/Our Lady, recognised by identity because Easter and Pentecost are kind=sunday yet aren't first-class Sundays),
  • the 4 per-class commemoration limits (n.111b–d).

The engine keeps the branching logic (which selector a day maps to); the data holds the facts (which ordinal a selector is, which ids are great feasts, how many commemorations a class admits). The resolver is now genuinely rubric-generic — the seam #59 will reuse for the 1954/1955 and Novus Ordo tables.

Shape (matches the frozen #39 schemas)

  • facts/precedence.yamleditions/roman-rubricae-1960/precedence-tiers.ndjson (precedence-tier) + editions/roman-rubricae-1960/precedence-rules.ndjson (precedence-rules — a oneOf of membership sets and commemoration limits). Per edition, since the precedence table is the rubric family.
  • Every row cites the 1960 rubrics (rg-1960); the born-cited provenance gate resolves each cite.

Design notes

  • PrecedenceTable lives in the Precedence layer and depends only on Corpus (no Calendar types) — so the layer stays acyclic. Cache is keyed by the corpus root dir, so a fixture tree never collides with the shipped one.
  • The frozen DayContract still computes its commemoration limit through the coded CommemorationLimit helper; that helper is left untouched and pinned equal to the data by test, so the two can't drift.

Safety

  • The golden fixture is unchangedresolveYear() output is byte-identical across the swap (1584–2200), proving the precedence refactor inert. Pre-existing corpus outputs and hashes untouched; the change is purely additive (2 new NDJSON files + manifest entries).
  • PrecedenceTableTest proves the AC directly: reordering the tier data (first-sunday → ordinal 99 in a fixture corpus) makes the same engine resolve a first-class Sunday to 99 with no code edit, and binds the commemoration limits to the coded baseline.
  • composer test615 tests, 31 586 assertions green · composer lint clean · composer analyse [OK] · npm run verify → 12 files, reproducible + matching committed.

Closes #43.

Completes **#43** — the last data-for-code refactor in Epic #38, and with it the whole corpus-generator epic. ## What moves to data `Rubrics1962Precedence` held the entire 1962 precedence rule set as private constants. Those move into cited corpus data behind a `PrecedenceTable` read seam: - the **23 tier ordinals** of the n.91 Table of Liturgical Days (`greatest`=1 … `fourth`=28, gaps preserved), - the **6 membership id-sets** the tier derivation branches on (the great feasts of the Lord/Our Lady, recognised by identity because Easter and Pentecost are `kind=sunday` yet aren't first-class Sundays), - the **4 per-class commemoration limits** (n.111b–d). The engine keeps the **branching logic** (which selector a day maps to); the data holds the **facts** (which ordinal a selector is, which ids are great feasts, how many commemorations a class admits). The resolver is now genuinely rubric-generic — the seam #59 will reuse for the 1954/1955 and Novus Ordo tables. ## Shape (matches the frozen #39 schemas) - `facts/precedence.yaml` → `editions/roman-rubricae-1960/precedence-tiers.ndjson` (`precedence-tier`) + `editions/roman-rubricae-1960/precedence-rules.ndjson` (`precedence-rules` — a `oneOf` of membership sets and commemoration limits). Per edition, since the precedence table *is* the rubric family. - Every row cites the 1960 rubrics (`rg-1960`); the born-cited provenance gate resolves each cite. ## Design notes - `PrecedenceTable` lives in the Precedence layer and depends only on `Corpus` (no Calendar types) — so the layer stays acyclic. Cache is keyed by the corpus root dir, so a fixture tree never collides with the shipped one. - The frozen `DayContract` still computes its commemoration limit through the coded `CommemorationLimit` helper; that helper is left untouched and **pinned equal to the data by test**, so the two can't drift. ## Safety - The **golden fixture is unchanged** — `resolveYear()` output is byte-identical across the swap (1584–2200), proving the precedence refactor inert. Pre-existing corpus outputs and hashes untouched; the change is purely additive (2 new NDJSON files + manifest entries). - `PrecedenceTableTest` proves the AC directly: reordering the tier data (`first-sunday` → ordinal 99 in a fixture corpus) makes the **same** engine resolve a first-class Sunday to 99 with no code edit, and binds the commemoration limits to the coded baseline. - `composer test` → **615 tests, 31 586 assertions** green · `composer lint` clean · `composer analyse` `[OK]` · `npm run verify` → 12 files, reproducible + matching committed. Closes #43.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Directorium/Core!420
No description provided.