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
complete "Let us pray ... Amen." block, not the more compressed real form
(one "Let us pray," only the last collect's doxology).
- Suffrage omission only checks `omitOnDouble` (+ Marian Saturday's own
Cross/BVM filter, + Christ the King's explicit id check, + the Cross-only
ferial-office check added 2026-08). Real seasonal suppression is still
missing: per the Tridentine 1906/1910 source (`specials.pl`'s
`checksuffragium`), suffrages are omitted *entirely* during Advent,
Christmastide, and Passiontide, and whenever any octave is active — none
of that is modeled, so e.g. an Advent feria currently still shows all
five suffrages when the real office would have none.
- Suffrage omission (`lauds.ts`'s `suffrages` case) now checks: Double-or-
higher rank, Christ the King, Advent/Christmastide/Passiontide season,
any active octave (however low its own rank), Marian Saturday's own
Cross/BVM filter, and the Cross-only ferial-office check — all
confirmed directly against **Monastic Tridentinum 1617** itself, not
just the Tridentine 1906/1910 track the suffrage content and the
Joseph/Cross-gating logic were sourced from (checked Advent 2026-12-03,
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
is from general knowledge of the pre-1955 tradition, not independently
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 { 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 { getTemporalFeastRecord } from '../calendar/temporal-feasts';
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
// Double-or-higher day.
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 [];
}
// "Of the Holy Cross" is the real *ferial*-office suffrage, sourced
+20
View File
@@ -219,6 +219,26 @@ describe('resolveOrdo("lauds", ...)', () => {
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', () => {
const ordo = resolveOrdo('lauds', FERIAL_TUESDAY);
const closingVersicle = ordo.parts.find(