summaryrefslogtreecommitdiff
path: root/lib/kernel/validate.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 15:16:00 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 15:16:00 +0200
commitea22ad2bde211998e6719fd5fb76233571a48383 (patch)
tree43c5a7532d910bf8d8f51fa4965c745bc7d8e300 /lib/kernel/validate.ml
parent81bb608ad60877df977af16f69f1ee0c99aa68f4 (diff)
downloadcolitur-ea22ad2bde211998e6719fd5fb76233571a48383.tar.gz
colitur-ea22ad2bde211998e6719fd5fb76233571a48383.zip
fix(kernel,rite-ef): admit orders commemorations by RG 113's table of precedence, not slug
Precedence_ef.admit broke a same-rank tie among commemoration candidates alphabetically by slug -- a deterministic engineering convention with no rubrical citation. RG 113's own second sentence, primary-source-verified against two independent scans and previously uncited in the register (only "commemoratio de Tempore fit primo loco" was quoted before), gives the real rule: "in admittendis et ordinandis aliis commemorationibus, servetur ordo tabellae praecedentiae" -- admitting and ordering commemorations both run on the rite's own table of precedence (band's 28-entry table), not RG 8's coarse four-class rank. Precedence.resolve now computes each commemoration candidate's own band value once, generically, and hands it to rules.admit as a third tuple element (Precedence.rules.admit's signature changed accordingly, ditto Precedence_ef.admit; every rule-record stub in the test suite updated to match). Precedence_ef.admit's own compare_dignity is replaced by compare_precedence, ordering by band then slug; a residual tie within one identical band value still falls back to slug, since RG 113 gives no further instruction there -- documented as a still-uncited engineering convention, not dressed up as a rubric. RG 98 ("in paritate autem Officium prius impeditum praecedit") was considered as a candidate authority for that residual and rejected: it governs the transfer queue order among several simultaneously-impeded I-class feasts (Caput XIII), a different operation in a different chapter from RG 113's commemoration admission (Caput XVI); nothing in the primary text connects the two. Blast radius measured against the pre-change binary across the entire 1583-9999 domain (not only 2005-2050): the admitted-commemoration-slug set is byte-identical, day for day, before and after this change. The fix corrects the citation and mechanism, not the answer, on this codebase's current data -- both of the task brief's named examples (22 Feb Chair-of-Peter/Lent-vs-Paul, 22 Sept Maurice-vs-Thomas-of- Villanova) are confirmed present and unchanged in both streams. A new test (RG113: admit picks by precedence order, not slug, when they disagree) proves admit actually consults the passed-in precedence value with a synthetic pair whose slug order and precedence order disagree -- teeth a same-band-only regression test could not have caught, since every real collision found in the domain happens to agree on both axes. 271 -> 272 tests, all green; COLITUR_EXHAUSTIVE_SWEEP=1 unaffected.
Diffstat (limited to 'lib/kernel/validate.ml')
-rw-r--r--lib/kernel/validate.ml26
1 files changed, 24 insertions, 2 deletions
diff --git a/lib/kernel/validate.ml b/lib/kernel/validate.ml
index a3208dd..433dc99 100644
--- a/lib/kernel/validate.ml
+++ b/lib/kernel/validate.ml
@@ -315,8 +315,29 @@ let run (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) ~year =
{ Precedence.cel = d.Liturgical_day.temporal.Temporal.office;
origin = Precedence.Temporal }
in
+ (* [band]'s own [context] (RG 113, docs/research/rules-register.md
+ ยง4 "Commemorations" -- {!Precedence.rules.admit}'s own new
+ [int] parameter, added alongside RG 113's fix: [admit] now
+ orders/selects by the rite's table-of-precedence value
+ {!resolve} attaches to each candidate, not by [Vocab.rank]
+ alone). Reconstructed from {!Liturgical_day.t}'s own embedded
+ [Temporal.t], the same source [temporal_candidate] above
+ already draws its [cel] from, so this is the exact [ctx]
+ {!Calendar} passed to {!Precedence.resolve} for this date in
+ the first place, not a re-derivation that could itself drift. *)
+ let day_ctx : 's Precedence.context =
+ { Precedence.date;
+ season = d.Liturgical_day.temporal.Temporal.season;
+ weekday = d.Liturgical_day.temporal.Temporal.weekday }
+ in
let as_candidates comms =
- List.map (fun (c, p) -> ({ Precedence.cel = c; origin = Precedence.Sanctoral }, p)) comms
+ List.map
+ (fun (c, p) ->
+ let cand : 'r Precedence.candidate =
+ { Precedence.cel = c; origin = Precedence.Sanctoral }
+ in
+ (cand, p, rite.Rite.rules.Precedence.band day_ctx cand))
+ comms
in
let offered = as_candidates d.Liturgical_day.commemorations in
let readmitted =
@@ -327,7 +348,8 @@ let run (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) ~year =
List.map (fun (c, p) -> (Slug.to_string c.Precedence.cel.Celebration.slug, p)) l
|> List.sort compare
in
- if norm readmitted <> norm offered then
+ let offered_pairs = List.map (fun (c, p, _) -> (c, p)) offered in
+ if norm readmitted <> norm offered_pairs then
fail date "admission"
(Printf.sprintf
"admit is not a fixed point on this day's own commemorations: re-offering %d \