From 42dd4dc04b4d7b786c8f309f5aa2454d3008fc53 Mon Sep 17 00:00:00 2001 From: Will Estes Date: Sun, 23 Aug 2026 09:03:04 -0400 Subject: [PATCH] Add Te decet laus to Matins, drop Ps 129/50 from ferial Preces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Matins now sings "Te decet laus" (Psalterium/Common/Prayers.txt's [Te decet]) immediately after the Te Deum on every 3-nocturn day, matching Rule of Benedict ch. 11's close of Vigils. New common text te-decet-laus.yml, wired into both matins.ts branches that render a Te Deum; absent on ferial days along with it. Also drops Psalm 129 (De profundis) from the Lauds ferial Preces and Psalm 50 (Miserere) from the Vespers ferial Preces, per direct instruction — both were embedded inline in the live-transcribed text. Updated the affected fixture tests to key off a versicle that survives the removal instead of the now-absent psalm text. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01EWzE3kaSZCs79fPBrrE1aF --- TODO.md | 36 +++++++------ .../propers/common/lauds-preces-feriales.yml | 28 ++-------- src/data/propers/common/te-decet-laus.yml | 17 ++++++ .../common/vespers-preces-feriales.yml | 53 ++----------------- src/hours/matins.ts | 2 + tests/hours/lauds.test.ts | 12 +++-- tests/hours/matins.test.ts | 10 +++- tests/hours/vespers.test.ts | 10 ++-- 8 files changed, 70 insertions(+), 98 deletions(-) create mode 100644 src/data/propers/common/te-decet-laus.yml diff --git a/TODO.md b/TODO.md index dd03b53..eb52923 100644 --- a/TODO.md +++ b/TODO.md @@ -32,25 +32,27 @@ across every hour and content type in this app. 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 begins. - - Matins: "Te decet laus" (the short versicle sung right after the closing Gospel+"Amen", - opening Lauds — Rule of Benedict ch. 13, `data/regula/02-13.yml`) isn't modeled or - displayed anywhere. `post-pentecost-15.yml`'s own header comment already flagged this as - "a known simplification"; the user asked (2026-08-23) for it to actually be shown. Needs a - text/part decided (own `ResolvedPart` kind vs. reusing an existing one like `versicle`) - and where in the Matins ordo it renders (after the Gospel-reading-again-in-full that - currently closes Nocturn 3, before/instead of the existing Benedicamus/Fidelium close — - see memory `vu-matins-closing`). Not started. + - ~~Matins: "Te decet laus"~~ — done (2026-08-23). Added as a new common text + (`data/propers/common/te-decet-laus.yml`, sourced from `Psalterium/Common/Prayers.txt`'s + `[Te decet]` block) rendered as a `versicle` part immediately after the Te Deum on every + 3-nocturn day (Sunday or Duplex+), in both `hours/matins.ts` branches that push a Te Deum. + Not modeled: the historical Gospel-read-again-in-full step that live-precedes it (a + pre-existing, separate known simplification — see + `data/propers/nocturn-readings/post-pentecost-15.yml`'s own header comment). Absent on + ferial (1-nocturn) days, same gating as the Te Deum itself. `tests/hours/matins.test.ts` + covers both the Sunday branch's presence/content and the ferial branch's absence. - ~~Matins nocturn-readings responsory formatting~~ — done (2026-08-23), see "Nocturn- readings responsory marker fix" below. - - Lauds/Vespers ferial Preces (`data/propers/common/lauds-preces-feriales.yml`, - `vespers-preces-feriales.yml`) each embed a full psalm inline within the Preces text - itself — Psalm 129 (De profundis) at Lauds, Psalm 50 (Miserere) at Vespers, per the - reference engine's own `[Preces feriales Vespera] @:Preces feriales Laudes:s/129/50/` - substitution (see each file's own header comment). User asked (2026-08-23) to remove - both psalms from the Preces. Not started — needs deciding whether to just delete the - psalm block (and its own trailing Gloria Patri line) from each file's `text.la`/`text.en` - wholesale, or whether the surrounding V/R litany needs any adjustment once the psalm's - gone. + - ~~Lauds/Vespers ferial Preces psalms~~ — done (2026-08-23). Psalm 129 (De profundis) and + its own trailing Gloria Patri deleted wholesale from + `data/propers/common/lauds-preces-feriales.yml`'s `text.la`/`text.en`; Psalm 50 (Miserere) + and its own Gloria Patri likewise from `vespers-preces-feriales.yml`. Surrounding V/R + litany needed no adjustment — the psalm block sat between two already-independent V/R + sequences. Both files' header comments updated to flag the drop as deliberate, not a + transcription gap. `tests/hours/lauds.test.ts` and `vespers.test.ts` updated to + distinguish the fuller ferial Preces from the short Sunday/feast litany via a versicle + that survives the psalm's removal ("Ego dixi: Dómine, miserére mei") instead of the now- + absent psalm text. 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 diff --git a/src/data/propers/common/lauds-preces-feriales.yml b/src/data/propers/common/lauds-preces-feriales.yml index a425fb2..624f724 100644 --- a/src/data/propers/common/lauds-preces-feriales.yml +++ b/src/data/propers/common/lauds-preces-feriales.yml @@ -15,6 +15,10 @@ # conditional markup. The King/nation versicle pair, by contrast, *is* # rendered live despite the template's own rubric note suggesting # otherwise -- same live-over-template discipline. +# +# Psalm 129 (De profundis), which the live render embeds between the first +# and second V/R blocks with its own Gloria Patri, is deliberately dropped +# here per direct instruction (2026-08) -- not a transcription gap. id: lauds-preces-feriales text: la: | @@ -51,18 +55,6 @@ text: V. Dómine, exáudi oratiónem meam. R. Et clamor meus ad te véniat. - Psalmus 129. - De profúndis clamávi ad te, Dómine: Dómine, exáudi vocem meam. - Fiant aures tuæ intendéntes, in vocem deprecatiónis meæ. - Si iniquitátes observáveris, Dómine: Dómine, quis sustinébit? - Quia apud te propitiátio est: et propter legem tuam sustínui te, Dómine. - Sustínuit ánima mea in verbo ejus: sperávit ánima mea in Dómino. - A custódia matutína usque ad noctem: speret Israël in Dómino. - Quia apud Dóminum misericórdia: et copiósa apud eum redémptio. - Et ipse rédimet Israël, ex ómnibus iniquitátibus ejus. - V. Glória Patri, et Fílio, et Spirítui Sancto. - R. Sicut erat in princípio, et nunc, et semper, et in sǽcula sæculórum. Amen. - V. Dómine, Deus virtútum, convérte nos. R. Et osténde fáciem tuam, et salvi érimus. V. Exsúrge, Christe, ádjuva nos. @@ -103,18 +95,6 @@ text: V. O Lord, hear my prayer. R. And let my cry come unto thee. - Psalm 129. - Out of the depths I have cried to thee, O Lord: Lord, hear my voice. - Let thy ears be attentive to the voice of my supplication. - If thou, O Lord, wilt mark iniquities: Lord, who shall stand it. - For with thee there is merciful forgiveness: and by reason of thy law, I have waited for thee, O Lord. - My soul hath relied on his word: my soul hath hoped in the Lord. - From the morning watch even until night, let Israel hope in the Lord. - Because with the Lord there is mercy: and with him plentiful redemption. - And he shall redeem Israel from all his iniquities. - V. Glory be to the Father, and to the Son, and to the Holy Ghost. - R. As it was in the beginning, is now, and ever shall be, world without end. Amen. - V. Turn us again, O Lord, God of Hosts. R. Show us thy face, and we shall be whole. V. Arise, O Christ, and help us. diff --git a/src/data/propers/common/te-decet-laus.yml b/src/data/propers/common/te-decet-laus.yml new file mode 100644 index 0000000..36bed11 --- /dev/null +++ b/src/data/propers/common/te-decet-laus.yml @@ -0,0 +1,17 @@ +# Sourced from Psalterium/Common/Prayers.txt's [Te decet] block. Rule of +# Benedict ch. 11: after the abbot reads the closing Gospel following the +# Te Deum, all reply Amen, then the abbot begins this versicle, closing +# Vigils — sung only on a 3-nocturn day (Sunday or Duplex+), right after +# the Te Deum, same gating as `te-deum` itself. This app doesn't model the +# Gospel-read-again-in-full step that precedes it live (a known +# simplification, see data/propers/nocturn-readings/post-pentecost-15.yml's +# own header comment) — this versicle is added directly after the Te Deum. +id: te-decet-laus +text: + la: | + V. Te decet laus, te decet hymnus: tibi glória Deo Patri, et Fílio, cum Spíritu Sancto in sǽcula sæculórum. Amen. + en: | + V. Thou art worthy of praise, thou art worthy of hymns, to Thee be glory: to God the Father and the Son with the Holy Ghost, world without end. Amen. +status: + la: verified + en: verified diff --git a/src/data/propers/common/vespers-preces-feriales.yml b/src/data/propers/common/vespers-preces-feriales.yml index adbc642..477bad5 100644 --- a/src/data/propers/common/vespers-preces-feriales.yml +++ b/src/data/propers/common/vespers-preces-feriales.yml @@ -15,6 +15,11 @@ # `textRef: lauds-conclusio` on a ferial or vigil day -- see hours/ # vespers.ts's 'vespers-preces' case for the exact predicate, mirroring # hours/lauds.ts's 'lauds-preces' case exactly. +# +# Psalm 50 (Miserere), which the live render embeds between the first and +# second V/R blocks with its own Gloria Patri, is deliberately dropped here +# per direct instruction (2026-08) -- not a transcription gap, same as +# lauds-preces-feriales.yml's own Psalm 129 drop. id: vespers-preces-feriales text: la: | @@ -51,30 +56,6 @@ text: V. Dómine, exáudi oratiónem meam. R. Et clamor meus ad te véniat. - Psalmus 50. - Miserére mei, Deus, secúndum magnam misericórdiam tuam. - Et secúndum multitúdinem miseratiónum tuárum, dele iniquitátem meam. - Ámplius lava me ab iniquitáte mea: et a peccáto meo munda me. - Quóniam iniquitátem meam ego cognósco: et peccátum meum contra me est semper. - Tibi soli peccávi, et malum coram te feci: ut justificéris in sermónibus tuis, et vincas cum judicáris. - Ecce enim, in iniquitátibus concéptus sum: et in peccátis concépit me mater mea. - Ecce enim, veritátem dilexísti: incérta et occúlta sapiéntiæ tuæ manifestásti mihi. - Aspérges me hyssópo, et mundábor: lavábis me, et super nivem dealbábor. - Audítui meo dabis gáudium et lætítiam: et exsultábunt ossa humiliáta. - Avérte fáciem tuam a peccátis meis: et omnes iniquitátes meas dele. - Cor mundum crea in me, Deus: et spíritum rectum ínnova in viscéribus meis. - Ne proícias me a fácie tua: et spíritum sanctum tuum ne áuferas a me. - Redde mihi lætítiam salutáris tui: et spíritu principáli confírma me. - Docébo iníquos vias tuas: et ímpii ad te converténtur. - Líbera me de sanguínibus, Deus, Deus salútis meæ: et exsultábit lingua mea justítiam tuam. - Dómine, lábia mea apéries: et os meum annuntiábit laudem tuam. - Quóniam si voluísses sacrifícium, dedíssem útique: holocáustis non delectáberis. - Sacrifícium Deo spíritus contribulátus: cor contrítum, et humiliátum, Deus, non despícies. - Benígne fac, Dómine, in bona voluntáte tua Sion: ut ædificéntur muri Jerúsalem. - Tunc acceptábis sacrifícium justítiæ, oblatiónes, et holocáusta: tunc impónent super altáre tuum vítulos. - V. Glória Patri, et Fílio, et Spirítui Sancto. - R. Sicut erat in princípio, et nunc, et semper, et in sǽcula sæculórum. Amen. - V. Dómine, Deus virtútum, convérte nos. R. Et osténde fáciem tuam, et salvi érimus. V. Exsúrge, Christe, ádjuva nos. @@ -115,30 +96,6 @@ text: V. O Lord, hear my prayer. R. And let my cry come unto thee. - Psalm 50. - Have mercy on me, O God, according to thy great mercy. - And according to the multitude of thy tender mercies blot out my iniquity. - Wash me yet more from my iniquity, and cleanse me from my sin. - For I know my iniquity, and my sin is always before me. - To thee only have I sinned, and have done evil before thee: that thou mayst be justified in thy words, and mayst overcome when thou art judged. - For behold I was conceived in iniquities; and in sins did my mother conceive me. - For behold thou hast loved truth: the uncertain and hidden things of thy wisdom thou hast made manifest to me. - Thou shalt sprinkle me with hyssop, and I shall be cleansed: thou shalt wash me, and I shall be made whiter than snow. - To my hearing thou shalt give joy and gladness: and the bones that have been humbled shall rejoice. - Turn away thy face from my sins, and blot out all my iniquities. - Create a clean heart in me, O God: and renew a right spirit within my bowels. - Cast me not away from thy face; and take not thy holy spirit from me. - Restore unto me the joy of thy salvation, and strengthen me with a perfect spirit. - I will teach the unjust thy ways: and the wicked shall be converted to thee. - Deliver me from blood, O God, thou God of my salvation: and my tongue shall extol thy justice. - O Lord, thou wilt open my lips: and my mouth shall declare thy praise. - For if thou hadst desired sacrifice, I would indeed have given it: with burnt offerings thou wilt not be delighted. - A sacrifice to God is an afflicted spirit: a contrite and humbled heart, O God, thou wilt not despise. - Deal favourably, O Lord, in thy good will with Sion; that the walls of Jerusalem may be built up. - Then shalt thou accept the sacrifice of justice, oblations and whole burnt offerings: then shall they lay calves upon thy altar. - V. Glory be to the Father, and to the Son, and to the Holy Ghost. - R. As it was in the beginning, is now, and ever shall be, world without end. Amen. - V. Turn us again, O Lord, God of Hosts. R. Show us thy face, and we shall be whole. V. Arise, O Christ, and help us. diff --git a/src/hours/matins.ts b/src/hours/matins.ts index 6ea2f19..8cecbcc 100644 --- a/src/hours/matins.ts +++ b/src/hours/matins.ts @@ -491,6 +491,7 @@ export function resolveOrdo(date: string): ResolvedOrdo { parts.push(...sundayCanticleNocturn(sundayAntiphons.nocturn3, day)); parts.push(...(nocturn3Readings ?? [])); parts.push({ kind: 'te-deum', text: resolveCommon('te-deum') }); + parts.push({ kind: 'versicle', text: resolveCommon('te-decet-laus') }); } else if (threeNocturns) { // A Duplex+ weekday feast — genuinely different psalmody from a real // Sunday's, not the same content reused. getMatinsPsalmodyOverride @@ -523,6 +524,7 @@ export function resolveOrdo(date: string): ResolvedOrdo { parts.push(...(nocturn3Readings ?? [])); } parts.push({ kind: 'te-deum', text: resolveCommon('te-deum') }); + parts.push({ kind: 'versicle', text: resolveCommon('te-decet-laus') }); } else { parts.push(...ferialAntiphonedNocturn(day)); parts.push(...(nocturn1Readings ?? [])); diff --git a/tests/hours/lauds.test.ts b/tests/hours/lauds.test.ts index b955dfe..c113849 100644 --- a/tests/hours/lauds.test.ts +++ b/tests/hours/lauds.test.ts @@ -166,11 +166,15 @@ describe('resolveOrdo("lauds", ...)', () => { expect(collectIndex).toBeGreaterThan(litanyIndex); }); - it('uses the fuller Tridentine 1906/1910 ferial Preces (with Psalm 129) on a plain ferial day, not the short Sunday/feast litany', () => { + it('uses the fuller Tridentine 1906/1910 ferial Preces on a plain ferial day, not the short Sunday/feast litany', () => { + // Psalm 129 (De profundis), present in the live render, is deliberately + // dropped from this app's own text (2026-08) — 'Ego dixi' is the next + // versicle after where the psalm used to sit, still unique to the + // fuller ferial form vs. the short litany. const ordo = resolveOrdo('lauds', FERIAL_TUESDAY); const preces = ordo.parts.find((p) => p.kind === 'preces' && p.text.text.la?.includes('Kýrie, eléison')); - expect(preces?.kind === 'preces' ? preces.text.text.la : undefined).toContain('De profúndis clamávi'); - expect(preces?.kind === 'preces' ? preces.text.text.en : undefined).toContain('Out of the depths'); + expect(preces?.kind === 'preces' ? preces.text.text.la : undefined).toContain('Ego dixi: Dómine, miserére mei'); + expect(preces?.kind === 'preces' ? preces.text.text.en : undefined).toContain('I said: Lord, be merciful'); }); it('uses the short Sunday/feast litany, without Psalm 129, on a Sunday and on a real saint\'s own winning day', () => { @@ -186,7 +190,7 @@ describe('resolveOrdo("lauds", ...)', () => { it('still uses the fuller ferial Preces on a Vigil, even though a Vigil is a sanctoral winner (unlike isSundayOrFeast elsewhere)', () => { const ordo = resolveOrdo('lauds', '2028-08-09'); // Vigil of St. Lawrence wins outright this year (not a Sunday) const preces = ordo.parts.find((p) => p.kind === 'preces' && p.text.text.la?.includes('Kýrie, eléison')); - expect(preces?.kind === 'preces' ? preces.text.text.la : undefined).toContain('Psalmus 129'); + expect(preces?.kind === 'preces' ? preces.text.text.la : undefined).toContain('Ego dixi: Dómine, miserére mei'); }); it('resolves just the winners own collect on a plain day with nothing commemorated', () => { diff --git a/tests/hours/matins.test.ts b/tests/hours/matins.test.ts index eda8f83..55fdb48 100644 --- a/tests/hours/matins.test.ts +++ b/tests/hours/matins.test.ts @@ -41,6 +41,9 @@ describe('resolveOrdo("matins", ...) ferial (1-nocturn) branch', () => { expect(psalms).toEqual([3, 94, 43, 44, 47, 48, 49, 54, 55, 58, 59]); expect(ordo.parts.some((p) => p.kind === 'te-deum')).toBe(false); expect(ordo.parts.some((p) => p.kind === 'canticle')).toBe(false); + // "Te decet laus" is sung only right after the Te Deum on a 3-nocturn + // day (2026-08) — absent here along with the Te Deum itself. + expect(ordo.parts.some((p) => p.kind === 'versicle' && p.text.text.la?.includes('Te decet laus'))).toBe(false); }); it("gives every nocturn psalm its own antiphon and includes Tuesday's own versicle (data/hours/matins-ferial-antiphons.yml)", () => { @@ -125,12 +128,15 @@ describe('resolveOrdo("matins", ...) Sunday (3-nocturn) branch', () => { it('renders each nocturn versicle with both its V. and R. lines (real bug: sundayPsalmNocturn/sundayCanticleNocturn used to drop the R. line entirely)', () => { const versicles = ordo.parts.filter((p) => p.kind === 'versicle') as { text: { text: Record } }[]; - // Opening Deus-in-adjutorium versicle + one per nocturn (1, 2, 3). - expect(versicles).toHaveLength(4); + // Opening Deus-in-adjutorium versicle + one per nocturn (1, 2, 3) + + // the closing "Te decet laus" versicle after the Te Deum (2026-08). + expect(versicles).toHaveLength(5); const [, nocturn1, nocturn2, nocturn3] = versicles; expect(nocturn1?.text.text.la).toBe('V. Memor fui nocte nóminis tui Dómine.\nR. Et custodívi legem tuam.'); expect(nocturn2?.text.text.la).toBe('V. Média nocte surgébam ad confiténdum tibi.\nR. Super justítia justificatiónis tuæ.'); expect(nocturn3?.text.text.la).toBe('V. Exaltáre Dómine in virtúte tua.\nR. Cantábimus et psallémus virtútes tuas.'); + // The 5th versicle is "Te decet laus", right after the Te Deum (2026-08). + expect(versicles[4]?.text.text.la).toContain('Te decet laus'); }); it("resolves the user's own bible-plan reading (Tobit/Sirach) for Nocturn 1", () => { diff --git a/tests/hours/vespers.test.ts b/tests/hours/vespers.test.ts index cd50a64..869aa36 100644 --- a/tests/hours/vespers.test.ts +++ b/tests/hours/vespers.test.ts @@ -229,11 +229,15 @@ describe('resolveOrdo("vespers", ...)', () => { expect(collectIndex).toBeGreaterThan(litanyIndex); }); - it('uses the fuller Tridentine 1906/1910 ferial Preces (with Psalm 50) on a plain ferial day, not the short Sunday/feast litany', () => { + it('uses the fuller Tridentine 1906/1910 ferial Preces on a plain ferial day, not the short Sunday/feast litany', () => { + // Psalm 50 (Miserere), present in the live render, is deliberately + // dropped from this app's own text (2026-08) — 'Ego dixi' is the next + // versicle after where the psalm used to sit, still unique to the + // fuller ferial form vs. the short litany. const ordo = resolveOrdo('vespers', '2026-06-16'); const preces = ordo.parts.find((p) => p.kind === 'preces' && p.text.text.la?.includes('Kýrie, eléison')); - expect(preces?.kind === 'preces' ? preces.text.text.la : undefined).toContain('Miserére mei, Deus'); - expect(preces?.kind === 'preces' ? preces.text.text.en : undefined).toContain('Have mercy on me, O God'); + expect(preces?.kind === 'preces' ? preces.text.text.la : undefined).toContain('Ego dixi: Dómine, miserére mei'); + expect(preces?.kind === 'preces' ? preces.text.text.en : undefined).toContain('I said: Lord, be merciful'); }); it("uses the short Sunday/feast litany, without Psalm 50, on a Sunday and on a real saint's own winning day", () => {