Commit Graph

6 Commits

Author SHA1 Message Date
will 397f31f4fc Implement octave-vs-octave precedence, and fix a real day-label bug it surfaced
Deploy / deploy (push) Successful in 52s
When more than one octave is active on a day with no temporal standing of
its own (St. Lawrence's and the Assumption's genuinely overlap every Aug
16-17), which one governs the day's content/label is now decided by rank
comparison instead of "whichever started first" (an accident of
insertion order). Per direct instruction:

- Highest effective rank wins outright; every other active octave still
  gets commemorated, same as a single octave already outranking a weak
  rival saint.
- A rank tie goes to whichever octave started more recently -- day 1 of
  a new octave needs to be fully present, the whole point of it
  starting. No real tied-rank case exists yet to verify this
  empirically, unlike everything else here -- documented in TODO.md as
  a stated decision, not a live finding.

New OctaveConfig.closingDayRank (default duplex): an octave's own final
day ("in Octava") is elevated above its ordinary in-between rank --
live-verified as a real, general pattern (both St. Lawrence's Aug 17 and
the Assumption's Aug 22 show as Duplex, above their otherwise-Semiduplex
ordinary days), not a one-off. This elevation is *why* Lawrence's octave
beats the Assumption's on their one real overlap day despite the
Assumption being the far higher-ranked feast overall. Feeds both the
existing rival-saint threshold and the new octave-vs-octave comparison.

calendar/octaves.ts gains resolveActiveOctave (+ pickWinningOctave, the
comparison itself factored out for direct unit testing against synthetic
data, since no real tied-rank overlap exists to test against yet).
hours/resolve-common.ts's resolveOfficeWinner and calendar/day-label.ts
both now call it instead of each keeping their own "activeOctavesFor(...)
[0]" logic.

That consolidation surfaced a real, independent bug: getDayLabel never
checked temporalCategory at all before choosing an octave name, unlike
resolveOfficeWinner -- found while testing the real Aug 16 overlap (a
Sunday that year, where the temporal Sunday has standing and should win
outright). Live-verified counterexample: the Christmas Octave's own
stack (Dec 30) was wrongly labeled "3rd Day within the Octave of The Holy
Innocents" instead of the correct plain temporal label -- the real title
never names any of the four stacked octaves there. Fixed by sharing the
exact same ordinary-feria gate resolveOfficeWinner already had, so the
two can no longer disagree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 09:38:06 -04:00
will 5f4cf33776 Add per-feast overrides to Prime/Terce/Sext/None; fill Lauds' own Lawrence office bundle
Deploy / deploy (push) Successful in 51s
Prime/Terce/Sext/None had no per-feast override mechanism at all before
this -- their psalm antiphon always fell back to the plain weekday
default and their chapter was always a single fixed per-annum/per-weekday
text, regardless of what was actually being celebrated, even on a
saint's own actual feast day (not just during an octave). Confirmed live
(Monastic Tridentinum 1617) that St. Lawrence has his own real antiphon
and chapter at every one of these hours, both on his own day and
throughout his octave.

New shared mechanism in hours/resolve-common.ts: getMinorHourOverrideId
(duplex-majus+ threshold, kept in sync with Lauds' own so it's one
shared backlog), resolveMinorHourAntiphon, resolveMinorHourChapter --
all resolveOfficeWinner-aware, so an octave day gets the same override
as the feast's own actual day. Prime additionally needed
isSundayOrFeastOffice (its capitulum/Preces Sunday-vs-ferial choice)
made octave-aware -- live-verified Prime's capitulum stays the
Sunday/feast form throughout the octave too, so no new file was needed
there, just reaching the existing one correctly.

Also fills in Lauds' own still-missing Lawrence office bundle
(capitulum/responsory/hymn/versicle) -- flagged in TODO.md as worth
fixing "even before tackling the other 32" and still not done until now.

Authored real content for St. Lawrence across all five hours from live
queries. The same ~31-saint backlog (TODO.md) now applies uniformly
across Lauds and the four hours fixed here.

Also documents a real, found-not-fixed gap: when two octaves overlap
with no temporal standing to yield to (St. Lawrence's and the
Assumption's genuinely overlap every Aug 16-17), this app currently
picks whichever started first, which happens to match the live engine
on Aug 17 (Lawrence's own elevated final "Octave Day" outranks the
Assumption's ordinary octave-day strength) but only by coincidence, not
a real rank comparison -- proposed design written up in TODO.md pending
a decision on scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 08:05:08 -04:00
will 143244203d Fix octave-day office content: use the octave's own feast, not the plain ferial
Deploy / deploy (push) Successful in 52s
Real, multi-part bug found by looking closely at today's rendering (day 3
of St. Lawrence's octave, St. Clare commemorated): the psalmody
antiphons, hymn, chapter/responsory/versicle, Benedictus antiphon, and
day collect were all keyed off `day.winner` directly, which stays the
plain temporal identity (post-pentecost-11) throughout an octave --
St. Lawrence's own already-authored psalmody override and collect never
engaged on any day but his actual feast day. Live-verified (Tridentine
1910, 2026-08-12) that the whole office on an ordinary octave day
actually comes from the octave's own feast ("{ex Commune aut Festo}" /
"{ex Proprio Sanctorum}"), with a *weaker* commemorated saint (Clare)
getting a separate Ant+V/R+collect block of her own alongside it.

Fixed with a new resolveOfficeWinner(day) in hours/resolve-common.ts,
used everywhere getDayCollect/getDayCollects/getBenedictusAntiphon/
getPsalmodyOverrideFor used to read day.winner directly. Deliberately
gated on temporalCategory === 'ordinary-feria' (the day has no standing
of its own) rather than "any active octave" -- the Christmas Octave's
own stacked octaves are the live-verified counterexample where the
*temporal* day keeps the office instead, each octave becoming its own
separate commemoration block (not yet modeled, flagged in TODO.md).

Also fixes the ferial Preces gate (added in the previous commit) to
exclude active octaves the same way the suffrages already do --
live-verified "Preces Feriales{omittitur}" on the same date.

getDayCollects now renders a sanctoral commemoration as a real
Ant+V/R+collect bundle when authored (${propers}-commemoration.yml),
not just a bare, unlabeled collect -- the old rendering was indeed the
confusing "PRAYER / (translation pending)" block spotted today, which
was St. Clare's placeholder with no indication whose it was or why.
Authored her real commemoration bundle and standalone collect from the
same live query rather than leaving it pending.

One correction to a guess floated mid-session: the Benedictus antiphon
does NOT go to the commemorated saint (Clare) -- live-verified it stays
with the octave's own feast (Lawrence's "In cratícula"), same as
everything else in the primary office; only the separate commemoration
block is Clare's.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 07:44:39 -04:00
will 5830e4cfd2 hours: build Lauds
Deploy / deploy (push) Successful in 48s
Opening versicle; Ps 66 (fixed, no antiphon at all); the weekday-
variable psalm groups (Ps 50 plus 1-2 more, framed by their own
antiphon each, wholesale-substituted by Sunday's Ps 50+117-share-one-
antiphon shape); the weekday OT canticle; the Laudate psalms (148-150)
under one more shared antiphon; the weekday chapter/responsory/hymn/
versicle bundle; the Benedictus (framed by a day-resolved antiphon,
same incipit-or-full/full-repeat shape as Compline's Nunc Dimittis);
the Kyrie/Pater-noster short litany; the day's collect(s) — winner
plus one per commemoration, via the new getDayCollects; the four
Tridentine suffrages (omitted on a Double-or-higher feast); the
conclusio; the closing versicle (the silent Our Father that precedes
it isn't re-rendered, its text having already appeared once at the
short litany); and the closing Marian antiphon, reusing Compline's own
mechanism exactly.

Known, deliberate gap, not yet fixed: the psalmody doesn't model the
Commune/feast override real practice applies on a Semiduplex-or-higher
day (substituting an entirely different psalm+canticle set) — every
day currently gets the plain ferial weekday distribution regardless of
who wins. That's a separate, larger propers-authoring task (a full
Common-of-Saints psalm set per Common).

Also a simplification in getDayCollects: each collect in a multi-
collect day renders as its own complete "Let us pray ... Amen." block,
rather than the more compressed real form (one "Let us pray," only the
last collect's doxology).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 11:45:25 -04:00
will 81440de832 hours: add the day's-collect resolver
Terce/Sext/None's Oratio is the day's own Proper-of-season/-saints
collect, not a fixed text — getDayCollect() resolves it from the
LiturgicalDay's winner (temporal id or sanctoral saint's propers),
falling through to an honest "missing" status for saints that don't
have authored propers yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 13:50:24 -04:00
will ec448bc35b compline: build the full hour, sharing Prime's resolver helpers
Deploy / deploy (push) Successful in 38s
Ordo is Monastic 1617 as a base, with deliberate Tridentine 1906
additions: the "In manus tuas" short responsory, the Nunc Dimittis
with its "Salva nos" antiphon, the fuller Preces (Monastic's own is
shorter), and Lent's "Christe, qui lux es et dies" hymn replacement
(English translation marked draft, pending a real historical one).
Psalms 4, 90, 133 are fixed daily with real verified text, unlike
Prime's weekday-rotated psalms.

The closing Marian antiphon now picks Alma Redemptoris Mater / Ave
Regina Caelorum / Regina Caeli / Salve Regina by real season, using
the calendar work from the previous commit — except Ave Regina
Caelorum's own window (Candlemas through the day before Maundy
Thursday) doesn't align to any single season value, so that one case
checks the real date directly instead of going through the by-season
table.

resolve-common.ts extracts the resolve/status/doxology/antiphon
helpers Prime already had into something Compline can share; a few
propers files lose their "prime-" prefix now that both hours use them.
2026-08-10 05:27:40 -04:00