From 12b97761019cfa02ca0da8a5fb50ef815d07685c Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Fri, 21 Aug 2026 14:42:30 +0200 Subject: feat(ef): implement RG 33's third omission trigger RG 33 omits a II/III-class vigil in three cases: it falls on a Sunday, it falls on a I-class feast, "vel si festum cui praemittitur in alium diem transferri aut ad commemorationem reduci contingat". Only the first two were built; the third was recorded in precedence_ef.ml as unimplemented on the grounds that no witness existed in the shipped data. That reasoning was wrong, and the rule fires on 1 744 days across 1583-9999. Both halves of the clause reduce to one observable question -- is the feast the OBSERVED office on the following day (RG 34 puts it there) -- so the kernel asks it once per candidate, after place_transfers has settled the year. No fixed point is needed: a vigil is a candidate only on its own day, never on its feast's, so suppressing it cannot change what the next day observes. Precedence.rules gains vigil_feast, which returns the slug of the feast a vigil precedes; the kernel cannot infer that itself, because only two of the five vigil/feast pairs share a slug stem. Blast radius, measured pre-change binary vs HEAD over the whole domain and classified: 1 744 days, three shapes, zero unexplained. 1 199 are the feast reduced to a commemoration (10 August on a Sunday, St Lawrence); 478 and 67 are the feast transferred under RG 96 after the Sacred Heart or Corpus Christi takes its day. The Assumption's and the Ascension's vigils never qualify -- their I-class feasts always keep their own day. Independently witnessed, which is unusual here. The published Ordo -- the only witness outside the Divinum Officium -> missalemeum -> lectio lineage -- omits St Lawrence's vigil on 2025-08-09, agreeing with colitur against both engines. That date had been read earlier as an Ordo gap; the Ordo was right, and correcting the misreading is what surfaced this clause. On 2027-08-09 the feast does keep its day and the Ordo omits a vigil colitur correctly keeps, which is a genuine Ordo gap. Allow-lists: C39 (lectio, 10 rows) and a 2038 oracle class citing the register, the 2026-2027 window having no instance. The golden pin asserting St Lawrence's vigil is violet moved 2025 -> 2027; its own comment had reasoned about the vigil's weekday and missed that RG 33 also looks at the feast's. Two new pins cover both shapes of the clause. The vigil/feast table is built with Slug.of_string_exn: mutation testing showed that of_string plus Result.to_option turns a typo into None, which this hook's contract reads as "not a vigil", switching the rule off in silence. Two tests assert the table against the shipped data in both directions. --- test/test_golden.ml | 80 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 8 deletions(-) (limited to 'test/test_golden.ml') diff --git a/test/test_golden.ml b/test/test_golden.ml index 33ca362..b882596 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -1327,14 +1327,70 @@ let test_rogation_wednesday_yields_to_a_feast_2026 () = white" -- which colitur already emits correctly (ef-ascension-vigil, white, pinned by the two Rogation Wednesday cases just above). - Pinned in years where each vigil is actually OBSERVED rather than impeded: - 9 August 2025 is a Saturday and 14 August 2025 a Thursday, both checked - with `date -d`. In 2026, 9 August is a Sunday and the vigil never appears. *) -let test_vigil_of_st_lawrence_is_violet_2025 () = - check ~msg:"RG 128: III-class vigil outside Paschaltide is violet, not red" 2025 8 9 - "2025-08-09 saturday season=time-after-pentecost week=8 slug=vigil-of-st-lawrence \ + Pinned in years where each vigil is actually OBSERVED rather than impeded. + 14 August 2025 is a Thursday and the Assumption always keeps 15 August (I + class, nothing in the universal calendar outranks it), so that pin is + stable in any year the vigil's own day is free. + + ST LAWRENCE'S PIN MOVED 2025 -> 2027 when RG 33's third omission trigger + was implemented. Its original comment reasoned only about the vigil's OWN + weekday -- "9 August 2025 is a Saturday", true, so RG 33's Sunday trigger + does not fire -- and missed that the rule also looks at the FEAST: 10 + August 2025 is a Sunday, the II-class Sunday reduces St Lawrence to a + commemoration, and "vel si festum cui praemittitur... ad commemorationem + reduci contingat" omits the vigil entirely. The day now keeps the BVM + Saturday Office instead, pinned separately just below. 9 August 2027 is a + Monday and 10 August 2027 a Tuesday (both `date -d`-checked), so St + Lawrence keeps his own day there and the vigil genuinely is observed -- + which is what this pin has always been trying to assert about RG 128's + colour rule. *) +let test_vigil_of_st_lawrence_is_violet_2027 () = + check ~msg:"RG 128: III-class vigil outside Paschaltide is violet, not red" 2027 8 9 + "2027-08-09 monday season=time-after-pentecost week=12 slug=vigil-of-st-lawrence \ rank=class-3 colour=violet subject=saint name_la=- comms=[romanus:ordinary] in=- out=[]" +(* RG 33's THIRD omission trigger -- "vel si festum cui praemittitur in alium + diem transferri aut ad commemorationem reduci contingat" -- on the shipped + universal calendar, no overlay involved. 10 August 2025 is a Sunday; the + II-class Sunday outranks St Lawrence's II-class feast (RG 15), so the feast + is "ad commemorationem reducta" on its own day and the vigil of 9 August is + "penitus omissa". + + This is the clause's most common live shape by a wide margin: 1 199 of the + 1 744 days it fires on across 1583-9999 are St Lawrence's vigil dropped + because 10 August fell on a Sunday. The day does not become a bare feria -- + 9 August 2025 is a Saturday, so RG 78's votive Office of the BVM takes the + freed IV-class Saturday, which is why this pin reads white and subject=bvm. + St Romanus keeps his commemoration either way (RG 111). + + Independently witnessed. The published Ordo -- the project's only witness + outside the Divinum Officium -> missalemeum -> lectio lineage -- omits the + vigil on this exact date, and lectio (and so missalemeum) keeps it; both + allow-lists gained cited entries for that disagreement. *) +let test_rg33_vigil_omitted_when_its_feast_is_commemorated_2025 () = + check + ~msg:"RG 33: 10 Aug 2025 is a Sunday, so St Lawrence is only commemorated and his vigil is \ + omitted entirely -- the BVM Saturday Office (RG 78) takes the freed day" + 2025 8 9 + "2025-08-09 saturday season=time-after-pentecost week=8 \ + slug=ef-time-after-pentecost-8-saturday rank=class-4 colour=white subject=bvm \ + name_la=Officium sanctae Mariae in sabbato comms=[romanus:ordinary] in=- out=[]" + +(* The same clause's OTHER half, "in alium diem transferri". 24 June 2038 is + impeded by the Sacred Heart (I class, movable), so the Nativity of St John + the Baptist is TRANSFERRED under RG 96 and its vigil of 23 June is omitted. + 478 days across the domain take this shape (a further 67 via Corpus + Christi). Pinned in 2038 because that year already carries an oracle + fixture, so the divergence this creates is visible to layer 4 as well. *) +let test_rg33_vigil_omitted_when_its_feast_transfers_2038 () = + check + ~msg:"RG 33: the Sacred Heart takes 24 June 2038, St John the Baptist transfers, \ + and his vigil of 23 June is omitted entirely" + 2038 6 23 + "2038-06-23 wednesday season=time-after-pentecost week=1 \ + slug=ef-time-after-pentecost-1-wednesday rank=class-4 colour=green subject=temporal \ + name_la=- comms=[] in=- out=[]" + let test_vigil_of_the_assumption_is_violet_2025 () = check ~msg:"RG 128: II-class vigil outside Paschaltide is violet, not white" 2025 8 14 "2025-08-14 thursday season=time-after-pentecost week=9 slug=vigil-of-the-assumption \ @@ -1342,8 +1398,16 @@ let test_vigil_of_the_assumption_is_violet_2025 () = let suite = ( "golden pins (known-tricky years)", - [ Alcotest.test_case "RG128: St Lawrence's vigil is violet (2025)" `Quick - test_vigil_of_st_lawrence_is_violet_2025; + [ Alcotest.test_case "RG128: St Lawrence's vigil is violet (2027)" `Quick + test_vigil_of_st_lawrence_is_violet_2027; + Alcotest.test_case + "RG33 third trigger: the vigil is omitted when its feast is reduced to a commemoration \ + (2025-08-09, St Lawrence)" + `Quick test_rg33_vigil_omitted_when_its_feast_is_commemorated_2025; + Alcotest.test_case + "RG33 third trigger: the vigil is omitted when its feast is transferred away \ + (2038-06-23, St John the Baptist)" + `Quick test_rg33_vigil_omitted_when_its_feast_transfers_2038; Alcotest.test_case "RG128: the Assumption's vigil is violet (2025)" `Quick test_vigil_of_the_assumption_is_violet_2025; Alcotest.test_case "Easter extreme: 1598 earliest (22 Mar, Gauss-verified)" `Quick -- cgit v1.3