diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/rites/rite_ef/lectionary_ef.ml | 66 | ||||
| -rw-r--r-- | lib/rites/rite_ef/lectionary_ef.mli | 12 |
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 |
