diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-11 20:07:55 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-11 20:07:55 +0200 |
| commit | 953427d8d1e3a34994be53b60e18662ec26fef4e (patch) | |
| tree | 9d4876bbbfd44af738a00e0da9d0ae727542ede3 /lib | |
| parent | 8de560db7fb1b7b7d3ca93285068c6a4214e0bff (diff) | |
| download | colitur-953427d8d1e3a34994be53b60e18662ec26fef4e.tar.gz colitur-953427d8d1e3a34994be53b60e18662ec26fef4e.zip | |
kernel: carry omitted celebrations on Liturgical_day.t, reason and all
Precedence.resolution already tracked what happened to every losing
candidate -- commemorated, deferred, or omitted with a reason -- but
Liturgical_day.t had nowhere for the deferred and omitted buckets to land,
so Calendar dropped them at the door. Task 12's no-celebration-lost
invariant needs to read that accounting off the day result itself, not
re-resolve every day to reconstruct it, so a reason recorded nowhere is not
recorded.
Add Liturgical_day.omitted : ('r Celebration.t * string) list, after
transferred_out and before citations. Calendar.resolve_day now folds
resolution.omitted (Precedence's own native omissions, reasons intact) and
resolution.deferred (mapped to "deferred: transfer placement not yet
implemented (Task 6)") into it.
Adds a full-day accounting test against the whole Calendar pipeline: four
colliding sanctoral entries plus the day's feria, checked as a slug set
(matching test_precedence.ml's own nothing-silently-lost test) so a
candidate silently dropped or duplicated into two buckets would fail it,
plus an identity check that the deferred and admission-limit reasons don't
get swapped.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/kernel/calendar.ml | 21 | ||||
| -rw-r--r-- | lib/kernel/calendar.mli | 13 | ||||
| -rw-r--r-- | lib/kernel/liturgical_day.ml | 3 | ||||
| -rw-r--r-- | lib/kernel/liturgical_day.mli | 3 |
4 files changed, 29 insertions, 11 deletions
diff --git a/lib/kernel/calendar.ml b/lib/kernel/calendar.ml index fd23377..1cdd1fa 100644 --- a/lib/kernel/calendar.ml +++ b/lib/kernel/calendar.ml @@ -35,6 +35,15 @@ let year_bounds (rite : ('s, 'r) Rite.t) (y : int) : Date.t * Date.t = in (start, stop) +(* [resolution.deferred] (RG 96-98 transfer candidates) has nowhere to be + PLACED yet -- Task 6 adds the fixed-point pass that does -- but it must + still be accounted for on the day it lost, not silently dropped: Task + 12's no-celebration-lost invariant reads [Liturgical_day.omitted], so a + deferred candidate folds in there too, with its own reason distinct from + Precedence's native omissions ("omitted: yielded to a higher day", + "omitted: admission limit reached"). *) +let deferred_reason = "deferred: transfer placement not yet implemented (Task 6)" + (* RG 91's contest for one date: the temporal office against every sanctoral entry whose Date_spec resolves to it. [Layer.on_date] is keyed on exactly (month, day), which for a [Fixed] spec -- the only form Plan 2 ships -- is @@ -53,13 +62,10 @@ let resolve_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.by_date) (date : Date.t in let ctx = { Precedence.date; season = temporal.Temporal.season; weekday = temporal.Temporal.weekday } in let resolution = Precedence.resolve rite.Rite.rules ctx ~temporal:temporal_candidate ~sanctoral in - (* [resolution.deferred] (RG 96-98 transfer candidates) and - [resolution.omitted] (yielded/admission-limit losers) have no field to - land in on Liturgical_day.t yet, so both are simply absent from today's - result -- deliberately incomplete for a deferred candidate, which is - thereby neither observed nor commemorated here, and not yet placed on - any later day either ("deferred: transfer placement not yet implemented - (Task 6)"). Task 6's fixed-point pass closes this gap. *) + let omitted = + List.map (fun (c, reason) -> (c.Precedence.cel, reason)) resolution.Precedence.omitted + @ List.map (fun c -> (c.Precedence.cel, deferred_reason)) resolution.Precedence.deferred + in { Liturgical_day.date; rite = rite.Rite.id; @@ -69,6 +75,7 @@ let resolve_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.by_date) (date : Date.t List.map (fun (c, p) -> (c.Precedence.cel, p)) resolution.Precedence.commemorations; transferred_in = None; transferred_out = None; + omitted; citations = []; } diff --git a/lib/kernel/calendar.mli b/lib/kernel/calendar.mli index 50ff8f8..2469f2a 100644 --- a/lib/kernel/calendar.mli +++ b/lib/kernel/calendar.mli @@ -9,10 +9,15 @@ This module resolves each day's temporal-vs-sanctoral contest but does not yet place deferred transfers (RG 96-98): a losing candidate the - rite's rules send to [Precedence.Transfer] is absent from the result - entirely on this pass -- not observed, not commemorated, and - [transferred_in]/[transferred_out] both stay [None] everywhere. Task 6 - adds the fixed-point placement pass that closes this gap. *) + rite's rules send to [Precedence.Transfer] is not observed and not + commemorated on the day it lost, and [transferred_in]/[transferred_out] + both stay [None] everywhere -- but it is not silently dropped either. It + lands in that day's [Liturgical_day.omitted] with the reason ["deferred: + transfer placement not yet implemented (Task 6)"], alongside + [Precedence]'s own native omissions (yielded to a higher day; admission + limit reached), each with its own reason. Task 6 adds the fixed-point + placement pass that actually places these; until then, this is the + day's complete, honest accounting of what happened to every candidate. *) (** [year rite layer y] resolves every day of the liturgical year that opens in civil year [y]: from [rite.year_start y] through the day before diff --git a/lib/kernel/liturgical_day.ml b/lib/kernel/liturgical_day.ml index 65e3ba5..cbaca9c 100644 --- a/lib/kernel/liturgical_day.ml +++ b/lib/kernel/liturgical_day.ml @@ -14,6 +14,9 @@ type ('s, 'r) t = { (** arrived here from an impeded day *) transferred_out : Date.t option; (** this day's celebration went there *) + omitted : ('r Celebration.t * string) list; + (** with the reason, never silent -- Task 12's no-celebration-lost + invariant reads this *) citations : Citation.t list; (** always empty until Plan 4 *) } [@@deriving sexp] diff --git a/lib/kernel/liturgical_day.mli b/lib/kernel/liturgical_day.mli index 3c331c3..38e7c76 100644 --- a/lib/kernel/liturgical_day.mli +++ b/lib/kernel/liturgical_day.mli @@ -12,6 +12,9 @@ type ('s, 'r) t = { (** arrived here from an impeded day *) transferred_out : Date.t option; (** this day's celebration went there *) + omitted : ('r Celebration.t * string) list; + (** with the reason, never silent -- Task 12's no-celebration-lost + invariant reads this *) citations : Citation.t list; (** always empty until Plan 4 *) } [@@deriving sexp] |
