| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Calendar.year now runs a placement pass after resolving every day: each
deferred candidate (RG 95's I-class-only right of translation, via
Precedence's Transfer disposition) is placed on the next day the rite's
new Rite.t.transfer_target names as admissible, transferred_in/out are
set on the two ends of the move, and the whole year is re-resolved to a
fixed point, bounded by a hard max_transfer_rounds = 64 guard.
transfer_target is rite-supplied rather than a generic search Calendar
drives itself: RG 96's 'not I or II class' is not derivable from band or
disposition alone (RG 91's own table lets a universal I-class feast
outrank an ordinary Sunday in a raw contest, yet RG 96 forbids landing a
translation there regardless), and the search's starting point is
rite-specific too (the Annunciation exception). It takes an occupant
callback exposing what Calendar currently resolves as observed on any
date, so the rite never has to re-implement occurrence resolution.
Two correctness properties drove most of the design:
- A candidate's permanent natural loss at its own origin (the layer entry
never moves) is rediscovered every round; left unfiltered this
oscillates a placed candidate between two dates forever, since its own
rank makes it look 'occupied' to a fresh search from its origin. Both
the round loop's gather and the final per-day omitted accounting filter
this out, keeping only sightings that are either brand new or losing at
a candidate's *current* target (a fresh RG 97-98 bump).
- RG 97-98's sort has to actually decide something, not just happen to
agree with Precedence.resolve's own tie-break next round: a
claimed-this-round overlay lets earlier-processed candidates in one
round block later ones in the same pass, so two coinciding I-class
feasts land on consecutive admissible days in the one round they
collide, in band order.
Also folds in Task 5's review finding: year_bounds clamps y to [1582,
9999] once, up front, rather than guarding start and stop independently
(each guard only ever covered one of the two rite.year_start calls,
leaving year 999 and year 100000 each able to call it out of domain
through the other branch).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
Transfers make per-date resolution impossible to do correctly: resolving 25
March can push a feast onto 26 March, and RG 97-98 has coinciding I-class
feasts transfer in table order, which needs global knowledge. So year computes
a whole liturgical year in one pass and day indexes into it. Pure, no cache, no
mutable state.
This commit resolves each day but does not yet place deferred transfers; they
are recorded with a reason. Task 6 adds the placement pass.
|