summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/rites/rite_of/temporal_of.ml28
-rw-r--r--test/test_rite_of.ml14
-rw-r--r--test/test_temporal_of.ml93
3 files changed, 129 insertions, 6 deletions
diff --git a/lib/rites/rite_of/temporal_of.ml b/lib/rites/rite_of/temporal_of.ml
index e375350..1da6cf9 100644
--- a/lib/rites/rite_of/temporal_of.ml
+++ b/lib/rites/rite_of/temporal_of.ml
@@ -532,8 +532,32 @@ let temporal d =
let anchors y =
let easter = Computus.gregorian_easter y in
let off n = Date.add_days easter n in
- [ ("of-nativity-vigil", mk y 12 24);
- ("of-nativity", mk y 12 25);
+ (* CORRECTED (celebrant-rubrics-phase1 review, 2026-08-26): this used to
+ list ("of-nativity-vigil", mk y 12 24) UNCONDITIONALLY -- a regression
+ from the earlier R8 fix, which added the Sunday guard to [named]
+ above (Tabula I.2 outranks I.3: on a Sunday, 24 December is the
+ Fourth Sunday of Advent, not the Vigil) but never updated this
+ function to match, so in 2028/2034 this list claimed the Vigil where
+ [temporal] actually returns "of-advent-sunday-4". This module's own
+ doc comment on [anchors] says its whole purpose is to "catch an
+ accidental single-site drift... rather than leaving it invisible" --
+ the irony being that it caught exactly this drift, just years later
+ than it should have, only once Validate.run was wired for the OF rite
+ at all (nothing before that compared [anchors] against [named]/
+ [temporal], so the two were free to disagree unnoticed). The Fourth
+ Sunday of Advent is not added as a replacement entry on a Sunday year:
+ it is an ordinary generic week-numbered Sunday
+ ({!sunday_slug}'s own "of-advent-sunday-4", produced every year
+ regardless of where in the week it falls), never one of the NAMED
+ days this function exists to restate -- see the module doc comment
+ above [anchors] ("Independent restatement of {!named}'s... dates").
+ [named]'s own guard is restated exactly, not re-derived: [named]
+ checks the WEEKDAY of the actual civil date, so this does too, rather
+ than assuming Advent 4 Sunday is always 24 December (it is any Sunday
+ 18-24 December; testing the weekday of 24 December itself is exact
+ regardless of which Sunday that turns out to be). *)
+ (if Date.weekday (mk y 12 24) = Date.Sun then [] else [ ("of-nativity-vigil", mk y 12 24) ])
+ @ [ ("of-nativity", mk y 12 25);
("of-mary-mother-of-god", mk y 1 1);
("of-epiphany", mk y 1 6);
("of-holy-family", holy_family y);
diff --git a/test/test_rite_of.ml b/test/test_rite_of.ml
index 90cedc1..d16bcf3 100644
--- a/test/test_rite_of.ml
+++ b/test/test_rite_of.ml
@@ -204,7 +204,19 @@ let test_year_9999_does_not_raise () =
THIS TASK'S SCOPE to fix -- lectionary_of.* and data/of/lectionary.sexp
are excluded from this task's brief, and temporal_of.ml's own rubric
logic is Task 1's deliverable -- reported as concerns in this task's own
- report rather than fixed here or silently excluded without explanation. *)
+ report rather than fixed here or silently excluded without explanation.
+
+ CORRECTED (fix-lectionary-anchors task, 2026-08-26): the of-nativity-
+ vigil/[anchors] disagreement described in the paragraph above is now
+ FIXED -- {!Temporal_of.anchors} carries the same Sunday guard [named]
+ already had. A dedicated exhaustive sweep
+ (test_temporal_of.ml's own [test_exhaustive_domain_sweep], every civil
+ year 1584-9997 under COLITUR_EXHAUSTIVE_SWEEP=1) confirmed this was
+ also the ONLY unexplained [anchors]/[temporal] drift anywhere in the
+ domain -- the other two items in this paragraph (the lectionary
+ weekday-cycle-letter asymmetry, and the Holy Family 30-December
+ fallback {!is_known_holy_family_fallback_gap} below already pins) are
+ unrelated and remain open, unchanged by this fix. *)
let sample_years =
let rec range a b = if a > b then [] else a :: range (a + 1) b in
range 2005 2050
diff --git a/test/test_temporal_of.ml b/test/test_temporal_of.ml
index 1ccd098..2522e44 100644
--- a/test/test_temporal_of.ml
+++ b/test/test_temporal_of.ml
@@ -252,6 +252,49 @@ let test_nativity_vigil_yields_to_advent_sunday () =
Alcotest.(check string) "2026: named still claims the vigil" "of-nativity-vigil" (named_slug thu);
Alcotest.(check string) "2026: temporal still resolves to the vigil" "of-nativity-vigil" (slug_of thu)
+(* FIX (celebrant-rubrics-phase1 review, 2026-08-26): [anchors] used to
+ list ("of-nativity-vigil", 24 December) UNCONDITIONALLY -- a regression
+ from the R8 fix just above, which added [named]'s own Sunday guard but
+ never updated [anchors] to match, so in any year 24 December is itself
+ a Sunday (2028 and 2034 among them, the SAME two years
+ {!test_nativity_vigil_yields_to_advent_sunday} already exercises)
+ [anchors] claimed the Vigil where [temporal] actually resolves to
+ "of-advent-sunday-4". Checked the same way
+ Colitur_kernel.Validate.run's own "anchor" check does: for EVERY
+ (slug, date) pair [anchors] returns, not only the 24 December one --
+ catching a drift anywhere else in the list, not merely confirming this
+ one entry. *)
+let test_anchors_agree_with_temporal () =
+ let check_year y =
+ List.iter
+ (fun (expected_slug, date) ->
+ Alcotest.(check string)
+ (Printf.sprintf "%d: anchors' %S (%s) agrees with temporal" y expected_slug (iso date))
+ expected_slug (slug_of date))
+ (T.anchors y)
+ in
+ List.iter check_year [ 2026; 2027; 2028; 2029; 2034; 2035 ]
+
+(* The specific regression, restated directly (not merely implied by the
+ agreement check above passing): on a Sunday-24-December year [anchors]
+ must not claim the Vigil AT ALL, since it is a generic week-numbered
+ Sunday ("of-advent-sunday-4") [anchors]'s own doc comment says it never
+ restates ("Independent restatement of [named]'s... dates") -- adding a
+ replacement entry would be a second, equally wrong shape (a NAMED
+ restatement of a non-named day). On a non-Sunday year, unaffected: the
+ Vigil entry is unchanged. *)
+let test_anchors_omit_vigil_on_advent_sunday_years () =
+ List.iter
+ (fun y ->
+ Alcotest.(check bool) (Printf.sprintf "%d: anchors omits of-nativity-vigil (24 Dec is Advent IV)" y) false
+ (List.mem_assoc "of-nativity-vigil" (T.anchors y)))
+ [ 2028; 2034 ];
+ List.iter
+ (fun y ->
+ Alcotest.(check bool) (Printf.sprintf "%d: anchors still lists of-nativity-vigil" y) true
+ (List.mem_assoc "of-nativity-vigil" (T.anchors y)))
+ [ 2026; 2027; 2029; 2035 ]
+
let test_colours () =
let colour_of dt = (T.temporal dt).Colitur_kernel.Temporal.office.Colitur_kernel.Celebration.colour in
Alcotest.(check string) "Good Friday is red" "red" (Colitur_kernel.Colour.to_string (colour_of (d 2026 4 3)));
@@ -366,7 +409,26 @@ let colitur_exhaustive_sweep_env = "COLITUR_EXHAUSTIVE_SWEEP"
let test_exhaustive_domain_sweep () =
if Sys.getenv_opt colitur_exhaustive_sweep_env = None then Alcotest.skip ()
- else
+ else begin
+ (* FIX (celebrant-rubrics-phase1 review, 2026-08-26): the audit half of
+ the anchors/temporal task brief -- does any OTHER {!T.anchors} entry
+ drift from {!T.temporal} the same way "of-nativity-vigil" used to
+ (fixed just above, {!test_anchors_agree_with_temporal})? Checked for
+ EVERY entry [anchors y] returns, across the WHOLE exhaustive domain,
+ not merely 24 December, folded into this loop rather than a separate
+ one so it shares its own env-var gate. Answer: no NEW drift -- the
+ only mismatch found is the ALREADY-KNOWN "of-holy-family" gap
+ (Normae n.35(a)'s own 30-December fallback, whenever 26-31 December
+ holds no Sunday): there, unlike the Vigil bug, {!T.anchors} (via
+ {!T.holy_family}) computes the RIGHT date and it is {!T.temporal}
+ that fails to reach it (its Holy-Family test lives inside
+ [sunday_slug]'s own Sunday-only dispatch arm) -- a different shape,
+ already found, pinned and explicitly deferred by test_rite_of.ml's
+ own [test_holy_family_fallback_1583_known_wrong_ferial]/
+ [is_known_holy_family_fallback_gap] (see that file's own extensive
+ citation), collected below rather than failing the sweep on it a
+ second time in a second place. *)
+ let holy_family_fallback_years = ref [] in
for y = 1584 to 9997 do
let days = walk_year y in
if compressed_seasons days <> expected_season_runs then
@@ -375,8 +437,30 @@ let test_exhaustive_domain_sweep () =
if not (prop_ordinary_time_week_in_range y) then Alcotest.failf "%d: an Ordinary Time week is out of 1..34" y;
if not (prop_advent_four_sundays y) then Alcotest.failf "%d: Advent does not have exactly four Sundays" y;
if not (prop_slug_uniqueness y) then Alcotest.failf "%d: a slug repeats within the liturgical year" y;
- if not (prop_weekday_agrees y) then Alcotest.failf "%d: a weekday disagrees with Date.weekday" y
- done
+ if not (prop_weekday_agrees y) then Alcotest.failf "%d: a weekday disagrees with Date.weekday" y;
+ List.iter
+ (fun (expected_slug, date) ->
+ let actual = slug_of date in
+ if actual <> expected_slug then
+ if expected_slug = "of-holy-family" then holy_family_fallback_years := y :: !holy_family_fallback_years
+ else
+ Alcotest.failf
+ "%d: anchors' %S (%s) disagrees with temporal (%S) -- an UNEXPECTED drift, not the known \
+ Holy Family fallback shape"
+ y expected_slug (iso date) actual)
+ (T.anchors y)
+ done;
+ (* The known shape is real, not vacuous, and really is the Normae
+ n.35(a) trigger, for every year collected above, not merely
+ plausible-sounding. *)
+ Alcotest.(check bool) "the Holy Family fallback shape is real and not empty" true
+ (!holy_family_fallback_years <> []);
+ List.iter
+ (fun y ->
+ Alcotest.(check bool) (Printf.sprintf "%d is really a Christmas-Day-is-Sunday year" y) true
+ (D.weekday (d y 12 25) = D.Sun))
+ !holy_family_fallback_years
+ end
(* 1583 and 9999 themselves sit at the domain edges, where the [y-1]/[y+1]
probes the properties above use would call [T.year_start] on an
@@ -410,6 +494,9 @@ let suite =
Alcotest.test_case "sunday slugs" `Quick test_sunday_slugs;
Alcotest.test_case "ferial slugs" `Quick test_ferial_slugs;
Alcotest.test_case "nativity vigil yields to Advent Sunday IV" `Quick test_nativity_vigil_yields_to_advent_sunday;
+ Alcotest.test_case "anchors agree with temporal" `Quick test_anchors_agree_with_temporal;
+ Alcotest.test_case "anchors omit the vigil on an Advent-IV-Sunday year" `Quick
+ test_anchors_omit_vigil_on_advent_sunday_years;
Alcotest.test_case "colours" `Quick test_colours;
Alcotest.test_case "domain edges do not raise" `Quick test_domain_edges_do_not_raise;
Alcotest.test_case "exhaustive domain sweep" `Slow test_exhaustive_domain_sweep ]