hours: build Lauds
Deploy / deploy (push) Successful in 48s

Opening versicle; Ps 66 (fixed, no antiphon at all); the weekday-
variable psalm groups (Ps 50 plus 1-2 more, framed by their own
antiphon each, wholesale-substituted by Sunday's Ps 50+117-share-one-
antiphon shape); the weekday OT canticle; the Laudate psalms (148-150)
under one more shared antiphon; the weekday chapter/responsory/hymn/
versicle bundle; the Benedictus (framed by a day-resolved antiphon,
same incipit-or-full/full-repeat shape as Compline's Nunc Dimittis);
the Kyrie/Pater-noster short litany; the day's collect(s) — winner
plus one per commemoration, via the new getDayCollects; the four
Tridentine suffrages (omitted on a Double-or-higher feast); the
conclusio; the closing versicle (the silent Our Father that precedes
it isn't re-rendered, its text having already appeared once at the
short litany); and the closing Marian antiphon, reusing Compline's own
mechanism exactly.

Known, deliberate gap, not yet fixed: the psalmody doesn't model the
Commune/feast override real practice applies on a Semiduplex-or-higher
day (substituting an entirely different psalm+canticle set) — every
day currently gets the plain ferial weekday distribution regardless of
who wins. That's a separate, larger propers-authoring task (a full
Common-of-Saints psalm set per Common).

Also a simplification in getDayCollects: each collect in a multi-
collect day renders as its own complete "Let us pray ... Amen." block,
rather than the more compressed real form (one "Let us pray," only the
last collect's doxology).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 11:45:25 -04:00
parent 70fbdd12d0
commit 5830e4cfd2
12 changed files with 547 additions and 11 deletions
+56
View File
@@ -35,6 +35,62 @@ export function getDayCollect(day: LiturgicalDay): ResolvedText {
return { text: {}, status: { la: 'missing', en: 'missing' } };
}
/**
* The day's own collect, plus one more per commemoration (calendar/
* types.ts's LiturgicalDay.commemorations) — Lauds/Vespers say all of
* these in sequence, unlike the Little Hours' single getDayCollect.
*
* Simplification, not yet corrected: real practice compresses this into
* one "Orémus" and lets only the *last* collect close with the full
* doxology, with earlier ones trailing straight into the next ("And:").
* Each collect file (data/propers/temporal/*-collect.yml, the per-saint
* *-collect.yml files) already bakes in its own "Orémus."/"Per Dóminum...
* Amen." for the single-collect case every other hour uses today, and
* stripping that back out per-collect to chain them properly would need
* text surgery this doesn't attempt — so on a commemorated day, each
* collect here renders as its own complete, separate block instead. An
* octave commemoration never contributes a collect (none authored — the
* octave content pull only ever sourced Matins readings, see propers/
* octave-readings.ts), so those are silently skipped, same as a
* not-yet-authored saint's own collect resolves to "missing" rather than
* throwing.
*/
export function getDayCollects(day: LiturgicalDay): ResolvedText[] {
const collects = [getDayCollect(day)];
for (const commemoration of day.commemorations) {
if (commemoration.kind === 'temporal') {
collects.push(toResolvedText(getTemporalProper(`${commemoration.id}-collect`)));
} else if (commemoration.kind === 'sanctoral') {
const saint = getSaintRecord(commemoration.id);
collects.push(
saint?.propers
? resolveCommon(`${saint.propers}-collect`)
: { text: {}, status: { la: 'missing', en: 'missing' } },
);
}
}
return collects;
}
/**
* Lauds' Benedictus antiphon, resolved the same way as getDayCollect: a
* per-saint `${propers}-antiphon` (already authored during the sanctoral
* pull, sourced from each saint's own raw [Ant 1] — see e.g.
* st-lawrence-antiphon.yml) for a sanctoral winner, `${id}-benedictus-
* antiphon` (not authored yet for any temporal id — resolves "missing",
* same pending convention as everywhere else) for a temporal one.
*/
export function getBenedictusAntiphon(day: LiturgicalDay): ResolvedText {
if (day.winner.kind === 'temporal') {
return toResolvedText(getTemporalProper(`${day.winner.id}-benedictus-antiphon`));
}
const saint = getSaintRecord(day.winner.id);
if (saint?.propers) {
return resolveCommon(`${saint.propers}-antiphon`);
}
return { text: {}, status: { la: 'missing', en: 'missing' } };
}
export function verifiedText(text: Partial<Record<string, string>>): ResolvedText {
const status: Partial<Record<string, 'verified'>> = {};
for (const lang of Object.keys(text)) {