aboutsummaryrefslogtreecommitdiff
path: root/scripts
Commit message (Collapse)AuthorAgeFilesLines
* gen-sanctoral-ef, caldata: fix round 2 -- review found five real problemsLukasz Kasprzak2026-08-121-84/+180
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* gen-sanctoral-ef: keep the Purification tagged lord -- the brief was wrong hereLukasz Kasprzak2026-08-121-7/+28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Disagreement with the task brief, found and corrected before landing, not after: the brief's defect 7 named the Purification (2 February) as "wrongly tagged lord... a BVM feast", citing the calendarium's own title, "IN PURIFICATIONE B. MARIAE VIRG." That citation is accurate -- but the tag's actual job is not naming or colour categorisation, it exists solely to drive one occurrence rule ("a II-class feast of the Lord takes an occurring Sunday's place outright"), and on THAT question the title is not the decisive evidence. Checked directly against missalemeum (this data's own oracle, live, not archival): the Purification takes a II-class Sunday's place OUTRIGHT, commemorations EMPTY (2014-02-02, 2020-02-02, 2025-02-02, all fetched fresh) -- the exact "festum Domini" pattern. Control: the Nativity of the BVM (8 September, an undisputed ordinary Marian feast) on a Sunday (2019-09-08) shows the opposite shape, the Sunday observed and the feast merely commemorated. The Purification's occurrence behaviour does not match an ordinary BVM feast's. Independent corroboration, found while comparing lectio's output against colitur's (the sibling engine this task's brief points to): colitur's own git history shows the identical retag, made and then reversed within the same review cycle it landed in -- cab8b07 "retag the Purification... as BVM, not Lord" (the brief's own reading), followed by 7d3b5ec "follow the oracle on the Purification" (the reversal, on the same live evidence independently re-derived here). That commit records the calendarium-title counter-evidence too and calls it "a genuinely contested point... a different project could reasonably rule the other way" -- not a clean-cut error either direction, but the weight of evidence for THIS tag's actual purpose favours keeping it lord. most-holy-name-of-mary (the OTHER entry the brief named) is untouched by this reversal and stays retagged away from lord -- its own occurrence behaviour matches the ordinary-BVM pattern, not contested by either project. internal/caldata/tridentine-calendar.ini regenerated; only the Purification's class field changes back (empty -> lord), 6 civil days 2005-2050 (every year 2 February falls on a Sunday: 2014, 2020, 2025, 2031, 2042, 2048) revert to matching what the branch point (and colitur's own current state) already had -- net zero change to PUBLISHED output on this specific point relative to before this task started, despite the intermediate wrong-then-reverted state along the way.
* scripts/gen-sanctoral-ef: fix rank inference, dedup, and class tagging ↵Lukasz Kasprzak2026-08-121-32/+236
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (defect 7) Six root-cause fixes to the generator, all in scripts/gen-sanctoral-ef.go, then a full regeneration of internal/caldata/tridentine-calendar.ini (323 -> 325 entries) against live missalemeum data: 1. Rank misinferred as "commemoration". A saint never OBSERVED in any of the six reference years was unconditionally hardcoded to RankCommemoration. 15 III-class feasts, all falling 6 March - 5 April, were always demoted by Lent's own privileged ferias in every reference year -- a sampling artifact, not their true rank. Fixed properly, not by hardcoding the 15: the commemoration object's own id names the SAINT's true rank (idParts already extracted it; only the colour half was kept before). But trusting that rank unconditionally for every commemoration-only entry over-corrected: some saints (St Blaise, St Canute, and others) really are without an independent Mass in the 1960-reformed books, demoted even on an ordinary, unprivileged day -- live-confirmed against missalemeum, not inferred. refYearExplainsAbsence distinguishes the two: the id's rank is trusted only when every reference year's OWN temporal day (ignoring sanctoral data entirely) was already strong enough to explain the demotion -- I/II class, an Ember day, or a privileged Lent/Passiontide feria. A single unprivileged-day counter-example keeps RankCommemoration. 2. Two silent slug collisions. entries (the generator's global map) is keyed by slug alone; two different fixed dates whose English titles are identical ("St. Agnes" 21 Jan and 28 Jan; "St. Boniface" 5 June and 14 May) silently dropped the second one add() ever saw. Fixed at the dedup itself: a same-slug collision on a DIFFERENT date is now disambiguated (a small curated slugOverride for these two -- 28 January is the traditional "secundo" commemoration of the same saint; 14 May's Boniface is a different martyr from 5 June's Boniface of Mainz) rather than dropped. 3. Two early-exit bugs in harvestDate, both replaced with `continue`: returning on the FIRST reference year that showed an observed office discarded commemorations only visible in a LATER year (St Theodore, 9 November, missing only from 2025, the first year tried); the Christ-the-King skip used to abandon the whole date outright instead of only that one movable year (St Evaristus, 26 October, invisible because 2025 -- tried first -- happens to be the one reference year Christ the King lands there). 4. A new class of artifact the fix for (2) exposed rather than caused: a movable-transfer feast displayed on whatever civil date it actually landed on in one reference year (St Joseph pushed to 20 March by a Sunday of Lent; the Annunciation deferred past Holy Week; All Souls moved to the Monday; St Matthias shown on the 25th in a leap year) was being harvested as if it were a genuine fixed-date office for THAT transferred date. idHomeDate compares info.id's own embedded date against the mmdd being queried (live-confirmed: 2023-03-20's id is "sancti:03-19:1:w") and skips a mismatch. 5. classOf's substring matching, checked against the calendarium's own verbatim titles: "purification" was never a Lord check at all (calendarium "IN PURIFICATIONE B. MARIAE VIRG.", a BVM feast) and is removed; "holy name" alone matched both "Holy Name of Jesus" and "Most Holy Name of MARY" (calendarium "Sanctissimi Nominis Mariae") and is now conditioned on the title not also naming Mary; "baptism" is a new case -- "Commemoration of the Baptism of the Lord" (13 January) ends "of THE Lord", not "of OUR Lord", the only suffix classOf checked, so it was missing the marker entirely. 6. One curated exclusion, NOT a generator bug: missalemeum's own "St. Romanus" commemoration on 9 August has no calendarium support (the row there reads only "Vigilia, III classis.", no "Com." line) -- an upstream data quirk, recorded in knownSpuriousComm rather than silently reproduced. "St. Eusebius" on 14 August (already known, register M6) gets the same treatment for the same reason. Witnesses (internal/caldata/caldata_test.go): TestTridentineClassOfLord, TestTridentineLentRankNotCommemoration, TestTridentineGenuineCommemorationStaysCommemoration, TestTridentineNoMissingEntries, TestTridentineNoSpuriousRomanus, TestTridentineNoTransferArtifacts, plus TestTridentineLoads' own Purification class assertion corrected from its previous (wrong) "lord" expectation. All fail against the pre-fix ini with messages such as: purification-of-the-blessed-virgin-mary: class = "lord", want "" sts-felicitas-perpetua: rank = "commemoration", want class-3 (not commemoration) missing "theodore" (11-09) romanus present: the calendarium's 9 August row has no Com. line to support it See the report for the full set. internal/caldata/tridentine-calendar.ini is regenerated output (go run scripts/gen-sanctoral-ef.go against live missalemeum data), not hand-edited.
* test(ef): strengthen the oracle test to assert rank and colourLukasz Kasprzak2026-08-121-26/+30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | internal/calendar/oracle_ef_test.go asserted Season only. lectio's own EF oracle test comment said as much ("rank/colour are reported informationally"), which is exactly why several precedence/colour defects in precedence_ef.go and temporal_ef.go shipped without ever failing a test: the suite was green while the observed office's own rank and liturgical colour could be wrong. Rebuild the oracle from the committed missalemeum snapshot (sources/snapshot.tar.gz, missalemeum/en/YYYY-MM-DD.json, 2026-01-01 .. 2027-12-31, 730 days) instead of a live 2025-2026 fetch, via a rewritten scripts/build-oracle-ef.sh -- offline, reproducible, and correctly separating info.rank/info.colors from info.id (whose embedded rank is the rank of the propers REUSED that day, not the day's own rank -- e.g. 2026-01-02 is a class-4 feria carrying id "sancti:01-01:1:w" because it reuses the Circumcision's propers). info.colors is an array (14 of 730 days carry two values -- Gaudete/ Laetare "pv", Palm Sunday "rv", Good Friday "bv", Holy Saturday "vw"), so the new Colour assertion is membership, not equality. The strengthened assertions immediately expose several real defects (rank mismatches on every Advent/Lent Sunday, the Ember days, the late-Advent and Christmas-octave ferias, and more) -- that is the point, this is the regression net subsequent commits fix against. Since the repo's convention is go test ./... green at every commit, TestOracleEF is gated behind LECTIO_EF_ORACLE_STRICT=1 for now rather than landed red; a later commit removes the gate once the fixes are in. Season alone stays green throughout (unchanged, always was). A small, cited allow-list (efAllowList) is included from this commit: genuine, defensible divergences unrelated to the fixes ahead of it -- RG 91 entry 27's un-built BVM Saturday Office, the pre-existing 6-13 January Christmastide/time-after-Epiphany season-boundary divergence, missalemeum's own RG 33 gap on a Sunday 9 August, and one collision (St Joseph vs the Friday of Passion Week, 2027) left explicitly unresolved even by the sibling project's much deeper primary-source pass. None of the four is one of the seven defects this branch fixes.
* sources: store the snapshot as one compressed archiveLukasz Kasprzak2026-07-301-1/+19
| | | | | | | | | Replace the ~1600 raw per-provider files (24 MB) with a single solid sources/snapshot.tar.gz (6 MB); manifest.tsv + README stay uncompressed for browsing. The extracted per-provider dirs are now git-ignored; snapshot-sources.sh gains pack/unpack, and the README documents the unpack -> refresh -> pack cycle. Distribution-ready: one archive, not a file tree.
* sources: archive upstream generator data for offline reproducibilityLukasz Kasprzak2026-07-301-0/+108
| | | | | | | | | | | | | | | | | Snapshot the raw third-party responses the gen*/oracle scripts fetch, so lectio's liturgical data can be regenerated and re-audited offline if the sites change or vanish (the 25-year-longevity aim). Reading references are facts, not creative works -- this is a provenance cache, not redistribution. scripts/snapshot-sources.sh (idempotent, manifest.tsv with sha256) archives: - catholic-resources.org: all 14 OF lectionary tables (browser UA; its WAF 465s unusual agents) - missalemeum en+pl, per-date 2026-2027 (1460 files, EF calendar/propers/pl names; widen MM_FROM/MM_TO for the full 2024-2029 harvest) - litcal whole-year OF calendars 2024-2035 - get.bible Douay-Rheims deuterocanon (Tobit..2 Maccabees) sources/README.md documents provenance, coverage, and the one gap: calapi.inadiutorium.cz is IPv6-only/unreachable here (litcal covers OF).
* build: add ci + release targets, a pre-push hook, and a CHANGELOGLukasz Kasprzak2026-07-291-0/+5
| | | | | | | | | | | | | | | Process hygiene so the version never silently drifts again and the two build-tag test sets stay green. - `make ci`: gofmt-clean + go vet + `go test ./...` (default tags) AND `make test` (-tags fullbible + corpus checks). The default-tag suite is easy to forget (only fullbible embeds the corpora), so ci runs both. - scripts/hooks/pre-push + `make install-hooks`: run `make ci` before any push (bypass with --no-verify). - `make release VERSION=x.y.z`: refuse a dirty tree or a missing CHANGELOG entry, bump const Version, run ci, commit, and tag vX.Y.Z (prints the push command -- publishing stays a manual choice). - CHANGELOG.md: terse, one line per release, starting at the first tags.
* bible(grb): restrict to the Vulgate canon; restore Hosea and ZechariahLukasz Kasprzak2026-07-291-0/+175
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | grb failed corpus-check outright: 15 errors, and two whole books absent from lectio at runtime with no error and no warning. Upstream grb is a standalone Septuagint reader carrying 87 books. lectio needs one uniform canon, so scripts/gen-grb-lectio.py derives the 73 books vul has rather than forking -- upstream stays intact and keeps everything. Eleven books have no Vulgate counterpart and are dropped, including 2 Esdras, which upstream's README explains IS Ezra + Nehemiah under the Septuagint's name; verified byte-identical, so nothing is lost. Five books ARE canonical under other names and are remapped, and the verse counts show which witness the Vulgate follows -- Jerome translated Theodotion, not the Old Greek: Bel and the Dragon (Theodotion) 42 = vul Daniel 14 (42) exact Bel and the Dragon (LXX) 37 no Sussana (Theodotion) 64 = vul Daniel 13 (65) Sussana (LXX) 37 no Letter of Jeremiah 73 = vul Baruch 6 (72) Wisdom of Solomon 435 = vul Wisdom (439) Upstream's plain "Daniel" is the Old Greek and is missing chapter 4 outright, so Theodotion supplies Daniel throughout: complete, and the tradition the lectionary actually cites. lectio thereby GAINS Wisdom, Daniel 4, Daniel 13-14 and Baruch 6 in Greek rather than losing them. Three upstream defects are repaired in transit: * 408 rows carried 5 fields, not 6 -- a lost tab fused the book number and chapter ("281" = book 28, chapter 1). Every one was in Hosea or Zechariah, and neither book had a single well-formed row, so bible.go skipped both entirely. Hosea (197 verses) and Zechariah (211) are back. * 5 merged verse labels ("27-28") that strconv.Atoi turns into verse 0. * A UTF-8 BOM welded to the first book name, making "Genesis" a phantom 74th book matching nothing. The SBLGNT apparatus sigla are stripped, as TestGrbNoApparatusMarkers requires; regenerating from raw upstream reintroduces ~8700 of them. grb now passes. The remaining warnings are the Septuagint being itself -- Jeremiah is LXX-numbered (grb 33:2 is the Vulgate's 26:2), Esther integrates its additions into chapters 1-10, LXX Malachi has three chapters, and 3 Kingdoms carries supplements like 10:22a that upstream stores as a duplicate verse 22. Silencing those would mean deleting real Greek text.
* bible(wuj): rebuild from source, repair to exact Vulgate parityLukasz Kasprzak2026-07-291-0/+177
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The Wujek corpus was missing text and mis-numbering what it had: 453 corpus-check warnings, 8 absent chapters, 84 duplicate rows, and 357 chapters holding fewer verses than the Vulgate. Most of it was never a sourcing problem. The original harvest of biblia.info.pl assumed one verse is one <p>, but many psalms print the superscription in the anchored paragraph and the psalm's opening line in an unanchored one behind a drop cap -- so 44 psalms lost their first line, and Acts 6:5 and others went the same way. Re-harvested with the anchor treated as a verse START rather than a whole verse (scripts/scrape-wujek.py), which also had to cope with four anchor shapes across books, hidden page markers opening paragraphs mid-verse, chapter ids that are simply wrong (Mark labels 87 anchors "15:*" across chapters 14-16), and Psalms heading its divisions "Psalm CXVII" where every other book says "Rozdzial N". Structure was then repaired against the Clementine Vulgate, by hand where a rule would have guessed: * The Acts 5 interleaved three streams -- Acts 4:27-37 duplicated, the real 5:1-37, and 5:38-39 mislabelled 28-29. Verified against the Latin, the duplicates dropped, the two renumbered. Acts 4:27 kept the site's own wording in place of a 1962 reading, removing a seam. * The site heads psalms by Hebrew division; its "Psalm 114" is two psalms. Split into Vulgate 113:9-26 and 114:1-9, confirmed at all four boundaries. * A corrupt anchor for 1 Chronicles 9:11 pushed 34 paragraphs of genealogy into chapter 10. Counts corroborate: 10 + 34 = 44 verses, remainder 14, both exactly the Vulgate's. * 16 chapters looked short at the end; only two verses were truly absent. The rest were merges, split at anchors read off the Latin -- including one across a chapter boundary (Colossians 4:1 sat inside 3:25) and four numbering offsets where a mid-chapter merge shifted everything after it. wuj now carries exactly the Vulgate's verse set: 35810 rows, every one of the 1334 chapters matching, no duplicates, no gaps. corpus-check: 0 warnings. The ~300 verses that could not come from biblia.info.pl are recorded in NOTICE; they are not public domain.
* scripts: add validate-engine.py per-day oracles (litcal/missalemeum)Lukasz Kasprzak2026-07-281-0/+253
| | | | | | | | | | | | Manual QA validators (not run by go test) that build the CLI and compare its computed calendar/readings day-by-day against authoritative online sources: of-calendar -- OF season/cycle/observed vs the LiturgicalCalendar API (litcal) of-readings -- every OF reading resolves against a corpus (offline) ef -- EF reading gaps + tail/resumed-Sunday gospels vs missalemeum ranks -- OF sanctoral ranks vs litcal These found the defects fixed in the flawlessness pass; keep them as standing regression oracles. API responses cache under scripts/.oracle-cache/ (ignored).
* fix(of): partial-proper memorials compose proper part + ferial (match USCCB)Lukasz Kasprzak2026-07-281-6/+15
| | | | | | | | | | | | | | | | | | A memorial marked 'PROPER Gospel' (Guardian Angels, Mary Magdalene, Our Lady of Sorrows, Joseph the Worker, the Passion of John the Baptist, Sts Martha/Mary/ Lazarus) uses the FERIAL first reading + its proper gospel; one marked 'PROPER First Reading' (Barnabas) uses its proper first + the ferial gospel. Previously we imported catholic-resources' *suggested* first for the gospel-proper ones, which differs from what USCCB publishes. - readings.go: when the observed's inline Mass has only a proper gospel (or only a proper first), fill the missing reading/psalm from the day's ferial and sort into canonical order. A fully-proper feast is untouched. Factored out ofFerial(). - gen-of-sanctoral-readings.py: keep only the strictly-proper part per the CR source-note. Verified vs USCCB: Guardian Angels (2025-10-02) now = Nehemiah 8 / Ps 19 / Matt 18:1-5,10 exactly. Updates TestUniversalSanctoralReadings.
* feat(of): Christmas-season weekday ferials from catholic-resources.org ↵Lukasz Kasprzak2026-07-281-5/+17
| | | | | | | | | | | | | | | (date-based) Completes the seasonal weekday lectionary. The Christmas-season weekdays sit on the AdventChristmas page in a SHIFTED 6-column layout (Day in c[1], not c[2] like Advent's 7-column) -- the generator now finds the Day column wherever it is. Adds date-based slugs to temporal.go: christmas-dec-<29..31> (octave), christmas-jan-<2..7> (before Epiphany), christmas-after-epiphany-<weekday> (before the Baptism). Fixes the christmas-octave-fri coverage gap and the by-date/by-week collision. 362/362 seasonal ferial-days render across 2026-2029 with 0 gaps. Extends TestOFSeasonalFerials. All of Advent/Lent/Easter/Christmas weekdays now CR-sourced; only the Commons remain (optional -- memorials default to the ferial).
* feat(of): seasonal weekday ferials from catholic-resources.org + date-based ↵Lukasz Kasprzak2026-07-281-0/+125
| | | | | | | | | | | | | | | | | | | | | | late Advent Re-sources the proper weekdays of Advent (weeks 1-3), Lent, and Eastertide from catholic-resources.org (scripts/genlect-of-season-ferials-cr.py; single-cycle, so both -I/-II keys get the same readings). The diff caught real niedziela defects: - Easter weekday first readings contaminated (easter-3-mon had 1 Cor 15 instead of Acts 6:8-15; easter-4-thu had Rev 12 instead of Acts 13:13-25). - The Easter octave weekdays (Easter Mon-Sat) had no readings at all. - Stray psalm-numbering artifacts (147A(146), Ps42(41)) cleared. Late Advent (Dec 17-24) is proper to the DATE, not the Advent week, so temporal.go now keys those days 'advent-dec-<day>' (fixing the same by-date/by-week collision as the OT numbering) and the generator supplies them (Dec 17 = the genealogy Matt 1:1-17; Dec 24 = the Benedictus). 157 seasonal ferial-days render across 2026-2028 with 1 residual gap (christmas-octave-fri). Adds TestOFSeasonalFerials, credits the source, bumps to 0.43.0. Christmas-season weekdays (Dec 29-31, Jan 2-5) remain niedziela-sourced (CR's shifted layout + Epiphany-dependent assignment -- a documented follow-up).
* chore: drop accidentally-committed __pycache__, ignore itLukasz Kasprzak2026-07-281-0/+0
|
* feat(of): complete Sunday lectionary from catholic-resources.org (restore ↵Lukasz Kasprzak2026-07-282-0/+182
| | | | | | | | | | | | | | | | | | | | | | | | | | 2nd readings) The niedziela harvest had dropped the SECOND reading on 271 of ~299 Sunday-cycle entries (a Sunday Mass is first/psalm/second/gospel). scripts/genlect-of-sundays-cr.py re-sources the temporal Sunday & solemnity lectionary (Ordinary Time, Advent, Christmas, Lent, Easter, Trinity, Corpus Christi, Sacred Heart, Christ the King; all three cycles A/B/C) from catholic-resources.org, keyed by liturgical position, and merges the complete readings into of-lectionary.ini. - Also corrects ~6 contaminated Sunday first readings the niedziela by-date harvest had picked up (ordinary-sunday-21-B had a Marian Prov 8; advent-2-A had the Immaculate Conception's Gen 3; Christ the King B/C, Trinity C, Pentecost, Ascension/7th-of-Easter were wrong). - Content-validated: every changed first reading verified against the old one (all changes are correct fixes or the identical passage, book spelling aside). - Handles source quirks: hyphen/en-dash/ABC cycle markers, verbose Year-A solemnity names, 'opt:' cycle-proper alternates, '[2025: Corpus Christi]' displacement notes appended to names, Palm Sunday (cycle-independent first/second, per-cycle Passion gospel kept), and abbreviation variants (Jon/Is/Mt/Act/Zac...). 157/157 Sundays render first+psalm+second+gospel across 2026-2028. Adds TestOFSundayCompleteReadings, credits the source in NOTICE, bumps to 0.42.0.
* feat(of): proper Mass readings for the sanctoral (from catholic-resources.org)Lukasz Kasprzak2026-07-281-0/+136
| | | | | | | | | | | | | | OF feasts, solemnities, and proper-reading memorials had no readings of their own and fell back to the day's ferial (Barnabas showed the ferial instead of Acts 11; Our Lady of Sorrows / the Guardian Angels showed the ferial instead of their proper gospel). scripts/gen-of-sanctoral-readings.py scrapes catholic-resources.org's Proper of Saints table and inlines reading.first/psalm/second/gospel into roman-calendar.ini for the 41 universal-calendar celebrations marked PROPER (plus every feast/solemnity); memorials that draw on a Common keep the ferial, which is the OF default. Handles the source quirks: 'or' alternatives (takes the first), cycle-specific A:/B:/C: gospels (takes A), lectionary # refs, and vigil-vs-day (keeps the day Mass). All 41 render; citations only. Adds TestUniversalSanctoralReadings, credits the source in NOTICE, bumps to 0.41.0.
* fix(of): canonical OT ferial week numbering + CR-sourced ferial lectionaryLukasz Kasprzak2026-07-281-0/+119
| | | | | | | | | | | | | | | | | | | | | | The Ordinary Form ferial lectionary had two linked defects, both from harvesting niedziela.pl by date: (1) post-Pentecost Ordinary-Time ferial WEEK NUMBERS were one too high (temporal.go counted back from Christ the King using the weekday itself, not its preceding Sunday, so a ferial took the next week's number), and (2) ~17 ferial keys held a SAINT's readings (stored when a saint displaced the ferial on the queried date, e.g. week 22 smeared with a Marian Prov 8/John 2), plus 16 weekday slots were missing entirely. - temporal.go: number a span-2 ferial from its preceding Sunday. Verified 388/388 ferial-week agreement with calapi across 2026-2028. - scripts/genlect-of-cr.py: regenerate the Ordinary-Time ferial lectionary (first/psalm/gospel, both years, all 34 weeks) from catholic-resources.org (Fr. Felix Just, S.J.), which is keyed by liturgical position (immune to by-date contamination) and complete. Citations only; text still rendered from the public-domain corpora. of-lectionary.ini OT ferials now 408 (was 392). Fixes ~17 contaminated entries + 16 gaps + the week-numbering audit item. Psalms already share modern numbering, so no conversion. Adds TestOFOrdinaryTimeFerialWeek. Credits catholic-resources.org in NOTICE. Bumps to 0.40.0.
* feat: complete OF+EF calendar coverage (movable feasts, vigils, ember days, ↵Lukasz Kasprzak2026-07-282-4/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | transfers) Ordinary Form: - Add the movable Marian memorials that have no fixed date: Mary, Mother of the Church (Monday after Pentecost) and the Immaculate Heart (Saturday after the Sacred Heart), computed in computeOF. - General Norms 60: two coinciding obligatory memorials both become optional and the ferial is observed -- except Mary, Mother of the Church, which keeps precedence per its 2018 decree. Extraordinary Form: - Capture fixed-date vigils in the sanctoral harvest (Christmas, the Nativity of St John the Baptist, Sts Peter & Paul, St Lawrence, the Assumption, All Saints, the Immaculate Conception) -- previously filtered out. - temporalEF: the movable vigils of the Ascension (I class eve) and Pentecost (Whitsun Eve), and the September/Advent Ember days (II class violet ferias). - transferIfImpededEF: the Annunciation transfers past Holy Week/the Easter octave to the Monday after Low Sunday; All Souls moves to Nov 3 when Nov 2 is a Sunday. - genlect harvests proper Masses for the new Ember/vigil days. Adds TestEFCoverage + TestOFMovableMemorials. Bumps to 0.39.0. Verified per-day: OF 0 real errors vs calapi (2026-2028, 100% correct-observed); EF 0 real errors vs missalemeum (2025-2027), residual = deep-tail vigil-occurrence and St Joseph Passiontide-transfer edges (~1-2 days/yr, documented).
* fix(of): recover season-commemorated saints + correct Irenaeus rankLukasz Kasprzak2026-07-281-5/+42
| | | | | | | | | | | | | | | | | | | Audit of the OF sanctoral vs calapi's full celebration list (2026-2028) found saints that calapi only ever shows as 'commemoration' in a privileged season (Lent, late Advent, the Christmas octave) were dropped by the generator, and 'first-year-wins' froze a stale rank. - gen-sanctoral.go: map calapi 'commemoration' -> optional memorial; keep the HIGHEST rank per saint across an expanded 2025-2029 reference span (so a saint is seen at its true rank on an ordinary weekday, e.g. Polycarp as a memorial when Feb 23 falls in Ordinary Time). - Recovers 15 saints: Polycarp, Perpetua & Felicity, John of God, Frances of Rome, Patrick, Cyril of Jerusalem, Turibius, Isidore, Vincent Ferrer, Peter Canisius, John of Kanty, Thomas Becket, Sylvester I, and others (197->212). - Corrects St Irenaeus (June 28) optional -> obligatory memorial (2022 change). Verified vs calapi per-day 2026-2028: remaining diffs are only the 3 skipped movable memorials and name-phrasing of temporal days.
* feat(ef): full 1962 universal sanctoral (16 -> 317 celebrations)Lukasz Kasprzak2026-07-282-2/+404
| | | | | | | | | | | | Generate internal/caldata/tridentine-calendar.ini from missalemeum's per-date proper API (Divinum Officium 1962 data) via scripts/gen-sanctoral-ef.go: each fixed date's observed saint with rank, colour, English name and proper Epistle/Gospel citations, plus sancti commemorations. Multiple reference years are tried per date so a saint occulted by a Sunday in one year is still captured observed in another; saints never observed are ranked commemoration. Feasts of the Lord are marked class=lord. Latin names hand-curated (missalemeum has none). Adds TestTridentineLoads, credits missalemeum in NOTICE, bumps to 0.38.0.
* fix(of): sanctoral generation — exclude temporal & movable, keep ↵Lukasz Kasprzak2026-07-281-5/+16
| | | | | | | | | | | | | | | | transferred-fixed - Skip ALL dates the temporal engine owns (Sundays + its solemnities/feasts), so Christ the King / Trinity no longer leak into the sanctoral as duplicate fixed-date entries; saints are captured on their weekday occurrences. - Explicit skip-list for genuinely movable memorials (Mary Mother of the Church, Immaculate Heart, BVM-on-Saturday) that a fixed-date calendar cannot represent -- a documented gap needing temporal-engine support. - 'first year wins' keeps a transferred-but-fixed feast (Annunciation Mar 25) on its proper date. Lord-feast class inference fixed earlier. Calendar rank vs calapi: 2026 1/86, 2027 2/86, 2028 2/92 mismatches (~98-99%); residual = the skipped movable memorials.
* chore(of): extend lectionary harvest to 2018-2025 (98.4% coverage)Lukasz Kasprzak2026-07-281-3/+3
| | | | | | | More true-ferial years per position; 359/365 days of 2026 now resolve. The 6 residual gaps are rare pre-Lent Ordinary-Time positions (weeks 3/4/9) that never occurred as a true ferial in the harvestable past (niedziela publishes no future years) -- an irreducible limit of date-based harvesting, documented.
* fix(of): correct memorial readings (ferial fallback) + Lord-feast classLukasz Kasprzak2026-07-282-10/+22
| | | | | | | | | | | | | | | | | | | Validation vs USCCB (Catholic Readings API) caught two bugs: - Obligatory memorials were keyed by their own slug, freezing ONE year's ferial (a memorial's fixed date lands in a different Ordinary-Time week each year), so most weekday memorials showed the wrong week's reading. Now memorials are skipped in the harvest and fall back to the day's ferial slug at runtime -> the correct week's reading. (Proper-reading memorials show the permitted ferial; their propers are a later refinement.) - gen-sanctoral classOf missed feasts of the Lord whose title says 'Holy Cross' (not 'of the Cross') etc., so the Exaltation of the Cross (and Transfiguration, Presentation) did not displace an Ordinary Sunday. Fixed the Lord/BVM class inference; the Cross now correctly displaces the Sunday and shows its proper. USCCB agreement on the sampled 2025 window rose to ~92%; residual diffs are proper-reading memorials (Guardian Angels/Sorrows gospels), citation-granularity (same reading, different verse range), and week numbering around feast-displaced Sundays -- documented as known refinements.
* feat(of): full sanctoral calendar + weekday/memorial readingsLukasz Kasprzak2026-07-282-29/+283
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Populate the OF General Roman Calendar sanctoral and extend the OF readings to weekdays and sanctoral days. Sanctoral calendar (roman-calendar.ini: 29 -> 202 celebrations): - scripts/gen-sanctoral.go generates it from calapi.inadiutorium.cz (authoritative General Roman Calendar) -- English name/rank/colour from general-en, Latin from general-la, Polish from niedziela DayInfo (obligatory celebrations). Fixed dates the temporal engine already computes are excluded. VALIDATED: my engine's observed rank agrees with calapi on all 86 obligatory sanctoral days of 2026 (0 mismatches). Precedence fix (precedence.go): an optional memorial no longer displaces the weekday as the DEFAULT observed celebration (it sorts below the ferial, shown as an option) -- matching the General Roman Calendar / calapi. Readings (of-lectionary.ini: 254 -> 1018 entries) via genlect-of.go: - Weekday (ferial) 2-year cycle I/II, keyed <ferial-slug>-<WeekdayCycle>, harvested from niedziela over 2020-2025 (multiple years per cycle; retry-on-failure so a glitch year never poisons a key; only TRUE ferials + optional-memorial days, where niedziela shows the ferial). - Obligatory memorials keyed by their OWN slug (niedziela shows their proper, e.g. Barnabas -> Acts 11, or the ferial for memorials without a proper) -- never miskeyed to a ferial position. - caldata.Readings: OF resolves slug+SundayCycle | slug+WeekdayCycle, else a memorial falls back to the day's ferial readings. - Source-glitch normalisation (1J->1 J, PnP->Pnp). books.ini: canonical names "Song of Solomon" and "The Acts" added as their own [en] forms (ToEnglishRef emits the canonical; it must resolve). Fixes ~32 cross-chapter Song-of-Songs/Acts citations. Coverage: 359/365 days of 2026 render (98.4%); 6 residual ferial positions never a true-ferial in 2020-2025. EF sanctoral and OF psalm renumbering still pending.
* feat(caldata): generate OF Sunday/solemnity lectionary from niedzielaLukasz Kasprzak2026-07-271-0/+162
| | | | | | | | | | | | | | | | | Task 2. scripts/genlect-of.go harvests niedziela over the PAST cycle-equivalent years 2023(A)/2024(B)/2025(C) -- niedziela only publishes ~6 weeks ahead, so the future 2025-2027 span was unfetchable; the 3-year cycle repeats, so a liturgical position's cycle-A readings are identical whether they fall in 2023 or 2026. Produces internal/caldata/of-lectionary.ini: 254 entries (A=86, B=87, C=81), keyed <slug>-<cycle>, citations English-canonical via ToEnglishRef. Also: books.ini [en] Matthew gains "Mat" (ToEnglishRef maps Polish Mt->Mat; now resolves and displays as Matt). of_test.go assertion relaxed to accept either spelling. KNOWN GAP: 8 cross-chapter range citations (e.g. Sirach 27:30-28:7, John 18:1-19:42) don't yet resolve -- the bible ref parser handles semicolon groups but not a dash-range spanning a chapter boundary. Fix next.
* build: corpus-validate script + add-corpus/check-corpora make targetsLukasz Kasprzak2026-07-271-0/+6
|
* feat(ef): complete offline EF readings — proper ferias, deuterocanon, ↵Lukasz Kasprzak2026-07-272-10/+110
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Latin fallback Finish the Extraordinary Form (1962) offline Mass readings so every day of the year resolves in both English and Polish. Temporal engine (temporal_ef.go): - Unique ferial slugs "ef-<season>-<week>-<weekday>" so penitential-season weekdays (Lent, Advent, Passiontide, Easter octave) can key their own proper Masses; green-season ferias have no entry and fall back to the preceding Sunday (the 1962 rule). - Special "ef-lent-after-ashes-<weekday>" case for the days between Ash Wednesday and Lent I, which have their own propers. Lectionary (tridentine-lectionary.ini, 55 -> 143 entries) via genlect.go: - Generator now fetches proper-season weekdays and stores a feria only when its Mass differs from the preceding Sunday's. - cleanCite() normalises two missalemeum data glitches: a stray comma between book and chapter ("4 Kings, 5:1" -> "4 Kings 5:1") and a period-as-separator ("John 20. 19-31" -> "John 20:19-31"), both guarded so legitimate multi-chapter citations are untouched. Citations & corpora: - books.ini: Douay abbreviations in the [en] dialect (Ex, Ezech, Jonas, and 3/4 Kings -> canonical 1/2 Kings), so the EF Lenten epistles resolve while the display keeps modern sigla. - drb.tsv: +205 rows backfilling the deuterocanonical Daniel 13-14 (Susanna, Bel) and Esther 11-16 (Greek additions) from get.bible douayrheims, which the base corpus omitted; scripts/gen-deutero.py documents the fetch. Rendering (liturgy.go): - readingLine() falls back to the Latin Vulgate (complete) when the vernacular corpus lacks a passage, with a clear note. Covers the Polish Wujek deuterocanon gap (no clean public-domain source exists) and any future vernacular gap; faithful to the EF as a Latin rite. Result: 730/730 days resolve for both EN (native) and PL (native + Latin fallback for 3 deuterocanon days). Version 0.32.0 -> 0.33.0.
* feat: EF temporal Sunday lectionary (offline Sunday Mass readings)Lukasz Kasprzak2026-07-271-0/+110
| | | | | | | | | Unique temporal-day slugs (season-sunday-week); scripts/genlect.go generates tridentine-lectionary.ini from missalemeum keyed by slug (55 entries: the Sundays of the whole EF year). caldata.TemporalReadings(form,slug) lookup; --liturgy resolves proper-of-feast else the temporal cycle and renders the text. bible.ParseRef strips abbreviation dots (1 Cor. -> 1 Cor). 52/53 Sundays fully resolve offline.
* test(calendar): EF oracle vs missalemeum 2025-2026 (0 season mismatches)Lukasz Kasprzak2026-07-271-0/+38
| | | | | | temporalEF validated against Divinum Officium data. Season-mapping handles the Whit octave (Paschaltide), the Christmas vigil (Advent), and the resumed Epiphany Sundays (within Time after Pentecost).
* test(calendar): regression oracle vs calapi 2020-2040 (0 season mismatches)Lukasz Kasprzak2026-07-271-0/+35
Caught + fixed an Advent-start bug: anchor on Christmas Eve, not Dec 25, so years where Dec 25 is a Sunday don't lose the first week of Advent. Rank-class diffs (324) are informational — the initial sanctoral dataset is partial.