From b6d0eba04d1da51515dad70127112898b500c1bc Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 26 Aug 2026 10:11:47 +0200 Subject: fix(of-lectionary): stop serving drifting readings for O-Antiphon and Christmas-season dates The bootstrap excluded 19 lectio bases as "date-keyed duplicates", but 17 of them were not duplicates at all: the 8 O-Antiphon days (17-24 December) and 8 further Christmas-season dates (29-31 December, 2-5 and 7 January) each carry unique per-date content found nowhere else among the emitted entries, verified against lectio's own ini directly. 17 December is Gen 49:2,8-10 / Matthew 1:1-17, but colitur was falling through to that year's unrelated weekday-keyed "advent-3-mon" content instead. OLM n. 69.3 explains why: those ferias are fixed by civil date, unlike every other Advent/Christmastide/Paschaltide feria, which is merely non-alternating within a weekday slot. Temporal_of's own ferial slugs for these dates are weekday-keyed, so looking one of those up served whichever OTHER date happened to share that year's weekday alignment -- a citation that silently drifted year to year on ~16 real civil dates (6 January stays excluded: Temporal_of fixes Epiphany there unconditionally, so that date can never reach the ferial path at all; two more, easter-6-thu and advent-4-sat, stay excluded for their own, different structural reasons). Fixed with a date-keyed lookup route (Lectionary_of.date_keyed_slug), tried before the weekday-keyed one in readings' own step 3. Temporal_of's slugs are unchanged -- only which lectionary key resolves a day's citations. tools/bootstrap_lectionary_of.ml now maps the 17 bases to synthetic date-keyed slugs instead of excluding them, and its own reachability sweep calls date_keyed_slug directly so the two can never drift apart. Re-emitted data/of/lectionary.sexp (754 -> 770 entries) and corrected its provenance header, which previously described these dates as excluded duplicates. Also states, for the first time, a Minor finding from the same review: 190 of 222 shipped sanctoral slugs have no dedicated lectionary entry and correctly fall through to the ferial per OLM norms -- expected behaviour, not a gap, but never said in the coverage report before. New tests pin 17 December (Gen 49:2,8-10 / Matthew 1:1-17) and 2 January (1 John 2:22-28 / John 1:19-28) in two years with different weekday alignments, proving neither reading drifts, plus direct boundary coverage of date_keyed_slug itself (both windows, the 6 January exclusion, and the Sunday guard). --- lib/rites/rite_of/lectionary_of.ml | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'lib/rites/rite_of/lectionary_of.ml') diff --git a/lib/rites/rite_of/lectionary_of.ml b/lib/rites/rite_of/lectionary_of.ml index 1664fad..eb3e4e0 100644 --- a/lib/rites/rite_of/lectionary_of.ml +++ b/lib/rites/rite_of/lectionary_of.ml @@ -60,6 +60,24 @@ let lookup_any lectionary ~year_start base date = | Some cs -> Some cs | None -> Lectionary.find lectionary (with_suffix (weekday_cycle_letter (weekday_cycle ~year_start date)))) +(* See lectionary_of.mli's own doc comment for the full citation (OLM n. + 69.3) and argument. Kept as ordinary [Printf.sprintf] string + construction, not a lookup table, so tools/bootstrap_lectionary_of.ml's + own reachability sweep (which calls this function directly, not a + hand-copied re-implementation) and this module's own runtime lookup can + never drift apart -- the single failure mode a hand-duplicated version + in each place would invite. *) +let date_keyed_slug date = + if Date.weekday date = Date.Sun then None + else + let m = Date.month date and dd = Date.day date in + if m = 12 && dd >= 17 && dd <= 24 then Some (Slug.of_string_exn (Printf.sprintf "of-advent-dec-%d" dd)) + else if m = 12 && dd >= 29 && dd <= 31 then + Some (Slug.of_string_exn (Printf.sprintf "of-christmas-dec-%d" dd)) + else if m = 1 && ((dd >= 2 && dd <= 5) || dd = 7) then + Some (Slug.of_string_exn (Printf.sprintf "of-christmas-jan-%d" dd)) + else None + let readings ~lectionary ~year_start ~(observed : Vocab_of.rank Celebration.t) ~(temporal : (Vocab_of.season, Vocab_of.rank) Temporal.t) ~date ~temporal_at:_ = match observed.Celebration.citations with @@ -99,9 +117,23 @@ let readings ~lectionary ~year_start ~(observed : Vocab_of.rank Celebration.t) Mass_formulary.source was never built to carry. *) (Some { Mass_formulary.said = Some observed.Celebration.slug; via = Mass_formulary.Proper }, cs) | None -> ( - match lookup_any lectionary ~year_start temporal.Temporal.office.Celebration.slug date with + (* OLM n. 69.3's own date-fixed windows (see date_keyed_slug's + own doc comment): tried BEFORE the ordinary weekday-keyed + slug lookup, not instead of it, so every other temporal day + is unaffected -- this branch only ever hits inside the two + named civil-date windows, and [date_keyed_slug] itself + already refuses a Sunday, where a NAMED Sunday office (never + one of these weekday-keyed ferial slugs) is what temporal. + office actually names. *) + match (match date_keyed_slug date with Some s -> Lectionary.find lectionary s | None -> None) with | Some cs -> ( Some { Mass_formulary.said = Some temporal.Temporal.office.Celebration.slug; via = Mass_formulary.Own_slug }, cs ) - | None -> (None, []))) + | None -> ( + match lookup_any lectionary ~year_start temporal.Temporal.office.Celebration.slug date with + | Some cs -> + ( Some { Mass_formulary.said = Some temporal.Temporal.office.Celebration.slug; + via = Mass_formulary.Own_slug }, + cs ) + | None -> (None, [])))) -- cgit v1.3