Compare commits

..

2 Commits

Author SHA1 Message Date
will a7da8b0211 Add Assumption's proper Matins hymn
Deploy / deploy (push) Successful in 1m13s
"Surge! Jam terris fera bruma cessit" / "Arise! the cold blasts from
earth have receded", read from the reference engine's Sancti/08-15.txt,
used throughout the Assumption's octave via that override mechanism.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3wxvz3mPXiHxkPB5JpnmD
2026-08-21 07:56:11 -04:00
will 62c74539f8 Add Matins hymn override mechanism
Matins always rendered the plain ferial hymn, unlike Lauds/Vespers which
already check a duplex-majus+ override then a seasonal default first.
Surfaced by the Assumption's octave, which keeps the feast's own proper
hymn all week rather than falling back to ferial.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3wxvz3mPXiHxkPB5JpnmD
2026-08-21 07:56:06 -04:00
2 changed files with 164 additions and 3 deletions
@@ -0,0 +1,124 @@
# The Assumption's own proper Matins hymn ("Surge! Jam terris fera bruma
# cessit" / "Arise! the cold blasts from earth have receded"), read from
# `Sancti/08-15.txt`'s own `[Hymnus Matutinum]` block (both Latin and
# English) -- distinct from the BVM Common (CommuneM/C10), which has no
# Matins hymn override of its own at all. Used throughout the Assumption's
# octave (Aug 15-22), not just on the feast day itself: `Sancti/08-21bmv.txt`
# (a representative octave day) carries `[Rule] ex Sancti/08-15`, meaning the
# octave day's whole office -- Matins hymn included -- is the Assumption's
# own, not the plain BVM Common's.
id: matins-hymn-assumption
text:
la: >-
Surge! Jam terris fera bruma cessit,
Ridet in pratis decus omne florum,
Alma quæ Vitæ Génetrix fuísti,
Surge, María!
Lílium fulgens velut in rubéto,
Mortis auctórem teris una, carpens
Sóntibus fructum pátribus negátum
Arbore vitæ.
Arca non putri fabricáta ligno
Manna tu servas, fluit unde virtus,
Ipsa qua surgent animáta rursus
Ossa sepúlcris.
Prǽsidis mentis dócilis minístra,
Haud caro tabo pátitur resólvi;
Spíritus imo sine fine consors
Tendit ad astra.
Surge! Dilécto pete nixa cælum,
Sume consértum diadéma stellis,
Teque natórum récinens beátam
Excipe carmen.
Laus sit excélsæ Tríadi perénnis,
Quæ tibi, Virgo, tríbuit corónam,
Atque regínam statuítque nostram
Próvida matrem.
Amen.
en: >-
Arise! the cold blasts from earth have receded,
And in the field are lovely flowers smiling,
For thee, O gracious Mother, bearer of Life,
Arise, O Mary!
Beautiful Lily blooming 'mid the brambles,
Death's haughty author thou alone didst conquer,
Plucking life-giving tree of fruits the fathers
By sin did not taste.
Ark of sweet wood not destined for ruin,
Holding the manna, whence springeth forth the power
Summoning forth the bones again arisen
From depths of the tomb.
Thou handmaid, faithful to the Ruler of hearts,
Thy flesh cruel decay could never touch,
Thy soul of Spirit partaking without end,
Hath winged to the stars.
Leaning on thy beloved, arise, go heav'nward!
Accept the crown with stars for thee bedecked,
List to the hymn thy children sing on this day,
Calling thee blessed.
Praise to the Triune Godhead everlasting,
Who hath caused thee, O Virgin, to be crowned,
And providently willed our Queen thou shouldst be
Also our Mother.
Amen.
status:
la: verified
en: verified
+40 -3
View File
@@ -60,7 +60,7 @@
// Only a small, growable slice of content is authored so far (one clean // Only a small, growable slice of content is authored so far (one clean
// ferial day, one clean Sunday) — this is the mechanism build, not the // ferial day, one clean Sunday) — this is the mechanism build, not the
// full-calendar content pass. See TODO.md for what's deferred. // full-calendar content pass. See TODO.md for what's deferred.
import type { ResolvedOrdo, ResolvedPart, ResolvedVerse } from './types'; import type { ResolvedOrdo, ResolvedPart, ResolvedText, ResolvedVerse } from './types';
import type { LiturgicalDay } from '../calendar/types'; import type { LiturgicalDay } from '../calendar/types';
import { resolveDay, resolveTemporalId, activeOctavesFor } from '../calendar'; import { resolveDay, resolveTemporalId, activeOctavesFor } from '../calendar';
import { isInTriduum } from '../calendar/temporal'; import { isInTriduum } from '../calendar/temporal';
@@ -70,7 +70,15 @@ import { getPsalmsFor, type PsalmRef } from '../psalter/distribution';
import { getScriptureVerses } from '../scripture'; import { getScriptureVerses } from '../scripture';
import { getOpeningVersicleId } from './opening-versicle'; import { getOpeningVersicleId } from './opening-versicle';
import { isDoubleOrHigher } from './antiphon'; import { isDoubleOrHigher } from './antiphon';
import { resolveCommon, getDayCollect, resolveOfficeWinner, verifiedText, splitNamedAntiphon } from './resolve-common'; import {
resolveCommon,
getDayCollect,
getOfficeOverrideId,
resolveOfficeWinner,
seasonalOfficeSuffix,
verifiedText,
splitNamedAntiphon,
} from './resolve-common';
import { getBiblePlanReadings } from '../propers/bible-plan'; import { getBiblePlanReadings } from '../propers/bible-plan';
import { getNocturnReadings, type NocturnReading } from '../propers/nocturn-readings'; import { getNocturnReadings, type NocturnReading } from '../propers/nocturn-readings';
import { getOctaveReading } from '../propers/octave-readings'; import { getOctaveReading } from '../propers/octave-readings';
@@ -180,6 +188,35 @@ function sundayCanticleNocturn(group: SundayNocturn, day: LiturgicalDay): Resolv
return parts; return parts;
} }
/** The Matins hymn — a duplex-majus+ saint's or eligible named temporal
* feast's own proper hymn (`matins-hymn-${overrideId}`, via the same
* getOfficeOverrideId eligibility Lauds/Vespers' own resolveOffice uses),
* when authored, else falls to a *seasonal* default (Advent/Lent/
* Passiontide/Paschaltide, none authored yet), else the plain year-round
* ferial hymn — same override > season > ferial precedence every other
* hour's own office bundle uses (see lauds.ts's resolveOffice), just never
* wired up here before. Concrete motivating case (user, 2026-08-21): the
* Assumption's octave (`Sancti/08-21bmv.txt`'s own `[Rule] ex Sancti/
* 08-15`) genuinely keeps the feast's own proper hymn all week, not the
* ferial one this always rendered previously. */
function resolveMatinsHymn(day: LiturgicalDay): ResolvedText {
const overrideId = getOfficeOverrideId(day);
if (overrideId) {
const proper = resolveCommon(`matins-hymn-${overrideId}`);
if (proper.status.la !== 'missing' || proper.status.en !== 'missing') {
return proper;
}
}
const seasonSuffix = seasonalOfficeSuffix(day.season);
if (seasonSuffix) {
const seasonal = resolveCommon(`matins-hymn-${seasonSuffix}`);
if (seasonal.status.la !== 'missing' || seasonal.status.en !== 'missing') {
return seasonal;
}
}
return resolveCommon('matins-hymn-ferial');
}
function ferialPsalmody(day: LiturgicalDay): ResolvedPart[] { function ferialPsalmody(day: LiturgicalDay): ResolvedPart[] {
return psalmRefParts(getPsalmsFor('matins', day.weekday)); return psalmRefParts(getPsalmsFor('matins', day.weekday));
} }
@@ -317,7 +354,7 @@ export function resolveOrdo(date: string): ResolvedOrdo {
plainPsalm(3), plainPsalm(3),
{ kind: 'section-heading', label: 'Invitatory' }, { kind: 'section-heading', label: 'Invitatory' },
...invitatoryParts(day), ...invitatoryParts(day),
{ kind: 'hymn', text: resolveCommon('matins-hymn-ferial') }, { kind: 'hymn', text: resolveMatinsHymn(day) },
]; ];
if (threeNocturns && day.weekday === 'sunday') { if (threeNocturns && day.weekday === 'sunday') {