Give each half of Saturday's split Deuteronomy canticle its own citation
Deploy / deploy (push) Successful in 1m27s
Deploy / deploy (push) Successful in 1m27s
canticleText() always returned the whole canticle's stored citation
("Deut 32:1-65"), even when only slicing out half its verses -- both
Saturday Lauds pieces showed the same full range regardless of which
half was actually being said. Derive the citation from the slice's own
first/last verse numbers instead, only when slicing; a full canticle's
stored citation is left as-is, since it already matches what's shown
and a formula can't reproduce an irregular one like
canticum-trium-puerorum's "Dan 3:57-88,56" (a non-contiguous liturgical
splice).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018sPkfGLiq58pmmDoBWH5UX
This commit is contained in:
+27
-2
@@ -3,7 +3,7 @@ import type { LiturgicalDay, Weekday } from '../calendar/types';
|
||||
import { resolveDay } from '../calendar';
|
||||
import { getDayLabel } from '../calendar/day-label';
|
||||
import { getPsalmVerses } from '../psalter';
|
||||
import { getCanticle } from './lauds-canticles';
|
||||
import { getCanticle, type CanticleVerse } from './lauds-canticles';
|
||||
import { getLaudsSaintOverride, getLaudsCommonOverride, type LaudsPsalmodyOverride } from './lauds-psalmody-overrides';
|
||||
import { getOpeningVersicleId } from './opening-versicle';
|
||||
import { getMarianAntiphonId, getMarianAntiphonLabel } from './marian-antiphon';
|
||||
@@ -145,6 +145,30 @@ function psalmParts(numbers: number[], antiphon: BilingualText, opening: Resolve
|
||||
|
||||
const STATUS_RANK = { verified: 0, draft: 1, missing: 2 } as const;
|
||||
|
||||
/** The general rule is that a citation must match whatever verses are
|
||||
* actually being displayed — not "the whole canticle's citation,
|
||||
* always." A full canticle's stored `citation` already satisfies that
|
||||
* (canticleText below only calls this helper for a `slice`), including
|
||||
* cases a formula couldn't reconstruct — canticum-trium-puerorum's
|
||||
* "Dan 3:57-88,56" is a non-contiguous liturgical splice, its last verse
|
||||
* (`n: "3:56"`) coming after verse 88 in display order, not after it
|
||||
* numerically. Only Saturday's split Deuteronomy canticle ever shows
|
||||
* fewer than all of a canticle's verses, so it's the only case needing
|
||||
* this: book name from the canticle's base citation (its first word),
|
||||
* verse range from the slice's own first/last `n` values (e.g.
|
||||
* "32:1"/"32:32" -> "Deut 32:1-32") — showing the full "1-65" range on
|
||||
* both halves would mislabel whichever half isn't actually being said. */
|
||||
function sliceCitation(baseCitation: string, verses: CanticleVerse[]): string {
|
||||
const book = baseCitation.split(' ')[0] ?? baseCitation;
|
||||
const first = verses[0]?.n;
|
||||
const last = verses[verses.length - 1]?.n;
|
||||
if (!first || !last) {
|
||||
return baseCitation;
|
||||
}
|
||||
const lastVerse = last.split(':')[1] ?? last;
|
||||
return `${book} ${first}-${lastVerse}`;
|
||||
}
|
||||
|
||||
/** Joins a canticle's verse array (or a `[start, end)` slice of it — see
|
||||
* Saturday's split canticle below) into one flowing text block, the same
|
||||
* shape nunc-dimittis/benedictus already use — Lauds' weekday canticles
|
||||
@@ -174,7 +198,8 @@ function canticleText(canticleId: string, slice?: [number, number]): ResolvedTex
|
||||
}
|
||||
status[lang] = worst;
|
||||
}
|
||||
return { text, status, citation: { la: canticle.citation, en: canticle.citation } };
|
||||
const citation = slice ? sliceCitation(canticle.citation, verses) : canticle.citation;
|
||||
return { text, status, citation: { la: citation, en: citation } };
|
||||
}
|
||||
|
||||
/** Ps 66 through the Laudate psalms — see hours/types.ts's 'lauds-psalmody'
|
||||
|
||||
@@ -56,6 +56,10 @@ describe('resolveOrdo("lauds", ...)', () => {
|
||||
|
||||
const canticle = ordo.parts.find((p) => p.kind === 'canticle' && p.canticleId !== 'benedictus');
|
||||
expect(canticle?.kind === 'canticle' ? canticle.canticleId : undefined).toBe('canticum-trium-puerorum');
|
||||
// Its non-contiguous liturgical citation (last verse said out of
|
||||
// numeric order) must come through as authored, not get mangled by
|
||||
// a first/last-verse-number formula the way a naive fix would.
|
||||
expect(canticle?.kind === 'canticle' ? canticle.text.citation?.la : undefined).toBe('Dan 3:57-88,56');
|
||||
});
|
||||
|
||||
it("resolves Saturday's own psalmody: only one weekday psalm (142), and the Canticle of Moses said in two pieces (RB 13's own division), one shared antiphon framing both", () => {
|
||||
@@ -78,6 +82,10 @@ describe('resolveOrdo("lauds", ...)', () => {
|
||||
expect(part2?.kind === 'canticle' ? part2.antiphon : 'should be undefined -- shares part 1s opening antiphon').toBeUndefined();
|
||||
expect(part1?.kind === 'canticle' ? part1.text.text.la : undefined).toContain('Audíte, cæli');
|
||||
expect(part2?.kind === 'canticle' ? part2.text.text.la : undefined).toContain('Ignis succénsus est');
|
||||
// Regression test: each half must cite its own verse range, not the
|
||||
// whole canticle's "Deut 32:1-65" on both pieces.
|
||||
expect(part1?.kind === 'canticle' ? part1.text.citation?.la : undefined).toBe('Deut 32:1-32');
|
||||
expect(part2?.kind === 'canticle' ? part2.text.citation?.la : undefined).toBe('Deut 32:33-65');
|
||||
// Each part gets its own Gloria Patri.
|
||||
expect(part1?.kind === 'canticle' ? part1.gloriaPatri?.text.la : undefined).toContain('Glória Patri');
|
||||
expect(part2?.kind === 'canticle' ? part2.gloriaPatri?.text.la : undefined).toContain('Glória Patri');
|
||||
|
||||
Reference in New Issue
Block a user