<feed xmlns='http://www.w3.org/2005/Atom'>
<title>colitur.git/lib/rites, branch v1.1.0</title>
<subtitle>deterministic OCaml engine to compute and validate liturgical calendars for multiple rites, template-driven output to year 9999</subtitle>
<id>https://git.labunix.xyz/colitur.git/atom?h=v1.1.0</id>
<link rel='self' href='https://git.labunix.xyz/colitur.git/atom?h=v1.1.0'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/'/>
<updated>2026-08-26T22:38:40Z</updated>
<entry>
<title>fix(of): correct the Annunciation's transfer_target slug, dead Rule 1</title>
<updated>2026-08-26T22:38:40Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T22:38:40Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=e26d7da141f7985fadc53d8399a69dbaa1153c94'/>
<id>urn:sha1:e26d7da141f7985fadc53d8399a69dbaa1153c94</id>
<content type='text'>
precedence_of.ml's transfer_target Rule 1 (Normae n. 60's fixed Easter+8
destination for the Annunciation falling in Holy Week) keyed off
"of-annunciation", a placeholder that never matched the real shipped
entry's own slug ("annunciation-of-the-lord",
data/of/calendar-2002.sexp). Rule 1 was therefore dead code: it could
only ever fire against test_precedence_of.ml's own synthetic candidate,
which happened to carry the identical placeholder.

Output was nonetheless correct on shipped data (latent, not live): Rule
3's general forward search structurally converges on the same Easter+8
target, because the Easter Octave occupies exactly Easter+1..+7 at band
20 and the 3-9 April window carries only memorials on the universal
calendar. The exposure is an overlay placing a proper solemnity (band
40) or proper feast (band 80) anywhere in 3-9 April, where Rule 3 would
diverge from Rule 1's fixed destination and Rule 1 would silently fail
to override it.

Fixed the slug, and updated the existing Rule-1 tests' own synthetic
candidate to the real slug so they keep exercising Rule 1 rather than
Rule 3's coincidental agreement. Added a test that tells the two rules
apart rather than only checking the output they happen to agree on: it
rigs the occupant callback to occupy Easter+8 with a synthetic
Tabula-entry-3 candidate, confirms (via a control case using a
different solemnity) that the rig genuinely forces Rule 3's search past
that date, then shows the real shipped Annunciation still lands on
Easter+8 despite the block -- only explicable by Rule 1 firing, not by
Rule 3 happening to agree.
</content>
</entry>
<entry>
<title>fix(of): break the movable-BVM-memorial tie per a CDWDS Notification</title>
<updated>2026-08-26T22:37:45Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T22:37:45Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=6e62e499aed1c5a31a18bcf5e9eaa7f2b0c4ff2e'/>
<id>urn:sha1:6e62e499aed1c5a31a18bcf5e9eaa7f2b0c4ff2e</id>
<content type='text'>
Mary, Mother of the Church (Easter+50) and the Immaculate Heart of Mary
(Easter+69) are both movable obligatory memorials that can land on a
FIXED obligatory memorial's own date -- e.g. 2011-06-13 and 2015-06-13,
both against Anthony of Padua. Both candidates banded identically at
Tabula III.10 (100, both universal Memoria_obligatoria), a tie band
could not break, so Precedence.resolve's kernel-level alphabetical
Slug.compare tie-break silently picked instead -- 892 days for Mater
Ecclesiae and 1109 for the Immaculate Heart across 1583-9999, all one
shape (measured against the pre-fix alphabetical order directly, zero
unexplained).

Searched the corpus rather than inventing a rule. colitur's two
previously-checked primary sources really are silent: the 2018 decree
(AAS 110, 437-438) never addresses universal-calendar coincidence, and
Normae n. 14's third clause resolves only two AD-LIBITUM memorials
colliding. A third source resolves it: the CDWDS's own "Notification on
the Memorial of the Blessed Virgin Mary, Mother of the Church" (24 March
2018 -- same dicastery and signatories as the decree; published only on
vatican.va, confirmed absent from the AAS 2018 combined index) states
that when this memorial "coincides with another Memorial of a Saint or
Blessed... the Memorial of the Blessed Virgin Mary is to prevail," and
extends the identical reasoning to the Immaculate Heart of Mary by name.
This is a rubric, not colitur's own reasoned choice.

Implemented as a sub-rank of 95, strictly between Tabula entries 9 and
10 -- the same "fits between neighbours" technique this module already
uses elsewhere -- scoped to the two named slugs rather than
[subject = Bvm], since the Notification's own reasoning is specifically
about a memorial's link to a movable solemnity of the Lord, not a
blanket Marian precedence.

Golden pin updated: test_rite_of.ml's 2011-06-13 tie now pins Mary,
Mother of the Church as observed (was Anthony of Padua, labelled "known
arbitrary"), with the header rewritten to state the outcome now rests on
the Notification, not a coin-flip. The litcal oracle allow-list gains
L10: litcal's own grade vocabulary has no representation for a 95
sub-rank (everything Memoria_obligatoria reads "Memorial"), a
representational gap in litcal, not a computational disagreement -- the
identity axis is untouched and needed no new entry.
</content>
</entry>
<entry>
<title>fix(of): dispatch Holy Family's Normae n.35(a) fallback (W2)</title>
<updated>2026-08-26T21:45:16Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T21:45:16Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=9ebb06983b7a26db5564302253f2551dfbcf834e'/>
<id>urn:sha1:9ebb06983b7a26db5564302253f2551dfbcf834e</id>
<content type='text'>
temporal_of.ml's temporal only ever reached holy_family's own correct
26-31 December / 30 December fallback logic through sunday_slug, which
returns None immediately for a non-Sunday date. In a year 25 December
is itself a Sunday (26-31 December then holding no Sunday of its own),
the feast vanished entirely and fell through to an ordinary Christmas
ferial slug -- confirmed on 2022, 2033, 2039, 2044, 2050 (and roughly
1583..9999 domain-wide, one year in seven).

temporal's dispatch now consults holy_family directly, independent of
weekday, between the sunday_slug and christmas_feria_slug branches,
carrying the identical identity the Sunday case already builds
(subject Lord, holy_family_names, rank Festum). holy_family and
anchors were already correct and needed no change.

Added a direct example-based test for the fallback across five
affected years plus a control for the unaffected Sunday case, and a
domain-wide property (Holy Family is observed in every liturgical
year 1583-9999) folded into both the 200-sample QCheck run and the
exhaustive sweep -- the property that would have caught this directly.

Un-pinned the now-fixed behaviour everywhere it was recorded as
known-wrong: test_rite_of.ml's own 1583 pin (rewritten to assert the
fix), test_validate_of.ml's own independent exhaustive-sweep counter
and landmark-year filter (found via make check, not the initial
survey), and data/of/expected-divergences-litcal.sexp's L2/L3
allow-list entries against the litcal oracle (closed and removed, with
a dedicated regression test replacing the allow-list's own silence).
</content>
</entry>
<entry>
<title>fix(of): anticipate St Joseph onto 18 March per Normae n. 56(f)</title>
<updated>2026-08-26T21:40:17Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T21:40:17Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=2f6d25aff88b4c76c61e36889f1c017b3cc8faee'/>
<id>urn:sha1:2f6d25aff88b4c76c61e36889f1c017b3cc8faee</id>
<content type='text'>
W1: when 19 March (St Joseph) falls on Palm Sunday, colitur sent him
forward to Easter+9 instead of anticipating the solemnity backward to
18 March, the one rule in the whole Normae transfer machinery that
names an earlier date. Live in 16 of 1583-2100 (1595, 1606, 1617,
1690, 1758, 1769, 1780, 1815, 1826, 1837, 1967, 1978, 1989, 2062,
2073, 2084).

Blocked on a kernel contract: Rite.t.transfer_target required its
result be strictly later than the impeded date, an obligation argued
purely from the EF's RG 96. Traced against Calendar's actual placement
pass rather than assumed unsafe: the round loop re-resolves the whole
year fresh every round, and its two safety nets (~start/~stop; the
round-count guard) both test the target's own value with no comparison
against origin, so a bounded backward jump converges the same way a
bounded forward one does. The one requirement that stays load-bearing
is target &lt;&gt; origin: Precedence.resolve never deduplicates by slug, so
a candidate returning its own argument unchanged would collide with
itself in the same round's contest. Relaxed the obligation accordingly
(rite.mli) and added the fourth transfer_target branch in
Precedence_of, ahead of the existing three (Precedence_of.transfer_target,
Rule 0). calendar.ml needed no logic change, only two comments that
asserted "every genuine transfer moves forward" as a blanket fact.

Unpinned the two tests that asserted the old wrong (forward) behaviour
and replaced them with assertions of the correct one, plus a new
direct unit test of the backward branch across two independent years.

EF output is untouched: data/ef/ and lib/rites/rite_ef/ carry no diff,
and colitur day/readings for 2026, 1583 and 9999 are byte-identical
against a build of the prior commit. OF's own 2005-2050 window carries
none of the 16 affected years and shows zero unexpected diffs; the
full 1583-9999 domain still reaches a fixed point under the exhaustive
sweep.
</content>
</entry>
<entry>
<title>fix(citation): make the well-formed reading-part set rite-supplied</title>
<updated>2026-08-26T12:28:50Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T12:28:50Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=e5cbc662227de4f3ab0089fe73f639f4f84601a4'/>
<id>urn:sha1:e5cbc662227de4f3ab0089fe73f639f4f84601a4</id>
<content type='text'>
I7 (final-review.md, 2026-08-25-colitur-of-phases-3-5): OF Sundays and
solemnities shipped without the Second reading, and the lectionary
data header called the First reading an "Epistle" -- EF vocabulary.
The sole blocker was Validate's own kernel-hardcoded assertion that a
well-formed day's sorted citation parts are exactly [First; Gospel];
Citation.part already carried Second.

Rite.t gains citation_shapes : Citation.part list list, the fourth
EF-shaped kernel misfit (after Preface.t, Mass_formulary.source,
transfer_target's strictly-later contract) and the first fixed rather
than only documented. EF supplies its one shape unchanged; OF supplies
two, [First; Gospel] on a feria/feast/memorial and [First; Second;
Gospel] on a Sunday or solemnity (OLM 1981 Praenotanda n. 66.1 vs
n. 69.1, n. 84(b)/(c), page-image verified against olm-1981.pdf).
Validate's "citations" check now matches against the rite's own
declared shapes rather than a single literal pattern, preserving all
three invariants it always enforced: zero or one complete shape, never
partial; no part outside what the rite declares; no part repeated.

bootstrap_lectionary_of.ml's cite now extracts a Second reading from
Sunday-cycle ini sections only, correctly excluding the one stray
`second` field lectio's snapshot carries on a weekday-cycle section (a
content-verified scraping artifact). The hand-authored Christmas Day
entry gains its own Second reading from the same OLM passage already
cited for its First/Gospel. Regenerated data/of/lectionary.sexp: 771
entries unchanged, citation fields 1542 -&gt; 1725 (+183, all Second
readings), SHA-256 re-pinned, header wording fixed and a new coverage
bullet added. Responsorial Psalm is deliberately still not emitted --
OLM 1981 Praenotanda n. 71 itself lists it apart from the readings
proper as a chant with its own refrain, and the finding driving this
change names only the missing Second reading.

EF output verified byte-identical to the branch start (empty
lib/rites/rite_ef + data/ef diff since v1.0.0, plus a direct binary
comparison of `day`/`readings` for 2026, 1583 and 9999 against a
worktree build of the pre-change commit).

Tests: three new fixtures in test_validate.ml prove the generalisation
holds both directions (a multi-shape rite accepts either of its
declared shapes; a rite that never declared a shape still rejects it).
test_citation_coverage_of.ml's pinned residual grows 41 -&gt; 49, all new
entries the same already-understood chapter-crossing shape.
</content>
</entry>
<entry>
<title>docs(of): correct stale precedence_of.mli note on St Joseph/Palm Sunday</title>
<updated>2026-08-26T10:55:47Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T10:55:47Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=b05d81daaf067bd21e078c6bb43a0dcfcdfbadc4'/>
<id>urn:sha1:b05d81daaf067bd21e078c6bb43a0dcfcdfbadc4</id>
<content type='text'>
The "KNOWN UNIMPLEMENTED FOURTH RULE" note (Normae n. 56(f)) still said
"No sanctoral data exists yet (Phase 3), so nothing is WRONG today" and
located the trigger at "25 March co-occurring with Palm Sunday". Both
are now false, and the date was already wrong when written: St Joseph
is 19 March, not 25 March -- 25 March is the Annunciation, a separate
solemnity handled elsewhere in this same module.

Phase 3 shipped data/of/calendar-2002.sexp on this branch, so the
defect is now live in 16 of the 1583-2100 years (19 March on Palm
Sunday); rewrite the note to say so and point at where it is pinned
(test_rite_of.ml's known-wrong-forward-transfer test, allow-list
entries L8/L9).
</content>
</entry>
<entry>
<title>fix(of-temporal): make anchors agree with temporal on the Nativity Vigil</title>
<updated>2026-08-26T08:35:33Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T08:35:33Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=e199ad6f599a263cad1f9886b512da8b3a46f465'/>
<id>urn:sha1:e199ad6f599a263cad1f9886b512da8b3a46f465</id>
<content type='text'>
Temporal_of.named correctly guards the Nativity Vigil: when 24 December
falls on a Sunday it is the Fourth Sunday of Advent (Tabula I.2 outranks
I.3), not the Vigil, and named returns None so temporal's dispatch falls
through to the ordinary Sunday slug. anchors, however, still listed
("of-nativity-vigil", 24 December) unconditionally -- a regression from
the earlier R8 fix, which added the guard to named but never updated
anchors to match. In 2028 and 2034, anchors claimed the Vigil where
temporal actually returns "of-advent-sunday-4".

anchors exists precisely as an independent restatement whose stated
purpose is to catch an accidental single-site drift rather than leaving
it invisible -- it caught exactly this drift, just later than it should
have, only once Validate.run was wired for the OF rite for the first
time (nothing before that compared anchors against named/temporal, so
the two were free to disagree unnoticed).

Fixed by restating named's own guard exactly: the Vigil entry is omitted
on a Sunday year rather than replaced, since the Fourth Sunday of Advent
is an ordinary generic week-numbered Sunday, not one of the named days
anchors exists to restate.

Audited every other anchors entry the same way, across the full
1584-9997 domain (test_temporal_of.ml's own exhaustive sweep, run under
COLITUR_EXHAUSTIVE_SWEEP=1): no further drift found. The sweep does
surface one already-known, already-pinned mismatch -- the "of-holy-family"
entry, in any year 26-31 December holds no Sunday (Normae n.35(a)'s own
30-December fallback) -- but it is a different shape, not a second
instance of this bug: there anchors computes the right date and it is
temporal that fails to reach it, which test_rite_of.ml already found,
pinned and deferred as out of scope. Corrected that file's own stale
comment, which still described the now-fixed Vigil disagreement as an
open gap.

New tests in test_temporal_of.ml cover both a Sunday-24-December year
(2028, 2034) and a non-Sunday one, checking every anchors entry against
temporal directly, plus a positive assertion that the Vigil entry is
correctly present or absent depending on the year.
</content>
</entry>
<entry>
<title>fix(of-lectionary): stop serving drifting readings for O-Antiphon and Christmas-season dates</title>
<updated>2026-08-26T08:11:47Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T08:11:47Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=b6d0eba04d1da51515dad70127112898b500c1bc'/>
<id>urn:sha1:b6d0eba04d1da51515dad70127112898b500c1bc</id>
<content type='text'>
The bootstrap excluded 19 lectio bases as "date-keyed duplicates", but
17 of them were not duplicates at all: the 8 O-Antiphon days
(17-24 December) and 8 further Christmas-season dates (29-31 December,
2-5 and 7 January) each carry unique per-date content found nowhere else
among the emitted entries, verified against lectio's own ini directly.
17 December is Gen 49:2,8-10 / Matthew 1:1-17, but colitur was falling
through to that year's unrelated weekday-keyed "advent-3-mon" content
instead.

OLM n. 69.3 explains why: those ferias are fixed by civil date, unlike
every other Advent/Christmastide/Paschaltide feria, which is merely
non-alternating within a weekday slot. Temporal_of's own ferial slugs
for these dates are weekday-keyed, so looking one of those up served
whichever OTHER date happened to share that year's weekday alignment --
a citation that silently drifted year to year on ~16 real civil dates
(6 January stays excluded: Temporal_of fixes Epiphany there
unconditionally, so that date can never reach the ferial path at all;
two more, easter-6-thu and advent-4-sat, stay excluded for their own,
different structural reasons).

Fixed with a date-keyed lookup route (Lectionary_of.date_keyed_slug),
tried before the weekday-keyed one in readings' own step 3.
Temporal_of's slugs are unchanged -- only which lectionary key resolves
a day's citations. tools/bootstrap_lectionary_of.ml now maps the 17
bases to synthetic date-keyed slugs instead of excluding them, and its
own reachability sweep calls date_keyed_slug directly so the two can
never drift apart. Re-emitted data/of/lectionary.sexp (754 -&gt; 770
entries) and corrected its provenance header, which previously
described these dates as excluded duplicates.

Also states, for the first time, a Minor finding from the same review:
190 of 222 shipped sanctoral slugs have no dedicated lectionary entry
and correctly fall through to the ferial per OLM norms -- expected
behaviour, not a gap, but never said in the coverage report before.

New tests pin 17 December (Gen 49:2,8-10 / Matthew 1:1-17) and 2 January
(1 John 2:22-28 / John 1:19-28) in two years with different weekday
alignments, proving neither reading drifts, plus direct boundary
coverage of date_keyed_slug itself (both windows, the 6 January
exclusion, and the Sunday guard).
</content>
</entry>
<entry>
<title>feat(of): assemble Rite_of.context and wire --rite of into the CLI</title>
<updated>2026-08-26T07:53:22Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T07:53:22Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=8fa9db25f55f4ae92ad6a28115484dd981593a3c'/>
<id>urn:sha1:8fa9db25f55f4ae92ad6a28115484dd981593a3c</id>
<content type='text'>
Task 5 of Phases 3-5 (the payoff task): bundles Tasks 1-4's separately
built pieces (Temporal_of, Precedence_of, Rubrics_of, Lectionary_of)
into a (Vocab_of.season, Vocab_of.rank) Colitur_kernel.Rite.t, and
wires colitur day/readings --rite of &lt;year&gt; into the CLI. Default
stays ef; every pre-existing invocation is byte-identical (asserted
directly in test/cli.t and against a real v1.0.0 build for three
years, plus git diff --stat v1.0.0..HEAD over lib/rites/rite_ef/,
lib/kernel/, data/ef/ is empty).

No kernel change was needed, confirming the spec's central claim.
season_runs needed real thought rather than the brief's own suggested
Vocab_of.seasons: Temporal_of.season's own 'first run'/'second run'
comments confirm Ordinary Time is genuinely interrupted by Lent and
Easter over one liturgical year, so the expected run-length-compressed
sequence names it twice, six runs over five seasons -- exactly the
case Rite.t.season_runs's own doc comment already names as its
illustration for why this field is rite-supplied rather than derived.
easter/fixed_key are as specified, each carrying its own citation
(Gregorian computus; the 2002 Missal's one bissextilis occurrence is a
leap-year table entry, not a kalends-doubling rule, so fixed_key is
identity).

Follows rite_ef.ml's template and its lectionary-parameter lesson
exactly: lectionary is caller-supplied, not closed over.

test/test_rite_of.ml resolves the real assembled rite (calendar-2002
.sexp + all 13 amendment overlays + lectionary.sexp) through Calendar
and Validate.run, the same discipline test_validate.ml/test_rite_ef.ml
already apply to EF. This is the first time rite_of has been checked
this way, and it surfaced two new, real, cited gaps outside this
task's own scope to fix: Temporal_of never reaches Normae n.35(a)'s
30-December Holy Family fallback in a year with no Sunday in 26-31
December (first hit 1583, the domain floor), and Temporal_of.anchors
disagrees with temporal on 24 December when that date is itself a
Sunday (Advent IV outranks the Vigil, first hit 2006). Both pinned as
known-wrong, not fixed, the same treatment the brief's own pre-known
St Joseph/Palm Sunday gap (Normae n.56(f)) gets, pinned on 2062. A
third gap was found in data/of/lectionary.sexp's own coverage (audited
against one civil year only, so the other weekday-cycle letter is
unaudited and at least one pairing is asymmetric on shipped data) --
that file is excluded from this task, so the wide 2005-2050 sweep
checks structural invariants only and leaves the citation-chain checks
to the small, individually-audited landmark-year set.

make check (COLITUR_EXHAUSTIVE_SWEEP=1): exit 0, 867 tests, ~316s.
</content>
</entry>
<entry>
<title>feat(of): Lectionary_of -- reading-cycle arithmetic and the lectionary chain</title>
<updated>2026-08-26T07:15:08Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T07:15:08Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=155c2e55948dc179a45458d856808f7105878d1f'/>
<id>urn:sha1:155c2e55948dc179a45458d856808f7105878d1f</id>
<content type='text'>
Task 4 of Phases 3-5: the last Rite.t field OF still needed, `readings`.

The cycle rules are CODE, cited to OLM (Ordo Lectionum Missae) 1981
Praenotanda -- verified against the real page image
(docs/research/of/olm-1981.pdf pp.32-33/"XXXII-XXXIII"), not the
document's own unreliable OCR text layer, per the design spec's own
caveat. n. 66 (with its footnote 102's worked example, 1980=C/1981=A/
1982=B/1983=C) gives the three-year Sunday cycle; n. 69 point 4 gives
the two-year weekday cycle, Year I in odd label years, Year II in
even. Both derive the "label year" the same way: one more than the
civil year Advent I opened in, per n. 66's footnote 102 itself
("nempe a prima hebdomada Adventus, quae cadit in anno civili
praecedente") -- so the cycle turns at Advent I, not 1 January, exactly
where the task brief said to get it right. The design spec's own
citation for the weekday rule, "Praenotanda n. 649", does not survive
a direct check against the primary source (search finds "649" only in
unrelated index/page-number entries); corrected here to n. 69, the
paragraph the rule's own Latin text actually sits under, confirmed by
the page image's own printed margin number.

The data is bootstrapped from lectio's of-lectionary.ini (988 keys,
niedziela.pl, harvest 2020-2025) by tools/bootstrap_lectionary_of.ml
-- OCaml, not the brief's own suggested .py: tools/bootstrap_lectionary
.ml already established this exact shape for EF, including a real
safety net ([assert_reachable], sweeping Rite_of.Temporal_of.temporal
AND the real merged sanctoral layer directly) that a second-language
reimplementation would either duplicate or lose. Handles two real
data anomalies found while building it: one malformed ini line (a
Polish Septuagint-numbering annotation for Sirach 3, "Syr 3,2-6.12-14"
against OLM/CEI/USA's shared Nova Vulgata, matching the design spec's
own sec5 finding); and two Sunday-cycle-labelled bases (Annunciation,
Immaculate Conception) where a scraping-year artifact left one of
three cycle letters carrying a different day's Mass entirely --
resolved by a general, mechanical 2-of-3 majority rule, not a
per-saint hand edit, and logged.

Coverage measured in both directions, per the brief's own Step 1:
- 1 of 365 days in civil year 2026 (25 December, the Christmas DAY
  Mass) has no Temporal_of-slug match anywhere in lectio's 988 keys --
  only the Vigil is present. Named in the data file's own header.
- 0 of 438 distinct lectio bases map to no colitur slug, after 19 are
  deliberately excluded (late-Advent/Christmas-season DATE-keyed
  duplicates lectio also carries in a WEEKDAY-keyed form colitur's own
  slugs actually match -- Lectionary.t has no date-keyed lookup at
  all, so the date-keyed family is transcription-inert, not silently
  dropped: named explicitly, not a residual).

Lectionary_of.readings is three steps (no Commons indirection, unlike
EF -- out of this task's scope): the observed celebration's own
embedded citations; else, for a real sanctoral winner, its own slug
looked up directly (flat, then both cycle letters); else the day's
own temporal slug, same lookup. No preceding-Sunday fallback: OF's own
two-year cycle assigns every Ordinary Time weekday its own reading by
design, and no OF norm was found instructing otherwise.

Tests (test/test_lectionary_of.ml, 12 cases): a cycle-arithmetic table
straddling Advent 1980/1981/1982/2025 inside single civil years,
reproducing OLM n.66's own worked example directly; SHA-256 pins on
both the emitted file and lectio's own source (embedded in the
provenance header); a pinned, NAMED 2026 coverage result (364 resolve,
exactly {25 December} does not -- never a bare count); two resolution-
chain spot checks (a plain ferial, and a saint's day beating the
ferial it would otherwise fall on); and a real subprocess test that a
missing lectio source file fails the generator loudly, non-zero exit,
no output written -- the exact bug Task 1's own generator shipped and
needed a review round to fix.

dune test: exit 0, 856 tests. make check (COLITUR_EXHAUSTIVE_SWEEP=1,
full 1583-9999 sweep): exit 0, 862 tests, ~342s, both run unpiped in
the foreground. git diff --stat v1.0.0..HEAD -- lib/rites/rite_ef/
lib/kernel/ data/ef/ still empty.
</content>
</entry>
</feed>
