aboutsummaryrefslogtreecommitdiff

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.