aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* feat(cli): install the runtime data, and resolve it in three waysLukasz Kasprzak2026-08-173-6/+99
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `dune install` produced a colitur that could not run: the binary locates its data relative to its own path, nothing installed the four .sexp files anywhere, and an installed colitur exited 2 unable to read sanctoral.sexp. `dune build @install` produced an empty tree. data/dune installs the four RUNTIME files into <prefix>/share/colitur/ef. Deliberately not the two allow-lists: those describe where colitur and a comparison oracle disagree, are read only by the differential and oracle tests, and are meaningless to a running colitur -- installing them would ship an assertion about lectio's and missalemeum's behaviour as though it were calendar data. No glob, on purpose: a glob would silently start shipping them again the moment one was renamed. Resolution now probes rather than computing one path and hoping. An installed prefix is tried first, the build tree second, and a candidate counts only if sanctoral.sexp is actually readable inside it -- so a failed or half-removed install falls through to a working tree instead of shadowing it and failing later with a per-file error. COLITUR_DATA_DIR overrides both and NEVER falls through. This was the one real design question and the first version got it wrong: it treated the override as just another candidate, and a deliberately bogus value produced a full, plausible, entirely un-flagged year computed off the build tree. Someone who names a directory has stated an intent, and silently calendaring off a different one is the silent substitution this project refuses everywhere else. It is now exit 2 naming the directory. Verified end to end, not inferred: installed to a scratch prefix, then ran the binary from an unrelated cwd with no build tree near it, and confirmed 2038-03-06 still resolves Perpetua and Felicitas through the Common route. All four paths exercised -- build tree, valid override, bogus override, and an empty share/ falling through. Environment reads stay confined to bin/. The kernel's contract forbids them and nothing below the CLI learns where the data came from; the loaders take a path.
* feat(cli): colitur readings, one line per dayLukasz Kasprzak2026-08-173-10/+163
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The lectionary has been resolvable since Task 4 but invisible from the command line: `colitur day` prints no citations, so the branch's whole deliverable could only be seen through the test suite. A separate command rather than extra columns on `day`, which is where the plan pointed. Its instruction was to append the Epistle and Gospel to each row "matching the existing column style", and that turns out not to be possible: a citation contains spaces and commas ("Ezech 34:11-16", "Ecclus 51:1-8, 12") while a day row is space-separated with a variable-length +slug commemoration tail, so appending them leaves the row unsplittable -- no awk or cut field number recovers where the Epistle ends. That is the opposite of what the row is shaped for. So `day` keeps its format byte-identical, asserted directly in cli.t rather than left implicit, and the citations get their own row with " | "-delimited fields, safe for values containing spaces. Both formats are a stopgap and say so in the source: the design calls for one schema rendered through a logic-less template engine, and two ad-hoc formats are easier to retire later than one overloaded format whose parsing rules nobody wrote down. The year walk is now shared. day_report and readings_report differ only in how a day is printed, and the two-liturgical-year indexing -- with its own reasoning about civil-versus-liturgical spans -- is exactly the part that must not be duplicated and drift. "-" for an absent part, though no EF day can currently print one: layer 2 asserts exactly one First and one Gospel on every day of every year 1583-9999. The CLI still does not assume a guarantee the kernel makes about data rather than about types. cli.t gains the four chain-step cases test_golden.ml pins against the scans, so the CLI path is checked to agree with the library path, plus the 2 January line, which reads the Circumcision's Mass under the Missal's own ferial rubric (scan1:6523-6526) and not RG 17(a). Verified the cram tests actually run and have teeth: corrupting one expectation produces a diff and exits 1. CLAUDE.md: state, test count (369, 370 with the sweep), the readings command and why it is separate, and What's next -- the lectionary landed early rather than inside Plan 4, so what remains there is OF's own lectionary, not the mechanism. Chants stay deliberately unbuilt, and Validate now rejects any part outside First/Gospel.
* test: lectionary properties, golden pins, and the blast radiusLukasz Kasprzak2026-08-175-8/+366
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Layers 2 and 5 for the reading citations, which until now had coverage from layers 3 and 4 only -- both oracle-bounded, at 2050 and 2027. test_validate.ml said so in its own words ("No fixture here exercises citations") and test_golden.ml's describe pinned twelve fields, none of them a citation. 358 tests -> 369, and 370 under the sweep. Layer 2 as two Validate checks rather than standalone QCheck properties, so they ride the existing prop_invariants and the exhaustive sweep instead of duplicating that machinery. "citations" asserts a day's parts, sorted, are exactly [First; Gospel] -- one condition carrying three invariants: zero or two but never one, nothing outside First and Gospel since the chants are deliberately unbuilt, and no part twice, which a bare cardinality test would miss. "citations-unresolved" is the separate coverage half: the chain fell through every step. Both are gated on the rite producing some citation somewhere, so a rite whose lectionary is not built is held to neither and the synthetic fixtures stay unaffected. The result over the whole domain: every day of every liturgical year 1583-9999 resolves exactly one Epistle and one Gospel. Mutation-proved live rather than merely silent -- treating a well-formed pair as malformed reddens the suite with real days showing [first,gospel]. citations-unresolved has no live witness at all, which is why test_validate.ml gains five fixtures driving both checks through a new ?readings override, including the gate itself. Layer 5 as six pins, one per chain step, so a regression names the step that broke. Every literal was read out of the scans before being compared with colitur, per that file's standing rule; scan lines are in each pin's comment. Two of the six cover step 4, and the plan's own dates for steps 1 and 4 were wrong: 2030-01-13 is a temporal day (Holy Family on the generic Sunday slug) and would not have exercised step 1, while 2038-03-08 is John of God, who has a full proper Mass and never routes through a Common -- so he becomes the step-1 pin instead. A separate formatter rather than widening describe: widening would invalidate all thirty existing literals at once and the only practical way back would be pasting in whatever colitur printed, which is what the file's header forbids. Restoring them honestly means hand-verifying thirty more Masses against the scans, a task of its own. Stated rather than hidden, and defensible because layer 2 now checks the shape of every day in every year, which is the breadth that widening would have bought. Two pins earned their keep immediately. Lent I Monday failed on "Ezek" against colitur's "Ezech" -- notation only, and colitur matches the Missal's own abbreviation; layer 3 normalises this away and cannot see it. The displaced-Sunday feria pins M26 shape 2(b)'s known-wrong walkback deliberately, labelled as such so a future fix fails loudly, and established two things M26 had not recorded: both parts come from the displacing feast, not only the Gospel it cites, and the reference carries a stray trailing period inherited through the bootstrap. Blast radius: zero. The CLI never calls Validate, no data file changed, and 20 years spanning 1583-9998 produce 7301 byte-identical lines between the pre-change binary and HEAD. Step 4's blind spot is now measured rather than argued. Corrupting the Common of Non-Virgins I's Epistle leaves layer 3 (16801 days) and layer 4 (730 days) both green, reddening only one unit test and one new pin. Both step-4 pins matched the scans on the first attempt -- the first independent confirmation that route has ever had. It narrows the gap without closing it: two of five Common-routed saints are witnessed, isidore-of-seville is not, and gregory-the-great and patrick are unreachable in any year 2005-2050. Register: section 6.7 addendum. validate.mli documents both new labels; liturgical_day.mli's "always empty until Plan 4" was outlived by the lectionary and is corrected.
* docs(ef): fix round 2 -- three miscitations and a recorded blind spotLukasz Kasprzak2026-08-175-23/+78
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Comment and citation corrections found by review, plus one finding that survived an attempt to reconstruct it. No behaviour change; the suite is unchanged at 358 green. The ferial rubric "Diebus ferialibus a 2 ad 5 ianuarii Missa dicitur ut die 1 ianuarii" was attributed to RG 17(a) in three test comments. It is not RG 17(a). RG 17(a) (LT.txt:843) fixes only WHEN the Holy Name is kept -- "celebrandum dominica quae occurrit a die 2 ad 5 ianuarii; secus die 2 ianuarii". The rubric quoted is a Mass-propers rubric printed under the Sanctissimi Nominis Iesu heading (scan1:6523-6526, corroborated scan2:7216), governing what the ordinary ferias of that window say when they are not the feast at all. Two rules sharing a date window. Fix round 1 had already corrected this on the data side; the generator's own comment was right throughout. Only the test comments were stale. test_differential.ml named bootstrap_lectionary.ml's derived_entries as the source of ef-holy-name's citations. That binding no longer exists -- it was the mechanism that derived them from ef-holy-name-sunday's wrongly-widened ini translation, removed in Task 9 because the derivation was itself the error. They are hand-authored from the scans now, as holy_name_entries. M26's shape 1 described its 21 rows as votive Masses of the BVM "on an otherwise-unoccupied feria, not only a Saturday", splitting them into 4 BVM-Saturday rows and 17 "ordinary weekday ferias". Every one of the 21 is a Saturday, each checked individually against date -d. The error was not merely verbal: it made the gap look broader than a single cause and pointed away from the one rule that explains all 21. The sub-groups differ by season, not weekday, and only in what colitur shows in the votive Mass's place. One gap -- the office is built (RG 91 entry 27), the seasonal selection among the five Masses is not. Recorded, not fixed: step 4 of the reading chain has no oracle witness at all. This began as a note to write up a live-oracle confirmation for Felicitas and Perpetua; the reconstruction contradicted its premise. The sweep ran over 2028, where 6 March resolves as a Lenten feria with the saint admitted only as a commemoration, so her own Mass is never read that year. The finding underneath is firmer: only five saints route through a Common, they are the observed office on five days in all of 2005-2050 (isidore-of-seville 2008/2035/2046, frances-rome and sts-felicitas-perpetua both 2038), two are never observed in 46 years, and none falls in layer 4's window. Layer 3 does not cover it either -- lectio resolves the literal "-"/"-" sentinel there, which is what C18 is gated on, so it confirms only that colitur emits a citation where lectio emits none, never that it is right. Closing it needs the oracle fixture extended to 2035, 2038 or 2046; that is a fixture-scope decision. Register: sections 6.6 and 6.7. CLAUDE.md's layer-blindness list gains the step-4 entry.
* fix(lectionary): two Judith citations wrong in both enginesLukasz Kasprzak2026-08-177-260/+327
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-177-114/+230
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-1711-867/+1714
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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): fix round 3 -- two more Ember Saturdays, doc fixesLukasz Kasprzak2026-08-155-24/+171
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two Ember Saturdays shipped an apostle's Mass verbatim from lectio's own ini, the exact shape already found and fixed for Passion Tuesday (C26): ef-advent-ember-sat carried St Thomas the Apostle's Mass (Eph 2:19-22 / John 20:24-29, 21 December) instead of the real "Sabbato Quatuor Temporum Adventus" (2 Thess 2:1-8 / Luke 3:1-6); ef-september- ember-sat carried St Matthew's Mass (Ezek 1:10-14 / Matt 9:9-13, 21 September) instead of the real "Sabbato Quatuor Temporum septembris" (Heb 9:2-12 / Luke 13:6-17). Both verified against both primary-source scans independently. Both readings are explicitly labelled "Lectio Epistolae" in the Missal on both days, so neither needed the "last lesson before the Gospel" convention Holy Wednesday/Good Friday relied on -- a textual fact, not an editorial choice. Fixed at the source in tools/bootstrap_lectionary.ml: colitur_keys excludes both wrong ini sections; a new ember_saturday_corrections list hand-authors the real citations. New allow-list entries C27/C28 (40 rows each, not the naive 46 -- traced to the identical 6 civil years where 21 September and 21 December, always exactly 91 days apart, land on a Saturday and the fixed saint's feast wins outright). Both values pinned in test_lectionary.ml and mutation-tested. Also: C25's per-weekday breakdown was mislabelled (right multiset, rotated weekday labels) in both test_differential.ml and expected- divergences.sexp -- corrected to Wednesday 6/Thursday 19/Friday 19/Saturday 13, and the claim that this split came directly from the comparator's own output is narrowed to what's actually true (only the total, 57, does). C26's scan2 citation range was extended to include the Gospel (previously stopped after the Epistle). lib/kernel/liturgical_day.mli's "citations always empty until Plan 4" comment is now stale on this branch -- left untouched (Task 11's own documentation pass), noted in the task report so it isn't lost. dune test: 358 tests green; COLITUR_EXHAUSTIVE_SWEEP=1: 359 green.
* test(differential): fix round 2 -- Good Friday, Passion Tuesday, stale headerLukasz Kasprzak2026-08-155-195/+400
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-158-170/+571
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-157-16890/+17363
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* ef(lectionary): fix round 1 -- the step-4 guard, and a miscited authorityLukasz Kasprzak2026-08-153-30/+154
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Review re-verified all fifteen saints and all seven formularies against both printings and found zero citation discrepancies, confirming the Gabriel printing error and all three proper/Common reversals; the reordering was adjudicated sound. Two Important findings, both about the guard rather than the data. IMPORTANT 1 -- the guard had no test, and a comment claimed it did. `test_step4_never_diverts_a_temporal_office` asserted 2026-07-04 keeps its ferial Mass and its comment claimed to "pin it behaviourally". It did not: no shipped assignment names a temporal slug, so the guard is a NO-OP on shipped data and that test passes with the guard removed. The reviewer proved it by forcing `sanctoral_office = true` -- all 357 tests stayed green. That mattered more than an ordinary missing test: the guard is the structural claim the whole reordering argument leans on, and on a task with no oracle the tests are the entire safety net. Fixed by exposing `Commons.of_tables` (already present internally; it applies exactly `load`'s validation, so a synthetic table is a well-formed table with a WRONG assignment rather than malformed data the loader would reject before the guard is reached -- and it is the constructor a future diocesan overlay needs anyway), adding `day_with ~commons` beside `day`, and writing test_step4_guard_refuses_a_common_assigned_to_a_ferial_slug: a synthetic Commons assigning the Common of Abbots to `ef-time-after-pentecost-5-saturday`, a really-observed ferial slug, asserting 2026-07-04 still says its ferial Mass. Two checks, the first making the second mean something -- it asserts the hostile assignment IS present and resolvable, so the ferial Mass wins because the GUARD refused it and not because the lookup missed. Mutation-verified: with the guard disabled exactly one test fails, this one; before this round that mutation reddened nothing. The old test is renamed test_step4_leaves_a_temporal_office_alone_on_shipped_data and its comment corrected to say it does not pin the guard. IMPORTANT 2 -- the guard cited an authority that does not say what was claimed. The comment said "[Validate] already asserts slug uniqueness per liturgical year, so a sanctoral feast can never collide with a temporal slug". validate.ml's check maps over `Temporal.office` slugs ONLY, compared to each other across the days of one year; it says nothing about cross-stream collision. An assumption was promoted to an asserted invariant -- the exact defect class this project's citation discipline exists to catch. Rewritten to state it as an assumption (no sanctoral slug carries the `ef-` prefix every temporal slug does; 0 of 327 today), to quote the real authority verbatim (validate.ml's own note that this "is the same assumption the rest of this codebase already leans on"), to carry a CORRECTED marker naming the wrong citation so it is not re-derived, and to record the failure mode if it ever broke: benign and one-directional -- a colliding saint is denied his Common and falls through to steps 2/3, i.e. to exactly the answer the unguarded chain would have given him. No day gains a reading it should not have. MINOR -- the previous commit body led with "Blast radius, measured ... ZERO differing lines", which is true but oversold: the CLI prints no citations, so that sweep is STRUCTURALLY INCAPABLE of observing the reorder or any citation value. It is a valid negative control for the sanctoral `Edit` overlay and nothing else. The evidence for the reorder is mutation 1 (relegating step 4 to last reddens exactly the four step-4 pins) plus those pins' own explicit expected values -- not the sweep. Correcting the record here. MINOR -- test_commons_load_rejects_bad_data wrote four FIXED-name files into the temp dir and removed none, colliding across concurrent runs. Replaced with a local `with_temp_file` copied from test_lectionary.ml's own (`Filename.temp_file` + `Fun.protect ~finally`); the four labels moved into the assertion messages where they belonged. Verified zero leftovers after a full run. MINOR -- added the sharper argument for the reorder, which makes the deviation obviously right rather than merely well-evidenced, and it now LEADS the branch comment as warrant (1), with the Missal citation demoted to a corroborating warrant (2): step 4 is STEP 1'S CONTINUATION. Step 1 already runs the observed office's own proper ahead of steps 2 and 3; step 4 is that same rule for the saints whose readings the Missal keeps in a Common instead of printing on the celebration. Placing it last would have made the chain internally inconsistent with code that already existed -- St Joseph beating a competing temporal entry because his readings sit on his Celebration.t, and St Vincent Ferrer losing to one because his sit one indirection away. Nothing in the rubrics draws that distinction; it is an artefact of where colitur stores the data. So the plan's ordering did not merely disagree with the data, it contradicted step 1. 358 tests green (357 before), 359 with the exhaustive sweep.
* ef(lectionary): the Commons, chain step 4Lukasz Kasprzak2026-08-1515-132/+1195
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Fifteen class-3 saints had no readings anywhere in colitur -- the 6 March - 5 April cluster whose RANKS lectio's generator fix of 2026-08-12 restored without backfilling readings, and lectio has no Commons concept to have backfilled them from. They are exactly the fifteen `status Feast` entries in data/ef/sanctoral.sexp carrying `(citations ())`. The Missal splits them eight/seven, and NOT as the plan predicted. It expected propers for Benedict, Patrick, Thomas Aquinas and Gregory the Great; only Thomas Aquinas has one. Benedict ("Missa Os iusti, de Communi Abbatum"), Patrick ("Missa Statuit, de Communi Confessoris Pontificis I loco") and Gregory the Great ("Missa Si diligis me, de Communi unius aut plurium Summorum Pontificum") are all sent to a Common, with only Oratio, Secreta and Postcommunio printed at the date. All three reversals verified on the page images (pp. 488, 492) as well as in both OCR text layers. PROPERS (data/ef/adjustments.sexp, `Edit ... Set_citation`): thomas-aquinas 7 Mar Sap 7:7-14 / Mt 5:13-19 john-of-god 8 Mar Eccli 31:8-11 / Mt 22:34-46 forty-holy-martyrs 10 Mar Hebr 11:33-39 / Luc 6:17-23 cyril-of-jerusalem 18 Mar Eccli 39:6-14 / Mt 10:23-28 gabriel-the-archangel 24 Mar Dan 9:21-26 / Luc 1:26-38 john-damascene 27 Mar Sap 10:10-17 / Luc 6:6-11 john-of-capistrano 28 Mar Sap 10:10-14 / Luc 9:1-6 francis-of-paola 2 Apr Philipp 3:7-12 / Luc 12:32-34 COMMONS (data/ef/commons.sexp), each quoting the Missal's own instruction at the saint's date: sts-felicitas-perpetua 6 Mar non Virginum I Eccli 51:1-8,12 / Mt 13:44-52 frances-rome 9 Mar non Virginum II Prov 31:10-31 / Mt 13:44-52 gregory-the-great 12 Mar Summ. Pontificum 1 Petri 5:1-4,10-11 / Mt 16:13-19 patrick 17 Mar Conf. Pont. I Eccli 44:16-27;45:3-20 / Mt 25:14-23 benedict 21 Mar Abbatum Eccli 45:1-6 / Mt 19:27-29 isidore-of-seville 4 Apr Doctorum 2 Tim 4:1-8 / Mt 5:13-19 vincent-ferrer 5 Apr Conf. non Pont. I Eccli 31:8-11 / Luc 12:35-40 Every value read in three independent places -- both OCR text layers (two DIFFERENT printings, whose Commune Sanctorum pagination differs by 1-2, so the data keys on heading plus Mass incipit and never on the bracketed page number) and the rendered page images. That caught a genuine typographical error in one printing: it gives St Gabriel's Gospel as "Luc. 2, 26-38" (on the page image, so not an OCR artefact). Luke 1:26-38 is settled by the other printing, by the pericope text in both, and by the SAME edition citing the identical pericope correctly two pages later at the Annunciation. Step 4 EXECUTES SECOND (order 1, 4, 2, 3), not last as the plan sketched. Measured: every day on which one of the fifteen is actually the observed office already has a non-empty step-2 or step-3 answer waiting, so a step 4 placed after the ferial fallback is unreachable on every date in 1583-9999 -- and wrong where it would fire (2038-03-06 would emit Septuagesima II Saturday's Mass on a day whose office is Sts Perpetua and Felicity). Step 4 is also the only step in the chain with a direct primary-source warrant: the Missal names the Mass at each such saint's own date, while steps 2 and 3 rest only on lectio's observed behaviour. Guarded so it can never divert a day whose observed office IS its temporal office -- ferias, Sundays, the Triduum and the RG 78 Saturday Office of the BVM (which deliberately shares the ferial slug) are structurally excluded. Step NUMBERS are unchanged so every "step 3" already written down still names the same branch. The Commons are caller-supplied, like the lectionary: `Rite_ef.context` gains a REQUIRED `~commons`, not an optional one -- a silently-defaulted empty table would give a rite whose class-3 saints quietly lose their Mass, and nothing in layers 3-5 compares citations, so that loss would be invisible. `Commons.load` rejects the four defects indistinguishable downstream from "this saint has no Common": duplicate common id, duplicate assignment, empty formulary, assignment naming a common that does not exist. KNOWN LIMITS, recorded rather than papered over. There is no oracle: lectio has no readings for these feasts and missalemeum's 2026-2027 window never observes one of the fifteen. Eight of the fifteen are NEVER the observed office anywhere in the domain (10-28 March always falls to a Lenten or Passiontide feria that outranks a III-class feast), so they have no end-to-end witness and are covered by dedicated table tests instead. The Commune Sanctorum's own opening rubric permits any Epistle or Gospel of a Common in any Mass of that Common unless one is assigned, so what is emitted is the reading PRINTED WITH the named formulary, not the only lawful one; labelled alternatives (e.g. the widow's Epistle 1 Tim 5:3-10, squarely available to St Frances of Rome) are noted in the data and deliberately not encoded. The BVM Common is NOT authored: the RG 78 Saturday Office shares the ordinary ferial slug, so assigning a Common to it would hand the BVM Mass to every feria sharing that slug; routing it needs a subject-keyed lookup and a season-keyed choice among the Missal's five "Missae de sancta Maria in sabbato" -- new behaviour, not new data. It currently emits its feria's Mass, now pinned as such. Blast radius, measured (git archive HEAD binary vs HEAD, full 1583-9999 `colitur day` sweep, diffed): ZERO differing lines, 3 074 246 lines each side, byte-identical -- the CLI prints no citations, so this change is invisible to layers 2-5 by construction, and the sanctoral overlay perturbed nothing else. 357 tests green (344 before), 358 with the exhaustive sweep. Mutation-tested six ways, all reverted after confirming: relegating step 4 to last reddens exactly the four step-4 pins and nothing else; corrupting a Common's citation, dropping an assignment, dropping a proper Edit, and corrupting either an unreachable proper or an unreachable Common each redden their own pins. The load-bearing assertion is stated over the loaded layer rather than a hard-coded list: every sanctoral entry that can ever BE the observed office now has either a proper or an assigned Common -- fifteen failures before, zero after, and a future re-bootstrap adding a readingless class-3 feast fails there instead of silently emitting its feria's Mass.
* ef(lectionary): fix round 1 -- correct a wrong test, a wrong comment, and ↵Lukasz Kasprzak2026-08-153-19/+52
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ba0adba's own wrong commit body Three Important findings, all traceable to the task-5 brief rather than the implementation itself; all confirmed against the real data and the real resolver before fixing, not taken on trust. 1. test_step3_uses_temporal_not_observed (2028-12-26) did not exercise step 3 at all: 26 December is always Stephen, a real sanctoral entry with its own citations, so that date resolves entirely at step 1. Its justifying comment was also wrong -- 24 December's TEMPORAL slug is ef-nativity-vigil, IDENTICAL to its observed slug (Temporal_ef.named hard-codes the Vigil for that date ahead of any Sunday computation), so there was never a temporal/observed split on that date to exploit. Replaced with 2025-02-03: 2 February 2025 (Sunday) is observed as the Purification (own citations Mal 3:1-4 / Luke 2:22-32) but its TEMPORAL identity is ef-time-after-epiphany-sunday-4 (Rom 13:8-10 / Matt 8:23-27, a different lectionary entry); 3 February has no proper of its own and reaches step 3, which must return the Sunday's TEMPORAL reading, not the Purification's. Verified against data/ef/sanctoral.sexp and data/ef/lectionary.sexp directly. 2. The termination-argument comment in lectionary_ef.ml (and its echo in lectionary_ef.mli) claimed an unguarded Sunday would loop. It would not: readings is not recursive -- step 3's fallback is one flat Lectionary.find, never a re-entrant call into readings -- so an unguarded Sunday would just repeat step 2's own already-failed lookup once (same pure inputs, same None) and return [] normally. Rewritten to say what is actually true: the guard exists because a Sunday has no PRECEDING Sunday to resume, not because skipping it would be dangerous; the chain terminates because every step consults data or a strictly earlier date, and no step ever calls back into readings. 3. Correcting the record, per instruction, rather than amending ba0adba: that commit's own body said Advent ferias carry 'Advent I's own readings copied onto the following Monday-Thursday'. Both details are wrong, verified directly against data/ef/lectionary.sexp: the duplicated readings are ef-advent-SUNDAY-2's (Rom 15:4-13 / Matt 11:2-10), not Advent I's, and they appear on ef-advent-2-monday, -tuesday, -thursday and -saturday -- four non-contiguous days, not a Monday-to-Thursday span. The in-code comment and the task-5 brief's own commit template both already said 'Advent II' correctly; only ba0adba's commit body had the error. dune test --force: 344 tests, all green (unchanged count -- one test's body changed, none added or removed).
* ef(lectionary): a feria says the preceding Sunday's MassLukasz Kasprzak2026-08-154-9/+104
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Chain step 3. Guarded on weekday <> Sun: a Sunday reaching this branch would look up its own slug via days_since_sunday Sun = 0 and loop -- every other chain step consults data, this one consults a strictly earlier date, so that guard is the whole chain's termination argument. Reaches the Sunday by Date.add_days plus a fresh temporal_at call, never by string surgery on the day's own slug -- the slug shapes are genuinely inconsistent across seasons (ef-advent-sunday-1 vs ef-advent-1-monday, week number on opposite sides of the season name). Uses the preceding Sunday's TEMPORAL slug, never its observed one: the rubric is the preceding Sunday's Mass even in a year a feast displaced that Sunday from being observed (pinned: 2028-12-26, the Monday after a Vigil-displaced Advent IV Sunday, still takes Advent IV's Mass). Measured over the full 1583-9999 domain (temporal cycle only, no sanctoral contest): of 412 distinct temporal slugs, 305 carry no lectionary entry of their own; of the 304 that are feria (non-Sunday) slugs, step 3 alone resolves 297 of them via their preceding Sunday. The 7 that remain, plus the 1 uncovered Sunday slug itself (ef-holy-name-sunday), all trace to the same two missing lectionary entries (Holy Name Sunday and 30 December), not to eight independent gaps or a step-3 defect -- traced date-by-date, not merely counted. Day-level effect, 2005-2050 (full Precedence+Calendar pipeline, matching this project's existing differential window): 16807 days, 16531 resolved (98.36%), 276 still empty; step 3 alone accounts for 4503 of the resolved days, more than either step 1 or step 2. Warrant is the same class as step 2's, not a confirmed Missal citation: lectio hard-codes this shape as literal duplicated data on the four Advent ferias (Advent I's own readings copied onto the following Monday-Thursday) and leaves the rest of that same shape simply absent; step 3 turns the duplication into a rule. dune test --force: 344 tests, all green (was 340).
* kernel+ef: fix round 1 -- lectionary caller-supplied, not eagerLukasz Kasprzak2026-08-1513-83/+250
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Critical (coordinator review): a clean `dune build` produced a `colitur` that died at startup on EVERY subcommand, including ones touching no lectionary data at all. Root cause was two-fold: data/ef/lectionary.sexp was never added to the root default-build alias (only materialised as a side effect of the test suite's own deps, which is why every check in the prior report passed), and Rite_ef.context loaded it as a module-init side effect via failwith, undoing Lectionary.load's own "never raises" promise at a point no caller could catch. Fixed structurally: Rite_ef.context is now a function taking ~lectionary, Lectionary_ef.readings takes ~lectionary, and neither touches the filesystem any more -- the same caller-supplied discipline the sanctoral layer already had, restoring rite_ef.mli's own pre-existing claim about it and leaving a seam for a future diocesan lectionary overlay. bin/main.ml grows load_ef_lectionary, a sibling of load_ef_layer, routed through the same colitur: %s / exit 2 path. data/ef/lectionary.sexp added to the root default alias. Every caller of Rite_ef.context updated to supply it. Also: two new tests that genuinely distinguish chain step 1 from step 2 (19 March 2026, Joseph's own proper over a competing temporal entry; 13 January 2030, Holy Family reached only through the temporal slug, the Baptism entirely absent) -- the prior two tests both survived swapping the chain order. Both new pins verified directly against the real data. The chain's own comment now states plainly that its warrant is lectio's observed behaviour, not a confirmed Missal citation, per the rules register's own open item.
* kernel+ef: resolve readings, chain steps 1 and 2Lukasz Kasprzak2026-08-1511-5/+165
| | | | | | | | | | | | Liturgical_day.citations has read "always empty until Plan 4" since Plan 3; it is now filled. Rite.t gains a readings function, rite-supplied for the same reason transfer_target is: what a day with no proper falls back to is a rubric, not a universal. Calendar calls it and passes its own temporal function as the callback the rite needs to reach another date. Steps 1 and 2 only: the observed celebration's own proper, else the day's own temporal slug. Nothing encodes "Lent has daily propers" -- the presence of an entry is the discriminator.
* data(ef): carry sanctoral propers on the celebrationLukasz Kasprzak2026-08-153-212/+886
| | | | | | | | | | | | | | 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-145-1/+495
| | | | | | | 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.
* kernel(lectionary): fix round 1 -- load never raisesLukasz Kasprzak2026-08-142-1/+39
| | | | | | | | | | | | | | | Sexplib.Sexp.load_sexp raises bare Failure for several malformed inputs (unterminated list/string, empty file, more than one sexp) rather than Sexplib.Sexp.Parse_error, so those cases escaped Lectionary.load as an uncaught exception -- breaking the .mli's own promise and the kernel's never-raises-on-fallible-construction constraint. Mirrors the catch-all already present in Layer.load and Overlay.load, plus a second catch-all on the t_of_sexp branch for defence in depth. Adds test_load_never_raises, covering all of the above plus a missing file, using Filename.temp_file rather than a hardcoded path. Verified the new test fails against the pre-fix load (uncaught Failure) and passes against the fix.
* kernel(lectionary): slug-keyed reading citationsLukasz Kasprzak2026-08-144-1/+116
| | | | | | | Data only, the same shape and discipline as Layer: slug-canonical, duplicates rejected at construction naming the offending slug, sexp round-trips. Which slug a day falls back to is a rubric and belongs to the rite module, so nothing here knows about ferias or Sundays.
* Merge branch 'ef-sanctoral-audit': the sanctoral against the MissalLukasz Kasprzak2026-08-147-5/+334
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The first full check of all 327 sanctoral entries against the primary calendarium, read from page images of both photographic scans rather than either PDF's OCR text layer -- the second scan's layer interleaves its two print columns row by row and is unusable. Findings that stand: St Barbara (4 December), a Commemoratio both scans carry with her own Mass propers, absent from colitur and from lectio's source alike -- 7196 clean additions, never displacing anything. And 18 colour corrections, all traced to the same bootstrap defect as the earlier eusebius-confessor fix, now shown systemic rather than isolated; several of lectio's own Polish name fields contradict its own colour tag. The Commemoration_only ranks are reframed rather than resolved: the calendarium carries 57 Commemoratio lines and not one gives a class numeral, for anyone. That is a source ceiling, not 105 open questions, and nothing a primary-source read can close. Three corrections did not survive review and are reverted here. RG 120(f) names Cathedrae S. Petri (22 februarii) and Conversionis S. Pauli (25 ianuarii) in its own enumeration of white, fifteen lines above the RG 124(b) they were cited to; RG 124(b)'s own 'in eorum die natalicio' excludes them independently; RG 475(e) confirms it a third time. Both oracles said white because both were right. The cause is worth more than the entries: the page-image method was applied to the calendarium and not to the rubric, and in the text layer the two-column interleave hyphen-splits Cathedrae so it does not grep. A text-layer read of that chapter finds 124 and cannot find 120(f). Two safeguards had been reporting this correctly and were overridden -- a golden pin asserting white, edited to red and rewritten to guard against restoring it, and two allow-lists told to ignore divergences where both oracles were correct. Both restored. Anastasia (25 December) was found and deliberately not added: she is scoped to the second of Christmas's three Masses and the data model has no Mass scope. Now recorded in the tracked tree, beside barbara, where the next auditor will be standing.
| * test,docs: tighten M21, and record what the bucket summary does not sayLukasz Kasprzak2026-08-141-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | M21 accepted `subset diffs [Colour_f; Comm_identity_unresolved]`, so a row with no colour diff at all could be absorbed under a colour citation. The unresolved allowance existed only for the 22 February rows the previous commit reverted; with those gone the predicate tightens to exact equality on Colour_f, matching C18's shape, and stays green. Records two things the audit's bucket summary did not carry. The effective dataset is 329, not 328: base 327 plus three overlay Adds minus one Suppress, so commemoration-of-st-peter and major-litanies fall outside both buckets despite being in the stated scope, and a suppressed entry is counted as verified. And the summary says colour was "independently checked against RG 124" for every entry where much of it is inferred from the saint's category rather than read -- the per-entry basis column makes that distinction and the summary line erases it. Also records that "zero blast radius" holds only for the observed office: 49766 commemoration-colour changes on 8 slugs are real data changes, invisible because the CLI omits commemoration colour.
| * fix(ef): three colour corrections that contradicted the Missal by nameLukasz Kasprzak2026-08-145-23/+74
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The sanctoral audit set chair-of-st-peter (22 Feb), conversion-of-st-paul (25 Jan) and peter (25 Jan, commemoration) to Red on RG 124(b). All three are reverted to White. RG 120(f), fifteen lines above RG 124(b) in the same column of the same page, names two of them in its enumeration of white: "Cathedrae S. Petri (22 februarii); Conversionis S. Pauli (25 ianuarii)". RG 124(b)'s own qualifier "in eorum die natalicio" independently excludes them, and RG 475(e) confirms it a third time by naming the Chair separately from "natalician feasts of Apostles". lectio and missalemeum both said white because both were right. peter rested on "match the host day's Red", whose host is white by that same clause, so the precedent inverts. Root cause: the audit's page-image method, its real advance, was applied to the calendarium and not to the rubric. RG 124 was read from the OCR text layer, where the two-column interleave shreds RG 120(f) across rows belonging to RG 115 and hyphen-splits Cathedrae, so a text-layer grep for it returns nothing while RG 124's enumeration survives cleanly. That read finds 124 and structurally cannot find 120(f). Two sentinels had been reporting this correctly and were overridden. The golden pin at 1902-02-22 asserted colour=white, was changed to red, and had its comment rewritten to guard against "a regression back to the old one" -- the only pin asserting a colour for any of the 21 slugs. And C18 and M21 both listed the two slugs under an RG 124(b) citation where both oracles were right. Pin restored; slugs dropped; C18 re-derived 526->450 from the observed-day counts, M21 20->18, M15 17->18. The audit's own discriminator would have isolated these. Its stated corroboration was that lectio's name.pl contradicts lectio's colour; run as a gate across all 21 that splits 17 genuine defects from 4 needing adjudication, and three of those four are these. It was quoted as illustration rather than used as a gate. 18 of 21 corrections stand, and the twelve whites are recited to RG 120(e) "Sanctorum non Martyrum" rather than to RG 117's negative residual. Also records Anastasia's deliberate non-fix in the tracked tree beside barbara, where the next auditor will be standing.
| * data(ef): sanctoral audit — one genuine gap, 21 wrong colours (RG124)Lukasz Kasprzak2026-08-147-5/+283
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Full re-extraction of the primary CALENDARIUM (both photographic scans, missale-romanum-1962.pdf and Missale Romanum 1962_text.pdf, rendered page images read directly rather than trusting either PDF's own OCR text layer) against all 327 base sanctoral entries plus the two prior overlay Adds. Both scans agree throughout; no OCR ambiguity required adjudication this round. Add `barbara` (4 December): both scans carry a Commemoratio nested under Peter Chrysologus's own entry, absent from data/ef/sanctoral.sexp AND from lectio's own tridentine-calendar.ini (confirmed directly) — the same upstream-gap shape as the five entries the ef-rebootstrap task already added (agnes-secundo/boniface-martyr/eusebius-confessor/evaristus/ theodore). Correct 21 sanctoral colours against RG 124 ("De coloribus paramentorum"), scan-verified word for word: 124(b) red for an Apostle/Evangelist's own feast day except John's (27 December, already correct); 124(e) red for a Martyr's own martyrdom; RG 117's five-colour residual otherwise white. Every one of the 21 is a confirmed lectio/missalemeum bootstrap defect — checked individually against lectio's own ini, several of whose Polish name fields already contradict that same record's own colour tag (e.g. `martha`: "Dziewicy", Virgin, no "Męczennicy", yet `colour = red`) — the identical root cause and mechanism the `eusebius-confessor` fix already established. Colour is confirmed read nowhere in Precedence_ef's resolution logic (grep), so these are output-correctness fixes with zero observed-office blast radius; three (apollinaris/josaphat/chair-of-st- peter) additionally disagree with missalemeum's own oracle, not only with lectio, so they are adjudicated from RG 124's text directly rather than by which engine happens to agree. Both corrections applied via data/ef/adjustments.sexp's established Overlay mechanism (data/ef/sanctoral.sexp stays generated, untouched). Pinned: test_rite_ef.ml (two new tests, verified to fail against the pre-fix data); test_differential.ml's new C18 predicate (526 rows, 2005-2050) and test_oracle.ml's new M21/M22 predicates (20 + 2 rows, 2026-2027), each with data/ef/expected-divergences{,-missalemeum}.sexp entries citing RG124 or the matching upstream-gap precedent; test_golden.ml's 1902-02-22 pin corrected (chair-of-st-peter was white in the stale pin, RG124(b) makes it red). All 329 tests green, including the full 1583-9999 exhaustive sweep. Full method, per-entry adjudication table and corrected bucket counts: docs/research/rules-register.md (gitignored) and .superpowers/sdd/2026-08-12-colitur-rg16a/sanctoral-audit-report.md (gitignored).
* Merge branch 'ef-major-litanies': the Major Litanies, and a kernel bugLukasz Kasprzak2026-08-1311-162/+1072
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Builds the Major Litanies (RG 80, 81, 109(f)) -- an item deferred three times, each time on a reason that did not survive contact. Two recorded blockers were false (a kernel signature said to be missing already carried what was needed; Easter Monday, said to be unmarkable, occurs exactly 8417 times in 8417 years) and a displacement estimate of ~97.7% of years measured at 4 years in 46. 8611 days change in four shapes, zero unclassified: the commemoration appearing on ordinary 25 Aprils, displacing St Mark's own on the four II-class Sundays RG 111(b) governs, and relocating to the following Tuesday in the 194 years RG 80's transfer clause fires. No day outside 25/26/27 April differs at all, and observed, rank, colour, season, week and transferred_in are byte-identical across all 3074246 days. The entity is a Commemoration_only sanctoral entry, which honours RG 81 structurally rather than by special case: Precedence.resolve partitions such candidates out of the band contest, so 'nihil fit in Officio' holds by construction, and being sanctoral-origin it can never claim RG 113's de-Tempore first place. And it surfaced a latent kernel bug. Calendar.build_day decided whether a transferred candidate had settled by testing only whether it became that day's observed celebration -- correct for every prior use, a losing feast that wins its unblocked target, and wrong for a Commemoration_only candidate, which can never be observed by the same status that lets it transfer. The first fix accepted two settlement channels and the review found a third: a candidate capped out by admit at its target. That third case was already visible inside the task's own mutation proof, as an undiagnosed ninth failure it had set aside. The fix is a provable no-op in isolation: a3afb19 plus the new calendar.ml alone, swept over every day of the domain, is byte-identical to base.
| * docs: a confidence raised while its revisit trigger was deletedLukasz Kasprzak2026-08-133-3/+27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Five follow-ups from the fix-round re-review, none touching a computed result. M20's note had said "moderate-high, not certain" with a specific revisit trigger attached. The fix round raised the label to "near-certain" and deleted the trigger in the same edit. Upgrading a confidence while removing the condition that would lower it again is the one move this record must not make, so the trigger is restored alongside the higher label: no primary text anywhere names the Major Litanies in a Mass-orations-count worked example, only the general II-class-Sunday rule twice over. RG 434(b) closes the Office-shaped doubt and nothing further. "WORD-IDENTICAL" overstated the relation between RG 111(b) and n. 434(b). Only the trailing "quae tamen omittitur si commemoratio privilegiata facienda sit" is verbatim in both; the openings differ, n. 434(b) recasting the rule into the orations register. The claim was self-refuting -- both texts are quoted adjacent to it -- and the argument never depended on it. Now "identical in its operative clause". And n. 434 is not "a different part of the same document": the running heads put RG 111 under Rubricae generales and n. 434 under Rubricae generales Missalis Romani, two distinct corpora bound in one volume, which is the entire force of the corroboration. The code comment had understated its own point. Also corrects the register's LT line range for n. 434(b) (3574-3576, not 3564-3570; the (b) clause is not in the cited range), and records in calendar.ml the diagnostic that channel (3) trades away: a rite whose transfer_target names a date its own disposition omits used to raise a loud, mislabelled Validate failure and is now silent at the origin. The kernel cannot distinguish that from a deliberate omission without rite knowledge it must not have, so accepting it is right -- but the signal is gone, and that should be written down rather than discovered later.
| * docs(ef): fix round 1 corrections (F2-F5) -- inert fields, a date, an ↵Lukasz Kasprzak2026-08-133-6/+45
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | attribution, RG434(b) F2: the task report's "colour/subject/names on the Litanies entry are currently inert" was false for names -- test_oracle.ml's identity axis reads Names.find on every commemoration, and the invented English name is exactly why 2026-04-25 registers as a Comm_identity_mismatch, the reason M5 had to be re-gated. Corrected precisely, field by field, in the report. F3: Easter 2027 is 28 March, not 18 April, as previously stated in the report's own "layer 4 is blind to the transfer" reasoning. The conclusion survives (28 March is not 25 April); the date is corrected. F4: "the exhaustive property sweep caught this immediately" was a wrong attribution, in the report, the register, and calendar.ml's own comment. The committed exhaustive sweep walks 1583-9998 in order and aborts at the first failure; the years actually reported (5700, 4747) were draws from prop_invariants, the default dune test run's 200-year random sample. Corrected in all three places. F5: the Litanies' privileged commemoration is due in the MASS specifically (RG 108 + RG 81), never a separate Office answer -- colitur emits one resolved day, and the M20 adjudication rests on that being the Mass reading. Stated explicitly now in M20's own note, the register, and precedence_ef.ml's own RG111(b) comment. Also added to M20 and the register: Rubricae Generales Missalis Romani n. 434(b) ("VIII - De diversis Missae partibus", "D) De orationibus", "I - De orationibus in genere"), verified word for word against all three documents -- word-identical to RG 111(b) but explicitly scoped to the Mass ("post orationem Missae"), answering the objection that RG 111 (Caput XVI, which RG 106 says governs both Office and Mass) might be read as Office-shaped. Moves the M20 adjudication from "moderate-high, not certain" to near-certain.
| * fix(kernel): a transferred candidate can settle by being capped out, too ↵Lukasz Kasprzak2026-08-132-26/+168
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (fix round 1, F1) The prior fix (settled_at) recognised two settlement channels for a transferred candidate at its target -- winning outright (observed) or surviving as a commemoration -- but missed a third: reaching the target and then being CAPPED OUT there, by admit's own RG-111-style admission count limit or by disposition's own Omit. That candidate lands in the target's own omitted list, genuinely settled and accurately labelled, but settled_at did not check that list, so the origin reported it as unresolved under the same wrong, hardcoded unconverged_reason -- the exact original bug, one level further out. Unreachable on shipped EF data (the Major Litanies are the only privileged Commemoration_only candidate real data carries, and no second one can ever share Easter+2), but reachable by construction: a second privileged Commemoration_only entry on the Litanies' own transfer target that outranks it in admit's Class1 selection, or -- without any synthetic data -- forcing the Litanies' own RG 109(f) privilege to Ordinary, which makes the transferred candidate lose that same cap against its own real target. Fixed by adding target-omitted membership as a third disjunct in settled_at. New regression test in test_calendar.ml, built the same way: the real EF layer plus one synthetic privileged Commemoration_only entry on the real 2011 transfer target, sorting ahead of the Litanies so it wins the Class1 slot. Mutation-verified to fail specifically when the third disjunct is removed. COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force stays clean after the fix, confirming it changes no shipped day's output.
| * docs: CLAUDE.md for the Major LitaniesLukasz Kasprzak2026-08-131-81/+96
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Records RG80/109(f) as RESOLVED (was "NARROWED, deliberately not built"), corrects a mis-citation carried in that entry (RG111(b), not RG111(c), is the clause that makes a privileged Litanies commemoration displace St Mark's own ordinary one), and narrows the shared "no channel for a movable, Easter-relative commemoration candidate" blocker to Rogation Wednesday alone -- the Major Litanies' own version of that blocker dissolved by reusing the existing RG96 transfer machinery, a route not available to Rogation Wednesday (nothing there is being transferred FROM a fixed civil date). Adds a "know what each layer cannot see" bullet: layer 3 (lectio) is entirely blind to this whole change; layer 4 (missalemeum) sees the entity and the RG111(b) question but is blind to the transfer specifically, confirmed by mutation, since neither year in its 2026-2027 fixture window is a trigger year. Updates the stale Plan-3-era test count and the allow-list summary (expected-divergences-missalemeum.sexp now 12 active entries, M5 corrected, M20 added, M18's count adjusted).
| * test(ef): oracle allow-list for the Major Litanies, and a source-fidelity ↵Lukasz Kasprzak2026-08-132-23/+119
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | correction M5 (the pre-existing register-cited gap this task closes) had a genuine backwards claim in its own note: it said missalemeum's single admitted commemoration on 25 April 2027 (the Sunday-conflict year) was "the Major Litanies". Checked directly against the raw fixture row while adjudicating this task's own new divergence -- missalemeum's own commemorations field there is "St. Mark"; "Pro rogationibus" (the Litanies) is the one listed as displaced. Corrected in place, both the .sexp entry and this file's own header note. M5 itself now covers only the ordinary case (25 April 2026): presence agrees now that the entity is built, identity does not (colitur's own English name vs missalemeum's Latin-ish descriptive string), the same declared comparator limit M15/M18 already document, hit here for a different reason. The real, substantive divergence the correction exposes: colitur now admits the Litanies on 2027-04-25 where missalemeum admits Mark. New entry M20, adjudicated colitur -- RG 109(f) places the Litanies in the same closed privileged list as (a)-(e), and RG 111(b)'s own text is unconditional ("the II-class commemoration is dropped if a privileged one is due", no table-order qualifier). The same mechanism already primary-source-verified and used for other RG 109 categories elsewhere in this codebase; honestly flagged as the first real, non-synthetic test of this specific admit clause, consistent with this oracle's own already-documented pattern of RG 108-111 gaps (M1, M8, M10). M18's own count moves 395 -> 394 (2027-04-25 no longer falls into "observed- identity-unresolved alone"; it moved into M20), traced to the specific date and mechanism, not merely re-baselined. data/ef/expected-divergences.sexp (the lectio allow-list) needed no change: lectio computes no Major Litanies at all, and its own row type carries no commemorations field -- layer 3 is entirely blind to this change, confirmed rather than assumed.
| * test(ef): commemoration, transfer, and privilege coverage for the Major LitaniesLukasz Kasprzak2026-08-133-14/+301
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | test_precedence_ef.ml: a privilege_cases row for RG109(f), deliberately rank Class4 so it cannot pass via the pre-existing "of a I-class day" branch by accident. Three disposition_cases rows: the two RG80 trigger shapes (Transfer, not Commemorate) and a synthetic third row proving the winner-slug guard is specific to the two named trigger days, not "any I-class winner". Two transfer_target unit tests, both using a pathological always-blocking occupant to prove no RG96 search runs at all for this candidate -- a much stronger witness than a cooperative occupant, which could pass by accident. test_golden.ml: three new pinned tests through the real data pipeline -- the ordinary case (2026-04-25, matches the oracle's own M5 date), the four Sunday-displacement years in 2005-2050 (2010, 2021, 2027, 2032, RG111(b)), and the Easter-Monday transfer shape (2011, complementing the Easter-Sunday shape already pinned by the existing 1666/2038 Easter-extreme tests, now widened to include the transfer and its target). test_rite_ef.ml: the existing "no transfer lands inside the Easter octave" property gets a named, cited exemption for the Major Litanies (RG80's own deliberate exception), plus a positive companion test asserting the exemption is not vacuous (exactly two landings in 2005-2050, both Easter+2). That companion test was written but never registered in its own suite list -- caught while mutation-testing the transfer (a mutation that should have failed it passed silently); fixed by registering it. Mutation-tested, each of the three parts separately (recorded in the task's own report): moving the data entity off 25 April fails 7 tests (golden + oracle only -- zero unit tests, since they build synthetic candidates); corrupting the transfer's winner-slug guard fails 6 (the two disposition_cases rows, 3 golden pins, and the newly-registered transfer-count test); flipping the privilege to Ordinary fails 9 (privilege_cases, the disposition SYNTHETIC row, the oracle's count pin, 5 golden pins, and the exhaustive Validate property sweep).
| * feat(ef): the Major Litanies -- entity, RG80 transfer, RG109(f) privilegeLukasz Kasprzak2026-08-133-37/+309
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | RG 80 (Caput X, "De Litaniis maioribus et minoribus", A): the Major Litanies are assigned to 25 April, transferred to the following Tuesday (always Easter+2) when that date is Easter Sunday or Easter Monday. RG 81, same division: nothing is done in the Office, only in the Mass. RG 109(f) (Caput XVI): a privileged commemoration. Entity: Commemoration_only, Fixed(4,25), via data/ef/adjustments.sexp's `Add major-litanies` -- RG 81 denies it Office standing entirely, which is exactly what Commemoration_only already means to Precedence.resolve (held out of the observed contest by construction). Rank Class4 deliberately, not Class1, to keep the new RG109(f) privilege branch live rather than shadowed by privilege_of's existing "of a I-class day" branch. Colour Violet by consistency with the sibling Minor Litanies; subject Saint, the same "no special exclusion" default this codebase's other Commemoration_only entries use. Names not primary-sourced, flagged as such in the data file's own comment. Transfer: reuses the existing RG 96 placement machinery (Precedence.disposition's Transfer constructor + Calendar's place_transfers/resolve_with_injected) rather than a new candidate channel -- RG 80's own transfer is structurally the same operation RG 96 already performs for an impeded I-class feast. disposition gains a branch, checked before the Commemoration_only catch-all: the Litanies losing to winner slug "ef-easter-sunday" or "ef-easter-1-monday" is Transfer, not Commemorate. transfer_target gains a branch, checked before the general RG 96 search is even computed, returning Easter+2 directly with no search at all -- necessary, not merely simpler, since Easter+2 is itself I-class and a search from there would walk past the exact day RG 80 names (a commemoration needs no unoccupied day the way a displaced feast does). Privilege: privilege_of's previously-dead RG 109(f) branch now recognises the Litanies' own slug. No other code change -- admit's existing Class1/Class2-Sunday branches already implement "a privileged commemoration categorically takes the slot" correctly, from an earlier task. Full blast-radius measurement, adjudication of the resulting missalemeum divergence, and the mutation proofs are in the task's own report, .superpowers/sdd/2026-08-12-colitur-rg16a/major-litanies-report.md.
| * fix(kernel): a transferred candidate can settle as a commemoration, not only ↵Lukasz Kasprzak2026-08-131-5/+40
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | as observed Calendar.build_day's `unresolved` check decided whether a Transfer-disposed candidate had genuinely settled at its target by checking only whether it became that day's own `observed` celebration. That was correct for every prior use of Precedence.Transfer: a losing FEAST, which RG-96-style rules guarantee an unblocked target to win outright once it arrives. It is not correct in general. A rite's rules are free to dispose a Celebration.status = Commemoration_only candidate as Transfer too (the EF Major Litanies, RG 80, do exactly this) -- and such a candidate can never become `observed` anywhere, by the same status that makes it eligible to transfer in the first place. The old check mislabelled a cleanly-settled transfer of that shape as "did not converge" (a hardcoded string, not a real read of the placement pass's own convergence) and double-counted it in Validate's own duplicated-sighting check. Replaced with `settled_at`, which re-resolves the target date and accepts either `observed` or membership in that day's own admitted commemorations. A strict superset of the old check -- every existing use (a transferred feast winning its target) is unaffected -- and stays rite-agnostic: it reads only Precedence.resolution's existing fields, no EF-specific knowledge added to the kernel. Found by the exhaustive property sweep (COLITUR_EXHAUSTIVE_SWEEP=1) the moment a rite first produced this shape, not anticipated in advance.
* Merge branch 'ef-bvm-saturday': the Office of Our Lady on SaturdayLukasz Kasprzak2026-08-1310-87/+1067
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Builds RG 91 entry 27 under RG 78 -- 'In sabbatis, in quibus occurrit Officium de feria IV classis, fit de sancta Maria in sabbato' -- a rank test, not a season list. band had been assigning entry 27 312966 times across the domain while the office was never constructed, so an otherwise-unoccupied Saturday got a bare ferial slug and its season's colour. 75863 days change, in two shapes: colour (green or violet to white) on 74642, and colour plus a dropped commemoration on 1221. The 312966 reconciles to the day across four buckets, 210808 of them impeded by a sanctoral winner. The first version introduced a live RG 112(d) violation on every Saturday falling on 16 July: the Office of Our Lady observed, commemorating Our Lady. Both photographic scans print a rubric at that Mass for exactly this collision -- 'Missa dici potest aut de sancta Maria in sabbato, aut propria' -- absent from the electronic transcription, and cited nowhere. It was missed because the check was a grep for subject = Bvm, and Mt Carmel is tagged Saint: the rubric's criterion is whose intercession the oration implores, not colitur's own tag. The I-V Mass numbering is a propers rotation and stays unbuilt; citations remain empty on all 3073916 days. Recorded rather than glossed: the new rule has no comparison-layer witness at all, and lectio's fixture prints a Mt Carmel commemoration on 8 in-window dates where colitur now prints none -- a real divergence that falls through a gap in what the comparators examine. Time after Epiphany's 5015 office days have zero oracle witnesses and rest on the scan alone.
| * docs: an invented Caput, a list that was not closed, and a blind spotLukasz Kasprzak2026-08-133-7/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three corrections from the fix-round re-review, none changing behaviour. RG 431(e) was attributed to "Caput XX, De hymno Gloria in excelsis". There is no Caput XX -- the Rubricae Generales' Caput series ends at XIX. The paragraph number, letter and subsection title were all right; only the containing division was invented. It sits under "VIII -- De diversis Missae partibus", subsection "C) De hymno Gloria in excelsis". Shipping a new invented structural citation in the very round that corrected a source-fidelity overclaim is what the gate exists to catch. marian_slugs claimed TWO entries found and deliberately excluded after individual consideration, "not merely omitted". There was a third: vigil-of-the-assumption, 14 August, which is de B. Maria Virg. and whose collect implores her protection. It was merely omitted. Added to the list -- behaviourally inert, verified: it wins 7196 times domain-wide, always with the non-Marian eusebius-confessor as its only commemoration, and never loses -- but a closed list's whole value is its enumeration claim, and that claim was false as written. And the new rule has no comparison-layer witness at all. Reverting it reddens three tests, none of them a comparison layer: the differential's row type carries no commemorations, and the oracle's window has no 16-July Saturday. Worse, lectio's fixture DOES carry commemorations and prints +our-lady-of-mt-carmel on all 8 in-window dates where colitur now prints nothing -- a real divergence, neither fixed nor cited, swallowed by a blind spot, which binding decision 1 forbids. Recorded, with the note that colitur's reading here has no reference confirmation anywhere and only extending the oracle fixture to 2033 or later would settle it. Also records the RG 112(d) branch's ordering constraint: it precedes RG 95's Transfer branch, which it does not need to, and must move below it if a Class1 Marian entry is ever added.
| * docs(ef): fix round 1 citation corrections in temporal_ef.ml (F5b, F6)Lukasz Kasprzak2026-08-131-24/+53
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | bvm_saturday_names's own comment: the "Missae de S. Maria in sabbato" heading quote corrected from an overclaimed "both scans, word for word" to showing both scans' own raw OCR (noisy, and genuinely differing in abbreviation from each other) side by side, with the substance-only corroboration stated plainly. Colour citation tightened: RG 431(e) (the Missal's own classification of this Mass as a "Missa votiva IV classis... de B. Maria Virg.") -> RG 121(a) (votive Masses take the colour of the feast-type they correspond to) -> RG 120(b) (BVM feasts are white), replacing RG 120(b) alone as the primary citation -- this Office is not itself a festum, so RG 120(b)'s own "in Officio et Missa de festis" was a stretch as the sole basis. Conclusion (white) unchanged.
| * docs: fix round 1 ride-along findings (F2-F7)Lukasz Kasprzak2026-08-131-5/+36
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | F2: test_christmastide_feria_slugs's 26 December assertions removed -- St Stephen/John/the Holy Innocents (RG 67-70) always outrank a Class4 Saturday candidate there, in every year, so the office can never actually be observed on any day in the 26-28 December stretch. 3 January (no competing sanctoral entry, any year) used for the "-1-" stretch demonstration instead, alongside the pre-existing 10 January one. F3: test_differential.ml's shared row type gains a subject field (populated for colitur's own rows only; lectio's fixture has no such column). C17's own predicate now also requires subject = "bvm" -- defence in depth against a future bug producing a white Class4 Saturday candidate some other way, particularly in Christmastide/ Paschaltide where colour alone could not catch it. F4: Precedence_ef.band's entry-27 comment gains its RG 78 citation, replacing "per the historical default that fills it". F5: two report inaccuracies corrected -- band was already receiving a real candidate 312,966 times pre-change (what was missing was its identity, not its reality); the "Missae de S. Maria in sabbato" heading quote was presented as "both scans, word for word" when both scans are OCR-noisy there AND genuinely differ in abbreviation from each other -- corrected in temporal_ef.ml's own comment and the register alongside the report. F6: the colour rule gains a tighter citation chain -- RG 431(e) (the Missal's own classification of this Mass as a "Missa votiva IV classis... de B. Maria Virg.") -> RG 121(a) (votive Masses take the colour of the feast-type they correspond to) -> RG 120(b) (BVM feasts are white) -- reaching the same conclusion through the category (votive) the Missal's own text actually places this Mass in, not by treating it as a festum RG 91's own table structure says it is not. F7: the oracle's real seasonal coverage recorded precisely in CLAUDE.md and the register -- of 26 BVM-Saturday-office days in the 2026-2027 fixture only 17 are colour-discriminating; Time after Epiphany (5,015 observed days domain-wide) has zero oracle witnesses. Full account in .superpowers/sdd/2026-08-12-colitur-rg16a/ bvm-saturday-report.md (local, gitignored).
| * fix(ef): RG 112(d) excludes a same-BVM commemoration (fix round 1, F1)Lukasz Kasprzak2026-08-135-27/+324
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The BVM Saturday Office is itself "de B. Maria Virg."; RG 112(d) (Caput XVI, "De Commemorationibus") excludes another commemoration whose own oration invokes the SAME BVM's intercession. our-lady-of-mt-carmel (16 July, Commemoration_only) was wrongly admitted as an ordinary commemoration alongside the Office on every 16-July-Saturday -- its own collect explicitly invokes "her" (eius) patronage, and both photographic scans print a rubric anticipating exactly this collision (an either/or of Mass texts, not a joint commemoration). Precedence_ef gains marian_slugs (a closed, hand-verified list of sanctoral slugs that are themselves feasts of the BVM) and is_bvm_office (true for subject = Bvm OR a marian_slugs member -- the disjunction is load-bearing: the Office is identified only by subject, since its own slug is deliberately reused from the ordinary ferial fallback; Mt Carmel is identified only by the list, since its own subject tag is Saint, not Bvm). disposition gains a new branch, checked before the pre-existing Commemoration_only catch-all, since the one live witness is Commemoration_only and that branch's own "always Commemorate" had no exception for this shape. Checked exhaustively against every Marian-named sanctoral entry: only Commemoration_only-status entries can ever reach this branch live (a Feast-status one always wins outright instead of losing). Of the two such entries in the data, only Mt Carmel is reachable -- our-lady-of-ransom (24 September) is provably unreachable: 24 September falling on a Saturday forces 1 September to a Thursday, which by construction makes 24 September the September Ember Saturday, Class2, every time, not a sampled coincidence. Re-measured against the full pre-change binary: the original 75,853-day blast radius is unchanged in total, splitting into 74,633 colour-only + 1,220 colour+comms (16-July Saturdays). Mutation-tested: exactly 3 new tests redden, nothing else.
| * docs: RG 91 entry 27 (BVM Saturday) resolvedLukasz Kasprzak2026-08-131-10/+73
| | | | | | | | | | | | | | | | | | | | | | | | | | CLAUDE.md's "Unbuilt, recorded" list and Data section move to reflect the closed gap: a new item 9 records the office's own build, citations and measured blast radius; the allow-list counts move to 7 active lectio entries (C17 new) and 11 active missalemeum entries (M2 closed). docs/research/rules-register.md (gitignored, not tracked) gets the matching update: §4's entry marked RESOLVED with a cross-reference, the §6 open-item checkbox closed, and a new §6.4 with the full primary-source account -- every scan reading quoted, the blast-radius reconciliation, and the mutation-test record.
| * data(ef): close and derive the RG 91 entry 27 allow-list rowsLukasz Kasprzak2026-08-134-49/+108
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The BVM Saturday office is now genuinely correct, so both comparators' allow-lists move -- every count derived from the harnesses' own failure output, never transcribed or guessed. lectio differential (data/ef/expected-divergences.sexp, C17, new, colitur): lectio builds no equivalent office at all, so it keeps showing the plain season colour on every otherwise-unoccupied Class4 Saturday where colitur now shows white. Season, slug and rank all still agree (the office deliberately reuses the ordinary ferial slug), so Colour_f alone is the complete diff set -- gated on colitur's own rank/weekday/colour rather than a date list, the same shape M2 used, at 416 rows over 2005-2050. missalemeum oracle (data/ef/expected-divergences-missalemeum.sexp): M2 is CLOSED, REMOVED, the same shape as this file's own M4/M6/M7/M9/M12/ M14/M17 precedents -- not re-adjudicated to a different verdict, gone because the colour divergence it named no longer occurs on any of its former 22 dates. What is left on all 22 is Observed_identity_unresolved alone (colitur's BVM-Saturday candidate is temporal-origin and deliberately carries no English name) -- exactly M18's own shape, not a distinct citation any more. Keeping M2 as a separate id would have kept silently absorbing these rows under a stale "colour differs" citation whose own reason had stopped firing (subset's own diff-set test admits a SMALLER set than the one named) -- the same "count proving cardinality where identity was required" trap this project's review process watches for. M18's own expected_rows moves 373 -> 395 accordingly.
| * test(ef): reach RG 91 entry 27 through the real pipeline, end-to-endLukasz Kasprzak2026-08-132-6/+179
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Entry 27's band branch has been unreachable for the right reason since it was written: precedence_ef.ml's own comment on it acknowledged the row existed, but the one test exercising it built a hand-typed candidate, never a real Temporal_ef.temporal output, and nothing built the office itself. Closes that gap on both sides: - The band-table row now sources its candidate from Temporal_ef.temporal directly (of_temporal), the same review-finding-3 discipline the Ember/vigil rows already use, so a future regression in the office's own construction fails this row too, not only the unit-level test. - Two new resolve-level end-to-end tests, using the real band/ disposition/admit triple (not stubs): the office winning a real otherwise-unoccupied Saturday and admitting an ordinary commemoration alongside it (1 August 2026, Holy Machabees -- the task brief's own worked oracle example), and the office genuinely losing to a real competing feast and being omitted under RG 26, with no confusion from the one live data witness that also carries subject Bvm (12 September, Most Holy Name of Mary) -- proving RG 26's rank-keyed omission fires before any subject-keyed rule ever could. - Two golden pins mirroring the same two dates, checked by hand against `colitur day 2026` before being typed in, following this file's own "a golden test blesses whatever it is given, verified independently" discipline. The existing 2038-03-06 pin (RG 91 entry 24 outranking a Class4 candidate) is corrected in passing: that date is itself a Saturday, so the displaced candidate the pin's own omitted-list check names is now precisely entry 27's own shape, not entry 28's bare catch-all -- the win/lose outcome the pin proves is unchanged, only its own more precise citation. Mutation-tested (recorded in the task report): reverting is_bvm_saturday to a constant false reddens the dedicated unit test, the new end-to-end resolve test, and the 2026-08-01 golden pin, while leaving the 2026-09-12 "loses to a real feast" pin untouched, exactly as expected since that date's outcome never depended on the fix.
| * temporal(ef): the votive Office of the BVM on Saturday, RG 91 entry 27Lukasz Kasprzak2026-08-132-10/+301
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | RG 91 entry 27, "Officium sanctae Mariae in sabbato", sits in the Table of Precedence between entry 26 (III-class vigils) and entry 28 (IV-class ferias). Precedence_ef.band already routed a plain IV-class Saturday feria to entry 27's own band value, but nothing in Temporal_ef.temporal ever constructed the office itself, so an otherwise-unoccupied Saturday still surfaced with a bare ferial slug and its season's ordinary colour. Established from Caput IX of the Rubricae Generales, both photographic scans and the electronic transcription, word for word (no scan-vs-transcription conflict: RG 78/79 are General Rubrics prose, not the Mass-propers body text the transcription is documented elsewhere as missing almost all of): "78. In sabbatis, in quibus occurrit Officium de feria IV classis, fit de sancta Maria in sabbato. 79. Officium sanctae Mariae in sabbato incipit a Matutino et explicit post Nonam." RG 78's own protasis IS "otherwise unoccupied IV-class Saturday" -- every Saturday whose temporal candidate would otherwise be Class4, across every season that rank reaches (Christmastide, Septuagesima, Time after Epiphany, Time after Pentecost, ordinary Paschaltide). Nothing else was needed to decide "otherwise unoccupied": band's own entry-27 branch already reads rank/weekday on the temporal candidate unconditionally and only wins the day when nothing outranks it -- RG 78's own condition, decided by the existing occurrence machinery. Colour is white, unconditionally, per RG 120(b) ("Adhibetur color albus in Officio et Missa de festis: ... b) B. Mariae Virg."), not RG 119/127/ 128's seasonal rules -- this office is never "de Tempore" for colour purposes, so it overrides the season's own violet/green. The slug is deliberately left UNCHANGED (reused from whatever the generic <season>-<week>-<weekday> ferial fallback would already have produced), for two independent reasons: it matches the Sacred Triduum's own precedent (identified structurally, off rank/weekday, never off the slug), and a bespoke uniform slug would have broken Colitur_kernel.Validate's own slug-uniqueness-per-liturgical-year invariant, sighted zero times before this task -- confirmed directly by writing it that way first and watching the landmark-years test fail. Subject is tagged Bvm (currently inert for precedence -- only Lord is ever read by band/disposition/admit) and the name is the Latin "Officium sanctae Mariae in sabbato" (RG 91 entry 27's own table title and RG 79's own heading), deliberately not English, the same zero-circularity discipline Holy Family/Holy Name/the Sacred Triduum already established. The I-V numbered "Missae de sancta Maria in sabbato" (both scans) are a Mass-propers selection detail (RG 309(a): "iuxta temporum diversitatem") governing which readings/texts are said, not which office is kept -- colitur computes no citations yet (Plan 4), so this is deliberately not modelled here. Two pre-existing tests needed correction, not because they were wrong before, but because RG 78 genuinely changes their dates' own facts: a Saturday inside the Holy Name of Jesus window (3 January) and two Saturdays inside christmastide_feria_slug's own "-0-"/"-2-" stretches (26 December, 10 January) are all otherwise-unoccupied Class4 Saturdays, so they now legitimately carry the BVM office instead of staying a bare Temporal-subject feria.
* Merge branch 'ef-triduum-litanies': the Sacred Triduum's Latin identityLukasz Kasprzak2026-08-134-65/+354
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Gives Holy Thursday, Good Friday and Holy Saturday the names the Missal prints for them, taken from each Office's own running header: 'Feria V in Cena Domini', 'Feria VI in Passione et Morte Domini', 'Sabbato sancto'. Rank and colour were already correct; only the identity was missing, the same shape the Holy Family had. The slug is deliberately unchanged -- it is the lectionary key -- and the names are Latin, which avoids promoting an English value from the oracle the engine is checked against. Not 'Feria VI Parasceve': that phrase occurs zero times as a title in either photographic scan, appearing only inside the Johannine Passion text, and RG 132(a) writes 'feriae VI in Passione et Morte Domini'. Output is byte-identical across all 8417 years -- colitur day prints no name column -- but layer 5 does see it: describe gained name_la, and reverting the change reddens six pins. The Major Litanies (RG 80) and the Rogation Wednesday commemoration were investigated to the same depth, scan-verified, cited, and deliberately not built. Rogation Wednesday is genuinely blocked: Easter+38 coincides with the Ascension Vigil, so no Fixed date-spec can anchor it. The Major Litanies are tractable in the rite module -- the two blockers first recorded for them were false, and are retracted here -- but 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. That blast radius is unmeasured, and measuring it is the prerequisite for its own task.
| * docs: a name that matched no scan, and a blocker that was not realLukasz Kasprzak2026-08-134-21/+78
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| * docs: close/narrow the Triduum and Litanies open items in CLAUDE.mdLukasz Kasprzak2026-08-131-9/+74
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| * temporal(ef): the Sacred Triduum's own Latin identity, RG 91 entry 2Lukasz Kasprzak2026-08-133-56/+223
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Holy Thursday, Good Friday and Holy Saturday resolved to the generic Passiontide ferial slugs with no distinct identity of their own -- rank and colour were already correct (RG 91 entry 2; Holy Thursday's white per RG 128(b)/RG 122), only Celebration.names/subject were missing. Keeps the slug (RG 91 entry 2 is identified structurally, off rank and Easter offset, by Precedence_ef.band -- never off the slug -- so renaming it would touch precedence for no reason and break the lectio differential's own slug mapping for these three lectionary keys, the same reasoning already given for Holy Family/Holy Name). Carries the identity in Celebration.names instead, the same treatment those two established: Latin, not English, to avoid reading the oracle this axis is compared against. Names sourced from both photographic scans, word for word, corroborated by the electronic transcription's own table-of-contents-style listing at the identical three headings (three-way agreement, nothing to adjudicate): "Feria V in Cena Domini" (Holy Thursday), "Feria VI in Passione et Morte Domini" (Good Friday -- not the transcription's own informal "Feria VI Parasceve" used in passing elsewhere), "Sabbato Sancto" (Holy Saturday). Also tags subject = Lord, going further than Holy Family/Holy Name strictly required (they needed it only because band's entry 14 reads subject to outrank a colliding fixed feast; entry 2 here needs no such thing). Safe: disposition's RG 112(a) branch only fires when both sides of an occurrence are tagged Lord, and no Lord-subject sanctoral entry has a fixed civil date inside Holy Week's own movable range; any Class1 sanctoral entry landing there (e.g. a transferred Annunciation) reaches disposition's earlier, subject-blind Transfer branch first regardless. Verified against the full 1583-9999 domain: zero observed/commemorations/transferred_* difference traceable to the tag. test_golden.ml's describe gains a name_la field alongside subject, for the same reason subject was added: a names-only change is otherwise invisible to every pin whose rank/colour/slug are already correct (this task's own catalogued vacuity flavour #1). Six existing pins move from subject=temporal to subject=lord (the three Easter-extreme Holy Saturday pins, 2016's Good Friday, and the 2026 Holy Thursday/Good Friday pair); the rest gain name_la=- by construction (no other temporal-cycle candidate carries a name yet). Mutation-tested: reverting temporal_ef.ml alone (keeping the tests) reddens the new Rite_ef unit test plus five golden pins across two independent layers.
* Merge branch 'ef-holyname-rg110': the Holy Name of Jesus, RG 110Lukasz Kasprzak2026-08-1313-87/+1188
|\ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Builds the Most Holy Name of Jesus with the fallback RG 17(a) and the calendarium both specify -- 'secus die 2 ianuarii', 'vel, ea deficiente, die 2 ianuarii' -- which was unimplemented, so in 3619 of the domain's 8417 years a II-class feast did not exist at all. Also retags it as a feast of the Lord per RG 91 entry 14. Builds RG 110's inseparable Peter/Paul commemoration ('in numero orationum computando, pro unica habeantur') across the three calendarium pairs, and adds the 30 June companion, which was missing from lectio's source and from missalemeum's output alike -- corroborated in both photographic scans plus RG 124(c), and added through adjustments.sexp so the bootstrap's provenance stands. 14627 days change, in four shapes, none unclassified. The register's earlier 852-day estimate for RG 110 reconciles exactly as the Lent Ember subset of one shape: 288 + 284 + 280. RG 110(c)'s ordering was implemented backwards at first and corrected: 'huic orationi additur altera' attaches the companion to the trigger's own oration, so it follows it, where (a) and (b) attach to the day's and so precede everything. The 'ante omnes alias commemorationes' half remains unimplemented and is unreachable by construction -- shape (c) needs the trigger to lose while still being admitted, which only a I-class day or a II-class Sunday allows, and both RG 111 caps are one. Records a fifth way the validation layers can be green while wrong: nothing anywhere compares commemoration order.
| * test,docs: RG110(c)'s adjacency is unobservable by constructionLukasz Kasprzak2026-08-132-5/+61
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The re-review asked for a third commemoration in the shape-(c) row, so that "spliced immediately after its trigger" could be told apart from "appended at the end" -- it had proved an append-at-end variant passed the whole suite green, the pair alone being too short to distinguish them. Attempting it establishes something better than the test requested. On this day-shape a third commemoration is rubrically impossible. Shape (c) requires the trigger to lose the day while still being admitted, and a Class2 trigger can only lose to a I-class day (RG 111(a): one privileged commemoration) or a II-class Sunday (RG 111(b): one, "scilicet de festo II classis"). Both caps are one. The third candidate is now offered and correctly refused, which is the assertion worth having. So RG 110(c)'s "ante omnes alias commemorationes" half has no reachable witness in the EF's rubrical space, and the re-review's own finding that shape (c) never co-occurs with a third commemoration across 8417 years is a consequence of the rubric rather than an accident of the data. The append-at-end variant stays indistinguishable from the splice on every input the engine can construct. Recorded rather than papered over with a synthetic row proving a shape no rubric permits. Also drops Record from the "nothing compares commemoration order" note -- Record.t has no commemorations field at all -- and names, at privilege_of's own (a) branch, that the Holy Name's Sunday privilege currently rests on a substring match rather than on RG 17's closing paragraph, which is its real warrant.
| * fix round 1: RG 110(c)'s ordering was inverted; M19 lacked an identity gateLukasz Kasprzak2026-08-137-61/+262
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | RG 110(c), word for word: "quoties vero oratio unius Apostoli addenda est ad modum commemorationis, huic orationi additur altera immediate, ante omnes alias commemorationes." "Huic orationi" (dative, "to this oration") refers to the trigger's own oration; "additur altera" means the companion is added to it, i.e. follows it. "Ante omnes alias commemorationes" governs the pair's position relative to other, unrelated commemorations, not the pair's own internal order. The previous build prepended the companion uniformly in both RG 110 shapes, citing this same clause for both -- correct for (a)/(b) (the day's own oration, not a list member, has nothing to order against), backwards for (c), where the trigger is itself a list member and the companion belongs after it. Fixed in rg110_additions: shape (a)/(b) still prepends; shape (c) now splices the companion in immediately after its own trigger. Re-pinned the unit test that had asserted the wrong order, and corrected both rows' comments to cite the clause that actually governs each shape. No layer in this codebase compares commemoration order: the lectio differential never compares commemorations, the oracle sorts both sides into a multiset, and the golden tests' own describe function sorts its commemorations field too. Recorded as a new, permanent blind spot in CLAUDE.md and the register, not merely fixed around -- the admit_cases unit table is the only place order is asserted at all. Re-measured the full blast radius against the same pre-change binary: same 14,627 differing days, same four shapes and counts, confirming the fix changed only the content of the 593 shape-(c) rows and nothing else. Mutation-tested: reverting the splice back to a uniform prepend reddens exactly the shape-(c) unit test; a new golden pin for a real shape-(c) day (2004-02-22) stays green under the same mutation, living proof that golden pins cannot see this axis, not merely a claim. Separately, M19's own predicate checked a date and a diff shape but not which candidate -- proved insufficient by fabricating a second, bogus companion on 30 June and confirming the whole suite stayed green. Fixed by requiring the admitted commemoration to be exactly commemoration-of-st-peter, mirroring C16's own identity guard. Added a golden pin for 30 June, the only assertion of that entry's identity outside the allow-list. Also: corrected a false claim in the task report that the electronic transcription carries no corroboration of the Holy Name fallback -- its own table of contents does, at a line not checked on the first pass. Added two further citations, both re-verified against the scan: RG 17's own closing paragraph, settling the Sunday shape's Sunday- exclusion at the rubric level rather than only in the propers; RG 124(c), which assigns red to 30 June by paragraph number rather than by the generic apostle/martyr default. Noted the fragility of deriving the Sunday shape's RG 109(a) privilege from a slug-substring match rather than from a citation. Cosmetic: a stray blank line and an indentation inconsistency.