From b0ff86ae552b90613be21c52433f4180deba7758 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Sat, 15 Aug 2026 03:40:19 +0200 Subject: test(differential): fix round 1 -- Holy Week, Lent Ember, RG 69 Two Critical findings, both real: Holy Week (including the entire Sacred Triduum) was reading Passion Week's Masses -- the original colitur_keys widened lectio's single, conflated ini section into both weeks instead of Passion week alone. Narrowed to Passion week only; Holy Week's own four cleanest days (Monday, Tuesday, Thursday, Saturday) are hand-authored directly from the Missal, verified twice against both scans (C23); Holy Wednesday and Good Friday are genuinely multi-lesson liturgies with no single Epistle slot and are deliberately left unanswered, not guessed (C24). The Lenten Ember days were still dead keys: Temporal_ef computes ef-lent-ember-{wed,fri,sat}, lectio's ini names the same data ef-lent-1-{wednesday,friday,saturday}, and colitur_keys never translated it -- both engines independently fell through to the same wrong ferial answer, so even the differential stayed green (C22, a genuine lectio bug recorded for upstream). Fixed the instance and the class: colitur_keys gained the rename, and tools/bootstrap_lectionary.ml gained assert_reachable, sweeping Rite_ef.Temporal_ef.temporal at generation time so a translation table entry naming a slug colitur never computes dies loudly instead of shipping silently, plus an informational log of every real slug with no entry at all. C21 ("colitur's step 3 cannot reach a correct answer for the fixed Christmastide slugs") is retired, superseded rather than merely patched: the Nativity-Octave days now have their own direct Missal formulary (Tit. 3,4-7/Luc. 2,15-20, "Diebus infra octavam Nativitatis Domini"), resolved at step 2, not step 3 at all. That surfaced a real RG 69 gap underneath it: a Sunday landing 26-31 December should keep its own Office, not the fixed weekday placeholder, which Temporal_ef does not currently distinguish. C6's verdict is re-opened (open, not colitur) for the 19 of its 138 rows where this applies directly, and a new entry C25 (57 rows) records the same gap propagating one hop further through step 3 into Holy Name week. Both are out of this task's own scope -- a behaviour change to a shared, multi-round-reviewed kernel-adjacent function -- and recorded, not fixed, per the coordinator's own instruction. C6's own note is rewritten in full rather than layered with another correction; C18's stale trailing count is fixed to match its own pin; the Vigil-misapplication risk flagged as a Minor closed itself once the Nativity-Octave fix landed, confirmed by inspection rather than assumed. dune test: 358/358. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359/359. --- data/ef/expected-divergences.sexp | 57 +++++-- data/ef/lectionary.sexp | 62 ++++---- lib/rites/rite_ef/temporal_ef.ml | 23 ++- test/test_differential.ml | 178 +++++++++++++++------- test/test_lectionary.ml | 81 +++++++--- test/test_lectionary_ef.ml | 23 ++- tools/bootstrap_lectionary.ml | 309 ++++++++++++++++++++++++++++++++------ tools/dune | 8 +- 8 files changed, 571 insertions(+), 170 deletions(-) diff --git a/data/ef/expected-divergences.sexp b/data/ef/expected-divergences.sexp index 8c39b43..8b23b9d 100644 --- a/data/ef/expected-divergences.sexp +++ b/data/ef/expected-divergences.sexp @@ -68,14 +68,18 @@ (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). REFRESHED (ef-rebootstrap fixture refresh, 2026-08-12): expected_rows moved 361 -> 368. NOT a new divergence -- the closed C13's own 7 rows (13 January in the 7 years it falls on a Sunday) fold back in here: lectio's fix wave independently fixed its own equivalent of the RG16(a) defect C13 used to allow-list, so on all 7 of those Sunday years lectio's slug now matches colitur's `commemoration-of-the-baptism-of-the-lord` too (not just rank/colour), leaving season as the ONLY residual difference on those rows -- squarely inside C1's own existing shape (diffs subset of [Season;Colour;Slug] with the Slug-diff escape clause unused). REFRESHED AGAIN (ef-rg112-rg110 task, branch ef-rg112-rg110): expected_rows moved 368 -> 361, back to its PRE-ef-rebootstrap value, but for a THIRD, different reason -- SPLIT OUT to its own new entry, C15, not silently re-absorbed: colitur now BUILDS Holy Family (RG 17(b)) and, on these same 7 dates, correctly observes it in the fixed Baptism's place (RG 91 entry 14, RG 112(a)), so colitur's own slug there changes from `commemoration-of-the-baptism-of-the-lord` (which happened to already match lectio's own slug) to `ef-time-after-epiphany-sunday-1` (Holy Family's own, lectio has no equivalent) -- a genuine SLUG divergence now, no longer inside C1's own [Season]-only shape. See C15's own entry for the full account. PROVENANCE, fix round 1 (coordinator finding 6, restoring the discipline this entry's own prior refresh recorded and this round's rewrite had dropped): 361 read directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations's `Received` array, run against the widened C15 predicate/allow-list together, not before it existed) -- not hand-counted first and cross-checked after, and not 368 minus 7 taken on faith: the arithmetic matches C15's own independently-derived 7, which is corroboration, not the derivation itself.") (expected_rows 361)) ((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. REFRESHED (ef-rebootstrap fixture refresh, 2026-08-12): expected_rows is UNCHANGED at 138, but the SHAPE narrowed -- lectio's fix wave independently promoted 26-31 December to II class too (its own \"beyond the seven\" fix, RG 67-68), so the RANK component of this divergence is gone; every one of the 138 rows now differs ONLY in slug (lectio keeps its generic ef-christmas-0- feria name; colitur's date-keyed ef-nativity-octave-day-N names are not adopted, since these are FIXED calendar dates within the octave, not a weekday-numbered family Layer A's own literal-pair table could safely absorb without re-entangling the two engines' differing office-identity conventions for this window -- see test_differential.ml's own norm_slug comment for why the analogous January case is handled there and this one deliberately is not). Re-derived the same way as C1 above (Python cross-check against the refreshed fixture and colitur's `day` CLI output, cross-checked against the OCaml comparator's own failure output): 138, exactly the prior count -- a coincidence of this being a pure renaming (identity/count both unchanged), not evidence the recount was skipped. + (citation "RG 91 entry 17 (\"days within the Octave of the Nativity\") for the slug/rank divergence; RG 69, scan1.txt:625-631 word for word, for the QUESTIONED citation component (Important 3(b), coordinator review, task 8 fix round 1): \"De dominica infra octavam Nativitatis Domini, quae scilicet a die 26 ad 31 decembris occurrit, semper fit Officium cum commemoratione festi forte occurrentis, iuxta rubricas, nisi dominica incidat in festum I classis: quo in casu, fit de festo cum commemoratione dominicae\" -- the Sunday's OWN Office (not the fixed weekday placeholder) is kept, WITH a commemoration of whatever feast falls that day, whenever a Sunday lands anywhere 26-31 December (unless that feast is itself I class, in which case the feast wins with the Sunday commemorated instead)") + (verdict open) + (note "REWRITTEN IN FULL, task 8 fix round 1 (coordinator review, Important 4: a prior version of this note left three successive NARROWED/CORRECTED paragraphs standing on top of each other in a file whose whole purpose is a top-down audit trail -- this replaces all of them with one current account, history preserved in git, not layered here again. + +29-31 December (RG 91 entry 17, II class) are colitur's own fixed `ef-nativity-octave-day-{5,6,7}` slugs regardless of weekday -- including on the years one of them IS itself a Sunday, which is the root of everything below. lectio keeps its own generic `ef-christmas-0-` (or, on a Sunday, `ef-christmas-sunday-0`) naming instead: a genuine, historically stable SLUG divergence (this part of the entry is unchanged since the fixture refresh that first measured 138). -NARROWED, task 8 (branch ef-lectionary, citation comparison): expected_rows moved 138 -> 31, and this is a GENUINE NARROWING, not a re-absorption -- the missing 107 are DELIBERATELY LEFT UNEXPLAINED (failing test_no_unexplained_differences), not folded into any entry here. Root cause, traced precisely: `ef-nativity-octave-day-{5,6,7}` are colitur-only FIXED slugs with no lectionary entry of their own; when the day is a WEEKDAY (not itself a Sunday), Lectionary_ef.readings' step 3 walks back to the ONE immediately preceding civil Sunday and reads THAT Sunday's own temporal identity -- which, for a run of consecutive December/January dates, is itself frequently ANOTHER entry-less fixed slug (another Nativity-Octave day, or `ef-nativity-vigil` on the one occasion 24 December itself falls on a Sunday) rather than a slug this generator was able to give a citation. lectio's own resolution reaches further (empirically: it sometimes finds Christmas Day's own Mass, sometimes Advent IV's, sometimes the Sunday-within-the-Octave's own distinct Mass -- Missal-verified, docs/research/scan1.txt, \"Dominica infra octavam Nativitatis Domini, II classis\": Epistle Gal. 4,1-7, Gospel Luc. 2,33-40) via a mechanism colitur's own single-hop step 3 does not implement. A genuine, precisely-diagnosed colitur LOGIC gap (Lectionary_ef.readings, not a data/bootstrap issue) -- fixing it properly (an iterative walk-back, or a per-year-verified direct entry for every fixed Christmastide slug, the same rigor task 6 applied to the 15 readingless saints) is out of this task's own safe scope; recommended as a follow-up task in task-8-report.md. Of the 138 prior rows, 88 still match this entry's own [Slug_f]-only shape (the day's OWN citation, via whatever it resolves to, happens to equal lectio's); the other 50 now also carry [First_f]/[Gospel_f] and fall outside this entry's [diffs = [ Slug_f ]] exact-equality guard, by design -- narrowing this guard to `subset` would have silently re-absorbed exactly the failure this task was asked to surface. +CITATION component, new this task: Important 3(a) (tools/bootstrap_lectionary.ml's own `nativity_octave_entries`) gave these three slugs a real, twice-Missal-verified citation for the first time (\"Diebus infra octavam Nativitatis Domini, II classis\": Tit. 3,4-7 / Luc. 2,15-20, docs/research/scan1.txt:6281-6329 and scan2.txt:6900-6960, each date's own rubric -- \"Missa Puer natus est nobis, ut supra [28]\" -- pointing straight at it). It does NOT converge with lectio's own answer, on ANY of the 138 rows, because lectio's own resolution never reaches this direct formulary either -- empirically, it always falls back to whichever true civil Sunday precedes the date (Christmas Day's own Mass, Advent IV's, or the genuinely different \"Dominica infra octavam\" Mass, Gal. 4,1-7/Luc. 2,33-40, depending on the year's own alignment) -- so all 138 rows, not merely a subset, now carry a citation diff alongside the pre-existing slug one. -CORRECTED, same task, fix round: an earlier pass of this note measured 31/107 (not 88/50) against a version of tools/bootstrap_lectionary.ml's own `colitur_keys` that REPLACED (rather than widened) the \"ef-christmas-sunday-0\" translation -- a regression caught by test_lectionary_ef.ml's own PRE-EXISTING `test_step3_christmas_feria_resumes_sunday` going from pass to fail (that test predates this task and pins the historical \"Sunday within the Octave of the Nativity\", 26-28 December, a DIFFERENT civil window from Holy Name Sunday that also, Missal-confirmed, shares the identical Mass -- docs/research/scan1.txt, \"Dominica infra octavam Nativitatis Domini\": Gal. 4,1-7 / Luc. 2,33-40). Fixed by widening `colitur_keys \"ef-christmas-sunday-0\"` to keep the original key ALONGSIDE the Holy-Name-Sunday alias, the same \"widen, never narrow\" rule Passiontide already used -- once fixed, every later feria whose one-hop preceding-Sunday walk lands on THAT Sunday now also resolves correctly, moving 57 more rows out of [C21] and into this entry's own [Slug_f]-only shape. Re-derived directly from the OCaml comparator's own failure output after the fix, not hand-counted first and cross-checked after: 88.") - (expected_rows 88)) +VERDICT, split by the SAME 138-row population, not two entries: on the 119 rows where 29-31 December is an ordinary WEEKDAY that year, colitur's `Tit. 3,4-7` citation is CONFIRMED CORRECT against the Missal (verdict colitur would be accurate for this subset alone). On the 19 rows where 29-31 December IS ITSELF a Sunday that year (2006-12-31, 2007-12-30, 2012-12-30, 2013-12-29, 2017-12-31, 2018-12-30, 2019-12-29, 2023-12-31, 2024-12-29, 2028-12-31, 2029-12-30, 2030-12-29, 2034-12-31, 2035-12-30, 2040-12-30, 2041-12-29, 2045-12-31, 2046-12-30, 2047-12-29 -- independently re-derived against `date -d +%u` for every one of the 138 dates, not estimated), RG 69's own unconditional rule means the SUNDAY'S distinct Office should be observed instead -- colitur's `Temporal_ef` does not distinguish the two cases at the slug level at all, so it emits the weekday formulary there too, which is WRONG by this reading. This is a genuine [Temporal_ef] defect (not a lectionary/bootstrap issue, and not something this generator can fix on its own), out of this task's own safe scope -- a behaviour change to a shared, multi-round-reviewed kernel-adjacent function, the same discipline C25 below explains for its own one-hop-removed consequence of this identical gap. [verdict] is [open], not [colitur], because a single per-entry verdict cannot honestly claim BOTH sub-populations at once, and the coordinator's own instruction was explicit: mark it questioned, not leave a verdict the evidence contradicts. + +Gated on the literal date window (month=12, day in {29,30,31}) AND colitur's own slug family (`nativity_octave_day_slugs`) AND `subset diffs [ Slug_f; First_f; Gospel_f ]` (widened this task from the exact-match `diffs = [ Slug_f ]` the pre-citation-comparison entry used -- narrowing back to exact-match would have silently re-excluded the very citation divergence this task exists to surface). Derived directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations), not hand-counted first and cross-checked after: 138.") + (expected_rows 138)) ((id C8) (citation "RG 87 (Minor Litanies/Rogations, Mon/Tue before Ascension); register §3c item 3") (verdict colitur) @@ -106,7 +110,9 @@ NARROWED, task 8: expected_rows moved 416 -> 406. Not a regression -- 10 of the ((id C18) (citation "RG 124 (\"XVIII - De coloribus paramentorum\", \"C) De colore rubro\"), both photographic scans word for word: 124(b) \"adhibetur color ruber in Officio et Missa de festis: ... Sanctorum Apostolorum et Evangelistarum, in eorum die natalicio, excepto festo S. Ioannis (27 decembris)\"; 124(e) \"Sanctorum Martyrum, quorum colitur aut martyrium\"; register's own audit rewrite, ef-sanctoral-audit task") (verdict colitur) - (note "ef-sanctoral-audit task (2026-08-14): 14 [status = Feast] sanctoral entries bootstrapped from lectio's own tridentine-calendar.ini carry a colour lectio's data gets wrong at the source (checked individually: e.g. `martha`'s own Polish field there already reads \"Dziewicy\" -- Virgin, no \"Męczennicy\" -- yet that SAME record's colour is red) -- corrected via data/ef/adjustments.sexp's own `Edit ((Set_colour ...))` directives, the same mechanism and root-cause shape `eusebius-confessor` (ef-rebootstrap fix round 1, F3) already used. Two rules: an Apostle/Evangelist's own feast day is red except John's (27 December, already correctly white, untouched); a plain Confessor/Bishop/Virgin (no \"Mart.\"/\"Mm.\" in the calendarium's own line, not an Apostle) is white by RG 117's five-colour residual; a Martyr's own martyrdom is red. `audit_colour_corrected_slugs` in test_differential.ml is the literal 14-slug list (conversion-of-st-paul, chair-of-st-peter, john-of-san-fecundo, ephrem-of-syria, julia-of-falconieri, john-gualbert, camillus-de-lellis, jerome-emiliani, apollinaris, martha, alphonsus-liguori, augustine, rose-of-lima, josaphat) -- every one of these [status = Feast], so it wins its own day most years (rarely impeded) and diffs on [Colour_f] alone against lectio's still-wrong value. A further eight corrections (prisca/peter/vitus/margaret/agapitus/liborii/mark-i, all [Commemoration_only], plus the new `barbara` Add) are NOT in this count: a [Commemoration_only] candidate can never be the OBSERVED day (Precedence.resolve's own design), so this row-level comparator (which reads only the observed day's own record) never has a row to diff for them -- inert to this fixture, not unaffected in truth. Derived directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations's own `Received`), not hand-counted first and cross-checked after: 526.") + (note "ef-sanctoral-audit task (2026-08-14): 14 [status = Feast] sanctoral entries bootstrapped from lectio's own tridentine-calendar.ini carry a colour lectio's data gets wrong at the source (checked individually: e.g. `martha`'s own Polish field there already reads \"Dziewicy\" -- Virgin, no \"Męczennicy\" -- yet that SAME record's colour is red) -- corrected via data/ef/adjustments.sexp's own `Edit ((Set_colour ...))` directives, the same mechanism and root-cause shape `eusebius-confessor` (ef-rebootstrap fix round 1, F3) already used. Two rules: an Apostle/Evangelist's own feast day is red except John's (27 December, already correctly white, untouched); a plain Confessor/Bishop/Virgin (no \"Mart.\"/\"Mm.\" in the calendarium's own line, not an Apostle) is white by RG 117's five-colour residual; a Martyr's own martyrdom is red. `audit_colour_corrected_slugs` in test_differential.ml is the literal 14-slug list (conversion-of-st-paul, chair-of-st-peter, john-of-san-fecundo, ephrem-of-syria, julia-of-falconieri, john-gualbert, camillus-de-lellis, jerome-emiliani, apollinaris, martha, alphonsus-liguori, augustine, rose-of-lima, josaphat) -- every one of these [status = Feast], so it wins its own day most years (rarely impeded) and diffs on [Colour_f] alone against lectio's still-wrong value. A further eight corrections (prisca/peter/vitus/margaret/agapitus/liborii/mark-i, all [Commemoration_only], plus the new `barbara` Add) are NOT in this count: a [Commemoration_only] candidate can never be the OBSERVED day (Precedence.resolve's own design), so this row-level comparator (which reads only the observed day's own record) never has a row to diff for them -- inert to this fixture, not unaffected in truth. Derived directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations's own `Received`), not hand-counted first and cross-checked after: 450. + +CORRECTED, task 8 fix round 1 (coordinator review, Minor): this note previously ended \"...cross-checked after: 526\", inconsistent with its own `(expected_rows 450)` immediately below and with the value `dune test` actually asserts and passes -- a stale transcription, not a live discrepancy (the test itself has been green at 450 throughout; only this trailing sentence disagreed with it). Fixed to read 450, matching the pin.") (expected_rows 450)) ((id C19) (citation "register §3c item 5 (Time-after-Epiphany week-numbering anchor, the SAME citation this file's own [strip_epiphany_index]/Layer B header comment already carries) -- not a new rubric dispute, the previously-invisible CITATION-level consequence of an already-adjudicated NUMBERING artifact") @@ -118,10 +124,35 @@ NARROWED, task 8: expected_rows moved 416 -> 406. Not a regression -- 10 of the (verdict colitur) (note "task 8 (branch ef-lectionary): lectio's own [caldata.Readings] genuinely resolves NOTHING at all -- literally \"-\"/\"-\", not merely a different value -- for 14 civil days across 2005-2050 (task-7-report.md's own count, independently reproduced here): every one is a day on which one of SEVEN readingless class-3 saints (vincent-ferrer x5, isidore-of-seville x3, john-of-god x2, thomas-aquinas, francis-of-paola, sts-felicitas-perpetua, frances-rome, x1 each) is the day's own observed office. Task 6 gave every one of these saints either a Common assignment (data/ef/commons.sexp, the Missal's own instruction at that saint's date, e.g. \"Missa Os iusti, de Communi Confessoris non Pontificis I loco\" for Vincent Ferrer) or, where the Missal instead prints a full proper Mass at the saint's own date (thomas-aquinas, john-of-god, francis-of-paola), a direct citation via data/ef/adjustments.sexp -- both routes read from three independent witnesses each (scan1, scan2, the page images; task-6-report.md's own cross-check discipline), not invented. colitur is MORE COMPLETE here, not wrong: lectio's own generator simply has no data for these particular saints, a genuine upstream gap this task's own report records for lectio, not a colitur defect. Gated on the literal 7-slug list AND on lectio's field reading the literal \"-\"/\"-\" sentinel, never \"any citation mismatch on these slugs\" -- a genuine future content disagreement on one of these seven saints (e.g. if lectio's own data ever grew an entry that DISAGREED with colitur's scan-verified citation) would still surface as unexplained, not silently absorbed here. Derived directly from the OCaml comparator's own failure output (test_layer_c_counts_match_citations), not hand-counted first and cross-checked after: 14.") (expected_rows 14)) - ((id C21) - (citation "NONE -- this entry cites no RG paragraph, deliberately: it is not a rubric dispute. Missal-verified anyway, for the ONE guessed value this task tried and reverted: docs/research/scan1.txt, \"Dominica infra octavam Nativitatis Domini, II classis\" -- Epistle \"Gal. 4, 1-7\", Gospel \"Luc. 2, 33-40\" (the SAME pair as Holy Name Sunday, corroborating tools/bootstrap_lectionary.ml's own [derived_entries] comment on why that particular guess was wrong for OTHER years anyway)") + ; C21 -- CLOSED, task 8 fix round 1 (coordinator review), REMOVED not + ; re-adjudicated to a different verdict: its own diagnosis ("colitur's + ; step 3 cannot reach a correct answer for the fixed Christmastide + ; slugs") was SUPERSEDED, not merely fixed around, by Important 3(a)'s + ; direct Missal formulary for ef-nativity-octave-day-{5,6,7} (which + ; resolves at step 2 now, not step 3 at all) and the Critical-1/2 fixes + ; elsewhere in this same fix round. What C21 used to cover has split + ; cleanly into C6 above (widened, re-opened) and C25 below (a newly, + ; more precisely diagnosed one-hop-removed consequence of the SAME RG 69 + ; gap) -- test_differential.ml's own layer_c_reason carries no C21 + ; predicate any more, the same discipline this file's own C2-C5/C7/ + ; C9-C13 closures already established. + ((id C22) + (citation "RG 91 entry 18 (\"Quatuor Tempora... post primam dominicam Quadragesimae\" -- the Lenten Ember days) + MR1962 \"De anno et eius partibus\" (register §3a, the same citation Rite_ef.Temporal_ef's own [ember] carries)") + (verdict colitur) + (note "task 8 fix round 1 (coordinator review, Critical 2): the Lenten Ember days (colitur's own `ef-lent-ember-{wed,fri,sat}`) are reachable for the first time this fix round -- tools/bootstrap_lectionary.ml's own [colitur_keys] comment has the full account of the dead-key bug this closes (lectio's own ini keys this data under the generic \"ef-lent-1-\" family, unreachable under colitur's own spelling until renamed). lectio's own SLUG for these three days already matched colitur's (\"ef-lent-ember-wed\" etc, both engines -- confirmed no [Slug_f] in this entry's own rows), but lectio's own READING resolution has the IDENTICAL internal mismatch colitur's used to have: its own [caldata.Readings] never reaches its own Ember data either, so it still falls back to Lent I Sunday's own Mass (e.g. \"2 Cor. 6:1-10\"/\"Matt 4:1-11\") on every one of these dates, where colitur now correctly shows the Ember day's own proper (e.g. \"3 Kgs. 19:3-8\"/\"Matt 12:38-50\"). A genuine LECTIO bug, reported here for upstream, NOT patched in this task (the brief's own triage rule). This is exactly the \"Holy Thursday was violet in both\" shape this project's own CLAUDE.md already names: layer 3 was STRUCTURALLY BLIND to this the whole time colitur also had the bug, since both engines independently produced the identical wrong answer -- a real illustration of why a passing differential means less here than it looks, not merely a citation elsewhere in this file's own header. Gated on the literal 3-slug list and `diffs = [ First_f; Gospel_f ]` exactly (slug/rank/colour already agree). Derived directly from the OCaml comparator's own failure output, not hand-counted first and cross-checked after: 128 (43 + 43 + 42 -- a few of the 46-year population are impeded by a competing sanctoral feast each year, so the Ember office itself is not always the observed day).") + (expected_rows 128)) + ((id C23) + (citation "Holy Monday (\"Feria II Hebdomadae sanctae, I classis\"): Isai. 50,5-10 / Io. 12,1-9. Holy Tuesday (\"Feria III Hebdomadae sanctae, I classis\"): Ier. 11,18-20 / the Passion according to Mark, 14,32-72;15,1-46. Holy Thursday (\"Feria V in Cena Domini\"): 1 Cor. 11,20-32 / Io. 13,1-15. Holy Saturday (Missa Vigiliae Paschalis' own Epistle+Gospel, explicitly labelled \"Lectio Epistolae\", distinct from its own preceding prophecies): Col. 3,1-4 / Matt. 28,1-7. All four: docs/research/scan1.txt AND scan2.txt, word for word, both scans independently (tools/bootstrap_lectionary.ml's own [holy_week_entries] comment has the exact line references)") + (verdict colitur) + (note "task 8 fix round 1 (coordinator review, Critical 1): the ORIGINAL version of this task's own `colitur_keys` widened lectio's single \"ef-passiontide-0-\" ini section into BOTH Passion week (`ef-passiontide-1-*`) and Holy Week (`ef-passiontide-2-*`, including the entire Sacred Triduum) on the strength of lectio's own citation being byte-identical between the two weeks -- WRONG: that only proves lectio CONFLATES the two weeks (it has no Holy Week propers of its own at all), not that the Missal does. The Mass of the Lord's Supper reading Passion Sunday's own ferial Mass was a real defect, not a citation nicety -- caught by coordinator review before this task's own commit, not by anything in this suite (the differential was GREEN under the wrong widening, since both sides showed the identical wrong value). Fixed: Holy Week's own four cleanest days (Monday, Tuesday, Thursday, Saturday -- ordinary single-Epistle-single-Gospel Mass structure, unlike Wednesday/Good Friday, see C24 below) are hand-authored directly from the Missal, twice-verified. lectio, still conflating the two weeks, keeps showing Passion week's own reused citation on every one of these dates, every year -- a genuine, permanent, structural divergence, not a data gap on either side that could close. Gated on the literal 4-slug list and `diffs = [ First_f; Gospel_f ]` exactly (identity already agrees on both sides -- season/slug/rank/colour were never the problem). Derived directly from the OCaml comparator's own failure output, not hand-counted first and cross-checked after: 184 (46 years x 4 days -- Holy Week recurs every single year without exception, so this is not a coincidental count the way a collision-driven entry's would be).") + (expected_rows 184)) + ((id C24) + (citation "Holy Wednesday (\"Feria IV Hebdomadae sanctae\"): TWO Old Testament lessons, Isai. 62,11;63,1-7 THEN Isai. 53,1-12, neither labelled \"Epistola\", before the Passion according to Luke (22,39-71;23,1-53). Good Friday (\"Feria VI in Passione et Morte Domini\" -- not even a Mass, \"Solemnis Actio liturgica\"): TWO peer lessons, Osee 6,1-6 THEN Exodus 12,1-11, again neither labelled \"Epistola\", directly into the Passion according to John (18,1-40;19,1-42). Both: docs/research/scan1.txt, corroborated scan2.txt") + (verdict colitur) + (note "task 8 fix round 1 (coordinator review, Critical 1's own scope boundary): unlike Holy Monday/Tuesday/Thursday/Saturday (C23 above), Holy Wednesday and Good Friday genuinely have no single reading in the \"Epistle\" position this schema's [First]/[Gospel] pair assumes every OTHER entry in data/ef/lectionary.sexp has -- TWO peer Old Testament lessons lead directly into the Passion narrative on both days, neither one labelled \"Epistola\" the way Holy Saturday's own Colossians reading explicitly is. Forcing either lesson into the [First] slot would be an uncited editorial choice this generator has no textual warrant to make on its own, so tools/bootstrap_lectionary.ml's own [holy_week_entries] deliberately leaves both slugs absent -- colitur emits [], an honest declination, not a guess. lectio, still conflating Holy Week with Passion week (C23's own account), shows Passion week's own reused (wrong) citation on both dates regardless. verdict colitur: declining to guess is the epistemically correct move here, and strictly more correct than lectio's confident wrong answer, even though colitur offers no citation of its own to point to. Gated on the literal 2-slug list and `diffs = [ First_f; Gospel_f ]` exactly. Derived directly from the OCaml comparator's own failure output, not hand-counted first and cross-checked after: 92 (46 years x 2 days).") + (expected_rows 92)) + ((id C25) + (citation "RG 69, scan1.txt:625-631 word for word (the SAME citation C6 above carries in full) -- this entry is the one-hop-removed CONSEQUENCE of that same gap, not an independent rubric question") (verdict open) - (note "task 8 (branch ef-lectionary): a genuine, precisely-diagnosed colitur LOGIC LIMITATION, not a data/bootstrap issue -- Rite_ef.Lectionary_ef.readings' step 3 is a SINGLE hop to the immediately preceding civil Sunday's own temporal identity. For a run of colitur-only FIXED Christmastide slugs (`ef-nativity-octave-day-{5,6,7}`, 29-31 December; the `ef-christmas-{0,1,2}-` ferias of the two weeks that follow) that ONE preceding Sunday is itself frequently ANOTHER entry-less fixed slug (another Nativity-Octave day, or, on the single occasion 24 December itself falls on a Sunday, `ef-nativity-vigil`) -- so step 3 finds nothing at all (95 of these 107 rows: colitur emits [], a conservative, honest absence, never a fabricated guess) or, on the 12 rows where the walk lands exactly on that Vigil-is-a-Sunday collision, finds the WRONG thing (the Vigil's own Mass -- Rom 1:1-6/Matt 1:18-21 -- correctly attached to 24 December itself, but misapplied here to a LATER feria that is not Christmas Eve). lectio's own resolution reaches further by a mechanism this task did not fully reverse-engineer: empirically (grep against the fixture) it variously finds Christmas Day's own Mass or Advent IV's own Mass (e.g. 2005-12-29 -> Heb 1:1-12/John 1:1-14, Christmas Day's own Mass, 25 December 2005 being a Sunday that year; 2006-12-29 -> 1 Cor. 4:1-5/Luke 3:1-6, Advent IV's, 24 December 2006 being a Sunday with no OTHER Sunday between it and 29 December). Properly fixing this needs either an ITERATIVE walk-back inside [readings] itself (keep walking to the next-earlier Sunday until an entry is found, a genuine behaviour change) or a per-year-verified direct entry for every fixed Christmastide slug across the whole window (task 6's own rigor, applied to a new, comparably-sized population) -- both are real units of work needing their own TDD/mutation-testing/review-round discipline (Tasks 4-6's own history: every one of them had at least one review round with real findings before being adjudicated DONE), not something to bolt onto this task's own scope (fixture regeneration + the comparator + triage) without that same rigor. RECOMMENDED as a dedicated follow-up task; task-8-report.md has the full diagnostic account. Gated on the two literal colitur slug-family prefixes (`ef-nativity-octave-day-`, `ef-christmas-`) and the diff set (`subset diffs [ Slug_f; First_f; Gospel_f ]`, never `Rank`/`Colour_f`/`Season`) -- broader than C14-C16's own short literal-DATE lists (matching C17's own precedent for a large, structurally-defined population instead), so a future regression that also changed RANK or COLOUR on one of these same slugs would still surface as unexplained; a future regression confined to CONTENT on one of these exact slugs, however, would be silently absorbed here -- the acknowledged, and unavoidable, cost of tracking a genuinely-open, unfixed area rather than a closed one. - -CORRECTED, same task, fix round: an earlier pass of this note measured 202 (190 empty + 12 misapplied-Vigil), before `colitur_keys \"ef-christmas-sunday-0\"` was fixed to WIDEN rather than replace (C6's own note above has the full account -- a regression this task introduced and then caught via test_lectionary_ef.ml's own pre-existing `test_step3_christmas_feria_resumes_sunday`). Fixing that regression also legitimately shrank THIS entry's own population: 57 of the 202 rows resolved outright once `ef-christmas-sunday-0` had its own direct entry again (the Sunday-within-the-Octave case, and every later feria whose one-hop walk lands there); 38 more matched lectio's own citation exactly with no Layer C entry needed at all, once the true target -- Christmas Day's own Mass, on a year 25 December falls on a Sunday -- became reachable via the SAME fix (a Sunday's own temporal identity resolving correctly is a precondition for a THIRD entity, a later feria's own preceding-Sunday walk finding it, to resolve correctly too). Re-derived directly from the OCaml comparator's own failure output after the fix, not hand-counted first and cross-checked after: 107 (95 empty + 12 misapplied-Vigil, the SAME 12 dates as before -- that sub-population is untouched by the `ef-christmas-sunday-0` fix, since it is caused by a DIFFERENT fixed slug, `ef-nativity-vigil`, colliding with a Sunday).") - (expected_rows 107)) + (note "task 8 fix round 1 (coordinator review, Important 3(b)): colitur's `ef-christmas-1-{wednesday,thursday,friday,saturday}` (Holy Name week ferias with no proper of their own) resume, via Lectionary_ef.readings' step 3, the preceding Sunday's temporal identity -- which, on these specific dates, is one of the Nativity-Octave Sundays C6 above already names as RG-69-QUESTIONED (a Sunday landing 29-31 December, where colitur wrongly emits the fixed weekday formulary \"Tit. 3,4-7\"/\"Luc. 2,15-20\" instead of the Sunday's own distinct Office). Confirmed, not merely inferred: every single one of these 57 rows' colitur-side citation is exactly \"Tit. 3, 4-7\" -- the SAME wrong value C6's own 19 Sunday-landing rows carry, propagated one civil-date hop later, not a second, independent cause. Monday and Tuesday of the SAME week can never reach this chain (they are always claimed first by the Circumcision's or Holy Name's own direct entries), which is why only four weekdays appear here, never six. A genuine [Temporal_ef] defect, out of this task's own scope (the same shared, multi-round-reviewed kernel-adjacent function C6's own note names) -- recorded here per the coordinator's own explicit instruction (\"record it prominently... for a follow-up task\"), not fixed and not left silently failing. Gated on the literal 4-slug family and `diffs = [ First_f; Gospel_f ]` exactly (identity already agrees -- this is a pure content consequence one hop removed). Derived directly from the OCaml comparator's own failure output, not hand-counted first and cross-checked after: 57 (19 + 19 + 13 + 6, Wednesday/Thursday/Friday/Saturday respectively).") + (expected_rows 57)) diff --git a/data/ef/lectionary.sexp b/data/ef/lectionary.sexp index 41504c1..75b6ad4 100644 --- a/data/ef/lectionary.sexp +++ b/data/ef/lectionary.sexp @@ -1,15 +1,20 @@ ; data/ef/lectionary.sexp -- EF (1962) temporal lectionary (Epistle + ; Gospel citations, never scripture text), bootstrapped from lectio. ; Generator: tools/bootstrap_lectionary.ml -- do not hand-edit; re-run the -; generator against a newer lectio and commit the diff instead. +; generator against a newer lectio and commit the diff instead. Every +; emitted key is asserted, at generation time, to be a slug +; Rite_ef.Temporal_ef actually computes (see this generator's own +; [assert_reachable]) -- a dead key cannot ship silently again. ; ; Source: ../lectio/internal/caldata/tridentine-lectionary.ini ; SHA-256: fde533289fb620fea33386b0a3e733ada3c1d01f10a36d8a7f5a2b500c92740e -; 128 entries (119 ini sections translated/widened into colitur's own +; 129 entries (119 ini sections translated/widened into colitur's own ; Temporal_ef vocabulary via [colitur_keys]; 1 hand-authored from a -; second source file [vigil_entries]; 1 derived from an already- +; second source file [vigil_entries]; 4 hand-authored directly from +; the Missal [holy_week_entries]; 3 hand-authored directly from the +; Missal [nativity_octave_entries]; 1 derived from an already- ; translated entry above rather than re-typed [derived_entries] -- -; see this generator's own comments on all three). Regenerate with: +; see this generator's own comments on all five). Regenerate with: ; eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- ../lectio/internal/caldata/tridentine-lectionary.ini data/ef/lectionary.sexp ((ef-advent-2-monday (((part First) (reference "Rom 15:4-13")) @@ -128,24 +133,15 @@ (ef-holy-name-sunday (((part First) (reference "Gal 4:1-7")) ((part Gospel) (reference "Luke 2:33-40")))) - (ef-lent-1-friday - (((part First) (reference "Ezech 18:20-28")) - ((part Gospel) (reference "John 5:1-15")))) (ef-lent-1-monday (((part First) (reference "Ezech 34:11-16")) ((part Gospel) (reference "Matt 25:31-46")))) - (ef-lent-1-saturday - (((part First) (reference "1 Thess. 5:14-23")) - ((part Gospel) (reference "Matt 17:1-9")))) (ef-lent-1-thursday (((part First) (reference "Ezech 18:1-9")) ((part Gospel) (reference "Matt 15:21-28")))) (ef-lent-1-tuesday (((part First) (reference "Isa 55:6-11")) ((part Gospel) (reference "Matt 21:10-17")))) - (ef-lent-1-wednesday - (((part First) (reference "3 Kgs. 19:3-8")) - ((part Gospel) (reference "Matt 12:38-50")))) (ef-lent-2-friday (((part First) (reference "Gen 37:6-22")) ((part Gospel) (reference "Matt 21:33-46")))) @@ -209,6 +205,15 @@ (ef-lent-after-ashes-thursday (((part First) (reference "Isa 38:1-6")) ((part Gospel) (reference "Matt 8:5-13")))) + (ef-lent-ember-fri + (((part First) (reference "Ezech 18:20-28")) + ((part Gospel) (reference "John 5:1-15")))) + (ef-lent-ember-sat + (((part First) (reference "1 Thess. 5:14-23")) + ((part Gospel) (reference "Matt 17:1-9")))) + (ef-lent-ember-wed + (((part First) (reference "3 Kgs. 19:3-8")) + ((part Gospel) (reference "Matt 12:38-50")))) (ef-lent-sunday-1 (((part First) (reference "2 Cor. 6:1-10")) ((part Gospel) (reference "Matt 4:1-11")))) @@ -227,6 +232,15 @@ (ef-nativity (((part First) (reference "Heb 1:1-12")) ((part Gospel) (reference "John 1:1-14")))) + (ef-nativity-octave-day-5 + (((part First) (reference "Tit. 3, 4-7")) + ((part Gospel) (reference "Luc. 2, 15-20")))) + (ef-nativity-octave-day-6 + (((part First) (reference "Tit. 3, 4-7")) + ((part Gospel) (reference "Luc. 2, 15-20")))) + (ef-nativity-octave-day-7 + (((part First) (reference "Tit. 3, 4-7")) + ((part Gospel) (reference "Luc. 2, 15-20")))) (ef-nativity-vigil (((part First) (reference "Rom 1:1-6")) ((part Gospel) (reference "Matt 1:18-21")))) @@ -254,24 +268,18 @@ (ef-passiontide-1-wednesday (((part First) (reference "Lev 19:1-2, 11-19, 25")) ((part Gospel) (reference "John 10:22-38")))) - (ef-passiontide-2-friday - (((part First) (reference "Jer 17:13-18")) - ((part Gospel) (reference "John 11:47-54")))) (ef-passiontide-2-monday - (((part First) (reference "Jonas 3:1-10")) - ((part Gospel) (reference "John 7:32-39")))) + (((part First) (reference "Isai. 50, 5-10")) + ((part Gospel) (reference "Io. 12, 1-9")))) (ef-passiontide-2-saturday - (((part First) (reference "Jer 18:18-23")) - ((part Gospel) (reference "John 12:10-36")))) + (((part First) (reference "Col. 3, 1-4")) + ((part Gospel) (reference "Matt. 28, 1-7")))) (ef-passiontide-2-thursday - (((part First) (reference "Dan 3:25, 34-45.")) - ((part Gospel) (reference "Luke 7:36-50")))) + (((part First) (reference "1 Cor. 11, 20-32")) + ((part Gospel) (reference "Io. 13, 1-15")))) (ef-passiontide-2-tuesday - (((part First) (reference "Jer 11:18-20")) - ((part Gospel) (reference "Mark 14:32-72; 15, 1-46")))) - (ef-passiontide-2-wednesday - (((part First) (reference "Lev 19:1-2, 11-19, 25")) - ((part Gospel) (reference "John 10:22-38")))) + (((part First) (reference "Ier. 11, 18-20")) + ((part Gospel) (reference "Mark 14, 32-72; 15, 1-46")))) (ef-pentecost (((part First) (reference "Acts 2:1-11.")) ((part Gospel) (reference "John 14:23-31.")))) diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 4add824..dacae9c 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -492,13 +492,22 @@ let third_sunday_of_september y = excepts the Lenten set from the III-class Lenten ferias. The Whitsun set falls inside the I-class Pentecost octave and takes its rank. - 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 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. *) + The September and Advent sets match lectio's own Ember SLUG spelling + directly. The Lent and Whitsun (Pentecost) sets do not -- "ef-lent-ember-*" + and "ef-pentecost-ember-*" are colitur-only slug KEYS, never lectio's own + naming for the identical civil days (Lent: "ef-lent-1-"; + Whitsun: "ef-easter-8-"). CORRECTED (task 8 fix round 1, + coordinator review, Critical 2): this comment previously said "lectio + has no Ember slug for either" and framed BOTH as "a lectionary gap for + the Plan 4 bootstrap to fill" -- true of the SLUG naming, misleading + about the underlying READING DATA. lectio's own tridentine-lectionary.ini + carries real citations for both sets, just filed under the non-Ember + names above -- tools/bootstrap_lectionary.ml's own [colitur_keys] + renames both onto colitur's own slugs (the Whitsun rename shipped with + Plan 4; the Lent one was missed for one fix round -- both engines + independently fell through to the wrong ferial-resumption answer there, + so even the differential could not see the gap until it was found; + data/ef/expected-divergences.sexp's own C22 has the full account). *) let ember d = let y = Date.year d in let easter = Computus.gregorian_easter y in diff --git a/test/test_differential.ml b/test/test_differential.ml index 74cc54d..bdb3377 100644 --- a/test/test_differential.ml +++ b/test/test_differential.ml @@ -717,54 +717,117 @@ let lectio_no_reading_slugs = [ "vincent-ferrer"; "francis-of-paola"; "isidore-of-seville"; "thomas-aquinas"; "john-of-god"; "sts-felicitas-perpetua"; "frances-rome" ] -(* C21 (task 8) -- [verdict open], the FIRST entry in this file that is not - [verdict colitur] (see data/ef/expected-divergences.sexp's own header for - the file-level rule this departs from, and the note on why). A genuine, - precisely-diagnosed colitur LOGIC LIMITATION, not a data/bootstrap issue - and not a rubric dispute: {!Rite_ef.Lectionary_ef.readings}'s step 3 is a - SINGLE hop to the immediately preceding civil Sunday's own temporal - identity. For a run of colitur-only FIXED Christmastide slugs - ([ef-nativity-octave-day-{5,6,7}], 29-31 December; the - [ef-christmas-{0,1,2}-] ferias of the two weeks that follow) that - ONE Sunday is itself frequently ANOTHER entry-less fixed slug (another - Nativity-Octave day, or, on the single occasion 24 December itself falls - on a Sunday, [ef-nativity-vigil]) -- so step 3 finds nothing (95 of these - 107 rows) or, on the 12 rows where the walk happens to land exactly on - that one Vigil-is-a-Sunday collision, finds the WRONG thing (the Vigil's - own Mass, misapplied to a later feria that is not itself Christmas Eve). - lectio's own resolution reaches further by some mechanism this task did - not fully reverse-engineer (empirically: it variously finds Christmas - Day's own Mass or Advent IV's own Mass, depending on the year's - alignment). Properly fixing this needs either an iterative walk-back in - [readings] itself or a per-year-verified direct entry for every fixed - Christmastide slug (task 6's own rigor, applied to a new population) -- - both are logic/behaviour changes to a shared, carefully-reviewed - kernel-adjacent function (Tasks 4-6's own multi-round review history), - out of this task's own safe scope. Recorded here, not left silently - failing, because the orchestrating instruction's own condition for - "leave it failing" is "if you cannot explain a divergence" -- this one - IS explained, precisely; what remains unresolved is which SAFE fix to - build, not what the mechanism is. Gated on the two literal colitur - slug-family prefixes (never a bare "any citation diff" rule) so an - unrelated future bug on any OTHER slug still surfaces as unexplained. - Derived directly from the OCaml comparator's own failure output - (test_layer_c_counts_match_citations), not hand-counted first and - cross-checked after: 107. NOTE: an earlier version of this comment (and - of [colitur_keys] in tools/bootstrap_lectionary.ml) measured 202 here, - before a regression in the FIRST version of the "ef-christmas-sunday-0" - translation (replaced instead of widened the key -- caught by - test_lectionary_ef.ml's own pre-existing test, not by this reasoning) - was fixed; fixing it also legitimately shrank this population by 95 - rows. data/ef/expected-divergences.sexp's own C21/C6 notes have the full - before/after account, including the ONE guess this task tried for - [ef-nativity-octave-day-{5,6,7}] (Holy Name Sunday's own citation, - uniformly) and reverted once measured -- Missal-confirmed - (docs/research/scan1.txt, "Dominica infra octavam Nativitatis Domini, - II classis": Gal. 4,1-7 / Luc. 2,33-40) to be right for the genuine - Sunday-within-the-Octave case alone, wrong whenever the true preceding - Sunday is Christmas Day or Advent IV instead. *) -let christmastide_fixed_slug_family (c : row) = - starts_with ~prefix:"ef-nativity-octave-day-" c.slug || starts_with ~prefix:"ef-christmas-" c.slug +(* C21 -- RETIRED, task 8 fix round 1 (coordinator review). Previously a + [verdict open] entry (107 rows) for "colitur's step 3 cannot reach a + correct answer for the fixed Christmastide slugs". That diagnosis has + been SUPERSEDED, not merely fixed around: Important 3(a)'s own direct + Missal formulary (tools/bootstrap_lectionary.ml's own + [nativity_octave_entries]) gives [ef-nativity-octave-day-{5,6,7}] a + real, correct answer via STEP 2, not step 3 at all, so "step 3 cannot + reach it" is no longer even true. What C21 used to cover has split + cleanly into two DIFFERENT, more precisely diagnosed populations: + colitur's own [ef-nativity-octave-day-N] rows now fold into C6 above + (widened, [verdict] re-opened -- see its own note); the + [ef-christmas-1-] rows that resume THAT slug's own (partly + wrong, per RG 69) answer one hop later via step 3 are C25 below, a + genuinely different id because the mechanism is now precisely RG 69 + propagating through step 3, not "step 3 has no answer". No predicate + or helper of this file's own still names "C21" -- removing the code + alongside the id is the same discipline this file's own header already + applies to C2-C5/C7/C9-C13 (closed, cited, never left as dead code). *) + +(* C22 (task 8, fix round 1, coordinator review, Critical 2): the Lenten + Ember days (RG 91 entry 18, colitur's own ef-lent-ember-{wed,fri,sat}) + are reachable for the first time this fix round -- tools/ + bootstrap_lectionary.ml's own [colitur_keys] comment has the full + account of the dead-key bug this closes. lectio's own SLUG for these + three days already matched colitur's (both call it "ef-lent-ember-wed" + etc, confirmed by this row's own [c.slug]/[l.slug] agreement -- no + [Slug_f] in this entry's own diff set), but lectio's own READING + resolution has the IDENTICAL internal mismatch colitur's used to have: + its own ini keys this data under "ef-lent-1-", and its own + [caldata.Readings] never reaches it either, so it still falls back to + Lent I Sunday's own Mass (e.g. "2 Cor. 6:1-10"/"Matt 4:1-11") on every + one of these dates. That is a genuine LECTIO bug, reported here for + upstream, NOT fixed in this task (the brief's own triage rule: record, + do not patch the sibling project) -- and it is exactly the "Holy + Thursday was violet in both" shape this project's own CLAUDE.md already + names: layer 3 was STRUCTURALLY BLIND to this the whole time colitur + also had the bug, because both engines independently produced the same + wrong answer. Gated on colitur's own slug family (a literal 3-member + list, not a pattern) and the diff set staying [First_f; Gospel_f] + alone (slug/rank/colour already agree). Derived directly from the OCaml + comparator's own failure output, not hand-counted first and cross- + checked after: 128 (43 + 43 + 42 -- a few of the 46-year population are + impeded by a competing sanctoral feast each year, so the Ember office + itself is not always the observed day). *) +let lent_ember_slugs = [ "ef-lent-ember-wed"; "ef-lent-ember-fri"; "ef-lent-ember-sat" ] + +(* C23/C24 (task 8, fix round 1, coordinator review, Critical 1): Holy + Week's own citations (tools/bootstrap_lectionary.ml's own + [holy_week_entries] comment has the full Missal citations and the two- + scan corroboration for each). lectio has no Holy Week propers at all -- + every "ef-passiontide-0-" ini section is Passion week's own + Mass (or, for Tuesday specifically, Holy Tuesday's own Passion Gospel + riding along on Passion week's own Epistle -- a genuine internal + inconsistency in lectio's own source, not this project's to fix), so + lectio shows THAT reused/conflated citation on every Holy Week weekday, + every year, regardless of what the real Missal prints there. + + C23 covers the four days colitur now answers correctly and completely + (Monday, Tuesday, Thursday, Saturday) -- verdict colitur, cited + directly. C24 covers the two days colitur deliberately answers with [] + instead of a guess (Wednesday, Good Friday -- both genuinely + multi-lesson liturgies with no single Epistle slot this schema could + honestly fill; [holy_week_entries]'s own comment has the full argument + for why forcing a pair would be an uncited editorial choice, not a + Missal fact). Both entries: gated on colitur's own literal slug list + and [diffs] staying exactly [First_f; Gospel_f] (identity already + agrees on both). Every occurrence across the whole 2005-2050 fixture + falls in all 46 years without exception, on every one of these six + slugs -- Holy Week recurs every year by construction, so this is not a + coincidental count the way a collision-driven entry's would be. + Derived directly from the OCaml comparator's own failure output, not + hand-counted first and cross-checked after: C23 184 (46 x 4), C24 92 + (46 x 2). *) +let holy_week_answered_slugs = + [ "ef-passiontide-2-monday"; "ef-passiontide-2-tuesday"; "ef-passiontide-2-thursday"; + "ef-passiontide-2-saturday" ] + +let holy_week_unanswered_slugs = [ "ef-passiontide-2-wednesday"; "ef-passiontide-2-friday" ] + +(* C25 (task 8, fix round 1, coordinator review, Important 3(b)) -- + [verdict open], the SAME RG 69 gap C6 above re-opened, one hop removed. + RG 69 (docs/research/scan1.txt:625-631, word for word: "De dominica + infra octavam Nativitatis Domini... semper fit Officium cum + commemoratione festi forte occurrentis... nisi dominica incidat in + festum I classis") is unconditional: a Sunday landing 26-31 December + should keep ITS OWN Office (with the day's feast merely commemorated), + not the fixed weekday placeholder -- but colitur's [Temporal_ef] gives + both the SAME slug, undifferentiated by weekday (C6's own note, and + data/ef/expected-divergences.sexp's own C6 entry, have the full + account). Confirmed, not merely inferred, + that THIS entry is the direct, one-hop-removed consequence of that same + gap: every single one of these rows' colitur-side citation is "Tit. 3, + 4-7" (the fixed weekday formulary), because [Lectionary_ef.readings]' + step 3 resumes the SAME wrongly-undifferentiated Sunday C6 already + names, not a second, independent cause. A genuine, precisely-diagnosed + [Temporal_ef] defect -- out of this task's own scope (a behaviour + change to a shared, multi-round-reviewed kernel-adjacent function) -- + recorded here per the coordinator's own instruction ("record it + prominently... for a follow-up task"), not fixed and not left silently + failing. Gated on the literal 4-slug family (the only members of + [ef-christmas-1-] this mechanism can ever reach -- Monday and + Tuesday of that same week are always claimed first by the Circumcision + or Holy Name's own DIRECT entries, so they can never fall through to + this specific step-3 chain) and [diffs] staying exactly + [First_f; Gospel_f] (identity already agrees -- this is a pure content + consequence). Derived directly from the OCaml comparator's own failure + output, not hand-counted first and cross-checked after: 57 + (19 + 19 + 13 + 6, Wednesday/Thursday/Friday/Saturday respectively). *) +let rg69_one_hop_slugs = + [ "ef-christmas-1-wednesday"; "ef-christmas-1-thursday"; "ef-christmas-1-friday"; + "ef-christmas-1-saturday" ] (* [layer_c_reason l c diffs] returns the [data/ef/expected-divergences.sexp] [id] this row-pair's remaining (post Layer A/B) diff set belongs to, or @@ -789,8 +852,18 @@ let layer_c_reason (l : row) (c : row) diffs = the reviewer: dropping the RG 91 entry 17 elevation in temporal_ef (Class2 -> Class4 on octave days 5-7) left BOTH differential tests green with C6 still reporting 138. The slug guard below already pins identity; - [Rank] only removed the ability to notice a rank regression. *) - && diffs = [ Slug_f ] + [Rank] only removed the ability to notice a rank regression. + + WIDENED, task 8 fix round 1 (coordinator review, Important 3(a)/(b)): + [First_f; Gospel_f] joined the accepted set. Important 3(a)'s own fix + (tools/bootstrap_lectionary.ml's [nativity_octave_entries]) gives + these dates a real, Missal-verified citation for the first time -- + and it does NOT converge with lectio's own (differently incomplete) + Sunday-walkback answer, so ALL 138 of this entry's rows now carry a + citation diff too, not merely the slug one. See data/ef/expected- + divergences.sexp's own C6 entry for why this entry's [verdict] is + now [open], not [colitur] -- Important 3(b)'s own RG 69 finding. *) + && subset diffs [ Slug_f; First_f; Gospel_f ] && List.mem c.slug nativity_octave_day_slugs then Some "C6" else if @@ -858,7 +931,10 @@ let layer_c_reason (l : row) (c : row) diffs = diffs = [ First_f; Gospel_f ] && String.equal l.first "-" && String.equal l.gospel "-" && List.mem c.slug lectio_no_reading_slugs then Some "C20" - else if christmastide_fixed_slug_family c && subset diffs [ Slug_f; First_f; Gospel_f ] then Some "C21" + else if List.mem c.slug rg69_one_hop_slugs && diffs = [ First_f; Gospel_f ] then Some "C25" + else if List.mem c.slug lent_ember_slugs && diffs = [ First_f; Gospel_f ] then Some "C22" + else if List.mem c.slug holy_week_answered_slugs && diffs = [ First_f; Gospel_f ] then Some "C23" + else if List.mem c.slug holy_week_unanswered_slugs && diffs = [ First_f; Gospel_f ] then Some "C24" else None (* ---------------------------------------------------------------------- *) diff --git a/test/test_lectionary.ml b/test/test_lectionary.ml index a95c98c..8fcf8e9 100644 --- a/test/test_lectionary.ml +++ b/test/test_lectionary.ml @@ -81,19 +81,24 @@ let test_ef_data_file_loads () = match Lectionary.load "../data/ef/lectionary.sexp" with | Error e -> Alcotest.fail e | Ok l -> - (* 119 lectio ini sections, translated/widened into colitur's own - Temporal_ef vocabulary (tools/bootstrap_lectionary.ml's own - [colitur_keys]/[vigil_entries]/[derived_entries] comments have the - full account, task 8, branch ef-lectionary): Passiontide's 6 - shared sections each widen into 2 colitur keys (+6); "ef-christmas- - sunday-0" widens into itself PLUS "ef-holy-name-sunday" (+1, a fix - round after the first version replaced rather than widened it and - broke a pre-existing test); one hand-authored entry sourced from - lectio's SANCTORAL calendar, not this lectionary ini - ([ef-nativity-vigil], +1); one entry derived from an + (* 119 lectio ini sections, translated/widened/renamed into colitur's + own Temporal_ef vocabulary, PLUS hand-authored/derived entries + (tools/bootstrap_lectionary.ml's own [colitur_keys]/ + [vigil_entries]/[holy_week_entries]/[nativity_octave_entries]/ + [derived_entries] comments have the full account, task 8, branch + ef-lectionary, fix round 1): "ef-christmas-sunday-0" widens into + itself PLUS "ef-holy-name-sunday" (+1); the six Passiontide/Lent- + Ember sections translate/rename 1:1 (no count change: Passiontide + no longer widens into two colitur keys since fix round 1's + Critical 1 correction, and the three Lent Ember sections rename + rather than widen); one hand-authored entry sourced from lectio's + SANCTORAL calendar, not this lectionary ini ([ef-nativity-vigil], + +1); four hand-authored directly from the Missal, Holy Week + (+4); three hand-authored directly from the Missal, the fixed + Nativity-Octave days (+3); one entry derived from an already-translated one rather than re-typed ([ef-holy-name], +1). - 119 + 6 + 1 + 1 + 1 = 128. *) - Alcotest.(check int) "entry count" 128 + 119 + 1 + 1 + 4 + 3 + 1 = 129. *) + Alcotest.(check int) "entry count" 129 (List.length (Lectionary.entries l)); (match Lectionary.find l (slug "ef-lent-1-monday") with | Some [ a; b ] -> @@ -101,9 +106,10 @@ let test_ef_data_file_loads () = Alcotest.(check string) "gospel" "Matt 25:31-46" b.Citation.reference | Some _ -> Alcotest.fail "expected exactly two citations" | None -> Alcotest.fail "ef-lent-1-monday missing"); - (* Task 8's own renamed/derived keys, spot-checked here so a future - regeneration that silently drops one of them fails loudly and - locally, not only via the much bigger differential suite. *) + (* Task 8's own renamed/derived/hand-authored keys, spot-checked here + so a future regeneration that silently drops one of them fails + loudly and locally, not only via the much bigger differential + suite. *) let check_entry name first gospel = match Lectionary.find l (slug name) with | Some [ a; b ] -> @@ -119,17 +125,42 @@ let test_ef_data_file_loads () = check_entry "ef-pentecost-ember-wed" "Acts 5:12-16" "John 6:44-52."; check_entry "ef-pentecost-ember-fri" "Joel 2:23-24; 26-27" "Luke 5:17-26"; check_entry "ef-pentecost-ember-sat" "Rom 5:1-5." "Luke 4:38-44."; + (* Critical 2 fix (coordinator review round 1): the Lenten Ember days, + previously dead keys under lectio's own "ef-lent-1-" + naming -- now reachable under colitur's own slugs. *) + check_entry "ef-lent-ember-wed" "3 Kgs. 19:3-8" "Matt 12:38-50"; + (* Passiontide widens into Passion week ONLY now (Critical 1 fix) -- + "ef-passiontide-1-monday" keeps lectio's own citation; + "ef-passiontide-2-monday" (Holy Monday) is Missal-sourced, + DIFFERENT text, hand-authored in [holy_week_entries]. *) check_entry "ef-passiontide-1-monday" "Jonas 3:1-10" "John 7:32-39"; - check_entry "ef-passiontide-2-monday" "Jonas 3:1-10" "John 7:32-39"; - (* This one MUST be absent: rejected and reverted in the generator's - own [derived_entries] comment -- a regression-net for that - decision, not merely documentation of it. *) - (match Lectionary.find l (slug "ef-nativity-octave-day-6") with - | None -> () - | Some _ -> - Alcotest.fail - "ef-nativity-octave-day-6: must stay absent -- see bootstrap_lectionary.ml's own \ - derived_entries comment for why a direct entry here was tried and reverted") + check_entry "ef-passiontide-2-monday" "Isai. 50, 5-10" "Io. 12, 1-9"; + check_entry "ef-passiontide-2-tuesday" "Ier. 11, 18-20" "Mark 14, 32-72; 15, 1-46"; + check_entry "ef-passiontide-2-thursday" "1 Cor. 11, 20-32" "Io. 13, 1-15"; + check_entry "ef-passiontide-2-saturday" "Col. 3, 1-4" "Matt. 28, 1-7"; + (* Important 3(a) fix (coordinator review round 1): the fixed + Nativity-Octave days now carry their own direct Missal formulary + ("Diebus infra octavam Nativitatis Domini"), not Holy Name + Sunday's (a first guess, measured wrong, reverted -- see + [nativity_octave_entries]'s own comment). *) + check_entry "ef-nativity-octave-day-5" "Tit. 3, 4-7" "Luc. 2, 15-20"; + check_entry "ef-nativity-octave-day-6" "Tit. 3, 4-7" "Luc. 2, 15-20"; + check_entry "ef-nativity-octave-day-7" "Tit. 3, 4-7" "Luc. 2, 15-20"; + (* These three MUST stay absent -- Holy Wednesday and Good Friday are + deliberately not hand-authored (multi-lesson liturgies, no single + Epistle slot; see [holy_week_entries]'s own comment for why), and + Lent's OWN "ef-lent-1-wednesday/friday/saturday" keys must not + reappear once renamed away (colitur never computes them as its + own slug at all). A regression net for those decisions, not + merely documentation of them. *) + let check_absent name = + match Lectionary.find l (slug name) with + | None -> () + | Some _ -> Alcotest.fail (name ^ ": must stay absent -- see bootstrap_lectionary.ml") + in + check_absent "ef-passiontide-2-wednesday"; + check_absent "ef-passiontide-2-friday"; + check_absent "ef-lent-1-wednesday" let suite = [ ("find present", `Quick, test_find_present); diff --git a/test/test_lectionary_ef.ml b/test/test_lectionary_ef.ml index 71888e3..51c3233 100644 --- a/test/test_lectionary_ef.ml +++ b/test/test_lectionary_ef.ml @@ -146,11 +146,30 @@ let test_step3_advent_feria_resumes_sunday () = [ "Rom 13:11-14"; "Luke 21:25-33" ] (refs (day 2025 12 1)) +(* Task 8, fix round 1 (coordinator review, Important 3(a)): this test used + to pin 2025-12-29 (a Monday), reached by resuming its preceding Sunday's + temporal identity. That date no longer exercises step 3 at all -- + tools/bootstrap_lectionary.ml's own [nativity_octave_entries] now gives + the fixed Nativity-Octave slug (RG 91 entry 17, "ef-nativity-octave- + day-5") its OWN direct citation ("Diebus infra octavam Nativitatis + Domini", Tit. 3,4-7/Luc. 2,15-20), so 29 December resolves at STEP 2, + unconditionally, regardless of weekday. Every civil day in 29-31 + December now resolves the same way -- there is no date left in that + specific window this test could use to exercise step 3. + + Replaced with 2028-01-05 (a Wednesday in Holy Name week, "ef-christmas- + 1-wednesday"), which still has no citation of its own and genuinely + resumes ITS preceding Sunday, 2028-01-02 -- confirmed a real Sunday + that year, and confirmed to be Holy Name Sunday specifically (its own + [ef-holy-name-sunday] slug, RG 17(a)), not a coincidence: the whole + POINT of this test is a Christmas-season feria genuinely falling + through to step 3, and this is the closest remaining civil-calendar + case to the original 29-31 December window that still does. *) let test_step3_christmas_feria_resumes_sunday () = Alcotest.(check (list string)) - "Monday after the Sunday within the octave of Christmas" + "Wednesday of Holy Name week resumes Holy Name Sunday's Mass" [ "Gal 4:1-7"; "Luke 2:33-40" ] - (refs (day 2025 12 29)) + (refs (day 2028 1 5)) (* Fix round 1 (coordinator review, Important finding 1): the original test here (2028-12-26) did not exercise step 3 at all -- 26 December is always diff --git a/tools/bootstrap_lectionary.ml b/tools/bootstrap_lectionary.ml index a4c82ae..ed40c92 100644 --- a/tools/bootstrap_lectionary.ml +++ b/tools/bootstrap_lectionary.ml @@ -4,8 +4,19 @@ which does not belong in a pure kernel. Only `first` and `gospel` are mapped -- the plan's Global Constraints fix - the scope at Epistle + Gospel, and lectio's data carries nothing else. *) + the scope at Epistle + Gospel, and lectio's data carries nothing else. + Links `rite_ef` (fix round 1, coordinator review, Critical 2): the + generator used to have no way to check its own [colitur_keys] table + against reality, which is exactly how a translation this table forgot + (Lent's own Ember days) shipped silently -- both engines independently + fall through to the same wrong ferial answer there, so even the + differential could not see it (the "Holy Thursday was violet in both" + shape this project's own CLAUDE.md already names). [assert_reachable] + below sweeps {!Rite_ef.Temporal_ef.temporal} directly -- no sanctoral + layer, no lectionary of its own, no circularity with the file this tool + is generating -- exactly the same "code, not data" seam + tools/bootstrap_sanctoral.ml already links this library for. *) open Colitur_kernel let default_source = "../lectio/internal/caldata/tridentine-lectionary.ini" @@ -72,12 +83,7 @@ let parse_ini path = baking an alias table into the rite's own runtime lookup would just move the vocabulary problem, not solve it. - A translation can widen ONE ini section into several colitur keys - (Passiontide: lectio never distinguishes Passion week from Holy week at - the slug level, "ef-passiontide-0-" for both -- confirmed - empirically, its OWN computed reading is byte-identical between the two - weeks' same weekday throughout 2005-2050 -- so both of colitur's own - week-specific keys legitimately point at the one source entry); it + A translation can widen ONE ini section into several colitur keys; it never narrows or drops one, and every key not named here passes through unchanged (e.g. "ef-easter-8-{monday,tuesday,thursday}", the three non-Ember Pentecost-octave ferias colitur does NOT rename). *) @@ -102,14 +108,56 @@ let colitur_keys name = working case -- caught by the existing test suite, not by this generator's own reasoning; keeping the original key alongside the Holy-Name-Sunday alias fixes it, following the same "widen, never - narrow" rule Passiontide below already established. *) + narrow" rule this table's own general design already intends. *) [ "ef-christmas-sunday-0"; "ef-holy-name-sunday" ] else if String.equal name "ef-easter-8-wednesday" then [ "ef-pentecost-ember-wed" ] else if String.equal name "ef-easter-8-friday" then [ "ef-pentecost-ember-fri" ] else if String.equal name "ef-easter-8-saturday" then [ "ef-pentecost-ember-sat" ] + else if String.equal name "ef-lent-1-wednesday" then + (* Critical 2, fix round 1 (coordinator review): the Lenten Ember days + (RG 91 entry 18, "Quatuor Tempora... post primam dominicam + Quadragesimae") fall on the Wednesday/Friday/Saturday of Lent's own + first week -- the SAME three civil days colitur names + "ef-lent-ember-{wed,fri,sat}" (Rite_ef.Temporal_ef's own [ember], + checked ahead of the generic week-numbering fallback, so colitur + NEVER emits "ef-lent-1-wednesday/friday/saturday" as its own slug + for ANY civil day -- confirmed by sweeping [Temporal_ef.temporal] + directly, not assumed; [assert_reachable] below would now catch it + if that stopped being true). A RENAME, not a widen, unlike + "ef-christmas-sunday-0" above: colitur has no second, legitimate use + for the un-renamed key the way it does there. temporal_ef.ml's own + comment on [ember] previously claimed "lectio has no Ember slug for + [Lent]" -- true only in the sense that lectio's OWN naming is the + generic "ef-lent-1-" family, not that the DATA is missing; + it is right there in the ini, just unreachable under colitur's own + spelling until this rename. That comment is corrected alongside + this fix. *) + [ "ef-lent-ember-wed" ] + else if String.equal name "ef-lent-1-friday" then [ "ef-lent-ember-fri" ] + else if String.equal name "ef-lent-1-saturday" then [ "ef-lent-ember-sat" ] else ( match List.find_opt (fun wd -> String.equal name ("ef-passiontide-0-" ^ wd)) weekdays with - | Some wd -> [ "ef-passiontide-1-" ^ wd; "ef-passiontide-2-" ^ wd ] + | Some wd -> + (* Critical 1, fix round 1 (coordinator review): the original + version of this table widened "ef-passiontide-0-" into + BOTH "ef-passiontide-1-" (Passion week) AND + "ef-passiontide-2-" (HOLY WEEK, including the entire + Sacred Triduum) on the strength of lectio's own citation being + byte-identical between the two weeks. That only proves lectio + CONFLATES the two weeks -- it has no Holy Week propers of its + own -- not that the Missal does: Holy Monday's real Mass + (docs/research/scan1.txt, "Feria II Hebdomadae sanctae, I + classis") is Isai. 50,5-10 / Io. 12,1-9, nothing like + Passion-week Monday's Ionae 3,1-10 / Io. 7,32-39 that the widen + was putting there, and Holy Thursday's is 1 Cor. 11,20-32 / Io. + 13,1-15 -- the Mass of the Lord's Supper reading Passion + Sunday's own ferial Mass was the actual defect, not merely a + citation nicety. Narrowed to Passion week ONLY; Holy Week's own + citations (Monday, Tuesday, Thursday, Saturday) are + hand-authored below, [holy_week_entries] -- see its own comment + for which two days of Holy Week (Wednesday, Good Friday) are + deliberately NOT included and why. *) + [ "ef-passiontide-1-" ^ wd ] | None -> [ name ]) let convert sec = @@ -146,12 +194,122 @@ let convert sec = data/ef/adjustments.sexp's own RG 110 companion and Major Litanies `Add` directives already use for a genuine upstream-source gap this generator's own single-ini design cannot reach on its own. *) +let slug_or_die name = match Slug.of_string name with Ok s -> s | Error e -> die "%s" e + +let pair ~first ~gospel = + [ { Citation.part = Citation.First; reference = first }; + { Citation.part = Citation.Gospel; reference = gospel } ] + let vigil_entries = - [ ( (match Slug.of_string "ef-nativity-vigil" with Ok s -> s | Error e -> die "%s" e), - [ { Citation.part = Citation.First; reference = "Rom 1:1-6" }; - { Citation.part = Citation.Gospel; reference = "Matt 1:18-21" } ] ) + [ (slug_or_die "ef-nativity-vigil", pair ~first:"Rom 1:1-6" ~gospel:"Matt 1:18-21") ] + +(* Critical 1, fix round 1 (coordinator review): Holy Week's own Mass + propers, hand-authored the same discipline [vigil_entries] above + already uses for a genuine upstream-source gap -- lectio's own ini has + no Holy Week data at all (see [colitur_keys]'s own Passiontide comment + for the full account of what it has instead). Every citation below is + Missal-verified TWICE, independently (docs/research/scan1.txt AND + scan2.txt, the two different printings/scans Task 6 also cross-checked + between): + + - [ef-passiontide-2-monday] (Holy Monday, "Feria II Hebdomadae sanctae, + I classis"): Isai. 50,5-10 / Io. 12,1-9 -- scan1.txt "Lectio Isaiae + Prophetae... Isai. 50, 5-10" + "Sequentia... secundum Ioannem. Io. 12, + 1-9"; scan2.txt corroborates both citations word for word. + - [ef-passiontide-2-tuesday] (Holy Tuesday, "Feria III Hebdomadae + sanctae, I classis"): Ier. 11,18-20 / the Passion according to Mark, + 14,32-72;15,1-46 -- scan1.txt "Lectio Ieremiae Prophetae... Ier. 11, + 18-20" + "Evangelium Passionis et Mortis Domini secundum Marcum. + 14,32-72; 15,1-46"; scan2.txt corroborates both. + - [ef-passiontide-2-thursday] (Holy Thursday, "Feria V in Cena Domini"): + 1 Cor. 11,20-32 / Io. 13,1-15 -- scan1.txt "Lectio Epistolae beati + Pauli Apostoli ad Corinthios... 1 Cor. 11, 20-32" + "Sequentia... + secundum Ioannem... Io. 13,1-15"; scan2.txt corroborates both. + - [ef-passiontide-2-saturday] (Holy Saturday, Missa Vigiliae Paschalis' + own Epistle+Gospel -- the actual Mass, distinct from the earlier + prophecies, explicitly labelled "Lectio EPISTOLAE"): Col. 3,1-4 / + Matt. 28,1-7 -- scan1.txt "Lectio Epistolae beati Pauli Apostoli ad + Colossenses... Col. 3,1-4" + "Sequentia... secundum Matthaeum. Mt. + 28,1-7"; scan2.txt corroborates both. + + DELIBERATELY NOT INCLUDED, and recorded here rather than guessed: + + - [ef-passiontide-2-wednesday] (Holy Wednesday, "Feria IV Hebdomadae + sanctae"): TWO Old Testament lessons (Isai. 62,11;63,1-7, then Isai. + 53,1-12), neither labelled "Epistola", before the Passion according + to Luke (22,39-71;23,1-53) -- no single reading occupies the + "Epistle" position this schema's [First]/[Gospel] pair assumes every + other entry in this file has, unlike Holy Saturday's genuinely + labelled Epistle above. + - [ef-passiontide-2-friday] (Good Friday, "Feria VI in Passione et + Morte Domini"): not even a Mass ("Solemnis Actio liturgica"), with + TWO peer lessons (Osee 6,1-6, then Exodus 12,1-11), again neither + labelled "Epistola", directly into the Passion according to John + (18,1-40;19,1-42). + + Forcing either into a single First/Gospel pair would be an editorial + choice this generator has no textual warrant to make on its own -- + unlike Holy Saturday, where the Mass's own Epistle is explicitly + labelled and distinct from its own preceding prophecies. Left absent: + data/ef/expected-divergences.sexp's own Layer C entry records what this + means for the differential (colitur emits [], an honest absence, not a + guess). *) +let holy_week_entries = + [ (slug_or_die "ef-passiontide-2-monday", pair ~first:"Isai. 50, 5-10" ~gospel:"Io. 12, 1-9"); + (slug_or_die "ef-passiontide-2-tuesday", + pair ~first:"Ier. 11, 18-20" ~gospel:"Mark 14, 32-72; 15, 1-46"); + (slug_or_die "ef-passiontide-2-thursday", pair ~first:"1 Cor. 11, 20-32" ~gospel:"Io. 13, 1-15"); + (slug_or_die "ef-passiontide-2-saturday", pair ~first:"Col. 3, 1-4" ~gospel:"Matt. 28, 1-7") ] +(* Important 3(a), fix round 1 (coordinator review): the fixed Nativity- + Octave days (RG 91 entry 17, 29-31 December, colitur's own + [ef-nativity-octave-day-{5,6,7}]) have a DIRECT formulary in the + Missal, not merely a resolvable-by-walk-back gap -- "Diebus infra + octavam Nativitatis Domini, II classis": Tit. 3,4-7 / Luc. 2,15-20 + (docs/research/scan1.txt:6281-6329, scan2.txt:6900-6960, both word for + word), and each specific date's own rubric points straight at it + ("Die 29 decembris... Missa Puer natus est nobis, ut supra [28]", + scan1.txt, repeated verbatim at 30 and 31 December). This is the SAME + shape [vigil_entries] above already uses (one direct entry, both + scans), not the Task-6-sized verification this task's own report + scoped Task 8 away from -- confirmed, not merely asserted, since a + single Missal heading answers it completely. + + REJECTED, and recorded so it is not re-attempted: the FIRST guess tried + here gave these three slugs [ef-holy-name-sunday]'s own citation + instead (Gal. 4,1-7 / Luc. 2,33-40, "Dominica infra octavam Nativitatis + Domini" -- a DIFFERENT heading, for the Sunday specifically, not the + weekdays). Measured against the real fixture and reverted: lectio's own + citation for these dates is not uniform across years (2005-12-29 reads + Christmas Day's own Mass, Heb 1:1-12/John 1:1-14, since 25 December + 2005 was itself a Sunday that year; 2006-12-29 reads Advent IV's, + 1 Cor. 4:1-5/Luke 3:1-6) -- neither matches "Diebus infra octavam"'s + own formulary, because in BOTH those years the civil date landed on an + ordinary WEEKDAY within the octave, not the (different) Sunday the + first guess's citation was actually for. This entry is right for the + weekday case precisely because it is sourced from the weekday's own + heading, not the Sunday's. + + IMPORTANT CAVEAT, carried into data/ef/expected-divergences.sexp's own + C6 entry, not fixed here: colitur's OWN [Temporal_ef] assigns + [ef-nativity-octave-day-N] to BOTH an ordinary weekday within 29-31 + December AND a Sunday landing there, undifferentiated at the temporal- + slug level. RG 69 ("De dominica infra octavam Nativitatis Domini... + semper fit Officium... nisi dominica incidat in festum I classis") is + unconditional -- the Sunday's own distinct Office (RG 91 places II-class + Sundays above days within the octave) should be observed instead of the + weekday placeholder whenever 29-31 December IS a Sunday. This entry + therefore gives the CORRECT citation for the majority (weekday) case + and the WRONG one on the years the civil date is itself a Sunday -- a + pre-existing [Temporal_ef] defect this generator cannot fix (it has no + day-of-week logic of its own to add), out of this task's own scope, and + the same RG 67/69 gap data/ef/expected-divergences-missalemeum.sexp's + own M11 already tracks from a different differential layer. *) +let nativity_octave_entries = + let cs = pair ~first:"Tit. 3, 4-7" ~gospel:"Luc. 2, 15-20" in + List.map (fun n -> (slug_or_die (Printf.sprintf "ef-nativity-octave-day-%d" n), cs)) [ 5; 6; 7 ] + (* One more colitur-only slug, DERIVED from [ef-holy-name-sunday]'s own citations (just translated above) rather than a second hand-typed copy of the same text: [ef-holy-name] is RG 17(a)'s own fallback ("secus die @@ -165,33 +323,11 @@ let vigil_entries = said in place of, not alongside, whatever ferial reading a bare fallback would otherwise find. - REJECTED, and recorded so it is not re-attempted: giving - [ef-nativity-octave-day-{5,6,7}] (29-31 December, RG 91 entry 17) this - SAME derived value too. Tried, then measured against the real - differential and REVERTED -- it is wrong. lectio's own citation for - those three dates is NOT uniform across years (confirmed against the - fixture: 2005-12-29 reads "Heb 1:1-12"/"John 1:1-14", 2006-12-29 reads - "1 Cor. 4:1-5"/"Luke 3:1-6" -- two different Masses, not one). Read - against the Missal directly (docs/research/scan1.txt, "Dominica infra - octavam Nativitatis Domini, II classis": Epistle "Gal. 4, 1-7", Gospel - "Luc. 2, 33-40" -- the SAME pair as Holy Name Sunday, corroborating the - guess would have been right ONLY for that specific case): the two - observed fixture values are each the PREVIOUS true civil Sunday's OWN - Mass -- Christmas Day itself (25 Dec 2005 was a Sunday that year: - "In Nativitate Domini... Missa in die", Heb 1:1-12/John 1:1-14) or, in - a year with no Sunday between Christmas and New Year at all, all the - way back to Advent IV (24 Dec 2006 was a Sunday; 1 Cor. 4:1-5/Luke - 3:1-6 is that Mass's own citation, already in [ef-advent-sunday-4] - above). colitur's OWN existing step 3 ([Lectionary_ef.readings], NOT - this generator) already walks back to the true PRECEDING CIVIL Sunday - regardless of season and would find EXACTLY these same answers on its - own -- a hard-wired entry here would have SHORT-CIRCUITED that correct, - already-general mechanism with one wrong, uniform guess. Left absent - deliberately: these three dates stay whatever step 3 resolves through - [ef-advent-sunday-4]/[ef-holy-name-sunday] et al, or [] on a date whose - own preceding Sunday has no entry either (data/ef/expected- - divergences.sexp's own C21 entry records what remains, if anything, - after that). *) + [ef-nativity-octave-day-{5,6,7}] does NOT reuse this value -- a first + attempt tried exactly that and was measured wrong and reverted; see + [nativity_octave_entries] above for the correct, DIFFERENT, directly + Missal-sourced formulary and the full account of why the two headings + differ. *) let derived_entries entries = let holy_name_sunday_citations = match @@ -211,12 +347,90 @@ let sha256 path = ignore (Unix.close_process_in ic); List.hd (String.split_on_char ' ' line) +(* Critical 2, fix round 1 (coordinator review): "fix the class, not just + the instance". [colitur_keys] is a hand-maintained table with no + reality check of its own -- exactly how the Lent Ember mismatch + survived the first pass (both engines independently fall through to + the same wrong answer there, so even the differential could not see + it). This sweeps {!Rite_ef.Temporal_ef.temporal} directly over a real + civil-year range -- no sanctoral layer, no lectionary, no circularity + with the file this tool generates -- and collects every DISTINCT office + slug it ever actually produces. [assert_reachable] then requires every + key this generator is about to EMIT to be a member of that set (an + emitted key that is not a real Temporal_ef slug is dead data, + unreachable by any caller -- exactly the shape both Critical findings + had), dying loudly and naming every offender if not. Separately, + informational only, it prints every real Temporal_ef slug that has NO + entry in the final table -- not an error (most such gaps are the + correctly-unproper ferias step 3 already resolves, task-5-report.md's + own "297 of 304" measurement), but a standing audit log a human reader + can check against that same report rather than trusting silence. + + 1583 is deliberately NOT the sweep's start: the full 1583-9999 domain is + the KERNEL's own contract, not this rite-specific bootstrap tool's -- + sweeping the differential's own window (2005-2050), widened by one year + on each side for step-3 preceding/following-Sunday edge cases, is + enough to enumerate every DISTINCT slug FAMILY (season/week/weekday + combinations recur every year; only which YEAR exhibits a given + alignment changes, e.g. how many Sundays fall in Time after + Epiphany) -- confirmed against task-5-report.md's own domain-wide + "412 distinct temporal slugs" figure: this sweep alone already finds + all 412. *) +let reachable_temporal_slugs () = + let tbl = Hashtbl.create 512 in + let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> die "%s" e in + for y = 2004 to 2051 do + let d = ref (mk y 1 1) in + let stop = mk y 12 31 in + while Date.compare !d stop <= 0 do + let t = Rite_ef.Temporal_ef.temporal !d in + Hashtbl.replace tbl (Slug.to_string t.Temporal.office.Celebration.slug) true; + d := Date.add_days !d 1 + done + done; + tbl + +let assert_reachable entries = + let reachable = reachable_temporal_slugs () in + let dead = + List.filter (fun (s, _) -> not (Hashtbl.mem reachable (Slug.to_string s))) entries + in + (match dead with + | [] -> () + | _ -> + List.iter + (fun (s, _) -> + Printf.eprintf + "bootstrap_lectionary: DEAD KEY -- %S is not a real Temporal_ef slug over any civil \ + day 2004-2051, so nothing can ever look it up\n" + (Slug.to_string s)) + dead; + die "%d emitted key(s) are unreachable (see above) -- fix colitur_keys/vigil_entries/\ + holy_week_entries/nativity_octave_entries/derived_entries, never delete this check" + (List.length dead)); + let emitted = Hashtbl.create 512 in + List.iter (fun (s, _) -> Hashtbl.replace emitted (Slug.to_string s) true) entries; + let uncovered = + Hashtbl.fold (fun s _ acc -> if Hashtbl.mem emitted s then acc else s :: acc) reachable [] + |> List.sort compare + in + Printf.eprintf + "bootstrap_lectionary: %d of %d real Temporal_ef slugs (2004-2051) have no lectionary entry \ + (informational -- most resolve correctly via step 3's ferial resumption; see \ + task-5-report.md):\n" + (List.length uncovered) (Hashtbl.length reachable); + List.iter (fun s -> Printf.eprintf " %s\n" s) uncovered + let () = let src = if Array.length Sys.argv > 1 then Sys.argv.(1) else default_source in let dst = if Array.length Sys.argv > 2 then Sys.argv.(2) else default_dest in let secs = parse_ini src in let translated = List.concat_map convert secs in - let entries = translated @ vigil_entries @ derived_entries translated in + let entries = + translated @ vigil_entries @ holy_week_entries @ nativity_octave_entries + @ derived_entries translated + in + assert_reachable entries; let lect = match Lectionary.of_entries entries with | Ok l -> l @@ -227,18 +441,25 @@ let () = "; data/ef/lectionary.sexp -- EF (1962) temporal lectionary (Epistle +\n\ ; Gospel citations, never scripture text), bootstrapped from lectio.\n\ ; Generator: tools/bootstrap_lectionary.ml -- do not hand-edit; re-run the\n\ - ; generator against a newer lectio and commit the diff instead.\n\ + ; generator against a newer lectio and commit the diff instead. Every\n\ + ; emitted key is asserted, at generation time, to be a slug\n\ + ; Rite_ef.Temporal_ef actually computes (see this generator's own\n\ + ; [assert_reachable]) -- a dead key cannot ship silently again.\n\ ;\n\ ; Source: %s\n\ ; SHA-256: %s\n\ ; %d entries (%d ini sections translated/widened into colitur's own\n\ ; Temporal_ef vocabulary via [colitur_keys]; %d hand-authored from a\n\ - ; second source file [vigil_entries]; %d derived from an already-\n\ + ; second source file [vigil_entries]; %d hand-authored directly from\n\ + ; the Missal [holy_week_entries]; %d hand-authored directly from the\n\ + ; Missal [nativity_octave_entries]; %d derived from an already-\n\ ; translated entry above rather than re-typed [derived_entries] --\n\ - ; see this generator's own comments on all three). Regenerate with:\n\ + ; see this generator's own comments on all five). Regenerate with:\n\ ; eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- %s %s\n" src (sha256 src) (List.length entries) (List.length secs) (List.length vigil_entries) - (List.length entries - List.length translated - List.length vigil_entries) + (List.length holy_week_entries) (List.length nativity_octave_entries) + (List.length entries - List.length translated - List.length vigil_entries + - List.length holy_week_entries - List.length nativity_octave_entries) src dst; Sexplib.Sexp.output_hum oc (Lectionary.sexp_of_t lect); output_char oc '\n'; diff --git a/tools/dune b/tools/dune index fa6943d..96b6b36 100644 --- a/tools/dune +++ b/tools/dune @@ -13,6 +13,12 @@ ; Run via `dune exec tools/bootstrap_lectionary.exe -- `. ; Same `unix` usage as bootstrap_sanctoral above: shells out to `sha256sum` ; for the provenance header only, never a new opam dependency. +; +; `rite_ef` added (task 8 fix round 1, coordinator review, Critical 2): +; the generator's own [assert_reachable] sweeps Rite_ef.Temporal_ef.temporal +; directly to catch a translation table entry that names a slug colitur +; never actually computes -- no new dependency, bootstrap_sanctoral above +; already links this same library for the identical "code, not data" reason. (executable (name bootstrap_lectionary) - (libraries colitur_kernel unix sexplib)) + (libraries colitur_kernel rite_ef unix sexplib)) -- cgit v1.3