<feed xmlns='http://www.w3.org/2005/Atom'>
<title>colitur.git/test/test_precedence.ml, 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-08-22T20:40:07Z</updated>
<entry>
<title>feat(ef): RG 111(a), the sung-Mass commemoration cap</title>
<updated>2026-08-22T20:40:07Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-22T20:40:07Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=761ae859d73660bcfaa59581312989cb942e704d'/>
<id>urn:sha1:761ae859d73660bcfaa59581312989cb942e704d</id>
<content type='text'>
Item 1 of Phase 3 (celebrant-rubrics-phase1): the "at Low Mass"
commemoration-placement rule the design spec recorded as unread. It is
not a new rule -- it is the sung/low axis of RG 111, which colitur
already implements.

RG 111(a) (LT.txt, "Ratio admittendi commemorationes"): a liturgical
day of the first class, AND any non-conventual sung Mass regardless of
the day's own class, admits at most one commemoration, and only if it
is privileged. (b)/(c)/(d), the same rubric's remaining clauses, give
the wider caps colitur's admit already computes -- which is exactly
the LOW MASS answer.

Exposed as Precedence.sung_mass_commemorations, a pure derivation over
the existing Low-Mass admitted list (filter to Privileged, keep the
first), not a new stored field on Liturgical_day.t: the input list is
already validated and privilege-tagged, so a second field would only
create a second place for the same fact to drift out of sync with the
first, with no new information gained. Liturgical_day.t.commemorations
is now documented as the Low Mass set explicitly, removing the
ambiguity its .mli previously left unstated.

Tested against synthetic Low-Mass sets (none/one/two privileged,
already-first, empty) and two real calendar days resolved through the
normal Cal.day pipeline: 2026-08-14 (Vigil of the Assumption, an
ordinary-only commemoration, dropped at Sung Mass) and 2026-04-25 (the
Major Litanies, RG 80/109(f), privileged, kept at both Masses).
</content>
</entry>
<entry>
<title>feat(ef): implement RG 33's third omission trigger</title>
<updated>2026-08-21T12:42:30Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-21T12:42:30Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=12b97761019cfa02ca0da8a5fb50ef815d07685c'/>
<id>urn:sha1:12b97761019cfa02ca0da8a5fb50ef815d07685c</id>
<content type='text'>
RG 33 omits a II/III-class vigil in three cases: it falls on a Sunday, it
falls on a I-class feast, "vel si festum cui praemittitur in alium diem
transferri aut ad commemorationem reduci contingat". Only the first two
were built; the third was recorded in precedence_ef.ml as unimplemented on
the grounds that no witness existed in the shipped data. That reasoning was
wrong, and the rule fires on 1 744 days across 1583-9999.

Both halves of the clause reduce to one observable question -- is the feast
the OBSERVED office on the following day (RG 34 puts it there) -- so the
kernel asks it once per candidate, after place_transfers has settled the
year. No fixed point is needed: a vigil is a candidate only on its own day,
never on its feast's, so suppressing it cannot change what the next day
observes. Precedence.rules gains vigil_feast, which returns the slug of the
feast a vigil precedes; the kernel cannot infer that itself, because only
two of the five vigil/feast pairs share a slug stem.

Blast radius, measured pre-change binary vs HEAD over the whole domain and
classified: 1 744 days, three shapes, zero unexplained. 1 199 are the feast
reduced to a commemoration (10 August on a Sunday, St Lawrence); 478 and 67
are the feast transferred under RG 96 after the Sacred Heart or Corpus
Christi takes its day. The Assumption's and the Ascension's vigils never
qualify -- their I-class feasts always keep their own day.

Independently witnessed, which is unusual here. The published Ordo -- the
only witness outside the Divinum Officium -&gt; missalemeum -&gt; lectio lineage
-- omits St Lawrence's vigil on 2025-08-09, agreeing with colitur against
both engines. That date had been read earlier as an Ordo gap; the Ordo was
right, and correcting the misreading is what surfaced this clause. On
2027-08-09 the feast does keep its day and the Ordo omits a vigil colitur
correctly keeps, which is a genuine Ordo gap.

Allow-lists: C39 (lectio, 10 rows) and a 2038 oracle class citing the
register, the 2026-2027 window having no instance. The golden pin asserting
St Lawrence's vigil is violet moved 2025 -&gt; 2027; its own comment had
reasoned about the vigil's weekday and missed that RG 33 also looks at the
feast's. Two new pins cover both shapes of the clause.

The vigil/feast table is built with Slug.of_string_exn: mutation testing
showed that of_string plus Result.to_option turns a typo into None, which
this hook's contract reads as "not a vigil", switching the rule off in
silence. Two tests assert the table against the shipped data in both
directions.
</content>
</entry>
<entry>
<title>fix(kernel,rite-ef): admit orders commemorations by RG 113's table of precedence, not slug</title>
<updated>2026-08-12T13:16:00Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T13:16:00Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=ea22ad2bde211998e6719fd5fb76233571a48383'/>
<id>urn:sha1:ea22ad2bde211998e6719fd5fb76233571a48383</id>
<content type='text'>
Precedence_ef.admit broke a same-rank tie among commemoration candidates
alphabetically by slug -- a deterministic engineering convention with no
rubrical citation. RG 113's own second sentence, primary-source-verified
against two independent scans and previously uncited in the register
(only "commemoratio de Tempore fit primo loco" was quoted before), gives
the real rule: "in admittendis et ordinandis aliis commemorationibus,
servetur ordo tabellae praecedentiae" -- admitting and ordering
commemorations both run on the rite's own table of precedence (band's
28-entry table), not RG 8's coarse four-class rank.

Precedence.resolve now computes each commemoration candidate's own band
value once, generically, and hands it to rules.admit as a third tuple
element (Precedence.rules.admit's signature changed accordingly, ditto
Precedence_ef.admit; every rule-record stub in the test suite updated to
match). Precedence_ef.admit's own compare_dignity is replaced by
compare_precedence, ordering by band then slug; a residual tie within one
identical band value still falls back to slug, since RG 113 gives no
further instruction there -- documented as a still-uncited engineering
convention, not dressed up as a rubric.

RG 98 ("in paritate autem Officium prius impeditum praecedit") was
considered as a candidate authority for that residual and rejected: it
governs the transfer queue order among several simultaneously-impeded
I-class feasts (Caput XIII), a different operation in a different
chapter from RG 113's commemoration admission (Caput XVI); nothing in the
primary text connects the two.

Blast radius measured against the pre-change binary across the entire
1583-9999 domain (not only 2005-2050): the admitted-commemoration-slug
set is byte-identical, day for day, before and after this change. The
fix corrects the citation and mechanism, not the answer, on this
codebase's current data -- both of the task brief's named examples
(22 Feb Chair-of-Peter/Lent-vs-Paul, 22 Sept Maurice-vs-Thomas-of-
Villanova) are confirmed present and unchanged in both streams. A new
test (RG113: admit picks by precedence order, not slug, when they
disagree) proves admit actually consults the passed-in precedence value
with a synthetic pair whose slug order and precedence order disagree --
teeth a same-band-only regression test could not have caught, since
every real collision found in the domain happens to agree on both axes.

271 -&gt; 272 tests, all green; COLITUR_EXHAUSTIVE_SWEEP=1 unaffected.
</content>
</entry>
<entry>
<title>fix(rite-ef,kernel): follow the oracle on the Purification; admit reads the day's own Sunday-ness, not observed</title>
<updated>2026-08-12T11:32:13Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-12T11:32:13Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=7d3b5ec831a60e8b63466251d63b2bd564acba2b'/>
<id>urn:sha1:7d3b5ec831a60e8b63466251d63b2bd564acba2b</id>
<content type='text'>
Fix round 1 review, two findings landed together because each golden pin's
correctness depends on both.

CRITICAL (item 1): reverts this branch's own first-pass retag of the
Purification (2 February) from Lord back to Bvm -&gt; Lord. The calendarium
titles it 'B. Mariae Virg.', but missalemeum -- this project's designated
EF oracle -- treats it as taking an occurring II-class Sunday's place
outright, commemorations empty, exactly RG16(a)'s own festum Domini
treatment (2020-02-02, 2014-02-02, independently fetched). Control,
2019-09-08 (the Nativity of the BVM, an ordinary Marian feast, on a
Sunday): the Sunday observed, the feast merely commemorated -- the
opposite pattern, proving the Purification's own oracle treatment is
deliberate, not a gap. RG16(a) alone, on the untouched data, would have
matched the oracle character for character; retagging Bvm is what moved
colitur away from it on 1200 domain-wide days. The user has ruled: follow
the oracle. Genuine primary-text counter-evidence (RG120(b), the colour
rule grouping 2 February under 'B. Mariae Virg.', a category separate
from 'Domini') is recorded in the register, not discarded -- a different
project could reasonably rule the other way on the same evidence.
most-holy-name-of-mary stays retagged Bvm; that one was never disputed.
Removes the now-unneeded C12 lectio allow-list entry (data/ef/
expected-divergences.sexp, test_differential.ml): with the Purification
Lord again, colitur matches lectio's own class = lord on every affected
date, no divergence to allow-list.

IMPORTANT (item 2): Precedence.rules.admit gains a ~temporal parameter --
a genuine kernel signature change, unlike RG16(a) itself. RG111(b)'s own
Sunday rank floor ('one commemoration, namely of a II-class feast') used
to be detected by reading observed's own slug for a Sunday marker, which
silently assumed observed IS the day's temporal-cycle office. RG16(a)
breaks that assumption on purpose: a Feast of the Lord standing in for an
impeded Sunday 'cum omnibus iuribus et privilegiis' keeps the day a
dominica II classis for RG111(b) too, even though the FEAST, not the
Sunday, is observed. Before this fix an unrelated saint (Pope Sixtus II
et al., 6 August, Class3) was wrongly admitted into the freed slot on
every one of 1178 domain-wide days. Oracle-confirmed directly: 2023-08-06
(a Sunday) shows him displaced, commemorations empty; 2026-08-06 (an
ordinary Thursday) shows him admitted -- being a Sunday is exactly what
excludes him. admit now reads Sunday-ness off ~temporal (the day's own
temporal-cycle candidate, passed through from Precedence.resolve
unchanged) instead of observed. Validate's own admission fixed-point
invariant updated to pass the same real temporal candidate through.

Both golden pins in test_golden.ml were promoted from actual colitur day
output without independently consulting the rubric or the oracle first --
a real defect the review caught. Re-derived from both before re-pinning:
2028-08-06 and 2025-02-02 now pin comms=[], no commemoration at all,
matching missalemeum exactly in both cases.
</content>
</entry>
<entry>
<title>test(precedence): strengthen accounting test, cover order and empty sanctoral</title>
<updated>2026-08-11T17:23:06Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-11T17:23:06Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=7e29712aadf2436b83de2d5e8d1f98b30d2f6d59'/>
<id>urn:sha1:7e29712aadf2436b83de2d5e8d1f98b30d2f6d59</id>
<content type='text'>
The accounting test only checked bucket lengths, which a mutant satisfies by
duplicating a candidate across two buckets while dropping another entirely.
Replace it with a sorted slug-set comparison (Alcotest.slist), which a
duplicate-or-missing slug both fail.

Add two cases the brief's three properties call for but nothing exercised:
input-order independence (permuting the sanctoral list must not change the
outcome) and a temporal-only day (empty sanctoral list), the case the
temporal/sanctoral split exists to make safe.
</content>
</entry>
<entry>
<title>kernel(precedence): rite-parameterised resolver</title>
<updated>2026-08-11T17:07:11Z</updated>
<author>
<name>Lukasz Kasprzak</name>
<email>lukas@labunix.xyz</email>
</author>
<published>2026-08-11T17:07:11Z</published>
<link rel='alternate' type='text/html' href='https://git.labunix.xyz/colitur.git/commit/?id=19d5bbaab8fbf40f6fa6de8906169e3bb7144e1f'/>
<id>urn:sha1:19d5bbaab8fbf40f6fa6de8906169e3bb7144e1f</id>
<content type='text'>
Three rite-supplied functions, not one: band (who wins, RG 91), disposition
(what happens to the loser, RG 92-95) and admit (how many commemorations are
admitted, RG 111). The loser's fate depends on the loser's own rank, so
conflating them would resist extension.

resolve takes the temporal candidate separately from the sanctoral list, which
makes it total by construction. Every candidate lands in exactly one of
observed, commemorations, deferred or omitted -- nothing is dropped silently,
which is what makes the no-celebration-lost invariant checkable.
</content>
</entry>
</feed>
