diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-26 09:53:22 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-26 09:53:22 +0200 |
| commit | 8fa9db25f55f4ae92ad6a28115484dd981593a3c (patch) | |
| tree | 8cdc9f253cc1f44759fccc1ca25be7eda950a34a /lib/rites | |
| parent | 155c2e55948dc179a45458d856808f7105878d1f (diff) | |
| download | colitur-8fa9db25f55f4ae92ad6a28115484dd981593a3c.tar.gz colitur-8fa9db25f55f4ae92ad6a28115484dd981593a3c.zip | |
feat(of): assemble Rite_of.context and wire --rite of into the CLI
Task 5 of Phases 3-5 (the payoff task): bundles Tasks 1-4's separately
built pieces (Temporal_of, Precedence_of, Rubrics_of, Lectionary_of)
into a (Vocab_of.season, Vocab_of.rank) Colitur_kernel.Rite.t, and
wires colitur day/readings --rite of <year> into the CLI. Default
stays ef; every pre-existing invocation is byte-identical (asserted
directly in test/cli.t and against a real v1.0.0 build for three
years, plus git diff --stat v1.0.0..HEAD over lib/rites/rite_ef/,
lib/kernel/, data/ef/ is empty).
No kernel change was needed, confirming the spec's central claim.
season_runs needed real thought rather than the brief's own suggested
Vocab_of.seasons: Temporal_of.season's own 'first run'/'second run'
comments confirm Ordinary Time is genuinely interrupted by Lent and
Easter over one liturgical year, so the expected run-length-compressed
sequence names it twice, six runs over five seasons -- exactly the
case Rite.t.season_runs's own doc comment already names as its
illustration for why this field is rite-supplied rather than derived.
easter/fixed_key are as specified, each carrying its own citation
(Gregorian computus; the 2002 Missal's one bissextilis occurrence is a
leap-year table entry, not a kalends-doubling rule, so fixed_key is
identity).
Follows rite_ef.ml's template and its lectionary-parameter lesson
exactly: lectionary is caller-supplied, not closed over.
test/test_rite_of.ml resolves the real assembled rite (calendar-2002
.sexp + all 13 amendment overlays + lectionary.sexp) through Calendar
and Validate.run, the same discipline test_validate.ml/test_rite_ef.ml
already apply to EF. This is the first time rite_of has been checked
this way, and it surfaced two new, real, cited gaps outside this
task's own scope to fix: Temporal_of never reaches Normae n.35(a)'s
30-December Holy Family fallback in a year with no Sunday in 26-31
December (first hit 1583, the domain floor), and Temporal_of.anchors
disagrees with temporal on 24 December when that date is itself a
Sunday (Advent IV outranks the Vigil, first hit 2006). Both pinned as
known-wrong, not fixed, the same treatment the brief's own pre-known
St Joseph/Palm Sunday gap (Normae n.56(f)) gets, pinned on 2062. A
third gap was found in data/of/lectionary.sexp's own coverage (audited
against one civil year only, so the other weekday-cycle letter is
unaudited and at least one pairing is asymmetric on shipped data) --
that file is excluded from this task, so the wide 2005-2050 sweep
checks structural invariants only and leaves the citation-chain checks
to the small, individually-audited landmark-year set.
make check (COLITUR_EXHAUSTIVE_SWEEP=1): exit 0, 867 tests, ~316s.
Diffstat (limited to 'lib/rites')
| -rw-r--r-- | lib/rites/rite_of/rite_of.ml | 85 | ||||
| -rw-r--r-- | lib/rites/rite_of/rite_of.mli | 57 |
2 files changed, 142 insertions, 0 deletions
diff --git a/lib/rites/rite_of/rite_of.ml b/lib/rites/rite_of/rite_of.ml new file mode 100644 index 0000000..9eeb5ef --- /dev/null +++ b/lib/rites/rite_of/rite_of.ml @@ -0,0 +1,85 @@ +(* This module's name matches the library's own name ("rite_of"), so dune + treats it as the library's top-level module instead of generating one + automatically -- every sibling module this library defines must be + re-exported here explicitly, or external references to e.g. + [Rite_of.Temporal_of] (bin/main.ml, every test/ file that opens this + rite) stop resolving. Same property {!Rite_ef.Rite_ef}'s own top-of-file + comment records for the EF side. *) +module Vocab_of = Vocab_of +module Temporal_of = Temporal_of +module Precedence_of = Precedence_of +module Rubrics_of = Rubrics_of +module Lectionary_of = Lectionary_of + +open Colitur_kernel + +(* [~lectionary], not a value closed over an internal load: {!Rite_ef.Rite_ef + .context}'s own comment records the exact failure this shape avoids -- + fix round 1 (coordinator review) found a prior version that closed over + [Lectionary_ef] loading data/ef/lectionary.sexp as a side effect of being + linked, which made `colitur easter <year>` (a command touching no + lectionary data at all) die at startup the moment that file was absent + from a bare `dune build`. Do not repeat that here: [context] is a + function, and [~lectionary] is caller-supplied, exactly mirroring how the + sanctoral [Layer.t] itself already travels (a loose argument to + {!Colitur_kernel.Calendar.year}/[day], never embedded in this record). + + No [~commons] parameter: unlike EF, the OF lectionary chain + ({!Lectionary_of.readings}) has no Commons indirection to thread through + -- see that function's own .mli citation for why (OF has no shared- + formulary table the way EF's Commons.sexp is one). *) +let context ~lectionary : (Vocab_of.season, Vocab_of.rank) Rite.t = + { Rite.id = Temporal_of.id; + vocab = Vocab_of.vocab; + year_start = Temporal_of.year_start; + temporal = Temporal_of.temporal; + anchors = Temporal_of.anchors; + (* The Roman rite -- OF as much as EF -- reckons Easter on the Gregorian + calendar; nothing in the Normae or the IGMR revisits that. Supplied + here rather than assumed by the kernel, so a Julian-reckoning rite + can supply {!Colitur_kernel.Computus.julian_easter} instead -- the + exact reasoning {!Rite_ef.Rite_ef.context}'s own [easter] field + carries, restated here because it is a per-rite decision, not one + this module inherits from EF. Read by [Layer.index] to resolve + {!Date_spec.Easter_offset} (the two movable universal solemnities, + Sacred Heart and the Immaculate Heart of Mary, and any + [--overlay]-supplied movable local feast). *) + easter = Colitur_kernel.Computus.gregorian_easter; + (* Identity: [Some (Date.month d, Date.day d)] for every date, unlike + {!Rite_ef.Temporal_ef.bissextile_fixed_key}. The OF has NO bissextile + (leap-year kalends-doubling) convention for [Layer.on_date]'s fixed + half to correct for -- verified, not merely assumed absent: the only + occurrence of "bissextilis" anywhere in the 2002 Missal (extracted + docs/research/of/missale-romanum-2002.pdf, pdftotext -layout, line + 4502) marks leap years in the calendarium's own table of MOVABLE + feasts (an Easter-date lookup table), not a rule that doubles a + kalends position the way the 1962 Missal's February footnote does + (LT.txt:5011-5014, {!Rite_ef.Temporal_ef.bissextile_fixed_key}'s own + citation). A reader coming from the EF side of this codebase should + expect [fixed_key] to carry that mechanism; the OF's own primary + source simply has no such rule to encode, so this field is the + [Rite.t.fixed_key] doc comment's own stated default for "a rite with + no reason to differ". *) + fixed_key = (fun d -> Some (Date.month d, Date.day d)); + rules = + { Precedence.band = Precedence_of.band; + disposition = Precedence_of.disposition; + admit = Precedence_of.admit; + vigil_feast = Precedence_of.vigil_feast }; + (* NOT [Vocab_of.seasons] (which lists each season once) -- this is + exactly the case {!Colitur_kernel.Rite.t.season_runs}'s own doc + comment and {!Colitur_kernel.Vocab.t.seasons}'s both name as their + illustration of why [season_runs] is rite-supplied rather than + derived: "a rite may legitimately have one season appear in two + separate runs (the modern form's Ordinary Time does)". Confirmed + directly in {!Temporal_of.season} (Normae n. 43-44, its own "first + run"/"second run" comments): over one Advent-anchored liturgical + year, Ordinary Time is interrupted by Lent+Easter, so the + run-length-compressed sequence {!Validate}'s "seasons" check expects + has SIX runs over five seasons, not five. *) + season_runs = Vocab_of.[ Advent; Christmas; Ordinary_time; Lent; Easter; Ordinary_time ]; + transfer_target = Precedence_of.transfer_target; + readings = Lectionary_of.readings ~lectionary ~year_start:Temporal_of.year_start; + creed = Rubrics_of.creed; + gloria = Rubrics_of.gloria; + preface = Rubrics_of.preface } diff --git a/lib/rites/rite_of/rite_of.mli b/lib/rites/rite_of/rite_of.mli new file mode 100644 index 0000000..eeb38d5 --- /dev/null +++ b/lib/rites/rite_of/rite_of.mli @@ -0,0 +1,57 @@ +(** The OF (post-1970) rite, bundled: this library's top-level module (its + filename matches the library's own name "rite_of", so dune uses it as + the library's entry point directly rather than generating one -- see the + .ml's own comment). Re-exports every sibling module this library + defines, so [Rite_of.Vocab_of], [Rite_of.Temporal_of], + [Rite_of.Precedence_of], [Rite_of.Rubrics_of] and [Rite_of.Lectionary_of] + keep resolving exactly as they did before this module existed. + + Task 5 (2026-08-25-colitur-of-phases-3-5): assembles Tasks 1-4's + separately-built pieces into a {!Colitur_kernel.Rite.t}, with NO kernel + change -- the spec's own central claim for this module. *) + +module Vocab_of = Vocab_of +module Temporal_of = Temporal_of +module Precedence_of = Precedence_of +module Rubrics_of = Rubrics_of +module Lectionary_of = Lectionary_of + +(** The OF rite (design spec's [RITE] signature, realised as a + {!Colitur_kernel.Rite.t} value, the same shape {!Rite_ef.Rite_ef.context} + already establishes): + - [id], [vocab], [year_start], [temporal], [anchors]: {!Temporal_of} + unchanged (Normae n. 35-44's seasons and named movable/fixed days). + - [easter]: {!Colitur_kernel.Computus.gregorian_easter} -- the Roman + rite's own computus, supplied by the rite rather than assumed by the + kernel, so a Julian-reckoning rite can differ (see the .ml's own + citation). + - [fixed_key]: identity. The OF carries no bissextile (kalends-doubling) + convention the way the 1962 Missal does -- see the .ml's own citation + for the primary-source check this rests on. + - [rules]: {!Precedence_of}'s Tabula/Normae n. 60 functions, wrapped as + one {!Colitur_kernel.Precedence.rules} record. + - [season_runs]: NOT {!Vocab_of.seasons} -- see the .ml's own citation: + Ordinary Time runs in two separate blocks over one liturgical year + (Normae n. 43-44), so the expected run-length-compressed sequence + names it twice. + - [transfer_target]: {!Precedence_of.transfer_target}, Normae n. 60/n. 5. + Known incomplete on one named date shape (Normae n. 56(f), St + Joseph's backward anticipation onto Palm Sunday) -- documented on that + value's own .mli, not repeated or silently worked around here. + - [readings]: {!Lectionary_of.readings} partially applied to the + caller's own [~lectionary] and {!Temporal_of.year_start} (the OF + reading-cycle arithmetic's own Advent anchor) -- no [~commons] + parameter, unlike EF: the OF lectionary chain has no Commons + indirection to thread through. + - [creed], [gloria], [preface]: {!Rubrics_of}, IGMR n. 53/67-68/364-365. + + [~lectionary] is a caller-supplied parameter, not a value closed over an + internal load -- {!Rite_ef.Rite_ef.context}'s own .ml comment records + the exact startup failure this shape avoids (a command touching no + lectionary data, like `colitur easter <year>`, dying at load time the + moment the data file is merely absent from a bare build). Follow that + same seam here: a caller loads data/of/lectionary.sexp however suits it + (bin/main.ml's own loader is the one this module ships with), and a + future diocesan/proper OF lectionary overlay has the identical seam to + attach to. *) +val context : lectionary:Colitur_kernel.Lectionary.t -> (Vocab_of.season, Vocab_of.rank) Colitur_kernel.Rite.t |
