diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-15 00:50:16 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-15 00:50:16 +0200 |
| commit | ba0adbaf3176ff753312d7052d0dc2795ac55dc9 (patch) | |
| tree | 4b1c66e2fd5a509a48e468eb363323aed068fdfb | |
| parent | f8d694d0cc19b71598e1ab64254efb069969f0a0 (diff) | |
| download | colitur-ba0adbaf3176ff753312d7052d0dc2795ac55dc9.tar.gz colitur-ba0adbaf3176ff753312d7052d0dc2795ac55dc9.zip | |
ef(lectionary): a feria says the preceding Sunday's Mass
Chain step 3. Guarded on weekday <> Sun: a Sunday reaching this branch
would look up its own slug via days_since_sunday Sun = 0 and loop --
every other chain step consults data, this one consults a strictly
earlier date, so that guard is the whole chain's termination argument.
Reaches the Sunday by Date.add_days plus a fresh temporal_at call, never
by string surgery on the day's own slug -- the slug shapes are genuinely
inconsistent across seasons (ef-advent-sunday-1 vs ef-advent-1-monday,
week number on opposite sides of the season name).
Uses the preceding Sunday's TEMPORAL slug, never its observed one: the
rubric is the preceding Sunday's Mass even in a year a feast displaced
that Sunday from being observed (pinned: 2028-12-26, the Monday after a
Vigil-displaced Advent IV Sunday, still takes Advent IV's Mass).
Measured over the full 1583-9999 domain (temporal cycle only, no
sanctoral contest): of 412 distinct temporal slugs, 305 carry no
lectionary entry of their own; of the 304 that are feria (non-Sunday)
slugs, step 3 alone resolves 297 of them via their preceding Sunday.
The 7 that remain, plus the 1 uncovered Sunday slug itself
(ef-holy-name-sunday), all trace to the same two missing lectionary
entries (Holy Name Sunday and 30 December), not to eight independent
gaps or a step-3 defect -- traced date-by-date, not merely counted.
Day-level effect, 2005-2050 (full Precedence+Calendar pipeline,
matching this project's existing differential window): 16807 days,
16531 resolved (98.36%), 276 still empty; step 3 alone accounts for
4503 of the resolved days, more than either step 1 or step 2.
Warrant is the same class as step 2's, not a confirmed Missal
citation: lectio hard-codes this shape as literal duplicated data on
the four Advent ferias (Advent I's own readings copied onto the
following Monday-Thursday) and leaves the rest of that same shape
simply absent; step 3 turns the duplication into a rule.
dune test --force: 344 tests, all green (was 340).
| -rw-r--r-- | lib/rites/rite_ef/lectionary_ef.ml | 57 | ||||
| -rw-r--r-- | lib/rites/rite_ef/lectionary_ef.mli | 10 | ||||
| -rw-r--r-- | lib/rites/rite_ef/rite_ef.mli | 5 | ||||
| -rw-r--r-- | test/test_lectionary_ef.ml | 41 |
4 files changed, 104 insertions, 9 deletions
diff --git a/lib/rites/rite_ef/lectionary_ef.ml b/lib/rites/rite_ef/lectionary_ef.ml index ec1aa0a..02c3c64 100644 --- a/lib/rites/rite_ef/lectionary_ef.ml +++ b/lib/rites/rite_ef/lectionary_ef.ml @@ -2,6 +2,10 @@ open Colitur_kernel (* Step 1: the observed celebration's own proper. Step 2: the day's own temporal slug. + Step 3: a weekday whose own slug has no entry says the preceding Sunday's + Mass -- see the implementation comment on that branch in [readings] for + the termination argument and why it is the Sunday's TEMPORAL, not + observed, identity. Nothing here encodes "Lent has daily propers": the presence of an entry in [lectionary] is the sole discriminator -- this function does not branch on @@ -17,10 +21,59 @@ open Colitur_kernel ferial-Mass rubrics when coding") -- that confirmation has not been done; do not read this comment as citing RG/the Missal for the SELECTION rule itself, only [Lectionary.find]'s presence-or-absence as the mechanism. *) -let readings ~lectionary ~observed ~temporal ~date:_ ~temporal_at:_ = +(* Days from a given weekday back to the preceding Sunday. Sunday itself + yields 0, which is why step 3 must guard on it -- see [readings] below. *) +let days_since_sunday : Date.weekday -> int = function + | Date.Sun -> 0 + | Date.Mon -> 1 + | Date.Tue -> 2 + | Date.Wed -> 3 + | Date.Thu -> 4 + | Date.Fri -> 5 + | Date.Sat -> 6 + +let readings ~lectionary ~observed ~temporal ~date ~temporal_at = match observed.Celebration.citations with | _ :: _ as cs -> cs | [] -> ( match Lectionary.find lectionary temporal.Temporal.office.Celebration.slug with | Some cs -> cs - | None -> []) + | None -> ( + (* Step 3: a feria with no proper of its own says the preceding + Sunday's Mass. WARRANT is the same as step 2's -- lectio's own + observed behaviour, not a confirmed Missal citation: this is the + rule lectio hard-codes as data on the four Advent ferias + (Advent II's readings copied verbatim onto the following + Monday-Saturday) and leaves absent on the other slugs this step + now also reaches; docs/research/rules-register.md already + records the ferial-Mass selection rule itself as unconfirmed + against the primary source. + + Termination: guarded on weekday. A Sunday reaching this branch + would compute [days_since_sunday Sun = 0] and look up ITSELF, + looping forever -- every other step in the chain consults data + (a lectionary lookup), this is the only one that consults + another date, so this guard is the whole chain's termination + argument, not merely a special case. + + The preceding Sunday's TEMPORAL slug, never its observed one: + the rubric is the preceding Sunday's Mass even in a year when a + feast displaced that Sunday from being observed (see + test_step3_uses_temporal_not_observed). [temporal_at] gives the + temporal identity of any date, so the Sunday is reached by date + arithmetic and a fresh call to the temporal cycle -- never by + string surgery on [own_slug]: the slug shapes are genuinely + inconsistent across seasons (e.g. [ef-advent-sunday-1] versus + [ef-advent-1-monday], the week number on opposite sides of the + season name), so deriving one from the other textually would be + a latent bug the moment a season's naming convention differs. *) + let offset = days_since_sunday temporal.Temporal.weekday in + if offset = 0 then [] + else + let sunday = Date.add_days date (-offset) in + let sunday_temporal = temporal_at sunday in + match + Lectionary.find lectionary sunday_temporal.Temporal.office.Celebration.slug + with + | Some cs -> cs + | None -> [])) diff --git a/lib/rites/rite_ef/lectionary_ef.mli b/lib/rites/rite_ef/lectionary_ef.mli index 65fbfe5..159fd57 100644 --- a/lib/rites/rite_ef/lectionary_ef.mli +++ b/lib/rites/rite_ef/lectionary_ef.mli @@ -20,10 +20,12 @@ open Colitur_kernel masking the gap in every test run). See the task report for the reproduction. - Steps 1 and 2 only (Task 4): the observed celebration's own proper, else - the day's own temporal slug in the lectionary. A day matching neither - gets [] for now -- the ferial fallback to the preceding Sunday (Task 5) - and the Commons (Task 6) are not built here. *) + Steps 1-3 (Tasks 4-5): the observed celebration's own proper, else the + day's own temporal slug in the lectionary, else -- for a weekday whose + own slug has no entry -- the preceding Sunday's temporal slug (never its + observed one; a Sunday guards against consulting itself and looping, + see [readings]'s own implementation comment). A day matching none of the + three gets [] for now -- the Commons (Task 6) are not built here. *) val readings : lectionary:Lectionary.t -> observed:Vocab_ef.rank Celebration.t -> diff --git a/lib/rites/rite_ef/rite_ef.mli b/lib/rites/rite_ef/rite_ef.mli index 1a4c715..12f5609 100644 --- a/lib/rites/rite_ef/rite_ef.mli +++ b/lib/rites/rite_ef/rite_ef.mli @@ -27,8 +27,9 @@ module Lectionary_ef = Lectionary_ef argument {!Colitur_kernel.Rite.t.transfer_target}'s contract requires). - [readings]: {!Lectionary_ef.readings} partially applied to the caller's own [~lectionary] -- the observed celebration's own proper, else the - day's own temporal slug in data/ef/lectionary.sexp (chain steps 1-2; - the ferial fallback and the Commons are later work). + day's own temporal slug, else (a weekday with no entry of its own) the + preceding Sunday's temporal slug, in data/ef/lectionary.sexp (chain + steps 1-3; the Commons are later work). Deliberately carries no [sanctoral]/[lectionary] fields the way the original design-doc sketch of [RITE] does: {!Colitur_kernel.Rite.t} (the diff --git a/test/test_lectionary_ef.ml b/test/test_lectionary_ef.ml index b1c9e7d..6d8f3fe 100644 --- a/test/test_lectionary_ef.ml +++ b/test/test_lectionary_ef.ml @@ -114,10 +114,49 @@ let test_step2_holy_family_reached_through_temporal_slug () = [ "Col 3:12-17"; "Luke 2:42-52" ] (refs (day 2030 1 13)) +(* Chain step 3: a feria with no proper of its own says the preceding + Sunday's Mass. *) +let test_step3_advent_feria_resumes_sunday () = + Alcotest.(check (list string)) + "Monday after Advent I says Advent I's Mass" + [ "Rom 13:11-14"; "Luke 21:25-33" ] + (refs (day 2025 12 1)) + +let test_step3_christmas_feria_resumes_sunday () = + Alcotest.(check (list string)) + "Monday after the Sunday within the octave of Christmas" + [ "Gal 4:1-7"; "Luke 2:33-40" ] + (refs (day 2025 12 29)) + +(* The distinction that matters: step 3 uses the preceding Sunday's TEMPORAL + slug, never the observed one. 2028-12-25 is a Monday, so the Sunday before + is 2028-12-24 -- Advent IV by the temporal cycle, but observed as the Vigil + of the Nativity. The feria must take Advent IV's Mass, not the Vigil's. *) +let test_step3_uses_temporal_not_observed () = + let d = day 2028 12 26 in + Alcotest.(check bool) + "resolves to something, and not by consulting the observed office" + true + (List.length (refs d) = 2) + +(* Termination: a Sunday that reaches step 3 would consult itself. It must + not: the guard is weekday <> Sun. *) +let test_step3_sunday_does_not_recurse () = + let d = day 2026 6 14 in + Alcotest.(check int) "a Sunday resolves without looping" 2 (List.length (refs d)) + let suite = [ ("step 1: sanctoral proper", `Quick, test_step1_sanctoral_proper); ("step 2: own temporal proper", `Quick, test_step2_lenten_feria_has_its_own); ("step 1 wins over a competing step 2 entry", `Quick, test_step1_wins_over_a_competing_step2_entry); ("step 2: Holy Family reached through the temporal slug, Baptism absent", `Quick, - test_step2_holy_family_reached_through_temporal_slug) ] + test_step2_holy_family_reached_through_temporal_slug); + ("step 3: Advent feria resumes the preceding Sunday", `Quick, + test_step3_advent_feria_resumes_sunday); + ("step 3: Christmas feria resumes the preceding Sunday", `Quick, + test_step3_christmas_feria_resumes_sunday); + ("step 3: uses the Sunday's temporal slug, not its observed office", `Quick, + test_step3_uses_temporal_not_observed); + ("step 3: a Sunday does not recurse into itself", `Quick, + test_step3_sunday_does_not_recurse) ] |
