Fix Vespers ferial psalmody weekday bug, add duplex-majus+ Common psalmody override
Deploy / deploy (push) Successful in 1m33s
Deploy / deploy (push) Successful in 1m33s
Vespers psalms on Mon-Sat were silently following the anticipated First-Vespers day's weekday instead of the actual calendar date's, since resolvePsalmody reused resolveEveningDay's result (correct for propers/office, which legitimately borrow tomorrow's identity, but wrong for the ferial psalm cycle, which has no per-feast override of its own). Also adds a duplex-majus+ Common-category psalmody override to both Lauds and Vespers, mirroring Matins' existing saint/category mechanism. Lauds already had a per-feast-only override; Vespers had none. 8 of 24 Common categories authored for both hours, live-verified against the reference engine. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHXJAWVAabCKX1JgmDh4Rn
This commit is contained in:
@@ -315,6 +315,52 @@ describe('resolveOrdo("lauds", ...)', () => {
|
||||
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('O gloriósa Dómina');
|
||||
});
|
||||
|
||||
it("uses Common-of-the-BVM's own psalmody (92, 99, 62 antiphons + Trium Puerorum) on Our Lady of Mount Carmel's own day, not the ferial default", () => {
|
||||
// Live-verified against Divinum Officium (Monastic Tridentinum 1617,
|
||||
// votive=C11), 2026-08-28 -- see data/hours/lauds-psalmody-overrides/
|
||||
// categories/common-of-the-bvm.yml's own header. Same date as the
|
||||
// office-bundle test just above; she has no per-feast Lauds psalmody
|
||||
// override authored, so this is her Common's own tier.
|
||||
const ordo = resolveOrdo('lauds', '2026-07-16');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([66, 92, 99, 62, 148, 149, 150]);
|
||||
const canticle = ordo.parts.find((p) => p.kind === 'canticle' && p.canticleId === 'canticum-trium-puerorum');
|
||||
expect(canticle).toBeDefined();
|
||||
});
|
||||
|
||||
it("uses Common-of-an-Abbot's own psalmody on St. Benedict's own day, not the ferial default (his proper chapter/hymn is separate, see below)", () => {
|
||||
// St. Benedict (duplex-1-classis, common-of-an-abbot) has his own
|
||||
// proper chapter/hymn (see the test below) but no per-feast Lauds
|
||||
// psalmody override, so his psalm numbers/antiphons fall to his
|
||||
// Common's own tier -- live-verified against Divinum Officium
|
||||
// (Monastic Tridentinum 1617, votive=C5), 2026-08-28. Same clean year
|
||||
// as the proper-chapter test below.
|
||||
const ordo = resolveOrdo('lauds', '2033-03-21');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([66, 92, 99, 62, 148, 149, 150]);
|
||||
});
|
||||
|
||||
it("uses Common-of-Several-Martyrs' own psalmody on Ss. Placid and Companions' own day, not the ferial default", () => {
|
||||
// 2026-10-05, Duplex II. classis, common-of-several-martyrs, no
|
||||
// per-feast Lauds psalmody override authored -- same date as
|
||||
// vespers.test.ts's own office-bundle proof for this Common.
|
||||
// Live-verified against Divinum Officium (Monastic Tridentinum 1617,
|
||||
// votive=C3), 2026-08-28.
|
||||
const ordo = resolveOrdo('lauds', '2026-10-05');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([66, 92, 99, 62, 148, 149, 150]);
|
||||
});
|
||||
|
||||
it('stays on the plain ferial default for a below-threshold feast (Duplex), even with a Common that has its own category psalmody', () => {
|
||||
// St. Jerome (Sept 30, plain Duplex, common-of-a-confessor-not-bishop)
|
||||
// is below the duplex-majus+ override threshold -- confirmed live
|
||||
// against Divinum Officium, which keeps this date on the plain
|
||||
// ferial Wednesday set, not his Common's own psalmody.
|
||||
const ordo = resolveOrdo('lauds', '2026-09-30');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([66, 50, 63, 64, 148, 149, 150]);
|
||||
});
|
||||
|
||||
it("resolves St. Gregory the Great's own proper chapter/hymn, not a Common fallback", () => {
|
||||
// Live-verified against Divinum Officium (Monastic Tridentinum
|
||||
// 1617), 2033-03-12 (clean year). See lauds-capitulum-st-gregory-
|
||||
|
||||
@@ -75,8 +75,14 @@ describe('resolveOrdo("vespers", ...)', () => {
|
||||
});
|
||||
|
||||
it('joins Ps 115 and 116 under one shared antiphon on Monday', () => {
|
||||
// 2026-08-17 is a Monday, in ordinary time.
|
||||
const ordo = resolveOrdo('vespers', '2026-08-17');
|
||||
// Not 2026-08-17: that Monday is actually the closing day of St.
|
||||
// Lawrence's own octave (Aug 10-17, Duplex II. classis) -- now that
|
||||
// Vespers has its own duplex-majus+ psalmody override (2026-08-28),
|
||||
// that evening correctly gets Common-of-a-Martyr's own psalms
|
||||
// instead of the plain ferial Monday set, same as Lauds' own
|
||||
// octave-day override already did. 2026-09-28 is a Monday clear of
|
||||
// any such collision.
|
||||
const ordo = resolveOrdo('vespers', '2026-09-28');
|
||||
const psalmParts = ordo.parts.filter((p) => p.kind === 'psalm');
|
||||
const numbers = psalmParts.map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([113, 114, 115, 116, 128]);
|
||||
@@ -107,6 +113,58 @@ describe('resolveOrdo("vespers", ...)', () => {
|
||||
expect(secondHalf?.kind === 'psalm' ? secondHalf.antiphon : undefined).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses Common-of-an-Apostle's own psalmody on St. Bartholomew's own day (Duplex II. classis, keeps own Vespers)", () => {
|
||||
// Live-verified against Divinum Officium (Monastic Tridentinum 1617,
|
||||
// votive=C1), 2026-08-28 -- see data/hours/vespers-psalmody-overrides/
|
||||
// categories/common-of-an-apostle.yml's own header. St. Bartholomew
|
||||
// has no proper Vespers psalmody of his own authored yet, so this is
|
||||
// his Common's own tier, not a per-feast override.
|
||||
const ordo = resolveOrdo('vespers', '2026-08-24');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([109, 112, 115, 138]);
|
||||
});
|
||||
|
||||
it("uses Common-of-a-Martyr's own psalmody on St. Lawrence's octave closing day, not the plain ferial Monday default", () => {
|
||||
// 2026-08-17 is the closing day of St. Lawrence's own octave (Aug
|
||||
// 10-17, Duplex II. classis) -- resolveOfficeWinner substitutes his
|
||||
// own identity there, so the new duplex-majus+ psalmody override
|
||||
// (getOfficeOverrideId) picks up his Common (common-of-a-martyr) even
|
||||
// though no per-feast Vespers override exists for him. See the
|
||||
// "joins Ps 115 and 116" test above for why this date was moved off
|
||||
// the plain-ferial-Monday proof.
|
||||
const ordo = resolveOrdo('vespers', '2026-08-17');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([109, 111, 112, 115]);
|
||||
});
|
||||
|
||||
it("uses Common-of-a-Confessor-Bishop's own psalmody for St. Martin of Tours's First Vespers", () => {
|
||||
// Not 2026-11-11 (his own day itself): St. Emilian (Nov 12, plain
|
||||
// Duplex, below this override's duplex-majus+ threshold) claims First
|
||||
// Vespers over Martin's own evening (Martin, Duplex Majus, falls
|
||||
// short of keepsOwnSecondVespers's duplex-2-classis+ floor) -- so
|
||||
// Nov 11's own evening resolves to Emilian's (below-threshold, plain
|
||||
// ferial) identity instead, not Martin's. 2026-11-10 anticipates
|
||||
// Martin's own First Vespers instead (Ss. Tryphon and Companions,
|
||||
// Nov 10, plain Simplex, doesn't keep its own evening either), so
|
||||
// that evening's governing day is Martin's, duplex-majus, eligible.
|
||||
// Live-verified against Divinum Officium (Monastic Tridentinum 1617,
|
||||
// votive=C4), 2026-08-28 -- see data/hours/vespers-psalmody-overrides/
|
||||
// categories/common-of-a-confessor-bishop.yml's own header.
|
||||
const ordo = resolveOrdo('vespers', '2026-11-10');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([109, 111, 112, 131]);
|
||||
});
|
||||
|
||||
it('stays on the plain ferial default for a below-threshold feast (Semiduplex), even on its own kept evening', () => {
|
||||
// 2026-08-08 (Ss. Cyriacus/Largus/Smaragdus, Semiduplex) is below the
|
||||
// duplex-majus+ override threshold -- confirmed live against Divinum
|
||||
// Officium, which also keeps this date on the plain ferial Saturday
|
||||
// set, not any Common's own psalmody.
|
||||
const ordo = resolveOrdo('vespers', '2026-08-08');
|
||||
const numbers = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p.kind === 'psalm' ? p.psalmNumber : undefined));
|
||||
expect(numbers).toEqual([144, 145, 146, 147]);
|
||||
});
|
||||
|
||||
it("uses the Assumption's own per-feast office override on her own Saturday, not the plain Saturday weekday default", () => {
|
||||
// 2026-08-15 is a Saturday and the Assumption (Duplex I. classis) —
|
||||
// well above keepsOwnSecondVespers's threshold, so it keeps its own
|
||||
|
||||
Reference in New Issue
Block a user