Precedence bug: a coincident saint can displace the Sacred Triduum #422

Closed
opened 2026-07-02 17:21:11 +00:00 by manlycucumber · 0 comments
manlycucumber commented 2026-07-02 17:21:11 +00:00 (Migrated from github.com)

Problem

The validation harness (#45), cross-checking against the missalemeum oracle,
surfaced a precedence bug: on a day of the Sacred Triduum that coincides with
a fixed saint, the engine celebrated the saint and displaced the Triduum feria.

Concrete case — Holy Thursday, 28 March 2024 (which is also St John of
Capistrano, III class in the 1962 calendar): day() resolved the celebration to
S. Ioannis a Capistrano and marked Feria V in Cena Domini (I class) as
displaced/omit. Nothing outranks the Triduum (Table of Liturgical Days, n. 91,
line 2 — below only the supreme feasts), and it admits no commemoration (n. 23).

Root cause

Rubrics1962Precedence::tierOf() awarded the apex triduum tier to every
observance whenever the date was in the Triduum ($context->isTriduum()), rather
than only to the Triduum's own feria office. The coincident saint therefore tied
the feria at the apex, and DayResolver::sortByTier()'s equal-tier tie-break (by
observance id) put roman:sanctorale:… before roman:temporale:…, so the saint won.

Fix

Gate the triduum tier on the office being the day's feria
($context->isTriduum() && kind === FERIA); a coincident saint keeps its normal
(far lower) tier, so the Triduum feria always wins and the saint is omitted.

Regression tests added; the golden fixture was re-frozen (the change is confined to
each affected Triduum day and its eve's First-Vespers concurrence — an improvement).

Notes

The golden fixture proves stability, not correctness, so it had frozen this
wrong output; the independent oracle is what caught it. This is exactly the class
of defect Epic #45 exists to find.

## Problem The validation harness (#45), cross-checking against the missalemeum oracle, surfaced a precedence bug: on a day of the **Sacred Triduum** that coincides with a fixed saint, the engine celebrated the *saint* and displaced the Triduum feria. Concrete case — **Holy Thursday, 28 March 2024** (which is also St John of Capistrano, III class in the 1962 calendar): `day()` resolved the celebration to *S. Ioannis a Capistrano* and marked *Feria V in Cena Domini* (I class) as `displaced/omit`. Nothing outranks the Triduum (Table of Liturgical Days, n. 91, line 2 — below only the supreme feasts), and it admits no commemoration (n. 23). ## Root cause `Rubrics1962Precedence::tierOf()` awarded the apex `triduum` tier to **every** observance whenever the *date* was in the Triduum (`$context->isTriduum()`), rather than only to the Triduum's own feria office. The coincident saint therefore tied the feria at the apex, and `DayResolver::sortByTier()`'s equal-tier tie-break (by observance id) put `roman:sanctorale:…` before `roman:temporale:…`, so the saint won. ## Fix Gate the `triduum` tier on the office being the day's feria (`$context->isTriduum() && kind === FERIA`); a coincident saint keeps its normal (far lower) tier, so the Triduum feria always wins and the saint is omitted. Regression tests added; the golden fixture was re-frozen (the change is confined to each affected Triduum day and its eve's First-Vespers concurrence — an improvement). ## Notes The golden fixture proves *stability*, not *correctness*, so it had frozen this wrong output; the independent oracle is what caught it. This is exactly the class of defect Epic #45 exists to find.
Sign in to join this conversation.
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#422
No description provided.