aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-13 10:04:21 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-13 10:04:21 +0200
commit59fbda87d565ad9b98c2493e9703390cd988609d (patch)
tree71ebfb8e9568cd5003c10dabc8804fba4ca66e76 /lib/rites/rite_ef
parent254cfd7d9111b0f23f9c44e82d3e31d5817dc5a4 (diff)
parente30a4be5a2594fb3b6c4b51345d138cd89ba78ad (diff)
downloadcolitur-59fbda87d565ad9b98c2493e9703390cd988609d.tar.gz
colitur-59fbda87d565ad9b98c2493e9703390cd988609d.zip
Merge branch 'ef-rg112-rg110': the Holy Family, RG 112(a), observed identity
Builds the Feast of the Holy Family (RG 17(b), first Sunday after Epiphany, II class), which did not exist in colitur at all -- it emitted an ordinary Sunday on all 8417 days the feast occurs. Adds RG 112(a)'s exclusion, on RG 95 paragraph 2's authority ('Si vero duo festa eiusdem Divinae Personae ... fit de festo, quod in tabella praecedentiae superiorem obtinet locum, et aliud omittitur'), corroborated by RG 112(a) and by the Holy Family Mass propers' own 13-January rubric. Three validation layers had missed the missing feast, each for its own reason: the differential because lectio lacks it too and shares colitur's data lineage; the golden layer for want of a pin; and the oracle because it compared the observed day's rank and colour but never its identity. That last gap is the more important half of this change and is now closed -- the oracle compares observed identity, with the unresolvable population counted and pinned rather than silently skipped. Also rescales RG 91's band table x10. entry_14_movable_band had been a negative sentinel, chosen to avoid collision with a real table position; it also inverted ordering, making a movable II-class feast of the Lord outrank every I-class day. Unreachable on universal data, live the moment a diocesan overlay places a I-class proper or indult feast in the 7-13 January window. A half-row is now an ordinary position between its neighbours. The rescale was verified entry-by-entry against a photographic scan and moves zero days across the domain. RG 110 is NOT built despite the branch name; it remains open, as do the Holy Name of Jesus office (missing in 3619 of 8417 years) and RG 98's transfer-ordering mechanism.
Diffstat (limited to 'lib/rites/rite_ef')
-rw-r--r--lib/rites/rite_ef/precedence_ef.ml252
-rw-r--r--lib/rites/rite_ef/precedence_ef.mli58
-rw-r--r--lib/rites/rite_ef/temporal_ef.ml116
-rw-r--r--lib/rites/rite_ef/temporal_ef.mli7
4 files changed, 376 insertions, 57 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml
index 750da8b..374e9a4 100644
--- a/lib/rites/rite_ef/precedence_ef.ml
+++ b/lib/rites/rite_ef/precedence_ef.ml
@@ -48,6 +48,82 @@ let universal_layer = "ef-universal"
let indult_prefix = "indult:"
let unclassified = max_int
+(* CORRECTED, fix round 1 (coordinator finding 1) -- the ORIGINAL version of
+ this constant was [-14], justified only by "cannot collide with a real
+ table position (1..28) or [unclassified]". That is a COLLISION argument,
+ not an ORDERING one, and [band]'s own return value is compared by
+ {!Precedence.compare_by}/{!compare_precedence} as a plain [<] -- a
+ negative value is not merely "distinct from 1..28", it is LOWER than
+ every one of them, so a movable Class2 Lord feast (Holy Family) would
+ have beaten every I-class day (entries 1-13) outright, not merely the
+ fixed Baptism it was built to beat. Not live against the shipped
+ UNIVERSAL sanctoral data (no I-class universal feast falls 7-13 January),
+ which is why the original 1583-9999 blast radius (§6.2) showed only the
+ one intended shape -- but entries 12/13 admit PROPER and INDULT I-class
+ feasts too (patron/titular/dedication days, {!is_universal}/{!is_indult}
+ below), which arrive via `data/ef/adjustments.sexp`-style overlays, this
+ architecture's own advertised extension point -- a diocesan I-class
+ patronal feast landing 7-13 January would have made the inversion live
+ immediately, transferring the I-class feast as though a II-class Sunday
+ had impeded it. Demonstrated directly (fix round 1, reproduced then
+ reverted): a synthetic Class1 sanctoral candidate on 11 January was
+ OBSERVED as Holy Family (band -14) and the synthetic candidate itself
+ TRANSFERRED to the next day -- backwards on every count.
+
+ FIXED: every {!band} branch now returns the real RG 91 entry number
+ TIMES TEN (entry 1 -> 10, entry 28 -> 280, {!unclassified} untouched at
+ [max_int]) instead of the bare 1..28 -- not an arbitrary rescale, a
+ deliberate one: RG 91's own text records a "primum mobilia, deinde
+ fixa" (movable-then-fixed) split at FIVE rows, not only this one --
+ entry 13 ("Festa indulta I classis, primum mobilia, deinde fixa", its
+ own numbered row on both scans), entry 14 (this one), entry 20, entry
+ 23's third sub-item ("deinde festa indulta, primum mobilia, deinde
+ fixa"), and entry 24 ("Festa III classis, in calendario Ecclesiae
+ universae inscripta, primum mobilia, deinde fixa") -- and only entry 14
+ has a live witness in this codebase's data today. NOTE, corrected by the
+ fix-round re-review: this comment previously said THREE rows and
+ attributed the first to "entry 12's own ninth sub-item". Both scans, and
+ the register's own §4 table, put that clause at entry 13; entry 12 has
+ eight sub-items and no such clause. Entry 24's split is the notable
+ omission -- it is the most-evaluated band in the whole domain and is
+ unsplit here, which is exactly the kind of sub-rank this headroom exists
+ for. The
+ ×10 scale reserves nine integers of genuine headroom immediately BEFORE
+ each real entry's own value for exactly this kind of sub-rank, without
+ requiring a second rescale if entry 12's or entry 20's own movable half
+ ever needs one too -- a value DERIVABLE from RG 91's own table structure,
+ not an arithmetic convenience chosen to dodge one collision. [band]'s own
+ entry-14 comments (below) restate this scale at each branch it touches. *)
+let entry_14_fixed_band = 140
+
+(* RG 91 entry 14's own text (scan-verified, docs/research/rules-register.md
+ §4): "14. Festa Domini II classis, PRIMUM MOBILIA, DEINDE FIXA" -- feasts
+ of the Lord, II class, MOVABLE FIRST, then fixed. Both halves are the
+ SAME numbered table row, but the primary text states a real priority
+ between them, not merely two disjoint categories -- [band]'s own
+ entry-14 branches below must not literally TIE at
+ {!entry_14_fixed_band}, because a tie would fall to
+ {!Precedence.resolve}'s own kernel-level fallback ([compare_by],
+ Slug.compare), which is deliberately NOT rubric-authoritative (the exact
+ anti-pattern RG 113's own fix already corrected once for commemoration
+ ordering, this file's own header) and would in fact pick the WRONG side
+ here: "commemoration-of-the-baptism-of-the-lord" sorts before
+ "ef-time-after-epiphany-sunday-1" alphabetically, backwards from "primum
+ mobilia". Derived from {!entry_14_fixed_band}, one better (lower) than
+ it, using the one integer of headroom the ×10 scale reserves immediately
+ before every real entry -- not a citation to some entry "13.5" that does
+ not exist in the primary text, just the ordering fact RG 91's own two
+ half-rows require, expressed the same way the whole table now is. The
+ one real witness today: Holy Family (RG 17(b), temporal_ef.ml's own
+ [holy_family_sunday]) against the fixed Commemoration of the Baptism of
+ the Lord (13 January) -- the only date they can ever coincide, since
+ Holy Family only ever falls 7-13 January and no OTHER fixed Class2-Lord
+ sanctoral entry shares that window (register §6.0's own subject audit:
+ Transfiguration 6 Aug, Exaltation of the Cross 14 Sep, Dedication of the
+ Archbasilica 9 Nov, the Purification 2 Feb, the Baptism 13 Jan -- the
+ Baptism alone falls in Holy Family's own window). *)
+let entry_14_movable_band = entry_14_fixed_band - 1
+
let is_indult layer = String.starts_with ~prefix:indult_prefix layer
let is_universal layer = String.equal layer universal_layer
@@ -127,8 +203,12 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
(* Named so entry 8's Sunday exception below can read "one worse than the
Sunday it must yield to" rather than a bare integer that happens to
equal entry 15's own value; entry 15's own branch returns this same
- binding, not a second literal, so the two can never drift apart. *)
- let entry_15_band = 15 in
+ binding, not a second literal, so the two can never drift apart.
+ CORRECTED, fix round 1 (coordinator finding 1): every branch below now
+ returns the real RG 91 entry number TIMES TEN, not the bare 1..28 --
+ see {!entry_14_fixed_band}'s own comment for why (a genuine ordering
+ bug the bare scale could not express, not a cosmetic rename). *)
+ let entry_15_band = 150 in
let open Vocab_ef in
(* CORRECTED (Task B fix round 1, ef-rg16a): a [Commemoration_only]
celebration has NO row in RG 91's table at all, checked FIRST, ahead of
@@ -171,9 +251,9 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
ea22ad2. The guard is right; its former justification was not. *)
if status = Celebration.Commemoration_only then unclassified
(* 1: Nativity, Easter Sunday, Pentecost Sunday (I class w/ octave). *)
- else if is_temporal && rank = Class1 && ((m = 12 && d = 25) || off = 0 || off = 49) then 1
+ else if is_temporal && rank = Class1 && ((m = 12 && d = 25) || off = 0 || off = 49) then 10
(* 2: Sacred Triduum (Thu-Sat of Holy Week). *)
- else if is_temporal && rank = Class1 && off >= -3 && off <= -1 then 2
+ else if is_temporal && rank = Class1 && off >= -3 && off <= -1 then 20
(* 3: Epiphany, Ascension, Holy Trinity, Corpus Christi, Sacred Heart,
Christ the King. *)
else if is_temporal && rank = Class1
@@ -181,25 +261,25 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
|| off = 39 (* Ascension *) || off = 56 (* Trinity *)
|| off = 60 (* Corpus Christi *) || off = 68 (* Sacred Heart *)
|| Date.compare date (Temporal_ef.christ_the_king (Date.year date)) = 0)
- then 3
+ then 30
(* 4: Immaculate Conception, Assumption BVM. *)
- else if (not is_temporal) && rank = Class1 && ((m = 12 && d = 8) || (m = 8 && d = 15)) then 4
+ else if (not is_temporal) && rank = Class1 && ((m = 12 && d = 8) || (m = 8 && d = 15)) then 40
(* 5: Vigil & Octave day of the Nativity. *)
- else if is_temporal && rank = Class1 && ((m = 12 && d = 24) || (m = 1 && d = 1)) then 5
+ else if is_temporal && rank = Class1 && ((m = 12 && d = 24) || (m = 1 && d = 1)) then 50
(* 6: Sundays of Advent, Lent, Passiontide, and Low Sunday. *)
else if is_temporal && rank = Class1 && is_sunday
&& (season = Advent || season = Lent || season = Passiontide || off = 7)
- then 6
+ then 60
(* 7: I-class ferias not above -- Ash Wednesday; Mon/Tue/Wed of Holy Week.
Thu-Sat of Holy Week are the Triduum, entry 2 above, not this entry. *)
- else if is_temporal && rank = Class1 && (off = -46 || (off >= -6 && off <= -4)) then 7
+ else if is_temporal && rank = Class1 && (off = -46 || (off >= -6 && off <= -4)) then 70
(* 8: All Souls -- RG 91 entry 8's own text (§4) carries a qualifier this
transcription must honour: "yields to an occurring Sunday". 2 November
is always Time_after_pentecost (well clear of Advent/Lent/Passiontide
and of every other entry's own Easter-relative or fixed date), so a
Sunday landing on it is always an ordinary entry-15 II-class Sunday --
the one and only rival this exception ever has to lose to. On such a
- Sunday this returns [entry_15_band + 1]: strictly worse than 15 (an
+ Sunday this returns [entry_15_band + 1]: strictly worse than 150 (an
exact tie would fall to Precedence.resolve's slug tie-break, which
for "ef-all-souls" against a "ef-time-after-pentecost-sunday-*" slug
would make All Souls WIN -- the precise bug this guards against), but
@@ -209,50 +289,67 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
feasts transfer) still sees the true I-class candidate it needs to
move to 3 November. *)
else if (not is_temporal) && rank = Class1 && m = 11 && d = 2 then
- if is_sunday then entry_15_band + 1 else 8
+ if is_sunday then entry_15_band + 1 else 80
(* 9: Vigil of Pentecost. *)
- else if is_temporal && rank = Class1 && off = 48 then 9
+ else if is_temporal && rank = Class1 && off = 48 then 90
(* 10: Days within the Octaves of Easter and Pentecost. *)
else if is_temporal && rank = Class1 && ((off >= 1 && off <= 6) || (off >= 50 && off <= 55))
- then 10
+ then 100
(* 11: I-class feasts of the universal Church not above. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class1 && is_universal layer then 11
+ else if (not is_temporal) && (not is_vigil) && rank = Class1 && is_universal layer then 110
(* 12: Proper I-class feasts. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class1 && not (is_indult layer) then 12
+ else if (not is_temporal) && (not is_vigil) && rank = Class1 && not (is_indult layer) then 120
(* 13: Indult I-class feasts. By elimination once 11 and 12 have failed:
not the universal layer (11), and marked as an indult overlay (12's
"not indult" test having just failed). *)
- else if (not is_temporal) && (not is_vigil) && rank = Class1 then 13
- (* 14: Feasts of the Lord, II class -- RG 91 entry 14, deliberately
- UNQUALIFIED (contrast entry 16, which explicitly says "not
- of the Lord"; RG 37c (§4, "Sundays") speaks of "II-class feasts of
- the Lord" replacing an occurring II-class Sunday with no universal
- qualifier either). No layer test here, unlike 11/12/13 and 16/19/20:
- the register does not split this entry into universal/proper/indult,
- so a proper or indult feast of the Lord still bands 14, not 19/20. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class2 && subject = Subject.Lord then 14
+ else if (not is_temporal) && (not is_vigil) && rank = Class1 then 130
+ (* 14, MOVABLE half -- RG 91 entry 14's own "primum mobilia, deinde
+ fixa" ({!entry_14_movable_band}'s own comment has the full citation
+ and reasoning). Checked before the FIXED half immediately below,
+ matching the primary text's own order, though [is_temporal] already
+ makes the two branches structurally disjoint on any one candidate
+ regardless of which is checked first -- the ordering here is for a
+ reader following RG 91's own prose, not for correctness. The only
+ witness this codebase currently constructs is Holy Family
+ (temporal_ef.ml's own [holy_family_sunday] branch, the one place
+ [subject] is ever [Lord] on a temporal-origin candidate); a
+ hypothetical movable Holy-Name-of-Jesus office (RG 17(a), still
+ unbuilt as its own named day, register §6) would reach this same
+ branch too, once built, since nothing here is keyed to Holy Family's
+ own slug. *)
+ else if is_temporal && rank = Class2 && subject = Subject.Lord then entry_14_movable_band
+ (* 14, FIXED half -- RG 91 entry 14, deliberately UNQUALIFIED (contrast
+ entry 16, which explicitly says "not of the Lord"; RG 37c (§4,
+ "Sundays") speaks of "II-class feasts of the Lord" replacing an
+ occurring II-class Sunday with no universal qualifier either). No
+ layer test here, unlike 11/12/13 and 16/19/20: the register does not
+ split this entry into universal/proper/indult, so a proper or
+ indult feast of the Lord still bands {!entry_14_fixed_band}, not
+ 19/20. *)
+ else if (not is_temporal) && (not is_vigil) && rank = Class2 && subject = Subject.Lord then
+ entry_14_fixed_band
(* 15: Sundays, II class (every Sunday not already named at 6). *)
else if is_temporal && rank = Class2 && is_sunday then entry_15_band
(* 16: II-class feasts of the universal Church, not of the Lord. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class2 && is_universal layer then 16
+ else if (not is_temporal) && (not is_vigil) && rank = Class2 && is_universal layer then 160
(* 17: Days within the Octave of the Nativity (26-28 Dec are Stephen,
John, the Innocents -- sanctoral, not this entry). *)
- else if is_temporal && rank = Class2 && m = 12 && (d = 29 || d = 30 || d = 31) then 17
+ else if is_temporal && rank = Class2 && m = 12 && (d = 29 || d = 30 || d = 31) then 170
(* 18: II-class ferias -- Advent 17-23 Dec; Ember days of Advent, Lent,
September. *)
else if is_temporal && rank = Class2
&& ((season = Advent && m = 12 && d >= 17 && d <= 23) || is_ember_18 slug)
- then 18
+ then 180
(* 19: Proper II-class feasts. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class2 && not (is_indult layer) then 19
+ else if (not is_temporal) && (not is_vigil) && rank = Class2 && not (is_indult layer) then 190
(* 20: Indult II-class feasts. By elimination, as at 13. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class2 then 20
+ else if (not is_temporal) && (not is_vigil) && rank = Class2 then 200
(* 21: II-class vigils (Ascension, Assumption, John Baptist, Peter & Paul
-- can be temporal- or sanctoral-origin, see the file comment above). *)
- else if rank = Class2 && is_vigil then 21
+ else if rank = Class2 && is_vigil then 210
(* 22: Ferias of Lent and Passiontide (Thursday after Ash Wednesday to the
Saturday before Palm Sunday), except the Ember days (18 above). *)
- else if is_temporal && rank = Class3 && (season = Lent || season = Passiontide) then 22
+ else if is_temporal && rank = Class3 && (season = Lent || season = Passiontide) then 220
(* 23: III-class feasts in particular calendars. Unlike 11/12 and 14/16
above, the universal entry (24) is the HIGHER number here -- RG 91's
own table ranks a particular-calendar III-class feast ahead of a
@@ -260,13 +357,13 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
the register states it, not "corrected" into the other classes'
pattern. RG 91 has no indult sub-rank at III class, so every non-base
layer lands here, not split further. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class3 && not (is_universal layer) then 23
+ else if (not is_temporal) && (not is_vigil) && rank = Class3 && not (is_universal layer) then 230
(* 24: III-class feasts in the universal calendar. *)
- else if (not is_temporal) && (not is_vigil) && rank = Class3 then 24
+ else if (not is_temporal) && (not is_vigil) && rank = Class3 then 240
(* 25: Ferias of Advent to 16 Dec, except the Ember days (18 above). *)
- else if is_temporal && rank = Class3 && season = Advent then 25
+ else if is_temporal && rank = Class3 && season = Advent then 250
(* 26: III-class vigils (St Lawrence). *)
- else if rank = Class3 && is_vigil then 26
+ else if rank = Class3 && is_vigil then 260
(* 27: Office of the BVM on Saturday -- every otherwise-unoccupied IV-class
Saturday, per the historical default that fills it; ordinary Mass
propers still make Rogation Mon/Tue/Wed proper without changing the
@@ -275,12 +372,12 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
vigils for the same reason 11-13/14/16/19/20/23/24 do: RG 91 has no
IV-class vigil at all (RG 91's own vigil list, §4 "Vigils",
stops at III class), so one would be an anomaly, not this entry. *)
- else if is_temporal && (not is_vigil) && rank = Class4 && weekday = Date.Sat then 27
+ else if is_temporal && (not is_vigil) && rank = Class4 && weekday = Date.Sat then 270
(* 28: IV-class ferias -- the unqualified catch-all (temporal_ef.ml's own
comment on [ferial_rank] cites the same primary text, "Feriae IV
classis"). Excludes vigils for the same reason as 27 above: a IV-class
"feria" that is also a vigil is not a feria RG 91 describes. *)
- else if (not is_vigil) && rank = Class4 then 28
+ else if (not is_vigil) && rank = Class4 then 280
else unclassified
(* Task 8: what happens to the day's LOSING candidate (docs/research/
@@ -732,6 +829,87 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate)
the RG26 branch above gives its own [not is_vigil] guard, rather than
relying on that absence silently. *)
Precedence.Omit
+ else if winner.Precedence.cel.Celebration.subject = Subject.Lord && cel.Celebration.subject = Subject.Lord
+ then
+ (* PRIMARY AUTHORITY -- CORRECTED, fix round 1 (coordinator finding 7):
+ RG 95's own SECOND paragraph (Caput XIII, "De dierum liturgicorum
+ occurrentia accidentali", immediately after the "only I-class feasts
+ translate" sentence {!disposition}'s own Transfer branch above
+ already cites), present verbatim in ALL THREE documents including
+ the electronic transcription -- an OCCURRENCE-level rule, closer to
+ this exact question than RG 112(a) below: "Si vero duo festa eiusdem
+ Divinae Personae aut duo festa eiusdem Sancti vel Beati simul
+ occurrunt, fit de festo, quod in tabella praecedentiae superiorem
+ obtinet locum et aliud omittitur" -- but if two feasts of the SAME
+ DIVINE PERSON, or two feasts of the same Saint or Blessed, occur
+ TOGETHER, [the Office] is made of the feast which holds the HIGHER
+ PLACE in the table of precedence, and the OTHER IS OMITTED. This is
+ the direct authority: two feasts of the same Divine Person
+ (RG 112(a)'s own vocabulary) occurring together, the higher-table one
+ kept, the other omitted -- exactly {!band}'s own
+ [entry_14_movable_band]/[entry_14_fixed_band] ordering plus this
+ branch's own [Omit], not [Commemorate].
+
+ CORROBORATION 1, RG 112(a) (docs/research/rules-register.md §4/§6.0,
+ primary text, verified against all three documents -- CORRECTED, fix
+ round 1 (coordinator finding 2): a previous version of this comment
+ claimed the electronic transcription drops this paragraph's own
+ worked example and the Mass-proper rubric below, "the transcription's
+ documented defect" -- WRONG on both counts, struck. RG 112 has NO
+ worked example in any of the three documents; there was nothing to
+ drop. The Mass-proper rubric's absence from the transcription is not
+ an instance of that document's documented defect either (dropped
+ CALENDARIUM commemoration lines) -- checked directly: that specific
+ transcription (1962-06-23,_SS_Ioannes_XXIII,_Missale_Romanum,
+ _LT.pdf, a 2006 web capture) contains almost no Mass-propers text of
+ any kind (5 "Introitus"/"Antiphona ad Introitum" occurrences in
+ 26,322 lines, against 61 and 402 in the two photographic scans); its
+ own "Proprium de Tempore" page is a TABLE OF CONTENTS linking to
+ separate PDF files the capture never pulled in. A coverage gap in a
+ partial web capture, not a silent drop from content it otherwise
+ has): "112. Ad commemorationes et orationes quod attinet, haec
+ insuper serventur: a) Officium, Missa aut commemoratio de aliquo
+ festo vel mysterio UNIUS DIVINAE PERSONAE excludit commemorationem
+ aut orationem de alio festo vel mysterio EIUSDEM DIVINAE PERSONAE" --
+ the Office, Mass, or commemoration of some feast or MYSTERY of ONE
+ Divine Person excludes a commemoration or oration of ANOTHER feast
+ or mystery of the SAME Divine Person.
+
+ {!Subject.t} has no finer split within [Lord] than "a mystery
+ touching the Divine Person of the Son" -- every real [subject =
+ Lord] entry this codebase's own data carries concerns Christ
+ specifically (register §6.0's subject audit: the Precious Blood, the
+ Transfiguration, the Exaltation of the Cross, the Dedication of the
+ Archbasilica, the Purification, the Baptism, and now Holy Family),
+ so reading "same Divine Person" as simply "both [Lord]" is safe at
+ today's data's own granularity -- a rite that ever needed to
+ distinguish, say, a Trinity-specific mystery from a Son-specific one
+ by Person would need a finer [Subject.t] first, not a special case
+ added here.
+
+ CORROBORATION 2 -- the Holy Family Mass propers' own note,
+ immediately following the Postcommunion, word for word on BOTH
+ photographic scans (real, and settles 13 January on its own even
+ apart from RG 95/112(a) -- the quotation itself is not in dispute,
+ only its earlier mis-attribution above): "Si festum S. Familiae
+ occurrerit die 13 ianuarii, Missa dicitur de festo S. Familiae, SINE
+ COMMEMORATIONE BAPTISMATIS D.N.I.C., et sine commemoratione
+ dominicae" -- if the feast of the Holy Family occurs on 13 January,
+ the Mass is said of the Holy Family, WITHOUT commemoration of the
+ Baptism of Our Lord Jesus Christ, and without commemoration of the
+ Sunday (RG 17(b)'s own general rule, restated for this specific
+ date). Independently confirmed against missalemeum (register §6.0):
+ title "The Holy Family: Jesus, Mary & Joseph", commemorations [],
+ the Baptism listed only under "displaced", never commemorated.
+
+ Checked after RG 16(a) above (so a genuine RG 16(a) Sunday-
+ suppression is never re-explained under this citation instead) and
+ before the final ordinary-commemoration catch-all below, since
+ without it a [Feast]-status loser of this shape would otherwise
+ reach RG 95's ordinary "commemorated or omitted" branch and be
+ admitted as an ordinary Class2 commemoration under RG 111(b) --
+ confirmed wrong against all three primary sources above. *)
+ Precedence.Omit
else
(* RG 95's other branch: "aut commemorantur aut penitus omittuntur" --
commemorated or wholly omitted. Reached by every SANCTORAL loser
diff --git a/lib/rites/rite_ef/precedence_ef.mli b/lib/rites/rite_ef/precedence_ef.mli
index b283a17..6a96c4e 100644
--- a/lib/rites/rite_ef/precedence_ef.mli
+++ b/lib/rites/rite_ef/precedence_ef.mli
@@ -62,16 +62,43 @@ val ember_prefixes : string list
new rule, not a silently wrong one. *)
val unclassified : int
+(** {!band}'s value for RG 91 entry 14's FIXED half (a sanctoral, i.e.
+ fixed-date, Class2 feast of the Lord) -- see {!entry_14_movable_band}'s
+ own comment for why entry 14 needs two distinct values at all. *)
+val entry_14_fixed_band : int
+
+(** RG 91 entry 14's own text (§4): "Festa Domini II classis, primum
+ mobilia, deinde fixa" -- feasts of the Lord, II class, MOVABLE first,
+ then fixed. {!band}'s value for the movable half (Holy Family, RG
+ 17(b)) -- [entry_14_fixed_band - 1], strictly better than the fixed
+ half without colliding with entry 13 or any other real table position.
+ CORRECTED, fix round 1 (coordinator finding 1): this used to be a
+ negative sentinel, justified only by "cannot collide with a real table
+ position" -- a COLLISION argument, not an ORDERING one; since {!band}
+ values are compared by plain [<], a negative value beats every I-class
+ entry (1-13) as well as the fixed half, not merely the fixed half it
+ was built to beat. {!band}'s own entries now use the real RG 91 number
+ TIMES TEN throughout, reserving genuine headroom before every entry --
+ see precedence_ef.ml's own comment for the full citation, the counter-
+ example that found this, and why entries 13, 20, 23 and 24 may need the
+ same treatment if their own "movable then fixed" halves ever get a
+ witness. (Corrected: this previously said "entries 12 and 20". RG 91
+ puts that clause at entry 13, not 12, and the split occurs at five rows
+ in all -- 13, 14, 20, 23's third sub-item and 24.) *)
+val entry_14_movable_band : int
+
(** [band ctx c]: RG 91's Table of Precedence. Returns the table's own entry
- number -- I class 1-13, II class 14-21, III class 22-26, IV class 27-28;
- lower wins (see {!Precedence.rules.band}) -- EXCEPT where the table's own
- text states an exception: entry 8 (All Souls) reads "yields to an
- occurring Sunday", so on a Sunday this returns a value that
- loses to entry 15 rather than the literal integer 8 (see the comment on
- entry 8 in precedence_ef.ml for the exact value and why). ALSO
- {!unclassified} for any [Cel.Commemoration_only] candidate, checked
- first, ahead of every rank-keyed branch -- CORRECTED, Task B fix round
- 1 (coordinator finding 1): RG 91's own table enumerates only "dies
+ number TIMES TEN -- I class 10-130, II class 140-210, III class 220-260,
+ IV class 270-280; lower wins (see {!Precedence.rules.band}) -- EXCEPT
+ where the table's own text states an exception: entry 8 (All Souls)
+ reads "yields to an occurring Sunday", so on a Sunday this returns a
+ value that loses to entry 15 (150) rather than 80 (see the comment on
+ entry 8 in precedence_ef.ml for the exact value and why); entry 14
+ (Feasts of the Lord, II class) splits into {!entry_14_movable_band} and
+ {!entry_14_fixed_band} per the table's own "primum mobilia, deinde
+ fixa". ALSO {!unclassified} for any [Cel.Commemoration_only] candidate,
+ checked first, ahead of every rank-keyed branch -- CORRECTED, Task B fix
+ round 1 (coordinator finding 1): RG 91's own table enumerates only "dies
liturgici" (entry 24's own text, "Festa III classis..." -- FEASTS), so
a celebration reduced to a bare commemoration was never a row in the
table to begin with, confirmed by the calendarium's own notation (a
@@ -134,6 +161,19 @@ val sunday_marker : string
weekday, never a saint's day, and RG 111(c)/(d) admit an "ordinary"
commemoration of a losing SAINT freely, with no such class-keyed
gate);
+ - a loser whose [subject] is [Lord] AND whose WINNER's [subject] is also
+ [Lord] is [Omit] (PRIMARY authority: RG 95's own second paragraph,
+ present in all three source documents including the electronic
+ transcription -- "if two feasts of the SAME DIVINE PERSON... occur
+ together, [the Office] is made of the one higher in the table of
+ precedence, and the OTHER IS OMITTED"; corroborated by RG 112(a) --
+ a feast or mystery of one Divine Person excludes a commemoration of
+ another feast or mystery of the SAME Divine Person -- and by the Holy
+ Family Mass propers' own more specific 13-January rubric on both
+ photographic scans; live witness: Holy Family, RG 17(b), excluding
+ the fixed Commemoration of the Baptism of the Lord on the years 13
+ January is itself Holy Family's own date -- see the .ml's own comment
+ for the full primary text of all three);
- everything else -- including an impeded I-class Sunday, and a
SANCTORAL loser of any rank below I class -- is [Commemorate],
carrying its real RG 109 privilege (see {!admit} below); RG 109(a)
diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml
index 7782876..1ce9de9 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,16 @@ 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) ?(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.Subject.Temporal ~layer:"temporal" ()
+ Colitur_kernel.Celebration.make ~slug:(Slug.of_string_exn slug) ~rank ~colour ~subject ~names
+ ~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 +482,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 +497,55 @@ 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] 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 ~names ~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 +553,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 +593,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 +619,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));
diff --git a/lib/rites/rite_ef/temporal_ef.mli b/lib/rites/rite_ef/temporal_ef.mli
index a07ffa2..68fe64e 100644
--- a/lib/rites/rite_ef/temporal_ef.mli
+++ b/lib/rites/rite_ef/temporal_ef.mli
@@ -14,6 +14,13 @@ val season : Date.t -> Vocab_ef.season
(** Last Sunday of October, per the 1960 calendar. *)
val christ_the_king : int -> Date.t
+(** RG 17(b): the first Sunday after Epiphany, in civil year [y] -- the
+ Feast of the Holy Family's own date. Always falls 7-13 January
+ inclusive; never empty of a Sunday (unlike RG 17(a)'s Holy Name, whose
+ narrower 2-5 January window can be, and carries an explicit calendarium
+ fallback for it), so this needs none. *)
+val holy_family_sunday : int -> Date.t
+
(** The named temporal days: I-class feasts of the Lord, vigils, and days within
the Octave of the Nativity. Returns (season, slug, colour, rank). Carries
no week of its own -- {!temporal} computes it uniformly via {!week} for