Redesign the Easter-octave backlog: floor-gated, one per day
Deploy / deploy (push) Successful in 1m28s

Replaced the multi-day cascading queue with the actual intended design:
only candidates at or above duplex-majus are worth carrying out of the
Palm-Sunday-through-Low-Sunday span at all - below that, a candidate
simply lapses for the year. Of those that clear the floor, the Nth day
after Low Sunday celebrates the Nth one (native-date order), winning
outright with no rank fight, demoting whatever's natively there to a
commemoration - one per day, positional, not a free-day search.

Caught a real bug while re-verifying: the ordinary single-hop transfer
check was independently leaking Low Sunday's own signal into the next
day, letting a below-floor candidate bypass the new gate. Fixed by
excluding the whole span from that path.

Inventoried the only duplex-majus+ candidates that can ever fall in the
reachable window (St. Benedict, St. Gabriel the Archangel, the
Annunciation, St. Mark, Ss. Philip & James), then bumped St. Patrick's
own rank to duplex-majus by direct instruction, adding him as the 6th.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
This commit is contained in:
2026-09-01 15:06:55 -04:00
parent fc4ac33903
commit 5b3dc2e9d5
6 changed files with 241 additions and 180 deletions
+15 -6
View File
@@ -18,21 +18,27 @@ describe('April sanctoral pull (first pass)', () => {
expect(getDayCollect(day).status.en).toBe('verified');
});
it('St. Mark gets nothing at all within the Easter Octave — not even a bare commemoration — and transfers past it instead', () => {
it('St. Mark gets nothing at all within the Easter Octave — not even a bare commemoration — and surfaces on the 1st day of the Easter-octave backlog instead', () => {
// Corrected 2026-09-01: nothing is ever commemorated anywhere from
// Palm Sunday through Low Sunday, no exception for any rank (direct
// instruction; live-verified against the Annunciation on Holy
// Thursday and Chair of St. Peter at Antioch on Ash Wednesday, both
// Duplex-majus+ and both transferring with zero commemoration). Mark
// (Duplex II. classis) transfers off Apr 25, 2030 the same way; vu's
// own dense calendar that week pushes his actual landing all the way
// to May 5 (every day in between already spoken for).
// Duplex-majus+ and both transferring with zero commemoration). Per
// further direct instruction the same day, this doesn't cascade
// day-by-day looking for a free day: the Nth day after Low Sunday
// celebrates the Nth candidate that cleared the backlog's own
// duplex-majus+ floor that year (native-date order), winning
// outright and demoting whatever would have natively been there to
// a commemoration — see calendar/index.ts's `applyEasterOctaveBacklog`.
// Mark (Duplex II. classis, well above the floor) is the only
// candidate in 2030's own backlog, so he lands the 1st day after Low
// Sunday (Apr 28 that year) outright.
const native = resolveDay('2030-04-25');
expect(native.winner).toEqual({ kind: 'temporal', id: 'easter-sunday' });
expect(native.commemorations).toEqual([]);
expect(native.transferredAway?.candidate.id).toBe('st-mark');
const landing = resolveDay('2030-05-05');
const landing = resolveDay('2030-04-29');
expect(landing.winner).toEqual({
kind: 'sanctoral',
id: 'st-mark',
@@ -41,6 +47,9 @@ describe('April sanctoral pull (first pass)', () => {
rank: 'duplex-2-classis',
transferredFrom: '2030-04-25',
});
// St. Robert, native to this day, is demoted to a commemoration
// rather than winning or being skipped past.
expect(landing.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'st-robert')).toBe(true);
});
it('St. Robert beats St. Catherine of Siena on their shared monastic-calendar date', () => {
+62 -74
View File
@@ -108,96 +108,78 @@ describe('transfer mechanism (resolveDay integration)', () => {
});
});
// A forward transfer can chain through more than one impeded day in a
// row — the whole Holy Week/Easter Day/Easter Octave/Low Sunday span
// Anything impeded anywhere from Palm Sunday through Low Sunday
// (2026-09-01: found real, live-verified precedence bugs in exactly this
// area, then built the actual multi-hop mechanism per direct
// instruction). `resolveForwardTransferLanding` in calendar/index.ts
// walks up to `MAX_FORWARD_TRANSFER_LOOKBACK_DAYS` days back to find
// where a still-unlanded candidate actually lands, in strict native-date
// (FIFO) order against every other pending candidate.
describe('multi-hop forward transfer through Holy Week/the Easter Octave', () => {
it('2033: St. Leo I (native Easter Monday, Duplex) chains all the way past the whole privileged span to land on Easter+8 — verified against the real reference engine', () => {
// Easter 2033 is Apr 17. Live-verified against the reference engine
// (Monastic Tridentinum 1617, 2026-09-01): Leo transfers off Apr 11
// (Monday of Holy Week) with no commemoration anywhere in Holy Week,
// Easter Day, the Octave, or Low Sunday (Apr 24), landing outright on
// Apr 27 there (Apr 25/26 already spoken for by St. Mark and Ss.
// Cletus & Marcellinus in the reference's own calendar). vu's own
// calendar differs on Apr 25-27 (different saints assigned), so this
// asserts the *mechanism* — Leo reaching a day past the whole span in
// the right relative order — not the exact reference-engine landing
// day, which depends on which calendar is in play.
// area) doesn't cascade day-by-day looking for the next free day — per
// direct instruction, that's not this app's mechanism (it already has
// real precedent for stacking commemorations, and rejected both a
// same-day batch and an open-ended search). Instead: only candidates at
// or above `EASTER_OCTAVE_BACKLOG_FLOOR` (duplex-majus) are worth
// carrying forward at all — anything weaker simply lapses for the year,
// no trace, same as it already gets none within the span itself. Of
// those that clear the floor, the Nth day after Low Sunday celebrates
// the Nth one (native-date order), winning outright — no rank fight —
// and demoting whatever would have natively been there to a
// commemoration. See calendar/index.ts's `applyEasterOctaveBacklog`.
describe('the Easter-octave backlog (Palm Sunday through Low Sunday)', () => {
it('2033: nothing impeded that year clears the duplex-majus floor, so the backlog is empty and every impeded candidate simply lapses', () => {
// Easter 2033 is Apr 17, Low Sunday Apr 24. Every candidate vu's own
// calendar puts inside that span this year (St. Leo I, Duplex;
// St. Hermenegild, Semiduplex; Ss. Tiburtius/Valerian/Maximus,
// Simplex; St. Anselm, Duplex; Ss. Soter & Caius, Semiduplex;
// St. George, Semiduplex; St. Fidelis of Sigmaringen, Duplex) is
// below duplex-majus.
const holyMonday = resolveDay('2033-04-11');
expect(holyMonday.winner).toEqual({ kind: 'temporal', id: 'palm-sunday' });
expect(holyMonday.commemorations).toEqual([]);
expect(holyMonday.transferredAway?.candidate.id).toBe('st-leo-i');
for (const impededDate of ['2033-04-13', '2033-04-17', '2033-04-21', '2033-04-24']) {
const day = resolveDay(impededDate);
expect(day.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'st-leo-i')).toBe(false);
for (const afterLowSunday of ['2033-04-25', '2033-04-26', '2033-04-27', '2033-04-28', '2033-04-29']) {
const day = resolveDay(afterLowSunday);
const ids = ['st-leo-i', 'st-hermenegild', 'ss-tiburtius-valerian-and-maximus', 'st-anselm', 'ss-soter-and-caius', 'st-george', 'st-fidelis-of-sigmaringen'];
expect(day.commemorations.some((c) => c.kind === 'sanctoral' && ids.includes(c.id))).toBe(false);
expect(day.winner.kind === 'sanctoral' ? ids.includes(day.winner.id) : false).toBe(false);
}
// Lands the first day past the whole span not already spoken for by
// vu's own calendar (Apr 25 = St. Mark, native and unaffected).
const landing = resolveDay('2033-04-25');
expect(landing.winner).toEqual({
kind: 'sanctoral',
id: 'st-mark',
name: 'St. Mark, Evangelist',
nameLa: 'Sanctus Marcus, Evangelista',
rank: 'duplex-2-classis',
});
expect(landing.commemorations).toEqual([
{
kind: 'sanctoral',
id: 'st-leo-i',
name: 'St. Leo I, Pope, Confessor and Doctor of the Church',
nameLa: 'Sanctus Leo I, Papa, Confessor et Ecclesiæ Doctor',
rank: 'duplex',
transferredFrom: '2033-04-11',
},
]);
});
it('2033: three simultaneously-pending candidates (Leo, Hermenegild, Ss. Tiburtius/Valerian/Maximus) land in strict native-date order, none skipping ahead', () => {
const leo = resolveDay('2033-04-25').commemorations.find((c) => c.kind === 'sanctoral' && c.id === 'st-leo-i');
const hermenegild = resolveDay('2033-04-26').commemorations.find((c) => c.kind === 'sanctoral' && c.id === 'st-hermenegild');
const tiburtius = resolveDay('2033-04-27').commemorations.find(
(c) => c.kind === 'sanctoral' && c.id === 'ss-tiburtius-valerian-and-maximus',
);
expect(leo?.kind === 'sanctoral' ? leo.transferredFrom : undefined).toBe('2033-04-11');
expect(hermenegild?.kind === 'sanctoral' ? hermenegild.transferredFrom : undefined).toBe('2033-04-13');
expect(tiburtius?.kind === 'sanctoral' ? tiburtius.transferredFrom : undefined).toBe('2033-04-14');
});
it("2035: the Annunciation (native Easter Sunday itself, Duplex I. classis) transfers past the whole span with zero commemoration anywhere in it, queued behind two earlier-native-dated candidates also caught by the same span", () => {
// Easter 2035 is Mar 25 -- the Annunciation's own fixed date. Live-
// verified 2026-09-01: the real Monastic 1617 engine shows her
// transferring off Easter Day with no commemoration anywhere in Holy
// Week/the Octave/Low Sunday (Apr 1 that year) -- corrected the same
// day, direct instruction: nothing is ever commemorated anywhere in
// this span, at any rank, not even duplex-majus+ -- so a *native*
// occurrence there (e.g. St. Mark within some other year's Octave)
// gets exactly the same "transfers, no exception" treatment as a
// transferred-in candidate passing through, not the "still gets a
// nod" treatment this test used to assert existed.
it('2035: three candidates clear the floor (St. Benedict, St. Gabriel the Archangel, the Annunciation) and land on the 1st/2nd/3rd day after Low Sunday, in native-date order, each demoting that day\'s own native winner to a commemoration', () => {
// Easter 2035 is Mar 25 (the Annunciation's own fixed date), Low
// Sunday Apr 1. St. Benedict (native Mar 21, Duplex I. classis),
// St. Gabriel the Archangel (native Mar 24, Duplex majus), and the
// Annunciation (native Mar 25, Duplex I. classis) are all impeded
// and all clear the floor — live-verified 2026-09-01 that the
// Annunciation herself gets zero commemoration anywhere in the span.
const easterSunday = resolveDay('2035-03-25');
expect(easterSunday.winner).toEqual({ kind: 'temporal', id: 'easter-sunday' });
expect(easterSunday.transferredAway?.candidate.id).toBe('annunciation');
for (const impededDate of ['2035-03-26', '2035-03-29', '2035-04-01']) {
for (const impededDate of ['2035-03-21', '2035-03-24', '2035-03-26', '2035-03-29', '2035-04-01']) {
const day = resolveDay(impededDate);
expect(day.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'annunciation')).toBe(false);
}
// vu's own calendar also has St. Benedict (native Mar 21, also
// Duplex I. classis, also impeded that year) and St. Gabriel the
// Archangel (native Mar 24, Duplex majus) queued *ahead* of the
// Annunciation by native date -- they land Apr 4 and Apr 5
// respectively, pushing her actual landing to Apr 6.
const landing = resolveDay('2035-04-06');
expect(landing.winner).toEqual({
const day1 = resolveDay('2035-04-02');
expect(day1.winner).toEqual({
kind: 'sanctoral',
id: 'st-benedict',
name: 'St. Benedict, Abbot (Transitus)',
nameLa: 'Sanctus Benedictus, Abbas (Transitus)',
rank: 'duplex-1-classis',
transferredFrom: '2035-03-21',
});
expect(day1.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'st-francis-of-paola')).toBe(true);
const day2 = resolveDay('2035-04-03');
expect(day2.winner).toEqual({
kind: 'sanctoral',
id: 'st-gabriel-archangel',
name: 'St. Gabriel the Archangel',
nameLa: 'Sanctus Gabriel Archangelus',
rank: 'duplex-majus',
transferredFrom: '2035-03-24',
});
const day3 = resolveDay('2035-04-04');
expect(day3.winner).toEqual({
kind: 'sanctoral',
id: 'annunciation',
name: 'The Annunciation of the Blessed Virgin Mary',
@@ -205,5 +187,11 @@ describe('multi-hop forward transfer through Holy Week/the Easter Octave', () =>
rank: 'duplex-1-classis',
transferredFrom: '2035-03-25',
});
expect(day3.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'st-isidore-of-seville')).toBe(true);
// The backlog is exhausted after 3 days -- the 4th day after Low
// Sunday resolves with no override at all.
const day4 = resolveDay('2035-04-05');
expect(day4.winner.kind === 'sanctoral' ? day4.winner.transferredFrom : undefined).toBeUndefined();
});
});