# colitur **Download:** [colitur-latest.tar.gz](https://git.labunix.xyz/colitur-latest.tar.gz) — a stable URL that always serves the current release, so a script can fetch it without knowing the version. Per-tag tarballs are on the [tags page](https://git.labunix.xyz/colitur.git/refs/tags) if you want a specific one. *computus liturgicus* — a safe, highly-tested, deterministic engine that computes and validates liturgical calendars, with template-driven output. Computes day-identity (season, celebration, rank, colour, precedence) and reading citations, correct to year 9999. **Two rites are built: the Roman EF (1962) and the Roman OF (post-1970, base calendar the 2002 *Missale Romanum*).** `lib/rites/` contains `rite_ef` and `rite_of`, both plugging into the same rite-agnostic kernel via `Rite.t` — the abstraction the OF module exists to prove generalises. Select the rite with `--rite ef|of` on `day`/`readings` (default `ef`); see `colitur.1`'s own `--rite` entry. The EF is the more thoroughly validated of the two, checked against five independent layers including a published Ordo witness; the OF is younger — no published-ordo witness yet, a Polish-vernacular lectionary lineage (not the Latin OLM itself), a handful of known-wrong days pinned rather than fixed (St Joseph anticipated onto Palm Sunday, Normae n. 56(f); the Holy Family Sunday fallback of Normae n. 35(a)), and its lectionary carries only the first reading and the Gospel, not the Second Reading or the responsorial psalm. See the design and rules research under `docs/` (local). ## Build ```sh opam switch create . 5.2.0 -y # first time: local OCaml switch opam install -y dune alcotest qcheck qcheck-alcotest sexplib ppx_sexp_conv dune build dune test # fast suite, 909 tests (~40s) COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force # + every year 1583-9999 (~8min) dune exec colitur -- easter 2026 # Easter and its Easter-relative anchors dune exec colitur -- temporal 2026 # the EF temporal cycle only, one line per day dune exec colitur -- day 2026 # the full resolved EF calendar (temporal + sanctoral) dune exec colitur -- day --rite of 2026 # the same, for the OF ``` ## Rendering ```sh # The ordo booklet has a table of contents (page numbers via \pageref), so # it needs TWO pdflatex passes -- the first pass leaves every entry showing # "??"; the second resolves them. A single pass is not a bug in the # template, it is just an unfinished build. Either form works: dune exec colitur -- table --year 2027 --template templates/ef/ordo.tex > ordo.tex && pdflatex ordo.tex && pdflatex ordo.tex # or: latexmk -pdf ordo.tex # The wall calendar has no cross-references, so one pass is enough: dune exec colitur -- table --year 2027 --template templates/ef/grid.tex > grid.tex && pdflatex grid.tex dune exec colitur -- publish --from 2027 --to 2027 --out ./public ``` See `colitur-templates(5)` for the template format (syntax, escaping, the full field reference) and `colitur(1)` for `emit`, `table`/`render` and `publish` in full. ## Language Every command's output is names, not slugs, by default (`--raw` restores the bare-slug form). Start a new translation from the shipped Latin table, edit a couple of entries, check it, and render with it -- every line below is real, not transcribed: ```sh $ dune exec colitur -- lang --dump la > my-lang.ini $ sed -i \ -e 's/^ef-circumcision = .*/ef-circumcision = My Own Circumcision Text/' \ -e 's/^ef-epiphany = .*/ef-epiphany = My Own Epiphany Text/' \ my-lang.ini $ dune exec colitur -- lang --check my-lang.ini my-lang.ini: 725 of 725 celebrations named, 0 missing, 0 unknown $ dune exec colitur -- day 2026 --lang ./my-lang.ini | head -1 2026-01-01 thursday christmastide - ef-circumcision class-1 white My Own Circumcision Text ``` `--check` reports what a language file is still missing, and rejects any entry naming a slug that does not exist (a typo, otherwise silently dead). `colitur lang --list` shows what this build can find; `colitur config --show` shows the effective `--lang` (and every other setting) plus where it came from. See `colitur(1)`'s `NAMING` section and `colitur-config(5)` for both in full. ## Citations A reading citation (`Jn 3:16`) is parsed into structure and re-rendered, so its book names, its punctuation/abbreviation convention, and its numbering tradition are each a file to edit, not something the engine hardcodes. One day, four ways -- every line below is real, not transcribed: ```sh $ dune exec colitur -- readings 2026 | grep ^2026-02-25 2026-02-25 ef-lent-ember-wed | 3 Reg 19:3-8 | Matth 12:38-50 | Feria IV Quatuor Temporum Quadragesimae $ dune exec colitur -- readings 2026 --sigla-book full | grep ^2026-02-25 2026-02-25 ef-lent-ember-wed | Liber Regum III 19:3-8 | Evangelium secundum Matthaeum 12:38-50 | Feria IV Quatuor Temporum Quadragesimae $ dune exec colitur -- readings 2026 --lang en | grep ^2026-02-25 2026-02-25 ef-lent-ember-wed | 3 Kgs. 19:3-8 | Matt 12:38-50 | Lenten Ember Wednesday $ dune exec colitur -- readings 2026 --lang en --sigla-tradition modern | grep ^2026-02-25 2026-02-25 ef-lent-ember-wed | 1 Kgs 19:3-8 | Matt 12:38-50 | Lenten Ember Wednesday ``` The same four results, from a config file instead of flags (each block is the entire `[defaults]` section on its own -- not one file with all four): ```ini # 1. Latin abbreviated -- the built-in default, no entries needed [defaults] # 2. Latin full [defaults] sigla_book = full # 3. English (Vulgate numbering: "3 Kings", not "1 Kings") [defaults] lang = en # 4. English, modern numbering [defaults] lang = en sigla_tradition = modern ``` Two axes are independent and easy to conflate. `--lang`/`sigla_book` (and a language file's own `[bible]` section) decide what a book is *called* -- this varies by language. `--sigla-tradition` (`lang/traditions.ini`) decides which book a reference *denotes* -- this does not vary by language. `3 Kings 19:3-8` and `1 Kings 19:3-8` above are the *same* verse, renumbered, not two different readings; conflating naming with renumbering is how a citation ends up naming the wrong book. `--raw` bypasses all of it: every citation is emitted exactly as stored, byte-for-byte, independently of the parser -- what a script diffing this engine's output against the sibling `lectio` engine depends on: ```sh $ dune exec colitur -- readings 2026 --raw | grep ^2026-02-25 2026-02-25 ef-lent-ember-wed | 3 Kgs. 19:3-8 | Matt 12:38-50 ``` See `colitur(1)`'s `SIGLA` section for the `[bible]`/`[sigla]` language-file sections, `lang/traditions.ini` in full, and all three `--sigla-*` flags. ## Status Releases through `v1.0.0` are EF only; `v1.1.0` added the OF module as a peer rite (see above), and `v1.2.0` adds `--pretty` and the `--month`/ `--date`/`--today` narrowing flags. **916 tests**, of which the property suite runs against every year in 1583-9999 rather than a sample — that is how confidence extends past the ~2050 horizon of any reference to check against. The six layers below describe the EF's own validation specifically; the OF's own (also property, differential — against `litcal`, an independent second lineage — and golden) is younger and lighter, with no published-ordo witness yet. Six validation layers, each blind to something the others catch (EF): | | | |---|---| | types | illegal states unrepresentable; resolution total | | properties | invariants over all 8 417 years (QCheck) | | differential | 16 801 days compared against the sibling `lectio` engine | | oracle | three captured years compared against missalemeum | | ordo | one liturgical year compared against a printed Latin Mass Society Ordo | | golden | 11 pinned template outputs | Knowing what each layer *cannot* see is load-bearing here: the differential and the missalemeum oracle share one data lineage (Divinum Officium → missalemeum → lectio → colitur), so an error inherited by both is invisible to either. The Ordo layer is a genuinely separate lineage — a clergy-compiled book, never passed through that chain — but has its own blind spots: it covers one civil year, only three of the reading-formulary's five sources (a documented data-representation ambiguity excludes the rest), and England & Wales diocesan propers are excluded from every comparison, universal-calendar days only. That is written up in full in `docs/` rather than left implied. **Chants are deliberately absent.** Psalm, Tract, Alleluia and Sequence have no source and no oracle, so `Validate` rejects any citation part outside First/Gospel — one appearing would be a defect, not a feature arriving early. A companion X11 viewer lives in [colitur-x](https://git.labunix.xyz/colitur-x.git/): it reads `colitur emit --format csv` on stdin and draws the month. ## License AGPL-3.0-or-later. See `LICENSE`.