aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_of/precedence_of.mli
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-26 23:40:17 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-26 23:40:17 +0200
commit2f6d25aff88b4c76c61e36889f1c017b3cc8faee (patch)
treebaf3027229ef3b95eff2d7fdf0b479f8bdbb2a87 /lib/rites/rite_of/precedence_of.mli
parent1c0137dee8ff3858707281366662891a5ff01044 (diff)
downloadcolitur-2f6d25aff88b4c76c61e36889f1c017b3cc8faee.tar.gz
colitur-2f6d25aff88b4c76c61e36889f1c017b3cc8faee.zip
fix(of): anticipate St Joseph onto 18 March per Normae n. 56(f)
W1: when 19 March (St Joseph) falls on Palm Sunday, colitur sent him forward to Easter+9 instead of anticipating the solemnity backward to 18 March, the one rule in the whole Normae transfer machinery that names an earlier date. Live in 16 of 1583-2100 (1595, 1606, 1617, 1690, 1758, 1769, 1780, 1815, 1826, 1837, 1967, 1978, 1989, 2062, 2073, 2084). Blocked on a kernel contract: Rite.t.transfer_target required its result be strictly later than the impeded date, an obligation argued purely from the EF's RG 96. Traced against Calendar's actual placement pass rather than assumed unsafe: the round loop re-resolves the whole year fresh every round, and its two safety nets (~start/~stop; the round-count guard) both test the target's own value with no comparison against origin, so a bounded backward jump converges the same way a bounded forward one does. The one requirement that stays load-bearing is target <> origin: Precedence.resolve never deduplicates by slug, so a candidate returning its own argument unchanged would collide with itself in the same round's contest. Relaxed the obligation accordingly (rite.mli) and added the fourth transfer_target branch in Precedence_of, ahead of the existing three (Precedence_of.transfer_target, Rule 0). calendar.ml needed no logic change, only two comments that asserted "every genuine transfer moves forward" as a blanket fact. Unpinned the two tests that asserted the old wrong (forward) behaviour and replaced them with assertions of the correct one, plus a new direct unit test of the backward branch across two independent years. EF output is untouched: data/ef/ and lib/rites/rite_ef/ carry no diff, and colitur day/readings for 2026, 1583 and 9999 are byte-identical against a build of the prior commit. OF's own 2005-2050 window carries none of the 16 affected years and shows zero unexpected diffs; the full 1583-9999 domain still reaches a fixed point under the exhaustive sweep.
Diffstat (limited to 'lib/rites/rite_of/precedence_of.mli')
-rw-r--r--lib/rites/rite_of/precedence_of.mli95
1 files changed, 54 insertions, 41 deletions
diff --git a/lib/rites/rite_of/precedence_of.mli b/lib/rites/rite_of/precedence_of.mli
index 4ef81a5..1b9457b 100644
--- a/lib/rites/rite_of/precedence_of.mli
+++ b/lib/rites/rite_of/precedence_of.mli
@@ -90,6 +90,9 @@ val rules : (Vocab_of.season, Vocab_of.rank) Precedence.rules
Normae n. 60 and Normae n. 5 together -- n. 60 defers to n. 5 explicitly
("servatis iis quae n. 5 statuuntur"), so they are one rule set:
+ - St Joseph, impeded by falling on Palm Sunday, is ANTICIPATED BACKWARD to
+ the preceding Saturday, 18 March (n. 56(f), a fixed one-day
+ displacement, checked first as the most specific collision);
- the Annunciation falling anywhere in Holy Week goes to Easter + 8, the
Monday after the Second Sunday of Easter (n. 60, a fixed destination);
- any other solemnity falling on a Sunday of Advent, Lent or Easter goes to
@@ -97,52 +100,62 @@ val rules : (Vocab_of.season, Vocab_of.rank) Precedence.rules
which n. 5 excludes and which fall through to the general rule;
- otherwise, the nearest later day free of Tabula entries 1-8 (n. 60).
- KNOWN UNIMPLEMENTED FOURTH RULE, recorded rather than built (final review
- Important #5, ledger ruling R9): Normae n. 56(f) --
- "Sollemnitas S. Ioseph, ubi est de praecepto servanda, si cum Dominica in
- palmis de Passione Domini occurrit, anticipatur sabbato praecedenti, die
- 18 martii" -- St Joseph, where his solemnity is of precept, falling on
- Palm Sunday is ANTICIPATED (a BACKWARD transfer) to the preceding
- Saturday, 18 March, not carried forward like every rule above. It is
- conditional ("ubi est de praecepto servanda") and the following sentence
- gives Episcopal Conferences discretion where it is not, so the universal
- calendar legitimately not implementing it is defensible; not citing it at
- all was not, hence this note.
+ FOURTH RULE RESOLVED ([of-normae-56f]/W1, 2026-08-26; previously KNOWN
+ UNIMPLEMENTED here, final review Important #5, ledger ruling R9). Normae
+ n. 56(f): "Sollemnitas S. Ioseph, ubi est de praecepto servanda, si cum
+ Dominica in palmis de Passione Domini occurrit, anticipatur sabbato
+ praecedenti, die 18 martii" -- St Joseph, where his solemnity is of
+ precept, falling on Palm Sunday is ANTICIPATED (a BACKWARD transfer) to
+ the preceding Saturday, 18 March, not carried forward like the other
+ three rules. It is conditional ("ubi est de praecepto servanda") and the
+ following sentence gives Episcopal Conferences discretion where it is
+ not; the universal calendar implements the unconditional reading (the
+ same discipline every other rule here already applies to a Normae
+ clause with a conference-discretion escape hatch).
- CORRECTED: this note previously said "No sanctoral data exists yet
- (Phase 3), so nothing is WRONG today" and located the trigger at
- "25 March co-occurring with Palm Sunday". Both are now false and were
- already wrong about the date: St Joseph is 19 March, not 25 March (25
- March is the Annunciation, a separate solemnity with its own Holy-Week
- handling). Phase 3 shipped sanctoral data on this same branch
- (data/of/calendar-2002.sexp), so the defect this note warns about is now
- LIVE: 19 March coincides with Palm Sunday in 16 of the 1583-2100 years
- (e.g. 2062), and St Joseph lands forward at Easter+9 via rule 3 instead
- of anticipated backward to 18 March. Pinned, not silently wrong: see
- test_rite_of.ml's [test_joseph_palm_sunday_2062_known_wrong_forward_transfer]
- (2062, both grade and identity) and allow-list entries L8/L9
- (data/of/expected-divergences-litcal.sexp, 2035-03-17, a second, distinct
- year witnessing the same limitation).
+ HISTORY (kept for the record; the defect is fixed, not merely
+ documented, as of this revision). 19 March coincides with Palm Sunday
+ in 16 of the 1583-2100 years (e.g. 2062), and before this fix St Joseph
+ landed forward at Easter+9 via rule 3 instead of anticipated backward
+ to 18 March -- pinned on that shape until this revision, in
+ test_rite_of.ml's own now-renamed/re-asserted test (previously
+ [test_joseph_palm_sunday_2062_known_wrong_forward_transfer]; see that
+ file for its corrected form) and allow-list entries L8/L9
+ (data/of/expected-divergences-litcal.sexp) -- L8/L9 name a DIFFERENT
+ year, 2035-03-17, where St Joseph is impeded by Holy Monday rather than
+ coinciding with Palm Sunday itself, so n. 56(f)'s own trigger does not
+ apply there; that instance of the general rule's forward-only reach is
+ untouched by this fix and remains open on its own terms.
- Implementing it would need either a kernel signature change (out of
- scope for this rite module and forbidden by this fix wave) or a
- deliberate violation of a documented obligation {!Colitur_kernel.Calendar}
- depends on: n. 60's own text is "ad proximiorem diem transferatur" --
- the NEAREST day, not "proximiorem diem SEQUENTEM" (the nearest
- FOLLOWING one) -- yet every branch above only ever searches or lands
- forward. The strictly-LATER reading is not this rite's own choice; it is
- imposed by {!Colitur_kernel.Rite.t.transfer_target}'s own contract
- (rite.mli:77-84), which is justified there purely by the EF's RG 96. That
- is EF-shaped vocabulary constraining rite-agnostic kernel code -- the same
- class of item CLAUDE.md already tracks under "EF-shaped things still in
- rite-agnostic kernel code" -- so a real fix has to start at the kernel
- field, not in this module.
+ What unblocked it: {!Colitur_kernel.Rite.t.transfer_target}'s own
+ contract (rite.mli) used to require the result be STRICTLY LATER than
+ [origin] -- an obligation argued there purely from the EF's RG 96, i.e.
+ EF-shaped vocabulary constraining rite-agnostic kernel code, the same
+ class of item CLAUDE.md tracks under "EF-shaped things still in
+ rite-agnostic kernel code". Traced against {!Colitur_kernel.Calendar}'s
+ actual placement pass (calendar.ml's [place_transfers]/[year], not
+ against the obligation's own prior wording) rather than assumed unsafe:
+ the round loop re-resolves the whole year fresh every round with no
+ comparison against [origin] anywhere in its two safety nets ([~start]/
+ [~stop]; the round-count guard), so a target earlier than [origin] is
+ handled identically to a later one. The kernel contract was relaxed
+ (rite.mli, [transfer_target]'s own comment carries the full argument)
+ from "strictly later" to "different from origin" -- the one requirement
+ the trace showed is still load-bearing (a same-day target would hand
+ {!Colitur_kernel.Precedence.resolve} the same candidate twice, which it
+ does not deduplicate). No change was needed in calendar.ml's actual
+ logic; two of its comments were corrected for accuracy (the round-guard
+ rationale and the out-of-range rationale both previously asserted
+ "every genuine transfer moves forward" as a blanket fact).
Shaped to match {!Colitur_kernel.Rite.t.transfer_target} exactly so a later
phase wires it with no adaptation, and honours both obligations that field
- states: the result is strictly later than [origin], and the search carries
- its own step bound rather than relying on {!Colitur_kernel.Calendar}'s
- round guard, which bounds rounds rather than one call's internal search.
+ states: the result differs from [origin] (rule 0's own [Date.add_days
+ origin (-1)] trivially satisfies this, going backward rather than
+ forward), and the search carries its own step bound rather than relying
+ on {!Colitur_kernel.Calendar}'s round guard, which bounds rounds rather
+ than one call's internal search (rule 0 needs no bound at all -- it runs
+ no search, so it terminates trivially).
Never raises. If no free day is found within 400 days, or the walk would
otherwise have to probe past 31 December 9999 (past which