From c464c0435c650cf019eb6c6ae1906217d557a15f Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 04:12:53 +0200 Subject: kernel(temporal): Holy Thursday is white, per RG128(b)'s named exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RG 128(b) excepts "Missa sive Chrismatis sive in Cena Domini feria V Hebdomadae sanctae" from the Septuagesima-to-Easter-Vigil violet span as a whole-Mass exception (unlike Palm Sunday's blessing/procession, which the same sentence carves out as only part of that day) -- so Holy Thursday's Mass is white, not violet. temporal_ef.ml fell through to the generic Passiontide ferial path (season_colour Passiontide = violet) for this date, since [named] has no entry for the Triduum at all (rank was already correct via [privileged_feria]; only colour was wrong). Found via Task 16's missalemeum oracle comparison: every other Triduum day's oracle colour set includes violet as an option (Good Friday "bv", Holy Saturday "vw" -- already-flagged, deliberately deferred per-action gaps, register §3b), but Holy Thursday's is white alone. This is a genuine NEW divergence from lectio too (lectio has no such exception and still prints violet for Holy Thursday) -- added as Layer C11 to the differential harness and data/ef/expected-divergences .sexp, 46 rows (2005-2050), rather than silently changing what that harness accepts. Also corrected a stale comment on season_colour claiming white's RG paragraph was never pinned -- RG 119 was found and cited in the register on 2026-08-11 but the correction was never copied into this file's own comment. --- lib/rites/rite_ef/temporal_ef.ml | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'lib/rites/rite_ef/temporal_ef.ml') diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 52a9adb..b39da79 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -314,13 +314,15 @@ let privileged_feria d = (n >= -6 && n <= -1) || (n >= 1 && n <= 6) || (n >= 50 && n <= 55) (* RG 117 enumerates the five colours (white, red, green, violet, black); - RG 127 assigns green and RG 128 violet to the seasons de Tempore below. - White's own specific paragraph (the "B) De colore albo" section, between - 117 and 123) was not pinned by the primary-source search available here -- - left uncited rather than guessed; see register §3 "Colours". *) + RG 127 assigns green and RG 128 violet to the seasons de Tempore below. RG + 119 (register §3b, primary-source-verified 2026-08-11 -- this comment was + stale until Task 16 noticed the correction had not been copied down here): + white "a festo Nativitatis Domini usque ad expletum tempus Epiphaniae" + and "a Missa Vigiliae paschalis usque ad Missam vigiliae Pentecostis + exclusive" -- exactly Christmastide and Paschaltide below. *) let season_colour = function | Advent | Septuagesima | Lent | Passiontide -> Colour.Violet (* RG 128 *) - | Christmastide | Paschaltide -> Colour.White + | Christmastide | Paschaltide -> Colour.White (* RG 119 *) | Time_after_epiphany | Time_after_pentecost -> Colour.Green (* RG 127 *) (* Gaudete (Advent III) and Laetare (Lent IV) are rose: RG 131, "may be used... @@ -411,6 +413,27 @@ let temporal d = let colour = (* The Pentecost octave weekdays are red, not Paschaltide's white. *) if days_between easter d >= 50 && days_between easter d <= 55 then Colour.Red + (* RG 128(b) (docs/research/rules-register.md §3b), primary + text: "...a dominica in Septuagesima usque ad Vigiliam + paschalem, EXCEPTIS: ... MISSA SIVE CHRISMATIS SIVE IN + CENA DOMINI FERIA V HEBDOMADAE SANCTAE; ..." -- violet + runs Septuagesima to the Easter Vigil EXCEPT (among + others) "the Mass, whether of the Chrism or in Cena + Domini [Holy Thursday], on Thursday of Holy Week" -- + named as a WHOLE-MASS exception (unlike Palm Sunday's + "blessing and procession of palms", which the SAME + sentence carves out as only PART of that day, register + §3b's own RG126 note on the not-yet-modelled per-action + nuance), so this is a clean whole-day colour fact, not + a per-action one the day/colour model cannot express. + Task 16, found via the missalemeum oracle comparison: + every other Triduum day's oracle colour SET includes + violet as one option (Good Friday "bv", Holy Saturday + "vw" -- RG 132's black is a separate, ALREADY-flagged + gap, register §3b, not touched here), but Holy + Thursday's is white ALONE -- confirming this specific + day, and only this one, needs the exception coded. *) + else if days_between easter d = -3 then Colour.White else season_colour s in let week_n = week d in -- cgit v1.3 From 0121abf66700879b31ef0b9a13e936b95e123c8f Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 05:25:58 +0200 Subject: kernel(precedence): fix round 1 -- RG23-26 govern ferial commemoration, not RG109 Review round 1 (F1, Critical): the commemoration-eligibility fix from the previous commit silently dropped II-class Advent and Lent Ember ferias when impeded, while ordinary (lower-solemnity) ferias of the same seasons kept their commemoration -- backwards on any reading. Reproduced: 1900-12-21 (an Advent Ember Friday) lost its commemoration entirely; 2026-12-21 (an ordinary Advent feria the same week) kept its. F2 (Important, the direct cause of F1): the branch's own justification mis-stated RG 109 as an exhaustive list of the only temporal circumstances that generate a commemoration. RG 109 is headed "Commemorationes privilegiatae sunt commemorationes" and closes "Omnes aliae commemorationes sunt commemorationes ordinariae" -- it sorts commemorations that already exist into two HONOUR classes (RG 108's differing liturgical hours), and says nothing about which offices have the right to be commemorated at all. That right belongs to Caput IV, "De feriis" (RG 21-27), never opened by the original pass: RG 24 (II-class ferias): "si vero impediuntur, commemorari debent" -- if impeded, MUST be commemorated. Not optional. RG 25 (III-class ferias): same mandate. RG 26: every feria not named in 23-25 is IV class, and IV-class ferias are NEVER commemorated -- the one exclusion RG 21-27 actually states. The omission branch is now gated on `rank = Class4` (RG 26) directly, not on `privilege_of = Ordinary` -- the old gate happened to reach the right answer for IV-class ferias (RG 26 also excludes them, for a reason the old citation did not give) and the wrong one for II/III- class Ember ferias RG 109(e) did not name by letter. RG 109(e) itself is also corrected: its bare "feriis Adventus, Quadragesimae et Passionis" previously excluded the Advent and Lent Ember sub-days by analogy with (d)'s separate September carve-out. RG 91's own TABLE needs an explicit "exceptis feriis Quatuor Temporum" at entries 22 and 25 to keep Ember days from being double- listed against their own entry 18 -- an exception that would be unnecessary drafting if "feriae Adventus"/"feriae Quadragesimae" did not already include their Ember sub-days by default. RG 109(e) carries no such exception, so it is read at that same default, inclusive scope: the Advent and Lent Ember ferias are privileged under (e), not merely ordinary-but-commemorable. (d)'s own existence is unaffected -- September Ember days sit outside Advent/Lent/Passiontide under any reading, so (d) remains necessary regardless. Verified: 1900-12-21 and 1902-02-22 now correctly commemorate their Ember ferias; 2026-12-21 and 1902-02-24 (ordinary ferias) unchanged. Blast radius re-measured against the prior commit, 1900-2100: 102 civil days changed, every one an Advent/Lent Ember (or Ember-vs- Joseph-collision) day regaining its commemoration, nothing else. Validate re-swept exhaustively, 1583-9998: 0 failures. F6: two privilege-boundary test rows that had been bent to expect the bug's own output (Commemorate -> Omit) are restored to what RG 24/25 actually require, now Commemorate(Privileged) given the RG 109(e) correction above -- these are the rows that should have caught F1. F7: admit_cases had no witness for RG 111(b)'s rank floor that wasn't already Class2, so reverting that filter only reddened the oracle suite, never this file. Added a row (II-class Sunday, sole candidate an ordinary Class3) that fails without the filter and passes with it. F9: RG 122 cited alongside RG 128(b) for Holy Thursday's white -- states the same fact affirmatively ("Demum adhibetur color albus, feria V Hebdomadae sanctae...") rather than as an exception to violet. --- lib/rites/rite_ef/precedence_ef.ml | 210 +++++++++++++++++++++++-------------- lib/rites/rite_ef/temporal_ef.ml | 7 ++ test/test_precedence_ef.ml | 96 ++++++++++++----- 3 files changed, 207 insertions(+), 106 deletions(-) (limited to 'lib/rites/rite_ef/temporal_ef.ml') diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index fd83383..88f1a54 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -380,22 +380,46 @@ let privilege_of (c : Vocab_ef.rank Precedence.candidate) : Precedence.privilege (* (c) register line 375: "of days within the Octave of the Nativity". *) else if is_temporal && String.starts_with ~prefix:nativity_octave_prefix slug then Precedence.Privileged - (* (d) register line 375-376: "of September Ember days" -- deliberately - ONLY the September set: RG 109 does not list the Advent or Lent Ember - sets (also II class, RG 91 entry 18), so those must fall through to - "ordinary", not be caught here or at (e) below. *) + (* (d) register line 375-376: "of September Ember days" -- named on its + own because September falls entirely outside (e)'s three seasons + (Advent/Lent/Passiontide) under ANY reading, not because it needs + excluding FROM (e) the way review round 1's F1/F2 finding corrected + the Advent/Lent Ember sets below to no longer need. *) else if is_temporal && String.starts_with ~prefix:september_ember_prefix slug then Precedence.Privileged (* (e) register line 376: "of ferias of Advent, Lent and Passiontide" -- - [not (is_ember_18 slug)] is required, not redundant with (d): the - Advent and Lent Ember prefixes ("ef-advent-ember-", "ef-lent-ember-") - also start with this branch's own [alp_feria_prefixes] entries - ("ef-advent-", "ef-lent-"), and RG 109 does not privilege them (see (d) - above) -- without this exclusion they would wrongly match here. *) - else if is_temporal - && (not (is_ember_18 slug)) - && List.exists (fun p -> String.starts_with ~prefix:p slug) alp_feria_prefixes - then Precedence.Privileged + CORRECTED, fix round 1 (F1/F2): this branch previously excluded the + Advent and Lent Ember sets via [not (is_ember_18 slug)], reading RG + 109(e)'s bare "feriis Adventus, Quadragesimae" as tacitly narrower than + the ordinary ferias of those seasons, on the theory that (d)'s separate + September carve-out implied Ember days needed excluding from (e) too. + That reading does not survive comparing (e)'s text against RG 91's own + TABLE entries for the same seasons (register §4, "Ferias of Lent and + Passiontide... EXCEPTIS feriis Quatuor Temporum" at entry 22; "Ferias + of Advent... EXCEPTIS feriis Quatuor Temporum" at entry 25): the table + needs an explicit "exceptis" to keep Ember days from being double- + listed at both their own entry 18 AND entries 22/25 -- and an explicit + exception is only necessary because, ABSENT one, "feriae Adventus"/ + "feriae Quadragesimae" already DO include their Ember sub-days by + default (an unnecessary exception is not how a rubrical text is + drafted). RG 109(e) carries no such "exceptis" clause, so its bare + "feriis Adventus, Quadragesimae" is read at that same default, + INCLUSIVE scope: the Advent and Lent Ember ferias ARE "ferias of + Advent"/"of Lent" in RG 109(e)'s sense, hence privileged, not merely + ordinary. (d)'s own separate existence is unaffected by this reading + either way -- September Ember days sit in "time after Pentecost", + never within Advent/Lent/Passiontide under any reading, so (d) remains + necessary regardless; it is not evidence for excluding Advent/Lent + Ember from (e), only for including September at all.) Consequently + [is_ember_18] is no longer tested here -- an Advent/Lent Ember slug + matches this branch exactly like an ordinary Advent/Lent feria slug + does, via the same [alp_feria_prefixes] prefix test; only a September + Ember slug is structurally excluded, because "ef-september-ember-*" + never starts with any of [alp_feria_prefixes] ("ef-advent-"/"ef-lent-"/ + "ef-passiontide-") in the first place -- (d) above already privileges + it under its own name. *) + else if is_temporal && List.exists (fun p -> String.starts_with ~prefix:p slug) alp_feria_prefixes then + Precedence.Privileged (* (f) register line 376-377: "of the Major Rogations, in Mass" -- the Major Litanies (25 April, RG 80) are not yet computed anywhere in this codebase (temporal_ef.ml's own comment on [temporal]'s Rogation branch: @@ -469,73 +493,102 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) else if is_temporal && (not (is_vigil (Slug.to_string cel.Celebration.slug))) - && (match privilege_of loser with Precedence.Ordinary -> true | Precedence.Privileged -> false) + && cel.Celebration.rank = Class4 then - (* Task 16, primary-source-verified (RG 93, 95, 109, 113): an ordinary, - NON-privileged TEMPORAL-cycle office has no standing to be - commemorated at all when impeded -- it is simply omitted, not the - "commemorated or omitted, per rubric" residual RG 95 leaves open for - everything else. Three primary texts read together settle this: - - - RG 95: "Alia festa, ab Officio gradus superioris accidentaliter - impedita, AUT COMMEMORANTUR AUT, eo anno, PENITUS OMITTUNTUR, IUXTA - RUBRICAS" -- impeded offices are "either commemorated or, that - year, entirely omitted, ACCORDING TO THE RUBRICS" -- i.e. some - OTHER rule decides which fate applies; RG 95 itself does not grant - a commemoration to everything impeded. - - RG 109 gives that other rule for the temporal cycle: an EXHAUSTIVE, - closed six-item list of the only temporal-origin circumstances that - ever generate a commemoration -- (a) of a Sunday; (b) of a I-class - day; (c) of days within the Nativity Octave; (d) of the September - Ember days; (e) of Advent/Lent/Passiontide ferias; (f) of the Major - Rogations. [privilege_of] above already implements exactly this - list (its own six branches, each cited to its own RG 109 letter); - its terminal "[else Precedence.Ordinary]" is what a TEMPORAL-origin - candidate falls through to when it matches NONE of (a)-(f) -- an - ordinary green-season feria of Time after Epiphany/Pentecost/ - Easter, a plain (non-Ember) Advent/Lent weekday already caught by - (e), or a Minor Rogation day (RG 87 -- deliberately NOT named by - RG 109(f), see [privilege_of]'s own comment on that letter). - - RG 113: "Commemoratio de Tempore fit primo loco" -- the - commemoration OF THE TEMPORAL DAY is made FIRST [in the list, when - one is due] -- presupposes RG 109 already answered whether one is - due; it does not itself create a right for every impeded feria. - - So testing [privilege_of loser = Ordinary] here, for a TEMPORAL-origin - loser specifically, is not a second, parallel "is this commemorable" - predicate that could drift from RG 109's own list -- it IS RG 109's - list, already computed by [privilege_of] for the commemoration this - branch is about to deny. SANCTORAL losers are entirely unaffected - (the [is_temporal] guard): RG 111(c)/(d) admit an "ordinary" - commemoration of a losing SAINT freely, with no such closed-list - gate -- this omission is specific to the temporal cycle's own - ferial/Sunday-tail offices, never to a saint. + (* CORRECTED, fix round 1 (F1/F2 -- both real, the second the direct + cause of the first): the branch this replaces gated on + [privilege_of loser = Ordinary], justified by treating RG 109 as an + "exhaustive, closed list of the only temporal-origin circumstances + that ever generate a commemoration". That justification does not + survive reading RG 109 itself: it is headed "Commemorationes + PRIVILEGIATAE sunt commemorationes" and closes "Omnes aliae + commemorationes sunt commemorationes ORDINARIAE" -- it sorts + commemorations that ALREADY exist into two HONOUR classes + (privileged vs ordinary, RG 108's differing liturgical hours), and + says nothing about which offices have the RIGHT to be commemorated + in the first place. Testing [privilege_of = Ordinary] as an + ELIGIBILITY gate therefore happened to reach the right answer for + IV-class ferias (they are never commemorated, but for a reason + RG 109 does not state) and the WRONG answer for II- and III-class + ferias impeded during a season RG 109(e) does not privilege by name + (Advent 17-23 Dec's own ordinary-non-Ember ferias were fine, already + matching (e)'s slug prefix; the Advent and Lent EMBER ferias were + not, since the pre-fix (e) excluded them -- see [privilege_of]'s own + fix-round-1 comment above, which independently corrects THAT half + too). Confirmed wrong by direct reproduction (fix-round-1 review): + 1900-12-21 (an Advent Ember Friday, RG 91 entry 18, II class) lost + its own commemoration entirely under the pre-fix code, while an + ORDINARY (non-Ember, lower-solemnity) Advent feria the same week + kept its commemoration -- backwards on any reading. + + The actual rule is Caput IV, "De feriis" (RG 21-27), which the + original Task 16 pass never opened -- a FERIAL-CLASS-keyed rule, + entirely separate from RG 109's HONOUR-class one: + - RG 23 (I-class ferias -- Ash Wednesday, Holy Week): "nullam + admittunt commemorationem, nisi unam privilegiatam" -- admit no + commemoration except one privileged one. Never actually reaches + this function as a loser (these ferias structurally always + outrank anything that could coincide with their dates -- {!band} + entries 2/7, see that function's own file comment and the Task 11 + Easter-window invariant), so this clause has no live witness, the + same as before. + - RG 24 (II-class ferias -- Advent 17-23 Dec, the Advent/Lent/ + September Ember ferias, RG 91 entry 18): "si vero impediuntur, + COMMEMORARI DEBENT" -- if indeed impeded, they MUST be + commemorated. Not optional, not conditioned on RG 109's list. + - RG 25 (III-class ferias -- ordinary Lent/Passiontide ferias, RG 91 + entry 22; ordinary Advent ferias to 16 Dec, entry 25): "Hae + feriae, si impediuntur, commemorari debent" -- same mandate. + - RG 26: "Omnes feriae, numeris 23-25 non nominatae, sunt feriae IV + classis; quae NUNQUAM COMMEMORANTUR" -- every feria not named in + 23-25 is IV class, and IV-class ferias are NEVER commemorated. + This is [ferial_rank]'s own unqualified IV-class catch-all + (temporal_ef.ml), covering the ordinary green-season ferias of + Time after Epiphany/Pentecost, Septuagesima, Paschaltide outside + its privileged octave, and the Minor Rogation days (RG 87/88 -- + they change nothing in the Office, so they take their season's + plain ferial class, which for Paschaltide-adjacent dates is + IV, not a special one). + + So this branch is now gated directly on RG 26's own condition + ([rank = Class4]), which is the ONLY ferial class RG 21-27 excludes + from commemoration -- Class1 is structurally unreachable here (RG + 23, above); Class2 and Class3 both fall through to the final + [Commemorate] branch below (RG 24/25's mandate), tagged with + whatever HONOUR class [privilege_of] separately computes for them + under RG 109 -- a question this branch no longer conflates with + eligibility. SANCTORAL losers are entirely unaffected (the + [is_temporal] guard): Caput IV governs FERIAE, RG 21's own opening + definition ("Nomine feriae intelleguntur singuli dies hebdomadae, + praeter dominicam"), never a saint's day; RG 111(c)/(d) admit an + "ordinary" commemoration of a losing SAINT freely, with no such + class-keyed gate. Empirically confirmed against the missalemeum oracle (Task 16, 2026-2027, both years): every one of ~190 days where a saint's feast - impedes an ordinary (non-privileged) temporal feria shows ZERO - commemorations in the oracle, including the exact shape this fixes - (e.g. "St. Marcellus I" impeding the plain "Friday after Epiphany", - 6/730 identical instances of the pattern per week of ordinary time) - -- and the SAME fix, for the same reason, independently corrects the - Minor Rogation days (RG 87) losing to a saint (9/730 days), which - [privilege_of]'s own (f) comment already flags as NOT RG 109(f). - - [is_vigil] is EXCLUDED from this branch deliberately: a II/III-class - vigil is temporal-origin too (when it is the Ascension/Pentecost- - adjacent case {!of_temporal} produces) and [privilege_of] rightly - calls it [Ordinary] (a vigil is none of RG 109(a)-(f)), but vigils - are NOT governed by RG 109 at all -- they carry their OWN, separate, - explicit commemoration mandate: RG 31 (II class) "Hae vigiliae - praeferuntur diebus liturgicis III et IV classis; ET, SI - IMPEDIUNTUR, COMMEMORANTUR, iuxta rubricas" and RG 32 (III class, St - Lawrence) "si impeditur, COMMEMORATUR, iuxta rubricas" -- "if - impeded, ARE/IS commemorated". So a vigil impeded WITHOUT triggering - RG 33's full omission (the [is_omissible_vigil] branch above, e.g. - impeded by an ordinary sanctoral feast that is neither a Sunday nor - I class) must still fall through to the final [Commemorate] branch - below, exactly like a sanctoral loser -- RG 31/32's own text, not - RG 109's closed list, is what governs it. *) + impedes an ordinary (IV-class, non-privileged) temporal feria shows + ZERO commemorations in the oracle (e.g. "St. Marcellus I" impeding + the plain "Friday after Epiphany"), and the SAME rank-4 gate, + independently, correctly still omits the Minor Rogation days (RG 87) + losing to a saint -- both consequences of RG 26 alone now, not of a + reading of RG 109 that RG 109's own text does not support. + + [is_vigil] is EXCLUDED from this branch for the same reason as + before, restated under the corrected citation: a II/III-class vigil + is temporal-origin too (the Ascension/Pentecost-adjacent case + {!of_temporal} produces) and typically Class2, so it would already + fall through this branch's [rank = Class4] test harmlessly on its + own -- RG 91 has no IV-class vigil at all (this file's own entry-27/ + 28 comments), so [is_vigil && rank = Class4] should never occur on + real data. Kept as an explicit, defensive guard (not load-bearing + for real data, but total over every candidate {!Precedence.resolve} + or {!Calendar} can construct, including shapes RG 91's table itself + does not describe) rather than relying on that absence silently: a + vigil, per RG 31 (II class, "si impediuntur, commemorantur") / RG 32 + (III class, "si impeditur, commemoratur"), is ALWAYS commemorated + once RG 33 does not omit it outright, regardless of ferial class -- + a rule Caput IV does not speak to at all (vigils are Caput V, RG + 28-34, not "feriae"). *) Precedence.Omit else (* RG 95's other branch: "aut commemorantur aut penitus omittuntur" -- @@ -543,8 +596,9 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) below I class (RG 111(c)/(d)'s "ordinary" commemoration, no closed list the way the temporal branch above has), AND by an impeded I-class Sunday (excluded from the [Transfer] branch above, and from - the temporal-Ordinary [Omit] branch above because [privilege_of]'s - (a) makes a Sunday loser [Privileged], never [Ordinary]): RG 109(a) + the temporal Class4 [Omit] branch above because a Sunday is never + IV class -- RG 11-12/91 entry 6/15 make every Sunday I or II class, + never a "feria" at all in Caput IV's own sense, RG 21): RG 109(a) (register line 374) lists "of a Sunday" as a privileged commemoration category, which presupposes an impeded Sunday stays put rather than moving to another day the way a feast does -- [privilege_of] tags it diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index b39da79..1b3d219 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -426,6 +426,13 @@ let temporal d = §3b's own RG126 note on the not-yet-modelled per-action nuance), so this is a clean whole-day colour fact, not a per-action one the day/colour model cannot express. + RG 122, fix round 1 (F9), states the same fact + affirmatively rather than as an exception to RG 128's + violet: "Demum adhibetur color albus, feria V + Hebdomadae sanctae, in Missa Chrismatis et in Missa in + Cena Domini" -- white is used, finally [among the + White section's own list], on Thursday of Holy Week, + in the Mass of Chrism and in the Mass in Cena Domini. Task 16, found via the missalemeum oracle comparison: every other Triduum day's oracle colour SET includes violet as one option (Good Friday "bv", Holy Saturday diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml index 2f08864..9bd9b7f 100644 --- a/test/test_precedence_ef.ml +++ b/test/test_precedence_ef.ml @@ -523,9 +523,13 @@ let privilege_cases = [Temporal_ef]'s own third-Sunday-of-September rule: first Sunday of September 2026 is the 6th, +14 days = 20th, +3 = 23rd), sourced from [Temporal_ef.temporal] itself, Class2. Not a Sunday, not Class1, not - within the Nativity octave, not an Advent/Lent Ember day (a DIFFERENT - Ember set, deliberately excluded by (d) -- see the negative row - below), not a plain Advent/Lent/Passiontide feria slug either. *) + within the Nativity octave, not a plain Advent/Lent/Passiontide feria + slug either -- and, unlike the Advent/Lent Ember rows below, its own + slug ("ef-september-ember-wed") never starts with any of (e)'s own + [alp_feria_prefixes] ("ef-advent-"/"ef-lent-"/"ef-passiontide-"), so + (d) is this candidate's ONLY route to [Privileged] -- a genuine, + still-necessary distinction from (e), unlike the Advent/Lent Ember + case below (fix round 1). *) ( "(d) a September Ember day is privileged", cand "ef-nativity", of_temporal (mk 2026 9 23), @@ -542,36 +546,49 @@ let privilege_cases = cand "ef-nativity", of_temporal (off (-41)), "Commemorate(Privileged)" ); - (* Negative, RG 109(d) vs (e)'s own boundary: the Advent and Lent Ember - sets are ALSO II-class ferias of Advent/Lent by RG 91 (entry 18), and - their slugs ("ef-advent-ember-*", "ef-lent-ember-*") share (e)'s own - season prefixes -- but RG 109 privileges ONLY the September set (d), - leaving these two ordinary. 16 Dec 2026 is the Advent Ember Wednesday - (independently derived: Advent I 2026 is 29 Nov, +14 days = 13 Dec, - +3 = 16 Dec); the Lent Ember Wednesday is the same date [off (-39)] - already used by the entry-18 [band] row above. Both sourced from - [Temporal_ef.temporal]. If [privilege_of] relied on the season prefix - alone without excluding Ember slugs, both would wrongly come back - [Privileged] -- the exact trap this pair of rows guards against. + (* CHANGED, fix round 1 (F1/F2/F6): these two rows used to be titled + "boundary: ... is NOT privileged (only September is, RG109(d))" and + expected [Omit] (a Task-16-pass reading that treated RG 109(e)'s bare + "feriis Adventus, Quadragesimae" as tacitly excluding the Advent and + Lent Ember sub-days, by analogy with (d)'s own separate, explicit + September carve-out). Review round 1 (F1) reproduced the real + consequence directly -- 1900-12-21, an Advent Ember Friday, lost its + own commemoration entirely, while an ordinary (lower-solemnity, + non-Ember) Advent feria the same week kept its commemoration -- + backwards on any reading, and traced it to this exact + misclassification (F2). - CHANGED, Task 16: these used to expect [Commemorate(Ordinary)] -- - "not privileged" originally meant "commemorated, but without RG 109's - higher liturgical honours". Now that a TEMPORAL-origin [Ordinary] - loser is Task 16's own [Omit] branch (see precedence_ef.ml), "not - privileged" for a temporal candidate means "not commemorable at all" - -- a sharper, more direct assertion of the same underlying - [privilege_of] classification, via the one vantage point available - on that private function. *) - ( "boundary: an Advent Ember day is NOT privileged (only September is, \ - RG109(d)) -- and being temporal+ordinary, TASK16 omits it entirely", + Corrected reading (precedence_ef.ml's own [privilege_of], (e) + branch, carries the full argument): RG 91's TABLE needs an explicit + "exceptis feriis Quatuor Temporum" at its own entries 22 and 25 to + keep Ember days from being double-listed against their own entry 18 + -- an exception that would be unnecessary drafting if "feriae + Adventus"/"feriae Quadragesimae" did not ALREADY include their Ember + sub-days by default. RG 109(e) carries no such "exceptis" clause, so + its bare text is read at that same default, INCLUSIVE scope: the + Advent and Lent Ember ferias ARE privileged under (e), not merely + commemorable-but-ordinary. (d)'s own separate existence survives + this reading intact -- September Ember days sit outside Advent/Lent/ + Passiontide under ANY reading, so (d) remains the ONLY way they + reach [Privileged], the point the row immediately above this one + makes explicit. + + 16 Dec 2026 is the Advent Ember Wednesday (independently derived: + Advent I 2026 is 29 Nov, +14 days = 13 Dec, +3 = 16 Dec); the Lent + Ember Wednesday is the same date [off (-39)] already used by the + entry-18 [band] row above. Both sourced from [Temporal_ef.temporal], + not hand-typed, for the same coupling-safety reason every + [of_temporal] row in this file uses it. *) + ( "(e), corrected fix round 1: an Advent Ember day is ALSO privileged, \ + not excluded from (e)", cand "ef-nativity", of_temporal (mk 2026 12 16), - "Omit" ); - ( "boundary: a Lent Ember day is NOT privileged (only September is, \ - RG109(d)) -- and being temporal+ordinary, TASK16 omits it entirely", + "Commemorate(Privileged)" ); + ( "(e), corrected fix round 1: a Lent Ember day is ALSO privileged, \ + not excluded from (e)", cand "ef-nativity", of_temporal (off (-39)), - "Omit" ); + "Commemorate(Privileged)" ); (* Negative, RG 109(f)'s own boundary: the Minor Litanies/Rogations (Monday/Tuesday before Ascension, RG 87 -- [Temporal_ef.temporal] DOES compute these, unlike the Major Litanies RG 109(f) actually @@ -623,6 +640,17 @@ let privileged_hi = cand ~rank:V.Class2 "ef-privileged-hi" thing under test, not merely how many. *) let ordinary_lowest = cand ~rank:V.Class4 "ef-ordinary-lowest" +(* Class3, tagged [Ordinary] -- fix round 1, F7: the RG 111(b) rank-floor + witness [admit_cases] was missing. [ordinary_hi] above is already Class2, + so every existing II-class-Sunday row here passes whether or not + [admit]'s "de festo II classis" filter is even present -- reverting that + filter would only redden [test_oracle.ml], not this file, which is + exactly the coverage gap the review round found. This candidate is the + ONLY thing due on the Sunday row below, so a version of [admit] without + the rank floor would (wrongly) admit it on pure "best available" + dignity, same as it would have admitted [ordinary_hi]. *) +let ordinary_class3 = cand ~rank:V.Class3 "ef-ordinary-class3" + let observed_class1 = cand "ef-nativity" (* Class1 by [cand]'s own default. *) let observed_class2_sunday = an_ordinary_sunday (* Class2, slug carries "-sunday". *) let observed_class2_other = cand ~rank:V.Class2 "ef-other-class2-day" (* Class2, no "-sunday". *) @@ -658,6 +686,18 @@ let admit_cases = observed_class2_sunday, [ (ordinary_hi, P.Ordinary); (privileged_lo, P.Privileged) ], [ "ef-privileged-lo" ] ); + (* RG 111(b)'s own rank floor ("scilicet DE FESTO II CLASSIS"), fix + round 1 F7: a Class3 ORDINARY candidate -- no privileged rival due, + so the pre-fix-round code's "no privileged? take the best of what's + left" fallback would (wrongly) admit it -- is admitted NOTHING. The + slot is reserved for a II-class candidate specifically; a III-class + ordinary one has no standing for it at all, unlike "other II class" + below, which has no such restriction. *) + ( "II-class Sunday, sole candidate is an ORDINARY Class3 (not \ + \"de festo II classis\") -> admitted nothing, not the best available", + observed_class2_sunday, + [ (ordinary_class3, P.Ordinary) ], + [] ); (* RG 111: "other II class: one" -- no privilege override, the exact asymmetry the brief and precedence_ef.ml's own [admit] comment flag: same candidate pair as the II-class-Sunday row above, OPPOSITE -- cgit v1.3 From 3159bed3c71c35fecc141cfd1453e27b6a4a3780 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 10:23:40 +0200 Subject: kernel(rite-ef): replace unearned/retracted citations with verified ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four citation problems, all resolved against the primary scans (docs/research/*.pdf via pdftotext): 1. temporal_ef.ml's Ember-day date-derivation comment still carried a retracted note claiming the scans have no numbered-paragraph statement of the rule. Register §3a corrected this in an earlier commit (the search used the genitive "Quatuor Temporum"; the rubric heading is the nominative "Quatuor Tempora") and the register's own words warn "a false 'not in the source' note is worse than no note" -- the code still carried the retracted note verbatim. Replaced with the verified citation and the primary Latin. 2. temporal_ef.ml cited RG 71 for "Advent I is the Sunday nearest 30 November". RG 71 is a season-boundary citation only ("a I Vesperis dominicae I Adventus..."); it does not say which Sunday opens the season. The actual rule is RG 20 (Caput III, "De Dominicis"): "Dominica I Adventus ea est, quae cadit die 30 novembris vel est ipsi proximior" -- verified against the scan and added to the register. Behaviour was always correct; only the citation was borrowed. 3. RG 88 ("de Litaniis minoribus nihil fit in Officio") is cited five times across code and tests, load-bearing twice (why Rogation days take their season's ordinary ferial class; why privilege_of lets them fall through to Ordinary), but appeared nowhere in the register. RG 67 and RG 69 were likewise cited at expected-divergences-missalemeum.sexp (M11) without register backing. All three verified against the scan and added to the register's §4 (Octaves / Rogations subsections). 4. Two citations flagged for re-verification, both CONFIRMED accurate against the primary scan (word for word): - precedence_ef.ml's RG 32 "si impeditur, commemoratur" (the sole textual basis for treating a vigil as always-commemorated once RG 33 doesn't omit it outright) -- the register previously stated RG 32 only as "same pattern as RG 31", not verbatim; the full sentence is now in the register too. - precedence_ef.ml's RG 111(b) full sentence (the sole textual basis for the shipped Sunday rank-floor fix) -- the register previously carried only the fragment "de festo II classis"; the full sentence is now there. - temporal_ef.ml's RG 91 entry 7 "feria IV cinerum et II, III et IV Hebdomadae sanctae" (the sole justification for stopping the I-class ferias at Wednesday) was also checked and matches the scan exactly; left as-is (already correctly cited), noted in the report. Bonus finding while re-verifying RG 20: RG 17(d), same chapter, states "festum D. N. Iesu Christi Regis, celebrandum dominica ultima mensis octobris" -- primary-source confirmation that Christ the King falls on the last Sunday of October. This was the register's one oracle-backed-but-not-primary-verified rule (CLAUDE.md's own carried-item language); it no longer is. Updated temporal_ef.ml's christ_the_king comment and closed the item in register §6. No behaviour change: every edit here is a comment/citation change to already-correct code. Verified byte-identical `colitur day` output across 1583, 1900, 1902, 2008, 2011, 2026, 2038, 9999. 259/259 tests green. --- lib/rites/rite_ef/precedence_ef.ml | 25 +++++++++++----- lib/rites/rite_ef/temporal_ef.ml | 59 +++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 24 deletions(-) (limited to 'lib/rites/rite_ef/temporal_ef.ml') diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index bfd3b93..3c417bb 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -596,7 +596,13 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) (III class, "si impeditur, commemoratur"), is ALWAYS commemorated once RG 33 does not omit it outright, regardless of ferial class -- a rule Caput IV does not speak to at all (vigils are Caput V, RG - 28-34, not "feriae"). *) + 28-34, not "feriae"). RG 32's own full sentence, primary-source- + verified (final fix wave): "Vigilia III classis est vigilia S. + Laurentii. Haec vigilia praefertur diebus liturgicis IV classis; et, + si impeditur, commemoratur, iuxta rubricas" -- confirmed word for + word against the scan, not constructed by analogy with RG 31 (the + register's own §4 "Vigils" entry states RG 32 only as "same pattern + [as RG 31]", not verbatim -- now closed here). *) Precedence.Omit else (* RG 95's other branch: "aut commemorantur aut penitus omittuntur" -- @@ -698,12 +704,17 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) several are. *) (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ best ]) | Class2, true -> - (* RG 111(b), primary text: "in dominicis II classis, una tantum - admittitur commemoratio, SCILICET DE FESTO II CLASSIS, quæ tamen - omittitur si commemoratio privilegiata facienda sit" -- "on Sundays - of the II class, only ONE commemoration is admitted, NAMELY OF A - FEAST OF THE II CLASS, which however is dropped if a privileged - commemoration is due." Two clauses, not one: (i) a privileged + (* RG 111(b), primary text, RE-VERIFIED word for word against the scan + (final fix wave; this sentence is the sole textual basis for the + shipped rank-floor fix below, and the register's own §4 "RG 111" + entry previously carried only the fragment "de festo II classis", + not the full sentence -- now added there too): "in dominicis II + classis, una tantum admittitur commemoratio, SCILICET DE FESTO II + CLASSIS, quæ tamen omittitur si commemoratio privilegiata facienda + sit" -- "on Sundays of the II class, only ONE commemoration is + admitted, NAMELY OF A FEAST OF THE II CLASS, which however is + dropped if a privileged commemoration is due." Two clauses, not + one: (i) a privileged commemoration, whenever due, categorically takes the day's one slot -- not by comparing its dignity against the ordinary contender's, so an ordinary commemoration that would otherwise win on raw diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 1b3d219..c3db15c 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -17,9 +17,17 @@ let weekday_index d = (* The Sunday on or before [d]. *) let sunday_on_or_before d = Date.add_days d (-(weekday_index d)) -(* RG 71: Advent I is the Sunday nearest 30 November -- equivalently the fourth - Sunday before Christmas, i.e. three weeks before the last Sunday on or before - 24 December. *) +(* RG 20 (Caput III, "De Dominicis"), primary-source-verified (final fix + wave): "Dominica I Adventus ea est, quae cadit die 30 novembris vel est + ipsi proximior" -- Advent I Sunday is that which falls on 30 November or + is nearest to it. CORRECTED citation: this comment previously cited RG + 71 for this placement rule -- WRONG, RG 71 (cited on [season] below) + states only Advent's own season BOUNDARY ("a I Vesperis dominicae I + Adventus..."), not which Sunday opens it; the register's own RG 71 entry + is a boundary citation, and the only "nearest 30 November" text there + before this fix was UNLYC nn. 39-42, the MODERN form's rule, not this + one's. Equivalently the fourth Sunday before Christmas, i.e. three weeks + before the last Sunday on or before 24 December. *) let advent_start y = Date.add_days (sunday_on_or_before (mk y 12 24)) (-21) let year_start = advent_start @@ -47,8 +55,13 @@ let season d = else if Date.compare d paschal_end <= 0 then Paschaltide (* RG 76 *) else Time_after_pentecost (* RG 77 *) -(* Last Sunday of October, per the 1960 calendar -- NOT the OF's last Sunday - before Advent. Register §6 flags this for primary-source confirmation. *) +(* RG 17(d) (Caput III, "De Dominicis"), PRIMARY-SOURCE-VERIFIED (final fix + wave, closing the item register §6 previously carried as "oracle-backed, + not yet primary-verified"): "festum D. N. Iesu Christi Regis, celebrandum + dominica ultima mensis octobris" -- the feast of Our Lord Jesus Christ + the King is to be celebrated on the LAST SUNDAY OF OCTOBER. NOT the OF's + last Sunday before Advent -- a genuine EF/OF divergence, not merely a + citation gap. *) let christ_the_king y = sunday_on_or_before (mk y 10 31) let same a b = Date.compare a b = 0 @@ -253,18 +266,30 @@ let id = "ef" (* The third Sunday of September: the Ember week's anchor. - This specific date-derivation rule is one of the more contested points in - the 1962 calendar: pre-1955 practice tied the September Ember days to the - week following the Exaltation of the Holy Cross (14 Sept) instead. The two - rules only disagree when 1 September is a Monday -- 2025 is such a year -- - and the primary-source scan available to this project does not contain an - explicit numbered-paragraph statement of either rule (searched; see - register §3 "Ember days"), so this citation is deliberately left at the - rank rules only (RG 91 entries 18/22, cited on [ember] below), not the - date-derivation rule itself: a wrong citation is worse than none. - Empirically: for 2025 this rule gives 24/26/27 September, confirmed - against an independent oracle; the Holy-Cross rule would give 17/19/20 - September instead. See register §3 for the full note. *) + [cited] PRIMARY-SOURCE-VERIFIED (register §3a): MR1962, "De anno et eius + partibus", under the heading "Quatuor Tempora" (not a numbered RG + paragraph, which is why an earlier paragraph-number search missed it): + + "Quatuor Tempora celebrantur quarta et sexta feria ac sabbato post + tertiam dominicam Adventus, post primam dominicam Quadragesimae, post + dominicam Pentecostes, post dominicam tertiam septembris." + + -- the Ember Days are kept on the Wednesday, Friday and Saturday after + Advent III, after Lent I, after Pentecost, [and] after the third Sunday + of September -- confirming all four of this module's anchors, including + this specific contested one. This specific date-derivation rule was one + of the more contested points in the 1962 calendar: pre-1955 practice + tied the September Ember days to the week following the Exaltation of + the Holy Cross (14 Sept) instead. The two rules only disagree when 1 + September is a Monday -- 2025 is such a year, and confirms the + third-Sunday reading empirically too (24/26/27 September against an + independent oracle, vs the Holy-Cross rule's 17/19/20). An earlier + version of this comment said the scan contained no numbered-paragraph + statement of either rule and left the citation at the rank rules only + (RG 91 entries 18/22) -- WRONG, corrected once the nominative heading + "Quatuor Tempora" was found rather than the genitive "Quatuor Temporum" + the original search used; register §3a records the correction, because a + false "not in the source" note is worse than no note. *) let third_sunday_of_september y = let sep1 = mk y 9 1 in let first_sunday = Date.add_days sep1 ((7 - weekday_index sep1) mod 7) in -- cgit v1.3 From 6d6ba502d8022d9e0b8cdc5302f5761d39895192 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 10:40:27 +0200 Subject: kernel+rite-ef: correct stale plan-relative deferrals (item 7, part 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several comments described work as "arriving with Plan 3" or "waiting for Plan 3" that either shipped without it or refer to the wrong future plan number, all now false in ways that would mislead the next reader: - temporal_ef.ml (two places) and precedence_ef.ml said the Major Litanies (25 April, RG 80) "arrive with Plan 3's sanctoral". Plan 3 shipped, in this branch, without them; register §6 tracks this as a plain open item with no plan committed to build it, and now says so. - temporal_ef.ml said the Sacred Triduum's "own named offices are a Plan 3 sanctoral addition". Wrong on two counts: Plan 3 shipped without adding them, AND a proper office for I-class FERIAS was never a sanctoral matter in the first place (RG 21 defines "feria" to exclude Sundays/feasts, not the reverse). 2026-04-02/03/04 still resolve to the ordinary Passiontide ferial fallback's own generic slugs (ef-passiontide-2-{thursday,friday,saturday}), confirmed against real output; register §6 now records this as its own open item. - temporal_ef.ml said the Rogation-Wednesday commemoration (Ascension Vigil day, entry 21) "waits for RG 108-111" -- both the precedence framework and RG 108-111 exist now; the Wednesday's own commemoration is still never constructed, but for a different, still-real reason (no candidate is wired for it), not a forward dependency. Fixed at both of this comment's two occurrences in the file. - vocab.ml/vocab.mli's `seasons` field doc said "Validate's contiguity check reads this" -- false since validate.ml's "seasons" check switched to Rite.t.season_runs in this branch (rite-supplied, to support a season appearing in more than one run, which the modern form's Ordinary Time needs and EF does not). - vocab.ml/vocab.mli's `ranks` field doc said "it is not a precedence relation until Plan 3 defines one" -- Plan 3 did define one (RG 111's dignity ordering), but as its own small, separately-hardcoded function in precedence_ef.ml, not one derived from this field; corrected to say so precisely rather than leaving a forward-looking claim unresolved. - The lectionary bootstrap (reading citations, Liturgical_day.t's own `citations` field) is called "Plan 3" at slug.ml, vocab_ef.ml, and three places in temporal_ef.ml, but "Plan 4" at liturgical_day.mli (whose own doc comment -- "always empty until Plan 4" -- is the authoritative one: the SANCTORAL bootstrap is Plan 3 and shipped; the LECTIONARY bootstrap is a separate, later Plan 4). All six corrected to say Plan 4, cross-referencing the Plan 3/4 distinction at the first (slug.ml) occurrence so the reasoning is not duplicated six times. No behaviour change: every edit here is a comment/documentation correction. Verified byte-identical `colitur day` output across 1583, 1900, 1902, 2008, 2011, 2026, 2038, 9999. 259/259 tests green. --- lib/kernel/slug.ml | 8 +++- lib/kernel/vocab.ml | 25 +++++++++++-- lib/kernel/vocab.mli | 25 +++++++++++-- lib/rites/rite_ef/precedence_ef.ml | 12 +++--- lib/rites/rite_ef/temporal_ef.ml | 77 +++++++++++++++++++++++++++----------- lib/rites/rite_ef/vocab_ef.ml | 5 ++- 6 files changed, 117 insertions(+), 35 deletions(-) (limited to 'lib/rites/rite_ef/temporal_ef.ml') diff --git a/lib/kernel/slug.ml b/lib/kernel/slug.ml index 5139c99..25281f2 100644 --- a/lib/kernel/slug.ml +++ b/lib/kernel/slug.ml @@ -1,6 +1,10 @@ (* Stable celebration identifiers. Also the lectionary key: EF slugs are adopted - verbatim from lectio so the Plan 3 lectionary bootstrap needs no mapping - table (spec §4.4). *) + verbatim from lectio so the Plan 4 lectionary bootstrap needs no mapping + table (spec §4.4). CORRECTED (final fix wave, item 7): this comment said + "Plan 3" -- the SANCTORAL bootstrap (data/ef/sanctoral.sexp) is Plan 3 and + shipped in this branch; the LECTIONARY bootstrap (reading citations, + Liturgical_day.t's own [citations] field) is a separate, later Plan 4, + per that field's own doc comment ("always empty until Plan 4"). *) type t = string let valid_char c = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c = '-' diff --git a/lib/kernel/vocab.ml b/lib/kernel/vocab.ml index 78729bb..bcb48b8 100644 --- a/lib/kernel/vocab.ml +++ b/lib/kernel/vocab.ml @@ -7,13 +7,32 @@ parametric types natively. *) type ('s, 'r) t = { seasons : 's list; - (** canonical liturgical-year order; Validate's contiguity check reads this *) + (** canonical liturgical-year order. CORRECTED (final fix wave, item + 7): this used to say "Validate's contiguity check reads this" -- + false since validate.ml's own "seasons" check switched to + {!Colitur_kernel.Rite.t}.season_runs in this branch (Plan 2 + carried item 1: EF has each season in one run, but the modern + form's Ordinary Time does not, so the expected run sequence had + to become rite-supplied rather than derived from this field). + For EF specifically {!Rite_ef.rite_ef.ml} sets season_runs to + this very list, so the two happen to agree there, but Validate + itself no longer reads [seasons] to build its expectation. *) season_to_string : 's -> string; season_of_string : string -> 's option; ranks : 'r list; (** documentation order, highest first. Plan 2 uses it only for the - closure check -- it is not a precedence relation until Plan 3 - defines one. *) + closure check. CORRECTED (final fix wave, item 7): this used to + say "it is not a precedence relation until Plan 3 defines one" -- + Plan 3 did define one (RG 111's dignity ordering, Rite_ef. + Precedence_ef.dignity/compare_dignity), but as its OWN small, + separately-hardcoded function, not one derived from this field: + [admit] needs Vocab_ef.rank's dignity as plain data (RG 8's four + classes), and reusing this field's own [int list] position would + couple that meaning to documentation order the way {!band} is + explicitly NOT allowed to (precedence_ef.ml's own file comment). + This field therefore still carries no precedence relation of its + own; a rite that wanted one derived from it would have to build + it itself. *) rank_to_string : 'r -> string; rank_of_string : string -> 'r option; } diff --git a/lib/kernel/vocab.mli b/lib/kernel/vocab.mli index 78729bb..bcb48b8 100644 --- a/lib/kernel/vocab.mli +++ b/lib/kernel/vocab.mli @@ -7,13 +7,32 @@ parametric types natively. *) type ('s, 'r) t = { seasons : 's list; - (** canonical liturgical-year order; Validate's contiguity check reads this *) + (** canonical liturgical-year order. CORRECTED (final fix wave, item + 7): this used to say "Validate's contiguity check reads this" -- + false since validate.ml's own "seasons" check switched to + {!Colitur_kernel.Rite.t}.season_runs in this branch (Plan 2 + carried item 1: EF has each season in one run, but the modern + form's Ordinary Time does not, so the expected run sequence had + to become rite-supplied rather than derived from this field). + For EF specifically {!Rite_ef.rite_ef.ml} sets season_runs to + this very list, so the two happen to agree there, but Validate + itself no longer reads [seasons] to build its expectation. *) season_to_string : 's -> string; season_of_string : string -> 's option; ranks : 'r list; (** documentation order, highest first. Plan 2 uses it only for the - closure check -- it is not a precedence relation until Plan 3 - defines one. *) + closure check. CORRECTED (final fix wave, item 7): this used to + say "it is not a precedence relation until Plan 3 defines one" -- + Plan 3 did define one (RG 111's dignity ordering, Rite_ef. + Precedence_ef.dignity/compare_dignity), but as its OWN small, + separately-hardcoded function, not one derived from this field: + [admit] needs Vocab_ef.rank's dignity as plain data (RG 8's four + classes), and reusing this field's own [int list] position would + couple that meaning to documentation order the way {!band} is + explicitly NOT allowed to (precedence_ef.ml's own file comment). + This field therefore still carries no precedence relation of its + own; a rite that wanted one derived from it would have to build + it itself. *) rank_to_string : 'r -> string; rank_of_string : string -> 'r option; } diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 9997bb2..fcd2116 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -429,11 +429,13 @@ let privilege_of (c : Vocab_ef.rank Precedence.candidate) : Precedence.privilege else if is_temporal && List.exists (fun p -> String.starts_with ~prefix:p slug) alp_feria_prefixes then Precedence.Privileged (* (f) RG 109(f) (§4): "of the Major Rogations, in Mass" -- the - Major Litanies (25 April, RG 80) are not yet computed anywhere in this - codebase (temporal_ef.ml's own comment on [temporal]'s Rogation branch: - "The Major Litanies... are a fixed date and are not yet computed; they - arrive with Plan 3's sanctoral"), so no candidate this engine can - currently construct represents one. There is no existing slug + Major Litanies (25 April, RG 80) are STILL not computed anywhere in + this codebase (temporal_ef.ml's own comment on [temporal]'s Rogation + branch, CORRECTED final fix wave item 7: they did not, in fact, + "arrive with Plan 3's sanctoral" -- Plan 3 shipped without them, + register §6 tracks this as an open item with no plan yet committed to + build it), so no candidate this engine can currently construct + represents one. There is no existing slug convention to anchor a check to, and guessing one risks silently misclassifying whatever a future task does name it -- a wrong citation is worse than a missing one, so this is left unimplemented and flagged diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index c3db15c..7782876 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -90,7 +90,10 @@ let named d = if m = 12 && dd = 25 then Some (Christmastide, "ef-nativity", Colour.White, Class1) else if m = 12 && dd = 24 then (* RG 91 entry 5: the Vigil of the Nativity is I class. lectio has no slug - for it, so this key has no lectionary entry until Plan 3 fills it. *) + for it, so this key has no lectionary entry until Plan 4 fills it + (CORRECTED, final fix wave, item 7 -- this is the lectionary/reading- + citations bootstrap, Plan 4, not the sanctoral one, Plan 3, which + already shipped in this branch). *) Some (Advent, "ef-nativity-vigil", Colour.Violet, Class1) else if m = 12 && (dd = 29 || dd = 30 || dd = 31) then (* Days within the Octave of the Nativity; 26-28 Dec are Stephen, John and @@ -111,9 +114,15 @@ let named d = else if same d (off 7) then Some (Paschaltide, "ef-low-sunday", Colour.White, Class1) (* RG 91 entry 6 *) else if same d (off 38) then - (* RG 91 entry 21: II-class vigil. It is also Rogation Wednesday; with no - precedence framework until Plan 3, temporal emits the higher-ranked - vigil and the Rogation commemoration waits for RG 108-111. *) + (* RG 91 entry 21: II-class vigil. It is also Rogation Wednesday; [named] + emits the higher-ranked vigil (entry 21 outranks any Rogation-day + ferial rank). CORRECTED (final fix wave, item 7): this comment + previously said the Rogation commemoration itself "waits for RG + 108-111" -- the precedence framework and RG 108-111 both exist now + (this branch), but no candidate for the Rogation Wednesday's own + observance is constructed here or anywhere else, so there is nothing + for RG 108-111 to admit; see the fuller comment on the Rogation + branch further down in [temporal] for the current, still-real gap. *) Some (Paschaltide, "ef-ascension-vigil", Colour.White, Class2) else if same d (off 39) then Some (Paschaltide, "ef-ascension", Colour.White, Class1) else if same d (off 48) then Some (Paschaltide, "ef-pentecost-vigil", Colour.Red, Class1) (* RG 91 entry 9 *) @@ -183,8 +192,10 @@ let week d = this to "ef-christmas-0-", indistinguishable from the stretch above in lectio's own data. colitur cannot preserve a distinction lectio doesn't make, so this becomes "ef-christmas-1-" -- a - colitur-only key and a lectionary gap for the Plan 3 bootstrap to fill, - exactly like the Nativity vigil and octave-day keys above. + colitur-only key and a lectionary gap for the Plan 4 bootstrap to fill + (CORRECTED, final fix wave, item 7: the lectionary bootstrap is Plan 4, + not Plan 3 -- see Slug.ml's own corrected comment), exactly like the + Nativity vigil and octave-day keys above. - 7-13 Jan, split in two by the *actual* first-Sunday-after-Epiphany origin ([week_origin Time_after_epiphany], which by construction always falls somewhere in this window -- see that function's own comment): @@ -303,8 +314,10 @@ let third_sunday_of_september y = The September and Advent sets match lectio's own Ember slugs. The Lent and Whitsun (Pentecost) sets do not -- lectio has no Ember slug for either, so "ef-lent-ember-*" and "ef-pentecost-ember-*" are colitur-only keys and a - lectionary gap for the Plan 3 bootstrap to fill (spec §4.4), the same - status as the Nativity vigil and the Rogation days below. *) + lectionary gap for the Plan 4 bootstrap to fill (spec §4.4; CORRECTED, + final fix wave, item 7 -- Plan 4, not Plan 3, is the lectionary + bootstrap), the same status as the Nativity vigil and the Rogation days + below. *) let ember d = let y = Date.year d in let easter = Computus.gregorian_easter y in @@ -327,12 +340,23 @@ let ember d = (* RG 91 entry 7: Ash Wednesday (named above) and Monday-Wednesday of Holy Week are I-class ferias -- the primary text reads "feria IV cinerum et II, - III et IV Hebdomadae sanctae", i.e. explicitly stops at Wednesday. Thursday - to Saturday of Holy Week are the Sacred Triduum, RG 91 entry 2 -- ranked - even above entry 7, not a mere feria -- but their own named offices are a - Plan 3 sanctoral addition; until then this gives them the same I-class rank - via the generic ferial path. RG 91 entry 10: the weekdays within the - privileged Octaves of Easter and Pentecost are I class too. *) + III et IV Hebdomadae sanctae", i.e. explicitly stops at Wednesday + (PRIMARY-SOURCE-VERIFIED, final fix wave: confirmed word for word + against the scan). Thursday to Saturday of Holy Week are the Sacred + Triduum, RG 91 entry 2 -- ranked even above entry 7, not a mere feria -- + but the Sacred Triduum has NO PROPER OFFICE of its own in this codebase + (CORRECTED, final fix wave, item 7: this comment previously said their + "own named offices are a Plan 3 sanctoral addition"; WRONG on two + counts -- Plan 3 shipped, in this branch, without adding them, AND a + proper office for I-class FERIAS was never a sanctoral matter to begin + with, RG 21's own definition of "feria" excludes Sundays/feasts, not the + other way round). `Temporal_ef.temporal 2026-04-02/03/04` (Holy + Thursday/Good Friday/Holy Saturday) still resolve today to the ordinary + Passiontide ferial fallback's own generic slugs, + "ef-passiontide-2-{thursday,friday,saturday}" -- register §6 records + this as its own open item now. This gives them the same I-class rank + via the generic ferial path regardless. RG 91 entry 10: the weekdays + within the privileged Octaves of Easter and Pentecost are I class too. *) let privileged_feria d = let easter = Computus.gregorian_easter (Date.year d) in let n = days_between easter d in @@ -392,16 +416,27 @@ let temporal d = | None -> ( (* Rogations (the Minor Litanies only -- RG 87, Monday and Tuesday before Ascension). The Major Litanies (25 April, RG 80) are a fixed - date and are not yet computed; they arrive with Plan 3's sanctoral - (register §6). The Wednesday here is the Ascension vigil (see Task - 11). RG 88: "de Litaniis minoribus nihil fit in Officio" -- the - Office (hence the day's rank) is unchanged by the Rogation; only the - Mass is proper. No RG 91 table entry elevates these days, so they + date and are STILL not computed (CORRECTED, final fix wave, item 7: + this comment previously said "they arrive with Plan 3's sanctoral" + -- Plan 3 shipped, in this branch, without them; register §6 tracks + this as a plain open item, with no plan committed to build it yet). + The Wednesday here is the Ascension vigil (see Task 11), which + happens to also fall on Rogation Wednesday -- the vigil (higher + RG 91 entry) is what [named] emits for that date; the Rogation + Wednesday's own commemoration is not separately constructed (a real + gap, not a forward dependency: the precedence framework and RG + 108-111 both exist now, but nothing wires a Rogation-Wednesday + candidate into the contest for this specific date the way Monday + and Tuesday get one below). RG 88: "de Litaniis minoribus nihil fit + in Officio" -- the Office (hence the day's rank) is unchanged by + the Rogation; only the Mass is proper. No RG 91 table entry + elevates these days, so they take the ordinary ferial rank of their season via [ferial_rank] rather than a fixed class. lectio has no Rogation slug at all, so "ef-rogation-monday"/"-tuesday" are colitur-only keys and a - lectionary gap for Plan 3, like the Ember and Nativity-vigil keys - above. *) + lectionary gap for Plan 4 (CORRECTED, final fix wave, item 7 -- + the lectionary bootstrap is Plan 4, not Plan 3), like the Ember + and Nativity-vigil keys above. *) let rogation = days_between easter d in if rogation = 36 || rogation = 37 then build ~season:s diff --git a/lib/rites/rite_ef/vocab_ef.ml b/lib/rites/rite_ef/vocab_ef.ml index 13e7309..e5726e8 100644 --- a/lib/rites/rite_ef/vocab_ef.ml +++ b/lib/rites/rite_ef/vocab_ef.ml @@ -45,7 +45,10 @@ let season_of_string = function (* Deliberately NOT season_to_string: slugs are lectionary keys adopted verbatim from lectio, which names these two seasons differently. Changing these words - would silently break the Plan 3 lectionary bootstrap. *) + would silently break the Plan 4 lectionary bootstrap (CORRECTED, final fix + wave, item 7 -- see Slug.ml's own corrected comment for the Plan 3/4 + distinction: the sanctoral bootstrap these slugs already serve is Plan 3 + and shipped; the lectionary/reading-citations bootstrap is Plan 4). *) let season_slug_word = function | Christmastide -> "christmas" | Paschaltide -> "easter" -- cgit v1.3