From fcfce4ba0ab4ea93837b647deaa5292300aaad5e Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 03:18:15 +0200 Subject: test: differential vs lectio 2005-2050 with a cited allow-list Validation layer 3 (design spec's five): compares colitur's real day-by-day EF output against lectio (sibling project, Go), 2005-2050, one line per civil day. Of 16801 day-pairs, 11206 already agree on the seven leading columns; the 5595 that don't resolve into exactly 25 distinct field-diff signatures, all triaged. Three strictly separate layers, per the controller's ruling (the brief's single flat allow-list assumed a handful of differences, not 5595): - Layer A (test_differential.ml, norm_season/norm_slug): vocabulary. An explicit, closed table of naming synonyms with no liturgical substance (lectio's easter/christmas vs colitur's paschaltide/christmastide; a handful of slugs that are two names for the identical office). No wildcards -- every entry is a literal string pair. - Layer B (strip_epiphany_index): numbering. The one slug family whose lectio/colitur index offset is not a constant (Time-after-Epiphany week numbering, register 3c#5) has its embedded digit stripped to a common form on both sides before comparing; rank and colour stay fully compared. - Layer C (data/ef/expected-divergences.sexp): the cited allow-list. Ten genuine liturgical disagreements, each citing its RG paragraph and naming which engine is right (always colitur, verified against the Missal/register, never against lectio's own behaviour). This is the only layer permitted to cover a difference in rank, colour, or which celebration is observed. Five extend or restate register 3c's already- documented divergences (season boundary, Sunday I-class, Advent Ember ferias, Rogations); five are new, found and adjudicated in this task (Lent Ember days, the Nativity Octave, Ember-day-vs-saint precedence, the St Joseph transfer off a Lent Sunday, and the 2011 Sacred Heart / Precious Blood / Visitation collision). expected_rows on each entry is an exact regression pin, asserted by the test, not documentation. 13 January (register 6's long-open "Baptism of the Lord" item) is confirmed empirically fixed already -- Task 11's sanctoral wiring closed it before this task started -- so it is not allow-listed; the only residual difference there is the season boundary already covered by C1. Two stated limits carried from the brief (commemorations are not comparable; lectio's own EF oracle asserts season only, 2025-2026 only, so a rank/colour difference is not presumptive evidence against colitur) plus a third found during this task (the week column is a display convention on both sides, not a liturgical fact, and is not compared at all) are documented in the test file's own doc comment. Fixture: test/fixtures/lectio-ef-2005-2050.txt, committed as plain text (1.4 MB), generated by lectio commit 2386a45; provenance recorded in the sibling .provenance file. Colitur's side is recomputed fresh from the library on every run, through the same Calendar/Rite_ef pipeline `colitur day` uses, not the compiled binary. Proved the harness has teeth by two reverted perturbations: a genuine colour difference injected into a fully-covered fixture row fails the "no unexplained differences" check with the exact mismatched row printed; a one-row drift in an allow-list entry's expected_rows fails the count check independently, showing it is not merely a duplicate of the first assertion. 236/236 tests green, clean-build verified, deterministic across OCAMLRUNPARAM=R. --- data/ef/expected-divergences.sexp | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 data/ef/expected-divergences.sexp (limited to 'data/ef/expected-divergences.sexp') diff --git a/data/ef/expected-divergences.sexp b/data/ef/expected-divergences.sexp new file mode 100644 index 0000000..17bafc1 --- /dev/null +++ b/data/ef/expected-divergences.sexp @@ -0,0 +1,69 @@ +; data/ef/expected-divergences.sexp -- Task 15's Layer C: the CITED allow-list +; for colitur's differential harness against lectio (test/test_differential.ml), +; 2005-2050. This is the only layer that may cover a difference in rank, +; colour, or which celebration is observed -- Layers A (vocabulary) and B +; (numbering) live in test_differential.ml's code because they normalise +; NAMES and INDICES only and never touch this file's territory. +; +; Each entry is a genuine liturgical disagreement between the two engines. +; [verdict] names which engine the register/Missal backs; every entry here +; is "colitur" -- an entry saying otherwise would mean "fix colitur", not +; "allow-list it" (task ruling). [expected_rows] is the exact row count this +; entry accounts for over the fixture's 2005-2050 span: a REGRESSION PIN, not +; documentation -- test_differential.ml asserts actual counts equal these +; exactly, so an unnoticed behaviour change here fails loudly instead of +; silently changing what the harness accepts. +; +; Regenerate expected_rows only after re-adjudicating the change against the +; Missal/register -- never by re-running the comparator and copying its +; output back here (that would launder a regression into a new baseline). +((id C1) + (citation "RG 72-73 (Nativity/Epiphany season boundary, Jan 1-13) + RG 119a (white through \"expletum tempus Epiphaniae\"); register §3c item 1") + (verdict colitur) + (note "6-13 January is Christmastide in colitur, matching RG 72-73's explicit boundary at 13 January (corroborated independently by RG 119a's colour rule, register lines 241-246). lectio's efSeason switches to time-after-epiphany on 6 January. The season disagreement cascades into colour (green vs white) and, on 7-8 January specifically, into the ferial slug family name (colitur's ef-christmas-2- vs lectio's ef-time-after-epiphany-1- for the same two days).") + (expected_rows 368)) + ((id C2) + (citation "RG 91 line 332 (\"Sundays of Advent, Lent, Passiontide, and Low Sunday\" -- I class, unqualified); extends register §3c item 4 (stated there for Advent only) to Lent on the same textual basis") + (verdict colitur) + (note "Advent II & IV and Lent I-III are I class in colitur, matching RG 91's own unqualified line 332 (already confirmed for Passion Sunday, Palm Sunday, Low Sunday and Advent I, where lectio agrees). lectio's temporal_ef.go generic Sunday branch assigns Class2 and special-cases only Advent I, Passion/Palm/Low Sunday -- a lectio gap for the remaining five Sundays this entry covers.") + (expected_rows 215)) + ((id C3) + (citation "RG 91 line 332 (same I-class Sunday rule as C2) + RG 131 (rose indult, Gaudete/Laetare, \"in Officio et Missa diei dominici tantum\")") + (verdict colitur) + (note "Gaudete (Advent III) and Laetare (Lent IV): I class per C2's citation, plus the RG 131 rose indult. lectio has no Rose case in its EF colour function and zero rose rows in tridentine-calendar.ini -- Rose exists only on lectio's OF path -- so it prints violet for both Sundays.") + (expected_rows 89)) + ((id C4) + (citation "RG 91 entry 18 (\"II-class ferias ... Advent 17-23 Dec\"); register §3c item 2") + (verdict colitur) + (note "Advent ferias 17-23 December (excluding the Advent Ember days, already correct on both sides) are II class in colitur. lectio marks every Advent feria III class.") + (expected_rows 140)) + ((id C5) + (citation "RG 91 entry 18 (\"Ember days of Advent, Lent, September\", the same entry as C4); extends register §3c item 2 to the Lent set on the same textual basis lectio already gets right for Advent and September") + (verdict colitur) + (note "The Lent I Ember Wednesday/Friday/Saturday are II class with colitur's own ef-lent-ember-{wed,fri,sat} slugs. lectio implements the Advent and September Ember exceptions (matches colitur exactly, confirmed empty diff there) but not the Lent one, falling back to a plain ef-lent-1- III-class feria -- a lectio gap not previously named in the register.") + (expected_rows 128)) + ((id C6) + (citation "RG 91 entry 17 (\"days within the Octave of the Nativity\")") + (verdict colitur) + (note "29-31 December (and any Sunday landing among them) are II class in colitur, with the ef-nativity-octave-day-{5,6,7} slugs. lectio treats them as plain class-4 ferias (ef-christmas-0-), not recognising the Nativity octave's own entry.") + (expected_rows 138)) + ((id C7) + (citation "RG 91 entries 16 (II-class feast of the universal Church) vs 18 (II-class ferias, incl. Ember days) -- entry 16 outranks entry 18") + (verdict colitur) + (note "St Matthew (21 September, colliding with the September Ember Wednesday or Friday in some years) and St Thomas (21 December, colliding with the Advent Ember Friday in some years) are both II-class universal feasts and win the day per RG 91's own numeric ordering -- matching Rite_ef.Precedence_ef.band's already-reviewed entry 16/18 handling. lectio does not contest Ember days against sanctoral feasts at all and keeps the Ember office observed.") + (expected_rows 34)) + ((id C8) + (citation "RG 87 (Minor Litanies/Rogations, Mon/Tue before Ascension); register §3c item 3") + (verdict colitur) + (note "Rogation Monday and Tuesday exist as colitur's own ef-rogation-{monday,tuesday} slugs (violet) whenever no higher-ranked saint intervenes. lectio computes no Rogation days at all and shows the plain paschaltide-week-6 feria instead.") + (expected_rows 31)) + ((id C9) + (citation "RG 95 (translation is a right of I-class FEASTS only) + RG 91 line 332 (Sundays are a separate table row, not feasts) + RG 109(a) (\"of a Sunday\" is a privileged commemoration, presupposing the Sunday stays put) + RG 96 (the general translation walk) -- the same reasoning already fixed for the analogous impeded-Sunday defect in Precedence_ef (Task 9)") + (verdict colitur) + (note "St Joseph (19 March, I class) colliding with a Lent Sunday (I class per C2): colitur keeps the Sunday observed and transfers Joseph to the next free day (usually 20 March; in years the walk is congested enough to cross Easter, as far as 1 April -- 2008, 2035, 2046, independently pinned by this project's own transfer tests). lectio instead puts Joseph ON the Sunday, displacing the Lenten Sunday office RG 95 says cannot be displaced by a translated feast.") + (expected_rows 15)) + ((id C10) + (citation "RG 91 entry 3 (Sacred Heart, a Feast of the Lord, outranks entry 11 Precious Blood) + RG 96 (impeded I-class feast moves to the next day that is NOT I or II class)") + (verdict colitur) + (note "2011: Sacred Heart (Friday after the Corpus Christi octave) falls on 1 July and impedes the Precious Blood (also 1 July, I class but a lower RG 91 entry). RG 96's walk must skip 2 July (Visitation, II class) and 3 July (Sunday), landing on 4 July (Monday, a free class-4 feria). lectio instead lands Precious Blood on 2 July, illegitimately displacing the Visitation -- exactly what RG 96's \"not I or II class\" clause forbids.") + (expected_rows 2)) -- cgit v1.3 From c464c0435c650cf019eb6c6ae1906217d557a15f Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 04:12:53 +0200 Subject: kernel(temporal): Holy Thursday is white, per RG128(b)'s named exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RG 128(b) excepts "Missa sive Chrismatis sive in Cena Domini feria V Hebdomadae sanctae" from the Septuagesima-to-Easter-Vigil violet span as a whole-Mass exception (unlike Palm Sunday's blessing/procession, which the same sentence carves out as only part of that day) -- so Holy Thursday's Mass is white, not violet. temporal_ef.ml fell through to the generic Passiontide ferial path (season_colour Passiontide = violet) for this date, since [named] has no entry for the Triduum at all (rank was already correct via [privileged_feria]; only colour was wrong). Found via Task 16's missalemeum oracle comparison: every other Triduum day's oracle colour set includes violet as an option (Good Friday "bv", Holy Saturday "vw" -- already-flagged, deliberately deferred per-action gaps, register §3b), but Holy Thursday's is white alone. This is a genuine NEW divergence from lectio too (lectio has no such exception and still prints violet for Holy Thursday) -- added as Layer C11 to the differential harness and data/ef/expected-divergences .sexp, 46 rows (2005-2050), rather than silently changing what that harness accepts. Also corrected a stale comment on season_colour claiming white's RG paragraph was never pinned -- RG 119 was found and cited in the register on 2026-08-11 but the correction was never copied into this file's own comment. --- data/ef/expected-divergences.sexp | 5 +++++ lib/rites/rite_ef/temporal_ef.ml | 33 ++++++++++++++++++++++++++++----- test/test_differential.ml | 16 +++++++++------- 3 files changed, 42 insertions(+), 12 deletions(-) (limited to 'data/ef/expected-divergences.sexp') diff --git a/data/ef/expected-divergences.sexp b/data/ef/expected-divergences.sexp index 17bafc1..6b7bbdd 100644 --- a/data/ef/expected-divergences.sexp +++ b/data/ef/expected-divergences.sexp @@ -67,3 +67,8 @@ (verdict colitur) (note "2011: Sacred Heart (Friday after the Corpus Christi octave) falls on 1 July and impedes the Precious Blood (also 1 July, I class but a lower RG 91 entry). RG 96's walk must skip 2 July (Visitation, II class) and 3 July (Sunday), landing on 4 July (Monday, a free class-4 feria). lectio instead lands Precious Blood on 2 July, illegitimately displacing the Visitation -- exactly what RG 96's \"not I or II class\" clause forbids.") (expected_rows 2)) + ((id C11) + (citation "RG 128(b), primary-source-verified 2026-08-12 (Task 16): \"...a dominica in Septuagesima usque ad Vigiliam paschalem, exceptis: ... Missa sive Chrismatis sive in Cena Domini feria V Hebdomadae sanctae; ...\" -- violet runs Septuagesima to the Easter Vigil EXCEPT (among other things) the Mass of Holy Thursday, named as a whole-Mass exception") + (verdict colitur) + (note "Holy Thursday (\"ef-passiontide-2-thursday\") is white in colitur, per RG 128(b)'s explicit exception -- independently confirmed by the missalemeum oracle (Task 16 report), whose colour set for that day is white alone, unlike the surrounding Triduum days which include violet as an option. lectio has no such exception and prints Passiontide's base violet straight through Holy Thursday. Found via Task 16's oracle comparison, not the original lectio differential (lectio's own colour did not previously disagree, because colitur's own bug matched it) -- fixed in colitur, so this row records a NEW divergence from lectio's still-uncorrected violet, not a pre-existing one.") + (expected_rows 46)) diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 52a9adb..b39da79 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -314,13 +314,15 @@ let privileged_feria d = (n >= -6 && n <= -1) || (n >= 1 && n <= 6) || (n >= 50 && n <= 55) (* RG 117 enumerates the five colours (white, red, green, violet, black); - RG 127 assigns green and RG 128 violet to the seasons de Tempore below. - White's own specific paragraph (the "B) De colore albo" section, between - 117 and 123) was not pinned by the primary-source search available here -- - left uncited rather than guessed; see register §3 "Colours". *) + RG 127 assigns green and RG 128 violet to the seasons de Tempore below. RG + 119 (register §3b, primary-source-verified 2026-08-11 -- this comment was + stale until Task 16 noticed the correction had not been copied down here): + white "a festo Nativitatis Domini usque ad expletum tempus Epiphaniae" + and "a Missa Vigiliae paschalis usque ad Missam vigiliae Pentecostis + exclusive" -- exactly Christmastide and Paschaltide below. *) let season_colour = function | Advent | Septuagesima | Lent | Passiontide -> Colour.Violet (* RG 128 *) - | Christmastide | Paschaltide -> Colour.White + | Christmastide | Paschaltide -> Colour.White (* RG 119 *) | Time_after_epiphany | Time_after_pentecost -> Colour.Green (* RG 127 *) (* Gaudete (Advent III) and Laetare (Lent IV) are rose: RG 131, "may be used... @@ -411,6 +413,27 @@ let temporal d = let colour = (* The Pentecost octave weekdays are red, not Paschaltide's white. *) if days_between easter d >= 50 && days_between easter d <= 55 then Colour.Red + (* RG 128(b) (docs/research/rules-register.md §3b), primary + text: "...a dominica in Septuagesima usque ad Vigiliam + paschalem, EXCEPTIS: ... MISSA SIVE CHRISMATIS SIVE IN + CENA DOMINI FERIA V HEBDOMADAE SANCTAE; ..." -- violet + runs Septuagesima to the Easter Vigil EXCEPT (among + others) "the Mass, whether of the Chrism or in Cena + Domini [Holy Thursday], on Thursday of Holy Week" -- + named as a WHOLE-MASS exception (unlike Palm Sunday's + "blessing and procession of palms", which the SAME + sentence carves out as only PART of that day, register + §3b's own RG126 note on the not-yet-modelled per-action + nuance), so this is a clean whole-day colour fact, not + a per-action one the day/colour model cannot express. + Task 16, found via the missalemeum oracle comparison: + every other Triduum day's oracle colour SET includes + violet as one option (Good Friday "bv", Holy Saturday + "vw" -- RG 132's black is a separate, ALREADY-flagged + gap, register §3b, not touched here), but Holy + Thursday's is white ALONE -- confirming this specific + day, and only this one, needs the exception coded. *) + else if days_between easter d = -3 then Colour.White else season_colour s in let week_n = week d in diff --git a/test/test_differential.ml b/test/test_differential.ml index dd601a1..a9bdb91 100644 --- a/test/test_differential.ml +++ b/test/test_differential.ml @@ -76,13 +76,14 @@ family would still be caught by everything except the digit itself. - Layer C (data/ef/expected-divergences.sexp, matched by - [layer_c_reason] below): the CITED allow-list. Ten genuine liturgical - disagreements, each citing its RG paragraph and stating which engine is - right (always colitur, verified against the Missal/register, never - against lectio's own behaviour -- "lectio does it differently" is not - itself a justification anywhere in this file). This is the ONLY layer - that may cover a difference in rank, colour, or which celebration is - observed; A and B never do (enforced structurally below: A/B only ever + [layer_c_reason] below): the CITED allow-list. Eleven genuine liturgical + disagreements (C11 added by Task 16's oracle work, below), each citing + its RG paragraph and stating which engine is right (always colitur, + verified against the Missal/register, never against lectio's own + behaviour -- "lectio does it differently" is not itself a justification + anywhere in this file). This is the ONLY layer that may cover a + difference in rank, colour, or which celebration is observed; A and B + never do (enforced structurally below: A/B only ever touch the season/slug fields, and Layer C's predicates each require an exact, narrow field-diff SET, not "anything goes"). @@ -448,6 +449,7 @@ let layer_c_reason (l : row) (c : row) diffs = (String.equal l.date "2011-07-02" || String.equal l.date "2011-07-04") && subset diffs [ Slug_f; Rank; Colour_f ] then Some "C10" + else if String.equal c.slug "ef-passiontide-2-thursday" && diffs = [ Colour_f ] then Some "C11" else None (* ---------------------------------------------------------------------- *) -- cgit v1.3 From 7ac8a25700c9d930eaaa4f9895665248dac32c52 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 10:26:11 +0200 Subject: docs(citations): migrate register line-number pointers to stable anchors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 68 "register line N" pointers in tracked code and data cited the register's line numbers directly. The register has moved since: line 334 (cited six times as RG 91 entry 8) is now rose/colour prose; lines 375-376 (cited as "RG 109 leaves Advent/Lent Ember ordinary") are now RG 26, the paragraph that refutes that exact reading; line 374 (cited as RG 109(a)) is now RG 25's Latin; line 378 (RG 111) is blank. expected-divergences.sexp even wrote "RG 91 line 332" three times, as though the line offset were part of the rubric's own identity. Since docs/research/rules-register.md is gitignored, no reader of the public repo could resolve any of these, and -- since the register has moved on its own copy too -- neither can the author's. Replaced every pointer with an anchor that survives editing: the RG paragraph/entry number itself (already present in nearly every case, since the citation text already names "RG 91 entry N" or "RG 109(a)" next to the stale line number -- the line number was redundant, not load-bearing) plus a "§4" or quoted-subsection-heading pointer into the register for readers who want to locate the fuller discussion. Also corrected one genuinely stale content error found while migrating its anchor: test_precedence_ef.ml's RG33 disposition-cases comment still said "a I/II-class vigil impeded by any Sunday" -- the pre-Task-16 mistranscription the register itself corrects to II/III class; fixed the prose alongside its citation, since leaving a wrong RG-class claim next to a freshly-verified anchor would be worse than the stale line number it replaced. Covers lib/rites/rite_ef/precedence_ef.ml (18), precedence_ef.mli (already fixed with item 1), test/test_precedence_ef.ml (44, including two instances that only word-wrapped "register\nline N" across a line break and so did not match a same-line grep), and data/ef/expected-divergences.sexp (5, including three "RG 91 line 332" instances). All 68 original pointers resolved -- none needed guessing; every citation's target rule was already named in the surrounding prose. No behaviour change: every edit is a comment/citation/data-note change. Verified byte-identical `colitur day` output across 1583, 1900, 1902, 2008, 2011, 2026, 2038, 9999. 259/259 tests green. --- data/ef/expected-divergences.sexp | 10 ++-- lib/rites/rite_ef/precedence_ef.ml | 37 +++++++-------- test/test_precedence_ef.ml | 97 +++++++++++++++++++------------------- 3 files changed, 72 insertions(+), 72 deletions(-) (limited to 'data/ef/expected-divergences.sexp') diff --git a/data/ef/expected-divergences.sexp b/data/ef/expected-divergences.sexp index 6b7bbdd..c8d863d 100644 --- a/data/ef/expected-divergences.sexp +++ b/data/ef/expected-divergences.sexp @@ -20,15 +20,15 @@ ((id C1) (citation "RG 72-73 (Nativity/Epiphany season boundary, Jan 1-13) + RG 119a (white through \"expletum tempus Epiphaniae\"); register §3c item 1") (verdict colitur) - (note "6-13 January is Christmastide in colitur, matching RG 72-73's explicit boundary at 13 January (corroborated independently by RG 119a's colour rule, register lines 241-246). lectio's efSeason switches to time-after-epiphany on 6 January. The season disagreement cascades into colour (green vs white) and, on 7-8 January specifically, into the ferial slug family name (colitur's ef-christmas-2- vs lectio's ef-time-after-epiphany-1- for the same two days).") + (note "6-13 January is Christmastide in colitur, matching RG 72-73's explicit boundary at 13 January (corroborated independently by RG 119a's colour rule, §3b). lectio's efSeason switches to time-after-epiphany on 6 January. The season disagreement cascades into colour (green vs white) and, on 7-8 January specifically, into the ferial slug family name (colitur's ef-christmas-2- vs lectio's ef-time-after-epiphany-1- for the same two days).") (expected_rows 368)) ((id C2) - (citation "RG 91 line 332 (\"Sundays of Advent, Lent, Passiontide, and Low Sunday\" -- I class, unqualified); extends register §3c item 4 (stated there for Advent only) to Lent on the same textual basis") + (citation "RG 91 entry 6, §4 (\"Sundays of Advent, Lent, Passiontide, and Low Sunday\" -- I class, unqualified); extends register §3c item 4 (stated there for Advent only) to Lent on the same textual basis") (verdict colitur) - (note "Advent II & IV and Lent I-III are I class in colitur, matching RG 91's own unqualified line 332 (already confirmed for Passion Sunday, Palm Sunday, Low Sunday and Advent I, where lectio agrees). lectio's temporal_ef.go generic Sunday branch assigns Class2 and special-cases only Advent I, Passion/Palm/Low Sunday -- a lectio gap for the remaining five Sundays this entry covers.") + (note "Advent II & IV and Lent I-III are I class in colitur, matching RG 91's own unqualified entry 6 (already confirmed for Passion Sunday, Palm Sunday, Low Sunday and Advent I, where lectio agrees). lectio's temporal_ef.go generic Sunday branch assigns Class2 and special-cases only Advent I, Passion/Palm/Low Sunday -- a lectio gap for the remaining five Sundays this entry covers.") (expected_rows 215)) ((id C3) - (citation "RG 91 line 332 (same I-class Sunday rule as C2) + RG 131 (rose indult, Gaudete/Laetare, \"in Officio et Missa diei dominici tantum\")") + (citation "RG 91 entry 6, §4 (same I-class Sunday rule as C2) + RG 131 (rose indult, Gaudete/Laetare, \"in Officio et Missa diei dominici tantum\")") (verdict colitur) (note "Gaudete (Advent III) and Laetare (Lent IV): I class per C2's citation, plus the RG 131 rose indult. lectio has no Rose case in its EF colour function and zero rose rows in tridentine-calendar.ini -- Rose exists only on lectio's OF path -- so it prints violet for both Sundays.") (expected_rows 89)) @@ -58,7 +58,7 @@ (note "Rogation Monday and Tuesday exist as colitur's own ef-rogation-{monday,tuesday} slugs (violet) whenever no higher-ranked saint intervenes. lectio computes no Rogation days at all and shows the plain paschaltide-week-6 feria instead.") (expected_rows 31)) ((id C9) - (citation "RG 95 (translation is a right of I-class FEASTS only) + RG 91 line 332 (Sundays are a separate table row, not feasts) + RG 109(a) (\"of a Sunday\" is a privileged commemoration, presupposing the Sunday stays put) + RG 96 (the general translation walk) -- the same reasoning already fixed for the analogous impeded-Sunday defect in Precedence_ef (Task 9)") + (citation "RG 95 (translation is a right of I-class FEASTS only) + RG 91 entry 6, §4 (Sundays are a separate table row, not feasts) + RG 109(a) (\"of a Sunday\" is a privileged commemoration, presupposing the Sunday stays put) + RG 96 (the general translation walk) -- the same reasoning already fixed for the analogous impeded-Sunday defect in Precedence_ef (Task 9)") (verdict colitur) (note "St Joseph (19 March, I class) colliding with a Lent Sunday (I class per C2): colitur keeps the Sunday observed and transfers Joseph to the next free day (usually 20 March; in years the walk is congested enough to cross Easter, as far as 1 April -- 2008, 2035, 2046, independently pinned by this project's own transfer tests). lectio instead puts Joseph ON the Sunday, displacing the Lenten Sunday office RG 95 says cannot be displaced by a translated feast.") (expected_rows 15)) diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 3c417bb..9997bb2 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -152,7 +152,7 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc (* 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 - (* 8: All Souls -- register line 334's own text carries a qualifier this + (* 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 @@ -182,9 +182,9 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc 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 -- register line 341, deliberately - UNQUALIFIED (contrast entry 16 at line 342, which explicitly says "not - of the Lord"; RG 37c, register line 393, speaks of "II-class feasts of + (* 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, @@ -232,7 +232,7 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc Office (RG 88, see temporal_ef.ml's [temporal]), so those never carry this entry unless they happen to fall on the Saturday itself. Excludes vigils for the same reason 11-13/14/16/19/20/23/24 do: RG 91 has no - IV-class vigil at all (its own vigil list, register lines 381-384, + 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 (* 28: IV-class ferias -- the unqualified catch-all (temporal_ef.ml's own @@ -339,7 +339,7 @@ let impedes_vigil (winner : Vocab_ef.rank Precedence.candidate) = let nativity_octave_prefix = "ef-nativity-octave-day-" (* RG 109's own three named seasons for (e), "of ferias of Advent, Lent and - Passiontide" (register line 376) -- temporal_ef.ml's generic + Passiontide" (§4, "Commemorations") -- temporal_ef.ml's generic -- ferial fallback slugs, whose season word is [season_slug_word]'s output for exactly these three (vocab_ef.ml: Advent and Passiontide are unmodified [season_to_string]; Lent likewise). Also @@ -349,7 +349,7 @@ let nativity_octave_prefix = "ef-nativity-octave-day-" [universal_layer] -- private: nothing outside [privilege_of] needs it. *) let alp_feria_prefixes = [ "ef-advent-"; "ef-lent-"; "ef-passiontide-" ] -(* RG 109 (register lines 374-377, docs/research/rules-register.md §4): the +(* RG 109 (docs/research/rules-register.md §4, "Commemorations"): the closed list of privileged commemorations, checked in the register's own lettered order. A candidate matching none of (a)-(f) is ordinary, per the register's own closing sentence, "All others are ordinary." Read entirely @@ -374,10 +374,10 @@ let privilege_of (c : Vocab_ef.rank Precedence.candidate) : Precedence.privilege let slug = Slug.to_string cel.Celebration.slug in let is_temporal = c.Precedence.origin = Precedence.Temporal in let open Vocab_ef in - (* (a) register line 374: "of a Sunday" -- the same slug marker RG 33's + (* (a) RG 109(a) (§4): "of a Sunday" -- the same slug marker RG 33's [impedes_vigil] already reads to answer "is this candidate a Sunday". *) if is_sunday_slug slug then Precedence.Privileged - (* (b) register line 374-375: "of a I-class day" -- the candidate's own + (* (b) RG 109(b) (§4): "of a I-class day" -- the candidate's own rank. In this codebase's current disposition rules the ONLY way a [Class1] candidate ever reaches [Commemorate] at all is via [Celebration.status = Commemoration_only] (a plain [Feast]-status @@ -385,17 +385,17 @@ let privilege_of (c : Vocab_ef.rank Precedence.candidate) : Precedence.privilege branch is real but its only reachable witness today is that shape; see the task report. *) else if rank = Class1 then Precedence.Privileged - (* (c) register line 375: "of days within the Octave of the Nativity". *) + (* (c) RG 109(c) (§4): "of days within the Octave of the Nativity". *) else if is_temporal && String.starts_with ~prefix:nativity_octave_prefix slug then Precedence.Privileged - (* (d) register line 375-376: "of September Ember days" -- named on its + (* (d) RG 109(d) (§4): "of September Ember days" -- named on its own because September falls entirely outside (e)'s three seasons (Advent/Lent/Passiontide) under ANY reading, not because it needs excluding FROM (e) the way review round 1's F1/F2 finding corrected the Advent/Lent Ember sets below to no longer need. *) else if is_temporal && String.starts_with ~prefix:september_ember_prefix slug then Precedence.Privileged - (* (e) register line 376: "of ferias of Advent, Lent and Passiontide" -- + (* (e) RG 109(e) (§4): "of ferias of Advent, Lent and Passiontide" -- CORRECTED, fix round 1 (F1/F2): this branch previously excluded the Advent and Lent Ember sets via [not (is_ember_18 slug)], reading RG 109(e)'s bare "feriis Adventus, Quadragesimae" as tacitly narrower than @@ -428,7 +428,7 @@ let privilege_of (c : Vocab_ef.rank Precedence.candidate) : Precedence.privilege it under its own name. *) else if is_temporal && List.exists (fun p -> String.starts_with ~prefix:p slug) alp_feria_prefixes then Precedence.Privileged - (* (f) register line 376-377: "of the Major Rogations, in Mass" -- the + (* (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 @@ -478,13 +478,12 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) cel.Celebration.rank = Class1 && not (is_sunday_slug (Slug.to_string cel.Celebration.slug)) then - (* RG 95 (register lines 323, 363): only I-class FEASTS have the right + (* RG 95 (§4, "Occurrence" and "Transfer/translation"): only I-class FEASTS have the right of translation -- RG 91's own table lists Sundays as a separate row - (entry 6, register line 332) from feasts (entries 11-13, register - lines 337-339), so a Sunday is never a "feast" in RG 95's sense, and + (entry 6) from feasts (entries 11-13), so a Sunday is never a "feast" in RG 95's sense, and [is_sunday_slug] (the same marker RG 33's [impedes_vigil] and RG 109(a)'s [privilege_of] already use) excludes it here. This is the - branch that completes Task 7's All Souls fix (register line 334, RG + branch that completes Task 7's All Souls fix (RG 91 entry 8): All Souls is I class, not a vigil, and not a Sunday slug, so once it loses to an occurring Sunday it still reaches here and transfers -- to 3 November, now DIRECTLY authorised by RG 96 @@ -613,7 +612,7 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) the temporal Class4 [Omit] branch above because a Sunday is never IV class -- RG 11-12/91 entry 6/15 make every Sunday I or II class, never a "feria" at all in Caput IV's own sense, RG 21): RG 109(a) - (register line 374) lists "of a Sunday" as a privileged commemoration + (§4) lists "of a Sunday" as a privileged commemoration category, which presupposes an impeded Sunday stays put rather than moving to another day the way a feast does -- [privilege_of] tags it [Privileged] via the same [is_sunday_slug] marker, with no further @@ -632,7 +631,7 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) Precedence.Commemorate (privilege_of loser) (* Task 9: how many of the day's commemorations RG 111 admits, and which - (docs/research/rules-register.md §4, register line 378, "Commemorations" + (docs/research/rules-register.md §4, "Commemorations", RG 111). [band] decides who wins the day; [disposition] decides who is even eligible to be commemorated, and tags each with its RG 109 privilege via [privilege_of]; this decides how many of THOSE survive. diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml index efbdb04..a0e1db1 100644 --- a/test/test_precedence_ef.ml +++ b/test/test_precedence_ef.ml @@ -49,45 +49,45 @@ let off n = D.add_days easter n (* (description, date, candidate, expected RG 91 entry). *) let cases = - [ (* Entry 1 -- register line 327: Nativity, Easter Sunday, Pentecost Sunday. *) + [ (* Entry 1 -- RG 91 entry 1 (§4): Nativity, Easter Sunday, Pentecost Sunday. *) ("1 Nativity", mk 2026 12 25, cand "ef-nativity", 1); ("1 Easter Sunday", off 0, cand "ef-easter-sunday", 1); ("1 Pentecost Sunday", off 49, cand "ef-pentecost", 1); - (* Entry 2 -- register line 328: Sacred Triduum. Thu-Sat of Holy Week, + (* Entry 2 -- RG 91 entry 2 (§4): Sacred Triduum. Thu-Sat of Holy Week, NOT entry 7 (which stops at Wednesday -- see entry 7 below). *) ("2 Holy Thursday", off (-3), cand "ef-holy-thursday", 2); ("2 Good Friday", off (-2), cand "ef-good-friday", 2); ("2 Holy Saturday", off (-1), cand "ef-holy-saturday", 2); - (* Entry 3 -- register line 329. *) + (* Entry 3 -- RG 91 entry 3 (§4). *) ("3 Epiphany", mk 2026 1 6, cand "ef-epiphany", 3); ("3 Ascension", off 39, cand "ef-ascension", 3); ("3 Trinity", off 56, cand "ef-trinity", 3); ("3 Corpus Christi", off 60, cand "ef-corpus-christi", 3); ("3 Sacred Heart", off 68, cand "ef-sacred-heart", 3); ("3 Christ the King", T.christ_the_king 2026, cand "ef-christ-the-king", 3); - (* Entry 4 -- register line 330. Sanctoral-origin: neither feast is part + (* Entry 4 -- RG 91 entry 4 (§4). Sanctoral-origin: neither feast is part of temporal_ef's movable cycle. *) ( "4 Immaculate Conception", mk 2026 12 8, cand ~origin:P.Sanctoral ~subject:Sub.Bvm ~layer:PE.universal_layer "ef-immaculate-conception", 4 ); ("4 Assumption", mk 2026 8 15, cand ~origin:P.Sanctoral ~subject:Sub.Bvm ~layer:PE.universal_layer "ef-assumption", 4); - (* Entry 5 -- register line 331. *) + (* Entry 5 -- RG 91 entry 5 (§4). *) ("5 Nativity Vigil", mk 2026 12 24, cand "ef-nativity-vigil", 5); ("5 Octave day (Circumcision)", mk 2026 1 1, cand "ef-circumcision", 5); - (* Entry 6 -- register line 332. *) + (* Entry 6 -- RG 91 entry 6 (§4). *) ("6 Advent Sunday", T.advent_start 2026, cand "ef-advent-sunday-1", 6); ("6 Lent Sunday", off (-42), cand "ef-lent-sunday-1", 6); ("6 Passion Sunday (I Passiontide)", off (-14), cand "ef-passion-sunday", 6); ("6 Palm Sunday (II Passiontide)", off (-7), cand "ef-palm-sunday", 6); ("6 Low Sunday", off 7, cand "ef-low-sunday", 6); - (* Entry 7 -- register line 333: Ash Wednesday and Mon/Tue/Wed of Holy + (* Entry 7 -- RG 91 entry 7 (§4): Ash Wednesday and Mon/Tue/Wed of Holy Week ONLY -- Thu-Sat are entry 2 above, not this entry. *) ("7 Ash Wednesday", off (-46), cand "ef-ash-wednesday", 7); ("7 Monday of Holy Week", off (-6), cand "ef-holy-monday", 7); ("7 Tuesday of Holy Week", off (-5), cand "ef-holy-tuesday", 7); ("7 Wednesday of Holy Week", off (-4), cand "ef-holy-wednesday", 7); - (* Entry 8 -- register line 334. 2 Nov 2026 is a Monday (verified + (* Entry 8 -- RG 91 entry 8 (§4). 2 Nov 2026 is a Monday (verified independently below the table), so this row is the plain case. The register's own qualifying case -- "yields to an occurring Sunday" -- gets its own row and its own end-to-end test after this table (2 Nov @@ -101,31 +101,31 @@ let cases = [test_all_souls_yields_to_sunday] below; this row pins the specific integer [band] returns. *) ("8 All Souls (yields to a Sunday, 2 Nov 2025)", mk 2025 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 16); - (* Entry 9 -- register line 335. *) + (* Entry 9 -- RG 91 entry 9 (§4). *) ("9 Pentecost Vigil", off 48, cand "ef-pentecost-vigil", 9); - (* Entry 10 -- register line 336: both range boundaries, to guard the + (* Entry 10 -- RG 91 entry 10 (§4): both range boundaries, to guard the off-by-one an inclusive Easter-offset window invites. *) ("10 Easter octave, day+1", off 1, cand "ef-easter-1-mon", 10); ("10 Easter octave, day+6", off 6, cand "ef-easter-1-sat", 10); ("10 Pentecost octave, day+50", off 50, cand "ef-pentecost-1-mon", 10); ("10 Pentecost octave, day+55", off 55, cand "ef-pentecost-1-sat", 10); - (* Entry 11 -- register line 337. *) + (* Entry 11 -- RG 91 entry 11 (§4). *) ( "11 Universal I-class feast", mk 2026 6 29, cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:PE.universal_layer "ef-ss-peter-paul", 11 ); - (* Entry 12 -- register line 338. The one non-base-layer case the brief + (* Entry 12 -- RG 91 entry 12 (§4). The one non-base-layer case the brief asks for explicitly: same date/rank/subject as 11, only the layer differs, so this row isolates the layer test as the deciding factor. *) ( "12 Proper I-class feast (non-base layer)", mk 2026 6 29, cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:"diocese-warsaw" "ef-local-patron", 12 ); - (* Entry 13 -- register line 339. *) + (* Entry 13 -- RG 91 entry 13 (§4). *) ( "13 Indult I-class feast", mk 2026 6 29, cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:(PE.indult_prefix ^ "local-grant") "ef-indult-feast-1", 13 ); - (* Entry 14 -- register line 341, deliberately UNQUALIFIED (contrast - entry 16, line 342, which explicitly says "not of the Lord"). *) + (* Entry 14 -- RG 91 entry 14 (§4), deliberately UNQUALIFIED (contrast + entry 16, which explicitly says "not of the Lord"). *) ( "14 Feast of the Lord, II class", mk 2026 7 1, cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer "ef-precious-blood", @@ -138,21 +138,21 @@ let cases = cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:"diocese-warsaw" "ef-local-feast-of-the-lord", 14 ); - (* Entry 15 -- register line 342: an ordinary Sunday not named at entry 6 + (* Entry 15 -- RG 91 entry 15 (§4): an ordinary Sunday not named at entry 6 -- Septuagesima is II class (RG 11-12 names only Advent/Lent/ Passiontide/Easter/Low/Pentecost as I class). *) ("15 II-class Sunday (Septuagesima)", off (-63), cand ~rank:V.Class2 "ef-septuagesima-sunday", 15); - (* Entry 16 -- register line 342. *) + (* Entry 16 -- RG 91 entry 16 (§4). *) ( "16 Universal II-class feast, not of the Lord", mk 2026 1 20, cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint ~layer:PE.universal_layer "ef-some-saint", 16 ); - (* Entry 17 -- register line 343: days WITHIN the Nativity octave (26-28 + (* Entry 17 -- RG 91 entry 17 (§4): days WITHIN the Nativity octave (26-28 Dec are Stephen/John/Innocents -- sanctoral, not this entry; 1 Jan is entry 5's Octave DAY, not this entry either). *) ("17 Nativity octave, 29 Dec", mk 2026 12 29, cand ~rank:V.Class2 "ef-nativity-octave-day-5", 17); ("17 Nativity octave, 31 Dec", mk 2026 12 31, cand ~rank:V.Class2 "ef-nativity-octave-day-7", 17); - (* Entry 18 -- register line 343-344: Advent 17-23 Dec ferias AND the + (* Entry 18 -- RG 91 entry 18 (§4): Advent 17-23 Dec ferias AND the Ember days of Advent/Lent/September share this one entry. The second row is deliberately a Lent date (season Lent, NOT Advent) to prove the Ember-slug path fires on its own, not merely because it also happens @@ -164,17 +164,17 @@ let cases = rather than a hand-typed "ef-lent-ember-wed" -- closes review finding 3's coupling concern for the Ember prefixes specifically. *) ("18 Lent Ember Wednesday (from Temporal_ef.temporal)", off (-39), of_temporal (off (-39)), 18); - (* Entry 19 -- register line 344. *) + (* Entry 19 -- RG 91 entry 19 (§4). *) ( "19 Proper II-class feast", mk 2026 1 20, cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint ~layer:"diocese-warsaw" "ef-local-saint-2", 19 ); - (* Entry 20 -- register line 345. *) + (* Entry 20 -- RG 91 entry 20 (§4). *) ( "20 Indult II-class feast", mk 2026 1 20, cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint ~layer:(PE.indult_prefix ^ "local-grant-2") "ef-indult-feast-2", 20 ); - (* Entry 21 -- register line 345 (RG 28-34). Two rows: the Ascension + (* Entry 21 -- RG 91 entry 21 (§4, RG 28-34). Two rows: the Ascension Vigil is the one II-class vigil temporal_ef already produces today (temporal-origin); the Assumption Vigil stands in for the sanctoral-origin case no task has loaded data for yet -- proving @@ -194,12 +194,12 @@ let cases = cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer "ef-precious-blood-vigil", 21 ); - (* Entry 22 -- register line 347-348 (corrected: ends at Palm Sunday, not + (* Entry 22 -- RG 91 entry 22 (§4) (corrected: ends at Palm Sunday, not Passion Sunday). Both a Lent and a Passiontide feria, clear of Ash Wednesday, Holy Week and the Ember days. *) ("22 Lent feria", off (-41), cand ~rank:V.Class3 "ef-lent-1-mon", 22); ("22 Passiontide feria", off (-12), cand ~rank:V.Class3 "ef-passiontide-1-tue", 22); - (* Entry 23 -- register line 349. NOTE the table's own order here is the + (* Entry 23 -- RG 91 entry 23 (§4). NOTE the table's own order here is the REVERSE of 11/12 and 14/16/19/20 above: entry 23 (particular calendars) is numbered BELOW entry 24 (universal), so a proper III-class feast outranks a universal one -- transcribed as the @@ -207,13 +207,13 @@ let cases = ( "23 Proper III-class feast (non-base layer)", mk 2026 6 30, cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:"diocese-warsaw" "ef-local-saint-3", 23 ); - (* Entry 24 -- register line 349. *) + (* Entry 24 -- RG 91 entry 24 (§4). *) ( "24 Universal III-class feast", mk 2026 6 30, cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "ef-some-saint-3", 24 ); - (* Entry 25 -- register line 350. *) + (* Entry 25 -- RG 91 entry 25 (§4). *) ("25 Advent feria to 16 Dec", mk 2026 12 1, cand ~rank:V.Class3 "ef-advent-1-tue", 25); - (* Entry 26 -- register line 350. *) + (* Entry 26 -- RG 91 entry 26 (§4). *) ( "26 III-class vigil", mk 2026 8 9, cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "ef-lawrence-vigil", 26 ); @@ -238,12 +238,12 @@ let cases = mk 2026 8 9, cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "vigil-of-st-lawrence", 26 ); - (* Entry 27 -- register line 352: an otherwise-unoccupied IV-class + (* Entry 27 -- RG 91 entry 27 (§4): an otherwise-unoccupied IV-class Saturday. *) ( "27 Office of the BVM on Saturday", off 62, cand ~rank:V.Class4 "ef-time-after-pentecost-1-sat", 27 ); - (* Entry 28 -- register line 352: the unqualified IV-class catch-all. *) + (* Entry 28 -- RG 91 entry 28 (§4): the unqualified IV-class catch-all. *) ("28 IV-class feria", off 65, cand ~rank:V.Class4 "ef-time-after-pentecost-1-tue", 28); (* Not an RG 91 row at all: a I-class candidate marked as a vigil, which is not the Nativity or Pentecost (entries 5/9, the only I-class @@ -264,7 +264,7 @@ let cases = [not is_temporal] guard's role in the entry-25 mutation test recorded in the task report). *) ("unclassified: I-class temporal candidate on an unnamed date", mk 2026 7 15, cand "ef-unnamed-day", PE.unclassified); - (* RG 91's own vigil list (register lines 381-384) stops at III class -- + (* RG 91's own vigil list (§4, "Vigils / octaves / Rogations / Sunday classes") stops at III class -- there is no IV-class vigil for entry 28's ferial catch-all to absorb. *) ( "unclassified: IV-class candidate marked as a vigil", mk 2026 6 20, cand ~rank:V.Class4 "ef-second-mystery-vigil", PE.unclassified ) @@ -298,7 +298,7 @@ let test_all_souls_yields_to_sunday () = (S.to_string resolution.P.observed.P.cel.Cel.slug) (* Task 8: [disposition] -- what happens to the day's LOSING candidate (RG - 92-95, 33, 94; register lines 316-325, 381-384). Table-driven like [band]'s + 92-95, 33, 94; §4). Table-driven like [band]'s own [cases] above, one row per rule, each checked against a description of which register clause it pins. [disposition] takes no context (see precedence.mli's [rules.disposition]), so "is the winner a Sunday" is read @@ -320,7 +320,7 @@ let an_ordinary_sunday = cand ~rank:V.Class2 "ef-time-after-pentecost-sunday-11" let disposition_cases = - [ (* RG 95 -- register line 323-325: only I-class feasts transfer; a + [ (* RG 95 -- §4, "Occurrence": only I-class feasts transfer; a II-class feast loses to a I-class day and is COMMEMORATED, not transferred. Paired with the next row (a I-class loser, same shape of winner) so the discriminating factor is provably the LOSER's own @@ -334,14 +334,14 @@ let disposition_cases = cand "ef-nativity", cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-local-i-class-feast", "Transfer" ); - (* Fix round 1 (post-Task-9 review): RG 95 (register lines 323, 363) + (* Fix round 1 (post-Task-9 review): RG 95 (§4, "Occurrence" and "Transfer/translation") restricts the right of translation to I-class FEASTS -- RG 91's own - table lists Sundays as a separate row (entry 6, register line 332) - from feasts (entries 11-13, lines 337-339) -- so an impeded I-class + table lists Sundays as a separate row (entry 6) + from feasts (entries 11-13) -- so an impeded I-class Sunday must NOT transfer, unlike the plain I-class feast row above: same [Class1] rank, same kind of winner, the ONLY difference is that - this loser's slug carries [PE.sunday_marker]. RG 109(a) (register - line 374) confirms this from the other direction: "of a Sunday" is a + this loser's slug carries [PE.sunday_marker]. RG 109(a) (§4) + confirms this from the other direction: "of a Sunday" is a privileged commemoration category, which presupposes an impeded Sunday stays put rather than moving to another day the way a feast does. Sourced from [Temporal_ef.temporal]'s own real output (Advent I @@ -356,8 +356,9 @@ let disposition_cases = cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-immaculate-conception", of_temporal (T.advent_start 2026), "Commemorate(Privileged)" ); - (* RG 33 -- register line 383-384: a I/II-class vigil impeded by any - Sunday or a I-class feast is entirely OMITTED, not commemorated. The + (* RG 33 -- §4, "Vigils / octaves / Rogations / Sunday classes": a II- or + III-class vigil impeded by any Sunday or a I-class feast is entirely + OMITTED, not commemorated. The vigil is sourced from [Temporal_ef.temporal]'s own real output (as [of_temporal]'s existing callers above do), not a hand-typed "ef-ascension-vigil", so a drift in temporal_ef's vigil-slug @@ -456,7 +457,7 @@ let disposition_cases = RG 95's transfer, not after. Its expected privilege is [Privileged], not [Ordinary]: this loser's [rank] is [Class1] (the default [cand] leaves unless overridden, deliberately kept here for the - branch-order proof above), and RG 109(b) (register line 374-375, "of + branch-order proof above), and RG 109(b) (§4, "of a I-class day") makes any [Class1] commemoration privileged regardless of how it reached [Commemorate] -- Task 8's placeholder [interim_privilege] used to hide this (always [Ordinary]); Task 9's @@ -542,7 +543,7 @@ let disposition_cases = "Commemorate(Ordinary)" ) ] -(* Task 9: [privilege_of]'s RG 109 categories (register lines 374-377), +(* Task 9: [privilege_of]'s RG 109 categories (§4, "Commemorations"), exercised through [PE.disposition]'s [Commemorate] payload -- [privilege_of] itself is private, so this is the only vantage point a test outside precedence_ef.ml has on it. Each row below is built to match ONLY the one @@ -560,14 +561,14 @@ let disposition_cases = correctly NOT conflated with it, which is the strongest claim available without inventing an unfounded slug convention. *) let privilege_cases = - [ (* (a) register line 374: "of a Sunday". [an_ordinary_sunday] is Class2, + [ (* (a) RG 109(a) (§4): "of a Sunday". [an_ordinary_sunday] is Class2, not Class1, not within the Nativity octave, not an Ember day, not a feria of Advent/Lent/Passiontide -- matches (a) alone. *) ( "(a) an ordinary Sunday commemoration is privileged", cand "ef-nativity", an_ordinary_sunday, "Commemorate(Privileged)" ); - (* (c) register line 375: "of days within the Octave of the Nativity" -- + (* (c) RG 109(c) (§4): "of days within the Octave of the Nativity" -- sourced from [Temporal_ef.temporal]'s own output (29 Dec 2026, Class2, "ef-nativity-octave-day-5"), not a hand-typed slug, for the same coupling-safety reason the file's own [of_temporal] rows use it @@ -577,7 +578,7 @@ let privilege_cases = cand "ef-nativity", of_temporal (mk 2026 12 29), "Commemorate(Privileged)" ); - (* (d) register line 375-376: "of September Ember days" -- 23 Sep 2026 is + (* (d) RG 109(d) (§4): "of September Ember days" -- 23 Sep 2026 is the September Ember Wednesday (independently derived from [Temporal_ef]'s own third-Sunday-of-September rule: first Sunday of September 2026 is the 6th, +14 days = 20th, +3 = 23rd), sourced from @@ -593,7 +594,7 @@ let privilege_cases = cand "ef-nativity", of_temporal (mk 2026 9 23), "Commemorate(Privileged)" ); - (* (e) register line 376: "of ferias of Advent, Lent and Passiontide" -- + (* (e) RG 109(e) (§4): "of ferias of Advent, Lent and Passiontide" -- two rows, one per season named, both from [Temporal_ef.temporal]'s own generic ferial fallback, neither a Sunday, Ember day, or within the Nativity octave. *) @@ -666,7 +667,7 @@ let privilege_cases = "Omit" ) ] -(* Task 9: [PE.admit] -- RG 111's admission counts (register line 378), +(* Task 9: [PE.admit] -- RG 111's admission counts (§4, "Commemorations"), given commemorations ALREADY tagged with their real privilege (as [PE.disposition] now tags them -- see [privilege_cases] above). Every candidate/privilege pair here is built directly, not routed through @@ -719,7 +720,7 @@ let slugs_of admitted = List.map (fun (c, _) -> S.to_string c.P.cel.Cel.slug) admitted let admit_cases = - [ (* RG 111 (register line 378): "I class: none save one privileged." *) + [ (* RG 111 (§4): "I class: none save one privileged." *) ( "I-class day, only an ordinary commemoration due -> none admitted", observed_class1, [ (ordinary_hi, P.Ordinary) ], @@ -928,7 +929,7 @@ let test_ii_class_sunday_privileged_witness_admitted_end_to_end () = [ ("ef-some-saint", "omitted: admission limit reached") ] (List.map (fun (c, reason) -> (S.to_string c.P.cel.Cel.slug, reason)) resolution.P.omitted) -(* Completes Task 7's carried fix (register line 334): on a real Sunday +(* Completes Task 7's carried fix (RG 91 entry 8, §4): on a real Sunday landing on 2 November, All Souls does not merely lose (that was Task 7's [band] fix, proved by [test_all_souls_yields_to_sunday] above) -- it must be TRANSFERRED, not commemorated and not omitted. All Souls is I class -- cgit v1.3