aboutsummaryrefslogtreecommitdiff
path: root/test/fixtures
Commit message (Collapse)AuthorAgeFilesLines
* test(differential): refresh the lectio fixture -- the lineage invertedLukasz Kasprzak2026-08-182-228/+256
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Every previous refresh of this fixture carried lectio's answers TO colitur, because colitur's data was bootstrapped from lectio. This one carries colitur's answers BACK. lectio has merged eight field corrections colitur argued from the 1962 Missal: ubaldus and didacus promoted from commemoration to III-class feast on the Missal's own calendarium, both August vigils recoloured violet under RG 128, and both Ember Saturdays' readings, which lectio had carrying St Thomas's and St Matthew's Masses respectively. 227 of the 16801 rows changed. Three cited classes went to zero and are CLOSED and REMOVED, citations preserved in the register, the same discipline C2-C5, C7, C9-C13, C21, C22 and C25 already record: C27 40 -> 0 the Advent Ember Saturday's readings C28 40 -> 0 the September Ember Saturday's readings C37 77 -> 0 the two vigil colours C37 is worth noting by name: its own note called the shared lineage "the sharpest demonstration in the project", colitur and lectio and missalemeum all carrying the same two wrong colours. Two of the three now carry the right one. C38 NARROWED rather than closed, and the distinction matters. Its identity half is gone -- lectio ranks both saints class-3 now -- but the same 70 rows still differ on readings, because both saints take their Mass from a Common and lectio has no Commons concept to resolve one. It is gated on diffs = [First_f; Gospel_f] exactly, which is C18's shape. It cannot close without lectio gaining a capability, which is a feature rather than a fix. Net: 24 cited classes to 21, 2252 divergent rows to 2095, agreement 86.6% to 87.5%. The number that matters is not the percentage though -- it is that those 227 days now agree BECAUSE BOTH ENGINES FOLLOW THE MISSAL, where before they agreed because both inherited the same errors from Divinum Officium. That is the failure mode CLAUDE.md's lineage note describes, resolved rather than documented, for the first time in this project.
* test(oracle): a third window, 2035, witnessing common-of-doctorsLukasz Kasprzak2026-08-172-0/+419
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Step 4's blind spot is now closed as far as the method allows. 2038 witnessed two of the five Common-routed saints; isidore-of-seville is the third and the last that any year in 2005-2050 can reach, being observed only in 2008, 2035 and 2046. He is also the only saint routing through common-of-doctors, so that Common had never been compared against any external source in any window. Three independent agreements on it: the scan (Commune Doctorum, scan1:41878ff, 2 Tim. 4, 1-8 / Mt. 5, 13-19), colitur, and missalemeum's own 2035-04-04 row. Asserted by date in its own test rather than folded into an aggregate count, so a regression there cannot hide. The remaining two saints are unreachable by construction, not for want of a fixture: gregory-the-great and patrick both sit in March and are impeded by Lent's privileged ferias in every one of the 46 years the differential covers. The blind spot is closed to its limit, not closed absolutely, and the register says so. The comparator is generalised rather than copied -- compare_live takes a fixture and a year -- so a fourth window would cost a fixture and a count list. 2035 differs on 3 days, both families pre-existing and already cited. 2035-04-02/03 are the Joseph/Annunciation transfer pair landing in the opposite order from missalemeum's: neither stream loses a feast, the sequence differs, which is C14's own RG 96 collision finally getting an oracle window. 2035-12-30 is the Sunday within the Christmas Octave against colitur's numbered octave-day slug, C6's family. Both are recognised by slug rather than by date, so a year exhibiting only half the shape would surface as unexplained rather than be quietly absorbed. 393 tests green with the exhaustive sweep. Register: section 6.15.
* feat(cli): --overlay FILE, so a local calendar can be suppliedLukasz Kasprzak2026-08-171-0/+45
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The Overlay algebra has been built and tested since Plan 2, and Date_spec grew movable variants last commit, but there was still no way to hand colitur a file: bin/main.ml applied exactly one overlay, the shipped data/ef/adjustments.sexp, with the path hardcoded. This is the plumbing. --overlay is repeatable and ordered, and applies ON TOP of the shipped adjustments rather than instead of them. That ordering is the whole point: adjustments.sexp carries RG 110's own 30 June companion, the Major Litanies, St Barbara and Rogation Wednesday, and a user file that replaced it would silently drop all four while appearing merely to add a local feast. Overlay.merge is last-writer-wins, so a local calendar can still override a universal entry deliberately, by naming its slug. The dispatch needed real argument parsing. It matched Sys.argv as an exact array, which does not survive a repeatable flag -- two --overlay arguments are a different array shape from one, and each further flag would multiply the patterns again. Flags are now stripped first and the remaining words matched as command plus year. Hand-rolled, because the dependency list is frozen and this is fifteen lines. One defect found by the cram suite on the first run: the unknown-option guard rejected --help and --version, having excluded only their short forms. Refused, not ignored, on easter and temporal. Neither reads sanctoral data -- temporal deliberately runs the cycle before any layer exists -- so accepting the flag there and silently doing nothing is the failure mode this project refuses everywhere else. A file that fails to load is fatal, exactly as the shipped overlay is; a directive naming a slug that does not exist warns on stderr and the run continues. The second matters more for a user file than for ours: a typo in a diocesan calendar should say so rather than quietly do nothing. test/fixtures/overlay-example-diocesan.sexp is a worked example and is labelled as invented, not calendar data. It carries both shapes a real local calendar needs: a fixed-date patron, and a dedication on "the first Sunday of October" -- which is the case the Date_spec work existed to unlock and which lands on 4 October in 2026, 3 October in 2027, 1 October in 2028. The dedication is I class because a church's own dedication anniversary is I class in that church; at III class it lost to the II-class Sunday every year, which a first draft demonstrated correctly and uselessly. The man page gains an OVERLAYS section saying plainly that an overlay is applied, NOT validated: the five test layers assert things about the shipped calendar and none of them can vouch for a file the user supplies. This is the first feature that lets someone change what colitur computes, and that distinction should not have to be inferred.
* test(oracle): a second oracle year, 2038, closing step 4's blind spotLukasz Kasprzak2026-08-172-0/+445
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Register section 6.7 recorded that step 4 of the reading chain -- the Common route -- had no external witness of any kind, and proved it by mutation: corrupting a Common citation left both the 16801-day differential and the 730-day oracle green. This closes that. 2038 is the only year in 2005-2050 in which two of the five Common-routed saints are the observed office (Perpetua and Felicitas on 6 March, Frances of Rome on 9 March). 365 days were captured live, one request per day, zero failures. A separate fixture, not more rows on the existing one. sources.md already records that the live endpoint has drifted from lectio's archived 2026-2027 snapshot; merging two versions of one source behind a single set of expectations would make any future disagreement unattributable -- calendar or drift, no way to tell. Nothing in the 2038 comparison is checked against the older fixture, and its provenance file says so, along with the fact that a live capture is not reproducible byte-for-byte on demand the way a snapshot-derived one is. 338 of 365 days match. 27 differ, every one of them in a named, already-adjudicated class, none unexplained: 13 the BVM Saturday votive Mass (M26 shape 1), 7 colour (M21), 2 Judith (M28), 2 Christ the King's week (M26 shape 2b), 1 Christmas multi-Mass (M27), 1 Perpetua's Common, 1 new. Each was decided in a different year, so 2038 re-confirms them independently -- the point of a second window is not new rulings but evidence the old ones are not artefacts of their own year. Two findings came out of it. Perpetua and Felicitas: missalemeum serves the Common of Virgins and calls them "Virgins and Martyrs" in its own oration, where the Missal directs "Missa Me exspectaverunt, de Communi non Virginum I loco" (scan1:27634-27635). Both Commons share the Introit Ps 118:95-96, which is exactly why the calendarium qualifies its direction, and both women were mothers. Verdict colitur; the mechanism is located on missalemeum's side, not merely asserted. Passion Tuesday: the Missal prints "Dan. 14, 27 et 28-42" (scan1:11106) and colitur reproduces that two-part form where missalemeum collapses it to 27-42. Same verses; the convention is deliberate, appearing also in the Seven Sorrows and the Common of Non-Virgins. Verdict colitur, cosmetic. Citations are notation-normalised before comparing, as layer 3 already does: Ecclus/Sir, Joann/John, Luc/Luke, Matth/Matt. Each pair was added because a real row needed it, and no target contains its own source as a substring, so the set is idempotent. Not compared, stated rather than left to be found: commemorations and observed-identity. That machinery is built around a date-literal 28-entry allow-list specific to 2026-2027, and re-deriving it for a second year is its own task. 2038 compares rank, colour, Epistle, Gospel. The extractor's day count was hardcoded to 730, which silently forbade any other window. It is now a parameter defaulting to 730, so the existing documented command keeps its guard and a partial fetch still fails loudly instead of producing a short fixture that passes a comparison it never ran. Teeth, by re-running section 6.7's own experiment: corrupting the Common of Non-Virgins II now reddens four tests, two of them external-oracle, where the same mutation previously reddened neither oracle layer. Residual: isidore-of-seville is still unwitnessed and needs 2035 or 2046; gregory-the-great and patrick are never the observed office in any year 2005-2050, so no fixture in that range can reach them. Register section 6.8 and 6.9.
* fix(lectionary): two Judith citations wrong in both enginesLukasz Kasprzak2026-08-172-215/+215
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A live-missalemeum sweep over a full liturgical year (2028, 364 days) returned 333 matching and 31 differing days: 17 already-known or pure notation, 3 new. All three now adjudicated against the photographic scans of the 1962 Missal. The Assumption's Epistle reads "Iudith 13, 22-25; 15, 10" (scan1:35357). Both engines carried "Judith 13:22-25; 13:15; 13:10" -- a chapter carry-over defect that re-applied chapter 13 to the second citation and so invented two verses the Missal never cites. The Seven Sorrows reads "Iudith 13, 22 et 23-25" (scan1:36938, corroborated at 28468); both carried "Judith 13:22; 13:25", dropping the range. The third, St Monica, resolves the other way: the Missal gives 1 Timothy (scan2:32679, heading and incipit both legible; scan1:29524 agrees) where missalemeum serves the Common of Holy Women's Proverbs -- colitur is right, no change. Layer 3 could not have found either citation defect. colitur's data is bootstrapped from lectio, and lectio is the other side of that differential, so while both engines produced the identical wrong answer the comparison held it against itself and reported agreement -- the shared-lineage blindness CLAUDE.md names, now with a second live witness beside Holy Thursday's colour. Only an external oracle could see it, and only outside the committed fixture's own window. Fixed upstream in lectio, re-bootstrapped here (provenance SHA-256 moved to 54f6bfc5, verified against the source), fixture regenerated. The upstream fix inverted layer 3 from blind to load-bearing here: reverting the Assumption citation now reddens both layers, where before it would have reddened neither. Verified by mutation, both directions. Allow-lists: - C22 closed and removed (128 rows -> 0), not re-adjudicated -- the other engine was fixed to agree, the same shape as the C2-C5/C7/C9-C13/C21 closures. Its own note had already flagged the Lenten Ember dead-key bug for upstream; lectio now also fails its build on any unreachable lectionary key rather than serving a plausible wrong Mass. - M28 opened (3 rows, verdict colitur), pinning both sides' citation text rather than the date and diff shape alone, so a different divergence landing on the same date cannot be swallowed. - M8's gate widened, expected_rows unchanged at 2: 2027-08-15 carries both its cause and M28's, and one entry is admitted per day. It stays under M8, which now requires its own commemoration difference to be present and admits the citation difference only when it satisfies M28's two-sided predicate. Neither cause absorbs the other. layer_m_reason's oracle-row parameter, kept named since M2 closed for exactly such a future reader, is live again. Register: docs/research/rules-register.md section 6.6.
* test(oracle): fix round 1 -- C30 disclosure, RG 17(a) misattribution, M26 shapesLukasz Kasprzak2026-08-171-5/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three Important findings from coordinator review of 8d1eb25, all fixed. 1. C30's own note claimed its 91-row population "is C25's OWN former population, before this task narrowed it" -- false. Independently re-derived against the real resolver (pre-fix vs post-fix binary) and the lectio fixture: only 44 of the 91 rows were ever in C25 (Wed 6/ Thu 19/Fri 19, matching C25's own fix-round-3 breakdown exactly); the other 47 (all of Monday/Tuesday, plus part of Wednesday/Friday) previously matched lectio outright -- both engines independently landing on the same wrong value -- and are newly allow-listed only because colitur is now right. Rewritten to the same "both engines silently agreed on the wrong answer" pattern C33/C34 already used. 2. "Diebus ferialibus" is not RG 17(a) -- RG 17(a) states only the day-identity privilege; the ferial-Mass rubrics are Mass-propers rubrics, a distinction the register's own §6.3 already drew before this task's first pass blurred it in code/allow-list comments. A further slip: C31/C32 called their own rubric "RG 17(a)'s own next/ third sentence" when it is a different rubric block ~230 lines away, in Holy Family's (not Holy Name's) own feast propers. Fixed the attribution chain throughout tools/bootstrap_lectionary.ml, data/ef/expected-divergences.sexp's C29-C34, and (gitignored, outside this commit) docs/research/rules-register.md -- closed the §6.3 park, added §6.5 recording all four citations with corrected attribution. Quotes and line numbers were already right; only the chain of custody to RG 17(a) was wrong. 3. M26's own 30 rows classify into two nameable shapes, not "three confirmed shapes" with an unexplained 23-row residue that was in fact empty: 21 rows are RG 78's own already-documented BVM votive-Mass gap; 9 rows are a second, distinct gap -- a week whose own Sunday was displaced by a movable named feast (Trinity->Corpus Christi, "Missa Dominicae I post Pentecosten"; Christ the King), where the Missal requires the displaced Sunday's own Mass to keep informing that week's ferias but step 3 walks back to the displacing feast's citation instead -- proven directly by colitur's own citation on the Christ- the-King rows being literally that feast's own Gospel. verdict open unchanged (not fixed here); the shape is now named instead of buried. Minors: section count corrected 1458->1456 (four places); "exactly one candidate, zero ambiguous" corrected to "correctness rests on ordering, not uniqueness" (2 of 1456 sections genuinely yield two candidates, Holy Saturday's own Lectio, both years); the stale "nothing in layers 3-5 compares citations" claim corrected (only layer 5 still doesn't); M18's note corrected (five new entries not six; ~18 of its remaining 352 rows only agree now because of this task's own fixes, not "already" before it); "five" corrected to "six" otherwise-unoccupied Saturdays in bootstrap_lectionary.ml; scan2 quotes softened where OCR-garbled. Also done outside the git-tracked files: the step-4 (Commons) coverage gap is now recorded in data/ef/expected-divergences-missalemeum.sexp's own header, not only the commit body and the (gitignored) report. dune test: 358 tests green. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359 tests green, 95.7s.
* test(oracle): compare reading citations against missalemeumLukasz Kasprzak2026-08-172-748/+757
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Layer 4 (test_oracle.ml) now extracts and compares the Epistle/Gospel citation for every one of the 730 days in the 2026-2027 fixture, the same axis Task 8 already added to layer 3. tools/ extract_missalemeum_oracle.py gained a structural citation-span detector (the first "*...*"-wrapped span under 40 chars containing a chapter:verse-shaped digit pair), verified exhaustively over all 1 458 Lectio/Evangelium sections. 728 of 730 days are comparable; the 2 that are not (Good Friday, both years) have no such section in missalemeum's own JSON at all -- a structural limit on the oracle's own side (M23), not a colitur gap. 451 of 730 days differ and are all explained across 19 cited entries; 0 unexplained. Chain step 4 (the Commons) has no layer-4 witness: confirmed empirically (a scratch sweep, not merely re-asserted from Task 6's own report) that none of the 15 Commons/proper saints is ever the observed office in this window. The comparison surfaced three genuine colitur bugs in data/ef/lectionary.sexp, all fixed at the source in tools/bootstrap_lectionary.ml: 1. Holy Name of Jesus (ef-holy-name-sunday/ef-holy-name) carried a mis-derived citation, borrowed by analogy from an unrelated ini section ("ef-christmas-sunday-0", the Sunday-within-the-Octave's own Mass) on the wrong assumption the two share a Mass. Disproved by missalemeum's own extraction for both real Holy Name Sundays in its window, confirmed against the Missal itself, twice, both scans (Acts 4:8-12/Luke 2:21, not Gal 4:1-7/Luke 2:33-40). 2. RG 17(a)'s own "diebus ferialibus" rubric (immediately following the Holy Name heading, both scans) was never implemented: 2-5 January ferias repeat 1 January's Mass, 7-12 January ferias before Holy Family Sunday repeat Epiphany's, and Holy Family's own week has a named formula ("Missa Dominicae Primae post Epiphaniam"). All three fell to step 3's undifferentiated Sunday walkback instead. Fixed for three 5-weekday families, deliberately excluding Saturday from all three -- RG 78's BVM Saturday Office wins there unconditionally, confirmed against missalemeum's own title on this window's own such Saturdays. 3. Corpus Christi and Sacred Heart (both real, never-impeded I-class movable feasts) had no lectionary entry at all. Both engines independently fell back to Trinity Sunday's own citation and silently agreed on the wrong answer -- the "Holy Thursday violet in both" shape CLAUDE.md names for exactly this reason. Because lectionary.sexp is shared with layer 3 (the lectio differential, 2005-2050), these fixes changed colitur's output for the whole domain, not only 2026-2027. data/ef/expected-divergences.sexp and test_differential.ml needed matching updates: C1 narrowed 361->173 and C25 narrowed 57->13 (rows that now resolve correctly moved to new entries), C16's note corrected (count unchanged), and six new entries added (C29-C34, 397 rows total, all verdict colitur). test_lectionary.ml and test_lectionary_ef.ml's own pins were updated to match (entry count 131->148; one step-3 regression test re-pointed to a date that still genuinely exercises that step). One entry, M26 (30 rows, verdict open), is a genuine unfixed gap: colitur's step-3 "resume the preceding Sunday" fallback is its only ferial-Mass mechanism, and missalemeum's own selection is more granular in three confirmed ways (a rotating votive Mass of the BVM, a named weekly formula for at least one week, and later Time-after-Pentecost weeks this task's own investigation did not narrow to a single rule) -- the same "needs a season-keyed selection mechanism, new behaviour not new data" shape data/ef/expected-divergences.sexp's own C6/C25 and Task 6's own residual concern already name, out of this task's own scope to build. dune test: 358 tests green. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force: 359 tests green, 89.5s.
* test(differential): compare reading citations, 16 801 daysLukasz Kasprzak2026-08-152-16842/+16855
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* test(differential): refresh the lectio fixture, close 9 entries, add C14Lukasz Kasprzak2026-08-122-16804/+16844
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | lectio moved 2386a45 -> 3b32c00 (seven EF calendar defects fixed, plus a sanctoral-generator root-cause fix); the committed fixture predates all of them. Regenerated with the same command the provenance note already named (`go run ./cmd/lectio-ef-dump 2005 2050`), read-only against lectio -- nothing there was touched. New SHA-256 asserted in both the provenance note and test_differential.ml's fixture_sha256. The dumper's two new name_en/name_pl columns are kept verbatim (the fixture is meant to be the exact command's raw output; the comparator already discards everything past field 7). Every C-entry's expected_rows was re-derived independently against the refreshed fixture (a Python re-implementation of diff_fields/ layer_c_reason, cross-checked against the real OCaml comparator's own failure output), not transcribed from a green run. Nine entries -- C2, C3, C4, C5, C7, C10, C11, C12, C13 -- now match zero rows and are closed: sexp row removed, layer_c_reason predicate and its now-dead helper bindings removed, citation preserved in docs/research/rules-register.md's new §3d with the specific lectio commit that fixed each one. C1 (361 -> 368) absorbs C13's former 7 rows now that lectio's own slug matches there too. C6 (138, unchanged count) narrows to a slug-only shape now that lectio's rank agrees. C8 (31, unchanged) survives untouched -- lectio still computes no Rogation days. C9's old shape ("Joseph observed ON a Lent Sunday") is also closed, but a narrower, different divergence survived under the same saint's slug: 3 years (2008, 2035, 2046) where Joseph's own congested RG96 walk collides with the Annunciation's separately-transferred "sedes propria". The old predicate's broad "any diff involving this slug" match was silently absorbing both under one citation -- split into a new id, C14, rather than folded back into C9, since the two are unrelated rules. lectio's own commit history (09a1444) documents this residual as a gap it deliberately left unresolved; colitur gives Joseph a real transferred day either way, which is why C14's verdict stands regardless of a genuinely open sub-question (recorded in the register, not resolved here) about which of the two feasts should claim the contested day first under RG98's "prius impeditum" wording. Re-triaged the full remaining 4975 raw differences: only 10 distinct field-diff signatures now, all resolving to Layer A/B noise or the four surviving citations, 0 unexplained. The crude "Sunday-rank" and "Holy Thursday" residues the task flagged both fully resolve once the real Layer A/B machinery runs (season-spelling and week-numbering artifacts, not substantive divergences) -- see the task report for the derivation. Teeth proof: perturbing ferial_rank to reproduce C4's old defect made 140 rows reappear as unexplained (the exact prior C4 count), reverted after capturing the failure message. 277 tests green (dune test) and 278 with COLITUR_EXHAUSTIVE_SWEEP=1 (the extra case is the 1583-9999 sweep itself).
* feat(test): oracle layer compares commemoration identity, not just countLukasz Kasprzak2026-08-122-746/+757
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-122-0/+769
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* test(differential): gate C1/C6 on slug family, pin the fixture's SHA-256Lukasz Kasprzak2026-08-121-0/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Fix round 1 on Task 15's review, four findings closed. C1 and C6 previously gated on calendar date alone -- unlike every other Layer C entry, which also checks the actual colitur slug that won. A future sanctoral change producing an unrelated celebration with the same diff shape (e.g. a promoted 29-31 December saint landing on C6's own [Slug_f; Rank]) would have been silently absorbed under a citation that has nothing to do with the real cause. Both entries now require, whenever Slug_f is in the diff set, that colitur's own slug be a member of an explicit closed list (jan_6_13_slug, nativity_octave_day_slugs) -- the same style advent_feria_slug already used for C4. Constructed the exact substitution via a temporary (unshipped) overlay Replace directive promoting thomas-becket to Class1 Feast White; confirmed the tightened predicate refuses to absorb it (46 unexplained rows, one per year); reverted. C10 gained the diff-field-subset restriction every other entry already had (it previously fired on the two pinned dates regardless of which fields differed). Fixed a stale doc comment ("SIX" columns; field has five constructors). The fixture's provenance note now records its SHA-256, and the suite asserts it as its own first test case (test_fixture_checksum), so a hand-edited or partially re-copied fixture fails loudly instead of silently becoming an unlabelled snapshot. Computed via the system sha256sum binary through Sys.command plus a redirected-output temp file -- no new library dependency, not even unix. Constructed a one-byte fixture edit and confirmed the checksum test fails independently of the row-comparison test; reverted. 237/237 tests green, clean-build verified.
* test: differential vs lectio 2005-2050 with a cited allow-listLukasz Kasprzak2026-08-122-0/+16822
Validation layer 3 (design spec's five): compares colitur's real day-by-day EF output against lectio (sibling project, Go), 2005-2050, one line per civil day. Of 16801 day-pairs, 11206 already agree on the seven leading columns; the 5595 that don't resolve into exactly 25 distinct field-diff signatures, all triaged. Three strictly separate layers, per the controller's ruling (the brief's single flat allow-list assumed a handful of differences, not 5595): - Layer A (test_differential.ml, norm_season/norm_slug): vocabulary. An explicit, closed table of naming synonyms with no liturgical substance (lectio's easter/christmas vs colitur's paschaltide/christmastide; a handful of slugs that are two names for the identical office). No wildcards -- every entry is a literal string pair. - Layer B (strip_epiphany_index): numbering. The one slug family whose lectio/colitur index offset is not a constant (Time-after-Epiphany week numbering, register 3c#5) has its embedded digit stripped to a common form on both sides before comparing; rank and colour stay fully compared. - Layer C (data/ef/expected-divergences.sexp): the cited allow-list. Ten genuine liturgical disagreements, each citing its RG paragraph and naming which engine is right (always colitur, verified against the Missal/register, never against lectio's own behaviour). This is the only layer permitted to cover a difference in rank, colour, or which celebration is observed. Five extend or restate register 3c's already- documented divergences (season boundary, Sunday I-class, Advent Ember ferias, Rogations); five are new, found and adjudicated in this task (Lent Ember days, the Nativity Octave, Ember-day-vs-saint precedence, the St Joseph transfer off a Lent Sunday, and the 2011 Sacred Heart / Precious Blood / Visitation collision). expected_rows on each entry is an exact regression pin, asserted by the test, not documentation. 13 January (register 6's long-open "Baptism of the Lord" item) is confirmed empirically fixed already -- Task 11's sanctoral wiring closed it before this task started -- so it is not allow-listed; the only residual difference there is the season boundary already covered by C1. Two stated limits carried from the brief (commemorations are not comparable; lectio's own EF oracle asserts season only, 2025-2026 only, so a rank/colour difference is not presumptive evidence against colitur) plus a third found during this task (the week column is a display convention on both sides, not a liturgical fact, and is not compared at all) are documented in the test file's own doc comment. Fixture: test/fixtures/lectio-ef-2005-2050.txt, committed as plain text (1.4 MB), generated by lectio commit 2386a45; provenance recorded in the sibling .provenance file. Colitur's side is recomputed fresh from the library on every run, through the same Calendar/Rite_ef pipeline `colitur day` uses, not the compiled binary. Proved the harness has teeth by two reverted perturbations: a genuine colour difference injected into a fully-covered fixture row fails the "no unexplained differences" check with the exact mismatched row printed; a one-row drift in an allow-list entry's expected_rows fails the count check independently, showing it is not merely a duplicate of the first assertion. 236/236 tests green, clean-build verified, deterministic across OCAMLRUNPARAM=R.