Compare commits

..

3 Commits

Author SHA1 Message Date
will 20b1401919 Log Sunday Nocturn 3 seasonal fix, close the flagged gap
Deploy / deploy (push) Successful in 1m27s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 08:17:18 -04:00
will 3d221c2a45 Give Sunday Matins Nocturn III its own antiphon Septuagesima-Palm Sunday
It was hardcoded to "Allelúja, allelúja, allelúja" year-round. Each of
the 9 Sundays from Septuagesima through Palm Sunday has its own
Gospel-tied antiphon instead, live-verified against Monastic Tridentinum
1617; Lent 1-4 also swap in their own canticles, and Lent 1-4 plus
Passion/Palm Sunday swap in their own versicle. New
matins-sunday-nocturn3-overrides.yml, keyed by temporalId and merged
over the base nocturn3 group by hours/matins.ts's new
sundayNocturn3For — only the real-Sunday branch uses it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 08:17:14 -04:00
will 36e2cc60cd Add Jeremiah 14, Lamentations 5, and Ezekiel 36 scripture chapters
Lent 1-4's Sunday Matins Nocturn III canticles, transcribed from a live
Divinum Officium query (Monastic Tridentinum 1617). First content under
the `lam` book code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 08:17:04 -04:00
7 changed files with 384 additions and 18 deletions
+29 -17
View File
@@ -29,23 +29,6 @@ across every hour and content type in this app.
needs bilingual voice selection (La/En) worked out, and quality for Latin pronunciation needs bilingual voice selection (La/En) worked out, and quality for Latin pronunciation
is an open question. Open, not started — needs its own design pass before content/UI work is an open question. Open, not started — needs its own design pass before content/UI work
begins. begins.
- Sunday Matins Nocturn 3 antiphon is hardcoded to "Allelúja, allelúja, allelúja"
(`matins-sunday-antiphons.yml`) for every Sunday year-round — wrong for Septuagesima
through Passiontide. Live-verified against Monastic Tridentinum 1617 (2026-09-01): each of
the 9 Sundays in that stretch (Septuagesima, Sexagesima, Quinquagesima, Lent 1-4, Passion
Sunday, Palm Sunday) has its own distinct, Gospel-tied Nocturn 3 antiphon — same 3
canticles and same versicle throughout, only the antiphon text changes (confirmed
Nocturn 1-2 are unaffected). Needs a new per-temporal-id override on top of
`sundayCanticleNocturn`, plus the 9 antiphons themselves (already collected, not yet
authored into YAML): Septuagesima "Ite et vos in víneam meam, † et quod justum fúerit,
dabo vobis."; Sexagesima "Semen cécidit in terram bonam, † et ortum fecit fructum
céntuplum."; Quinquagesima "Miserére mei, Fili David! † Quid vis ut fáciam tibi? Dómine,
ut vídeam."; Lent 1 "Non in solo pane vivit homo, † sed in omni verbo quod procédit de ore
Dei."; Lent 2 "Faciámus hic tria tabernácula: † tibi unum, Móysi unum, et Elíæ unum."; Lent
3 "Si in dígito Dei..."; Lent 4 "Satiávit Dóminus quinque míllia hóminum de quinque pánibus
et duóbus píscibus."; Passion Sunday "Deus ádjuvat me, * et Dóminus suscéptor est ánimæ
meæ."; Palm Sunday "Hosánna..." (the last two need their full text, not just the
incipit, pulled from a live query before authoring).
2. **Minimal-proof-done, bulk content pending** — mechanism is built and trusted (a proof 2. **Minimal-proof-done, bulk content pending** — mechanism is built and trusted (a proof
date/entry already resolves correctly end-to-end); what's left is authoring/importing more date/entry already resolves correctly end-to-end); what's left is authoring/importing more
@@ -4537,3 +4520,32 @@ first psalm" — now every psalm in the nocturn has one). `npm test` (814 passed
"Allelúja, allelúja, allelúja" antiphon is wrong for Septuagesima-Passiontide — see this file's "Allelúja, allelúja, allelúja" antiphon is wrong for Septuagesima-Passiontide — see this file's
"Mechanism gaps" section above for the live-verified detail and the 9 antiphons already "Mechanism gaps" section above for the live-verified detail and the 9 antiphons already
collected. collected.
### Sunday Matins Nocturn III seasonal override, Septuagesima-Palm Sunday (2026-09-01)
Fixed the gap flagged above. New `data/hours/matins-sunday-nocturn3-overrides.yml`, keyed by
`temporalId`, plus `hours/matins.ts`'s `sundayNocturn3For` (looked up with the `temporalId`
`resolveOrdo` already computes, merged field-by-field over `matins-sunday-antiphons.yml`'s base
`nocturn3` group — only the real Sunday branch uses it, not the Duplex+-weekday-feast branches,
which stay saint/Common-specific as before).
Re-verifying live (one `prayMatutinum` query per Sunday of the 2026 cycle, Monastic Tridentinum
1617) turned up more than the antiphon-only change the earlier note assumed:
- **Septuagesima, Sexagesima, Quinquagesima**: antiphon only — canticles (Isa 33:2-10/33:13-18,
Sir 36:14-19) and versicle ("Exaltáre Dómine in virtúte tua. / Cantábimus et psallémus
virtútes tuas.") are the same as the plain-season default.
- **Lent 1-4**: a genuinely different set of 3 canticles (Jer 14:17-21, Lam 5:1-7,15-17,19-21,
Ezek 36:24-28 — new `data/scripture/jer-14.yml`, `lam-5.yml`, `ezek-36.yml`, the first content
under the `lam` book code) *and* a different versicle ("Scuto circúmdabit te véritas ejus. /
Non timébis a timóre noctúrno."), on top of the antiphon.
- **Passion Sunday, Palm Sunday**: base canticles, but the versicle is "Ne perdas cum ímpiis,
Deus, ánimam meam. / Et cum viris sánguinum vitam meam." — not "Exaltáre Dómine." The earlier
note's placeholder Passion Sunday antiphon text ("Deus ádjuvat me...") was actually this
versicle, not the antiphon; the real antiphon is "Ego glóriam meam non quæro: † est qui
quærat, et júdicet." (John 8:50, that Sunday's own Gospel). Palm Sunday's full antiphon:
"Hosánna fílio David: † benedíctus, qui venit in nómine Dómini, Rex Israël: † Hosánna in
excélsis."
New regression tests in `tests/hours/matins.test.ts` cover all three shapes (antiphon-only,
antiphon+canticles+versicle, antiphon+versicle-only). `npm test` (817 passed), `tsc --noEmit`,
and `npm run build` all pass.
@@ -0,0 +1,119 @@
# Sunday Matins Nocturn III per-season override, keyed by temporalId.
# matins-sunday-antiphons.yml's own [Daym0] antiphon ("Allelúja, allelúja,
# allelúja") is the plain-season default; from Septuagesima through Palm
# Sunday inclusive (9 Sundays), Monastic Tridentinum 1617 gives Nocturn
# III its own distinct, Gospel-tied antiphon instead — live-verified
# against the local reference-engine CGI instance (`prayMatutinum`,
# Monastic Tridentinum 1617, 2026-09-01, one query per Sunday of 2026's
# cycle).
#
# That same live check found the original TODO note incomplete: it isn't
# only the antiphon that changes.
# - Septuagesima, Sexagesima, Quinquagesima keep the base canticles
# (Isa 33:2-10 / Isa 33:13-18 / Sir 36:14-19) and the base versicle
# ("Exaltáre Dómine in virtúte tua. / Cantábimus et psallémus virtútes
# tuas.") — only `antiphon` is listed here for these three.
# - Lent 1-4 substitute a wholly different set of 3 canticles (Jer
# 14:17-21, Lam 5:1-7,15-17,19-21, Ezek 36:24-28 — new scripture
# chapter files authored alongside this one) and a different versicle
# ("Scuto circúmdabit te véritas ejus. / Non timébis a timóre
# noctúrno.").
# - Passion Sunday and Palm Sunday keep the base canticles but take the
# Lent versicle's own neighbor verse instead ("Ne perdas cum ímpiis,
# Deus, ánimam meam. / Et cum viris sánguinum vitam meam.").
#
# `canticles`/`versicle` are omitted where the base
# matins-sunday-antiphons.yml value already applies; hours/matins.ts's
# lookup falls back to the base nocturn3 group for any field not given
# here.
septuagesima:
antiphon:
la: "Ite et vos in víneam meam, † et quod justum fúerit, dabo vobis."
en: "Go ye also into my vineyard, and whatsoever is right, that will I give you."
sexagesima:
antiphon:
la: "Semen cécidit in terram bonam, † et ortum fecit fructum céntuplum."
en: "Some seed fell on good ground, and gave fruit one hundred-fold."
quinquagesima:
antiphon:
la: "Miserére mei, Fili David! † Quid vis ut fáciam tibi? Dómine, ut vídeam."
en: "Son of David, have mercy on me. What wilt thou that I do to thee? Lord, that I may see."
lent-1:
antiphon:
la: "Non in solo pane vivit homo, † sed in omni verbo quod procédit de ore Dei."
en: "Not on bread alone doth man live, but on every word that proceedeth from the mouth of God."
canticles:
- refs: [{ book: jer, chapter: 14, verses: "17-21" }]
- refs:
- { book: lam, chapter: 5, verses: "1-7" }
- { book: lam, chapter: 5, verses: "15-17" }
- { book: lam, chapter: 5, verses: "19-21" }
- refs: [{ book: ezek, chapter: 36, verses: "24-28" }]
versicle:
v: { la: "Scuto circúmdabit te véritas ejus.", en: "His truth shall compass thee with a shield." }
r: { la: "Non timébis a timóre noctúrno.", en: "Thou shalt not be afraid of the terror of the night." }
lent-2:
antiphon:
la: "Faciámus hic tria tabernácula: † tibi unum, Móysi unum, et Elíæ unum."
en: "Let us make here three tabernacles, one for thee, and one for Moses, and one for Elias."
canticles:
- refs: [{ book: jer, chapter: 14, verses: "17-21" }]
- refs:
- { book: lam, chapter: 5, verses: "1-7" }
- { book: lam, chapter: 5, verses: "15-17" }
- { book: lam, chapter: 5, verses: "19-21" }
- refs: [{ book: ezek, chapter: 36, verses: "24-28" }]
versicle:
v: { la: "Scuto circúmdabit te véritas ejus.", en: "His truth shall compass thee with a shield." }
r: { la: "Non timébis a timóre noctúrno.", en: "Thou shalt not be afraid of the terror of the night." }
lent-3:
antiphon:
la: "Si in dígito Dei ejício dæmónia, profécto pervénit in vos regnum Dei."
en: "If I by the finger of God cast out devils; doubtless the kingdom of God is come upon you."
canticles:
- refs: [{ book: jer, chapter: 14, verses: "17-21" }]
- refs:
- { book: lam, chapter: 5, verses: "1-7" }
- { book: lam, chapter: 5, verses: "15-17" }
- { book: lam, chapter: 5, verses: "19-21" }
- refs: [{ book: ezek, chapter: 36, verses: "24-28" }]
versicle:
v: { la: "Scuto circúmdabit te véritas ejus.", en: "His truth shall compass thee with a shield." }
r: { la: "Non timébis a timóre noctúrno.", en: "Thou shalt not be afraid of the terror of the night." }
lent-4:
antiphon:
la: "Satiávit Dóminus quinque míllia hóminum de quinque pánibus et duóbus píscibus."
en: "The Lord filled up five thousand men with five loaves of bread and two fish."
canticles:
- refs: [{ book: jer, chapter: 14, verses: "17-21" }]
- refs:
- { book: lam, chapter: 5, verses: "1-7" }
- { book: lam, chapter: 5, verses: "15-17" }
- { book: lam, chapter: 5, verses: "19-21" }
- refs: [{ book: ezek, chapter: 36, verses: "24-28" }]
versicle:
v: { la: "Scuto circúmdabit te véritas ejus.", en: "His truth shall compass thee with a shield." }
r: { la: "Non timébis a timóre noctúrno.", en: "Thou shalt not be afraid of the terror of the night." }
passion-sunday:
antiphon:
la: "Ego glóriam meam non quæro: † est qui quærat, et júdicet."
en: "I seek not Mine Own glory; there is One That seeketh and judgeth."
versicle:
v: { la: "Ne perdas cum ímpiis, Deus, ánimam meam.", en: "Take not away my soul, O God, with the wicked." }
r: { la: "Et cum viris sánguinum vitam meam.", en: "Nor my life with bloody men." }
palm-sunday:
antiphon:
la: "Hosánna fílio David: † benedíctus, qui venit in nómine Dómini, Rex Israël: † Hosánna in excélsis."
en: "Hosanna to the Son of David! Blessed is He that cometh in the Name of the Lord. O King of Israel: Hosanna in the highest!"
versicle:
v: { la: "Ne perdas cum ímpiis, Deus, ánimam meam.", en: "Take not away my soul, O God, with the wicked." }
r: { la: "Et cum viris sánguinum vitam meam.", en: "Nor my life with bloody men." }
+37
View File
@@ -0,0 +1,37 @@
# Ezekiel 36 — verses 24-28 transcribed from a live Divinum Officium query
# (Monastic Tridentinum 1617, 2026-09-01, Lent Sundays' Matins Nocturn III
# canticle — see data/hours/matins-sunday-nocturn3-overrides.yml). Only
# this verse range is authored; the rest of the chapter is unauthored.
book: ezek
chapter: 36
verses:
- n: 24
text:
la: "Tollam quippe vos de géntibus, et congregábo vos de univérsis terris, * et addúcam vos in terram vestram."
en: "For I will take you from among the Gentiles, and will gather you together out of all the countries, and will bring you into your own land."
status: { la: verified, en: verified }
- n: 25
text:
la: "Et effúndam super vos aquam mundam, et mundabímini ab ómnibus inquinaméntis vestris, * et ab univérsis idólis vestris mundábo vos."
en: "And I will pour upon you clean water, and you shall be cleansed from all your filthiness, and I will cleanse you from all your idols."
status: { la: verified, en: verified }
- n: 26
text:
la: "Et dabo vobis cor novum, * et spíritum novum ponam in médio vestri:"
en: "And I will give you a new heart, and put a new spirit within you:"
status: { la: verified, en: verified }
- n: 26
text:
la: "Et áuferam cor lapídeum de carne vestra, * et dabo vobis cor cárneum."
en: "And I will take away the stony heart out of your flesh, and will give you a heart of flesh."
status: { la: verified, en: verified }
- n: 27
text:
la: "Et spíritum meum ponam in médio vestri: * et fáciam ut in præcéptis meis ambulétis, et judícia mea custodiátis et operémini."
en: "And I will put my spirit in the midst of you: and I will cause you to walk in my commandments, and to keep my judgments, and do them."
status: { la: verified, en: verified }
- n: 28
text:
la: "Et habitábitis in terra quam dedi pátribus vestris: * et éritis mihi in pópulum, et ego ero vobis in Deum."
en: "And you shall dwell in the land which I gave to your fathers, and you shall be my people, and I will be your God."
status: { la: verified, en: verified }
+57
View File
@@ -0,0 +1,57 @@
# Jeremiah 14 — verses 17-21 transcribed from a live Divinum Officium query
# (Monastic Tridentinum 1617, 2026-09-01, Lent Sundays' Matins Nocturn III
# canticle — see data/hours/matins-sunday-nocturn3-overrides.yml). Only
# this verse range is authored; the rest of the chapter is unauthored.
book: jer
chapter: 14
verses:
- n: 17
text:
la: "Dedúcant óculi mei lácrymam per noctem et diem, * et non táceant,"
en: "Let my eyes shed down tears night and day, and let them not cease,"
status: { la: verified, en: verified }
- n: 17
text:
la: "Quóniam contritióne magna contríta est virgo fília pópuli mei, * plaga péssima veheménter."
en: "Because the virgin daughter of my people is afflicted with a great affliction, with an exceeding grievous evil."
status: { la: verified, en: verified }
- n: 18
text:
la: "Si egréssus fúero ad agros, ecce occísi gládio: * et si introíero in civitátem, ecce attenuáti fame."
en: "If I go forth into the fields, behold the slain with the sword: and if I enter into the city, behold them that are consumed with famine."
status: { la: verified, en: verified }
- n: 18
text:
la: "Prophéta quoque et sacérdos abiérunt in terram * quam ignorábant."
en: "The prophet also and the priest are gone into a land which they knew not."
status: { la: verified, en: verified }
- n: 19
text:
la: "Numquid proíciens abiecísti Judam? * aut Sion abomináta est ánima tua?"
en: "Hast thou utterly cast away Juda, or hath thy soul abhorred Sion?"
status: { la: verified, en: verified }
- n: 19
text:
la: "Quare ergo percussísti nos, * ita ut nulla sit sánitas?"
en: "Why then hast thou struck us, so that there is no healing for us?"
status: { la: verified, en: verified }
- n: 19
text:
la: "Exspectávimus pacem, et non est bonum: * et tempus curatiónis, et ecce turbátio."
en: "We have looked for peace, and there is no good: and for the time of healing, and behold trouble."
status: { la: verified, en: verified }
- n: 20
text:
la: "Cognóvimus Dómine impietátes nostras, * iniquitátes patrum nostrórum, quia peccávimus tibi."
en: "We acknowledge, O Lord, our wickedness, the iniquities of our fathers, because we have sinned against thee."
status: { la: verified, en: verified }
- n: 21
text:
la: "Ne des nos in oppróbrium, propter nomen tuum, * neque fácias nobis contuméliam sólii glóriæ tuæ:"
en: "Give us not to be a reproach, for thy name's sake, and do not disgrace in us the throne of thy glory:"
status: { la: verified, en: verified }
- n: 21
text:
la: "Recordáre, * ne írritum fácias fœdus tuum nobíscum."
en: "Remember, break not thy covenant with us."
status: { la: verified, en: verified }
+73
View File
@@ -0,0 +1,73 @@
# Lamentations 5 — verses 1-7, 15-17, 19-21 transcribed from a live
# Divinum Officium query (Monastic Tridentinum 1617, 2026-09-01, Lent
# Sundays' Matins Nocturn III canticle — see
# data/hours/matins-sunday-nocturn3-overrides.yml). Only these verses are
# authored; the rest of the chapter is unauthored.
book: lam
chapter: 5
verses:
- n: 1
text:
la: "Recordáre Dómine quid accíderit nobis: * intuére et réspice oppróbrium nostrum."
en: "Remember, O Lord, what is come upon us: consider and behold our reproach."
status: { la: verified, en: verified }
- n: 2
text:
la: "Heréditas nostra versa est ad aliénos, * domus nostræ ad extráneos."
en: "Our inheritance is turned to aliens: our houses to strangers."
status: { la: verified, en: verified }
- n: 3
text:
la: "Pupílli facti sumus absque patre, * matres nostræ quasi víduæ."
en: "We are become orphans without a father: our mothers are as widows."
status: { la: verified, en: verified }
- n: 4
text:
la: "Aquam nostram pecúnia bíbimus: * ligna nostra prétio comparávimus."
en: "We have drunk our water for money: we have bought our wood."
status: { la: verified, en: verified }
- n: 5
text:
la: "Cervícibus nostris minabámur, * lassis non dabátur réquies."
en: "We were dragged by the necks, we were weary and no rest was given us."
status: { la: verified, en: verified }
- n: 6
text:
la: "Ægýpto dédimus manum et Assýriis, * ut saturarémur pane."
en: "We have given our hand to Egypt, and to the Assyrians, that we might be satisfied with bread."
status: { la: verified, en: verified }
- n: 7
text:
la: "Patres nostri peccavérunt, et non sunt: * et nos iniquitátes eórum portávimus."
en: "Our fathers have sinned, and are not: and we have borne their iniquities."
status: { la: verified, en: verified }
- n: 15
text:
la: "Defécit gáudium cordis nostri: * versus est in luctum chorus noster."
en: "The joy of our heart is ceased, our dancing is turned into mourning."
status: { la: verified, en: verified }
- n: 16
text:
la: "Cécidit coróna cápitis nostri: * væ nobis, quia peccávimus."
en: "The crown is fallen from our head: woe to us, because we have sinned."
status: { la: verified, en: verified }
- n: 17
text:
la: "Proptérea mœstum factum est cor nostrum: * ídeo contenebráti sunt óculi nostri."
en: "Therefore is our heart sorrowful, therefore are our eyes become dim."
status: { la: verified, en: verified }
- n: 19
text:
la: "Tu autem Dómine in ætérnum permanébis, * sólium tuum in generatiónem et generatiónem."
en: "But thou, O Lord, shalt remain for ever, thy throne from generation to generation."
status: { la: verified, en: verified }
- n: 20
text:
la: "Quare in perpétuum obliviscéris nostri, * derelínques nos in longitúdine diérum?"
en: "Why wilt thou forget us for ever? why wilt thou forsake us for a long time?"
status: { la: verified, en: verified }
- n: 21
text:
la: "Convérte nos Dómine ad te, et convertémur: * ínnova dies nostros, sicut a princípio."
en: "Convert us, O Lord, to thee, and we shall be converted: renew our days, as from the beginning."
status: { la: verified, en: verified }
+20 -1
View File
@@ -94,6 +94,7 @@ import { getNocturnReadings, type NocturnReading } from '../propers/nocturn-read
import { getOctaveReading } from '../propers/octave-readings'; import { getOctaveReading } from '../propers/octave-readings';
import { getMatinsSaintOverride, getMatinsCommonOverride, type MatinsPsalmodyScheme } from './matins-psalmody-overrides'; import { getMatinsSaintOverride, getMatinsCommonOverride, type MatinsPsalmodyScheme } from './matins-psalmody-overrides';
import matinsSundayAntiphonsData from '../data/hours/matins-sunday-antiphons.yml'; import matinsSundayAntiphonsData from '../data/hours/matins-sunday-antiphons.yml';
import matinsSundayNocturn3OverridesData from '../data/hours/matins-sunday-nocturn3-overrides.yml';
import matinsFerialAntiphonsData from '../data/hours/matins-ferial-antiphons.yml'; import matinsFerialAntiphonsData from '../data/hours/matins-ferial-antiphons.yml';
import type { Weekday } from '../calendar/types'; import type { Weekday } from '../calendar/types';
@@ -126,6 +127,24 @@ interface MatinsSundayAntiphons {
} }
const sundayAntiphons = matinsSundayAntiphonsData as unknown as MatinsSundayAntiphons; const sundayAntiphons = matinsSundayAntiphonsData as unknown as MatinsSundayAntiphons;
// Septuagesima through Palm Sunday inclusive: Nocturn III takes a
// per-Sunday antiphon (and, for some of these Sundays, its own canticles/
// versicle too) instead of the plain-season "Allelúja" default — see
// data/hours/matins-sunday-nocturn3-overrides.yml's own header.
type SundayNocturn3Overrides = Record<string, Partial<SundayNocturn>>;
const sundayNocturn3Overrides = matinsSundayNocturn3OverridesData as unknown as SundayNocturn3Overrides;
function sundayNocturn3For(temporalId: string | undefined): SundayNocturn {
const base = sundayAntiphons.nocturn3;
const override = temporalId ? sundayNocturn3Overrides[temporalId] : undefined;
if (!override) return base;
return {
canticles: override.canticles ?? base.canticles,
antiphon: override.antiphon ?? base.antiphon,
versicle: override.versicle ?? base.versicle,
};
}
interface FerialGroup { interface FerialGroup {
psalms: PsalmRef[]; psalms: PsalmRef[];
antiphon: BilingualText; antiphon: BilingualText;
@@ -691,7 +710,7 @@ export function resolveOrdo(date: string): ResolvedOrdo {
parts.push(...sundayPsalmNocturn(sundayAntiphons.nocturn2, day)); parts.push(...sundayPsalmNocturn(sundayAntiphons.nocturn2, day));
parts.push(...(nocturn2Readings ?? [])); parts.push(...(nocturn2Readings ?? []));
parts.push({ kind: 'section-heading', label: `Nocturn ${NOCTURN_NUMERAL[2]}` }); parts.push({ kind: 'section-heading', label: `Nocturn ${NOCTURN_NUMERAL[2]}` });
parts.push(...sundayCanticleNocturn(sundayAntiphons.nocturn3, day)); parts.push(...sundayCanticleNocturn(sundayNocturn3For(temporalId), day));
parts.push(...(nocturn3Readings ?? [])); parts.push(...(nocturn3Readings ?? []));
parts.push({ kind: 'te-deum', text: resolveCommon('te-deum') }); parts.push({ kind: 'te-deum', text: resolveCommon('te-deum') });
parts.push({ kind: 'versicle', text: resolveCommon('te-decet-laus') }); parts.push({ kind: 'versicle', text: resolveCommon('te-decet-laus') });
+49
View File
@@ -188,6 +188,55 @@ describe('resolveOrdo("matins", ...) Sunday (3-nocturn) branch', () => {
}); });
}); });
// Real bug (found 2026-09-01, see TODO.md/vu_ferial_matins_antiphons.md):
// Sunday Matins Nocturn III's antiphon was hardcoded to "Allelúja, allelúja,
// allelúja" year-round; Septuagesima through Palm Sunday inclusive each
// have their own Gospel-tied antiphon instead, live-verified against the
// reference engine (Monastic Tridentinum 1617, 2026 calendar) — and for
// Lent 1-4 the canticles and versicle differ too, not just the antiphon.
describe('resolveOrdo("matins", ...) Sunday Nocturn III seasonal override (2026-09-01 fix)', () => {
it('Septuagesima: proper antiphon, base canticles/versicle unchanged', () => {
const ordo = resolveOrdo('matins', '2026-02-01');
const canticles = ordo.parts.filter((p) => p.kind === 'canticle') as { canticleId: string }[];
expect(canticles.map((c) => c.canticleId)).toEqual(['isa-33-2-10', 'isa-33-13-18', 'sir-36-14-19']);
const antiphons = ordo.parts.filter((p) => p.kind === 'antiphon') as { text: { text: Record<string, string> } }[];
expect(antiphons.at(-1)?.text.text.la).toBe('Ant. Ite et vos in víneam meam, † et quod justum fúerit, dabo vobis.');
const versicles = ordo.parts.filter((p) => p.kind === 'versicle') as { text: { text: Record<string, string> } }[];
expect(versicles.at(-2)?.text.text.la).toBe('V. Exaltáre Dómine in virtúte tua.\nR. Cantábimus et psallémus virtútes tuas.');
});
it('Lent 1: proper antiphon, proper canticles (Jer 14/Lam 5/Ezek 36), proper versicle', () => {
const ordo = resolveOrdo('matins', '2026-02-22');
const canticles = ordo.parts.filter((p) => p.kind === 'canticle') as { canticleId: string; text: { text: Record<string, string> } }[];
expect(canticles.map((c) => c.canticleId)).toEqual([
'jer-14-17-21',
'lam-5-1-7_lam-5-15-17_lam-5-19-21',
'ezek-36-24-28',
]);
expect(canticles.every((c) => c.text.text.la && c.text.text.en)).toBe(true);
const antiphons = ordo.parts.filter((p) => p.kind === 'antiphon') as { text: { text: Record<string, string> } }[];
expect(antiphons.at(-1)?.text.text.la).toBe('Ant. Non in solo pane vivit homo, † sed in omni verbo quod procédit de ore Dei.');
const versicles = ordo.parts.filter((p) => p.kind === 'versicle') as { text: { text: Record<string, string> } }[];
expect(versicles.at(-2)?.text.text.la).toBe('V. Scuto circúmdabit te véritas ejus.\nR. Non timébis a timóre noctúrno.');
});
it('Passion Sunday and Palm Sunday: base canticles, but the "Ne perdas" versicle, not the base "Exaltáre"', () => {
for (const date of ['2026-03-22', '2026-03-29']) {
const ordo = resolveOrdo('matins', date);
const canticles = ordo.parts.filter((p) => p.kind === 'canticle') as { canticleId: string }[];
expect(canticles.map((c) => c.canticleId)).toEqual(['isa-33-2-10', 'isa-33-13-18', 'sir-36-14-19']);
const versicles = ordo.parts.filter((p) => p.kind === 'versicle') as { text: { text: Record<string, string> } }[];
expect(versicles.at(-2)?.text.text.la).toBe('V. Ne perdas cum ímpiis, Deus, ánimam meam.\nR. Et cum viris sánguinum vitam meam.');
}
const passionOrdo = resolveOrdo('matins', '2026-03-22');
const palmOrdo = resolveOrdo('matins', '2026-03-29');
const antiphonLa = (ordo: ReturnType<typeof resolveOrdo>) =>
(ordo.parts.filter((p) => p.kind === 'antiphon').at(-1) as { text: { text: Record<string, string> } }).text.text.la;
expect(antiphonLa(passionOrdo)).toBe('Ant. Ego glóriam meam non quæro: † est qui quærat, et júdicet.');
expect(antiphonLa(palmOrdo)).toBe('Ant. Hosánna fílio David: † benedíctus, qui venit in nómine Dómini, Rex Israël: † Hosánna in excélsis.');
});
});
describe('resolveOrdo("matins", ...) Duplex+ weekday-feast (3-nocturn, non-Sunday) branch', () => { describe('resolveOrdo("matins", ...) Duplex+ weekday-feast (3-nocturn, non-Sunday) branch', () => {
// 2026-08-17 -- St. Lawrence's own octave closing day ("In Octava S. // 2026-08-17 -- St. Lawrence's own octave closing day ("In Octava S.
// Laurentii Martyris ~ Duplex"), live-verified directly against the // Laurentii Martyris ~ Duplex"), live-verified directly against the