From bbc8ce26916ca92b3c2286c57342a6565323b66f Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 08:11:26 +0200 Subject: temporal(ef): the Holy Family, first Sunday after Epiphany (RG 17(b)) RG 17(b), verified against both photographic scans (missale-romanum- 1962.pdf and "Missale Romanum 1962_text.pdf" -- the electronic transcription carries the same paragraph, so this one is not itself a transcription-vs-scan divergence): the Holy Family of Jesus, Mary and Joseph is celebrated on the first Sunday after Epiphany, II class, EVERY year, taking the Sunday's own place "cum omnibus iuribus et privilegiis" -- the same formula RG 16(a) already uses. The slug, rank and colour temporal_ef.ml already computed for that Sunday (ef-time-after-epiphany-sunday-1, Class2, white) turn out to be exactly right by coincidence -- an ordinary, unnamed Sunday and Holy Family share both. The only field that was silently wrong is subject: always Temporal before this, when RG 91 entry 14 ("Festa Domini II classis, primum mobilia, deinde fixa") calls Holy Family a movable Feast of the Lord. temporal's build now takes an optional ~subject, applied only on Holy Family's own date -- every other Sunday, including the still-unbuilt Holy Name of Jesus (RG 17(a)), is unaffected. RG 17(b)'s window (7-13 January) can never be empty of a Sunday -- sunday_on_or_before(6 Jan) + 7 always lands in [7, 13] regardless of 6 January's weekday -- unlike RG 17(a)'s Holy Name (2-5 January, CAN be empty), whose calendarium entry carries an explicit "vel ea deficiente, die 2 ianuarii" fallback right next to Holy Family's own, fallback-free one. No fallback needed or built. Added to anchors (holy_family_sunday is independently computed, not routed through named -- see temporal_ef.ml's own comment on why) and to its own dedicated anchor/erosion test in test_temporal_ef.ml, kept separate from the generic named-days coverage test since this genuinely isn't one of named's own outputs. Does not yet touch precedence: band still has no notion of a movable Class2 Lord feast (entry 14 gates on sanctoral origin only), so on the seven years 13 January is itself Holy Family's date (2008, 2013, 2019, 2030, 2036, 2041, 2047), the fixed Commemoration of the Baptism of the Lord still wins the day exactly as before this commit -- the next commit fixes the ordering RG 91 entry 14's own text states ("primum mobilia, deinde fixa"). --- lib/rites/rite_ef/temporal_ef.ml | 89 +++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 11 deletions(-) (limited to 'lib/rites/rite_ef/temporal_ef.ml') diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 7782876..0647aac 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -64,6 +64,46 @@ let season d = citation gap. *) let christ_the_king y = sunday_on_or_before (mk y 10 31) +(* RG 17(b) (Caput III, "De Dominicis"), primary text, verified against BOTH + photographic scans (missale-romanum-1962.pdf and "Missale Romanum + 1962_text.pdf" -- the electronic transcription, 1962-06-23,_SS_Ioannes + _XXIII,_Missale_Romanum,_LT.pdf, carries the SAME text here, so this one + paragraph is not itself a transcription-vs-scan divergence): "17. + Dominica excludit, per se, assignationem perpetuam festorum. Excipiuntur: + a) festum Ssmi Nominis Iesu, celebrandum dominica quae occurrit a die 2 + ad 5 ianuarii (secus die 2 ianuarii); b) festum S. Familiae Iesu, Mariae, + Ioseph, celebrandum dominica prima post Epiphaniam; ... Haec festa locum + tenent dominicae occurrentis cum omnibus iuribus et privilegiis: de + dominica, proinde, nulla fit commemoratio" -- a Sunday of itself excludes + the perpetual assignment of feasts to it; EXCEPTED: (a) the Most Holy + Name of Jesus, celebrated on the Sunday falling 2-5 January (otherwise 2 + January); (b) the HOLY FAMILY of Jesus, Mary and Joseph, celebrated on + the FIRST SUNDAY AFTER EPIPHANY; ... these feasts hold the place of the + occurring Sunday with ALL its rights and privileges: of the Sunday, + therefore, NO commemoration is made -- the identical "cum omnibus + iuribus et privilegiis: nulla fit commemoratio" formula RG 16(a) already + uses for a FIXED-date Lord feast impeding a Sunday (register §6.0), + stated here for the Sunday-ASSIGNED feasts RG 17 itself lists by letter. + + UNLIKE (a)'s own narrow 2-5 January window (which CAN be empty of a + Sunday -- the calendarium's own January table carries an explicit "vel ea + deficiente, die 2 ianuarii" fallback for it, scan-verified), (b)'s 7-13 + January window can NEVER be empty: [sunday_on_or_before (mk y 1 6)] is, + by construction, at most 6 days before 6 January, so adding 7 always + lands in [7, 13] regardless of which weekday 6 January falls on (every + one of the 7 possible cases is exercised by test_temporal_ef.ml's own + [test_holy_family]). The calendarium's own text for (b), both in RG 17 + itself and in its January table entry ("Dominica I post Epiphaniam: + S. Familiae, Iesu, Mariae, Ioseph, II classis"), carries no fallback + clause of any kind -- consistent with there being no gap for one to + cover. + + Formula IDENTICAL to [week_origin]'s own [Time_after_epiphany] case below + (the first Sunday after Epiphany) -- not re-derived a second time, just + named here for its own citation and so [temporal] can test a specific + 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 + 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), @@ -404,15 +444,15 @@ let temporal d = let easter = Computus.gregorian_easter y in let s = season d in let weekday = Date.weekday d in - let build ~season ~slug ~colour ~rank ~week = + let build ?(subject = Colitur_kernel.Subject.Temporal) ~season ~slug ~colour ~rank ~week () = let office = - Colitur_kernel.Celebration.make ~slug:(Slug.of_string_exn slug) ~rank ~colour - ~subject:Colitur_kernel.Subject.Temporal ~layer:"temporal" () + Colitur_kernel.Celebration.make ~slug:(Slug.of_string_exn slug) ~rank ~colour ~subject + ~layer:"temporal" () in { Colitur_kernel.Temporal.season; week; weekday; office } in match named d with - | Some (season, slug, colour, rank) -> build ~season ~slug ~colour ~rank ~week:(week d) + | Some (season, slug, colour, rank) -> build ~season ~slug ~colour ~rank ~week:(week d) () | None -> ( (* Rogations (the Minor Litanies only -- RG 87, Monday and Tuesday before Ascension). The Major Litanies (25 April, RG 80) are a fixed @@ -441,10 +481,10 @@ let temporal d = 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:Colour.Violet ~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) + | Some (slug, rank, colour) -> build ~season:s ~slug ~colour ~rank ~week:(week d) () | None -> ( match sunday_slug d with | Some slug -> @@ -456,11 +496,29 @@ let temporal d = reaches this fallback -- leaving II class here except the remaining Advent and Lent Sundays. *) let rank = match s with Advent | Lent -> Class1 | _ -> Class2 in - build ~season:s ~slug ~colour ~rank ~week:(week d) + (* RG 17(b) (this file's own [holy_family_sunday], full + citation there): the ONE Sunday a year this branch must + NOT leave [subject = Temporal] (this function's own + default) -- the Holy Family, whose slug/rank/colour are + otherwise EXACTLY what this branch already computes for + the 7-13 January Sunday (an accident this task's own + oracle-strengthening work exposed: rank/colour alone + could never distinguish "Holy Family" from "an ordinary, + unnamed Sunday", since they happen to coincide). Every + OTHER Sunday this branch ever builds -- including the + narrower Holy Name of Jesus window, RG 17(a), still + unbuilt as its own named day, register §6 -- keeps + [Temporal], unaffected. *) + let subject = + if same d (holy_family_sunday y) then Colitur_kernel.Subject.Lord + else Colitur_kernel.Subject.Temporal + in + build ~subject ~season:s ~slug ~colour ~rank ~week:(week d) () | None -> ( match christmastide_feria_slug d with | Some slug -> - build ~season:s ~slug ~colour:(season_colour s) ~rank:(ferial_rank d s) ~week:(week d) + build ~season:s ~slug ~colour:(season_colour s) ~rank:(ferial_rank d s) + ~week:(week d) () | None -> (* The days between Ash Wednesday and Lent I have proper Masses and belong to no numbered week. *) @@ -468,7 +526,7 @@ let temporal d = if after_ashes >= -45 && after_ashes <= -43 then build ~season:s ~slug:(Printf.sprintf "ef-lent-after-ashes-%s" (weekday_word d)) - ~colour:Colour.Violet ~rank:Class3 ~week:None + ~colour:Colour.Violet ~rank:Class3 ~week:None () else let colour = (* The Pentecost octave weekdays are red, not Paschaltide's white. *) @@ -508,14 +566,22 @@ 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))) + build ~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 NOT derived from [named] itself -- consumed by [Validate]'s anchor- agreement check (design spec §5.7), which exists precisely to catch an accidental single-site drift (e.g. Ascension's [off 39] silently becoming - [off 40]) that both sides moving together would hide. *) + [off 40]) that both sides moving together would hide. + + [holy_family_sunday] is NOT one of [named]'s own outputs (its own + citation above explains why -- [temporal] applies it as a targeted + [subject] override inside [sunday_slug]'s branch, not through [named]'s + 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. *) let anchors y = let easter = Computus.gregorian_easter y in let off n = Date.add_days easter n in @@ -526,6 +592,7 @@ let anchors y = ("ef-nativity-octave-day-7", mk y 12 31); ("ef-circumcision", mk y 1 1); ("ef-epiphany", mk y 1 6); + ("ef-time-after-epiphany-sunday-1", holy_family_sunday y); ("ef-ash-wednesday", off (-46)); ("ef-passion-sunday", off (-14)); ("ef-palm-sunday", off (-7)); -- cgit v1.3 From b7da41445d89cc83e749ec19c57e321ceb6d5cf2 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 09:16:39 +0200 Subject: temporal(ef): fix round 1 (F3) -- give Holy Family a primary-sourced Latin name On 7,197 of the 8,417 days Holy Family occurs (every year that isn't the 13-January collision), every emitted byte was identical to the pre-change binary -- subject appeared in no output path the CLI or any test read. Celebration.make already takes ?names, defaulting to empty, and Temporal_ef.build never passed it. build now takes an optional ?names, set only on Holy Family's own branch: a LATIN name, not English. The oracle's own observed-identity axis (test_oracle.ml) reads only `en`, so this is deliberately invisible to it -- an English name here would mean reading missalemeum's own title text, the oracle this exact axis is compared against, to decide colitur's "ground truth" name: the "expected value promoted from actual output" vacuity flavour this project's review process watches for. Latin has no such circularity: the calendarium's own January table and the Mass propers' own heading, both photographic scans, word for word, "Sanctae Familiae Iesu, Mariae, Ioseph" -- the same genitive-title convention test_names.ml's own worked example already uses for Easter ("Dominica Resurrectionis"). Every other temporal-cycle candidate, including the neighbouring Sundays, still carries no name of any kind -- a targeted addition for the one day this task built, not a claim that the general gap (register's own open item on Holy Name of Jesus, RG 17(a)) is closed. --- lib/rites/rite_ef/temporal_ef.ml | 41 +++++++++++++++++++++++++++++++++------- test/test_temporal_ef.ml | 16 ++++++++++++++++ 2 files changed, 50 insertions(+), 7 deletions(-) (limited to 'lib/rites/rite_ef/temporal_ef.ml') diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 0647aac..1ce9de9 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -444,9 +444,10 @@ let temporal d = let easter = Computus.gregorian_easter y in let s = season d in let weekday = Date.weekday d in - let build ?(subject = Colitur_kernel.Subject.Temporal) ~season ~slug ~colour ~rank ~week () = + let build ?(subject = Colitur_kernel.Subject.Temporal) ?(names = Colitur_kernel.Names.empty) ~season + ~slug ~colour ~rank ~week () = let office = - Colitur_kernel.Celebration.make ~slug:(Slug.of_string_exn slug) ~rank ~colour ~subject + Colitur_kernel.Celebration.make ~slug:(Slug.of_string_exn slug) ~rank ~colour ~subject ~names ~layer:"temporal" () in { Colitur_kernel.Temporal.season; week; weekday; office } @@ -508,12 +509,38 @@ let temporal d = OTHER Sunday this branch ever builds -- including the narrower Holy Name of Jesus window, RG 17(a), still unbuilt as its own named day, register §6 -- keeps - [Temporal], unaffected. *) - let subject = - if same d (holy_family_sunday y) then Colitur_kernel.Subject.Lord - else Colitur_kernel.Subject.Temporal + [Temporal] and no name, unaffected. + + [names] -- fix round 1 (coordinator finding 3): a LATIN + name, not English. The oracle's own observed-identity + axis (test_oracle.ml) reads only [en], so this is + deliberately invisible to it -- setting an ENGLISH name + here would mean reading missalemeum's own title text + (the oracle this exact axis is compared against) to + decide colitur's own "ground truth" name, precisely the + "expected value promoted from actual output" vacuity + flavour this project's own review process watches for. + Latin has no such circularity: the calendarium's own + January table, both photographic scans, word for word: + "Dominica I post Epiphaniam: S. Familiae, Iesu, Mariae, + Ioseph, II classis" -- and the Mass propers' own heading + (also both scans): "SANCTAE FAMILIAE IESU, MARIAE, + IOSEPH, II classis", the exact string used here, the + same genitive-title convention test_names.ml's own + worked example already uses for Easter ("Dominica + Resurrectionis"). Every other temporal-cycle candidate + still carries no name at all (register §6.2's own open + 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. *) + 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 (Colitur_kernel.Subject.Temporal, Colitur_kernel.Names.empty) in - build ~subject ~season:s ~slug ~colour ~rank ~week:(week d) () + build ~subject ~names ~season:s ~slug ~colour ~rank ~week:(week d) () | None -> ( match christmastide_feria_slug d with | Some slug -> diff --git a/test/test_temporal_ef.ml b/test/test_temporal_ef.ml index 7837fb5..8a0f38e 100644 --- a/test/test_temporal_ef.ml +++ b/test/test_temporal_ef.ml @@ -175,6 +175,9 @@ let rank_of dt = V.rank_to_string (office dt).Cel.rank let colour_of dt = Colr.to_string (office dt).Cel.colour let subject_of dt = (office dt).Cel.subject let temporal_week dt = (T.temporal dt).Colitur_kernel.Temporal.week +let la = Colitur_kernel.Lang.of_string_exn "la" +let en = Colitur_kernel.Lang.of_string_exn "en" +let name_la_of dt = Colitur_kernel.Names.find (office dt).Cel.names la (* RG 17(b) (Caput III, "De Dominicis"), primary text (scan-verified, docs/research/rules-register.md §4): "festum S. Familiae Iesu, Mariae, @@ -207,6 +210,19 @@ let test_holy_family () = classis\" -- the mystery of the Holy Family touches Christ)" true (subject_of (d 2026 1 11) = Sub.Lord); + (* fix round 1 (coordinator finding 3): a primary-sourced LATIN name, + zero circularity (the calendarium's own January table and the Mass + propers' own heading, both photographic scans, word for word: + "Sanctae Familiae Iesu, Mariae, Ioseph"). No ENGLISH name -- deliberate + (temporal_ef.ml's own comment), and every OTHER temporal-cycle + candidate still carries no name of any kind. *) + Alcotest.(check (option string)) "2026: Latin name from the calendarium/propers, verbatim" + (Some "Sanctae Familiae Iesu, Mariae, Ioseph") (name_la_of (d 2026 1 11)); + Alcotest.(check (option string)) "2026: no English name (deliberate -- would read the oracle it is \ + compared against)" + None (Colitur_kernel.Names.find (office (d 2026 1 11)).Cel.names en); + Alcotest.(check (option string)) "2026: an ordinary neighbouring Sunday carries no Latin name either" + None (name_la_of (d 2026 1 18)); (* 2019: Epiphany itself is a Sunday, so the first Sunday AFTER it is the LATEST possible date, 13 January -- the one date Holy Family collides with the fixed Commemoration of the Baptism of the Lord (data/ef/ -- cgit v1.3