aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef/precedence_ef.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 17:13:06 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-12 17:13:06 +0200
commita2654a1e4d8987a251bcefbad2b0f0b867a6c6e6 (patch)
tree39360eccd3fa8ee5379fc46276d1e0ef0e6ff2b5 /lib/rites/rite_ef/precedence_ef.ml
parente19adb9c7ea369cafe76e7eff50e9248a4a954d0 (diff)
parente5b368dec8fdc9ab983ee0f1dee69c37883cbe12 (diff)
downloadcolitur-a2654a1e4d8987a251bcefbad2b0f0b867a6c6e6.tar.gz
colitur-a2654a1e4d8987a251bcefbad2b0f0b867a6c6e6.zip
Merge branch 'ef-rg16a': RG 16(a), RG 111(b), RG 113, and commemoration identity
Closes the largest known-wrong-output item on record (RG 16(a): a Feast of the Lord occurring on a II-class Sunday leaves the Sunday uncommemorated -- 5996 wrong days over 1583-9999) together with RG 111(b)'s rank floor on the day it holds, and gives the oracle layer the ability to compare commemoration identity rather than only presence and count. Also corrects a methodological defect that had produced wrong rulings in both this project and its sibling: docs/research/ holds an electronic transcription alongside two photographic scans, and the transcription silently drops commemoration lines -- seven across the year. Reasoning from its silence had convicted the oracle wrongly on 14 August and ruled a genuine 9 August commemoration spurious. The scans are the primary source; the rule is now recorded at the top of the register. RG 113 replaces an alphabetical same-rank tie-break that had no rubrical warrant. A bare Commemoratio has no row in RG 91's table at all, so band no longer hands one the entry of a III-class universal feast: 4451 days, all four reordered pairs verified against a photographic scan, zero observed-day changes.
Diffstat (limited to 'lib/rites/rite_ef/precedence_ef.ml')
-rw-r--r--lib/rites/rite_ef/precedence_ef.ml333
1 files changed, 267 insertions, 66 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml
index db7e708..750da8b 100644
--- a/lib/rites/rite_ef/precedence_ef.ml
+++ b/lib/rites/rite_ef/precedence_ef.ml
@@ -110,6 +110,7 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
int =
let cel = c.Precedence.cel in
let rank = cel.Celebration.rank in
+ let status = cel.Celebration.status in
let subject = cel.Celebration.subject in
let layer = cel.Celebration.layer in
let slug = Slug.to_string cel.Celebration.slug in
@@ -129,8 +130,48 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
binding, not a second literal, so the two can never drift apart. *)
let entry_15_band = 15 in
let open Vocab_ef in
- (* 1: Nativity, Easter Sunday, Pentecost Sunday (I class w/ octave). *)
- if is_temporal && rank = Class1 && ((m = 12 && d = 25) || off = 0 || off = 49) then 1
+ (* CORRECTED (Task B fix round 1, ef-rg16a): a [Commemoration_only]
+ celebration has NO row in RG 91's table at all, checked FIRST, ahead of
+ every rank-keyed branch below -- RG 91's own text enumerates only "dies
+ liturgici" (entry 24's own wording, e.g., "Festa III classis, in
+ calendario Ecclesiae universae inscripta" -- FEASTS, inscribed in the
+ calendar), and the calendarium itself marks the difference in its own
+ notation: 22 September's row reads "S. Thomae de Villanova Ep. et
+ Conf., III classis. / Commemoratio Ss. Mauritii et Soc. Mm." -- Thomas
+ gets a class number (a "festum"); Maurice gets "Commemoratio" and NO
+ class number at all, because he never had a row in the table for that
+ occasion to begin with. Before this fix, [band] read [rank] alone, so
+ a [Commemoration_only] entry silently borrowed the SAME entry number
+ as a genuine [Feast] of its own rank (a Class3 Commemoration_only
+ entry banded to 24, indistinguishable from a real Class3 universal
+ feast) -- manufacturing a "tie" at {!compare_precedence}/RG 113's own
+ admission ordering that the primary text never creates: RG 113 does
+ not run out of instruction between two same-rank commemorations;
+ [band] ran out of fidelity, handing out a table row that does not
+ exist for one of them. [unclassified] (worse than every real entry) is
+ the same value already used for a candidate this table's 28 branches
+ otherwise fail to describe, which is exactly the right answer here
+ too: "not in this table" for a different reason, same table-position
+ consequence. Confirmed by the oracle: missalemeum's own commemoration
+ id embeds a rank that AGREES with the demoted status this represents
+ (e.g. sancti:09-22o:4:r for Maurice, rank 4, against Thomas's own
+ sancti:09-22:3:w, rank 3, on the day Thomas is observed -- test_oracle
+ .ml's own [test_identity_rank_corroboration]). Restricted to [status];
+ [rank] itself is untouched, and stays load-bearing for a demoted feast:
+ RG 111(b)'s "scilicet de festo II classis" floor ({!admit} below) reads
+ [rank] directly, and it is exactly that floor which excludes these
+ Class3 entries from a II-class Sunday's single slot. Only ORDERING among
+ candidates already offered to {!admit} changes here.
+ NOTE, corrected: this comment previously cited {!Celebration.status}'s
+ own doc comment as saying a demoted feast retains its rank for RG 111's
+ admission-COUNT purposes. That misquotes it -- celebration.mli says RG
+ 111 "orders admitted commemorations by dignity", i.e. it names the
+ ORDERING use this guard removes, not a counting one. celebration.mli's
+ line is itself now stale: ordering moved from dignity to [band] in
+ ea22ad2. The guard is right; its former justification was not. *)
+ if status = Celebration.Commemoration_only then unclassified
+ (* 1: Nativity, Easter Sunday, Pentecost Sunday (I class w/ octave). *)
+ else if is_temporal && rank = Class1 && ((m = 12 && d = 25) || off = 0 || off = 49) then 1
(* 2: Sacred Triduum (Thu-Sat of Holy Week). *)
else if is_temporal && rank = Class1 && off >= -3 && off <= -1 then 2
(* 3: Epiphany, Ascension, Holy Trinity, Corpus Christi, Sacred Heart,
@@ -244,13 +285,14 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc
(* Task 8: what happens to the day's LOSING candidate (docs/research/
rules-register.md §4, "Occurrence" RG 92-95 and "Vigils" RG 33, plus RG
- 94). [band] above decides who wins; this decides the loser's fate, which
- turns on the LOSER's own rank and status (RG 95), except RG 33's vigil
- omission, which also has to read the winner. Nothing here ever returns
- [Precedence.Repose]: that disposition denotes RG 100-102's *repositio*
- (perpetual impediment from a proper/diocesan calendar), out of this
- plan's scope -- see calendar.mli's own note that nothing in the EF
- ruleset currently emits it. *)
+ 94; also §6.0/Caput III "De Dominicis" RG 16(a), below). [band] above
+ decides who wins; this decides the loser's fate, which turns on the
+ LOSER's own rank and status (RG 95), except RG 33's vigil omission and RG
+ 16(a)'s Sunday-suppression, which also have to read the winner. Nothing
+ here ever returns [Precedence.Repose]: that disposition denotes RG
+ 100-102's *repositio* (perpetual impediment from a proper/diocesan
+ calendar), out of this plan's scope -- see calendar.mli's own note that
+ nothing in the EF ruleset currently emits it. *)
(* RG 33 -- CORRECTED 2026-08-12 (Task 16, primary-source-verified against
docs/research/1962-06-23,_SS_Ioannes_XXIII,_Missale_Romanum,_LT.pdf, the
@@ -605,6 +647,91 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate)
register's own §4 "Vigils" entry states RG 32 only as "same pattern
[as RG 31]", not verbatim -- now closed here). *)
Precedence.Omit
+ else if
+ is_temporal
+ && cel.Celebration.rank = Class2
+ && is_sunday_slug (Slug.to_string cel.Celebration.slug)
+ && (let wcel = winner.Precedence.cel in
+ (wcel.Celebration.rank = Class1 || wcel.Celebration.rank = Class2)
+ && wcel.Celebration.subject = Subject.Lord)
+ then
+ (* RG 16(a) (docs/research/rules-register.md §6.0, Caput III "De
+ Dominicis", primary text): "Dominica II classis, in occurrentia,
+ festis II classis praefertur. Attamen: a) festum Domini I aut II
+ classis, in dominica II classis occurrens, locum tenet ipsius
+ dominicae cum omnibus iuribus et privilegiis: de dominica, proinde,
+ NULLA FIT COMMEMORATIO" -- a Feast of the Lord, I or II class,
+ occurring on a II-class Sunday, takes the Sunday's own place with all
+ its rights and privileges: OF THE SUNDAY, THEREFORE, NO
+ COMMEMORATION IS MADE. {!band} entry 14 already ranks such a feast
+ above the Sunday (RG 91 entry 14 < entry 15), so the feast is
+ correctly [observed]; this is [disposition]'s own answer for what
+ becomes of the SUNDAY once it has lost -- [Omit], not the [Commemorate]
+ every other impeded II-class Sunday gets via RG 109(a)/RG 111(b)
+ below. This is the ONE place in this function that needs the WINNER's
+ [subject], not only the loser's own fields -- like RG 33's vigil
+ omission above, not a new kind of signature: [Precedence.rules.disposition]
+ already takes [~winner], this is simply its first other reader.
+
+ Both winner-side conjuncts are load-bearing, proved by
+ [test_precedence_ef.ml]'s own paired rows (the brief's "one without
+ the other proves nothing"):
+ - [rank = Class1 || rank = Class2]: {!band} entries 11-13 admit ANY
+ I-class feast -- Lord or Saint alike, no subject test at all (entry
+ 14's own comment: unlike entry 16, entries 11-13 draw no such
+ line) -- ahead of a II-class Sunday. REACHABLE at Class1 on real
+ data: e.g. the Nativity of St John the Baptist (24 June, I class,
+ Saint) landing on a Time-after-Pentecost Sunday already wins the
+ day under {!band} alone; without this conjunct RG 16(a) would
+ wrongly fire for it too. (At Class2 this conjunct adds no further
+ cases beyond what the [subject = Lord] conjunct below already
+ requires -- entry 16's ordinary II-class feasts never outrank a
+ Sunday in {!band} in the first place, 16 > 15 -- so it is only
+ independently reachable at Class1.)
+ - [subject = Lord]: at Class2 this is what {!band} entry 14 already
+ requires of its own winners, so it adds no further restriction
+ there; at Class1 it is independently reachable and necessary, per
+ the St John Baptist example above -- dropping it would fire RG
+ 16(a) for any winning I-class feast at all, exactly the
+ over-wide branch the task brief warns against.
+
+ The SUNDAY-side conjuncts: [rank = Class2] excludes a I-class Sunday
+ (Advent/Lent/Passiontide, Low Sunday, {!band} entry 6) -- RG 16(a)'s
+ own text says "Dominica II classis", not "any Sunday", and a I-class
+ Sunday can never actually reach here as a [loser] against a
+ Class1-or-2 Lord winner in the first place, for TWO DIFFERENT reasons
+ depending on the winner's shape (CORRECTED, fix round 1: the previous
+ version of this comment claimed "entry 6's own band value (6) is
+ lower than every entry [3, 11-14]" as a single numeric argument --
+ WRONG on its face, 6 is not lower than 3, and the claim would in any
+ case prove the opposite of what it was cited for: if a band-3
+ candidate really did contest a Class1 Sunday, the LOWER number (3)
+ would win, i.e. the Sunday would LOSE, not "always win outright" as
+ claimed):
+ - Against entries 11-14 (SANCTORAL-origin Lord feasts, e.g. the
+ Transfiguration): the numeric argument genuinely holds here --
+ entry 6 (6) IS lower than 11-14, so a I-class Sunday always wins
+ outright against these.
+ - Against entry 3 (TEMPORAL-origin Lord feasts -- Epiphany,
+ Ascension, Trinity, Corpus Christi, Sacred Heart, Christ the King):
+ not a numeric argument at all, but a STRUCTURAL one -- every
+ band-3 celebration is [is_temporal], and {!Precedence.resolve}
+ takes exactly ONE temporal candidate per day ([~temporal], not a
+ list), so a band-3 Lord feast IS that date's own single temporal
+ candidate, never a SEPARATE candidate contesting an
+ independently-produced Sunday on the same date. There is no
+ collision to resolve by band comparison in the first place.
+ Either way, no I-class Sunday can reach here as a loser against a
+ Class1-or-2 Lord winner -- this is exactly why the rubric restricts
+ itself to II class. [is_sunday_slug]
+ (the same marker RG 33's [impedes_vigil] and RG 109(a)'s
+ [privilege_of] already use, with no [is_temporal] guard there either
+ -- no sanctoral slug this codebase's data produces contains
+ {!sunday_marker}) is the "Dominica" half; [is_temporal] is kept
+ alongside it anyway, the same explicit-but-not-load-bearing defence
+ the RG26 branch above gives its own [not is_vigil] guard, rather than
+ relying on that absence silently. *)
+ Precedence.Omit
else
(* RG 95's other branch: "aut commemorantur aut penitus omittuntur" --
commemorated or wholly omitted. Reached by every SANCTORAL loser
@@ -642,42 +769,105 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate)
classis", "dominicis II classis", "aliis diebus II classis", "diebus III
et IV classis") -- read here off [observed]'s own [rank] and, for the
Sunday/non-Sunday II-class split, the same slug marker [privilege_of] and
- RG 33's [impedes_vigil] already use ([is_sunday_slug]). No [context]
- (date/season/weekday) is available to [admit] (see precedence.mli's
- [rules.admit]) or needed: [observed] IS the day's own celebration, so its
- rank and slug already carry everything RG 111's own four categories test. *)
+ RG 33's [impedes_vigil] already use ([is_sunday_slug]).
+
+ CORRECTED (fix round 1, RG16(a) task): the previous version of this
+ comment claimed "[observed] IS the day's own celebration, so its rank and
+ slug already carry everything RG 111's own four categories test" --
+ WRONG once RG 16(a) exists. RG 16(a)'s own text says the winning Feast of
+ the Lord holds the Sunday's place "cum omnibus iuribus et privilegiis"
+ (with ALL its rights and privileges) -- the day remains a "dominica II
+ classis" for RG 111(b)'s own purposes even though [observed] is now the
+ FEAST, not the Sunday, so [observed]'s own slug is no longer a reliable
+ signal of "is this a Sunday" once something can legitimately observe in
+ the Sunday's place. Confirmed wrong for real data by the oracle: 6 August
+ falling on a Sunday shows the Transfiguration observed and NO
+ commemoration (missalemeum: `commemorations: []`, Pope Sixtus II et al.
+ `displaced`); the pre-fix code, reading Sunday-ness off [observed]'s own
+ slug ("transfiguration-of-our-lord", no Sunday marker), wrongly took the
+ [Class2, false] "other II class: one" branch below and admitted Sixtus
+ (Class3) regardless of RG 111(b)'s own "de festo II classis" rank floor.
+ Control, also oracle-confirmed: 6 August on an ordinary WEEKDAY (no
+ Sunday collision) correctly admits Sixtus -- being a Sunday is exactly
+ what excludes him, and [observed]'s own identity cannot tell the two
+ cases apart on its own.
+
+ Fixed by reading Sunday-ness off [temporal] instead -- {!Precedence.rules.admit}'s
+ own [~temporal] parameter, {!Precedence.resolve}'s [~temporal] argument
+ passed straight through, unaffected by whether it won the day. [context]
+ (date/season/weekday) is still not needed: [temporal]'s own slug already
+ carries everything this split needs, the same way [observed]'s used to
+ before a competing office could occupy the Sunday's place. *)
-(* RG 8's four-class dignity order, Class1 highest. Deliberately NOT [band]
- (RG 91's much finer 28-entry table): [band] needs a [context] [admit]
- does not have (see above), and Celebration.mli's own comment on [status]
- -- "RG 111 orders admitted commemorations by dignity" -- names [rank]
- itself as that dignity, not the finer occurrence-table entry. *)
-let dignity = function
- | Vocab_ef.Class1 -> 1
- | Vocab_ef.Class2 -> 2
- | Vocab_ef.Class3 -> 3
- | Vocab_ef.Class4 -> 4
+(* RG 113 -- CORRECTED, Task B (branch ef-rg16a): docs/research/rules-
+ register.md §4's RG 113 entry previously quoted only its FIRST sentence
+ ("commemoratio de Tempore fit primo loco"); its SECOND, load-bearing
+ sentence, primary-source-verified against two independent scans, is the
+ real rule for this function: *"In admittendis et ordinandis aliis
+ commemorationibus, servetur ordo tabellae praecedentiae"* -- in ADMITTING
+ and ORDERING the other commemorations, the order of the table of
+ precedence (RG 91, {!band}'s own 28-entry table) is to be kept.
-(* Deterministic selection order for RG 111: dignity first, then slug --
- the same tie-break {!Precedence.compare_by} uses for [band] itself (the
- brief: "break ties on slug"), so which candidate wins a shared rank never
- depends on the order [comms] arrives in. *)
-let compare_dignity (a, _) (b, _) =
- let da = dignity a.Precedence.cel.Celebration.rank
- and db = dignity b.Precedence.cel.Celebration.rank in
- if da <> db then Int.compare da db
+ This REPLACES a previous [compare_dignity], which sorted by RG 8's coarse
+ four-class [rank] ("dignity") and broke same-rank ties alphabetically by
+ slug -- an engineering convention with no rubrical warrant. Measured,
+ fix round 1 (2005-2050, a temporary pre-fix [git worktree]): a
+ same-[rank] tie existed on 599 days, most never reaching a real
+ decision; reversing the slug tie-break alone changed the ADMITTED SET on
+ 65 of them and the printed ORDER of an already-admitted pair on a
+ further 149 (docs/research/rules-register.md §6.1's full account,
+ correcting this comment's own earlier, unreproduced "66 days" claim).
+
+ CORRECTED, fix round 1 (coordinator finding 1): checking every one of
+ those 65+149 real decisions against data/ef/sanctoral.sexp found the
+ SAME underlying shape in all of them, no exceptions -- one candidate is
+ always [Cel.Commemoration_only] (e.g. "maurice-and-companions-martyrs",
+ 22 September) and the other always a genuine [Cel.Feast] or a temporal
+ office of the SAME [rank] (e.g. "thomas-of-villanova", same day; or a
+ Lent feria, {!band} entry 22, against "paul", 22 February). That is
+ {!band}'s OWN fidelity bug, fixed separately at its source (see [band]'s
+ own top-of-branch guard, RG 91's table has no row for a bare
+ commemoration at all) -- NOT a case RG 113's table-order alone resolves,
+ since a [Commemoration_only] candidate never had a real table entry to
+ compare in the first place. With that fixed, none of these 599 days'
+ real decisions any longer depend on THIS function's own slug fallback:
+ {!band} alone (a real entry vs {!unclassified}) already decides every
+ one. What remains genuinely open -- two DIFFERENT candidates landing on
+ the IDENTICAL real table entry, e.g. two different Class3 universal
+ feasts both at entry 24 -- was checked and found EMPTY across
+ 2005-2050 (register §6.1): RG 113's own table-order, once {!band} is
+ accurate, already decides every real case this codebase's current data
+ produces; the slug fallback below is exercised only between two
+ [Commemoration_only] candidates tied at {!unclassified} (138 of the 599,
+ all order/count-invisible -- RG 91 has no table position for either of
+ them to compare, so there is nothing more specific RG 113 could supply
+ here either). {!band} needs a [context] this function itself does not
+ have (date/season/weekday) -- unlike [dignity], which read [rank] alone
+ -- so {!Precedence.resolve} now computes each candidate's own [band]
+ value once, generically, and hands it to [admit] as the trailing [int]
+ on each input triple (see {!Precedence.rules.admit}'s own doc). [comms]
+ below is [(candidate * privilege * int) list], not the pair it used to
+ be. *)
+let compare_precedence (a, _, ba) (b, _, bb) =
+ if ba <> bb then Int.compare (ba : int) bb
else Slug.compare a.Precedence.cel.Celebration.slug b.Precedence.cel.Celebration.slug
let rec take n = function
| [] -> []
| x :: xs -> if n <= 0 then [] else x :: take (n - 1) xs
+(* [comms]'s own candidate/privilege pair, its [band] value dropped once a
+ selection has been made -- {!Precedence.rules.admit}'s return type is
+ still the pair, not the triple; only the INPUT carries [band]. *)
+let drop_band (c, p, (_ : int)) = (c, p)
+
let admit ~(observed : Vocab_ef.rank Precedence.candidate)
- (comms : (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list) :
+ ~(temporal : Vocab_ef.rank Precedence.candidate)
+ (comms : (Vocab_ef.rank Precedence.candidate * Precedence.privilege * int) list) :
(Vocab_ef.rank Precedence.candidate * Precedence.privilege) list =
- (* Sorted once, by dignity then slug (see [compare_dignity]); every branch
- below either takes a prefix of this list or filters it, so the RESULT
- is always a sub-list of [comms] with its elements untouched -- never
+ (* Sorted once, by {!band} then slug (see [compare_precedence]); every
+ branch below either takes a prefix of this list or filters it, so the
+ RESULT is always built from [comms]'s own elements, untouched -- never
rebuilt -- which matters beyond determinism: {!Precedence.resolve}'s
own [dropped] computation tells an admitted candidate from a dropped
one by physical equality (==) on the candidate value (Task 2's own
@@ -689,21 +879,29 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate)
celebration would surface TWICE in the same day -- once in
[commemorations] (the rebuilt copy) and once in [omitted] (the original,
which nothing admitted matches). One admission, double-reported, and no
- crash to announce it, which is exactly why this comment exists. *)
- let sorted = List.stable_sort compare_dignity comms in
- let is_privileged (_, p) = p = Precedence.Privileged in
+ crash to announce it, which is exactly why this comment exists.
+ [drop_band] only unwraps the pair back out of the triple -- it does not
+ rebuild [c] or [p] themselves, so this obligation still holds. *)
+ let sorted = List.stable_sort compare_precedence comms in
+ let is_privileged (_, p, _) = p = Precedence.Privileged in
let observed_rank = observed.Precedence.cel.Celebration.rank in
- let observed_is_sunday =
- is_sunday_slug (Slug.to_string observed.Precedence.cel.Celebration.slug)
+ (* CORRECTED (fix round 1, RG16(a) task): read off [temporal], not
+ [observed] -- see this function's own doc comment above for the full
+ argument and the oracle evidence. [temporal] is the day's own
+ temporal-cycle candidate regardless of who is [observed]; RG 16(a)
+ makes that distinction live for the first time (a Feast of the Lord
+ can now be [observed] on a day whose [temporal] candidate is a Sunday). *)
+ let day_is_sunday =
+ is_sunday_slug (Slug.to_string temporal.Precedence.cel.Celebration.slug)
in
let open Vocab_ef in
- match (observed_rank, observed_is_sunday) with
+ match (observed_rank, day_is_sunday) with
| Class1, _ ->
(* RG 111: "I class: none save one privileged." Ordinary commemorations
never get a slot at all on a I-class day, no matter how many are
- due; at most one privileged one does, the highest-dignity one if
- several are. *)
- (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ best ])
+ due; at most one privileged one does, the highest-precedence one
+ (RG 113: {!band}'s own table order) if several are. *)
+ (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ drop_band best ])
| Class2, true ->
(* RG 111(b), primary text, RE-VERIFIED word for word against the scan
(final fix wave; this sentence is the sole textual basis for the
@@ -717,17 +915,18 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate)
dropped if a privileged commemoration is due." Two clauses, not
one: (i) a privileged
commemoration, whenever due, categorically takes the day's one slot
- -- not by comparing its dignity against the ordinary contender's,
+ -- not by comparing its precedence against the ordinary contender's,
so an ordinary commemoration that would otherwise win on raw
- dignity is still dropped once any privileged one is also due (the
- asymmetric clause the brief and task report flag as deliberate, not
- present at "other II class" below); (ii) failing that, the slot is
- reserved SPECIFICALLY for a [Class2] candidate -- "de festo II
- classis" is a RANK restriction, not merely "whichever ordinary
- candidate has the best dignity": a III- or IV-class ordinary loser
- (a plain commemoration-only saint with no privilege of its own) has
- NO standing for this slot at all and must be entirely omitted, even
- when it is the only candidate present.
+ table order is still dropped once any privileged one is also due
+ (the asymmetric clause the brief and task report flag as
+ deliberate, not present at "other II class" below); (ii) failing
+ that, the slot is reserved SPECIFICALLY for a [Class2] candidate --
+ "de festo II classis" is a RANK restriction, not merely "whichever
+ ordinary candidate has the best table position": a III- or
+ IV-class ordinary loser (a plain commemoration-only saint with no
+ privilege of its own) has NO standing for this slot at all and
+ must be entirely omitted, even when it is the only candidate
+ present.
Fix, Task 16 (primary-source-verified + missalemeum-confirmed):
previously this fell back to "the best of [sorted], whatever its
@@ -739,21 +938,21 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate)
shows him "displaced" (omitted), never commemorated; the
pre-fix code admitted him regardless. *)
(match List.filter is_privileged sorted with
- | best :: _ -> [ best ]
+ | best :: _ -> [ drop_band best ]
| [] -> (
- match List.filter (fun (c, _) -> c.Precedence.cel.Celebration.rank = Class2) sorted with
+ match List.filter (fun (c, _, _) -> c.Precedence.cel.Celebration.rank = Class2) sorted with
| [] -> []
- | best :: _ -> [ best ]))
+ | best :: _ -> [ drop_band best ]))
| Class2, false ->
(* RG 111: "other II class: one" -- no privilege-override clause here,
unlike the Sunday case immediately above, so the day's one slot
- goes to whichever candidate outranks the rest by dignity alone,
- privileged or not. *)
- (match sorted with [] -> [] | best :: _ -> [ best ])
+ goes to whichever candidate outranks the rest by RG 113's own
+ table-of-precedence order ({!band}), privileged or not. *)
+ (match sorted with [] -> [] | best :: _ -> [ drop_band best ])
| (Class3 | Class4), _ ->
- (* RG 111: "III-IV class: at most two" -- by dignity, same as the
- non-Sunday II-class case, just with room for two. *)
- take 2 sorted
+ (* RG 111: "III-IV class: at most two" -- by RG 113's table order, same
+ as the non-Sunday II-class case, just with room for two. *)
+ List.map drop_band (take 2 sorted)
(* Task 11: RG 96 -- where an impeded I-class feast lands (docs/research/
rules-register.md §4, "Transfer/translation"). [band] decides who is
@@ -766,9 +965,11 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate)
RG 96's own text, register-transcribed: "the next following day that is
not I or II class." [is_blocking] reads that off [Vocab_ef.rank] --
- RG 96 speaks of the day's CLASS (RG 8's four-way dignity), not [band]'s
- finer 28-entry occurrence-table row, the same distinction {!admit} above
- already draws for RG 111 ({!dignity}, not [band]). *)
+ RG 96 speaks of the day's CLASS (RG 8's four-way "dignity"), not [band]'s
+ finer 28-entry occurrence-table row -- unlike {!admit} above, which (RG
+ 113, Task B/ef-rg16a) now DOES use [band] itself for its own selection
+ order; RG 96's own text has no such finer-table reading, so [is_blocking]
+ stays on [Vocab_ef.rank] alone. *)
let is_blocking (rank : Vocab_ef.rank) = rank = Vocab_ef.Class1 || rank = Vocab_ef.Class2
(* RG 96's own named exception (docs/research/rules-register.md §4,