(** RG 91's Table of Precedence for the EF (1962) rite: ranks any candidate for a given day by its RG 91 entry number. See docs/research/rules-register.md §4, whose 28-entry transcription this module follows line by line. *) open Colitur_kernel (** The universal (base) sanctoral layer's {!Celebration.t}.layer id. A Sanctoral-origin candidate whose layer is anything else is an overlay: "proper" (RG 91 entries 12, 19, 23) unless its layer id also carries {!indult_prefix} ("indult", entries 13, 20). This id and the prefix are colitur's own data-modelling convention, not an RG citation -- RG 91 prescribes the ranking, not a machine encoding for it. Whichever task loads the real EF sanctoral base layer and its overlays must either reuse these two constants or this classifier will misfile them. *) val universal_layer : string (** See {!universal_layer}. *) val indult_prefix : string (** Slug suffix marking a celebration as a vigil (RG 91 entries 21, 26), e.g. "ef-ascension-vigil" -- colitur's own temporal-cycle convention (rite_ef/temporal_ef.ml's [named]). Also colitur's own convention, not an RG citation, exposed for the same reason as {!universal_layer}. See {!vigil_prefix} for the sanctoral data's own, different convention: a vigil can arrive named either way, and {!band}/{!disposition} must recognise both. *) val vigil_suffix : string (** Slug prefix marking a celebration as a vigil, e.g. "vigil-of-st-lawrence" -- the sanctoral data's own convention (data/ef/sanctoral.sexp, adopted verbatim from lectio's naming, per spec §4.4's "slugs are lectionary keys, not re-derived"). Also colitur's own convention, not an RG citation -- see {!universal_layer}. Task 10 bootstrapped four real sanctoral vigils named this way (St Lawrence 08-09, Sts Peter & Paul 06-28, the Assumption 08-14, the Nativity of St John the Baptist 06-23; a fifth, Christmas, is suppressed as a duplicate of the temporal cycle's own "ef-nativity-vigil" -- see data/ef/adjustments.sexp), none of which end in {!vigil_suffix}: without this prefix also being checked, {!band} would misfile all four at 16/24 (an ordinary feast of the same rank) instead of RG 91's 21/26, and RG 33's vigil omission ({!disposition}'s [is_vigil] test, the same predicate) would never fire for them either -- two rubrics silently broken for four celebrations, exactly what Task 7's review predicted when it asked for {!vigil_suffix} to be exposed. *) val vigil_prefix : string (** Slug prefixes marking a celebration as one of RG 91 entry 18's three Ember-day sets (Advent, Lent, September -- the Pentecost/Whitsun set is I class and matched by entry 10 before this is ever consulted). Also colitur's own convention mirroring rite_ef/temporal_ef.ml's own "ef- -ember-" slug format, not re-derived from first principles; exposed so a rename of that format has somewhere to be caught other than a silently-wrong entry 18. *) val ember_prefixes : string list (** Returned for a candidate shape RG 91's 28-entry table has no row for -- e.g. a [Class1] vigil that is not the Nativity or Pentecost (entries 5, 9 are the only I-class vigils the table names), or a [Class4] candidate also marked as a vigil. Deliberately outside 1..28 and larger than any real entry, so an unclassified candidate can never win an occurrence contest by accident; a caller that sees it back knows the shape needs a new rule, not a silently wrong one. *) val unclassified : int (** {!band}'s value for RG 91 entry 14's FIXED half (a sanctoral, i.e. fixed-date, Class2 feast of the Lord) -- see {!entry_14_movable_band}'s own comment for why entry 14 needs two distinct values at all. *) val entry_14_fixed_band : int (** RG 91 entry 14's own text (§4): "Festa Domini II classis, primum mobilia, deinde fixa" -- feasts of the Lord, II class, MOVABLE first, then fixed. {!band}'s value for the movable half (Holy Family, RG 17(b)) -- [entry_14_fixed_band - 1], strictly better than the fixed half without colliding with entry 13 or any other real table position. CORRECTED, fix round 1 (coordinator finding 1): this used to be a negative sentinel, justified only by "cannot collide with a real table position" -- a COLLISION argument, not an ORDERING one; since {!band} values are compared by plain [<], a negative value beats every I-class entry (1-13) as well as the fixed half, not merely the fixed half it was built to beat. {!band}'s own entries now use the real RG 91 number TIMES TEN throughout, reserving genuine headroom before every entry -- see precedence_ef.ml's own comment for the full citation, the counter- example that found this, and why entries 13, 20, 23 and 24 may need the same treatment if their own "movable then fixed" halves ever get a witness. (Corrected: this previously said "entries 12 and 20". RG 91 puts that clause at entry 13, not 12, and the split occurs at five rows in all -- 13, 14, 20, 23's third sub-item and 24.) *) val entry_14_movable_band : int (** [band ctx c]: RG 91's Table of Precedence. Returns the table's own entry number TIMES TEN -- I class 10-130, II class 140-210, III class 220-260, IV class 270-280; lower wins (see {!Precedence.rules.band}) -- EXCEPT where the table's own text states an exception: entry 8 (All Souls) reads "yields to an occurring Sunday", so on a Sunday this returns a value that loses to entry 15 (150) rather than 80 (see the comment on entry 8 in precedence_ef.ml for the exact value and why); entry 14 (Feasts of the Lord, II class) splits into {!entry_14_movable_band} and {!entry_14_fixed_band} per the table's own "primum mobilia, deinde fixa". ALSO {!unclassified} for any [Cel.Commemoration_only] candidate, checked first, ahead of every rank-keyed branch -- CORRECTED, Task B fix round 1 (coordinator finding 1): RG 91's own table enumerates only "dies liturgici" (entry 24's own text, "Festa III classis..." -- FEASTS), so a celebration reduced to a bare commemoration was never a row in the table to begin with, confirmed by the calendarium's own notation (a genuine feast carries a class number on its occurrence line; a bare "Commemoratio" carries none). Before this fix, a [Commemoration_only] candidate silently borrowed the SAME entry number as a genuine [Feast] of its own [rank] -- manufacturing ties {!Precedence.compare_by} and {!compare_precedence} both then had to break some other way, that RG 91 itself never created. Total over every candidate {!Precedence.resolve} or {!Calendar} can construct, including shapes the 1962 table itself does not describe (see {!unclassified}). *) val band : Vocab_ef.season Precedence.context -> Vocab_ef.rank Precedence.candidate -> int (** RG 33's marker: every Sunday slug this rite's temporal cycle produces (temporal_ef.ml's [named] and [sunday_slug]) contains this substring; nothing else {!band} classifies does. Also colitur's own convention, not an RG citation -- see {!universal_layer} -- exposed for the same reason as {!vigil_suffix}: a rename of temporal_ef's Sunday-slug format has somewhere to be caught other than a silently-wrong RG 33 disposition. *) val sunday_marker : string (** The Major Litanies' own invented slug (RG 80, Caput X "De Litaniis maioribus et minoribus" §A; RG 81; RG 109(f) -- see the .ml's own citation, above [privilege_of], for the full text). Not adopted from lectio, which does not compute the Major Litanies at all (this task's own measurement) -- colitur's own key, data/ef/adjustments.sexp's matching [Add] entry. Exposed for the same reason as {!annunciation_slug} below: a future rename in the data with no matching rename here silently stops {!disposition}, {!admit}'s RG 109(f) privilege, and {!transfer_target}'s own RG 80 branch from ever recognising a live candidate again. *) val major_litanies_slug : string (** RG 112(d)'s own closed, hand-verified list of sanctoral entries that are themselves a feast/commemoration OF the Blessed Virgin Mary in her own right -- see the .ml's own citation for the full derivation and the entries considered and excluded. A celebration is "of the BVM" for {!disposition}'s RG 112(d) branch when its [subject] is [Subject.Bvm] OR its slug is on this list ({!Celebration.t}'s [subject] field is NOT reliably [Bvm] for most Marian sanctoral entries -- bootstrapped from lectio, most carry [Saint] instead, this list is the reliable signal). Exposed (task 5, the Creed, RG 475(c) "festis II classis... B. Mariae Virg.") so a second rite-local consumer can test the identical Marian-identity question without re-deriving its own list and risking drift from this one -- the same reasoning {!vigil_feast_table} and {!is_vigil} are already exposed for. *) val marian_slugs : string list (** [disposition ~winner ~loser]: RG 92-95, 33, 21-27, 94 (docs/research/ rules-register.md §4, "Occurrence", "Vigils" and "Caput IV, 'De feriis'"). What becomes of a losing candidate, decided by the LOSER's own rank and status (RG 95), except RG 33's vigil omission, which also reads the winner: - a loser whose slug is {!major_litanies_slug} (ef-major-litanies task) is [Transfer], NOT [Commemorate], when the WINNER's own slug is Easter Sunday or Easter Monday (RG 80: 25 April's Major Litanies are transferred to the following Tuesday, always Easter+2, when 25 April is itself one of those two days) -- checked first, ahead of the [Commemoration_only] bullet immediately below (of which this would otherwise be a case), because RG 80's own condition overrides RG 81's default "always Commemorate" for these two specific winners only. See the .ml's own citation, and {!transfer_target}'s own RG 80 branch for where the transfer lands; - a {!Celebration.status} of [Commemoration_only] is always [Commemorate] otherwise (checked first among the remaining cases: it can never win -- see {!Precedence.resolve} -- and, by that same status's own definition, already denotes an office with nothing left to translate under RG 95's ordinary rule, so it never transfers either there; not itself a further RG citation beyond RG 93's general four-mechanism statement above); - a [Class2] or [Class3] loser whose slug marks it a vigil ({!vigil_suffix} OR {!vigil_prefix} -- both conventions this codebase's data uses, see {!vigil_prefix}'s own comment) is [Omit] when the winner is any Sunday ({!sunday_marker}) or itself [Class1] (RG 33 -- entirely omitted, not merely commemorated). A [Class1] vigil (Nativity, Pentecost) is outside RG 33 entirely -- RG 30 makes it preferred to any feast whatsoever, so a real one can never reach this function as a [loser] in the first place (see the .ml's own comment on [is_omissible_vigil] for the full argument); - any other [Class1] loser that is NOT a Sunday ({!sunday_marker}) is [Transfer] (RG 95 -- only I-class FEASTS have the right of translation; RG 91's own table lists Sundays as a separate row, entry 6, from feasts, entries 11-13, so a Sunday is never a "feast" in RG 95's sense and does not transfer even when impeded by a higher I-class day. This is also what moves All Souls, RG 91 entry 8, once it loses to an occurring Sunday -- WHERE it lands is {!Rite.t.transfer_target}'s job, not this function's); - a TEMPORAL-origin, non-vigil loser of [Class4] is [Omit] (RG 26, "Caput IV, De feriis" -- "every feria not named in [RG 23-25] is IV class ... and IV-class ferias are NEVER commemorated." A SEPARATE rule from RG 109's honour-class one immediately below, keyed on ferial CLASS rather than on RG 109's privilege letters: RG 109 sorts commemorations that already exist into honour classes (RG 108's differing liturgical hours), it does not itself decide which offices have the right to be commemorated at all -- that is Caput IV's own business. A SANCTORAL loser of the same rank is unaffected by this branch (the [is_temporal] guard): RG 21 defines "feria" as any weekday, never a saint's day, and RG 111(c)/(d) admit an "ordinary" commemoration of a losing SAINT freely, with no such class-keyed gate); - a loser whose [subject] is [Lord] AND whose WINNER's [subject] is also [Lord] is [Omit] (PRIMARY authority: RG 95's own second paragraph, present in all three source documents including the electronic transcription -- "if two feasts of the SAME DIVINE PERSON... occur together, [the Office] is made of the one higher in the table of precedence, and the OTHER IS OMITTED"; corroborated by RG 112(a) -- a feast or mystery of one Divine Person excludes a commemoration of another feast or mystery of the SAME Divine Person -- and by the Holy Family Mass propers' own more specific 13-January rubric on both photographic scans; live witness: Holy Family, RG 17(b), excluding the fixed Commemoration of the Baptism of the Lord on the years 13 January is itself Holy Family's own date -- see the .ml's own comment for the full primary text of all three); - everything else -- including an impeded I-class Sunday, and a SANCTORAL loser of any rank below I class -- is [Commemorate], carrying its real RG 109 privilege (see {!admit} below); RG 109(a) lists "of a Sunday" as a privileged commemoration category precisely because an impeded Sunday stays put rather than moving to another day, and RG 24/25 make a losing II- or III-class FERIA's own commemoration mandatory when impeded, not merely eligible. Total over every winner/loser pair {!Precedence.resolve} or {!Calendar} can construct: [Vocab_ef.rank] (RG 8) and {!Celebration.status} are both closed variants, and the five cases above -- an if/else-if chain ending in the unconditional [Commemorate] catch-all -- exhaust every value those two fields can take between them; there is no sixth, "unclassified" case the way {!band} needs one, because this function's own return type has no such slot to fall into by accident. *) val disposition : winner:Vocab_ef.rank Precedence.candidate -> loser:Vocab_ef.rank Precedence.candidate -> Precedence.disposition (** RG 33's third omission trigger. Given a candidate, the feast it is a VIGIL of, when that vigil is one this rule can omit; [None] otherwise -- which is every candidate that is not one of the five II/III-class vigils in the 1962 universal calendar. Answers only the identity question. Whether the feast actually kept its own day is {!Colitur_kernel.Calendar}'s to determine, because only the kernel holds the settled post-transfer placement of the whole year; see {!Colitur_kernel.Precedence.rules.vigil_feast} for the division of labour and for why the feast is NAMED here rather than inferred there. The I-class vigils (Nativity Eve, Pentecost Vigil) are outside this rule by RG 33's own wording and outside losing at all by RG 30, so they are absent from the table and this returns [None] for them -- the same argument {!is_omissible_vigil} already makes for the rule's other two triggers. *) val vigil_feast : Vocab_ef.rank Precedence.candidate -> Slug.t option (** The (vigil slug, feast slug) pairs {!vigil_feast} answers from, exposed so the test suite can assert both directions against the shipped sanctoral data: that every slug named here exists, and that no II/III-class vigil in the data is missing from it. Neither is checkable by a type, and both fail SILENTLY -- an unmatched slug simply makes RG 33 inert for that entry. *) val vigil_feast_table : (string * Slug.t) list (** Whether a slug names a vigil, by either of the two conventions this calendar's data uses (a "-vigil" suffix from the temporal cycle, a "vigil-of-" prefix from the sanctoral bootstrap). Exposed for the same table-drift assertions as {!vigil_feast_table}. *) val is_vigil : string -> bool (** Whether a rank is one RG 33 can omit -- II or III class. I-class vigils are outside the rule (RG 30). Exposed alongside {!is_vigil}. *) val is_omissible_vigil : Vocab_ef.rank -> bool (** Slug prefix marking a celebration as one of RG 91 entry 17's days within the Octave of the Nativity (29-31 Dec -- 26-28 Dec are Stephen, John, the Innocents, sanctoral, never this prefix). Also colitur's own convention mirroring rite_ef/temporal_ef.ml's own "ef-nativity-octave-day-%d" slug format, not an RG citation -- see {!universal_layer} -- exposed for the same reason as {!vigil_suffix}: a rename of that format has somewhere to be caught other than a silently-wrong RG 109(c) privilege. *) val nativity_octave_prefix : string (** The September set of {!ember_prefixes}, broken out on its own because RG 109(d) (§4, "Commemorations") privileges September Ember days under its own name, and September sits outside RG 109(e)'s three named seasons (Advent, Lent, Passiontide) under any reading of that clause -- NOT because the Advent and Lent Ember sets need excluding from (e), which they do not: (e)'s own bare text privileges them too, the same as any other Advent/Lent feria (see {!Precedence_ef.privilege_of}'s own (e) comment in the .ml for the full argument, corrected fix round 1). {!ember_prefixes} is built from this constant, not a duplicated literal, so the two cannot silently drift apart. *) val september_ember_prefix : string (** [admit ~observed ~temporal comms]: RG 108-111 (docs/research/rules-register.md §4, "Commemorations"). How many of [comms] -- each already tagged with its real RG 109 privilege by {!disposition}, and its own {!band} value (RG 91's table-of-precedence entry, computed once by {!Precedence.resolve} -- see {!Precedence.rules.admit}'s own doc) -- RG 111 admits, and which, given the day actually observed: - [observed] a [Class1] day: none, except at most one privileged commemoration (the highest-{!band}-precedence one, if several are due) -- an ordinary one is never admitted here, no matter how many are due; - the CIVIL DAY is a [Class2] Sunday ([temporal]'s slug carries {!sunday_marker} -- CORRECTED, fix round 1, RG16(a) task: read off [temporal], not [observed]; see below): one, subject to TWO conditions, not one -- (i) a privileged commemoration, whenever due, categorically takes the day's one slot over any ordinary one, not by comparing table position, so an ordinary commemoration that would otherwise win on table order is still dropped; (ii) failing that, the slot is reserved for a [Class2] candidate SPECIFICALLY ("de festo II classis", RG 111(b)'s own wording -- a RANK FLOOR, not "whichever ordinary candidate has the best table position"): a III- or IV-class ordinary loser has no standing for this slot at all and is admitted nothing, even when it is the only candidate due; - the civil day is any other [Class2] day: one, by {!band}'s table order alone -- no privilege override and no rank floor, unlike the Sunday case immediately above; - [observed] a [Class3] or [Class4] day: at most two, by {!band}'s table order alone. RG 109(f) (ef-major-litanies task): every candidate in [comms] arrives already tagged with its real privilege by {!disposition}, which now includes {!major_litanies_slug} tagged [Privileged] -- this function itself needed no change for it, the same "no new machinery" shape RG 109(a)/(c)/(d)/(e) already had: it competes for whichever of the four cases above its host day falls into, exactly like any other privileged candidate. ADDED, ef-holyname-rg110 task: RG 110 (docs/research/rules-register.md §4, Caput XIV) -- "In Officio et Missa S. Petri semper fit commemoratio S. Pauli, et vicissim... pro unica habeantur" -- whenever [observed], or one of the candidates the FOUR cases above already admit, is one of RG 110's own three trigger slugs (25 January's [conversion-of-st-paul], 22 February's [chair-of-st-peter], 30 June's [in-commemoratione-sancti-pauli-apostoli]), its companion Apostle ([peter]/[paul]/[commemoration-of-st-peter] respectively) is looked up directly in [comms] and PREPENDED to whatever the four cases above already produced -- UNCAPPED, i.e. added on top of, not competing for, any of those cases' own admission limits ("pro unica habeantur": the pair is held as ONE for RG 111's own counting purposes, so it never consumes a second ordinary/privileged slot). This is layered on AFTER the four cases above decide the day's ordinarily-capped result, uniformly regardless of which of the four produced it -- see the .ml's own [rg110_additions] for the full citation, the measured blast radius, and why the two lookups (observed itself, and each already-admitted candidate) together cover both of RG 110's own textual triggers, (a)/(b) and (c). CORRECTED, Task B (branch ef-rg16a): this used to sort by [Vocab_ef.rank] (RG 8's coarse four-class "dignity") and break same-rank ties alphabetically by slug -- an engineering convention with no rubrical warrant, cited nowhere. Measured, fix round 1 (2005-2050, reproducible via a temporary [git worktree] at the pre-fix commit): a same-[rank] tie existed in the candidate pool offered to [admit] on 599 days, most never reaching a real decision (RG 111's own admission count often has room for both, e.g. a III-class day admits two); of those, reversing the slug tie-break ALONE actually changed the ADMITTED SET on 65 days and only the printed ORDER of an already-admitted pair on a further 149 (docs/research/rules-register.md §6.1's own full account, superseding this comment's earlier, unreproduced "66 days" claim). RG 113's own second sentence, previously uncited (docs/research/rules-register.md §4's RG 113 entry), is the real rule: *"in admittendis et ordinandis aliis commemorationibus, servetur ordo tabellae praecedentiae"* -- admitting and ordering commemorations both run on {!band}'s own finer RG 91 table, not RG 8's four classes. {!band} needs a [context] (date/season/weekday) this function does not receive on its own, so {!Precedence.resolve} computes it once, generically, and hands each candidate its own value as the trailing [int] on [comms]'s triples (see {!Precedence.rules.admit}). Ties break on slug ONLY within one identical [band] value -- CORRECTED, fix round 1 (coordinator finding 1): [band] itself used to hand a [Cel.Commemoration_only] candidate the same table entry as a genuine [Cel.Feast] of its own rank (RG 91's table has no row for a bare commemoration at all), manufacturing most of the "identical [band] value" ties this comment used to describe as RG 113's own residual. Fixed in [band] itself; checked afterward (2005-2050 and the full 1583-9999 domain, register §6.1) and found EMPTY of any remaining "two different real table entries tied" case -- the slug fallback below is exercised only between two [Commemoration_only] candidates both at {!unclassified}, still an uncited engineering convention (register §6 "RG 113 tie-break"), matching {!Precedence.compare_by}'s own fallback, so the result never depends on the order [comms] arrives in. [temporal] -- CORRECTED, fix round 1 (RG16(a) task): this function used to read the Sunday/non-Sunday split off [observed]'s own slug, which silently assumed [observed] IS the day's temporal-cycle office. RG 16(a) breaks that assumption on purpose: a Feast of the Lord holds an impeded II-class Sunday's own place "cum omnibus iuribus et privilegiis" (RG 16(a)'s own text), so the day still counts as a "dominica II classis" for RG 111(b) even though [observed] is now the FEAST. [temporal] -- {!Precedence.rules.admit}'s own parameter, the day's temporal-cycle candidate regardless of who won -- is the reliable signal; oracle-confirmed (6 August: Sunday excludes a Class3 saint Sixtus II from the freed slot, an ordinary weekday admits him). Every row of the previous behaviour where [observed] and [temporal] coincide (no RG 16(a) displacement occurred) is unaffected. Every candidate this returns is a value taken unchanged from [comms], never rebuilt: {!Precedence.resolve}'s own [dropped]/[omitted] accounting tells an admitted candidate from a dropped one by physical equality on the candidate value, so anything this function admitted stays admitted, and anything it did not is reported in {!Precedence.resolution.omitted}, never silently lost. Total: every [Vocab_ef.rank] is one of the four cases above, and every branch is itself total over an empty or arbitrarily long [comms]. *) val admit : observed:Vocab_ef.rank Precedence.candidate -> temporal:Vocab_ef.rank Precedence.candidate -> (Vocab_ef.rank Precedence.candidate * Precedence.privilege * int) list -> (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list (** The Annunciation's own bootstrapped slug (data/ef/sanctoral.sexp, Task 10), reused verbatim by {!transfer_target} to recognise RG 96's named exception. Not an RG citation -- see {!universal_layer} -- exposed so a future re-bootstrap that renames the slug has somewhere to be caught other than a silently-wrong transfer target. *) val annunciation_slug : string (** [transfer_target c origin occupant]: RG 96 (docs/research/rules-register .md §4, "Transfer/translation") -- where an impeded I-class feast, once {!disposition} has decided it is [Transfer]-disposed, is placed. This *is* {!Colitur_kernel.Rite.t}.transfer_target; see that field's own fuller rationale for why the search has to be rite-supplied at all. RG 80 (ef-major-litanies task): checked FIRST, ahead of everything below -- when [c] is {!major_litanies_slug}, the target is Easter+2 directly, with no RG 96 search at all (this candidate is only ever a commemoration at its target, RG 81's own "nihil fit in Officio", which needs no unoccupied day the way a displaced FEAST does; the general RG 96 search below would in fact walk PAST Easter+2, since it is itself I-class). See the .ml's own citation for the full text and the "why not the general search" argument. RG 96's own rule, for every OTHER candidate: the next following day whose currently-resolved occupant is not I or II class (read off [Vocab_ef.rank], RG 8's dignity -- not {!band}'s finer occurrence-table entry, the same distinction {!admit} draws for RG 111). This general target is computed for every such candidate, always, first. RG 96's own named exception (Attamen (a), primary-source-verified -- see {!annunciation_slug}'s comment for the Latin and the register's own correction note): for the Annunciation specifically, IF that general target would fall after Easter Sunday itself ("quando est transferendum post Pascha" -- when it is to be transferred past Easter), the Annunciation is placed instead at the Monday after Low Sunday (its [sedes propria]), searching onward from there only if that day is itself occupied by a blocking class. The exception is CONDITIONAL, not unconditional: an Annunciation impeded for a reason that resolves BEFORE Easter (e.g. an ordinary Lent Sunday with a free feria the next day) takes the general target like any other I-class feast. Operationally the condition holds exactly when 25 March falls close enough to Easter that the general walk crosses it -- concretely, when 25 March itself falls within Holy Week or Easter Week. Total, terminating, and its result is always strictly later than [origin] -- {!Colitur_kernel.Rite.t}.transfer_target's own obligations, which {!Colitur_kernel.Calendar}'s placement pass relies on and its own round guard does not itself enforce (calendar.ml's [place_transfers] bounds ROUNDS across a whole year, not one call's internal walk). Terminating by a structural bound on the internal walk (max 400 days, an engineering ceiling, not an RG citation -- see the .ml) AND a guard at {!Colitur_kernel.Date}'s own domain ceiling (31 December 9999, beyond which probing [occupant] can itself raise -- see the .ml's [domain_max_date]), not by an argument about the real 1962 calendar's own structure, so a rite/data shape this function has not anticipated fails FINITELY rather than hanging or crashing the caller. Strictly later than [origin]: the general search starts at [origin + 1] and only ever advances forward from there; the Annunciation's own alternate starting point is provably later than 25 March for every representable year (Easter's documented range, register §0) -- see the .ml for the full argument. *) val transfer_target : Vocab_ef.rank Precedence.candidate -> Date.t -> (Date.t -> Vocab_ef.rank Celebration.t) -> Date.t