Omit Lauds suffrages entirely during Advent/Christmastide/Passiontide and any active octave
Deploy / deploy (push) Successful in 54s

Checked directly against Monastic Tridentinum 1617 (not just the
Tridentine 1906/1910 track the suffrage content itself comes from) --
this omission isn't a rubric-track-specific quirk, it holds under this
app's own primary source too:

- Advent, Christmastide, and Passiontide ferias: suffrages omitted
  entirely. Confirmed ordinary Lent (weeks 1-4, not Passiontide) is NOT
  excluded -- only the last two weeks are.
- Any day within an active octave: suffrages omitted entirely, however
  low that octave's own rank is -- confirmed against day 2/3 of St.
  Lawrence's own Semiduplex octave, well below the existing Duplex+
  threshold.

Today (within Lawrence's octave) was showing all five suffrages before
this fix; the real office has none but the Marian antiphon.

Still not modeled, flagged in TODO.md: Paschaltide's own substitution
(a single alleluia-form "Suffragium Paschale" replaces the whole set
during Eastertide, rather than the ordinary set showing or being
omitted) -- a separate, larger feature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 06:59:17 -04:00
parent fb66f60770
commit 2f8f744cd1
3 changed files with 48 additions and 10 deletions
+14 -8
View File
@@ -271,14 +271,20 @@ real `draft` status went through it.
- `getDayCollects`: each collect in a multi-collect day renders as its own - `getDayCollects`: each collect in a multi-collect day renders as its own
complete "Let us pray ... Amen." block, not the more compressed real form complete "Let us pray ... Amen." block, not the more compressed real form
(one "Let us pray," only the last collect's doxology). (one "Let us pray," only the last collect's doxology).
- Suffrage omission only checks `omitOnDouble` (+ Marian Saturday's own - Suffrage omission (`lauds.ts`'s `suffrages` case) now checks: Double-or-
Cross/BVM filter, + Christ the King's explicit id check, + the Cross-only higher rank, Christ the King, Advent/Christmastide/Passiontide season,
ferial-office check added 2026-08). Real seasonal suppression is still any active octave (however low its own rank), Marian Saturday's own
missing: per the Tridentine 1906/1910 source (`specials.pl`'s Cross/BVM filter, and the Cross-only ferial-office check — all
`checksuffragium`), suffrages are omitted *entirely* during Advent, confirmed directly against **Monastic Tridentinum 1617** itself, not
Christmastide, and Passiontide, and whenever any octave is active — none just the Tridentine 1906/1910 track the suffrage content and the
of that is modeled, so e.g. an Advent feria currently still shows all Joseph/Cross-gating logic were sourced from (checked Advent 2026-12-03,
five suffrages when the real office would have none. Christmastide 2026-12-30, Passiontide 2026-03-23, an active octave via
St. Lawrence's own Aug 11-12, and ordinary Lent 2026-02-20 specifically
*not* excluded, to confirm it's Passiontide-only, not all of Lent).
Still not modeled: the Paschaltide *substitution* (real practice
replaces the whole suffrage set with a single alleluia-form "Suffragium
Paschale" during Eastertide rather than omitting or showing the
ordinary set) — flagged, not built.
- `data/calendar/temporal-categories.yml`'s season→category reconstruction - `data/calendar/temporal-categories.yml`'s season→category reconstruction
is from general knowledge of the pre-1955 tradition, not independently is from general knowledge of the pre-1955 tradition, not independently
verified against a primary source for every entry (flagged inline where verified against a primary source for every entry (flagged inline where
+14 -2
View File
@@ -1,6 +1,6 @@
import type { HourDefinition, HourPart, ResolvedOrdo, ResolvedPart, ResolvedText } from './types'; import type { HourDefinition, HourPart, ResolvedOrdo, ResolvedPart, ResolvedText } from './types';
import type { LiturgicalDay, Weekday } from '../calendar/types'; import type { LiturgicalDay, Weekday } from '../calendar/types';
import { resolveDay, isAtLeast, isSundayOrFeast } from '../calendar'; import { resolveDay, isAtLeast, isSundayOrFeast, activeOctavesFor } from '../calendar';
import { getDayLabel } from '../calendar/day-label'; import { getDayLabel } from '../calendar/day-label';
import { getTemporalFeastRecord } from '../calendar/temporal-feasts'; import { getTemporalFeastRecord } from '../calendar/temporal-feasts';
import { getPsalmVerses } from '../psalter'; import { getPsalmVerses } from '../psalter';
@@ -244,7 +244,19 @@ function resolvePart(part: HourPart, day: LiturgicalDay): ResolvedPart[] {
// "Suffragium{omittitur}" — the whole set drops, same as any other // "Suffragium{omittitur}" — the whole set drops, same as any other
// Double-or-higher day. // Double-or-higher day.
const isChristTheKing = day.winner.kind === 'temporal' && day.winner.id === 'christ-the-king'; const isChristTheKing = day.winner.kind === 'temporal' && day.winner.id === 'christ-the-king';
if (part.omitOnDouble && (isDoubleOrHigher(day.winner) || isChristTheKing)) { // Confirmed live against Monastic Tridentinum 1617 directly (this
// isn't a Tridentine-1906-only quirk -- the same exclusions hold
// under the app's own primary rubric track): suffrages are omitted
// *entirely* during Advent, Christmastide, and Passiontide (not all
// of Lent -- ordinary Lent ferias still get them, checked directly
// against 2026-02-20; only the last two weeks), and on any day
// within an active octave, however low that octave's own rank is
// -- checked directly against day 3 of St. Lawrence's own
// (Semiduplex) octave, which still omits them despite being well
// below the Duplex+ rank threshold below.
const isSeasonallyExcluded = day.season === 'advent' || day.season === 'christmastide' || day.season === 'passiontide';
const isWithinAnOctave = activeOctavesFor(day.date).length > 0;
if (part.omitOnDouble && (isDoubleOrHigher(day.winner) || isChristTheKing || isSeasonallyExcluded || isWithinAnOctave)) {
return []; return [];
} }
// "Of the Holy Cross" is the real *ferial*-office suffrage, sourced // "Of the Holy Cross" is the real *ferial*-office suffrage, sourced
+20
View File
@@ -219,6 +219,26 @@ describe('resolveOrdo("lauds", ...)', () => {
expect(crossSuffrage).toBeUndefined(); expect(crossSuffrage).toBeUndefined();
}); });
it('omits the suffrages entirely during Advent, Christmastide, and Passiontide -- confirmed against Monastic Tridentinum 1617 directly, not just Tridentine 1906/1910', () => {
const advent = resolveOrdo('lauds', '2026-12-03'); // plain Advent feria
const christmastide = resolveOrdo('lauds', '2026-12-30'); // plain day within the Christmas Octave
const passiontide = resolveOrdo('lauds', '2026-03-23'); // Monday of Passion Week
for (const ordo of [advent, christmastide, passiontide]) {
expect(ordo.parts.some((p) => p.kind === 'preces' && p.label === 'Of the Holy Cross')).toBe(false);
expect(ordo.parts.some((p) => p.kind === 'preces' && p.label === 'For Peace')).toBe(false);
}
});
it('shows the suffrages on an ordinary Lent feria (not Passiontide) -- only the last two weeks of Lent are excluded, not all of it', () => {
const ordo = resolveOrdo('lauds', '2026-02-20'); // Friday after Ash Wednesday, week 1 of Lent
expect(ordo.parts.some((p) => p.kind === 'preces' && p.label === 'For Peace')).toBe(true);
});
it('omits the suffrages entirely on any day within an active octave, however low the octave\'s own rank is', () => {
const ordo = resolveOrdo('lauds', LAWRENCE_OCTAVE_DAY); // day 2 of St. Lawrence's own (Semiduplex) octave
expect(ordo.parts.some((p) => p.kind === 'preces' && p.label === 'For Peace')).toBe(false);
});
it('closes with the Conclusio, the closing versicle ("may the Lord grant us his peace... and life everlasting"), and the Marian antiphon -- without spelling out the silent Our Father', () => { it('closes with the Conclusio, the closing versicle ("may the Lord grant us his peace... and life everlasting"), and the Marian antiphon -- without spelling out the silent Our Father', () => {
const ordo = resolveOrdo('lauds', FERIAL_TUESDAY); const ordo = resolveOrdo('lauds', FERIAL_TUESDAY);
const closingVersicle = ordo.parts.find( const closingVersicle = ordo.parts.find(