From 89b6cfbc8c2a9f5921c19543f893decc2497ef69 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Sat, 22 Aug 2026 12:29:06 +0200 Subject: fix(ef): check RG 24/25's ferial invariant instead of asserting it rubrics_ef.ml's Creed check excluded feriae from 475(b)/(c)/(e) via an explicit test for feria I classis (Ash Wednesday, Holy Week) and vigils only. RG 24/25's feriae II and III classis (Advent 17-23 December, the Advent/Lent/September Ember sets, the numbered Lenten/Passiontide ferias) were excluded by no check at all, correct today only because of an unstated Temporal_ef property (no ferial-origin office it builds carries subject Lord/Bvm or an apostle-list slug). A comment read as if this were established "by construction of the two exclusions immediately above", which overclaimed: those two exclusions cover feria I classis only. Corrected the comments to name RG 24/25 explicitly, state what is and is not checked, and cite where the real guarantee comes from. Added a test that sweeps Temporal_ef.temporal across a century (1583-1682), asserting the invariant directly against every ferial-origin day found, with its own coverage counters confirming the span actually reaches Advent 17-23, all three non-Whitsun Ember sets, and Lenten/Passiontide ferias. The first run of the test found a real gap in its own population filter (RG 17(a)'s 2 January Holy Name fallback, a genuine feast sitting outside Temporal_ef.named for an architectural reason), fixed by naming that date explicitly rather than narrowing the span. Proved the test has teeth by mutation: forcing the ferial builder's default subject to Lord reddened it (and 24 other tests, expected collateral from an intentionally broad mutation) before being reverted. No production logic changed -- only comments in rubrics_ef.ml. day, readings and rubrics output confirmed byte-identical against the pre-fix binary for four spot years across the domain. Also corrects a stale test-case count in a prior task report (18 in the array, not the 19 recorded there); the array now genuinely holds 19. --- lib/rites/rite_ef/rubrics_ef.ml | 70 +++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) (limited to 'lib/rites/rite_ef/rubrics_ef.ml') diff --git a/lib/rites/rite_ef/rubrics_ef.ml b/lib/rites/rite_ef/rubrics_ef.ml index eef86cb..6345399 100644 --- a/lib/rites/rite_ef/rubrics_ef.ml +++ b/lib/rites/rite_ef/rubrics_ef.ml @@ -160,7 +160,48 @@ let creed ~(temporal : (Vocab_ef.season, Vocab_ef.rank) Temporal.t) liturgical action has no Mass in the 1955-restored Holy Week to begin with, so the question is moot there independent of this check -- but this structural test correctly excludes it too, since - it is also named in RG 23(b).) *) + it is also named in RG 23(b).) + + RG 24-25 (immediately following, same Caput IV) name TWO MORE + ferial classes this branch does not reach: feriae II classis (RG + 24 -- the Advent ferias 17-23 December, and the Quatuor Temporum + sets of Advent, Lent and September) and feriae III classis (RG 25 + -- the numbered Lenten/Passiontide ferias from the Thursday after + Ash Wednesday to the Saturday before Passion Sunday II, and the + un-Embered Advent ferias to 16 December). NEITHER is excluded by a + check anywhere in this module -- this branch and RG 33's vigil + branch immediately below are the whole of what [creed] tests + before falling through to 475(b)/(c)/(e)'s own rank/subject/slug + guards. They do not need an explicit exclusion of their own for + 475(b): temporal_ef.ml's own [ferial_rank] never returns [Class1] + for them (only Ash Wednesday and Holy Week do, RG 23's own population, + already excluded above), so that branch is unreachable for them + regardless of anything checked here. But RG 24's own II-class set + DOES reach 475(c)'s [Class2] guard below, and 475(e)'s + apostle-slug guard carries no rank floor at all, so both classes + genuinely reach a live branch of this function. Getting [false] + there is NOT established by this exclusion, or by any check in + this file: it rests on an unstated property of {!Temporal_ef} -- + every ferial-origin office it builds carries [subject = Temporal] + and an "ef-"-prefixed slug, never [Lord]/[Bvm] or a name on + {!creed_apostle_slugs}. Verified directly in temporal_ef.ml: the + Ember/Rogation/generic-ferial branches all go through [build]'s + own default [subject = Temporal], with exactly two documented + exceptions, neither able to reach a live branch below -- the + Sacred Triduum ([subject = Lord], but [Class1], already excluded + above) and the votive Office of the BVM on Saturday, RG 91 entry + 27 ([subject = Bvm], but [Class4], never satisfying 475(c)'s own + [Class2] guard). + + (CORRECTED, review fix: this comment, and 475(b)'s own immediately + below, previously read as though "the two exclusions immediately + above" disposed of ferias in general -- they dispose of feria I + classis only. RG 24/25's own higher classes were excluded by NO + check, an unstated-invariant gap now named here rather than + silently relied on: test/test_rubrics_ef.ml's own ferial-invariant + sweep now asserts the Temporal_ef property above directly against + real output, so a future change that broke it would fail that + test rather than silently changing the Creed.) *) n = -46 || (n >= -6 && n <= -1) then false else if @@ -183,13 +224,17 @@ let creed ~(temporal : (Vocab_ef.season, Vocab_ef.rank) Temporal.t) then false else if (* RG 475(b): "in festis I classis". Genuine feasts only, by - construction of the two exclusions immediately above (feriae, - vigils) -- every remaining [Class1] candidate reaching this branch - is a real festum: the Nativity, Epiphany, Ascension, Corpus Christi, - the Sacred Heart, Christ the King, a I-class sanctoral feast (the - Assumption, the Immaculate Conception...), or a I-class Sunday - (already [true] above via 475(a), so this branch is never the FIRST - to grant those a [true], only ever redundant with it). *) + construction of the two exclusions immediately above (feria I + classis -- Ash Wednesday and Holy Week, the only feriae that are + ever [Class1]; RG 24/25's II- and III-class feriae are a different + population and never reach this rank at all, see the RG 23 + comment's own note above -- and vigils) -- every remaining + [Class1] candidate reaching this branch is a real festum: the + Nativity, Epiphany, Ascension, Corpus Christi, the Sacred Heart, + Christ the King, a I-class sanctoral feast (the Assumption, the + Immaculate Conception...), or a I-class Sunday (already [true] + above via 475(a), so this branch is never the FIRST to grant those + a [true], only ever redundant with it). *) observed.Celebration.rank = Vocab_ef.Class1 then true else if @@ -207,7 +252,14 @@ let creed ~(temporal : (Vocab_ef.season, Vocab_ef.rank) Temporal.t) ships [subject = Saint] instead -- {!Precedence_ef.marian_slugs} is the list built (and, here, reused rather than re-derived) precisely because the [subject] field cannot be trusted alone for this - question; see its own citation in precedence_ef.mli. *) + question; see its own citation in precedence_ef.mli. + + This [Class2] guard is also the one live branch RG 24's own + II-class feriae (Advent 17-23, the Advent/Lent/September Ember + sets) genuinely reach -- the RG 23 comment above (this file, the + [n = -46 ...] branch) has the full account of why they still come + out [false] here: an unstated Temporal_ef property, checked by a + test, not a guard in this file. *) observed.Celebration.rank = Vocab_ef.Class2 && (observed.Celebration.subject = Subject.Lord || observed.Celebration.subject = Subject.Bvm -- cgit v1.3