diff options
Diffstat (limited to 'lib/rites/rite_ef/precedence_ef.ml')
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 149 |
1 files changed, 97 insertions, 52 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 87cdeeb..39251a7 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -48,33 +48,72 @@ let universal_layer = "ef-universal" let indult_prefix = "indult:" let unclassified = max_int -(* Not an RG citation -- an ordering device for 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 the shared integer 14, because a tie would fall to +(* 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 THREE rows, not only this one -- + entry 12's own ninth sub-item ("Festa indulta I classis, primum + mobilia, deinde fixa"), entry 14 (this one), and entry 20 ("indult + II-class feasts (movable then fixed)", register §4's own summary) -- + and only entry 14 has a live witness in this codebase's data today. 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". Negative, so it can never collide with any real table position - (1..28) or [unclassified] (positive [max_int]) by construction -- not a - citation to some entry "13.5" that does not exist in the primary text, - just an ordering fact this integer has to carry because RG 91's own two - half-rows must compare unequal here. 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 = -14 + 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 @@ -155,8 +194,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 @@ -199,9 +242,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 @@ -209,25 +252,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 @@ -237,20 +280,20 @@ 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 + 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, @@ -272,30 +315,32 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc 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 + 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 @@ -303,13 +348,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 @@ -318,12 +363,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/ |
