aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 12:16:53 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 12:16:53 +0200
commit04f7521a9143d8888d1e29a3e5c2d60eaae56951 (patch)
tree32fc3bf61c0717f2456046befdf7605425cf0093
parente19adb9c7ea369cafe76e7eff50e9248a4a954d0 (diff)
downloadcolitur-04f7521a9143d8888d1e29a3e5c2d60eaae56951.tar.gz
colitur-04f7521a9143d8888d1e29a3e5c2d60eaae56951.zip
kernel(rite-ef): apply RG16(a) -- a Feast of the Lord leaves the impeded Sunday uncommemorated
Caput III "De Dominicis", RG 16(a): 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's entry 14 already ranked such a feast above the Sunday correctly; disposition had no exception for the collision, so the impeded Sunday fell through to the ordinary RG 95/109(a) commemorate-or-omit catch-all and was wrongly admitted as a privileged commemoration. Adds a branch to Rite_ef.Precedence_ef.disposition, gated on all four of: loser is temporal-origin, loser rank Class2, loser carries the Sunday slug marker, winner rank Class1-or-2 with subject Lord. No signature change -- disposition already receives the winner (RG 33's own vigil-omission branch already reads it); each conjunct is proved independently load-bearing by a dedicated disposition_cases row, including one real-data case each for the winner's subject and rank conjuncts, and a defensive synthetic case for the winner rank conjunct's currently-unreachable Class3 shape. A new end-to-end resolve-level test (2028-08-06, the Transfiguration) proves the real band/disposition pipeline together, checking the Omit reason string distinguishes this from admit's own admission-limit cut.
-rw-r--r--lib/rites/rite_ef/precedence_ef.ml79
-rw-r--r--test/test_precedence_ef.ml137
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"