Full precedence sweep of temporal-categories.yml, two more real bugs
Deploy / deploy (push) Successful in 1m27s
Deploy / deploy (push) Successful in 1m27s
Following the Ascensiontide fix, swept every remaining unverified Sunday/ feria precedence tier against the live reference engine: - commemorations.ts's privileged-sunday rule was itself wrong: a duplex-majus+ candidate was given a bare commemoration instead of transferring, never actually verified. Live-checked the Immaculate Conception (transfers off Advent II) and the Annunciation (transfers off Palm/Easter Sunday) - confirmed correct by direct instruction. privileged-sunday now transfers every rank unconditionally. - eastertide's own Sunday tier was backwards (privileged-sunday instead of ordinary-sunday) for the ordinary Sundays after Easter - live- verified twice (Finding of the Holy Cross, St. Mark both win outright). Easter Day itself still needs the stronger tier, now special-cased directly since Sundays never consult the offset table. Six existing tests updated to reflect real, live-verified behavior - each had the old wrong "commemorated in place" assumption baked into its expected value. Remaining unverified entries (Vigil of Pentecost/ Christmas, Corpus Christi/Sacred Heart) documented as such rather than silently assumed correct. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
This commit is contained in:
@@ -4653,3 +4653,55 @@ season-wide overrides, ahead of the plain default.
|
|||||||
New regression tests in `tests/hours/matins.test.ts` (`Sunday within the Octave of Ascension`)
|
New regression tests in `tests/hours/matins.test.ts` (`Sunday within the Octave of Ascension`)
|
||||||
assert both the calendar winner (Sunday over the saint) and the full Matins content. `npm test`
|
assert both the calendar winner (Sunday over the saint) and the full Matins content. `npm test`
|
||||||
(821 passed), `tsc --noEmit`, and `npm run build` all pass.
|
(821 passed), `tsc --noEmit`, and `npm run build` all pass.
|
||||||
|
|
||||||
|
### Full sweep of `temporal-categories.yml` — two more real bugs found and fixed (2026-09-01)
|
||||||
|
|
||||||
|
Follow-up, same day: user pushed back hard on the "not a bug" framing above ("the immaculate
|
||||||
|
conception transfer is correct... annuncition should transfer") and asked to sweep every
|
||||||
|
remaining unverified entry in `data/calendar/temporal-categories.yml`, not just the one already
|
||||||
|
fixed. Found two more real, calendar-wide bugs:
|
||||||
|
|
||||||
|
1. **`commemorations.ts`'s `privileged-sunday` case itself was wrong**, not just one season's
|
||||||
|
tag. It gave any duplex-majus+ candidate a bare commemoration instead of transferring —
|
||||||
|
never actually verified against a primary source. Live-checked 3 independent cases: the
|
||||||
|
Immaculate Conception (Duplex I. classis) transfers off Advent II with **no** commemoration
|
||||||
|
(2024/2030/2041); the Annunciation (also Duplex I. classis) transfers off both Palm Sunday
|
||||||
|
and Easter Sunday itself the same way (2029/2035/2040/2046). Confirmed by direct instruction
|
||||||
|
as correct (with a known follow-on: the Annunciation's *real* rule jumps it past the whole
|
||||||
|
Easter Octave rather than the next open day, which lands inside it — blocked on the
|
||||||
|
not-yet-built Easter-octave mechanism, noted but not built here). Fixed: `privileged-sunday`
|
||||||
|
now transfers every rank unconditionally, no exception.
|
||||||
|
|
||||||
|
2. **`eastertide`'s own `sunday` tier was backwards** — coded `privileged-sunday`, should be
|
||||||
|
`ordinary-sunday` for the *ordinary* Sundays after Easter (not Easter Day itself, not within
|
||||||
|
its Octave, both already separately handled). Live-verified twice: the Finding of the Holy
|
||||||
|
Cross (Duplex-II-classis, 2026-05-03) and St. Mark (Duplex-II-classis, 2027-04-25) both win
|
||||||
|
outright over "Dominica IV Post Pascha," with the Sunday merely noted as a `Tempora` line
|
||||||
|
underneath — exactly `ordinary-sunday`'s rule. Easter Day itself still needs the stronger
|
||||||
|
tier (confirmed by the Annunciation evidence above), so it's now special-cased directly in
|
||||||
|
`calendar/temporal.ts`'s `resolveTemporalCategory` (Sundays never consult `offsets`, so this
|
||||||
|
couldn't be expressed as a plain offset entry the way every other exception in that file is).
|
||||||
|
|
||||||
|
**Six test failures from the `privileged-sunday` fix, all genuine fallout, not regressions** —
|
||||||
|
each test had encoded the old, wrong "commemorated in place" behavior as its expected value:
|
||||||
|
St. Andrew (Duplex II. classis) no longer commemorated alongside Advent I, but shown
|
||||||
|
transferred away (landing on Dec 1, where he now wins outright — `privileged-feria-minor`'s
|
||||||
|
Semiduplex+ threshold easily clears); St. Antoninus similarly now wins outright on his own day
|
||||||
|
(the real Easter-6 Sunday) instead of being pushed to the next day by the old wrong rule. All
|
||||||
|
six tests updated to real, live-verified dates/values, not just loosened to pass.
|
||||||
|
|
||||||
|
**Remaining entries in the file, not independently live-collision-tested but documented as
|
||||||
|
such** (each has its own comment now): the Vigil of Pentecost and Vigil of Christmas
|
||||||
|
(`privileged-feria-major`) are corroborated by their own formal rank in the reference source
|
||||||
|
("Semiduplex I classis" / "Duplex I classis" respectively) rather than a live saint collision,
|
||||||
|
which is rare by construction; Pentecost Sunday itself (`privileged-sunday`) is consistent with
|
||||||
|
the confirmed "great octave Sunday" pattern but has no fixed-date-saint collision to test
|
||||||
|
against; Corpus Christi/Sacred Heart's `feria` (`privileged-feria-major`) shares the same
|
||||||
|
"feast of the Lord" reasoning already confirmed elsewhere, and their `sunday` key is flagged as
|
||||||
|
genuinely unreachable (both are always fixed weekdays, never a Sunday, per
|
||||||
|
`easter-offsets.yml`'s own `days:` table). Every other `feria` entry is the trivial
|
||||||
|
`ordinary-feria` default (no rank threshold — any real content wins), already implicitly
|
||||||
|
exercised correctly across hundreds of ordinary weekdays in the existing test suite.
|
||||||
|
|
||||||
|
`npm test` (820 passed — one net fewer than before since two tests merged into one clearer
|
||||||
|
case), `tsc --noEmit`, and `npm run build` all pass.
|
||||||
|
|||||||
@@ -188,13 +188,22 @@ export function decideOccurrence(
|
|||||||
};
|
};
|
||||||
|
|
||||||
case 'privileged-sunday':
|
case 'privileged-sunday':
|
||||||
if (isAtLeast(sanctoral.rank, 'duplex-majus')) {
|
// A privileged Sunday wants nothing at all, no matter how high the
|
||||||
// Never displaced, but a sufficiently high feast still gets a nod.
|
// candidate ranks — not even the bare commemoration an ordinary
|
||||||
return { winner: temporalWinner, commemorations: [sanctoralCommemoration(sanctoral)] };
|
// Sunday would allow a Simplex, and no exception for duplex-majus+ —
|
||||||
}
|
// everything transfers. Live-verified 2026-09-01, three independent
|
||||||
// Below duplex-majus, a privileged Sunday wants nothing at all —
|
// dates: the Immaculate Conception (Duplex I. classis) transfers off
|
||||||
// not even the bare commemoration an ordinary Sunday would allow a
|
// Advent II with no commemoration at all (real dates 2024/2030/2041),
|
||||||
// Simplex — so everything here transfers.
|
// and the Annunciation (also Duplex I. classis) transfers off both
|
||||||
|
// Palm Sunday and Easter Sunday itself the same way (2029/2035/2040/
|
||||||
|
// 2046) — confirmed by direct instruction as the correct behavior,
|
||||||
|
// correcting an earlier, never-actually-verified "duplex-majus+
|
||||||
|
// still gets a nod" guess. (Where a transferred candidate actually
|
||||||
|
// lands is a separate, still-generic `transferDirectionOf`/next-
|
||||||
|
// open-day mechanism — the Annunciation's own real rule of jumping
|
||||||
|
// *past* the whole Easter Octave rather than landing inside it is a
|
||||||
|
// known follow-on, blocked on the not-yet-built Easter-octave
|
||||||
|
// mechanism; see TODO.md.)
|
||||||
return {
|
return {
|
||||||
winner: temporalWinner,
|
winner: temporalWinner,
|
||||||
commemorations: [],
|
commemorations: [],
|
||||||
|
|||||||
@@ -202,12 +202,23 @@ export function adventEmberDayOffset(isoDate: string): number | undefined {
|
|||||||
* the Christmas vigil (Dec 24 can land on a Sunday) — and a Sunday's own
|
* the Christmas vigil (Dec 24 can land on a Sunday) — and a Sunday's own
|
||||||
* privileged/ordinary status should win in that case regardless, so
|
* privileged/ordinary status should win in that case regardless, so
|
||||||
* Sundays are resolved straight from `bySeason` without consulting the
|
* Sundays are resolved straight from `bySeason` without consulting the
|
||||||
* overrides at all.
|
* overrides at all. The one deliberate exception is Easter Sunday itself
|
||||||
|
* (offset 0): `eastertide`'s own `bySeason` default is `ordinary-sunday`
|
||||||
|
* (live-verified for the *ordinary* Sundays that follow it — see that
|
||||||
|
* entry's own comment), but Easter Day needs the stronger
|
||||||
|
* `privileged-sunday` tier instead (live-verified 2026-09-01: the
|
||||||
|
* Annunciation, Duplex I. classis, transfers off Easter Day itself in
|
||||||
|
* years they coincide, the same "transfers, no exception" behavior as
|
||||||
|
* every other privileged-sunday case) — checked directly here rather
|
||||||
|
* than via `offsets`, since offsets are never consulted for a Sunday.
|
||||||
*/
|
*/
|
||||||
export function resolveTemporalCategory(isoDate: string, season: Season, weekday: Weekday): TemporalCategory {
|
export function resolveTemporalCategory(isoDate: string, season: Season, weekday: Weekday): TemporalCategory {
|
||||||
const bySeasonEntry = temporalCategories.bySeason[season];
|
const bySeasonEntry = temporalCategories.bySeason[season];
|
||||||
const base = bySeasonEntry ? (weekday === 'sunday' ? bySeasonEntry.sunday : bySeasonEntry.feria) : 'ordinary-feria';
|
const base = bySeasonEntry ? (weekday === 'sunday' ? bySeasonEntry.sunday : bySeasonEntry.feria) : 'ordinary-feria';
|
||||||
if (weekday === 'sunday') {
|
if (weekday === 'sunday') {
|
||||||
|
if (season === 'eastertide' && easterOffsetOf(isoDate) === 0) {
|
||||||
|
return 'privileged-sunday';
|
||||||
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,19 @@
|
|||||||
# fixability but currently fall to the plain `ordinary-feria` default
|
# fixability but currently fall to the plain `ordinary-feria` default
|
||||||
# instead (see the offsetRanges comment below) — unverified, a real gap,
|
# instead (see the offsetRanges comment below) — unverified, a real gap,
|
||||||
# not a guess dressed up as one.
|
# not a guess dressed up as one.
|
||||||
|
#
|
||||||
|
# 2026-09-01 full sweep: every `bySeason` entry now live-verified or
|
||||||
|
# explicitly noted as not (see each entry's own comment below) — found
|
||||||
|
# and fixed two real bugs in the process (`eastertide`'s and
|
||||||
|
# `ascensiontide`'s own `sunday` tiers were each backwards), and a third,
|
||||||
|
# deeper one in `commemorations.ts` itself (`privileged-sunday`'s
|
||||||
|
# duplex-majus+ candidates were wrongly given a bare commemoration
|
||||||
|
# instead of transferring like every other rank there). Every `feria`
|
||||||
|
# entry in `bySeason` besides the ones with their own verification
|
||||||
|
# comment is the trivial `ordinary-feria` default (no rank threshold at
|
||||||
|
# all — any real content wins outright) — not worth a dedicated live
|
||||||
|
# collision test on top of the hundreds of ordinary weekdays already
|
||||||
|
# exercising it correctly across this app's whole test suite.
|
||||||
bySeason:
|
bySeason:
|
||||||
# Advent's ordinary ferias verified `privileged-feria-minor` during the
|
# Advent's ordinary ferias verified `privileged-feria-minor` during the
|
||||||
# December sanctoral pull (see calendar/types.ts's TemporalCategory doc
|
# December sanctoral pull (see calendar/types.ts's TemporalCategory doc
|
||||||
@@ -81,7 +94,20 @@ bySeason:
|
|||||||
septuagesima: { sunday: privileged-sunday, feria: ordinary-feria }
|
septuagesima: { sunday: privileged-sunday, feria: ordinary-feria }
|
||||||
lent: { sunday: privileged-sunday, feria: ordinary-feria }
|
lent: { sunday: privileged-sunday, feria: ordinary-feria }
|
||||||
passiontide: { sunday: privileged-sunday, feria: ordinary-feria }
|
passiontide: { sunday: privileged-sunday, feria: ordinary-feria }
|
||||||
eastertide: { sunday: privileged-sunday, feria: ordinary-feria }
|
# The ordinary Sundays after Easter (i.e. not Easter Day itself, not
|
||||||
|
# within its Octave — both handled elsewhere) are `ordinary-sunday`,
|
||||||
|
# NOT `privileged-sunday` — originally miscoded, live-verified
|
||||||
|
# 2026-09-01, two independent Duplex-II-classis collisions on "Dominica
|
||||||
|
# IV Post Pascha" (the Finding of the Holy Cross, 2026-05-03; St. Mark,
|
||||||
|
# 2027-04-25): both win outright, with the Sunday itself only noted as
|
||||||
|
# a `Tempora` commemoration underneath — exactly `ordinary-sunday`'s
|
||||||
|
# Duplex+ rule, the reverse of `privileged-sunday`'s (which would keep
|
||||||
|
# the Sunday primary and transfer the feast entirely, per the
|
||||||
|
# Ascensiontide/Advent/Palm-Sunday/Easter-Day evidence above and
|
||||||
|
# below). Easter Day itself (offset 0) and its Octave (offsets 1-6,
|
||||||
|
# `privileged-feria-major` via offsetRanges below) are unaffected by
|
||||||
|
# this — only the ordinary Sundays past the Octave use this default.
|
||||||
|
eastertide: { sunday: ordinary-sunday, feria: ordinary-feria }
|
||||||
# The Sunday within the Octave of Ascension is `privileged-sunday`, not
|
# The Sunday within the Octave of Ascension is `privileged-sunday`, not
|
||||||
# `ordinary-sunday` — live-verified 2026-09-01: the reference engine
|
# `ordinary-sunday` — live-verified 2026-09-01: the reference engine
|
||||||
# titles it "Dominica infra Octavam Ascensionis ~ Semiduplex Dominica
|
# titles it "Dominica infra Octavam Ascensionis ~ Semiduplex Dominica
|
||||||
@@ -92,6 +118,11 @@ bySeason:
|
|||||||
# Matches the same "great octave" pattern as eastertide/pentecost above,
|
# Matches the same "great octave" pattern as eastertide/pentecost above,
|
||||||
# both already `privileged-sunday`.
|
# both already `privileged-sunday`.
|
||||||
ascensiontide: { sunday: privileged-sunday, feria: ordinary-feria }
|
ascensiontide: { sunday: privileged-sunday, feria: ordinary-feria }
|
||||||
|
# Pentecost Sunday itself — no fixed-date saint collision found/
|
||||||
|
# attempted to independently verify this specific entry, but consistent
|
||||||
|
# with the confirmed "great octave Sundays are privileged-sunday"
|
||||||
|
# pattern (Easter Day, Ascension's own octave Sunday) — Pentecost
|
||||||
|
# outranks both.
|
||||||
pentecost: { sunday: privileged-sunday, feria: ordinary-feria }
|
pentecost: { sunday: privileged-sunday, feria: ordinary-feria }
|
||||||
trinitytide: { sunday: ordinary-sunday, feria: ordinary-feria }
|
trinitytide: { sunday: ordinary-sunday, feria: ordinary-feria }
|
||||||
# Corpus Christi/Sacred Heart are themselves "feasts of the Lord" that
|
# Corpus Christi/Sacred Heart are themselves "feasts of the Lord" that
|
||||||
@@ -99,7 +130,14 @@ bySeason:
|
|||||||
# privileged-feria-major (rather than injecting a separate synthetic
|
# privileged-feria-major (rather than injecting a separate synthetic
|
||||||
# FeastClass) since with the small sanctoral calendar this app has, a
|
# FeastClass) since with the small sanctoral calendar this app has, a
|
||||||
# real clash is unlikely and the practical effect (temporal wins) is the
|
# real clash is unlikely and the practical effect (temporal wins) is the
|
||||||
# same either way.
|
# same either way. Not independently live-verified (no fixed-date saint
|
||||||
|
# collision found/attempted), but same "feast of the Lord" reasoning
|
||||||
|
# already confirmed for Ash Wednesday/Holy Week/the Easter Octave.
|
||||||
|
# `sunday` is checked here for shape-consistency with every other
|
||||||
|
# season entry, but genuinely unreachable in practice — per
|
||||||
|
# data/calendar/easter-offsets.yml's `days:` table, both are single
|
||||||
|
# fixed weekdays (Corpus Christi always a Thursday, Sacred Heart always
|
||||||
|
# the following Friday), never a Sunday.
|
||||||
corpus-christi: { sunday: ordinary-sunday, feria: privileged-feria-major }
|
corpus-christi: { sunday: ordinary-sunday, feria: privileged-feria-major }
|
||||||
sacred-heart: { sunday: ordinary-sunday, feria: privileged-feria-major }
|
sacred-heart: { sunday: ordinary-sunday, feria: privileged-feria-major }
|
||||||
|
|
||||||
@@ -108,7 +146,7 @@ offsets:
|
|||||||
-39: privileged-feria # Lent Ember Wednesday (Wed after 1st Sunday of Lent) — verified: Duplex wins outright here
|
-39: privileged-feria # Lent Ember Wednesday (Wed after 1st Sunday of Lent) — verified: Duplex wins outright here
|
||||||
-37: privileged-feria # Lent Ember Friday
|
-37: privileged-feria # Lent Ember Friday
|
||||||
-36: privileged-feria # Lent Ember Saturday — verified: Duplex wins outright here (St. Gregory the Great)
|
-36: privileged-feria # Lent Ember Saturday — verified: Duplex wins outright here (St. Gregory the Great)
|
||||||
48: privileged-feria-major # Vigil of Pentecost — unverified, defaulted to the stricter tier
|
48: privileged-feria-major # Vigil of Pentecost — no live saint-collision test found/attempted (rare by construction), but corroborated 2026-09-01 by its own formal rank in the reference source (Sancti/Pentecost Vigil file's own [Rank]: "Semiduplex I classis" — a genuinely first-class day, consistent with the stricter tier)
|
||||||
|
|
||||||
offsetRanges:
|
offsetRanges:
|
||||||
- { fromOffset: -6, toOffset: -1, category: privileged-feria-major } # ferias of Holy Week
|
- { fromOffset: -6, toOffset: -1, category: privileged-feria-major } # ferias of Holy Week
|
||||||
@@ -129,4 +167,4 @@ offsetRanges:
|
|||||||
# existing one, not introduced by this change.
|
# existing one, not introduced by this change.
|
||||||
|
|
||||||
fixedDates:
|
fixedDates:
|
||||||
"12-24": privileged-feria-major # Vigil of Christmas — unverified, defaulted to the stricter tier
|
"12-24": privileged-feria-major # Vigil of Christmas — no live saint-collision test found/attempted, but corroborated 2026-09-01 by its own formal rank in the reference source (Sancti/12-24.txt's own [Rank]: "Duplex I classis", consistent with the stricter tier)
|
||||||
|
|||||||
@@ -34,27 +34,30 @@ describe('April sanctoral pull (first pass)', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('a privileged Eastertide Sunday bumps even St. Robert (plain Duplex, not Duplex majus) to transfer forward', () => {
|
it('an ordinary Eastertide Sunday (not Easter Day, not within its Octave) still lets St. Robert (plain Duplex) win outright, same as any weekday', () => {
|
||||||
// 2029: Apr 29 is a privileged Sunday in Eastertide, so Robert can't
|
// 2029: Apr 29 is an ordinary (not privileged) Sunday in Eastertide —
|
||||||
// win or be commemorated there — he transfers to Monday and wins the
|
// live-verified 2026-09-01 (`eastertide`'s own `bySeason` entry, see
|
||||||
// collision against St. Peter Martyr instead.
|
// data/calendar/temporal-categories.yml): "S. Roberti Abbatis ~
|
||||||
|
// Duplex" wins outright there, same as data/calendar/temporal-
|
||||||
|
// categories.yml's `ordinary-sunday` rule for any other season, with
|
||||||
|
// the Sunday itself only noted as a `Scriptura` line, not primary.
|
||||||
const sunday = resolveDay('2029-04-29');
|
const sunday = resolveDay('2029-04-29');
|
||||||
expect(sunday.winner).toEqual({ kind: 'temporal', id: 'easter-5' });
|
expect(sunday.winner).toEqual({ kind: 'sanctoral', id: 'st-robert', name: 'St. Robert, Abbot', nameLa: 'Sanctus Robertus, Abbas', rank: 'duplex' });
|
||||||
expect(sunday.commemorations).toEqual([
|
expect(sunday.commemorations).toEqual([
|
||||||
|
{ kind: 'temporal', id: 'easter-5' },
|
||||||
{ kind: 'sanctoral', id: 'st-catherine-of-siena', name: 'St. Catherine of Siena, Virgin', nameLa: 'Sancta Catharina Senensis, Virgo', rank: 'simplex' },
|
{ kind: 'sanctoral', id: 'st-catherine-of-siena', name: 'St. Catherine of Siena, Virgin', nameLa: 'Sancta Catharina Senensis, Virgo', rank: 'simplex' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Apr 30 resolves St. Peter Martyr cleanly on his own day — no
|
||||||
|
// transferred-Robert collision, since Robert already won on the 29th.
|
||||||
const monday = resolveDay('2029-04-30');
|
const monday = resolveDay('2029-04-30');
|
||||||
expect(monday.winner).toEqual({
|
expect(monday.winner).toEqual({
|
||||||
kind: 'sanctoral',
|
kind: 'sanctoral',
|
||||||
id: 'st-robert',
|
id: 'st-peter-martyr',
|
||||||
name: 'St. Robert, Abbot',
|
name: 'St. Peter Martyr, Martyr',
|
||||||
nameLa: 'Sanctus Robertus, Abbas',
|
nameLa: 'Sanctus Petrus Martyr, Martyr',
|
||||||
rank: 'duplex',
|
rank: 'semiduplex',
|
||||||
transferredFrom: '2029-04-29',
|
|
||||||
});
|
});
|
||||||
expect(monday.commemorations).toEqual([
|
expect(monday.commemorations).toEqual([]);
|
||||||
{ kind: 'sanctoral', id: 'st-peter-martyr', name: 'St. Peter Martyr, Martyr', nameLa: 'Sanctus Petrus Martyr, Martyr', rank: 'semiduplex' },
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -141,18 +141,16 @@ describe('decideOccurrence — ordinary-sunday', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('decideOccurrence — privileged-sunday', () => {
|
describe('decideOccurrence — privileged-sunday', () => {
|
||||||
it('is never displaced; duplex-majus and up are commemorated', () => {
|
it('is never displaced, and never gives even a bare commemoration to any rank — everything transfers forward', () => {
|
||||||
for (const rank of ['duplex-majus', 'duplex-2-classis', 'duplex-1-classis'] as const) {
|
// Live-verified 2026-09-01 (see data/calendar/temporal-categories.yml
|
||||||
|
// and commemorations.ts's own `privileged-sunday` case comment):
|
||||||
|
// even a Duplex I. classis feast (the Immaculate Conception on Advent
|
||||||
|
// II, the Annunciation on Palm/Easter Sunday) transfers with no
|
||||||
|
// commemoration at all — corrects an earlier, never-actually-verified
|
||||||
|
// "duplex-majus+ still gets a nod" guess.
|
||||||
|
for (const rank of ['duplex-majus', 'duplex-2-classis', 'duplex-1-classis', 'duplex', 'semiduplex', 'simplex'] as const) {
|
||||||
const result = decideOccurrence('privileged-sunday', 'advent-1', saint(rank));
|
const result = decideOccurrence('privileged-sunday', 'advent-1', saint(rank));
|
||||||
expect(result.winner).toEqual({ kind: 'temporal', id: 'advent-1' });
|
expect(result.winner).toEqual({ kind: 'temporal', id: 'advent-1' });
|
||||||
expect(result.commemorations).toEqual([{ kind: 'sanctoral', id: 'x', name: 'St. Ereden', rank }]);
|
|
||||||
expect(result.transfer).toBeUndefined();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('duplex, semiduplex, and simplex all transfer forward — no bare commemoration here', () => {
|
|
||||||
for (const rank of ['duplex', 'semiduplex', 'simplex'] as const) {
|
|
||||||
const result = decideOccurrence('privileged-sunday', 'advent-1', saint(rank));
|
|
||||||
expect(result.commemorations).toEqual([]);
|
expect(result.commemorations).toEqual([]);
|
||||||
expect(result.transfer).toEqual({ candidate: saint(rank), direction: 'forward' });
|
expect(result.transfer).toEqual({ candidate: saint(rank), direction: 'forward' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,21 +66,31 @@ describe('getDayLabel — ordinal temporal label', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Advent's own anchor Sunday is already week 1, not a separate anchor-week case", () => {
|
it("Advent's own anchor Sunday is already week 1, not a separate anchor-week case", () => {
|
||||||
// 2025-11-30 is also St. Andrew's own day (Duplex II. classis) — since
|
// 2025-11-30 is also St. Andrew's own day (Duplex II. classis) — a
|
||||||
// the November sanctoral pull, he's correctly commemorated alongside
|
// privileged Sunday transfers *everything*, no rank exception
|
||||||
// Advent I rather than simply absent (confirmed against the live
|
// (live-verified 2026-09-01, correcting an earlier, inaccurate claim
|
||||||
// reference engine, which shows the same pairing), so the label
|
// that he was commemorated in place: the real Monastic 1617 engine
|
||||||
// reflects both, winner (Advent I, so privileged nothing can displace
|
// shows "Transfer: S. Andreæ Apostoli", not a commemoration), so the
|
||||||
// it) first, commemorated saint after.
|
// label shows him as transferred away rather than paired alongside
|
||||||
expect(getDayLabel(resolveDay('2025-11-30')).en).toBe('The 1st Sunday of Advent (Semiduplex) — St. Andrew, Apostle');
|
// Advent I.
|
||||||
|
expect(getDayLabel(resolveDay('2025-11-30')).en).toBe('The 1st Sunday of Advent (Semiduplex) — St. Andrew, Apostle (transferred away)');
|
||||||
|
// Dec 1 is where Andrew's own transfer actually lands (the next open
|
||||||
|
// day, `privileged-feria-minor`'s Semiduplex+ threshold easily clears
|
||||||
|
// for a Duplex II. classis) — live-verified 2026-09-01, the real
|
||||||
|
// Monastic 1617 engine shows "S. Andreæ Apostoli ~ Duplex II. classis"
|
||||||
|
// there outright, "Tempora: Feria II infra Hebdomadam I Adventus"
|
||||||
|
// underneath, matching this.
|
||||||
|
expect(getDayLabel(resolveDay('2025-12-01')).en).toBe('St. Andrew, Apostle (Duplex II Class, transferred) — In the 1st week of Advent');
|
||||||
|
// Dec 4 (St. Barbara, merely commemorated, not high-ranked enough to
|
||||||
|
// win outright) is a clean example of the season-ordinal label itself.
|
||||||
// Advent drops the redundant leading weekday word now that the header
|
// Advent drops the redundant leading weekday word now that the header
|
||||||
// always shows the weekday on its own (see temporalLabel's
|
// always shows the weekday on its own (see temporalLabel's
|
||||||
// `dropWeekdayPrefix`).
|
// `dropWeekdayPrefix`).
|
||||||
expect(getDayLabel(resolveDay('2025-12-01')).en).toBe('In the 1st week of Advent (Feria)');
|
expect(getDayLabel(resolveDay('2025-12-04')).en).toBe('In the 1st week of Advent (Feria) — St. Barbara, Virgin and Martyr');
|
||||||
// Latin: same weekday-drop, and the season-ordinal genitive
|
// Latin: same weekday-drop, and the season-ordinal genitive
|
||||||
// construction ("Hebdomada prima Adventus") needs no extra linking
|
// construction ("Hebdomada prima Adventus") needs no extra linking
|
||||||
// word — 4th-declension "Adventus" has the same genitive form.
|
// word — 4th-declension "Adventus" has the same genitive form.
|
||||||
expect(getDayLabel(resolveDay('2025-12-01')).la).toBe('In hebdomada prima Adventus (Feria)');
|
expect(getDayLabel(resolveDay('2025-12-04')).la).toBe('In hebdomada prima Adventus (Feria) — Sancta Barbara, Virgo et Martyr');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to weekday + season name for seasons with no ordinal convention modeled', () => {
|
it('falls back to weekday + season name for seasons with no ordinal convention modeled', () => {
|
||||||
|
|||||||
@@ -104,17 +104,21 @@ describe('resolveOrdo("compline", ...)', () => {
|
|||||||
|
|
||||||
it('anticipates Advent I: the Saturday evening before switches to the Alma Redemptoris Mater and shows the anticipated day label', () => {
|
it('anticipates Advent I: the Saturday evening before switches to the Alma Redemptoris Mater and shows the anticipated day label', () => {
|
||||||
// Nov 30, 2025 is Advent I Sunday -- also St. Andrew's own day
|
// Nov 30, 2025 is Advent I Sunday -- also St. Andrew's own day
|
||||||
// (Duplex II. classis), correctly commemorated alongside it since the
|
// (Duplex II. classis), but a privileged Sunday transfers him
|
||||||
// November sanctoral pull (see tests/calendar/day-label.test.ts). This
|
// entirely rather than commemorating him in place (live-verified
|
||||||
// evening also anticipates Advent I from Nov 29, itself the Vigil of
|
// 2026-09-01, see tests/calendar/day-label.test.ts). This evening
|
||||||
// St. Andrew -- but the Vigil isn't also shown alongside his own
|
// also anticipates Advent I from Nov 29, itself the Vigil of St.
|
||||||
// feast (calendar/vespers.ts's own isVigilOfTomorrow check, keyed off
|
// Andrew -- calendar/vespers.ts's own isVigilOfTomorrow check only
|
||||||
// each Vigil's explicit `vigilOf` field): showing both would just be
|
// suppresses the Vigil's own commemoration when tomorrow's page
|
||||||
// redundant, the same feast named twice.
|
// *also* shows St. Andrew (which would be redundant); since he no
|
||||||
|
// longer does (he's transferred away, not commemorated), both pieces
|
||||||
|
// of information are genuinely distinct and both show.
|
||||||
const eve = resolveOrdo('compline', '2025-11-29');
|
const eve = resolveOrdo('compline', '2025-11-29');
|
||||||
const last = eve.parts[eve.parts.length - 1];
|
const last = eve.parts[eve.parts.length - 1];
|
||||||
expect(last?.kind === 'preces' ? last.label : undefined).toBe('Alma Redemptoris Mater');
|
expect(last?.kind === 'preces' ? last.label : undefined).toBe('Alma Redemptoris Mater');
|
||||||
expect(eve.dayLabel?.en).toBe('The 1st Sunday of Advent (Semiduplex) — St. Andrew, Apostle');
|
expect(eve.dayLabel?.en).toBe(
|
||||||
|
'The 1st Sunday of Advent (Semiduplex) — Vigil of St. Andrew (of today) — St. Andrew, Apostle (transferred away)',
|
||||||
|
);
|
||||||
|
|
||||||
const dayBefore = resolveOrdo('compline', '2025-11-28');
|
const dayBefore = resolveOrdo('compline', '2025-11-28');
|
||||||
const lastBefore = dayBefore.parts[dayBefore.parts.length - 1];
|
const lastBefore = dayBefore.parts[dayBefore.parts.length - 1];
|
||||||
|
|||||||
+12
-10
@@ -565,18 +565,20 @@ describe('resolveOrdo("lauds", ...)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('resolves both the winners collect and a commemorated saints collect on a day with a real commemoration', () => {
|
it('resolves both the winners collect and a commemorated saints collect on a day with a real commemoration', () => {
|
||||||
// 2025-11-30: Advent I (the winner) with St. Andrew, Duplex II. classis,
|
// 2029-01-14: post-Epiphany II (the winner, `ordinary-sunday`) with
|
||||||
// commemorated alongside it (see tests/calendar/day-label.test.ts) --
|
// St. Felix Presbyter, Simplex, commemorated alongside it — live-
|
||||||
// he has only a bare collect authored (no st-andrew-commemoration.yml
|
// verified 2026-09-01. (Not 2025-11-30/St. Andrew as this test used
|
||||||
// Ant+V/R bundle yet), so this renders as a labeled 'preces' block
|
// to: a privileged Sunday transfers *everything*, no rank exception
|
||||||
// (see resolve-common.ts's sanctoralCommemorationPart), not a second
|
// — Andrew doesn't get a bare commemoration there at all, see
|
||||||
// unlabeled 'prayer' the way it used to.
|
// tests/calendar/day-label.test.ts. `ordinary-sunday`'s own Simplex
|
||||||
const ordo = resolveOrdo('lauds', '2025-11-30');
|
// branch is the real case that still commemorates a saint in place.)
|
||||||
|
const ordo = resolveOrdo('lauds', '2029-01-14');
|
||||||
const collects = ordo.parts.filter((p) => p.kind === 'prayer');
|
const collects = ordo.parts.filter((p) => p.kind === 'prayer');
|
||||||
expect(collects.length).toBe(1);
|
expect(collects.length).toBe(1);
|
||||||
const andrewCommemoration = ordo.parts.find((p) => p.kind === 'preces' && p.label?.includes('Andrew'));
|
const felixCommemoration = ordo.parts.find((p) => p.kind === 'preces' && p.label?.includes('Felix'));
|
||||||
expect(andrewCommemoration?.kind === 'preces' ? andrewCommemoration.text.text.en : undefined).toContain(
|
expect(felixCommemoration).toBeDefined();
|
||||||
'Andrew',
|
expect(felixCommemoration?.kind === 'preces' ? felixCommemoration.text.text.en : undefined).toContain(
|
||||||
|
'palm tree',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -718,7 +718,7 @@ describe('resolveOrdo("matins", ...) rest-of-year temporal nocturn-readings swee
|
|||||||
['2026-04-19', 'easter-3'],
|
['2026-04-19', 'easter-3'],
|
||||||
['2026-04-27', 'easter-4'],
|
['2026-04-27', 'easter-4'],
|
||||||
['2026-05-04', 'easter-5'],
|
['2026-05-04', 'easter-5'],
|
||||||
['2026-05-11', 'easter-6'],
|
['2026-05-10', 'easter-6 (the real Sunday — not 2026-05-11: since the eastertide-Sunday precedence fix (2026-09-01), St. Antoninus, Duplex, now correctly wins outright on his own day, May 10, rather than transferring onto Monday, so easter-6\'s own pooled readings show up there commemorated, not on the 11th)'],
|
||||||
['2026-05-18', 'sunday-after-ascension'],
|
['2026-05-18', 'sunday-after-ascension'],
|
||||||
['2026-04-05', 'easter-sunday'],
|
['2026-04-05', 'easter-sunday'],
|
||||||
['2026-05-24', 'pentecost-sunday'],
|
['2026-05-24', 'pentecost-sunday'],
|
||||||
|
|||||||
Reference in New Issue
Block a user