aboutsummaryrefslogtreecommitdiff
path: root/data/ef
Commit message (Collapse)AuthorAgeFilesLines
* precedence(ef): RG 110, the inseparable Peter/Paul commemorationLukasz Kasprzak2026-08-132-8/+75
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | RG 110 (Caput XIV): "In Officio et Missa S. Petri semper fit commemoratio S. Pauli, et vicissim... pro unica habeantur" -- in the Office and Mass of either Peter or Paul, a commemoration of the other is always made, uncapped by RG 111's own admission count. Both photographic scans, full text including sub-clause (c) (a separate trigger this register previously had not transcribed: the same rule also fires when one Apostle's oration is admitted merely as an ordinary/privileged commemoration, not only when it is the day's own office). Three real pairs in the 1962 calendar, all confirmed on both scans: 25 January (conversion-of-st-paul + peter), 22 February (chair-of-st-peter + paul), 30 June (in-commemoratione-sancti-pauli-apostoli + a new commemoration-of-st-peter). The first two already existed as ordinary sanctoral candidates but were wired into RG 111's ordinary, capped admission contest instead of this rule's own uncapped one -- the defect this closes. 30 June had no companion at all: a genuine gap in lectio's own source data (confirmed directly against tridentine-calendar.ini) and in missalemeum's own oracle output for both fixture years, not only a colitur bootstrap miss, so colitur is now uniquely correct on this date among the three engines compared. Added via data/ef/adjustments.sexp's own Add directive, the architecture's existing extension point for exactly this shape of gap, since data/ef/sanctoral.sexp is generated and lectio must not be touched. Precedence_ef.admit gained rg110_additions/rg110_companion_slug, layered on after admit's own four ordinarily-capped branches decide their normal result: checks both the observed candidate and every already-admitted candidate against the three-pair lookup and prepends any companion found, uncapped and additional, never competing for RG 111's own slot. Measured full blast radius (1583-9999, pre-change binary from git archive at 59fbda8 vs the fixed binary, 3,074,246 days each side, 14,627 differing days, all four predicted shapes, zero unclassified): 3,619 Holy Name fallback days (previous commit), 3,533 days Paul was wrongly excluded from Chair of St Peter's own day by a competing privileged feria, 593 days Chair of St Peter itself was admitted only as an ordinary commemoration with Paul entirely absent, and 6,882 days the new 30 June companion is added. The 3,533+593 figure supersedes an earlier, narrower 852-day estimate that measured only the delta between two older code commits, not the rule's full scope. The lectio differential needed no change: it does not compare commemorations at all, and this fix never touches season, slug, rank, or colour. The missalemeum oracle allow-list needed three changes: M12 removed (22 February 2027 no longer diverges in commemoration count), M15's date list gained 2027-02-22 (now reaching identity comparison for the first time, where it lands on that entry's own pre-existing, unrelated limit -- one of the two commemorations is temporal-origin and unnamed), and a new M19 for the 30 June gap. Mutation-tested, both halves separately: disabling the admit-level addition reddens the two new unit tests, the oracle's own M12-shaped checks, and an existing golden pin (updated to expect the Paul companion); removing only the 30 June data entry reddens solely the oracle's new M19 count, confirming the code and data halves are independently load-bearing.
* temporal(ef): Holy Name of Jesus, RG 17(a) -- fallback + Lord subjectLukasz Kasprzak2026-08-131-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | RG 17(a): "festum Ss.mi Nominis Iesu, celebrandum dominica quae occurrit a die 2 ad 5 ianuarii (secus die 2 ianuarii)" -- both photographic scans, confirmed on the calendarium's own January table and the Mass propers' own heading. Two defects closed together, since they interact through RG 91 entry 14/RG 16(a): 1. The Sunday shape (2-5 January) now tags subject = Lord, matching RG 91 entry 14 ("Festa Domini II classis") -- previously subject = Temporal, the same defect shape Holy Family had before it. 2. The fallback (RG 17(a)'s own "secus die 2 ianuarii", the calendarium's "vel, ea deficiente, die 2 ianuarii") is now built: 2 January carries the feast whenever no Sunday falls 2-5 January that year. Before this fix colitur emitted no Holy Name office at all in such a year -- a real II-class feast simply missing, on 3,619 of 8,417 years in the domain (43%; 20 of the 46 years 2005-2050, independently re-derived against `date -d`). Both shapes band identically at RG 91 entry 14's MOVABLE half (Precedence_ef.entry_14_movable_band) -- one feast, not two differently classified ones, per the Mass propers' own single heading covering both dates. No live collision exists in the current sanctoral data (nothing fixed-date falls 2-5 January besides a Commemoration_only entry on 5 Jan), so the RG 16(a)/RG 112(a) interaction is proved with synthetic precedence-level fixtures mirroring Holy Family's own precedent, not a live-data regression. One asymmetry the synthetic tests found and kept honest rather than forced: a losing Holy-Name-Sunday is privileged (RG 109(a), "of a Sunday") and survives RG 111(a)'s cap; a losing Holy-Name-fallback is not (2 January is not a Sunday, and no other RG 109(a)-(f) category applies to it), so it gets no commemoration at all if ever outranked -- both individually correct readings of RG 109/111's closed lists. Golden pins follow the brief's own vacuity warning: the Sunday shape already emitted the right slug/rank/colour before this fix (an ordinary Sunday and Holy Name coincide on those fields), so its pin asserts subject explicitly rather than pinning the whole line uncritically; the fallback pin (2029-01-02) is the one with teeth against the larger defect, reddening on slug/rank/subject together if reverted. Mutation-tested: each half of the fix reverted separately reddens exactly its own tests (Sunday subject: the dedicated unit test, the golden pin's subject field, one precedence end-to-end test; fallback: the fallback unit/anchor/golden tests, the new C16 differential count, and both shapes of the precedence end-to-end tests) and nothing else. Adds data/ef/expected-divergences.sexp's C16 (lectio has no fallback for this window at all) -- the lectio differential's own count, independently re-derived against `date -d` for the fixture's 2005-2050 span, not transcribed from the domain-wide figure. The missalemeum oracle window (2026-2027) exercises only the Sunday shape and needed no new entry: subject is not one of its compared fields, and both years' rows already corroborate the Sunday-shape ranking (missalemeum's own 2026-01-04 and 2027-01-03 rows: rank 2, white, title "Holy Name of Jesus").
* test(oracle): fix round 1 (F8) -- correct the M18 headline, name the blind ↵Lukasz Kasprzak2026-08-131-1/+1
| | | | | | | | | | | | | | | | | | | | spot precisely The full breakdown, precisely: of 730 days, 331 are resolved (330 matching, 1 mismatched -- M13, Joseph vs the Seven Sorrows), 399 unresolved -- 373 in M18's own bucket, 26 absorbed inside M1/M2/M3/ M16's own widened subsets where the axis fires alongside their pre-existing citation. M18's own note previously stated only its own 373 without this fuller context. Also names the blind spot the axis exists to close in exact terms, not just "the observed day's identity": a TEMPORAL-origin observed day silently replaced by a DIFFERENT temporal-origin observed day of the SAME rank and colour -- exactly Holy Family's own shape, and the reason every prior layer stayed green through the whole gap. States explicitly what the count pin does and does not prove: it guards against the 373/399 population moving silently in either direction, but cannot say which specific day moved or why.
* data(ef): fix round 1 (F6) -- restore C1's provenance record for 361Lukasz Kasprzak2026-08-131-1/+1
| | | | | | | | | | | The previous refresh's own note recorded HOW 368 was derived (a Python cross-check against the comparator's own failure output, not hand-counted and not copied from a green run); this task's rewrite explained WHY 361 replaces it (C15's own split) but dropped the provenance sentence, stating 368 -> 361 with no record of how the new number was checked. Restored: 361 read directly from the OCaml comparator's own failure output, with the 368-minus-7 arithmetic noted as corroboration, not the derivation.
* test(oracle): green -- widen five entries, add M18 for observed identityLukasz Kasprzak2026-08-131-2/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | layer_m_reason's own five entries whose diff shape was checked by EXACT list equality (M1, M2, M3, M13, M16) lost their match once the observed-identity axis widened every row's diff set two commits ago. Each is widened the same way commemoration-identity was already widened for M13 once before: the axis now ALSO fires alongside their existing citation, same root cause, same id -- M1/M3 (temporal-origin observed day, no name), M2 (same, plus five Paschaltide dates that used to match cleanly and now surface for the first time), M13 (Joseph, sanctoral, now a genuine title MISMATCH, not merely unresolved), M16 (the temporal Passiontide feria observed there). M18 covers the remainder: 373 of 730 days whose observed celebration is temporal-origin and so carries no English name at all -- the overwhelming majority of the window, including Holy Family's own two dates in it (2026-01-11, 2027-01-10), counted here rather than shown as a false Matched. Gated on the diff SHAPE alone (this file's own convention for a population this size, the same reasoning M2's own title-substring predicate already uses instead of a date list), verdict unresolvable -- the same fourth verdict M15 already established for the identical limit on the commemoration axis. Building an English name onto the temporal cycle is Plan 4's own scope, register §6. Teeth, per the brief: temporarily corrupted hilary's own English name in data/ef/sanctoral.sexp (14 January 2026, an OBSERVED sanctoral day, rank and colour left untouched) and reran. Reddened immediately: 2026-01-14: observed-identity-mismatch differ -- colitur=(rank=3 colour=w ...) oracle=(rank=3 colours=[w] title="St. Hilary" ...) rank and colour agree on both sides -- exactly the shape the pre-strengthening comparator would have slept through completely. Reverted (git diff on the data file is empty) before this commit.
* test(differential): split the Holy Family/Baptism collision out of C1 (C15)Lukasz Kasprzak2026-08-131-2/+7
| | | | | | | | | | | | | | | | | | | | | | | Before this task's Holy Family fix, colitur's generic Sunday fallback lost outright to the fixed Commemoration of the Baptism of the Lord on 13 January whenever it fell on a Sunday, producing the SAME slug lectio's own tridentine-calendar.ini shows there -- squarely inside C1's own 6-13 January blanket (only Season differed). Now that Holy Family (RG 17(b)) correctly outranks the fixed Baptism and excludes it as a commemoration (RG 91 entry 14, RG 112(a)), colitur's own slug on those seven dates (2008, 2013, 2019, 2030, 2036, 2041, 2047) changes to ef-time-after-epiphany-sunday-1 -- a genuine identity divergence against lectio, which has no Holy Family at all. C1's own predicate does not absorb it (jan_6_13_slug has no entry for that slug), so it surfaced as unexplained, exactly the check the task brief asked for: split it out into its own cited entry rather than widening C1's predicate to swallow it silently. C1's own expected_rows moves 368 -> 361 (back to its pre-rebootstrap value, for a third and different reason than either of the first two moves recorded in its own note); the new C15 covers the 7 rows, gated on the literal date list AND colitur's own slug, the same identity guard C1/C6/C8/C14 already apply.
* test(differential): refresh the lectio fixture, close 9 entries, add C14Lukasz Kasprzak2026-08-121-53/+33
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | lectio moved 2386a45 -> 3b32c00 (seven EF calendar defects fixed, plus a sanctoral-generator root-cause fix); the committed fixture predates all of them. Regenerated with the same command the provenance note already named (`go run ./cmd/lectio-ef-dump 2005 2050`), read-only against lectio -- nothing there was touched. New SHA-256 asserted in both the provenance note and test_differential.ml's fixture_sha256. The dumper's two new name_en/name_pl columns are kept verbatim (the fixture is meant to be the exact command's raw output; the comparator already discards everything past field 7). Every C-entry's expected_rows was re-derived independently against the refreshed fixture (a Python re-implementation of diff_fields/ layer_c_reason, cross-checked against the real OCaml comparator's own failure output), not transcribed from a green run. Nine entries -- C2, C3, C4, C5, C7, C10, C11, C12, C13 -- now match zero rows and are closed: sexp row removed, layer_c_reason predicate and its now-dead helper bindings removed, citation preserved in docs/research/rules-register.md's new §3d with the specific lectio commit that fixed each one. C1 (361 -> 368) absorbs C13's former 7 rows now that lectio's own slug matches there too. C6 (138, unchanged count) narrows to a slug-only shape now that lectio's rank agrees. C8 (31, unchanged) survives untouched -- lectio still computes no Rogation days. C9's old shape ("Joseph observed ON a Lent Sunday") is also closed, but a narrower, different divergence survived under the same saint's slug: 3 years (2008, 2035, 2046) where Joseph's own congested RG96 walk collides with the Annunciation's separately-transferred "sedes propria". The old predicate's broad "any diff involving this slug" match was silently absorbing both under one citation -- split into a new id, C14, rather than folded back into C9, since the two are unrelated rules. lectio's own commit history (09a1444) documents this residual as a gap it deliberately left unresolved; colitur gives Joseph a real transferred day either way, which is why C14's verdict stands regardless of a genuinely open sub-question (recorded in the register, not resolved here) about which of the two feasts should claim the contested day first under RG98's "prius impeditum" wording. Re-triaged the full remaining 4975 raw differences: only 10 distinct field-diff signatures now, all resolving to Layer A/B noise or the four surviving citations, 0 unexplained. The crude "Sunday-rank" and "Holy Thursday" residues the task flagged both fully resolve once the real Layer A/B machinery runs (season-spelling and week-numbering artifacts, not substantive divergences) -- see the task report for the derivation. Teeth proof: perturbing ferial_rank to reproduce C4's old defect made 140 rows reappear as unexplained (the exact prior C4 count), reverted after capturing the failure message. 277 tests green (dune test) and 278 with COLITUR_EXHAUSTIVE_SWEEP=1 (the extra case is the 1583-9999 sweep itself).
* data(ef): fix round 1 (F7) -- track the 5 new entries' primary citationsLukasz Kasprzak2026-08-121-2/+29
| | | | | | | | | | | | | | Removing M4/M6/M7/M9/M14 (an earlier commit) deleted the only TRACKED primary-source citations for these five entries: docs/research/ (where the full quotes originally lived) is gitignored, and data/ef/sanctoral.sexp is generated data with no citations field of its own, so the public repo held no record of why agnes-secundo, boniface-martyr, eusebius-confessor, evaristus and theodore exist. Added a provenance note quoting each one's calendarium line verbatim from the photographic scan. Also fixes a name omitted from the "FIVE MORE REMOVED" note's own list (boniface-martyr was missing; no other consequence, since neither the removal itself nor the row-count pin ever depended on that prose list).
* data(ef): fix round 1 (F3) -- eusebius-confessor is white, not lectio's redLukasz Kasprzak2026-08-121-1/+28
| | | | | | | | | | | | | | | | | | | | | | | | data/ef/sanctoral.sexp bootstraps colour=red for this new entry (14 Aug), inherited from lectio's own ini; missalemeum's fixture independently tags it :r too -- two non-primary witnesses agreeing with each other, not with the Missal, the exact "trap" this project's own transcription-audit task already named once. Checked directly against the scan (missale-romanum-1962.pdf), not taken on a reviewer's transcription: the 14 August proper reads "S. Eusebii Conf. / Commemoratio / Missa Iustus, ut in festo S. Pauli primi Eremitae, die 15 ianuarii" -- Conf. (Confessor, not Martyr), borrowing the SAME Mass 15 January's own Paul the First Hermit uses, a Confessor's Common, white in both engines' own data for THAT entry. Every other Confessor/Abbot commemoration in lectio's own data is white (maur-abbot, giles, remigius, didacus, ubaldus, hilarion, sabbas, silvester, alexis); Eusebius Confessor is the one exception, with no textual support found for it. No behavioural impact today (Commemoration_only entries are never the printed observed colour under the current pipeline), fixed via the overlay mechanism since sanctoral.sexp is generated and the source is upstream. New test proves it through the real pipeline, the same discipline already applied to the subject retags.
* test(differential): fix round 1 (F2, F5) -- split 13-Jan-Sunday into C13Lukasz Kasprzak2026-08-121-3/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 13 January retagged subject=Lord (previous commit's regeneration) makes RG16(a) fire on the 7 years it falls on a Sunday within 2005-2050 (2008, 2013, 2019, 2030, 2036, 2041, 2047): colitur now observes the Baptism of the Lord itself where it used to observe the generic Sunday slug. The diff signature against lectio's fixture widens from {season;colour} to {season;slug;colour} -- but jan_6_13_slug already pre-authorised this exact slug across the whole 6-13 January window (correctly, for the other 39 non-Sunday years), so layer_c_reason routed the new, wider-shape rows into C1 unchanged, and C1's own expected_rows (a pure row count) did not move. A count proving cardinality where identity was required -- on the one day this branch itself already flags as still wrong under RG 112. Split into its own entry, C13, matched by an exact 7-date literal list checked before C1 in the if/elif chain. C1's own expected_rows corrected 368 -> 361 (independently re-derived: a Python script reading data/ef/sanctoral.sexp's regenerated output and the committed lectio fixture directly, replicating diff_fields/layer_c_reason's own logic outside the OCaml comparator -- not transcribed from the failing comparator's own count). C13's own citation records a refinement over the coordinator's framing, checked directly rather than assumed: lectio's committed fixture is a frozen snapshot that predates lectio's own equivalent fix to this entry's class field (the same staleness shape as C12). Confirmed by building lectio's own cmd/lectio-ef-dump from its current source tree (read-only) and running it fresh for all 7 dates -- lectio's LIVE engine now agrees with colitur's slug/rank/colour exactly, disagreeing only on season (the already-understood C1 divergence). This is not evidence colitur's answer is right against the Missal -- it is not (register's own RG112 item) -- only that this specific divergence from the fixture is a staleness artifact, not a live disagreement between the two engines' current logic. Also fixes F5: C12's own note said "5 of the 15 corrected slugs" while listing and counting 7 -- corrected to 7. Also corrects the module header's stale "13 January ... slug/rank/colour already equal lectio's exactly, in all 46 years" claim (true only for the 39 non-Sunday years now).
* test(oracle): close M4, M6, M7, M9 and M14 -- the five now-fixed data gapsLukasz Kasprzak2026-08-121-25/+23
| | | | | | | | | | | | | | | | | | Each was recorded as a genuine data gap "traced upstream into lectio, unfixable without touching lectio". That constraint is gone: all five commemorations (St Agnes secundo 28 Jan, St Eusebius Confessor 14 Aug, St Evaristus 26 Oct, St Theodore 9 Nov, St Boniface Martyr 14 May) are now present in the re-bootstrapped sanctoral.sexp, and each divergence stops occurring in the 2026-2027 oracle fixture -- verified by re-running the comparator and confirming zero rows for all five ids, not assumed from the data change alone. Removed rather than kept as dead 0-row citations, matching the M17 precedent already in this file: the count pin (test_layer_m_counts_match_citations) now enforces that none of these five divergences can silently reappear. Corresponding dead code (m4_dates .. m14_dates and their layer_m_reason branches) removed from test_oracle.ml; a short pointer comment left at each id's old location.
* test(differential): allow-list C12 for the 15-entry rank correctionLukasz Kasprzak2026-08-121-0/+5
| | | | | | | | | | | | | | | | | | | | | | | Re-bootstrapping sanctoral.sexp (previous commit) makes 7 of the 15 newly-corrected III-class feasts win an occurrence they previously lost as Commemoration_only: RG 91 entry 24 (a universal III-class feast) outranks entry 28 (a IV-class feria), and the occurrence code that resolves this already existed and needed no change. lectio's own committed differential fixture (test/fixtures/lectio-ef-2005-2050.txt) is a frozen snapshot predating lectio's own equivalent generator fix, so it still shows the class-4 feria observed with the same saint only as a trailing commemoration -- a new, expected divergence from that fixture, 14 rows in the 2005-2050 window. expected_rows (14) is independently re-derived by grepping the regenerated sanctoral.sexp's own post-regeneration civil-day output against the committed lectio fixture directly (two flat-file extractions cross-checked against each other), not transcribed from the comparator's own failing-test output. C12 reuses an id last used, and removed, by the RG16(a) task's own reverted Purification retag -- noted at both the old and new entries' own comments so the id's history is visible to a future reader.
* data(ef): document why adjustments.sexp's directives survive the re-bootstrapLukasz Kasprzak2026-08-121-0/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Neither of adjustments.sexp's two directives is a no-op after the regeneration, and neither is removed -- reasoned through deliberately rather than deleted on sight, per Overlay.apply's own documented diagnostic behaviour (Edit has no "already correct" case; it unconditionally folds every field_edit over the current value when the slug is present, no diagnostic either way). `Edit most-holy-name-of-mary (Set_subject Bvm)`: the base bootstrap no longer needs this correction (the source dropped `class = lord` for this slug, so the default is already Subject.Saint), but the overlay still forces `Saint -> Bvm`. Kept for two reasons: data precision (the calendarium's "Sanctissimi Nominis Mariae" title still argues for Bvm over the generic Saint default, independent of what lectio's class field says) and as a regression guard (Set_subject is unconditional, so it would also correct a future re-introduction of `class = lord` for this slug, the one subject value RG16(a) behaviourally reads). `Suppress vigil-of-christmas`: re-verified, not assumed -- the slug is still present, unchanged, in both the regenerated source and the regenerated sanctoral.sexp, so the directive still fires its ordinary slug-present path. test_rite_ef.ml's own comment on the overlay-in-effect test previously claimed the base file carries `subject Lord` for most-holy-name-of-mary, which the regeneration made false; corrected in place rather than left stale, per this project's own discipline around wrong claims.
* data(ef): re-bootstrap sanctoral.sexp from the corrected lectio sourceLukasz Kasprzak2026-08-121-35/+60
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Regenerated via tools/bootstrap_sanctoral.ml against lectio's tridentine-calendar.ini (SHA-256 6a25e634... -> 1b303ef2...), whose own generator was independently fixed in a concurrent session. 327 entries (223 feast, 104 commemoration-only), up from 322 (208/114): - 15 entries wrongly bootstrapped `rank = commemoration` are now `class-3` (a real III-class Feast) -- all 6 March-5 April, all primary-source-verified against the calendarium (register's own "Commemoration-only entries' inferred STATUS" item). - 5 new entries: agnes-secundo (28 Jan), boniface-martyr (14 May), eusebius-confessor (14 Aug), evaristus (26 Oct), theodore (9 Nov) -- all Commemoration_only, all primary-source-verified (register's "Four confirmed sanctoral DATA GAPS" item + expected-divergences-missalemeum .sexp's former M4/M6/M7/M9/M14). - commemoration-of-the-baptism-of-the-lord (13 Jan) is now `(subject Lord)` (the source's own `class` field, not a colitur overlay); most-holy-name-of-mary (12 Sep) no longer carries `class = lord` at all (the source dropped it), so the bootstrap default (Subject.Saint) is now correct there without help from adjustments.sexp's own Edit. test_sanctoral_ef.ml's pinned counts (327/104/12 Class1) are re-derived directly from the source INI (`grep -c '^\['`, `grep '^rank' | sort | uniq -c`), not transcribed from a bootstrap-tool or `colitur day` run. data/ef/adjustments.sexp and the differential/oracle allow-lists still need updating for the consequences of this regeneration -- follow-up commits.
* fix(rite-ef): band has no table entry for a bare commemoration, everLukasz Kasprzak2026-08-121-17/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Fix round 1 on the commemoration-identity/tie-break work: the "genuine same-band tie" M17 adjudicated as open was not a tie at all. RG 91's table enumerates only "dies liturgici" (real feasts) -- entry 24's own text is "Festa III classis, in calendario Ecclesiae universae inscripta" -- so a Celebration.t.status = Commemoration_only candidate (a saint reduced to a bare commemoration) has no row in the table to begin with. Precedence_ef.band used to read rank alone, so it silently lent such a candidate the same table entry as a genuine Feast of its own rank; the calendarium's own 22 September row confirms the distinction in its own notation ("S. Thomae de Villanova ... III classis. / Commemoratio Ss. Mauritii et Soc. Mm." -- Thomas carries a class number, Maurice carries none). Fixed at the source, not by adding a status tier to compare_precedence: band now checks status first, ahead of every rank-keyed branch, and returns unclassified for any Commemoration_only candidate. M17 deleted (not re-adjudicated) -- 22 September now resolves identically on both sides, with no divergence left to name. Blast radius measured independently (a temporary git worktree at the pre-fix commit): 4451 days change across the whole 1583-9999 domain, exactly 4 verified shapes -- 3576 x maurice-and-companions-martyrs -> thomas-of-villanova and 282 x eleutherius -> philip-neri (admitted set changes), 502 x appollonia/cyril-of-alexandria and 91 x dorothy/titus (order only, independently confirmed against the calendarium's own listing convention). Zero days change who is observed. Further swept the whole domain for a genuine "two different candidates on the identical real table entry" residual -- the shape RG 113 truly gives no instruction for -- and found it empty: of 109201 same-rank ties domain-wide, 83950 now resolve by band alone and the remaining 25251 are exclusively two Commemoration_only candidates tied at unclassified, never two real feasts. The residual admit still breaks alphabetically is real, but narrower than previously documented. The "66 days" figure is reconciled, not merely disputed: 599 is the tie population, 65 (whole-comparator: 67) is the real admitted-set decision count within it (independently reproduced against the pre-Task-B binary), 149 is order-only. Every one of the 65 real decisions traces to the same Commemoration_only/Feast root cause as Maurice/Thomas. Also: validate.ml's admission fixed-point check recovers a commemoration's real origin (by matching its slug against the day's own temporal office) instead of reconstructing every candidate as Sanctoral, since band -- unlike EF's own admit -- does read origin; precedence_ef.ml/.mli's motivating example and blast-radius claims corrected to no longer rest on the fixed Maurice/Thomas case; test_identity_rank_corroboration's own population count corrected now that 22 September (and 21 other Feast-status matches) became reachable; its rank-agreement pin documents exactly what it does and does not prove. Register (§4, §6.1, §6 checklist) and CLAUDE.md corrected in place, including the RG 113 first-sentence gap this exercise surfaced but did not close, recorded as a new open item. 275 -> 276 tests (with the exhaustive sweep), all green.
* feat(test): oracle layer compares commemoration identity, not just countLukasz Kasprzak2026-08-121-8/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | test_oracle.ml (validation layer 4) used to compare commemoration presence and List.length only -- cardinality, never which candidate is admitted. A reviewer proved this vacuous by reversing admit's dignity sort (the engine admitting the worst commemoration, an outright RG 111 violation) and finding every assertion in this file green across all 730 days. tools/extract_missalemeum_oracle.py now also emits each commemoration's own missalemeum id (field 9, commemoration_ids), parallel to the existing title field; fixture regenerated from the same underlying lectio snapshot (commit d7da4b0, re-verified, first 8 fields byte-identical), read from a scratch copy so ~/git/projects/lectio was never touched. SHA-256 pin and provenance updated. The mapping: colitur identifies a commemoration by slug, missalemeum by an English title string. No hand-built slug<->title table -- colitur's own Celebration.t.names already carries the same English text for every SANCTORAL-origin commemoration (bootstrapped from lectio, verified to match missalemeum's titles character-for-character). This resolves identity for every sanctoral-origin commemoration but cannot resolve a TEMPORAL-origin one (Rite_ef.Temporal_ef never sets a celebration name) -- that case is a new, distinct outcome (Comm_identity_unresolved), counted and allow-listed, never silently skipped: 19 of 227 non-empty-commemoration days over 2026-2027. Two genuine identity mismatches found and adjudicated (new allow-list entries M15-M17, one is the unresolved bucket): M16, 27 March 2026, the already-known unimplemented Seven Sorrows of Passion Friday office (verdict missalemeum); M17, 22 September 2027, the RG 113 same-band tie-break residual (verdict open, per the sibling admit fix). A new corroborating check cross-references missalemeum's own id-embedded rank against colitur's, split by status: Feast-status matches must agree exactly (none reachable in this 2-year window, checked regardless); Commemoration_only-status matches either genuinely agree or fit a known, pre-existing, already-registered convention gap (colitur's bootstrap rank vs missalemeum's own sentinel) -- not a title-matching defect. Perturbation test performed and reverted: reversing admit's dignity- based ordering (compare_precedence's real committed form uses band, not dignity -- this reproduces the historical defect shape directly) turns the oracle layer red: "no differences outside the cited allow-list" fails with an unexplained commemoration-identity-mismatch on 2026-09-23 (colitur admits "St. Thecla" where the rubric and missalemeum both require "St. Linus"). Reverted; diff against the prior commit confirms no residue. CLAUDE.md's "know what each layer cannot see" section updated: the commemoration-identity gap it documented as open is now closed, with the mapping's limits and the RG 113 tie-break finding summarised. 271 -> 273 tests (274 with the exhaustive sweep), all green.
* fix(ef): M6 was convicted on the transcription's silence, not a scanLukasz Kasprzak2026-08-121-3/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | data/ef/expected-divergences-missalemeum.sexp's M6 entry and its matching comment in test/test_oracle.ml both cited 1962-06-23,_SS_Ioannes_XXIII, _Missale_Romanum,_LT.pdf's 14 August row ("Vigilia, II classis." -- nothing else) as if it were the calendarium itself. It is an electronic transcription that silently drops commemoration lines the photographic scans carry. missale-romanum-1962.pdf's 14 August row reads "Vigilia, II classis, Commemoratio S. Eusebii Conf.", with its own proper following -- independently confirmed in the second scan, Missale Romanum 1962_text.pdf. M6's verdict flips colitur -> missalemeum: this is a genuine colitur data gap, the same shape as M4/M7/M9/M14 (absent from data/ef/sanctoral.sexp and from lectio's own tridentine-calendar.ini), not a spurious oracle entry. Not fixed here for the same reason those four are not: it requires touching lectio's own bootstrap source, out of reach for this task. expected_rows is unchanged (2) -- the diff shape the allow-list predicate matches does not depend on which side is judged correct, confirmed by the suite passing unchanged. A companion register ruling that convicted romanus (9 August) on the identical reasoning -- and the register's own sanctoral-audit bucket counts that followed from it -- are corrected in docs/research/ rules-register.md (gitignored, not part of this commit). Full audit method, the complete list of dropped commemorations found across the whole calendarium, and every other ruling checked for the same defect are in .superpowers/sdd/2026-08-12-colitur-rg16a/transcription-audit- report.md (also gitignored).
* fix(rite-ef,kernel): follow the oracle on the Purification; admit reads the ↵Lukasz Kasprzak2026-08-122-38/+38
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | day's own Sunday-ness, not observed Fix round 1 review, two findings landed together because each golden pin's correctness depends on both. CRITICAL (item 1): reverts this branch's own first-pass retag of the Purification (2 February) from Lord back to Bvm -> Lord. The calendarium titles it 'B. Mariae Virg.', but missalemeum -- this project's designated EF oracle -- treats it as taking an occurring II-class Sunday's place outright, commemorations empty, exactly RG16(a)'s own festum Domini treatment (2020-02-02, 2014-02-02, independently fetched). Control, 2019-09-08 (the Nativity of the BVM, an ordinary Marian feast, on a Sunday): the Sunday observed, the feast merely commemorated -- the opposite pattern, proving the Purification's own oracle treatment is deliberate, not a gap. RG16(a) alone, on the untouched data, would have matched the oracle character for character; retagging Bvm is what moved colitur away from it on 1200 domain-wide days. The user has ruled: follow the oracle. Genuine primary-text counter-evidence (RG120(b), the colour rule grouping 2 February under 'B. Mariae Virg.', a category separate from 'Domini') is recorded in the register, not discarded -- a different project could reasonably rule the other way on the same evidence. most-holy-name-of-mary stays retagged Bvm; that one was never disputed. Removes the now-unneeded C12 lectio allow-list entry (data/ef/ expected-divergences.sexp, test_differential.ml): with the Purification Lord again, colitur matches lectio's own class = lord on every affected date, no divergence to allow-list. IMPORTANT (item 2): Precedence.rules.admit gains a ~temporal parameter -- a genuine kernel signature change, unlike RG16(a) itself. RG111(b)'s own Sunday rank floor ('one commemoration, namely of a II-class feast') used to be detected by reading observed's own slug for a Sunday marker, which silently assumed observed IS the day's temporal-cycle office. RG16(a) breaks that assumption on purpose: a Feast of the Lord standing in for an impeded Sunday 'cum omnibus iuribus et privilegiis' keeps the day a dominica II classis for RG111(b) too, even though the FEAST, not the Sunday, is observed. Before this fix an unrelated saint (Pope Sixtus II et al., 6 August, Class3) was wrongly admitted into the freed slot on every one of 1178 domain-wide days. Oracle-confirmed directly: 2023-08-06 (a Sunday) shows him displaced, commemorations empty; 2026-08-06 (an ordinary Thursday) shows him admitted -- being a Sunday is exactly what excludes him. admit now reads Sunday-ness off ~temporal (the day's own temporal-cycle candidate, passed through from Precedence.resolve unchanged) instead of observed. Validate's own admission fixed-point invariant updated to pass the same real temporal candidate through. Both golden pins in test_golden.ml were promoted from actual colitur day output without independently consulting the rubric or the oracle first -- a real defect the review caught. Re-derived from both before re-pinning: 2028-08-06 and 2025-02-02 now pin comms=[], no commemoration at all, matching missalemeum exactly in both cases.
* data(ef): retag the Purification and Most Holy Name of Mary as BVM, not LordLukasz Kasprzak2026-08-122-1/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Both entries carried (subject Lord) in data/ef/sanctoral.sexp, inherited unchecked from lectio's own class = lord field. The calendarium says otherwise: 2 February reads "IN PURIFICATIONE B. MARIAE VIRG." and 12 September reads "Sanctissimi Nominis Mariae" -- both name Mary, not the Lord, unlike the four genuine Lord entries' own "D. N. I. C." formula (Precious Blood, Transfiguration, Exaltation of the Holy Cross, Dedication of the Archbasilica of the Most Holy Saviour), all of which were checked against the same calendarium and left untouched. This matters together with RG16(a): applying the rule while the Purification was still mistagged Lord would have made it outrank an occurring II-class Sunday and then wrongly suppress that Sunday's own commemoration under RG16(a) too, the opposite of the intended fix. Retagged, the Purification no longer bands at RG91 entry 14, so the Sunday is observed and the Purification survives as its own ordinary commemoration. Corrected via a new data/ef/adjustments.sexp overlay (Set_subject Bvm), the same mechanism that file already uses to suppress vigil-of-christmas as a bootstrap duplicate, not a hand-edit of sanctoral.sexp itself: that file's own header asks for edits to go through the generator, and its SHA-256 provenance covers a byte-faithful mirror of lectio's INI. The Purification retag changes which day the lectio differential observes on 2 February whenever it falls on a Sunday (six years, 2005-2050); allow- listed as C12 in data/ef/expected-divergences.sexp, expected_rows derived independently (date -d <year>-02-02 for each year in range), not transcribed from a comparator run.
* docs+test: small factual corrections (item 7, part 1)Lukasz Kasprzak2026-08-121-3/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Six independent, small corrections found during the final review: - dune (workspace root): the comment said the stanza used "(:standard)" to preserve dune's default `default` alias target; the stanza actually spells that out explicitly via (alias_rec install). Comment now matches the code. - test_validate.ml's test_easter_extremes asserted `List.length ys = 2` where an identity check was called for -- the comment already named 1598 and 1666, but nothing confirmed extreme_years() found THOSE two rather than some other pair with the right cardinality. Now asserts the identities directly (the project's "cardinality where identity was required" vacuity flavour, per the review). - test_oracle.ml and expected-divergences-missalemeum.sexp both claimed "one entry (M13) is [verdict open]" -- M11 is open too (its own verdict changed from colitur to open in fix round 1); both now say "two entries (M11 and M13)". - expected-divergences-missalemeum.sexp's M2 note attributed `band` to temporal_ef.ml; `band` is precedence_ef.ml's own function. - lib/kernel/precedence.mli documented `dropped`/`admit`'s physical- equality obligation nowhere -- it lived only in one rite's own module (Rite_ef.Precedence_ef.admit's doc comment), but this signature is what an author of the next rite actually reads. Added the obligation here, cross-referencing the EF instance as precedent, not the only source. - README's opam install line omitted sexplib and ppx_sexp_conv (both in dune-project's own depends; `dune build` fails without them for a contributor following the README verbatim) and documented only `colitur easter`, though `temporal` and `day` both exist and are the more useful entry points. Fixed both. No behaviour change: comment/doc/test-assertion corrections only (the easter-extremes fix strengthens an assertion, it does not change what passes). Verified byte-identical `colitur day` output across 1583, 1900, 1902, 2008, 2011, 2026, 2038, 9999. 259/259 tests green.
* docs(citations): migrate register line-number pointers to stable anchorsLukasz Kasprzak2026-08-121-5/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 68 "register line N" pointers in tracked code and data cited the register's line numbers directly. The register has moved since: line 334 (cited six times as RG 91 entry 8) is now rose/colour prose; lines 375-376 (cited as "RG 109 leaves Advent/Lent Ember ordinary") are now RG 26, the paragraph that refutes that exact reading; line 374 (cited as RG 109(a)) is now RG 25's Latin; line 378 (RG 111) is blank. expected-divergences.sexp even wrote "RG 91 line 332" three times, as though the line offset were part of the rubric's own identity. Since docs/research/rules-register.md is gitignored, no reader of the public repo could resolve any of these, and -- since the register has moved on its own copy too -- neither can the author's. Replaced every pointer with an anchor that survives editing: the RG paragraph/entry number itself (already present in nearly every case, since the citation text already names "RG 91 entry N" or "RG 109(a)" next to the stale line number -- the line number was redundant, not load-bearing) plus a "§4" or quoted-subsection-heading pointer into the register for readers who want to locate the fuller discussion. Also corrected one genuinely stale content error found while migrating its anchor: test_precedence_ef.ml's RG33 disposition-cases comment still said "a I/II-class vigil impeded by any Sunday" -- the pre-Task-16 mistranscription the register itself corrects to II/III class; fixed the prose alongside its citation, since leaving a wrong RG-class claim next to a freshly-verified anchor would be worse than the stale line number it replaced. Covers lib/rites/rite_ef/precedence_ef.ml (18), precedence_ef.mli (already fixed with item 1), test/test_precedence_ef.ml (44, including two instances that only word-wrapped "register\nline N" across a line break and so did not match a same-line grep), and data/ef/expected-divergences.sexp (5, including three "RG 91 line 332" instances). All 68 original pointers resolved -- none needed guessing; every citation's target rule was already named in the surrounding prose. No behaviour change: every edit is a comment/citation/data-note change. Verified byte-identical `colitur day` output across 1583, 1900, 1902, 2008, 2011, 2026, 2038, 9999. 259/259 tests green.
* test(oracle): correct M11's citation and verdict (fix round 1, F3)Lukasz Kasprzak2026-08-121-3/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | M11 (the Nativity Octave's 26/27/28 December commemoration) previously read "No RG paragraph found to support this" with verdict colitur. Wrong on both counts, found by fix-round-1 review. RG 67 ("dies infra octavam sunt II classis") and RG 109(c) ("de diebus infra octavam Nativitatis Domini") both name this commemoration; the 1962 calendarium's own text confirms it directly, verbatim, under all three dates: "S. STEPHANI PROTOMARTYRIS, II classis. / Com. octavae Nativitatis." (same pattern for John the Evangelist and the Holy Innocents). It is also a genuine regression the previous commit's own commemoration-eligibility fix introduced: before that fix, colitur happened to commemorate the day's generic Christmastide loser unconditionally, which coincidentally kept presence agreeing with the oracle even though the reasoning was wrong; the fix correctly stopped that, but 26-28 December's own temporal candidate falls through to [ferial_rank]'s Christmastide catch-all, which gives it Class4 -- the wrong ferial rank for a day RG 67 says is II class. A naive fix would also get RG 69 backwards: when this window falls on a Sunday, RG 69 names the SUNDAY as observed with a commemoration of THE FEAST ITSELF (Stephen/John/the Innocents), never the generic octave placeholder -- so this needs a commemoration that attaches to whichever named feast is observed, not one more competing RG 92-95 candidate the way everything else in this engine is modelled. Out of this fix round's own scope. Verdict changed from colitur to open: neither side is confidently right without that fix, and the previous "colitur is correct, nothing to implement" framing was itself the risk under review.
* test: oracle vs missalemeum 2026-2027; audit the sanctoralLukasz Kasprzak2026-08-121-0/+102
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Validation layer 4: an oracle harness against missalemeum (Divinum Officium data), independent of the lectio bootstrap chain colitur's own sanctoral data comes from -- the only layer that can catch an error inherited from that bootstrap, and the only one that can validate commemorations at all (the lectio differential explicitly excludes them, per its own header comment). tools/extract_missalemeum_oracle.py shapes the fixture from lectio's sources/snapshot.tar.gz outside the test (no JSON library in this project's frozen deps, same reasoning test_differential.ml's own fixture already documents). test/fixtures/missalemeum-ef-2026-2027 .txt (730 days, SHA-256 pinned and asserted) + its own .provenance note record exactly how to regenerate it. test_oracle.ml compares three axes the oracle actually supports: rank, colour (SET MEMBERSHIP -- 14 of 730 days carry two colours, e.g. rose+violet on Gaudete/Laetare, which independently vindicates this project's own rose reading against lectio's violet-only one, recorded in the register), and commemoration presence/count. Slug identity is deliberately out of scope (needs a title->slug mapping, the data audit's own business, not the automated comparator's). Of 730 days, 688 matched cleanly outright. The remaining 42 are all named in data/ef/expected-divergences-missalemeum.sexp (14 cited entries, M1-M14): most are genuine primary-source-confirmed findings this task adjudicated and fixed in the two preceding commits (RG 33, RG 109/111, Holy Thursday's colour); the rest are real, cited, deferred feature/data gaps (RG 91 entry 27's BVM-Saturday office, RG 110's inseparable Peter/Paul commemoration, four sanctoral entries missing from lectio's own source) or genuine oracle-side artifacts -- honestly verdicted against whichever side this task's own primary-source research actually backs, never defaulted to colitur. One entry (M13, St Joseph vs the Friday of Passion Week 2027) is verdict open: adjudicated as unresolved after real search effort, not guessed past. The data audit: every sanctoral entry the comparison flagged was hand-checked against the 1962 calendarium, plus a 20-entry deterministic random control sample (seed 20260812) drawn independently of the flagged set. The control sample caught two entries (benedict, frances-rome) marked Commemoration_only in the bootstrapped data when the primary calendarium lists them as plain III-class feasts with their own Office -- traced to lectio's own source, not fixable here, and reported as a signal (10% of a random sample) rather than a blanket claim. Coverage recorded honestly in docs/research/rules-register.md's own three buckets: confirmed by oracle (200/322), confirmed by hand (23/322, 2 of them wrong), unverified (115/322) -- the unverified bucket stated explicitly rather than left implicit. Harness teeth demonstrated and reverted (not committed): a fixture rank/colour edit on a previously-clean day fails both the checksum pin and the no-unexplained-differences assertion independently; an expected_rows drift on the allow-list fails the citation-count assertion. Both captured with their exact failure messages, both reverted before this commit.
* kernel(temporal): Holy Thursday is white, per RG128(b)'s named exceptionLukasz Kasprzak2026-08-121-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | RG 128(b) excepts "Missa sive Chrismatis sive in Cena Domini feria V Hebdomadae sanctae" from the Septuagesima-to-Easter-Vigil violet span as a whole-Mass exception (unlike Palm Sunday's blessing/procession, which the same sentence carves out as only part of that day) -- so Holy Thursday's Mass is white, not violet. temporal_ef.ml fell through to the generic Passiontide ferial path (season_colour Passiontide = violet) for this date, since [named] has no entry for the Triduum at all (rank was already correct via [privileged_feria]; only colour was wrong). Found via Task 16's missalemeum oracle comparison: every other Triduum day's oracle colour set includes violet as an option (Good Friday "bv", Holy Saturday "vw" -- already-flagged, deliberately deferred per-action gaps, register §3b), but Holy Thursday's is white alone. This is a genuine NEW divergence from lectio too (lectio has no such exception and still prints violet for Holy Thursday) -- added as Layer C11 to the differential harness and data/ef/expected-divergences .sexp, 46 rows (2005-2050), rather than silently changing what that harness accepts. Also corrected a stale comment on season_colour claiming white's RG paragraph was never pinned -- RG 119 was found and cited in the register on 2026-08-11 but the correction was never copied into this file's own comment.
* test: differential vs lectio 2005-2050 with a cited allow-listLukasz Kasprzak2026-08-121-0/+69
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Validation layer 3 (design spec's five): compares colitur's real day-by-day EF output against lectio (sibling project, Go), 2005-2050, one line per civil day. Of 16801 day-pairs, 11206 already agree on the seven leading columns; the 5595 that don't resolve into exactly 25 distinct field-diff signatures, all triaged. Three strictly separate layers, per the controller's ruling (the brief's single flat allow-list assumed a handful of differences, not 5595): - Layer A (test_differential.ml, norm_season/norm_slug): vocabulary. An explicit, closed table of naming synonyms with no liturgical substance (lectio's easter/christmas vs colitur's paschaltide/christmastide; a handful of slugs that are two names for the identical office). No wildcards -- every entry is a literal string pair. - Layer B (strip_epiphany_index): numbering. The one slug family whose lectio/colitur index offset is not a constant (Time-after-Epiphany week numbering, register 3c#5) has its embedded digit stripped to a common form on both sides before comparing; rank and colour stay fully compared. - Layer C (data/ef/expected-divergences.sexp): the cited allow-list. Ten genuine liturgical disagreements, each citing its RG paragraph and naming which engine is right (always colitur, verified against the Missal/register, never against lectio's own behaviour). This is the only layer permitted to cover a difference in rank, colour, or which celebration is observed. Five extend or restate register 3c's already- documented divergences (season boundary, Sunday I-class, Advent Ember ferias, Rogations); five are new, found and adjudicated in this task (Lent Ember days, the Nativity Octave, Ember-day-vs-saint precedence, the St Joseph transfer off a Lent Sunday, and the 2011 Sacred Heart / Precious Blood / Visitation collision). expected_rows on each entry is an exact regression pin, asserted by the test, not documentation. 13 January (register 6's long-open "Baptism of the Lord" item) is confirmed empirically fixed already -- Task 11's sanctoral wiring closed it before this task started -- so it is not allow-listed; the only residual difference there is the season boundary already covered by C1. Two stated limits carried from the brief (commemorations are not comparable; lectio's own EF oracle asserts season only, 2025-2026 only, so a rank/colour difference is not presumptive evidence against colitur) plus a third found during this task (the week column is a display convention on both sides, not a liturgical fact, and is not compared at all) are documented in the test file's own doc comment. Fixture: test/fixtures/lectio-ef-2005-2050.txt, committed as plain text (1.4 MB), generated by lectio commit 2386a45; provenance recorded in the sibling .provenance file. Colitur's side is recomputed fresh from the library on every run, through the same Calendar/Rite_ef pipeline `colitur day` uses, not the compiled binary. Proved the harness has teeth by two reverted perturbations: a genuine colour difference injected into a fully-covered fixture row fails the "no unexplained differences" check with the exact mismatched row printed; a one-row drift in an allow-list entry's expected_rows fails the count check independently, showing it is not merely a duplicate of the first assertion. 236/236 tests green, clean-build verified, deterministic across OCAMLRUNPARAM=R.
* cli: colitur day <year> dumps resolved liturgical daysLukasz Kasprzak2026-08-121-0/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Assembles Rite_ef.context (lib/rites/rite_ef/rite_ef.ml[i]): temporal, anchors and vocab from Temporal_ef; rules from Precedence_ef's band, disposition and admit; season_runs = Vocab_ef.seasons; transfer_target newly implemented here. transfer_target (RG 96): the next following day that is not I or II class, with the Annunciation's own exception (Monday after Low Sunday). Terminates by a structural step bound on its internal search, independent of Calendar's own round guard, which bounds rounds across a year, not one call's walk; documented as an obligation on rite.mli's transfer_target field, which did not previously state it. Fixes the vigil-naming mismatch Task 7's review predicted: the sanctoral bootstrap names its vigils with a vigil-of-X prefix (lectio's own convention), while Precedence_ef's is_vigil only recognised the temporal cycle's own X-vigil suffix. Both are now recognised, fixing RG 91 entries 21/26 and RG 33's vigil omission for the four affected celebrations. Verified by unit test and by mutation-testing the fix (reverting it fails exactly the new rows) and against real output across several years. Suppresses data/ef/sanctoral.sexp's vigil-of-christmas via a new overlay, data/ef/adjustments.sexp: it is the same celebration as the temporal cycle's own ef-nativity-vigil, both dated 24 December. colitur day <year>: one line per civil-year day, temporal and sanctoral fully resolved through Layer, Overlay, Precedence_ef and Calendar -- the first CLI path exercising the whole Plan 3 pipeline against real data. Verified the All Souls transfer chain (Tasks 7-8-11) end to end against real output for both a Sunday year (2025, lands on 3 Nov) and a non-Sunday year (2026, observed directly on 2 Nov).
* data(ef): bootstrap the 1962 sanctoral from lectioLukasz Kasprzak2026-08-121-0/+2644
Convert lectio's tridentine-calendar.ini (322 entries) into data/ef/sanctoral.sexp via a validating OCaml converter, tools/ bootstrap_sanctoral.ml, rather than a hand-written script: every field is built through Slug.of_string, Colour.of_string and Vocab_ef.rank_of_string, so the emitted sexp is valid by construction. Two conversion decisions, both documented rather than buried: - subject defaults to Subject.Saint, overriding Celebration.make's kernel default of Subject.Temporal, for the 316 entries with no explicit class; - rank = commemoration maps to status = Commemoration_only with an inferred Class3 (not a citation -- it is what the 1960 reform reduced most simple feasts from), recorded as an open item in the rules register for the oracle to adjudicate. Every celebration is tagged layer = Precedence_ef.universal_layer, the provenance id RG 91's band classifier reads to tell the universal calendar from proper/indult data. The generated file carries a provenance header: source path, its SHA-256, and the UTC conversion date, so re-bootstrapping against a newer lectio is reproducible and diffable. Output is byte-identical across runs. test/test_sanctoral_ef.ml loads the file through Layer.load and checks the counts independently derived from the source INI (322 entries, 114 Commemoration_only, 12 Class1, no Subject.Temporal, every date resolves in a leap year), plus two named spot-checks against the INI's own text -- one entry with an explicit class field, one commemoration -- so a passing count cannot hide the wrong 322 entries having been converted.