diff options
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 79 | ||||
| -rw-r--r-- | test/test_precedence_ef.ml | 137 |
2 files changed, 208 insertions, 8 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index db7e708..179fa13 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -244,13 +244,14 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc (* Task 8: what happens to the day's LOSING candidate (docs/research/ rules-register.md §4, "Occurrence" RG 92-95 and "Vigils" RG 33, plus RG - 94). [band] above decides who wins; this decides the loser's fate, which - turns on the LOSER's own rank and status (RG 95), except RG 33's vigil - omission, which also has to read the winner. Nothing here ever returns - [Precedence.Repose]: that disposition denotes RG 100-102's *repositio* - (perpetual impediment from a proper/diocesan calendar), out of this - plan's scope -- see calendar.mli's own note that nothing in the EF - ruleset currently emits it. *) + 94; also §6.0/Caput III "De Dominicis" RG 16(a), below). [band] above + decides who wins; this decides the loser's fate, which turns on the + LOSER's own rank and status (RG 95), except RG 33's vigil omission and RG + 16(a)'s Sunday-suppression, which also have to read the winner. Nothing + here ever returns [Precedence.Repose]: that disposition denotes RG + 100-102's *repositio* (perpetual impediment from a proper/diocesan + calendar), out of this plan's scope -- see calendar.mli's own note that + nothing in the EF ruleset currently emits it. *) (* 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 @@ -605,6 +606,70 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) register's own §4 "Vigils" entry states RG 32 only as "same pattern [as RG 31]", not verbatim -- now closed here). *) Precedence.Omit + else if + is_temporal + && cel.Celebration.rank = Class2 + && is_sunday_slug (Slug.to_string cel.Celebration.slug) + && (let wcel = winner.Precedence.cel in + (wcel.Celebration.rank = Class1 || wcel.Celebration.rank = Class2) + && wcel.Celebration.subject = Subject.Lord) + then + (* RG 16(a) (docs/research/rules-register.md §6.0, Caput III "De + Dominicis", primary text): "Dominica II classis, in occurrentia, + festis II classis praefertur. Attamen: a) festum Domini I aut II + classis, in dominica II classis occurrens, locum tenet ipsius + dominicae cum omnibus iuribus et privilegiis: de dominica, proinde, + NULLA FIT COMMEMORATIO" -- a Feast of the Lord, I or II class, + occurring on a II-class Sunday, takes the Sunday's own place with all + its rights and privileges: OF THE SUNDAY, THEREFORE, NO + COMMEMORATION IS MADE. {!band} entry 14 already ranks such a feast + above the Sunday (RG 91 entry 14 < entry 15), so the feast is + correctly [observed]; this is [disposition]'s own answer for what + becomes of the SUNDAY once it has lost -- [Omit], not the [Commemorate] + every other impeded II-class Sunday gets via RG 109(a)/RG 111(b) + below. This is the ONE place in this function that needs the WINNER's + [subject], not only the loser's own fields -- like RG 33's vigil + omission above, not a new kind of signature: [Precedence.rules.disposition] + already takes [~winner], this is simply its first other reader. + + Both winner-side conjuncts are load-bearing, proved by + [test_precedence_ef.ml]'s own paired rows (the brief's "one without + the other proves nothing"): + - [rank = Class1 || rank = Class2]: {!band} entries 11-13 admit ANY + I-class feast -- Lord or Saint alike, no subject test at all (entry + 14's own comment: unlike entry 16, entries 11-13 draw no such + line) -- ahead of a II-class Sunday. REACHABLE at Class1 on real + data: e.g. the Nativity of St John the Baptist (24 June, I class, + Saint) landing on a Time-after-Pentecost Sunday already wins the + day under {!band} alone; without this conjunct RG 16(a) would + wrongly fire for it too. (At Class2 this conjunct adds no further + cases beyond what the [subject = Lord] conjunct below already + requires -- entry 16's ordinary II-class feasts never outrank a + Sunday in {!band} in the first place, 16 > 15 -- so it is only + independently reachable at Class1.) + - [subject = Lord]: at Class2 this is what {!band} entry 14 already + requires of its own winners, so it adds no further restriction + there; at Class1 it is independently reachable and necessary, per + the St John Baptist example above -- dropping it would fire RG + 16(a) for any winning I-class feast at all, exactly the + over-wide branch the task brief warns against. + + The SUNDAY-side conjuncts: [rank = Class2] excludes a I-class Sunday + (Advent/Lent/Passiontide, Low Sunday, {!band} entry 6) -- RG 16(a)'s + own text says "Dominica II classis", not "any Sunday", and a I-class + Sunday can never actually reach here as a [loser] against a + Class1-or-2 Lord winner in the first place: entry 6's own band value + (6) is lower than every entry a Lord feast of Class1 or Class2 could + occupy (3, 11-14), so a I-class Sunday always wins outright -- this + is exactly why the rubric restricts itself to II class. [is_sunday_slug] + (the same marker RG 33's [impedes_vigil] and RG 109(a)'s + [privilege_of] already use, with no [is_temporal] guard there either + -- no sanctoral slug this codebase's data produces contains + {!sunday_marker}) is the "Dominica" half; [is_temporal] is kept + alongside it anyway, the same explicit-but-not-load-bearing defence + the RG26 branch above gives its own [not is_vigil] guard, rather than + relying on that absence silently. *) + 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/test/test_precedence_ef.ml b/test/test_precedence_ef.ml index a0e1db1..facf3eb 100644 --- a/test/test_precedence_ef.ml +++ b/test/test_precedence_ef.ml @@ -540,7 +540,100 @@ let disposition_cases = Omit under the refuted privilege_of=Ordinary gate", an_ordinary_sunday, cand ~rank:V.Class3 "ef-synthetic-ordinary-class3-feria", - "Commemorate(Ordinary)" ) + "Commemorate(Ordinary)" ); + (* RG 16(a) (register §6.0, Caput III "De Dominicis"): "festum Domini I + aut II classis, in dominica II classis occurrens, ... de dominica, + proinde, nulla fit commemoratio" -- a Feast of the Lord, I or II + class, impeding a II-class Sunday, leaves the Sunday with NO + commemoration at all, unlike every other impeded II-class Sunday + (contrast the very first row of this table, and [privilege_cases]'s + own (a): both plain [Class1]/other-sanctoral winners still admit the + Sunday, [Commemorate(Privileged)]). Two positive rows, I and II + class, both real slugs/ranks/subjects this project's own audit + confirmed against the calendarium (this task's own report): the + Transfiguration (6 Aug, II class, "IN TRANSFIGURATIONE D. N. I. C.") + and the Precious Blood (1 Jul, I class, "PRETIOSISSIMI SANGUINIS + D. N. I. C."). *) + ( "RG16(a): a Feast of the Lord, II class, impedes a II-class Sunday -> \ + Omit, no commemoration at all", + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "transfiguration-of-our-lord", + an_ordinary_sunday, + "Omit" ); + ( "RG16(a): a Feast of the Lord, I class, impedes a II-class Sunday -> \ + Omit, no commemoration at all", + cand ~origin:P.Sanctoral ~rank:V.Class1 ~subject:Sub.Lord ~layer:PE.universal_layer + "precious-blood-of-our-lord-jesus-christ", + an_ordinary_sunday, + "Omit" ); + (* Conjunct 1/4, WINNER subject: RG 16(a) names a feast "Domini", not any + I-class feast. {!band} entries 11-13 admit any I-class feast ahead of + a II-class Sunday with no subject test (unlike entry 14, see that + entry's own comment) -- so a real I-class SAINT feast can win the day + exactly as a Lord feast would, and must NOT trigger RG 16(a). Real + slug/rank/subject from data/ef/sanctoral.sexp (24 June, I class, + "NATIVITAS S. IOANNIS BAPTISTAE", subject Saint): 24 June falls on a + Time-after-Pentecost Sunday in real years (e.g. 2029), so this is a + live, not merely synthetic, shape -- {!band} entries 11-13 rank it + ahead of the ordinary Sunday's own entry 15 regardless of subject. + Dropping the [subject = Lord] conjunct would wrongly Omit the Sunday + here too. *) + ( "RG16(a) does NOT fire for a I-class feast of a SAINT (not the Lord) \ + impeding a II-class Sunday -- stays Commemorate(Privileged)", + cand ~origin:P.Sanctoral ~rank:V.Class1 ~subject:Sub.Saint ~layer:PE.universal_layer + "nativity-of-st-john-the-baptist", + an_ordinary_sunday, + "Commemorate(Privileged)" ); + (* Conjunct 2/4, LOSER rank: RG 16(a) names "Dominica II classis", not + any Sunday. A I-class Sunday (Advent/Lent/Passiontide, Low Sunday, + {!band} entry 6) can never actually reach here as a loser against a + Class1-or-2 Lord winner in real banding (entry 6's own band value, 6, + beats every entry such a winner could occupy, 3/11-14), which is + exactly why the primary text restricts itself to II class -- but + [disposition] takes no [context] and cannot itself re-derive that + band fact, so the rank check is what actually enforces it here. + Sourced from [Temporal_ef.temporal]'s own real output (Advent I + Sunday 2026, Class1, "ef-advent-sunday-1"), the same row + [disposition_cases]'s own RG95/RG109(a) entry above already uses. + Dropping the [rank = Class2] conjunct on the loser would wrongly Omit + this Sunday's commemoration too. *) + ( "RG16(a) does NOT fire for a I-class Sunday, even losing to a Feast of \ + the Lord -- stays Commemorate(Privileged)", + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "transfiguration-of-our-lord", + of_temporal (T.advent_start 2026), + "Commemorate(Privileged)" ); + (* Conjunct 3/4, LOSER is a Sunday at all: RG 16(a) protects "de + dominica" specifically, not every ordinary office a Feast of the Lord + happens to impede. An ordinary sanctoral SAINT losing to the same + Lord-subject Class2 winner as the row above must still reach the + ordinary RG 95/111(c) commemorate-or-omit flow, not RG 16(a)'s + special no-commemoration rule. Dropping [is_sunday_slug] (or its + [is_temporal] companion) here would wrongly Omit this ordinary + saint's commemoration too. *) + ( "RG16(a) does NOT fire for an ordinary (non-Sunday) SAINT losing to a \ + Feast of the Lord -- stays Commemorate(Ordinary)", + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "transfiguration-of-our-lord", + cand ~origin:P.Sanctoral ~rank:V.Class2 ~layer:PE.universal_layer "ef-some-other-saint", + "Commemorate(Ordinary)" ); + (* Conjunct 4/4, WINNER rank: RG 16(a) says "I aut II classis", not any + class. Every real [subject = Lord] entry this project's own sanctoral + audit found is Class1 or Class2 (this task's own report), so this + conjunct is not independently reachable on real data -- SYNTHETIC, + the same defensive-but-not-load-bearing status this file already + gives the RG26 branch's own [not is_vigil] guard above. Without the + rank conjunct, [subject = Lord] alone would wrongly Omit this + Sunday's commemoration under a hypothetical Class3 "feast of the + Lord" too, which RG 16(a)'s own "I aut II classis" text does not + cover. *) + ( "SYNTHETIC: RG16(a) does NOT fire for a Class3 feast of the Lord \ + (outside \"I aut II classis\") impeding a II-class Sunday -- stays \ + Commemorate(Privileged)", + cand ~origin:P.Sanctoral ~rank:V.Class3 ~subject:Sub.Lord ~layer:PE.universal_layer + "ef-synthetic-class3-lord-feast", + an_ordinary_sunday, + "Commemorate(Privileged)" ) ] (* Task 9: [privilege_of]'s RG 109 categories (§4, "Commemorations"), @@ -929,6 +1022,44 @@ let test_ii_class_sunday_privileged_witness_admitted_end_to_end () = [ ("ef-some-saint", "omitted: admission limit reached") ] (List.map (fun (c, reason) -> (S.to_string c.P.cel.Cel.slug, reason)) resolution.P.omitted) +(* RG 16(a) (register §6.0) reached end-to-end through the REAL pipeline + ([PE.band] AND [PE.disposition] together, not [disposition] in + isolation as [disposition_cases]'s own rows above test it): a real civil + date this task's own blast-radius measurement names as a live instance + of the bug this branch fixes -- 6 August 2028 is a Sunday (independently + checked with `date -d 2028-08-06 +%A`, the same cross-check discipline + [test_golden.ml]'s own header requires), Time after Pentecost week 9, and + the Transfiguration (6 Aug, II class, "IN TRANSFIGURATIONE D. N. I. C.") + falls on it. Before this fix `colitur day` emitted this Sunday as an + admitted [+ef-time-after-pentecost-sunday-9] commemoration on that date + (register §6.0's own reproduction); this proves the full pipeline now + agrees with the isolated [disposition] rows above: [observed] is the + Transfiguration ({!band} entry 14 already did this correctly before this + task), [commemorations] is EMPTY (not merely missing the Sunday from + [admit]'s cut -- RG 16(a) is a [disposition]-level Omit, before [admit] + ever runs), and the Sunday appears in [omitted] with + ["omitted: yielded to a higher day"] ({!Precedence.resolve}'s own Omit + reason), never ["omitted: admission limit reached"] (that reason would + mean the Sunday was tagged [Commemorate] and then cut by [admit] -- a + different, wrong mechanism for the same wrong end state). *) +let test_rg16a_lord_feast_suppresses_sunday_end_to_end () = + let date = mk 2028 8 6 in + let day_ctx = ctx date in + let sunday = of_temporal date in + let transfiguration = + cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer + "transfiguration-of-our-lord" + in + let resolution = P.resolve real_rules day_ctx ~temporal:sunday ~sanctoral:[ transfiguration ] in + Alcotest.(check string) "the Transfiguration is observed, not the Sunday" + "transfiguration-of-our-lord" (S.to_string resolution.P.observed.P.cel.Cel.slug); + Alcotest.(check int) "no commemorations at all -- RG16(a)'s own \"nulla fit commemoratio\"" + 0 (List.length resolution.P.commemorations); + Alcotest.(check (list (pair string string))) + "the Sunday is omitted with RG16(a)'s own disposition-level reason, not admit's cut" + [ ("ef-time-after-pentecost-sunday-9", "omitted: yielded to a higher day") ] + (List.map (fun (c, reason) -> (S.to_string c.P.cel.Cel.slug, reason)) 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 @@ -1166,6 +1297,10 @@ let suite = Alcotest.test_case "II-class Sunday override: RG109(b) witness admitted over an ordinary saint, end-to-end" `Quick test_ii_class_sunday_privileged_witness_admitted_end_to_end; + Alcotest.test_case + "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 "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" |
