Extract shared Commons for Prime/Terce/Sext/None minor-hour content
Deploy / deploy (push) Successful in 57s

A systematic diff across all 29 duplex-majus+ saints' P/T/S/N files found
real shared-Common groups beyond the pairs noticed in passing while
authoring them. Extracted into shared data/propers/common/{hour}-antiphon-
common-of-*.yml / {hour}-capitulum-common-of-*.yml files and wired via a
new SaintRecord.minorHoursCommon field, with resolveMinorHourAntiphon/
resolveMinorHourChapter falling back to it when a saint has no proper file
of their own. Deliberately a separate field from the existing (previously
unused) `common` field: the minor hours don't always draw from the same
Common category as the saint's overall classification -- both Chair of
St. Peter feasts are `common: common-of-an-apostle` by rank, but their
P/T/S/N content is Common of a Confessor Bishop (Peter considered as
bishop of that see).

Four groups extracted (87 duplicate files collapsed to 21 shared ones),
each confirmed byte-identical via diff first: common-of-apostles (8
saints, all 4 hours), common-of-a-confessor-bishop (Chair of Peter pair
share antiphons+chapters, Martin of Tours shares only chapters), common-
of-the-bvm (Nativity BVM + Immaculate Conception's Sext/None/Terce
chapters -- the Assumption's own stay proper, different reading + proper
versicles), and common-of-st-peter (St. Peter ad Vincula + Ss. Peter and
Paul, not a real Common category, just genuinely identical Petrine
content).

Deliberately left un-extracted: St. Mark, St. John Before the Latin Gate,
and Ss. Philip and James share identical text that live-checking shows is
Paschaltide-seasonal (all three landed within the Easter octave weeks on
their queried dates, despite disagreeing `common` classifications) rather
than a fixed per-saint-category Common -- folding it into the same
mechanism would misrepresent season-contingent content as timeless. Left
as individually-authored files; flagged in TODO.md as needing a real
Paschaltide-awareness mechanism this app doesn't have yet for any hour.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 15:47:13 -04:00
parent 6dad25fef6
commit 735f677815
108 changed files with 270 additions and 884 deletions
+25 -7
View File
@@ -113,10 +113,13 @@ export function getMinorHourOverrideId(day: LiturgicalDay): string | undefined {
/** 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 — 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 without one authored yet just falls
* through to the plain weekday default silently. */
* authored, then by that saint's shared Common (`${hourId}-antiphon-
* ${minorHoursCommon}`, see SaintRecord'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 neither
* authored yet just falls through to the plain weekday default silently.
*/
export function resolveMinorHourAntiphon(
hourId: string,
day: LiturgicalDay,
@@ -128,6 +131,13 @@ export function resolveMinorHourAntiphon(
if (proper.status.la !== 'missing' || proper.status.en !== 'missing') {
return proper;
}
const commonId = getSaintRecord(overrideId)?.minorHoursCommon;
if (commonId) {
const common = resolveCommon(`${hourId}-antiphon-${commonId}`);
if (common.status.la !== 'missing' || common.status.en !== 'missing') {
return common;
}
}
}
// The plain weekday default (data/hours/{hour}-antiphons.yml) never
// carries its own status field at all — always implicitly verified by
@@ -136,9 +146,10 @@ export function resolveMinorHourAntiphon(
}
/** Same idea as resolveMinorHourAntiphon, for the chapter
* (`${hourId}-capitulum-${id}`) — falls back to `fallbackId` (the plain
* per-annum/per-weekday one already in place for that hour) when no
* override is eligible, or none has been authored yet for one that is. */
* (`${hourId}-capitulum-${id}`, then `${hourId}-capitulum-
* ${minorHoursCommon}`) — falls back to `fallbackId` (the plain
* per-annum/per-weekday one already in place for that hour) when nothing
* eligible is authored, either proper or shared-Common. */
export function resolveMinorHourChapter(hourId: string, day: LiturgicalDay, fallbackId: string): ResolvedText {
const overrideId = getMinorHourOverrideId(day);
if (overrideId) {
@@ -146,6 +157,13 @@ export function resolveMinorHourChapter(hourId: string, day: LiturgicalDay, fall
if (proper.status.la !== 'missing' || proper.status.en !== 'missing') {
return proper;
}
const commonId = getSaintRecord(overrideId)?.minorHoursCommon;
if (commonId) {
const common = resolveCommon(`${hourId}-capitulum-${commonId}`);
if (common.status.la !== 'missing' || common.status.en !== 'missing') {
return common;
}
}
}
return resolveCommon(fallbackId);
}