colitur
Download: 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 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
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
# 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:
$ 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:
$ 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):
# 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:
$ 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), v1.2.0 adds --pretty and the --month/--date/
--today narrowing flags, and v1.2.1 corrects ten OF reading references.
917 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: it reads
colitur emit --format csv on stdin and draws the month.
License
AGPL-3.0-or-later. See LICENSE.
