(* Coordinator review (Task 11 fix round): integration tests wiring [Rite_ef.context] together with the REAL data/ef/sanctoral.sexp + data/ef/adjustments.sexp through [Colitur_kernel.Calendar] -- the same pipeline `colitur day` uses, proven here at the OCaml level so these properties are pinned by the test suite, not merely observable in CLI output (which is exactly what finding 3 flagged: with the overlay's one directive replaced by [()], `colitur day 2026`'s stdout is byte-identical for all 365 days, since [ef-nativity-vigil] always outranks [vigil-of-christmas] regardless -- the suppression's only observable effect is on [Liturgical_day.omitted], which no cram assertion reads). *) module Cal = Colitur_kernel.Calendar module Layer = Colitur_kernel.Layer module Overlay = Colitur_kernel.Overlay module LD = Colitur_kernel.Liturgical_day module Slug = Colitur_kernel.Slug module Date = Colitur_kernel.Date module Date_spec = Colitur_kernel.Date_spec module Cel = Colitur_kernel.Celebration module Colour = Colitur_kernel.Colour module Subject = Colitur_kernel.Subject module P = Colitur_kernel.Precedence module Comp = Colitur_kernel.Computus module V = Rite_ef.Vocab_ef module T = Rite_ef.Temporal_ef module PE = Rite_ef.Precedence_ef (* Relative to this test's own build directory (_build/default/test/), same convention test_sanctoral_ef.ml uses -- test/dune declares both as deps of the (test ...) stanza. *) let sanctoral_path = "../data/ef/sanctoral.sexp" let adjustments_path = "../data/ef/adjustments.sexp" let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e let real_layer () = let layer = match Layer.load V.rank_of_sexp sanctoral_path with | Ok l -> l | Error e -> Alcotest.failf "%s: failed to load: %s" sanctoral_path e in let overlay = match Overlay.load V.rank_of_sexp adjustments_path with | Ok o -> o | Error e -> Alcotest.failf "%s: failed to load: %s" adjustments_path e in let layer, diagnostics = Overlay.apply layer overlay in Alcotest.(check (list string)) "the committed overlay applies cleanly, no diagnostics" [] (List.map Overlay.diagnostic_to_string diagnostics); layer (* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- caller-supplied, same as [real_layer] above. *) let real_lectionary () = match Colitur_kernel.Lectionary.load "../data/ef/lectionary.sexp" with | Ok l -> l | Error e -> Alcotest.failf "../data/ef/lectionary.sexp: failed to load: %s" e (* The Commons (data/ef/commons.sexp) travel the same caller-supplied seam as the lectionary above, and [~commons] is required rather than defaulted so that no caller can silently run with none -- nothing in layers 3-5 compares reading citations, so a rite quietly missing its Commons would be invisible. Loaded here even where this file asserts nothing about readings, so that the rite under test is the same one bin/main.ml assembles. *) let real_commons () = match Rite_ef.Lectionary_ef.Commons.load "../data/ef/commons.sexp" with | Ok c -> c | Error e -> Alcotest.failf "../data/ef/commons.sexp: failed to load: %s" e let context () = Rite_ef.context ~lectionary:(real_lectionary ()) ~commons:(real_commons ()) let slug_of (c : V.rank Cel.t) = Slug.to_string c.Cel.slug (* Finding 3: the suppression's ONLY observable effect is on 24 December's [omitted] (and, belt-and-braces, on every OTHER field of every OTHER day too -- vigil-of-christmas must not surface anywhere at all, since the overlay removes it from the layer before resolution ever runs, unlike an ordinary occurrence loss). Mutation-verified (see the task report): substituting `(directives ())` for the real overlay makes this test's first assertion fail (24 December's [omitted] gains "vigil-of-christmas"), and confirmed the whole cram suite (test/cli.t) still passes under that same mutation -- so this closes the "removing the deliverable breaks no test" gap the coordinator flagged, which cram alone structurally cannot. *) let test_vigil_of_christmas_suppressed () = let layer = real_layer () in let days = Cal.year (context ()) layer 2026 in let christmas_eve = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date (mk 2026 12 24) = 0) in Alcotest.(check string) "ef-nativity-vigil is still the observed day" "ef-nativity-vigil" (slug_of christmas_eve.LD.observed); Alcotest.(check (list string)) "24 December has nothing omitted -- vigil-of-christmas never enters the RG91 contest at all" [] (List.map (fun (c, _) -> slug_of c) christmas_eve.LD.omitted); let appears_anywhere = Array.to_list days |> List.exists (fun d -> let is_it c = slug_of c = "vigil-of-christmas" in is_it d.LD.observed || List.exists (fun (c, _) -> is_it c) d.LD.commemorations || List.exists (fun (c, _) -> is_it c) d.LD.omitted || (match d.LD.transferred_in with Some c -> is_it c | None -> false) || List.exists (fun (c, _) -> is_it c) d.LD.transferred_out) in Alcotest.(check bool) "vigil-of-christmas appears NOWHERE in the resolved year" false appears_anywhere (* RG16(a) task (register §6.0 + the sanctoral subject audit, this task's own report). data/ef/sanctoral.sexp is a faithful mirror of lectio, not hand-patched (see test_sanctoral_ef.ml's own "explicit class = lord" spot-check, which loads that file WITHOUT applying this overlay and still passes) -- the one live correction, `most-holy-name-of-mary` -> `Bvm`, lives entirely in data/ef/adjustments.sexp's [Edit] directive. CORRECTED, ef-rebootstrap (2026-08-12): this comment previously said the base file itself carries `subject Lord` for `most-holy-name-of-mary`, which this overlay then corrects. That stopped being true the moment lectio's own generator was fixed (source SHA-256 1b303ef2...): the regenerated INI dropped `class = lord` for that slug outright, so the base bootstrap now already produces `Saint`, and this overlay's [Edit] forces `Saint -> Bvm` instead of `Lord -> Bvm` -- same final answer, different starting point. See adjustments.sexp's own "ef-rebootstrap" note for why the directive is kept anyway (data precision + a regression guard against `class = lord` being reintroduced upstream). CORRECTED, fix round 1 (CRITICAL finding, item 1): this test used to also assert `purification-of-the-blessed-virgin-mary` was retagged `Bvm`. REVERTED -- the user has ruled: follow the oracle (register §6.0 has the full account, both the calendarium argument for `Bvm` and the oracle evidence against it). The Purification is `Lord` again, one of FIVE untouched entries this test now re-verifies, not four. This is the ONE test in the suite that observes the one live retag actually takes effect through the real pipeline -- without it, a typo in the directive's slug (silently a no-op diagnostic, not a hard failure -- Overlay.apply's own contract) or a reverted adjustments.sexp would leave every other test green ([test_sanctoral_ef.ml] deliberately never applies the overlay). Also re-verifies the five entries the audit confirmed correct and left untouched, so a future accidental retag of one of THEM would be caught here too, not just the one that changed. *) let test_rg16a_subject_retag_in_effect () = let layer = real_layer () in let subject_of slug = match Layer.find layer (Slug.of_string_exn slug) with | Some e -> e.Layer.cel.Cel.subject | None -> Alcotest.failf "slug %s not found in the overlaid layer" slug in Alcotest.(check bool) "Most Holy Name of Mary retagged Bvm (calendarium: \"Nominis Mariae\")" true (subject_of "most-holy-name-of-mary" = Subject.Bvm); Alcotest.(check bool) "Purification stays Lord (reverted, fix round 1 -- the user's ruling: follow the oracle)" true (subject_of "purification-of-the-blessed-virgin-mary" = Subject.Lord); Alcotest.(check bool) "Precious Blood stays Lord (calendarium: \"D. N. I. C.\") -- audit, not touched" true (subject_of "precious-blood-of-our-lord-jesus-christ" = Subject.Lord); Alcotest.(check bool) "Transfiguration stays Lord (calendarium: \"D. N. I. C.\") -- audit, not touched" true (subject_of "transfiguration-of-our-lord" = Subject.Lord); Alcotest.(check bool) "Exaltation of the Holy Cross stays Lord -- audit, not touched" true (subject_of "exaltation-of-the-holy-cross" = Subject.Lord); Alcotest.(check bool) "Dedication of the Archbasilica of the Most Holy Saviour stays Lord -- audit, not touched" true (subject_of "dedication-of-the-archbasilica-of-our-holy-savior" = Subject.Lord) (* ef-rebootstrap fix round 1, F3: eusebius-confessor (14 Aug, one of the 5 entries this re-bootstrap added) is bootstrapped `Red` -- a lectio data defect (both lectio's own ini and missalemeum's `:r` colour tag agree with each other, not with the Missal). `missale-romanum-1962.pdf`'s own proper for the day, verified directly against the scan (not transcribed from a reviewer's reading): "S. Eusebii Conf. / Commemoratio / Missa Iustus, ut in festo S. Pauli primi Eremitae..." -- a CONFESSOR ("Conf."), borrowing the SAME Mass 15 January's own Paul the First Hermit uses, a white Confessor's Common in both engines' own data. Fixed via adjustments.sexp's new Edit directive -- proven here through the real pipeline, the same discipline [test_rg16a_subject_retag_in_effect] above already applies to the subject retags, so a typo'd slug or a reverted directive fails a test rather than only silently changing unread data. *) let test_eusebius_confessor_colour_corrected () = let layer = real_layer () in match Layer.find layer (Slug.of_string_exn "eusebius-confessor") with | None -> Alcotest.fail "slug eusebius-confessor not found in the overlaid layer" | Some e -> Alcotest.(check bool) "Eusebius Confessor is White (RG's own Common of Confessors, via the borrowed Mass \"Iustus\"), not \ lectio's bootstrapped Red" true (e.Layer.cel.Cel.colour = Colour.White) (* ef-sanctoral-audit task (2026-08-14): a full extraction of the primary CALENDARIUM (both photographic scans, page images read directly, not their OCR text layers) against all 327 base entries plus the two prior overlay Adds -- data/ef/adjustments.sexp's own audit-block comment has the full method and RG 124 citation; docs/research/rules-register.md's audit rewrite and .superpowers/sdd/2026-08-12-colitur-rg16a/sanctoral- audit-report.md have the full findings. Same discipline as [test_eusebius_confessor_colour_corrected] above (its own predecessor, same root cause): a typo'd slug or a reverted directive fails THIS test, not only silently changes unread data. One table, all eleven [Feast] colour corrections plus the seven [Commemoration_only] ones (inert to [Precedence_ef.band]/[admit] today -- colour is read nowhere in either, confirmed by grep -- but real for `colitur day`'s own printed output the moment either entry is ever observed/commemorated). *) let test_audit_colours_corrected () = let layer = real_layer () in let check_colour slug expected reason = match Layer.find layer (Slug.of_string_exn slug) with | None -> Alcotest.failf "slug %s not found in the overlaid layer" slug | Some e -> Alcotest.(check bool) (Printf.sprintf "%s: %s" slug reason) true (e.Layer.cel.Cel.colour = expected) in (* RG 124(e): a Martyr's own martyrdom is red -- these SIX were White. Three more were set Red by the audit and REVERTED by the fix-round review: conversion-of-st-paul and chair-of-st-peter are NAMED, by name and by date, in RG 120(f)'s own enumeration of WHITE, fifteen lines above the RG 124(b) the audit cited; and `peter` (25 Jan) rested on "match the host day's Red", whose host is white by that same clause. They are asserted White below, where the reverted values belong. *) List.iter (fun (slug, reason) -> check_colour slug Colour.Red reason) [ ("prisca", "Virg. et Mart., 18 Jan -- RG124(e); lectio's own NAME dropped 'et Mart.', so its name is the defective field, not its colour"); ("vitus", "Ss. ... Mm., 15 Jun -- RG124(e)"); ("margaret", "Virg. et Mart., 20 Jul (comm) -- RG124(e)"); ("apollinaris", "Ep. et Mart., 23 Jul -- RG124(e)"); ("agapitus", "Mart., 18 Aug -- RG124(e)"); ("josaphat", "Ep. et Mart., 14 Nov -- RG124(e)") ]; (* RG 120(e), "Sanctorum non Martyrum" -- a POSITIVE rule, four lines above the one the audit misapplied. These were cited to "RG 117's five-colour residual", a negative inference from the red list's exhaustiveness; the outcome was right and the citation was weaker than the Missal's own. The first three are the fix-round reverts, white by RG 120(f)'s own naming rather than by (e). *) List.iter (fun (slug, reason) -> check_colour slug Colour.White reason) [ ("conversion-of-st-paul", "25 Jan -- NAMED white by RG120(f), 'Conversionis S. Pauli (25 ianuarii)'"); ("chair-of-st-peter", "22 Feb -- NAMED white by RG120(f), 'Cathedrae S. Petri (22 februarii)'"); ("peter", "25 Jan commemoration -- its host is white by RG120(f), so the audit's own precedent yields White"); ("john-of-san-fecundo", "Conf. only, 12 Jun"); ("ephrem-of-syria", "Conf. et Eccl. Doct. only, 18 Jun"); ("julia-of-falconieri", "Virg. only, 19 Jun"); ("john-gualbert", "Abb. (Conf.) only, 12 Jul"); ("camillus-de-lellis", "Conf. only, 18 Jul"); ("jerome-emiliani", "Conf. only, 20 Jul"); ("liborii", "Ep. et Conf. only, 23 Jul (comm)"); ("martha", "Virg. only, 29 Jul"); ("alphonsus-liguori", "Ep., Conf. et Eccl. Doct. only, 2 Aug"); ("augustine", "Ep., Conf. et Eccl. Doct. only, 28 Aug"); ("rose-of-lima", "Virg. only, 30 Aug"); ("mark-i", "Papae et Conf. only (Pope Mark I, not the Evangelist), 7 Oct (comm)") ] (* `barbara` (4 December): a genuine DATA GAP, not a colour correction -- present in both photographic scans as a Commemoratio nested under Peter Chrysologus's own entry, absent from data/ef/sanctoral.sexp AND from lectio's own tridentine-calendar.ini (same upstream-gap shape as the ef-rebootstrap task's five: agnes-secundo/boniface-martyr/ eusebius-confessor/evaristus/theodore). Added via data/ef/ adjustments.sexp's own new `Add` directive. *) let test_barbara_added () = let layer = real_layer () in match Layer.find layer (Slug.of_string_exn "barbara") with | None -> Alcotest.fail "slug barbara not found in the overlaid layer (ef-sanctoral-audit Add missing?)" | Some e -> Alcotest.(check bool) "barbara: 4 December" true (match e.Layer.date with Date_spec.Fixed { month = 12; day = 4 } -> true | Date_spec.Fixed _ -> false); Alcotest.(check bool) "barbara: Commemoration_only (no separate feast row in the calendarium, a bare \ \"Commemoratio\" nested under Peter Chrysologus)" true (e.Layer.cel.Cel.status = Cel.Commemoration_only); Alcotest.(check bool) "barbara: Class3 (the same bootstrap-inference convention every OTHER \ Commemoration_only entry in this file uses -- the calendarium gives no numeral \ for any commemoration, hers included)" true (e.Layer.cel.Cel.rank = V.Class3); Alcotest.(check bool) "barbara: Red (RG124(e), \"Virg. et Mart.\")" true (e.Layer.cel.Cel.colour = Colour.Red); Alcotest.(check bool) "barbara: appears as a commemoration on 4 December in real output" true (let days = Cal.year (context ()) layer 2026 in Array.to_list days |> List.exists (fun d -> Date.compare d.LD.date (mk 2026 12 4) = 0 && List.exists (fun (c, _) -> slug_of c = "barbara") d.LD.commemorations)) (* Coordinator review, finding 2, reproduced through the project's OWN extension path (an overlay), the same way the reviewer found it: adding an I-class feast on 25 December (competing against, and losing to, the real Nativity) forces an RG 96 search starting 26 December -- which, with the real sanctoral data (Stephen/John/the Innocents, all II class) plus the temporal cycle's own Nativity-octave-day entries (29-31 Dec, also II class), is blocking every single day through 31 December 9999. Before the domain-ceiling fix this raised (Computus: year 10000 out of range); confirmed by mutation-testing at the precedence_ef.ml unit level (see the task report) -- this is the same defect reproduced end to end, through Calendar, with real data, exactly as the review found it. *) let test_transfer_search_does_not_raise_at_domain_ceiling () = let layer = real_layer () in let impeding_entry : V.rank Layer.entry = { Layer.date = (match Date_spec.fixed ~month:12 ~day:25 with Ok d -> d | Error e -> failwith e); cel = Cel.make ~slug:(Slug.of_string_exn "test-domain-ceiling-impeder") ~rank:V.Class1 ~colour:Colour.White ~layer:Rite_ef.Precedence_ef.universal_layer () } in let overlay : V.rank Overlay.t = { Overlay.id = "test-domain-ceiling"; directives = [ Overlay.Add impeding_entry ] } in let layer, _diagnostics = Overlay.apply layer overlay in (* Must not raise -- the whole point of the fix. *) let days = Cal.year (context ()) layer 9999 in Alcotest.(check bool) "year 9999 resolves without raising, even with an impeded Christmas Day" true (Array.length days > 0); let impeder_placed_or_recorded = Array.to_list days |> List.exists (fun d -> slug_of d.LD.observed = "test-domain-ceiling-impeder" || List.exists (fun (c, _) -> slug_of c = "test-domain-ceiling-impeder") d.LD.omitted) in Alcotest.(check bool) "the impeding candidate is accounted for (observed somewhere, or omitted \ with a recorded reason) -- never silently dropped" true impeder_placed_or_recorded (* Task 11's re-review finding, closed here: [Precedence_ef.transfer_target]'s Annunciation/Easter condition (and, more generally, RG 96's whole "not I or II class" test) is correct only because [Temporal_ef] happens to make every day from Easter Sunday through Low Sunday (Easter+0..+7) blocking -- Easter itself and Low Sunday via [named], every day between via [privileged_feria]. Nothing in the type system enforces that; a future edit narrowing [privileged_feria]'s Easter-octave range would silently let the RG 96 walk land a translated feast inside the octave. This tests the CONSEQUENCE (no day in that window ever receives one), not the mechanism ([privileged_feria] itself), so it stays sensitive to any way that consequence could break, not only the one code path that currently protects it. *) let easter_offset (d : Date.t) = let easter = Comp.gregorian_easter (Date.year d) in Date.to_rata d - Date.to_rata easter let in_easter_octave d = let off = easter_offset d in off >= 0 && off <= 7 (* 2005-2050: the project's own differential-testing window (CLAUDE.md), reused here as a deterministic, non-trivial sample -- 46 liturgical years, each with several genuine transfers (All Souls onto a Sunday, impeded universal feasts, and so on; test_precedence_ef.ml's own 21k-day manual review already confirmed "all 7 slugs that ever transfer have a verified rubrical cause" over a similar span), so this is not a vacuous sweep over years where nothing ever transfers. *) let sample_years = let rec range a b = if a > b then [] else a :: range (a + 1) b in range 2005 2050 (* ef-major-litanies task: [major-litanies] (RG 80, Precedence_ef's own [major_litanies_slug]) is a DELIBERATE, cited exception to this property, not a bug to catch -- RG 80's own text sends the Major Litanies to "the following Tuesday" unconditionally in a transfer year, which is always Easter+2, squarely inside [Easter, Easter+7] (precedence_ef.ml's own [transfer_target] Litanies branch has the full citation and the "why not the general RG96 search" argument). Every OTHER slug this rite ever transfers is still held to the original property below: RG 96's general walk (and its own named Annunciation exception) is what this property actually protects, and neither of those two mechanisms is exempted here -- only this one rite-cited, hand-verified exception, matched by NAME so a future regression in some OTHER slug cannot silently hide behind this exemption. *) let is_major_litanies_slug slug = String.equal slug PE.major_litanies_slug (* Property 1: no day in the resolved output, across the whole sample, is EVER a transfer's landing point inside [Easter, Easter+7] -- checked two ways. [transferred_out]'s own recorded target is what [transfer_target] itself returned (calendar.ml's [assignment], stored verbatim), so this is the more direct signal; [transferred_in] is also checked, in case some future Calendar change ever let the two disagree. *) let test_no_transfer_lands_in_easter_octave () = let layer = real_layer () in let violations = ref [] in List.iter (fun y -> let days = Cal.year (context ()) layer y in Array.iter (fun (d : (V.season, V.rank) LD.t) -> (match d.LD.transferred_in with | Some c when in_easter_octave d.LD.date && not (is_major_litanies_slug (slug_of c)) -> violations := (Printf.sprintf "%s transferred_in on %s (Easter+%d)" (slug_of c) (Date.to_iso8601 d.LD.date) (easter_offset d.LD.date)) :: !violations | _ -> ()); List.iter (fun (c, target) -> if in_easter_octave target && not (is_major_litanies_slug (slug_of c)) then violations := Printf.sprintf "%s transferred_out from %s to %s (Easter+%d)" (slug_of c) (Date.to_iso8601 d.LD.date) (Date.to_iso8601 target) (easter_offset target) :: !violations) d.LD.transferred_out) days) sample_years; Alcotest.(check (list string)) "no day (other than the RG80-cited Major Litanies exception) in [Easter, Easter+7] is ever a transfer's \ target, 2005-2050" [] (List.rev !violations) (* The positive contrast to the property above: RG 80's own exception DOES fire, exactly twice in the 2005-2050 sample (register: 194/8417 domain- wide trigger years; this task's own report has the full count), both landing on Easter+2 as RG 80's text requires -- proving the exemption above is not merely masking an absent case. 2011 (Easter Monday = 25 April, Easter = 24 April) and 2038 (Easter Sunday = 25 April) are this file's own worked examples (precedence_ef.ml's [major_litanies_slug] citation). *) let test_major_litanies_transfers_inside_easter_octave_exactly_when_rg80_requires () = let layer = real_layer () in let landings = ref [] in List.iter (fun y -> let days = Cal.year (context ()) layer y in Array.iter (fun (d : (V.season, V.rank) LD.t) -> List.iter (fun (c, target) -> if is_major_litanies_slug (slug_of c) then landings := (Date.to_iso8601 d.LD.date, Date.to_iso8601 target) :: !landings) d.LD.transferred_out) days) sample_years; Alcotest.(check (list (pair string string))) "RG80 fires exactly twice in 2005-2050, both landing on Easter+2" [ ("2011-04-25", "2011-04-26"); ("2038-04-25", "2038-04-27") ] (List.rev !landings) (* Property 2, and the LIVE case: [PE.transfer_target] called directly, with an origin that genuinely starts the search INSIDE Holy Week -- Holy Thursday 2026 (Easter - 3), a date no real sanctoral entry in data/ef/sanctoral.sexp occupies (Holy Week carries none), so this is deliberately constructed, not found. [occupant] is the REAL [Temporal_ef.temporal] (not a synthetic stand-in), so the search is driven by the actual blocking shape [privileged_feria] produces, not a hand-picked one -- this is genuinely live: search_from walks origin+1 (Good Friday, Easter-2) forward through every remaining day of Holy Week, all of Easter through Low Sunday (Easter+0..+7, all Class1), and only stops at Easter+8 (the Monday after Low Sunday), which [named] and [ferial_rank]/[privileged_feria] agree is Class4 -- confirmed below by checking the OCCUPANT's own rank there, not asserted blind. Without an origin inside the window itself, [search_from] could stop before ever reaching it and this test would prove nothing (the vacuity trap the task brief names explicitly) -- [test_search_genuinely_enters_the_window] pins that it does not stop early. *) let holy_week_origin_2026 = let easter_2026 = Comp.gregorian_easter 2026 in Date.add_days easter_2026 (-3) let real_occupant d = (T.temporal d).Colitur_kernel.Temporal.office let test_transfer_target_skips_the_whole_easter_octave () = let easter_2026 = Comp.gregorian_easter 2026 in let c = { P.cel = Cel.make ~slug:(Slug.of_string_exn "test-impeded-in-holy-week") ~rank:V.Class1 ~colour:Colour.White ~subject:Subject.Temporal ~layer:PE.universal_layer (); origin = P.Sanctoral } in let target = PE.transfer_target c holy_week_origin_2026 real_occupant in Alcotest.(check string) "lands on Easter + 8 (Monday after Low Sunday), past the entire octave" (Date.to_iso8601 (Date.add_days easter_2026 8)) (Date.to_iso8601 target); Alcotest.(check bool) "strictly after origin (rite.mli's own obligation)" true (Date.compare target holy_week_origin_2026 > 0); Alcotest.(check bool) "not inside [Easter, Easter+7]" false (let off = Date.to_rata target - Date.to_rata easter_2026 in off >= 0 && off <= 7) (* The vacuity check itself: proves the search genuinely walked THROUGH the window rather than [search_from] having some other reason to stop before it (e.g. an off-by-one that happened to also land past the octave). Reads the real occupant's own rank at every day from the origin through Easter+7 and requires every one of them to be blocking (Class1 or Class2) -- if any single one were not, [search_from] would have stopped there instead of at Easter+8, and the test above would be passing for the wrong reason. *) let test_search_genuinely_enters_the_window () = let easter_2026 = Comp.gregorian_easter 2026 in let rec days_from a b = if Date.compare a b > 0 then [] else a :: days_from (Date.add_days a 1) b in let walked = days_from (Date.add_days holy_week_origin_2026 1) (Date.add_days easter_2026 7) in Alcotest.(check bool) "every day from origin+1 through Easter+7 (the whole span search_from must cross) is blocking" true (List.for_all (fun d -> match (real_occupant d).Cel.rank with V.Class1 | V.Class2 -> true | V.Class3 | V.Class4 -> false) walked); Alcotest.(check bool) "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 (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 test_vigil_of_christmas_suppressed; Alcotest.test_case "RG16(a) task: the two subject retags are in effect, the other four untouched" `Quick test_rg16a_subject_retag_in_effect; Alcotest.test_case "ef-rebootstrap fix round 1 (F3): eusebius-confessor is White, not lectio's Red" `Quick test_eusebius_confessor_colour_corrected; Alcotest.test_case "ef-sanctoral-audit: 18 colour corrections in effect (RG120(e)/124(e)); 3 reverted per RG120(f)" `Quick test_audit_colours_corrected; Alcotest.test_case "ef-sanctoral-audit: barbara (4 Dec) added, a genuine data gap" `Quick test_barbara_added; Alcotest.test_case "RG96 search does not raise at the domain ceiling (real data)" `Quick test_transfer_search_does_not_raise_at_domain_ceiling; Alcotest.test_case "no transfer ever lands inside [Easter, Easter+7], 2005-2050" `Quick test_no_transfer_lands_in_easter_octave; Alcotest.test_case "RG80: the Major Litanies DO transfer inside the octave, exactly twice in 2005-2050" `Quick test_major_litanies_transfers_inside_easter_octave_exactly_when_rg80_requires; 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; Alcotest.test_case "band fidelity end-to-end: Ember Wednesday observed, Thomas of Villanova the sole \ commemoration, Maurice omitted (2027-09-22)" `Quick test_maurice_thomas_band_fidelity_end_to_end ] )