diff options
Diffstat (limited to 'lib/rites/rite_ef/precedence_ef.ml')
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 215 |
1 files changed, 180 insertions, 35 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 0feb3e0..fd83383 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -244,15 +244,60 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc plan's scope -- see calendar.mli's own note that nothing in the EF ruleset currently emits it. *) -(* RG 33 (register line 383-384): a I- or II-class vigil falling on any - Sunday or a I-class feast is entirely omitted. Every Sunday slug this - rite's temporal cycle produces -- named (temporal_ef.ml's [named], e.g. - "ef-easter-sunday") or the generic "ef-<season>-sunday-<n>" fallback - ([sunday_slug]) -- contains this marker; nothing else [band] classifies - does. Not an RG citation itself -- see [universal_layer]'s note on this - file's own naming conventions -- exposed for the same reason as - {!vigil_suffix}: a future rename of temporal_ef's Sunday-slug format has - somewhere to be caught other than a silently-wrong RG 33 disposition. *) +(* RG 33 -- CORRECTED 2026-08-12 (Task 16, primary-source-verified against + docs/research/1962-06-23,_SS_Ioannes_XXIII,_Missale_Romanum,_LT.pdf, the + General Rubrics' own Chapter XI "De Vigiliis"). The register previously + transcribed this as "a I/II-class vigil is entirely omitted"; the + PRIMARY TEXT reads the other way round: + + "33. Vigilia II aut III classis penitus omittitur, si occurrat in + dominica quavis, aut in festo I classis, vel si festum cui + præmittitur in alium diem transferri aut ad commemorationem reduci + contingat." + + -- "A vigil of the II OR III class is entirely omitted, if it occurs on + ANY Sunday whatsoever, or on a feast of the I class, or if the feast it + precedes happens to be transferred to another day or reduced to a + commemoration." I-class vigils (Nativity, Pentecost, RG 30) are outside + this rule entirely -- RG 30's own text says they "festis quibuslibet + præferunt, et nullam admittunt commemorationem" (are preferred to ANY + feast whatsoever, and admit no commemoration at all), i.e. they can never + lose in the first place: {!band} entries 1/5/9 already rank Nativity Eve + and the Pentecost Vigil above every Sunday and every other I-class row + that could coincide with their fixed/Easter-relative dates (verified: no + date collision is even representable), so no I-class vigil can ever reach + this function as a [loser] -- the branch below never needs to test for + [Class1] and, before this fix, its stray inclusion of [Class1] here was + simply dead code, not a second bug (see the task report for the + argument). The bug was the OTHER half: [Class3] (the sole III-class + vigil, St Lawrence, RG 32) was MISSING from this branch, so it fell + through to the generic "commemorated or omitted" branch below instead of + RG 33's mandatory omission -- confirmed wrong for real data: 9 August + 2026 is a Sunday, and before this fix "vigil-of-st-lawrence" competed for + (and could in principle win) that Sunday's single commemoration slot, + when RG 33 says it must not even be a candidate. The oracle comparison + (missalemeum, Task 16) independently confirms: 9 Aug 2026 shows no trace + of the vigil surviving as a commemoration. + + The third omission trigger in RG 33's own text -- "or if the feast it + precedes is transferred to another day or reduced to a commemoration" -- + is NOT implemented: no II/III-class vigil's own feast (Ascension, + Assumption, John Baptist, Sts Peter & Paul, Lawrence) is ever + transferred or reduced to a commemoration anywhere in this codebase's + current data (all fixed I-class, none coincide with anything of equal or + higher rank within any year this project has sampled), so no witness + exists to build or test this clause against; flagged in the register + (§6) rather than guessed. *) +let is_omissible_vigil (rank : Vocab_ef.rank) = rank = Vocab_ef.Class2 || rank = Vocab_ef.Class3 + +(* Every Sunday slug this rite's temporal cycle produces -- named + (temporal_ef.ml's [named], e.g. "ef-easter-sunday") or the generic + "ef-<season>-sunday-<n>" fallback ([sunday_slug]) -- contains this + marker; nothing else [band] classifies does. Not an RG citation itself -- + see [universal_layer]'s note on this file's own naming conventions -- + exposed for the same reason as {!vigil_suffix}: a future rename of + temporal_ef's Sunday-slug format has somewhere to be caught other than a + silently-wrong RG 33 disposition. *) let sunday_marker = "-sunday" let contains_substring s ~needle = @@ -372,6 +417,7 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) ~(loser : Vocab_ef.rank Precedence.candidate) : Precedence.disposition = let open Vocab_ef in let cel = loser.Precedence.cel in + let is_temporal = loser.Precedence.origin = Precedence.Temporal in if cel.Celebration.status = Celebration.Commemoration_only then (* Always -- checked before RG 33's omission and RG 95's transfer so neither can override it: a Commemoration_only entry can never win @@ -384,13 +430,17 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) it would to any other candidate. *) Precedence.Commemorate (privilege_of loser) else if - (cel.Celebration.rank = Class1 || cel.Celebration.rank = Class2) + is_omissible_vigil cel.Celebration.rank && is_vigil (Slug.to_string cel.Celebration.slug) && impedes_vigil winner then - (* RG 33. Checked before the generic Class1 -> Transfer rule below, or a - I-class vigil (Nativity, Pentecost) impeded on its own Sunday/ - I-class-feast terms would wrongly transfer instead of vanishing. *) + (* RG 33, corrected (see {!is_omissible_vigil}'s own comment): II- or + III-class vigils only -- a real I-class vigil can never reach this + function as a loser at all (see that comment), so this branch would + never have fired for [Class1] even before the fix; what changed is + that [Class3] (St Lawrence) now correctly reaches RG 33's omission + instead of falling through to the generic "commemorated or omitted" + branch below. *) Precedence.Omit else if cel.Celebration.rank = Class1 @@ -416,19 +466,93 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) Rite.transfer_target's job, not this function's -- disposition only says THAT it moves. *) Precedence.Transfer + else if + is_temporal + && (not (is_vigil (Slug.to_string cel.Celebration.slug))) + && (match privilege_of loser with Precedence.Ordinary -> true | Precedence.Privileged -> false) + 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. + + 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. *) + Precedence.Omit else (* RG 95's other branch: "aut commemorantur aut penitus omittuntur" -- - commemorated or wholly omitted. Reached by everything below I class, - AND by an impeded I-class Sunday (excluded from the [Transfer] branch - above): 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 [Privileged] via the same - [is_sunday_slug] marker, with no further code needed here. Which of - commemorate/omit survives is RG 108-111's admission count ([admit], - below), not this function's decision; this only opens the - commemoration, tagged with its real RG 109 privilege via - [privilege_of]. + commemorated or wholly omitted. Reached by every SANCTORAL loser + 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) + (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 + [Privileged] via the same [is_sunday_slug] marker, with no further + code needed here. Which of commemorate/omit survives is RG 108-111's + admission count ([admit], below), not this function's decision; this + only opens the commemoration, tagged with its real RG 109 privilege + via [privilege_of]. RG 94 (a fixed-day commemoration is not carried along with a transferred feast) needs no code here: [Precedence.resolve] calls @@ -512,19 +636,40 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) several are. *) (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ best ]) | Class2, true -> - (* RG 111: "II-class Sundays: one (dropped if a privileged one is - due)." Read as: the day's one slot goes to a privileged - commemoration whenever one is due, categorically -- not by - comparing its dignity against the ordinary contender's -- so an - ordinary commemoration that would otherwise win the slot on raw - dignity is still dropped once any privileged commemoration is also - due. This is the asymmetric clause the brief and task report flag - as deliberate, not present at "other II class" below; see the task - report for the reasoning and its residual uncertainty (the register - does not spell out the mechanism beyond this one sentence). *) + (* 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 + 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 + dignity is still dropped once any privileged one is also due (the + asymmetric clause the brief and task report flag as deliberate, not + present at "other II class" below); (ii) failing that, the slot is + reserved SPECIFICALLY for a [Class2] candidate -- "de festo II + classis" is a RANK restriction, not merely "whichever ordinary + candidate has the best dignity": a III- or IV-class ordinary loser + (a plain commemoration-only saint with no privilege of its own) has + NO standing for this slot at all and must be entirely omitted, even + when it is the only candidate present. + + Fix, Task 16 (primary-source-verified + missalemeum-confirmed): + previously this fell back to "the best of [sorted], whatever its + rank" once no privileged candidate was due, silently admitting a + III/IV-class ordinary saint that RG 111(b)'s own wording excludes. + Confirmed wrong for real data by the oracle comparison: e.g. 11 Jan + 2026 (Holy Family, a II-class Sunday) has St Hyginus (Class3, + commemoration-only) as its only competing candidate -- missalemeum + shows him "displaced" (omitted), never commemorated; the + pre-fix code admitted him regardless. *) (match List.filter is_privileged sorted with | best :: _ -> [ best ] - | [] -> ( match sorted with [] -> [] | best :: _ -> [ best ] )) + | [] -> ( + match List.filter (fun (c, _) -> c.Precedence.cel.Celebration.rank = Class2) sorted with + | [] -> [] + | best :: _ -> [ best ])) | Class2, false -> (* RG 111: "other II class: one" -- no privilege-override clause here, unlike the Sunday case immediately above, so the day's one slot |
