From 044aa0dccbfe76ac9afd299af6074296eeb2cca3 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 15:57:00 +0200 Subject: temporal(ef): the votive Office of the BVM on Saturday, RG 91 entry 27 RG 91 entry 27, "Officium sanctae Mariae in sabbato", sits in the Table of Precedence between entry 26 (III-class vigils) and entry 28 (IV-class ferias). Precedence_ef.band already routed a plain IV-class Saturday feria to entry 27's own band value, but nothing in Temporal_ef.temporal ever constructed the office itself, so an otherwise-unoccupied Saturday still surfaced with a bare ferial slug and its season's ordinary colour. Established from Caput IX of the Rubricae Generales, both photographic scans and the electronic transcription, word for word (no scan-vs-transcription conflict: RG 78/79 are General Rubrics prose, not the Mass-propers body text the transcription is documented elsewhere as missing almost all of): "78. In sabbatis, in quibus occurrit Officium de feria IV classis, fit de sancta Maria in sabbato. 79. Officium sanctae Mariae in sabbato incipit a Matutino et explicit post Nonam." RG 78's own protasis IS "otherwise unoccupied IV-class Saturday" -- every Saturday whose temporal candidate would otherwise be Class4, across every season that rank reaches (Christmastide, Septuagesima, Time after Epiphany, Time after Pentecost, ordinary Paschaltide). Nothing else was needed to decide "otherwise unoccupied": band's own entry-27 branch already reads rank/weekday on the temporal candidate unconditionally and only wins the day when nothing outranks it -- RG 78's own condition, decided by the existing occurrence machinery. Colour is white, unconditionally, per RG 120(b) ("Adhibetur color albus in Officio et Missa de festis: ... b) B. Mariae Virg."), not RG 119/127/ 128's seasonal rules -- this office is never "de Tempore" for colour purposes, so it overrides the season's own violet/green. The slug is deliberately left UNCHANGED (reused from whatever the generic -- ferial fallback would already have produced), for two independent reasons: it matches the Sacred Triduum's own precedent (identified structurally, off rank/weekday, never off the slug), and a bespoke uniform slug would have broken Colitur_kernel.Validate's own slug-uniqueness-per-liturgical-year invariant, sighted zero times before this task -- confirmed directly by writing it that way first and watching the landmark-years test fail. Subject is tagged Bvm (currently inert for precedence -- only Lord is ever read by band/disposition/admit) and the name is the Latin "Officium sanctae Mariae in sabbato" (RG 91 entry 27's own table title and RG 79's own heading), deliberately not English, the same zero-circularity discipline Holy Family/Holy Name/the Sacred Triduum already established. The I-V numbered "Missae de sancta Maria in sabbato" (both scans) are a Mass-propers selection detail (RG 309(a): "iuxta temporum diversitatem") governing which readings/texts are said, not which office is kept -- colitur computes no citations yet (Plan 4), so this is deliberately not modelled here. Two pre-existing tests needed correction, not because they were wrong before, but because RG 78 genuinely changes their dates' own facts: a Saturday inside the Holy Name of Jesus window (3 January) and two Saturdays inside christmastide_feria_slug's own "-0-"/"-2-" stretches (26 December, 10 January) are all otherwise-unoccupied Class4 Saturdays, so they now legitimately carry the BVM office instead of staying a bare Temporal-subject feria. --- test/test_temporal_ef.ml | 131 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 3 deletions(-) (limited to 'test/test_temporal_ef.ml') diff --git a/test/test_temporal_ef.ml b/test/test_temporal_ef.ml index 5394173..ff3cc0b 100644 --- a/test/test_temporal_ef.ml +++ b/test/test_temporal_ef.ml @@ -296,13 +296,31 @@ let test_holy_name_sunday_shape () = compared against)" None (Colitur_kernel.Names.find (office (d 2026 1 4)).Cel.names en); (* Negative controls: every OTHER day in the 2-5 January window that year - stays [subject Temporal] -- the override is scoped to exactly the one - Sunday [T.holy_name_sunday] names, not the whole window. *) + is NOT Lord -- RG 17(a)'s own override is scoped to exactly the one + Sunday [T.holy_name_sunday] names, not the whole window. 2 and 5 January + (Friday, Monday) stay plain [Temporal] ferias, as before. 3 January is a + SATURDAY -- an unrelated rubric (RG 78, "De sancta Maria in sabbato", + {!bvm_saturday_names}'s own citation in temporal_ef.ml) legitimately + retags it [Bvm], not [Temporal]: it is an otherwise-unoccupied IV-class + Saturday (Christmastide's "ef-christmas-1-*" ferial branch, [ferial_rank]'s + unconditional Class4 there), so RG 78 fires regardless of RG 17(a) being + in play the same week -- the two rubrics are independent and this is not + a collision. This is deliberately NOT folded into the [Temporal] check + above: asserting "stays Temporal" for 3 January would be WRONG once RG + 78 is built, not merely incomplete. *) List.iter (fun dd -> Alcotest.(check bool) (Printf.sprintf "2026-01-%02d: NOT Lord (not the Sunday)" dd) true - (subject_of (d 2026 1 dd) = Sub.Temporal)) + (subject_of (d 2026 1 dd) <> Sub.Lord)) [ 2; 3; 5 ]; + Alcotest.(check bool) "2026-01-02: plain Temporal feria (not a Saturday)" true + (subject_of (d 2026 1 2) = Sub.Temporal); + Alcotest.(check bool) "2026-01-05: plain Temporal feria (not a Saturday)" true + (subject_of (d 2026 1 5) = Sub.Temporal); + Alcotest.(check bool) "2026-01-03: a Saturday -- RG 78's BVM Saturday office fires here \ + independently of RG 17(a)" + true + (subject_of (d 2026 1 3) = Sub.Bvm); (* 2027: 3 January is the Sunday (`date -d 2027-01-03 +%u` = 7) -- the oracle window's own second instance of the Sunday shape. missalemeum's 2027-01-03 row independently corroborates: rank 2, white, title "Holy @@ -489,7 +507,20 @@ let test_colours () = (6 Jan) is a Tuesday, so the actual first-Sunday-after-Epiphany origin is 11 Jan -- exercising all four sub-stretches of the fix. *) let test_christmastide_feria_slugs () = + (* 26 Dec 2026 and 10 Jan 2026 are both SATURDAYS -- ef-bvm-saturday task + (RG 78, "De sancta Maria in sabbato"): every Christmastide feria + [christmastide_feria_slug] builds is unconditionally Class4 + ([ferial_rank]'s own catch-all outside Advent/Lent/Passiontide), so on a + Saturday {!Rite_ef.Temporal_ef.bvm_saturday_names}'s own override fires + here too -- but per that citation's own "Slug" paragraph, the SLUG is + deliberately left UNCHANGED (reused from this same "-0-"/"-2-" naming, + not replaced): only colour/subject/name change. So the slug assertions + below are unaffected by the fix; what is NEW is the colour/subject + override on these two specific dates, asserted alongside them. *) Alcotest.(check string) "26 Dec (Sat)" "ef-christmas-0-saturday" (slug_of (d 2026 12 26)); + Alcotest.(check string) "26 Dec (Sat): the BVM Saturday office overrides the season colour (white, RG 120(b))" + "white" (colour_of (d 2026 12 26)); + Alcotest.(check bool) "26 Dec (Sat): subject Bvm" true (subject_of (d 2026 12 26) = Sub.Bvm); Alcotest.(check string) "28 Dec (Mon)" "ef-christmas-0-monday" (slug_of (d 2026 12 28)); Alcotest.(check string) "2 Jan (Fri)" "ef-christmas-1-friday" (slug_of (d 2026 1 2)); Alcotest.(check string) "5 Jan (Mon)" "ef-christmas-1-monday" (slug_of (d 2026 1 5)); @@ -497,6 +528,7 @@ let test_christmastide_feria_slugs () = lectio's "week 1" key, precisely to avoid colliding with 12-13 Jan below. *) Alcotest.(check string) "7 Jan (Wed, before the origin)" "ef-christmas-2-wednesday" (slug_of (d 2026 1 7)); Alcotest.(check string) "10 Jan (Sat, before the origin)" "ef-christmas-2-saturday" (slug_of (d 2026 1 10)); + Alcotest.(check bool) "10 Jan (Sat): subject Bvm" true (subject_of (d 2026 1 10) = Sub.Bvm); (* 12-13 Jan are on/after the origin: genuinely week 1, lectio's key. *) Alcotest.(check string) "12 Jan (Mon, on/after the origin)" "ef-time-after-epiphany-1-monday" (slug_of (d 2026 1 12)); @@ -813,6 +845,98 @@ let test_sacred_triduum_identity () = Alcotest.(check bool) "1666-04-22 Holy Thursday: subject Lord" true (subject_of (d 1666 4 22) = Sub.Lord) +(* RG 91 entry 27 / RG 78-79 (Caput IX, "De sancta Maria in sabbato") -- + {!Rite_ef.Temporal_ef.bvm_saturday_names}'s own citation has the full + primary-source argument (both photographic scans and the electronic + transcription for RG 78/79 themselves; both photographic scans for RG + 120(b)'s colour rule and the Missal's own "Missae de sancta Maria in + sabbato" heading). This test proves it holds across every season family + [ferial_rank] can give Class4 outside a privileged run -- Christmastide, + Septuagesima, Time after Epiphany, Time after Pentecost and ordinary + Paschaltide -- and that it does NOT fire where RG 78's own protasis is + false (Advent/Lent/Passiontide Saturdays, already II/III class; Ember + Saturdays, II/I class; a non-Saturday day in the very same week). Every + date below was independently checked against `colitur day `'s own + real output (data/ef/sanctoral.sexp + adjustments.sexp) before being + pinned here, exactly like test_golden.ml's own discipline -- not merely + asserted against [T.temporal]'s own predicted shape a second time. *) +let test_bvm_saturday () = + (* Septuagesima, clean (no competing sanctoral candidate that year): + 19 February 2028, Septuagesima I Saturday. Colour is the clearest + possible witness here -- Septuagesima's own season colour is violet + (RG 128(b)), so a white result can only come from RG 120(b)'s override, + never from [season_colour] agreeing by coincidence the way + Christmastide/Paschaltide do below. *) + Alcotest.(check string) "2028-02-19 (Septuagesima I Saturday): slug stays the ordinary ferial key" + "ef-septuagesima-1-saturday" (slug_of (d 2028 2 19)); + Alcotest.(check string) "2028-02-19: class-4, unconditional (RG 91's own IV-class table position)" + "class-4" (rank_of (d 2028 2 19)); + Alcotest.(check string) "2028-02-19: white overrides Septuagesima's own violet (RG 120(b), not RG 128(b))" + "white" (colour_of (d 2028 2 19)); + Alcotest.(check bool) "2028-02-19: subject Bvm" true (subject_of (d 2028 2 19) = Sub.Bvm); + Alcotest.(check (option string)) "2028-02-19: Latin name, both photographic scans and the transcription, \ + word for word" + (Some "Officium sanctae Mariae in sabbato") (name_la_of (d 2028 2 19)); + (* Time after Epiphany, clean: 16 February 2030. Season colour there is + green (RG 127), like Time after Pentecost below -- a second, + independent witness that the override is season-blind, not merely + "true for Septuagesima's violet and Time after Pentecost's green + happen to share a fix". *) + Alcotest.(check string) "2030-02-16 (Time after Epiphany V Saturday): slug stays the ordinary ferial key" + "ef-time-after-epiphany-5-saturday" (slug_of (d 2030 2 16)); + Alcotest.(check string) "2030-02-16: white overrides Time after Epiphany's own green (RG 127)" "white" + (colour_of (d 2030 2 16)); + Alcotest.(check bool) "2030-02-16: subject Bvm" true (subject_of (d 2030 2 16) = Sub.Bvm); + (* Time after Pentecost, clean: 27 June 2026. *) + Alcotest.(check string) "2026-06-27 (Time after Pentecost IV Saturday): slug stays the ordinary ferial key" + "ef-time-after-pentecost-4-saturday" (slug_of (d 2026 6 27)); + Alcotest.(check string) "2026-06-27: white overrides Time after Pentecost's own green (RG 127)" "white" + (colour_of (d 2026 6 27)); + Alcotest.(check bool) "2026-06-27: subject Bvm" true (subject_of (d 2026 6 27) = Sub.Bvm); + (* Ordinary Paschaltide, clean, outside the privileged Easter octave: 18 + April 2026. Season colour there is ALREADY white (RG 119) -- colour + alone cannot distinguish the override firing from it simply not being + needed, so subject/name are the load-bearing assertions on this one, + not colour (the SAME reasoning [test_holy_family]'s own "Christmastide + already white" gap does not arise for -- this is a genuine, deliberate + exception recorded here, not an oversight). *) + Alcotest.(check string) "2026-04-18 (Paschaltide II Saturday): slug stays the ordinary ferial key" + "ef-easter-2-saturday" (slug_of (d 2026 4 18)); + Alcotest.(check string) "2026-04-18: white (coincides with Paschaltide's own RG 119 white -- not a \ + distinguishing witness on its own)" + "white" (colour_of (d 2026 4 18)); + Alcotest.(check bool) "2026-04-18: subject Bvm (the real witness this date proves, since colour alone \ + cannot)" + true (subject_of (d 2026 4 18) = Sub.Bvm); + Alcotest.(check (option string)) "2026-04-18: Latin name present too" (Some "Officium sanctae Mariae in sabbato") + (name_la_of (d 2026 4 18)); + (* Negative controls: RG 78's own protasis ("in quibus occurrit Officium + de feria IV classis") is false for these -- each is already II or III + class, never reaching Class4 at all, so none of them is overridden. *) + Alcotest.(check string) "2026-03-14 (an ordinary Lent Saturday): stays III class, violet, Temporal" + "class-3" (rank_of (d 2026 3 14)); + Alcotest.(check string) "2026-03-14: violet, NOT overridden (RG 78 never applies -- already III class)" + "violet" (colour_of (d 2026 3 14)); + Alcotest.(check bool) "2026-03-14: subject stays Temporal" true (subject_of (d 2026 3 14) = Sub.Temporal); + Alcotest.(check string) "2026-12-12 (an ordinary Advent Saturday, before 17 Dec): stays III class" + "class-3" (rank_of (d 2026 12 12)); + Alcotest.(check bool) "2026-12-12: subject stays Temporal (not overridden)" true + (subject_of (d 2026 12 12) = Sub.Temporal); + Alcotest.(check string) "2026-02-28 (Lent Ember Saturday): stays II class, NOT overridden" "class-2" + (rank_of (d 2026 2 28)); + Alcotest.(check bool) "2026-02-28: subject stays Temporal" true (subject_of (d 2026 2 28) = Sub.Temporal); + Alcotest.(check string) "2026-09-26 (September Ember Saturday): stays II class, NOT overridden" "class-2" + (rank_of (d 2026 9 26)); + (* A non-Saturday day in the SAME week as a clean BVM Saturday stays an + ordinary ferial -- the override is exactly one weekday wide, not the + whole week. 26 June 2026 is the Friday immediately before the 27 June + Saturday pinned above. *) + Alcotest.(check string) "2026-06-26 (Friday, day before a BVM Saturday): plain ferial, not overridden" + "ef-time-after-pentecost-4-friday" (slug_of (d 2026 6 26)); + Alcotest.(check string) "2026-06-26: green, the season's own colour, unaffected" "green" + (colour_of (d 2026 6 26)); + Alcotest.(check bool) "2026-06-26: subject stays Temporal" true (subject_of (d 2026 6 26) = Sub.Temporal) + let test_totality () = (* Every day of 2026 yields an office without raising. Not a slug re-validation -- Slug.t is a private string validated on construction, @@ -848,6 +972,7 @@ let suite_extra = Alcotest.test_case "colours" `Quick test_colours; Alcotest.test_case "sacred triduum identity (RG 91 entry 2, Gap 1)" `Quick test_sacred_triduum_identity; + Alcotest.test_case "BVM Saturday office (RG 91 entry 27, RG 78-79)" `Quick test_bvm_saturday; Alcotest.test_case "christmastide feria slugs" `Quick test_christmastide_feria_slugs; Alcotest.test_case "named days carry their week" `Quick test_named_days_carry_their_week; Alcotest.test_case "totality" `Quick test_totality; -- cgit v1.3