aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/test_oracle.ml146
-rw-r--r--test/test_precedence_ef.ml21
-rw-r--r--test/test_rite_ef.ml44
3 files changed, 156 insertions, 55 deletions
diff --git a/test/test_oracle.ml b/test/test_oracle.ml
index 07a5634..2624664 100644
--- a/test/test_oracle.ml
+++ b/test/test_oracle.ml
@@ -107,18 +107,25 @@
entries; RG 110's inseparable-Peter/Paul commemoration is unimplemented
code, a real feature this task did not build) -- honestly verdicted
[missalemeum] (colitur is short a feature or a row, not right), never
- silently absorbed as if colitur were correct. THREE entries (M11, M13
- and, since Task B/ef-rg16a, M17) are [verdict open] -- CORRECTED, final
- fix wave, item 7: this comment previously said "one entry (M13)",
- missing M11, whose own verdict was changed from [colitur] to [open] in
- fix round 1 (see M11's own entry below for why) but this summary was
- never updated to match; M17 (the RG 113 same-band tie-break residual,
- register §6.1) is a genuinely NEW open item, not a stale-comment fix.
- ONE entry (M15) carries its own fourth verdict, [unresolvable] -- not a
- rubric dispute or a data gap either engine is wrong about, but a LIMIT
- of this comparator itself (see M15's own entry). All are adjudicated as
- unresolved/unresolvable, not resolved either way -- the brief's own
- explicit permission ("say so as an open item") used for real, not
+ silently absorbed as if colitur were correct. TWO entries (M11 and M13)
+ are [verdict open] -- CORRECTED, final fix wave, item 7: this comment
+ previously said "one entry (M13)", missing M11, whose own verdict was
+ changed from [colitur] to [open] in fix round 1 (see M11's own entry
+ below for why) but this summary was never updated to match. Task B
+ (branch ef-rg16a) briefly added a THIRD, M17 (the same-band tie-break
+ between Maurice and Thomas of Villanova, 22 September) -- CORRECTED,
+ fix round 1 of that same task: M17 was itself wrong. The "tie" was
+ manufactured by {!Precedence_ef.band} lending a [Commemoration_only]
+ candidate the same table entry as a genuine [Feast] of its own rank
+ (RG 91's table has no row for a bare commemoration at all -- fixed in
+ [band] itself, not here); once fixed, 22 September resolves cleanly on
+ both sides and M17 was deleted, not merely re-adjudicated. ONE entry
+ (M15) carries its own fourth verdict, [unresolvable] -- not a rubric
+ dispute or a data gap either engine is wrong about, but a LIMIT of this
+ comparator itself (see M15's own entry). All remaining OPEN entries are
+ adjudicated as unresolved/unresolvable, not resolved either way -- the
+ brief's own explicit permission ("say so as an open item") used for real,
+ not
defaulted past. See the task report for every entry's full
reasoning and primary-source citation. *)
@@ -655,30 +662,43 @@ let m15_dates =
already tracks). Only 2026 shows here -- 2027's Friday of Passion Week
IS 19 March, M13's own date, where the identity axis cannot even be
reached (M13's own rank/colour mismatch already excludes that day from
- count-matched identity comparison). *)
+ count-matched identity comparison).
+
+ NOTE for whoever builds the office (fix round 1, coordinator finding 7):
+ 27 March 2026 is a III-class day, where RG 111(d) admits TWO
+ commemorations, not one -- yet missalemeum admits only the Seven Sorrows
+ and DISPLACES John Damascene entirely (its own "displaced" list carries
+ his title that day), not merely drops him to second place. Implementing
+ the Seven Sorrows candidate naively (as one more ordinary III-class
+ commemoration competing for the day's two slots) will not reproduce
+ this: John Damascene would still win one of the two admitted slots by
+ dignity/band, giving colitur TWO commemorations where missalemeum shows
+ one. Whatever privilege or precedence the Seven Sorrows commemoration
+ carries must itself explain the exclusion, not just the admission --
+ register §6's own open item for this office should carry this caveat
+ forward. *)
let m16_dates = [ "2026-03-27" ]
-(* M17 -- Task B: the SAME-band residual tie-break (docs/research/rules-
- register.md §4's own "RG 113" entry, and §6.1's "RG 113 same-band
- residual" record) made visible for the first time by identity
- comparison. 22 September 2027 (September Ember Wednesday): colitur
- admits "St. Maurice and Companions, Martyrs" (Commemoration_only,
- Class3); missalemeum shows "St. Thomas of Villanova" (Feast, Class3)
- instead -- both land on {!Precedence_ef.band} entry 24 (III-class
- universal feasts), a genuine tie RG 113 gives no further instruction for
- (its own text only reaches "servetur ordo tabellae praecedentiae", the
- TABLE order; nothing in the primary text breaks a tie WITHIN one table
- entry). colitur's own residual tie-break (alphabetical by slug, §6.1's
- own uncited-convention record) picks "maurice..." over "thomas..." purely
- because 'm' < 't' -- no rubrical warrant either way, so this is NOT
- adjudicated colitur or missalemeum: verdict OPEN, the same explicit
- permission the brief gives M11/M13 ("say so as an open item rather than
- absorbing it"), register §6.1. Only 2027 falls in this window with this
- EXACT collision -- 2026's 22 September is an ordinary (non-Ember)
- Tuesday, so Thomas of Villanova simply wins the day outright on both
- sides (colitur: observed; missalemeum: title) with Maurice commemorated
- alongside him identically on both -- no tie to observe that year. *)
-let m17_dates = [ "2027-09-22" ]
+(* M17 was DELETED, fix round 1 (Task B): the "genuine tie" it adjudicated
+ as [open] was itself wrong. 22 September (any year the September Ember
+ Wednesday falls on the 22nd -- 2027 in this window): colitur used to
+ admit "St. Maurice and Companions, Martyrs" (Commemoration_only, Class3)
+ where missalemeum shows "St. Thomas of Villanova" (Feast, Class3) --
+ NOT because RG 113 runs out of instruction between two same-rank
+ candidates (the framing this entry used to carry), but because
+ {!Precedence_ef.band} used to lend a [Commemoration_only] candidate the
+ SAME table entry (24) as a genuine [Feast] of its own rank, manufacturing
+ a tie the primary text never creates: RG 91's own table enumerates only
+ "dies liturgici" (entry 24: "Festa III classis..." -- FEASTS), and the
+ calendarium's own 22 September row confirms it in its own notation --
+ "S. Thomae de Villanova Ep. et Conf., III classis. / Commemoratio Ss.
+ Mauritii et Soc. Mm." -- Thomas carries a class number, Maurice carries
+ none. Fixed at the source ([band] itself now returns [Precedence_ef
+ .unclassified] for any [Commemoration_only] candidate, docs/research/
+ rules-register.md §6.1's own corrected account) rather than here: 22
+ September now resolves identically on both sides with no allow-list
+ entry needed at all -- removed, not re-adjudicated to a different
+ verdict, since there is no longer a divergence to name. *)
let layer_m_reason (c : colitur_row) (o : oracle_row) diffs =
if diffs = [] then None
@@ -704,7 +724,6 @@ let layer_m_reason (c : colitur_row) (o : oracle_row) diffs =
Some "M13"
else if List.mem c.c_date m15_dates && diffs = [ Comm_identity_unresolved ] then Some "M15"
else if List.mem c.c_date m16_dates && diffs = [ Comm_identity_mismatch ] then Some "M16"
- else if List.mem c.c_date m17_dates && diffs = [ Comm_identity_mismatch ] then Some "M17"
else None
(* ---------------------------------------------------------------------- *)
@@ -862,23 +881,36 @@ let test_layer_m_counts_match_citations () =
combination would break it) that stays honest about what remains
unverified.
+ WHAT THIS DOES NOT PIN (coordinator finding 8, fix round 1, honestly
+ named rather than left implicit): the [oracle_rank = colitur_rank]
+ branch accepts ANY genuine agreement, including one this check cannot
+ independently verify is the CORRECT rank -- a regression that silently
+ flipped some entry's [rank] from [Class3] to [Class4] would land in the
+ agreement branch and pass cleanly if missalemeum's own id happened to
+ read 4 for that entry too (data drift on one side coinciding with data
+ drift on the other is not ruled out by this check, only coincidence
+ independent of any real cause is). This test pins "no third pattern
+ appears", not "every individual rank is correct" -- a narrower, still
+ genuinely useful claim (see the [> 50] population guard below, which
+ confirms the pinned shape is actually exercised at scale, not vacuously
+ true over an empty or trivial set), and this comment says so rather than
+ letting the assertion's own name imply more than it checks.
+
[Feast]-status candidates get the ORIGINAL, unrestricted check (any
- disagreement at all is unexpected) -- but this window's own data never
- actually offers a [Feast]-status LOSING candidate whose identity is
- independently clean (checked: 0 in the 2026-2027 fixture -- a [Feast]
- candidate here either wins its own day outright, in which case it is
- never a commemoration at all, or the one day where it does lose,
- 22 September 2027/Thomas of Villanova, is ITSELF the M17 tie-break
- mismatch and so is excluded by the [identity_diff = None] guard before
- ever reaching this check). Kept anyway, not deleted: a real disagreement
- would still be reported the moment one becomes reachable (a wider oracle
- window, or a data change), and this project's own vacuity catalogue
- flags "an assertion true by construction" as a defect shape to avoid,
- not "an assertion whose population happens to be empty in the one
- fixture available" -- the CHECK still does real work when its input is
- non-empty; it is the DATA, not the code, that is currently silent here. *)
+ disagreement at all is unexpected). CORRECTED (fix round 1, coordinator
+ finding 1): this comment previously claimed this branch was unreachable
+ in the 2026-2027 window (checked: 0) because its one candidate, Thomas
+ of Villanova on 22 September, was M17's own mismatch -- WRONG, once
+ traced further: M17 itself was wrong (see {!Precedence_ef.band}'s own
+ fidelity fix, register §6.1), and fixing it made 22 September resolve
+ cleanly on both sides, reachable here after all. Measured, not assumed:
+ 22 Feast-status commemorations are now examined by this branch (not
+ only Thomas of Villanova -- every OTHER genuinely clean Feast-status
+ match in the window reaches it too, which the earlier version of this
+ comment did not check for before asserting "0"). All 22 agree. *)
let test_identity_rank_corroboration () =
let oracle, colitur = compare_streams () in
+ let feast_checked = ref 0 in
let feast_mismatches = ref [] in
let commemoration_only_checked = ref 0 in
let commemoration_only_surprises = ref [] in
@@ -896,6 +928,7 @@ let test_identity_rank_corroboration () =
| Some oracle_rank -> (
match List.find_opt (fun (_, _, _, n) -> n = Some title) c.c_commemorations with
| Some (slug, colitur_rank, Cel.Feast, _) ->
+ incr feast_checked;
if colitur_rank <> oracle_rank then
feast_mismatches :=
Printf.sprintf "%s: %s oracle-id-rank=%d colitur-rank=%d" c.c_date slug
@@ -921,14 +954,21 @@ let test_identity_rank_corroboration () =
o.o_commemorations o.o_commemoration_ids)
oracle colitur;
Alcotest.(check (list string))
- "Feast-status matches: oracle id-rank agrees with colitur's own rank (none reachable in this window, \
- see this test's own comment; the check still runs)"
- [] (List.rev !feast_mismatches);
+ "Feast-status matches: oracle id-rank agrees with colitur's own rank" [] (List.rev !feast_mismatches);
Alcotest.(check (list string))
"Commemoration_only-status matches: every one fits the KNOWN oracle=4/colitur=3 convention gap -- any \
other combination would be a genuine, new surprise"
[] (List.rev !commemoration_only_surprises);
- (* Vacuity guard for the branch that IS populated in this window. *)
+ (* Vacuity guard: BOTH branches must actually run now. CORRECTED (fix
+ round 1, coordinator finding 1): the [Feast] branch used to be
+ unreachable in this window (checked: 0) because its one candidate,
+ Thomas of Villanova on 22 September, was M17's own mismatch (excluded
+ by the [identity_diff = None] guard above). Fixing {!Precedence_ef
+ .band}'s Commemoration_only fidelity (register §6.1) made that day
+ resolve cleanly, so it is reachable here too -- this guard now expects
+ at least 1, not merely documents the branch as dormant (measured: 22
+ Feast-status commemorations now examined, up from 0). *)
+ Alcotest.(check bool) "the Feast-status population actually examined is non-trivial" true (!feast_checked > 0);
Alcotest.(check bool) "the Commemoration_only population actually examined is non-trivial" true
(!commemoration_only_checked > 50)
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml
index ca623d2..2935e40 100644
--- a/test/test_precedence_ef.ml
+++ b/test/test_precedence_ef.ml
@@ -267,7 +267,26 @@ let cases =
(* RG 91's own vigil list (§4, "Vigils / octaves / Rogations / Sunday classes") stops at III class --
there is no IV-class vigil for entry 28's ferial catch-all to absorb. *)
( "unclassified: IV-class candidate marked as a vigil", mk 2026 6 20,
- cand ~rank:V.Class4 "ef-second-mystery-vigil", PE.unclassified )
+ cand ~rank:V.Class4 "ef-second-mystery-vigil", PE.unclassified );
+ (* Task B fix round 1 (coordinator finding 1): RG 91's table enumerates
+ only "dies liturgici" -- a [Cel.Commemoration_only] candidate is
+ never a row in the table at all, regardless of what rank it would
+ otherwise band to. [Class1]/universal-layer is deliberate: absent
+ the status guard this would band to entry 11, the FIRST, highest-
+ priority real entry any sanctoral candidate can reach, so this
+ proves the guard is checked BEFORE and overrides every rank-keyed
+ branch, not only the low-priority ones an accidental placement
+ might happen to catch. Real-world witness: "maurice-and-companions-
+ martyrs" (data/ef/sanctoral.sexp, 22 September, Class3,
+ Commemoration_only) used to band to entry 24 alongside the genuine
+ Class3 feast "thomas-of-villanova" -- test_oracle.ml's own former
+ M17 allow-list entry, deleted once this fix made the divergence it
+ named disappear. *)
+ ( "unclassified: a Commemoration_only candidate has no row in RG91's \
+ table at all, even at Class1", mk 2026 5 20,
+ cand ~origin:P.Sanctoral ~status:Cel.Commemoration_only ~layer:PE.universal_layer
+ "ef-some-commemoration-only",
+ PE.unclassified )
]
(* Review finding 1's end-to-end proof: on a real Sunday landing on 2
diff --git a/test/test_rite_ef.ml b/test/test_rite_ef.ml
index 6fcb25d..2e5d2fd 100644
--- a/test/test_rite_ef.ml
+++ b/test/test_rite_ef.ml
@@ -298,6 +298,45 @@ let test_search_genuinely_enters_the_window () =
"the walked span is at least 8 days -- the octave alone (Easter..Easter+7), not a one-day hop"
true (List.length walked >= 8)
+(* Task B fix round 1 (coordinator finding 1), end to end with REAL data:
+ 22 September 2027 is a September Ember Wednesday -- the OBSERVED day is
+ the Ember office itself ([band] entry 18, RG 91, beats both sanctoral
+ candidates), and RG 111(c) ("other II class: one") admits exactly ONE
+ of the two competing losers, "maurice-and-companions-martyrs"
+ ([Commemoration_only], Class3) and "thomas-of-villanova" ([Feast],
+ Class3). Before the fix, [Precedence_ef.band] gave Maurice the SAME
+ table entry (24) as Thomas, so the residual slug tie-break picked
+ Maurice ('m' < 't') and OMITTED Thomas -- contrary to the calendarium's
+ own 22 September row ("S. Thomae de Villanova Ep. et Conf., III
+ classis. / Commemoratio Ss. Mauritii et Soc. Mm.": Thomas is the one
+ WITH a table entry) and to missalemeum (test/test_oracle.ml's own
+ former M17 allow-list entry -- oracle "commemorations": Thomas,
+ "displaced": Maurice -- deleted once this fix made the divergence
+ disappear). This is the SAME date the oracle harness itself checks,
+ pinned here too so a regression shows up at the OCaml level, not only
+ via the fixture comparison. *)
+let test_maurice_thomas_band_fidelity_end_to_end () =
+ let layer = real_layer () in
+ (* [Calendar.year]'s own liturgical-year convention (Advent-anchored, see
+ calendar.ml's [year_bounds]): the label [y] spans December of civil
+ year [y] through November of civil year [y+1], so 22 September 2027
+ is reached via [Cal.year ... 2026], not 2027 -- the same
+ [y-1]/straddling indexing test_oracle.ml's own header comment already
+ documents for exactly this reason. *)
+ let days = Cal.year Rite_ef.context layer 2026 in
+ let day = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date (mk 2027 9 22) = 0) in
+ Alcotest.(check string) "the September Ember Wednesday office itself is observed (band entry 18)"
+ "ef-september-ember-wed" (slug_of day.LD.observed);
+ Alcotest.(check (list string))
+ "St Thomas of Villanova (band entry 24, a real table row) is the sole commemoration"
+ [ "thomas-of-villanova" ]
+ (List.map (fun (c, _) -> slug_of c) day.LD.commemorations);
+ Alcotest.(check (list string))
+ "St Maurice and Companions (Commemoration_only, unclassified -- no table row) is omitted, not \
+ commemorated"
+ [ "maurice-and-companions-martyrs" ]
+ (List.map (fun (c, _) -> slug_of c) day.LD.omitted)
+
let suite =
( "Rite_ef (real data: overlay-in-effect, domain-ceiling)",
[ Alcotest.test_case "the overlay suppression is observably in effect" `Quick
@@ -311,4 +350,7 @@ let suite =
Alcotest.test_case "transfer_target skips the whole Easter octave from inside Holy Week" `Quick
test_transfer_target_skips_the_whole_easter_octave;
Alcotest.test_case "the search genuinely enters the window (not vacuous)" `Quick
- test_search_genuinely_enters_the_window ] )
+ test_search_genuinely_enters_the_window;
+ Alcotest.test_case
+ "band fidelity end-to-end: Thomas of Villanova observed, Maurice commemorated (2027-09-22)" `Quick
+ test_maurice_thomas_band_fidelity_end_to_end ] )