From 6d6ba502d8022d9e0b8cdc5302f5761d39895192 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 10:40:27 +0200 Subject: kernel+rite-ef: correct stale plan-relative deferrals (item 7, part 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several comments described work as "arriving with Plan 3" or "waiting for Plan 3" that either shipped without it or refer to the wrong future plan number, all now false in ways that would mislead the next reader: - temporal_ef.ml (two places) and precedence_ef.ml said the Major Litanies (25 April, RG 80) "arrive with Plan 3's sanctoral". Plan 3 shipped, in this branch, without them; register §6 tracks this as a plain open item with no plan committed to build it, and now says so. - temporal_ef.ml said the Sacred Triduum's "own named offices are a Plan 3 sanctoral addition". Wrong on two counts: Plan 3 shipped without adding them, AND a proper office for I-class FERIAS was never a sanctoral matter in the first place (RG 21 defines "feria" to exclude Sundays/feasts, not the reverse). 2026-04-02/03/04 still resolve to the ordinary Passiontide ferial fallback's own generic slugs (ef-passiontide-2-{thursday,friday,saturday}), confirmed against real output; register §6 now records this as its own open item. - temporal_ef.ml said the Rogation-Wednesday commemoration (Ascension Vigil day, entry 21) "waits for RG 108-111" -- both the precedence framework and RG 108-111 exist now; the Wednesday's own commemoration is still never constructed, but for a different, still-real reason (no candidate is wired for it), not a forward dependency. Fixed at both of this comment's two occurrences in the file. - vocab.ml/vocab.mli's `seasons` field doc said "Validate's contiguity check reads this" -- false since validate.ml's "seasons" check switched to Rite.t.season_runs in this branch (rite-supplied, to support a season appearing in more than one run, which the modern form's Ordinary Time needs and EF does not). - vocab.ml/vocab.mli's `ranks` field doc said "it is not a precedence relation until Plan 3 defines one" -- Plan 3 did define one (RG 111's dignity ordering), but as its own small, separately-hardcoded function in precedence_ef.ml, not one derived from this field; corrected to say so precisely rather than leaving a forward-looking claim unresolved. - The lectionary bootstrap (reading citations, Liturgical_day.t's own `citations` field) is called "Plan 3" at slug.ml, vocab_ef.ml, and three places in temporal_ef.ml, but "Plan 4" at liturgical_day.mli (whose own doc comment -- "always empty until Plan 4" -- is the authoritative one: the SANCTORAL bootstrap is Plan 3 and shipped; the LECTIONARY bootstrap is a separate, later Plan 4). All six corrected to say Plan 4, cross-referencing the Plan 3/4 distinction at the first (slug.ml) occurrence so the reasoning is not duplicated six times. No behaviour change: every edit here is a comment/documentation correction. Verified byte-identical `colitur day` output across 1583, 1900, 1902, 2008, 2011, 2026, 2038, 9999. 259/259 tests green. --- lib/kernel/slug.ml | 8 +++- lib/kernel/vocab.ml | 25 +++++++++++-- lib/kernel/vocab.mli | 25 +++++++++++-- lib/rites/rite_ef/precedence_ef.ml | 12 +++--- lib/rites/rite_ef/temporal_ef.ml | 77 +++++++++++++++++++++++++++----------- lib/rites/rite_ef/vocab_ef.ml | 5 ++- 6 files changed, 117 insertions(+), 35 deletions(-) diff --git a/lib/kernel/slug.ml b/lib/kernel/slug.ml index 5139c99..25281f2 100644 --- a/lib/kernel/slug.ml +++ b/lib/kernel/slug.ml @@ -1,6 +1,10 @@ (* Stable celebration identifiers. Also the lectionary key: EF slugs are adopted - verbatim from lectio so the Plan 3 lectionary bootstrap needs no mapping - table (spec §4.4). *) + verbatim from lectio so the Plan 4 lectionary bootstrap needs no mapping + table (spec §4.4). CORRECTED (final fix wave, item 7): this comment said + "Plan 3" -- the SANCTORAL bootstrap (data/ef/sanctoral.sexp) is Plan 3 and + shipped in this branch; the LECTIONARY bootstrap (reading citations, + Liturgical_day.t's own [citations] field) is a separate, later Plan 4, + per that field's own doc comment ("always empty until Plan 4"). *) type t = string let valid_char c = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c = '-' diff --git a/lib/kernel/vocab.ml b/lib/kernel/vocab.ml index 78729bb..bcb48b8 100644 --- a/lib/kernel/vocab.ml +++ b/lib/kernel/vocab.ml @@ -7,13 +7,32 @@ parametric types natively. *) type ('s, 'r) t = { seasons : 's list; - (** canonical liturgical-year order; Validate's contiguity check reads this *) + (** canonical liturgical-year order. CORRECTED (final fix wave, item + 7): this used to say "Validate's contiguity check reads this" -- + false since validate.ml's own "seasons" check switched to + {!Colitur_kernel.Rite.t}.season_runs in this branch (Plan 2 + carried item 1: EF has each season in one run, but the modern + form's Ordinary Time does not, so the expected run sequence had + to become rite-supplied rather than derived from this field). + For EF specifically {!Rite_ef.rite_ef.ml} sets season_runs to + this very list, so the two happen to agree there, but Validate + itself no longer reads [seasons] to build its expectation. *) season_to_string : 's -> string; season_of_string : string -> 's option; ranks : 'r list; (** documentation order, highest first. Plan 2 uses it only for the - closure check -- it is not a precedence relation until Plan 3 - defines one. *) + closure check. CORRECTED (final fix wave, item 7): this used to + say "it is not a precedence relation until Plan 3 defines one" -- + Plan 3 did define one (RG 111's dignity ordering, Rite_ef. + Precedence_ef.dignity/compare_dignity), but as its OWN small, + separately-hardcoded function, not one derived from this field: + [admit] needs Vocab_ef.rank's dignity as plain data (RG 8's four + classes), and reusing this field's own [int list] position would + couple that meaning to documentation order the way {!band} is + explicitly NOT allowed to (precedence_ef.ml's own file comment). + This field therefore still carries no precedence relation of its + own; a rite that wanted one derived from it would have to build + it itself. *) rank_to_string : 'r -> string; rank_of_string : string -> 'r option; } diff --git a/lib/kernel/vocab.mli b/lib/kernel/vocab.mli index 78729bb..bcb48b8 100644 --- a/lib/kernel/vocab.mli +++ b/lib/kernel/vocab.mli @@ -7,13 +7,32 @@ parametric types natively. *) type ('s, 'r) t = { seasons : 's list; - (** canonical liturgical-year order; Validate's contiguity check reads this *) + (** canonical liturgical-year order. CORRECTED (final fix wave, item + 7): this used to say "Validate's contiguity check reads this" -- + false since validate.ml's own "seasons" check switched to + {!Colitur_kernel.Rite.t}.season_runs in this branch (Plan 2 + carried item 1: EF has each season in one run, but the modern + form's Ordinary Time does not, so the expected run sequence had + to become rite-supplied rather than derived from this field). + For EF specifically {!Rite_ef.rite_ef.ml} sets season_runs to + this very list, so the two happen to agree there, but Validate + itself no longer reads [seasons] to build its expectation. *) season_to_string : 's -> string; season_of_string : string -> 's option; ranks : 'r list; (** documentation order, highest first. Plan 2 uses it only for the - closure check -- it is not a precedence relation until Plan 3 - defines one. *) + closure check. CORRECTED (final fix wave, item 7): this used to + say "it is not a precedence relation until Plan 3 defines one" -- + Plan 3 did define one (RG 111's dignity ordering, Rite_ef. + Precedence_ef.dignity/compare_dignity), but as its OWN small, + separately-hardcoded function, not one derived from this field: + [admit] needs Vocab_ef.rank's dignity as plain data (RG 8's four + classes), and reusing this field's own [int list] position would + couple that meaning to documentation order the way {!band} is + explicitly NOT allowed to (precedence_ef.ml's own file comment). + This field therefore still carries no precedence relation of its + own; a rite that wanted one derived from it would have to build + it itself. *) rank_to_string : 'r -> string; rank_of_string : string -> 'r option; } diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 9997bb2..fcd2116 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -429,11 +429,13 @@ let privilege_of (c : Vocab_ef.rank Precedence.candidate) : Precedence.privilege else if is_temporal && List.exists (fun p -> String.starts_with ~prefix:p slug) alp_feria_prefixes then Precedence.Privileged (* (f) RG 109(f) (§4): "of the Major Rogations, in Mass" -- the - Major Litanies (25 April, RG 80) are not yet computed anywhere in this - codebase (temporal_ef.ml's own comment on [temporal]'s Rogation branch: - "The Major Litanies... are a fixed date and are not yet computed; they - arrive with Plan 3's sanctoral"), so no candidate this engine can - currently construct represents one. There is no existing slug + Major Litanies (25 April, RG 80) are STILL not computed anywhere in + this codebase (temporal_ef.ml's own comment on [temporal]'s Rogation + branch, CORRECTED final fix wave item 7: they did not, in fact, + "arrive with Plan 3's sanctoral" -- Plan 3 shipped without them, + register §6 tracks this as an open item with no plan yet committed to + build it), so no candidate this engine can currently construct + represents one. There is no existing slug convention to anchor a check to, and guessing one risks silently misclassifying whatever a future task does name it -- a wrong citation is worse than a missing one, so this is left unimplemented and flagged diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index c3db15c..7782876 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -90,7 +90,10 @@ let named d = if m = 12 && dd = 25 then Some (Christmastide, "ef-nativity", Colour.White, Class1) else if m = 12 && dd = 24 then (* RG 91 entry 5: the Vigil of the Nativity is I class. lectio has no slug - for it, so this key has no lectionary entry until Plan 3 fills it. *) + for it, so this key has no lectionary entry until Plan 4 fills it + (CORRECTED, final fix wave, item 7 -- this is the lectionary/reading- + citations bootstrap, Plan 4, not the sanctoral one, Plan 3, which + already shipped in this branch). *) Some (Advent, "ef-nativity-vigil", Colour.Violet, Class1) else if m = 12 && (dd = 29 || dd = 30 || dd = 31) then (* Days within the Octave of the Nativity; 26-28 Dec are Stephen, John and @@ -111,9 +114,15 @@ let named d = else if same d (off 7) then Some (Paschaltide, "ef-low-sunday", Colour.White, Class1) (* RG 91 entry 6 *) else if same d (off 38) then - (* RG 91 entry 21: II-class vigil. It is also Rogation Wednesday; with no - precedence framework until Plan 3, temporal emits the higher-ranked - vigil and the Rogation commemoration waits for RG 108-111. *) + (* RG 91 entry 21: II-class vigil. It is also Rogation Wednesday; [named] + emits the higher-ranked vigil (entry 21 outranks any Rogation-day + ferial rank). CORRECTED (final fix wave, item 7): this comment + previously said the Rogation commemoration itself "waits for RG + 108-111" -- the precedence framework and RG 108-111 both exist now + (this branch), but no candidate for the Rogation Wednesday's own + observance is constructed here or anywhere else, so there is nothing + for RG 108-111 to admit; see the fuller comment on the Rogation + branch further down in [temporal] for the current, still-real gap. *) Some (Paschaltide, "ef-ascension-vigil", Colour.White, Class2) else if same d (off 39) then Some (Paschaltide, "ef-ascension", Colour.White, Class1) else if same d (off 48) then Some (Paschaltide, "ef-pentecost-vigil", Colour.Red, Class1) (* RG 91 entry 9 *) @@ -183,8 +192,10 @@ let week d = this to "ef-christmas-0-", indistinguishable from the stretch above in lectio's own data. colitur cannot preserve a distinction lectio doesn't make, so this becomes "ef-christmas-1-" -- a - colitur-only key and a lectionary gap for the Plan 3 bootstrap to fill, - exactly like the Nativity vigil and octave-day keys above. + colitur-only key and a lectionary gap for the Plan 4 bootstrap to fill + (CORRECTED, final fix wave, item 7: the lectionary bootstrap is Plan 4, + not Plan 3 -- see Slug.ml's own corrected comment), exactly like the + Nativity vigil and octave-day keys above. - 7-13 Jan, split in two by the *actual* first-Sunday-after-Epiphany origin ([week_origin Time_after_epiphany], which by construction always falls somewhere in this window -- see that function's own comment): @@ -303,8 +314,10 @@ let third_sunday_of_september y = The September and Advent sets match lectio's own Ember slugs. The Lent and Whitsun (Pentecost) sets do not -- lectio has no Ember slug for either, so "ef-lent-ember-*" and "ef-pentecost-ember-*" are colitur-only keys and a - lectionary gap for the Plan 3 bootstrap to fill (spec §4.4), the same - status as the Nativity vigil and the Rogation days below. *) + lectionary gap for the Plan 4 bootstrap to fill (spec §4.4; CORRECTED, + final fix wave, item 7 -- Plan 4, not Plan 3, is the lectionary + bootstrap), the same status as the Nativity vigil and the Rogation days + below. *) let ember d = let y = Date.year d in let easter = Computus.gregorian_easter y in @@ -327,12 +340,23 @@ let ember d = (* RG 91 entry 7: Ash Wednesday (named above) and Monday-Wednesday of Holy Week are I-class ferias -- the primary text reads "feria IV cinerum et II, - III et IV Hebdomadae sanctae", i.e. explicitly stops at Wednesday. Thursday - to Saturday of Holy Week are the Sacred Triduum, RG 91 entry 2 -- ranked - even above entry 7, not a mere feria -- but their own named offices are a - Plan 3 sanctoral addition; until then this gives them the same I-class rank - via the generic ferial path. RG 91 entry 10: the weekdays within the - privileged Octaves of Easter and Pentecost are I class too. *) + III et IV Hebdomadae sanctae", i.e. explicitly stops at Wednesday + (PRIMARY-SOURCE-VERIFIED, final fix wave: confirmed word for word + against the scan). Thursday to Saturday of Holy Week are the Sacred + Triduum, RG 91 entry 2 -- ranked even above entry 7, not a mere feria -- + but the Sacred Triduum has NO PROPER OFFICE of its own in this codebase + (CORRECTED, final fix wave, item 7: this comment previously said their + "own named offices are a Plan 3 sanctoral addition"; WRONG on two + counts -- Plan 3 shipped, in this branch, without adding them, AND a + proper office for I-class FERIAS was never a sanctoral matter to begin + with, RG 21's own definition of "feria" excludes Sundays/feasts, not the + other way round). `Temporal_ef.temporal 2026-04-02/03/04` (Holy + Thursday/Good Friday/Holy Saturday) still resolve today to the ordinary + Passiontide ferial fallback's own generic slugs, + "ef-passiontide-2-{thursday,friday,saturday}" -- register §6 records + this as its own open item now. This gives them the same I-class rank + via the generic ferial path regardless. RG 91 entry 10: the weekdays + within the privileged Octaves of Easter and Pentecost are I class too. *) let privileged_feria d = let easter = Computus.gregorian_easter (Date.year d) in let n = days_between easter d in @@ -392,16 +416,27 @@ let temporal d = | None -> ( (* Rogations (the Minor Litanies only -- RG 87, Monday and Tuesday before Ascension). The Major Litanies (25 April, RG 80) are a fixed - date and are not yet computed; they arrive with Plan 3's sanctoral - (register §6). The Wednesday here is the Ascension vigil (see Task - 11). RG 88: "de Litaniis minoribus nihil fit in Officio" -- the - Office (hence the day's rank) is unchanged by the Rogation; only the - Mass is proper. No RG 91 table entry elevates these days, so they + date and are STILL not computed (CORRECTED, final fix wave, item 7: + this comment previously said "they arrive with Plan 3's sanctoral" + -- Plan 3 shipped, in this branch, without them; register §6 tracks + this as a plain open item, with no plan committed to build it yet). + The Wednesday here is the Ascension vigil (see Task 11), which + happens to also fall on Rogation Wednesday -- the vigil (higher + RG 91 entry) is what [named] emits for that date; the Rogation + Wednesday's own commemoration is not separately constructed (a real + gap, not a forward dependency: the precedence framework and RG + 108-111 both exist now, but nothing wires a Rogation-Wednesday + candidate into the contest for this specific date the way Monday + and Tuesday get one below). RG 88: "de Litaniis minoribus nihil fit + in Officio" -- the Office (hence the day's rank) is unchanged by + the Rogation; only the Mass is proper. No RG 91 table entry + elevates these days, so they take the ordinary ferial rank of their season via [ferial_rank] rather than a fixed class. lectio has no Rogation slug at all, so "ef-rogation-monday"/"-tuesday" are colitur-only keys and a - lectionary gap for Plan 3, like the Ember and Nativity-vigil keys - above. *) + lectionary gap for Plan 4 (CORRECTED, final fix wave, item 7 -- + the lectionary bootstrap is Plan 4, not Plan 3), like the Ember + and Nativity-vigil keys above. *) let rogation = days_between easter d in if rogation = 36 || rogation = 37 then build ~season:s diff --git a/lib/rites/rite_ef/vocab_ef.ml b/lib/rites/rite_ef/vocab_ef.ml index 13e7309..e5726e8 100644 --- a/lib/rites/rite_ef/vocab_ef.ml +++ b/lib/rites/rite_ef/vocab_ef.ml @@ -45,7 +45,10 @@ let season_of_string = function (* Deliberately NOT season_to_string: slugs are lectionary keys adopted verbatim from lectio, which names these two seasons differently. Changing these words - would silently break the Plan 3 lectionary bootstrap. *) + would silently break the Plan 4 lectionary bootstrap (CORRECTED, final fix + wave, item 7 -- see Slug.ml's own corrected comment for the Plan 3/4 + distinction: the sanctoral bootstrap these slugs already serve is Plan 3 + and shipped; the lectionary/reading-citations bootstrap is Plan 4). *) let season_slug_word = function | Christmastide -> "christmas" | Paschaltide -> "easter" -- cgit v1.3