(* 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 | _ -> 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) (* The SHIPPED example overlay (data/ef/examples/diocesan-example.sexp) is documentation a user is invited to run and copy, and the man page names its installed path. An example that silently stopped loading -- a directive renamed, a Date_spec variant changed -- would be worse than shipping none, because it would fail only for whoever tried it. This asserts it PARSES against the REAL EF rank vocabulary and that it still demonstrates the range it claims to. It deliberately does NOT assert which days it produces: those celebrations are invented, and pinning their dates would make an illustrative file behave like calendar data. *) let test_shipped_example_overlay_loads () = let path = "../data/ef/examples/diocesan-example.sexp" in match Overlay.load Rite_ef.Vocab_ef.rank_of_sexp path with | Error e -> Alcotest.failf "the shipped example overlay must load: %s" e | Ok o -> Alcotest.(check string) "its id" "diocesan-example" o.Overlay.id; Alcotest.(check bool) "it demonstrates at least four directives" true (List.length o.Overlay.directives >= 4); (* Applying it must be clean: a directive naming a slug the universal calendar lacks would warn, and an EXAMPLE that warns teaches the wrong lesson. *) let layer = real_layer () in let _, diagnostics = Overlay.apply layer o in Alcotest.(check (list string)) "it applies with no diagnostics" [] (List.map Overlay.diagnostic_to_string diagnostics) let suite = ( "Rite_ef (real data: overlay-in-effect, domain-ceiling)", [ Alcotest.test_case "the shipped example overlay loads and applies cleanly" `Quick test_shipped_example_overlay_loads; 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 ] )