Kyrie eleison and Pater Noster now render every day regardless of rank
(minor-hour-kyrie-pater); the further Preces V/R versicle pairs are
ferial-only, dropped on Sunday and any sanctoral winner.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
compline-preces.yml was already the secular Tridentine 1906/1910 text
word-for-word (verified live against both tracks), so no text change
was needed there. The actual gap: both Monastic and secular tracks drop
Compline's Preces to nothing on a Double-or-higher, unlike Vespers/Lauds
which fall back to a short form. Per explicit request, this is now a
deliberate vu-specific deviation from both tracks: on a Double, Compline
substitutes the same short Kyrie/Pater-Noster litany Vespers already
falls back to on Sundays/feasts, instead of showing nothing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AHXJAWVAabCKX1JgmDh4Rn
Replaces the lesson+isGospel flag with a dedicated 'gospel' ResolvedPart
carrying an optional nested homily. Previously a Gospel pericope and its
patristic homily were two separate pool entries, which the front-light
1/1/rest nocturn slicer could split into different nocturns purely by
index. buildReadingPool now folds an isGospel:true reading and its
immediately-following same-nocturn homily into a single pool entry, so
the pairing is structural rather than a convention the slicer has to
honor. hour-view.ts renders the pair as one visually grouped section.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D1LqodVbQdQVoTGPz9sSw4
Vespers had no suffrages (Holy Cross, BVM, Joseph, Apostles, Peace)
at all -- types.ts even documented it as Lauds-only. Live-verified
against the reference engine (Monastic Tridentinum 1617): the
"Suffragium" block after the day's collect(s) renders byte-identical
after both Lauds and Vespers, so this was an honest content gap, not
a deliberate omission.
Extracted the suffrage ids/labels and full omission logic (Double-or-
higher, Christ the King, Advent/Christmastide/Passiontide, active
octaves, Cross/Marian-Saturday special cases) out of lauds.ts into a
shared resolveSuffrages() in resolve-common.ts, and wired it into
vespers.ts/vespers.yml the same way Lauds already uses it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
isSundayOrFeastOffice treats any sanctoral winner, Vigils included, as a
"feast," so a Vigil day was getting the Sunday/feast Preces. Add a
by-day-kind 'vigilIsFerial' flag, set only on Prime's Preces part (its
capitulum keeps the old behavior), so a Vigil-ranked winner now routes
to the ferial Preces text instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HWN869GrMFHqrer9fdCBbF
The heading above each Matins octave reading was a bare Latin string
(e.g. "Sermo sancti Bernárdi Abbátis") rendered as-is in both the
Latin and English columns, since the app shows both languages side
by side rather than toggling. Widen OctaveReadingText.source and the
lesson part's label to carry per-language text, and render it as its
own lang-columns row like the body text beneath it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3wxvz3mPXiHxkPB5JpnmD
HOUR_IDS drove the nav's render order but was declared arbitrarily;
reorder to Matins, Lauds, Prime, Terce, Sext, None, Vespers, Compline
so the nav reads left-to-right the way the office actually unfolds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Benedictus, Magnificat, and Nunc Dimittis had the Gloria Patri baked
verbatim into their propers YAML with no Triduum suppression; Matins'
Nocturn 3 OT canticles had none at all. Give the 'canticle' ResolvedPart
the same centrally-attached gloriaPatri field psalms already got, strip
the baked-in text, and drop Lauds' now-redundant local append/Triduum
check — live-verified that canticles follow the exact same Triduum-only
omission as psalms (e.g. Benedictus at Lauds of Holy Thursday).
Psalms across every hour rendered with no closing Gloria Patri at all.
Add it centrally (hours/index.ts) rather than per-hour, using a new
calendar/temporal.ts isInTriduum helper so it's correctly dropped from
Maundy Thursday through Holy Saturday and nowhere else — live-verified
against both of this app's actual source tracks (Divino Afflatu 1954
and Tridentine 1906), not just the modern Rubrics 1960 default. Also
fixes Lauds' existing but Triduum-blind canticle Gloria Patri append.
Adds a section-heading ResolvedPart kind and emits "Invitatory" and
"Nocturn I/II/III" headings in matins.ts, so the hour's structural
boundaries are visible instead of rendering as one undifferentiated
stream of parts. Ferial (1-nocturn) days get no Nocturn heading, matching
their existing single-unnumbered-nocturn design.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Builds every day label as a {en, la} pair (weekdays, ranks, season/ordinal
phrasing authored in Latin; proper names without an authored Latin form
fall back to their English string) and merges the date+day-label into one
header in the day-nav bar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New ResolvedPart kinds (nocturn-psalmody, te-deum) and lesson fields
(nocturn, source, isGospel, responsory) in hours/types.ts. Three new
content-store modules: propers/bible-plan.ts (the user's own continuous
scripture-reading plan, replacing the historical Nocturn-1 lectionary),
propers/nocturn-readings.ts (general feast/temporal-day Nocturn 2-3
readings, extending propers/octave-readings.ts's combine-into-one-reading
pattern via a newly-exported resolvePassages), and
propers/matins-responsories.ts (a per-book responsory pool, matched
loosely rather than by exact citation).
Data: Sunday's fixed 12-psalm/3-canticle psalmody (live-verified against
the reference engine), the invitatory antiphon, ferial hymn and
capitulum, the Te Deum, a seeded Isaiah responsory, two live-transcribed
scripture chapters, and the two bible-plan rows needed for this pass'
proof content.
Monastic 1617 has no ferial Preces at Vespers, same gap Lauds already
had — the fuller "Preces feriales Vespera" is byte-for-byte the Lauds
ferial Preces with Psalm 129 swapped for Psalm 50 in the source, live-
verified against the same clean Advent feria already used for Lauds.
The short Sunday/feast form turns out to be identical at both hours too,
so it's reused directly rather than duplicated.
Factors the shared ferial-or-vigil predicate out of hours/lauds.ts into
resolve-common.ts's isFerialOrVigil so both hours' -preces cases resolve
it the same way instead of keeping two copies in sync by hand.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wires resolveOrdo('vespers', ...) end to end: opening versicle,
weekday psalmody (4 groups, handling the joined Ps 115+116 and the
split Ps 138/143 via a PsalmRef-like shape), the chapter/responsory/
hymn/versicle office bundle, Magnificat, day-collects — reusing
resolveEveningDay so First/Second Vespers precedence and the cross-day
commemoration (both added in the previous commit) apply automatically
— and a closing that reuses Lauds' own conclusio, since no distinct
"Vespera" version of that exact text turned up in the source.
getMagnificatAntiphon mirrors getBenedictusAntiphon's saint/temporal-id
lookup order, but also falls back to real content Benedictus has no
equivalent of: the classic ferial set that quotes the Magnificat's own
text in sequence across the week, with the correct Monastic-rubric
variant picked for Tue/Thu/Fri/Sat (two of those needed a fresh
translation where the Monastic Latin diverges from the given English
in content, not just wording).
Also includes Vespers' own seasonal content (Advent/Lent/Passiontide/
Paschaltide chapter+responsory+hymn+versicle), transcribed from the
same source blocks as the weekday content, with the Monastic-specific
hymn variants hand-applied from the source's substitution notation
against each base Roman text (one transcription slip caught and fixed
mid-pass: the Passiontide hymn's closing lines had been copied from
the Roman text instead of the Monastic one actually being
transcribed). Passiontide's own responsory/hymn correctly omit the
Gloria Patri, matching real rubric and the source data as given.
All weekday/seasonal content marked `draft`, not `verified` — read
directly from the reference source, not cross-checked against a live
rendering per day the way the psalmody was.
Six other hours' resolvePart switches needed the three new HourPart
kinds ('vespers-psalmody', 'vespers-office', 'magnificat') added to
their exhaustive not-used lists so they still typecheck; three
pre-existing tests assumed Vespers was still "coming soon" and are
updated to exercise Matins (still the genuine stub) instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Monastic 1617 has no Preces feriales at Lauds at all -- content sourced
from Tridentine 1906/1910 per direct instruction, same track as the
Cross suffrage and St. Joseph's suffrage. New lauds-preces-feriales.yml
(Kyrie, Pater noster, the full versicle litany, Psalm 129, closing
versicles) transcribed from a live query against a clean Advent feria,
matching what actually rendered rather than the underlying template's
own conditional markup (the Pope/Bishop and benefactors' versicles are
both dropped live despite being present in the template; the King/nation
versicle pair is kept despite the template's own rubric note suggesting
otherwise).
New `lauds-preces` part kind (hours/types.ts) picks between this and the
existing short Sunday/feast litany (lauds-short-litany.yml, unchanged
content) on a ferial-or-vigil day. Deliberately not the shared
`isSundayOrFeast` predicate, which treats a Vigil as a "feast" -- here a
Vigil needs to land on the ferial side instead, per direct instruction.
Also deliberately wider than the real 1906/1910 rubric itself, which
restricts the fuller form to Advent/Lent/Ember days specifically -- shown
on *every* ferial or vigil day instead, mirroring the identical explicit
choice already made for Prime's own ferial Preces.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
On a free Saturday (nothing privileged already claims it, no active
octave), the day's own identity is now "Our Lady's Saturday" rather
than an anonymous ordinary-feria -- mirrors privileged-feria-minor
exactly, per direct instruction: Semiduplex-or-higher still wins
outright (Marian Saturday doesn't apply at all); below that (Simplex,
and Vigil, both under semiduplex on the FeastClass scale) loses and is
commemorated instead. Layered additively after decideOccurrence, same
shape as applyOctaves -- never touches decideOccurrence's own rules.
Needed a real winner identity (not just the plain temporal feria id)
so day-label/antiphon/collect sourcing can recognize it -- added a
temporal-feasts record purely for that, and taught getDayLabel to
check it for any named temporal winner (a small, free improvement for
Christmas/Pentecost's own labels too, previously unhandled).
hours: build the duplex-majus+ Lauds psalmody override (per-feast)
lauds-psalmody no longer unconditionally uses the plain weekday
default: a duplex-majus-or-higher sanctoral winner, or Our Lady's
Saturday (unconditional, no rank threshold -- it isn't competing with
the weekday default the way a saint is), now substitutes its own
proper psalm groups/antiphons/canticle. Per-feast, not per-Common, per
direct instruction, even though most duplex-majus+ saints across the
year don't have one authored yet and fall back to the plain weekday
default until they do.
Content for the worked example (St. Lawrence's own day) surfaced a
real bug while sourcing it: st-lawrence-antiphon.yml had been read
from the Roman/secular rite's own [Ant 1], not Monastic 1617's actual
Benedictus antiphon for that day -- corrected from a fresh live query.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
Three separate resolver files (terce.ts/sext.ts/none.ts), not a shared
little-hour module — they're structurally identical but kept independent
by choice. Ordo verified against Divinum Officium (Monastic Tridentinum
1617): opening versicle, self-contained hymn, one antiphon framing three
psalms (no closing repeat, unlike Prime), a capitulum with its own
embedded short versicle/response, the day's own collect, and a closing
identical across all three hours.
Also fills in psalter-distribution.yml's previously-missing Sunday/
Monday/Tuesday psalm entries (Ps 118 sections for Sunday/Monday; Tuesday
turns out to share Wednesday/Thursday's native gradual-psalm set, not a
distinct one). Friday/Saturday's antiphons are derived from each day's
first psalm rather than reusing the native gradual antiphons, since
those two days already use a redistributed, non-native psalm set.
hours/types.ts gains a 'day-collect' part kind — the first ordo to use
the day's own Proper collect rather than a fixed text.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a real label under each hour's heading — "Monday in the 10th week
after Trinity", "The 1st Sunday of Advent" — computed from whichever
LiturgicalDay the hour actually resolved against, which can differ from
the nav date for Compline's evening anticipation.
calendar/day-label.ts combines two things: an ordinal week-within-season
label (pure date arithmetic on the season anchors from calendar/temporal.ts
and calendar/easter.ts — Trinity-counted, not Divinum Officium's own
Pentecost-counted convention; meant to become configurable later via the
same day->id indirection already used for the sanctoral calendar, not
hardcoded forever), and a feast name from calendar/commemorations.ts's
occurrence decision — shown alone if the feast displaces the day outright,
prefixed onto the temporal label if merely commemorated, or omitted
entirely if nothing's occurring.
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.
Pulls in verified content from Divinum Officium rather than placeholders:
17 psalms (2, 6, 7-19, 118, 129), 365 days of the Martyrology, and the full
121-reading Regula cycle, plus the Athanasian Creed.
Ordo corrections driven by review against the real engine output:
- Capitulum and Preces now pick a Sunday/feast vs. ferial form
(calendar/isSundayOrFeast); ferial Preces said every ferial day by choice.
- Chapter responsory, hymn doxology, and the opening versicle's
Alleluia/Laus tibi all vary by season via a shared resolver
(hours/seasonal-propers.ts).
- Real Roman Kalends/Nones/Ides Latin dating (calendar/roman-date.ts,
verified against 363/365 real Martyrology headings) plus the historical
"bis sextus" Feb 29 handling, and the Martyrology's Luna (moon-day)
heading (a ported Golden-Number calculation).
- Fixed responsory structure (was missing its initial full repeat),
weekday psalm antiphons (opening as incipit-or-full by rank, full
repeat after the psalms/Creed as its own part, "*" chant mark kept),
scripture citations on the capitulum/lectio brevis, and V./R. markers
switched from Unicode symbols to plain text for reliable font rendering.
- Dropped Pretiosa and the dead-commemoration psalm (129) for time;
trimmed section headings down to the ones that are actually named
things (Preces, Chapter Office, etc. no longer relabel connective text).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Client-side-first PWA (Vite/TS, no backend) per the approved plan: day-
navigable shell listing all 8 hours, Prime fully resolves via the
calendar -> psalter -> ordo pipeline, the other 7 hours are registered
but flagged not-implemented. Sanctoral/temporal calendar data uses a
day -> id indirection layer (saints, easter-offsets, fixed-date-calendar)
so reassigning a feast to a different day is a data edit, not a code
change. Docker (Caddy-serving-static) + Gitea CI workflow scaffolded to
match the eec/drip/bookshop operational pattern.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>