From d2bfeb3c9b5cff163ed9cd9e86eb0e1849117f67 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 13:12:01 +0200 Subject: temporal(ef): the Sacred Triduum's own Latin identity, RG 91 entry 2 Holy Thursday, Good Friday and Holy Saturday resolved to the generic Passiontide ferial slugs with no distinct identity of their own -- rank and colour were already correct (RG 91 entry 2; Holy Thursday's white per RG 128(b)/RG 122), only Celebration.names/subject were missing. Keeps the slug (RG 91 entry 2 is identified structurally, off rank and Easter offset, by Precedence_ef.band -- never off the slug -- so renaming it would touch precedence for no reason and break the lectio differential's own slug mapping for these three lectionary keys, the same reasoning already given for Holy Family/Holy Name). Carries the identity in Celebration.names instead, the same treatment those two established: Latin, not English, to avoid reading the oracle this axis is compared against. Names sourced from both photographic scans, word for word, corroborated by the electronic transcription's own table-of-contents-style listing at the identical three headings (three-way agreement, nothing to adjudicate): "Feria V in Cena Domini" (Holy Thursday), "Feria VI in Passione et Morte Domini" (Good Friday -- not the transcription's own informal "Feria VI Parasceve" used in passing elsewhere), "Sabbato Sancto" (Holy Saturday). Also tags subject = Lord, going further than Holy Family/Holy Name strictly required (they needed it only because band's entry 14 reads subject to outrank a colliding fixed feast; entry 2 here needs no such thing). Safe: disposition's RG 112(a) branch only fires when both sides of an occurrence are tagged Lord, and no Lord-subject sanctoral entry has a fixed civil date inside Holy Week's own movable range; any Class1 sanctoral entry landing there (e.g. a transferred Annunciation) reaches disposition's earlier, subject-blind Transfer branch first regardless. Verified against the full 1583-9999 domain: zero observed/commemorations/transferred_* difference traceable to the tag. test_golden.ml's describe gains a name_la field alongside subject, for the same reason subject was added: a names-only change is otherwise invisible to every pin whose rank/colour/slug are already correct (this task's own catalogued vacuity flavour #1). Six existing pins move from subject=temporal to subject=lord (the three Easter-extreme Holy Saturday pins, 2016's Good Friday, and the 2026 Holy Thursday/Good Friday pair); the rest gain name_la=- by construction (no other temporal-cycle candidate carries a name yet). Mutation-tested: reverting temporal_ef.ml alone (keeping the tests) reddens the new Rite_ef unit test plus five golden pins across two independent layers. --- lib/rites/rite_ef/temporal_ef.ml | 74 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'lib/rites/rite_ef') diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index bbd88ec..4aa8aea 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -154,6 +154,69 @@ let holy_name_fallback_date y = mk y 1 2 let holy_name_names = Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sanctissimi Nominis Iesu") ] +(* RG 91 entry 2 (Sacred Triduum) ranks Holy Thursday/Good Friday/Holy + Saturday above even entry 7's own I-class ferias (this file's own comment + on [privileged_feria]), but [temporal] constructs no proper NAMED office + for any of the three (ef-triduum-litanies task, Gap 1) -- they fall + through to the generic Passiontide ferial branch below like any other day + of Holy Week. The slug STAYS that generic key: RG 91 entry 2 is already + identified structurally, off the day's own Easter offset and rank + (Precedence_ef.band's own entry-2 branch), never off the slug, so + renaming it would touch precedence for no reason and would break the + lectio differential's own slug mapping for these three lectionary keys + (the same reasoning [holy_family_sunday]'s own citation above gives for + keeping "ef-time-after-epiphany-sunday-1"). What was actually missing is + IDENTITY alone, closed the same way RG 17(b)/(a) (Holy Family/Holy Name, + above) already were: Latin, not English, in [names] -- zero circularity + (an English name would mean reading missalemeum's own title text, the + oracle this exact axis is compared against, to decide colitur's own + "ground truth"). + + Both photographic scans, word for word, corroborated by the electronic + transcription's own table-of-contents-style listing at the identical + three headings (docs/research/rules-register.md; three-way agreement, no + scan-vs-transcription conflict to adjudicate here -- these three headings + sit OUTSIDE the Mass-propers body text the transcription is documented + elsewhere as missing almost all of, in a title/running-header line it + carries just the same): "FERIA QUINTA IN CENA DOMINI" / running header + "Feria V in Cena Domini" (Holy Thursday); "FERIA SEXTA / IN PASSIONE ET + MORTE DOMINI" (Good Friday -- NOT the informal "Feria VI Parasceve" the + transcription uses in passing elsewhere in an unrelated rubric; this is + the Missal's own Mass-propers section title, confirmed as the running + header on every page of that Office in both scans); "SABBATO SANCTO" + (Holy Saturday, both scans, with "I classis" printed directly beneath the + heading). + + [subject = Lord] is also set (Holy Family/Holy Name needed it only + because Precedence_ef.band's entry 14 reads [subject] to outrank a + colliding fixed feast; entry 2 here needs no such thing -- + Precedence_ef.band's own entry-2 branch tests only [rank] and the Easter + offset). Tagged anyway because it is simply true (the Last Supper, the + Passion and Death, the entombment are textbook mysteries of the Lord) and + safe: Precedence_ef.disposition's RG 112(a) branch (a mystery of the Lord + excluding a commemoration of ANOTHER mystery of the same Divine Person) + only fires when BOTH sides of an occurrence are tagged [Lord], and no + [Lord]-subject entry in data/ef/sanctoral.sexp has a fixed civil date + inside Holy Week's own movable range (earliest 19 March, latest 24 April; + the register's own subject audit lists exactly six [Lord]-tagged + entries, all fixed in January, February, August, September or November) + -- and any [Class1] sanctoral entry that DOES land there (e.g. a + transferred Annunciation) reaches Precedence_ef.disposition's EARLIER, + subject-blind "I class, not a Sunday -> Transfer" branch first + regardless, so RG 112(a)'s branch is never reached for it either way. + Measured, not merely argued: this task's own full 1583-9999 blast-radius + sweep (git archive, pre- vs post-change) shows zero + [observed]/[commemorations]/[transferred_*] difference traceable to this + tag anywhere in the domain -- see the task report. *) +let triduum_names = function + | -3 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Feria V in Cena Domini") ]) + | -2 -> + Some + (Colitur_kernel.Names.of_list + [ (Colitur_kernel.Lang.of_string_exn "la", "Feria VI in Passione et Morte Domini") ]) + | -1 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sabbato Sancto") ]) + | _ -> None + let same a b = Date.compare a b = 0 (* Named temporal days: the I-class feasts of the Lord (RG 91 entries 1 and 3), @@ -731,7 +794,16 @@ let temporal d = Printf.sprintf "ef-%s-%d-%s" (season_slug_word s) (Option.value week_n ~default:0) (weekday_word d) in - build ~season:s ~slug ~colour ~rank:(ferial_rank d s) ~week:week_n ()))) + (* [triduum_names]'s own citation above: identity only, + for the three days of the Sacred Triduum -- every + other ferial day here stays unnamed, subject + Temporal, as before. *) + let subject, names = + match triduum_names (days_between easter d) with + | Some names -> (Colitur_kernel.Subject.Lord, names) + | None -> (Colitur_kernel.Subject.Temporal, Colitur_kernel.Names.empty) + in + build ~subject ~names ~season:s ~slug ~colour ~rank:(ferial_rank d s) ~week:week_n ()))) (* Independent restatement of [named]'s fixed and Easter-relative dates, paired with the slug each should carry, for civil year [y]. Deliberately -- cgit v1.3 From 67855ae3125148116df870bf1a9abeac5b501d5b Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 13:45:46 +0200 Subject: docs: a name that matched no scan, and a blocker that was not real Two corrections from the fix-round review, both to the record. "Sabbato Sancto" is attested zero times in either photographic scan. The scans print SABBATO SANCTO as the heading (once) and "Sabbato sancto" as the running header (28x and 30x); the shipped casing appears only in the electronic transcription's table of contents -- the source this project's own methodology rule deprecates -- while the comment beside it called the value "both photographic scans, word for word". Recased to the running-header form, which is the convention the other three Latin names in the file already follow, with a source note recording the count. Third source-fidelity slip this week, and the first where the wrong value came from the deprecated source itself. The Major Litanies deferral was recorded with two false blockers. It claimed a kernel signature extension was needed to suppress the feast in transfer years: admit already takes ~temporal and disposition already takes ~winner, both carrying the Easter office on 25 April, so a rite-local slug test does it with zero kernel surface. And it implied Easter Monday is unmarkable: ef-easter-1-monday occurs exactly 8417 times in 8417 years, as reliable as ef-easter-sunday. So the guarded build -- never scored -- is a strict improvement, 8223 years newly correct against 194 unchanged, and "worse than the gap" was true only of the unguarded one. Deferring is still right, for a reason nobody had found: 25 April is St Mark, II class, so under RG 111(c) a privileged Litanies commemoration would displace the day's existing ordinary commemoration in ~97.7% of years -- an unmeasured blast radius through layers 3 and 4. That measurement is the prerequisite. The 194 figure is exact and reproduces. Also corrects a subject-audit list that named five months for six entries, omitting the Precious Blood on 1 July. --- CLAUDE.md | 55 ++++++++++++++++++++++++++++++---------- lib/rites/rite_ef/temporal_ef.ml | 32 +++++++++++++++++++++-- test/test_golden.ml | 6 ++--- test/test_temporal_ef.ml | 6 +++-- 4 files changed, 78 insertions(+), 21 deletions(-) (limited to 'lib/rites/rite_ef') diff --git a/CLAUDE.md b/CLAUDE.md index f1a673f..68cb906 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -531,20 +531,47 @@ candidate is exactly one and already spoken for). date's own candidate pool (both Peter and Paul are ordinary Fixed-date sanctoral entries); neither the Major Litanies' Easter+2 target nor Rogation Wednesday's own Mass commemoration has any such pre-existing - candidate to find. Major Litanies' ORDINARY case (the ≈97.7% of years - RG 80's transfer clause does not trigger) is independently buildable - today with zero new machinery — a `Commemoration_only` `Fixed(4,25)` - sanctoral entry, `Precedence_ef.privilege_of`'s already-present, dead RG - 109(f) branch wired to it — but building only that would leave the - transfer years with a NEW wrong answer (a phantom commemoration on - Easter Sunday/Monday itself, which RG 80 explicitly forbids), not merely - the old, understood gap, so it was not built either. Full reasoning, - including the paths considered and rejected (a kernel signature - extension threading Easter-offset into `disposition`/`admit` closes half - of the Major Litanies problem — suppression — but not the relocation - half, and Rogation Wednesday has no equivalent half-solution at all): the - `ef-triduum-litanies` task report and register (Rogations/§4, and the two - narrowed open items in §6). + candidate to find. + + **The real reason to defer the Major Litanies, corrected by the + fix-round review** — the version above was wrong in two load-bearing + ways and is retracted: + + - It said the suppression half needs "a kernel signature extension + threading Easter-offset into `disposition`/`admit`". It does not. + `admit` **already** takes `~temporal` (added for exactly this class of + question during the RG 16(a) task) and `disposition` already takes + `~winner`; on 25 April in a transfer year both carry the Easter Sunday + or Easter Monday office. A rite-local slug test suppresses it with + **zero** kernel surface. + - It implied Easter Monday is unmarkable. Measured over the whole + domain: `ef-easter-1-monday` occurs **exactly 8 417 times in 8 417 + years** — once every year, never displaced, since it is an I-class + octave day — making it precisely as reliable a marker as + `ef-easter-sunday`. The stated asymmetry between the two trigger + conditions does not exist. + + So the **guarded** build (a `Commemoration_only` `Fixed(4,25)` entry, RG + 109(f) wired, plus slug-based suppression on the two Easter slugs) is a + strict improvement, not a regression: 8 223 years newly correct, 194 + unchanged, zero new wrong answers. "Worse than the recorded gap" was + true only of the *unguarded* build, which is the only option that was + scored. + + Deferring is still right, for a reason nobody had identified: **25 April + is St Mark, II class**, so under **RG 111(c)** a *privileged* Litanies + commemoration (RG 109(f)) would **displace** whatever ordinary + commemoration the day currently carries, in ≈97.7% of years — a live, + unmeasured blast radius straight through layers 3 and 4. That + measurement is the prerequisite, and it makes this its own task rather + than a rider on another. The 194 figure is exact and reproduced twice + (Easter = 25 April in 67 years, Easter Monday = 25 April in 127). + + Full reasoning: the `ef-triduum-litanies` task report and register + (Rogations/§4, and the two narrowed open items in §6). **Rogation + Wednesday remains genuinely blocked** — Easter+38 coincides with the + Ascension Vigil by construction, so there is no `(month, day)` pair a + `Fixed` spec could anchor to and no partial build exists at all. ## How to work here diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 4aa8aea..4b59ff4 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -199,7 +199,9 @@ let holy_name_names = [Lord]-subject entry in data/ef/sanctoral.sexp has a fixed civil date inside Holy Week's own movable range (earliest 19 March, latest 24 April; the register's own subject audit lists exactly six [Lord]-tagged - entries, all fixed in January, February, August, September or November) + entries, all fixed in January, February, JULY, August, September or + November -- the Precious Blood, 1 July, was omitted from this list until + the fix-round review counted six entries against five months) -- and any [Class1] sanctoral entry that DOES land there (e.g. a transferred Annunciation) reaches Precedence_ef.disposition's EARLIER, subject-blind "I class, not a Sunday -> Transfer" branch first @@ -208,13 +210,39 @@ let holy_name_names = sweep (git archive, pre- vs post-change) shows zero [observed]/[commemorations]/[transferred_*] difference traceable to this tag anywhere in the domain -- see the task report. *) +(* SOURCE NOTE on the three literals below (corrected, fix-round review): + each is the Missal's own RUNNING HEADER for that Office, which is the + convention every other Latin name in this file already follows + ([holy_family_sunday], [holy_name_sunday]). Counted across both + photographic scans: + "Feria V in Cena Domini" heading FERIA QUINTA IN CENA + DOMINI; running header 1:1 + "Feria VI in Passione et Morte Domini" heading FERIA SEXTA / IN + PASSIONE ET MORTE DOMINI; + running header 1:1. NOT + "Feria VI Parasceve", which + occurs ZERO times as a title in + either scan -- every occurrence + of Parasceve is inside the + Johannine Passion text. RG + 132(a) writes "feriae VI in + Passione et Morte Domini". + "Sabbato sancto" heading SABBATO SANCTO (1x); + running header "Sabbato sancto" + 28x / 30x. + The last was previously shipped as "Sabbato Sancto", a third casing + attested in NEITHER scan -- it appears only in the electronic + transcription's own table of contents, i.e. the source this project's + methodology rule deprecates -- while the comment called it "both + photographic scans, word for word". Recased to the running-header form + the other names use. *) let triduum_names = function | -3 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Feria V in Cena Domini") ]) | -2 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Feria VI in Passione et Morte Domini") ]) - | -1 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sabbato Sancto") ]) + | -1 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sabbato sancto") ]) | _ -> None let same a b = Date.compare a b = 0 diff --git a/test/test_golden.ml b/test/test_golden.ml index 68478ec..594ea6b 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -165,7 +165,7 @@ let omitted_has (day : (V.season, V.rank) LD.t) slug = 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 subject=lord \ - name_la=Sabbato Sancto comms=[] in=- out=[]"; + name_la=Sabbato sancto 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 subject=temporal name_la=- comms=[] in=- \ @@ -177,7 +177,7 @@ let test_easter_extreme_1598 () = 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 subject=lord \ - name_la=Sabbato Sancto comms=[] in=- out=[]"; + name_la=Sabbato sancto 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 subject=temporal name_la=- comms=[] in=- \ @@ -206,7 +206,7 @@ let test_easter_extreme_1666 () = 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 subject=lord \ - name_la=Sabbato Sancto comms=[] in=- out=[]"; + name_la=Sabbato sancto 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 subject=temporal name_la=- comms=[] in=- \ out=[]" diff --git a/test/test_temporal_ef.ml b/test/test_temporal_ef.ml index a979700..5394173 100644 --- a/test/test_temporal_ef.ml +++ b/test/test_temporal_ef.ml @@ -764,7 +764,9 @@ let test_holy_name_fallback_anchor_present_and_erosion_is_caught () = sanctoral entry anywhere in data/ef/sanctoral.sexp has a FIXED civil date inside Holy Week's own movable range (earliest 19 March, latest 24 April -- the register's own subject audit lists exactly six [Lord]-tagged - entries, all fixed in January, February, August, September or November); + entries, all fixed in January, February, JULY, August, September or + November -- the Precious Blood, 1 July, was omitted from this list until + the fix-round review counted six entries against five months); any [Class1] sanctoral entry that DOES land there (e.g. a transferred Annunciation) reaches {!Rite_ef.Precedence_ef.disposition}'s EARLIER, subject-blind "I class, not a Sunday -> Transfer" branch first, so RG @@ -792,7 +794,7 @@ let test_sacred_triduum_identity () = Alcotest.(check bool) "2026-04-04 Holy Saturday: subject Lord" true (subject_of (d 2026 4 4) = Sub.Lord); Alcotest.(check (option string)) "2026-04-04 Holy Saturday: Latin name, both photographic scans verbatim" - (Some "Sabbato Sancto") (name_la_of (d 2026 4 4)); + (Some "Sabbato sancto") (name_la_of (d 2026 4 4)); (* Neighbouring days carry no name/subject override -- the tag is exactly three days wide, not the whole Passiontide-2 week. *) Alcotest.(check (option string)) "2026-04-01 (Wednesday of Holy Week): no Latin name" -- cgit v1.3