Fix Vespers status wrongly saying "Second Vespers" when a Sunday anticipates it
Deploy / deploy (push) Successful in 1m29s

getVespersStatusLabel detected First-Vespers anticipation only via
winner.vespersFrom, a tag only ever set for a sanctoral tomorrow-winner
(tagVespersFrom). When Vespers anticipates a Sunday that wins its own
day outright (a temporal winner, e.g. 2026-08-29 anticipating 2026-08-30
since the Decollation of St. John Baptist is below the duplex-2-classis
floor that would let it keep its own evening), the tag never gets set
and the status wrongly read "Second Vespers (of today)" even though the
whole page was showing tomorrow's identity.

Comparing the resolved day's date against the originally-requested date
detects anticipation uniformly regardless of winner kind, so this drops
the vespersFrom dependency entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ME8QNPdEmHSbSx1r6VmDRG
This commit is contained in:
2026-08-30 06:10:52 -04:00
parent 95901ef56a
commit 5ae1f3a3e0
4 changed files with 63 additions and 6 deletions
+34 -1
View File
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest';
import { resolveDay } from '../../src/calendar';
import { getDayLabel } from '../../src/calendar/day-label';
import { resolveEveningDay } from '../../src/calendar/vespers';
import { getDayLabel, getVespersStatusLabel } from '../../src/calendar/day-label';
import type { LiturgicalDay } from '../../src/calendar/types';
describe('getDayLabel — ordinal temporal label', () => {
@@ -380,3 +381,35 @@ describe('getDayLabel — feast name combination', () => {
expect(getDayLabel(base).en).toBe('Monday in the 3rd week after Trinity (Feria)');
});
});
describe('getDayLabel/getVespersStatusLabel — Vespers evening with a temporal (Sunday) winner', () => {
it('2026-08-29 (Decollation of St. John Baptist, plain Duplex, below keepsOwnSecondVespers threshold) anticipates the following ordinary Sunday, which wins outright over Ss. Felix and Adauctus (Simplex) -- the Sunday must lead the label, with both real commemorations (Felix/Adauctus and the displaced Decollation) trailing, and the status must read First, not Second, Vespers', () => {
// Regression test: getDayLabel's temporal-fallback branch used to list
// commemorations *before* the winner, so this read "Ss. Felix and
// Adauctus, Martyrs -- The Beheading of St. John the Baptist (of
// today) -- The 13th Sunday after Trinity (Semiduplex)", making the
// actual winner (the Sunday) look like an afterthought and the
// Sunday's own rank label ("Semiduplex") look like it belonged to
// Felix and Adauctus instead. And getVespersStatusLabel used to say
// "Second Vespers (of today)" here since its old detection
// (`winner.vespersFrom`) only ever fires for a *sanctoral*
// tomorrow-winner, never a temporal one like this Sunday.
const day = resolveEveningDay('2026-08-29');
expect(day.date).toBe('2026-08-30');
expect(day.winner).toEqual({ kind: 'temporal', id: 'post-pentecost-14' });
expect(getDayLabel(day).en).toBe(
'The 13th Sunday after Trinity (Semiduplex) — Ss. Felix and Adauctus, Martyrs — The Beheading of St. John the Baptist (of today)',
);
expect(getVespersStatusLabel(day, '2026-08-29').en).toBe('First Vespers (of tomorrow)');
});
it("a plain kept evening (no anticipation) still reads Second Vespers", () => {
// 2026-08-17, St. Lawrence's own octave closing day (Duplex), keeps
// its own evening outright -- sanity check that the date-comparison
// rewrite of getVespersStatusLabel didn't flip the non-anticipating
// case too.
const day = resolveEveningDay('2026-08-17');
expect(day.date).toBe('2026-08-17');
expect(getVespersStatusLabel(day, '2026-08-17').en).toBe('Second Vespers (of today)');
});
});
+18
View File
@@ -69,6 +69,24 @@ describe('shell', () => {
expect(root.querySelector('.day-nav-vespers-status')?.textContent).toContain('Second Vespers');
});
it('shows First Vespers and the Sunday leading the header when Vespers anticipates a Sunday that wins outright (temporal, not sanctoral, winner)', () => {
// Regression test: getVespersStatusLabel used to detect anticipation
// only via a tag that's only ever set for a *sanctoral* tomorrow-
// winner (see the equivalent calendar-level test in
// tests/calendar/day-label.test.ts) -- 2026-08-29 (the Decollation of
// St. John Baptist, Duplex) anticipates 2026-08-30's ordinary Sunday,
// which wins outright over Ss. Felix and Adauctus (Simplex), so
// tomorrow's winner is `temporal`, not `sanctoral`.
setDate('2026-08-29');
setSelectedHour('vespers');
window.history.replaceState(null, '', '/vu/2026-08-29/vespers');
const root = document.getElementById('app')!;
mountShell(root);
expect(root.querySelector('.day-nav-label')?.textContent).toContain('13th Sunday after Trinity');
expect(root.querySelector('.day-nav-vespers-status')?.textContent).toContain('First Vespers');
});
it('switches to Matins and shows its real content, not the pending message', () => {
const root = document.getElementById('app')!;
mountShell(root);