aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef/temporal_ef.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-24 16:29:23 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-24 16:29:23 +0200
commit8a5da8756cf6b57aa36c01b71ce893f7967d3a29 (patch)
tree10873a692400fa25ce54c67ac9fc34d46af86272 /lib/rites/rite_ef/temporal_ef.ml
parent4f87cbde4ee79ac96aa7ebfed105b3a5ec02be43 (diff)
parent73b15551804bb63ee0081005e2869d36afb54be2 (diff)
downloadcolitur-8a5da8756cf6b57aa36c01b71ce893f7967d3a29.tar.gz
colitur-8a5da8756cf6b57aa36c01b71ce893f7967d3a29.zip
merge: celebrant Mass rubrics, and four rubrical corrections
colitur now prints what a real ordo prints for the Mass: which formulary is said and how it was reached, the Gloria, the Creed, the preface, and the commemorations with their Low-Mass/sung distinction. colitur rubrics joins day and readings. Four defects were found and fixed on the way, each by an external witness rather than by inspection: the Creed said at Requiem Masses (RG 476(f)), the missing bissextile shift of St Matthias and St Gabriel (2 041 leap years), Rogation Monday and Tuesday coloured violet in Paschaltide (RG 119(b)), and the ferias after the Ascension resuming the wrong Sunday's Mass rather than the Ascension's. Validation gained a sixth layer and then some: the preface is checked against three independent publishers over seven witness-years (FIUV, three LMS editions, three extraordinaryform.org editions), none of which shares the Divinum Officium -> missalemeum -> lectio lineage the older layers all descend from.
Diffstat (limited to 'lib/rites/rite_ef/temporal_ef.ml')
-rw-r--r--lib/rites/rite_ef/temporal_ef.ml90
1 files changed, 88 insertions, 2 deletions
diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml
index c16732b..fe08b2e 100644
--- a/lib/rites/rite_ef/temporal_ef.ml
+++ b/lib/rites/rite_ef/temporal_ef.ml
@@ -823,12 +823,36 @@ let temporal d =
"ef-rogation-monday"/"-tuesday" are colitur-only keys and a
lectionary gap for Plan 4 (CORRECTED, final fix wave, item 7 --
the lectionary bootstrap is Plan 4, not Plan 3), like the Ember
- and Nativity-vigil keys above. *)
+ and Nativity-vigil keys above.
+
+ COLOUR (L6, data/ef/expected-divergences-lms.sexp, CLOSED by this
+ fix): this branch used to hardcode [Colour.Violet] with no
+ citation at all. RG 119 (Caput XVIII, "De Colore Paramentorum"):
+ white is used "in Officio et Missa de Tempore ... a Missa Vigiliae
+ paschalis usque ad Missam vigiliae Pentecostes exclusive" -- the
+ de-Tempore Office AND Mass are white from the Paschal Vigil Mass
+ until the Pentecost Vigil Mass exclusive, and days 36/37 (Rogation
+ Monday/Tuesday) sit squarely inside that window. RG 128's own
+ exhaustive four-case list for violet (Advent I-Christmas Eve;
+ Septuagesima-Paschal Vigil; the September Ember ferias; vigils of
+ II/III class OUTSIDE Paschaltide) names Rogation days nowhere, and
+ its vigil clause is inapplicable twice over -- these are not
+ vigils, and they ARE inside Paschaltide. RG 88 ("de Litaniis
+ minoribus nihil fit in Officio, sed tantum in Missa") is why: the
+ violet belongs to the ROGATION MASS/procession alone, a distinct
+ votive text this engine does not model as a separate Mass (the
+ same per-action colour nuance temporal_ef.ml's own RG 126/RG 128
+ Good-Friday-Communion citations already flag as unmodelled
+ elsewhere) -- the day's own OFFICE, which is what [colour] here
+ expresses, stays [season_colour s] like any other Paschaltide
+ feria. Witnessed directly: LMS 2023-2024, 2024-05-06 (Rogation
+ Monday), "FERIA IV Cl W" (white), "Mass of 5th Sunday after
+ Easter". *)
let rogation = days_between easter d in
if rogation = 36 || rogation = 37 then
build ~season:s
~slug:(if rogation = 36 then "ef-rogation-monday" else "ef-rogation-tuesday")
- ~colour:Colour.Violet ~rank:(ferial_rank d s) ~week:(week d) ()
+ ~colour:(season_colour s) ~rank:(ferial_rank d s) ~week:(week d) ()
else
match ember d with
| Some (slug, rank, colour) -> build ~season:s ~slug ~colour ~rank ~week:(week d) ()
@@ -1158,6 +1182,68 @@ let anchors y =
| Some d -> [ ("ef-holy-name-sunday", d) ]
| None -> [ ("ef-holy-name", holy_name_fallback_date y) ])
+(* The Missale Romanum's own CALENDARIUM table, February, footnote
+ (docs/research/LT.txt:5011-5014, scan-corroborated at
+ docs/research/scan2.txt:3050-3058, where OCR mangles it to
+ "bi&sextili"/"Matthue"/"Cabrielis" -- transcription and scan agree, so
+ this rests on two witnesses of the same document):
+
+ "In anno bissextili mensis februarius est dierum 29, et festum S.
+ Matthiae celebratur die 25 februarii, ac festum S. Gabrielis a
+ Virgine perdolente 28 februarii, et bis dicitur sexto calendas, id
+ est die 24 et die 25; et littera dominicalis, quae assumpta fuit in
+ mense ianuario, mutetur in praecedentem..."
+
+ In a leap year February has 29 days, St Matthias is kept on 25 February
+ (not 24), St Gabriel of Our Lady of Sorrows on 28 February (not 27), and
+ the sixth kalends of March is said TWICE -- "bis dicitur sexto
+ calendas... die 24 et die 25". That last clause names the MECHANISM, not
+ two independent exceptions: the Roman calendar's intercalary day is
+ inserted by DOUBLING the sixth kalends (civil 24 February in a common
+ year), not by appending a 29th day at the month's end. Reckoned by
+ kalends position: 24 Feb = VI Kal. Mart., 25 Feb = V Kal., 26 Feb = IV
+ Kal., 27 Feb = III Kal., 28 Feb = pridie Kal. In a leap year VI Kal.
+ itself falls on TWO consecutive civil days (24th and 25th, "bis"), and
+ every kalends position after it is pushed one civil day later as a
+ result: V Kal. from 25th to 26th, IV Kal. from 26th to 27th, III Kal.
+ (Gabriel) from 27th to 28th, pridie Kal. from 28th to 29th. A feast fixed
+ at VI Kal. itself (Matthias) is kept on the SECOND occurrence, not the
+ first -- 25 February carries it, 24 February carries no fixed office at
+ all that year (confirmed against the LMS Ordo witness,
+ data/ef/expected-divergences-lms.sexp entry L3: 24 February 2024 is
+ titled plainly "EMBER SATURDAY of LENT", no Matthias anywhere near it).
+
+ IMPLEMENTED AS THE GENERAL MECHANISM, not as "shift these two named
+ feasts": only three sanctoral entries exist in 23-29 February on shipped
+ data (peter-damien the 23rd, before the doubled kalends and therefore
+ unaffected; matthias the 24th; gabriel-of-our-lady-of-sorrows the 27th),
+ so "shift the two named feasts" and "shift every fixed entry from 24
+ February" are indistinguishable on universal data -- they produce
+ identical output. They differ only for a locally-supplied entry, e.g. an
+ [--overlay] patronal feast fixed at 26 February: under the mechanism
+ reading it shifts to 27 February in a leap year (IV Kal. -> the civil day
+ IV Kal. falls on that year), exactly as a diocesan calendar compiled
+ against the same kalends convention would expect. The rubric's own text
+ states a MECHANISM ("bis dicitur sexto calendas"), not a closed list of
+ two saints, and this project has already been bitten once by a rule
+ implemented against the shipped data's coincidental shape rather than the
+ rubric itself (RG 16(a), CLAUDE.md's own carried lesson) -- so the general
+ reading is the one implemented here.
+
+ Threaded through {!Rite.t}'s [fixed_key] field, read only by
+ {!Colitur_kernel.Layer.on_date}'s FIXED half: the kernel stays
+ rite-agnostic (a Byzantine or other non-Roman rite supplies nothing and
+ is unaffected), and the MOVABLE half ({!Date_spec.Easter_offset},
+ {!Date_spec.Nth_weekday}) is untouched -- nothing in the footnote
+ concerns Easter-relative dates. *)
+let bissextile_fixed_key (d : Date.t) : (int * int) option =
+ let m = Date.month d and day = Date.day d in
+ if m = 2 && Date.is_leap (Date.year d) then
+ if day = 24 then None (* the FIRST VI Kal.: no fixed entry lands here in a leap year *)
+ else if day >= 25 && day <= 29 then Some (2, day - 1) (* the SECOND VI Kal. onward, one day later *)
+ else Some (m, day)
+ else Some (m, day)
+
(* Compile-time check that this module satisfies the kernel's rite contract. *)
module _ : Colitur_kernel.Temporal.RITE = struct
let id = id