Prime: real content (psalms, Martyrology, Regula) and ordo corrections
Deploy / deploy (push) Failing after 21s

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>
This commit is contained in:
2026-08-09 20:03:53 -04:00
parent a5dc6502d7
commit 58958aa847
571 changed files with 21842 additions and 72 deletions
+50 -2
View File
@@ -27,14 +27,51 @@ export interface PropersRef {
export type HourPart =
| { kind: 'hymn' | 'chapter' | 'responsory' | 'versicle' | 'prayer'; textRef: PropersRef }
| { kind: 'psalm'; psalmNumber: number; antiphonRef?: PropersRef }
// A fixed multi-line cluster of versicles/responses/prayers (Preces, the
// monastic "De Officio Capituli" mini-office, the dead-commemoration
// responsory/collect) — these don't decompose usefully into individual
// hymn/chapter/versicle parts, so they're stored and rendered as one block.
// `label`, when given, is shown as a heading (e.g. "Preces", "Pretiosa") —
// omitted for parts that are just connective tissue around a reading
// (a blessing, a closing dialogue) that don't read as their own section.
| { kind: 'preces'; textRef: PropersRef; label?: string }
| { kind: 'psalm'; psalmNumber: number; verses?: string; antiphonRef?: PropersRef }
| { kind: 'canticle'; canticleId: string; antiphonRef?: PropersRef }
| {
kind: 'lesson';
textRef: PropersRef;
label?: string;
nocturn?: number;
lessonSource?: 'historical' | 'continuous-plan';
}
// Resolved by date, not by textRef — see src/martyrology. Monastic Prime
// reads *tomorrow's* entry (the announcement of the next day's saints).
| { kind: 'martyrology' }
// Resolved by date via the day->canonical-reading table (RB is read
// through 3x/year) — see src/regula.
| { kind: 'rule-reading' }
// Chooses between two fixed texts by calendar/isSundayOrFeast — e.g.
// Prime's capitulum (1 Tim on Sunday/feasts, Zach on ferias). Renders as
// a plain 'chapter' or 'preces' ResolvedPart per resolvedKind.
| {
kind: 'by-day-kind';
resolvedKind: 'chapter' | 'preces';
label?: string;
sundayOrFeastRef: PropersRef;
ferialRef: PropersRef;
}
// The Athanasian Creed — included on Sundays that aren't a Double-or-higher
// feast (see calendar/isDoubleOrHigher and hours/prime.ts's 'creed' case
// for how far that's actually implemented today).
| { kind: 'creed' }
// Resolved by season — Allelúja normally, Laus tibi Septuagesima through
// Holy Saturday. See hours/opening-versicle.ts.
| { kind: 'opening-versicle' }
// The full antiphon, repeated after the whole psalm group (and the
// Athanasian Creed, on days it's said) — real practice says it once more
// in full here, distinct from the incipit-or-full opening attached to the
// first psalm. See hours/antiphon.ts.
| { kind: 'closing-antiphon' }
// Unused by Prime/Compline/the little hours — exercised starting milestone 4.
| { kind: 'variable'; resolve: 'by-weekday' | 'by-season' | 'by-feast-rank' };
@@ -46,8 +83,16 @@ export interface HourDefinition {
/** A part with its text actually filled in, ready for the UI to render. */
export type ResolvedPart =
| { kind: 'hymn' | 'chapter' | 'responsory' | 'versicle' | 'prayer'; text: ResolvedText }
| { kind: 'preces'; text: ResolvedText; label?: string }
| { kind: 'psalm'; psalmNumber: number; antiphon?: ResolvedText; verses: ResolvedVerse[] }
| { kind: 'canticle'; canticleId: string; antiphon?: ResolvedText };
| { kind: 'canticle'; canticleId: string; antiphon?: ResolvedText }
// A standalone antiphon, said on its own rather than framing a psalm —
// see 'closing-antiphon' above.
| { kind: 'antiphon'; text: ResolvedText }
// Martyrology and Regula both render as a block of prose text with a
// label (a fixed section name for Martyrology, a date-derived chapter
// title for Regula) rather than a fixed textRef.
| { kind: 'lesson'; text: ResolvedText; label?: string };
export interface ResolvedVerse {
n: number;
@@ -58,6 +103,9 @@ export interface ResolvedVerse {
export interface ResolvedText {
text: Partial<Record<string, string>>;
status: Partial<Record<string, 'verified' | 'draft' | 'missing'>>;
/** e.g. "1 Tim 1:17" — set when the text is a scripture quotation. Always
* given in full (book, chapter, verse) rather than just a book name. */
citation?: Partial<Record<string, string>>;
}
/**