aboutsummaryrefslogtreecommitdiff
path: root/test/test_precedence_ef.ml
Commit message (Collapse)AuthorAgeFilesLines
* fix(ef): the seasonal commemoration comes first (RG 113 sentence one)Lukasz Kasprzak2026-08-181-0/+28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | "113. Commemoratio de Tempore fit primo loco. In admittendis et ordinandis aliis commemorationibus, servetur ordo tabellae praecedentiae." Only the second sentence was implemented, adopted on ef-rg16a as the admission and ordering criterion. The first -- the commemoration OF THE SEASON is made in the first place -- was not, so ordering ran through band alone for every commemoration including the seasonal one. Reconciled with RG 110(c), which adds the inseparable Peter/Paul pair "ante omnes alias commemorationes". Both rubrics are primary-source certain: the photographic scans' OCR is illegible at RG 110(c), but docs/research/LT.txt, the electronic transcription, carries the Rubricae Generales complete, and O'Connell's footnote 39 turns out to render that clause exactly rather than gloss it. They share a technical term. RG 113's own sentence fixes the sense of "aliae commemorationes" as the ones other than the de Tempore one it has just placed primo loco, and RG 110(c) sits three paragraphs earlier in the same code. Read consistently, the pair heads the OTHERS, not the whole list: season, then the pair, then the table order. So this runs after rg110_additions, and List.partition's stability keeps the pair adjacent while the seasonal commemoration steps in front of it. Blast radius, two full 1583-9999 sweeps diffed: 3 533 days, every one ORDER-ONLY -- the commemoration set is identical on both sides of every changed day, checked as sorted multisets, zero days where anything else moved. All are 22 February with chair-of-st-peter observed. Exactly the population measured before the rule was written. The order-sensitive test row is in test_precedence_ef.ml's admit_cases, which is still the only place in the suite that asserts commemoration order at all. It needed a mixed-origin candidate: every other candidate in that table is origin = Temporal by default, so the rule is an identity on them.
* test(ef): commemoration, transfer, and privilege coverage for the Major LitaniesLukasz Kasprzak2026-08-131-7/+117
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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).
* fix(ef): RG 112(d) excludes a same-BVM commemoration (fix round 1, F1)Lukasz Kasprzak2026-08-131-0/+76
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* test(ef): reach RG 91 entry 27 through the real pipeline, end-to-endLukasz Kasprzak2026-08-131-4/+98
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* test,docs: RG110(c)'s adjacency is unobservable by constructionLukasz Kasprzak2026-08-131-1/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-131-8/+27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* precedence(ef): RG 110, the inseparable Peter/Paul commemorationLukasz Kasprzak2026-08-131-1/+67
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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/+105
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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").
* precedence(ef): fix round 1 (F2, F7) -- correct a false transcription claim, ↵Lukasz Kasprzak2026-08-131-25/+42
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | cite RG95's real primary authority F2: the disposition comment for RG112(a) claimed the electronic transcription "carries NEITHER this paragraph's own worked example NOR its own more specific Mass-proper rubric... exactly the transcription's documented defect" -- wrong on both counts, checked directly rather than taken on trust. RG 112 has no worked example in any of the three source documents (the transcription included) -- there was nothing to drop. The Holy Family Mass propers' own 13-January rubric is absent from the transcription not because it silently drops content it otherwise has (the project's own documented defect, dropped calendarium commemoration lines) but because that specific document (a 2006 web capture) contains almost no Mass-propers text of any day: 5 Introit-line occurrences across 26,322 lines, against 61 and 402 in the two photographic scans; its own "Proprium de Tempore" page is a table of contents linking to PDFs the capture never pulled in. A coverage gap in a partial web capture, not an instance of the narrower, different defect the methodology note actually describes. The ruling is unaffected -- the propers' quotation is real, on two independent photographic scans, and settles 13 January on its own -- only the attribution is struck, here and in test_precedence_ef.ml (the register and the task report are corrected separately). F7: RG 95's own second paragraph -- present in all three documents, including the transcription -- is a more direct, occurrence-level authority for the exact collision than RG 112(a): "if two feasts of the same Divine Person... occur together, the one higher in the table is kept, the other omitted." Added as the primary citation on disposition's RG112(a) branch, with RG 112(a) and the propers' note kept as corroboration. Also corrects RG 112(b)'s own framing: it is bidirectional ("et vicissim"), and only the Lord-feast-wins direction (RG 16(a)) is implemented -- the Sunday-wins direction has no witness anywhere in this codebase's data and was wrongly described as already covered.
* precedence(ef): fix round 1 (F1) -- band's -14 sentinel inverted the tableLukasz Kasprzak2026-08-131-81/+151
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | entry_14_movable_band = -14 was justified only by "cannot collide with a real table position (1..28) or unclassified" -- a collision argument, not an ordering one. band's own value is compared by plain <, so a negative number beats every I-class entry (1-13), not merely the fixed Baptism it was built to beat. Not live against the shipped universal sanctoral data (no I-class universal feast falls 7-13 January, which is why the original 1583-9999 sweep showed only the intended shape), but entries 12/13 admit PROPER and INDULT I-class feasts too, and those arrive via adjustments.sexp-style overlays -- this architecture's own advertised extension path. A diocesan patronal feast landing in Holy Family's window would have made the inversion live immediately: the I-class feast displaced and transferred to the next day, reproduced and confirmed before this fix, reverted after. Fixed by rescaling every band branch to the real RG 91 entry number times ten (unclassified untouched at max_int) instead of the bare 1..28, reserving genuine headroom immediately before each entry for a movable sub-rank -- not an arbitrary rescale: RG 91's own text records a "primum mobilia, deinde fixa" split at three rows, not only entry 14 (entry 12's own ninth sub-item, entry 14, and entry 20), so this leaves room for the other two without a second rescale if either ever gets a live witness. entry_14_movable_band now derives from entry_14_fixed_band - 1, tied together so they cannot drift apart. Added a synthetic I-class end-to-end test reproducing the exact counter-example that found this: a universal I-class candidate inside Holy Family's own 7-13 January window is now observed and does not transfer, with Holy Family itself falling to RG109(a)'s ordinary privileged-Sunday-commemoration path, the same treatment any genuine feast outranking an ordinary Sunday gets.
* precedence(ef): RG91 e14 "primum mobilia" + RG112(a) inseparable exclusionLukasz Kasprzak2026-08-131-1/+130
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two related fixes, both needed for Holy Family to actually observe correctly on the seven years it coincides with the fixed Commemoration of the Baptism of the Lord (13 January): 2008, 2013, 2019, 2030, 2036, 2041, 2047. band's entry 14 ("Festa Domini II classis") gated on sanctoral origin only, so a movable Class2 Lord feast had no row in the table at all. RG 91 entry 14's own text says otherwise: "primum mobilia, deinde fixa" -- movable first, then fixed. entry_14_movable_band gives Holy Family (the one movable candidate this codebase builds with subject Lord) a value strictly better than the fixed half's own literal 14, negative so it can never collide with a real table position -- without it, the two would tie at 14 and fall to Precedence.resolve's own alphabetical tie-break, which picks the WRONG side ("commemoration-of-the-baptism-of-the-lord" sorts before "ef-time-after-epiphany-sunday-1"). disposition gets a new branch for RG 112(a) (Caput XVI, "De Commemorationibus"): the Office, Mass or commemoration of a mystery of one Divine Person excludes a commemoration of another mystery of the SAME Divine Person. Winner and loser both subject Lord -> Omit, not Commemorate -- without it, Holy Family observing correctly still left the Baptism admitted as an ordinary Class2 commemoration under RG111(b), which the primary text does not allow. Confirmed by a MORE SPECIFIC primary source than RG 112(a) alone: the Holy Family Mass propers' own note, found on both photographic scans, word for word, immediately after the Postcommunio -- "Si festum S. Familiae occurrerit die 13 ianuarii, Missa dicitur de festo S. Familiae, sine commemoratione Baptismatis D.N.I.C., et sine commemoratione dominicae." The electronic transcription carries neither this rubric nor RG 112(a)'s own worked example -- another instance of its documented defect, not merely absent from one check. Still expected red: the lectio differential (2005-2050) now shows a new, real divergence on those seven 13-January-Sunday dates -- lectio has no Holy Family at all, so it still shows the fixed Baptism observed there, unlike colitur now. The oracle's own M-series entries also still need widening for the observed-identity axis added two commits ago. Both are the next commits.
* fix(rite-ef): band has no table entry for a bare commemoration, everLukasz Kasprzak2026-08-121-1/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* fix(kernel,rite-ef): admit orders commemorations by RG 113's table of ↵Lukasz Kasprzak2026-08-121-15/+70
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | precedence, not slug Precedence_ef.admit broke a same-rank tie among commemoration candidates alphabetically by slug -- a deterministic engineering convention with no rubrical citation. RG 113's own second sentence, primary-source-verified against two independent scans and previously uncited in the register (only "commemoratio de Tempore fit primo loco" was quoted before), gives the real rule: "in admittendis et ordinandis aliis commemorationibus, servetur ordo tabellae praecedentiae" -- admitting and ordering commemorations both run on the rite's own table of precedence (band's 28-entry table), not RG 8's coarse four-class rank. Precedence.resolve now computes each commemoration candidate's own band value once, generically, and hands it to rules.admit as a third tuple element (Precedence.rules.admit's signature changed accordingly, ditto Precedence_ef.admit; every rule-record stub in the test suite updated to match). Precedence_ef.admit's own compare_dignity is replaced by compare_precedence, ordering by band then slug; a residual tie within one identical band value still falls back to slug, since RG 113 gives no further instruction there -- documented as a still-uncited engineering convention, not dressed up as a rubric. RG 98 ("in paritate autem Officium prius impeditum praecedit") was considered as a candidate authority for that residual and rejected: it governs the transfer queue order among several simultaneously-impeded I-class feasts (Caput XIII), a different operation in a different chapter from RG 113's commemoration admission (Caput XVI); nothing in the primary text connects the two. Blast radius measured against the pre-change binary across the entire 1583-9999 domain (not only 2005-2050): the admitted-commemoration-slug set is byte-identical, day for day, before and after this change. The fix corrects the citation and mechanism, not the answer, on this codebase's current data -- both of the task brief's named examples (22 Feb Chair-of-Peter/Lent-vs-Paul, 22 Sept Maurice-vs-Thomas-of- Villanova) are confirmed present and unchanged in both streams. A new test (RG113: admit picks by precedence order, not slug, when they disagree) proves admit actually consults the passed-in precedence value with a synthetic pair whose slug order and precedence order disagree -- teeth a same-band-only regression test could not have caught, since every real collision found in the domain happens to agree on both axes. 271 -> 272 tests, all green; COLITUR_EXHAUSTIVE_SWEEP=1 unaffected.
* docs(rite-ef): correct a false band-value comparison in the RG16(a) commentLukasz Kasprzak2026-08-121-6/+16
| | | | | | | | | | | | | | | | | | | | Fix round 1 review, MINOR finding (item 4). Both precedence_ef.ml and test_precedence_ef.ml claimed the loser-side Class2 conjunct held because 'entry 6's own band value (6) is lower than every entry [3, 11-14]' -- false on its face (6 is not lower than 3) and, worse, the claim proves the opposite of what it was cited for: if a band-3 candidate really did contest a Class1 Sunday, the lower number would win, meaning the Sunday would lose, not beat it as claimed. The conclusion itself was never wrong, only the justification. Against entries 11-14 (sanctoral-origin Lord feasts) the numeric argument holds (6 < 11-14). Against entry 3 (Epiphany, Ascension, Trinity, Corpus Christi, Sacred Heart, Christ the King) it is not numeric at all but structural: every band-3 celebration is temporal-origin, and Precedence.resolve takes exactly one temporal candidate per day, so a band-3 Lord feast IS that date's own single temporal candidate, never a second one contesting a separately-produced Sunday -- there is no band comparison to make in the first place. Comment-only; no behaviour change.
* fix(rite-ef,kernel): follow the oracle on the Purification; admit reads the ↵Lukasz Kasprzak2026-08-121-39/+119
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* kernel(rite-ef): apply RG16(a) -- a Feast of the Lord leaves the impeded ↵Lukasz Kasprzak2026-08-121-1/+136
| | | | | | | | | | | | | | | | | | | | | | | | Sunday uncommemorated Caput III "De Dominicis", RG 16(a): a Feast of the Lord, I or II class, occurring on a II-class Sunday takes the Sunday's own place with all its rights and privileges -- of the Sunday, therefore, no commemoration is made. band's entry 14 already ranked such a feast above the Sunday correctly; disposition had no exception for the collision, so the impeded Sunday fell through to the ordinary RG 95/109(a) commemorate-or-omit catch-all and was wrongly admitted as a privileged commemoration. Adds a branch to Rite_ef.Precedence_ef.disposition, gated on all four of: loser is temporal-origin, loser rank Class2, loser carries the Sunday slug marker, winner rank Class1-or-2 with subject Lord. No signature change -- disposition already receives the winner (RG 33's own vigil-omission branch already reads it); each conjunct is proved independently load-bearing by a dedicated disposition_cases row, including one real-data case each for the winner's subject and rank conjuncts, and a defensive synthetic case for the winner rank conjunct's currently-unreachable Class3 shape. A new end-to-end resolve-level test (2028-08-06, the Transfiguration) proves the real band/disposition pipeline together, checking the Omit reason string distinguishes this from admit's own admission-limit cut.
* docs(citations): migrate register line-number pointers to stable anchorsLukasz Kasprzak2026-08-121-48/+49
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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(precedence-ef): witness the RG26 Class4 gate and correct a vacuous ↵Lukasz Kasprzak2026-08-121-6/+65
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | guard claim Item 2(a): the RG 26 omission gate (rank = Class4) is extensionally identical, over every candidate this engine actually constructs, to the refuted "privilege_of loser = Ordinary" predicate it replaced -- confirmed by enumerating the whole candidate space over 1583-9999, substituting the old predicate back leaves 257/257 green and byte-identical output. Adds a synthetic row: a temporal-origin, Ordinary-privilege Class3 loser, which the two gates disagree on (RG26: Commemorate; the refuted gate: Omit, since it never looked at rank). Reverting the gate to the old predicate turns exactly this row red: Expected: "Commemorate(Ordinary)" Received: "Omit" Item 2(b): the existing "RG33 boundary" row's comment claimed it proved the RG26 branch's own (not (is_vigil ...)) guard. It does not: the vigil sourced there is Class2, already excluded by the branch's rank = Class4 test alone, with or without the vigil guard -- verified by deleting the guard, which leaves 257/257 green. Corrected the comment to say what the row actually shows (RG31's "if impeded, commemorated" vigil mandate, a real and separate fact worth keeping) and added a genuinely discriminating synthetic row: a temporal-origin Class4 loser whose slug is ALSO vigil-shaped, a combination RG 91 does not describe but Precedence.resolve can still be asked to construct. Deleting the guard turns this row red: Expected: "Commemorate(Ordinary)" Received: "Omit" Both reversions were applied locally to precedence_ef.ml, confirmed, and reverted; the committed .ml is unchanged. Test count 257 -> 259, both new rows green under the current, correct code.
* kernel(precedence): fix round 1 -- RG23-26 govern ferial commemoration, not ↵Lukasz Kasprzak2026-08-121-28/+68
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | RG109 Review round 1 (F1, Critical): the commemoration-eligibility fix from the previous commit silently dropped II-class Advent and Lent Ember ferias when impeded, while ordinary (lower-solemnity) ferias of the same seasons kept their commemoration -- backwards on any reading. Reproduced: 1900-12-21 (an Advent Ember Friday) lost its commemoration entirely; 2026-12-21 (an ordinary Advent feria the same week) kept its. F2 (Important, the direct cause of F1): the branch's own justification mis-stated RG 109 as an exhaustive list of the only temporal circumstances that generate a commemoration. RG 109 is headed "Commemorationes privilegiatae sunt commemorationes" and closes "Omnes aliae commemorationes sunt commemorationes ordinariae" -- it sorts commemorations that already exist into two HONOUR classes (RG 108's differing liturgical hours), and says nothing about which offices have the right to be commemorated at all. That right belongs to Caput IV, "De feriis" (RG 21-27), never opened by the original pass: RG 24 (II-class ferias): "si vero impediuntur, commemorari debent" -- if impeded, MUST be commemorated. Not optional. RG 25 (III-class ferias): same mandate. RG 26: every feria not named in 23-25 is IV class, and IV-class ferias are NEVER commemorated -- the one exclusion RG 21-27 actually states. The omission branch is now gated on `rank = Class4` (RG 26) directly, not on `privilege_of = Ordinary` -- the old gate happened to reach the right answer for IV-class ferias (RG 26 also excludes them, for a reason the old citation did not give) and the wrong one for II/III- class Ember ferias RG 109(e) did not name by letter. RG 109(e) itself is also corrected: its bare "feriis Adventus, Quadragesimae et Passionis" previously excluded the Advent and Lent Ember sub-days by analogy with (d)'s separate September carve-out. RG 91's own TABLE needs an explicit "exceptis feriis Quatuor Temporum" at entries 22 and 25 to keep Ember days from being double- listed against their own entry 18 -- an exception that would be unnecessary drafting if "feriae Adventus"/"feriae Quadragesimae" did not already include their Ember sub-days by default. RG 109(e) carries no such exception, so it is read at that same default, inclusive scope: the Advent and Lent Ember ferias are privileged under (e), not merely ordinary-but-commemorable. (d)'s own existence is unaffected -- September Ember days sit outside Advent/Lent/Passiontide under any reading, so (d) remains necessary regardless. Verified: 1900-12-21 and 1902-02-22 now correctly commemorate their Ember ferias; 2026-12-21 and 1902-02-24 (ordinary ferias) unchanged. Blast radius re-measured against the prior commit, 1900-2100: 102 civil days changed, every one an Advent/Lent Ember (or Ember-vs- Joseph-collision) day regaining its commemoration, nothing else. Validate re-swept exhaustively, 1583-9998: 0 failures. F6: two privilege-boundary test rows that had been bent to expect the bug's own output (Commemorate -> Omit) are restored to what RG 24/25 actually require, now Commemorate(Privileged) given the RG 109(e) correction above -- these are the rows that should have caught F1. F7: admit_cases had no witness for RG 111(b)'s rank floor that wasn't already Class2, so reverting that filter only reddened the oracle suite, never this file. Added a row (II-class Sunday, sole candidate an ordinary Class3) that fails without the filter and passes with it. F9: RG 122 cited alongside RG 128(b) for Holy Thursday's white -- states the same fact affirmatively ("Demum adhibetur color albus, feria V Hebdomadae sanctae...") rather than as an exception to violet.
* kernel(precedence): three RG-verified EF precedence bugs, found via oracleLukasz Kasprzak2026-08-121-17/+76
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Task 16's missalemeum oracle comparison (2026-2027) surfaced three distinct precedence-engine bugs, each confirmed against the primary 1962 Missale Romanum text and cross-checked against the oracle: 1. RG 33 (vigil omission) was transcribed backwards. The primary text reads "Vigilia II AUT III classis penitus omittitur" (a II OR III class vigil is entirely omitted on any Sunday or I-class feast) -- not "I aut II" as the register and this module's own rank check (Class1 || Class2) previously had it. I-class vigils (Nativity, Pentecost) can never actually lose in this engine (band always ranks them above anything that could coincide with their dates), so the Class1 half was dead code; the real bug was that Class3 (St Lawrence's vigil, the sole III-class vigil) was MISSING, so it fell through to an ordinary commemoration instead of RG 33's mandatory omission whenever 9 August landed on a Sunday. 2. RG 93/95/109/113 read together: an ordinary, non-privileged TEMPORAL-cycle office (a bog-standard green-season feria, a plain Advent/Lent Ember day, a Minor Rogation day) has no standing to be commemorated at all when impeded -- RG 109's six-item list is a CLOSED set of the only temporal circumstances that generate a commemoration, not a floor under which anything ordinary still gets one. The engine previously commemorated the losing feria itself in this situation; confirmed wrong against ~190 independent missalemeum days (2026-2027) showing zero commemorations for the exact shape. Vigils are explicitly excluded from this new rule -- RG 31/32 give them their own "if impeded, commemorated" mandate, independent of RG 109's list. 3. RG 111(b)'s Sunday admission slot is restricted to "de festo II classis" -- a rank restriction, not merely the best available ordinary candidate. A III/IV-class ordinary saint competing for a II-class Sunday's single commemoration slot had no such restriction applied before; confirmed wrong via St Hyginus (11 Jan, Class3) losing to Holy Family, which missalemeum shows entirely displaced, never commemorated. All three fixes are pure disposition/admission changes -- they never touch band, so the observed day (season/slug/rank/colour) is unaffected in every case; the lectio differential (which never compares commemorations) stays green untouched. test_precedence_ef.ml updated throughout: two previously-wrong test expectations corrected (the RG33 III-class-vigil boundary row, the IV-class-feria totality row), three privilege boundary rows sharpened from "commemorated but unprivileged" to "omitted entirely" now that temporal+ordinary means omission, and a new contrast row added to keep the SANCTORAL side of rule 2 covered separately from the TEMPORAL side.
* rite(ef): the Annunciation's RG96 exception is conditional, not unconditionalLukasz Kasprzak2026-08-121-15/+59
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The register was transcribed faithfully but was itself wrong: RG 96's Attamen (a), primary-source-verified from the scans and now corrected in the register, reads 'festum Annuntiationis B. Mariae Virg., quando est transferendum post Pascha, transfertur ... in feriam II post dominicam in albis' -- the Monday-after-Low-Sunday seat applies ONLY 'quando est transferendum post Pascha', when the feast is to be transferred PAST EASTER. The unconditional transcription made the exception fire on every impeded Annunciation regardless of cause. transfer_target now computes the general RG 96 target first, for every candidate, and overrides to the Monday after Low Sunday only when that general target itself falls after Easter Sunday -- testing the rubric's own condition directly rather than re-deriving a date-proximity rule from first principles. Confirmed against three real years the review named: 2007, 2012 and 2057 all previously sent the Annunciation to Easter + 8 (16 April, 16 April, 30 April respectively) when the correct, now-produced target is the next free day before Easter (26 March in each case -- Passion Sunday in 2007/2012, Lent III Sunday in 2057). Verified with actual CLI output for all three, before and after. Also cites RG 96 Attamen (b), the same primary-source passage, as the direct authority for All Souls' own move to the following Monday when impeded by a Sunday -- previously inferred from RG 91 entry 8's parenthetical plus the general walk, which happened to produce the right date; now stated directly. Rewrote the two existing Annunciation unit tests, whose synthetic occupants no longer trigger the (now correctly conditional) exception, and added a 2057 regression test using the real Temporal_ef.temporal as occupant plus a real-data cram pin -- both mutation-verified against the unconditional reading.
* rite(ef): clamp the RG96 search at the domain ceilingLukasz Kasprzak2026-08-121-1/+31
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | search_from could walk up to 400 days past origin before Calendar's own ~start ~stop clamp is ever consulted, and nothing stopped it probing occupant on a date past 31 December 9999 -- occupant chains through the real EF rite's temporal, which calls Computus.gregorian_easter, not total outside 1583..9999 (it Date.makes and failwiths on Error). Not reachable with the shipped sanctoral data alone, but reachable through the project's own primary extension path: an overlay adding an I-class feast on 25 December leaves nothing but Class2 Nativity-octave days for the rest of civil year 9999, so the unguarded search reached 1 January of year 10000 and crashed there with 'computus: year 10000 out of range 1583..9999'. 9999 is an in-range year and the kernel's contract is 'never raises on in-range input'. search_from now also stops, without probing occupant again, once it passes Date's own domain ceiling -- the same 'return a finite date, let Calendar's own out-of-range handling record it, never pretend to have found something admissible' contract the existing step-count guard already follows. Two new tests, both mutation-verified to actually reproduce the crash when the guard is removed (see the task report): a precedence_ef.ml unit test using the real Temporal_ef.temporal as occupant (a synthetic occupant can never discriminate this, since it never calls Computus itself), and a Calendar-level integration test reproducing the exact overlay-based scenario the review found.
* cli: colitur day <year> dumps resolved liturgical daysLukasz Kasprzak2026-08-121-1/+129
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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).
* test(ef): prove the II-class-Sunday privilege override end-to-endLukasz Kasprzak2026-08-111-1/+67
| | | | | | | | | | | | | | | | | | | | | | | | | Adds test_ii_class_sunday_privileged_witness_admitted_end_to_end, proving RG 111's "II-class Sundays: one, dropped if a privileged one is due" clause through the real pipeline (band, disposition, privilege_of, admit together), not only at the isolated admit level. RG 109(b) is the one privileged category that is sanctoral and rank-based rather than temporal-origin: a Commemoration_only, Class1 sanctoral candidate (the same shape disposition_cases already uses as (b)'s witness) is held out of the band contest entirely by Precedence.resolve, so it always loses regardless of what wins the day. Paired with an ordinary sanctoral saint, both losing to an ordinary Class2 Sunday, gives observed_is_sunday = true with one privileged and one ordinary loser due at once, with no new fixture. Documents, and mutation-confirms, what this fixture does and does not prove: RG 109(b)'s only reachable witness is Class1 (the highest dignity), so the privileged loser here is unavoidably also the higher-dignity one, and the admission outcome alone cannot distinguish "privilege overrides dignity" from "dignity alone picked the same winner" -- that distinction remains admit_cases's job. What this test does prove, and what a category-(b) regression in privilege_of breaks, is the privilege tag itself surviving the real pipeline end-to-end.
* rite(ef): an impeded I-class Sunday commemorates, not transfersLukasz Kasprzak2026-08-111-0/+22
| | | | | | | | | | | | | | | | | | | | | | | | | disposition's Class1 branch was unconditional on rank, so an impeded I-class Sunday (Advent/Lent/Passiontide/Low Sunday) currently transferred like a feast. RG 95 (register lines 323, 363) restricts the right of translation to I-class FEASTS -- RG 91's own table lists Sundays as a separate row (entry 6, line 332) from feasts (entries 11-13, lines 337-339) -- and RG 109(a) (line 374) lists "of a Sunday" as a privileged commemoration category, which presupposes an impeded Sunday stays put rather than moving to another day. Excludes is_sunday_slug losers from the Transfer branch so they fall through to the existing Commemorate (privilege_of loser) branch, which already tags them Privileged via RG 109(a) with no further change. Fires on real dates in the 2005-2050 differential window: 24 December on Advent IV in 2023, 2028, 2034, 2045; 8 December on an Advent Sunday in 2024, 2030, 2041. Verified no previously-passing Transfer assertion used a Sunday-shaped loser (grepped test_precedence_ef.ml, test_calendar.ml -- which uses its own synthetic rite unrelated to Precedence_ef.disposition -- test_colitur.ml, test_validate.ml); confirmed by mutation-reverting the fix and observing exactly the one new test fail, nothing else.
* rite(ef): commemoration privilege and admission (RG 108-111)Lukasz Kasprzak2026-08-111-4/+306
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Precedence_ef.privilege_of classifies a commemoration candidate against RG 109's closed list of privileged commemorations (a Sunday; a I-class day; a day within the Octave of the Nativity; a September Ember day; a feria of Advent, Lent or Passiontide; the Major Rogations in Mass), read entirely off the candidate's own rank/slug/origin, no context needed. Major Rogations (f) is left unimplemented rather than guessed: no producer for the Major Litanies exists anywhere in this codebase yet. disposition's two Commemorate sites now call privilege_of instead of Task 8's interim_privilege placeholder, which is removed entirely (binding and .mli export both gone). Precedence_ef.admit applies RG 111's four admission counts, keyed on the observed day's own class and Sunday-ness: a I-class day admits none except one privileged commemoration; a II-class Sunday admits one, but a privileged commemoration due displaces any ordinary one regardless of dignity; any other II-class day admits one by dignity alone, with no such override; III/IV-class days admit at most two by dignity. Ties break on slug, matching Precedence.compare_by, so the admitted set never depends on input order. Every admitted candidate is a value taken unchanged from the input list, never rebuilt, so Precedence.resolve's physical-equality-based dropped/omitted accounting keeps working -- closing a note left open since Task 2. Tests: RG 109 category rows (one per reachable category, plus boundary rows proving Advent/Lent Ember days and Minor Rogations are correctly excluded), RG 111 admission rows checked on slug identity rather than count, an order-independence check, and three end-to-end integration tests proving the admission limit's drop lands in resolution.omitted rather than vanishing.
* rite(ef): occurrence dispositions (RG 92-95, 33, 94)Lukasz Kasprzak2026-08-111-4/+144
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Precedence_ef.disposition decides the loser's fate in an occurrence: a Commemoration_only celebration is always commemorated (it can never win or transfer); a I- or II-class vigil impeded by any Sunday or a I-class feast is entirely omitted (RG 33), checked before the generic rule below or the Nativity/Pentecost Vigil could wrongly transfer; any other I-class loser transfers (RG 95 -- only I class has the right of translation); everything else is commemorated, with the admit-or-omit decision left to RG 108-111's admission count (Task 9). RG 94 needs no code: resolve always compares a loser against the day's actual winner, never against a departed sibling, so no commemoration can ride along with a transferred feast in this design. This is the branch that completes Task 7's carried All Souls fix: once it loses to an occurring Sunday, its untouched Class1 rank routes it to Transfer via the generic rule, not a special case. Landing on 3 November is Rite.transfer_target's job, not wired up yet. Commemorate carries an interim Precedence.Ordinary privilege pending Task 9's RG 109 implementation, exposed as interim_privilege for that task to replace. Table-driven tests cover each rule, including RG 33's boundary from both sides and a Commemoration_only loser that is also Class1 and vigil-shaped to pin the branch ordering. Mutation-tested: disabling RG 33, either direction of RG 95's rank condition, or the Commemoration_only priority check each fail exactly the rows built to catch them.
* rite(ef): fix entry 8's Sunday exception and entry 14's over-restrictionLukasz Kasprzak2026-08-111-6/+96
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Review of 436ba75 found two calendar defects and one coupling risk. Entry 8 (All Souls) dropped register line 334's own qualifier, "(yields to an occurring Sunday)" -- it returned 8 unconditionally, so on 2 November falling on a Sunday (2025, 2031, 2036, 2042 in the 2005-2050 differential window), All Souls incorrectly outranked and observed over the Sunday. 2 November is always Time_after_pentecost and never coincides with any other entry's own date, so the only rival this exception ever has is an ordinary entry-15 II-class Sunday; on such a Sunday, band now returns one worse than entry 15's own value rather than the literal 8 -- strictly worse, not merely different, since an exact tie would fall to Precedence.resolve's slug tie-break, which for "ef-all-souls" against a Sunday slug would make All Souls win the tie anyway. Entry 8's own rank is untouched, so Task 8's disposition still sees a true I-class candidate to transfer. Entry 14 (Feasts of the Lord, II class) added a universal-layer restriction the register's line 341 does not carry -- contrast entry 16 at line 342, which explicitly says "not of the Lord"; RG 37c (register line 393) also speaks of "II-class feasts of the Lord" with no universal qualifier. Dropped the layer test: a proper or indult feast of the Lord now bands 14, not 19/20. Also exposes vigil_suffix and ember_prefixes from precedence_ef.mli, matching universal_layer/indult_prefix's existing "colitur convention, not an RG citation" treatment -- both were previously private literals duplicated in the test file, so a rename of temporal_ef.ml's slug format could have drifted silently past both sides agreeing with each other. Two test rows now build their candidate from Temporal_ef.temporal's own real output (entry 18's Lent Ember day, entry 21's Ascension Vigil) instead of a hand-typed slug, closing that specific coupling. Adds the three cheap rows review named as closing the remaining unpinned guards (a temporal-origin Class1 candidate on an unnamed date; a universal Class2 vigil of the Lord; a non-universal Class3 vigil), plus a resolve()-level test proving the Sunday is actually observed over All Souls, not just that band returns the right integer in isolation.
* rite(ef): RG 91 Table of PrecedenceLukasz Kasprzak2026-08-111-0/+202
Precedence_ef.band transcribes RG 91's 28-entry Table of Precedence (rules-register.md §4) for the EF rite: given a day's context and a candidate celebration, returns the table's own entry number, 1-28 (I class 1-13, II class 14-21, III class 22-26, IV class 27-28); lower wins. Every branch carries its entry number and register citation in a comment, checked in the table's own numeric order. Two entries are transcribed as the register states them even though they invert the pattern the rest of the table follows: at III class, 23 (particular calendars) outranks 24 (universal), the reverse of how 11/12 and 14/16/19/20 rank a universal feast ahead of a proper one at I and II class. Sanctoral-origin, layer-decided entries (11-13, 14/16/19/20, 23/24) follow the brief's structural insight: a celebration whose layer is not the universal base is an overlay -- proper, or indult if its layer id also carries the indult prefix. Neither the universal-layer id nor the indult prefix is an RG citation; both are colitur's own data-modelling convention, exposed from the module so whichever task loads the real EF sanctoral overlays can align to them. Vigils (21, 26) are read off the temporal cycle's own -vigil slug suffix rather than gated on origin, since a II/III-class vigil can be either temporal-origin (Ascension, already produced by temporal_ef) or sanctoral-origin (a saint's vigil, no task has loaded yet); Ember days (part of entry 18) are read off temporal_ef's own ember slug prefixes rather than re-derived, since the September anchor is independently flagged there as one of the more contested dates in the calendar. A candidate shape the table has no row for (e.g. a Class1 vigil that is not Nativity or Pentecost, or a Class4 candidate marked as a vigil -- RG 91 has no IV-class vigil either) returns a dedicated unclassified sentinel (max_int) rather than being folded into a same-rank entry it does not belong to. test_precedence_ef.ml is table-driven: one Alcotest.test_case per RG 91 entry (55 rows total, several entries covered by more than one named day so a single missed offset cannot hide behind a passing sibling), each date computed from Computus.gregorian_easter rather than hand-typed, so an arithmetic slip cannot pass by accident.