summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/rites/rite_ef/temporal_ef.ml74
-rw-r--r--test/test_golden.ml124
-rw-r--r--test/test_temporal_ef.ml81
3 files changed, 223 insertions, 56 deletions
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
diff --git a/test/test_golden.ml b/test/test_golden.ml
index 0f90ee7..68478ec 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..a979700 100644
--- a/test/test_temporal_ef.ml
+++ b/test/test_temporal_ef.ml
@@ -732,6 +732,85 @@ 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, August, September or November);
+ 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 +844,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;