# colitur — working context for Claude Read this first, then skim the two authoritative docs it points to. This file orients a fresh session; the specs hold the exhaustive detail. ## What colitur is *computus liturgicus* + Latin `colitur` ("He is worshipped"). A **safe, highly-tested, deterministic OCaml engine** that computes and validates **liturgical calendars** for multiple rites and emits universal, template-driven output. It computes the **day identity** (season, week, cycle, observed celebration with rank/colour/flags, commemorations, transfers) and the day's **reading citations** (references like `Jn 3:16`, never Bible text), correct all the way to year **9999**. Starts with the Roman **EF (1962)** and **OF** forms; the architecture generalizes to any deterministic rite (Byzantine, Ambrosian, pre-Trent) as future modules. **Sibling projects** (same author, `~/git/projects/`): **lectio** (Go; the shipped OF+EF readings engine, 0-error vs references 2005–2050 — colitur bootstraps its data from lectio and uses lectio as a differential oracle), **dlectio** (offline Android app over lectio), **clectio** (tiny C build). colitur is a **standalone** tool, not part of lectio. ## The authoritative docs (read these) - **Design:** `docs/superpowers/specs/2026-07-30-colitur-design.md` — the full, approved design (scope, kernel+rites+overlays architecture, data model, output, the 5 validation layers, phasing, success criteria). **This is the contract.** - **Rules register:** `docs/research/rules-register.md` — every temporal/precedence rule the engine computes against, each citing its normative paragraph. The **EF rubrics are primary-source-verified** against the 1962 Missal (RG 91 Table of Precedence full 28 entries, occurrence RG 92–95, commemorations RG 108–111, vigils/octaves/Rogations/Sunday-classes, seasons RG 71–77). OF side cites UNLYC. - **Plans:** `docs/superpowers/plans/` — `…-plan1-computus-skeleton.md` (done). - **Primary scans:** `docs/research/*.pdf` — the 1962 Missale Romanum (Latin) and the *Rubricarum instructum* motu proprio. `docs/` is **gitignored** (research + copyrighted scans stay off the public repo). ## Binding decisions (do not relitigate) 1. **Source of truth = the 1962 Missale Romanum + its Rubricae Generales.** Divinum Officium, missalemeum, gcatholic are **comparison oracles only** — any **divergence from a reference is flagged LOUDLY** in validation, never silently swallowed. 2. **Scope is strictly the 1962 Missal** (1960 rubrics + 1955 Holy Week). Every addition (2020 *Quo Magis*/*Cum Sanctissima*, any community's proper) is an **overlay**, never core. 3. **Data is bootstrapped from lectio** (0-error vs missalemeum), then validated by **rigorous property + differential testing over a large RANDOM sample across the whole 1583–9999 range** — not only 2005–2050. 4. **EF and OF are peer rite modules** — a form is never an overlay of another form. Each has its own `temporal` + `precedence` **code** and its own **data**; they share only the kernel. 5. **Build EF end-to-end first** as the pilot vertical slice (it is the harder, more idiosyncratic form, and its rules are already fully researched), learn from it, *then* add OF as the second module to prove the `RITE` abstraction generalizes. ## Architecture (one screen) Rite-agnostic **kernel** + **rite modules** (plug in via a signature) + **data overlays**. - **Kernel** (`lib/kernel`, pure, total, bounded 1583–9999): `Computus` (Gregorian + Julian Easter + anchors), `Date` (proleptic Gregorian arithmetic), `Overlay` (ordered layer-merge algebra: field-level add/suppress/replace/edit, last-writer- wins, `empty` = identity), `Precedence` (general resolver parameterized by a rite's ruleset → observed day + commemorations + transfers; deterministic, terminating), `Calendar` (orchestrator), `Validate` (the invariant/property harness). - **Rite module** (`lib/rites/`) satisfies: ```ocaml module type RITE = sig val id : string val temporal : Date.t -> Temporal.t (* season, week, cycle, movable feasts — CODE *) val precedence : Precedence.rules (* rite ranking + resolution — CODE *) val sanctoral : Calendar.layer (* fixed-date base calendar — DATA *) val lectionary : Lectionary.t (* slug/day → citations — DATA *) end ``` Temporal + precedence are **code** (auditable, property-tested); sanctoral + lectionary are **data** (`.sexp`, bootstrapped from lectio). `Rite_ef` then `Rite_of`. - **Result type** `LiturgicalDay` = { date; rite; season; week/cycle; observed (slug, names, rank, colour, flags); commemorations; transfer; citations } — the single stable schema for all output. - **Output**: one schema → CSV / JSON / S-expression, rendered by a **logic-less Mustache-family template engine** (user supplies the target-language template; the engine never executes code). Unix-composable CLIs: `compute | render`, `table`. - **Data format**: **S-expressions** (`sexplib`/`ppx_sexp_conv`) — the OCaml type *is* the format, parse/print auto-derived, no hand-written parser. ## Validation (the "sure bet" pillar — 5 layers) 1. **Types** — illegal states unrepresentable (closed variants for ranks/colours/ seasons; dates validated at construction; resolution total). 2. **Property (QCheck), year-independent** — hold for every year 1583–9999: exactly one observed day per date; year covered once, no gaps; seasons contiguous; Easter a Sunday in [Mar 22, Apr 25]; movable feasts at correct Easter-offset weekday; overlay merge deterministic + `empty` identity; sexp round-trips. **This is how confidence extends past the oracle horizon (~2050).** 3. **Differential vs lectio** — every day 2005–2050 agrees (season, rank, observed, citations). 4. **Oracle cross-check** — vs missalemeum (EF) / litcal (OF) in lectio's `sources/`. 5. **Golden regression** — landmark + known-tricky years pinned. **Status: all five layers are built and green.** Layer 2 is exhaustively clean over all 8 417 years (`COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force`, ~50 s; the default suite samples). Layer 3 compares 16 801 days against lectio; layer 4, 730 days against missalemeum; layer 5 pins ~30 dates. **Know what each layer cannot see** — this is load-bearing, not a caveat: - Layer 3 **never compares commemorations** (lectio has no RG 111 admission logic, so its "others" are losing candidates, not the admitted set) and never compares the week column. It also **shares colitur's own lineage**: colitur's data was bootstrapped from lectio, so an error both inherited is invisible here. Proven: Holy Thursday was violet in both because both were wrong. - **Nothing anywhere compares commemoration ORDER** (found `ef-holyname-rg110` task, fix round 1, RG 110's own shape-(c) ordering bug): layer 3 doesn't compare commemorations at all (above); layer 4's own `identity_diff` sorts both sides into a multiset before comparing; layer 5's own `describe` (test_golden.ml) sorts its `comms` field too, deliberately, so a golden pin's string comparison is not accidentally order-sensitive where nothing textual requires it to be. The ONLY place in the whole suite that asserts commemoration order is `test_precedence_ef.ml`'s own `admit_cases` table (`Alcotest.(check (list string))`, unsorted) — proven by mutation: reverting RG 110(c)'s own trigger/companion order left every layer green except that one table. - **Layer 4's OBSERVED-identity gap is CLOSED** (2026-08-13, branch `ef-rg112-rg110`, register §6.2): it used to compare the observed day's rank and colour and stop there — never whether it is actually the RIGHT day. Holy Family (RG 17(b), missing from colitur entirely until this task) was rank 2/white on both sides purely by coincidence (an ordinary, unnamed Sunday and Holy Family share both), so this layer stayed silently green through the whole gap's lifetime. Now compares `Celebration.t.names` against missalemeum's own title, same mapping/limits as the commemoration-identity fix below: resolvable only for a SANCTORAL-origin observed day; a TEMPORAL-origin one (an ordinary Sunday, a feria, a movable named feast — 373 of 730 days in the 2026–2027 window) is `Observed_identity_unresolved`, counted and allow-listed (`M18`), never silently skipped. Full breakdown: 331 of 730 days resolved (330 matching, 1 mismatched — Joseph vs the Seven Sorrows, `M13`), 399 unresolved (373 in `M18`, 26 absorbed inside four other entries' own widened subsets). The blind spot, precisely: a TEMPORAL-origin observed day silently replaced by a DIFFERENT temporal-origin observed day of the SAME rank and colour — exactly Holy Family's own shape. Teeth proved and reverted: corrupting one sanctoral saint's own English name on an OBSERVED day (rank/colour untouched) reddened the suite immediately with an `observed-identity-mismatch` and nothing else — the exact shape the pre-strengthening comparator would have slept through completely. - **Layer 4's commemoration-identity gap is CLOSED** (2026-08-12, Task B, branch `ef-rg16a`, two fix rounds): it used to compare presence/count only, never *which* commemoration won — reversing `admit`'s dignity sort (the engine admitting the *worst* commemoration, an outright RG 111 violation) left all eight differential and oracle assertions green across 17 531 days. Layer 4 now also compares **identity** — colitur's own resolved English name (`Celebration.t.names`, `en`) against missalemeum's title text, for every day both streams admit the same count. The mapping resolves **every SANCTORAL-origin commemoration** (colitur's own name field, bootstrapped from lectio, verified to match missalemeum's titles character-for-character); it **cannot resolve a TEMPORAL-origin one** (an impeded feria/Ember/Rogation day — `Rite_ef.Temporal_ef` never sets a celebration name) — that case is **never silently skipped**: it is a separate, counted, allow-listed outcome (`Comm_identity_unresolved`, 19 of 227 non-empty-commemoration days over 2026–2027), not a silent pass. ONE genuine identity mismatch remains adjudicated open (register §4/§6.1, `data/ef/expected-divergences-missalemeum.sexp` M16): a known unimplemented office (the Seven Sorrows of Passion Friday). Proof of teeth, reproduced twice: reversing `admit`'s *dignity*-based sort (the historical defect shape) turns layer 4 red — an unexplained `commemoration-identity-mismatch` day (colitur admitting "St. Thecla" where both the rubric and missalemeum require "St. Linus"); separately, disabling `band`'s own `Commemoration_only` guard (below) also turns it red, on the same date this whole gap was originally found through — both reverted after confirming. - **Step 4 of the reading chain (the Common route) — CLOSED** (register §6.9, `ef-oracle-2038`, 2026-08-17). Layer 4 gained a SECOND fixture, 2038 (365 days, live-captured, its own provenance/SHA/suite, deliberately kept apart from the 2026–2027 one because the live endpoint has drifted from lectio's archived snapshot). 2038 is the only year in 2005–2050 covering two Common-routed saints as the observed office. 338/365 match; 27 differ; all 27 in already-adjudicated classes; zero unexplained — an independent year re-confirming rulings made in a different one. Proved by re-running §6.7's own mutation: corrupting a Common citation now reddens four tests, two of them external-oracle, where it previously reddened none of them. Residual: `isidore-of-seville` still unwitnessed (needs 2035/2046), and `gregory-the-great`/`patrick` are never observed in 2005–2050 at all, so no fixture in that range can reach them. **The original gap, for the record** (register §6.7, `ef-lectionary` fix round 2). Only five saints route through `data/ef/commons.sexp`, and across all of 2005–2050 they are the *observed* office on five days total — `isidore-of-seville` 2008/2035/2046, `frances-rome` and `sts-felicitas-perpetua` both 2038, while `gregory-the-great` and `patrick` are **never** observed in 46 years (both sit in March, impeded by Lent's privileged ferias every year). None falls in layer 4's 2026–2027 window, so no Common-routed citation has ever been compared against an external source. Layer 3 does not fill the gap either, and the distinction is exact: those days are in its range, but lectio resolves the literal `-`/`-` sentinel there (that is *why* C18 exists and what it is gated on), so layer 3 confirms only that colitur produces *a* citation where lectio produces none — never that it is the right one. Step 4 rests on the scan-verified Common assignments and unit tests alone. Closing it means extending the oracle fixture to 2035, 2038 or 2046 (2038 covers two of the five at once) — a fixture-scope decision, deliberately not taken. - **Major Litanies (RG 80/109(f), `ef-major-litanies` task): layer 3 is entirely BLIND to a commemoration-only entity, confirmed not merely argued** — lectio computes no Major Litanies at all, and its own `row` type carries no commemorations field in the first place (limit 1, same file). `data/ef/expected-divergences.sexp` needed no change; a whole new privileged commemoration, present or absent, present-but-displacing-a- saint, or transferring to a different date entirely, is genuinely invisible to that layer. Layer 4 (missalemeum) sees the entity and the RG 111(b) question (both years in its 2026-2027 window), but is ALSO blind to the transfer specifically, because neither year in that window is a trigger year — confirmed by mutation (disabling the transfer branch produced zero oracle failures), not merely by the calendar coincidence. Only golden pins see the transfer at all. Full account: `.superpowers/sdd/2026-08-12-colitur-rg16a/major-litanies-report.md`. - **The `admit` same-rank tie-break is RG 113, not an uncited convention** (same task, fix round 1): RG 113's own second sentence ("in admittendis et ordinandis aliis commemorationibus, servetur ordo tabellae praecedentiae"), previously quoted only in its first half, is the real rule — `admit` now orders/selects by `Precedence_ef.band` (RG 91's own table), not RG 8's coarse four-class rank. **`band` itself had a fidelity bug this exercise surfaced**: RG 91's table enumerates only "dies liturgici" (real feasts), so a `Celebration.t.status = Commemoration_only` candidate has NO row in it at all — `band` used to read `rank` alone and silently lent such a candidate the same table entry as a genuine `Feast` of its own rank, manufacturing ties RG 113 never created (the original "Maurice vs Thomas of Villanova, both entry 24" example was this bug, not a real RG 91 tie). Fixed at the source: `band` now returns `unclassified` for any `Commemoration_only` candidate, checked first. Measured, independently, twice (`compare_precedence`'s own ordering-criterion change, then `band`'s fidelity fix): the ORDERING-CRITERION change alone is zero-blast-radius (byte-identical across the whole 1583–9999 domain — a correctness-of-citation fix, not an answer-changing one); the `band`-FIDELITY fix has a real, large, fully-classified effect, **4 451 days across the whole domain, exactly 4 verified shapes, no surprises** (register §6.1). A genuine "two different candidates on the identical REAL table entry" residual was searched for exhaustively across the whole domain and found EMPTY — the tie-break `admit` still breaks alphabetically is real but narrower than first thought: it is only ever exercised between two `Commemoration_only` candidates, neither of which has any RG 91 table position to compare in the first place. ## Current state (Plans 1–3 + the EF lectionary DONE — verify with `git log`) **Plans 1 + 2 are on `main` (35 commits). Plan 3 and its follow-on fix/feature tasks (RG 16(a), Holy Family/RG 112(a), Holy Name/RG 110, the Sacred Triduum, the BVM Saturday Office, the Major Litanies) have landed on a chain of feature branches since — test count keeps climbing task by task (374 tests green, 375 with the exhaustive sweep, as of `ef-oracle-2038`; this line is not kept in lockstep with every task, `git log`/`dune test` are the actual source of truth).** The kernel, the **complete EF temporal cycle**, the **resolution engine**, the **sanctoral data**, and **all five validation layers** are built. `colitur day ` emits a full resolved year. **Kernel** (`lib/kernel`, pure, total, 1583–9999): - `date.ml[i]` — opaque rata-die (Hinnant civil↔days); validated `make`; `to_iso8601`/`of_iso8601`; sexp form is an ISO-8601 atom that revalidates. - `computus.ml[i]` — `gregorian_easter`, `julian_easter`, Easter anchors. - Shared vocabulary: `colour` · `subject` (Lord/BVM/saint/temporal; named `Subject` because `class` is an OCaml keyword) · `slug` · `lang` · `names` (open lang-keyed assoc, canonically sorted) · `citation` · `date_spec`. - Rite-parametric: `vocab` (operations record) · `celebration` · `temporal` (+ the `RITE` module type). **`Celebration.t` takes one parameter (`'r`)**, not two — season is contextual to the day, not intrinsic to a celebration. - `layer` (slug-canonical, date-indexed once) · `overlay` (add/suppress/replace/ field-edit, ordered, last-writer-wins, **diagnostics not silence or failure**) · `record` (flat all-string output view) · `validate` (the invariant harness). - **Plan 3 additions**: `precedence` (the rite-parameterised resolver — a rite supplies `band` / `disposition` / `admit`) · `liturgical_day` (the result schema) · `rite` (everything a rite supplies, bundled, so mismatched assembly is unrepresentable) · `calendar` (**year is the primitive**, day derived — transfers need whole-year knowledge, so per-date resolution cannot be correct). **EF rite module** (`lib/rites/rite_ef`): `vocab_ef` (8 RG-cited seasons, 4 classes) · `temporal_ef` (season boundaries RG 71–77, named feasts including Holy Family (RG 17(b)), Sunday slugs, week numbering, the resumed-Sunday tail, ferias, four Ember sets, Rogations, `anchors`) · `precedence_ef` (the full RG 91 28-entry table including entry 14's movable/fixed split, occurrence RG 92–95, commemorations RG 108–112, transfers RG 96–98) · `rite_ef` (the bundle). **Data**: `data/ef/sanctoral.sexp` (322 entries, bootstrapped from lectio, SHA-256 in its provenance header) · `data/ef/adjustments.sexp` (overlay — `Add` as well as `Suppress`/`Edit`: RG 110's own 30 June companion, `commemoration-of-st-peter`, is genuinely missing from lectio's own source, not merely from colitur's bootstrap, so it is hand-authored here rather than upstream; `Add major-litanies`, `ef-major-litanies` task, RG 80/81, same reasoning) · two cited allow-lists, `expected-divergences.sexp` (7 active entries, vs lectio — C1, C6, C8, C14, C15, C16, C17; several more closed and recorded in the register, not deleted — untouched by the Major Litanies, layer 3 is blind to that entity, see above) and `expected-divergences-missalemeum.sexp` (12 active, vs the oracle — M2 closed/M18 widened by the `ef-bvm-saturday` task; M12 closed/M19 opened by an earlier one; M5 corrected (a prior note had 2027's own outcome backwards) and M20 added by the `ef-major-litanies` task, M18 394 not 395 accordingly). Fixtures live in `test/fixtures/` with asserted SHA-256s. **CLI**: `colitur easter `, `temporal `, `day `, `readings `. `readings` is a **separate command, not extra columns on `day`**, for a mechanical reason worth not rediscovering: a citation contains spaces and commas (`Ezech 34:11-16`, `Ecclus 51:1-8, 12`) while a `day` row is space-separated with a variable-length `+slug` commemoration tail, so appending them there leaves the row unsplittable by field number. `day`'s format is therefore **byte-identical** to what it was before the lectionary existed (asserted in `test/cli.t`). Both are a **stopgap**, not the project's answer to output: the design still calls for one schema rendered through a logic-less template engine — two ad-hoc formats are easier to retire than one overloaded format with unwritten parsing rules. `band` is **provably total** over everything the engine constructs: zero `unclassified` across all 8 417 years, for a 28-branch hand-transcribed table. Transfers reach a fixed point everywhere — 6 739 out, 6 739 in, zero unconverged. Deps are `dune alcotest qcheck qcheck-alcotest sexplib ppx_sexp_conv` and are **frozen**. A Mustache lib is still **not** added — it arrives with rendering. **Gotcha that costs an hour if unknown:** `[@@deriving sexp]` on a type with primitive fields fails with `Unbound value string_of_sexp` unless the `.ml` opens `Sexplib0.Sexp_conv`. Every kernel module with primitive fields does. Argument-less variants (`Colour`, `Subject`) don't need it. Do **not** hand-write converters instead — that is reserved for `Slug`/`Lang`, whose `private string` smart constructors deriving would bypass. ### Build & test ```sh eval $(opam env) # activate the project-local switch (run from this dir) dune build dune test # fast suite, ~3 s COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force # + every year 1583-9999, ~50 s dune exec colitur -- day 2026 | head ``` ## What's next - **The EF lectionary is DONE** (branch `ef-lectionary`, 20 commits): the four- step reading-resolution chain, its data (`data/ef/lectionary.sexp`, `commons.sexp`, sanctoral propers), all five validation layers extended to citations, and `colitur readings`. Layer 2 asserts that **every day of every year 1583–9999 resolves exactly one Epistle and one Gospel** — measured, and mutation-proved live rather than silently inert. **Chants (Psalm, Second, Tract, Alleluia, Sequence) remain deliberately unbuilt**: no source, no oracle, and `Validate`'s own `citations` check now *rejects* any part outside First/Gospel, so one appearing would be a defect rather than a feature arriving early. - **Plan 4 — OF rite module** (proves `RITE` generalizes) → full output/ rendering → hardening and a first tag. (The lectionary bootstrap and citations this line used to defer to Plan 4 landed early, on `ef-lectionary`; what remains here is OF's own lectionary, not the mechanism, which is now built and rite-agnostic.) **All four behaviour items below are now RESOLVED** (RG 16(a) and commemoration identity, closed on branch `ef-rg16a`; Holy Family/RG 112(a) and observed identity, closed on branch `ef-rg112-rg110`; Holy Name of Jesus/RG 110, closed on branch `ef-holyname-rg110`) — kept here as the record of what the five layers, taken together, used to sleep through, and as the shape a future gap of the same kind would need to be caught by. ### Carried into Plan 4 (read before starting) The full record — every task's outcome, every ruling, the 21-item deferred-minor triage, and the whole-branch review — is in `.superpowers/sdd/2026-08-11-colitur-plan3-resolution-engine/progress.md`. That workspace is deliberately kept, because it and the register corrections exist nowhere in git (`docs/` is gitignored). **The four behaviour items, in order:** 1. **RG 16(a) — RESOLVED (RG16(a) task, branch `ef-rg16a`, 2026-08-12; ONE FIX ROUND of review after the first pass — see register §6.0 for the full, corrected account).** Was the largest known-wrong output on the branch: a Feast of the Lord occurring on a II-class Sunday takes the Sunday's place *"cum omnibus iuribus et privilegiis: de dominica, proinde, **nulla fit commemoratio**"*, and colitur used to commemorate the Sunday anyway (**5 996 wrong days over 1583–9999**, 369 of them in 1583–2100, re-confirmed exactly, twice, independently). Fixed in `Rite_ef.Precedence_ef.disposition` with **no signature change** — `disposition` already took `winner:...` (RG 33's own vigil-omission branch already read it). A SECOND, related bug needed a genuine kernel signature change: `Precedence.rules.admit` gained a `~temporal` parameter, because RG 16(a) also breaks the assumption that `observed` IS the day's own temporal-cycle office for RG 111(b)'s Sunday rank-floor check (an unrelated saint could otherwise be wrongly admitted into the freed slot — confirmed on 1 178 real days, 6 August, before this second fix). **The sanctoral data question was more contested than the first pass found**: the Purification (2 Feb) was FIRST retagged `Bvm` (calendarium title argument), then REVERTED to `Lord` in fix round 1 on the user's own ruling — follow the oracle, which treats the Purification as taking an occurring Sunday's place outright, unlike an ordinary Marian feast (real primary-text counter-evidence, RG 120(b)'s colour rule, remains on record as the argument the other way). Only `most-holy-name-of-mary` stays retagged `Bvm`. A related, unresolved primary-source finding: the Common of the Dedication of a Church's own classification (*"Festum Dedicationis Ecclesiae est festum Domini"*) means St Michael's Dedication (29 Sep) may also be `Lord`, not `Saint` — measured, not applied (1 200 days domain-wide if it were). Two further open items were recorded here, not fixed at the time: 13 January (Baptism of the Lord, mistagged `Saint`) and RG 112, unimplemented. **Both are now RESOLVED — see item 3 below.** (The `Saint` mistag turned out to be independently fixed by the `ef-rebootstrap` re-bootstrap, upstream of item 3's own task; RG 112 is item 3's own work.) 2. **Commemoration identity — RESOLVED (Task B, branch `ef-rg16a`, 2026-08-12; ONE FIX ROUND of review after the first pass — see register §6.1 for the full, corrected account).** Was unasserted outside ~3 test rows — **the exact gap the RG 16(a) fix round above had exploited**: the lectio differential (layer 3) compares season/slug/rank/colour only, never commemorations, BY DESIGN (lectio has no RG 111 admission logic of its own) and still does not — that part of this item is unchanged and remains the reason layer 4, not layer 3, had to close this gap. Layer 4 (missalemeum, 2026–2027) now compares commemoration IDENTITY, not only presence/count (see the "know what each layer cannot see" section above for the mapping and its limits). While building it, found and fixed a SECOND, independent bug the exercise surfaced: `Precedence_ef.band` gave a `Commemoration_only` candidate the same RG 91 table entry as a genuine `Feast` of its own rank (RG 91's table has no row for a bare commemoration at all) — **4 451 days wrong across the whole 1583–9999 domain**, exactly 4 verified shapes, fixed at the source. The `admit` same-rank tie-break itself is RG 113 (previously uncited), not the alphabetical convention this item used to describe — reconciled against the Plan-3-era "66 days" figure: 599 is the tie POPULATION, 65 (or 67) the real ADMITTED-SET decisions within it, 149 order-only — all now independently reproduced (register §6.1), not merely asserted. 3. **Holy Family (RG 17(b)) + RG 112(a) + layer 4's observed-identity gap — RESOLVED (2026-08-13, branch `ef-rg112-rg110`; see register §6.2 for the full account).** Holy Family did not exist anywhere in colitur — a `grep` found no trace in `lib/` or `data/`, and the day it should have observed emitted an ordinary Sunday instead, undetected because **layer 4 compared the observed day's rank and colour, never its identity** (Holy Family is rank 2/white on both sides purely by coincidence — see the "know what each layer cannot see" section above, closed first, as the regression net, before any production code changed). Built: `Temporal_ef.temporal`'s existing Sunday-fallback branch already computed the right slug/rank/ colour for 7-13 January by coincidence; the only silently-wrong field was `subject` (always `Temporal`), now `Lord` on `holy_family_sunday y` (`RG 91 entry 14`, "primum mobilia, deinde fixa") alone. RG 17(b)'s own window can never be empty of a Sunday (unlike RG 17(a)'s Holy Name, which carries an explicit calendarium fallback for its own narrower window) — checked, not assumed; no fallback built. `Precedence_ef.band` gained a movable-half priority for entry 14 — without it, Holy Family would tie with the fixed Commemoration of the Baptism of the Lord (13 January) and lose the kernel's alphabetical tie-break, backwards from RG 91's own stated order. **The whole table is now scaled ×10** (entry *n* → 10*n*), so a half-row is expressed as an ordinary position between its neighbours. *(CORRECTED: this paragraph previously described `entry_14_movable_band` as "negative so it can never collide with a real table position". That was the bug, not the design — a negative sentinel avoids **collision** but also inverts **ordering**, making a movable II-class feast of the Lord outrank every I-class day. Unreachable on universal data; live the moment a diocesan overlay puts a I-class proper or indult feast, RG 91 entries 12–13, in the 7–13 January window. Do not re-derive the sentinel approach.)* `disposition` gained RG 112(a) (a mystery of one Divine Person excludes a commemoration of another mystery of the SAME Divine Person). The primary authority is **RG 95 ¶2** — *"Si vero duo festa eiusdem Divinæ Personæ… fit de festo, quod in tabella præcedentiæ superiorem obtinet locum, et aliud omittitur"* — an occurrence-level rule present in all three documents; RG 112(a) and the Holy Family Mass propers' own 13-January rubric corroborate it. *(CORRECTED: this previously called the propers' rubric a further instance of the transcription defect. It is not. The transcription carries RG 112 in full; RG 112 has no worked example in either scan; and the propers' note is absent because that document is a partial 2006 web capture containing almost no propers text — one `Introitus` in 26 322 lines against 52 in a scan. Diagnose the cause of a silence before invoking the rule.)* **Blast radius, measured (`git archive` pre-change binary vs HEAD, full 1583–9999 sweep, diffed): 1 220 days, every single one the identical shape, cross-verified against `date -d` independently (exactly 1 220 years have 13 January on a Sunday) — no anomaly, nothing outside what was expected.** The differential's own C1 (the 6-13 January blanket) lost exactly those 7 (of the 1 220) rows within its 2005–2050 window and they were split into their own new cited entry (C15), not silently re-absorbed — the same discipline the task brief demanded. Not built at the time: RG 110 (inseparable Peter/Paul, still open, M12) — out of this task's own dispatched scope despite the branch name. Holy Name of Jesus (RG 17(a)) had the identical "generic-Sunday-slug masking a real named feast" shape Holy Family had, PLUS a second, more severe gap (no RG 17(a) fallback for its own 2–5 January window when empty of a Sunday — 3 619 of 8 417 domain years). **Both are now RESOLVED — see item 4 below.** 4. **Holy Name of Jesus (RG 17(a)) + RG 110 — RESOLVED (2026-08-13, branch `ef-holyname-rg110`; see register §6.3 for the full account).** Holy Name gained the `subject = Lord` tag Holy Family already had (RG 91 entry 14), plus a genuinely new office: RG 17(a)'s own fallback, *"secus die 2 ianuarii"* — 2 January carries the feast whenever no Sunday falls 2–5 January that year, tagged and ranked identically to the Sunday shape (ONE feast, per the Mass propers' own single heading covering both dates, both scans). Before this fix colitur emitted no Holy Name office at all in 3 619 of 8 417 domain years — a genuine missing II-class feast, not merely an unnamed one. A real asymmetry the fix's own synthetic precedence tests found and kept honest rather than forced: a losing Holy-Name-SUNDAY is RG 109(a)-privileged and survives RG 111(a)'s cap; a losing Holy-Name-FALLBACK is not (2 January genuinely is not a Sunday, no other RG 109 category names it) — both correct readings of RG 109/111's own closed lists, no live witness for either today. RG 110 (*"In Officio et Missa S. Petri semper fit commemoratio S. Pauli, et vicissim... pro unica habeantur"*) gained a THIRD sub-clause this register had not transcribed before, (c) — the same inseparable commemoration also fires when one Apostle is admitted merely AS a commemoration, not only when he is the day's own office. Three real pairs in the 1962 calendar (25 January, 22 February, 30 June); the third had no companion candidate anywhere — a genuine gap in lectio's own source data AND in missalemeum's own oracle output, not only a colitur bootstrap miss — closed via `data/ef/adjustments.sexp`'s own `Add` directive. Built in `Precedence_ef.admit` (`rg110_additions`), layered on AFTER `admit`'s own four RG 111 branches decide their ordinarily-capped result, uncapped and additional, never competing for a slot. **Blast radius, measured (`git archive` pre-change binary vs HEAD, full 1583–9999 sweep, diffed): 14 627 days, ALL FOUR predicted shapes, zero unclassified** — 3 619 the Holy Name fallback itself; 3 533 Paul wrongly excluded from Chair of St Peter's own day by a competing privileged feria (RG 110 shapes (a)/(b) — supersedes an earlier 852-day estimate that measured only a delta between two older commits, not the rule's full scope); 593 Chair of St Peter admitted only as an ordinary commemoration, Paul entirely absent (shape (c) — a real 7% of the domain, not a corner case); 6 882 the new 30-June companion. The lectio differential needed no RG 110 change at all (it does not compare commemorations); the oracle allow-list needed M12 removed, M15 widened by one date (a newly-exposed instance of its own pre-existing limit), and a new M19 for the 30-June gap. **CORRECTED, fix round 1: RG 110(c)'s own ordering was inverted on all 593 shape-(c) days.** *"Huic orationi additur altera"* (the companion is added TO the trigger's own oration) means the trigger comes first, the companion follows — the original build prepended the companion uniformly in both shapes, right for (a)/(b) (nothing in the list to order against) but backwards for (c). Fixed in `rg110_additions` (splice the companion in immediately after its own trigger for shape (c) only); the wrong-order unit test is re-pinned. New, permanent blind spot found and recorded: **nothing in this suite compares commemoration order except that one unit table** — see "know what each layer cannot see" above. M19's own predicate was also strengthened to check commemoration IDENTITY, not merely presence (the same C6/C14 failure mode, proven by fabricating a second companion and watching the whole suite stay green pre-fix). 6. **`Record` and `Liturgical_day` both claim to be "the single stable output schema".** `Record` cannot express what the engine now computes (no observed celebration, no commemorations, no transfers), has **no test file**, and is used only by the legacy `colitur temporal` path — `day` hand-formats instead. Plan 2's carried item (add a `cycle` field for OF's Sunday A/B/C and weekday I/II) is still open and now costlier: it must pass through `Temporal.t`, which is embedded in the sexp-derived `Liturgical_day.t`. 7. **`Temporal.RITE` and `Rite.t` are two competing abstractions.** The module type still exists and `temporal_ef` still satisfies it, but it carries none of `rules`, `anchors`, `season_runs`, `transfer_target` — satisfying it now proves almost nothing. `Rite.t` is the load-bearing one. 8. **EF-shaped things still in "rite-agnostic" kernel code**: `validate.ml` hardcodes Sunday as the week start; `Liturgical_day.transferred_in` is an `option` justified by RG 96; `Precedence.privilege` is defined by RG 111; `Repose` is EF vocabulary emitted by nothing. Each is one field short of the remedy already applied to `season_runs`. **Data defects traced upstream into lectio's generator** (register §6): 15 entries wrongly marked `Commemoration_only` that are really III-class feasts, clustered 6 March – 5 April, **six of which produce a wrong observed office** in real years (2008-04-02/04/05, 2038-03-06/08/09); four missing entries (Agnes *secundo*, Boniface 14 May, Evaristus, Theodore); and `romanus`, which should not exist on 9 August. lectio's ini is **generated from missalemeum**, so the two are one lineage, not two independent sources. **Unbuilt, recorded**: RG 112(b)/(c)/(d, non-BVM half) (only (a) and (d)'s BVM half have a live witness this codebase's data can construct). Allow-list entries M11 and M13 are `verdict open` by design. Holy Name of Jesus (RG 17(a)) and RG 110 (inseparable Peter/Paul commemorations) are **RESOLVED — see item 4 above.** The Sacred Triduum's own identity is **RESOLVED — see item 5 below.** RG 91 entry 27's BVM Saturday Office is **RESOLVED — see item 9 below.** The Major Litanies (25 April, RG 80/109(f)) are **RESOLVED — see item 5 below.** Rogation Wednesday's own commemoration (RG 87-89) **remains genuinely unbuilt** — see item 5 below: the Major Litanies' own "no third channel" blocker turned out to be dissolved by REUSING the existing RG 96 transfer machinery rather than by adding the missing channel, but that reuse is not available to Rogation Wednesday, whose trigger is not a fixed civil date at all (it is the day's own temporal identity, Easter+38, which coincides structurally with the Ascension Vigil) — confirmed still blocked for the original, distinct architectural reason. 5. **The Sacred Triduum (RG 91 entry 2) — RESOLVED (2026-08-13, `ef-triduum-litanies` task); Major Litanies (RG 80/81/109(f)) — RESOLVED (2026-08-13, `ef-major-litanies` task); Rogation Wednesday's own commemoration (RG 87-89) remains genuinely unbuilt.** Holy Thursday/Good Friday/Holy Saturday kept their existing slugs (`ef-passiontide-2-{thursday,friday,saturday}` — RG 91 entry 2 is identified structurally by `Precedence_ef.band`, off rank and Easter offset, never off the slug) and gained `Celebration.names` (Latin, both photographic scans, corroborated by the electronic transcription's own table-of-contents listing at the identical headings: "Feria V in Cena Domini", "Feria VI in Passione et Morte Domini", "Sabbato Sancto") and `subject = Lord` (verified safe: RG 112(a) only fires when both sides of an occurrence are `Lord`, and no `Lord`-subject sanctoral entry has a fixed date inside Holy Week's own movable range). **Full 1583-9999 blast radius, measured (`git archive` pre- vs post-change, `colitur day` CLI output diffed): ZERO differing lines anywhere in the domain** — the CLI prints neither `names` nor `subject`, so this whole change is invisible to the differential (layer 3), the oracle (layer 4), and even the exhaustive property sweep (layer 2); only `test_golden.ml`'s `describe` (widened this task to add a `name_la` field, the same lesson its own `subject` field was added for) and one new `test_temporal_ef.ml` unit test see it at all. Mutation-tested: reverting `temporal_ef.ml` alone reddens 6 tests across those two files. **Major Litanies (25 April, RG 80/81/109(f)) — RESOLVED (2026-08-13, `ef-major-litanies` task).** The "no channel for a movable, Easter-relative commemoration candidate" blocker this entry previously recorded (and the item-5 header used to describe as blocking BOTH the Litanies and Rogation Wednesday) turned out to be dissolved by a DIFFERENT design, not by adding the missing channel: RG 80's own transfer is structurally the SAME operation RG 96 already performs for an impeded I-class feast (a losing candidate relocated to a named later date), so it is built by REUSING `Precedence.disposition`'s existing `Transfer` constructor and `Calendar`'s existing placement machinery, with a fixed target (Easter+2) instead of a searched one — no new `Date_spec` variant, no third candidate stream. Entity: `Commemoration_only`, `Fixed(4,25)`, `data/ef/adjustments.sexp`'s `Add major-litanies`. **The genuine reason to defer, correctly identified by an earlier fix-round review** (25 April is St Mark, II class, so RG 111(b) — not (c), a citation this task corrected — makes a privileged Litanies commemoration DISPLACE Mark's own ordinary one whenever both compete) **is now measured and adjudicated**: full domain blast radius (1583-9999, zero unclassified findings) is 7 394 years the Litanies simply appear, 829 years they displace Mark (4 of them, 2010/2021/ 2027/2032, in the 2005-2050 window), 194 origin departures + 194 target arrivals for the transfer (the same 194 figure this entry already had, now independently re-derived through the real `Calendar`/`Precedence` pipeline). The Sunday-displacement question itself (RG 111(b): does a privileged commemoration categorically override an ordinary II-class one, or does missalemeum's own divergent data mean otherwise?) is ADJUDICATED colitur, honestly flagged as the first real (non-synthetic) test of that specific admit clause — see the "know what each layer cannot see" section above and the task's own full report for the reasoning and the correction this task made to a PRE-EXISTING allow-list note (M5) that had 2027's own oracle outcome backwards. A genuine kernel bug was found and fixed along the way, kept rite-agnostic: `calendar.ml`'s `build_day` used to decide "did a transfer settle" by checking ONLY whether the candidate became `observed` at its target — impossible by construction for a `Commemoration_only` candidate (RG 81), caught by `prop_invariants`' SAMPLED 200-year property (the default `dune test` run), NOT the committed exhaustive sweep (which walks in order and would have found it deterministically at year 1638, not the later, seed-dependent year the sample happened to draw) — attribution corrected in fix round 1 (F4). Fix round 1 also found and closed a THIRD settlement channel `settled_at` still missed (a transferred candidate capped out by admission limits AT its own target, F1) — unreachable on shipped data, caught by the same sampled property while mutation-testing the RG 109(f) privilege. Full account: `.superpowers/sdd/2026-08-12-colitur-rg16a/major- litanies-report.md`. **Rogation Wednesday remains genuinely blocked**, and for the ORIGINAL architectural reason, now confirmed distinct from the Litanies' own (dissolved) one: its trigger is not a fixed civil date at all, but the day's OWN temporal identity (Easter+38), which coincides structurally with the Ascension Vigil — there is no `(month, day)` pair a `Fixed` spec could ever anchor to, so the Litanies' own "reuse the RG 96 transfer machinery" trick does not apply here (nothing is being transferred FROM a civil date; the commemoration would have to be synthesised from the day's own Easter offset, which still has no channel). No partial build exists. 9. **RG 91 entry 27, the votive Office of the BVM on Saturday — RESOLVED (2026-08-13, `ef-bvm-saturday` task).** `Precedence_ef.band` already routed a plain IV-class Saturday feria to entry 27's own band value (312 966 times domain-wide), but `Temporal_ef.temporal` never constructed the office itself. Caput IX of the Rubricae Generales, both photographic scans and the electronic transcription, word for word (no scan-vs-transcription conflict — RG 78/79 are General Rubrics prose, not the Mass-propers body text the transcription is missing): *"78. In sabbatis, in quibus occurrit Officium de feria IV classis, fit de sancta Maria in sabbato."* RG 78's own protasis IS "otherwise unoccupied IV-class Saturday" — decided entirely by the existing occurrence machinery (`band`'s own entry-27 branch already reads `rank = Class4 && weekday = Sat` unconditionally and only wins when nothing outranks it), so **`band` and `admit` needed no change** — rank stays Class4 either way, and neither reads slug/colour/name to decide anything (`disposition` DID need one, in a fix round — see below, RG 112(d)). Colour is white, unconditionally — the tighter chain, found in a fix round (RG 431(e), the Missal's own classification of this exact Mass as a "Missa votiva IV classis... de B. Maria Virg.", → RG 121(a), votive Masses take the colour of the feast-type they "respondent" → RG 120(b), BVM feasts are white — see below), not RG 120(b) alone (a stretch: this Office is not itself a *festum*, RG 120(b)'s own "de festis") and never RG 119/127/128's seasonal rules either way. The slug is deliberately UNCHANGED (reused from the ordinary `--` ferial fallback), the same precedent the Sacred Triduum (item 5) already set — identified structurally, never off the slug — and for a second, load- bearing reason found while building it: a bespoke uniform slug would have broken `Validate`'s own slug-uniqueness-per-liturgical-year invariant (asserted with zero exceptions since Plan 2), since the office recurs many times a year. Subject is tagged `Bvm` (a real `Subject.t` variant that existed, unused, since the kernel's vocabulary was designed — no kernel change needed) and the name is Latin only ("Officium sanctae Mariae in sabbato", RG 91 entry 27's own table title and RG 79's own heading), the same zero-circularity discipline items 3–5 already established. The I–V numbered "Missae de sancta Maria in sabbato" (both scans) are a Mass-propers selection detail (RG 309(a): *"iuxta temporum diversitatem"*) governing which readings are said, not which office is kept — out of scope until Plan 4's lectionary. **Blast radius, measured (`git archive` pre-change binary vs HEAD, full 1583–9999 `Calendar`-resolved sweep, diffed): 75 853 days, every single one the identical single-field shape (`colour` alone, always on a Saturday, always `class-4` on both sides) — no anomaly.** Reconciled against the full 312 930-day eligible population on the same sweep domain: 102 144 are actually observed (the office wins), split by season — 63 195 Time after Pentecost + 7 643 Septuagesima + 5 015 Time after Epiphany (75 853 total, all VISIBLE, green/violet → white) and 14 213 Paschaltide + 12 078 Christmastide (26 291, all INVISIBLE — RG 119 already made those seasons white); the remaining 210 786 are impeded by a real sanctoral winner and genuinely unaffected. Both allow-lists moved: the lectio differential gained a new cited entry (C17, colitur, 416 rows — lectio builds no equivalent office); the missalemeum oracle's own M2 (previously `verdict missalemeum`, "colitur is missing a whole office") is **CLOSED, REMOVED** — the colour divergence it named no longer occurs on any of its 22 dates, and what is left (`Observed_identity_unresolved` alone, the office is temporal- origin and deliberately unnamed in English) is exactly `M18`'s own shape, not a distinct citation any more; `M18` widens 373 → 395 accordingly. Mutation-tested: reverting the office to a constant-false guard reddens the dedicated unit test, an end-to-end resolve test, a golden pin, and both allow-list count pins (in both directions — the fix present with M2 still declared fails identically to the fix absent with M2 removed), while a second end-to-end test/golden pin (the office losing to a real competing feast, 12 September, the one live data witness that also carries `subject = Bvm`) is correctly untouched by the mutation, proving RG 26's rank-keyed omission fires before any subject-keyed rule ever could. **Fix round 1 (2026-08-13, coordinator review) — one blocking finding, fixed.** RG 112(d) (Caput XVI): the Office, itself "de B. Maria Virg.", excludes another commemoration invoking the SAME BVM's intercession — violated live: `our-lady-of-mt-carmel` (16 July, `Commemoration_only`) was wrongly commemorated on every 16-July-Saturday. Fixed in `Precedence_ef.disposition` (`marian_slugs` ∪ `subject = Bvm` on both sides of the collision — the disjunction matters, neither signal alone identifies both real sides). Checked exhaustively, not merely for Mt Carmel: only `Commemoration_only`-status Marian entries can ever reach this live, and the ONLY other one, `our-lady-of-ransom` (24 September), is PROVABLY unreachable (forces the September Ember Saturday every time, by construction). Re-measured: the original 75,853-day blast radius is unchanged in total, splitting into 74,633 `colour`-only + 1,220 `colour+comms`. Mutation-tested: exactly 3 tests redden. Six further ride-along findings closed the same round: a test pin corrected (26 December can never hold the office, St Stephen always wins there — 3 January used instead); C17 gained a `subject`- based identity guard; `band`'s own entry-27 comment gained its RG 78 citation; a tighter RG 431(e) → RG 121(a) → RG 120(b) colour chain (conclusion unchanged); and the oracle's own coverage recorded precisely — of 26 office days in the 2026-2027 fixture only 17 are colour-discriminating, and **Time after Epiphany (5,015 observed days domain-wide) has ZERO oracle witnesses**, resting on the scan and the dedicated unit test alone. Full account: `.superpowers/sdd/2026-08-12-colitur-rg16a/bvm-saturday-report.md`. ## How to work here - **Superpowers workflow**: `brainstorming` → `writing-plans` → `executing-plans` or `subagent-driven-development`. Present a design and get approval before coding. Plans live in `docs/superpowers/plans/`, specs in `docs/superpowers/specs/`. - **TDD**, bite-sized tasks, a commit per task on the feature branch (never straight to `main` without consent). - **Every temporal/precedence rule carries a source citation** (RG/UNLYC paragraph) in a comment — grep-able, matching the register. - **Kernel is total & deterministic**: no wall-clock, randomness, or environment reads; fallible construction returns `result`/`option`, never raises on in-range input; years outside 1583–9999 rejected at the boundary. - **Commits**: conventional-commit style, subject + body only. **No AI/tool trailer of any kind** (the author considers them noise in a public repo). - **License header**: files may carry a short SPDX `AGPL-3.0-or-later` line. - `docs/` is gitignored — design/research/scans stay local; only code + README + LICENSE + this file are tracked.