aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rites/rite_ef')
-rw-r--r--lib/rites/rite_ef/lectionary_ef.ml66
-rw-r--r--lib/rites/rite_ef/lectionary_ef.mli12
2 files changed, 70 insertions, 8 deletions
diff --git a/lib/rites/rite_ef/lectionary_ef.ml b/lib/rites/rite_ef/lectionary_ef.ml
index 2658cd3..329faff 100644
--- a/lib/rites/rite_ef/lectionary_ef.ml
+++ b/lib/rites/rite_ef/lectionary_ef.ml
@@ -125,8 +125,11 @@ let commons_for ~commons saint = Commons.find commons saint
ferial-Mass rubrics when coding") -- that confirmation has not been done;
do not read this comment as citing RG/the Missal for the SELECTION rule
itself, only [Lectionary.find]'s presence-or-absence as the mechanism.
- Step 4 is the one step of the four that DOES have a direct primary-source
- warrant; its own comment gives it. *)
+ Step 4 is the one step of the four that does NOT rest on that shape: it
+ is step 1's own rule continued (the observed office's readings win over
+ the day's temporal ones, wherever colitur happens to store them), and it
+ is separately corroborated by a direct Missal citation. Its own comment
+ gives both. *)
(* Days from a given weekday back to the preceding Sunday. Sunday itself
yields 0, which is why step 3 must guard on it -- see [readings] below. *)
let days_since_sunday : Date.weekday -> int = function
@@ -160,8 +163,23 @@ let readings ~lectionary ~commons ~observed ~temporal ~date ~temporal_at =
theirs -- not Septuagesima II Saturday's 2 Cor 11:19-33 / Luke
8:4-15, which is what the brief's ordering emits.
- WARRANT, and it is the strongest in this chain: the Missal itself,
- at each of these saints' own dates, names the Mass to be said --
+ WARRANT (1) -- INTERNAL, and it is the decisive one: step 4 is
+ STEP 1'S CONTINUATION, not a fifth thing bolted after the temporal
+ fallbacks. Step 1 already runs the observed office's own proper
+ ahead of steps 2 and 3; step 4 is that same rule for the saints
+ whose readings the Missal keeps in a Common instead of printing on
+ the celebration. Placing it last would have made the chain
+ internally inconsistent with code that already existed -- one saint
+ (St Joseph, test_step1_wins_over_a_competing_step2_entry) beating a
+ competing temporal entry because his readings happen to sit on his
+ Celebration.t, and another (St Vincent Ferrer) losing to one
+ because his sit one indirection away. Nothing in the rubrics draws
+ that distinction; it is an artefact of where colitur stores the
+ data. So the plan's ordering was not merely wrong on the data
+ (below) -- it contradicted step 1.
+
+ WARRANT (2) -- EXTERNAL, corroborating: the Missal itself, at each
+ of these saints' own dates, names the Mass to be said --
"Missa Cognovi, de Communi non Virginum II loco, praeter orationem
sequentem" (9 March), "Missa Os iusti, de Communi Abbatum"
(21 March), and so on. That is a direct instruction about what is
@@ -173,16 +191,48 @@ let readings ~lectionary ~commons ~observed ~temporal ~date ~temporal_at =
office read", a question that only governs when the temporal office
is the one being celebrated.
- The guard makes that precondition structural rather than a property
+ THE GUARD makes that precondition structural rather than a property
of the data file: the Commons are consulted only when the observed
celebration is not itself the day's temporal office. Without it, a
future overlay that assigned a Common to a temporal slug by mistake
would silently replace a feria's Mass; with it, ferias, Sundays, the
Triduum and the RG 78 Saturday Office of the BVM (whose observed
celebration IS its temporal office, deliberately sharing the ferial
- slug) can never be diverted here at all. [Validate] already asserts
- slug uniqueness per liturgical year, so a sanctoral feast can never
- collide with a temporal slug and be wrongly excluded by it. *)
+ slug) can never be diverted here at all.
+
+ The guard is a NO-OP on the shipped data -- no assignment in
+ data/ef/commons.sexp names a temporal slug -- which is exactly what
+ makes it defensive rather than load-bearing, and exactly why
+ nothing in the real data can exercise it. It is nevertheless
+ tested, against a SYNTHETIC [Commons.t] built by [Commons.of_tables]
+ that deliberately assigns a Common to a really-observed ferial slug:
+ test_step4_guard_refuses_a_common_assigned_to_a_ferial_slug. That
+ test fails with this guard removed; without it the guard had no
+ coverage at all (fix round 1, coordinator review -- the reviewer
+ forced [sanctoral_office = true] and the whole suite stayed green).
+
+ COULD THE GUARD WRONGLY EXCLUDE A REAL SAINT? Only if a sanctoral
+ slug ever equalled the day's own temporal slug. It cannot today --
+ every temporal slug [Temporal_ef] builds carries the "ef-" prefix
+ and 0 of the 327 entries in data/ef/sanctoral.sexp do -- but that
+ is an ASSUMPTION this codebase leans on, NOT an asserted invariant,
+ and it should not be dressed up as one. (CORRECTED, fix round 1:
+ this comment previously cited [Validate]'s slug-uniqueness check as
+ the authority. That check compares [Temporal.office] slugs to each
+ other across the days of one liturgical year -- it says nothing
+ about collision BETWEEN the temporal and sanctoral streams.)
+ [Validate] is candid about the same assumption where it makes it,
+ for a different purpose -- recovering a commemoration's origin,
+ validate.ml's own note: "This is exact whenever slugs cannot
+ collide across the two streams (Task 12's own 'observed' check
+ already assumes this for a different purpose), which is the same
+ assumption the rest of this codebase already leans on."
+
+ The failure mode if that assumption ever broke is benign and
+ one-directional: a colliding saint would be denied his Common and
+ fall through to steps 2/3, i.e. to the temporal Mass -- which is
+ precisely the answer the unguarded chain would have given him
+ anyway. No day gains a reading it should not have. *)
let sanctoral_office =
not (Slug.equal observed.Celebration.slug temporal.Temporal.office.Celebration.slug)
in
diff --git a/lib/rites/rite_ef/lectionary_ef.mli b/lib/rites/rite_ef/lectionary_ef.mli
index baf25d4..caefc74 100644
--- a/lib/rites/rite_ef/lectionary_ef.mli
+++ b/lib/rites/rite_ef/lectionary_ef.mli
@@ -53,6 +53,18 @@ module Commons : sig
assignment naming a common that does not exist (likewise). *)
val load : string -> (t, string) result
+ (** Builds a table directly, with exactly the validation {!load} applies to
+ a file (same four [Error]s, same canonical sort). Exposed so that a
+ caller can construct Commons from something other than a sexp file --
+ a future diocesan overlay, or a test that needs a table the shipped
+ data deliberately does not contain. The guard on {!readings}' step 4
+ is one such case: no shipped assignment names a temporal slug, so the
+ only way to exercise the guard at all is to build a table that does. *)
+ val of_tables :
+ commons:(Slug.t * Citation.t list) list ->
+ assigned:(Slug.t * Slug.t) list ->
+ (t, string) result
+
(** The Commons themselves, canonically sorted by id. *)
val formularies : t -> (Slug.t * Citation.t list) list