diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-13 15:20:24 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-13 15:20:24 +0200 |
| commit | 36eba4eff58162cbe4c7b4ff1b9d049432fc1acc (patch) | |
| tree | aad8cd588a8a33202bfed990fec99d5eafc25dcd | |
| parent | dc00d1f730b23fa99cd15ff142ecba515a9fe0b7 (diff) | |
| parent | 67855ae3125148116df870bf1a9abeac5b501d5b (diff) | |
| download | colitur-36eba4eff58162cbe4c7b4ff1b9d049432fc1acc.tar.gz colitur-36eba4eff58162cbe4c7b4ff1b9d049432fc1acc.zip | |
Merge branch 'ef-triduum-litanies': the Sacred Triduum's Latin identity
Gives Holy Thursday, Good Friday and Holy Saturday the names the Missal
prints for them, taken from each Office's own running header: 'Feria V
in Cena Domini', 'Feria VI in Passione et Morte Domini', 'Sabbato
sancto'. Rank and colour were already correct; only the identity was
missing, the same shape the Holy Family had. The slug is deliberately
unchanged -- it is the lectionary key -- and the names are Latin, which
avoids promoting an English value from the oracle the engine is checked
against.
Not 'Feria VI Parasceve': that phrase occurs zero times as a title in
either photographic scan, appearing only inside the Johannine Passion
text, and RG 132(a) writes 'feriae VI in Passione et Morte Domini'.
Output is byte-identical across all 8417 years -- colitur day prints no
name column -- but layer 5 does see it: describe gained name_la, and
reverting the change reddens six pins.
The Major Litanies (RG 80) and the Rogation Wednesday commemoration were
investigated to the same depth, scan-verified, cited, and deliberately
not built. Rogation Wednesday is genuinely blocked: Easter+38 coincides
with the Ascension Vigil, so no Fixed date-spec can anchor it. The Major
Litanies are tractable in the rite module -- the two blockers first
recorded for them were false, and are retracted here -- but 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. That blast radius is unmeasured, and measuring it is
the prerequisite for its own task.
| -rw-r--r-- | CLAUDE.md | 110 | ||||
| -rw-r--r-- | lib/rites/rite_ef/temporal_ef.ml | 102 | ||||
| -rw-r--r-- | test/test_golden.ml | 124 | ||||
| -rw-r--r-- | test/test_temporal_ef.ml | 83 |
4 files changed, 354 insertions, 65 deletions
@@ -449,18 +449,18 @@ nowhere in git (`docs/` is gitignored). failure mode, proven by fabricating a second companion and watching the whole suite stay green pre-fix). -5. **`Record` and `Liturgical_day` both claim to be "the single stable output +6. **`Record` and `Liturgical_day` both claim to be "the single stable output schema".** `Record` cannot express what the engine now computes (no observed celebration, no commemorations, no transfers), has **no test file**, and is used only by the legacy `colitur temporal` path — `day` hand-formats instead. Plan 2's carried item (add a `cycle` field for OF's Sunday A/B/C and weekday I/II) is still open and now costlier: it must pass through `Temporal.t`, which is embedded in the sexp-derived `Liturgical_day.t`. -6. **`Temporal.RITE` and `Rite.t` are two competing abstractions.** The module +7. **`Temporal.RITE` and `Rite.t` are two competing abstractions.** The module type still exists and `temporal_ef` still satisfies it, but it carries none of `rules`, `anchors`, `season_runs`, `transfer_target` — satisfying it now proves almost nothing. `Rite.t` is the load-bearing one. -7. **EF-shaped things still in "rite-agnostic" kernel code**: `validate.ml` +8. **EF-shaped things still in "rite-agnostic" kernel code**: `validate.ml` hardcodes Sunday as the week start; `Liturgical_day.transferred_in` is an `option` justified by RG 96; `Precedence.privilege` is defined by RG 111; `Repose` is EF vocabulary emitted by nothing. Each is one field short of the @@ -474,12 +474,104 @@ Boniface 14 May, Evaristus, Theodore); and `romanus`, which should not exist on 9 August. lectio's ini is **generated from missalemeum**, so the two are one lineage, not two independent sources. -**Unbuilt, recorded**: the Sacred Triduum has no proper office (it uses ordinary -Passiontide ferial slugs); RG 91 entry 27's BVM Saturday Office; Major Litanies -(25 April); the Rogation-Wednesday commemoration; RG 112(b)/(c)/(d) (only (a) -has a live witness this codebase's data can construct). Allow-list entries M11 -and M13 are `verdict open` by design. Holy Name of Jesus (RG 17(a)) and RG 110 -(inseparable Peter/Paul commemorations) are **RESOLVED — see item 4 above.** +**Unbuilt, recorded**: RG 91 entry 27's BVM Saturday Office; RG 112(b)/(c)/(d) +(only (a) has a live witness this codebase's data can construct). Allow-list +entries M11 and M13 are `verdict open` by design. Holy Name of Jesus (RG 17(a)) +and RG 110 (inseparable Peter/Paul commemorations) are **RESOLVED — see item 4 +above.** The Sacred Triduum's own identity is **RESOLVED — see item 5 below.** +Major Litanies (25 April) and the Rogation-Wednesday commemoration are +**NARROWED, still unbuilt — see item 5 below**: both rules are now +scan-verified and register-cited, but neither is built, because both need the +SAME missing machinery (a rite-computed, Easter-relative, non-competing +commemoration candidate — no channel in `Precedence.resolve`/`calendar.ml` +constructs one; sanctoral candidates are Fixed-date only, the day's temporal +candidate is exactly one and already spoken for). + +5. **The Sacred Triduum (RG 91 entry 2) — RESOLVED (2026-08-13, + `ef-triduum-litanies` task); Major Litanies (RG 80) and Rogation + Wednesday's own commemoration (RG 87-89) — established from a + photographic scan, deliberately NOT built.** Holy Thursday/Good + Friday/Holy Saturday kept their existing slugs + (`ef-passiontide-2-{thursday,friday,saturday}` — RG 91 entry 2 is + identified structurally by `Precedence_ef.band`, off rank and Easter + offset, never off the slug) and gained `Celebration.names` (Latin, both + photographic scans, corroborated by the electronic transcription's own + table-of-contents listing at the identical headings: "Feria V in Cena + Domini", "Feria VI in Passione et Morte Domini", "Sabbato Sancto") and + `subject = Lord` (verified safe: RG 112(a) only fires when both sides of + an occurrence are `Lord`, and no `Lord`-subject sanctoral entry has a + fixed date inside Holy Week's own movable range). **Full 1583-9999 + blast radius, measured (`git archive` pre- vs post-change, `colitur day` + CLI output diffed): ZERO differing lines anywhere in the domain** — the + CLI prints neither `names` nor `subject`, so this whole change is + invisible to the differential (layer 3), the oracle (layer 4), and even + the exhaustive property sweep (layer 2); only `test_golden.ml`'s + `describe` (widened this task to add a `name_la` field, the same lesson + its own `subject` field was added for) and one new `test_temporal_ef.ml` + unit test see it at all. Mutation-tested: reverting `temporal_ef.ml` + alone reddens 6 tests across those two files. + + Major Litanies (25 April, RG 80) and Rogation Wednesday's own + commemoration (RG 87-89, Minor Litanies) were both investigated to the + same depth and **both scan-verified and fully register-cited, but + deliberately not built**: RG 80's own transfer clause (25 April moving to + the following Tuesday whenever it coincides with Easter Sunday or Easter + Monday — **194 of 8,417 domain years, ≈2.3%, measured**, not assumed + rare) and Rogation Wednesday's own commemoration (RG 88/89, competing + only when the Ascension Vigil already occupies the day) both need a + commemoration candidate keyed to a MOVABLE, Easter-relative date that + is not the day's own temporal office — a kind of thing + `Precedence.resolve`'s architecture has no channel for: sanctoral + candidates come exclusively from `Layer.on_date`'s Fixed-`Date_spec` + lookup (`date_spec.ml`'s own header: Easter-relative forms "arrive with + the OF sanctoral" — not yet), and the day's temporal candidate is + exactly one, already spoken for. RG 110's own `rg110_additions` + mechanism (item 4 above) looks like a precedent but is not one: it only + ever RE-INCLUDES a candidate that already exists elsewhere in that + 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. + + **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 bbd88ec..4b59ff4 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -154,6 +154,97 @@ 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, 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 + 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. *) +(* 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") ]) + | _ -> 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 +822,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 diff --git a/test/test_golden.ml b/test/test_golden.ml index 0f90ee7..594ea6b 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -94,6 +94,8 @@ 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 subject_s (c : V.rank Cel.t) = Colitur_kernel.Subject.to_string c.Cel.subject +let la = Colitur_kernel.Lang.of_string_exn "la" +let name_la_s (c : V.rank Cel.t) = match Colitur_kernel.Names.find c.Cel.names la with Some n -> n | None -> "-" let privilege_s = function Prec.Privileged -> "privileged" | Prec.Ordinary -> "ordinary" (* fix round 1 (coordinator finding 4): [subject] added. Two of the three @@ -107,7 +109,19 @@ let privilege_s = function Prec.Privileged -> "privileged" | Prec.Ordinary -> "o 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. *) + why one string, not five checks. + + ef-triduum-litanies task (Gap 1), same lesson applied pre-emptively rather + than caught after the fact: [name_la] added for the SAME reason [subject] + was -- a names-only change (the Sacred Triduum's own Latin identity, RG 91 + entry 2) would otherwise be invisible to every pin whose rank/colour/slug + are already correct (this project's own catalogued vacuity flavour #1, + "an assertion already true before the code under test ran" -- this task's + own hazard, stated in its brief). Read off [Cel.names]'s own Latin entry + (the same field/language {!Cel.of_list}'s callers in temporal_ef.ml use + for Holy Family/Holy Name/the Triduum -- no temporal-cycle candidate + currently carries any OTHER language), "-" for none, matching [week]'s + own None-as-"-" convention immediately above. *) 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 @@ -120,12 +134,12 @@ let describe (day : (V.season, V.rank) LD.t) = 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 subject=%s comms=[%s] in=%s out=[%s]" + Printf.sprintf "%s %s season=%s week=%s slug=%s rank=%s colour=%s subject=%s name_la=%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) - (subject_s day.LD.observed) comms tin tout + (subject_s day.LD.observed) (name_la_s day.LD.observed) comms tin tout let check ~msg y m d expected = Alcotest.(check string) msg expected (describe (fetch y m d)) @@ -150,11 +164,11 @@ let omitted_has (day : (V.season, V.rank) LD.t) slug = 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 subject=temporal \ - comms=[] in=- out=[]"; + "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=[]"; 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 comms=[] in=- \ + "1598-03-22 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ out=[]" (* 1666: latest Gregorian Easter possible, 25 April -- hand-verified via @@ -162,11 +176,11 @@ let test_easter_extreme_1598 () = 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 subject=temporal \ - comms=[] in=- out=[]"; + "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=[]"; 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 comms=[] in=- \ + "1666-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ out=[]" (* 2038: the brief's "late modern case" -- Easter also 25 April (Gauss- @@ -191,10 +205,10 @@ let test_easter_extreme_1666 () = though nothing forces that any more). *) 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=temporal \ - comms=[] in=- out=[]"; + "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=[]"; 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 comms=[] in=- \ + "2038-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ out=[]" (* ------------------------------------------------------------------ *) @@ -215,15 +229,15 @@ let test_annunciation_transfer_inside_holy_week_2016 () = 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 subject=temporal \ - comms=[] in=- out=[annunciation-of-the-blessed-virgin-mary->2016-04-04]"; + "2016-03-25 friday season=passiontide week=2 slug=ef-passiontide-2-friday rank=class-1 colour=violet subject=lord \ + name_la=Feria VI in Passione et Morte Domini 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 subject=saint comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]" + colour=white subject=saint name_la=- comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]" (* ------------------------------------------------------------------ *) (* 2008: the double transfer (register §4, "cases worth adding"). Easter = @@ -239,7 +253,7 @@ let test_annunciation_joseph_double_transfer_2008 () = 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 subject=temporal \ - comms=[] in=- out=[joseph-spouse-of-the-bl-virgin-mary->2008-04-01]"; + name_la=- 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 @@ -266,7 +280,7 @@ let test_annunciation_joseph_double_transfer_2008 () = 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 subject=saint comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]"; + colour=white subject=saint name_la=- 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 @@ -275,7 +289,7 @@ let test_annunciation_joseph_double_transfer_2008 () = 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 subject=saint comms=[] in=joseph-spouse-of-the-bl-virgin-mary out=[]" + colour=white subject=saint name_la=- comms=[] in=joseph-spouse-of-the-bl-virgin-mary out=[]" (* ------------------------------------------------------------------ *) (* RG 96 Attamen (a) is CONDITIONAL ("quando est transferendum post @@ -299,15 +313,15 @@ 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 subject=saint comms=[ef-lent-3-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]"; + colour=white subject=saint name_la=- 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 subject=saint comms=[ef-passiontide-1-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]"; + colour=white subject=saint name_la=- 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 subject=saint comms=[ef-passiontide-1-monday:privileged] in=annunciation-of-the-blessed-virgin-mary out=[]" + colour=white subject=saint name_la=- 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 @@ -323,7 +337,7 @@ let test_annunciation_exception_triggered_when_walk_would_cross_easter () = (RG26)" 2024 4 8 "2024-04-08 monday season=paschaltide week=2 slug=annunciation-of-the-blessed-virgin-mary rank=class-1 \ - colour=white subject=saint comms=[] in=annunciation-of-the-blessed-virgin-mary out=[]" + colour=white subject=saint name_la=- 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. *) @@ -342,18 +356,18 @@ 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 subject=temporal \ - comms=[] in=- out=[precious-blood-of-our-lord-jesus-christ->2011-07-04]"; + name_la=- 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 subject=saint comms=[processus-and-martinian:ordinary] in=- out=[]"; + rank=class-2 colour=white subject=saint name_la=- 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 subject=temporal comms=[] in=- out=[]"; + colour=green subject=temporal name_la=- 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 subject=lord comms=[] in=precious-blood-of-our-lord-jesus-christ out=[]" + rank=class-1 colour=red subject=lord name_la=- comms=[] in=precious-blood-of-our-lord-jesus-christ out=[]" (* ------------------------------------------------------------------ *) (* All Souls falling on a Sunday (register §4, RG96 Attamen (b), primary- @@ -368,11 +382,11 @@ let test_all_souls_on_a_sunday_2025 () = 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 subject=temporal comms=[] in=- out=[commemoration-of-all-souls->2025-11-03]"; + colour=green subject=temporal name_la=- 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 subject=saint comms=[] in=commemoration-of-all-souls out=[]" + colour=black subject=saint name_la=- comms=[] in=commemoration-of-all-souls out=[]" (* ------------------------------------------------------------------ *) (* Christmas falling on a Sunday (RG91 entry 1: Nativity, Easter, Pentecost @@ -386,7 +400,7 @@ let test_all_souls_on_a_sunday_2025 () = 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 subject=temporal comms=[] in=- \ + "2022-12-25 sunday season=christmastide week=- slug=ef-nativity rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ out=[]" (* ------------------------------------------------------------------ *) @@ -400,18 +414,18 @@ let test_christmas_on_a_sunday_2022 () = 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 subject=temporal \ - comms=[] in=- out=[]"; + name_la=- 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 subject=temporal \ - comms=[] in=- out=[]"; + "2026-04-02 thursday season=passiontide week=2 slug=ef-passiontide-2-thursday rank=class-1 colour=white subject=lord \ + name_la=Feria V in Cena Domini 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 subject=temporal \ - comms=[] in=- out=[]" + "2026-04-03 friday season=passiontide week=2 slug=ef-passiontide-2-friday rank=class-1 colour=violet subject=lord \ + name_la=Feria VI in Passione et Morte Domini comms=[] in=- out=[]" (* ------------------------------------------------------------------ *) (* Advent/Lent Ember ferias are commemorated when impeded (RG 24: "si vero @@ -431,7 +445,7 @@ let test_ember_ferias_commemorated_when_impeded () = 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 subject=saint \ - comms=[ef-advent-ember-fri:privileged] in=- out=[]"; + name_la=- 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. ALSO, ef-holyname-rg110 task: RG 110's own @@ -448,7 +462,7 @@ let test_ember_ferias_commemorated_when_impeded () = AND RG110's Paul companion rides alongside it, uncapped" 1902 2 22 "1902-02-22 saturday season=lent week=1 slug=chair-of-st-peter rank=class-2 colour=white subject=saint \ - comms=[ef-lent-ember-sat:privileged,paul:ordinary] in=- out=[]" + name_la=- comms=[ef-lent-ember-sat:privileged,paul:ordinary] in=- out=[]" let test_iv_class_ferias_never_commemorated () = (* 2026-07-22: an ordinary Time-after-Pentecost feria (Class4, RG91 entry @@ -465,14 +479,14 @@ let test_iv_class_ferias_never_commemorated () = (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 subject=saint \ - comms=[] in=- out=[]" + name_la=- 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 subject=saint comms=[] in=- \ + "2026-08-10 monday season=time-after-pentecost week=11 slug=lawrence rank=class-2 colour=red subject=saint name_la=- comms=[] in=- \ out=[]" (describe d2) @@ -497,7 +511,7 @@ let test_ii_class_sunday_admits_only_ii_class_commemoration () = (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 subject=temporal comms=[] in=- out=[]" + colour=green subject=temporal name_la=- 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" @@ -505,7 +519,7 @@ let test_ii_class_sunday_admits_only_ii_class_commemoration () = (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 subject=temporal comms=[] in=- out=[]" + colour=green subject=temporal name_la=- comms=[] in=- out=[]" (describe d2026) (* ------------------------------------------------------------------ *) @@ -570,7 +584,7 @@ let test_rg16a_lord_feast_no_sunday_commemoration_2028 () = 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 subject=lord comms=[] in=- out=[]" + colour=white subject=lord name_la=- comms=[] in=- out=[]" (describe d) (* Holy Family (RG 17(b), scan-verified, docs/research/rules-register.md @@ -629,20 +643,20 @@ let test_holy_family_ordinary_year_2026 () = standing for the day's II-class-only Sunday slot" 2026 1 11 "2026-01-11 sunday season=christmastide week=- slug=ef-time-after-epiphany-sunday-1 rank=class-2 \ - colour=white subject=lord comms=[] in=- out=[]" + colour=white subject=lord name_la=Sanctae Familiae Iesu, Mariae, Ioseph comms=[] in=- out=[]" let test_holy_family_excludes_baptism_2030 () = check ~msg:"2030-01-13: Holy Family on the latest possible date -- RG91 e14 \"primum mobilia\" keeps it \ observed over the fixed Baptism, RG112(a) excludes the Baptism entirely, not merely demotes it" 2030 1 13 "2030-01-13 sunday season=christmastide week=- slug=ef-time-after-epiphany-sunday-1 rank=class-2 \ - colour=white subject=lord comms=[] in=- out=[]" + colour=white subject=lord name_la=Sanctae Familiae Iesu, Mariae, Ioseph comms=[] in=- out=[]" let test_holy_family_earliest_possible_date_2024 () = check ~msg:"2024-01-07: Holy Family on the earliest possible date (Epiphany a Saturday)" 2024 1 7 "2024-01-07 sunday season=christmastide week=- slug=ef-time-after-epiphany-sunday-1 rank=class-2 \ - colour=white subject=lord comms=[] in=- out=[]" + colour=white subject=lord name_la=Sanctae Familiae Iesu, Mariae, Ioseph comms=[] in=- out=[]" (* Most Holy Name of Jesus (RG 17(a), scan-verified, docs/research/rules- register.md §6.2/§4): "festum Ss.mi Nominis Iesu, celebrandum dominica @@ -679,7 +693,7 @@ let test_holy_name_sunday_shape_2026 () = 2026-01-04 row: rank 2, white, title \"Holy Name of Jesus\")" 2026 1 4 "2026-01-04 sunday season=christmastide week=- slug=ef-holy-name-sunday rank=class-2 colour=white \ - subject=lord comms=[] in=- out=[]" + subject=lord name_la=Sanctissimi Nominis Iesu comms=[] in=- out=[]" let test_holy_name_fallback_2029 () = check ~msg:"2029-01-02: Holy Name of Jesus, FALLBACK shape (RG 17(a)'s own \"secus die 2 ianuarii\", the \ @@ -689,7 +703,7 @@ let test_holy_name_fallback_2029 () = here instead -- a real, II-class office simply missing, not merely misnamed" 2029 1 2 "2029-01-02 tuesday season=christmastide week=- slug=ef-holy-name rank=class-2 colour=white \ - subject=lord comms=[] in=- out=[]" + subject=lord name_la=Sanctissimi Nominis Iesu comms=[] in=- out=[]" (* RG 110 (docs/research/rules-register.md §4, Caput XIV), fix round 1 (coordinator findings F1/F2): two golden pins neither existed before this @@ -725,7 +739,7 @@ let test_rg110_june_30_2026 () = commemoration -- missalemeum shows neither (M19, verdict colitur)" 2026 6 30 "2026-06-30 tuesday season=time-after-pentecost week=5 slug=in-commemoratione-sancti-pauli-apostoli \ - rank=class-3 colour=red subject=saint comms=[commemoration-of-st-peter:ordinary] in=- out=[]" + rank=class-3 colour=red subject=saint name_la=- comms=[commemoration-of-st-peter:ordinary] in=- out=[]" let test_rg110_shape_c_2004 () = check ~msg:"2004-02-22: RG 110 shape (c) -- Chair of St Peter itself loses the day to an ordinary \ @@ -734,7 +748,7 @@ let test_rg110_shape_c_2004 () = Sunday, independently confirmed)" 2004 2 22 "2004-02-22 sunday season=septuagesima week=3 slug=ef-septuagesima-sunday-3 rank=class-2 colour=violet \ - subject=temporal comms=[chair-of-st-peter:ordinary,paul:ordinary] in=- out=[]" + subject=temporal name_la=- comms=[chair-of-st-peter:ordinary,paul:ordinary] in=- out=[]" (* 2025-02-02, the other real instance of the SAME pattern -- not a contrasting second direction, per this task's fix round 1 (CRITICAL @@ -767,7 +781,7 @@ let test_purification_on_a_sunday_2025 () = 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 subject=lord comms=[] in=- out=[]" + rank=class-2 colour=white subject=lord name_la=- comms=[] in=- out=[]" (* ------------------------------------------------------------------ *) (* RG 91 entry 24 ("Festa III classis, in calendario Ecclesiae universae @@ -826,15 +840,15 @@ let test_iii_class_feast_wins_class4_feria () = true (omitted_has d1 "ef-easter-2-wednesday"); Alcotest.(check string) "2008-04-02: Francis of Paola (III class, RG91 entry 24) outranks the class-4 feria" - "2008-04-02 wednesday season=paschaltide week=2 slug=francis-of-paola rank=class-3 colour=white subject=saint comms=[] \ + "2008-04-02 wednesday season=paschaltide week=2 slug=francis-of-paola rank=class-3 colour=white subject=saint name_la=- comms=[] \ in=- out=[]" (describe d1); check ~msg:"2008-04-04: Isidore of Seville (III class) outranks the class-4 Paschaltide feria, same shape" 2008 4 4 - "2008-04-04 friday season=paschaltide week=2 slug=isidore-of-seville rank=class-3 colour=white subject=saint comms=[] in=- \ + "2008-04-04 friday season=paschaltide week=2 slug=isidore-of-seville rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \ out=[]"; check ~msg:"2008-04-05: Vincent Ferrer (III class) outranks the class-4 Paschaltide feria, same shape" 2008 4 5 - "2008-04-05 saturday season=paschaltide week=2 slug=vincent-ferrer rank=class-3 colour=white subject=saint comms=[] in=- \ + "2008-04-05 saturday season=paschaltide week=2 slug=vincent-ferrer rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \ out=[]"; let d4 = fetch 2038 3 6 in Alcotest.(check bool) @@ -844,15 +858,15 @@ let test_iii_class_feast_wins_class4_feria () = Alcotest.(check string) "2038-03-06: Sts Perpetua and Felicity (III class, RG91 entry 24) outrank the class-4 feria; RED per their \ own martyrs' colour, not the season's violet" - "2038-03-06 saturday season=septuagesima week=2 slug=sts-felicitas-perpetua rank=class-3 colour=red subject=saint comms=[] \ + "2038-03-06 saturday season=septuagesima week=2 slug=sts-felicitas-perpetua rank=class-3 colour=red subject=saint name_la=- comms=[] \ in=- out=[]" (describe d4); check ~msg:"2038-03-08: John of God (III class) outranks the class-4 Septuagesima feria, same shape" 2038 3 8 - "2038-03-08 monday season=septuagesima week=3 slug=john-of-god rank=class-3 colour=white subject=saint comms=[] in=- \ + "2038-03-08 monday season=septuagesima week=3 slug=john-of-god rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \ out=[]"; check ~msg:"2038-03-09: Frances of Rome (III class) outranks the class-4 Septuagesima feria, same shape" 2038 3 9 - "2038-03-09 tuesday season=septuagesima week=3 slug=frances-rome rank=class-3 colour=white subject=saint comms=[] in=- \ + "2038-03-09 tuesday season=septuagesima week=3 slug=frances-rome rank=class-3 colour=white subject=saint name_la=- comms=[] in=- \ out=[]" let suite = diff --git a/test/test_temporal_ef.ml b/test/test_temporal_ef.ml index f341533..5394173 100644 --- a/test/test_temporal_ef.ml +++ b/test/test_temporal_ef.ml @@ -732,6 +732,87 @@ let test_holy_name_fallback_anchor_present_and_erosion_is_caught () = Alcotest.(check string) "sanity: temporal itself is unaffected by the anchors-list erosion" expected_slug (slug_of expected_date) +(* Gap 1 (ef-triduum-litanies, docs/research/rules-register.md, "the Sacred + Triduum has no proper office"): the Missal's own Mass-propers headings for + Holy Thursday/Good Friday/Holy Saturday, both photographic scans, word for + word (missale-romanum-1962.pdf and "Missale Romanum 1962_text.pdf", + corroborated by the electronic transcription's own table-of-contents-style + listing at the identical three headings -- three-way agreement, no + scan-vs-transcription conflict to adjudicate): "FERIA QUINTA IN CENA + DOMINI" / running header "Feria V in Cena Domini"; "FERIA SEXTA / IN + PASSIONE ET MORTE DOMINI"; "SABBATO SANCTO". The slug stays the generic + Passiontide ferial one (RG 91 entry 2 already identifies the Triduum + structurally, off the day's own Easter offset and rank -- + {!Rite_ef.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 missing IDENTITY is carried in [names], the same treatment RG + 17(b)/(a) (Holy Family/Holy Name) already established -- Latin, not + English, for the same zero-circularity reason those two entries give + (would otherwise read the oracle this exact axis is compared against). + + [subject] is ALSO set to [Lord] here, going further than Holy Family/Holy + Name's own precedent strictly required (they needed it because + {!Rite_ef.Precedence_ef.band}'s entry 14 reads [subject] to outrank a + colliding fixed feast). The Triduum has no such live need: entry 2 is + decided purely by [rank]/Easter-offset, never [subject] -- confirmed by + reading {!Rite_ef.Precedence_ef.band}'s own entry-2 branch, which tests + neither. Tagged anyway because it is simply true (the Last Supper, the + Passion and Death, the entombment are textbook mysteries of the Lord) and + safe: {!Rite_ef.Precedence_ef.disposition}'s own RG 112(a) branch (Lord vs + Lord) only fires when BOTH sides are tagged [Lord], and no [Lord]-subject + 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, 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 + 112(a)'s own branch is never reached for it regardless of this tag. + Measured empirically too: this task's own full 1583-9999 blast-radius + sweep shows no [observed]/[commemorations]/[transferred_*] difference + traceable to this tag anywhere in the domain. *) +let test_sacred_triduum_identity () = + (* 2026: Easter 5 April, so Holy Thursday/Good Friday/Holy Saturday are + 2/3/4 April. *) + Alcotest.(check string) "2026-04-02 Holy Thursday: slug stays the generic ferial key" + "ef-passiontide-2-thursday" (slug_of (d 2026 4 2)); + Alcotest.(check bool) "2026-04-02 Holy Thursday: subject Lord" true + (subject_of (d 2026 4 2) = Sub.Lord); + Alcotest.(check (option string)) "2026-04-02 Holy Thursday: Latin name, both photographic scans verbatim" + (Some "Feria V in Cena Domini") (name_la_of (d 2026 4 2)); + Alcotest.(check string) "2026-04-03 Good Friday: slug stays the generic ferial key" + "ef-passiontide-2-friday" (slug_of (d 2026 4 3)); + Alcotest.(check bool) "2026-04-03 Good Friday: subject Lord" true + (subject_of (d 2026 4 3) = Sub.Lord); + Alcotest.(check (option string)) "2026-04-03 Good Friday: Latin name, both photographic scans verbatim" + (Some "Feria VI in Passione et Morte Domini") (name_la_of (d 2026 4 3)); + Alcotest.(check string) "2026-04-04 Holy Saturday: slug stays the generic ferial key" + "ef-passiontide-2-saturday" (slug_of (d 2026 4 4)); + 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)); + (* 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" + None (name_la_of (d 2026 4 1)); + Alcotest.(check bool) "2026-04-01 (Wednesday of Holy Week): subject stays temporal" true + (subject_of (d 2026 4 1) = Sub.Temporal); + Alcotest.(check (option string)) "2026-04-05 Easter Sunday: no Latin name from this branch" + None (name_la_of (d 2026 4 5)); + (* A second, independent year (1666: the latest possible Easter, 25 April, + already this file's own Easter-extreme witness elsewhere) -- proves the + mechanism generalises off [days_between easter d], not hand-pinned to + 2026's own civil dates. Holy Thursday/Good Friday/Holy Saturday are + 22/23/24 April 1666. *) + Alcotest.(check (option string)) "1666-04-22 Holy Thursday (latest-Easter year): Latin name still fires" + (Some "Feria V in Cena Domini") (name_la_of (d 1666 4 22)); + Alcotest.(check bool) "1666-04-22 Holy Thursday: subject Lord" true + (subject_of (d 1666 4 22) = Sub.Lord) + 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, @@ -765,6 +846,8 @@ let suite_extra = Alcotest.test_case "ember days" `Quick test_ember_days; Alcotest.test_case "rogations" `Quick test_rogations; 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 "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; |
