From 08c18dd43f0fb891806df1077cdf1bab35a59341 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 13:19:40 +0200 Subject: docs: close/narrow the Triduum and Litanies open items in CLAUDE.md Item 5: the Sacred Triduum's own identity is RESOLVED (RG 91 entry 2), full 1583-9999 blast radius measured at zero differing CLI lines -- the change is invisible to layers 2-4, visible only to the two golden/ unit tests built for it. Major Litanies (RG 80) and Rogation Wednesday's own commemoration (RG 87-89) are established from a photographic scan and fully cited, but deliberately not built: both need a rite-computed, Easter-relative, non-competing commemoration candidate -- a kind of thing Precedence.resolve's architecture has no channel for. Measured, not assumed: RG 80's own transfer clause triggers in 194 of 8417 domain years (~2.3%), so building only the ordinary case would trade a known, understood gap for a new wrong answer in those years. Renumbers the trailing three architectural-debt items (5-7 -> 6-8) to make room. --- CLAUDE.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 9 deletions(-) (limited to 'CLAUDE.md') diff --git a/CLAUDE.md b/CLAUDE.md index b809f00..f1a673f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -449,18 +449,18 @@ nowhere in git (`docs/` is gitignored). failure mode, proven by fabricating a second companion and watching the whole suite stay green pre-fix). -5. **`Record` and `Liturgical_day` both claim to be "the single stable output +6. **`Record` and `Liturgical_day` both claim to be "the single stable output schema".** `Record` cannot express what the engine now computes (no observed celebration, no commemorations, no transfers), has **no test file**, and is used only by the legacy `colitur temporal` path — `day` hand-formats instead. Plan 2's carried item (add a `cycle` field for OF's Sunday A/B/C and weekday I/II) is still open and now costlier: it must pass through `Temporal.t`, which is embedded in the sexp-derived `Liturgical_day.t`. -6. **`Temporal.RITE` and `Rite.t` are two competing abstractions.** The module +7. **`Temporal.RITE` and `Rite.t` are two competing abstractions.** The module type still exists and `temporal_ef` still satisfies it, but it carries none of `rules`, `anchors`, `season_runs`, `transfer_target` — satisfying it now proves almost nothing. `Rite.t` is the load-bearing one. -7. **EF-shaped things still in "rite-agnostic" kernel code**: `validate.ml` +8. **EF-shaped things still in "rite-agnostic" kernel code**: `validate.ml` hardcodes Sunday as the week start; `Liturgical_day.transferred_in` is an `option` justified by RG 96; `Precedence.privilege` is defined by RG 111; `Repose` is EF vocabulary emitted by nothing. Each is one field short of the @@ -474,12 +474,77 @@ Boniface 14 May, Evaristus, Theodore); and `romanus`, which should not exist on 9 August. lectio's ini is **generated from missalemeum**, so the two are one lineage, not two independent sources. -**Unbuilt, recorded**: the Sacred Triduum has no proper office (it uses ordinary -Passiontide ferial slugs); RG 91 entry 27's BVM Saturday Office; Major Litanies -(25 April); the Rogation-Wednesday commemoration; RG 112(b)/(c)/(d) (only (a) -has a live witness this codebase's data can construct). Allow-list entries M11 -and M13 are `verdict open` by design. Holy Name of Jesus (RG 17(a)) and RG 110 -(inseparable Peter/Paul commemorations) are **RESOLVED — see item 4 above.** +**Unbuilt, recorded**: RG 91 entry 27's BVM Saturday Office; RG 112(b)/(c)/(d) +(only (a) has a live witness this codebase's data can construct). Allow-list +entries M11 and M13 are `verdict open` by design. Holy Name of Jesus (RG 17(a)) +and RG 110 (inseparable Peter/Paul commemorations) are **RESOLVED — see item 4 +above.** The Sacred Triduum's own identity is **RESOLVED — see item 5 below.** +Major Litanies (25 April) and the Rogation-Wednesday commemoration are +**NARROWED, still unbuilt — see item 5 below**: both rules are now +scan-verified and register-cited, but neither is built, because both need the +SAME missing machinery (a rite-computed, Easter-relative, non-competing +commemoration candidate — no channel in `Precedence.resolve`/`calendar.ml` +constructs one; sanctoral candidates are Fixed-date only, the day's temporal +candidate is exactly one and already spoken for). + +5. **The Sacred Triduum (RG 91 entry 2) — RESOLVED (2026-08-13, + `ef-triduum-litanies` task); Major Litanies (RG 80) and Rogation + Wednesday's own commemoration (RG 87-89) — established from a + photographic scan, deliberately NOT built.** Holy Thursday/Good + Friday/Holy Saturday kept their existing slugs + (`ef-passiontide-2-{thursday,friday,saturday}` — RG 91 entry 2 is + identified structurally by `Precedence_ef.band`, off rank and Easter + offset, never off the slug) and gained `Celebration.names` (Latin, both + photographic scans, corroborated by the electronic transcription's own + table-of-contents listing at the identical headings: "Feria V in Cena + Domini", "Feria VI in Passione et Morte Domini", "Sabbato Sancto") and + `subject = Lord` (verified safe: RG 112(a) only fires when both sides of + an occurrence are `Lord`, and no `Lord`-subject sanctoral entry has a + fixed date inside Holy Week's own movable range). **Full 1583-9999 + blast radius, measured (`git archive` pre- vs post-change, `colitur day` + CLI output diffed): ZERO differing lines anywhere in the domain** — the + CLI prints neither `names` nor `subject`, so this whole change is + invisible to the differential (layer 3), the oracle (layer 4), and even + the exhaustive property sweep (layer 2); only `test_golden.ml`'s + `describe` (widened this task to add a `name_la` field, the same lesson + its own `subject` field was added for) and one new `test_temporal_ef.ml` + unit test see it at all. Mutation-tested: reverting `temporal_ef.ml` + alone reddens 6 tests across those two files. + + Major Litanies (25 April, RG 80) and Rogation Wednesday's own + commemoration (RG 87-89, Minor Litanies) were both investigated to the + same depth and **both scan-verified and fully register-cited, but + deliberately not built**: RG 80's own transfer clause (25 April moving to + the following Tuesday whenever it coincides with Easter Sunday or Easter + Monday — **194 of 8,417 domain years, ≈2.3%, measured**, not assumed + rare) and Rogation Wednesday's own commemoration (RG 88/89, competing + only when the Ascension Vigil already occupies the day) both need a + commemoration candidate keyed to a MOVABLE, Easter-relative date that + is not the day's own temporal office — a kind of thing + `Precedence.resolve`'s architecture has no channel for: sanctoral + candidates come exclusively from `Layer.on_date`'s Fixed-`Date_spec` + lookup (`date_spec.ml`'s own header: Easter-relative forms "arrive with + the OF sanctoral" — not yet), and the day's temporal candidate is + exactly one, already spoken for. RG 110's own `rg110_additions` + mechanism (item 4 above) looks like a precedent but is not one: it only + ever RE-INCLUDES a candidate that already exists elsewhere in that + date's own candidate pool (both Peter and Paul are ordinary Fixed-date + sanctoral entries); neither the Major Litanies' Easter+2 target nor + Rogation Wednesday's own Mass commemoration has any such pre-existing + candidate to find. Major Litanies' ORDINARY case (the ≈97.7% of years + RG 80's transfer clause does not trigger) is independently buildable + today with zero new machinery — a `Commemoration_only` `Fixed(4,25)` + sanctoral entry, `Precedence_ef.privilege_of`'s already-present, dead RG + 109(f) branch wired to it — but building only that would leave the + transfer years with a NEW wrong answer (a phantom commemoration on + Easter Sunday/Monday itself, which RG 80 explicitly forbids), not merely + the old, understood gap, so it was not built either. Full reasoning, + including the paths considered and rejected (a kernel signature + extension threading Easter-offset into `disposition`/`admit` closes half + of the Major Litanies problem — suppression — but not the relocation + half, and Rogation Wednesday has no equivalent half-solution at all): the + `ef-triduum-litanies` task report and register (Rogations/§4, and the two + narrowed open items in §6). ## How to work here -- cgit v1.3 From 67855ae3125148116df870bf1a9abeac5b501d5b Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Thu, 13 Aug 2026 13:45:46 +0200 Subject: docs: a name that matched no scan, and a blocker that was not real Two corrections from the fix-round review, both to the record. "Sabbato Sancto" is attested zero times in either photographic scan. The scans print SABBATO SANCTO as the heading (once) and "Sabbato sancto" as the running header (28x and 30x); the shipped casing appears only in the electronic transcription's table of contents -- the source this project's own methodology rule deprecates -- while the comment beside it called the value "both photographic scans, word for word". Recased to the running-header form, which is the convention the other three Latin names in the file already follow, with a source note recording the count. Third source-fidelity slip this week, and the first where the wrong value came from the deprecated source itself. The Major Litanies deferral was recorded with two false blockers. It claimed a kernel signature extension was needed to suppress the feast in transfer years: admit already takes ~temporal and disposition already takes ~winner, both carrying the Easter office on 25 April, so a rite-local slug test does it with zero kernel surface. And it implied Easter Monday is unmarkable: ef-easter-1-monday occurs exactly 8417 times in 8417 years, as reliable as ef-easter-sunday. So the guarded build -- never scored -- is a strict improvement, 8223 years newly correct against 194 unchanged, and "worse than the gap" was true only of the unguarded one. Deferring is still right, for a reason nobody had found: 25 April is St Mark, II class, so under RG 111(c) a privileged Litanies commemoration would displace the day's existing ordinary commemoration in ~97.7% of years -- an unmeasured blast radius through layers 3 and 4. That measurement is the prerequisite. The 194 figure is exact and reproduces. Also corrects a subject-audit list that named five months for six entries, omitting the Precious Blood on 1 July. --- CLAUDE.md | 55 ++++++++++++++++++++++++++++++---------- lib/rites/rite_ef/temporal_ef.ml | 32 +++++++++++++++++++++-- test/test_golden.ml | 6 ++--- test/test_temporal_ef.ml | 6 +++-- 4 files changed, 78 insertions(+), 21 deletions(-) (limited to 'CLAUDE.md') diff --git a/CLAUDE.md b/CLAUDE.md index f1a673f..68cb906 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -531,20 +531,47 @@ candidate is exactly one and already spoken for). date's own candidate pool (both Peter and Paul are ordinary Fixed-date sanctoral entries); neither the Major Litanies' Easter+2 target nor Rogation Wednesday's own Mass commemoration has any such pre-existing - candidate to find. Major Litanies' ORDINARY case (the ≈97.7% of years - RG 80's transfer clause does not trigger) is independently buildable - today with zero new machinery — a `Commemoration_only` `Fixed(4,25)` - sanctoral entry, `Precedence_ef.privilege_of`'s already-present, dead RG - 109(f) branch wired to it — but building only that would leave the - transfer years with a NEW wrong answer (a phantom commemoration on - Easter Sunday/Monday itself, which RG 80 explicitly forbids), not merely - the old, understood gap, so it was not built either. Full reasoning, - including the paths considered and rejected (a kernel signature - extension threading Easter-offset into `disposition`/`admit` closes half - of the Major Litanies problem — suppression — but not the relocation - half, and Rogation Wednesday has no equivalent half-solution at all): the - `ef-triduum-litanies` task report and register (Rogations/§4, and the two - narrowed open items in §6). + candidate to find. + + **The real reason to defer the Major Litanies, corrected by the + fix-round review** — the version above was wrong in two load-bearing + ways and is retracted: + + - It said the suppression half needs "a kernel signature extension + threading Easter-offset into `disposition`/`admit`". It does not. + `admit` **already** takes `~temporal` (added for exactly this class of + question during the RG 16(a) task) and `disposition` already takes + `~winner`; on 25 April in a transfer year both carry the Easter Sunday + or Easter Monday office. A rite-local slug test suppresses it with + **zero** kernel surface. + - It implied Easter Monday is unmarkable. Measured over the whole + domain: `ef-easter-1-monday` occurs **exactly 8 417 times in 8 417 + years** — once every year, never displaced, since it is an I-class + octave day — making it precisely as reliable a marker as + `ef-easter-sunday`. The stated asymmetry between the two trigger + conditions does not exist. + + So the **guarded** build (a `Commemoration_only` `Fixed(4,25)` entry, RG + 109(f) wired, plus slug-based suppression on the two Easter slugs) is a + strict improvement, not a regression: 8 223 years newly correct, 194 + unchanged, zero new wrong answers. "Worse than the recorded gap" was + true only of the *unguarded* build, which is the only option that was + scored. + + Deferring is still right, for a reason nobody had identified: **25 April + is St Mark, II class**, so under **RG 111(c)** a *privileged* Litanies + commemoration (RG 109(f)) would **displace** whatever ordinary + commemoration the day currently carries, in ≈97.7% of years — a live, + unmeasured blast radius straight through layers 3 and 4. That + measurement is the prerequisite, and it makes this its own task rather + than a rider on another. The 194 figure is exact and reproduced twice + (Easter = 25 April in 67 years, Easter Monday = 25 April in 127). + + Full reasoning: the `ef-triduum-litanies` task report and register + (Rogations/§4, and the two narrowed open items in §6). **Rogation + Wednesday remains genuinely blocked** — Easter+38 coincides with the + Ascension Vigil by construction, so there is no `(month, day)` pair a + `Fixed` spec could anchor to and no partial build exists at all. ## How to work here diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 4aa8aea..4b59ff4 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -199,7 +199,9 @@ let holy_name_names = [Lord]-subject entry in data/ef/sanctoral.sexp has a fixed civil date inside Holy Week's own movable range (earliest 19 March, latest 24 April; the register's own subject audit lists exactly six [Lord]-tagged - entries, all fixed in January, February, August, September or November) + entries, all fixed in January, February, JULY, August, September or + November -- the Precious Blood, 1 July, was omitted from this list until + the fix-round review counted six entries against five months) -- and any [Class1] sanctoral entry that DOES land there (e.g. a transferred Annunciation) reaches Precedence_ef.disposition's EARLIER, subject-blind "I class, not a Sunday -> Transfer" branch first @@ -208,13 +210,39 @@ let holy_name_names = sweep (git archive, pre- vs post-change) shows zero [observed]/[commemorations]/[transferred_*] difference traceable to this tag anywhere in the domain -- see the task report. *) +(* SOURCE NOTE on the three literals below (corrected, fix-round review): + each is the Missal's own RUNNING HEADER for that Office, which is the + convention every other Latin name in this file already follows + ([holy_family_sunday], [holy_name_sunday]). Counted across both + photographic scans: + "Feria V in Cena Domini" heading FERIA QUINTA IN CENA + DOMINI; running header 1:1 + "Feria VI in Passione et Morte Domini" heading FERIA SEXTA / IN + PASSIONE ET MORTE DOMINI; + running header 1:1. NOT + "Feria VI Parasceve", which + occurs ZERO times as a title in + either scan -- every occurrence + of Parasceve is inside the + Johannine Passion text. RG + 132(a) writes "feriae VI in + Passione et Morte Domini". + "Sabbato sancto" heading SABBATO SANCTO (1x); + running header "Sabbato sancto" + 28x / 30x. + The last was previously shipped as "Sabbato Sancto", a third casing + attested in NEITHER scan -- it appears only in the electronic + transcription's own table of contents, i.e. the source this project's + methodology rule deprecates -- while the comment called it "both + photographic scans, word for word". Recased to the running-header form + the other names use. *) let triduum_names = function | -3 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Feria V in Cena Domini") ]) | -2 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Feria VI in Passione et Morte Domini") ]) - | -1 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sabbato Sancto") ]) + | -1 -> Some (Colitur_kernel.Names.of_list [ (Colitur_kernel.Lang.of_string_exn "la", "Sabbato sancto") ]) | _ -> None let same a b = Date.compare a b = 0 diff --git a/test/test_golden.ml b/test/test_golden.ml index 68478ec..594ea6b 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -165,7 +165,7 @@ let omitted_has (day : (V.season, V.rank) LD.t) slug = let test_easter_extreme_1598 () = check ~msg:"1598-03-21 Holy Saturday: still Passiontide, violet (RG 128)" 1598 3 21 "1598-03-21 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet subject=lord \ - name_la=Sabbato Sancto comms=[] in=- out=[]"; + name_la=Sabbato sancto comms=[] in=- out=[]"; check ~msg:"1598-03-22 earliest possible Easter (Gauss-verified): Paschaltide begins, white, class-1" 1598 3 22 "1598-03-22 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ @@ -177,7 +177,7 @@ let test_easter_extreme_1598 () = let test_easter_extreme_1666 () = check ~msg:"1666-04-24 Holy Saturday: still Passiontide, violet (RG 128)" 1666 4 24 "1666-04-24 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet subject=lord \ - name_la=Sabbato Sancto comms=[] in=- out=[]"; + name_la=Sabbato sancto comms=[] in=- out=[]"; check ~msg:"1666-04-25 latest possible Easter (Gauss-verified): Paschaltide begins, white, class-1" 1666 4 25 "1666-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ @@ -206,7 +206,7 @@ let test_easter_extreme_1666 () = let test_easter_extreme_2038_late_modern () = check ~msg:"2038-04-24 Holy Saturday: still Passiontide, violet (RG 128)" 2038 4 24 "2038-04-24 saturday season=passiontide week=2 slug=ef-passiontide-2-saturday rank=class-1 colour=violet subject=lord \ - name_la=Sabbato Sancto comms=[] in=- out=[]"; + name_la=Sabbato sancto comms=[] in=- out=[]"; check ~msg:"2038-04-25 late-modern instance of the latest possible Easter (Gauss-verified)" 2038 4 25 "2038-04-25 sunday season=paschaltide week=1 slug=ef-easter-sunday rank=class-1 colour=white subject=temporal name_la=- comms=[] in=- \ out=[]" diff --git a/test/test_temporal_ef.ml b/test/test_temporal_ef.ml index a979700..5394173 100644 --- a/test/test_temporal_ef.ml +++ b/test/test_temporal_ef.ml @@ -764,7 +764,9 @@ let test_holy_name_fallback_anchor_present_and_erosion_is_caught () = sanctoral entry anywhere in data/ef/sanctoral.sexp has a FIXED civil date inside Holy Week's own movable range (earliest 19 March, latest 24 April -- the register's own subject audit lists exactly six [Lord]-tagged - entries, all fixed in January, February, August, September or November); + entries, all fixed in January, February, JULY, August, September or + November -- the Precious Blood, 1 July, was omitted from this list until + the fix-round review counted six entries against five months); any [Class1] sanctoral entry that DOES land there (e.g. a transferred Annunciation) reaches {!Rite_ef.Precedence_ef.disposition}'s EARLIER, subject-blind "I class, not a Sunday -> Transfer" branch first, so RG @@ -792,7 +794,7 @@ let test_sacred_triduum_identity () = Alcotest.(check bool) "2026-04-04 Holy Saturday: subject Lord" true (subject_of (d 2026 4 4) = Sub.Lord); Alcotest.(check (option string)) "2026-04-04 Holy Saturday: Latin name, both photographic scans verbatim" - (Some "Sabbato Sancto") (name_la_of (d 2026 4 4)); + (Some "Sabbato sancto") (name_la_of (d 2026 4 4)); (* Neighbouring days carry no name/subject override -- the tag is exactly three days wide, not the whole Passiontide-2 week. *) Alcotest.(check (option string)) "2026-04-01 (Wednesday of Holy Week): no Latin name" -- cgit v1.3