aboutsummaryrefslogtreecommitdiff
path: root/lib/rites
Commit message (Collapse)AuthorAgeFilesLines
* rite(ef): clamp the RG96 search at the domain ceilingLukasz Kasprzak2026-08-122-10/+36
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | search_from could walk up to 400 days past origin before Calendar's own ~start ~stop clamp is ever consulted, and nothing stopped it probing occupant on a date past 31 December 9999 -- occupant chains through the real EF rite's temporal, which calls Computus.gregorian_easter, not total outside 1583..9999 (it Date.makes and failwiths on Error). Not reachable with the shipped sanctoral data alone, but reachable through the project's own primary extension path: an overlay adding an I-class feast on 25 December leaves nothing but Class2 Nativity-octave days for the rest of civil year 9999, so the unguarded search reached 1 January of year 10000 and crashed there with 'computus: year 10000 out of range 1583..9999'. 9999 is an in-range year and the kernel's contract is 'never raises on in-range input'. search_from now also stops, without probing occupant again, once it passes Date's own domain ceiling -- the same 'return a finite date, let Calendar's own out-of-range handling record it, never pretend to have found something admissible' contract the existing step-count guard already follows. Two new tests, both mutation-verified to actually reproduce the crash when the guard is removed (see the task report): a precedence_ef.ml unit test using the real Temporal_ef.temporal as occupant (a synthetic occupant can never discriminate this, since it never calls Computus itself), and a Calendar-level integration test reproducing the exact overlay-based scenario the review found.
* cli: colitur day <year> dumps resolved liturgical daysLukasz Kasprzak2026-08-124-11/+228
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Assembles Rite_ef.context (lib/rites/rite_ef/rite_ef.ml[i]): temporal, anchors and vocab from Temporal_ef; rules from Precedence_ef's band, disposition and admit; season_runs = Vocab_ef.seasons; transfer_target newly implemented here. transfer_target (RG 96): the next following day that is not I or II class, with the Annunciation's own exception (Monday after Low Sunday). Terminates by a structural step bound on its internal search, independent of Calendar's own round guard, which bounds rounds across a year, not one call's walk; documented as an obligation on rite.mli's transfer_target field, which did not previously state it. Fixes the vigil-naming mismatch Task 7's review predicted: the sanctoral bootstrap names its vigils with a vigil-of-X prefix (lectio's own convention), while Precedence_ef's is_vigil only recognised the temporal cycle's own X-vigil suffix. Both are now recognised, fixing RG 91 entries 21/26 and RG 33's vigil omission for the four affected celebrations. Verified by unit test and by mutation-testing the fix (reverting it fails exactly the new rows) and against real output across several years. Suppresses data/ef/sanctoral.sexp's vigil-of-christmas via a new overlay, data/ef/adjustments.sexp: it is the same celebration as the temporal cycle's own ef-nativity-vigil, both dated 24 December. colitur day <year>: one line per civil-year day, temporal and sanctoral fully resolved through Layer, Overlay, Precedence_ef and Calendar -- the first CLI path exercising the whole Plan 3 pipeline against real data. Verified the All Souls transfer chain (Tasks 7-8-11) end to end against real output for both a Sunday year (2025, lands on 3 Nov) and a non-Sunday year (2026, observed directly on 2 Nov).
* rite(ef): an impeded I-class Sunday commemorates, not transfersLukasz Kasprzak2026-08-112-21/+42
| | | | | | | | | | | | | | | | | | | | | | | | | disposition's Class1 branch was unconditional on rank, so an impeded I-class Sunday (Advent/Lent/Passiontide/Low Sunday) currently transferred like a feast. RG 95 (register lines 323, 363) restricts the right of translation to I-class FEASTS -- RG 91's own table lists Sundays as a separate row (entry 6, line 332) from feasts (entries 11-13, lines 337-339) -- and RG 109(a) (line 374) lists "of a Sunday" as a privileged commemoration category, which presupposes an impeded Sunday stays put rather than moving to another day. Excludes is_sunday_slug losers from the Transfer branch so they fall through to the existing Commemorate (privilege_of loser) branch, which already tags them Privileged via RG 109(a) with no further change. Fires on real dates in the 2005-2050 differential window: 24 December on Advent IV in 2023, 2028, 2034, 2045; 8 December on an Advent Sunday in 2024, 2030, 2041. Verified no previously-passing Transfer assertion used a Sunday-shaped loser (grepped test_precedence_ef.ml, test_calendar.ml -- which uses its own synthetic rite unrelated to Precedence_ef.disposition -- test_colitur.ml, test_validate.ml); confirmed by mutation-reverting the fix and observing exactly the one new test fail, nothing else.
* rite(ef): commemoration privilege and admission (RG 108-111)Lukasz Kasprzak2026-08-112-25/+261
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Precedence_ef.privilege_of classifies a commemoration candidate against RG 109's closed list of privileged commemorations (a Sunday; a I-class day; a day within the Octave of the Nativity; a September Ember day; a feria of Advent, Lent or Passiontide; the Major Rogations in Mass), read entirely off the candidate's own rank/slug/origin, no context needed. Major Rogations (f) is left unimplemented rather than guessed: no producer for the Major Litanies exists anywhere in this codebase yet. disposition's two Commemorate sites now call privilege_of instead of Task 8's interim_privilege placeholder, which is removed entirely (binding and .mli export both gone). Precedence_ef.admit applies RG 111's four admission counts, keyed on the observed day's own class and Sunday-ness: a I-class day admits none except one privileged commemoration; a II-class Sunday admits one, but a privileged commemoration due displaces any ordinary one regardless of dignity; any other II-class day admits one by dignity alone, with no such override; III/IV-class days admit at most two by dignity. Ties break on slug, matching Precedence.compare_by, so the admitted set never depends on input order. Every admitted candidate is a value taken unchanged from the input list, never rebuilt, so Precedence.resolve's physical-equality-based dropped/omitted accounting keeps working -- closing a note left open since Task 2. Tests: RG 109 category rows (one per reachable category, plus boundary rows proving Advent/Lent Ember days and Minor Rogations are correctly excluded), RG 111 admission rows checked on slug identity rather than count, an order-independence check, and three end-to-end integration tests proving the admission limit's drop lands in resolution.omitted rather than vanishing.
* rite(ef): fix false justification in impedes_vigil's commentLukasz Kasprzak2026-08-111-7/+11
| | | | | | | | | | | | | | | | Review finding: the comment claimed every RG 91 entry that can outrank a II-class vigil (entry 21) without being a Sunday is I class 'by the table's own structure (entries 1-13)'. False -- entries 14 and 16-20 (Feasts of the Lord II class, universal/proper/indult II-class feasts, days within the Nativity octave) are all Class2, all outrank entry 21, and none is a Sunday. The code was always correct: impedes_vigil implements RG 33's own two named conditions (any Sunday, or a I-class feast) directly, and does not depend on the band table's numeric ordering at all. Reworded to say so, citing the counter-example entries the review named instead of appealing to a table structure that does not guarantee what the old comment claimed. Comment-only change; no logic, signature, or test changes.
* rite(ef): occurrence dispositions (RG 92-95, 33, 94)Lukasz Kasprzak2026-08-112-0/+141
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Precedence_ef.disposition decides the loser's fate in an occurrence: a Commemoration_only celebration is always commemorated (it can never win or transfer); a I- or II-class vigil impeded by any Sunday or a I-class feast is entirely omitted (RG 33), checked before the generic rule below or the Nativity/Pentecost Vigil could wrongly transfer; any other I-class loser transfers (RG 95 -- only I class has the right of translation); everything else is commemorated, with the admit-or-omit decision left to RG 108-111's admission count (Task 9). RG 94 needs no code: resolve always compares a loser against the day's actual winner, never against a departed sibling, so no commemoration can ride along with a transferred feast in this design. This is the branch that completes Task 7's carried All Souls fix: once it loses to an occurring Sunday, its untouched Class1 rank routes it to Transfer via the generic rule, not a special case. Landing on 3 November is Rite.transfer_target's job, not wired up yet. Commemorate carries an interim Precedence.Ordinary privilege pending Task 9's RG 109 implementation, exposed as interim_privilege for that task to replace. Table-driven tests cover each rule, including RG 33's boundary from both sides and a Commemoration_only loser that is also Class1 and vigil-shaped to pin the branch ordering. Mutation-tested: disabling RG 33, either direction of RG 95's rank condition, or the Commemoration_only priority check each fail exactly the rows built to catch them.
* rite(ef): fix entry 8's Sunday exception and entry 14's over-restrictionLukasz Kasprzak2026-08-112-36/+102
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Review of 436ba75 found two calendar defects and one coupling risk. Entry 8 (All Souls) dropped register line 334's own qualifier, "(yields to an occurring Sunday)" -- it returned 8 unconditionally, so on 2 November falling on a Sunday (2025, 2031, 2036, 2042 in the 2005-2050 differential window), All Souls incorrectly outranked and observed over the Sunday. 2 November is always Time_after_pentecost and never coincides with any other entry's own date, so the only rival this exception ever has is an ordinary entry-15 II-class Sunday; on such a Sunday, band now returns one worse than entry 15's own value rather than the literal 8 -- strictly worse, not merely different, since an exact tie would fall to Precedence.resolve's slug tie-break, which for "ef-all-souls" against a Sunday slug would make All Souls win the tie anyway. Entry 8's own rank is untouched, so Task 8's disposition still sees a true I-class candidate to transfer. Entry 14 (Feasts of the Lord, II class) added a universal-layer restriction the register's line 341 does not carry -- contrast entry 16 at line 342, which explicitly says "not of the Lord"; RG 37c (register line 393) also speaks of "II-class feasts of the Lord" with no universal qualifier. Dropped the layer test: a proper or indult feast of the Lord now bands 14, not 19/20. Also exposes vigil_suffix and ember_prefixes from precedence_ef.mli, matching universal_layer/indult_prefix's existing "colitur convention, not an RG citation" treatment -- both were previously private literals duplicated in the test file, so a rename of temporal_ef.ml's slug format could have drifted silently past both sides agreeing with each other. Two test rows now build their candidate from Temporal_ef.temporal's own real output (entry 18's Lent Ember day, entry 21's Ascension Vigil) instead of a hand-typed slug, closing that specific coupling. Adds the three cheap rows review named as closing the remaining unpinned guards (a temporal-origin Class1 candidate on an unnamed date; a universal Class2 vigil of the Lord; a non-universal Class3 vigil), plus a resolve()-level test proving the Sunday is actually observed over All Souls, not just that band returns the right integer in isolation.
* rite(ef): RG 91 Table of PrecedenceLukasz Kasprzak2026-08-112-0/+208
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Precedence_ef.band transcribes RG 91's 28-entry Table of Precedence (rules-register.md ยง4) for the EF rite: given a day's context and a candidate celebration, returns the table's own entry number, 1-28 (I class 1-13, II class 14-21, III class 22-26, IV class 27-28); lower wins. Every branch carries its entry number and register citation in a comment, checked in the table's own numeric order. Two entries are transcribed as the register states them even though they invert the pattern the rest of the table follows: at III class, 23 (particular calendars) outranks 24 (universal), the reverse of how 11/12 and 14/16/19/20 rank a universal feast ahead of a proper one at I and II class. Sanctoral-origin, layer-decided entries (11-13, 14/16/19/20, 23/24) follow the brief's structural insight: a celebration whose layer is not the universal base is an overlay -- proper, or indult if its layer id also carries the indult prefix. Neither the universal-layer id nor the indult prefix is an RG citation; both are colitur's own data-modelling convention, exposed from the module so whichever task loads the real EF sanctoral overlays can align to them. Vigils (21, 26) are read off the temporal cycle's own -vigil slug suffix rather than gated on origin, since a II/III-class vigil can be either temporal-origin (Ascension, already produced by temporal_ef) or sanctoral-origin (a saint's vigil, no task has loaded yet); Ember days (part of entry 18) are read off temporal_ef's own ember slug prefixes rather than re-derived, since the September anchor is independently flagged there as one of the more contested dates in the calendar. A candidate shape the table has no row for (e.g. a Class1 vigil that is not Nativity or Pentecost, or a Class4 candidate marked as a vigil -- RG 91 has no IV-class vigil either) returns a dedicated unclassified sentinel (max_int) rather than being folded into a same-rank entry it does not belong to. test_precedence_ef.ml is table-driven: one Alcotest.test_case per RG 91 entry (55 rows total, several entries covered by more than one named day so a single missed offset cannot hide behind a passing sibling), each date computed from Computus.gregorian_easter rather than hand-typed, so an arithmetic slip cannot pass by accident.
* rite(ef): fix duplicate slugs and omitted weeks in the temporal cycleLukasz Kasprzak2026-08-112-77/+199
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two correctness bugs in Temporal_ef, both only visible across many years, not a single point assertion: 1. Duplicate slugs within one liturgical year. Christmastide has no numbered weeks, so the ferial fallback's <season>-<week>-<weekday> scheme collapsed every feria in it to literal week "0". Since colitur's Christmastide runs 25 Dec - 13 Jan (RG 72-73, a deliberate divergence from lectio), the same weekday recurs across that span and the keys collided (e.g. ef-christmas-0-saturday on three different dates). Fixed with a dedicated christmastide_feria_slug that splits the span into four sub-stretches, prioritising compatibility with lectio's own keys wherever lectio has one: - 26-28 Dec keeps lectio's existing ef-christmas-0-<weekday>. - 2-5 Jan becomes ef-christmas-1-<weekday> -- lectio collapses this indistinguishably into the same key as the stretch above, so there is nothing to preserve; a colitur-only lectionary gap. - 7-13 Jan splits at the actual first-Sunday-after-Epiphany origin: on/after it, this is genuinely week 1 of Time after Epiphany and takes lectio's own ef-time-after-epiphany-1-<weekday> (which also can't collide with that season's own later week-1 ferias, since it's the same computation). Before it (0-6 days, whenever Epiphany doesn't fall on a Saturday), a first attempt at labelling this "week 1" too, matching a literal reading of lectio's behaviour, was verified empirically (a throwaway sweep of the full 1583..9998 domain) to reproduce duplicates in most years -- so this remainder is its own ef-christmas-2-<weekday>, a further colitur-only gap. Verified with the same throwaway sweep: zero duplicate slugs across 1583..9998 after the fix. 2. named's week field was set by hand on some branches (Passion/Palm Sunday, Easter, Low Sunday, Pentecost and its Vigil, Christ the King) and left at None on others (Ascension and its Vigil, Corpus Christi, Sacred Heart) even though all of them sit inside a numbered season run. named no longer carries a week at all -- temporal now calls week itself for every day, named or not, so "a named day inside a run carries that run's week" holds by construction rather than by remembering to set it on each branch. temporal.mli's week field doc is reworded to state the actual rule. Covering tests: point assertions for all four Christmastide sub-stretches; a QCheck property scanning random years for any duplicate slug within a liturgical year (excluding the deliberate resumed-Sunday reuse); point assertions for the four previously-None days now carrying their week (first to fail without the fix: Ascension Vigil, expected Some 6, got None); a QCheck property asserting temporal's week equals week for every day of the year, replacing a prior property whose fallback made it structurally incapable of detecting an omitted week. Also: cite RG 91 e18 for the September/Advent Ember days matching lectio and flag the Lent/Whitsun Ember and Rogation slugs as colitur-only lectionary gaps inline, matching the existing Nativity-vigil/octave-day convention; cite RG 117/123/127/128/131 for season colours and Gaudete/Laetare rose; drop the unreachable Passiontide arm from the Sunday-rank match (Passiontide has only two Sundays and both are already named above, so no Passiontide Sunday ever reaches that fallback).
* rite(ef): give Pentecost, its Vigil and Christ the King their weekLukasz Kasprzak2026-08-111-3/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | named hardcoded week = None for ef-pentecost-vigil, ef-pentecost and ef-christ-the-king, unlike the five sibling named Sundays (Easter, Low Sunday, Passion Sunday, Palm Sunday, Trinity), which already carry the explicit week number that week computes for the same date. Because these three sit inside a season run rather than at a run boundary, the omission let the week number jump on the following Monday without an intervening Sunday marking the change -- breaking week continuity in every single year in the domain. This was found by the new Validate invariant harness (Task 14), which flagged exactly two failures in every one of the 8416 years 1583..9998, both on the Monday after one of these days. Season contiguity itself was clean across the whole domain; only week numbering was affected. Pentecost and its Vigil are fixed Easter offsets, so they take the literal values (7 and 8) that week already computes for them. Christ the King's date varies by year, so the same expression week evaluates is inlined instead, since week is defined later in the file and cannot be called from named. Added a general property test, prop_named_week_agrees_with_week, checking across 200 random years that wherever named gives an explicit week, it matches what week independently computes for that date -- covering all sibling entries, not just the three fixed here, so the same drift cannot silently reappear in a future addition to named.
* rite(ef): ferias, Ember days, Rogations and the temporal entry pointLukasz Kasprzak2026-08-112-0/+150
| | | | | | | | | | | | | | | | | | | | Ferial ranks per RG 91: I class in Holy Week and the privileged octaves (entries 7, 10), II class for Advent 17-23 and the Ember days (entry 18), III class for Advent to 16 December and Lent/Passiontide (entries 22, 25), IV class per annum (entry 28). Two further divergences from lectio, both from the table: Advent 17-23 are II class, and the Lenten Ember days are II class rather than ordinary Lenten ferias. Rogations (RG 80/87) exist at all, which lectio does not compute. Gaudete and Laetare are rose. Two corrections against the primary source beyond the task brief's draft. Entry 7 (verified against the Latin: "feria IV cinerum et II, III et IV Hebdomadae sanctae") covers only Ash Wednesday and Monday-Wednesday of Holy Week; Thursday-Saturday are the Sacred Triduum, entry 2, cited separately. Rogation Monday and Tuesday keep the ordinary ferial rank of their season (RG 88: "de Litaniis minoribus nihil fit in Officio") rather than a fixed class, since no RG 91 entry ranks them specially. A module-type constraint proves Temporal_ef satisfies the kernel RITE contract.
* rite(ef): pin down floor_div and week/sunday_slug agreementLukasz Kasprzak2026-08-111-14/+18
| | | | | | | | | Task 12 review: add a permanent regression test for floor_div's negative branch (Ash Wednesday, 4 days before the Lent I origin -- the only date in the system where it fires), and stop sunday_slug from independently recomputing the Pentecost-relative week number that week already computes. A cross-check test pins the two together before the refactor and continues to guard against future duplication.
* rite(ef): Sunday slugs, week numbering and the resumed-Sunday tailLukasz Kasprzak2026-08-112-0/+90
| | | | | | | | Week origins are Sundays, so week numbers are constant Sunday-to-Saturday; the origin is season-specific, which is why Validate must not assume a run starts at 1. When Easter is early the surplus Sundays after Pentecost resume the Sundays after Epiphany that Septuagesima cut short, and the last Sunday before Advent always keeps the 24th (Last) Mass.
* rite(ef): fix RG 91 citation gaps in named temporal daysLukasz Kasprzak2026-08-111-8/+17
| | | | | | | | | | | | | | | Circumcision (1 Jan) is the Octave Day of the Nativity, RG 91 entry 5, the same table entry as the Nativity vigil -- not covered by the entries-1-3 group it sat under. Passion Sunday, Palm Sunday and Low Sunday are RG 91 entry 6 (I-class Sundays of Passiontide and Low Sunday); Ash Wednesday is entry 7 (I-class ferias). All five branches now carry their own citation, and the function-level comment states the full coverage instead of a narrower one. Also corrects the Nativity-octave range from RG 64-70 to RG 63-70, matching the register's own citation for the octave rules. Comment-only change; behaviour is unaffected.
* rite(ef): named temporal days, vigils and the Nativity octaveLukasz Kasprzak2026-08-112-0/+58
| | | | | | | | | | The I-class feasts of the Lord (RG 91 entries 1-3), the Nativity, Ascension and Pentecost vigils (RG 28-34), and the days within the Octave of the Nativity (RG 64-70). Christ the King is the last Sunday of October per the 1960 calendar, not the OF's last Sunday before Advent. Rogation Wednesday coincides with the Ascension vigil; with no precedence framework until Plan 3, temporal emits the higher-ranked vigil.
* rite(ef): season boundaries per RG 71-77Lukasz Kasprzak2026-08-112-0/+60
| | | | | | | | Christmas Time runs to 13 January inclusive (RG 72-73) and time after Epiphany opens on 14 January (RG 77), diverging from lectio, which starts time-after-epiphany at 6 January. Holy Saturday stays in Passiontide: the Easter Vigil is a night Mass, and a per-day calendar assigns a date by the season governing its day-hours.
* rite(ef): season and rank vocabulary per RG 71-77 and RG 8Lukasz Kasprzak2026-08-113-0/+96
Eight seasons in canonical liturgical-year order, each carrying its RG citation, and the four classes. season_slug_word is deliberately distinct from season_to_string: slugs are lectionary keys adopted verbatim from lectio, which calls Paschaltide 'easter' and Christmastide 'christmas'.