aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_of/rite_of.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-26 09:53:22 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-26 09:53:22 +0200
commit8fa9db25f55f4ae92ad6a28115484dd981593a3c (patch)
tree8cdc9f253cc1f44759fccc1ca25be7eda950a34a /lib/rites/rite_of/rite_of.ml
parent155c2e55948dc179a45458d856808f7105878d1f (diff)
downloadcolitur-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/rite_of/rite_of.ml')
-rw-r--r--lib/rites/rite_of/rite_of.ml85
1 files changed, 85 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 }