Finish the last 5 vigils: weekday-keyed commemoration antiphons
Deploy / deploy (push) Successful in 1m32s
Deploy / deploy (push) Successful in 1m32s
Corrected an earlier "no source exists" claim by live-querying real
commemorated instances of the Vigils of St. Lawrence and the Assumption
across six different weekdays. Their source files' own [Rule] block
("Versum Feria") turns out to be a real, sourced mechanism, not an
absence: a mere commemoration doesn't reuse the vigil's own antiphon at
all -- it borrows the plain ferial day's antiphon/versicle instead, and
that antiphon quotes the Benedictus canticle in weekday sequence (same
shape vespers-magnificat-antiphons.yml already uses for the Magnificat).
The versicle is a single fixed text. Sunday has no entry: a Vigil
transfers off an ordinary Sunday rather than ever reaching a
commemoration there, confirmed live as well as already true in this
app's own precedence rules.
sanctoralCommemorationPart now takes weekday and resolves this
dynamically for common-of-a-vigil saints, rather than needing a static
per-saint file. All 218 saints with an authored collect now have a real
commemoration antiphon.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ME8QNPdEmHSbSx1r6VmDRG
This commit is contained in:
@@ -64,12 +64,17 @@ across every hour and content type in this app.
|
||||
Holy Cross/Most Precious Blood/Transfiguration, given their own real `[Ant 2]`/`[Versum 2]`
|
||||
straight from their Sancti files). **213 of ~218 real saints now covered**
|
||||
(`tests/propers/commemoration-sweep.test.ts`, 213 cases); every entry not independently
|
||||
live-verified is marked `status: draft`. Only 5 remain, genuinely open: the Vigils
|
||||
(`common-of-a-vigil` — All Saints, Ss. Simon and Jude, St. John the Baptist, St. Lawrence,
|
||||
the Assumption) — no dedicated "Commune Vigiliarum" antiphon set exists in the reference
|
||||
engine at all (only `C1v`, Vigil of Apostles specifically, itself empty/inherited), and
|
||||
`FeastClass: 'vigil'` is weak enough that these rarely reach the commemoration code path in
|
||||
the first place — real, but low-priority.
|
||||
live-verified is marked `status: draft`. **DONE, full 218/218**: the remaining 5 Vigils
|
||||
(`common-of-a-vigil`) turned out not to need a static template at all — pushed back on the
|
||||
earlier "no source exists" claim by live-querying real commemorated instances directly, which
|
||||
found their own source file's `[Rule]` ("Versum Feria") is a real, sourced mechanism: the
|
||||
antiphon quotes the Benedictus canticle in weekday sequence (confirmed against 6 real
|
||||
weekdays, same shape `vespers-magnificat-antiphons.yml` already uses for the Magnificat), the
|
||||
versicle is a single fixed text, and Sunday genuinely has no entry (a Vigil transfers off an
|
||||
ordinary Sunday rather than ever reaching a commemoration there — confirmed live too).
|
||||
Implemented as a small weekday-aware code path in `sanctoralCommemorationPart` (now takes
|
||||
`weekday`) rather than a per-saint file — see `data/hours/lauds-vigil-commemoration-
|
||||
antiphons.yml` and `tests/hours/vigil-commemoration.test.ts`.
|
||||
- Vespers Magnificat antiphon: after the 2026-08-30 sweep plus the same-day O-Antiphon
|
||||
follow-up (see "O Antiphons (Dec 17-23) added" below), only two narrow gaps remain —
|
||||
Advent 4's own Sunday landing on Dec 24 specifically (outside the O-Antiphon range), and
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# A mere commemoration of a Vigil (`common: common-of-a-vigil` — St.
|
||||
# Lawrence's, the Assumption's, All Saints', Ss. Simon and Jude's, St.
|
||||
# John the Baptist's) does NOT reuse that vigil's own antiphon (the one
|
||||
# used when it wins outright, e.g. vigil-of-st-lawrence-antiphon.yml) —
|
||||
# its own source file's [Rule] block says "Versum Feria" (borrow the
|
||||
# plain ferial day's own versicle/antiphon instead), confirmed live
|
||||
# (Monastic Tridentinum 1617): the Vigil of St. Lawrence's commemoration
|
||||
# on a real Monday (2027-08-09) and the Vigil of the Assumption's on six
|
||||
# different real weekdays (2026 Fri/2027 Sat/2028 Mon/2029 Tue/2030
|
||||
# Wed/2031 Thu) all gave the exact same result for their weekday — the
|
||||
# antiphon quotes the Benedictus canticle's own text in weekday sequence
|
||||
# (same "quote the framing canticle in order through the week" pattern
|
||||
# vespers-magnificat-antiphons.yml already uses for Vespers), same text
|
||||
# regardless of *which* vigil. Sunday has no entry: a Vigil (`FeastClass:
|
||||
# 'vigil'`, weaker than Semiduplex) never actually reaches a commemoration
|
||||
# on an ordinary Sunday in this app's own precedence rules either — it
|
||||
# transfers instead (calendar/commemorations.ts's `ordinary-sunday`
|
||||
# branch) — confirmed live too (2033-08-14, a real Sunday: the Vigil of
|
||||
# the Assumption doesn't appear at all, only the Lawrence-octave day is
|
||||
# commemorated).
|
||||
#
|
||||
# English: the reference engine's own English files have no translation
|
||||
# for this specific commemoration antiphon at all (checked across every
|
||||
# weekday) -- these are direct translations, informed by the Benedictus
|
||||
# canticle's own already-verified English (propers/common/benedictus.yml)
|
||||
# for vocabulary consistency, but not independently sourced -- `status`
|
||||
# marks them `draft` for that reason.
|
||||
|
||||
monday:
|
||||
antiphon:
|
||||
la: "Benedíctus Dóminus, Deus Israël."
|
||||
en: "Blessed be the Lord God of Israel."
|
||||
status: { la: verified, en: draft }
|
||||
|
||||
tuesday:
|
||||
antiphon:
|
||||
la: "Eréxit nobis * Dóminus cornu salútis in domo David púeri sui."
|
||||
en: "The Lord hath raised up for us * a horn of salvation in the house of David his servant."
|
||||
status: { la: verified, en: draft }
|
||||
|
||||
wednesday:
|
||||
antiphon:
|
||||
la: "De manu ómnium qui odérunt nos, líbera nos Dómine."
|
||||
en: "From the hand of all that hate us, deliver us, O Lord."
|
||||
status: { la: verified, en: draft }
|
||||
|
||||
thursday:
|
||||
antiphon:
|
||||
la: "In sanctitáte serviámus Dómino, et liberábit nos ab inimícis nostris."
|
||||
en: "Let us serve the Lord in holiness, and he will deliver us from our enemies."
|
||||
status: { la: verified, en: draft }
|
||||
|
||||
friday:
|
||||
antiphon:
|
||||
la: "Per víscera misericórdiæ Dei nostri visitávit nos Óriens ex alto."
|
||||
en: "Through the tender mercy of our God, the Dayspring from on high hath visited us."
|
||||
status: { la: verified, en: draft }
|
||||
|
||||
saturday:
|
||||
antiphon:
|
||||
la: "Illumináre, Dómine, his qui in ténebris sedent: et dírige pedes nostros in viam pacis, Deus Israël."
|
||||
en: "Give light, O Lord, to them that sit in darkness: and direct our feet into the way of peace, O God of Israel."
|
||||
status: { la: verified, en: draft }
|
||||
@@ -10,6 +10,7 @@ import { splitAntiphon, isDoubleOrHigher } from './antiphon';
|
||||
import { getLaudsSaintOverride } from './lauds-psalmody-overrides';
|
||||
import vespersMagnificatAntiphonsData from '../data/hours/vespers-magnificat-antiphons.yml';
|
||||
import vespersOAntiphonsData from '../data/hours/vespers-o-antiphons.yml';
|
||||
import laudsVigilCommemorationAntiphonsData from '../data/hours/lauds-vigil-commemoration-antiphons.yml';
|
||||
|
||||
type BilingualText = Partial<Record<string, string>>;
|
||||
type TranslationStatus = 'verified' | 'draft' | 'missing';
|
||||
@@ -26,6 +27,10 @@ const vespersMagnificatAntiphons = vespersMagnificatAntiphonsData as Partial<
|
||||
// these seven can't live in a per-Advent-week temporal-proper file the
|
||||
// way every other week's Magnificat antiphon does.
|
||||
const vespersOAntiphons = vespersOAntiphonsData as Partial<Record<string, MagnificatWeekdayDefault>>;
|
||||
// Monday-Saturday only — see lauds-vigil-commemoration-antiphons.yml's own
|
||||
// header for why Sunday has no entry (a Vigil never reaches a
|
||||
// commemoration on an ordinary Sunday in the first place).
|
||||
const vigilCommemorationAntiphons = laudsVigilCommemorationAntiphonsData as Partial<Record<Weekday, MagnificatWeekdayDefault>>;
|
||||
|
||||
function toResolvedText(proper: ProperText): ResolvedText {
|
||||
return { text: proper.text, status: proper.status, citation: proper.citation };
|
||||
@@ -597,6 +602,41 @@ export function getDayCollect(day: LiturgicalDay): ResolvedText {
|
||||
return { text: {}, status: { la: 'missing', en: 'missing' } };
|
||||
}
|
||||
|
||||
/** A Vigil (`common: common-of-a-vigil`) doesn't reuse its own antiphon
|
||||
* when merely commemorated — its own source file's `[Rule]` says "Versum
|
||||
* Feria" (borrow the plain ferial day's own antiphon/versicle instead),
|
||||
* confirmed live across six different real weekdays (see
|
||||
* data/hours/lauds-vigil-commemoration-antiphons.yml's own header for the
|
||||
* dates checked) — the antiphon quotes the Benedictus canticle in weekday
|
||||
* sequence (same pattern vespers-magnificat-antiphons.yml already uses
|
||||
* for the Magnificat), same text regardless of *which* vigil, and the
|
||||
* versicle is a single fixed text (`lauds-versicle-monday.yml`'s own,
|
||||
* confirmed identical even on a live Saturday, so genuinely constant, not
|
||||
* itself weekday-keyed despite superficially matching most weekdays'
|
||||
* plain ferial versicle). Sunday has no entry — a Vigil never reaches a
|
||||
* commemoration on an ordinary Sunday in this app's own precedence rules
|
||||
* either (calendar/commemorations.ts's `ordinary-sunday` branch
|
||||
* transfers it instead), confirmed live too. */
|
||||
function vigilCommemorationAntiphon(weekday: Weekday): ResolvedText | undefined {
|
||||
const entry = vigilCommemorationAntiphons[weekday];
|
||||
return entry ? (entry.status ? { text: entry.antiphon, status: entry.status } : verifiedText(entry.antiphon)) : undefined;
|
||||
}
|
||||
|
||||
function combineCommemorationBundle(antiphon: ResolvedText, versicle: ResolvedText, collect: ResolvedText): ResolvedText {
|
||||
const text: Partial<Record<string, string>> = {};
|
||||
const status: Partial<Record<string, 'verified' | 'draft' | 'missing'>> = {};
|
||||
for (const lang of new Set([...Object.keys(antiphon.text), ...Object.keys(versicle.text), ...Object.keys(collect.text)])) {
|
||||
const parts = [antiphon, versicle, collect].map((t) => t.text[lang]).filter((t): t is string => !!t);
|
||||
if (parts.length === 0) continue;
|
||||
text[lang] = `Ant. ${parts[0] ?? ''}\n${parts[1] ?? ''}\n${parts[2] ?? ''}`.trim();
|
||||
const statuses = [antiphon.status[lang], versicle.status[lang], collect.status[lang]].filter(
|
||||
(s): s is 'verified' | 'draft' | 'missing' => !!s,
|
||||
);
|
||||
status[lang] = statuses.reduce((worst, s) => (STATUS_RANK[s] > STATUS_RANK[worst] ? s : worst), 'verified' as const);
|
||||
}
|
||||
return { text, status };
|
||||
}
|
||||
|
||||
/** A sanctoral commemoration's own rendering: the fuller Ant+V/R+collect
|
||||
* bundle (`${propers}-commemoration`, e.g. st-clare-commemoration.yml)
|
||||
* when authored — live-verified this is the real shape a commemoration
|
||||
@@ -605,8 +645,13 @@ export function getDayCollect(day: LiturgicalDay): ResolvedText {
|
||||
* replaces) when only that's been authored, and to an honestly labeled
|
||||
* "missing" block when neither has. Always labeled ("Commemoration of St.
|
||||
* X") so an unauthored one reads as "this saint's commemoration isn't
|
||||
* written up yet," not as a mystery blank prayer. */
|
||||
function sanctoralCommemorationPart(commemoration: Extract<Commemoration, { kind: 'sanctoral' }>): ResolvedPart {
|
||||
* written up yet," not as a mystery blank prayer. A Vigil is a special
|
||||
* case within this same fallback chain: see vigilCommemorationAntiphon's
|
||||
* own doc comment. */
|
||||
function sanctoralCommemorationPart(
|
||||
commemoration: Extract<Commemoration, { kind: 'sanctoral' }>,
|
||||
weekday: Weekday,
|
||||
): ResolvedPart {
|
||||
const label = `Commemoration of ${commemoration.name}`;
|
||||
const saint = getSaintRecord(commemoration.id);
|
||||
if (saint?.propers) {
|
||||
@@ -616,6 +661,13 @@ function sanctoralCommemorationPart(commemoration: Extract<Commemoration, { kind
|
||||
}
|
||||
const properCollect = resolveCommon(`${saint.propers}-collect`);
|
||||
if (properCollect.status.la !== 'missing' || properCollect.status.en !== 'missing') {
|
||||
if (saint.common === 'common-of-a-vigil') {
|
||||
const antiphon = vigilCommemorationAntiphon(weekday);
|
||||
if (antiphon) {
|
||||
const versicle = resolveCommon('lauds-versicle-monday');
|
||||
return { kind: 'preces', text: combineCommemorationBundle(antiphon, versicle, properCollect), label };
|
||||
}
|
||||
}
|
||||
return { kind: 'preces', text: properCollect, label };
|
||||
}
|
||||
}
|
||||
@@ -693,7 +745,7 @@ export function getDayCollects(day: LiturgicalDay): ResolvedPart[] {
|
||||
if (commemoration.kind === 'temporal') {
|
||||
parts.push({ kind: 'prayer', text: toResolvedText(getTemporalProper(`${commemoration.id}-collect`)) });
|
||||
} else if (commemoration.kind === 'sanctoral') {
|
||||
parts.push(sanctoralCommemorationPart(commemoration));
|
||||
parts.push(sanctoralCommemorationPart(commemoration, day.weekday));
|
||||
} else if (commemoration.kind === 'octave' && commemoration.id !== substitutedOctaveId) {
|
||||
parts.push(octaveCommemorationPart(commemoration));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { resolveOrdo } from '../../src/hours';
|
||||
|
||||
// A Vigil's mere commemoration (as opposed to winning outright, which
|
||||
// uses its own real antiphon — see data/propers/common/vigil-of-st-
|
||||
// lawrence-antiphon.yml etc.) doesn't reuse that antiphon at all: its own
|
||||
// source file's [Rule] says "Versum Feria" — borrow the plain ferial
|
||||
// day's own antiphon/versicle instead. Live-verified (Monastic
|
||||
// Tridentinum 1617) across six real weekdays: the antiphon quotes the
|
||||
// Benedictus canticle in weekday sequence (same shape as Vespers' own
|
||||
// Magnificat-antiphon weekday default), the versicle is a single fixed
|
||||
// text. See hours/resolve-common.ts's vigilCommemorationAntiphon and
|
||||
// data/hours/lauds-vigil-commemoration-antiphons.yml's own header for the
|
||||
// exact dates checked.
|
||||
describe('a Vigil\'s mere commemoration (not winning outright)', () => {
|
||||
it('2027-08-09 (Monday): Vigil of St. Lawrence commemorated under Ss. Cyriacus etc., Monday\'s own Benedictus-verse antiphon', () => {
|
||||
const lauds = resolveOrdo('lauds', '2027-08-09');
|
||||
const commem = lauds.parts.find((p) => p.kind === 'preces' && p.label?.includes('Vigil of St. Lawrence'));
|
||||
expect(commem?.kind === 'preces' ? commem.text.text.la : undefined).toContain('Benedíctus Dóminus, Deus Israël.');
|
||||
expect(commem?.kind === 'preces' ? commem.text.text.la : undefined).toContain('Repléti sumus mane misericórdia tua');
|
||||
expect(commem?.kind === 'preces' ? commem.text.text.la : undefined).toContain('Adésto, Dómine'); // his own real collect, unchanged
|
||||
expect(commem?.kind === 'preces' ? commem.text.status.la : undefined).toBe('verified');
|
||||
});
|
||||
|
||||
it('2029-08-14 (Tuesday) vs. 2030-08-14 (Wednesday): the same Vigil of the Assumption gets a different antiphon on a different weekday, same fixed versicle', () => {
|
||||
const tuesday = resolveOrdo('lauds', '2029-08-14');
|
||||
const wednesday = resolveOrdo('lauds', '2030-08-14');
|
||||
const tueCommem = tuesday.parts.find((p) => p.kind === 'preces' && p.label?.includes('Vigil of the Assumption'));
|
||||
const wedCommem = wednesday.parts.find((p) => p.kind === 'preces' && p.label?.includes('Vigil of the Assumption'));
|
||||
const tueText = tueCommem?.kind === 'preces' ? tueCommem.text.text.la : undefined;
|
||||
const wedText = wedCommem?.kind === 'preces' ? wedCommem.text.text.la : undefined;
|
||||
expect(tueText).toContain('Eréxit nobis');
|
||||
expect(wedText).toContain('De manu ómnium qui odérunt nos');
|
||||
expect(tueText).toContain('Repléti sumus mane misericórdia tua');
|
||||
expect(wedText).toContain('Repléti sumus mane misericórdia tua');
|
||||
});
|
||||
|
||||
it('2033-08-14 (a real Sunday): the Vigil is never even commemorated — a Vigil transfers off an ordinary Sunday, matching the live reference exactly', () => {
|
||||
const lauds = resolveOrdo('lauds', '2033-08-14');
|
||||
const commem = lauds.parts.find((p) => p.kind === 'preces' && p.label?.includes('Vigil of the Assumption'));
|
||||
expect(commem).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user