diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-13 11:19:19 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-13 11:19:19 +0200 |
| commit | ee5c81e70adcd2ce6cecc57da4c416d482c187ca (patch) | |
| tree | 45b5d7951bae89b0720d876a2abfddc82627e78c /test | |
| parent | b5dcdd8d4048295cfbdf1606f4cafd39b3c433e6 (diff) | |
| download | colitur-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')
| -rw-r--r-- | test/test_golden.ml | 16 | ||||
| -rw-r--r-- | test/test_oracle.ml | 81 | ||||
| -rw-r--r-- | test/test_precedence_ef.ml | 68 |
3 files changed, 137 insertions, 28 deletions
diff --git a/test/test_golden.ml b/test/test_golden.ml index c5aa65a..80e2c9d 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -434,11 +434,21 @@ let test_ember_ferias_commemorated_when_impeded () = comms=[ef-advent-ember-fri:privileged] in=- out=[]"; (* 1902-02-22: Lent Ember Saturday (RG91 entry 18, II class) impeded by the Chair of St Peter (II class, universal, entry 16). Same RG24/ - RG109(e) mandate. *) - check ~msg:"1902-02-22: Lent Ember Saturday, impeded, MUST be commemorated (RG24) and is privileged (RG109e)" + RG109(e) mandate. ALSO, ef-holyname-rg110 task: RG 110's own + inseparable Peter/Paul commemoration -- "in Officio ... S. Petri semper + fit commemoratio S. Pauli" -- fires unconditionally whenever + [chair-of-st-peter] is observed, uncapped by RG 111's own admission + count, so `+paul` now rides alongside the Ember feria's own + commemoration rather than being displaced by it (precedence_ef.ml's own + [rg110_additions]). This pin's own [ef-lent-ember-sat:privileged] half + is UNCHANGED from before this task -- only [paul:ordinary] is new, + confirming RG 110 adds a genuinely SEPARATE, uncapped slot rather than + competing for the Ember feria's own privileged one. *) + check ~msg:"1902-02-22: Lent Ember Saturday, impeded, MUST be commemorated (RG24) and is privileged (RG109e) -- \ + AND RG110's Paul companion rides alongside it, uncapped" 1902 2 22 "1902-02-22 saturday season=lent week=1 slug=chair-of-st-peter rank=class-2 colour=white subject=saint \ - comms=[ef-lent-ember-sat:privileged] in=- out=[]" + comms=[ef-lent-ember-sat:privileged,paul:ordinary] in=- out=[]" let test_iv_class_ferias_never_commemorated () = (* 2026-07-22: an ordinary Time-after-Pentecost feria (Class4, RG91 entry diff --git a/test/test_oracle.ml b/test/test_oracle.ml index 35d4501..370eec0 100644 --- a/test/test_oracle.ml +++ b/test/test_oracle.ml @@ -599,23 +599,23 @@ let m10_dates = [ "2026-11-30"; "2027-11-30"; "2026-12-08"; "2027-12-08" ] own full note and the task report. *) let m11_dates = [ "2026-12-26"; "2026-12-28"; "2027-12-27"; "2027-12-28" ] -(* M12 -- RG 110, primary text: "In Officio et Missa S. Petri semper fit - commemoratio S. Pauli, et vicissim" -- in the Office and Mass of EITHER - Peter or Paul, a commemoration of the OTHER is ALWAYS made (the - "inseparable commemoration"), uncapped by RG 111's ordinary admission - count ("in numero orationum computando, pro unica habeantur" -- counted - as one for counting purposes, i.e. it rides alongside whatever RG 111 - already admits). Unimplemented in this codebase -- confirmed by the one - row in this window where it is visible: 22 February 2027 (Chair of - St Peter) shows only the privileged Lent feria commemorated in colitur, - while missalemeum shows that AND "St. Paul". A genuine, primary-cited - feature gap -- verdict missalemeum, register §6 open item. Real but - NARROW: every other Peter/Paul office in this window (29 June, both - years; 1 August M4-adjacent dates were checked and found clean) either - had no room for the extra commemoration to become visible against this - comparator's axes, or fell on a day this predicate does not otherwise - touch -- see the task report for the specific check. *) -let m12_dates = [ "2027-02-22" ] +(* M12 -- CLOSED (ef-holyname-rg110 task): RG 110 is now built + (precedence_ef.ml's own [rg110_additions]/[rg110_companion_slug]), and + this window's one visible instance -- 22 February 2027 -- no longer + diverges from missalemeum on [Comm_count] at all: colitur now shows the + privileged Lent feria's own commemoration AND "St. Paul", same COUNT + (two) as missalemeum. Removed rather than kept as a dead 0-row citation, + the SAME shape M17's own removal already established (this file's own + header): the count pin now enforces that this exact divergence cannot + silently reappear. NOT closed with zero remaining difference, though -- + see M15's own widened date list immediately below: once count agrees, + IDENTITY comparison is reached for the first time on this date, and + trips a SEPARATE, pre-existing, unrelated comparator limit (one of + colitur's two admitted commemorations, [ef-lent-2-monday], is + TEMPORAL-origin and carries no English name) -- a newly-exposed + instance of M15's own gap, not a new one, folded into M15's date list + rather than kept here under RG 110's own citation, which no longer + explains ANY part of this row's remaining diff. *) (* M13 -- OPEN, NOT adjudicated (the brief's own explicit permission, "say so as an open item rather than absorbing it"). 19 March 2027: St @@ -647,13 +647,13 @@ let m13_dates = [ "2027-03-19" ] either engine is wrong about: a genuine LIMIT of this comparator, honestly counted rather than silently passed (the brief's own explicit instruction: "a day whose commemoration cannot be matched must be - visibly counted and reported, not quietly passed"). On each of these 19 + visibly counted and reported, not quietly passed"). On 19 of these 20 rows, colitur's own admitted commemoration is TEMPORAL-origin (an impeded Advent/Lent feria) -- Rite_ef.Temporal_ef's own [build] (temporal_ef.ml) never sets a celebration's [names] field, so there is no English string on colitur's side to compare against missalemeum's title text at all (this file's own header, "THE MAPPING, AND EXACTLY WHAT IT - CANNOT RESOLVE"). Every one of these 19 oracle titles reads as a plain + CANNOT RESOLVE"). Every one of those 19 oracle titles reads as a plain prose description of the SAME temporal ferial slot colitur independently computes for that date (e.g. 2 Dec 2026: oracle "Feria IV after I Sunday of Advent" against colitur's own [ef-advent-1-wednesday]; 4 Dec 2027: @@ -664,12 +664,25 @@ let m13_dates = [ "2027-03-19" ] unresolved. Fixing this at the ROOT (giving every temporal-cycle candidate an English name) is a data/lectionary-bootstrap task, not something this comparator can do for itself -- register §6, "RG 113 - tie-break" section's own neighbour, records it as an open item. *) + tie-break" section's own neighbour, records it as an open item. + + ADDED, ef-holyname-rg110 task: 2027-02-22 -- the closed M12's own date, + above. NOT the same shape as the other 19 (colitur admits TWO + commemorations there, not one -- [paul], sanctoral-origin and + resolvable, and [ef-lent-2-monday], temporal-origin and not): the + comparator's own [identity_diff] bails out to [Comm_identity_unresolved] + the moment ANY admitted commemoration is unresolvable, regardless of how + many others resolve cleanly (test_oracle.ml's own [identity_diff], + "[List.exists] ... name = None"), so this row reaches the identical + diagnosis for the identical structural reason, once RG 110 stopped + [Comm_count] from masking it. Genuinely newly-EXPOSED by this task's own + fix, not newly-CAUSED by it: the underlying gap (no English name for a + temporal-cycle candidate) is exactly M15's own pre-existing one. *) let m15_dates = [ "2026-02-24"; "2026-03-19"; "2026-03-25"; "2026-12-02"; "2026-12-03"; "2026-12-04"; - "2026-12-07"; "2026-12-11"; "2026-12-21"; "2027-02-24"; "2027-12-02"; "2027-12-03"; - "2027-12-04"; "2027-12-06"; "2027-12-07"; "2027-12-11"; "2027-12-13"; "2027-12-16"; - "2027-12-21" ] + "2026-12-07"; "2026-12-11"; "2026-12-21"; "2027-02-22"; "2027-02-24"; "2027-12-02"; + "2027-12-03"; "2027-12-04"; "2027-12-06"; "2027-12-07"; "2027-12-11"; "2027-12-13"; + "2027-12-16"; "2027-12-21" ] (* M16 -- Task B: a genuine IDENTITY mismatch, both sides otherwise agreeing on rank/colour/count/presence. 27 March 2026 (Friday of Passion Week): @@ -766,6 +779,26 @@ let m16_dates = [ "2026-03-27" ] bootstrap task (Plan 4), not something this comparator can do for itself -- register §6 tracks it, the same open item [M15]'s own note already points to. *) +(* M19 -- ef-holyname-rg110 task: RG 110's OTHER direction, 30 June (this + window's two instances -- both years -- of + [in-commemoratione-sancti-pauli-apostoli], data/ef/adjustments.sexp's + own `Add commemoration-of-st-peter` directive; see that file's own + citation for the full scan quote). Unlike 22 February above, + missalemeum's own row for both dates shows an EMPTY commemorations array + ("St. Peter" absent, and not even listed as "displaced" -- nothing + competes for the slot on either side, so there is no candidate for + missalemeum's own engine to have rejected, only one it never + constructed): missalemeum has the SAME gap this task's own scan found in + lectio's source data (`~/git/projects/lectio/internal/caldata/ + tridentine-calendar.ini` has no 06-30 companion entry either) -- a + genuine feature gap, verdict colitur, the SAME shape as M1/M3/M8/M10 + above (each already "missalemeum does not implement X"), not a + citation-count coincidence: RG 110's own text is unconditional ("in + Officio et Missa S. Petri semper fit commemoratio S. Pauli, ET + VICISSIM"), and the calendarium's own June table states the SAME + pattern already built for the other two pairs, word for word. *) +let m19_dates = [ "2026-06-30"; "2027-06-30" ] + let layer_m_reason (c : colitur_row) (o : oracle_row) diffs = if diffs = [] then None (* M1's own subset widened (this task): colitur's observed day on both @@ -795,7 +828,6 @@ let layer_m_reason (c : colitur_row) (o : oracle_row) diffs = else if List.mem c.c_date m8_dates && diffs = [ Comm_presence ] then Some "M8" else if List.mem c.c_date m10_dates && diffs = [ Comm_presence ] then Some "M10" else if List.mem c.c_date m11_dates && diffs = [ Comm_presence ] then Some "M11" - else if List.mem c.c_date m12_dates && diffs = [ Comm_count ] then Some "M12" (* M13's own subset widened AGAIN (this task, following the SAME pattern Task B's own widening comment below records): colitur's observed celebration here is Joseph, SANCTORAL-origin, so its name IS @@ -818,6 +850,7 @@ let layer_m_reason (c : colitur_row) (o : oracle_row) diffs = && subset diffs [ Comm_identity_mismatch; Observed_identity_unresolved ] then Some "M16" else if diffs = [ Observed_identity_unresolved ] then Some "M18" + else if List.mem c.c_date m19_dates && diffs = [ Comm_presence ] then Some "M19" else None (* ---------------------------------------------------------------------- *) 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 |
