<feed xmlns='http://www.w3.org/2005/Atom'>
<title>colitur.git/lib/kernel/rite.ml, branch v0.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=v0.1.0</id>
<link rel='self' href='https://git.labunix.xyz/colitur.git/atom?h=v0.1.0'/>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/'/>
<updated>2026-08-14T22:16:35Z</updated>
<entry>
<title>kernel+ef: resolve readings, chain steps 1 and 2</title>
<updated>2026-08-14T22:16:35Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-14T22:16:35Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=124d7e2261c721c4a49f7203efc276088c7cb217'/>
<id>urn:sha1:124d7e2261c721c4a49f7203efc276088c7cb217</id>
<content type='text'>
Liturgical_day.citations has read "always empty until Plan 4" since Plan 3;
it is now filled. Rite.t gains a readings function, rite-supplied for the
same reason transfer_target is: what a day with no proper falls back to is a
rubric, not a universal. Calendar calls it and passes its own temporal
function as the callback the rite needs to reach another date.

Steps 1 and 2 only: the observed celebration's own proper, else the day's own
temporal slug. Nothing encodes "Lent has daily propers" -- the presence of an
entry is the discriminator.
</content>
</entry>
<entry>
<title>kernel(calendar): place transferred celebrations (RG 96-98)</title>
<updated>2026-08-11T19:03:00Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-11T19:03:00Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=f15e44dd4c1b871c2daeb952b1c8c848274ea1f1'/>
<id>urn:sha1:f15e44dd4c1b871c2daeb952b1c8c848274ea1f1</id>
<content type='text'>
Calendar.year now runs a placement pass after resolving every day: each
deferred candidate (RG 95's I-class-only right of translation, via
Precedence's Transfer disposition) is placed on the next day the rite's
new Rite.t.transfer_target names as admissible, transferred_in/out are
set on the two ends of the move, and the whole year is re-resolved to a
fixed point, bounded by a hard max_transfer_rounds = 64 guard.

transfer_target is rite-supplied rather than a generic search Calendar
drives itself: RG 96's 'not I or II class' is not derivable from band or
disposition alone (RG 91's own table lets a universal I-class feast
outrank an ordinary Sunday in a raw contest, yet RG 96 forbids landing a
translation there regardless), and the search's starting point is
rite-specific too (the Annunciation exception). It takes an occupant
callback exposing what Calendar currently resolves as observed on any
date, so the rite never has to re-implement occurrence resolution.

Two correctness properties drove most of the design:

- A candidate's permanent natural loss at its own origin (the layer entry
  never moves) is rediscovered every round; left unfiltered this
  oscillates a placed candidate between two dates forever, since its own
  rank makes it look 'occupied' to a fresh search from its origin. Both
  the round loop's gather and the final per-day omitted accounting filter
  this out, keeping only sightings that are either brand new or losing at
  a candidate's *current* target (a fresh RG 97-98 bump).

- RG 97-98's sort has to actually decide something, not just happen to
  agree with Precedence.resolve's own tie-break next round: a
  claimed-this-round overlay lets earlier-processed candidates in one
  round block later ones in the same pass, so two coinciding I-class
  feasts land on consecutive admissible days in the one round they
  collide, in band order.

Also folds in Task 5's review finding: year_bounds clamps y to [1582,
9999] once, up front, rather than guarding start and stop independently
(each guard only ever covered one of the two rite.year_start calls,
leaving year 999 and year 100000 each able to call it out of domain
through the other branch).
</content>
</entry>
<entry>
<title>kernel(rite): bundle what a rite supplies; make season runs rite-supplied</title>
<updated>2026-08-11T17:39:37Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-11T17:39:37Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=6436509d6b599b7d7c6467bd39c8090cb9634889'/>
<id>urn:sha1:6436509d6b599b7d7c6467bd39c8090cb9634889</id>
<content type='text'>
Validate took four loose arguments that had to come from the same rite with
nothing enforcing it, and Calendar is about to add more. Bundling makes a
mismatched assembly unrepresentable through the normal path.

season_runs replaces the hardcoded assumption that every season occupies exactly
one unbroken run. That holds for the 1962 rite but is false for the modern
form's Ordinary Time, which is one season in two runs -- as written the check
would have reported a false failure every year for the second rite.
</content>
</entry>
</feed>
