Author Easter Sunday/Monday/Tuesday's shared proper Matins
Deploy / deploy (push) Successful in 1m29s
Deploy / deploy (push) Successful in 1m29s
Easter Sunday, Monday, and Tuesday of the Octave share winner {kind:
temporal, id: easter-sunday}, and now share one live-verified proper
Matins psalmody: Nocturn 1 (Ps 1,2,8,15,23,27 under one antiphon),
Nocturn 2 (Ps 29,63,65,75,87,107 under one antiphon), Nocturn 3 (the
same 3 canticles already authored for the ordinary-Paschaltide
seasonal override, under its own antiphon) — sourced from
Pofficium.pl (Monastic Tridentinum 1617, prayMatutinum) against
2026-04-05/06/07. All 3 nocturn-closing versicles are byte-identical
to the ones already authored for the Paschaltide override, confirming
those are shared season-wide rather than coincidentally reused. The
12 patristic lessons were already authored in an earlier sweep
(nocturn-readings/easter-sunday.yml) and needed no new work.
Restricted to just these 3 days via the new weekdays field: the
Octave's other 4 weekdays (Wed-Sat) share the same winner id but have
their own different, not-yet-authored antiphons, tracked as a
separate open item in TODO.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X26BkFT3ARbtUGXuBmzUmD
This commit is contained in:
@@ -19,12 +19,13 @@ across every hour and content type in this app.
|
|||||||
|
|
||||||
1. **Mechanism gaps** — a real design/code piece not built yet, blocking any content pass on
|
1. **Mechanism gaps** — a real design/code piece not built yet, blocking any content pass on
|
||||||
it from starting:
|
it from starting:
|
||||||
- ~~Easter's own octave~~ — DONE (2026-09-01): the transfer-cascade piece (a candidate
|
- ~~Easter's own octave~~ — DONE (2026-09-01/02): the transfer-cascade piece (calendar
|
||||||
impeded within Holy Week/Easter Day/the Octave/Low Sunday now correctly skips the whole
|
precedence within Holy Week/Easter Day/the Octave/Low Sunday) and Easter Sunday/Monday/
|
||||||
span and keeps walking forward, FIFO, until it lands) is built and live-verified — see
|
Tuesday's own proper Matins (psalms/antiphons/versicles/canticles, sharing one authored
|
||||||
the dated log's "Easter octave transfer cascade" entry. Content-wise, Easter Day/the
|
entry via a new general-purpose temporal-override mechanism) are both built and
|
||||||
Octave's own full Matins proper (distinct psalms, not just antiphons) is still not
|
live-verified — see the dated log's "Easter octave transfer cascade" and "Easter's own
|
||||||
modeled — a separate, narrower remaining gap, not this one.
|
octave" entries. What's left is narrower, moved to the bulk-content section below:
|
||||||
|
Wednesday-Saturday of the Octave's own (different, not yet authored) antiphons.
|
||||||
- Date picker: `day-nav.ts` currently only steps a day at a time (prev/next); there's no
|
- Date picker: `day-nav.ts` currently only steps a day at a time (prev/next); there's no
|
||||||
way to jump to an arbitrary date without walking there one day at a time or editing the
|
way to jump to an arbitrary date without walking there one day at a time or editing the
|
||||||
URL. Needs a calendar-grid picker UI — open, not started.
|
URL. Needs a calendar-grid picker UI — open, not started.
|
||||||
@@ -52,6 +53,11 @@ across every hour and content type in this app.
|
|||||||
nocturn-readings sweep" below, including the easter-sunday/pentecost-sunday correction).
|
nocturn-readings sweep" below, including the easter-sunday/pentecost-sunday correction).
|
||||||
Moved out of this "bulk content pending" section since there's no more bulk content left to
|
Moved out of this "bulk content pending" section since there's no more bulk content left to
|
||||||
author here.
|
author here.
|
||||||
|
- Wednesday-Saturday of the Easter Octave: real Paschal-alleluia antiphons exist in the
|
||||||
|
reference engine (live-verified 2026-04-08) over a differently-sized psalm set than vu's
|
||||||
|
own fixed ferial table — 4 days of antiphon-only content, same shape as the existing
|
||||||
|
Advent/Paschaltide seasonal-overlay pattern, layered onto vu's own psalm numbers rather
|
||||||
|
than the reference engine's. See the dated log's "Easter's own octave" entry.
|
||||||
- The bulk Vulgate (Latin) + Douay-Rheims (English) scripture import into
|
- The bulk Vulgate (Latin) + Douay-Rheims (English) scripture import into
|
||||||
`src/data/scripture/` — long-term project, explicitly deferred; Matins scripture
|
`src/data/scripture/` — long-term project, explicitly deferred; Matins scripture
|
||||||
citations resolve to `missing` text until it lands.
|
citations resolve to `missing` text until it lands.
|
||||||
@@ -4876,3 +4882,55 @@ Terce/Sext/None antiphons+versicles, Te Deum present.
|
|||||||
See memory `vu_matins_semiduplex_threshold` for full detail.
|
See memory `vu_matins_semiduplex_threshold` for full detail.
|
||||||
|
|
||||||
`npm test` (823 passed), `tsc --noEmit`, and `npm run build` all pass.
|
`npm test` (823 passed), `tsc --noEmit`, and `npm run build` all pass.
|
||||||
|
|
||||||
|
### Easter's own octave — Sunday/Monday/Tuesday proper Matins authored, general temporal-override mechanism added (2026-09-02)
|
||||||
|
|
||||||
|
Closes most of the long-tabled "Easter's own octave" mechanism gap (Priority section, item 1).
|
||||||
|
User pushed back on an initial overcomplicated read ("psalms with lections" looked like a new
|
||||||
|
rendering shape at first glance) — turned out, after live-verifying the actual structure, to
|
||||||
|
be exactly what the user guessed: a temporal override file that "looks weird" only because of
|
||||||
|
its content (13 psalms under one antiphon per nocturn, not the usual per-pair grouping), not
|
||||||
|
because it needs new mechanism. Every psalm already gets its own Gloria Patri centrally
|
||||||
|
(`hours/index.ts`'s `withGloriaPatri`) regardless of hour, and the 12 patristic lessons were
|
||||||
|
*already* authored (`data/propers/nocturn-readings/easter-sunday.yml`, from the earlier
|
||||||
|
temporal nocturn-readings sweep) — the only real gap was the psalmody/antiphon/versicle/
|
||||||
|
canticle layer.
|
||||||
|
|
||||||
|
**Content**: new `easter-sunday` entry in `matins-sunday-named-nocturn-overrides.yml` —
|
||||||
|
live-verified against the reference engine (`Pofficium.pl`, `prayMatutinum`, Monastic
|
||||||
|
Tridentinum 1617, 2026-04-05/06/07) — Nocturn 1: psalms 1,2,8,15,23,27 under one shared
|
||||||
|
antiphon ("Ego sum qui sum"); Nocturn 2: psalms 29,63,65,75,87,107 under one shared antiphon
|
||||||
|
("Terra trémuit"); Nocturn 3: the same 3 canticles already authored for the ordinary-
|
||||||
|
Paschaltide seasonal override (Isa 63:1-5, Osee 6:1-6, Soph 3:8-13), under its own antiphon
|
||||||
|
("Nolíte expavéscere"). All 3 nocturn-closing versicles are byte-identical to the ones
|
||||||
|
already authored in `matins-sunday-paschaltide-nocturn-overrides.yml`, confirming those are
|
||||||
|
shared season-wide rather than coincidentally reused.
|
||||||
|
|
||||||
|
**Mechanism (small, general-purpose, not Easter-specific)**: `hours/matins.ts`'s
|
||||||
|
`threeNocturns` gate and the Sunday-branch dispatch previously only ever fired for
|
||||||
|
`day.weekday === 'sunday'` or a sanctoral winner — a *temporal* winner had no path into 3-
|
||||||
|
nocturn proper content at all, even with a matching `sundayNamedOverrides` entry. Generalized:
|
||||||
|
a temporal winner with an authored named override is now eligible too (`hasTemporalNamedOverride`),
|
||||||
|
reusing the exact same `sundayPsalmNocturn`/`sundayCanticleNocturn` render functions the Sunday
|
||||||
|
and saint-proper branches already share — deliberately built as a reusable feature (user,
|
||||||
|
2026-09-02: "we want temporal override anyway... there are temporal feasts that could have
|
||||||
|
their own psalms/lessons"), not a one-off hack.
|
||||||
|
|
||||||
|
**The scoping wrinkle that made this non-trivial**: `winner.id` is `'easter-sunday'` for the
|
||||||
|
*entire* Octave (all 6 weekdays), but only Sunday/Monday/Tuesday are live-verified to share
|
||||||
|
this content — Wednesday-Saturday have their own different (Paschal-alleluia-flavored, not yet
|
||||||
|
authored) antiphons over a differently-shaped psalm set. A naive "any day with this winner.id"
|
||||||
|
check would have wrongly applied Sunday's content through Saturday. Fixed with a new optional
|
||||||
|
`weekdays` field on a named-override entry (`easter-sunday: weekdays: [sunday, monday,
|
||||||
|
tuesday]`) — omitted (as `sunday-after-ascension` still does) means "every day carrying this
|
||||||
|
temporalId," the common case for a temporalId that already maps to exactly one calendar day.
|
||||||
|
|
||||||
|
**Still open, a separate smaller gap**: Wednesday-Saturday of the Easter Octave — live-verified
|
||||||
|
(2026-04-08) to carry real Paschal-alleluia antiphons ("Jesum qui crucifíxus est quǽritis,
|
||||||
|
allelúja...") over a psalm set the reference engine sizes differently from vu's own
|
||||||
|
already-fixed 9-psalm ferial table (itself deliberate — see "No RB summer contraction" in
|
||||||
|
CLAUDE.md). 4 more days of antiphon-only content, same shape as the existing Advent/Paschaltide
|
||||||
|
seasonal-overlay pattern, layered onto vu's own ferial psalm numbers rather than trying to
|
||||||
|
reproduce the reference engine's differently-sized set — not started.
|
||||||
|
|
||||||
|
`npm test` (828 passed), `tsc --noEmit`, and `npm run build` all pass.
|
||||||
|
|||||||
@@ -7,6 +7,14 @@
|
|||||||
#
|
#
|
||||||
# hours/matins.ts's lookup checks this map first (most specific), ahead of
|
# hours/matins.ts's lookup checks this map first (most specific), ahead of
|
||||||
# the season-wide overrides, when `temporalId` matches a key here.
|
# the season-wide overrides, when `temporalId` matches a key here.
|
||||||
|
#
|
||||||
|
# `easter-sunday` (below) is also reused for Easter Monday and Tuesday
|
||||||
|
# (`hours/matins.ts`'s threeNocturns/Sunday-branch dispatch route any
|
||||||
|
# temporal winner with a matching entry here through the same rendering as
|
||||||
|
# a real Sunday, not just `day.weekday === 'sunday'` literally) — the
|
||||||
|
# reference engine's own content is byte-identical across all three days
|
||||||
|
# (live-verified 2026-09-02, 2026-04-05/06/07), so one entry covers all
|
||||||
|
# three rather than three separate near-duplicate ones.
|
||||||
|
|
||||||
# Sunday within the Octave of Ascension (`sunday-after-ascension`) —
|
# Sunday within the Octave of Ascension (`sunday-after-ascension`) —
|
||||||
# live-verified against the local reference-engine CGI instance
|
# live-verified against the local reference-engine CGI instance
|
||||||
@@ -32,6 +40,71 @@
|
|||||||
# translation of the underlying Mark 16:20 text (a well-known, unambiguous
|
# translation of the underlying Mark 16:20 text (a well-known, unambiguous
|
||||||
# formula, not a guess), consistent with how every other antiphon in this
|
# formula, not a guess), consistent with how every other antiphon in this
|
||||||
# file is sourced.
|
# file is sourced.
|
||||||
|
# Easter Sunday itself (and Monday/Tuesday of the Octave, which share this
|
||||||
|
# entry — see the file header above). Live-verified against the local
|
||||||
|
# reference-engine CGI instance (`prayMatutinum`, Monastic Tridentinum
|
||||||
|
# 1617, 2026-09-02, proof dates 2026-04-05/06/07). Unlike every other
|
||||||
|
# Sunday scheme in this file, each nocturn here uses ONE shared antiphon
|
||||||
|
# over ALL 6 of its psalms (not one antiphon per psalm, nor per pair) —
|
||||||
|
# confirmed by the live query: a single "Ant." before the first psalm and
|
||||||
|
# the same antiphon repeated in full after the last, nothing in between.
|
||||||
|
# Nocturn III reuses the same 3 canticles as the ordinary-Paschaltide
|
||||||
|
# override (Isa 63:1-5, Osee 6:1-6, Sophonias 3:8-13 — already authored,
|
||||||
|
# no new scripture files needed) under its own different antiphon; all 3
|
||||||
|
# nocturn-closing versicles are also byte-identical to the ones already
|
||||||
|
# authored in matins-sunday-paschaltide-nocturn-overrides.yml, confirming
|
||||||
|
# those are shared season-wide, not coincidentally reused.
|
||||||
|
#
|
||||||
|
# The reference engine's own [Rule] for this day actually reads "1
|
||||||
|
# nocturn" (the whole night office collapses to a single nocturn of 3
|
||||||
|
# lessons on this most privileged of feasts) — not modeled that way here;
|
||||||
|
# this app's own threeNocturns rule renders every Sunday with 3 nocturns
|
||||||
|
# unconditionally regardless of the historical single-nocturn rubric for
|
||||||
|
# this particular feast (same convention already noted in
|
||||||
|
# nocturn-readings/easter-sunday.yml's own header for the reading side).
|
||||||
|
#
|
||||||
|
# Explicitly NOT covered by this entry: Wednesday-Saturday of the Octave
|
||||||
|
# have their own real Paschal-alleluia antiphons layered over (in the
|
||||||
|
# reference engine) a differently-sized psalm set — live-verified present
|
||||||
|
# (2026-04-08, Wednesday) but not authored here. A separate, smaller gap
|
||||||
|
# (4 more days of antiphon-only content, analogous to the existing Advent/
|
||||||
|
# Paschaltide seasonal-overlay shape) — see TODO.md.
|
||||||
|
easter-sunday:
|
||||||
|
# winner.id 'easter-sunday' spans the whole Octave (all 6 weekdays), but
|
||||||
|
# only these 3 calendar days are live-verified to share this content —
|
||||||
|
# restricting the entry so Wednesday-Saturday keep falling through to
|
||||||
|
# their own (not yet authored) path instead of wrongly reusing this one.
|
||||||
|
weekdays: [sunday, monday, tuesday]
|
||||||
|
nocturn1:
|
||||||
|
groups:
|
||||||
|
- psalms: [1, 2, 8, 15, 23, 27]
|
||||||
|
antiphon:
|
||||||
|
la: "Ego sum qui sum, * et consílium meum non est cum ímpiis, sed in lege Dómini volúntas mea est, allelúja."
|
||||||
|
en: "I am who I am, * and my counsel is not with the ungodly, my will is in the law of God, alleluia."
|
||||||
|
versicle:
|
||||||
|
v: { la: "Surréxit Dóminus de sepúlcro, allelúja.", en: "The Lord is risen from the sepulchre, alleluia." }
|
||||||
|
r: { la: "Qui pro nobis pepéndit in ligno, allelúja.", en: "Who for us was hanged on the tree, alleluia." }
|
||||||
|
nocturn2:
|
||||||
|
groups:
|
||||||
|
- psalms: [29, 63, 65, 75, 87, 107]
|
||||||
|
antiphon:
|
||||||
|
la: "Terra * trémuit et quiévit, † dum exsúrgeret in judício Deus, allelúja."
|
||||||
|
en: "The earth * trembled and was still, when God arose to judgment, alleluia."
|
||||||
|
versicle:
|
||||||
|
v: { la: "Surréxit Dóminus vere, allelúja.", en: "The Lord is risen indeed, alleluia." }
|
||||||
|
r: { la: "Et appáruit Simóni, allelúja.", en: "And hath appeared to Simon, alleluia." }
|
||||||
|
nocturn3:
|
||||||
|
canticles:
|
||||||
|
- refs: [{ book: isa, chapter: 63, verses: "1-5" }]
|
||||||
|
- refs: [{ book: osee, chapter: 6, verses: "1-6" }]
|
||||||
|
- refs: [{ book: soph, chapter: 3, verses: "8-13" }]
|
||||||
|
antiphon:
|
||||||
|
la: "Nolíte expavéscere: * Jesum quǽritis Nazarénum, crucifíxum: surréxit, non est hic, allelúja."
|
||||||
|
en: "Be not affrighted; * you seek Jesus of Nazareth, who was crucified: he is risen, he is not here, alleluia."
|
||||||
|
versicle:
|
||||||
|
v: { la: "Gavísi sunt discípuli, allelúja.", en: "The disciples were glad, alleluia." }
|
||||||
|
r: { la: "Viso Dómino, allelúja.", en: "When they saw the Lord, alleluia." }
|
||||||
|
|
||||||
sunday-after-ascension:
|
sunday-after-ascension:
|
||||||
nocturn1:
|
nocturn1:
|
||||||
groups:
|
groups:
|
||||||
|
|||||||
@@ -360,6 +360,54 @@ describe('resolveOrdo("matins", ...) Sunday within the Octave of Ascension (2026
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resolveOrdo("matins", ...) Easter Sunday/Monday/Tuesday shared proper Matins (2026-09-02 fix)', () => {
|
||||||
|
// All 6 weekdays of the Easter Octave share winner {kind: 'temporal', id:
|
||||||
|
// 'easter-sunday'} -- only Sunday/Monday/Tuesday are live-verified to
|
||||||
|
// share this exact proper content, restricted via the override's own
|
||||||
|
// `weekdays` field (see matins-sunday-named-nocturn-overrides.yml).
|
||||||
|
const EASTER_WEEK_2026 = ['2026-04-05', '2026-04-06', '2026-04-07'] as const;
|
||||||
|
|
||||||
|
it.each(EASTER_WEEK_2026)('%s shares the identical 13-psalm proper scheme, 3 nocturns, Te Deum', (date) => {
|
||||||
|
const day = resolveDay(date);
|
||||||
|
expect(day.winner).toEqual({ kind: 'temporal', id: 'easter-sunday' });
|
||||||
|
const ordo = resolveOrdo('matins', date);
|
||||||
|
const psalms = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p as { psalmNumber: number }).psalmNumber);
|
||||||
|
expect(psalms).toEqual([3, 94, 1, 2, 8, 15, 23, 27, 29, 63, 65, 75, 87, 107]);
|
||||||
|
expect(ordo.parts.some((p) => p.kind === 'te-deum')).toBe(true);
|
||||||
|
|
||||||
|
const canticles = ordo.parts.filter((p) => p.kind === 'canticle') as { canticleId: string }[];
|
||||||
|
expect(canticles.map((c) => c.canticleId)).toEqual(['isa-63-1-5', 'osee-6-1-6', 'soph-3-8-13']);
|
||||||
|
|
||||||
|
const antiphons = ordo.parts.filter((p) => p.kind === 'antiphon') as { text: { text: Record<string, string> } }[];
|
||||||
|
expect(antiphons[1]?.text.text.la).toBe('Ant. Ego sum qui sum, * et consílium meum non est cum ímpiis, sed in lege Dómini volúntas mea est, allelúja.');
|
||||||
|
expect(antiphons.at(-1)?.text.text.la).toBe(
|
||||||
|
'Ant. Nolíte expavéscere: * Jesum quǽritis Nazarénum, crucifíxum: surréxit, non est hic, allelúja.',
|
||||||
|
);
|
||||||
|
|
||||||
|
const versicles = ordo.parts.filter((p) => p.kind === 'versicle') as { text: { text: Record<string, string> } }[];
|
||||||
|
const [, , nocturn1, nocturn2, nocturn3] = versicles;
|
||||||
|
expect(nocturn1?.text.text.la).toBe('V. Surréxit Dóminus de sepúlcro, allelúja.\nR. Qui pro nobis pepéndit in ligno, allelúja.');
|
||||||
|
expect(nocturn2?.text.text.la).toBe('V. Surréxit Dóminus vere, allelúja.\nR. Et appáruit Simóni, allelúja.');
|
||||||
|
expect(nocturn3?.text.text.la).toBe('V. Gavísi sunt discípuli, allelúja.\nR. Viso Dómino, allelúja.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Wednesday-Saturday of the Octave (same winner.id, excluded by `weekdays`) keep the plain 1-nocturn ferial branch unchanged', () => {
|
||||||
|
const ordo = resolveOrdo('matins', '2026-04-08'); // Wednesday of Easter week
|
||||||
|
const day = resolveDay('2026-04-08');
|
||||||
|
expect(day.winner).toEqual({ kind: 'temporal', id: 'easter-sunday' });
|
||||||
|
const psalms = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p as { psalmNumber: number }).psalmNumber);
|
||||||
|
// Plain ferial Wednesday table, same as any other Wednesday.
|
||||||
|
expect(psalms.slice(2)).toEqual(getPsalmsFor('matins', 'wednesday').map((r) => r.number));
|
||||||
|
expect(ordo.parts.some((p) => p.kind === 'te-deum')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Low Sunday (a different temporalId) is unaffected, still the plain fixed Sunday scheme', () => {
|
||||||
|
const ordo = resolveOrdo('matins', '2026-04-12');
|
||||||
|
const psalms = ordo.parts.filter((p) => p.kind === 'psalm').map((p) => (p as { psalmNumber: number }).psalmNumber);
|
||||||
|
expect(psalms).toEqual([3, 94, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('resolveOrdo("matins", ...) Duplex+ weekday-feast (3-nocturn, non-Sunday) branch', () => {
|
describe('resolveOrdo("matins", ...) Duplex+ weekday-feast (3-nocturn, non-Sunday) branch', () => {
|
||||||
// 2026-08-17 -- St. Lawrence's own octave closing day ("In Octava S.
|
// 2026-08-17 -- St. Lawrence's own octave closing day ("In Octava S.
|
||||||
// Laurentii Martyris ~ Duplex"), live-verified directly against the
|
// Laurentii Martyris ~ Duplex"), live-verified directly against the
|
||||||
|
|||||||
Reference in New Issue
Block a user