Model Ascension, Corpus Christi, and the Sacred Heart as temporal feasts
Deploy / deploy (push) Successful in 1m23s
Deploy / deploy (push) Successful in 1m23s
Adds pre-1955 TemporalFeastRecords (rank duplex-1-classis, real octaves) for the three "feasts of the Lord" that were previously only referenced as bare date offsets: Corpus Christi/Sacred Heart's own days already couldn't lose to a sanctoral candidate, but with no record the day fell back to the generic Trinity-week feria collect and never displayed its real name; Ascension had no protection at all against an ordinary saint winning it outright. Sacred Heart's own record carries the forbidsSuccessorCommemoration flag added in the prior commit, refusing to commemorate Most Precious Blood on the rare late-Easter date they fall adjacent. octave.wins is set to duplex on all three, matching Pentecost's own verified threshold, despite a known conflict with a previously live-verified case (St. Ubald winning First Vespers within Ascension's own octave) -- kept per direct instruction; the affected tests (Ubald, St. Margaret of Scotland) were updated to assert the new behavior, and the tradeoff is documented in ascension.yml and TODO.md. A handful of unrelated test fixtures that happened to land inside these new octave windows were moved to nearby clean dates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
This commit is contained in:
@@ -1314,6 +1314,52 @@ draft-English antiphon (`tests/hours/resolve-common.test.ts`).
|
|||||||
commemorated back, regardless of the winner's own rank. Not addressed
|
commemorated back, regardless of the winner's own rank. Not addressed
|
||||||
here; this pass only covered the Vespers cross-day direction.
|
here; this pass only covered the Vespers cross-day direction.
|
||||||
|
|
||||||
|
- **Ascension, Corpus Christi, and the Feast of the Sacred Heart now
|
||||||
|
modeled as real temporal feasts (2026-08-26)**: none of the three had a
|
||||||
|
`TemporalFeastRecord` at all — only referenced as bare date offsets in
|
||||||
|
`easter-offsets.yml`/`temporal-categories.yml`/
|
||||||
|
`isMajorFixedFeastOfTheLord`. Corpus Christi/Sacred Heart's own days
|
||||||
|
were already blocked from any sanctoral winner (`privileged-feria-
|
||||||
|
major`), but with no record, `resolveTemporalId` fell back to whatever
|
||||||
|
Sunday collect governs that week (`post-pentecost-01` for both), so the
|
||||||
|
day displayed as generic Trinity-week feria content, never "Corpus
|
||||||
|
Christi"/"Sacred Heart" by name. Ascension (Easter+39) was worse:
|
||||||
|
`ascensiontide`'s own `temporal-categories.yml` entry is plain
|
||||||
|
`ordinary-feria` for every day in the season including Ascension
|
||||||
|
Thursday itself, so nothing stopped an ordinary sanctoral candidate
|
||||||
|
from winning that day outright had one ever been scheduled there. All
|
||||||
|
three now have `data/calendar/temporal-feasts/{ascension,corpus-
|
||||||
|
christi,sacred-heart}.yml` (`rank: duplex-1-classis`, an `easter`
|
||||||
|
anchor, `octave: {enabled: true, wins: duplex}`) — the pre-1955
|
||||||
|
calendar specifically (each carries a real octave; the reference
|
||||||
|
engine's own "(rubrica 196)"-tagged variant strips it, later-reform
|
||||||
|
text this project doesn't follow, per CLAUDE.md). Sacred Heart's own
|
||||||
|
record also carries `forbidsSuccessorCommemoration` (see the previous
|
||||||
|
entry above) — the live-sourced Precious Blood exclusion this whole
|
||||||
|
pass started from.
|
||||||
|
- `octave.wins: duplex` is matched to `pentecost-sunday.yml`'s own
|
||||||
|
verified threshold as the closest analogue for all three, and was
|
||||||
|
kept **despite a known, direct counter-example already in this
|
||||||
|
codebase**: `tests/hours/vespers.test.ts`'s St. Ubald case was
|
||||||
|
previously live-verified against Divinum Officium (Monastic
|
||||||
|
Tridentinum 1617) to have him — Semiduplex — win First Vespers
|
||||||
|
*within* Ascension's own octave window, which a strict `duplex`
|
||||||
|
threshold contradicts. Real octaves come in three privilege
|
||||||
|
"orders"; Ascension/Sacred Heart are the weakest (Order III),
|
||||||
|
historically permissive enough for an ordinary Semiduplex saint to
|
||||||
|
still occur inside them, unlike Pentecost's own (Order I) octave
|
||||||
|
this threshold is borrowed from. Kept at `duplex` anyway per direct
|
||||||
|
instruction — the Ubald test (and St. Margaret of Scotland's
|
||||||
|
equivalent First Vespers case, inside Corpus Christi's own window)
|
||||||
|
were updated to assert the new behavior instead of the old
|
||||||
|
live-verified one. Revisit if this turns out to misfire elsewhere;
|
||||||
|
see `ascension.yml`'s own comment for the full note.
|
||||||
|
- No propers content (collects/antiphons/hymns/lessons) authored yet
|
||||||
|
for any of the three — this pass is calendar/occurrence mechanics
|
||||||
|
only, same "content vs. mechanism" split as everywhere else in this
|
||||||
|
file; an hour resolving one of these ids will correctly show
|
||||||
|
`status: 'missing'` text until that's authored.
|
||||||
|
|
||||||
### Matins ordo — mechanism built, small content slice (2026-08)
|
### Matins ordo — mechanism built, small content slice (2026-08)
|
||||||
|
|
||||||
`src/hours/matins.ts` was the last stub hour. Built as a genuinely different
|
`src/hours/matins.ts` was the last stub hour. Built as a genuinely different
|
||||||
|
|||||||
@@ -85,7 +85,12 @@ export function movableTemporalFeasts(): TemporalFeastRecord[] {
|
|||||||
const FIXED_DATE_STARTS: [string, string][] = [['12-25', 'christmas-day']];
|
const FIXED_DATE_STARTS: [string, string][] = [['12-25', 'christmas-day']];
|
||||||
|
|
||||||
/** Easter-offset starts (offset -> temporal feast id). */
|
/** Easter-offset starts (offset -> temporal feast id). */
|
||||||
const EASTER_OFFSET_STARTS: [number, string][] = [[49, 'pentecost-sunday']];
|
const EASTER_OFFSET_STARTS: [number, string][] = [
|
||||||
|
[39, 'ascension'],
|
||||||
|
[49, 'pentecost-sunday'],
|
||||||
|
[60, 'corpus-christi'],
|
||||||
|
[68, 'sacred-heart'],
|
||||||
|
];
|
||||||
|
|
||||||
/** Which temporal feast(s), if any, have their own (day-1) octave start on this date. */
|
/** Which temporal feast(s), if any, have their own (day-1) octave start on this date. */
|
||||||
export function temporalFeastIdsStartingOn(isoDate: string): string[] {
|
export function temporalFeastIdsStartingOn(isoDate: string): string[] {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Read from the reference engine: web/www/horas/Latin/Tempora/Pasc5-4.txt,
|
||||||
|
# untagged/default [Rank] block — "Duplex I classis cum Octava Privilegiata
|
||||||
|
# III classis;;7" (the "(rubrica 196)" variant strips the octave entirely,
|
||||||
|
# 1955/1960-reform text this project deliberately doesn't follow — see
|
||||||
|
# CLAUDE.md's pre-1955 calendar/rubrics note). Previously not modeled as
|
||||||
|
# its own record at all: `resolveSeason` already carves Easter+39 into its
|
||||||
|
# own `ascensiontide` season, but nothing gave that Thursday itself a real
|
||||||
|
# identity — `temporal-categories.yml`'s `ascensiontide` entry maps every
|
||||||
|
# feria in the season (including Ascension Thursday itself) to plain
|
||||||
|
# `ordinary-feria`, so before this record existed the day would have shown
|
||||||
|
# generic "Thursday in Ascensiontide" content, with no recognition it's
|
||||||
|
# the feast at all. Duplex I classis outranks any real sanctoral
|
||||||
|
# candidate regardless, so this record's `rank` alone is enough to make
|
||||||
|
# `calendar/movable-feasts.ts`'s `applyMovableFeasts` win the day outright
|
||||||
|
# — the `ordinary-feria` category was never actually load-bearing here.
|
||||||
|
id: ascension
|
||||||
|
name: "The Ascension of Our Lord"
|
||||||
|
rank: duplex-1-classis
|
||||||
|
anchor:
|
||||||
|
kind: easter
|
||||||
|
offset: 39
|
||||||
|
octave:
|
||||||
|
enabled: true
|
||||||
|
# Deliberately `duplex`, matching pentecost-sunday.yml's own verified
|
||||||
|
# threshold as the closest analogue — despite a real, pre-existing
|
||||||
|
# counter-example already in this codebase:
|
||||||
|
# tests/hours/vespers.test.ts's St. Ubald test ("Live-verified against
|
||||||
|
# Divinum Officium (Monastic Tridentinum 1617) votive=C4") has him
|
||||||
|
# (Semiduplex) winning First Vespers on May 15, *within* this octave's
|
||||||
|
# own window, which a strict `duplex` threshold contradicts — real
|
||||||
|
# octaves come in three privilege "orders", and Ascension/Sacred Heart
|
||||||
|
# are the weakest (Order III), historically permissive enough that an
|
||||||
|
# ordinary Semiduplex saint can still occur inside them, unlike
|
||||||
|
# Pentecost's own (Order I) octave this threshold is borrowed from.
|
||||||
|
# Kept at `duplex` anyway per direct instruction (2026-08-26) despite
|
||||||
|
# this known conflict — the Ubald test itself was updated to match, see
|
||||||
|
# its own comment. Revisit if this turns out to misfire elsewhere.
|
||||||
|
wins: duplex
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Read from the reference engine: web/www/horas/Latin/Tempora/Pent01-4.txt,
|
||||||
|
# untagged/default [Rank] block — "Duplex I classis cum Octava privilegiata
|
||||||
|
# II ordinis;;7" (the "(rubrica 1960)" variant strips the octave — 1960-
|
||||||
|
# reform text this project deliberately doesn't follow, see CLAUDE.md's
|
||||||
|
# pre-1955 calendar/rubrics note). `temporal-categories.yml` already
|
||||||
|
# forces this single day to `privileged-feria-major` (no sanctoral
|
||||||
|
# candidate can win it), so this record doesn't change *occurrence* on
|
||||||
|
# the feast day itself — what it fixes is identity: without it,
|
||||||
|
# `calendar/temporal-id.ts`'s `resolveTemporalId` falls back to
|
||||||
|
# `post-pentecost-01` (Trinity Sunday's own governing collect) for this
|
||||||
|
# date, so the day would display as generic Trinity-week feria content,
|
||||||
|
# not "Corpus Christi" by name. `rank` here is enough for
|
||||||
|
# `calendar/movable-feasts.ts`'s `applyMovableFeasts` to make this id the
|
||||||
|
# day's real winner.
|
||||||
|
id: corpus-christi
|
||||||
|
name: "The Most Holy Body of Christ"
|
||||||
|
rank: duplex-1-classis
|
||||||
|
anchor:
|
||||||
|
kind: easter
|
||||||
|
offset: 60
|
||||||
|
octave:
|
||||||
|
enabled: true
|
||||||
|
# See ascension.yml's own note (same `duplex` choice, same known
|
||||||
|
# conflict with a live-verified counter-example elsewhere in this
|
||||||
|
# codebase — St. Margaret of Scotland's own First Vespers test, updated
|
||||||
|
# to match) — kept at `duplex` per direct instruction (2026-08-26).
|
||||||
|
wins: duplex
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Read from the reference engine: web/www/horas/Latin/Tempora/Pent02-5.txt,
|
||||||
|
# untagged/default [Rank] block — "Duplex I classis cum Octava
|
||||||
|
# privilegiata III ordinis;;6.51" (the "(rubrica 196)" variant strips the
|
||||||
|
# octave — 1955/1960-reform text this project deliberately doesn't
|
||||||
|
# follow, see CLAUDE.md's pre-1955 calendar/rubrics note).
|
||||||
|
# `temporal-categories.yml` already forces this single day to
|
||||||
|
# `privileged-feria-major`; this record's job is identity (a real name
|
||||||
|
# and rank instead of falling back to whatever Sunday governs that week's
|
||||||
|
# collect) and giving `forbidsSuccessorCommemoration` (see
|
||||||
|
# temporal-feasts.ts's own doc comment) a record to live on.
|
||||||
|
id: sacred-heart
|
||||||
|
name: "The Most Sacred Heart of Our Lord Jesus Christ"
|
||||||
|
rank: duplex-1-classis
|
||||||
|
anchor:
|
||||||
|
kind: easter
|
||||||
|
offset: 68
|
||||||
|
octave:
|
||||||
|
enabled: true
|
||||||
|
# See ascension.yml's own note (same `duplex` choice, same known
|
||||||
|
# conflict with a live-verified counter-example, kept per direct
|
||||||
|
# instruction 2026-08-26).
|
||||||
|
wins: duplex
|
||||||
|
forbidsSuccessorCommemoration: true
|
||||||
@@ -75,10 +75,14 @@ describe('getDayLabel — ordinal temporal label', () => {
|
|||||||
|
|
||||||
it('falls back to weekday + season name for seasons with no ordinal convention modeled', () => {
|
it('falls back to weekday + season name for seasons with no ordinal convention modeled', () => {
|
||||||
// 2026-05-15 is now St. John Baptist de la Salle's own fixed day
|
// 2026-05-15 is now St. John Baptist de la Salle's own fixed day
|
||||||
// (Duplex, added per the full-year sanctoral import) -- moved to
|
// (Duplex, added per the full-year sanctoral import); May 21 is now
|
||||||
// May 21, still within Ascensiontide 2026 (May 14-23) and still
|
// Ascension's own octave closing day (Ascension now modeled as a
|
||||||
// free of any sanctoral entry.
|
// real TemporalFeastRecord with an octave, Easter+39 through +46 —
|
||||||
expect(getDayLabel(resolveDay('2026-05-21')).en).toContain('in Ascensiontide');
|
// see TODO.md's 2026-08-26 entry), and May 22/23 both carry a fixed
|
||||||
|
// saint of their own -- 2028-06-03 (Easter+48, the day before
|
||||||
|
// Pentecost) is the nearest genuinely free Ascensiontide day: past
|
||||||
|
// Ascension's own octave, no fixed saint, still short of Pentecost.
|
||||||
|
expect(getDayLabel(resolveDay('2028-06-03')).en).toContain('in Ascensiontide');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -339,12 +343,20 @@ describe('getDayLabel — named temporal feast rank', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getDayLabel — feast name combination', () => {
|
describe('getDayLabel — feast name combination', () => {
|
||||||
|
// 2026-06-15 (the "2nd week after Trinity" Monday, Easter+71) used to
|
||||||
|
// be free of any octave; now it's day 4 of Sacred Heart's own octave
|
||||||
|
// (Easter+68 through +75, added 2026-08-26 -- see TODO.md), and that
|
||||||
|
// offset range is fixed relative to Easter every year, not just this
|
||||||
|
// one, so no year-swap avoids it for "2nd week after Trinity"
|
||||||
|
// specifically. Moved to the "3rd week after Trinity" Monday
|
||||||
|
// (Easter+78) instead, clear of Ascension's (+39..+46) and the
|
||||||
|
// Corpus Christi/Sacred Heart combined (+60..+75) octave ranges.
|
||||||
const base: LiturgicalDay = {
|
const base: LiturgicalDay = {
|
||||||
date: '2026-06-15',
|
date: '2026-06-22',
|
||||||
weekday: 'monday',
|
weekday: 'monday',
|
||||||
season: 'trinitytide',
|
season: 'trinitytide',
|
||||||
temporalCategory: 'ordinary-feria',
|
temporalCategory: 'ordinary-feria',
|
||||||
winner: { kind: 'temporal', id: 'post-pentecost-02' },
|
winner: { kind: 'temporal', id: 'post-pentecost-03' },
|
||||||
commemorations: [],
|
commemorations: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -361,10 +373,10 @@ describe('getDayLabel — feast name combination', () => {
|
|||||||
...base,
|
...base,
|
||||||
commemorations: [{ kind: 'sanctoral', id: 'x', name: 'St. Ereden', rank: 'duplex-2-classis' }],
|
commemorations: [{ kind: 'sanctoral', id: 'x', name: 'St. Ereden', rank: 'duplex-2-classis' }],
|
||||||
};
|
};
|
||||||
expect(getDayLabel(day).en).toBe('St. Ereden — Monday in the 2nd week after Trinity (Feria)');
|
expect(getDayLabel(day).en).toBe('St. Ereden — Monday in the 3rd week after Trinity (Feria)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows just the temporal label when nothing is commemorated at all', () => {
|
it('shows just the temporal label when nothing is commemorated at all', () => {
|
||||||
expect(getDayLabel(base).en).toBe('Monday in the 2nd week after Trinity (Feria)');
|
expect(getDayLabel(base).en).toBe('Monday in the 3rd week after Trinity (Feria)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,8 +10,12 @@ describe('resolveEveningDay', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not anticipate between two plain ferias', () => {
|
it('does not anticipate between two plain ferias', () => {
|
||||||
const day = resolveEveningDay('2026-06-16'); // Tuesday, ordinary trinitytide
|
// 2033-09-06: Tuesday, ordinary trinitytide, genuinely free of any
|
||||||
expect(day.date).toBe('2026-06-16');
|
// saint or active octave on both this date and the next (2026-06-16,
|
||||||
|
// used here previously, fell inside Sacred Heart's own octave once
|
||||||
|
// that was modeled 2026-08-26 -- see TODO.md).
|
||||||
|
const day = resolveEveningDay('2033-09-06');
|
||||||
|
expect(day.date).toBe('2033-09-06');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('anticipates Easter from Holy Saturday', () => {
|
it('anticipates Easter from Holy Saturday', () => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { resolveOrdo } from '../../src/hours';
|
|||||||
// why these particular dates.
|
// why these particular dates.
|
||||||
const FERIAL_SUNDAY = '2026-06-21';
|
const FERIAL_SUNDAY = '2026-06-21';
|
||||||
const FERIAL_MONDAY = '2026-06-08'; // not 08-31 -- St. Raymond Nonnatus's own day (common-of-a-confessor)
|
const FERIAL_MONDAY = '2026-06-08'; // not 08-31 -- St. Raymond Nonnatus's own day (common-of-a-confessor)
|
||||||
const FERIAL_TUESDAY = '2026-06-16';
|
const FERIAL_TUESDAY = '2033-09-06'; // not 2026-06-16 -- that fell inside Sacred Heart's own octave once modeled 2026-08-26, see TODO.md
|
||||||
const LAWRENCE_OCTAVE_DAY = '2026-08-11'; // Semiduplex -- exercises isDoubleOrHigher's false branch differently than a plain feria only in rank, not in psalms (Commune override not modeled, see below)
|
const LAWRENCE_OCTAVE_DAY = '2026-08-11'; // Semiduplex -- exercises isDoubleOrHigher's false branch differently than a plain feria only in rank, not in psalms (Commune override not modeled, see below)
|
||||||
|
|
||||||
describe('resolveOrdo("lauds", ...)', () => {
|
describe('resolveOrdo("lauds", ...)', () => {
|
||||||
@@ -170,11 +170,17 @@ describe('resolveOrdo("lauds", ...)', () => {
|
|||||||
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Martyr Dei, qui únicum');
|
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Martyr Dei, qui únicum');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses Common-of-a-Confessor-Bishop's real office bundle on St. Ubald's own day, not the ferial default", () => {
|
it("uses Common-of-a-Confessor-Bishop's real office bundle on St. Robert Bellarmine's own day, not the ferial default", () => {
|
||||||
// St. Ubald (May 16, Semiduplex, common-of-a-confessor-bishop) has no
|
// Was St. Ubald (May 16, Semiduplex, common-of-a-confessor-bishop) --
|
||||||
// proper Lauds content of his own -- see vespers-hymn-common-of-a-
|
// moved to St. Robert Bellarmine (May 13, Duplex, same Common) once
|
||||||
// confessor-bishop.yml's header.
|
// Ascension's own octave was modeled (2026-08-26, see TODO.md):
|
||||||
const ordo = resolveOrdo('lauds', '2026-05-16');
|
// Ubald now loses his own day outright to the octave (`octave.wins:
|
||||||
|
// duplex`, and Semiduplex Ubald falls short of it), so his own
|
||||||
|
// Common-of-a-Confessor-Bishop content no longer surfaces there at
|
||||||
|
// all. Bellarmine sits safely before the octave's own window (May
|
||||||
|
// 14-21) and has no proper Lauds content of his own either -- see
|
||||||
|
// vespers-hymn-common-of-a-confessor-bishop.yml's header.
|
||||||
|
const ordo = resolveOrdo('lauds', '2026-05-13');
|
||||||
const chapter = ordo.parts.find((p) => p.kind === 'chapter');
|
const chapter = ordo.parts.find((p) => p.kind === 'chapter');
|
||||||
expect(chapter?.kind === 'chapter' ? chapter.text.citation?.en : undefined).toBe('Sir 44:16-17');
|
expect(chapter?.kind === 'chapter' ? chapter.text.citation?.en : undefined).toBe('Sir 44:16-17');
|
||||||
const hymn = ordo.parts.find((p) => p.kind === 'hymn');
|
const hymn = ordo.parts.find((p) => p.kind === 'hymn');
|
||||||
|
|||||||
+41
-19
@@ -129,10 +129,11 @@ describe('resolveOrdo("vespers", ...)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to the classic weekday-default Magnificat antiphon on a bare ferial', () => {
|
it('falls back to the classic weekday-default Magnificat antiphon on a bare ferial', () => {
|
||||||
// 2026-06-16 is a Tuesday, plain ordinary trinitytide, no saint, no
|
// 2033-09-06 is a Tuesday, plain ordinary trinitytide, no saint, no
|
||||||
// active octave (same date already used by calendar/vespers.test.ts's
|
// active octave (same date already used by calendar/vespers.test.ts's
|
||||||
// "does not anticipate between two plain ferias").
|
// "does not anticipate between two plain ferias" -- see that test's
|
||||||
const ordo = resolveOrdo('vespers', '2026-06-16');
|
// own comment for why this moved off 2026-06-16).
|
||||||
|
const ordo = resolveOrdo('vespers', '2033-09-06');
|
||||||
const magnificatIndex = ordo.parts.findIndex((p) => p.kind === 'canticle' && p.canticleId === 'magnificat');
|
const magnificatIndex = ordo.parts.findIndex((p) => p.kind === 'canticle' && p.canticleId === 'magnificat');
|
||||||
expect(magnificatIndex).toBeGreaterThanOrEqual(0);
|
expect(magnificatIndex).toBeGreaterThanOrEqual(0);
|
||||||
const magnificat = ordo.parts[magnificatIndex];
|
const magnificat = ordo.parts[magnificatIndex];
|
||||||
@@ -250,15 +251,24 @@ describe('resolveOrdo("vespers", ...)', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses Common-of-a-Confessor-Bishop's real office bundle for St. Ubald's First Vespers", () => {
|
it("no longer gives St. Ubald First Vespers now that Ascension's own octave is modeled", () => {
|
||||||
// St. Ubald (May 16, Semiduplex, common-of-a-confessor-bishop) claims
|
// Historically (live-verified against Divinum Officium, Monastic
|
||||||
// First Vespers over May 15's own evening (no stronger rival that
|
// Tridentinum 1617, votive=C4) St. Ubald (May 16, Semiduplex,
|
||||||
// year). Live-verified against Divinum Officium (Monastic Tridentinum
|
// common-of-a-confessor-bishop) claims First Vespers over May 15's
|
||||||
// 1617) votive=C4 -- see vespers-hymn-common-of-a-confessor-bishop.yml's
|
// own evening -- a real Order-III-octave permissiveness this app's
|
||||||
// header.
|
// own `ascension.yml` (added 2026-08-26) doesn't reproduce: its
|
||||||
|
// `octave.wins: duplex` is deliberately borrowed from Pentecost's own
|
||||||
|
// (Order I) threshold rather than modeled per privilege-order, a
|
||||||
|
// known, accepted conflict with this specific live-verified case
|
||||||
|
// (see ascension.yml's own comment). Semiduplex Ubald now loses to
|
||||||
|
// the octave outright on his own day (May 16), so May 15's evening is
|
||||||
|
// just St. John Baptist de la Salle's (Duplex, wins May 15 itself
|
||||||
|
// outright) own kept Second Vespers -- his own Common-of-a-Confessor
|
||||||
|
// (not Bishop) bundle, not Ubald's Confessor-Bishop one -- with
|
||||||
|
// Ascension's octave merely commemorated alongside him.
|
||||||
const ordo = resolveOrdo('vespers', '2026-05-15');
|
const ordo = resolveOrdo('vespers', '2026-05-15');
|
||||||
const chapter = ordo.parts.find((p) => p.kind === 'chapter');
|
const chapter = ordo.parts.find((p) => p.kind === 'chapter');
|
||||||
expect(chapter?.kind === 'chapter' ? chapter.text.citation?.en : undefined).toBe('Sir 44:16-17');
|
expect(chapter?.kind === 'chapter' ? chapter.text.citation?.en : undefined).toBe('Sir 31:8-9');
|
||||||
const hymn = ordo.parts.find((p) => p.kind === 'hymn');
|
const hymn = ordo.parts.find((p) => p.kind === 'hymn');
|
||||||
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Iste Conféssor Dómini sacrátus');
|
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Iste Conféssor Dómini sacrátus');
|
||||||
});
|
});
|
||||||
@@ -322,16 +332,28 @@ describe('resolveOrdo("vespers", ...)', () => {
|
|||||||
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Exsúltet cælum láudibus');
|
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Exsúltet cælum láudibus');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses common-of-a-widow's real office bundle for St. Margaret of Scotland's First Vespers", () => {
|
it("no longer gives St. Margaret of Scotland First Vespers now that Corpus Christi's own octave is modeled", () => {
|
||||||
// St. Margaret of Scotland (Jun 10, Semiduplex, common-of-a-widow --
|
// St. Margaret of Scotland (Jun 10, Semiduplex, common-of-a-widow --
|
||||||
// live-verified 2026-08-26 to be real content identical to Common-
|
// content itself live-verified 2026-08-26 to be real, identical to
|
||||||
// of-a-Holy-Woman's, see lauds-capitulum-common-of-a-holy-woman.yml's
|
// Common-of-a-Holy-Woman's, see lauds-capitulum-common-of-a-holy-
|
||||||
// header) claims First Vespers over Jun 9's own evening.
|
// woman.yml's header) used to claim First Vespers over Jun 9's own
|
||||||
|
// evening. Now inside Corpus Christi's own octave (Jun 4-11, added
|
||||||
|
// 2026-08-26, `octave.wins: duplex` -- same deliberate, accepted
|
||||||
|
// conflict with a real Order-III-octave permissiveness as
|
||||||
|
// ascension.yml's own St. Ubald case, see corpus-christi.yml's own
|
||||||
|
// comment), Semiduplex Margaret loses her own day (Jun 10) outright
|
||||||
|
// to the octave -- and Jun 9 itself is *also* inside the same
|
||||||
|
// octave, already having lost its own Simplex saint (Ss. Primus and
|
||||||
|
// Felicianus) to it, so neither day has any sanctoral winner left to
|
||||||
|
// anticipate. Jun 9's evening falls all the way through to the plain
|
||||||
|
// ferial Vespers default (Trinity week's own governing chapter/hymn),
|
||||||
|
// with both Primus & Felicianus and Corpus Christi's octave merely
|
||||||
|
// commemorated, not any widow-specific content at all.
|
||||||
const ordo = resolveOrdo('vespers', '2026-06-09');
|
const ordo = resolveOrdo('vespers', '2026-06-09');
|
||||||
const chapter = ordo.parts.find((p) => p.kind === 'chapter');
|
const chapter = ordo.parts.find((p) => p.kind === 'chapter');
|
||||||
expect(chapter?.kind === 'chapter' ? chapter.text.citation?.en : undefined).toBe('Prov 31:10-11');
|
expect(chapter?.kind === 'chapter' ? chapter.text.citation?.en : undefined).toBe('2 Cor 1:3-4');
|
||||||
const hymn = ordo.parts.find((p) => p.kind === 'hymn');
|
const hymn = ordo.parts.find((p) => p.kind === 'hymn');
|
||||||
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Fortem viríli péctore');
|
expect(hymn?.kind === 'hymn' ? hymn.text.text.la : undefined).toContain('Tellúris ingens Cónditor');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses common-of-the-bvm's real office bundle for Our Lady of Mount Carmel's First Vespers", () => {
|
it("uses common-of-the-bvm's real office bundle for Our Lady of Mount Carmel's First Vespers", () => {
|
||||||
@@ -409,9 +431,9 @@ describe('resolveOrdo("vespers", ...)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('includes the Kyrie + Our Father lead-in right after the Magnificat, before the day collect (either form)', () => {
|
it('includes the Kyrie + Our Father lead-in right after the Magnificat, before the day collect (either form)', () => {
|
||||||
// 2026-06-16 is a Tuesday, plain ordinary trinitytide -- same bare
|
// 2033-09-06 is a Tuesday, plain ordinary trinitytide -- same bare
|
||||||
// ferial already used above for the Magnificat weekday-default test.
|
// ferial already used above for the Magnificat weekday-default test.
|
||||||
const ordo = resolveOrdo('vespers', '2026-06-16');
|
const ordo = resolveOrdo('vespers', '2033-09-06');
|
||||||
const litanyIndex = ordo.parts.findIndex((p) => p.kind === 'preces' && p.text.text.la?.includes('Kýrie, eléison'));
|
const litanyIndex = ordo.parts.findIndex((p) => p.kind === 'preces' && p.text.text.la?.includes('Kýrie, eléison'));
|
||||||
const magnificatIndex = ordo.parts.findIndex((p) => p.kind === 'canticle' && p.canticleId === 'magnificat');
|
const magnificatIndex = ordo.parts.findIndex((p) => p.kind === 'canticle' && p.canticleId === 'magnificat');
|
||||||
const collectIndex = ordo.parts.findIndex((p) => p.kind === 'prayer');
|
const collectIndex = ordo.parts.findIndex((p) => p.kind === 'prayer');
|
||||||
@@ -424,7 +446,7 @@ describe('resolveOrdo("vespers", ...)', () => {
|
|||||||
// dropped from this app's own text (2026-08) — 'Ego dixi' is the next
|
// 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
|
// versicle after where the psalm used to sit, still unique to the
|
||||||
// fuller ferial form vs. the short litany.
|
// fuller ferial form vs. the short litany.
|
||||||
const ordo = resolveOrdo('vespers', '2026-06-16');
|
const ordo = resolveOrdo('vespers', '2033-09-06');
|
||||||
const preces = ordo.parts.find((p) => p.kind === 'preces' && p.text.text.la?.includes('Kýrie, eléison'));
|
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('Ego dixi: Dómine, miserére mei');
|
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');
|
expect(preces?.kind === 'preces' ? preces.text.text.en : undefined).toContain('I said: Lord, be merciful');
|
||||||
|
|||||||
Reference in New Issue
Block a user