summaryrefslogtreecommitdiff
path: root/test/test_precedence_ef.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 01:19:37 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 01:19:52 +0200
commit9ce4527550fed24036f6f116474c1f67714f5194 (patch)
treefa941f55a64c930b3c8c12606d1c7a3eadcf3aaa /test/test_precedence_ef.ml
parent4774ef07edc3c197cbd0134e2839eeee2701db2e (diff)
downloadcolitur-9ce4527550fed24036f6f116474c1f67714f5194.tar.gz
colitur-9ce4527550fed24036f6f116474c1f67714f5194.zip
rite(ef): the Annunciation's RG96 exception is conditional, not unconditional
The register was transcribed faithfully but was itself wrong: RG 96's Attamen (a), primary-source-verified from the scans and now corrected in the register, reads 'festum Annuntiationis B. Mariae Virg., quando est transferendum post Pascha, transfertur ... in feriam II post dominicam in albis' -- the Monday-after-Low-Sunday seat applies ONLY 'quando est transferendum post Pascha', when the feast is to be transferred PAST EASTER. The unconditional transcription made the exception fire on every impeded Annunciation regardless of cause. transfer_target now computes the general RG 96 target first, for every candidate, and overrides to the Monday after Low Sunday only when that general target itself falls after Easter Sunday -- testing the rubric's own condition directly rather than re-deriving a date-proximity rule from first principles. Confirmed against three real years the review named: 2007, 2012 and 2057 all previously sent the Annunciation to Easter + 8 (16 April, 16 April, 30 April respectively) when the correct, now-produced target is the next free day before Easter (26 March in each case -- Passion Sunday in 2007/2012, Lent III Sunday in 2057). Verified with actual CLI output for all three, before and after. Also cites RG 96 Attamen (b), the same primary-source passage, as the direct authority for All Souls' own move to the following Monday when impeded by a Sunday -- previously inferred from RG 91 entry 8's parenthetical plus the general walk, which happened to produce the right date; now stated directly. Rewrote the two existing Annunciation unit tests, whose synthetic occupants no longer trigger the (now correctly conditional) exception, and added a 2057 regression test using the real Temporal_ef.temporal as occupant plus a real-data cram pin -- both mutation-verified against the unconditional reading.
Diffstat (limited to 'test/test_precedence_ef.ml')
-rw-r--r--test/test_precedence_ef.ml74
1 files changed, 59 insertions, 15 deletions
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml
index 0973a93..aa98686 100644
--- a/test/test_precedence_ef.ml
+++ b/test/test_precedence_ef.ml
@@ -844,36 +844,76 @@ let test_transfer_target_general_multi_step_search () =
Alcotest.(check string) "lands on the first day past the blocked run"
"2026-01-13" (D.to_iso8601 target)
-(* RG 96's Annunciation exception: starts the search at the Monday after Low
- Sunday, NOT [origin + 1] -- occupant is unconditionally free, so a
- general-path implementation would return [origin + 1] (26 March), a date
- this test explicitly rules out as well as pinning the real expected one,
- so the assertion genuinely discriminates the two starting points rather
- than merely checking "some date after origin". *)
+(* Coordinator review (fix round 1): RG 96 Attamen (a) (register-transcribed,
+ primary-source-verified) makes the Annunciation exception CONDITIONAL on
+ the general RG 96 walk carrying the feast past Easter Sunday -- NOT
+ unconditional as the first transcription had it. The occupant here blocks
+ every day from [origin + 1] through the day after Easter (26 March - 6
+ April 2026 inclusive), so the GENERAL target itself would land at 7
+ April -- after Easter (5 April) -- which is exactly the trigger
+ condition, not merely "the Annunciation is impeded at all". *)
let test_transfer_target_annunciation_starts_at_monday_after_low_sunday () =
let origin = mk 2026 3 25 in
- let occupant = occupant_blocking_on [] in
+ let easter_2026 = Comp.gregorian_easter 2026 in
+ let blocked_through_day_after_easter =
+ let rec range a b = if D.compare a b > 0 then [] else a :: range (D.add_days a 1) b in
+ range (D.add_days origin 1) (D.add_days easter_2026 1)
+ in
+ let occupant = occupant_blocking_on blocked_through_day_after_easter in
let c = cand ~origin:P.Sanctoral ~layer:PE.universal_layer PE.annunciation_slug in
let target = PE.transfer_target c origin occupant in
- let monday_after_low_sunday = D.add_days (Comp.gregorian_easter 2026) 8 in
- Alcotest.(check string) "lands on the Monday after Low Sunday (Easter + 8)"
+ let monday_after_low_sunday = D.add_days easter_2026 8 in
+ Alcotest.(check string) "lands on the Monday after Low Sunday (Easter + 8), the general \
+ walk having crossed Easter itself"
(D.to_iso8601 monday_after_low_sunday) (D.to_iso8601 target);
- Alcotest.(check bool) "NOT the general path's origin + 1 (discriminates the branch)" true
- (D.compare target (D.add_days origin 1) <> 0)
+ Alcotest.(check bool) "NOT the general target (2 days after Easter, discriminates the branch)"
+ true
+ (D.compare target (D.add_days easter_2026 2) <> 0)
(* RG 96's own qualifier on the exception -- "searching onward from there
only if that day is itself blocked" (rite.mli) -- is [search_from]'s
- ORDINARY behaviour, not a second mechanism: block the Monday after Low
- Sunday itself and confirm the search continues exactly one more day. *)
+ ORDINARY behaviour, not a second mechanism: same blocked run as above
+ (forcing the general target past Easter, so the exception fires), PLUS
+ the Monday after Low Sunday itself blocked, confirming the search
+ continues exactly one more day from there. *)
let test_transfer_target_annunciation_searches_onward_if_blocked () =
let origin = mk 2026 3 25 in
- let monday_after_low_sunday = D.add_days (Comp.gregorian_easter 2026) 8 in
- let occupant = occupant_blocking_on [ monday_after_low_sunday ] in
+ let easter_2026 = Comp.gregorian_easter 2026 in
+ let monday_after_low_sunday = D.add_days easter_2026 8 in
+ let blocked =
+ let rec range a b = if D.compare a b > 0 then [] else a :: range (D.add_days a 1) b in
+ range (D.add_days origin 1) (D.add_days easter_2026 1) @ [ monday_after_low_sunday ]
+ in
+ let occupant = occupant_blocking_on blocked in
let c = cand ~origin:P.Sanctoral ~layer:PE.universal_layer PE.annunciation_slug in
let target = PE.transfer_target c origin occupant in
Alcotest.(check string) "searches onward one more day when that Monday is itself blocked"
(D.to_iso8601 (D.add_days monday_after_low_sunday 1)) (D.to_iso8601 target)
+(* THE REGRESSION PIN (coordinator review): the bug an unconditional
+ exception produced. 25 March 2057 is Lent III Sunday (I class, RG 91
+ entry 6), impeding the Annunciation; 26 March 2057 is an ordinary Lent
+ feria (III class, well before Easter, 22 April 2057) -- the general RG
+ 96 target. The general target does NOT fall after Easter, so the
+ exception must NOT fire: the Annunciation lands on 26 March, not 13
+ April (Easter + 8), which is what the unconditional reading produced
+ (verified by reverting the fix and re-running this exact test -- see the
+ task report's mutation record). Uses the REAL [Temporal_ef.temporal] as
+ [occupant] (not a synthetic stand-in), the same coupling-safety
+ convention [of_temporal]'s callers use elsewhere in this file, so this
+ is also effectively an end-to-end check of the real 2057 calendar
+ shape, not just the search's own logic in isolation. *)
+let test_transfer_target_annunciation_not_overridden_when_general_target_precedes_easter () =
+ let origin = mk 2057 3 25 in
+ Alcotest.(check string) "25 March 2057 is a Sunday (Lent III)" "sunday"
+ (D.weekday_to_string (D.weekday origin));
+ let occupant d = (T.temporal d).Colitur_kernel.Temporal.office in
+ let c = cand ~origin:P.Sanctoral ~layer:PE.universal_layer PE.annunciation_slug in
+ let target = PE.transfer_target c origin occupant in
+ Alcotest.(check string) "lands on 26 March 2057 (the general RG96 target), NOT the \
+ Annunciation exception's Monday after Low Sunday"
+ "2057-03-26" (D.to_iso8601 target)
+
(* rite.mli's own obligations on [transfer_target] (Task 11 brief): the call
must TERMINATE and its result must be STRICTLY AFTER [origin], even for a
rite/data shape this function cannot have anticipated -- an occupant that
@@ -974,6 +1014,10 @@ let suite =
Alcotest.test_case
"transfer_target: Annunciation exception searches onward if that Monday is blocked" `Quick
test_transfer_target_annunciation_searches_onward_if_blocked;
+ Alcotest.test_case
+ "transfer_target: Annunciation NOT overridden when the general target precedes Easter \
+ (2057 regression)"
+ `Quick test_transfer_target_annunciation_not_overridden_when_general_target_precedes_easter;
Alcotest.test_case "transfer_target: terminates and stays forward under a pathological occupant"
`Quick test_transfer_target_terminates_under_pathological_occupant;
Alcotest.test_case "transfer_target: does not raise probing past the domain ceiling" `Quick