<feed xmlns='http://www.w3.org/2005/Atom'>
<title>lectio.git/internal/caldata/tridentine-calendar.ini, branch perf/hoist-date-independent-work</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=perf%2Fhoist-date-independent-work</id>
<link rel='self' href='https://git.labunix.xyz/lectio.git/atom?h=perf%2Fhoist-date-independent-work'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/'/>
<updated>2026-08-18T15:11:04Z</updated>
<entry>
<title>fix(ef): three saints with proper Masses had no readings at all</title>
<updated>2026-08-18T15:11:04Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-18T15:11:04Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=c4bface0c72283cef55f548feb2a4a3d9eff1e6c'/>
<id>urn:sha1:c4bface0c72283cef55f548feb2a4a3d9eff1e6c</id>
<content type='text'>
Francis of Paola (2 April), Thomas Aquinas (7 March) and John of God (8
March). Unlike the nine added in the previous commit these are NOT Common-
routed -- the Missal prints a proper Mass for each -- they simply had no
reading in this repository and resolved nothing.

John of God is the one worth a note: his Epistle, Ecclus 31:8-11, is also the
Common of a Confessor not a Bishop I, which makes him look Common-routed. He
is not; the Gospel differs and the Missal prints the Mass at his own date.

With these, every day of 2005-2050 resolves a reading. Nothing in that range
comes back empty any more, where before there were four such days.
</content>
</entry>
<entry>
<title>feat(ef): the Masses of saints that come from a Common</title>
<updated>2026-08-18T15:06:28Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-18T15:06:28Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=09f30b32bd5ad0895b23a13b63c1eb5a19184b1c'/>
<id>urn:sha1:09f30b32bd5ad0895b23a13b63c1eb5a19184b1c</id>
<content type='text'>
Nine saints have no proper Mass in the Missal. Its rubric at each one's own
date names a Common -- "Missa Statuit, de Communi Confessoris Pontificis I
loco", "Missa Iustus ... II loco", and so on -- and only the Oratio is proper.
lectio has no Commons concept, so those days resolved no reading at all.

Benedict, Frances of Rome, Gregory the Great, Isidore of Seville, Patrick,
Perpetua &amp; Felicity, Vincent Ferrer, Ubaldus and Didacus.

The readings are written per saint rather than by adding a Commons abstraction.
Each Common resolves to exactly one first/gospel pair for the saint that names
it, so a table plus an assignment layer would buy indirection and nothing else
here; if a future saint needs a Common's OTHER formulary, that is when the
abstraction earns its place.

They go into the generator's missalOverrides table like the rank and colour
corrections, because missalemeum supplies these days with whatever it supplies
them and a regeneration would otherwise leave nine saints mute. The table
learned two new field names to carry them. Four are pinned in
TestTridentineMissalOverrides.

Citations are colitur's, verified there against both photographic scans and
the rendered page images; its data/ef/commons.sexp header records the
three-source method and the per-value evidence.
</content>
</entry>
<entry>
<title>fix(ef): twelve RG 124 colours -- missalemeum has the rule inverted</title>
<updated>2026-08-18T13:39:48Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-18T13:39:48Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=967e911728eefb85dc53065c73c5c5d4cdce6a59'/>
<id>urn:sha1:967e911728eefb85dc53065c73c5c5d4cdce6a59</id>
<content type='text'>
RG 124, "De coloribus paramentorum", section C, both photographic scans word
for word: 124(b) gives red to "Sanctorum Apostolorum et Evangelistarum, in
eorum die natalicio, excepto festo S. Ioannis (27 decembris)", and 124(e) to
"Sanctorum Martyrum, quorum colitur aut martyrium". Red is for Apostles,
Evangelists and Martyrs. A Confessor, Doctor or non-martyr Virgin is white.

missalemeum has twelve of these INVERTED IN BOTH DIRECTIONS: ten confessors,
doctors and virgins in red (Ephrem, Julia of Falconieri, John Gualbert,
Camillus de Lellis, Jerome Emiliani, Martha, Alphonsus Liguori, Augustine,
Rose of Lima, John of San Fecundo), and two actual martyrs in white
(Apollinaris and Josaphat, both Bishop and Martyr). Both directions failing is
why this reads as one rule applied backwards rather than twelve separate
slips, and why they are corrected as a block.

Adjudicated by colitur against the scans -- its C18 carries the per-slug
reasoning and its M21 the oracle side, verdict colitur. Two of its fourteen,
conversion-of-st-paul and chair-of-st-peter, already agreed here.

Found by a full-year sweep comparing clectio's output against colitur day by
day, not by the patch tool that produced the earlier corrections. That tool
reads colitur's sanctoral.sexp directly and so misses everything in
adjustments.sexp, the overlay where all twenty of colitur's colour edits
actually live -- its SECOND structural blind spot, after temporal days.

All twelve go into the generator's missalOverrides table, so regeneration
preserves them, and into TestTridentineMissalOverrides, now sixteen pinned
fields. Mutation-tested in BOTH directions: reverting a martyr to white and a
confessor to red each redden the test with the rubric named. Pinning only one
direction would have let a careless "make them all white" fix pass ten of
twelve.

The oracle allow-list gains one entry covering the twelve dates in both years.
</content>
</entry>
<entry>
<title>feat(gen): teach the EF generator to preserve the Missal corrections</title>
<updated>2026-08-18T12:58:20Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-18T12:58:20Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=46653b24b0afff60be35c3e876830eb522fd2acc'/>
<id>urn:sha1:46653b24b0afff60be35c3e876830eb522fd2acc</id>
<content type='text'>
The four calendar corrections adopted from colitur are not in missalemeum, so
re-running gen-sanctoral-ef.go silently reverted them. Warning about that in
three places was the stopgap; this fixes it.

The generator now carries a missalOverrides table -- slug, field, required
value, and the authority for it -- applied after every harvest and collision
fix-up has settled, so the Missal's value is the last word. Regenerating is
now safe; hand-editing the generated file is not, and both the file's header
and the generator's comment now say which way round that is.

An explicit table rather than "preserve whatever the existing file had",
which is how the name.&lt;lang&gt; preservation beside it works and would have been
less code. Preserving from the file cannot distinguish a deliberate
correction from staleness: if missalemeum ever fixes one of these, a
preserve-from-file rule would keep overriding it forever with nobody the
wiser. Naming each override with its rubric makes an obsolete one findable by
reading four lines. The apply step reports "already correct upstream" when it
finds one, which is the signal to retire it.

It FAILS rather than warns when an override names a slug the run did not
harvest. A rename upstream would otherwise drop a correction silently, which
is the precise accident the table exists to prevent.

TestTridentineMissalOverrides pins all four values from the other side, so a
regeneration that stopped applying them fails the suite rather than only
failing quietly. Mutation-tested: reverting the Assumption vigil to
missalemeum's white reddens it with a message naming the cause. It also
asserts the Ascension's vigil is NOT violet -- RG 128's rule is "outside
Paschaltide" and that one is inside it -- so a careless "make every vigil
violet" fix fails too.
</content>
</entry>
<entry>
<title>docs(caldata): the EF data is no longer purely generated -- say so</title>
<updated>2026-08-18T12:45:37Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-18T12:45:37Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=118dacf366d7864bc8e9ecb8f7b6a70d1b9fdb84'/>
<id>urn:sha1:118dacf366d7864bc8e9ecb8f7b6a70d1b9fdb84</id>
<content type='text'>
Both ini headers described this data as generated from missalemeum, and the
generator described its own output as the file. Neither is true since the
eight corrections landed, and the gap is not cosmetic: missalemeum does not
carry any of them, so running scripts/gen-sanctoral-ef.go would silently
revert all four calendar fields, with nothing to notice it.

The two ini headers and the generator's own doc comment now each carry the
list of hand-corrected fields, the rubric behind each, and the warning that a
regeneration reverts them. Deliberately repeated in both places rather than
cross-referenced from one: either can rot, and repeating the list means the
other still shows it.

Also records what the six allow-listed oracle days actually mean now. The
oracle is not wrong to flag them -- missalemeum is simply the party that
disagrees with the Missal, which is a different situation from the one that
test was written for.
</content>
</entry>
<entry>
<title>fix(ef): adopt eight corrections from colitur, with their citations</title>
<updated>2026-08-18T11:02:49Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-18T11:02:49Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=910ea22fd90ded9d4bf1845d5a4cfd9f11a624e1'/>
<id>urn:sha1:910ea22fd90ded9d4bf1845d5a4cfd9f11a624e1</id>
<content type='text'>
lectio's EF data has been the upstream for colitur, whose data was
bootstrapped from this ini. colitur has since validated that data against the
1962 Missal itself, and these eight fields are where the two now disagree and
the Missal decides against us. Each carries the rubric.

Calendar. St Ubaldus (16 May) and St Didacus (13 November) become III-class
feasts rather than commemorations: the Missal's own universal calendarium
ranks both "III classis" outright with no commemoration rubric, and each has
its own Mass entry in the Proprium taking its readings from a Common with
only its Oratio proper -- which a bare commemoration never has. Found by
auditing all 290 of colitur's fixed-date entries against that calendarium;
these two were the only status defects in the file. The Vigil of the
Assumption becomes violet from white and the Vigil of St Lawrence violet from
red: RG 128 gives violet to vigils of II and III class outside Paschaltide,
and its sole exception is the Ascension's vigil, inside Paschaltide, which is
white and is untouched here.

Lectionary. Both Ember Saturdays carried the wrong saint's Mass entirely --
St Thomas the Apostle's under ef-advent-ember-sat and St Matthew's under
ef-september-ember-sat, in both cases because 21 December and 21 September
fall on a Saturday in the same six years and a bootstrap generalised one
year's coincidence into a template value.

Two oracle allow-list entries, both placed ahead of the BVM-Saturday entry
deliberately. When 16 May or 13 November falls on a Saturday, that entry's
predicate also matches and would absorb the divergence under a reason no
longer true of it -- it says both engines agree the day is unimpeded, and the
point here is that the day IS impeded. The vigil entry excludes Sundays for
the mirror reason: RG 33 omits a vigil falling on one, and without the guard
2026-08-09 is stolen from the RG 33 entry that actually explains it.

Six days of the 730-day oracle window are affected. Readings are untouched by
that test, so the two lectionary corrections cost it nothing.
</content>
</entry>
<entry>
<title>caldata: two Judith citations lost a chapter boundary</title>
<updated>2026-08-17T11:56:21Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-17T11:56:21Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/lectio.git/commit/?id=e1f4b8f0ca7ff328d6c232b9dbb4cb5f17db5d3e'/>
<id>urn:sha1:e1f4b8f0ca7ff328d6c232b9dbb4cb5f17db5d3e</id>
<content type='text'>
The Assumption's epistle is Iudith 13, 22-25 et 15, 10 -- two chapters. It was
recorded as "Judith 13:22-25; 13:15; 13:10", carrying chapter 13 across the
boundary and inventing two verses that are not in the Mass at all, on a
I-class feast.

The Seven Sorrows' is Iudith 13, 22 et 23-25. It was recorded as
"Judith 13:22; 13:25", keeping only the range's endpoint.

Both verified in the photographic scans of the 1962 Missal:
  Assumption      IN ASSUMPTIONE B.M.V.   "Iudith 13, 22-25; 15, 10"
  Seven Sorrows   SEPTEM DOLORUM B.M.V.   "Iudith 13, 22 et 23-25",
                                          printed identically at its Mass and
                                          in the votive Mass of the same feast.

Found by comparing a full liturgical year (2028-12-03 .. 2029-12-01) against
missalemeum's live API, day by day, while giving colitur a lectionary of its
own. 333 of 364 days already agreed; these were two of three genuine
divergences in the remainder.

The third was St Monica (4 May): missalemeum serves Prov 31:10-17, this data
serves 1 Tim 5:3-10. The Missal gives her a proper epistle -- "Lectio
Epistolae beati Pauli Apostoli ad Timotheum", 1 Tim 5, 3-10, "Carissime:
Viduas honora, quae vere viduae sunt" -- in both scans. No change: the oracle
is wrong there, not this data.
</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>
</feed>
