From fc5c9297fa6763989d0e239c702f9a8e87574c1c Mon Sep 17 00:00:00 2001 From: Will Estes Date: Tue, 25 Aug 2026 08:07:00 -0400 Subject: [PATCH] Give Prime's Preces the ferial form on Vigil days isSundayOrFeastOffice treats any sanctoral winner, Vigils included, as a "feast," so a Vigil day was getting the Sunday/feast Preces. Add a by-day-kind 'vigilIsFerial' flag, set only on Prime's Preces part (its capitulum keeps the old behavior), so a Vigil-ranked winner now routes to the ferial Preces text instead. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01HWN869GrMFHqrer9fdCBbF --- src/data/hours/prime.yml | 7 +++++++ src/hours/prime.ts | 7 +++++-- src/hours/types.ts | 6 ++++++ tests/hours/prime.test.ts | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/data/hours/prime.yml b/src/data/hours/prime.yml index e9fc2bf..98fc2e9 100644 --- a/src/data/hours/prime.yml +++ b/src/data/hours/prime.yml @@ -30,6 +30,12 @@ # ferial Preces are said on *every* ferial day, not just Advent/Lent/ # vigils/Ember days the way later rubrics restricted them. # +# Preces additionally set `vigilIsFerial: true` — isSundayOrFeastOffice on +# its own treats any sanctoral winner, Vigils included, as a "feast," but +# by explicit choice a Vigil day gets the ferial Preces instead. The +# capitulum doesn't set this flag, so a Vigil still gets the Sunday/feast +# capitulum, same as before. +# # Preces deliberately use the *secular* Tridentine form # (propers/common/prime-preces-dominicales.yml), not Monastic Prime's own # shorter Dominicales form — chosen because the secular form keeps content @@ -65,6 +71,7 @@ parts: - kind: by-day-kind resolvedKind: preces omitOnDouble: true + vigilIsFerial: true sundayOrFeastRef: { source: common, id: prime-preces-dominicales } ferialRef: { source: common, id: prime-preces-feriales } - kind: prayer diff --git a/src/hours/prime.ts b/src/hours/prime.ts index ef7e946..0e4dece 100644 --- a/src/hours/prime.ts +++ b/src/hours/prime.ts @@ -97,10 +97,13 @@ function resolvePart(part: HourPart, date: string, day: LiturgicalDay): Resolved case 'canticle': return [{ kind: 'canticle', canticleId: part.canticleId, text: resolveCommon(part.textRef.id) }]; case 'by-day-kind': { - if (part.omitOnDouble && isDoubleOrHigher(resolveOfficeWinner(day))) { + const winner = resolveOfficeWinner(day); + if (part.omitOnDouble && isDoubleOrHigher(winner)) { return []; } - const ref = isSundayOrFeastOffice(day) ? part.sundayOrFeastRef : part.ferialRef; + const isVigil = winner.kind === 'sanctoral' && winner.rank === 'vigil'; + const useFeastForm = isSundayOrFeastOffice(day) && !(part.vigilIsFerial && isVigil); + const ref = useFeastForm ? part.sundayOrFeastRef : part.ferialRef; if (part.resolvedKind === 'preces') { return [{ kind: 'preces', text: resolveCommon(ref.id), label: part.label }]; } diff --git a/src/hours/types.ts b/src/hours/types.ts index d345d15..77c3607 100644 --- a/src/hours/types.ts +++ b/src/hours/types.ts @@ -61,6 +61,12 @@ export type HourPart = resolvedKind: 'chapter' | 'preces'; label?: string; omitOnDouble?: true; + // When true, a Vigil-ranked sanctoral winner takes the ferialRef + // instead of the sundayOrFeastRef isSundayOrFeastOffice would + // otherwise give it (that function treats any sanctoral winner, + // Vigils included, as a "feast"). Prime's Preces use this; its + // capitulum doesn't. + vigilIsFerial?: true; sundayOrFeastRef: PropersRef; ferialRef: PropersRef; } diff --git a/tests/hours/prime.test.ts b/tests/hours/prime.test.ts index 75f9b28..e3f6a19 100644 --- a/tests/hours/prime.test.ts +++ b/tests/hours/prime.test.ts @@ -130,6 +130,25 @@ describe('resolveOrdo("prime", ...)', () => { ).toContain('Et ego ad te, Dómine, clamávi'); }); + it('uses the ferial Preces (but the Sunday/feast capitulum) on a Vigil day', () => { + // 2025-11-29: a real, unimpeded Vigil of St. Andrew — day.winner is + // itself sanctoral/vigil (not merely commemorated), see + // data/calendar/saints/vigil-of-st-andrew.yml's own header comment. + const VIGIL = '2025-11-29'; + const ordo = resolveOrdo('prime', VIGIL); + const chapter = ordo.parts.find((p) => p.kind === 'chapter'); + // The capitulum doesn't set vigilIsFerial, so it stays on the + // Sunday/feast form even on a Vigil day. + expect(chapter && chapter.kind === 'chapter' ? chapter.text.text.en : undefined).toBe( + 'Now to the king of ages, immortal, invisible, the only God, be honour and glory for ever and ever. Amen.', + ); + const preces = ordo.parts.filter((p) => p.kind === 'preces'); + const ferialPreces = preces.find((p) => p.kind === 'preces' && p.text.text.la?.startsWith('V. Éripe me')); + expect(ferialPreces).toBeDefined(); + const dominicalPreces = preces.find((p) => p.kind === 'preces' && p.text.text.la?.includes('Confíteor Deo')); + expect(dominicalPreces).toBeUndefined(); + }); + it('gives the capitulum its scripture citation', () => { const ordo = resolveOrdo('prime', MONDAY); const chapter = ordo.parts.find((p) => p.kind === 'chapter');