aboutsummaryrefslogtreecommitdiff
path: root/test/test_precedence_ef.ml
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_precedence_ef.ml')
-rw-r--r--test/test_precedence_ef.ml354
1 files changed, 285 insertions, 69 deletions
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml
index 2935e40..4ee2fd8 100644
--- a/test/test_precedence_ef.ml
+++ b/test/test_precedence_ef.ml
@@ -48,110 +48,162 @@ let easter = Comp.gregorian_easter 2026
let off n = D.add_days easter n
(* (description, date, candidate, expected RG 91 entry). *)
+(* CORRECTED, fix round 1 (coordinator finding 1): every expected [int]
+ below is now the real RG 91 entry number TIMES TEN (entry 1 -> 10, entry
+ 28 -> 280), matching [PE.band]'s own rescale -- see precedence_ef.ml's
+ own [entry_14_fixed_band] comment for why the bare 1..28 scale was a real
+ ordering bug, not a style choice. The description strings still say the
+ bare entry number ("14 Feast of the Lord...") -- that names the RG 91
+ ROW, not the band integer, and is unaffected by the scale. *)
let cases =
[ (* Entry 1 -- RG 91 entry 1 (§4): Nativity, Easter Sunday, Pentecost Sunday. *)
- ("1 Nativity", mk 2026 12 25, cand "ef-nativity", 1);
- ("1 Easter Sunday", off 0, cand "ef-easter-sunday", 1);
- ("1 Pentecost Sunday", off 49, cand "ef-pentecost", 1);
+ ("1 Nativity", mk 2026 12 25, cand "ef-nativity", 10);
+ ("1 Easter Sunday", off 0, cand "ef-easter-sunday", 10);
+ ("1 Pentecost Sunday", off 49, cand "ef-pentecost", 10);
(* Entry 2 -- RG 91 entry 2 (§4): Sacred Triduum. Thu-Sat of Holy Week,
NOT entry 7 (which stops at Wednesday -- see entry 7 below). *)
- ("2 Holy Thursday", off (-3), cand "ef-holy-thursday", 2);
- ("2 Good Friday", off (-2), cand "ef-good-friday", 2);
- ("2 Holy Saturday", off (-1), cand "ef-holy-saturday", 2);
+ ("2 Holy Thursday", off (-3), cand "ef-holy-thursday", 20);
+ ("2 Good Friday", off (-2), cand "ef-good-friday", 20);
+ ("2 Holy Saturday", off (-1), cand "ef-holy-saturday", 20);
(* Entry 3 -- RG 91 entry 3 (§4). *)
- ("3 Epiphany", mk 2026 1 6, cand "ef-epiphany", 3);
- ("3 Ascension", off 39, cand "ef-ascension", 3);
- ("3 Trinity", off 56, cand "ef-trinity", 3);
- ("3 Corpus Christi", off 60, cand "ef-corpus-christi", 3);
- ("3 Sacred Heart", off 68, cand "ef-sacred-heart", 3);
- ("3 Christ the King", T.christ_the_king 2026, cand "ef-christ-the-king", 3);
+ ("3 Epiphany", mk 2026 1 6, cand "ef-epiphany", 30);
+ ("3 Ascension", off 39, cand "ef-ascension", 30);
+ ("3 Trinity", off 56, cand "ef-trinity", 30);
+ ("3 Corpus Christi", off 60, cand "ef-corpus-christi", 30);
+ ("3 Sacred Heart", off 68, cand "ef-sacred-heart", 30);
+ ("3 Christ the King", T.christ_the_king 2026, cand "ef-christ-the-king", 30);
(* Entry 4 -- RG 91 entry 4 (§4). Sanctoral-origin: neither feast is part
of temporal_ef's movable cycle. *)
( "4 Immaculate Conception", mk 2026 12 8,
cand ~origin:P.Sanctoral ~subject:Sub.Bvm ~layer:PE.universal_layer
"ef-immaculate-conception",
- 4 );
- ("4 Assumption", mk 2026 8 15, cand ~origin:P.Sanctoral ~subject:Sub.Bvm ~layer:PE.universal_layer "ef-assumption", 4);
+ 40 );
+ ("4 Assumption", mk 2026 8 15, cand ~origin:P.Sanctoral ~subject:Sub.Bvm ~layer:PE.universal_layer "ef-assumption", 40);
(* Entry 5 -- RG 91 entry 5 (§4). *)
- ("5 Nativity Vigil", mk 2026 12 24, cand "ef-nativity-vigil", 5);
- ("5 Octave day (Circumcision)", mk 2026 1 1, cand "ef-circumcision", 5);
+ ("5 Nativity Vigil", mk 2026 12 24, cand "ef-nativity-vigil", 50);
+ ("5 Octave day (Circumcision)", mk 2026 1 1, cand "ef-circumcision", 50);
(* Entry 6 -- RG 91 entry 6 (§4). *)
- ("6 Advent Sunday", T.advent_start 2026, cand "ef-advent-sunday-1", 6);
- ("6 Lent Sunday", off (-42), cand "ef-lent-sunday-1", 6);
- ("6 Passion Sunday (I Passiontide)", off (-14), cand "ef-passion-sunday", 6);
- ("6 Palm Sunday (II Passiontide)", off (-7), cand "ef-palm-sunday", 6);
- ("6 Low Sunday", off 7, cand "ef-low-sunday", 6);
+ ("6 Advent Sunday", T.advent_start 2026, cand "ef-advent-sunday-1", 60);
+ ("6 Lent Sunday", off (-42), cand "ef-lent-sunday-1", 60);
+ ("6 Passion Sunday (I Passiontide)", off (-14), cand "ef-passion-sunday", 60);
+ ("6 Palm Sunday (II Passiontide)", off (-7), cand "ef-palm-sunday", 60);
+ ("6 Low Sunday", off 7, cand "ef-low-sunday", 60);
(* Entry 7 -- RG 91 entry 7 (§4): Ash Wednesday and Mon/Tue/Wed of Holy
Week ONLY -- Thu-Sat are entry 2 above, not this entry. *)
- ("7 Ash Wednesday", off (-46), cand "ef-ash-wednesday", 7);
- ("7 Monday of Holy Week", off (-6), cand "ef-holy-monday", 7);
- ("7 Tuesday of Holy Week", off (-5), cand "ef-holy-tuesday", 7);
- ("7 Wednesday of Holy Week", off (-4), cand "ef-holy-wednesday", 7);
+ ("7 Ash Wednesday", off (-46), cand "ef-ash-wednesday", 70);
+ ("7 Monday of Holy Week", off (-6), cand "ef-holy-monday", 70);
+ ("7 Tuesday of Holy Week", off (-5), cand "ef-holy-tuesday", 70);
+ ("7 Wednesday of Holy Week", off (-4), cand "ef-holy-wednesday", 70);
(* Entry 8 -- RG 91 entry 8 (§4). 2 Nov 2026 is a Monday (verified
independently below the table), so this row is the plain case. The
register's own qualifying case -- "yields to an occurring Sunday" --
gets its own row and its own end-to-end test after this table (2 Nov
2025 is a real Sunday). *)
- ("8 All Souls (non-Sunday)", mk 2026 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 8);
+ ("8 All Souls (non-Sunday)", mk 2026 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 80);
(* Entry 8's qualifier: "(yields to an occurring Sunday)". 2 Nov 2025 is
- a Sunday, so this must NOT be 8 -- it must lose to entry 15 (16 =
- entry 15's own value + 1, the exact value precedence_ef.ml documents
- and justifies at entry 8's branch). The end-to-end resolve-level
- proof that the Sunday actually wins the day is
+ a Sunday, so this must NOT be 80 -- it must lose to entry 15 (151 =
+ entry 15's own value, 150, + 1, the exact value precedence_ef.ml
+ documents and justifies at entry 8's branch). The end-to-end
+ resolve-level proof that the Sunday actually wins the day is
[test_all_souls_yields_to_sunday] below; this row pins the specific
integer [band] returns. *)
- ("8 All Souls (yields to a Sunday, 2 Nov 2025)", mk 2025 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 16);
+ ("8 All Souls (yields to a Sunday, 2 Nov 2025)", mk 2025 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 151);
(* Entry 9 -- RG 91 entry 9 (§4). *)
- ("9 Pentecost Vigil", off 48, cand "ef-pentecost-vigil", 9);
+ ("9 Pentecost Vigil", off 48, cand "ef-pentecost-vigil", 90);
(* Entry 10 -- RG 91 entry 10 (§4): both range boundaries, to guard the
off-by-one an inclusive Easter-offset window invites. *)
- ("10 Easter octave, day+1", off 1, cand "ef-easter-1-mon", 10);
- ("10 Easter octave, day+6", off 6, cand "ef-easter-1-sat", 10);
- ("10 Pentecost octave, day+50", off 50, cand "ef-pentecost-1-mon", 10);
- ("10 Pentecost octave, day+55", off 55, cand "ef-pentecost-1-sat", 10);
+ ("10 Easter octave, day+1", off 1, cand "ef-easter-1-mon", 100);
+ ("10 Easter octave, day+6", off 6, cand "ef-easter-1-sat", 100);
+ ("10 Pentecost octave, day+50", off 50, cand "ef-pentecost-1-mon", 100);
+ ("10 Pentecost octave, day+55", off 55, cand "ef-pentecost-1-sat", 100);
(* Entry 11 -- RG 91 entry 11 (§4). *)
( "11 Universal I-class feast", mk 2026 6 29,
cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:PE.universal_layer "ef-ss-peter-paul",
- 11 );
+ 110 );
(* Entry 12 -- RG 91 entry 12 (§4). The one non-base-layer case the brief
asks for explicitly: same date/rank/subject as 11, only the layer
differs, so this row isolates the layer test as the deciding factor. *)
( "12 Proper I-class feast (non-base layer)", mk 2026 6 29,
cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:"diocese-warsaw" "ef-local-patron",
- 12 );
+ 120 );
(* Entry 13 -- RG 91 entry 13 (§4). *)
( "13 Indult I-class feast", mk 2026 6 29,
cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:(PE.indult_prefix ^ "local-grant")
"ef-indult-feast-1",
- 13 );
+ 130 );
+ (* fix round 1 (coordinator finding 1), NEW: a synthetic I-CLASS
+ candidate landing INSIDE Holy Family's own 7-13 January window --
+ the exact counter-example that found the original [-14] sentinel's
+ inversion (Holy Family used to beat every I-class entry outright,
+ not merely the fixed Baptism it was built to beat). Must still
+ outrank the movable-14 entry below (110 < entry_14_movable_band),
+ proving the fix, not merely the absence of the old bug's OWN
+ witness (13 January). Not live against the shipped universal data
+ (register §6.2) -- this is exactly the kind of I-class PROPER/
+ INDULT feast an overlay could add, entries 11-13's own [not
+ is_temporal] tests notwithstanding. *)
+ ( "11 SYNTHETIC: a universal I-class feast inside Holy Family's own window \
+ (fix round 1, coordinator finding 1) must still outrank the movable half",
+ mk 2026 1 11,
+ cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:PE.universal_layer
+ "ef-synthetic-class1-probe",
+ 110 );
(* Entry 14 -- RG 91 entry 14 (§4), deliberately UNQUALIFIED (contrast
entry 16, which explicitly says "not of the Lord"). *)
( "14 Feast of the Lord, II class", mk 2026 7 1,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer
"ef-precious-blood",
- 14 );
+ PE.entry_14_fixed_band );
(* Entry 14, non-base layer: unlike 11-13/16/19/20/23/24, entry 14 draws
- no universal/proper/indult line at all, so this must STILL be 14, not
- 19 -- the exact restriction review finding 2 flagged and this row
- exists to keep from silently coming back. *)
+ no universal/proper/indult line at all, so this must STILL be
+ [PE.entry_14_fixed_band], not 19 -- the exact restriction review
+ finding 2 flagged and this row exists to keep from silently coming
+ back. *)
( "14 Feast of the Lord, II class (non-base layer)", mk 2026 7 2,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:"diocese-warsaw"
"ef-local-feast-of-the-lord",
- 14 );
+ PE.entry_14_fixed_band );
+ (* 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 ([PE.entry_14_fixed_band] 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. CORRECTED, fix round 1
+ (coordinator finding 1): [PE.entry_14_movable_band] used to be a
+ negative sentinel, which beat every I-class entry too, not merely
+ entry 14's own fixed half -- see the SYNTHETIC I-class probe row
+ above, and [PE.entry_14_movable_band]'s own comment in
+ precedence_ef.ml, for the fix and the counter-example that found it. *)
+ ( "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). *)
- ("15 II-class Sunday (Septuagesima)", off (-63), cand ~rank:V.Class2 "ef-septuagesima-sunday", 15);
+ ("15 II-class Sunday (Septuagesima)", off (-63), cand ~rank:V.Class2 "ef-septuagesima-sunday", 150);
(* Entry 16 -- RG 91 entry 16 (§4). *)
( "16 Universal II-class feast, not of the Lord", mk 2026 1 20,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint ~layer:PE.universal_layer
"ef-some-saint",
- 16 );
+ 160 );
(* Entry 17 -- RG 91 entry 17 (§4): days WITHIN the Nativity octave (26-28
Dec are Stephen/John/Innocents -- sanctoral, not this entry; 1 Jan is
entry 5's Octave DAY, not this entry either). *)
- ("17 Nativity octave, 29 Dec", mk 2026 12 29, cand ~rank:V.Class2 "ef-nativity-octave-day-5", 17);
- ("17 Nativity octave, 31 Dec", mk 2026 12 31, cand ~rank:V.Class2 "ef-nativity-octave-day-7", 17);
+ ("17 Nativity octave, 29 Dec", mk 2026 12 29, cand ~rank:V.Class2 "ef-nativity-octave-day-5", 170);
+ ("17 Nativity octave, 31 Dec", mk 2026 12 31, cand ~rank:V.Class2 "ef-nativity-octave-day-7", 170);
(* Entry 18 -- RG 91 entry 18 (§4): Advent 17-23 Dec ferias AND the
Ember days of Advent/Lent/September share this one entry. The second
row is deliberately a Lent date (season Lent, NOT Advent) to prove the
@@ -159,21 +211,21 @@ let cases =
to fall in the Dec 17-23 window -- the exact trap the brief warns
about, worked the other way round: this Ember day must NOT be
mistaken for an ordinary entry-22 Lent feria either. *)
- ("18 Advent 17-23 Dec feria", mk 2026 12 21, cand ~rank:V.Class2 "ef-advent-4-mon", 18);
+ ("18 Advent 17-23 Dec feria", mk 2026 12 21, cand ~rank:V.Class2 "ef-advent-4-mon", 180);
(* Sourced from Temporal_ef.temporal's own output (see [of_temporal])
rather than a hand-typed "ef-lent-ember-wed" -- closes review finding
3's coupling concern for the Ember prefixes specifically. *)
- ("18 Lent Ember Wednesday (from Temporal_ef.temporal)", off (-39), of_temporal (off (-39)), 18);
+ ("18 Lent Ember Wednesday (from Temporal_ef.temporal)", off (-39), of_temporal (off (-39)), 180);
(* Entry 19 -- RG 91 entry 19 (§4). *)
( "19 Proper II-class feast", mk 2026 1 20,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint ~layer:"diocese-warsaw"
"ef-local-saint-2",
- 19 );
+ 190 );
(* Entry 20 -- RG 91 entry 20 (§4). *)
( "20 Indult II-class feast", mk 2026 1 20,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint
~layer:(PE.indult_prefix ^ "local-grant-2") "ef-indult-feast-2",
- 20 );
+ 200 );
(* Entry 21 -- RG 91 entry 21 (§4, RG 28-34). Two rows: the Ascension
Vigil is the one II-class vigil temporal_ef already produces today
(temporal-origin); the Assumption Vigil stands in for the
@@ -183,22 +235,22 @@ let cases =
(* Sourced from Temporal_ef.temporal's own output (see [of_temporal])
rather than a hand-typed "ef-ascension-vigil" -- closes review finding
3's coupling concern for [vigil_suffix]. *)
- ("21 Ascension Vigil (from Temporal_ef.temporal)", off 38, of_temporal (off 38), 21);
+ ("21 Ascension Vigil (from Temporal_ef.temporal)", off 38, of_temporal (off 38), 210);
( "21 Assumption Vigil (sanctoral-origin)", mk 2026 8 14,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~layer:PE.universal_layer "ef-assumption-vigil",
- 21 );
+ 210 );
(* Also review finding 3 / "worth doing": a UNIVERSAL-layer Class2 vigil
whose subject is the Lord must still be 21, not 14 -- pins entry 14's
[not is_vigil] guard even after finding 2 dropped its layer test. *)
( "21 Universal II-class vigil of the Lord", mk 2026 6 23,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer
"ef-precious-blood-vigil",
- 21 );
+ 210 );
(* Entry 22 -- RG 91 entry 22 (§4) (corrected: ends at Palm Sunday, not
Passion Sunday). Both a Lent and a Passiontide feria, clear of Ash
Wednesday, Holy Week and the Ember days. *)
- ("22 Lent feria", off (-41), cand ~rank:V.Class3 "ef-lent-1-mon", 22);
- ("22 Passiontide feria", off (-12), cand ~rank:V.Class3 "ef-passiontide-1-tue", 22);
+ ("22 Lent feria", off (-41), cand ~rank:V.Class3 "ef-lent-1-mon", 220);
+ ("22 Passiontide feria", off (-12), cand ~rank:V.Class3 "ef-passiontide-1-tue", 220);
(* Entry 23 -- RG 91 entry 23 (§4). NOTE the table's own order here is the
REVERSE of 11/12 and 14/16/19/20 above: entry 23 (particular
calendars) is numbered BELOW entry 24 (universal), so a proper
@@ -206,45 +258,45 @@ let cases =
register states it, not "corrected" to match the other classes. *)
( "23 Proper III-class feast (non-base layer)", mk 2026 6 30,
cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:"diocese-warsaw" "ef-local-saint-3",
- 23 );
+ 230 );
(* Entry 24 -- RG 91 entry 24 (§4). *)
( "24 Universal III-class feast", mk 2026 6 30,
cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "ef-some-saint-3",
- 24 );
+ 240 );
(* Entry 25 -- RG 91 entry 25 (§4). *)
- ("25 Advent feria to 16 Dec", mk 2026 12 1, cand ~rank:V.Class3 "ef-advent-1-tue", 25);
+ ("25 Advent feria to 16 Dec", mk 2026 12 1, cand ~rank:V.Class3 "ef-advent-1-tue", 250);
(* Entry 26 -- RG 91 entry 26 (§4). *)
( "26 III-class vigil", mk 2026 8 9,
cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "ef-lawrence-vigil",
- 26 );
+ 260 );
(* Also worth doing: a NON-universal-layer Class3 vigil must still be 26,
not 23 -- pins entry 23's [not is_vigil] guard. *)
( "26 III-class vigil (non-base layer)", mk 2026 8 10,
cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:"diocese-warsaw" "ef-local-patron-vigil",
- 26 );
+ 260 );
(* Task 11, issue (a): the sanctoral bootstrap (data/ef/sanctoral.sexp)
names its vigils with lectio's OWN "vigil-of-X" PREFIX convention, not
[PE.vigil_suffix]'s "-vigil" SUFFIX every row above uses -- exactly
the mismatch Task 7's review predicted. These two rows use the real
bootstrapped slugs verbatim (data/ef/sanctoral.sexp: 28 Jun, 9 Aug),
proving [band] recognises the prefix convention too: without it, both
- would misfile at 16/24 (an ordinary feast of the same rank) instead
- of 21/26. *)
+ would misfile at 160/240 (an ordinary feast of the same rank) instead
+ of 210/260. *)
( "21 II-class vigil via the sanctoral data's own \"vigil-of-X\" prefix",
mk 2026 6 28,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~layer:PE.universal_layer "vigil-of-sts-peter-paul",
- 21 );
+ 210 );
( "26 III-class vigil via the sanctoral data's own \"vigil-of-X\" prefix",
mk 2026 8 9,
cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "vigil-of-st-lawrence",
- 26 );
+ 260 );
(* Entry 27 -- RG 91 entry 27 (§4): an otherwise-unoccupied IV-class
Saturday. *)
( "27 Office of the BVM on Saturday", off 62,
cand ~rank:V.Class4 "ef-time-after-pentecost-1-sat",
- 27 );
+ 270 );
(* Entry 28 -- RG 91 entry 28 (§4): the unqualified IV-class catch-all. *)
- ("28 IV-class feria", off 65, cand ~rank:V.Class4 "ef-time-after-pentecost-1-tue", 28);
+ ("28 IV-class feria", off 65, cand ~rank:V.Class4 "ef-time-after-pentecost-1-tue", 280);
(* Not an RG 91 row at all: a I-class candidate marked as a vigil, which
is not the Nativity or Pentecost (entries 5/9, the only I-class
vigils the table names) and so has no entry to fall into. Proves the
@@ -662,7 +714,87 @@ 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)" );
+ (* PRIMARY authority -- CORRECTED, fix round 1 (coordinator finding 7):
+ RG 95's own second paragraph, present in ALL THREE documents
+ including the electronic transcription: "Si vero duo festa eiusdem
+ Divinae Personae... simul occurrunt, fit de festo, quod in tabella
+ praecedentiae superiorem obtinet locum et aliud omittitur" -- if two
+ feasts of the SAME DIVINE PERSON occur together, the one higher in
+ the table is kept, the OTHER OMITTED -- an occurrence-level rule
+ closer to this exact collision than RG 112(a) below.
+ CORROBORATION, RG 112(a) (docs/research/rules-register.md §4/§6.0,
+ primary text, verified against all three documents): "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).
+ CORROBORATION 2, the Holy Family Mass propers' own more specific
+ rubric, both photographic scans, word for word (real, and settles 13
+ January on its own regardless of RG 95/112(a)): "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).
+ CORRECTED, fix round 1 (coordinator finding 2): a previous version of
+ this comment claimed the electronic transcription's absence of this
+ specific rubric was "exactly the transcription's own documented
+ defect" -- WRONG, struck. That transcription (a 2006 web capture)
+ carries almost no Mass-propers text of any kind (5 Introit
+ occurrences in 26,322 lines against 61/402 in the two photographic
+ scans; its own "Proprium de Tempore" page is a table of contents
+ linking to PDFs the capture never pulled in) -- a coverage gap, not
+ a silent drop from content it otherwise has. RG 112 itself, unlike
+ this rubric, IS present in full in the transcription (all four
+ sub-clauses, verbatim) -- checked directly, not assumed. *)
+ ( "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 +1350,82 @@ 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)
+
+(* Fix round 1 (coordinator finding 1), end-to-end proof of the
+ [entry_14_movable_band] fix itself: a genuine I-class candidate landing
+ inside Holy Family's own 7-13 January window (a synthetic proper/indult
+ feast, standing in for the diocesan-overlay shape {!band}'s own new
+ comment names as the live risk) must be OBSERVED and must NOT transfer
+ -- reproducing, at the [Precedence.resolve] level, the exact regression
+ the coordinator demonstrated against the pre-fix [-14] sentinel ("the
+ I-class feast displaced and transferred to the next day"). With the fix,
+ {!band}'s entry 11 (110) beats {!PE.entry_14_movable_band} (139)
+ outright, so the I-class candidate wins the day, and Holy Family itself
+ -- losing, is_temporal, its own slug carrying the "-sunday" marker --
+ falls to RG 109(a)'s ordinary privileged-Sunday-commemoration path
+ (disposition's final catch-all, [privilege_of]'s (a) branch), the same
+ treatment ANY genuine feast outranking an ordinary Sunday gets: neither
+ omitted nor transferred, a Privileged commemoration. *)
+let test_class1_feast_inside_holy_familys_window_end_to_end () =
+ let date = mk 2026 1 11 in
+ Alcotest.(check string) "sanity: this is Holy Family's own date in 2026" "2026-01-11" (D.to_iso8601 date);
+ let day_ctx = ctx date in
+ let holy_family = of_temporal date in
+ let synthetic_class1 =
+ cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:PE.universal_layer "ef-synthetic-class1-probe"
+ in
+ let resolution = P.resolve real_rules day_ctx ~temporal:holy_family ~sanctoral:[ synthetic_class1 ] in
+ Alcotest.(check string) "the I-class candidate is observed, NOT Holy Family (RG91 e11 beats e14-movable)"
+ "ef-synthetic-class1-probe" (S.to_string resolution.P.observed.P.cel.Cel.slug);
+ Alcotest.(check (list string)) "nothing transfers -- the pre-fix bug moved the I-class feast to the next day"
+ [] (List.map (fun c -> S.to_string c.P.cel.Cel.slug) resolution.P.deferred);
+ (match resolution.P.commemorations with
+ | [ (c, P.Privileged) ] ->
+ Alcotest.(check string)
+ "Holy Family itself is privileged-commemorated (RG109(a), a Sunday-shaped office \
+ outranked by a genuine feast), neither omitted nor transferred"
+ "ef-time-after-epiphany-sunday-1" (S.to_string c.P.cel.Cel.slug)
+ | other ->
+ Alcotest.failf "expected exactly one Privileged commemoration (Holy Family), got %d"
+ (List.length other))
+
(* 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 +1673,14 @@ 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
+ "fix round 1, finding 1: a synthetic I-class candidate inside Holy Family's own window is \
+ observed and does not transfer (2026-01-11)"
+ `Quick test_class1_feast_inside_holy_familys_window_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"