diff options
Diffstat (limited to 'lib/rites')
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 120 | ||||
| -rw-r--r-- | lib/rites/rite_ef/temporal_ef.ml | 37 |
2 files changed, 118 insertions, 39 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index c488596..cd0c53b 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -1125,37 +1125,73 @@ let rg110_find_companion comms companion_slug = (* RG 110's own uncapped addition, applied to [normal] -- the day's ALREADY- COMPUTED, ORDINARILY-CAPPED admission result (every branch of {!admit} below, unchanged otherwise). Two triggers, covering RG 110(a)/(b) and - RG 110(c) respectively, in this function's own comment above: + RG 110(c) respectively, in this function's own comment above -- and, + fix round 1, TWO DIFFERENT ORDERINGS, not one, because the two shapes' + own primary texts say different things about where the companion goes: + - [observed] itself is one of the three trigger slugs (shape (a)/(b): Peter's or Paul's own Office/Mass IS today's day) -- the companion is - looked up directly and appended, uncapped. + looked up directly and PREPENDED to [normal]. (a)/(b)'s own text, + "oratio alterius Apostoli additur ... orationi diei" (the OTHER + Apostle's oration is added ... TO THE DAY'S OWN oration), has nothing + in [normal] to order the companion AGAINST in the first place: the + day's own oration is [observed], not a member of this list, so + leading the list is simply where an item with nothing to be "before" + or "after" inside it goes -- not itself a citation for a POSITION + the primary text does not describe a list position for at all. - one of [normal]'s OWN members is a trigger slug (shape (c): Peter's or Paul's own office lost the day outright but is itself being admitted - as an ordinary/privileged commemoration of whatever else won) -- same - lookup, same append. - Both can never fire together on one call ({!Precedence.resolve} only - ever calls {!Precedence.rules.admit} once per date, and all three - trigger slugs are distinct FIXED calendar dates -- 25 January, 22 - February, 30 June -- so at most one of the two conditions is ever true - for a given [observed]/[normal] pair; [List.filter_map] over both - sources together is simply the shortest way to express "check every - place a trigger slug could legitimately appear", not evidence they are - expected to co-occur). + as an ordinary/privileged commemoration of whatever else won) -- the + companion is looked up the same way, but SPLICED IN IMMEDIATELY AFTER + its own trigger, not prepended to the whole list. RG 110(c)'s own + text, word for word: "quoties vero oratio unius Apostoli addenda est + ad modum commemorationis, HUIC ORATIONI ADDITUR ALTERA immediate, ante + omnes alias commemorationes" -- "huic orationi" (dative, "to THIS + oration") refers back to "oratio unius Apostoli" (the trigger's OWN + oration, the one just named as "addenda... ad modum commemorationis") + -- so "additur altera" (the OTHER is added) means the companion is + added TO the trigger's own oration, i.e. FOLLOWS it, not precedes it; + the pair as a BLOCK then precedes "omnes ALIAS commemorationes" + ("alias" = OTHER, unrelated ones -- cannot include the pair's own + first member). CORRECTED, fix round 1: the original version of this + function prepended in BOTH shapes uniformly, citing (c)'s own "ante + omnes alias" for shape (a)/(b) too by analogy -- which is fine for + WHERE the pair sits relative to unrelated commemorations, but wrongly + also reordered the PAIR'S OWN internal order in shape (c), producing + `paul, chair-of-st-peter` where the primary text requires + `chair-of-st-peter, paul` (verified against BOTH photographic scans, + word for word, no divergence to adjudicate). Confirmed live: every one + of the 593 shape-(c) days measured in this task's own report emitted + the companion FIRST before this fix; RG 111's own admission branches + never build a second, unrelated commemoration alongside a shape-(c) + trigger in today's data (RG 111(b) admits exactly one candidate on a + II-class Sunday, the only day shape (c) ever occurs), so this + splice-after-trigger behaviour is Total the same way {!take}/ + {!drop_band} are, but has no OTHER live witness to also prove the + "ante omnes alias" half of (c) against. - PREPENDED, not appended, per RG 110(c)'s own explicit ordering directive - ("ante omnes alias commemorationes") -- the one clause in this rule that - actually specifies WHERE, restated here for shape (a)/(b) too rather than - drawing an unwarranted distinction between the two: no live data in this - codebase ever has a THIRD, unrelated commemoration alongside a RG 110 - pair to make the ordering choice visible in real output (the companion is - always the only OTHER item, or the only item, in [normal] already), so - this is closer to a defensive convention than a tested outcome -- flagged - honestly rather than claimed as independently verified. + Both shapes can never fire together on one call ({!Precedence.resolve} + only ever calls {!Precedence.rules.admit} once per date, and all three + trigger slugs are distinct FIXED calendar dates -- 25 January, 22 + February, 30 June -- so at most one of [observed]/[normal]'s own members + is ever a trigger for a given call). A companion already present in [normal] on its own merits (structurally unreachable today -- {!unclassified}'s own comment above -- but not provably so for every future rite/data shape) is not added a second - time: [List.exists] guards each append. *) + time: [List.exists] guards both branches. + + NEW BLIND SPOT, fix round 1: no validation layer in this codebase + compares commemoration ORDER at all -- the lectio differential does not + compare commemorations (its own "limit 1"); the oracle's own + [identity_diff] sorts both sides into a multiset before comparing; + {!Colitur_kernel.Record}/`describe` (test_golden.ml) both sort their own + comms field too, for the same reason presence/identity checks should not + be order-sensitive by accident. This function's own [admit_cases] unit + test (test_precedence_ef.ml) is therefore the ONLY place in this + codebase's test suite where commemoration order is asserted at all -- + flagged here, and in CLAUDE.md's own "know what each layer cannot see" + section, as a genuine, permanent limit, not merely this fix's own gap. *) let rg110_additions (comms : (Vocab_ef.rank Precedence.candidate * Precedence.privilege * int) list) ~(observed : Vocab_ef.rank Precedence.candidate) (normal : (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list) : @@ -1163,20 +1199,28 @@ let rg110_additions (comms : (Vocab_ef.rank Precedence.candidate * Precedence.pr let already_has slug = List.exists (fun (c, _) -> Slug.to_string c.Precedence.cel.Celebration.slug = slug) normal in - let trigger_slugs = - Slug.to_string observed.Precedence.cel.Celebration.slug - :: List.map (fun (c, _) -> Slug.to_string c.Precedence.cel.Celebration.slug) normal - in - let companions = - List.filter_map - (fun trigger_slug -> - match rg110_companion_slug trigger_slug with - | None -> None - | Some companion_slug -> - if already_has companion_slug then None else rg110_find_companion comms companion_slug) - trigger_slugs - in - companions @ normal + match rg110_companion_slug (Slug.to_string observed.Precedence.cel.Celebration.slug) with + | Some companion_slug when not (already_has companion_slug) -> ( + (* Shape (a)/(b): prepend -- see this function's own comment above. *) + match rg110_find_companion comms companion_slug with + | Some companion -> companion :: normal + | None -> normal) + | _ -> + (* Shape (c): splice each trigger's companion in immediately after it + -- {!List.concat_map} rather than a fold, so a trigger with no + companion found (defensive; {!rg110_find_companion} returning + [None]) simply passes through unchanged, and a member that is not + a trigger at all ([rg110_companion_slug] returns [None]) is never + touched. *) + List.concat_map + (fun ((c, _) as pair) -> + match rg110_companion_slug (Slug.to_string c.Precedence.cel.Celebration.slug) with + | Some companion_slug when not (already_has companion_slug) -> ( + match rg110_find_companion comms companion_slug with + | Some companion -> [ pair; companion ] + | None -> [ pair ]) + | _ -> [ pair ]) + normal let admit ~(observed : Vocab_ef.rank Precedence.candidate) ~(temporal : Vocab_ef.rank Precedence.candidate) @@ -1227,7 +1271,7 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) due; at most one privileged one does, the highest-precedence one (RG 113: {!band}'s own table order) if several are. *) (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ drop_band best ]) - | Class2, true -> + | Class2, true -> (* RG 111(b), primary text, RE-VERIFIED word for word against the scan (final fix wave; this sentence is the sole textual basis for the shipped rank-floor fix below, and the register's own ยง4 "RG 111" @@ -1268,7 +1312,7 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) match List.filter (fun (c, _, _) -> c.Precedence.cel.Celebration.rank = Class2) sorted with | [] -> [] | best :: _ -> [ drop_band best ])) - | Class2, false -> + | Class2, false -> (* RG 111: "other II class: one" -- no privilege-override clause here, unlike the Sunday case immediately above, so the day's one slot goes to whichever candidate outranks the rest by RG 113's own diff --git a/lib/rites/rite_ef/temporal_ef.ml b/lib/rites/rite_ef/temporal_ef.ml index 721790d..bbd88ec 100644 --- a/lib/rites/rite_ef/temporal_ef.ml +++ b/lib/rites/rite_ef/temporal_ef.ml @@ -598,7 +598,42 @@ let temporal d = check this way, against the SAME independently-computed date [holy_name_sunday] returns rather than a bare month/ day range, is what test_temporal_ef.ml's own anchor test - below cross-checks against. *) + below cross-checks against. + + ADDED, fix round 1 (coordinator finding F4): the Sunday + shape's "takes the Sunday's own place, no commemoration of + it" treatment is settled at RG LEVEL, not only in Holy + Name's own Mass propers (this file's own [holy_name_sunday] + citation already has that quote) -- RG 17's own CLOSING + paragraph, immediately after its own lettered list + (a)-(e), scan-verified: "Haec festa locum tenent dominicae + occurrentis cum omnibus iuribus et privilegiis; de + dominica, proinde, nulla fit commemoratio" -- "these + feasts" (plural, covering the WHOLE list (a)-(e), Holy Name + included) "hold the place of the occurring Sunday with all + rights and privileges; of the Sunday, therefore, no + commemoration is made". A second, independent primary + source for the same conclusion the propers-level quote + already gives, not a new claim. + + FRAGILE DERIVATION, noted per fix round 1 (coordinator + finding F5): the mechanism that actually grants a LOSING + Holy-Name-Sunday its RG 109(a) privilege when outranked + (Precedence_ef.privilege_of's own (a) branch, {!Precedence + _ef.is_sunday_slug}) reads it off the "-sunday" SUBSTRING + in the slug ("ef-holy-name-sunday") -- a naming convention, + not a citation of RG 17's own text quoted immediately + above, which is the actual warrant ("these feasts hold the + place of the occurring Sunday WITH ALL ITS RIGHTS AND + PRIVILEGES" -- RG 109(a)'s own "of a Sunday" privilege is + one of those rights). Right answer, fragile path: a future + rename of this slug family would silently drop the + privilege with no compiler or test failure pointing here. + No live witness exists to test it either way (no + fixed-date candidate ever outranks Holy Name in today's + data -- test_precedence_ef.ml's own synthetic + [test_class1_feast_inside_holy_name_window_end_to_end] + proves the SYNTHETIC case only). *) let subject, names = if same d (holy_family_sunday y) then ( Colitur_kernel.Subject.Lord, |
