aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* temporal_ef: Sunday ranks, Rose Sundays, Holy Thursday whiteLukasz Kasprzak2026-08-123-18/+200
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* test(ef): strengthen the oracle test to assert rank and colourLukasz Kasprzak2026-08-123-2821/+5231
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* cmd(lectio-ef-dump): new EF calendar dumper for colitur's differential oracleLukasz Kasprzak2026-08-121-0/+106
| | | | | | | | | | | | | | | | | | | | Prints one line per day, sorted ascending, for a civil-year range: date, weekday, season, week, observed slug, rank, colour, then zero or more +slug tokens for the day's other (losing) candidates. The line format matches colitur's `colitur day` output field-for-field so the two streams diff directly; every column carries lectio's own vocabulary (season names, slugs, rank/colour spellings) with no translation toward colitur's — that mapping belongs to the differential comparator, not this dumper. Uses calendar.Compute with Selection{Form: "old"} over the embedded tridentine layer (caldata.Tridentine()), mirroring the setup already used by oracle_ef_test.go. Lives in cmd/ because internal/calendar cannot be imported outside this module; colitur's build is untouched. Note for the comparator: lectio has no RG 111 commemoration-admission logic, so the trailing +slug tokens are the day's losing candidates, not admitted commemorations.
* calendar/readings: report Sunday as its own display rank, not solemnityLukasz Kasprzak2026-08-057-6/+103
| | | | | | | | | | | | | | | | The Ordinary Form's Table of Liturgical Days treats Sunday as its own category, never a solemnity. temporal.go's sundayDay() already marks these days distinctly from named solemnities (Class stays unset, unlike solemn()'s ClassLord); internal/readings/offline.go now reads that existing signal to relabel the REPORTED rank to "sunday" for ordinary and privileged-season Sundays alike, leaving Celebration.Rank, ofRankOrder and all precedence untouched. Named solemnities landing on a Sunday (Easter, Pentecost, ...) and feasts of the Lord (Holy Family) keep reporting their own rank. The 1962 form is untouched. Added calendar.RankSunday (display-only, deliberately excluded from ofRankOrder), the i18n Sunday/niedziela words, and a test pinning the exact dates from the original bug report.
* mobile: fix the documented gomobile bind command so it reproduces the ↵Lukasz Kasprzak2026-08-051-1/+8
| | | | | | | | | | | | shipped .aar The comment omitted -javapkg, the second -target ABI and -androidapi. A clean clone following it got a .aar whose classes sit under go.mobile.gojni instead of xyz.labunix.dlectio.engine.mobile, carries only arm64-v8a, and declares no minSdkVersion match to the app's 26 -- the app cannot import the result. Verified by rebuilding the binding with the corrected command and confirming the artifact's ABIs, package and minSdkVersion against the shipped one.
* readings: PartIDs(new) stops declaring aklamacja, which the engine never emitsLukasz Kasprzak2026-08-053-13/+52
| | | | | | | | | | | | | | | | | | | | | caldata.go:42 parses only first/psalm/second/gospel out of the lectionary data, so no OF reading ever carries Part == "acclamation". PartIDs("new") listed aklamacja anyway, so the app built a checkbox from it that filters an ID that never appears -- a dead control, same defect class the app previously shipped for the whole 1962 form. ofPartOrder stays the full five-ID set: it also drives render.LocalizeHeading's label matching, where a scraped heading can still read "Aklamacja" even though this engine's own readings never produce that section. PartIDs now draws from a new, narrower ofEmittedPartOrder instead. Rewrote TestPartLabelsMatchesWhatTheEngineEmits's OF half: it compared PartLabels("of") to a hand-copied duplicate of ofPartOrder, asserting a declaration against itself, which cannot fail for this class of bug. It now sweeps Days over calendar year 2026 and asserts PartLabels("of") matches the observed part-ID set exactly (0.5s). Confirmed red against the pre-fix code, green after.
* mobile: strengthen the PartLabels fallback test and count boundsLukasz Kasprzak2026-08-042-4/+20
|
* i18n: pin the Polish part labels; cross-reference the part-ID ordersLukasz Kasprzak2026-08-043-0/+48
|
* mobile: add PartLabels so the app stops hardcoding part IDsLukasz Kasprzak2026-08-032-0/+76
|
* mobile: add Days, a text-free week projection for the calendarLukasz Kasprzak2026-08-032-0/+155
| | | | | A week through Days benchmarks at ~9-10ms/op, versus ~23ms for seven full days through Day.
* mobile: return the day's rank and its localised labelLukasz Kasprzak2026-08-032-9/+56
|
* readings: 1962 part labels become i18n data; export PartIDsLukasz Kasprzak2026-08-035-13/+90
|
* i18n: correct rank doc comments and pin the Polish wordingLukasz Kasprzak2026-08-034-6/+36
|
* liturgy: carry the observed rank on DayInfoLukasz Kasprzak2026-08-033-0/+36
|
* i18n: drop the colour word table, it has no consumerLukasz Kasprzak2026-08-035-36/+1
|
* i18n: rank and colour vocabulary tablesLukasz Kasprzak2026-08-035-2/+124
|
* baseline: track the gomobile engine facade as-isLukasz Kasprzak2026-08-034-4/+154
| | | | | | | | mobile/mobile.go and its go.mod/go.sum additions were sitting untracked in the working tree. Recorded here unchanged so the work that follows has a diff baseline; no lines of it are modified by this commit. Also ignore the cmd/dlectio-gen build product and the SDD scratch dir.
* sources: store the snapshot as one compressed archiveLukasz Kasprzak2026-07-304-4/+45
| | | | | | | | | 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.
* caldata(ef): trim redundant trailing ', Apostołów' from Peter&Paul basilicasLukasz Kasprzak2026-07-301-1/+1
|
* caldata(ef): fix Apostołow -> Apostołów typo (Simon & Jude)Lukasz Kasprzak2026-07-301-1/+1
|
* caldata(of): capitalise the Apostoł/Ewangelist title in Polish namesLukasz Kasprzak2026-07-301-6/+6
| | | | | | | | | | Per the review, the apostle title was split ("św. X, apostoła" vs ", Apostoła") and the evangelist title too (Mark/Matthew already "Ewangelisty", John/Luke lowercase). Standardise both high author-ranks to uppercase -- all apostles "Apostoła"/"Apostołów", all four evangelists "Ewangelisty" -- matching the entries that were already capitalised. The functional titles (biskupa, prezbitera, męczennika, doktora, dziewicy...) stay lowercase, as they consistently were.
* sources: archive upstream generator data for offline reproducibilityLukasz Kasprzak2026-07-304-0/+1774
| | | | | | | | | | | | | | | | | 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-293-1/+38
| | | | | | | | | | | | | | | 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.
* caldata(of): fix Philip & James Polish name (was Queen of Poland)Lukasz Kasprzak2026-07-291-1/+1
| | | | | | | | | | | The universal [saints-philip-and-james-apostles] entry carried name.pl = "Najświętszej Maryi Panny, Królowej Polski" -- Our Lady Queen of Poland, the Polish NATIONAL feast that supersedes Philip & James on that date, pasted in during the pl backfill instead of translating the universal celebration. name.la and the readings correctly say Philip & James. Set name.pl = "świętych Apostołów Filipa i Jakuba" (matching the "świętych Apostołów Piotra i Pawła" style). Scanned the rest of both universal calendars for other national-feast contamination -- none.
* calendar: name the liturgical form fully in the printed title; v0.45.1v0.45.1Lukasz Kasprzak2026-07-296-3/+16
| | | | | | | | | | | The exported month calendar's title showed the terse dropdown word ("August 2026 — traditional"). Spell the form out: "traditional roman calendar" / "novus ordo calendar". Add dedicated i18n labels CalModern/CalTraditional (translatable like the rest of the chrome; pl = "kalendarz Novus Ordo" / "tradycyjny kalendarz rzymski") rather than overloading the short OptModern/OptTraditional the settings dropdown uses. Bump version to 0.45.1.
* release: bump version to 0.45.0v0.45.0Lukasz Kasprzak2026-07-291-1/+1
| | | | | | | | The version constant sat at 0.44.0 while 44 commits landed on top of it: the engine-flawlessness pass, the localization + bible-corpora-split epic (any-language day/saint names, embedded-chrome-as-data, EF Polish sanctoral 322/322), the web UI rework, the EF Christmas-ferial name fix, and the cli test-config isolation. Mark the release as 0.45.0.
* test(cli): isolate the suite from the real ~/.config/lectioLukasz Kasprzak2026-07-291-0/+26
| | | | | | | | | | | | | | The ref/rand cli tests assume English book abbreviations ("Jn 3:16"), but with no LECTIO_CONFIG set they read the developer's real user config. On a machine whose config has ui_language = pl (sigla_style = auto -> Polish dialect), "Jn" is not a valid abbreviation and TestRefSingle, TestRefEnglishDialect, TestRefRejectsPolishAbbrevInEnglish, TestRefCompare and TestRandVerse fail -- while passing on a clean CI. Add a package TestMain that points LECTIO_CONFIG at a throwaway temp path, forcing built-in defaults for every cli test and guaranteeing the suite never reads or writes the real user config. Suite now green on a Polish config too.
* caldata(ef): finalise the 4 review-flagged Polish namesLukasz Kasprzak2026-07-291-5/+1
| | | | | | | | | | Reviewed the four EF names missalemeum never returns. George (Martyr), Stanislaus (Bishop and Martyr), and Zephyrinus (Pope and Martyr) were correct as best-guessed -- flags cleared as-is. Alexander & Companions had enumerated only three of the martyrs (omitting Juvenal); since the English name is the shortened "& Companions", match it with the faithful Polish "śś. Aleksandra i Towarzyszy, Męczenników". No more # review-pl flags; all 322 EF names stand.
* naming(pl): localise the Christmas-season name in composed ferial namesLukasz Kasprzak2026-07-291-0/+2
| | | | | | | | | | | | EF ferials in the Christmas season rendered with an English season word in a Polish UI -- "Poniedziałek Christmas" (2026-01-05) -- because the Polish [seasons] table had no "christmas" key, so season() fell back to the English title-cased slug. Add christmas = "Narodzenia Pańskiego" (and the empty season_prep entry), matching the existing genitive style (Adwentu, Wielkiego Postu). Now "Poniedziałek Narodzenia Pańskiego". Verified across the whole Christmas-Epiphany window for both forms; English output unchanged.
* web(settings): drop the terminal "versions" field; it is CLI-onlyLukasz Kasprzak2026-07-292-8/+7
| | | | | | | | | | | | | The settings page showed two near-identical version checkbox rows -- "versions" (the terminal compare set) and "web_versions" (the web page's own default ticks). The first only affects the CLI, so editing it from the web is pointless and was actively confusing (both rows list the same wuj/vul/grb/drb boxes). Remove the "versions" row from the form and stop reading it in settingsPost, so cfg.Versions is preserved from the live config (same pattern as offline/width/pager). The web settings now expose only what the web uses: default_version and web_versions.
* web: pin settings/download to the top-right corner, never wrap it downLukasz Kasprzak2026-07-291-2/+15
| | | | | | | | | | | | | | Per the target layout: the utility cluster (settings + the pobierz disclosure) must hold the top-right corner on the same row as the reader link, not drop onto a second full-width line below the controls. Make #controls itself non-wrapping so the side cluster keeps the corner; the left controls (.controls-main) absorb any tightness by wrapping their OWN items onto a further line (min-width:0 lets them shrink). At the 64rem page width the whole bar cannot fit every left control AND the corner cluster on one line, so a trailing control (layout) wraps beneath -- the intended block-on-the-left, cluster-in-the-corner shape. A tighter column gap buys a little room; on phones (<=40rem) the bar wraps normally.
* web: reader link to the left, downloads into a "pobierz" disclosureLukasz Kasprzak2026-07-293-13/+88
| | | | | | | | | | | | | | | | | Finish the top-bar rework. The reader entry point now leads the controls bar on the far left (it was in the right-hand cluster); settings stays in the top-right cluster. The flat "download: txt md pdf · kalendarz" strip becomes a single "pobierz" summary that opens a small floating panel with two rows: - a month calendar with its own month + year <select> pickers (localised month names, both pre-selected from the shown date) and a PDF button that hits /calendar?month=YYYY-MM; - the shown day's readings as txt/md/pdf, labelled with the date. The panel is absolutely positioned under the link so opening it doesn't reflow the bar. indexData gains MonthOpts/YearOpts (a calOpt list); the year picker is a small window around the shown year.
* caldata(ef): add Polish names to the traditional sanctoralLukasz Kasprzak2026-07-291-0/+326
| | | | | | | | | | | | | | | | | | The EF (traditional) calendar showed English memorial names even with the UI switched to Polish, because tridentine-calendar.ini carried only name.en. Add name.pl to all 322 sanctoral entries. Source: missalemeum.com's Polish propers (/pl/api/v5/proper/DATE), the authoritative Polish traditional-calendar naming -- paired to each entry by the celebration id shared across its /en/ and /pl/ responses, fetched across two years so saints superseded by a Sunday in one year are still resolved. 318 entries matched that way; a missalemeum source typo ("Dokotra" -> "Doktora") is corrected in the two it affected. The remaining 4 -- George, Alexander & Companions, Stanislaus, Zephyrinus -- are never returned by missalemeum in the fetched years (superseded every time); they carry a best-guess Polish name tagged with a "# review-pl" comment for a human to verify.
* web: move lectionary + mono to settings only; make reader link prominentLukasz Kasprzak2026-07-292-12/+11
| | | | Lectionary becomes settings-only (kept as a hidden field so readings/export/calendar still use the configured value); mono toggle removed (settings has it). The reader link is uppercased + bold as the primary action.
* web: pin the utility cluster to the top-right of the controls barLukasz Kasprzak2026-07-292-3/+17
| | | | | | | Wrap the essential controls in a single .controls-main block that wraps internally, so reader/settings + downloads stay at the TOP-right instead of dropping to a second row. #controls (index only) becomes top-aligned; the reader's .controls form is untouched.
* web: declutter the UI, fix date arrows, red * for bookmarked verses in readerLukasz Kasprzak2026-07-2910-82/+93
| | | | | | | | | | | | | | | | | | | | | | | | | Daily page (index.html): - Date arrows now step the date input client-side and dispatch its change, so they move relative to the CURRENT day and update the shown date (they were stuck one step from the initial date, computed server-side). - Theme picker removed (theme lives only in /settings now); bookmarks link removed (bookmarks belong to the reader, not the daily readings). - reader/settings nav + the download links move to a right-aligned .controls-side cluster; essential controls (date, lectionary, versions, parts, layout, mono) stay on the left. mono stops its change bubbling so it no longer refetches. Reader (reader.html + RenderPassage): a bookmarked verse now shows a red "*" (end of line in columns, after the verse number interlinear). Threaded a marked set (keyed "chap:verse") through the reader render path only; the daily RenderReadings path is untouched. Settings (settings.html): Save button moved to the top; removed offline (dead), width and pager (CLI/TUI-only, no web effect) -- their config values are preserved since the handler no longer reads/zeroes them. Footer: source/licence centred. Web tests + full suite green both build modes.
* tui(reader): flag bookmarked verses with a red * in the reading paneLukasz Kasprzak2026-07-292-7/+71
| | | | | | | A verse that has a bookmark now shows a red "*" at the end of its last line, so saved verses are visible while reading. The set of bookmarked verses for the current book+chapter is refreshed on chapter load, on save, and on returning from the bookmarks list (one store read each). Test covers the flag + render.
* tui(reader): filter bookmarks by tag, and show tags in the listLukasz Kasprzak2026-07-295-45/+157
| | | | | | | | | Bookmarks stored tags but the list neither showed them nor let you use them. Now each row shows "Ref — note #tag1 #tag2", and `/` opens a live tag filter (case-insensitive substring over a bookmark's tags): type to narrow, Enter keeps the filter while j/k navigate the filtered list, Esc clears it. Navigation, open and delete all act on the visible (filtered) list. Reuses the store's existing tag data; help keybar (en/pl) updated; test covers filter/keys.
* make: add install-full (installs all three with wuj/drb/grb embedded)Lukasz Kasprzak2026-07-291-1/+9
| | | | | | | `install` builds the vul-only default; `install-full` mirrors it but with -tags fullbible, so lectio, lectio-ui and lectio-web all ship the optional corpora. Prints a reminder that those are third-party scripture and such a build is not freely redistributable (see NOTICE).
* bible(wuj): strip leaked verse numbers from the deuterocanon textLukasz Kasprzak2026-07-291-3797/+3797
| | | | | | | | | | | | | | ~3,800 verses carried their own verse number at the start of the text (a biblia.info.pl harvest artifact concentrated in the deuterocanonical books -- Sirach, 1-2 Maccabees, Wisdom, Judith, Baruch, at 90-98% of each, plus 3 strays in Ezra/Proverbs). E.g. Sirach 4:4 read "4 Nie odrzucaj..." instead of "Nie odrzucaj...". It passed every check: verse counts are unaffected, and the verses aren't short. Strip a leading token only where it exactly equals the verse number (so a genuine "40 dni" in a non-matching verse is untouched), and only when text follows. 3797 verses fixed; 35,810-row parity preserved, no verse emptied, no new warnings; --corpus-check wuj still clean; make test green.
* bible(corpus-check): warn on a verse present but stubbed vs the VulgateLukasz Kasprzak2026-07-294-4/+59
| | | | | | | | | | | | | | | | | | The count-based checks (gap, duplicate, missing chapter) only see a verse's presence, so a verse that IS there but truncated -- a lost opening line, a merge artifact, the exact class that lost 44 psalm openings and Acts 6:5 from the old Wujek scrape -- passes silently. Add a warning for it, compared CROSS-CORPUS to the aligned Vulgate verse (never within the chapter). That is deliberate: a genuinely terse verse ("Non occides", "Jesus wept") is short in the Vulgate too, so it is not flagged; only a corpus stub against a substantial Vulgate verse fires (corpus <12 chars, <1/4 of a Vulgate verse >=40). Rune-counted (script-agnostic) and skipped on the Psalms when the corpus renumbers them (psalm_system != vulgate), where per-verse alignment with the Vulgate would not hold. Warning only, never an error. Zero false positives across vul/wuj/drb/grb; a `stub` testdata fixture (Genesis 1:1 = "x") exercises a real hit.
* test(bible): skip corpus-dependent tests when optional corpora aren't embeddedLukasz Kasprzak2026-07-2910-101/+287
| | | | | | | | | | | | | | | | | | | | | | | | Since 51c0f4e split the corpora (only vul embedded by default; wuj/drb/grb behind -tags fullbible), `go test ./...` on the default build was red across six packages -- every failure was a test assuming an optional corpus is present. Guard those assertions with a skip keyed on bible.Meta(code), so they run under -tags fullbible and skip -- not fail -- on the default vul-only build. Mixed tests are split into subtests so the always-embedded vul assertions and pure logic (pl->vul fallback, explicit passthrough, bt-rejection, i18n labels) keep running on both builds. Test-only change; no product code or corpora touched. - internal/bible: TestVerses, TestLookup, TestCorpusBooks, TestChapters, TestGrbNoApparatusMarkers, TestCrossChapterRange* (requireCorpus helper). - internal/cli, config, render, tui, web: the same pattern for their corpus-dependent tests. - Fixes an index-out-of-range panic in internal/tui's reader tests that was aborting the package binary and masking 3 further corpus-absence failures (TestReaderBookmarkFlow, TestReaderChapterJump, TestReaderRemembersPlace). Verified: `go test ./...` and `go test -tags fullbible ./...` both green (0 FAIL); guards active only on the default build (29 skips vs 1 unrelated pre-existing); gofmt and go vet clean.
* NOTICE: record corpus provenance and licences accuratelyLukasz Kasprzak2026-07-291-8/+84
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The file asserted that no copyrighted translation is redistributed and listed all four corpora as public domain. Neither is true any longer, and one of them never was. * wuj is public domain EXCEPT roughly 300 verses -- the deuterocanonical passages biblia.info.pl does not carry at all -- which come from the 1962 Styś revision. Wujek's translation is public domain; ks. Styś died in 1959, so his revision is in copyright in Poland until the end of 2029. It is used by permission granted for this project, which is NOT a general licence: anyone redistributing the corpus, or shipping a fullbible binary, needs their own. The passages are listed. * grb's New Testament is the SBLGNT under CC BY 4.0, which REQUIRES attribution -- previously absent entirely. grb's own README still calls it "non-commercial", which is out of date; sblgnt.com now serves CC BY 4.0. The required notice is given, including that the text is modified here (apparatus sigla stripped, non-canonical books omitted). * grb's Old Testament is Rahlfs' Septuagint and its status is genuinely unsettled -- Rahlfs died in 1935, but the Deutsche Bibelgesellschaft asserts rights, the 2006 editio altera is a separate work, and get.bible labels the same text "Copyrighted; Free non-commercial distribution". Recorded as unsettled rather than claimed either way. Also notes the eight Douay-Rheims verses filled from get.bible's public-domain douayrheims, and that Baruch 6:37 is a versification difference rather than a gap.
* bible(grb): restrict to the Vulgate canon; restore Hosea and ZechariahLukasz Kasprzak2026-07-292-26696/+23126
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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(drb): renumber three chapters, fill eight absent versesLukasz Kasprzak2026-07-291-4618/+4626
| | | | | | | | | | | | | | | | | | | | | | Eight corpus-check warnings, of two kinds. Three chapters (Exodus 40, Genesis 49, Song of Solomon 1) held exactly as many verses as the Vulgate but numbered with a gap. The count pins the mapping, so renumbering 1..N is safe and changes no text. Five chapters were genuinely short. The missing verses came from get.bible's douayrheims -- the same public-domain source and API scripts/gen-deutero.py already uses for this corpus, so no new provenance is introduced. Scope was deliberately narrowed to the flagged chapters. A first pass over every chapter shorter than the Vulgate pulled in 938 verses, 820 of them in Psalms -- but drb.ini declares psalm_system = drb, a different numbering, so matching by Vulgate verse number there would have inserted the wrong text under the wrong numbers across the Psalter. One warning remains and is not a defect: Baruch 6 runs 1-72 with verse 37 absent in drb and in get.bible alike, a genuine Douay/Vulgate versification difference faithfully represented.
* bible(wuj): rebuild from source, repair to exact Vulgate parityLukasz Kasprzak2026-07-293-362/+492
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* feat(clectio-gen): apply a calendar layer/sanctorale when generatingLukasz Kasprzak2026-07-292-12/+54
| | | | | | | | | | | | | | | | | | | Add opt-in flags so clectio's tables can be regenerated from a customized calendar instead of only the embedded universal one: clectio-gen -caldir DIR -use a,b [-sanctorale DIR] <new|old> y0 y1 out/ - -caldir + -use stack calendar layers (rank, colour, name, reading.*) over the calendar; -sanctorale DIR applies a full of.ini/ef.ini replacement. - The BOOK table stays hermetic (LECTIO_CONFIG still points nowhere), so the generator can't read a stale user books.ini -- calendar customization and the book aliases are kept separate concerns. - config.SetCalendarsDir lets CalendarsDir be pointed at an explicit layer dir without a full config tree (and without an unrelated books.ini there). Verified end to end: a layer adding a feast with a proper gospel regenerates into liturgy_of.h + verses_of.keys, and the rebuilt clectio shows it. Default (no flags) output is byte-identical to before. make test green.
* feat(cli): --cal-dump NAME to generate a commented calendar templateLukasz Kasprzak2026-07-295-1945/+130
| | | | | | | | | | | | | | | Replace the static examples/sanctorale-of.ini with a `--cal-dump NAME` flag that writes the running universal calendar to ~/.config/lectio/calendars/NAME.ini as a fully-commented layer template (every celebration present, all commented). Uncomment one day and edit a field to override it; enable with `use = NAME`. - Form-aware: respects -l/--lectionary, so `-l trad --cal-dump x` dumps the 1962 calendar (322 entries) and the default dumps the OF calendar (212 entries). - Entries sorted by date then slug; refuses to overwrite an existing file. - Always in sync with the embedded (or sanctorale-overridden) calendar, so no stale checked-in template to maintain. - Update help text, man page (OPTIONS/FILES/EXAMPLES) and README to match; drop examples/sanctorale-of.ini.
* examples: ship a commented sanctorale calendar-layer templateLukasz Kasprzak2026-07-293-7/+1943
| | | | | | | | | | | Add examples/sanctorale-of.ini: every universal Ordinary Form sanctorale entry as a COMMENTED calendar-layer block (date, rank, class, colour, name.<lang>). As shipped it changes nothing; to alter a day you copy it into ~/.config/lectio/calendars/, uncomment its block, edit a field, and enable the layer with `use =`. The layer overlays the embedded calendar field by field, so untouched days keep their defaults (verified: uncommenting St Sebastian with rank=feast makes it displace the ferial). Header documents suppress/add/readings. Man page and README point at it.
* man: add lectio(1), lectio-ui(1), lectio-web(1); install via MakefileLukasz Kasprzak2026-07-294-4/+296
| | | | | | | | Add man pages for all three binaries under man/, and install them with `make install` to $(MANDIR) (= $(PREFIX)/share/man/man1); `make uninstall` removes them. lectio(1) documents every flag, the config files (corpora, names, ui, calendars, sanctorale) and examples; the ui/web pages are short. They render clean under man/mandoc.