1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
|
(* Task 5 (2026-08-25-colitur-of-phases-3-5): [Rite_of.context], wired
together with the REAL data/of/calendar-2002.sexp + all 13 decree
overlays + data/of/lectionary.sexp, resolved through
{!Colitur_kernel.Calendar} and checked by {!Colitur_kernel.Validate.run}
-- the same pipeline `colitur day --rite of` uses, and the same
discipline test_validate.ml/test_rite_ef.ml already apply to the EF
side: exactly one observed office per day, the year covered once with no
gaps, slug-uniqueness within the liturgical year, and transfers reaching
a fixed point are all invariants {!Colitur_kernel.Validate.run} already
checks generically -- this file supplies the REAL OF rite and data and
asserts the result is clean, rather than re-deriving each invariant by
hand. *)
module Val = Colitur_kernel.Validate
module Layer = Colitur_kernel.Layer
module Overlay = Colitur_kernel.Overlay
module Cal = Colitur_kernel.Calendar
module LD = Colitur_kernel.Liturgical_day
module Slug = Colitur_kernel.Slug
module Date = Colitur_kernel.Date
module Cel = Colitur_kernel.Celebration
module P = Colitur_kernel.Precedence
module Computus = Colitur_kernel.Computus
module V = Rite_of.Vocab_of
(* Relative to this test's own build directory (_build/default/test/), same
convention test_amendments_of.ml/test_calendar_of_data.ml already use --
test/dune declares each of these as a dep of the (test ...) stanza. *)
let base_path = "../data/of/calendar-2002.sexp"
let amendments_dir = "../data/of/amendments/"
let lectionary_path = "../data/of/lectionary.sexp"
(* Decree-chronological order -- the exact list and order
test_amendments_of.ml's own [files] table already pins by SHA-256; not
re-pinning the hashes here (that is that file's own job), just applying
them in the same order so the rite under test here is the one bin/main.ml
will assemble. *)
let amendment_files =
[ "001-padre-pio.sexp"; "002-juan-diego-cuauhtlatoatzin.sexp"; "003-our-lady-of-guadalupe.sexp";
"004-john-xxiii-john-paul-ii.sexp"; "005-mary-magdalene-rank.sexp"; "006-mary-mother-of-the-church.sexp";
"007-paul-vi.sexp"; "008-our-lady-of-loreto.sexp"; "009-faustina-kowalska.sexp";
"010-narek-avila-hildegard.sexp"; "011-martha-mary-lazarus.sexp"; "012-teresa-of-calcutta.sexp";
"013-john-henry-newman.sexp" ]
let real_of_layer =
let base =
match Layer.load V.rank_of_sexp base_path with
| Ok l -> l
| Error e -> failwith (Printf.sprintf "%s: failed to load: %s" base_path e)
in
let overlays =
List.map
(fun name ->
let path = amendments_dir ^ name in
match Overlay.load V.rank_of_sexp path with
| Ok o -> o
| Error e -> failwith (Printf.sprintf "%s: failed to load: %s" path e))
amendment_files
in
let layer, diagnostics = Overlay.merge base overlays in
if diagnostics <> [] then
failwith
(Printf.sprintf "unexpected amendment diagnostics: %s"
(String.concat "; " (List.map Overlay.diagnostic_to_string diagnostics)));
layer
(* [Rite_of.context] takes [~lectionary] as a caller-supplied parameter, not
a value closed over an internal load -- see rite_of.ml's own citation for
why (the exact `colitur easter <year>` startup-death lesson
{!Rite_ef.Rite_ef.context}'s own comment records). *)
let real_of_lectionary =
match Colitur_kernel.Lectionary.load lectionary_path with
| Ok l -> l
| Error e -> failwith (Printf.sprintf "%s: failed to load: %s" lectionary_path e)
let real_of_rite = Rite_of.context ~lectionary:real_of_lectionary
let run year = Val.run real_of_rite real_of_layer ~year
(* CLOSED, fix wave I8 (final-review.md, 2026-08-25-colitur-of-phases-3-5).
This paragraph used to describe a LIVE gap: data/of/lectionary.sexp's
own provenance header named ONE informational temporal-slug gap,
[of-nativity] (25 December -- Christmas Day itself), because
niedziela.pl's own source never covered the Christmas DAY Mass at all
(only the Vigil), which made {!Colitur_kernel.Validate.run}'s
["citations-unresolved"]/["formulary"] checks fire every year, on 25
December, on real shipped data. tools/bootstrap_lectionary_of.ml's own
[hand_authored] table now injects that entry directly (Isaiah 52:7-10 /
John 1:1-18, OLM 1981's own "16 Ad Missam in die",
docs/research/of/olm-1981-ocr.txt:4513-4520) -- a primary-source
citation, not a guess, the same discipline every other hand-authored
entry in this project follows. [is_known_nativity_gap] is kept, not
deleted, but is no longer in [is_known]'s own filter below: it now
exists ONLY as an explicit, named "this must never fire again" check
({!check_year_allowing_known_gaps}'s own dedicated assertion) -- a
regression here surfaces as an ordinary unexpected-failure test error,
not a silently-absorbed known gap, which is the whole point of moving a
predicate out of the filter once its own defect closes. *)
let ends_with ~suffix s =
let ls = String.length s and lx = String.length suffix in
ls >= lx && String.sub s (ls - lx) lx = suffix
(* No [Str]/regex (deps frozen) -- a plain substring scan. *)
let contains ~substring s =
let ls = String.length s and lx = String.length substring in
let rec go i = i + lx <= ls && (String.sub s i lx = substring || go (i + 1)) in
lx = 0 || go 0
let is_known_nativity_gap (f : Val.failure) =
(f.Val.check = "citations-unresolved" || f.Val.check = "formulary") && ends_with ~suffix:"12-25" f.Val.date
(* A SECOND, NEWLY-FOUND gap this task's own wiring surfaced -- [rite_of] was
never previously wired into a real {!Colitur_kernel.Rite.t}, so
[Validate.run]'s ["anchor"] check has never before run [Temporal_of
.anchors] against [Temporal_of.temporal] for real. Normae n. 35(a)'s own
text is "Dominica infra octavam Nativitatis, VEL, EA DEFICIENTE, die 30
decembris" -- the Holy Family fallback is a FIXED-DATE fallback (30
December, whatever weekday it falls on that year), confirmed by
{!Temporal_of.holy_family}'s own [None -> mk y 12 30] branch and its own
.mli citation ("this 6-day window CAN be [empty of a Sunday] -- confirmed
by the fallback clause's own existence"). But {!Temporal_of.temporal}
only ever TESTS [same d (holy_family y)] inside its [sunday_slug d]
dispatch arm -- i.e. only when [d] genuinely IS a Sunday -- so in a year
whose 26-31 December window has NO Sunday (equivalently: 25 December,
Christmas Day, is itself a Sunday -- a 6-day window starting the day
after a Sunday omits Sunday from every other position), the fallback
date is computed correctly by [holy_family]/[anchors] but [temporal]
itself silently falls through to an ordinary ferial slug instead
(["of-christmas-0-friday"] et al.) -- exactly the "generic day slug
masking a real named feast" shape CLAUDE.md already records EF's own
Holy Family/Holy Name gaps as, before they were fixed there. Not rare:
this fires whenever Christmas Day is a Sunday, roughly one year in
seven (1583, 2005, 2011, 2016, 2022, 2033, 2039, 2044, 2050 all
independently confirmed via Python's own proleptic-Gregorian
[datetime]). OUT OF THIS TASK'S SCOPE to fix -- temporal_of.ml's own
rubric logic is Task 1's deliverable, not Task 5's, and the task brief
models exactly this "pin the known-wrong behaviour, do not fix it here"
treatment for a different, already-known gap (Normae n.56(f), St
Joseph/Palm Sunday) -- so the same discipline is applied to this
newly-found one rather than silently working around it or fixing rite
logic this task was not chartered to touch. Reported in this task's own
report as a concern. *)
let is_known_holy_family_fallback_gap (f : Val.failure) =
f.Val.check = "anchor" && contains ~substring:"of-holy-family" f.Val.detail
let check_year_allowing_known_gaps year =
let fs = run year in
(* Fix wave I8: [is_known_nativity_gap] is deliberately NOT part of
[is_known] any more -- see that predicate's own header. Only the
Holy Family fallback gap is still filtered out here. *)
let is_known f = is_known_holy_family_fallback_gap f in
let unexpected = List.filter (fun f -> not (is_known f)) fs in
(match unexpected with
| [] -> ()
| fs ->
Alcotest.failf "%d: %s" year
(String.concat "; " (List.map Val.failure_to_string (List.filteri (fun i _ -> i < 5) fs))));
Alcotest.(check int)
(Printf.sprintf "%d: no of-nativity citations-unresolved/formulary gap (fix wave I8 -- was 2)" year)
0 (List.length (List.filter is_known_nativity_gap fs))
(* Landmark years, the same choice test_validate.ml's own
[test_landmark_years] makes for EF: both domain edges plus two ordinary
years well inside it. *)
let test_landmark_years () = List.iter check_year_allowing_known_gaps [ 1583; 2026; 2035; 9998 ]
(* 9999 mirrors test_validate.ml's own [test_year_9999_does_not_raise]: the
liturgical year opening in civil year 9999 continues into out-of-domain
civil year 10000, so [Val.run]/[Calendar.year] clamp the walk to 31
December 9999 rather than raising, and the truncated season run is
*expected* to fail the "seasons" check -- pinning that this surfaces as
an ordinary failure, not an uncaught exception, and that resolution
itself ("coverage"/"lost"/"duplicated") stays clean through the clamp. *)
let test_year_9999_does_not_raise () =
let fs = run 9999 in
Alcotest.(check bool) "no coverage failures (temporal stayed total through the clamp)" true
(not (List.exists (fun f -> f.Val.check = "coverage") fs));
Alcotest.(check bool) "seasons check flags the truncated final year as incomplete" true
(List.exists (fun f -> f.Val.check = "seasons") fs)
(* A real transfer-bearing sample, the OF counterpart of test_rite_ef.ml's
own 2005-2050 sweep: every impeded Sollemnitas [Precedence_of.disposition]
defers is either placed by [transfer_target] and reaches a fixed point
(no "unconverged" failure -- Validate's own check), or is recorded
[omitted] with a reason, never silently dropped ("lost"/"duplicated"),
over the project's own 2005-2050 differential-testing window (CLAUDE.md).
STRUCTURAL checks only -- ["coverage"; "weekday"; "rank"; "colour";
"determinism"; "slugs"; "seasons"; "week"; "lost"; "duplicated";
"unconverged"; "admission"; "observed"] -- deliberately NOT the
citation-chain ones ("anchor"/"citations"/"citations-unresolved"/
"formulary"), and this exclusion is itself a finding, not a shortcut:
sweeping the full 46-year window (not just the 4 landmark years above)
surfaced that [data/of/lectionary.sexp]'s own coverage was measured
against exactly ONE civil year (2026, per that file's own provenance
header) -- OTHER years exercise the OTHER weekday-cycle letter for the
same temporal slug (OLM n.69.4, {!Lectionary_of.weekday_cycle}: which
letter applies flips on the LITURGICAL year label's parity, independent
of which civil year is being swept), and at least one such pairing is
asymmetric on real shipped data: [of-christmas-0-friday-ii] exists,
[of-christmas-0-friday-i] does not (confirmed directly against
data/of/lectionary.sexp; first reached in this sweep at 2006-12-29).
Separately, 2006-12-24 (a year Christmas Eve itself is a Sunday) also
exposed a THIRD newly-found gap: {!Temporal_of.anchors} lists
[of-nativity-vigil] on 24 December UNCONDITIONALLY, but
{!Temporal_of.named}'s own coded exception (Tabula I.2/Normae n.5:
Advent IV outranks the Vigil when they coincide) means
{!Temporal_of.temporal} does not always agree. All three are OUT OF
THIS TASK'S SCOPE to fix -- lectionary_of.* and data/of/lectionary.sexp
are excluded from this task's brief, and temporal_of.ml's own rubric
logic is Task 1's deliverable -- reported as concerns in this task's own
report rather than fixed here or silently excluded without explanation.
CORRECTED (fix-lectionary-anchors task, 2026-08-26): the of-nativity-
vigil/[anchors] disagreement described in the paragraph above is now
FIXED -- {!Temporal_of.anchors} carries the same Sunday guard [named]
already had. A dedicated exhaustive sweep
(test_temporal_of.ml's own [test_exhaustive_domain_sweep], every civil
year 1584-9997 under COLITUR_EXHAUSTIVE_SWEEP=1) confirmed this was
also the ONLY unexplained [anchors]/[temporal] drift anywhere in the
domain -- the other two items in this paragraph (the lectionary
weekday-cycle-letter asymmetry, and the Holy Family 30-December
fallback {!is_known_holy_family_fallback_gap} below already pins) are
unrelated and remain open, unchanged by this fix. *)
let sample_years =
let rec range a b = if a > b then [] else a :: range (a + 1) b in
range 2005 2050
let structural_check = function
| "anchor" | "citations" | "citations-unresolved" | "formulary" -> false
| _ -> true
let check_year_structural_only year =
match List.filter (fun f -> structural_check f.Val.check) (run year) with
| [] -> ()
| fs ->
Alcotest.failf "%d: %s" year
(String.concat "; " (List.map Val.failure_to_string (List.filteri (fun i _ -> i < 5) fs)))
let test_transfers_reach_a_fixed_point_2005_2050 () =
List.iter check_year_structural_only sample_years
let slug_of (c : V.rank Cel.t) = Slug.to_string c.Cel.slug
(* precedence_of.mli's own documented, KNOWN-UNIMPLEMENTED fourth
transfer_target rule (final review Important #5, ledger ruling R9):
Normae n. 56(f) -- St Joseph's solemnity, falling on Palm Sunday, is
ANTICIPATED BACKWARD to 18 March, not carried forward. colitur's
[transfer_target] has no backward-search branch (rite.mli's own
[transfer_target] contract requires the result be strictly LATER than
the origin -- an EF-shaped kernel obligation, per that value's own
citation), so it instead sends Joseph forward via the general rule 3,
the same target date the Annunciation would already be heading to that
same year (Easter + 8).
This is NOT fixed by this task (explicitly out of scope, per the task
brief) -- pinned here, on a REAL affected year, so the current
(known-wrong) behaviour is asserted deliberately rather than passing
silently as if it were correct. All years with Gregorian Easter = 26
March put Palm Sunday on 19 March, coinciding with Joseph; 2062 is the
nearest one after today (2026) still inside the differential-adjacent
range this project favours for worked examples.
2062 is a doubly-worked example, not chosen only for Joseph: that same
year, 25 March (the Annunciation) falls on HOLY SATURDAY (Easter - 1),
so BOTH solemnities are impeded and BOTH nominally target Easter + 8 via
the general rule -- {!Colitur_kernel.Calendar}'s own placement pass
settles that collision by landing whichever it processes first (Joseph,
departing the earlier date, 19 March) on Easter + 8 and pushing the
other to the next admissible day; empirically, on shipped data, it is
JOSEPH who is pushed one day further still, to Easter + 9 -- read as
further confirmation this is genuinely rule 3's general forward search
(sensitive to whatever else is competing that year), not the fixed,
collision-proof Easter+8 destination rule 3's cousin (the Annunciation's
own Holy-Week branch) names outright -- not as a claim about exactly
which of the two wins the tie, which this test does not depend on. *)
let test_joseph_palm_sunday_2062_known_wrong_forward_transfer () =
let easter_2062 = Computus.gregorian_easter 2062 in
Alcotest.(check string) "2062 Easter is really 26 March (the Normae n.56(f) trigger)" "2062-03-26"
(Date.to_iso8601 easter_2062);
let palm_sunday = Date.add_days easter_2062 (-7) in
Alcotest.(check string) "Palm Sunday 2062 is really 19 March, coinciding with St Joseph" "2062-03-19"
(Date.to_iso8601 palm_sunday);
let holy_saturday = Date.add_days easter_2062 (-1) in
Alcotest.(check string) "25 March 2062 is really Holy Saturday, coinciding with the Annunciation too"
"2062-03-25" (Date.to_iso8601 holy_saturday);
let days = Cal.year real_of_rite real_of_layer 2061 in
let joseph_day =
Array.to_list days |> List.find (fun d -> Date.compare d.LD.date palm_sunday = 0)
in
Alcotest.(check string) "Palm Sunday itself is observed on 19 March (Joseph impeded, correctly)"
"of-palm-sunday" (slug_of joseph_day.LD.observed);
let joseph_slug = "joseph-husband-of-the-blessed-virgin-mary" in
let departure =
List.find_opt (fun (c, _) -> slug_of c = joseph_slug) joseph_day.LD.transferred_out
in
match departure with
| None -> Alcotest.fail "expected Joseph to be transferred_out from 19 March 2062 (impeded by Palm Sunday)"
| Some (_, target) ->
(* Concrete, not merely qualitative: 2062-04-04 is the actual pinned
value on shipped data today (Easter + 9, one day past the
Annunciation's own Easter + 8 -- see this test's own header). A
future data or Calendar change that moves it is meant to be
caught here; the point being pinned is the SHAPE (forward, past
Easter, not 18 March), asserted again below in a form that does
not depend on the exact day. *)
Alcotest.(check string)
"KNOWN WRONG (Normae n.56(f) unimplemented, precedence_of.mli): Joseph lands forward, past \
Easter (currently Easter+9, one day beyond the Annunciation's own Easter+8), not the \
rubrically-correct 18 March (backward)"
"2062-04-04" (Date.to_iso8601 target);
Alcotest.(check bool) "not 18 March -- confirming this really is the anticipation defect, not a \
coincidence" false
(Date.compare target (Date.add_days palm_sunday (-1)) = 0);
Alcotest.(check bool) "and it lands on or after Easter itself (rule 3's forward search, not a \
backward anticipation)" true
(Date.compare target easter_2062 >= 0)
(* Second NEWLY-FOUND defect, pinned the same way (see
[is_known_holy_family_fallback_gap]'s own citation just above for the
full argument): 1583 is the domain floor AND a year Christmas Day falls
on a Sunday, so 26-31 December has no Sunday of its own and
[Temporal_of.holy_family] falls back to its own fixed 30 December --
correctly, per [Temporal_of.anchors] -- but [Temporal_of.temporal]
itself never reaches that fallback (its Holy-Family test lives inside
the Sunday-only dispatch arm), so 30 December 1583 is observed as an
ordinary Friday-of-Christmastide ferial office instead of the Feast of
the Holy Family. *)
let test_holy_family_fallback_1583_known_wrong_ferial () =
let christmas_1583 = Date.of_iso8601 "1583-12-25" |> Result.get_ok in
Alcotest.(check bool) "1583 Christmas Day really is a Sunday (the Normae n.35(a) trigger)" true
(Date.weekday christmas_1583 = Date.Sun);
let days = Cal.year real_of_rite real_of_layer 1583 in
let dec30 = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date (Date.add_days christmas_1583 5) = 0) in
Alcotest.(check string)
"KNOWN WRONG (Normae n.35(a) fallback unreached, see [is_known_holy_family_fallback_gap]'s own \
citation): 30 December 1583 is an ordinary ferial slug, not of-holy-family"
"of-christmas-0-friday" (slug_of dec30.LD.observed);
Alcotest.(check bool) "and that really is NOT the Holy Family (confirming this really is the defect)" false
(slug_of dec30.LD.observed = "of-holy-family")
(* Fix wave I6 (final-review.md, 2026-08-25-colitur-of-phases-3-5): the
obligatory-memorial tie data/of/amendments/006-mary-mother-of-the-church
.sexp's own header documents at length (its "*** FINDING ***" comment)
was documented but never PINNED -- the ledger's own remedy (F-MEMORIAL-TIE)
asked for both halves. Mary, Mother of the Church (Easter+50) and Anthony
of Padua (13 June, fixed) both band at Tabula III.10 (100, both universal
Memoria_obligatoria) in any year Easter+50 lands on 13 June; 2011 is the
worked example that amendment header itself names (Easter 24 April 2011,
so Easter+50 = 13 June -- re-derived here, not trusted from the comment).
KNOWN ARBITRARY, not KNOWN WRONG: unlike the two tests above, there is no
rubric this contradicts. The 2018 decree (AAS 110, 437-438) is silent on
a universal-calendar coincidence with an existing obligatory memorial, and
Normae n. 14's own third clause resolves only the AD-LIBITUM/AD-LIBITUM
case ("Si eodem die plures inscribuntur in calendario memoriae ad
libitum") -- it does not reach two OBLIGATORY memorials. So colitur's
Slug.compare tie-break ("anthony-of-padua-priest-and-doctor" sorts before
"mary-mother-of-the-church") is not this project inventing a rule; it is
the kernel's own generic, rite-agnostic fallback, applied here for lack of
a rite-specific one. This test pins that CURRENT behaviour so a future
Calendar/kernel change, a slug rename, or a future overlay cannot flip
which of the two is observed with no test going red -- the same shape of
invisible-drift risk the EF side's RG 113/[band] fidelity work (`ef-rg16a`,
register §6.1) eliminated on that side. *)
let test_mater_ecclesiae_anthony_tie_2011_known_arbitrary () =
let easter_2011 = Computus.gregorian_easter 2011 in
Alcotest.(check string) "2011 Easter is really 24 April (the Easter+50 trigger)" "2011-04-24"
(Date.to_iso8601 easter_2011);
let tie_date = Date.add_days easter_2011 50 in
Alcotest.(check string) "Easter+50 2011 is really 13 June, coinciding with Anthony of Padua's fixed date"
"2011-06-13" (Date.to_iso8601 tie_date);
(* [Cal.year]'s own [y] labels an Advent-to-Advent LITURGICAL year, not a
civil one (year_bounds = [year_start y, year_start (y+1) - 1], and
[year_start] is Advent Sunday WITHIN civil year y) -- so 13 June 2011
falls inside liturgical year 2010 (Advent 2010 .. the eve of Advent
2011), the same off-by-one every other test in this file that reaches
for a spring date already accounts for (the Joseph test above uses
2061 to reach March 2062). *)
let days = Cal.year real_of_rite real_of_layer 2010 in
let june13 = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date tie_date = 0) in
Alcotest.(check string)
"KNOWN ARBITRARY (no rubric decides this; see this test's own header): the kernel's generic \
alphabetical tie-break observes Anthony of Padua, not Mary Mother of the Church"
"anthony-of-padua-priest-and-doctor" (slug_of june13.LD.observed);
let mater_ecclesiae_omitted =
List.exists (fun (c, _) -> slug_of c = "mary-mother-of-the-church") june13.LD.omitted
in
Alcotest.(check bool)
"and Mary, Mother of the Church is recorded in `omitted`, not silently dropped -- {!Colitur_kernel \
.Liturgical_day.t.omitted}'s own contract"
true mater_ecclesiae_omitted;
Alcotest.(check bool) "she carries no commemoration either (OF's own admit always returns [])" false
(List.exists (fun (c, _) -> slug_of c = "mary-mother-of-the-church") june13.LD.commemorations)
let suite =
( "Rite_of (real data: assembled Rite.t end-to-end via Calendar/Validate)",
[ Alcotest.test_case "landmark years validate cleanly (modulo two documented gaps)" `Quick
test_landmark_years;
Alcotest.test_case "year 9999 does not raise; seasons flags the clamp, coverage stays clean" `Quick
test_year_9999_does_not_raise;
Alcotest.test_case "2005-2050: transfers reach a fixed point, nothing lost or duplicated" `Slow
test_transfers_reach_a_fixed_point_2005_2050;
Alcotest.test_case
"KNOWN OPEN DEFECT pinned, not fixed: St Joseph on Palm Sunday 2062 transfers forward, not \
backward per Normae n.56(f)" `Quick
test_joseph_palm_sunday_2062_known_wrong_forward_transfer;
Alcotest.test_case
"NEWLY-FOUND DEFECT pinned, not fixed: Holy Family's Normae n.35(a) 30-December fallback is \
unreached when Christmas Day is a Sunday (1583)" `Quick
test_holy_family_fallback_1583_known_wrong_ferial;
Alcotest.test_case
"KNOWN ARBITRARY pinned: Mary Mother of the Church vs Anthony of Padua tie, 2011-06-13 \
(Easter+50)" `Quick
test_mater_ecclesiae_anthony_tie_2011_known_arbitrary ] )
|