aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef/temporal_ef.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-13 12:44:15 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-13 12:44:15 +0200
commitdc00d1f730b23fa99cd15ff142ecba515a9fe0b7 (patch)
treee40f7f003008b33104723a6d8856168bbdea7633 /lib/rites/rite_ef/temporal_ef.ml
parent59fbda87d565ad9b98c2493e9703390cd988609d (diff)
parente45ec1ccce68d9edb9655004c21861d6bfb33a9d (diff)
downloadcolitur-dc00d1f730b23fa99cd15ff142ecba515a9fe0b7.tar.gz
colitur-dc00d1f730b23fa99cd15ff142ecba515a9fe0b7.zip
Merge branch 'ef-holyname-rg110': the Holy Name of Jesus, RG 110
Builds the Most Holy Name of Jesus with the fallback RG 17(a) and the calendarium both specify -- 'secus die 2 ianuarii', 'vel, ea deficiente, die 2 ianuarii' -- which was unimplemented, so in 3619 of the domain's 8417 years a II-class feast did not exist at all. Also retags it as a feast of the Lord per RG 91 entry 14. Builds RG 110's inseparable Peter/Paul commemoration ('in numero orationum computando, pro unica habeantur') across the three calendarium pairs, and adds the 30 June companion, which was missing from lectio's source and from missalemeum's output alike -- corroborated in both photographic scans plus RG 124(c), and added through adjustments.sexp so the bootstrap's provenance stands. 14627 days change, in four shapes, none unclassified. The register's earlier 852-day estimate for RG 110 reconciles exactly as the Lent Ember subset of one shape: 288 + 284 + 280. RG 110(c)'s ordering was implemented backwards at first and corrected: 'huic orationi additur altera' attaches the companion to the trigger's own oration, so it follows it, where (a) and (b) attach to the day's and so precede everything. The 'ante omnes alias commemorationes' half remains unimplemented and is unreachable by construction -- shape (c) needs the trigger to lose while still being admitted, which only a I-class day or a II-class Sunday allows, and both RG 111 caps are one. Records a fifth way the validation layers can be green while wrong: nothing anywhere compares commemoration order.
Diffstat (limited to 'lib/rites/rite_ef/temporal_ef.ml')
-rw-r--r--lib/rites/rite_ef/temporal_ef.ml157
1 files changed, 155 insertions, 2 deletions
diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml
index 1ce9de9..bbd88ec 100644
--- a/lib/rites/rite_ef/temporal_ef.ml
+++ b/lib/rites/rite_ef/temporal_ef.ml
@@ -104,6 +104,56 @@ let christ_the_king y = sunday_on_or_before (mk y 10 31)
date against it without reaching into [week_origin]'s implementation. *)
let holy_family_sunday y = Date.add_days (sunday_on_or_before (mk y 1 6)) 7
+(* RG 17(a) (Caput III, "De Dominicis"; full text on [holy_family_sunday]'s
+ own citation above): "festum Ss.mi Nominis Iesu, celebrandum dominica quae
+ occurrit a die 2 ad 5 ianuarii (secus die 2 ianuarii)" -- the feast of the
+ Most Holy Name of Jesus, celebrated on the Sunday falling 2-5 January,
+ OTHERWISE [on] 2 January [itself]. [None] here means no such Sunday exists
+ that year -- [holy_name_fallback_date]'s own fallback branch is for
+ exactly that case; see its own citation for why the window CAN be empty
+ (unlike (b)'s 7-13 January window, which never is).
+
+ Formula: the Sunday on or before 5 January is in [2,5] iff it is not
+ before 2 January -- the window is only 4 days wide, so there is at most
+ one Sunday in it either way. *)
+let holy_name_sunday y =
+ let s = sunday_on_or_before (mk y 1 5) in
+ if on_or_after s (mk y 1 2) then Some s else None
+
+(* RG 17(a)'s own "secus die 2 ianuarii" clause, and the calendarium's
+ January table, BOTH photographic scans, word for word (docs/research/
+ rules-register.md; also confirmed in the Mass propers' own heading for
+ this feast, both scans, Caput "Sanctissimi Nominis Iesu": "DOMINICA a die
+ 2 ad diem 5 ianuarii occurrenti, VEL, SI HAEC DEFECERIT, DIE 2 IANUARII" --
+ the SUNDAY occurring 2-5 January, OR, IF THIS IS LACKING, [on] 2 January):
+ *"Dominica inter octavam Nativitatis Domini et Epiphaniam, vel, EA
+ DEFICIENTE, die 2 ianuarii: Sanctissimi Nominis Iesu, II classis"* -- the
+ Sunday between the Octave of the Nativity and Epiphany, or, THAT FAILING,
+ 2 January. Unlike [holy_family_sunday]'s 7-13 January window (which can
+ never be empty of a Sunday), this 2-5 January window CAN be, and is in
+ 3,619 of the 8,417 years in [1583, 9999] (a plain weekday check,
+ independently cross-checked against `date -d`) -- [temporal]'s own
+ dispatch applies this fallback date, tagged exactly like the Sunday shape
+ (subject Lord, the same Latin name), only when [holy_name_sunday y] is
+ [None] for that date's own civil year. *)
+let holy_name_fallback_date y = mk y 1 2
+
+(* The ONE Latin title both of RG 17(a)'s two shapes (the Sunday and the 2
+ January fallback) carry -- ONE feast, RG 91's own "primum mobilia, deinde
+ fixa" language notwithstanding (see [temporal]'s own fallback-branch
+ comment for why both shapes band identically at entry 14's MOVABLE half,
+ not split across the two halves by year). Bound once so the two [temporal]
+ branches that use it cannot silently drift apart, the same discipline
+ [holy_family_sunday] already gets from being named instead of re-derived.
+ Both photographic scans, word for word, twice over (the calendarium's own
+ January table AND the Mass propers' own running header/heading, this
+ file's own citations on [holy_name_sunday]/[holy_name_fallback_date]
+ above): "Sanctissimi Nominis Iesu". No English name, deliberately -- the
+ SAME "would read the oracle it is compared against" reasoning
+ [holy_family_sunday]'s own [names] comment below gives for Holy Family. *)
+let holy_name_names =
+ Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sanctissimi Nominis Iesu") ]
+
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),
@@ -533,15 +583,103 @@ let temporal d =
item on Holy Name of Jesus, RG 17(a)) -- this is a
targeted addition for the one day this task built, not a
claim that the gap is closed generally. *)
+ (* RG 17(a) (this file's own [holy_name_sunday], full citation
+ there): the SECOND Sunday a year this branch must not
+ leave [subject = Temporal] -- added alongside Holy
+ Family's own check above, not replacing it (the two
+ windows, 2-5 and 7-13 January, never overlap, so at most
+ one of the two conditions is ever true for a given [d]).
+ Guarded on [holy_name_sunday y = Some d] rather than only
+ "is [d] in [2,5] January": every OTHER Sunday in that
+ range must stay unnamed, and in a year where a DIFFERENT
+ day of the window is the one true Sunday, [d] itself is
+ never reached by [sunday_slug] as a Sunday to begin with
+ (weekday alone already excludes it) -- but writing the
+ check this way, against the SAME independently-computed
+ date [holy_name_sunday] returns rather than a bare month/
+ day range, is what test_temporal_ef.ml's own anchor test
+ below cross-checks against.
+
+ ADDED, fix round 1 (coordinator finding F4): the Sunday
+ shape's "takes the Sunday's own place, no commemoration of
+ it" treatment is settled at RG LEVEL, not only in Holy
+ Name's own Mass propers (this file's own [holy_name_sunday]
+ citation already has that quote) -- RG 17's own CLOSING
+ paragraph, immediately after its own lettered list
+ (a)-(e), scan-verified: "Haec festa locum tenent dominicae
+ occurrentis cum omnibus iuribus et privilegiis; de
+ dominica, proinde, nulla fit commemoratio" -- "these
+ feasts" (plural, covering the WHOLE list (a)-(e), Holy Name
+ included) "hold the place of the occurring Sunday with all
+ rights and privileges; of the Sunday, therefore, no
+ commemoration is made". A second, independent primary
+ source for the same conclusion the propers-level quote
+ already gives, not a new claim.
+
+ FRAGILE DERIVATION, noted per fix round 1 (coordinator
+ finding F5): the mechanism that actually grants a LOSING
+ Holy-Name-Sunday its RG 109(a) privilege when outranked
+ (Precedence_ef.privilege_of's own (a) branch, {!Precedence
+ _ef.is_sunday_slug}) reads it off the "-sunday" SUBSTRING
+ in the slug ("ef-holy-name-sunday") -- a naming convention,
+ not a citation of RG 17's own text quoted immediately
+ above, which is the actual warrant ("these feasts hold the
+ place of the occurring Sunday WITH ALL ITS RIGHTS AND
+ PRIVILEGES" -- RG 109(a)'s own "of a Sunday" privilege is
+ one of those rights). Right answer, fragile path: a future
+ rename of this slug family would silently drop the
+ privilege with no compiler or test failure pointing here.
+ No live witness exists to test it either way (no
+ fixed-date candidate ever outranks Holy Name in today's
+ data -- test_precedence_ef.ml's own synthetic
+ [test_class1_feast_inside_holy_name_window_end_to_end]
+ proves the SYNTHETIC case only). *)
let subject, names =
if same d (holy_family_sunday y) then
( Colitur_kernel.Subject.Lord,
Colitur_kernel.Names.of_list
[ (Colitur_kernel.Lang.of_string_exn "la", "Sanctae Familiae Iesu, Mariae, Ioseph") ] )
+ else if holy_name_sunday y = Some d then (Colitur_kernel.Subject.Lord, holy_name_names)
else (Colitur_kernel.Subject.Temporal, Colitur_kernel.Names.empty)
in
build ~subject ~names ~season:s ~slug ~colour ~rank ~week:(week d) ()
- | None -> (
+ | None ->
+ (* RG 17(a)'s own fallback, "secus die 2 ianuarii" (full
+ citation on [holy_name_fallback_date] above): ONLY when
+ [holy_name_sunday y] is [None] for [d]'s own civil year --
+ i.e. 2-5 January genuinely has no Sunday that year -- does
+ 2 January itself carry the feast; every other year, 2
+ January is an ordinary Christmastide feria, exactly what
+ [christmastide_feria_slug] below already computes for it.
+ Checked here, ahead of that generic fallback, the same way
+ [named]'s own fixed dates are checked ahead of everything
+ ferial -- NOT folded into [named] itself, because unlike
+ every one of [named]'s ~20 entries this one is
+ CONDITIONAL on a per-year fact ([holy_name_sunday y] =
+ [None]) that [named]'s bare [Date.t -> ... option] shape
+ has no way to express without threading [subject] through
+ every one of its other branches too -- the same reason
+ Holy Family, immediately above, is not one of [named]'s
+ outputs either (that branch's own comment).
+
+ Season Christmastide (RG 72-73: 2 January is always within
+ it); rank/colour/subject/names identical to the Sunday
+ shape immediately above -- ONE feast, {!holy_name_names}'s
+ own comment on why both shapes share it. Band-classified
+ at entry 14's MOVABLE half regardless of which shape fired
+ this year (Precedence_ef.band; both shapes are built
+ through this same [temporal] function, hence [origin =
+ Temporal] either way) -- RG 91's "primum mobilia, deinde
+ fixa" split is a property of the TABLE ENTRY a feast
+ occupies, and RG 17(a) names ONE feast with a fallback
+ clause, not two differently-classified feasts that happen
+ to share a Mass formulary; the Mass propers' own heading
+ (docs/research/rules-register.md) states both shapes under
+ the identical title for exactly this reason. *)
+ if same d (holy_name_fallback_date y) && holy_name_sunday y = None then
+ build ~subject:Colitur_kernel.Subject.Lord ~names:holy_name_names ~season:Christmastide
+ ~slug:"ef-holy-name" ~colour:Colour.White ~rank:Class2 ~week:(week d) ()
+ else (
match christmastide_feria_slug d with
| Some slug ->
build ~season:s ~slug ~colour:(season_colour s) ~rank:(ferial_rank d s)
@@ -608,7 +746,19 @@ let temporal d =
4-tuple), but it is exactly the same kind of independently-computed
anchor this list exists to guard -- an accidental drift in
[holy_family_sunday]'s own [+7] would silently move Holy Family without
- this guard catching it, same as any other entry here. *)
+ this guard catching it, same as any other entry here.
+
+ Holy Name of Jesus (RG 17(a)) is the SAME shape, restated per-year rather
+ than as a single fixed pair: exactly ONE of its two slugs exists for any
+ given [y] ([holy_name_sunday y] is [Some] xor [None]), so exactly one
+ entry -- "ef-holy-name-sunday" at that Sunday, or "ef-holy-name" at
+ [holy_name_fallback_date y] -- is appended below, matching whichever
+ shape [temporal] itself will actually build that year. An erosion of
+ EITHER branch is still caught: {!Validate.run} calls [anchors] for both
+ the civil year a date falls in and the one before it (a liturgical year
+ straddles two), so across any two consecutive years both shapes are
+ exercised regardless of which one civil year [y] itself happens to
+ land on. *)
let anchors y =
let easter = Computus.gregorian_easter y in
let off n = Date.add_days easter n in
@@ -633,6 +783,9 @@ let anchors y =
("ef-corpus-christi", off 60);
("ef-sacred-heart", off 68);
("ef-christ-the-king", christ_the_king y) ]
+ @ (match holy_name_sunday y with
+ | Some d -> [ ("ef-holy-name-sunday", d) ]
+ | None -> [ ("ef-holy-name", holy_name_fallback_date y) ])
(* Compile-time check that this module satisfies the kernel's rite contract. *)
module _ : Colitur_kernel.Temporal.RITE = struct