Model cross-day Vespers commemorations, fix First Vespers threshold
Deploy / deploy (push) Successful in 1m1s

Whichever of today/tomorrow doesn't govern a given evening's Vespers
can still be commemorated there, unless it's Simplex (or a bare
temporal winner with no standing of its own) — live-verified in both
directions against the real Monastic 1617 engine: Ss. Cyriacus/Largus/
Smaragdus (Semiduplex) commemorated when Sunday claims First Vespers
over them; St. Anne commemorated even though St. James (Duplex II.
classis) keeps his own Second Vespers outright; St. Donatus (Simplex)
gets nothing ("nihil de præcedenti") in the same situation Cyriacus/
etc. did get a commemoration. The isMajorFixedFeastOfTheLord override
(Christmas/Epiphany/Candlemas/Easter/Ascension/Corpus Christi/Sacred
Heart) deliberately doesn't inherit this yet — no live data on whether
those suppress it the way privileged-sunday/privileged-feria-major do
during the day.

Also fixes hasFirstVespers's threshold, found wrong while gathering
the above evidence: it required duplex-majus+, but Ss. Cyriacus/etc.
(plain Semiduplex) actually claims First Vespers over St. Donatus in
the live engine. Lowered to semiduplex, matching the sibling
privileged-feria-minor threshold already used just below it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 18:59:16 -04:00
parent 3aee8eca2f
commit 979bbe6aa4
2 changed files with 102 additions and 10 deletions
+33
View File
@@ -39,4 +39,37 @@ describe('resolveEveningDay', () => {
expect(day.winner.vespersFrom).toBe('firstVespersOfTomorrow');
}
});
// The three cases below are live-verified against the real Monastic
// Tridentinum 1617 engine (see commemorationOf's own doc comment in
// calendar/vespers.ts) — "commemoratio de præcedenti/sequenti" vs.
// "nihil de præcedenti" in the raw output, not just inferred from the
// daytime rule.
it('a Semiduplex loser is still commemorated when tomorrow claims First Vespers', () => {
// Aug 8, 2026 (Ss. Cyriacus/Largus/Smaragdus, Semiduplex) loses the
// evening to Aug 9 (Sunday); still commemorated.
const day = resolveEveningDay('2026-08-08');
expect(day.date).not.toBe('2026-08-08');
expect(day.commemorations).toContainEqual(
expect.objectContaining({ kind: 'sanctoral', id: 'ss-cyriacus-largus-and-smaragdus' }),
);
});
it('a Simplex loser is not commemorated when tomorrow claims First Vespers', () => {
// Aug 7, 2026 (St. Donatus, Simplex) loses the evening to Aug 8
// (Ss. Cyriacus/Largus/Smaragdus, now Semiduplex-threshold-eligible
// for First Vespers — see hasFirstVespers); not commemorated.
const day = resolveEveningDay('2026-08-07');
expect(day.date).not.toBe('2026-08-07');
expect(day.commemorations.some((c) => c.kind === 'sanctoral' && c.id === 'st-donatus')).toBe(false);
});
it('a winning day still commemorates a non-Simplex loser on the other side', () => {
// Jul 25, 2026 (St. James the Greater, Duplex II. classis) keeps its
// own Second Vespers outright, but still commemorates Jul 26
// (St. Anne).
const day = resolveEveningDay('2026-07-25');
expect(day.date).toBe('2026-07-25');
expect(day.commemorations).toContainEqual(expect.objectContaining({ kind: 'sanctoral', id: 'st-anne' }));
});
});