<feed xmlns='http://www.w3.org/2005/Atom'>
<title>lectio.git/scripts, branch v0.46.1</title>
<subtitle>offline Catholic daily readings and liturgical calendar in Go, with CLI, TUI and web clients</subtitle>
<id>https://git.labunix.xyz/lectio.git/atom?h=v0.46.1</id>
<link rel='self' href='https://git.labunix.xyz/lectio.git/atom?h=v0.46.1'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/'/>
<updated>2026-08-12T13:27:00Z</updated>
<entry>
<title>gen-sanctoral-ef, caldata: fix round 3 -- guard coverage, rank pins, name test, Anastasia</title>
<updated>2026-08-12T13:27:00Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T13:27:00Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=c655c525ef07deafb113a291489b396c81d7ce4a'/>
<id>urn:sha1:c655c525ef07deafb113a291489b396c81d7ce4a</id>
<content type='text'>
Second external re-review returned Spec pass on round 2 (all ten prior
findings addressed, C1/C2 fixed at the root, mechanism claims held
under mutation) and found two blocking items in the I3 guard itself,
plus three recommended hardenings. All verified independently before
fixing.

BLOCKING 1: refYearExplainsAbsence's own doc comment said the id-rank
exclusion covers "6-31 December"; the code implemented `Day() &gt;= 26`.
A typo (26 -&gt; 6), but the wrong kind to have on the one comment this
whole review chain exists to keep honest -- fixed as part of the
rewrite below, not left as an isolated one-character correction.

BLOCKING 2: the guard covered only 6 of the 13 MM-DD values round 1's
own temporal_ef.go changes actually made refYearExplainsAbsence
couple to. Measured, not just reasoned about: running the function's
body against the branch-point engine versus HEAD over the six
reference years flips its verdict on 50 dates across 12-17 through
12-23 (round 1's late-Advent promotion, RG 91 entry 18, a 140-day
change) AND 12-26 through 12-31 (the Nativity-octave promotion, RG
67/68, already guarded). The late-Advent half was unguarded.

No live bug today -- confirmed by regenerating: the ini is
BYTE-IDENTICAL before and after this fix. The only sanctoral entry in
17-23 December is `thomas` (21 December), reached via harvestDate's
`obs` (observed-office) path, never refYearExplainsAbsence's
commemoration-id-trust path at all. Widened the guard to 17-31
December anyway, on the side of the known-safe default
(RankCommemoration) rather than wait for the first real entry in that
range to repeat I3's exact failure shape. The two sub-ranges are NOT
the same strength of evidence, and the doc comment says so explicitly
now: 26-31 December rests on a positive RG 68(d)/(e) citation naming
Thomas Becket and Silvester directly as bare "Commemoratio, no
class"; 17-23 December is a precautionary exclusion with no equivalent
citation -- RG 91 entry 18 ranks the FERIA, not any commemoration
that might coincide with it, and (per defect 2's own finding) a
genuine class-2 feast there would actually WIN against the ferias's
own privilege, so "the day is class-2" does not reliably explain a
class-2 saint's absence here the way it does for the Nativity octave's
two named cases. Recorded as a real asymmetry, not smoothed over.

RECOMMENDED 3: rank = commemoration is now pinned for all four entries
this task's two review rounds touched -- romanus, eusebius-confessor
(C2), thomas-becket, silvester (I3) -- not just their presence and
date. `git grep` before this commit returned zero hits for
thomas-becket or silvester rank anywhere in *_test.go; a future
regeneration could have rewritten either back to class-4, the exact
I3 failure mode, with the suite green throughout.

RECOMMENDED 4: TestTridentineNamesPreservedAcrossRegeneration
rewritten on both axes the review found loose. It counted name.pl
only -- a hardcoded single language, the identical "whitelist of two"
mistake C1's own generator fix exists to stop repeating, just moved
into the test instead. Now DISCOVERS every name.&lt;lang&gt; key actually
present in the data (so a regeneration dropping name.la, or losing a
future name.de, is caught the same way as name.pl) and asserts the
discovered language set is exactly {en, pl} -- not just each count.
The &gt;= 315 floor had 7 entries of slack (the review proved 6 missing
Polish names still passed it); tightened to the exact, independently
verified true count (322), zero slack.

RECOMMENDED 5: the 25 December Anastasia gap and its cause are now
recorded, not just her absence. Two compounding, independently
checked causes: fetchOnce reads only `data[0]`, missalemeum's FIRST
of Christmas Day's three Masses, so a commemoration attached to a
second or third Mass (the calendarium's own "In secunda Missa:
Commemoratio S. Anastasiae Mart.") is structurally invisible to this
harvester for ANY date, not just this one -- but ALSO, checked live,
missalemeum's own "commemorations" list is empty on all three of the
date's records, not just the first, so a fetchOnce rewritten to merge
all of a date's Masses would still not recover her. Documented at
both fetchOnce's own definition and the PRIMARY-SOURCE NOTE, since
the two facts belong to different questions (a real code limitation
worth fixing someday vs. an upstream data gap this generator cannot
close at all).

Witnesses: TestTridentineCommemorationRanksPinned (new),
TestTridentineRomanusAndEusebiusPresent (extended with rank),
TestTridentineNamesPreservedAcrossRegeneration (rewritten). No pre-fix
failure to report for the two new/tightened tests specifically --
these are regression GUARDS against a bug class already fixed in
round 2, not fixes for a currently-red state; their value is in what
they would now catch, not what they catch today. internal/caldata/
tridentine-calendar.ini is NOT regenerated by this commit -- confirmed
byte-identical before and after re-running the generator, exactly as
"no live bug today" predicts.
</content>
</entry>
<entry>
<title>gen-sanctoral-ef, caldata: fix round 2 -- review found five real problems</title>
<updated>2026-08-12T12:50:34Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T12:50:34Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=e122fa4368e156b9c90c128a8daad9545b22ed11'/>
<id>urn:sha1:e122fa4368e156b9c90c128a8daad9545b22ed11</id>
<content type='text'>
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 -&gt; 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] -&gt; 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.&lt;lang&gt;" 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" -&gt; "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 &gt;= 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.
</content>
</entry>
<entry>
<title>gen-sanctoral-ef: keep the Purification tagged lord -- the brief was wrong here</title>
<updated>2026-08-12T11:53:22Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T11:53:22Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=50e39702bad835962b6e9c04253572e5b48a951f'/>
<id>urn:sha1:50e39702bad835962b6e9c04253572e5b48a951f</id>
<content type='text'>
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 -&gt; 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.
</content>
</entry>
<entry>
<title>scripts/gen-sanctoral-ef: fix rank inference, dedup, and class tagging (defect 7)</title>
<updated>2026-08-12T11:29:41Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T11:29:41Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=4a1db8df19425736fc82907c967586a3053a7ad1'/>
<id>urn:sha1:4a1db8df19425736fc82907c967586a3053a7ad1</id>
<content type='text'>
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 -&gt; 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.
</content>
</entry>
<entry>
<title>test(ef): strengthen the oracle test to assert rank and colour</title>
<updated>2026-08-12T11:03:57Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T11:03:57Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=ca3403893bfbebb325fe3ab6a98081640574bf2a'/>
<id>urn:sha1:ca3403893bfbebb325fe3ab6a98081640574bf2a</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>sources: store the snapshot as one compressed archive</title>
<updated>2026-07-30T06:56:34Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-07-30T06:56:34Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=d7da4b09f7775276231d0241cfe2700d247728ee'/>
<id>urn:sha1:d7da4b09f7775276231d0241cfe2700d247728ee</id>
<content type='text'>
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 -&gt; refresh -&gt; pack cycle. Distribution-ready: one archive, not a
file tree.
</content>
</entry>
<entry>
<title>sources: archive upstream generator data for offline reproducibility</title>
<updated>2026-07-30T06:23:03Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-07-30T06:23:03Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=ff1071eedad3c6891192babe74ed65f5d4f60f29'/>
<id>urn:sha1:ff1071eedad3c6891192babe74ed65f5d4f60f29</id>
<content type='text'>
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).
</content>
</entry>
<entry>
<title>build: add ci + release targets, a pre-push hook, and a CHANGELOG</title>
<updated>2026-07-29T19:46:26Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-07-29T19:46:26Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=ddaf8f63bc0e77930da4762fc31afa1c9d439b05'/>
<id>urn:sha1:ddaf8f63bc0e77930da4762fc31afa1c9d439b05</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>bible(grb): restrict to the Vulgate canon; restore Hosea and Zechariah</title>
<updated>2026-07-29T09:13:03Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-07-29T09:13:03Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=ab9a02b34d88e6c85e4d494077bb474dc19adbce'/>
<id>urn:sha1:ab9a02b34d88e6c85e4d494077bb474dc19adbce</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>bible(wuj): rebuild from source, repair to exact Vulgate parity</title>
<updated>2026-07-29T09:12:24Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-07-29T09:12:24Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=390535ff00d61df63a15de6fba23761a2c57cead'/>
<id>urn:sha1:390535ff00d61df63a15de6fba23761a2c57cead</id>
<content type='text'>
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 &lt;p&gt;, 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.
</content>
</entry>
</feed>
