From 6319c8d5c012db40206b463dc912a1edf7aeec5d Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 08:15:48 +0200 Subject: precedence(ef): RG91 e14 "primum mobilia" + RG112(a) inseparable exclusion Two related fixes, both needed for Holy Family to actually observe correctly on the seven years it coincides with the fixed Commemoration of the Baptism of the Lord (13 January): 2008, 2013, 2019, 2030, 2036, 2041, 2047. band's entry 14 ("Festa Domini II classis") gated on sanctoral origin only, so a movable Class2 Lord feast had no row in the table at all. RG 91 entry 14's own text says otherwise: "primum mobilia, deinde fixa" -- movable first, then fixed. entry_14_movable_band gives Holy Family (the one movable candidate this codebase builds with subject Lord) a value strictly better than the fixed half's own literal 14, negative so it can never collide with a real table position -- without it, the two would tie at 14 and fall to Precedence.resolve's own alphabetical tie-break, which picks the WRONG side ("commemoration-of-the-baptism-of-the-lord" sorts before "ef-time-after-epiphany-sunday-1"). disposition gets a new branch for RG 112(a) (Caput XVI, "De Commemorationibus"): the Office, Mass or commemoration of a mystery of one Divine Person excludes a commemoration of another mystery of the SAME Divine Person. Winner and loser both subject Lord -> Omit, not Commemorate -- without it, Holy Family observing correctly still left the Baptism admitted as an ordinary Class2 commemoration under RG111(b), which the primary text does not allow. Confirmed by a MORE SPECIFIC primary source than RG 112(a) alone: the Holy Family Mass propers' own note, found on both photographic scans, word for word, immediately after the Postcommunio -- "Si festum S. Familiae occurrerit die 13 ianuarii, Missa dicitur de festo S. Familiae, sine commemoratione Baptismatis D.N.I.C., et sine commemoratione dominicae." The electronic transcription carries neither this rubric nor RG 112(a)'s own worked example -- another instance of its documented defect, not merely absent from one check. Still expected red: the lectio differential (2005-2050) now shows a new, real divergence on those seven 13-January-Sunday dates -- lectio has no Holy Family at all, so it still shows the fixed Baptism observed there, unlike colitur now. The oracle's own M-series entries also still need widening for the observed-identity axis added two commits ago. Both are the next commits. --- lib/rites/rite_ef/precedence_ef.ml | 108 +++++++++++++++++++++++++++-- lib/rites/rite_ef/precedence_ef.mli | 17 +++++ test/test_precedence_ef.ml | 131 +++++++++++++++++++++++++++++++++++- 3 files changed, 248 insertions(+), 8 deletions(-) diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 750da8b..87cdeeb 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -48,6 +48,34 @@ let universal_layer = "ef-universal" let indult_prefix = "indult:" let unclassified = max_int +(* Not an RG citation -- an ordering device for RG 91 entry 14's own text + (scan-verified, docs/research/rules-register.md §4): "14. Festa Domini II + classis, PRIMUM MOBILIA, DEINDE FIXA" -- feasts of the Lord, II class, + MOVABLE FIRST, then fixed. Both halves are the SAME numbered table row, + but the primary text states a real priority between them, not merely two + disjoint categories -- [band]'s own entry-14 branches below must not + literally TIE at the shared integer 14, because a tie would fall to + {!Precedence.resolve}'s own kernel-level fallback ([compare_by], + Slug.compare), which is deliberately NOT rubric-authoritative (the exact + anti-pattern RG 113's own fix already corrected once for commemoration + ordering, this file's own header) and would in fact pick the WRONG side + here: "commemoration-of-the-baptism-of-the-lord" sorts before + "ef-time-after-epiphany-sunday-1" alphabetically, backwards from "primum + mobilia". Negative, so it can never collide with any real table position + (1..28) or [unclassified] (positive [max_int]) by construction -- not a + citation to some entry "13.5" that does not exist in the primary text, + just an ordering fact this integer has to carry because RG 91's own two + half-rows must compare unequal here. The one real witness today: Holy + Family (RG 17(b), temporal_ef.ml's own [holy_family_sunday]) against the + fixed Commemoration of the Baptism of the Lord (13 January) -- the only + date they can ever coincide, since Holy Family only ever falls 7-13 + January and no OTHER fixed Class2-Lord sanctoral entry shares that + window (register §6.0's own subject audit: Transfiguration 6 Aug, + Exaltation of the Cross 14 Sep, Dedication of the Archbasilica 9 Nov, + the Purification 2 Feb, the Baptism 13 Jan -- the Baptism alone falls in + Holy Family's own window). *) +let entry_14_movable_band = -14 + let is_indult layer = String.starts_with ~prefix:indult_prefix layer let is_universal layer = String.equal layer universal_layer @@ -223,13 +251,28 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc not the universal layer (11), and marked as an indult overlay (12's "not indult" test having just failed). *) else if (not is_temporal) && (not is_vigil) && rank = Class1 then 13 - (* 14: Feasts of the Lord, II class -- RG 91 entry 14, deliberately - UNQUALIFIED (contrast entry 16, which explicitly says "not - of the Lord"; RG 37c (§4, "Sundays") speaks of "II-class feasts of - the Lord" replacing an occurring II-class Sunday with no universal - qualifier either). No layer test here, unlike 11/12/13 and 16/19/20: - the register does not split this entry into universal/proper/indult, - so a proper or indult feast of the Lord still bands 14, not 19/20. *) + (* 14, MOVABLE half -- RG 91 entry 14's own "primum mobilia, deinde + fixa" ({!entry_14_movable_band}'s own comment has the full citation + and reasoning). Checked before the FIXED half immediately below, + matching the primary text's own order, though [is_temporal] already + makes the two branches structurally disjoint on any one candidate + regardless of which is checked first -- the ordering here is for a + reader following RG 91's own prose, not for correctness. The only + witness this codebase currently constructs is Holy Family + (temporal_ef.ml's own [holy_family_sunday] branch, the one place + [subject] is ever [Lord] on a temporal-origin candidate); a + hypothetical movable Holy-Name-of-Jesus office (RG 17(a), still + unbuilt as its own named day, register §6) would reach this same + branch too, once built, since nothing here is keyed to Holy Family's + own slug. *) + else if is_temporal && rank = Class2 && subject = Subject.Lord then entry_14_movable_band + (* 14, FIXED half -- RG 91 entry 14, deliberately UNQUALIFIED (contrast + entry 16, which explicitly says "not of the Lord"; RG 37c (§4, + "Sundays") speaks of "II-class feasts of the Lord" replacing an + occurring II-class Sunday with no universal qualifier either). No + layer test here, unlike 11/12/13 and 16/19/20: the register does not + split this entry into universal/proper/indult, so a proper or + indult feast of the Lord still bands 14, not 19/20. *) else if (not is_temporal) && (not is_vigil) && rank = Class2 && subject = Subject.Lord then 14 (* 15: Sundays, II class (every Sunday not already named at 6). *) else if is_temporal && rank = Class2 && is_sunday then entry_15_band @@ -732,6 +775,57 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) the RG26 branch above gives its own [not is_vigil] guard, rather than relying on that absence silently. *) Precedence.Omit + else if winner.Precedence.cel.Celebration.subject = Subject.Lord && cel.Celebration.subject = Subject.Lord + then + (* RG 112(a) (docs/research/rules-register.md §4/§6.0, primary text, + verified against BOTH photographic scans -- missale-romanum-1962.pdf + and "Missale Romanum 1962_text.pdf" -- the electronic transcription, + 1962-06-23,_SS_Ioannes_XXIII,_Missale_Romanum,_LT.pdf, carries + NEITHER this paragraph's own worked example NOR its own more + specific Mass-proper rubric below, exactly the transcription's + documented defect, this project's own methodology note): "112. Ad + commemorationes et orationes quod attinet, haec insuper serventur: + a) Officium, Missa aut commemoratio de aliquo festo vel mysterio + UNIUS DIVINAE PERSONAE excludit commemorationem aut orationem de + alio festo vel mysterio EIUSDEM DIVINAE PERSONAE" -- the Office, + Mass, or commemoration of some feast or MYSTERY of ONE Divine Person + excludes a commemoration or oration of ANOTHER feast or mystery of + the SAME Divine Person. + + {!Subject.t} has no finer split within [Lord] than "a mystery + touching the Divine Person of the Son" -- every real [subject = + Lord] entry this codebase's own data carries concerns Christ + specifically (register §6.0's subject audit: the Precious Blood, the + Transfiguration, the Exaltation of the Cross, the Dedication of the + Archbasilica, the Purification, the Baptism, and now Holy Family), + so reading "same Divine Person" as simply "both [Lord]" is safe at + today's data's own granularity -- a rite that ever needed to + distinguish, say, a Trinity-specific mystery from a Son-specific one + by Person would need a finer [Subject.t] first, not a special case + added here. + + The one live witness -- confirmed DIRECTLY, not only by this general + paragraph: the Holy Family Mass propers' own note, immediately + following the Postcommunion, word for word on BOTH photographic + scans: "Si festum S. Familiae occurrerit die 13 ianuarii, Missa + dicitur de festo S. Familiae, SINE COMMEMORATIONE BAPTISMATIS + D.N.I.C., et sine commemoratione dominicae" -- if the feast of the + Holy Family occurs on 13 January, the Mass is said of the Holy + Family, WITHOUT commemoration of the Baptism of Our Lord Jesus + Christ, and without commemoration of the Sunday (RG 17(b)'s own + general rule, restated for this specific date). Independently + confirmed against missalemeum (register §6.0): title "The Holy + Family: Jesus, Mary & Joseph", commemorations [], the Baptism + listed only under "displaced", never commemorated. + + Checked after RG 16(a) above (so a genuine RG 16(a) Sunday- + suppression is never re-explained under this citation instead) and + before the final ordinary-commemoration catch-all below, since + without it a [Feast]-status loser of this shape would otherwise + reach RG 95's ordinary "commemorated or omitted" branch and be + admitted as an ordinary Class2 commemoration under RG 111(b) -- + confirmed wrong against both primary sources above. *) + Precedence.Omit else (* RG 95's other branch: "aut commemorantur aut penitus omittuntur" -- commemorated or wholly omitted. Reached by every SANCTORAL loser diff --git a/lib/rites/rite_ef/precedence_ef.mli b/lib/rites/rite_ef/precedence_ef.mli index b283a17..86a0ead 100644 --- a/lib/rites/rite_ef/precedence_ef.mli +++ b/lib/rites/rite_ef/precedence_ef.mli @@ -62,6 +62,15 @@ val ember_prefixes : string list new rule, not a silently wrong one. *) val unclassified : int +(** RG 91 entry 14's own text (§4): "Festa Domini II classis, primum + mobilia, deinde fixa" -- feasts of the Lord, II class, MOVABLE first, + then fixed. {!band}'s value for the movable half (Holy Family, RG + 17(b)) -- strictly better than the fixed half's own literal [14], + negative so it can never collide with a real table position or + {!unclassified}. See precedence_ef.ml's own comment for the full + citation and why the two halves must not literally tie. *) +val entry_14_movable_band : int + (** [band ctx c]: RG 91's Table of Precedence. Returns the table's own entry number -- I class 1-13, II class 14-21, III class 22-26, IV class 27-28; lower wins (see {!Precedence.rules.band}) -- EXCEPT where the table's own @@ -134,6 +143,14 @@ val sunday_marker : string weekday, never a saint's day, and RG 111(c)/(d) admit an "ordinary" commemoration of a losing SAINT freely, with no such class-keyed gate); + - a loser whose [subject] is [Lord] AND whose WINNER's [subject] is also + [Lord] is [Omit] (RG 112(a) -- a feast or mystery of one Divine Person + excludes a commemoration of another feast or mystery of the SAME + Divine Person; live witness: Holy Family, RG 17(b), excluding the + fixed Commemoration of the Baptism of the Lord on the years 13 + January is itself Holy Family's own date -- see the .ml's own comment + for the primary text, including the Holy Family Mass propers' own + more specific 13-January rubric, on both photographic scans); - everything else -- including an impeded I-class Sunday, and a SANCTORAL loser of any rank below I class -- is [Commemorate], carrying its real RG 109 privilege (see {!admit} below); RG 109(a) diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml index 2935e40..01a54c0 100644 --- a/test/test_precedence_ef.ml +++ b/test/test_precedence_ef.ml @@ -138,6 +138,30 @@ let cases = cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:"diocese-warsaw" "ef-local-feast-of-the-lord", 14 ); + (* Entry 14, MOVABLE half -- RG 91 entry 14's own text (§4, scan-verified): + "Festa Domini II classis, PRIMUM MOBILIA, DEINDE FIXA" -- movable + FIRST, then fixed. Holy Family (RG 17(b), temporal_ef.ml's own + [holy_family_sunday]) is the one movable Class2 Lord feast this + codebase builds -- sourced from [Temporal_ef.temporal]'s own real + output ([of_temporal], the same review-finding-3 discipline entry + 18's own Ember row above uses), not a hand-typed slug, so a rename + or a dropped [~subject:Lord] on temporal_ef.ml's own side fails this + row too, not only test_temporal_ef.ml's. Must be a value STRICTLY + BETTER than the fixed half's own literal 14 immediately above (not + merely different) -- the two are structurally disjoint by [is_temporal] + alone so they can never literally collide on any one candidate, but + {!compare_by}'s own kernel-level tie-break (Slug.compare, deliberately + NOT rubric-authoritative -- the exact anti-pattern RG 113's own fix + already corrected once, this file's own header) is what would decide + between them if they returned the SAME band on the one date they can + coincide (13 January, when it is itself Holy Family's date) -- + "commemoration-of-the-baptism-of-the-lord" sorts before + "ef-time-after-epiphany-sunday-1" alphabetically, backwards from RG + 91's own stated order. See [PE.entry_14_movable_band]'s own comment + in precedence_ef.ml for why the value is negative rather than some + other distinct integer. *) + ( "14 Feast of the Lord, II class, MOVABLE half (Holy Family, RG 17(b))", + T.holy_family_sunday 2026, of_temporal (T.holy_family_sunday 2026), PE.entry_14_movable_band ); (* Entry 15 -- RG 91 entry 15 (§4): an ordinary Sunday not named at entry 6 -- Septuagesima is II class (RG 11-12 names only Advent/Lent/ Passiontide/Easter/Low/Pentecost as I class). *) @@ -662,7 +686,70 @@ let disposition_cases = cand ~origin:P.Sanctoral ~rank:V.Class3 ~subject:Sub.Lord ~layer:PE.universal_layer "ef-synthetic-class3-lord-feast", an_ordinary_sunday, - "Commemorate(Privileged)" ) + "Commemorate(Privileged)" ); + (* RG 112(a) (docs/research/rules-register.md §4/§6.0, primary text, + verified against BOTH photographic scans): "112. Ad commemorationes + et orationes quod attinet, haec insuper serventur: a) Officium, Missa + aut commemoratio de aliquo festo vel mysterio unius Divinae Personae + excludit commemorationem aut orationem de alio festo vel mysterio + eiusdem Divinae personae" -- the Office, Mass or commemoration of + some feast or MYSTERY of ONE Divine Person excludes a commemoration + or oration of ANOTHER feast or mystery of the SAME Divine Person. + Real witness, the ONE date these two candidates can ever coincide: + Holy Family (RG 17(b)) as winner, the Commemoration of the Baptism + of the Lord (13 January, data/ef/sanctoral.sexp: Class2, subject + Lord, status Feast) as loser, on the seven years 13 January is + itself Holy Family's own date (2008, 2013, 2019, 2030, 2036, 2041, + 2047). Directly confirmed by the Missal's own MORE SPECIFIC rubric, + found in the Holy Family Mass propers themselves (both photographic + scans, word for word): "Si festum S. Familiae occurrerit die 13 + ianuarii, Missa dicitur de festo S. Familiae, SINE COMMEMORATIONE + BAPTISMATIS D.N.I.C., et sine commemoratione dominicae" -- if the + feast of the Holy Family occurs on 13 January, the Mass is said of + the Holy Family, WITHOUT commemoration of the Baptism, and without + commemoration of the Sunday (RG 17(b)'s own general rule, restated). + The electronic transcription (1962-06-23,_SS_Ioannes_XXIII,_Missale + _Romanum,_LT.pdf) carries NEITHER sentence at all -- exactly the + transcription's own documented defect (this project's methodology + note), not merely absent from this one check. *) + ( "RG112(a): a feast/mystery of the Lord excludes a commemoration of \ + ANOTHER feast/mystery of the Lord -- Omit, not Commemorate (Holy \ + Family vs the Baptism of the Lord, 13 January)", + of_temporal (T.holy_family_sunday 2019), + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "commemoration-of-the-baptism-of-the-lord", + "Omit" ); + (* Conjunct 1/2, LOSER subject: an ORDINARY saint losing to the SAME + Lord-subject winner must still reach the ordinary commemorate flow, + not RG 112(a)'s exclusion -- RG 112(a) excludes another mystery of + the SAME Divine Person, not every commemoration a Lord feast happens + to outrank. Dropping the loser's own [subject = Lord] test would + wrongly Omit this ordinary saint's commemoration too. *) + ( "RG112(a) does NOT fire when only the WINNER is a mystery of the Lord \ + -- an ordinary saint stays Commemorate(Ordinary)", + of_temporal (T.holy_family_sunday 2019), + cand ~origin:P.Sanctoral ~rank:V.Class3 ~subject:Sub.Saint ~layer:PE.universal_layer + "ef-synthetic-ordinary-saint", + "Commemorate(Ordinary)" ); + (* Conjunct 2/2, WINNER subject: a Class2 SAINT'S feast (not of the + Lord) impeding an ordinary temporal candidate must not exclude an + otherwise-due Lord commemoration either -- RG 112(a) is keyed on the + WINNER's own subject too, not just the loser's. Real slug/rank from + data/ef/sanctoral.sexp (25 January, Class3, "Conversion of St + Paul") standing in for a same-rank saint winner; only the [subject] + distinction is under test here, not a real-date collision (Holy + Family's own subject is what makes this branch reachable at all -- + an ordinary sanctoral saint never wins against Holy Family in + practice, {!band}'s [entry_14_movable_band] sees to that, so this + row is SYNTHETIC in the same sense the RG16(a) block's own Class3 + row above is: defensive, not a live witness). *) + ( "SYNTHETIC: RG112(a) does NOT fire when only the LOSER is a mystery \ + of the Lord -- stays Commemorate(Ordinary)", + cand ~origin:P.Sanctoral ~rank:V.Class3 ~subject:Sub.Saint ~layer:PE.universal_layer + "conversion-of-st-paul", + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "commemoration-of-the-baptism-of-the-lord", + "Commemorate(Ordinary)" ) ] (* Task 9: [privilege_of]'s RG 109 categories (§4, "Commemorations"), @@ -1218,6 +1305,44 @@ let test_rg16a_lord_feast_suppresses_sunday_end_to_end () = (List.map (fun (c, reason) -> (S.to_string c.P.cel.Cel.slug, reason)) resolution.P.omitted |> List.sort compare) +(* End-to-end proof of BOTH fixes this task adds together, {!P.resolve} in + full (band picks the winner, disposition and admit decide the loser's + fate), not just the two isolated functions [cases]/[disposition_cases] + already prove: 13 January 2019, the latest possible date Holy Family can + fall on (Epiphany itself a Sunday) and one of the seven years it + coincides with the fixed Commemoration of the Baptism of the Lord. + {!band}'s [entry_14_movable_band] (RG 91 entry 14, "primum mobilia") must + pick Holy Family as [observed], not the fixed Baptism candidate (which, + absent that fix, would win outright via {!compare_by}'s own alphabetical + tie-break at the shared literal 14 -- backwards from the primary text); + {!PE.disposition}'s new RG 112(a) branch must then Omit the Baptism + entirely, not merely demote it to a commemoration -- confirmed against + BOTH the general rubric (RG 112(a)) and the Holy Family Mass propers' own + more specific 13-January note (this file's own citation on the + RG112(a) [disposition_cases] rows above), and independently against + missalemeum's own JSON for this exact date (register §6.0): title "The + Holy Family: Jesus, Mary & Joseph", commemorations [], the Baptism + listed only under "displaced". *) +let test_holy_family_excludes_baptism_end_to_end () = + let date = T.holy_family_sunday 2019 in + Alcotest.(check string) "sanity: this really is 13 January 2019" "2019-01-13" (D.to_iso8601 date); + let day_ctx = ctx date in + let holy_family = of_temporal date in + let baptism = + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "commemoration-of-the-baptism-of-the-lord" + in + let resolution = P.resolve real_rules day_ctx ~temporal:holy_family ~sanctoral:[ baptism ] in + Alcotest.(check string) "Holy Family is observed, not the fixed Baptism (RG91 e14: movable first)" + "ef-time-after-epiphany-sunday-1" (S.to_string resolution.P.observed.P.cel.Cel.slug); + Alcotest.(check string) "II class" "class-2" (V.rank_to_string resolution.P.observed.P.cel.Cel.rank); + Alcotest.(check int) + "no commemorations at all -- RG112(a)'s own exclusion, not merely dropped by an admission count" + 0 (List.length resolution.P.commemorations); + Alcotest.(check (list string)) "the Baptism is OMITTED, not merely uncommemorated" + [ "commemoration-of-the-baptism-of-the-lord" ] + (List.map (fun (c, _) -> S.to_string c.P.cel.Cel.slug) resolution.P.omitted) + (* Completes Task 7's carried fix (RG 91 entry 8, §4): on a real Sunday landing on 2 November, All Souls does not merely lose (that was Task 7's [band] fix, proved by [test_all_souls_yields_to_sunday] above) -- it must @@ -1465,6 +1590,10 @@ let suite = "RG16(a): a Feast of the Lord suppresses the Sunday's own commemoration entirely, \ end-to-end (2028-08-06)" `Quick test_rg16a_lord_feast_suppresses_sunday_end_to_end; + Alcotest.test_case + "RG112(a) + RG91 e14 \"primum mobilia\": Holy Family excludes the Baptism of the Lord \ + entirely, end-to-end (13 Jan 2019)" + `Quick test_holy_family_excludes_baptism_end_to_end; Alcotest.test_case "transfer_target: general RG96 search walks past more than one blocked day" `Quick test_transfer_target_general_multi_step_search; Alcotest.test_case "transfer_target: Annunciation exception starts at Monday after Low Sunday" -- cgit v1.3