aboutsummaryrefslogtreecommitdiff
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/calendar.ml45
1 files changed, 40 insertions, 5 deletions
diff --git a/lib/kernel/calendar.ml b/lib/kernel/calendar.ml
index 5d3572c..27e564f 100644
--- a/lib/kernel/calendar.ml
+++ b/lib/kernel/calendar.ml
@@ -304,14 +304,49 @@ let build_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.by_date)
day it landed and [transferred_out] here, not via [omitted] too --
double-booking it in both would fail Task 12's "appears exactly once"
reading of this day alone. *)
+ (* Whether a transferred/injected candidate is genuinely accounted for at
+ its assigned target -- true if it won the day outright there (every
+ [Transfer]-disposed candidate this kernel produced before a rite could
+ transfer a [Celebration.status = Commemoration_only] one: a losing
+ FEAST, which RG 96's own "next day not I or II class" guarantees an
+ unblocked day to win once it arrives -- [occupant_of] alone answered
+ this), OR if it survives at the target as one of the day's own admitted
+ COMMEMORATIONS instead (a shape [place_transfers] itself never used to
+ produce, because nothing before could dispose a [Commemoration_only]
+ candidate as [Transfer] -- {!Precedence.resolve} holds such a candidate
+ out of the band contest entirely, so it can never win a day outright,
+ only ever be commemorated on one; a rite is nonetheless free to
+ [Transfer] one to a named date, e.g. RG 80's Major Litanies, RG 81's
+ own "nihil fit in Officio" making [observed] structurally impossible
+ for it anywhere). [occupant_of] alone under-reports this second shape
+ as unsettled, which previously had no live witness to catch it: a
+ transferred candidate that only ever becomes a commemoration, never the
+ day's own office, was mislabelled here with [unconverged_reason] (a
+ hardcoded string, not a true read of the placement pass's own
+ convergence -- {!place_transfers} itself had already reached a fixed
+ point) and double-counted by {!Validate}'s own "duplicated" check
+ (sighted once in [omitted] here under that wrong label, and correctly
+ again in [commemorations] at its target) -- found by this kernel's own
+ exhaustive property sweep once a rite (Rite_ef, RG 80) first produced
+ this shape, not guessed at in advance. Kept rite-agnostic: nothing here
+ reads anything EF-specific, only {!Precedence.resolution}'s own
+ [observed]/[commemorations] fields, the same two channels
+ {!Liturgical_day.t} already promises never to lose. *)
+ let settled_at target slug =
+ let _, _, target_resolution = resolve_with_injected rite idx injected target in
+ let matches (c : 'r Precedence.candidate) =
+ Slug.equal c.Precedence.cel.Celebration.slug slug
+ in
+ matches target_resolution.Precedence.observed
+ || List.exists (fun (c, _) -> matches c) target_resolution.Precedence.commemorations
+ in
let unresolved c =
- let slug = Slug.to_string c.Precedence.cel.Celebration.slug in
- if Hashtbl.mem out_of_range slug then true
+ let slug = c.Precedence.cel.Celebration.slug in
+ if Hashtbl.mem out_of_range (Slug.to_string slug) then true
else
- match Hashtbl.find_opt assignment slug with
+ match Hashtbl.find_opt assignment (Slug.to_string slug) with
| None -> true
- | Some (_, target) ->
- not (Slug.equal (occupant_of rite idx injected target).Celebration.slug c.Precedence.cel.Celebration.slug)
+ | Some (_, target) -> not (settled_at target slug)
in
let reason_for c =
if Hashtbl.mem out_of_range (Slug.to_string c.Precedence.cel.Celebration.slug) then