aboutsummaryrefslogtreecommitdiff
path: root/tools
Commit message (Collapse)AuthorAgeFilesLines
* test(differential): fix round 2 -- Good Friday, Passion Tuesday, stale headerLukasz Kasprzak2026-08-151-57/+150
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Critical, and worse than what it replaced: removing a lectionary key does not mean "no reading" to Lectionary_ef.readings -- it means step 3 silently resumes the preceding Sunday. Good Friday, which has no Mass at all, was emitting Palm Sunday's own Epistle and Passion narrative every year; Holy Wednesday had the same defect. Fixed by authoring both days fully: the Gospel was always unambiguous and is now always authored; the Epistle uses the same "last lesson before the Gospel" convention this file's own Lenten Ember Wednesday entry already established for an identical two-peer-lesson shape, applied for consistency rather than invented to close this gap. C24 is retired, folded into a widened C23 covering all six Holy Week days. Important: Passion Tuesday was shipping Holy Tuesday's Mass, a defect the previous round's own footnote had found and left unrecorded. Fixed at the source -- colitur_keys now excludes lectio's own (wrong) ini section for this one weekday rather than translating it; a new passion_tuesday_entry hand-authors the real Missal citation (Dan 14:27, 28-42 / John 7:1-13, both scans). New entry C26 records the resulting divergence, 43 of 46 years, traced (not merely counted) to three years where a competing I-class feast impedes the day entirely. Important: the allow-list header was stale one level up, still naming and restating the diagnosis of an id (C21) retired in the previous round. Rewritten in full rather than layered with another correction. Minors: all three Lent Ember values now pinned (only Wednesday was before); C22's citation corrected to attribute the date-anchor text and the rank text to their own separate sources; assert_reachable's own comment no longer overclaims what it can catch; C25's falsified reasoning replaced with the directly observed fact (a first replacement derivation was itself found wrong on a second reading and is recorded as rejected, not silently discarded); hand-authored citations standardised to English book names with a colon, matching this file's own dominant convention, with one stated exception where a value must stay byte-identical to lectio's own data rather than be reformatted. dune test: 358/358. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359/359.
* test(differential): fix round 1 -- Holy Week, Lent Ember, RG 69Lukasz Kasprzak2026-08-152-45/+272
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* test(differential): compare reading citations, 16 801 daysLukasz Kasprzak2026-08-151-9/+150
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Layer 3 is NOT blind to the ferial resumption rule, unusually for this project: lectio implements the same rubric independently, so the differential checks colitur's rule against a second implementation rather than against silence. It IS blind to the Commons -- lectio has no such concept -- so chain step 4 rests on layer 4 alone. Divergence triage, 1 532 of 16 801 days differ (15 269 match exactly), 0 unexplained: - A genuine colitur bug found and fixed: data/ef/lectionary.sexp carried lectio's own ini section names verbatim, never translated into the slugs Temporal_ef actually computes for the identical office. Data the bootstrap already held (Passiontide both weeks, the Pentecost Ember days, Holy Name Sunday) was unreachable by chain step 2, silently falling through to a wrong step-3 answer -- invisible until citation content became comparable. tools/bootstrap_lectionary.ml gained colitur_keys (rename/widen), a hand-authored ef-nativity-vigil entry cross-sourced from lectio's own sanctoral calendar and corroborated by colitur's own (overlay-suppressed) sanctoral bootstrap of the same source, and a derived ef-holy-name entry (RG 17(a)'s single Mass heading covers both the Sunday and 2-January shapes). One guess (giving the fixed Nativity-Octave days Holy Name Sunday's own citation uniformly) was tried and reverted once measured against the fixture -- Missal-verified to be right only for the genuine "Sunday within the Octave" case, wrong whenever the true preceding Sunday is Christmas Day or Advent IV instead. - 10 existing/widened Layer C entries (C1/C6/C8/C14/C15/C16/C17/C18 -- C6 and C17 narrowed with the removed population accounted for, not silently re-absorbed) plus two new ones: C19 (the already-adjudicated Time-after-Epiphany week-numbering offset, now visible in citation content), C20 (lectio computes no reading at all for the seven readingless saints Task 6 gave a Common or proper -- colitur is more complete, not wrong). - One entry, C21, is a genuine colitur LOGIC limitation found but not fixed: Lectionary_ef.readings' step 3 is a single hop to the preceding Sunday, insufficient for a run of colitur-only fixed Christmastide slugs whose own preceding Sunday is itself another entry-less fixed slug. Recorded verdict open (data/ef/expected-divergences-missalemeum.sexp's own established precedent for this shape), not left silently failing -- fully diagnosed in task-8-report.md, recommended as a dedicated follow-up task rather than a hasty fix to a shared, multi-round-reviewed kernel-adjacent function.
* data(ef): carry sanctoral propers on the celebrationLukasz Kasprzak2026-08-151-2/+26
| | | | | | | | | | | | | | Celebration.citations has existed in the type, the .mli and the sexp schema since Plan 2 and has been emitted as () for every entry; the bootstrap now writes the real values for the 208 entries lectio has them for. On the celebration rather than in the lectionary table deliberately: a diocesan overlay adding a saint then carries his readings through the existing overlay algebra, with no second file to edit and no new directive. Asserted both ways -- the 13 January proper is present, and no Commemoration_only entry has readings, because in the EF a commemoration contributes an oration, not a reading.
* data(ef): bootstrap the temporal lectionary from lectioLukasz Kasprzak2026-08-142-0/+113
| | | | | | | 119 entries, Epistle + Gospel only. The generator refuses a section carrying exactly one of the two: an Epistle without a Gospel is malformed and wants investigating, not shipping. SHA-256 of the source INI is in the provenance header, as with sanctoral.sexp.
* tools(bootstrap_sanctoral): fix round 1 (F6) -- stale entry-count commentsLukasz Kasprzak2026-08-121-8/+18
| | | | | | | | | | | Three comments still said "322" after the previous commit's regeneration moved the total to 327: the date-form comment (rewritten to not carry a count at all, since it is about a FORM, not a quantity); the explicit-class comment (327, plus recorded that the 6 entries carrying it changed membership -- 12 September dropped it, 13 January gained it -- even though the count of 6 itself did not move); the name.pl comment (327 total, 5 missing it, 322 carrying it -- was "5 of the source's 322 entries", conflating the new total with the old one).
* tools(bootstrap_sanctoral): name.pl is optional, not requiredLukasz Kasprzak2026-08-121-2/+17
| | | | | | | | | | | | | | | | | lectio's regenerated tridentine-calendar.ini (the fix that corrects the 15-entry rank cluster and adds the 5 missing entries this branch re-bootstraps from) has 5 entries with no name.pl field at all (agnes-secundo, boniface-martyr, eusebius-confessor, evaristus, theodore -- all either newly added or newly un-hidden). parse_names previously required name.pl unconditionally (`field sec "name.pl"`, which dies on absence) -- correct for a source that always had it, wrong now that it legitimately does not. Names.t is an open, per-language assoc list (names.mli) with no rule that every entry must carry every language colitur happens to know about, so requiring pl here was this tool's own invented constraint, not a kernel one. Falls back to an en-only Names.t when pl is absent, never a placeholder string.
* feat(test): oracle layer compares commemoration identity, not just countLukasz Kasprzak2026-08-121-3/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | test_oracle.ml (validation layer 4) used to compare commemoration presence and List.length only -- cardinality, never which candidate is admitted. A reviewer proved this vacuous by reversing admit's dignity sort (the engine admitting the worst commemoration, an outright RG 111 violation) and finding every assertion in this file green across all 730 days. tools/extract_missalemeum_oracle.py now also emits each commemoration's own missalemeum id (field 9, commemoration_ids), parallel to the existing title field; fixture regenerated from the same underlying lectio snapshot (commit d7da4b0, re-verified, first 8 fields byte-identical), read from a scratch copy so ~/git/projects/lectio was never touched. SHA-256 pin and provenance updated. The mapping: colitur identifies a commemoration by slug, missalemeum by an English title string. No hand-built slug<->title table -- colitur's own Celebration.t.names already carries the same English text for every SANCTORAL-origin commemoration (bootstrapped from lectio, verified to match missalemeum's titles character-for-character). This resolves identity for every sanctoral-origin commemoration but cannot resolve a TEMPORAL-origin one (Rite_ef.Temporal_ef never sets a celebration name) -- that case is a new, distinct outcome (Comm_identity_unresolved), counted and allow-listed, never silently skipped: 19 of 227 non-empty-commemoration days over 2026-2027. Two genuine identity mismatches found and adjudicated (new allow-list entries M15-M17, one is the unresolved bucket): M16, 27 March 2026, the already-known unimplemented Seven Sorrows of Passion Friday office (verdict missalemeum); M17, 22 September 2027, the RG 113 same-band tie-break residual (verdict open, per the sibling admit fix). A new corroborating check cross-references missalemeum's own id-embedded rank against colitur's, split by status: Feast-status matches must agree exactly (none reachable in this 2-year window, checked regardless); Commemoration_only-status matches either genuinely agree or fit a known, pre-existing, already-registered convention gap (colitur's bootstrap rank vs missalemeum's own sentinel) -- not a title-matching defect. Perturbation test performed and reverted: reversing admit's dignity- based ordering (compare_precedence's real committed form uses band, not dignity -- this reproduces the historical defect shape directly) turns the oracle layer red: "no differences outside the cited allow-list" fails with an unexplained commemoration-identity-mismatch on 2026-09-23 (colitur admits "St. Thecla" where the rubric and missalemeum both require "St. Linus"). Reverted; diff against the prior commit confirms no residue. CLAUDE.md's "know what each layer cannot see" section updated: the commemoration-identity gap it documented as open is now closed, with the mapping's limits and the RG 113 tie-break finding summarised. 271 -> 273 tests (274 with the exhaustive sweep), all green.
* test: oracle vs missalemeum 2026-2027; audit the sanctoralLukasz Kasprzak2026-08-121-0/+87
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Validation layer 4: an oracle harness against missalemeum (Divinum Officium data), independent of the lectio bootstrap chain colitur's own sanctoral data comes from -- the only layer that can catch an error inherited from that bootstrap, and the only one that can validate commemorations at all (the lectio differential explicitly excludes them, per its own header comment). tools/extract_missalemeum_oracle.py shapes the fixture from lectio's sources/snapshot.tar.gz outside the test (no JSON library in this project's frozen deps, same reasoning test_differential.ml's own fixture already documents). test/fixtures/missalemeum-ef-2026-2027 .txt (730 days, SHA-256 pinned and asserted) + its own .provenance note record exactly how to regenerate it. test_oracle.ml compares three axes the oracle actually supports: rank, colour (SET MEMBERSHIP -- 14 of 730 days carry two colours, e.g. rose+violet on Gaudete/Laetare, which independently vindicates this project's own rose reading against lectio's violet-only one, recorded in the register), and commemoration presence/count. Slug identity is deliberately out of scope (needs a title->slug mapping, the data audit's own business, not the automated comparator's). Of 730 days, 688 matched cleanly outright. The remaining 42 are all named in data/ef/expected-divergences-missalemeum.sexp (14 cited entries, M1-M14): most are genuine primary-source-confirmed findings this task adjudicated and fixed in the two preceding commits (RG 33, RG 109/111, Holy Thursday's colour); the rest are real, cited, deferred feature/data gaps (RG 91 entry 27's BVM-Saturday office, RG 110's inseparable Peter/Paul commemoration, four sanctoral entries missing from lectio's own source) or genuine oracle-side artifacts -- honestly verdicted against whichever side this task's own primary-source research actually backs, never defaulted to colitur. One entry (M13, St Joseph vs the Friday of Passion Week 2027) is verdict open: adjudicated as unresolved after real search effort, not guessed past. The data audit: every sanctoral entry the comparison flagged was hand-checked against the 1962 calendarium, plus a 20-entry deterministic random control sample (seed 20260812) drawn independently of the flagged set. The control sample caught two entries (benedict, frances-rome) marked Commemoration_only in the bootstrapped data when the primary calendarium lists them as plain III-class feasts with their own Office -- traced to lectio's own source, not fixable here, and reported as a signal (10% of a random sample) rather than a blanket claim. Coverage recorded honestly in docs/research/rules-register.md's own three buckets: confirmed by oracle (200/322), confirmed by hand (23/322, 2 of them wrong), unverified (115/322) -- the unverified bucket stated explicitly rather than left implicit. Harness teeth demonstrated and reverted (not committed): a fixture rank/colour edit on a previously-clean day fails both the checksum pin and the no-unexplained-differences assertion independently; an expected_rows drift on the allow-list fails the citation-count assertion. Both captured with their exact failure messages, both reverted before this commit.
* data(ef): bootstrap the 1962 sanctoral from lectioLukasz Kasprzak2026-08-122-0/+245
Convert lectio's tridentine-calendar.ini (322 entries) into data/ef/sanctoral.sexp via a validating OCaml converter, tools/ bootstrap_sanctoral.ml, rather than a hand-written script: every field is built through Slug.of_string, Colour.of_string and Vocab_ef.rank_of_string, so the emitted sexp is valid by construction. Two conversion decisions, both documented rather than buried: - subject defaults to Subject.Saint, overriding Celebration.make's kernel default of Subject.Temporal, for the 316 entries with no explicit class; - rank = commemoration maps to status = Commemoration_only with an inferred Class3 (not a citation -- it is what the 1960 reform reduced most simple feasts from), recorded as an open item in the rules register for the oracle to adjudicate. Every celebration is tagged layer = Precedence_ef.universal_layer, the provenance id RG 91's band classifier reads to tell the universal calendar from proper/indult data. The generated file carries a provenance header: source path, its SHA-256, and the UTC conversion date, so re-bootstrapping against a newer lectio is reproducible and diffable. Output is byte-identical across runs. test/test_sanctoral_ef.ml loads the file through Layer.load and checks the counts independently derived from the source INI (322 entries, 114 Commemoration_only, 12 Class1, no Subject.Temporal, every date resolves in a leap year), plus two named spot-checks against the INI's own text -- one entry with an explicit class field, one commemoration -- so a passing count cannot hide the wrong 322 entries having been converted.