aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar/precedence_ef_repro_test.go
Commit message (Collapse)AuthorAgeFilesLines
* gen-sanctoral-ef, caldata: fix round 2 -- review found five real problemsLukasz Kasprzak2026-08-121-0/+38
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | External review of the seven-defect fix (round 1) confirmed all seven correctly and structurally fixed, then found five further problems in what shipped alongside, two of them critical. All five verified independently before being fixed, not applied on the review's say-so alone -- see each item below for the primary-source check performed. CRITICAL 1: the regeneration destroyed all 322 Polish display names. main() preserved name.la across a regeneration (missalemeum has no Latin titles) but had no equivalent for name.pl, and never emitted one -- name.pl count went 322 -> 0 (confirmed: `git show 2386a45:internal/caldata/tridentine-calendar.ini | grep -c '^name.pl'` = 322; the pre-round-2 file = 0). naming.CelebrationName's own name[lang] -> name.en fallback then silently substituted English for every Polish EF display, reaching mobile.Day(date, "ef", version, "pl") -- a shipped dlectio entry point -- with no error anywhere in the chain, and nothing in this repo's test suite asserting any name.* field at all. Fixed at the root, not by re-adding name.pl specifically: `entry.la` (one hardcoded language) is replaced by `entry.otherNames map[string]string`, populated from every "name.<lang>" field already present except name.en (English always comes fresh from missalemeum) and re-emitted verbatim, sorted for deterministic output -- a third or fourth language added later survives a regeneration without this function changing again. CRITICAL 2: St Romanus was deleted, and St Eusebius (14 August) excluded, on a primary-source claim the primary source itself contradicts. The exclusion cited the calendarium's 9 August row as reading only "Vigilia, III classis.", no "Com." line -- checked against ONE of the three local Missal scans, "1962-06-23,...LT.pdf", an ELECTRONIC TRANSCRIPTION. The other two, PHOTOGRAPHIC scans of the actual 1962 Missale Romanum, both carry it: "missale-romanum-1962.pdf" calendarium, 9 August: "XVI d V 9 Vigilia, III classis, Commemoratio S. Romani Mart.", with the saint's own proper text elsewhere in the same scan ("Et fit commemoratio S. Romani Mar-") and its own back-of-book index ("Romani Mart., 9 augusti ... 621"). The transcription silently drops vigil commemorations generally (also missing there, present in both photographic scans: 7 August Donatus, 25 December Anastasia). 14 August's "St. Eusebius" is the identical shape (calendarium: "XI b XIX 14 Vigilia, II classis, Commemoratio S. Eusebii Conf.") and genuinely a DIFFERENT person from 16 December's "St. Eusebius, Ep. et Mart." (calendarium: "V XVII S. Eusebii Ep. et Mart., III classis.") -- a Confessor and a Bishop-and-Martyr, not the same saint duplicated. knownSpuriousComm (both entries) is removed outright; slugOverride gains "08-14/eusebius" -> "eusebius-confessor" so the two no longer collide by slug. Going forward: the photographic scans are the primary source; the electronic transcription is a convenience index only; where they disagree, the scan wins -- recorded in the generator's own comments, not just here. IMPORTANT 3+4: Thomas Becket (29 Dec) and Silvester (31 Dec) were silently promoted to class-4 by a hidden coupling. RG 68(d)/(e): "die 29 decembris, fit commemoratio S. Thomae Episcopi et Mart.; die 31 decembris, fit commemoratio S. Silvestri I Papae et Conf." -- both a bare "Commemoratio" with NO class of their own; the DAY they fall on (within the Nativity Octave) is II class, confirmed in the same calendarium row. refYearExplainsAbsence calls calendar.Compute and trusts a commemoration's own id-rank when the temporal day looks class-1/2-strong; after this task's own earlier fix promoted 26-31 December from class-4 to class-2 (RG 67/68), that trust flipped for these two from "not explained" to "explained" purely as a side effect of an unrelated temporal_ef.go change -- the generator's data inference reads the engine's own computed ranks, so a temporal_ef.go rank change can silently rewrite generated data. Fixed narrowly (6-31 December excluded from ever trusting the id-rank, citing RG 68(d)/(e) directly) and the coupling itself documented in refYearExplainsAbsence's own doc comment as a standing hazard for the next temporal_ef.go rank change, not just this one instance. Separately, the SAME function's doc comment overstated its own guarantee ("ANY class-1..4 saint would lose there") -- false for its Lent/Passiontide limb, where a III-class privileged feria does not beat a I- or II-class feast (RG 109(e) is privilege over an equal-or-lower class only) -- and the "commemoration id names the saint's TRUE rank" claim at the call site was falsified by its own worked example (St Blaise's id claims rank 4, and he is still correctly ruled RankCommemoration). Both rewritten to describe this as the rank-blind sampling heuristic it actually is, not a rubric evaluator. IMPORTANT 5: the Purification's own citation, strengthened. Three fixes to classOf's doc comment, no data change (the tag stays "lord", per round 1's own decision): the colitur cross-reference is removed (colitur bootstraps from lectio and was reading the same oracle a second time, not independent corroboration, and citing a sibling project's in-flight branch is not itself an argument); the rule that actually makes the occurrence pattern diagnostic is now named -- RG 91 entry 14 ("Festa Domini II classis") above entry 15 ("Dominicae II classis") above entry 16 ("Festa II classis... quae non [sunt Domini]"), each verified directly against the scan, not paraphrased; RG 120(b) is recorded as genuine primary-text counter-evidence ("Adhibetur color albus... b) B. Mariae Virg., etiam in benedictione et processione candelarum die 2 februarii" -- 2 February filed under the white-colour rule's OWN "B. Mariae Virg." heading, separate from 120(a)'s "Domini" heading), and RG 112(b) ("Officium, Missa aut commemoratio de dominica excludit commemorationem... de festo vel mysterio Domini, et vicissim") is cited as independently backing the empty commemoration list. A new committed fixture test replaces reliance on the report alone, since the deciding years (2 February on a Sunday) fall outside this repo's committed 2026-2027 oracle snapshot: TestPurificationBeatsFebruarySunday (internal/calendar/precedence_ef_repro_test.go), five independently fetched years. One of the years this reasoning is sometimes quoted against, 2036, is corrected in passing: 2 February 2036 is in fact a Saturday, not a Sunday (`date -d 2036-02-02 +%A`) -- checked here rather than repeated, 2042 used instead. Witnesses (internal/caldata/caldata_test.go): TestTridentineNamesPreservedAcrossRegeneration, TestTridentineRomanusAndEusebiusPresent (replaces TestTridentineNoSpuriousRomanus, whose own name asserted the now-corrected wrong claim). Pre-fix failures (captured against the committed pre-round-2 state, commit 50e3970): name.pl coverage = 0 entries, want >= 315 assumption-of-the-blessed-virgin-mary: name.pl = "", want the preserved Polish name romanus missing: the calendarium's photographic scans both carry "Commemoratio S. Romani Mart." on 9 August missing "eusebius-confessor" (08-14) internal/caldata/tridentine-calendar.ini regenerated from the ORIGINAL branch-point data (`git show 2386a45:...`), not from this session's own already-damaged intermediate file -- regenerating from an already-corrupted source would have preserved nothing, since the preservation mechanism can only preserve what is actually on disk when it runs. Verified directly (not merely re-tested): all 322 name.en/name.pl values byte-identical to the branch point; 15 rank fields and 2 class fields differ (the round-1 Lenten-rank and class-tag fixes, unchanged by this round); 5 new slugs added (agnes-secundo, boniface-martyr, eusebius-confessor, evaristus, theodore); 0 slugs removed.
* calendar, precedence_ef: two more RG 91 gaps exposed by defect 4Lukasz Kasprzak2026-08-121-0/+39
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Found during the mandated 2005-2050 before/after verification (not one of the seven, but caused by defect 4's own fix, so fixed in the same pass rather than left as a self-introduced regression). Neither is a hypothetical: both are live in the fixed calendar and both were individually confirmed via cmd/lectio-ef-dump before being written up as tests. 1. RG 91 entry 4 (Immaculate Conception, Assumption BVM) sits ABOVE entry 6 (Sundays of Advent/Lent/Passiontide) -- unlike an ORDINARY I-class feast (entry 11, e.g. St Joseph), the Immaculate Conception (8 December) is not impeded by an Advent Sunday at all. Before defect 4, Advent Sundays were wrongly II class, so this was accidentally right (I beats II outright, no tie reached); once Sundays became I class the tie-break mattered for the first time, and precedenceEF had no branch for it -- the Sunday wrongly won. 2. RG 91 entry 5 (Vigil & Octave day of the Nativity) sits above entry 6 the same way, and its own gap was worse than a wrong winner: the Vigil of Christmas (24 December) falling on Advent IV, once defect 4 made that a genuine I-class tie, sent the Vigil into transferIfImpededEF's forward walk -- which has no way to re-place a transfer crossing the Dec 31/Jan 1 boundary (celebrationDate re-resolves a fixed date using the YEAR OF THE DAY BEING QUERIED, so a walk landing in the following January can never match the query that produced it). The Vigil did not move to the wrong day; it vanished for the whole year, for every year 24 December is a Sunday (2006, 2017, 2023, 2028, 2034, 2045). Both fixed the same way as the existing II-class-feast-of-the-Lord bonus in precedenceEF (a one-line precedence adjustment keyed on slug), generalised into a single beatsClass1Sunday helper covering both RG 91 entries. A separate, unrelated bug surfaced by the SAME verification pass and fixed alongside it: transferIfImpededEF's destination check reused precedenceEF's tie-break BAND to decide "is this day I or II class", but band encodes a different question (which of two EQUAL-class candidates wins a tie) -- an ordinary, non-Sunday II-class temporal candidate YIELDS under defect 2's own fix (band 5), even though it is genuinely II class. A day within the Octave of the Nativity (26-31 Dec, RG 67) is exactly such a day, so band<=3 alone let a transfer wrongly land inside it (a real reproduction: "vigil-of-christmas" would have landed on 29 December 2006 instead of vanishing outright, caught while tracing the entry-5 bug above). Replaced with a direct class test (isHighClass), which is both correct and simpler -- it no longer needs the Sunday flag at all for this particular check. Witnesses (precedence_ef_repro_test.go): TestImmaculateConceptionBeatsAdventSunday, TestVigilOfChristmasSurvivesAdventSunday. Fail before this commit with: 2013-12-08 observed = "ef-advent-sunday-2" want immaculate-conception-of-the-blessed-virgin-mary (RG 91 entry 4 beats entry 6) 2006-12-24 observed = "ef-advent-sunday-4" want vigil-of-christmas (RG 91 entry 5 beats entry 6; must not vanish) (full set: 2013/2019/2024 for the first, 2006/2017/2023/2028 for the second)
* precedence_ef: a II-class privileged feria yields to a feast (defect 2)Lukasz Kasprzak2026-08-121-0/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | precedenceEF's equal-class tie-break only distinguished "ordinary III/IV-class feria yields" from "everything else wins" -- Sundays, named feasts, Lent/Passiontide ferias, AND II-class privileged ferias (the Ember days, the late-Advent 17-23 Dec ferias) were all bucketed into the same "wins its tie" branch. RG 91 disagrees at class 2: entry 15 (Sundays) sits ABOVE entry 16 (II-class feasts of the universal Church), so a Sunday wins -- but entry 16 sits ABOVE entry 18 (II-class ferias, including the Ember days), so those FERIAS yield instead, the opposite direction from a Sunday. St Matthew (21 September, II class) was losing to the September Ember Wednesday every time the two coincided; St Thomas (21 December, II class) was losing to an ordinary late-Advent feria the same way once the previous commit correctly promoted those ferias to II class. The fix distinguishes a II-class Sunday from a II-class feria using a Sunday flag that already existed on `candidate` (used by the OF path) but was never wired up for EF: temporal_ef.go's efCel always set Sunday: false, even on an actual Sunday. A new efSunday helper (efCel plus the flag) replaces the three efCel calls inside temporalEF's Sunday branch, and computeEF now propagates td.Sunday into the day's temporal candidate. precedence_ef_test.go's own pre-existing witness ("at equal class, the temporal office wins") encoded exactly the bug: a bare class-2 temporal candidate with no Sunday/season information, standing in for "the temporal office" in general. It is rewritten into two explicit cases (Sunday wins its tie; a privileged feria yields) plus the existing Lent/Passiontide-vs-ordinary III/IV-class case restated explicitly rather than left implicit. Witness (precedence_ef_repro_test.go): TestMatthewBeatsSeptemberEmberWednesday. Fails before this commit with: 2016-09-21 observed = "ef-september-ember-wed" want matthew (RG 91 entry 16 beats entry 18) 2022-09-21 observed = "ef-september-ember-wed" want matthew (RG 91 entry 16 beats entry 18) With this commit, all seven named defects are fixed: LECTIO_EF_ORACLE_STRICT=1 go test ./internal/calendar/... -run TestOracleEF passes (0 unallow-listed rank/colour mismatches over 730 days). The gate stays in place for this commit; a following commit removes it.
* calendar: RG 96 transfers must skip II-class days too (defect 1)Lukasz Kasprzak2026-08-121-0/+22
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | transferIfImpededEF's forward walk only skipped days whose temporal office was I class. RG 96 requires skipping to "the next following day that is not I or II class" -- II class also blocks. 2011: the Sacred Heart (Friday after the Corpus Christi octave) falls on 1 July and impedes the Precious Blood, also fixed on 1 July. The walk landed the Precious Blood on 2 July, displacing the Visitation (II class, fixed) outright, and never reached 3 July (an ordinary II-class Sunday) at all. Two changes were needed together, not one: 1. The walk's continuation threshold widens from "temporal band <= 2" to "<= 3", which is what actually captures every II-class temporal day (an ordinary Sunday, or a II-class named feast) as well as every I-class one -- band 3 is precedenceEF's own value for any non-Sunday-privileged II-class temporal candidate, unchanged by this commit. 2. A FIXED sanctoral II-class feast (the Visitation) has no temporal band at all -- the walk needs to also check whether some OTHER fixed celebration, class 1 or 2, already resolves onto the candidate day. computeEF now builds this check once (occupiedByRank, parameterised by which ranks count) and passes it in. Widening (1) alone is wrong on its own: a class-1 feast is NEVER actually impeded by a mere II-class day (I class always outranks II class outright, no tie exists) -- naively applying the wide "<=3" threshold to decide whether the ORIGINAL date is impeded, not just where to land afterwards, wrongly bumped unimpeded feasts landing on an ordinary Sunday (caught while testing this: All Saints, 1 Nov 2026, a Sunday that year, was wrongly pushed to 3 Nov). transferIfImpededEF now uses two different thresholds for two different questions -- class 1 only to decide IF a candidate is impeded at all, class 1 OR 2 to decide where an already-impeded one may land -- see its own doc comment for the full reasoning. Witness (precedence_ef_repro_test.go): TestTransferSkipsBothIAndIIClass. Fails before this commit with: 2011-07-02 observed = "precious-blood-of-our-lord-jesus-christ" want visitation-of-the-blessed-virgin-mary (RG 96: the Precious Blood must skip past it, not displace it) 2011-07-04 observed = "ef-time-after-pentecost-3-monday" want precious-blood-of-our-lord-jesus-christ (RG 96: first day that is neither I nor II class)
* temporal_ef: Sunday ranks, Rose Sundays, Holy Thursday whiteLukasz Kasprzak2026-08-121-0/+45
Four fixes to internal/calendar/temporal_ef.go, all found by the strengthened oracle test (previous commit): 1. Sunday ranks (defect 4). The generic Sunday branch assigned class-2 to every Sunday except Advent I. RG 11-12 / RG 91 entry 6: every Sunday of Advent and every Sunday of Lent is I class (as are Passiontide's own two Sundays and Low Sunday, already correct via their named-feast cases). Fixes St Joseph wrongly taking a Sunday of Lent (defect 3, precedence_ef_repro_test.go's TestJosephYieldsToSundayOfLent) as a direct consequence: once Lent Sundays are I class, Joseph (also I class) no longer wins the tie outright and correctly transfers to the 20th via the existing RG 96 walk -- no separate code change was needed for defect 3. 2. Rose (defect 5). efColour had no Rose case at all; Gaudete (Advent III) and Laetare (Lent IV) now get Rose on that Sunday specifically (RG 131: rose vestments may be used "in Officio et Missa diei dominici tantum", for that Sunday's Office and Mass only), not the surrounding Sundays. 3. Holy Thursday's colour (defect 6). RG 128(b) names the Missa in Cena Domini as a whole-Mass exception to Passiontide's violet; RG 122 states the same fact affirmatively, in the White section itself. Good Friday and Holy Saturday, either side, are unchanged (still violet -- their own black/no-colour treatment is a separate, unmodelled gap, noted in precedence_ef.go's own doc comments). 4. Beyond the seven, found by the same strengthened test and fixed for the same RG 91 entry 18 reason defect 2 (next commit) relies on: the Ember days of Lent had no case in efEmberSlug at all (only September and Advent did), so they fell through to the ordinary III-class Lenten-feria rank instead of the II class RG 91 entry 18 requires. The late-Advent ferias (17-23 Dec, RG 91 entry 18) and the days within the Octave of the Nativity (26-31 Dec, RG 67-68) had no elevation at all, defaulting to III/IV class. All three are one-line, unambiguous, primary-cited additions to the same rank logic already being touched here -- left unfixed, the strengthened oracle test could only reach green by allow-listing them as if they were defensible divergences, which they are not. Witnesses (temporal_ef_test.go): TestTemporalEFSundayRanks, TestTemporalEFRoseSundays, TestTemporalEFHolyThursdayColour, TestTemporalEFEmberDayRanks. All fail before this commit; see the report for the exact pre-fix failure messages.