aboutsummaryrefslogtreecommitdiff
path: root/test/test_precedence_ef.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-13 11:19:19 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-13 11:19:19 +0200
commitee5c81e70adcd2ce6cecc57da4c416d482c187ca (patch)
tree45b5d7951bae89b0720d876a2abfddc82627e78c /test/test_precedence_ef.ml
parentb5dcdd8d4048295cfbdf1606f4cafd39b3c433e6 (diff)
downloadcolitur-ee5c81e70adcd2ce6cecc57da4c416d482c187ca.tar.gz
colitur-ee5c81e70adcd2ce6cecc57da4c416d482c187ca.zip
precedence(ef): RG 110, the inseparable Peter/Paul commemoration
RG 110 (Caput XIV): "In Officio et Missa S. Petri semper fit commemoratio S. Pauli, et vicissim... pro unica habeantur" -- in the Office and Mass of either Peter or Paul, a commemoration of the other is always made, uncapped by RG 111's own admission count. Both photographic scans, full text including sub-clause (c) (a separate trigger this register previously had not transcribed: the same rule also fires when one Apostle's oration is admitted merely as an ordinary/privileged commemoration, not only when it is the day's own office). Three real pairs in the 1962 calendar, all confirmed on both scans: 25 January (conversion-of-st-paul + peter), 22 February (chair-of-st-peter + paul), 30 June (in-commemoratione-sancti-pauli-apostoli + a new commemoration-of-st-peter). The first two already existed as ordinary sanctoral candidates but were wired into RG 111's ordinary, capped admission contest instead of this rule's own uncapped one -- the defect this closes. 30 June had no companion at all: a genuine gap in lectio's own source data (confirmed directly against tridentine-calendar.ini) and in missalemeum's own oracle output for both fixture years, not only a colitur bootstrap miss, so colitur is now uniquely correct on this date among the three engines compared. Added via data/ef/adjustments.sexp's own Add directive, the architecture's existing extension point for exactly this shape of gap, since data/ef/sanctoral.sexp is generated and lectio must not be touched. Precedence_ef.admit gained rg110_additions/rg110_companion_slug, layered on after admit's own four ordinarily-capped branches decide their normal result: checks both the observed candidate and every already-admitted candidate against the three-pair lookup and prepends any companion found, uncapped and additional, never competing for RG 111's own slot. Measured full blast radius (1583-9999, pre-change binary from git archive at 59fbda8 vs the fixed binary, 3,074,246 days each side, 14,627 differing days, all four predicted shapes, zero unclassified): 3,619 Holy Name fallback days (previous commit), 3,533 days Paul was wrongly excluded from Chair of St Peter's own day by a competing privileged feria, 593 days Chair of St Peter itself was admitted only as an ordinary commemoration with Paul entirely absent, and 6,882 days the new 30 June companion is added. The 3,533+593 figure supersedes an earlier, narrower 852-day estimate that measured only the delta between two older code commits, not the rule's full scope. The lectio differential needed no change: it does not compare commemorations at all, and this fix never touches season, slug, rank, or colour. The missalemeum oracle allow-list needed three changes: M12 removed (22 February 2027 no longer diverges in commemoration count), M15's date list gained 2027-02-22 (now reaching identity comparison for the first time, where it lands on that entry's own pre-existing, unrelated limit -- one of the two commemorations is temporal-origin and unnamed), and a new M19 for the 30 June gap. Mutation-tested, both halves separately: disabling the admit-level addition reddens the two new unit tests, the oracle's own M12-shaped checks, and an existing golden pin (updated to expect the Paul companion); removing only the 30 June data entry reddens solely the oracle's new M19 count, confirming the code and data halves are independently load-bearing.
Diffstat (limited to 'test/test_precedence_ef.ml')
-rw-r--r--test/test_precedence_ef.ml68
1 files changed, 67 insertions, 1 deletions
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml
index d873e34..af7f65e 100644
--- a/test/test_precedence_ef.ml
+++ b/test/test_precedence_ef.ml
@@ -1016,6 +1016,33 @@ let sixtus =
UNIVERSALLY), so the two values coinciding is the faithful shape, not a
simplification that dodges the new parameter. The last two rows are
where they genuinely differ -- see their own comment. *)
+(* RG 110 (§4, Caput XIV): candidates built with the REAL trigger/companion
+ slugs {!PE.rg110_companion_slug} matches on -- unlike every other
+ [admit_cases] candidate above, which is deliberately built with a
+ SYNTHETIC slug {!PE.band}'s 28 branches (and this file's own RG 110
+ lookup) cannot recognise, these three rows need [PE.admit] to recognise
+ the slug itself, so the real ones are used directly. [~status:
+ Cel.Commemoration_only] on the companions matches the real data shape
+ (data/ef/sanctoral.sexp's own [peter]/[paul];
+ data/ef/adjustments.sexp's own [commemoration-of-st-peter]) even though
+ [admit] itself never reads [status] -- only [Precedence.resolve]'s own
+ partition does, upstream of [admit] -- kept for a reader's sake, not a
+ test requirement. *)
+let chair_of_st_peter_synth = cand ~rank:V.Class2 "chair-of-st-peter"
+let paul_companion_synth = cand ~rank:V.Class3 ~status:Cel.Commemoration_only "paul"
+
+(* Any real Lent/Ember feria's own shape (a Class3, PRIVILEGED, temporal-
+ origin candidate) -- {!PE.rg110_companion_slug} does not match this slug,
+ so it is a neutral bystander in [admit]'s own normal (pre-RG110) contest,
+ exactly like [privileged_lo] elsewhere in this table, just kept
+ separately named so this block's own rows are self-contained and do not
+ depend on a binding declared for an unrelated purpose above. Given the
+ WORSE (higher) precedence int than [paul_companion_synth] would need to
+ win the day's one "other II class" slot on its own dignity ALONE -- the
+ whole point of the row below is that RG 110 does not care who wins that
+ contest; it adds Paul regardless. *)
+let lent_feria_synth = (cand ~origin:P.Temporal ~rank:V.Class3 "ef-lent-2-monday-synth", P.Privileged, 180)
+
let admit_cases =
[ (* RG 111 (§4): "I class: none save one privileged." *)
( "I-class day, only an ordinary commemoration due -> none admitted",
@@ -1109,7 +1136,46 @@ let admit_cases =
admitted",
lord_winner, lord_winner,
[ po sixtus P.Ordinary ],
- [ "pope-sixtus-ii-felicissimus-and-agapitus-martyrs" ] )
+ [ "pope-sixtus-ii-felicissimus-and-agapitus-martyrs" ] );
+ (* RG 110, shape (a)/(b): [chair-of-st-peter] observed, a competing
+ PRIVILEGED Lent feria also due for the day's single "other II class"
+ slot (RG 111(c) itself has no privilege-override clause -- unlike the
+ Sunday branch above -- so pure precedence order decides that slot on
+ its own, and [lent_feria_synth]'s own 180 beats
+ [paul_companion_synth]'s 999). Without RG 110, this is exactly
+ precedence_ef.ml's own measured defect (3,533 of 8,417 domain-wide
+ years, this task's own report): Paul loses the slot outright and is
+ never admitted at all. With RG 110, Paul is added ADDITIONALLY,
+ PREPENDED ahead of the feria per RG 110(c)'s own "ante omnes alias
+ commemorationes" -- both admitted, not one displacing the other. *)
+ ( "RG110 (a)/(b): chair-of-st-peter observed, Paul rides alongside a \
+ competing privileged Lent feria, uncapped and prepended",
+ chair_of_st_peter_synth, chair_of_st_peter_synth,
+ [ (paul_companion_synth, P.Ordinary, 999); lent_feria_synth ],
+ [ "paul"; "ef-lent-2-monday-synth" ] );
+ (* RG 110, shape (c): [chair-of-st-peter] itself LOSES the day outright
+ (observed is some unrelated II-class Sunday) but is admitted, on its
+ OWN Class2 dignity, as RG 111(b)'s single ordinary commemoration --
+ exactly the domain-wide 593-year pattern this task's own report
+ measures. RG 110(c)'s own text ("quoties vero oratio unius Apostoli
+ addenda est ad modum commemorationis...") governs precisely this: the
+ companion (Paul, Class3, no standing of its OWN for this Sunday's
+ rank-floored slot) rides along anyway, uncapped. *)
+ ( "RG110 (c): chair-of-st-peter admitted merely AS a commemoration \
+ (loses the day to an ordinary II-class Sunday), Paul still rides \
+ along",
+ cand ~rank:V.Class2 "ef-some-other-sunday", cand ~rank:V.Class2 "ef-some-other-sunday",
+ [ po chair_of_st_peter_synth P.Ordinary; (paul_companion_synth, P.Ordinary, 999) ],
+ [ "paul"; "chair-of-st-peter" ] );
+ (* Defensive: a trigger slug is [observed], but its companion is simply
+ not present among [comms] at all (a data gap, or -- as this exact
+ shape was for 30 June before this task's own [Add
+ commemoration-of-st-peter] directive -- an upstream omission).
+ {!PE.rg110_find_companion} must return [None] gracefully, not raise;
+ the day's own (empty, here) normal result is unaffected. *)
+ ( "RG110 defensive: chair-of-st-peter observed, no companion present in \
+ comms at all -> no crash, nothing added",
+ chair_of_st_peter_synth, chair_of_st_peter_synth, [], [] )
]
(* Order independence (brief: "the admitted set must not depend on input