aboutsummaryrefslogtreecommitdiff
path: root/test/test_oracle.ml
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_oracle.ml')
-rw-r--r--test/test_oracle.ml121
1 files changed, 114 insertions, 7 deletions
diff --git a/test/test_oracle.ml b/test/test_oracle.ml
index ae82b31..df56bcc 100644
--- a/test/test_oracle.ml
+++ b/test/test_oracle.ml
@@ -1805,6 +1805,12 @@ let norm_citation s =
let m21_2038_slugs = m21_colour_slugs
let classify_2038 (c : colitur_row) (o : oracle_row) diffs =
+ let has_comm slug = List.exists (fun (s, _, _, _) -> s = slug) c.c_commemorations in
+ let has_comm_prefix p =
+ List.exists
+ (fun (s, _, _, _) -> String.length s >= String.length p && String.sub s 0 (String.length p) = p)
+ c.c_commemorations
+ in
let title = o.o_title in
let contains hay needle =
let nh = String.length needle and lh = String.length hay in
@@ -1839,12 +1845,106 @@ let classify_2038 (c : colitur_row) (o : oracle_row) diffs =
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)"
+ (* ---- commemoration classes (2026-08-18) ------------------------------
+ Every one below is an already-cited family from the 2026-2027
+ allow-list, re-appearing in a live window now that these compare
+ commemorations. None is a new adjudication; each points at the entry
+ that decided it. *)
+ else if has_comm "commemoration-of-the-seven-sorrows" || has_comm "major-litanies" then
+ (* Same entity, different English name form: colitur's own resolved name
+ ("The Seven Sorrows of the Blessed Virgin Mary", "The Major Litanies")
+ against missalemeum's ("For Our Lady of the Seven Sorrows", "Pro
+ rogationibus"). Both streams commemorate the SAME thing. Counted as a
+ divergence because the comparator matches on name text, which is the
+ only identity signal available -- not because the calendars differ. *)
+ Some "COMM-NAME-FORM (same entity, different English name)"
+ else if has_comm "commemoration-of-st-peter" then
+ Some "PETER-COMPANION-ABSENT-UPSTREAM (M19)"
+ else if has_comm "barbara" then Some "BARBARA-ABSENT-UPSTREAM (M22)"
+ else if has_comm_prefix "ef-advent-" then Some "ADVENT-FERIA-COMM (M10)"
+ else if
+ List.mem Comm_presence diffs && c.c_commemorations = []
+ && String.length c.c_observed_slug > 3
+ then Some "NATIVITY-OCTAVE-COMM-MISSING (M11, colitur's own gap, verdict open)"
+ (* THE ONE GENUINELY NEW FINDING of this extension, and it is not a naming
+ artefact. 2038-06-02: the Ascension Vigil (II class) admits ONE
+ commemoration under RG 111(c), and TWO candidates compete --
+ `rogation-wednesday` and `sts-marcellinus-peter-erasmus`. BOTH are
+ Commemoration_only, so neither has a row in RG 91's table and [band]
+ returns [unclassified] for both; RG 113's "servetur ordo tabellae
+ praecedentiae" has nothing to order by, and [admit] falls through to its
+ alphabetical slug tie-break, which picks the Rogation. missalemeum picks
+ the saints.
+
+ This is the tie-break CLAUDE.md records as "only ever exercised between
+ two Commemoration_only candidates, neither of which has any RG 91 table
+ position" -- and this is its FIRST live, externally-witnessed instance.
+ The decision is currently made by slug alphabet, which is arbitrary and
+ not rubrical.
+
+ There may be a real rule: RG 113's own FIRST sentence is "Commemoratio de
+ Tempore fit primo loco", and a Rogation is arguably de Tempore, which
+ would make colitur right for a reason it is not currently using. That
+ turns on whether the entry should carry subject Temporal rather than the
+ Saint it inherited from the Major Litanies precedent -- a question the
+ movable-date-specs spec explicitly deferred as "behaviourally inert",
+ which it no longer is. NOT decided here: recorded, cited, and left for a
+ dedicated task rather than settled by whichever answer happens to match
+ the oracle. *)
+ else if has_comm "rogation-wednesday" then
+ Some "COMM-ONLY-TIE-BREAK (alphabetical, arbitrary -- see register 6.16)"
+ (* The documented comparator LIMIT, not a calendar disagreement: colitur
+ resolves a commemoration's identity through its own English name, and a
+ TEMPORAL-origin commemoration (an impeded feria, an Ember or Rogation
+ day) has none -- Temporal_ef never sets one. M15 carries the same shape
+ in the 2026-2027 window. Counted and named rather than skipped, so the
+ population is visible instead of silently passing. *)
+ (* M8's own shape: a fixed I-class feast landing on an ordinary Sunday. RG
+ 109(a) makes "of a Sunday" always privileged and RG 111(a) admits exactly
+ one privileged commemoration on a I-class day, so the impeded Sunday IS
+ that one. missalemeum shows no commemoration at all. M8 names All Saints
+ and the Assumption in the 2026-2027 window; this is the same rule
+ reaching a different feast. *)
+ else if has_comm_prefix "ef-" && List.mem Comm_presence diffs && c.c_rank = 1 then
+ Some "IMPEDED-SUNDAY-COMM (M8's rule, RG 109(a)/111(a))"
+ else if List.mem Comm_identity_unresolved diffs then
+ Some "COMM-IDENTITY-UNRESOLVED (temporal-origin, no English name -- M15's limit)"
else None
+(* COMMEMORATIONS join the live-window comparison (2026-08-18).
+
+ Until now the live windows compared rank, colour and the two citations
+ only, and commemorations were checked against an oracle in the 2026-2027
+ fixture alone -- 227 days out of the ~968 000 commemoration-bearing days in
+ the domain, about 0.02%. That was by some distance the thinnest axis in the
+ project, and the reason is worth restating: layer 3 (16 801 days) compares
+ NO commemorations at all and never can, because lectio's own trailing
+ "+slug" tokens are its LOSING candidates rather than an RG 111 admitted
+ set, so the largest evidence source is structurally silent here. Roughly a
+ third of all days carry at least one commemoration, and `admit` is the most
+ intricate code in the engine, so the gap mattered.
+
+ Presence, count and IDENTITY are compared, reusing [identity_diff] --
+ the same function the 2026-2027 comparator uses, with the same limits: it
+ resolves a SANCTORAL-origin commemoration by colitur's own English name and
+ returns [Comm_identity_unresolved] for a temporal-origin one, which carries
+ no name to match. Unresolved is a counted, classified outcome, never a
+ silent pass.
+
+ OBSERVED-identity is deliberately still NOT compared in the live windows.
+ That axis is a separate, already-understood limit (M18's own shape: a
+ temporal-origin observed day has no English name either), and folding it in
+ here would flood these windows with a divergence class that says nothing
+ about commemorations. *)
let diffs_2038 (c : colitur_row) (o : oracle_row) =
let d = ref [] in
if c.c_rank <> o.o_rank then d := Rank :: !d;
if not (List.mem c.c_colour o.o_colours) then d := Colour_f :: !d;
+ let c_has = c.c_commemorations <> [] and o_has = o.o_commemorations <> [] in
+ if c_has <> o_has then d := Comm_presence :: !d;
+ if c_has && o_has && List.length c.c_commemorations <> List.length o.o_commemorations then
+ d := Comm_count :: !d;
+ (match identity_diff c o with Some f -> d := f :: !d | None -> ());
(match (c.c_first, o.o_first) with
| Some a, Some b when norm_citation a <> norm_citation b -> d := First_mismatch :: !d
| _ -> ());
@@ -1923,13 +2023,20 @@ let test_2038_class_counts () =
those 13 days no longer differ at all. An id with no rows must not be
declared -- the count assertion rejects an unused one, which is what
keeps this list honest. *)
- [ ("CHRIST-THE-KING-WEEK (M26 shape 2b)", 2);
- ("CHRISTMAS-MULTI-MASS (M27)", 1);
- ("COLOUR (M21)", 7);
- ("JUDITH (M28, register 6.6)", 2);
- ("MISSAL-TWO-PART-CITATION (register 6.9)", 1);
- ("PERPETUA-COMMON (register 6.8)", 1)
- ]
+ [ ("ADVENT-FERIA-COMM (M10)", 10);
+ ("BARBARA-ABSENT-UPSTREAM (M22)", 1);
+ ("CHRIST-THE-KING-WEEK (M26 shape 2b)", 2);
+ ("CHRISTMAS-MULTI-MASS (M27)", 1);
+ ("COLOUR (M21)", 7);
+ ("COMM-NAME-FORM (same entity, different English name)", 2);
+ ("COMM-ONLY-TIE-BREAK (alphabetical, arbitrary -- see register 6.16)", 1);
+ ("JOSEPH-ANNUNCIATION-TRANSFER-ORDER (C14's own collision)", 2);
+ ("JUDITH (M28, register 6.6)", 2);
+ ("MISSAL-TWO-PART-CITATION (register 6.9)", 1);
+ ("NATIVITY-OCTAVE-COMM-MISSING (M11, colitur's own gap, verdict open)", 2);
+ ("PERPETUA-COMMON (register 6.8)", 1);
+ ("PETER-COMPANION-ABSENT-UPSTREAM (M19)", 1)
+ ]
|> List.sort compare
in
Alcotest.(check (list (pair string int))) "2038 divergence classes and their counts" expected actual