<feed xmlns='http://www.w3.org/2005/Atom'>
<title>colitur.git/test, branch main</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=main</id>
<link rel='self' href='https://git.labunix.xyz/colitur.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/'/>
<updated>2026-09-02T10:54:07Z</updated>
<entry>
<title>fix(of): correct ten reading references, regenerated from lectio</title>
<updated>2026-09-02T10:54:07Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-09-02T10:54:07Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=b14a3078c1a4db87010f5df955442bf1e440e0ec'/>
<id>urn:sha1:b14a3078c1a4db87010f5df955442bf1e440e0ec</id>
<content type='text'>
Nine ended at verse 22 where the OLM ends them at 20; the tenth read
"Matthew 17:9a,10-135", a verse Matthew 17 does not have. Every one was in a
Year II entry, or in of-advent-2-saturday, which has no year cycle. Not one
Year I entry was affected -- that distribution is what made this a
transcription fault rather than ten independent slips.

Corrected upstream in lectio's own internal/caldata/of-lectionary.ini (its
suite green) and this file regenerated from it, per the header's own
instruction not to hand-edit a generated artifact. The diff is exactly the
source SHA-256 pin plus the ten references; both pins in test_lectionary_of.ml
move with it.

The Ordinary Time invariant added in the previous commit now asserts outright
rather than pinning six exceptions: in Ordinary Time the OF weekday Gospel does
not depend on the year of the cycle, so a -i/-ii pair whose Gospel differs is a
defect, and the expected violation set is empty. Mutation-tested against the
Year II entry alone -- an earlier attempt changed both halves of the pair and
passed for the wrong reason, which is worth knowing about this shape of test.

Measured: the 20-vs-22 divergence class against litcal goes 26 -&gt; 0 over the
twelve witnessed years. 917 tests, every year 1583-9999.

Also, L6/L7 (the Sacred Heart / John the Baptist tie at an equal Tabula entry)
gains the citation its note said did not exist. Tabula I.3 enumerates
"Sollemnitates Domini, beatae Mariae Virginis, et Sanctorum" -- an order, not a
bare category, and read that way it settles the tie against colitur's current
alphabetical answer. Recorded with its counter-argument (entry 4 marks its own
internal order with explicit lettering; entry 2's list cannot be an ordering at
all) and with what would settle it. Verdict stays open, behaviour unchanged.
</content>
</entry>
<entry>
<title>feat(cli): --help is one screen, not the whole manual</title>
<updated>2026-09-02T10:30:56Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-09-02T10:30:56Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=5dd14266f2609faadc3ec60077127a7ef7e8ac8b'/>
<id>urn:sha1:5dd14266f2609faadc3ec60077127a7ef7e8ac8b</id>
<content type='text'>
It printed 374 lines: every output format, the RFC 4180 quoting rule, the
template scope hazard, the publish manifest semantics. All of that is real
documentation and all of it is already in colitur(1), installed beside the
binary. A help text nobody reads at the terminal is not more helpful than a
short one, it is less -- the reader stops looking.

Now 42 lines: the commands, the flags most runs need, the year domain, the
exit status, and where the rest lives. Everything removed is in the man
pages, which is checked rather than assumed -- each dropped section has a
counterpart section there.

One thing moved rather than went. cli.t pinned `rubrics`' documented field
list against what `rubrics` actually prints, because that field count drifted
once already and stayed wrong for a whole review round. Deleting the prose
would have quietly retired the check, so it now reads the worked example out
of man/colitur.1 instead, and test/dune's cram stanza gains the man page as a
dep -- a sandbox holds only what that stanza names. Mutation-tested: changing
the documented example reddens the suite.

The tagline also stopped saying "Roman rite, 1962" -- the OF has shipped since
v1.1.0.
</content>
</entry>
<entry>
<title>test(of): pin the six corrupt Year II Gospels in Ordinary Time</title>
<updated>2026-08-28T14:15:13Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-28T14:15:13Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=4fb9eac2fdb9bc22af2b63c1d5df6da7780b58cf'/>
<id>urn:sha1:4fb9eac2fdb9bc22af2b63c1d5df6da7780b58cf</id>
<content type='text'>
Built the litcal readings comparison the OF provenance notes had left as
future work, and it found a defect class the existing layers structurally
could not see: the litcal layer compares season, grade and identity, and
stops there, so every reading colitur serves has been validated by nothing
except the lineage it arrived on.

Ten references are corrupt. Nine end at verse 22 where the OLM ends them at
20; the tenth reads "Matthew 17:9a,10-135", a verse Matthew 17 does not have.
Every one lives in a Year II entry or in of-advent-2-saturday, which has no
year cycle -- not one Year I entry is affected, which is what makes this a
transcription fault rather than ten independent slips.

This commit adds only the detection, and only for the six the invariant can
reach. In Ordinary Time the OF weekday Gospel does not depend on the year of
the cycle: OLM n. 69 point 4 alternates the FIRST reading and leaves the
Gospel a single one-year series. So of-ordinary-time-&lt;n&gt;-&lt;day&gt;-i and its -ii
twin must carry the same Gospel string, and six pairs do not.

Scoped to Ordinary Time deliberately. The unscoped sweep reports 17 further
disagreeing pairs in of-advent-3/4-* and of-christmas-*, all of them correct:
those weekdays are keyed to the calendar DATE (OLM n. 69 point 3), so their
-i/-ii entries describe two different dates rather than two years of one
cycle. An unscoped test would report seventeen false positives and would,
rightly, be switched off by whoever read it next.

Pinned rather than green, because the fix is not ours to make here:
data/of/lectionary.sexp's own header forbids hand-editing a generated
artifact, and lectio's internal/caldata/of-lectionary.ini carries all ten
verbatim. The exact six are listed, so the test fails if a seventh appears
and equally if one is fixed without updating the list -- both are changes a
reader must see.

Mutation-tested in both directions: introducing a seventh violation reddens
it, and correcting Mark 1:14-22 reddens it too.

Evidence for the corrections, in docs/research/of/olm-1981-ocr.txt, the Latin
OLM itself rather than a witness: "Mc 1, 14-20", "Mc 4, 1-20", "Mc 5, 1-20",
"Mt 7, 15-20", "Mt 17, 14-20", "Mt 18, 15-20", "1 Sam 1, 9-20",
"1 Sam 3, 1-10. 19-20", "Mt 17, 10-13". litcal independently confirms three of
the six Gospels and all four references the invariant cannot reach. James
5:13-22 is unlocatable in the OCR but impossible on its face: James ends at
5:20.
</content>
</entry>
<entry>
<title>feat(cli): --month, --date and --today narrow a report</title>
<updated>2026-08-28T08:03:27Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-28T08:03:27Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=b39c8187ef1b87eea084e3c3d2226be5cf320dca'/>
<id>urn:sha1:b39c8187ef1b87eea084e3c3d2226be5cf320dca</id>
<content type='text'>
--pretty made a report readable and, in the same stroke, ungreppable: a box
spans seven lines, so `grep 2026-03-` returns fragments of March rather than
March. The nearest working equivalent is a paragraph-mode awk incantation that
depends on the boxes happening to be blank-line separated -- knowledge no
reader should need to select a month.

The three flags are ALTERNATIVES, not a stack. Naming two exits 2 rather than
letting one quietly win, the same discipline the positional year and --year
already follow, and the same reason this program refuses a flag it cannot
honour instead of accepting and ignoring it.

--date and --today CARRY a year, so the year becomes optional on those two and
required only after the window has had its say: `colitur day --today` is a
complete command while `colitur day` still is not. A year named alongside them
must agree. The disagreement message names the flag rather than printing two
bare numbers, because with --today the second year appears nowhere on the
command line for the reader to trace.

temporal refuses --year yet accepts --date/--today including as its year
source. That is not a back door to the refused spelling: --year is a second
spelling of the positional year, which temporal deliberately does not offer,
whereas --date selects a DAY and merely happens to determine which year
contains it.

The window is a predicate over an ISO date STRING rather than over a Date.t,
because the two report shapes reach it differently -- resolved days carry a
Date.t, temporal_report carries a flat Record whose date is already text --
and one shared test is one implementation rather than two that can drift.

Accepted by day, readings, rubrics and temporal; refused by easter, emit,
table, render, publish, check, convert, lang, config and new-overlay.

--pretty had shipped undocumented in both --help and colitur(1). Both now
carry it, alongside the new narrowing section; the man page's awk example is
shown precisely so the flag that replaces it is justified.

Default output is byte-identical to 1.1.0 across seven years, five commands
and both rites, and across all five emit formats and the shipped templates.
</content>
</entry>
<entry>
<title>feat(cli): --pretty draws each day as an ASCII box</title>
<updated>2026-08-27T13:45:49Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukasz@arcofasiagroup.com</email>
</author>
<published>2026-08-27T13:45:49Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=f1d562a3a4d53a707385334ab553b8a6cad36b1b'/>
<id>urn:sha1:f1d562a3a4d53a707385334ab553b8a6cad36b1b</id>
<content type='text'>
Aligned columns made the fields legible but the days ran together -- with 365
of them the eye had nothing to catch on. Each day now gets its own box: a
heading carrying the date and the liturgical colour, then the celebration, its
rank and season, and any commemorations, each on its own line.

The box art is pure ASCII, only + - and |, never Unicode box-drawing. That is
the point rather than a limitation: this format exists to be pasted or piped
into a document, a mail or a plain-text ordo, and U+2500 and its relatives
survive that only when every stage agrees about encoding and font. +---+ has
never failed to render anywhere.

readings gets a labelled block, so a citation says what it is instead of being
the second of three bar-separated fields; the OF second reading simply omits
its row on the days without one. rubrics becomes a label/value list. temporal
is the day box minus the sanctoral it does not have.

A blank line separates consecutive boxes -- without it the bottom rule of one
day and the top rule of the next sit adjacent and read as a single doubled
line, which is the same "not distinct enough" this change set out to fix.

Alignment counts UTF-8 code points, not bytes, so "Pen~afort" and "Fremiot"
still line the right edge up at 76 columns; a byte-counting pad shears the box
by one per multi-byte character. Over-long values are truncated with a ~
rather than allowed to overflow, since a box whose right edge does not line up
is worse than a clipped name that the default output still carries in full.

Verified: no ANSI escape reaches a pipe on any of the four commands, and the
default output is byte-identical to installed 1.1.0 across all five.
</content>
</entry>
<entry>
<title>feat(cli): --pretty, for reading in a terminal</title>
<updated>2026-08-27T12:12:43Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukasz@arcofasiagroup.com</email>
</author>
<published>2026-08-27T12:12:43Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=34a36fcb0956db7f06c4b8860414283c03996293'/>
<id>urn:sha1:34a36fcb0956db7f06c4b8860414283c03996293</id>
<content type='text'>
The row commands print for awk: single-space fields, slugs, a variable tail.
That is right for the default and wrong for a person, who mostly wants to know
what today is.

--pretty gives the same four commands -- day, readings, rubrics, temporal --
aligned columns, the day's liturgical colour as a swatch, and commemorations
on their own indented line rather than lengthening the row. The colour was
already computed and simply thrown away on a terminal.

Colour is written only when stdout is a terminal, so piping or redirecting
yields plain aligned text: the alignment survives, the escapes do not, and the
swatch degrades to the colour's initial so the information is not lost with
them. NO_COLOR is honoured on PRESENCE whatever its value, which is the
convention's own rule -- treating it as a boolean is the usual way to get it
wrong.

Every other command refuses the flag rather than accepting it and doing
nothing: emit, table, render and publish already choose their shape through
--format and --template, and easter prints six key/value lines, not a grid.
An intermediate version accepted it everywhere and silently ignored it on
five commands, which is the failure mode this program refuses everywhere else.

Two things the layout had to learn. Column widths are a minimum, not a
maximum: the Latin season names run past them ("Tempus per annum ante
Septuagesimam" is 35 against 34), so pad always leaves a separator or the next
field fuses onto it -- that is how "Septuagesimam 1S. Hilarii" happened. And
the commemoration indent is measured from the row actually printed rather than
computed from the column constants, or it sits under the wrong column on
exactly the rows that have something to indent.

Presentation only: bin/pretty.ml decides nothing about what a day is, and
nothing reads it. Default output is byte-identical to v1.1.0 -- verified
across day, readings, rubrics, temporal and easter for 2026, 1583 and 9999.
</content>
</entry>
<entry>
<title>fix(cli): make the flag surface systematic</title>
<updated>2026-08-27T10:33:43Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukasz@arcofasiagroup.com</email>
</author>
<published>2026-08-27T10:33:43Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=32e792a0741f62fc2c0c7dcf9255408b3256fad5'/>
<id>urn:sha1:32e792a0741f62fc2c0c7dcf9255408b3256fad5</id>
<content type='text'>
An audit probed every flag against every command rather than reading --help,
and found three inconsistencies.

--rite reached only `day` and `readings`. main.ml's own comment gave the
reason -- "has not been widened to a second rite in this task" -- a scope note
that had outlived its task and hardened into apparent design. View.of_days was
already polymorphic over the rite's type parameters, so widening was plumbing,
not library work: `temporal`, `rubrics`, `emit`, `table`, `render` and
`publish` now all take it.

`temporal` was the sharpest case. Its refusal said "--rite has no effect on
`temporal`", which was false: the EF has Septuagesima and Passiontide, the OF
neither, and every EF slug is ef-prefixed, so the flag would change nearly
every line. A message claiming no effect where the effect is total is exactly
what the audit set out to find.

Three ways of naming a year (positional, --year, --from/--to) now cross-accept
additively; naming two that disagree is a usage error rather than one silently
winning.

emit --format csv gained a rite-dependent header: EF's 16 columns are
unchanged, OF gets a 17th "second" between "first" and "gospel". An earlier
task had recorded RFC 4180 as permanently blocking this; the rule constrains
one file, not a family of them.

`rubrics` keeps refusing --lang/--raw, now with its reason. An intermediate
version accepted them by adding a name column, which changed the default from
six tab-separated fields to seven and broke both existing consumers and the
byte-identical-EF rule. The asymmetry is real but principled: the row is a
date, a slug, a source keyword, two booleans and a preface key, so there is
nothing to translate and nothing to strip. `easter` refuses --rite for the
same kind of reason -- its six anchors sit at identical Easter offsets in both
rites.

EF output verified byte-identical to 8590338 across day, readings, rubrics,
temporal and easter for 2026, 1583 and 9999, and across all five emit formats.
</content>
</entry>
<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(render): derive citation keys from the day, not a hardcoded pair (W5)</title>
<updated>2026-08-26T22:24:03Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-26T22:24:03Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=bf42e85ed622da5227a69c26ec801d32c767bb32'/>
<id>urn:sha1:bf42e85ed622da5227a69c26ec801d32c767bb32</id>
<content type='text'>
lib/render/view.ml hardcoded exactly two template keys, "first" and
"gospel", regardless of what a day's citations actually contain. The OF
now resolves a third reading, Second, on Sundays and solemnities (OLM
1981 Praenotanda n. 69.1/n. 84(b)(c)) via the rite-supplied
Rite.t.citation_shapes, which the render layer could not express. Latent
today (bin/main.ml's reject_rite_for refuses --rite on every command that
reaches View.of_days, so only EF ever reaches this path), but the wrong
shape to carry forward.

View.citation_fields now derives one key per Citation.part ACTUALLY
PRESENT in a day's own citations, in Citation.all_parts order, using
Citation.part_to_string for the key name. An absent part contributes no
key at all, not a key holding the empty string: Template.render already
treats a missing key and a present Str "" identically in Var, Section and
Inverted position, so nothing is lost, and Emit_json.year dumps the whole
Obj verbatim, so an unconditionally-present-but-empty key for a
still-unbuilt part (Psalm/Tract/Alleluia/Sequence) would sit in every EF
day's JSON forever. Filtering to present parts alone is what makes EF's
output identical byte for byte BY CONSTRUCTION rather than by a rite
check: EF's citations are always exactly [First; Gospel], so the derived
key set is always {"first"; "gospel"}, in that order.

padding_cell keeps its own fixed "first"/"gospel" pair rather than
deriving from a (nonexistent) day: it mirrors the closed set every rite
currently reachable through this render path (EF alone) always carries,
documented as debt for whenever a rite whose citation_shapes includes
Second is admitted here -- test_padding_and_real_share_key_set would
catch the mismatch immediately.

Downstream consumers: Emit_json needed no change (already a verbatim
dump). Emit_xml and Emit_ics gained a third, order-preserving element/line
(a &lt;citation part="second"&gt; element; a "Second ..." DESCRIPTION line
spliced between Epistle and Gospel) since neither is a fixed-column
format. Emit_csv is deliberately left unwidened and commented: RFC 4180's
header-row contract makes a variable column count illegal, and OF cannot
reach `emit` today regardless, so there is nothing to widen for yet;
admitting OF here later needs a disclosed, deliberate format change, not
a silent one.

term_keys (line ~239) is left unchanged, with a comment recording the
judgment call: "epistle" and "lesson" are indeed EF/Roman vocabulary
(Epistola vs. a non-apostolic Lectio, e.g. an Ember day's Old Testament
reading) sitting in an otherwise rite-agnostic list, but nothing forces a
fix yet -- no OF template exists, and every in-repo EF template already
reads term.epistle, so renaming it would break 11 shipped templates and
any user template for zero present benefit. "lesson" already sits unused,
already translated in both lang files, and is the natural neutral term a
future OF ordo template would reach for.

Tests: test_view.ml gains test_ef_days_never_carry_a_second_key (a whole
year, not one day) and test_three_citation_day_exposes_all_three, driving
View.of_days directly with a hand-built Liturgical_day.t since OF cannot
reach this path through the CLI yet -- the only honest way to exercise a
latent path. test_emit.ml/test_ics.ml extend coverage to Emit_xml/Emit_ics
(presence, value, and reading order) and pin Emit_csv's deliberate
16-column non-widening.

EF byte-identity: zero diff in data/ef/ or lib/rites/rite_ef/ against the
pre-change commit. `colitur day`, `readings`, `emit --format csv/xml/json/
ics`, and `table --template templates/ef/ordo.txt` for 2026, all non-empty,
all byte-identical (cmp) against a worktree build of the pre-change commit.
</content>
</entry>
</feed>
