summaryrefslogtreecommitdiff
path: root/lib/rites
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rites')
-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