# 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. **What is built today is the Roman EF (1962) — that rite alone.** `lib/rites/` contains `rite_ef` and nothing else. The kernel is rite-agnostic and the OF is the next module, which is what will prove the abstraction generalises; until then, treat any claim of multi-rite support as a design goal rather than a feature. 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, 568 tests (~8s) COLITUR_EXHAUSTIVE_SWEEP=1 dune test --force # + every year 1583-9999 (~80s) 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) ``` ## 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 v0.9.0, EF only. **569 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. Five validation layers, each blind to something the others catch: | | | |---|---| | 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 | | golden | 11 pinned template outputs | Knowing what each layer *cannot* see is load-bearing here: the differential and the oracle share one data lineage (Divinum Officium → missalemeum → lectio → colitur), so an error inherited by both is invisible to either. 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`.