Easter and its Easter-relative movable feasts for a year: $ colitur easter 2026 easter 2026-04-05 ash-wednesday 2026-02-18 palm-sunday 2026-03-29 ascension 2026-05-14 pentecost 2026-05-24 corpus-christi 2026-06-04 A year outside the supported domain is rejected (exit 2): $ colitur easter 1000 colitur: year 1000 out of range 1583..9999 [2] No/garbage arguments give a usage error (exit 2): $ colitur colitur: usage: colitur easter | colitur temporal | colitur day | colitur readings [2] The EF temporal cycle for a year, one line per day: $ colitur temporal 2026 | head -3 2026-01-01 thursday christmastide - ef-circumcision class-1 white 2026-01-02 friday christmastide - ef-christmas-1-friday class-4 white 2026-01-03 saturday christmastide - ef-christmas-1-saturday class-4 white $ colitur temporal 2026 | wc -l 365 $ colitur temporal 2026 | grep -c '^2026-04-05 ' 1 $ colitur temporal 2026 | grep '^2026-04-05 ' 2026-04-05 sunday paschaltide 1 ef-easter-sunday class-1 white A year outside the supported domain is rejected (exit 2): $ colitur temporal 1000 colitur: year 1000 out of range 1583..9999 [2] The resolved EF calendar for a year (Task 11) -- temporal AND sanctoral, occurrence and transfers applied: one line per civil-year day, "YYYY-MM-DD weekday season week slug rank colour [+commemoration-slug]...". $ colitur day 2026 | wc -l 365 Easter is the observed day exactly once, and carries no commemoration (an impeded I class day admits at most one PRIVILEGED commemoration, RG 111, and nothing outranks Easter to be impeded by it in the first place): $ colitur day 2026 | grep -c '^2026-04-05 ' 1 $ colitur day 2026 | grep '^2026-04-05 ' 2026-04-05 sunday paschaltide 1 ef-easter-sunday class-1 white Ash Wednesday: I class (RG 91 entry 7), violet, no numbered week (it falls 4 days before Lent I's own origin -- rite_ef/temporal_ef.ml's [week]): $ colitur day 2026 | grep '^2026-02-18 ' 2026-02-18 wednesday lent - ef-ash-wednesday class-1 violet Christmas: I class, white (RG 91 entry 1): $ colitur day 2026 | grep '^2026-12-25 ' 2026-12-25 friday christmastide - ef-nativity class-1 white All Souls (2 Nov, RG 91 entry 8) end to end: 2 Nov 2025 is a Sunday (verified independently -- 1 Jan 2025 is a Wednesday, day-of-year 306, (3+305) mod 7 = 0 = Sunday), so entry 8 yields to it (still an ordinary II-class Sunday, "sunday" in season time-after-pentecost, colour green -- the week number itself is not re-asserted here, already covered by test_temporal_ef.ml); RG 95 then transfers All Souls (I class, not a Sunday, not a vigil) and RG 96 places it on 3 Nov, the next day that is not I or II class: $ colitur day 2025 | grep -c 'commemoration-of-all-souls' 1 $ colitur day 2025 | grep '^2025-11-02 ' | sed -E 's/ [0-9]+ ef-time-after-pentecost-sunday-[0-9]+ / ef-time-after-pentecost-sunday- /' 2025-11-02 sunday time-after-pentecost ef-time-after-pentecost-sunday- class-2 green $ colitur day 2025 | grep '^2025-11-03 ' 2025-11-03 monday time-after-pentecost 21 commemoration-of-all-souls class-1 black (week 21: Pentecost 2025 is 8 June (colitur easter 2025); 8 Jun - 3 Nov is 148 days, floor_div(148, 7) = 21 -- rite_ef/temporal_ef.ml's own [week] formula, hand-verified before promoting this line, not merely printed and trusted.) All Souls observed directly on 2 Nov in a year where it does not fall on a Sunday: 2 Nov 2026 is a Monday (1 Jan 2026 is a Thursday, same day-of-year 306 offset, (4+305) mod 7 = 1 = Monday): $ colitur day 2026 | grep -c 'commemoration-of-all-souls' 1 $ colitur day 2026 | grep '^2026-11-02 ' 2026-11-02 monday time-after-pentecost 23 commemoration-of-all-souls class-1 black (week 23: Pentecost 2026 is 24 May (colitur easter 2026); 24 May - 2 Nov is 162 days, floor_div(162, 7) = 23 -- same formula, same independent check.) RG 96's Annunciation exception (25 March) is CONDITIONAL, not unconditional -- fix round 1, coordinator review, register corrected 2026-08-12. In 2057, 25 March is Lent III Sunday (I class, impedes it); 26 March is an ordinary Lent feria (III class, well before Easter, 22 April 2057), so the GENERAL RG 96 target -- not the Monday after Low Sunday -- is what governs, since the general walk never crosses Easter. Before this fix the unconditional reading sent it to 30 April (Easter + 8) instead: $ colitur day 2057 | grep '^2057-03-26 ' 2057-03-26 monday lent 3 annunciation-of-the-blessed-virgin-mary class-1 white +ef-lent-3-monday $ colitur day 2057 | grep -c 'annunciation-of-the-blessed-virgin-mary' 1 A year outside the supported domain is rejected (exit 2): $ colitur day 1000 colitur: year 1000 out of range 1583..9999 [2] The reading citations for a year, one line per day: date, the observed day's slug, then the Epistle and the Gospel, " | "-delimited. A SEPARATE command rather than extra columns on `colitur day`, for a mechanical reason: 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 would leave the row unsplittable by field number. `day`'s own format is therefore byte-identical to what it was before readings existed -- asserted directly, below. $ colitur readings 2026 | head -3 2026-01-01 ef-circumcision | Titus 2:11-15 | Luke 2:21 2026-01-02 ef-christmas-1-friday | Titus 2:11-15 | Luke 2:21 2026-01-03 ef-christmas-1-saturday | Gal 4:1-7 | Luke 2:33-40 $ colitur readings 2026 | wc -l 365 2 January reads the Circumcision's own Mass, not its own: the Missal's Mass-propers rubric printed under the Sanctissimi Nominis Iesu heading -- "Diebus ferialibus a 2 ad 5 ianuarii Missa dicitur ut die 1 ianuarii, cum Gloria et praefatione de Nativitate, sine Credo et Communicantes proprio" (docs/research/scan1.txt:6523-6526, corroborated scan2.txt:7216). Note this is NOT RG 17(a), which fixes only WHEN the Holy Name is kept; the two are different rules sharing a date window. Hence the identical citation on the 1st and the 2nd above, which is correct and not a duplicated row. One line per step of the reading-resolution chain, the same four cases test_golden.ml pins against the photographic scans (each literal there was read out of the Missal before being compared with colitur -- these assert that the CLI path agrees with the library path, nothing more): $ colitur readings 2038 | grep '^2038-03-08 ' 2038-03-08 john-of-god | Ecclus 31:8-11 | Matt 22:34-46 $ colitur readings 2026 | grep '^2026-02-23 ' 2026-02-23 ef-lent-1-monday | Ezech 34:11-16 | Matt 25:31-46 $ colitur readings 2025 | grep '^2025-12-01 ' 2025-12-01 ef-advent-1-monday | Rom 13:11-14 | Luke 21:25-33 $ colitur readings 2038 | grep '^2038-03-06 ' 2038-03-06 sts-felicitas-perpetua | Ecclus 51:1-8, 12 | Matt 13:44-52 The Advent line above is the ferial resumption (chain step 3) and is the one worth reading twice: 1 December 2025 is the Monday after Advent I, and Advent's ferias -- unlike Lent's -- have no Mass of their own, so the citation shown IS Advent I Sunday's ("ad Romanos. Rom. 13, 11-14" / "dum Lucam. Luc. 21, 25-33", scan1.txt:4878 + 4912). A regression that stopped walking back would change this line and nothing else. Every day of the year carries both parts -- no "-" anywhere. This is the CLI view of the invariant Validate asserts over the whole 1583..9999 domain ("citations" / "citations-unresolved"), so it is a spot-check of one year rather than the guarantee itself: $ colitur readings 2026 | grep -c ' | - \| | -$' 0 [1] `colitur day`'s own format is unaffected by any of the above: $ colitur day 2026 | head -1 2026-01-01 thursday christmastide - ef-circumcision class-1 white A year outside the supported domain is rejected (exit 2): $ colitur readings 1000 colitur: year 1000 out of range 1583..9999 [2] An explicit COLITUR_DATA_DIR that does not hold the data is an error (exit 2), never a silent fall-back to some other copy. A packager or operator who names a directory has stated an intent; quietly calendaring off a different one because theirs was wrong is the silent substitution this project refuses everywhere else. The first version of this resolution DID fall through, and a deliberately bogus value produced a full, plausible, entirely un-flagged year: $ COLITUR_DATA_DIR=/nonexistent/xyz colitur readings 2026 colitur: COLITUR_DATA_DIR is set to /nonexistent/xyz, which contains no sanctoral.sexp colitur: refusing to fall back to another data directory -- unset it, or point it at one [2] An empty or half-populated installed directory is a different case and DOES fall through to the build tree: nobody stated an intent there, and a failed or partially removed install must not shadow a working tree and then fail later with a confusing per-file error. (Exercised directly in the task, by creating _build/default/share/colitur/ef and confirming the year still resolves; not reproduced here because the cram sandbox's own exe path makes the layout awkward to stage without asserting on dune internals.)