diff --git a/TODO.md b/TODO.md index 0e63687..0345b40 100644 --- a/TODO.md +++ b/TODO.md @@ -4767,3 +4767,32 @@ New tests in `tests/calendar/transfer.test.ts` cover the full chain (Leo/Hermene landing in strict native-date order past the whole 2033 span) and the Annunciation's own zero-commemoration case (2035). `npm test` (823 passed), `tsc --noEmit`, and `npm run build` all pass. + +### Correction, same day: `privileged-feria-major`'s own "duplex-majus+ still gets a nod" rule was wrong + +User pushed back immediately: "you dont commemorate -anything- from palm sunday through low +sunday" — and was right. The cascade mechanism above was built reusing `applyIncomingTransfer`'s +existing `privileged-feria-major` no-op unchanged, on the assumption that its sibling behavior +in `decideOccurrence` (a *native* duplex-majus+ candidate there gets a bare commemoration, only +below-majus ranks transfer) was itself correct — it had a citation (St. Mark, Duplex II. classis, +"merely commemorated" within the Easter Octave). That citation turned out to be a misreading, +not just unverified. + +Live-checked two more real dates before touching code: the Annunciation (Duplex I. classis, the +highest rank that exists) transfers with **zero** commemoration on Holy Thursday itself in years +it lands there (2027/2032/2043) — not just off a Sunday, off an ordinary Holy Week feria too. +Then checked whether this was Holy-Week-specific or true of every `privileged-feria-major` use: +Chair of St. Peter at Antioch (Duplex majus) transfers the same way off Ash Wednesday (2034). +So the exception was wrong everywhere this category is used, not just the Easter span. + +Fixed: `commemorations.ts`'s `privileged-feria-major` case now matches `privileged-sunday` +exactly — transfers every rank unconditionally, no commemoration ever, for anyone. Three +existing tests had the old (wrong) assumption baked into their expected values and needed +updating to real, live-verified outcomes, not just loosened: St. Mark (2030) now transfers +instead of being commemorated, landing May 5 that year (vu's own dense April/May calendar +pushes him well past his native date); the Annunciation's own 2035 landing moved from Apr 4 to +Apr 6, since two *earlier*-native-dated candidates (St. Benedict, also Duplex I. classis, native +Mar 21; St. Gabriel the Archangel, Duplex majus, native Mar 24) are now also correctly caught by +the same span that year and queue ahead of her. + +`npm test` (822 passed), `tsc --noEmit`, and `npm run build` all pass. diff --git a/src/calendar/commemorations.ts b/src/calendar/commemorations.ts index 192be9a..d0952ce 100644 --- a/src/calendar/commemorations.ts +++ b/src/calendar/commemorations.ts @@ -143,11 +143,22 @@ export function decideOccurrence( }; case 'privileged-feria-major': - // Ash Wednesday, Holy Week, the Easter Octave, the Vigil of - // Christmas — never displaced, same as privileged-sunday. - if (isAtLeast(sanctoral.rank, 'duplex-majus')) { - return { winner: temporalWinner, commemorations: [sanctoralCommemoration(sanctoral)] }; - } + // Ash Wednesday, Holy Week, the Easter Octave, Low Sunday, the + // Vigils of Christmas/Pentecost — never displaced, and never gives + // even a bare commemoration to any rank, same as privileged-sunday. + // The "duplex-majus+ still gets a nod" branch this case used to + // have was wrong, not just unverified: the one case that seemed to + // support it (St. Mark, Duplex II. classis, "merely commemorated" + // within the Easter Octave) turns out to have been a misreading — + // live-verified 2026-09-01, direct instruction ("you dont + // commemorate -anything- from palm sunday through low sunday"), + // confirmed with two more dates on top of the Annunciation/Palm- + // Easter-Sunday evidence already in the privileged-sunday case + // below: the Annunciation (Duplex I. classis) transfers with zero + // commemoration on Holy Thursday itself in years it lands there + // (2027/2032/2043), and Chair of St. Peter at Antioch (Duplex + // majus) transfers off Ash Wednesday the same way (2034) — so the + // exception was wrong for Ash Wednesday too, not just Holy Week. return { winner: temporalWinner, commemorations: [], diff --git a/src/calendar/types.ts b/src/calendar/types.ts index d72f49f..bcd0b4a 100644 --- a/src/calendar/types.ts +++ b/src/calendar/types.ts @@ -76,12 +76,16 @@ export type FeastClass = // 1617 engine, which the original single-tier model wrongly forbade) — // see calendar/commemorations.ts's rules for `privileged-feria` (behaves // like `ordinary-sunday`: Duplex+ wins outright) vs `privileged-feria-major` -// (behaves like `privileged-sunday`: never displaced at all, confirmed by -// checking St. Mark, Duplex II. classis, merely commemorated rather than -// winning within the Easter Octave). See data/calendar/temporal- -// categories.yml for exactly which days fall in which tier — some of that -// split (e.g. whether Pentecost's own Ember days share Lent's lesser tier -// or Easter's major one) is still a reconstructed guess, not verified. +// (behaves like `privileged-sunday`: never displaced, and — corrected +// 2026-09-01 — never gets even a bare commemoration at any rank either; +// an earlier claim that a duplex-majus+ candidate is "merely commemorated" +// there, citing St. Mark within the Easter Octave, turned out to be a +// misreading — direct instruction confirmed nothing is ever commemorated +// anywhere from Palm Sunday through Low Sunday, and live-checking Ash +// Wednesday found the same). See data/calendar/temporal-categories.yml +// for exactly which days fall in which tier — some of that split (e.g. +// whether Pentecost's own Ember days share Lent's lesser tier or +// Easter's major one) is still a reconstructed guess, not verified. // `privileged-feria-minor` is a third, weaker tier, added during the // December sanctoral pull after finding Advent's own ordinary (non-Ember) // ferias have real standing of their own in the live engine — a Simplex diff --git a/tests/calendar/april-sanctoral.test.ts b/tests/calendar/april-sanctoral.test.ts index 9c05dbf..ab262d7 100644 --- a/tests/calendar/april-sanctoral.test.ts +++ b/tests/calendar/april-sanctoral.test.ts @@ -18,12 +18,29 @@ describe('April sanctoral pull (first pass)', () => { expect(getDayCollect(day).status.en).toBe('verified'); }); - it('St. Mark is only ever commemorated, never wins, within the Easter Octave', () => { - const day = resolveDay('2030-04-25'); - expect(day.winner.kind).toBe('temporal'); - expect(day.commemorations).toEqual([ - { kind: 'sanctoral', id: 'st-mark', name: 'St. Mark, Evangelist', nameLa: 'Sanctus Marcus, Evangelista', rank: 'duplex-2-classis' }, - ]); + it('St. Mark gets nothing at all within the Easter Octave — not even a bare commemoration — and transfers past it 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). + 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'); + expect(landing.winner).toEqual({ + kind: 'sanctoral', + id: 'st-mark', + name: 'St. Mark, Evangelist', + nameLa: 'Sanctus Marcus, Evangelista', + rank: 'duplex-2-classis', + transferredFrom: '2030-04-25', + }); }); it('St. Robert beats St. Catherine of Siena on their shared monastic-calendar date', () => { diff --git a/tests/calendar/commemorations.test.ts b/tests/calendar/commemorations.test.ts index 07deb15..78fc39e 100644 --- a/tests/calendar/commemorations.test.ts +++ b/tests/calendar/commemorations.test.ts @@ -89,22 +89,23 @@ describe('decideOccurrence — privileged-feria', () => { }); describe('decideOccurrence — privileged-feria-major', () => { - it('is never displaced, same as privileged-sunday — duplex-majus and up are commemorated', () => { - // Verified: St. Mark, Duplex II. classis, only ever commemorated — - // never wins outright — within the Easter Octave. - for (const rank of ['duplex-majus', 'duplex-2-classis', 'duplex-1-classis'] as const) { + it('is never displaced, and never gives even a bare commemoration to any rank — everything transfers forward', () => { + // Corrected 2026-09-01, direct instruction: nothing is ever + // commemorated anywhere from Palm Sunday through Low Sunday — the + // earlier "duplex-majus+ still gets a nod" claim (citing St. Mark, + // Duplex II. classis, within the Easter Octave) was a misreading. + // Live-checked two more dates: the Annunciation (Duplex I. classis) + // transfers with zero commemoration on Holy Thursday itself in years + // it lands there, and Chair of St. Peter at Antioch (Duplex majus) + // does the same on Ash Wednesday — so the fix applies to every use + // of this category, not just Holy Week. + for (const rank of ['duplex-majus', 'duplex-2-classis', 'duplex-1-classis', 'duplex', 'semiduplex', 'simplex'] as const) { const result = decideOccurrence('privileged-feria-major', 'id', saint(rank)); expect(result.winner).toEqual({ kind: 'temporal', id: 'id' }); - expect(result.commemorations).toEqual([{ kind: 'sanctoral', id: 'x', name: 'St. Ereden', rank }]); + expect(result.commemorations).toEqual([]); + expect(result.transfer).toEqual({ candidate: saint(rank), direction: 'forward' }); } }); - - it('yields nothing short of duplex-majus — transfers instead', () => { - const result = decideOccurrence('privileged-feria-major', 'id', saint('duplex')); - expect(result.winner).toEqual({ kind: 'temporal', id: 'id' }); - expect(result.commemorations).toEqual([]); - expect(result.transfer).toEqual({ candidate: saint('duplex'), direction: 'forward' }); - }); }); describe('decideOccurrence — ordinary-sunday', () => { diff --git a/tests/calendar/transfer.test.ts b/tests/calendar/transfer.test.ts index 8f70a73..7cd903e 100644 --- a/tests/calendar/transfer.test.ts +++ b/tests/calendar/transfer.test.ts @@ -171,15 +171,17 @@ describe('multi-hop forward transfer through Holy Week/the Easter Octave', () => 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 — matching the real reference engine's own behavior, not just a native-date-order queue effect", () => { + 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), unlike a same-ranked - // *native* occurrence there (e.g. St. Mark, Duplex II. classis, - // commemorated when native within some other year's Octave) -- - // confirming a transferred-in candidate never gets the "duplex-majus+ - // still gets a nod" treatment on these days, only a native one does. + // 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. const easterSunday = resolveDay('2035-03-25'); expect(easterSunday.winner).toEqual({ kind: 'temporal', id: 'easter-sunday' }); expect(easterSunday.transferredAway?.candidate.id).toBe('annunciation'); @@ -189,7 +191,12 @@ describe('multi-hop forward transfer through Holy Week/the Easter Octave', () => expect(day.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'annunciation')).toBe(false); } - const landing = resolveDay('2035-04-04'); + // 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({ kind: 'sanctoral', id: 'annunciation',