feat: Novus Ordo precedence — the reformed Table of Liturgical Days (#257) #481

Merged
manlycucumber merged 1 commit from feature/257-novus-ordo-precedence into develop 2026-07-11 04:44:16 +00:00
manlycucumber commented 2026-07-11 04:42:44 +00:00 (Migrated from github.com)

Adds NovusOrdoPrecedence, the Ordinary-Form precedence engine, beside the three traditional editions (#257). It reads the reformed Table of Liturgical Days (Normae universales 1969, n. 59) line by line — a lower line wins, like the traditional n. 91 table but flatter.

What lands

  • No commemorations. commemorationLimit / commemorationClassLimit are 0, isPrivilegedCommemoration false, and tierOf never returns a commemoration tier. When two celebrations occur the higher wins; the loser is transferred (an impeded Solemnity, reusing forcedTransferDate — e.g. the Annunciation out of Holy Week → Easter+8) or omitted. Never commemorated.
  • All Souls stays on a Sunday. officeOfTheDeadYieldsToSunday() is false, so 2 November (line 3) displaces a Sunday of Ordinary/Christmas Time (line 6) rather than yielding.
  • No Vespers concurrence. An additive PrecedenceRules::observesVespersConcurrence() (true for the three traditional editions, false for NO) gates DayResolver::withConcurrence — the resolver skips a meaningless second-Vespers pass for the reformed Office. Byte-identical for the traditional editions (a top-of-method early return; golden fixture proves 1962/1954/1955 unchanged).
  • tierOf maps observances to the n.59 lines: Triduum feria (1); privileged temporal — Nativity/Epiphany/Ascension/Pentecost + Sundays of Advent/Lent/Easter (2); Solemnities + All Souls (3); Feasts of the Lord (5); Sundays of Christmas/Ordinary Time (6); Feasts of saints (7); Lenten weekdays (9); obligatory (10) / optional (12) memorials; other weekdays (13).
  • rulesFor gains a NOVUS_ORDO_2002 case; the edition is still isBuilt=false and refused at the public boundary (the isBuilt gate fires before rulesFor).

Safety / scope

  • All 1074 tests green (144k assertions); PHPStan [OK]; 1962 golden byte-identical; corpus untouched (verify OK — 32 files).
  • Tiers + membership are corpus data read through PrecedenceTable, tested here against a minimal Novus-Ordo fixture table; the real cited table + full-year oracle validation land with the general-calendar corpus (#108) and #260.
  • Adversarially reviewed against the actual Normae universales table: no reachable bugs, all routing faithful for every office the engine can emit today. The review surfaced 3 dormant items that activate in #108 — the code comments now flag them explicitly:
    • Ash Wednesday / Holy Week Mon–Thu / Easter-octave days must route to line 2 (they're currently only reachable via the deferred fillers; load-bearing for the Annunciation transfer).
    • Sunday-occupying solemnities/feasts (Trinity, Corpus Christi, Baptism, …) must be authored kind=feast (movable-feast overlays), not kind=sunday.
    • (Cosmetic) Easter Sunday reports line 2 not line 1 — trace-only, never changes the resolved celebration.
Adds `NovusOrdoPrecedence`, the Ordinary-Form precedence engine, beside the three traditional editions (#257). It reads the reformed **Table of Liturgical Days** (Normae universales 1969, n. 59) line by line — a lower line wins, like the traditional n. 91 table but flatter. ## What lands - **No commemorations.** `commemorationLimit` / `commemorationClassLimit` are 0, `isPrivilegedCommemoration` false, and `tierOf` never returns a commemoration tier. When two celebrations occur the higher wins; the loser is **transferred** (an impeded Solemnity, reusing `forcedTransferDate` — e.g. the Annunciation out of Holy Week → Easter+8) or **omitted**. Never commemorated. - **All Souls stays on a Sunday.** `officeOfTheDeadYieldsToSunday()` is false, so 2 November (line 3) displaces a Sunday of Ordinary/Christmas Time (line 6) rather than yielding. - **No Vespers concurrence.** An additive `PrecedenceRules::observesVespersConcurrence()` (true for the three traditional editions, false for NO) gates `DayResolver::withConcurrence` — the resolver skips a meaningless second-Vespers pass for the reformed Office. **Byte-identical** for the traditional editions (a top-of-method early return; golden fixture proves 1962/1954/1955 unchanged). - **`tierOf`** maps observances to the n.59 lines: Triduum feria (1); privileged temporal — Nativity/Epiphany/Ascension/Pentecost + Sundays of Advent/Lent/Easter (2); Solemnities + All Souls (3); Feasts of the Lord (5); Sundays of Christmas/Ordinary Time (6); Feasts of saints (7); Lenten weekdays (9); obligatory (10) / optional (12) memorials; other weekdays (13). - `rulesFor` gains a `NOVUS_ORDO_2002` case; the edition is still **`isBuilt=false`** and refused at the public boundary (the isBuilt gate fires before `rulesFor`). ## Safety / scope - All **1074 tests green** (144k assertions); PHPStan `[OK]`; **1962 golden byte-identical**; corpus untouched (`verify OK — 32 files`). - Tiers + membership are corpus data read through `PrecedenceTable`, tested here against a **minimal Novus-Ordo fixture table**; the real cited table + full-year oracle validation land with the general-calendar corpus (#108) and #260. - **Adversarially reviewed** against the actual Normae universales table: **no reachable bugs**, all routing faithful for every office the engine can emit today. The review surfaced 3 **dormant** items that activate in #108 — the code comments now flag them explicitly: - Ash Wednesday / Holy Week Mon–Thu / Easter-octave days must route to **line 2** (they're currently only reachable via the deferred fillers; load-bearing for the Annunciation transfer). - Sunday-occupying solemnities/feasts (Trinity, Corpus Christi, Baptism, …) must be authored **kind=feast** (movable-feast overlays), not kind=sunday. - (Cosmetic) Easter Sunday reports line 2 not line 1 — trace-only, never changes the resolved celebration.
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!481
No description provided.