diff options
Diffstat (limited to 'test/test_oracle.ml')
| -rw-r--r-- | test/test_oracle.ml | 122 |
1 files changed, 119 insertions, 3 deletions
diff --git a/test/test_oracle.ml b/test/test_oracle.ml index e90a45a..ae82b31 100644 --- a/test/test_oracle.ml +++ b/test/test_oracle.ml @@ -1819,6 +1819,26 @@ let classify_2038 (c : colitur_row) (o : oracle_row) diffs = else if c.c_observed_slug = "sts-felicitas-perpetua" then Some "PERPETUA-COMMON (register 6.8)" else if c.c_observed_slug = "ef-nativity" then Some "CHRISTMAS-MULTI-MASS (M27)" else if first_is "Dan 14:27, 28-42" then Some "MISSAL-TWO-PART-CITATION (register 6.9)" + (* Both saints transferred out of Holy Week, landing on adjacent days in the + opposite ORDER from missalemeum's: colitur puts the Annunciation first, + the oracle puts Joseph first. Neither stream loses a feast -- the pair is + the same, the sequence differs. This is the collision C14 already cites on + the differential side (Joseph's own transfer against the Annunciation's + fixed "sedes propria", RG 96); it simply had no oracle window until a + live-capture year happened to contain it. Recognised by the pair of + slugs, not by date, so a year where only ONE of them moved would not + match and would surface as unexplained. *) + else if + c.c_observed_slug = "annunciation-of-the-blessed-virgin-mary" + || c.c_observed_slug = "joseph-spouse-of-the-bl-virgin-mary" + then Some "JOSEPH-ANNUNCIATION-TRANSFER-ORDER (C14's own collision)" + (* RG 69's Sunday within the Octave of the Nativity against colitur's own + numbered octave-day slug -- C6's family on the differential side, and the + same disagreement about which office a late-December Sunday carries. *) + else if + String.length c.c_observed_slug > 19 + && String.sub c.c_observed_slug 0 19 = "ef-nativity-octave-" + then Some "NATIVITY-OCTAVE-SUNDAY (C6's family, RG 69)" else None let diffs_2038 (c : colitur_row) (o : oracle_row) = @@ -1833,9 +1853,13 @@ let diffs_2038 (c : colitur_row) (o : oracle_row) = | _ -> ()); List.rev !d -let compare_2038 () = - let oracle = List.map oracle_row_of_line (read_lines fixture_2038_path) in - let colitur = colitur_rows ~from_year:2038 ~to_year:2038 in +(* Parameterised over (fixture, year) so a second live-capture year costs a + fixture and a count list, not a copy of the comparator. 2035 was added for + `isidore-of-seville`, the last Common-routed saint reachable in any year the + differential covers. *) +let compare_live ~fixture ~year = + let oracle = List.map oracle_row_of_line (read_lines fixture) in + let colitur = colitur_rows ~from_year:year ~to_year:year in let by_date = Hashtbl.create 400 in List.iter (fun (c : colitur_row) -> Hashtbl.replace by_date c.c_date c) colitur; List.filter_map @@ -1847,6 +1871,8 @@ let compare_2038 () = if diffs = [] then None else Some (o.o_date, diffs, classify_2038 c o diffs, c, o)) oracle +let compare_2038 () = compare_live ~fixture:fixture_2038_path ~year:2038 + let test_2038_fixture_checksum () = Alcotest.(check string) "2038 fixture SHA-256 matches its provenance note" fixture_2038_sha256 (sha256_of_file fixture_2038_path) @@ -1943,3 +1969,93 @@ let suite_2038 = Alcotest.test_case "the two Common-route days: Frances agrees, Perpetua is the known divergence" `Quick test_2038_common_route_days ] ) + +(* ====================================================================== *) +(* The 2035 oracle extension (2026-08-17) *) +(* ====================================================================== *) + +(* A THIRD comparison window, and the one that finally witnesses the last + reachable Common-routed saint. + + Register §6.7 recorded step 4 -- the Common route -- as having no external + witness at all. 2038 (§6.9) closed two of the five saints, + `sts-felicitas-perpetua` and `frances-rome`. `isidore-of-seville` is the + third and last that ANY year in 2005-2050 can witness: he is the observed + office on 2008-04-04, 2035-04-04 and 2046-04-04 and nowhere else, and he is + the only saint routing through `common-of-doctors`, which no other fixture + touches. The remaining two, `gregory-the-great` and `patrick`, are never + the observed office in any of those 46 years -- both sit in March and are + impeded by Lent's privileged ferias every year -- so no fixture in that + range can reach them by construction, and this is as far as the method + goes. + + Same provenance shape as 2038: a live capture, kept separate from the + snapshot-derived 2026-2027 fixture because the endpoint has drifted from + it. Same narrower comparison too (rank, colour, Epistle, Gospel), for the + same reason -- the commemoration and observed-identity axes are built round + a date-literal allow-list specific to 2026-2027. *) + +let fixture_2035_path = "fixtures/missalemeum-ef-2035.txt" +let fixture_2035_sha256 = "39b000827f45cb0c2c712625fd9514a8c0e71e8a94fc9f69df4a97d91f90014d" +let compare_2035 () = compare_live ~fixture:fixture_2035_path ~year:2035 + +let test_2035_fixture_checksum () = + Alcotest.(check string) "2035 fixture SHA-256 matches its provenance note" fixture_2035_sha256 + (sha256_of_file fixture_2035_path) + +let test_2035_dates_align () = + let oracle = List.map oracle_row_of_line (read_lines fixture_2035_path) in + let colitur = colitur_rows ~from_year:2035 ~to_year:2035 in + Alcotest.(check int) "oracle is 365 rows" 365 (List.length oracle); + Alcotest.(check (list string)) "dates align 1:1" + (List.map (fun (o : oracle_row) -> o.o_date) oracle) + (List.map (fun (c : colitur_row) -> c.c_date) colitur) + +(* The whole point of this fixture. `common-of-doctors` has never been + compared against any external source, in any window, until now: it is the + one Common that only `isidore-of-seville` routes through, and he is the + observed office on exactly three days in 2005-2050. The scan says + "2 Tim. 4, 1-8" / "Mt. 5, 13-19" (Commune Doctorum, scan1.txt:41878 ff); + colitur says the same; this asserts missalemeum does too. *) +let test_2035_isidore_common_of_doctors () = + match List.find_opt (fun (d, _, _, _, _) -> d = "2035-04-04") (compare_2035 ()) with + | None -> () + | Some (_, diffs, _, c, o) -> + Alcotest.failf + "2035-04-04 (isidore-of-seville, the ONLY common-of-doctors witness) should agree: %s -- colitur \ + first=%s gospel=%s, oracle title=%s first=%s gospel=%s" + (String.concat "," (List.map field_name diffs)) + (Option.value c.c_first ~default:"-") + (Option.value c.c_gospel ~default:"-") + o.o_title + (Option.value o.o_first ~default:"-") + (Option.value o.o_gospel ~default:"-") + +let test_2035_every_difference_is_classified () = + let unexplained = + compare_2035 () + |> List.filter_map (fun (date, diffs, cls, c, o) -> + match cls with + | Some _ -> None + | None -> + Some + (Printf.sprintf "%s: %s -- colitur=(%s rank=%d colour=%c first=%s) oracle=(%s rank=%d first=%s)" + date + (String.concat "," (List.map field_name diffs)) + c.c_observed_slug c.c_rank c.c_colour + (Option.value c.c_first ~default:"-") + o.o_title o.o_rank + (Option.value o.o_first ~default:"-"))) + in + Alcotest.(check (list string)) "every 2035 difference falls in a named, adjudicated class" [] unexplained + +let suite_2035 = + ( "oracle (missalemeum, EF, 2035 -- common-of-doctors)", + [ Alcotest.test_case "2035 fixture SHA-256 matches its provenance note" `Quick + test_2035_fixture_checksum; + Alcotest.test_case "streams are 365 rows each, dates aligned 1:1" `Quick test_2035_dates_align; + Alcotest.test_case "isidore-of-seville: the only common-of-doctors witness agrees" `Quick + test_2035_isidore_common_of_doctors; + Alcotest.test_case "every difference falls in a named, adjudicated class" `Quick + test_2035_every_difference_is_classified + ] ) |
