diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-26 23:40:17 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-26 23:40:17 +0200 |
| commit | 2f6d25aff88b4c76c61e36889f1c017b3cc8faee (patch) | |
| tree | baf3027229ef3b95eff2d7fdf0b479f8bdbb2a87 /test/test_precedence_of.ml | |
| parent | 1c0137dee8ff3858707281366662891a5ff01044 (diff) | |
| download | colitur-2f6d25aff88b4c76c61e36889f1c017b3cc8faee.tar.gz colitur-2f6d25aff88b4c76c61e36889f1c017b3cc8faee.zip | |
fix(of): anticipate St Joseph onto 18 March per Normae n. 56(f)
W1: when 19 March (St Joseph) falls on Palm Sunday, colitur sent him
forward to Easter+9 instead of anticipating the solemnity backward to
18 March, the one rule in the whole Normae transfer machinery that
names an earlier date. Live in 16 of 1583-2100 (1595, 1606, 1617,
1690, 1758, 1769, 1780, 1815, 1826, 1837, 1967, 1978, 1989, 2062,
2073, 2084).
Blocked on a kernel contract: Rite.t.transfer_target required its
result be strictly later than the impeded date, an obligation argued
purely from the EF's RG 96. Traced against Calendar's actual placement
pass rather than assumed unsafe: the round loop re-resolves the whole
year fresh every round, and its two safety nets (~start/~stop; the
round-count guard) both test the target's own value with no comparison
against origin, so a bounded backward jump converges the same way a
bounded forward one does. The one requirement that stays load-bearing
is target <> origin: Precedence.resolve never deduplicates by slug, so
a candidate returning its own argument unchanged would collide with
itself in the same round's contest. Relaxed the obligation accordingly
(rite.mli) and added the fourth transfer_target branch in
Precedence_of, ahead of the existing three (Precedence_of.transfer_target,
Rule 0). calendar.ml needed no logic change, only two comments that
asserted "every genuine transfer moves forward" as a blanket fact.
Unpinned the two tests that asserted the old wrong (forward) behaviour
and replaced them with assertions of the correct one, plus a new
direct unit test of the backward branch across two independent years.
EF output is untouched: data/ef/ and lib/rites/rite_ef/ carry no diff,
and colitur day/readings for 2026, 1583 and 9999 are byte-identical
against a build of the prior commit. OF's own 2005-2050 window carries
none of the 16 affected years and shows zero unexpected diffs; the
full 1583-9999 domain still reaches a fixed point under the exhaustive
sweep.
Diffstat (limited to 'test/test_precedence_of.ml')
| -rw-r--r-- | test/test_precedence_of.ml | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/test/test_precedence_of.ml b/test/test_precedence_of.ml index e1af486..23c26c2 100644 --- a/test/test_precedence_of.ml +++ b/test/test_precedence_of.ml @@ -405,7 +405,13 @@ let test_annunciation_in_easter_octave () = let test_solemnity_on_a_lenten_sunday () = (* Normae n. 5: a solemnity occurring on a Sunday of Advent, Lent or Easter goes to the FOLLOWING MONDAY, not to a searched free day. 19 March 2028 - (St Joseph) is the Third Sunday of Lent. *) + (St Joseph) is the Third Sunday of Lent, not Palm Sunday -- Rule 0 + (n. 56(f)) does not fire here even with the REAL slug (see the test + right below this one for that), because its own guard is [is_palm_sunday + origin], not merely "this candidate is St Joseph". A synthetic slug is + used deliberately, to keep this test about rule 2's own generic + "solemnity on a privileged Sunday" behaviour, not about St Joseph + specifically. *) let joseph = mk ~slug:"of-joseph" ~rank:Vocab_of.Sollemnitas ~origin:Precedence.Sanctoral () in let target = @@ -413,10 +419,48 @@ let test_solemnity_on_a_lenten_sunday () = Alcotest.(check string) "St Joseph 2028 goes to the following Monday" "2028-03-20" (Date.to_iso8601 target) +(* Normae n. 56(f), Rule 0 -- the one EARLIER-target shape + {!Colitur_kernel.Rite.t.transfer_target}'s relaxed obligation now permits + (rite.mli's own comment carries the argument; W1, + [of-normae-56f], 2026-08-26). Unit-level counterpart to + test_rite_of.ml's own full-Calendar [test_joseph_palm_sunday_2062_ + anticipated_backward] -- this one calls [transfer_target] directly, the + same shape every other test in this file uses, and checks the REAL + shipped slug (unlike [test_solemnity_on_a_lenten_sunday] above, which + uses a placeholder deliberately). Two independent years, not one: 2062 + and 1978 both have Gregorian Easter = 26 March (Palm Sunday = 19 March), + confirmed directly rather than assumed. *) +let test_st_joseph_on_palm_sunday_anticipated_backward () = + let joseph = + mk ~slug:"joseph-husband-of-the-blessed-virgin-mary" ~rank:Vocab_of.Sollemnitas + ~origin:Precedence.Sanctoral () in + List.iter + (fun (palm_sunday, expected_target) -> + let origin = iso palm_sunday in + Alcotest.(check bool) + (Printf.sprintf "%s really is a Sunday (Easter = 26 March that year)" palm_sunday) + true (Date.weekday origin = Date.Sun); + let target = Rite_of.Precedence_of.transfer_target joseph origin occupant in + Alcotest.(check string) + (Printf.sprintf "St Joseph on Palm Sunday %s is anticipated BACKWARD to the preceding Saturday" + palm_sunday) + expected_target (Date.to_iso8601 target); + Alcotest.(check bool) (Printf.sprintf "%s -> earlier than origin, not later" palm_sunday) true + (Date.compare target origin < 0)) + [ ("2062-03-19", "2062-03-18"); ("1978-03-19", "1978-03-18") ] + let test_target_is_strictly_later () = - (* Rite.t.transfer_target's own stated obligation: Calendar treats - target <= origin as a legitimate placement, so a rule that can stand - still would loop candidates in place rather than fail loudly. *) + (* This is the Annunciation's OWN obligation (rules 1-3), not a claim + about {!Rite_of.Precedence_of.transfer_target} as a whole any more -- + {!Colitur_kernel.Rite.t.transfer_target}'s kernel-level obligation was + relaxed from "strictly later" to "different from origin" (W1, + [of-normae-56f], 2026-08-26; see rite.mli's own comment), precisely so + Rule 0 above could return an EARLIER target for St Joseph. The + Annunciation's own three rules are untouched by that relaxation -- + none of them was rewritten, and Calendar's placement pass still treats + [target = origin] as a legitimate (bad) placement for whichever + candidate returns it, so a rule that can stand still would still loop + in place rather than fail loudly. *) List.iter (fun s -> let d = iso s in @@ -496,7 +540,10 @@ let suite = test_annunciation_in_easter_octave; Alcotest.test_case "solemnity on a Lenten Sunday -> following Monday" `Quick test_solemnity_on_a_lenten_sunday; - Alcotest.test_case "transfer_target is always strictly later" `Quick + Alcotest.test_case + "Normae n.56(f), W1: St Joseph on Palm Sunday is anticipated backward to 18 March" `Quick + test_st_joseph_on_palm_sunday_anticipated_backward; + Alcotest.test_case "the Annunciation's own rules 1-3 are always strictly later" `Quick test_target_is_strictly_later; Alcotest.test_case "transfer_target lands free of Tabula entries 1-8" `Quick test_target_is_free_of_entries_1_to_8; |
