diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-12 13:32:13 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-12 13:32:13 +0200 |
| commit | 7d3b5ec831a60e8b63466251d63b2bd564acba2b (patch) | |
| tree | f9e5015704ff74f42b777d7aac207ffda0d55673 /lib/kernel | |
| parent | 5c9f2cdbffdff67ec8a1939ed6958767acf406fd (diff) | |
| download | colitur-7d3b5ec831a60e8b63466251d63b2bd564acba2b.tar.gz colitur-7d3b5ec831a60e8b63466251d63b2bd564acba2b.zip | |
fix(rite-ef,kernel): follow the oracle on the Purification; admit reads the day's own Sunday-ness, not observed
Fix round 1 review, two findings landed together because each golden pin's
correctness depends on both.
CRITICAL (item 1): reverts this branch's own first-pass retag of the
Purification (2 February) from Lord back to Bvm -> Lord. The calendarium
titles it 'B. Mariae Virg.', but missalemeum -- this project's designated
EF oracle -- treats it as taking an occurring II-class Sunday's place
outright, commemorations empty, exactly RG16(a)'s own festum Domini
treatment (2020-02-02, 2014-02-02, independently fetched). Control,
2019-09-08 (the Nativity of the BVM, an ordinary Marian feast, on a
Sunday): the Sunday observed, the feast merely commemorated -- the
opposite pattern, proving the Purification's own oracle treatment is
deliberate, not a gap. RG16(a) alone, on the untouched data, would have
matched the oracle character for character; retagging Bvm is what moved
colitur away from it on 1200 domain-wide days. The user has ruled: follow
the oracle. Genuine primary-text counter-evidence (RG120(b), the colour
rule grouping 2 February under 'B. Mariae Virg.', a category separate
from 'Domini') is recorded in the register, not discarded -- a different
project could reasonably rule the other way on the same evidence.
most-holy-name-of-mary stays retagged Bvm; that one was never disputed.
Removes the now-unneeded C12 lectio allow-list entry (data/ef/
expected-divergences.sexp, test_differential.ml): with the Purification
Lord again, colitur matches lectio's own class = lord on every affected
date, no divergence to allow-list.
IMPORTANT (item 2): Precedence.rules.admit gains a ~temporal parameter --
a genuine kernel signature change, unlike RG16(a) itself. RG111(b)'s own
Sunday rank floor ('one commemoration, namely of a II-class feast') used
to be detected by reading observed's own slug for a Sunday marker, which
silently assumed observed IS the day's temporal-cycle office. RG16(a)
breaks that assumption on purpose: a Feast of the Lord standing in for an
impeded Sunday 'cum omnibus iuribus et privilegiis' keeps the day a
dominica II classis for RG111(b) too, even though the FEAST, not the
Sunday, is observed. Before this fix an unrelated saint (Pope Sixtus II
et al., 6 August, Class3) was wrongly admitted into the freed slot on
every one of 1178 domain-wide days. Oracle-confirmed directly: 2023-08-06
(a Sunday) shows him displaced, commemorations empty; 2026-08-06 (an
ordinary Thursday) shows him admitted -- being a Sunday is exactly what
excludes him. admit now reads Sunday-ness off ~temporal (the day's own
temporal-cycle candidate, passed through from Precedence.resolve
unchanged) instead of observed. Validate's own admission fixed-point
invariant updated to pass the same real temporal candidate through.
Both golden pins in test_golden.ml were promoted from actual colitur day
output without independently consulting the rubric or the oracle first --
a real defect the review caught. Re-derived from both before re-pinning:
2028-08-06 and 2025-02-02 now pin comms=[], no commemoration at all,
matching missalemeum exactly in both cases.
Diffstat (limited to 'lib/kernel')
| -rw-r--r-- | lib/kernel/precedence.ml | 3 | ||||
| -rw-r--r-- | lib/kernel/precedence.mli | 16 | ||||
| -rw-r--r-- | lib/kernel/validate.ml | 17 |
3 files changed, 34 insertions, 2 deletions
diff --git a/lib/kernel/precedence.ml b/lib/kernel/precedence.ml index 05ad69f..d48203e 100644 --- a/lib/kernel/precedence.ml +++ b/lib/kernel/precedence.ml @@ -17,6 +17,7 @@ type ('s, 'r) rules = { disposition : winner:'r candidate -> loser:'r candidate -> disposition; admit : observed:'r candidate -> + temporal:'r candidate -> ('r candidate * privilege) list -> ('r candidate * privilege) list; } @@ -55,7 +56,7 @@ let resolve rules ctx ~temporal ~sanctoral = ([], [], []) losers in let comms = List.rev comms and deferred = List.rev deferred in - let admitted = rules.admit ~observed comms in + let admitted = rules.admit ~observed ~temporal comms in let dropped = List.filter (fun c -> not (List.exists (fun a -> fst a == fst c) admitted)) comms in diff --git a/lib/kernel/precedence.mli b/lib/kernel/precedence.mli index ae054dd..d394cb1 100644 --- a/lib/kernel/precedence.mli +++ b/lib/kernel/precedence.mli @@ -34,12 +34,28 @@ type ('s, 'r) rules = { (** RG 92-95: the loser's fate, which depends on the loser's own rank. *) admit : observed:'r candidate -> + temporal:'r candidate -> ('r candidate * privilege) list -> ('r candidate * privilege) list; (** RG 108-111: how many commemorations are admitted, and in what order; anything filtered out here is recorded in {!resolution.omitted}, not dropped. + [temporal] is {!resolve}'s own [~temporal] argument, passed through + unchanged -- the day's temporal-cycle candidate, regardless of + whether it won. Fix round 1 (RG16(a) task): before this, a rite's + [admit] could only infer properties of the CIVIL DAY (chiefly "is + this a Sunday", RG 111(b)'s own two-tier admission rule) from + [observed]'s own fields -- a proxy that breaks the moment something + OTHER than the day's own temporal candidate can be [observed], the + exact shape RG 16(a) introduces (a Feast of the Lord standing in + the impeded Sunday's place "cum omnibus iuribus et privilegiis", + RG 91 entry 14). This is NOT a kernel definition of "Sunday" -- + the kernel does not gain any rite-specific knowledge by this + parameter, it only threads through a value {!resolve} already + holds; a rite's own [admit] is free to ignore it entirely, the + same as [observed]. + OBLIGATION ON THE IMPLEMENTATION, not enforced by this type: every candidate this function returns must be a value taken UNCHANGED from its input list, never rebuilt (e.g. via a [{ c with ... }] diff --git a/lib/kernel/validate.ml b/lib/kernel/validate.ml index cc8bdce..a3208dd 100644 --- a/lib/kernel/validate.ml +++ b/lib/kernel/validate.ml @@ -303,11 +303,26 @@ let run (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) ~year = let observed_candidate : 'r Precedence.candidate = { Precedence.cel = d.Liturgical_day.observed; origin = Precedence.Sanctoral } in + (* [~temporal] (fix round 1, RG16(a) task): {!Precedence.rules.admit} + now also takes the day's own temporal-cycle candidate, reused + here from {!Liturgical_day.t}'s own embedded [Temporal.t] -- + the exact same value {!Calendar} passed to {!Precedence.resolve} + in the first place, so re-offering is against the SAME inputs, + not a reconstruction that could itself introduce a false + negative. [origin] is [Precedence.Temporal], genuinely (this IS + the temporal candidate, not a reconstructed sanctoral one). *) + let temporal_candidate : 'r Precedence.candidate = + { Precedence.cel = d.Liturgical_day.temporal.Temporal.office; + origin = Precedence.Temporal } + in let as_candidates comms = List.map (fun (c, p) -> ({ Precedence.cel = c; origin = Precedence.Sanctoral }, p)) comms in let offered = as_candidates d.Liturgical_day.commemorations in - let readmitted = rite.Rite.rules.Precedence.admit ~observed:observed_candidate offered in + let readmitted = + rite.Rite.rules.Precedence.admit ~observed:observed_candidate + ~temporal:temporal_candidate offered + in let norm l = List.map (fun (c, p) -> (Slug.to_string c.Precedence.cel.Celebration.slug, p)) l |> List.sort compare |
