summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/kernel/calendar.ml4
-rw-r--r--lib/kernel/liturgical_day.ml1
-rw-r--r--lib/kernel/liturgical_day.mli4
-rw-r--r--lib/kernel/rite.ml1
-rw-r--r--lib/kernel/rite.mli6
-rw-r--r--lib/rites/rite_ef/rite_ef.ml3
-rw-r--r--lib/rites/rite_ef/rite_ef.mli3
-rw-r--r--lib/rites/rite_ef/rubrics_ef.ml378
-rw-r--r--lib/rites/rite_ef/rubrics_ef.mli30
9 files changed, 429 insertions, 1 deletions
diff --git a/lib/kernel/calendar.ml b/lib/kernel/calendar.ml
index db6c58e..7178780 100644
--- a/lib/kernel/calendar.ml
+++ b/lib/kernel/calendar.ml
@@ -551,6 +551,9 @@ let build_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.index)
let creed =
rite.Rite.creed ~temporal ~observed:resolution.Precedence.observed.Precedence.cel ~date
in
+ let gloria =
+ rite.Rite.gloria ~temporal ~observed:resolution.Precedence.observed.Precedence.cel ~date
+ in
{
Liturgical_day.date;
rite = rite.Rite.id;
@@ -564,6 +567,7 @@ let build_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.index)
citations;
formulary;
creed;
+ gloria;
}
let year (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) (y : int) :
diff --git a/lib/kernel/liturgical_day.ml b/lib/kernel/liturgical_day.ml
index 09ac0a1..98c5084 100644
--- a/lib/kernel/liturgical_day.ml
+++ b/lib/kernel/liturgical_day.ml
@@ -27,5 +27,6 @@ type ('s, 'r) t = {
(** which Mass the day says, and how that was decided; [None] only for
a rite with no lectionary -- see {!Mass_formulary} *)
creed : bool; (** whether the Creed is said at this day's Mass; see {!Rite.t.creed} *)
+ gloria : bool; (** whether the Gloria in excelsis is said; see {!Rite.t.gloria} *)
}
[@@deriving sexp]
diff --git a/lib/kernel/liturgical_day.mli b/lib/kernel/liturgical_day.mli
index eb45e61..939f8f5 100644
--- a/lib/kernel/liturgical_day.mli
+++ b/lib/kernel/liturgical_day.mli
@@ -37,5 +37,9 @@ type ('s, 'r) t = {
(** Whether the Creed is said at this day's Mass -- {!Rite.t.creed},
EF: RG 475-476. A decision, not an [option]: [false] for a rite
that has not implemented the rule, same as [creed] itself. *)
+ gloria : bool;
+ (** Whether the Gloria in excelsis is said at this day's Mass --
+ {!Rite.t.gloria}, EF: RG 431-432. Same seam as [creed] in every
+ respect. *)
}
[@@deriving sexp]
diff --git a/lib/kernel/rite.ml b/lib/kernel/rite.ml
index 738785a..915941d 100644
--- a/lib/kernel/rite.ml
+++ b/lib/kernel/rite.ml
@@ -20,4 +20,5 @@ type ('s, 'r) t = {
temporal_at:(Date.t -> ('s, 'r) Temporal.t) ->
Mass_formulary.t option * Citation.t list;
creed : temporal:('s, 'r) Temporal.t -> observed:'r Celebration.t -> date:Date.t -> bool;
+ gloria : temporal:('s, 'r) Temporal.t -> observed:'r Celebration.t -> date:Date.t -> bool;
}
diff --git a/lib/kernel/rite.mli b/lib/kernel/rite.mli
index 0dcf17e..b9d1961 100644
--- a/lib/kernel/rite.mli
+++ b/lib/kernel/rite.mli
@@ -134,4 +134,10 @@ type ('s, 'r) t = {
Easter-relative window (e.g. "within the octave of Easter") needs
the civil date and the rite's own Easter to test it, and neither
[temporal] nor [observed] alone carries that arithmetic. *)
+ gloria : temporal:('s, 'r) Temporal.t -> observed:'r Celebration.t -> date:Date.t -> bool;
+ (** Whether the Gloria in excelsis is said at this day's Mass (EF: RG
+ 431-432). Same seam as {!creed} in every respect: same three
+ parameters and the same reasons for each, a [bool] not an
+ [option], and [false] is the answer a rite that has not
+ implemented the rule returns explicitly. *)
}
diff --git a/lib/rites/rite_ef/rite_ef.ml b/lib/rites/rite_ef/rite_ef.ml
index ba1274b..d7b3d83 100644
--- a/lib/rites/rite_ef/rite_ef.ml
+++ b/lib/rites/rite_ef/rite_ef.ml
@@ -51,4 +51,5 @@ let context ~lectionary ~commons : (Vocab_ef.season, Vocab_ef.rank) Rite.t =
season_runs = Vocab_ef.seasons;
transfer_target = Precedence_ef.transfer_target;
readings = Lectionary_ef.readings ~lectionary ~commons;
- creed = Rubrics_ef.creed }
+ creed = Rubrics_ef.creed;
+ gloria = Rubrics_ef.gloria }
diff --git a/lib/rites/rite_ef/rite_ef.mli b/lib/rites/rite_ef/rite_ef.mli
index a4b5d87..7025143 100644
--- a/lib/rites/rite_ef/rite_ef.mli
+++ b/lib/rites/rite_ef/rite_ef.mli
@@ -36,6 +36,9 @@ module Rubrics_ef = Rubrics_ef
- [creed]: {!Rubrics_ef.creed}, RG 475-476 -- whether the Creed is said.
The first rubric in this phase governing a part of Mass rather than
occurrence/precedence.
+ - [gloria]: {!Rubrics_ef.gloria}, RG 431-432 -- whether the Gloria in
+ excelsis is said. Reads {!Rubrics_ef.te_deum} (Breviary nn. 237-238)
+ for RG 431(a)/432(a)'s own deferral.
Deliberately carries no [sanctoral]/[lectionary] fields the way the
original design-doc sketch of [RITE] does: {!Colitur_kernel.Rite.t} (the
diff --git a/lib/rites/rite_ef/rubrics_ef.ml b/lib/rites/rite_ef/rubrics_ef.ml
index ab9da32..a497b57 100644
--- a/lib/rites/rite_ef/rubrics_ef.ml
+++ b/lib/rites/rite_ef/rubrics_ef.ml
@@ -319,3 +319,381 @@ let creed ~(temporal : (Vocab_ef.season, Vocab_ef.rank) Temporal.t)
entry on the list (Sts Peter & Paul) is already [true] via 475(b),
so this branch is redundant, never wrong, for those. *)
List.mem slug creed_apostle_slugs
+
+(* Breviarium Romanum, 1961 Codex Rubricarum, "N) De hymno Te Deum"
+ (docs/research/breviary/rubricae-breviarii-1961.txt; docs/research/
+ breviary/PROVENANCE.md has this source's own provenance and weakness in
+ full), quoted here in full so every branch below can cite its own
+ letter without re-quoting the whole rubric:
+
+ "237. Hymnus Te Deum dicitur ad Matutinum, post ultimam lectionem, loco
+ noni vel tertii responsorii:
+ a) in dominica in albis, in dominica Pentecostes, et in Matutino
+ dominicae Resurrectionis, quod recitatur ab iis qui Vigiliae paschali
+ non interfuerunt;
+ b) in dominicis II classis, exceptis dominicis in Septuagesima, in
+ Sexagesima et in Quinquagesima;
+ c) in omnibus festis;
+ d) per octavas Nativitatis Domini, Paschatis et Pentecostes;
+ e) in Officio feriali temporis natalicii et temporis paschalis;
+ f) in vigiliis Ascensionis et Pentecostes;
+ g) in Officio sanctae Mariae in sabbato.
+
+ 238. Omittitur vero hymnus Te Deum:
+ a) in Officiis de Tempore a dominica I Adventus usque ad vigiliam
+ Nativitatis Domini inclusive; et a dominica in Septuagesima usque ad
+ Sabbatum sanctum inclusive;
+ b) in vigiliis II et III classis, excepta vigilia Ascensionis
+ Domini;
+ c) in omnibus feriis per annum;
+ d) in Officio defunctorum."
+
+ THE WEAKNESS, restated (PROVENANCE.md has the full account): this is a
+ SINGLE WEB TRANSCRIPTION (ceremoniaire.net), not yet checked against a
+ photographic scan -- the weakest-sourced rule in this project. Mitigated,
+ not resolved, by the FIUV universal Ordo's own Te Deum column
+ (test/fixtures/fiuv-ordo-2025-2026.sexp, 262 "Te Deum" rows out of 400 --
+ an independent day-level witness, never itself derived from this
+ transcription) -- see test_fiuv_ordo.ml, which compares [te_deum]'s own
+ output against it. A mismatch there may indict this transcription rather
+ than [te_deum]; adjudicated per that file's own allow-list, not assumed
+ either way.
+
+ SCOPE: this predicate exists ONLY because RG 431(a) below defers a MASS
+ question to it. Building it is not the Divine Office arriving in scope --
+ CLAUDE.md's own "Divine Office remains out of scope" line, and the
+ 2026-08-21 design spec's own SS1, are both unchanged: this borrows ONE
+ Breviary FACT per day (whether Te Deum was said at Matins), never models
+ Matins/Vespers/the psalter/concurrence. *)
+let te_deum ~(temporal : (Vocab_ef.season, Vocab_ef.rank) Temporal.t)
+ ~(observed : Vocab_ef.rank Celebration.t) ~(date : Date.t) : bool =
+ let easter = Computus.gregorian_easter (Date.year date) in
+ let n = Date.to_rata date - Date.to_rata easter in
+ let m = Date.month date and dd = Date.day date in
+ let slug = Slug.to_string observed.Celebration.slug in
+ if
+ (* 238(d): "in Officio defunctorum" -- checked first, the same position
+ and the same {!Colour.Black} proxy [creed]'s own 476(f) branch uses
+ (this file's own header has the full argument for why colour is a
+ sound proxy for "this is a Requiem" on the shipped data, and
+ {!test_colour_black_population_is_exactly_two} in test_rubrics_ef.ml
+ is the SAME two-member population this predicate also depends on --
+ no separate test needed). *)
+ observed.Celebration.colour = Colour.Black
+ then false
+ else if
+ (* 237(a): the three explicitly named Paschaltide days -- Easter
+ Sunday's own Matins (n=0), Low Sunday (n=7), Pentecost Sunday
+ (n=49). Pure Easter-offset arithmetic, the same style [creed]'s own
+ 475(d) uses and for the identical reason: season/rank alone cannot
+ express "this exact day", and nothing else in 237/238 names these
+ three individually.
+
+ HISTORY WORTH KEEPING: this task's own FIRST pass REPLACED this
+ branch (and 237(b) below) with a blanket "every Sunday" rule,
+ having found what looked like a clean 15-for-15 FIUV Ordo
+ contradiction of 237(b)'s own Septuagesima/Sexagesima/Quinquagesima
+ exception. That evidence was ITSELF corrupted: tools/extract_fiuv_
+ ordo.ml's own Te Deum parser recognised only ONE of the source's two
+ negative phrasings ("non dicitur Te Deum"), so every "sine Te Deum"
+ occurrence -- which is how the source actually negates a SUNDAY's
+ own Te Deum, found only by reading the raw pdftotext dump by hand,
+ not by trusting the fixture's own coverage counts -- fell through to
+ a bare "Te Deum" substring match and was wrongly recorded [true].
+ Fixed in the extractor (see its own citation, tools/extract_fiuv_
+ ordo.ml); the fixture was regenerated; 24 of the fixture's 400 rows
+ flipped, EVERY ONE true->false, EVERY ONE a day this branch or
+ 237(b) below governs. The blanket rule was reverted the moment the
+ corrected data confirmed the ORIGINAL literal reading instead:
+ 238(a)'s own window DOES silence Advent/Septuagesima/Lent/
+ Passiontide Sundays after all. Left as a worked example, not
+ scrubbed from history: the failure mode was believing a clean-
+ looking oracle correlation over re-deriving the primary text,
+ exactly backwards from what "adjudicate, don't assume" should have
+ produced -- caught only by cross-checking the raw source directly
+ once the shape looked suspiciously total. *)
+ n = 0 || n = 7 || n = 49
+ then true
+ else if
+ (* 237(f): the vigils of Ascension (n=38) and Pentecost (n=48), checked
+ BEFORE 238(b)'s general vigil omission below -- both are otherwise
+ reachable by it (Ascension's vigil is [Class2], squarely inside
+ 238(b)'s own "II et III classis"; Pentecost's is [Class1], RG 91
+ entry 9, so 238(b) could never have reached it regardless, but is
+ named here anyway rather than left to fall through to 237(c), the
+ same "cite the specific clause, not a catch-all" discipline this
+ whole module holds to). *)
+ n = 38 || n = 48
+ then true
+ else if
+ (* 237(d): the three privileged octaves -- reuses [creed]'s own three
+ windows verbatim (that function's own 475(d) comment has the full
+ citation and the argument for why a season- or rank-based test
+ would wrongly include the Ascension/Pentecost-Vigil days this
+ window must exclude). Overlaps 237(a) at n=0/7/49 -- redundant, not
+ wrong, the same "never the FIRST branch to grant those [true]"
+ pattern 475(b) documents for I-class Sundays. *)
+ (m = 12 && dd >= 25 && dd <= 31)
+ || (m = 1 && dd = 1)
+ || (n >= 0 && n <= 7)
+ || (n >= 49 && n <= 56)
+ then true
+ else if
+ (* 238(b): "vigiliis II et III classis" -- {!Precedence_ef.is_omissible_vigil}
+ is exactly this rank test (Class2 or Class3), reused rather than
+ re-derived, paired with {!Precedence_ef.is_vigil} the same way
+ [creed]'s own RG 28-34 branch already pairs them. The Ascension
+ vigil (237(f) above, already [true]) can never reach this branch;
+ the four sanctoral vigils (StJohnBaptist, SsPeter&Paul, StLawrence,
+ the Assumption -- {!Precedence_ef.vigil_feast_table}'s own
+ population) are exactly what this branch excludes. *)
+ Precedence_ef.is_omissible_vigil observed.Celebration.rank && Precedence_ef.is_vigil slug
+ then false
+ else if
+ (* NOT 238(b)'s own text (which names only "II et III classis"): the
+ Nativity Vigil ([Class1], RG 91 entry 5) is excluded here on the
+ SAME RG 21/35 taxonomy argument [creed]'s own RG 28-34 comment
+ already makes for the Paschal Vigil -- "vigilia" is its own
+ liturgical-day category, distinct from "festum", regardless of
+ class; RG 30's "beyond losing" is a PRECEDENCE exemption (nothing
+ lesser can displace it), not a claim that a vigil IS a festum for
+ Breviary purposes. Flagged honestly as an INFERENCE, not a literal
+ 238(b) citation -- checked against the FIUV Ordo's own Te Deum
+ marker for 24 December in test_fiuv_ordo.ml, since this is exactly
+ the shape a transcription gap could get wrong either direction. *)
+ Precedence_ef.is_vigil slug
+ then false
+ else if
+ (* 238(c)/RG 23: Ash Wednesday and every feria of Holy Week, the Sacred
+ Triduum included -- reuses [creed]'s own RG 23 test verbatim (that
+ function's own comment has the full citation and the argument for
+ why this excludes feria I classis specifically, not ferias in
+ general). *)
+ n = -46 || (n >= -6 && n <= -1)
+ then false
+ else if
+ (* 237(b): "in dominicis II classis, exceptis dominicis in Septuagesima,
+ in Sexagesima et in Quinquagesima" -- colitur's own single
+ [Septuagesima] season covers exactly those three Sundays (see
+ [season_colour]'s own grouping, temporal_ef.ml), so the exception is
+ one season-equality test. RE-VERIFIED, not merely restored: the
+ corrected FIUV extraction (237(a)'s own comment above has the full
+ account) shows all three Septuagesima-season Sundays [false], and
+ every OTHER, ordinary Class2 Sunday the fixture's window reaches
+ [true] -- exactly this clause's own literal text, no correction
+ needed here after all. Reads [temporal.weekday]/[.season], not
+ [observed]'s slug: unlike [creed]'s own 475(a), the Breviary's text
+ carries NO "even when a feast displaces the Sunday's own Office"
+ exception -- when a feast genuinely takes the Sunday's place (RG
+ 16(a)), Matins says the FEAST's own Office, and 237(c) below decides
+ it on the feast's own terms, not this clause. Advent/Lent/
+ Passiontide Sundays are [Class1], never [Class2] ([creed]'s own RG
+ 11-12 citation, temporal_ef.ml), so this guard correctly excludes
+ them without a separate season check; [observed.rank], not
+ [temporal]'s own season-derived rank, is read here on purpose, for
+ the same RG 16(a) reason [Precedence.rules.admit]'s own
+ [~temporal] parameter exists: a feast that wins the day can carry a
+ DIFFERENT rank than the Sunday it displaced. *)
+ temporal.Temporal.weekday = Date.Sun
+ && observed.Celebration.rank = Vocab_ef.Class2
+ && temporal.Temporal.season <> Vocab_ef.Septuagesima
+ then true
+ else if
+ (* 237(g): the votive Office of the BVM on Saturday, RG 78/91 entry 27
+ -- {!Temporal_ef}'s own [subject = Bvm]/[Class4] pairing, the same
+ shape [creed]'s own 476(d) comment and the register's RG 112(d) fix
+ already establish as unique to this office (every OTHER
+ [subject = Bvm] candidate in the shipped data is [Commemoration_only]
+ and can therefore never be [observed]). *)
+ observed.Celebration.subject = Subject.Bvm && observed.Celebration.rank = Vocab_ef.Class4
+ then true
+ else if
+ (* 237(e): "Officio feriali temporis natalicii et temporis paschalis" --
+ every remaining (non-octave, non-vigil, non-BVM-Saturday) FERIA of
+ Christmastide or Paschaltide: the 2-5 January ferias, and the
+ ordinary weeks of Paschaltide (Rogation Monday/Tuesday included).
+ [temporal.weekday <> Sun] keeps this to FERIAS only, matching the
+ clause's own "Officio FERIALI" text; a Sunday in either season is
+ already [true] via the Sunday rule above regardless, so this guard
+ changes no OUTCOME, only which clause gets credit for it. *)
+ (temporal.Temporal.season = Vocab_ef.Christmastide
+ || temporal.Temporal.season = Vocab_ef.Paschaltide)
+ && temporal.Temporal.weekday <> Date.Sun
+ then true
+ else
+ (* 237(c): "in omnibus festis" -- every remaining genuine festum. By
+ this point every named Paschaltide day, every ordinary Sunday
+ (Class2, outside Septuagesima), every vigil, every feria I classis,
+ every Christmastide/Paschaltide feria and every BVM Saturday Office
+ has already been excluded or granted above, so what reaches here is
+ exactly: {!Temporal_ef.named}'s remaining population
+ (Epiphany, Ascension, Corpus Christi, the Sacred Heart, Christ the
+ King -- tested by PRESENCE in that table, since every [named] entry
+ carries [subject = Temporal] like any other, see [creed]'s own
+ 475(c) comment); and every genuine sanctoral feast actually observed
+ ([subject = Saint], or one of the handful of [subject = Lord]/[Bvm]
+ entries -- Holy Family, Holy Name of Jesus, the six [Lord]-tagged
+ sanctoral feasts, [most-holy-name-of-mary] -- [creed]'s own 475(c)
+ comment has the full census). The two ferial exceptions that ALSO
+ carry [Lord]/[Bvm] (the Sacred Triduum, the BVM Saturday Office) are
+ unreachable here: both were already excluded above (feria I
+ classis; 237(g)).
+
+ [Temporal_ef.named]'s FIRST disjunct EXCLUDES Passion Sunday and
+ Palm Sunday BY THEIR OWN SLUG -- not by [temporal.weekday <> Sun],
+ which a first pass of this fix tried and had to REVERT: Christ the
+ King is ALSO always a Sunday (its own [christ_the_king] anchor
+ IS "the last Sunday of October"), [Class1] like Passion/Palm
+ Sunday, so a blanket weekday guard wrongly excluded it too --
+ caught immediately by the LMS Ordo's own Gloria axis
+ (2024-10-27, "colitur gloria=false, Ordo gloria=true"), a
+ regression a same-session review round found before this task
+ closed. Passion Sunday and Palm Sunday are excluded because
+ neither is a genuine "festum" (RG 35's own taxonomy makes
+ "dominica" its own category, distinct from "festum") -- {!Temporal_
+ ef.named} carries them anyway (RG 91 entry 6, for its own
+ occurrence-table reasons), so without SOME exclusion both would
+ wrongly reach [true] here BY ACCIDENT of table membership --
+ confirmed wrong directly against the corrected FIUV extraction
+ (237(a)'s own comment above has the full account of the extractor
+ bug this was found alongside): both dates are [false] in the
+ source. Epiphany, Ascension, Corpus Christi, the Sacred Heart and
+ Christ the King -- {!Temporal_ef.named}'s only OTHER population --
+ are genuine festa and must NOT be excluded, which is exactly why
+ the exclusion is two named slugs, not a day-of-week predicate. The
+ SECOND disjunct (subject) carries no such guard: a genuine feast
+ that has fully displaced a Sunday's own Office (RG16(a)) still
+ deserves 237(c)'s grant on the FEAST's own terms, regardless of
+ what day of the week it falls on.
+
+ A plain, unnamed weekday feria (no Sunday, no vigil, no octave,
+ [named] = [None], [subject = Temporal]) correctly falls through to
+ [false] here -- 238(c)'s own "in omnibus feriis". *)
+ (Temporal_ef.named date <> None && slug <> "ef-passion-sunday" && slug <> "ef-palm-sunday")
+ || observed.Celebration.subject = Subject.Saint
+ || observed.Celebration.subject = Subject.Lord
+ || observed.Celebration.subject = Subject.Bvm
+
+(* Missale Romanum, Rubricae Generales, Caput XVII("De Ritibus servandis in
+ celebratione Missae"), "C) De hymno Glória in excélsis" (docs/research/
+ LT.txt, grep "Hymnus Glória"), quoted here in full:
+
+ "431. Hymnus Gloria in excelsis dicitur:
+ a) in Missis quae respondent Officio diei, quotiescumque ad
+ Matutinum dictus est hymnus Te Deum;
+ b) in Missis festivis de quibus n. 302;
+ c) in Missis feriae V in Cena Domini, et in Missa Vigiliae
+ paschalis;
+ d) in Missis votivis I, II et III classis, nisi adhibeatur color
+ violaceus paramentorum;
+ e) in Missis votivis IV classis de Angelis, quocumque die, et de B.
+ Maria Virg. quae in sabbato celebrantur.
+
+ 432. Hymnus Gloria in excelsis omittitur:
+ a) in Missis quae respondent Officio diei, quando ad Matutinum
+ omittitur hymnus Te Deum;
+ b) in omnibus Missis in quibus adhibetur color violaceus
+ paramentorum;
+ c) in Missis votivis IV classis, iis exceptis de quibus n. 431 e;
+ d) in Missis defunctorum."
+
+ SCOPE NOTE, checked once here rather than at every clause, the same
+ discipline [creed]'s own header uses: this engine resolves ONE Mass per
+ civil day (Rite.t.readings' own doc comment) -- it has no separate
+ "which votive Mass is said" dimension. n. 301-303 (LT.txt, immediately
+ above 431), quoted in substance: 301 defines "Missa de festo" in the
+ NARROW sense as the Mass of the day's own Office -- exactly what
+ [Rite_ef.Lectionary_ef.readings] already resolves for every day,
+ including a BORROWED formulary (a weekday resuming the preceding
+ Sunday's Mass, a saint using his assigned Common): still "the Mass which
+ corresponds to the day's own Office" in 431(a)/432(a)'s own sense, so
+ 431(a)/432(a) alone already cover it. 302's WIDER sense -- (a) a
+ III-class feast's own Mass said despite being impeded by another
+ III-class feast, (b) a commemoration's own Mass said in place of the
+ day's Office, (c) a saint's Mass said on his Martyrology elogium day --
+ are all cases of a DIFFERENT Mass than the day's own resolved Office
+ being said, which this engine does not model; 431(b) is therefore
+ genuinely N/A, not merely unread. 431(d)/(e) and 432(c) are about VOTIVE
+ MASS CLASSES (I-IV), a dimension this engine has no field for at all --
+ also N/A, EXCEPT 431(e)'s own "de B. Maria Virg. quae in sabbato
+ celebrantur" half: colitur does not model that Office as a votive Mass
+ (it has no votive-Mass dimension to model it AS), it models it as an
+ ORDINARY Office (RG 78's own text, [te_deum]'s own 237(g) branch above),
+ so its Gloria is produced as a side effect of 431(a) reading [te_deum],
+ not by a dedicated 431(e) branch -- checked directly: [te_deum]'s 237(g)
+ branch is unconditional (not colour-gated), and this Office's own colour
+ is white (never violet), so 432(b) below can never suppress it either.
+ 431(e)'s "de Angelis" half (the votive Mass of the Angels) has no data
+ in this engine at all and stays N/A. *)
+let gloria ~(temporal : (Vocab_ef.season, Vocab_ef.rank) Temporal.t)
+ ~(observed : Vocab_ef.rank Celebration.t) ~(date : Date.t) : bool =
+ let easter = Computus.gregorian_easter (Date.year date) in
+ let n = Date.to_rata date - Date.to_rata easter in
+ if
+ (* 432(d): "in Missis defunctorum" -- checked first, the same
+ {!Colour.Black} proxy [creed]'s own 476(f) and [te_deum]'s own
+ 238(d) branch both use. Correctly also excludes Good Friday
+ (n=-2, [Colour.Black], temporal_ef.ml's own RG 132 citation) --
+ which has no Mass at all in the 1955-restored Holy Week, so the
+ question is moot there regardless; the same "belt and braces"
+ stance [creed]'s own 476(f) comment takes for the identical day. *)
+ observed.Celebration.colour = Colour.Black
+ then false
+ else if
+ (* 431(c): "in Missis feriae V in Cena Domini, et in Missa Vigiliae
+ paschalis" -- Holy Thursday (n=-3) and the Easter Vigil Mass (n=-1,
+ Holy Saturday's own date). Checked BEFORE 432(b)'s general violet
+ exclusion below and before [te_deum] is ever read: this clause is
+ lex specialis over both. It must outrank 432(b) specifically
+ because colitur's own per-day colour model gives Holy Saturday
+ [Colour.Violet] (Passiontide's [season_colour], temporal_ef.ml --
+ the historical vestment change from violet to white happens AT the
+ Gloria itself, a per-action nuance this whole day/colour model
+ already cannot express, the same acknowledged gap RG 126's palm
+ procession and RG 128's Good Friday Communion carry, temporal_ef.ml's
+ own citations) -- without this clause checked first, 432(b) would
+ wrongly silence the one Mass whose own Gloria is historically
+ unmistakable (the bells and organ restored at the Vigil). It must
+ also outrank [te_deum]: neither day's own Matins says Te Deum
+ (both are governed by [te_deum]'s own feria-I-classis exclusion,
+ n=-46/[-6,-1], the Sacred Triduum included), so without this
+ explicit override the Gloria would be wrongly silenced there too. *)
+ n = -3 || n = -1
+ then true
+ else if
+ (* 432(b): "in omnibus Missis in quibus adhibetur color violaceus
+ paramentorum" -- independent and colour-keyed, exactly as the task
+ brief states; NOT a substitute for [te_deum] below, which still
+ decides every Mass this clause does not itself silence. Genuinely
+ unconditional ("in omnibus Missis") -- checked directly against
+ every violet day in the domain-wide sweep (see the module's own
+ test file), never merely assumed.
+
+ [Colour.Rose] found and DELIBERATELY NOT added here, a real
+ "checked, then reverted" episode kept for the record: a first pass
+ of this task, WHILE the (since-reverted) blanket "every Sunday
+ says Te Deum" mutation to [te_deum] was in place, found Gaudete
+ and Laetare ([is_rose_sunday]) wrongly getting [gloria]=true and
+ fixed it by unioning [Colour.Rose] into this branch. Once
+ [te_deum] reverted to 237(b)'s own literal [Class2] guard, the
+ fix became REDUNDANT, not merely coincidentally silent: Rose can
+ ONLY ever colour a Sunday of Advent or Lent
+ ({!Temporal_ef.is_rose_sunday}'s own two cases), and EVERY Sunday
+ of Advent or Lent is [Class1] BY CONSTRUCTION
+ ({!Temporal_ef.temporal}'s own [match s with Advent | Lent ->
+ Class1 | _ -> Class2]) -- a structural guarantee, not a
+ coincidence of the shipped data, so [te_deum]'s own [Class2] guard
+ ALREADY excludes every Rose day before this branch is ever
+ reached. Verified, not assumed: removing this branch's own Rose
+ arm and re-running the full suite (including the two LMS dates,
+ 2023-12-17 and 2024-03-10, this finding was originally pinned
+ against) left every test green. Left out rather than kept as
+ dead code that would misleadingly read as load-bearing. *)
+ observed.Celebration.colour = Colour.Violet
+ then false
+ else
+ (* 431(a)/432(a): "in Missis quae respondent Officio diei,
+ quotiescumque/quando... Te Deum [dictus est/omittitur]" -- the
+ Gloria mirrors [te_deum] exactly for every Mass not already decided
+ above. This is the ONE call site [te_deum] exists to serve. *)
+ te_deum ~temporal ~observed ~date
diff --git a/lib/rites/rite_ef/rubrics_ef.mli b/lib/rites/rite_ef/rubrics_ef.mli
index 7a1284b..618af65 100644
--- a/lib/rites/rite_ef/rubrics_ef.mli
+++ b/lib/rites/rite_ef/rubrics_ef.mli
@@ -38,3 +38,33 @@ val creed :
observed:Vocab_ef.rank Celebration.t ->
date:Date.t ->
bool
+
+(** Whether the Te Deum was said at Matins (Breviary 1961 Codex Rubricarum
+ nn. 237-238) -- NOT the Divine Office arriving in scope, but a single
+ Breviary fact {!gloria}'s own RG 431(a) defers a Mass question to. See
+ the .ml's own header for the rubric quoted in full, every branch's own
+ citation, and this source's own stated weakness (a single, not yet
+ scan-verified, web transcription -- docs/research/breviary/PROVENANCE.md).
+ Same three parameters as {!creed}, for the same reasons: [temporal] for
+ the day's own weekday/season, [observed] for the celebration whose
+ Office is actually kept, [date] for the Easter-relative window
+ questions. *)
+val te_deum :
+ temporal:(Vocab_ef.season, Vocab_ef.rank) Temporal.t ->
+ observed:Vocab_ef.rank Celebration.t ->
+ date:Date.t ->
+ bool
+
+(** Whether the Gloria in excelsis is said at this day's Mass (RG 431-432).
+ 431(a)/432(a) defer to {!te_deum}; 431(c) (Holy Thursday, the Easter
+ Vigil Mass) and 432(b)/(d) (violet vestments; a Requiem) are
+ independent overrides, checked ahead of the Te Deum-derived answer --
+ see the .ml's own header for the full account, including which of
+ 431/432's own clauses this engine has no votive-Mass-class dimension to
+ implement and are therefore marked not-applicable rather than silently
+ skipped. *)
+val gloria :
+ temporal:(Vocab_ef.season, Vocab_ef.rank) Temporal.t ->
+ observed:Vocab_ef.rank Celebration.t ->
+ date:Date.t ->
+ bool