(* Task 17: golden pins -- validation layer 5 of the design spec's five (colitur CLAUDE.md "Validation" section; layers 1-4 -- Types, Property, the lectio differential, the missalemeum oracle -- are already built and green: test_validate.ml, test_differential.ml, test_oracle.ml). *** THE ONE RULE THIS FILE IS BUILT AROUND *** A golden test blesses whatever it is given. Every other layer derives its expectation from something independent of colitur's own output -- an invariant, a sibling engine, a published calendar oracle. This layer's expectation comes from ME, so every literal string below was checked by hand, against sources OUTSIDE this codebase, BEFORE it was typed in here -- never copied from a `colitur day` run and then rationalised: 1. The three Easter dates this file pins (1598-03-22, 1666-04-25, 2038-04-25) were independently computed by hand using the standard Gauss/Meeus Gregorian Easter algorithm (the same algorithm register §0's "first Sunday on/after the ecclesiastical full moon on/after 21 March" reduces to), NOT read off Computus.gregorian_easter or the CLI. All three matched colitur's own output exactly on the first attempt. 2. EVERY weekday asserted below was independently cross-checked against `date -d +%A` (GNU coreutils, glibc's own proleptic- Gregorian calendar arithmetic -- a completely separate implementation from anything in this repository) before being written into a literal string. All matched. 3. Every precedence/transfer/commemoration outcome was traced by hand against its RG citation (quoted or paraphrased in each test's own comment) and against the actual `band`/`disposition`/`admit`/ `transfer_target` logic in precedence_ef.ml, not merely observed to "look plausible". Where the primary text alone does not fully settle an outcome (the 2008 Annunciation/Joseph tie, see that test's own comment), this is said explicitly rather than papered over with a confident-sounding citation. *** WHY [describe] BUILDS ONE STRING PER DAY *** Every assertion below compares ONE formatted line against a single expected literal, rather than five or six separate field checks. This is deliberate: a one-line diff on failure still names the day (it is the first token) and shows exactly which field changed (Alcotest's own diff highlights the differing substring) -- "prove the pins have teeth" in the brief's own words -- while keeping each test's body to one literal per date instead of five, which is what makes 30-odd pinned dates reviewable at all. [describe]'s own five components (season/week/slug/rank/colour, commemorations, transferred_in, transferred_out) are exactly the fields {!Colitur_kernel.Liturgical_day.t} promises never to lose (that type's own doc comment) -- nothing is cherry-picked to make a case look cleaner than it is. *) 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 Cel = Colitur_kernel.Celebration module Colour = Colitur_kernel.Colour module Temporal = Colitur_kernel.Temporal module Prec = Colitur_kernel.Precedence module V = Rite_ef.Vocab_ef (* Same relative paths every other suite in this directory uses (dune test runs from _build/default/test/). *) let sanctoral_path = "../data/ef/sanctoral.sexp" let adjustments_path = "../data/ef/adjustments.sexp" (* Loaded once at module init, same convention test_validate.ml's own [real_ef_layer] uses (not test_oracle.ml/test_differential.ml's per-test-case reload, which recomputes a whole 46-year or 2-year sweep per call for reasons specific to those files -- loading the immutable layer itself has no such per-call cost and nothing here mutates it). *) let real_ef_layer = match Layer.load V.rank_of_sexp sanctoral_path with | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" sanctoral_path e) | Ok layer -> ( match Overlay.load V.rank_of_sexp adjustments_path with | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" adjustments_path e) | Ok overlay -> let layer, diagnostics = Overlay.apply layer overlay in if diagnostics <> [] then failwith (Printf.sprintf "unexpected overlay diagnostics: %s" (String.concat "; " (List.map Overlay.diagnostic_to_string diagnostics))); layer) let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e (* [Cal.day] recomputes its whole liturgical year on every call (calendar.mli -- "pure, no cache"); acceptable here, same as test_rite_ef.ml's own use of [Cal.year] for a handful of dates, not a hot loop. *) let fetch y m d = Cal.day Rite_ef.context real_ef_layer (mk y m d) let slug_s (c : V.rank Cel.t) = Slug.to_string c.Cel.slug let rank_s (c : V.rank Cel.t) = V.rank_to_string c.Cel.rank let colour_s (c : V.rank Cel.t) = Colour.to_string c.Cel.colour let privilege_s = function Prec.Privileged -> "privileged" | Prec.Ordinary -> "ordinary" (* The five fields {!Colitur_kernel.Liturgical_day.t} promises are never silently lost, folded into one comparable line -- see the file header for why one string, not five checks. *) let describe (day : (V.season, V.rank) LD.t) = let t = day.LD.temporal in let week = match t.Temporal.week with Some n -> string_of_int n | None -> "-" in let comms = List.map (fun (c, p) -> Printf.sprintf "%s:%s" (slug_s c) (privilege_s p)) day.LD.commemorations |> List.sort compare |> String.concat "," in let tin = match day.LD.transferred_in with None -> "-" | Some c -> slug_s c in let tout = List.map (fun (c, d) -> Printf.sprintf "%s->%s" (slug_s c) (Date.to_iso8601 d)) day.LD.transferred_out |> List.sort compare |> String.concat "," in Printf.sprintf "%s %s season=%s week=%s slug=%s rank=%s colour=%s comms=[%s] in=%s out=[%s]" (Date.to_iso8601 day.LD.date) (Date.weekday_to_string t.Temporal.weekday) (V.season_to_string t.Temporal.season) week (slug_s day.LD.observed) (rank_s day.LD.observed) (colour_s day.LD.observed) comms tin tout let check ~msg y m d expected = Alcotest.(check string) msg expected (describe (fetch y m d)) let omitted_has (day : (V.season, V.rank) LD.t) slug = List.exists (fun (c, _) -> slug_s c = slug) day.LD.omitted (* ------------------------------------------------------------------ *) (* Easter extremes (register §0; independently Gauss-computed, see file header point 1). extreme_years in test_validate.ml already pins that 1598/1666 are the true earliest/latest within 1583..2500 -- this file pins what the RESOLVED DAY looks like there, not merely that Easter lands on the expected date. *) (* ------------------------------------------------------------------ *) (* 1598: earliest Gregorian Easter possible, 22 March -- hand-verified via Gauss's algorithm (a = 2, b = 15, c = 98, ..., h = 0, l = 0, m = 0 -> 22 March), independent of Computus.gregorian_easter. RG 76 (Paschaltide begins with the Vigil Mass) + RG 91 entry 1 (Easter Sunday, I class) + RG 119(b) (white from the Vigil Mass) fix season/rank/colour; the day before (Holy Saturday, still Passiontide, violet per RG 128) is pinned alongside it to prove the season boundary itself falls in the right place, not merely that 22 March is white. *) let test_easter_extreme_1598 () = check ~msg:"1598-03-21 Holy Saturday: still Passiontide, violet (RG 128)" 1598 3 21 "1598-03-21 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet \ comms=[] in=- out=[]"; check ~msg:"1598-03-22 earliest possible Easter (Gauss-verified): Paschaltide begins, white, class-1" 1598 3 22 "1598-03-22 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white comms=[] in=- \ out=[]" (* 1666: latest Gregorian Easter possible, 25 April -- hand-verified via Gauss's algorithm (a = 13, b = 16, c = 66, ..., h = 29, l = 5, m = 0 -> 25 April). Same citations as 1598 above. *) let test_easter_extreme_1666 () = check ~msg:"1666-04-24 Holy Saturday: still Passiontide, violet (RG 128)" 1666 4 24 "1666-04-24 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet \ comms=[] in=- out=[]"; check ~msg:"1666-04-25 latest possible Easter (Gauss-verified): Paschaltide begins, white, class-1" 1666 4 25 "1666-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white comms=[] in=- \ out=[]" (* 2038: the brief's "late modern case" -- Easter also 25 April (Gauss- verified: a = 5, b = 20, c = 38, ..., h = 29, l = 5, m = 0 -> 25 April, the SAME extreme as 1666, reached independently), chosen because it is a near-term year rather than a 17th-century one. DELIBERATE EXCLUSION, not an oversight: register §6's F4 finding confirms 2038-03-06 (+sts-felicitas-perpetua), 2038-03-08 (+john-of-god) and 2038-03-09 (+frances-rome) are WRONG -- three sanctoral entries bootstrapped from lectio carry a Commemoration_only status that should be a real III-class Feast (a bootstrap-generator defect traced to missalemeum's own commemoration-id ranks, not fixable without touching lectio). Pinning those three dates as "correct" would freeze a known bug into a regression test that then fights its own fix -- the report's own warning against exactly this. Only the Easter-week days (temporal-cycle territory, untouched by the March sanctoral bug) are pinned here. *) let test_easter_extreme_2038_late_modern () = check ~msg:"2038-04-24 Holy Saturday: still Passiontide, violet (RG 128)" 2038 4 24 "2038-04-24 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet \ comms=[] in=- out=[]"; check ~msg:"2038-04-25 late-modern instance of the latest possible Easter (Gauss-verified)" 2038 4 25 "2038-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white comms=[] in=- \ out=[]" (* ------------------------------------------------------------------ *) (* Annunciation transfer, 25 March inside Holy Week (register §4, RG 96 Attamen (a)). 2016: Easter = 27 March (Gauss-independent of this file's own concern -- already covered by extreme-year hand-checks above; 2016 is an ordinary year, trusted via the property sweep + the weekday cross-check below), so 25 March 2016 falls on GOOD FRIDAY itself -- literally inside Holy Week, not merely "impeded by the Easter octave" the way the 1598/2008/1666 cases are. Independently confirmed: `date -d 2016-03-25 +%A` = Friday, `date -d 2016-03-27 +%A` = Sunday (Easter). *) let test_annunciation_transfer_inside_holy_week_2016 () = (* RG 91 entry 2 (Sacred Triduum) outranks entry 11 (an ordinary universal I-class feast) outright -- Good Friday wins, Annunciation is Transfer-disposed (RG 95: only I-class feasts transfer). RG 23 (I-class ferias admit no commemoration except one privileged) leaves nothing on the day itself: register's own note on RG 23 says this branch "never actually reaches a live case" for exactly this reason. *) check ~msg:"2016-03-25 Good Friday: Triduum outranks the Annunciation (RG91 entry 2 > 11), no commemoration" 2016 3 25 "2016-03-25 friday season=passiontide week=2 slug=ef-passiontide-2-friday rank=class-1 colour=violet \ comms=[] in=- out=[annunciation-of-the-blessed-virgin-mary->2016-04-04]"; (* The general RG 96 walk from 26 March would still be inside the Triduum, the Easter octave (all I class, entry 2/10) -- carrying the feast PAST Easter -- so RG 96 Attamen (a)'s condition fires: sedes propria = Monday after Low Sunday = Easter + 8 = 27 Mar + 8 = 4 April. *) check ~msg:"2016-04-04 Easter+8 (Monday after Low Sunday): RG96 Attamen(a) sedes propria fires" 2016 4 4 "2016-04-04 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ colour=white comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]" (* ------------------------------------------------------------------ *) (* 2008: the double transfer (register §4, "cases worth adding"). Easter = 23 March 2008 (trusted via the property sweep, not independently Gauss-checked a fourth time -- the algorithm is already hand-verified above; what this case adds is the TWO-CANDIDATE collision, not a fourth Easter-date check). Independently confirmed: `date -d 2008-03-19 +%A` = Wednesday, `-03-31` = Monday, `-04-01` = Tuesday. *) let test_annunciation_joseph_double_transfer_2008 () = (* St Joseph (19 March, I class, universal, band 11 -- same table entry as the Annunciation) falls on Wednesday of Holy Week (RG 91 entry 7: an I-class feria, band 7, above 11) -- impeded, Transfer-disposed. *) check ~msg:"2008-03-19 Wednesday of Holy Week outranks Joseph (RG91 entry 7 > 11), no commemoration" 2008 3 19 "2008-03-19 wednesday season=passiontide week=2 slug=ef-passiontide-2-wednesday rank=class-1 colour=violet \ comms=[] in=- out=[joseph-spouse-of-the-bl-virgin-mary->2008-04-01]"; (* Both Joseph and the Annunciation are band 11 (register: neither is Immaculate Conception/Assumption, band 4; both are ordinary universal I-class feasts). RG 96 Attamen (a) is CONDITIONAL, so it is evaluated for BOTH: the Annunciation's own general walk from 26 March would cross the Easter octave and land past Easter, so its exception fires -- Easter + 8 = 23 Mar + 8 = 31 March, its NAMED sedes propria. Joseph has no such named exception; its own general walk (from 20 March) also crosses the octave and, absent Annunciation, would land on the SAME 31 March. WEAKER-THAN-IT-LOOKS, stated honestly: RG 97-98's plain text ("the higher in the table is kept") does not itself distinguish two candidates tied at the SAME table entry (11) the way it does for a genuine dignity difference -- the engine breaks this specific tie by slug (calendar.ml's own [compare_deferred]: alphabetical, an engineering convention, not itself an RG citation), and "annunciation-of-the-blessed-virgin-mary" < "joseph-spouse-of-the-bl- virgin-mary" is exactly why Annunciation claims 31 March first in [place_transfers]'s per-round sort. A textual argument beyond the bare tie-break does exist -- the Annunciation's own Attamen clause names its target as a "sedes propria" (proper seat), a positively-assigned day, where Joseph is merely wherever the generic RG96 search happens to land -- but this is this report's own reading of the primary text, not something the register states outright, so it is offered as corroboration, not as the citation carrying the outcome. *) check ~msg:"2008-03-31 Easter+8: Annunciation's named sedes propria (RG96 Attamen a) claims the day first" 2008 3 31 "2008-03-31 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ colour=white comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]"; (* Joseph's own general RG96 walk, finding 31 March already claimed (occupant reports Class1 there, still blocking per [is_blocking]), continues one more day to 1 April -- RG 97-98's "in order": the second I-class candidate to reach an already-occupied target keeps searching rather than displacing the first. *) check ~msg:"2008-04-01: Joseph's RG96 walk continues past the now-claimed 31 March (RG97-98 'in order')" 2008 4 1 "2008-04-01 tuesday season=paschaltide week=2 slug=joseph-spouse-of-the-bl-virgin-mary rank=class-1 \ colour=white comms=[] in=joseph-spouse-of-the-bl-virgin-mary out=[]" (* ------------------------------------------------------------------ *) (* RG 96 Attamen (a) is CONDITIONAL ("quando est transferendum post Pascha") -- pinning both sides so the condition itself, not merely the exception's existence, is under test. All six weekdays independently confirmed via `date -d`. *) (* ------------------------------------------------------------------ *) (* Before Easter: 2057 (Easter 22 Apr), 2007 and 2012 (both Easter 8 Apr). In each, 25 March is a Sunday impeding the Annunciation (Lent III in 2057; Passion Sunday, Dominica I Passionis, in 2007/2012), and the GENERAL RG96 walk lands on 26 March -- an ordinary III-class feria, band 22/25, well before Easter -- so the condition never fires: no named exception, just the plain walk. Each arrival day's own displaced feria is RG25/RG109(e)-privileged (an ordinary Lent/Passiontide feria, impeded, must be commemorated) -- a second, independent confirmation of the RG24/25/RG109(e) mandate this file also pins directly below at 1900/1902, in a different code path (an ARRIVING transferred feast's displaced office, not a plain sanctoral winner's). *) let test_annunciation_exception_not_triggered_general_walk_suffices () = check ~msg:"2057-03-26: general RG96 walk suffices (target before Easter, 22 Apr) -- no Attamen(a) exception" 2057 3 26 "2057-03-26 monday season=lent week=3 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ colour=white comms=[ef-lent-3-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]"; check ~msg:"2007-03-26: general RG96 walk suffices (target before Easter, 8 Apr) -- no Attamen(a) exception" 2007 3 26 "2007-03-26 monday season=passiontide week=1 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ colour=white comms=[ef-passiontide-1-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]"; check ~msg:"2012-03-26: general RG96 walk suffices (target before Easter, 8 Apr) -- no Attamen(a) exception" 2012 3 26 "2012-03-26 monday season=passiontide week=1 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ colour=white comms=[ef-passiontide-1-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]" (* After Easter: 2016 (already pinned above, Easter+8 = 4 April) and 2024 (Easter 31 March, Easter+8 = 8 April). In both, the general walk would cross Easter, so Attamen(a) fires and lands the feast on the Monday after Low Sunday exactly. Both arrival days are Paschaltide-2 (Class4 ferias) -- RG26 (IV-class ferias never commemorated), so unlike the "before" trio above, no commemoration is left behind; a second, independent confirmation of RG26 in yet another code path. *) let test_annunciation_exception_triggered_when_walk_would_cross_easter () = check ~msg: "2024-04-08: Easter+8 (Easter=31 Mar): RG96 Attamen(a) fires, arrival day is Class4 so no commemoration \ (RG26)" 2024 4 8 "2024-04-08 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ colour=white comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]" (* 2016's own pin (test_annunciation_transfer_inside_holy_week_2016) is the other half of this pair -- not repeated here to avoid asserting the same date twice. *) (* ------------------------------------------------------------------ *) (* 2011-07-04, the Precious Blood (register §4/§6, differential C10). Sacred Heart (Easter + 68, RG 91 entry 3) coincides with the fixed 1 July feast of the Most Precious Blood (entry 11) in 2011 (Easter = 24 April, so Easter + 68 = 1 July). Entry 3 outranks entry 11 outright (RG 97-98: the higher in the table is kept) -- no tie, unlike the 2008 case above. The general RG96 walk from 2 July must skip Visitation (2 July, II class, entry 19) and the ordinary Sunday (3 July, entry 15, II class) before landing on 4 July, an ordinary Class4 feria. All four weekdays independently confirmed via `date -d`. *) let test_precious_blood_transfer_2011 () = check ~msg:"2011-07-01: Sacred Heart (RG91 entry 3) outranks the fixed Precious Blood (entry 11) outright" 2011 7 1 "2011-07-01 friday season=time-after-pentecost week=2 slug=ef-sacred-heart rank=class-1 colour=white \ comms=[] in=- out=[precious-blood-of-our-lord-jesus-christ->2011-07-04]"; check ~msg:"2011-07-02: the RG96 walk's first blocked day (Visitation, II class) -- Precious Blood not here" 2011 7 2 "2011-07-02 saturday season=time-after-pentecost week=2 slug=visitation-of-the-blessed-virgin-mary \ rank=class-2 colour=white comms=[processus-and-martinian:ordinary] in=- out=[]"; check ~msg:"2011-07-03: the RG96 walk's second blocked day (an ordinary Sunday, II class) -- still not here" 2011 7 3 "2011-07-03 sunday season=time-after-pentecost week=3 slug=ef-time-after-pentecost-sunday-3 rank=class-2 \ colour=green comms=[] in=- out=[]"; check ~msg:"2011-07-04: the first non-I/II-class day (RG96) -- the Precious Blood lands here" 2011 7 4 "2011-07-04 monday season=time-after-pentecost week=3 slug=precious-blood-of-our-lord-jesus-christ \ rank=class-1 colour=red comms=[] in=precious-blood-of-our-lord-jesus-christ out=[]" (* ------------------------------------------------------------------ *) (* All Souls falling on a Sunday (register §4, RG96 Attamen (b), primary- source-verified: "quando occurrit cum dominica, transfertur ... in feriam II sequentem"). 2025: All Souls (2 Nov) falls on an ordinary Sunday. Weekdays independently confirmed via `date -d`. *) let test_all_souls_on_a_sunday_2025 () = (* RG91 entry 8's own parenthetical ("yields to an occurring Sunday") plus Attamen (b): the Sunday is simply observed, and RG94 ("a fixed-day commemoration is not carried with a transferred feast") means the transferred All Souls leaves nothing behind on the Sunday itself. *) check ~msg:"2025-11-02: All Souls yields to the occurring Sunday (RG91 entry 8), no commemoration (RG94)" 2025 11 2 "2025-11-02 sunday season=time-after-pentecost week=21 slug=ef-time-after-pentecost-sunday-21 rank=class-2 \ colour=green comms=[] in=- out=[commemoration-of-all-souls->2025-11-03]"; check ~msg:"2025-11-03: All Souls transfers to the following Monday, its sedes propria (RG96 Attamen b)" 2025 11 3 "2025-11-03 monday season=time-after-pentecost week=21 slug=commemoration-of-all-souls rank=class-1 \ colour=black comms=[] in=commemoration-of-all-souls out=[]" (* ------------------------------------------------------------------ *) (* Christmas falling on a Sunday (RG91 entry 1: Nativity, Easter, Pentecost are I class WITH THEIR OWN OCTAVE, listed first in the whole table -- temporal_ef.ml assigns 25 December the Nativity office unconditionally, so unlike a Feast of the Lord merely REPLACING an occurring Sunday (RG 16(a), register §6), there is no Sunday-of-Advent/-per-annum candidate to even contest here: 25 December is never, in the EF temporal cycle, an "ordinary Sunday" in the first place. 2022: `date -d 2022-12-25 +%A` = Sunday, independently confirmed. *) let test_christmas_on_a_sunday_2022 () = check ~msg:"2022-12-25 falling on a Sunday: the Nativity (RG91 entry 1) takes the day outright, no contest" 2022 12 25 "2022-12-25 sunday season=christmastide week=- slug=ef-nativity rank=class-1 colour=white comms=[] in=- \ out=[]" (* ------------------------------------------------------------------ *) (* Holy Thursday is white (register §3b, RG 128(b) + RG 122, Task 16) while the surrounding Passiontide days stay violet -- the case the brief flags because colitur and lectio previously AGREED on violet (both wrong), so the lectio differential was structurally incapable of ever catching this; only a golden pin (or the missalemeum oracle, layer 4) can. 2026: Easter 5 April (trusted via the property sweep). Weekdays independently confirmed via `date -d`. *) let test_holy_thursday_is_white_2026 () = check ~msg:"2026-04-01 (Wednesday of Holy Week): ordinary Passiontide violet (RG128)" 2026 4 1 "2026-04-01 wednesday season=passiontide week=2 slug=ef-passiontide-2-wednesday rank=class-1 colour=violet \ comms=[] in=- out=[]"; check ~msg: "2026-04-02 Holy Thursday: white (RG128(b)'s named exception + RG122's affirmative statement), not \ Passiontide's violet" 2026 4 2 "2026-04-02 thursday season=passiontide week=2 slug=ef-passiontide-2-thursday rank=class-1 colour=white \ comms=[] in=- out=[]"; check ~msg:"2026-04-03 Good Friday: back to violet (RG128) -- Thursday's white is a one-day exception" 2026 4 3 "2026-04-03 friday season=passiontide week=2 slug=ef-passiontide-2-friday rank=class-1 colour=violet \ comms=[] in=- out=[]" (* ------------------------------------------------------------------ *) (* Advent/Lent Ember ferias are commemorated when impeded (RG 24: "si vero impediuntur, commemorari debent" -- MUST be commemorated) while IV-class ferias never are (RG 26: "quae nunquam commemorantur") -- the fix-round-1 F1/F2 finding, a Critical bug caught late precisely because no golden pin existed for it before now. Weekdays independently confirmed via `date -d`. *) (* ------------------------------------------------------------------ *) let test_ember_ferias_commemorated_when_impeded () = (* 1900-12-21: Advent Ember Friday (RG91 entry 18, II class) impeded by St Thomas (II class, universal, entry 16 -- Thomas outranks an II-class feria only because entry 16 < 18 in the table). RG24 makes the Ember feria's commemoration MANDATORY, and RG109(e) (corrected, fix round 1) makes it PRIVILEGED, not merely eligible. *) check ~msg:"1900-12-21: Advent Ember Friday, impeded, MUST be commemorated (RG24) and is privileged (RG109e)" 1900 12 21 "1900-12-21 friday season=advent week=3 slug=thomas rank=class-2 colour=red \ comms=[ef-advent-ember-fri:privileged] in=- out=[]"; (* 1902-02-22: Lent Ember Saturday (RG91 entry 18, II class) impeded by the Chair of St Peter (II class, universal, entry 16). Same RG24/ RG109(e) mandate. *) check ~msg:"1902-02-22: Lent Ember Saturday, impeded, MUST be commemorated (RG24) and is privileged (RG109e)" 1902 2 22 "1902-02-22 saturday season=lent week=1 slug=chair-of-st-peter rank=class-2 colour=white \ comms=[ef-lent-ember-sat:privileged] in=- out=[]" let test_iv_class_ferias_never_commemorated () = (* 2026-07-22: an ordinary Time-after-Pentecost feria (Class4, RG91 entry 28) impeded by St Mary Magdalene (III class). RG26 ("IV-class ferias are NEVER commemorated") means the displaced feria is dropped outright -- present in [omitted], never in [commemorations]. Asserting only "comms=[]" would be vacuous if there had been no losing candidate at all (one of this project's own catalogued vacuity flavours); the [omitted_has] check below proves a real candidate existed and was actively excluded, not merely absent. *) let d1 = fetch 2026 7 22 in Alcotest.(check bool) "2026-07-22: the displaced IV-class feria is in [omitted], proving RG26 actually fired" true (omitted_has d1 "ef-time-after-pentecost-8-wednesday"); Alcotest.(check string) "2026-07-22: Mary Magdalene observed, no commemoration (RG26)" "2026-07-22 wednesday season=time-after-pentecost week=8 slug=mary-magdalene rank=class-3 colour=white \ comms=[] in=- out=[]" (describe d1); let d2 = fetch 2026 8 10 in Alcotest.(check bool) "2026-08-10: the displaced IV-class feria is in [omitted], proving RG26 actually fired" true (omitted_has d2 "ef-time-after-pentecost-11-monday"); Alcotest.(check string) "2026-08-10: St Lawrence observed, no commemoration (RG26)" "2026-08-10 monday season=time-after-pentecost week=11 slug=lawrence rank=class-2 colour=red comms=[] in=- \ out=[]" (describe d2) (* ------------------------------------------------------------------ *) (* RG 111(b): a II-class SUNDAY admits only a commemoration "de festo II classis" -- a rank restriction, not "the best available ordinary candidate". 9 August falling on a Sunday: `romanus` (Class3, commemoration-only, register §6 notes his very existence is questionable, but that is irrelevant here -- RG111(b) excludes him on RANK alone, regardless of that open question) has no standing for the day's single slot. The brief names four recurring years (2009, 2015, 2020, 2026); two are pinned here (2009 for an older-era instance, 2026 for a current one) -- 2015/2020 were hand-checked via the CLI and found identical in shape (same slug family, same `romanus` exclusion, only the week number differs), so pinning all four would repeat the same assertion four times without exercising a different code path; rejected as duplicative. *) let test_ii_class_sunday_admits_only_ii_class_commemoration () = let d2009 = fetch 2009 8 9 in Alcotest.(check bool) "2009-08-09: romanus (Class3) is offered and excluded, proving RG111(b) actually fired" true (omitted_has d2009 "romanus"); Alcotest.(check string) "2009-08-09: the Sunday observed, no commemoration at all (RG111(b) rank floor)" "2009-08-09 sunday season=time-after-pentecost week=10 slug=ef-time-after-pentecost-sunday-10 rank=class-2 \ colour=green comms=[] in=- out=[]" (describe d2009); let d2026 = fetch 2026 8 9 in Alcotest.(check bool) "2026-08-09: romanus (Class3) is offered and excluded, proving RG111(b) actually fired" true (omitted_has d2026 "romanus"); Alcotest.(check string) "2026-08-09: the Sunday observed, no commemoration at all (RG111(b) rank floor)" "2026-08-09 sunday season=time-after-pentecost week=11 slug=ef-time-after-pentecost-sunday-11 rank=class-2 \ colour=green comms=[] in=- out=[]" (describe d2026) (* ------------------------------------------------------------------ *) (* RG 16(a) (register §6.0, Caput III "De Dominicis", primary text quoted in this task's own report): "festum Domini I aut II classis, in dominica II classis occurrens, ... de dominica, proinde, nulla fit commemoratio" -- a Feast of the Lord, I or II class, impeding a II-class Sunday, takes the Sunday's own place with no commemoration of the Sunday at all, unlike every OTHER impeded II-class Sunday (RG 109(a)/RG 111(b), the [test_ii_class_sunday_admits_only_ii_class_commemoration] pin above). Two real dates, both directions, exactly the pair the task brief asks for. *) (* ------------------------------------------------------------------ *) (* 2028-08-06: the Transfiguration (6 Aug, II class, calendarium "IN TRANSFIGURATIONE D. N. I. C.") falls on an ordinary Time-after-Pentecost Sunday. `date -d 2028-08-06 +%A` = Sunday, independently confirmed. Also the exact date register §6.0's own reproduction names as a live instance of the bug this pin closes -- before this fix `colitur day` admitted "+ef-time-after-pentecost-sunday-9" here. CORRECTED, fix round 1 (IMPORTANT finding, item 2): this pin used to expect Pope Sixtus II et al. (6 August's own unrelated Class3 saint, Commemoration_only) admitted as an ordinary commemoration in the freed slot -- promoted from `colitur day`'s own actual output without consulting either the primary text or the oracle first (the exact vacuous-pin flavour the review caught, and this file's own header warns against). Re-derived properly this time: - PRIMARY TEXT: RG 16(a) itself says the winning Feast of the Lord holds the Sunday's place cum omnibus iuribus et privilegiis -- WITH ALL the Sunday's own rights and privileges. RG 111(b) is one of those: in dominicis II classis, una tantum admittitur commemoratio, SCILICET DE FESTO II CLASSIS -- a Sunday's own slot is reserved for a Class2 candidate specifically. The day is still a dominica II classis for this purpose even though the Transfiguration, not the Sunday, is [observed] -- so Sixtus (Class3) has no standing for the slot at all, the same as on any other II-class Sunday (contrast [test_ii_class_sunday_admits_only_ii_class_commemoration] above, romanus, Class3, excluded the identical way on an ORDINARY II-class Sunday). - ORACLE, independently fetched (missalemeum's own /api/v5/calendar JSON, not the small-model webpage summary, which proved unreliable for this exact question): 2023-08-06 (a Sunday) -- title Transfiguration of Our Lord, commemorations empty, displaced Pope Sixtus II, Felicissimus and Agapitus, Martyrs -- Sixtus displaced, not commemorated. Control, 2026-08-06 (a Thursday, no Sunday collision) -- Sixtus present in commemorations. Being a Sunday is exactly what excludes him. Deliberately NOT a bare "comms=[]" pin (this project's own catalogued vacuity flavour: a count/emptiness assertion that would pass even if nothing had ever been offered): [omitted_has] first proves the Sunday really WAS a candidate that reached [disposition] and was actively excluded, not merely never constructed. *) let test_rg16a_lord_feast_no_sunday_commemoration_2028 () = let d = fetch 2028 8 6 in Alcotest.(check bool) "2028-08-06: the impeded Sunday is in [omitted], proving RG16(a) actively fired (not merely never offered)" true (omitted_has d "ef-time-after-pentecost-sunday-9"); Alcotest.(check string) "2028-08-06: the Transfiguration observed; NO commemoration at all -- RG16(a)'s own \"cum omnibus iuribus \ et privilegiis\" keeps the day a dominica II classis for RG111(b), so Sixtus (Class3) has no standing \ either, oracle-confirmed" "2028-08-06 sunday season=time-after-pentecost week=9 slug=transfiguration-of-our-lord rank=class-2 \ colour=white comms=[] in=- out=[]" (describe d) (* 2025-02-02, the other real instance of the SAME pattern -- not a contrasting second direction, per this task's fix round 1 (CRITICAL finding, item 1, reverted; see register §6.0 for the full account). The Purification (2 Feb, calendarium "IN PURIFICATIONE B. MARIAE VIRG.") stays [subject = Lord] -- the user has ruled: follow the oracle. missalemeum treats it exactly as RG 16(a)'s own "festum Domini" case (independently fetched, `/api/v5/calendar` JSON): 2020-02-02 (a Sunday) -- `{"title":"Purification of the Blessed Virgin Mary","tags":["IV Sunday after Epiphany"],...,"commemorations":[],"displaced":[]}` -- the Sunday named only as a TAG (the underlying temporal placement), not commemorated at all; matches 2014-02-02 exactly. Contrast, the SAME oracle on an ordinary Marian feast (2019-09-08, the Nativity of the BVM): `{"title":"XIII Sunday after Pentecost",...,"commemorations": [{"title":"Nativity of the Blessed Virgin Mary",...}],...}` -- the Sunday observed, the feast merely commemorated, the OPPOSITE pattern -- proving missalemeum does not treat every Marian feast this way, only the Purification specifically. `date -d 2025-02-02 +%A` = Sunday, independently confirmed. Unlike 2028-08-06, no second sanctoral candidate exists on 2 February (data/ef/sanctoral.sexp has exactly one 2 February entry) -- a genuinely clean "comms=[]" case, complementing the pair above rather than duplicating it. *) let test_purification_on_a_sunday_2025 () = check ~msg: "2025-02-02: the Purification observed (subject Lord, unretagged -- the oracle's own treatment); NO \ commemoration of the Sunday, oracle-confirmed" 2025 2 2 "2025-02-02 sunday season=time-after-epiphany week=4 slug=purification-of-the-blessed-virgin-mary \ rank=class-2 colour=white comms=[] in=- out=[]" let suite = ( "golden pins (known-tricky years)", [ Alcotest.test_case "Easter extreme: 1598 earliest (22 Mar, Gauss-verified)" `Quick test_easter_extreme_1598; Alcotest.test_case "Easter extreme: 1666 latest (25 Apr, Gauss-verified)" `Quick test_easter_extreme_1666; Alcotest.test_case "Easter extreme: 2038 late-modern instance of the latest date" `Quick test_easter_extreme_2038_late_modern; Alcotest.test_case "Annunciation transfer: 25 March falls inside Holy Week (2016)" `Quick test_annunciation_transfer_inside_holy_week_2016; Alcotest.test_case "Annunciation/Joseph double transfer (2008)" `Quick test_annunciation_joseph_double_transfer_2008; Alcotest.test_case "Annunciation RG96 exception NOT triggered: general walk suffices (2057, 2007, 2012)" `Quick test_annunciation_exception_not_triggered_general_walk_suffices; Alcotest.test_case "Annunciation RG96 exception triggered: walk would cross Easter (2024; see also 2016)" `Quick test_annunciation_exception_triggered_when_walk_would_cross_easter; Alcotest.test_case "2011-07-04: the Precious Blood transfer" `Quick test_precious_blood_transfer_2011; Alcotest.test_case "All Souls falling on a Sunday (2025)" `Quick test_all_souls_on_a_sunday_2025; Alcotest.test_case "Christmas falling on a Sunday (2022)" `Quick test_christmas_on_a_sunday_2022; Alcotest.test_case "Holy Thursday is white amid violet Passiontide (2026)" `Quick test_holy_thursday_is_white_2026; Alcotest.test_case "Advent/Lent Ember ferias commemorated when impeded (1900, 1902)" `Quick test_ember_ferias_commemorated_when_impeded; Alcotest.test_case "IV-class ferias never commemorated (2026)" `Quick test_iv_class_ferias_never_commemorated; Alcotest.test_case "RG111(b): a II-class Sunday admits only a de-festo-II-classis commemoration (2009, 2026)" `Quick test_ii_class_sunday_admits_only_ii_class_commemoration; Alcotest.test_case "RG16(a): a Feast of the Lord leaves the impeded Sunday with NO commemoration at all, not even an \ unrelated saint's own (2028-08-06, rubric + oracle)" `Quick test_rg16a_lord_feast_no_sunday_commemoration_2028; Alcotest.test_case "RG16(a), the Purification (oracle-ruled): observed outright, the Sunday not commemorated (2025-02-02)" `Quick test_purification_on_a_sunday_2025 ] )