aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-18 12:30:08 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-18 12:30:08 +0200
commite07d0b1f49e1bb006b12a5e612453101d9e641a7 (patch)
treedac3a618fe63e91f4cf9cae4e17b264f25b8a7dc
parenta6daed7820a97f4032f21964b5b1bb30fe00b36b (diff)
downloadcolitur-e07d0b1f49e1bb006b12a5e612453101d9e641a7.tar.gz
colitur-e07d0b1f49e1bb006b12a5e612453101d9e641a7.zip
fix(ef): the seasonal commemoration comes first (RG 113 sentence one)
"113. Commemoratio de Tempore fit primo loco. In admittendis et ordinandis aliis commemorationibus, servetur ordo tabellae praecedentiae." Only the second sentence was implemented, adopted on ef-rg16a as the admission and ordering criterion. The first -- the commemoration OF THE SEASON is made in the first place -- was not, so ordering ran through band alone for every commemoration including the seasonal one. Reconciled with RG 110(c), which adds the inseparable Peter/Paul pair "ante omnes alias commemorationes". Both rubrics are primary-source certain: the photographic scans' OCR is illegible at RG 110(c), but docs/research/LT.txt, the electronic transcription, carries the Rubricae Generales complete, and O'Connell's footnote 39 turns out to render that clause exactly rather than gloss it. They share a technical term. RG 113's own sentence fixes the sense of "aliae commemorationes" as the ones other than the de Tempore one it has just placed primo loco, and RG 110(c) sits three paragraphs earlier in the same code. Read consistently, the pair heads the OTHERS, not the whole list: season, then the pair, then the table order. So this runs after rg110_additions, and List.partition's stability keeps the pair adjacent while the seasonal commemoration steps in front of it. Blast radius, two full 1583-9999 sweeps diffed: 3 533 days, every one ORDER-ONLY -- the commemoration set is identical on both sides of every changed day, checked as sorted multisets, zero days where anything else moved. All are 22 February with chair-of-st-peter observed. Exactly the population measured before the rule was written. The order-sensitive test row is in test_precedence_ef.ml's admit_cases, which is still the only place in the suite that asserts commemoration order at all. It needed a mixed-origin candidate: every other candidate in that table is origin = Temporal by default, so the rule is an identity on them.
-rw-r--r--lib/rites/rite_ef/precedence_ef.ml41
-rw-r--r--test/test_precedence_ef.ml28
2 files changed, 68 insertions, 1 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml
index dd75765..b69ee52 100644
--- a/lib/rites/rite_ef/precedence_ef.ml
+++ b/lib/rites/rite_ef/precedence_ef.ml
@@ -1532,6 +1532,45 @@ let rg110_find_companion comms companion_slug =
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. *)
+(* RG 113, sentence ONE -- "Commemoratio de Tempore fit primo loco" (the
+ commemoration of the Season is made in the first place), verified in the
+ electronic transcription docs/research/LT.txt and in both photographic
+ scans; register §6.26.
+
+ §4 of the register and this file's own {!admit} already implement RG 113's
+ SECOND sentence ("In admittendis et ordinandis ALIIS commemorationibus,
+ servetur ordo tabellae praecedentiae") as the admission/ordering criterion,
+ adopted on the ef-rg16a branch. The first sentence was never implemented:
+ ordering was by {!band} alone, for every commemoration including the
+ seasonal one.
+
+ RECONCILED WITH RG 110(c), which says the inseparable Peter/Paul pair is
+ added "ante omnes alias commemorationes". The two rubrics share a technical
+ term: RG 113's own sentence fixes the sense of *aliae commemorationes* as
+ "the ones other than the de Tempore commemoration it has just placed primo
+ loco", and RG 110(c) sits three paragraphs earlier in the same code. Read
+ consistently, RG 110(c) puts the pair at the head of the OTHERS, not ahead
+ of the season. Hence this runs AFTER {!rg110_additions}, not before:
+ RG 110(c)'s own "immediate" adjacency (trigger then companion, fix round 1,
+ register §6.3) is preserved by {!List.partition}'s stability, and the
+ seasonal commemoration then steps in front of the pair rather than
+ splitting it.
+
+ [origin] is the exact discriminator the kernel already carries -- a
+ candidate is {!Precedence.Temporal} iff it came from the rite's temporal
+ cycle, which is what "de Tempore" means. Measured over 1583-9999, no day
+ ever carries more than one such commemoration (RG 112(c), "an Office, Mass
+ or commemoration of the season excludes another seasonal commemoration",
+ holding empirically), so this promotes at most one entry; [partition] is
+ used rather than a find-and-move so the zero case and the never-observed
+ many case are both total without a special branch. *)
+let rg113_season_first
+ (comms : (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list) :
+ (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list =
+ let de_tempore (c, _) = c.Precedence.origin = Precedence.Temporal in
+ let seasonal, others = List.partition de_tempore comms in
+ seasonal @ others
+
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) :
@@ -1702,7 +1741,7 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate)
as the non-Sunday II-class case, just with room for two. *)
List.map drop_band (take 2 sorted)
in
- rg110_additions comms ~observed normal
+ rg113_season_first (rg110_additions comms ~observed normal)
(* Task 11: RG 96 -- where an impeded I-class feast lands (docs/research/
rules-register.md §4, "Transfer/translation"). [band] decides who is
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml
index 790dab6..3f3e070 100644
--- a/test/test_precedence_ef.ml
+++ b/test/test_precedence_ef.ml
@@ -1170,12 +1170,40 @@ let unrelated_comm_synth =
contest; it adds Paul regardless. *)
let lent_feria_synth = (cand ~origin:P.Temporal ~rank:V.Class3 "ef-lent-2-monday-synth", P.Privileged, 180)
+(* RG 113 sentence ONE (docs/research/rules-register.md §6.26):
+ "Commemoratio de Tempore fit primo loco." The commemoration OF THE SEASON
+ is made in the first place; the second sentence's table order governs the
+ OTHERS. Every other candidate in this table is [origin = P.Temporal] by
+ [cand]'s own default, so the rule is an identity on them and no existing
+ row's expectation moves; these two candidates exist to make a MIXED-origin
+ row possible, which is the only shape that can witness it.
+
+ This file's [admit_cases] is the ONLY place in the whole suite that
+ asserts commemoration ORDER -- the differential never compares
+ commemorations, the oracle sorts both sides into a multiset, and
+ test_golden.ml's own [describe] deliberately sorts its comms field. So a
+ row here is the only thing standing between this rule and a silent
+ regression. *)
+let sanctoral_hi =
+ { (cand ~rank:V.Class2 "ef-sanctoral-hi") with P.origin = P.Sanctoral }
+
+let seasonal_lo = cand ~rank:V.Class3 "ef-seasonal-lo"
+
let admit_cases =
[ (* RG 111 (§4): "I class: none save one privileged." *)
( "I-class day, only an ordinary commemoration due -> none admitted",
observed_class1, observed_class1,
[ po ordinary_hi P.Ordinary ],
[] );
+ (* RG 113 s1: the de Tempore commemoration leads, even though the
+ sanctoral one outranks it on RG 91's table (Class2 beats Class3) and
+ would therefore come first under sentence TWO alone. Both slots of a
+ III-class day are filled, so this row tests ORDER, not selection. *)
+ ( "III-class day, seasonal + sanctoral both admitted -> the SEASONAL one \
+ first (RG 113 s1), despite the sanctoral one's higher table position",
+ observed_class3, observed_class3,
+ [ po sanctoral_hi P.Ordinary; po seasonal_lo P.Ordinary ],
+ [ "ef-seasonal-lo"; "ef-sanctoral-hi" ] );
( "I-class day, ordinary + privileged both due -> only the privileged \
one, regardless of the ordinary one's higher dignity",
observed_class1, observed_class1,