summaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef/precedence_ef.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-21 15:07:44 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-21 15:07:44 +0200
commit35c11c7ccf95acfb35a7ec34edfa86d92da60e4a (patch)
tree63e2e0c8f9c6317f68a52961c7770f72c3ba79c0 /lib/rites/rite_ef/precedence_ef.ml
parent0eae32f67145065b0e4960650ae06c293fd9f272 (diff)
parentf3ac69d0a679200a48716be065a0fa2e70a2c6da (diff)
downloadcolitur-35c11c7ccf95acfb35a7ec34edfa86d92da60e4a.tar.gz
colitur-35c11c7ccf95acfb35a7ec34edfa86d92da60e4a.zip
merge: RG 33's third omission trigger
The last unbuilt clause in the EF module. A II/III-class vigil is omitted entirely when the feast it precedes fails to keep its own day -- 1 744 days across 1583-9999, three verified shapes, zero unexplained. Rule and vigil inventory independently confirmed by O'Connell 1964, outside the Divinum Officium -> missalemeum -> lectio lineage. The 10 affected days in 2005-2050 are allow-listed as C39; no external day-level witness exists, which the note states rather than glosses.
Diffstat (limited to 'lib/rites/rite_ef/precedence_ef.ml')
-rw-r--r--lib/rites/rite_ef/precedence_ef.ml55
1 files changed, 55 insertions, 0 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml
index b69ee52..36aea83 100644
--- a/lib/rites/rite_ef/precedence_ef.ml
+++ b/lib/rites/rite_ef/precedence_ef.ml
@@ -600,6 +600,61 @@ let is_bvm_office (c : Vocab_ef.rank Precedence.candidate) =
(ยง6) rather than guessed. *)
let is_omissible_vigil (rank : Vocab_ef.rank) = rank = Vocab_ef.Class2 || rank = Vocab_ef.Class3
+(* RG 33's THIRD omission trigger, the clause [is_omissible_vigil]'s own
+ comment above records as unimplemented: "vel si festum cui praemittitur in
+ alium diem transferri aut ad commemorationem reduci contingat" -- "or if
+ the feast it precedes happens to be transferred to another day or reduced
+ to a commemoration".
+
+ Each pair is (vigil slug, the slug of the feast it precedes). RG 34 fixes
+ the feast on the day AFTER the vigil ("Vigiliae... celebrantur die
+ praecedenti festum"), so the kernel needs no date here, only the identity
+ -- and it must be given the identity rather than deriving it, because
+ these slugs do not derive from one another: two of the five share a stem
+ with their feast and three do not (see {!Precedence.rules.vigil_feast}).
+
+ The five are exhaustive for the 1962 universal calendar. The four
+ sanctoral ones are the only entries in data/ef/sanctoral.sexp matching
+ [is_vigil]; the fifth is temporal_ef's own Ascension vigil. The two
+ I-class vigils (Nativity Eve, Pentecost Vigil) are deliberately ABSENT:
+ RG 33 governs only "Vigilia II aut III classis", and RG 30 puts a I-class
+ vigil beyond losing in the first place -- the same argument
+ [is_omissible_vigil] already makes for the other two triggers.
+
+ A user overlay adding a diocesan vigil is NOT covered by this table and
+ its vigil will not be omitted. That is a data limit, not an architectural
+ one -- the kernel hook takes any candidate -- and it is stated here rather
+ than papered over with a slug-shape heuristic that would be wrong for
+ three of the five universal cases it can already be checked against. *)
+(* Built with [Slug.of_string_exn], deliberately, and at module initialisation
+ rather than per call. A malformed literal here is a PROGRAMMING error in a
+ static table, not untrusted input, and the alternative found by mutation
+ testing is worse than a crash: with [Slug.of_string] and [Result.to_option]
+ a typo (an uppercase letter is enough -- slugs are lowercase-only) collapses
+ to [None], which this function's own contract reads as "not a vigil", and
+ the whole rule switches itself off for that entry in silence. That failure
+ was reproduced: mutating "lawrence" to "lawrence-WRONG" reddened FOUR tests,
+ all of them the ones that notice the rule missing, and none that notice it
+ pointing at the wrong feast -- indistinguishable from deleting the row.
+ The same mutation with a well-formed "lawrence-wrong" reddens eighteen.
+ Raising at startup keeps a typo loud; [Rite_ef]'s own bundle is constructed
+ at initialisation too, so the failure surfaces before any calendar is
+ resolved. *)
+let vigil_feast_table =
+ List.map
+ (fun (vigil, feast) -> (vigil, Slug.of_string_exn feast))
+ [ ("ef-ascension-vigil", "ef-ascension");
+ ("vigil-of-the-nativity-of-st-john-the-baptist", "nativity-of-st-john-the-baptist");
+ ("vigil-of-sts-peter-paul", "sts-peter-paul");
+ ("vigil-of-st-lawrence", "lawrence");
+ ("vigil-of-the-assumption", "assumption-of-the-blessed-virgin-mary") ]
+
+let vigil_feast (c : Vocab_ef.rank Precedence.candidate) : Slug.t option =
+ let cel = c.Precedence.cel in
+ if not (is_omissible_vigil cel.Celebration.rank) then None
+ else List.assoc_opt (Slug.to_string cel.Celebration.slug) vigil_feast_table
+
+
(* Every Sunday slug this rite's temporal cycle produces -- named
(temporal_ef.ml's [named], e.g. "ef-easter-sunday") or the generic
"ef-<season>-sunday-<n>" fallback ([sunday_slug]) -- contains this