Derive Prime/Terce/Sext/None antiphons from a saint's own Lauds set
resolveMinorHourAntiphon fell straight from a (rarely-authored) dedicated per-hour proper file to the saint's generic Common-category antiphon, skipping the real proper text a saint may already have as its 5-antiphon Lauds/Vespers set. The reference engine's own getanthoras() mechanism derives exactly these four minor-hour antiphons from that same 5-antiphon set by fixed index (Prime/Terce/ Sext/None <- antiphons 1/2/3/5) whenever no dedicated per-hour proper exists; vu had no equivalent, so any minor hour fell back to the generic Common antiphon even when the feast's own text was already sitting right there in its Lauds override. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFGHb4XMe6Wya4pGpPhEEi
This commit is contained in:
@@ -7,6 +7,7 @@ import { resolveActiveOctave, activeOctavesFor, octaveGoverningPrivilegedDay, is
|
||||
import { getTemporalFeastRecord } from '../calendar/temporal-feasts';
|
||||
import { isInTriduum } from '../calendar/temporal';
|
||||
import { splitAntiphon, isDoubleOrHigher } from './antiphon';
|
||||
import { getLaudsSaintOverride } from './lauds-psalmody-overrides';
|
||||
import vespersMagnificatAntiphonsData from '../data/hours/vespers-magnificat-antiphons.yml';
|
||||
|
||||
type BilingualText = Partial<Record<string, string>>;
|
||||
@@ -356,19 +357,52 @@ function withPaschaltideAlleluia(resolved: ResolvedText, day: LiturgicalDay): Re
|
||||
return appendPaschaltideAlleluia(resolved);
|
||||
}
|
||||
|
||||
// The reference engine's own `getanthoras()` (specials.pl): when a saint
|
||||
// has no dedicated per-hour minor-hour antiphon of its own, one is
|
||||
// derived from that saint's 5-antiphon Lauds/Vespers set (`[Ant Laudes]`,
|
||||
// which for most saints is `@:Ant Vespera`) by fixed index — Prime takes
|
||||
// the first (the same antiphon as the first psalm-group), Terce/Sext the
|
||||
// second/third, None the fifth (the Laudate antiphon); the fourth
|
||||
// (canticle) antiphon is never used for a minor hour. vu's own
|
||||
// `LaudsPsalmodyOverride` already stores that same 5-antiphon set in that
|
||||
// exact order (`groups[0..2]`, `canticle`, `laudate`), so once a saint's
|
||||
// Lauds proper is authored, no separate per-hour file is needed for this
|
||||
// tier — same "mechanism first, content incrementally" pattern as
|
||||
// everywhere else in this codebase.
|
||||
const MINOR_HOUR_LAUDS_ANTIPHON_INDEX: Record<string, number> = { prime: 0, terce: 1, sext: 2, none: 4 };
|
||||
|
||||
function deriveMinorHourAntiphonFromLauds(hourId: string, overrideId: string): ResolvedText | undefined {
|
||||
const index = MINOR_HOUR_LAUDS_ANTIPHON_INDEX[hourId];
|
||||
if (index === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const lauds = getLaudsSaintOverride(overrideId);
|
||||
if (!lauds) {
|
||||
return undefined;
|
||||
}
|
||||
const antiphon = index === 4 ? lauds.laudate.antiphon : lauds.groups[index]?.antiphon;
|
||||
if (!antiphon) {
|
||||
return undefined;
|
||||
}
|
||||
return verifiedText(antiphon);
|
||||
}
|
||||
|
||||
/** A Little Hour's (or Prime's) plain per-weekday antiphon, overridden by
|
||||
* a duplex-majus+ feast's own proper (`${hourId}-antiphon-${id}`) when
|
||||
* authored, then by that saint's shared Common (`${hourId}-antiphon-
|
||||
* authored, then by that saint's own Lauds/Vespers antiphon set (see
|
||||
* `deriveMinorHourAntiphonFromLauds` above — a real liturgical derivation,
|
||||
* not a guess), then by that saint's shared Common (`${hourId}-antiphon-
|
||||
* ${minorHoursCommon}`, see SaintRecord's own doc comment) — tried first
|
||||
* in its Paschaltide-variant form (`${minorHoursCommon}-paschaltide`)
|
||||
* when the day falls in Paschaltide and that variant has been authored,
|
||||
* per PASCHALTIDE_SEASONS's own doc comment — when a proper one hasn't
|
||||
* been authored — same honest "not gated behind whether content exists,
|
||||
* just eligible to override at all" fallback as every other override in
|
||||
* this codebase: an eligible feast with none of these authored yet just
|
||||
* falls through to the plain weekday default silently. The plain (non-
|
||||
* `-paschaltide`) Common fallback itself still picks up a seasonal change
|
||||
* in Paschaltide — see `withPaschaltideAlleluia`.
|
||||
* per PASCHALTIDE_SEASONS's own doc comment — when neither of the first
|
||||
* two has been authored — same honest "not gated behind whether content
|
||||
* exists, just eligible to override at all" fallback as every other
|
||||
* override in this codebase: an eligible feast with none of these
|
||||
* authored yet just falls through to the plain weekday default silently.
|
||||
* The plain (non-`-paschaltide`) Common fallback itself still picks up a
|
||||
* seasonal change in Paschaltide — see `withPaschaltideAlleluia`. (The
|
||||
* derived-from-Lauds tier does not get that treatment — see TODO.md.)
|
||||
*/
|
||||
export function resolveMinorHourAntiphon(
|
||||
hourId: string,
|
||||
@@ -381,6 +415,10 @@ export function resolveMinorHourAntiphon(
|
||||
if (proper.status.la !== 'missing' || proper.status.en !== 'missing') {
|
||||
return proper;
|
||||
}
|
||||
const derived = deriveMinorHourAntiphonFromLauds(hourId, overrideId);
|
||||
if (derived) {
|
||||
return derived;
|
||||
}
|
||||
const commonId = getSaintRecord(overrideId)?.minorHoursCommon;
|
||||
if (commonId) {
|
||||
if (PASCHALTIDE_SEASONS.has(day.season)) {
|
||||
|
||||
Reference in New Issue
Block a user