Commit Graph

132 Commits

Author SHA1 Message Date
will 5ec3394644 Import the Purification's own proper Lauds/Vespers chapter
Deploy / deploy (push) Successful in 1m18s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2026-02-02 (fixed date, always winnable): only the chapter is genuinely
her own (Mal 3:1, "Ecce ego mitto Angelum meum"). Responsory, both
hymns, and both versicles are all byte-identical to Common-of-the-BVM's
own already-authored content -- duplicated under her own id.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:20:13 -04:00
will 545c11afe1 Import the Annunciation's own proper Lauds/Vespers content
Deploy / deploy (push) Successful in 1m19s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2026-03-25 (fixed date, always winnable). Own chapter (Isa 7:14-15,
"Ecce virgo concípiet") and own versicle ("Ave, María, grátia plena")
at both hours, own Vespers responsory ("Ángelus Dómini * Nuntiávit
Maríæ," the opening of the Angelus). Lauds responsory and both hours'
hymns turn out byte-identical to Common-of-the-BVM's own already-
authored content -- reused verbatim.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:18:06 -04:00
will 9ce89c0312 Import St. Agnes's own proper Lauds/Vespers chapter
Deploy / deploy (push) Successful in 1m18s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2029-01-21: byte-identical to St. Agatha's own override (Sir 51:1-3).
Confirmed a real, saint-specific shared override between these two
ancient Roman virgin martyrs specifically, not a general Virgin-Martyr
rank-tier variant -- St. Lucy (also a Canon-of-the-Mass virgin martyr)
uses the plain Common chapter on her own clean day. Responsory, hymn,
and versicle reuse Common-of-a-Virgin-Martyr's own content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:14:12 -04:00
will 186ce63303 Import St. Agatha's own proper Lauds/Vespers chapter
Deploy / deploy (push) Successful in 1m18s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2026-02-05: only the chapter is genuinely her own (Sir 51:1-3,
"Confitébor tibi, Dómine, Rex"). Responsory, hymn, and versicle are all
byte-identical to Common-of-a-Virgin-Martyr's own already-authored
content -- duplicated under her own id.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:11:34 -04:00
will 9c741545d8 Import Commemoration of St. Paul's own proper Lauds/Vespers chapter
Deploy / deploy (push) Successful in 1m18s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2035-06-30 (clean year): only the chapter is genuinely his own (2 Tim
4:7-8, "Bonum certámen certávi" -- Paul's own "I have fought the good
fight"). Responsory, hymn, and versicle are all byte-identical to
Common-of-an-Apostle's own already-authored content at Lauds -- reused
under his own id, since resolveOfficeBundle resolves a whole bundle per
id, not per part. Vespers content inferred by the same pattern (his real
Vespers is permanently masked by the Octave of St. John the Baptist's
Nativity closing the same day every year checked, 2026-2030 -- not
modeled in this app), not independently verified.

Updates one existing test whose date (2026-06-30) now correctly shows
his own proper chapter winning over the plain Common fallback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:08:25 -04:00
will 3ca59bb370 Import St. Benedict's own proper Lauds/Vespers content
Deploy / deploy (push) Successful in 1m19s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2033-03-21 (clean year), cross-checked directly against the source's own
Monastic-specific file (SanctiM/03-21.txt), which carries a full proper
office for his Transitus despite the plain secular Sancti file having
none.

Own chapter (Eccli 50:6-7, "Ecce Confessor magnus") and own hymns at
both hours ("Inter ætérnas súperum corónas" Lauds, "Láudibus cives
résonent canóris" Vespers, recounting Subiaco and the founding of Monte
Cassino). Lauds responsory/versicle and Vespers versicle turn out
byte-identical to Common-of-a-Confessor-Bishop's own -- authored
explicitly under his own id rather than left to fall through, since his
actual common (common-of-an-abbot) resolves to a different Confessor-
Not-Bishop responsory that would be wrong here. Vespers has its own
proper responsory invoking him by name ("Sancte Pater Benedícte").
Neither hymn (nor the chapter) has an English rendering in the reference
engine -- own translations, marked draft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:04:56 -04:00
will a168175a7f Import St. Gregory the Great's own proper Lauds/Vespers content
Deploy / deploy (push) Successful in 1m21s
Live-verified against Divinum Officium (Monastic Tridentinum 1617),
2033-03-12 (clean year, per his own saint file's comment). Own chapter
(Eccli 50:1, "Ecce sacérdos magnus... suffúlsit domum") and own hymns at
both hours ("Mella cor obdulcántia" Lauds, "Anglórum jam Apóstolus"
Vespers, the latter recounting his sending Augustine of Canterbury and
the "non Angli sed Angeli" legend); responsory and versicle turn out
byte-identical to Common-of-a-Confessor-Bishop's own, reused verbatim.
Neither hymn has an English rendering in the reference engine at all --
own original translations, marked draft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 10:00:13 -04:00
will dde539d2e4 Fix Vespers commemoration: a Simplex successor is commemorated too
Deploy / deploy (push) Successful in 1m18s
User-reported: 2026-08-24 Vespers (St. Bartholomew, Duplex II. classis,
keeps his own evening) should have commemorated 2026-08-25's St. Louis
(Simplex), but didn't. This was the exact untested asymmetry the old
commemorationOf's own doc comment flagged as "inferred by symmetry, not
independently live-verified." Live-verifying now shows the two
directions genuinely differ: a Simplex predecessor losing to tomorrow's
First Vespers is correctly excluded (Aug 7 Donatus case, "nihil de
præcedenti"), but a Simplex successor displaced because today keeps its
own Vespers IS still commemorated (Aug 24->25, "commemoratio de
sequenti").

Split the one shared commemorationOf into
commemorationOfDisplacedPredecessor (keeps the Simplex exclusion) and
commemorationOfDisplacedSuccessor (drops it).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:57:03 -04:00
will b7036edb35 Confirm Vigils are ferial-by-design; author Common-of-the-BVM Lauds/Vespers bundle
Deploy / deploy (push) Successful in 1m19s
Finished the "ferial/other" audit bucket via each saint's own source
file. Vigils (9 saints) are not a gap at all: every vigil's own [Rule]
explicitly says "Responsory Feria"/"Versum Feria" -- deliberate rubric,
matching this app's existing isFerialOrVigil convention. Even the "ex
C1v" tag on the 6 apostle vigils resolves to the same plain ferial text
when live-queried, confirming no override applies to the Lauds/Vespers
bundle for any vigil.

Common-of-the-BVM (5 feasts) is a real category: each source file says
plain "ex C11"/"vide C11". Live-verified votive=C11: chapter Sir 24:14,
Lauds hymn "O gloriósa Dómina" (the same pre-Urban-VIII text as the
Annunciation's own separate proper), Vespers hymn "Ave maris stella,"
same responsory at both hours, distinct versicles per hour.

Surfaced one masked collision: the Annunciation now resolves through
this Common instead of the ferial default (real improvement, still
interim until its own proper content is imported). Moved the
Passiontide-seasonal-office Vespers test off its date (Mar 25) onto the
clean Mar 30.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:52:41 -04:00
will 476f068fde Author Common-of-Several-Confessors Lauds/Vespers bundle (= Confessor-Not-Bishop content)
Deploy / deploy (push) Successful in 1m19s
Read straight from the source (Sancti/02-12.txt, Ss. Seven Holy Founders
of the Servite Order -- the only saint in this category): its own [Rule]
says plain "vide C5," no votive number of its own. Duplicated the same 7
files under common-of-several-confessors.

This closes every Common category this session's audit found with real
users -- resolveOfficeBundle now has real Lauds/Vespers content for
every category, not just the two it started with.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:44:05 -04:00
will 98b5c05a84 Author Guardian Angels/Archangels' Lauds/Vespers bundle from St. Michael's Apparition
Deploy / deploy (push) Successful in 1m19s
Methodology upgrade this session: read each saint's own Sancti/MM-DD.txt
[Rank]/[Rule] field directly instead of hunting for a per-saint clean
year to live-query -- it states the real Common assignment (or a direct
proper-content inheritance) with zero calendar dependency.

Guardian Angels/St. Gabriel/St. Raphael the Archangels' own files say
"vide C4," but that's a Missal/Mass-propers designation -- their real
Monastic-breviary file (SanctiM/10-02.txt) cross-references St. Michael's
Apparition (Sancti/05-08, already fully authored in this app) directly
for the chapter/responsory at every hour. No common-of-an-angel category
exists in the source at all. Duplicated St. Michael's Apparition's 7
Lauds/Vespers files under each of the three saints' own ids (proper
content, not a Common fallback), live-verified on their own real 2026
days.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:41:46 -04:00
will 0e1a1fe08e Author Common-of-a-Holy-Woman and Common-of-a-Widow Lauds/Vespers bundles
Deploy / deploy (push) Successful in 1m19s
Both genuinely distinct from Virgin(-Martyr) -- different chapter
Prov 31:10-11 "Mulíerem fortem," different hymn "Fortem viríli péctore"
-- but share that category's exact responsory pattern and versicle
byte-for-byte in both hours. Live-verified via St. Anne's own real day
(Jul 26). St. Monica's real day (May 4) confirms common-of-a-widow
shares the identical content with common-of-a-holy-woman, so the same 7
files were duplicated under that id too -- first content authored for
either category at all.

Proof: St. Anne's and St. Monica's own days for Lauds, St. Margaret of
Scotland's real First-Vespers anticipation (Jun 9 -> 10) for Vespers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:24:01 -04:00
will 751594d35d Author Common-of-a-Confessor-Doctor and Common-of-an-Apostle Lauds/Vespers bundles
Deploy / deploy (push) Successful in 1m20s
common-of-a-confessor-doctor (4 saints) is another "same content,
different id" case: live-verified votive=C4a byte-identical to plain
votive=C4 (Common-of-a-Confessor-Bishop), so duplicated verbatim rather
than authored fresh, matching the already-existing matins-hymn-common-
of-a-confessor-doctor.yml precedent.

common-of-an-apostle (3 saints/feasts) is genuinely distinct: chapter
Eph 2:19-20, hymn "Exsúltet cælum láudibus" (byte-identical Lauds/
Vespers), shared versicle "Annuntiavérunt ópera Dei." Also duplicated
under the separate common-of-apostles (plural) id used by the Octave of
Ss. Peter and Paul.

Proofs: St. John Damascene (own day) + St. Robert Bellarmine (First
Vespers) for Confessor-Doctor; Commemoration of St. Paul (own day) + the
Beheading of St. John the Baptist (First Vespers) for Apostle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:19:23 -04:00
will 7f9a1f7c98 Author Common-of-a-Virgin-Martyr / Common-of-a-Virgin Lauds/Vespers bundle
Deploy / deploy (push) Successful in 1m19s
Live-verified against Divinum Officium (Monastic Tridentinum 1617)
votive=C6: chapter 2 Cor 10:17-18, hymn "Jesu, coróna Vírginum" (byte-
identical Lauds/Vespers), shared versicle "Diffúsa est grátia in lábiis
tuis" -- only the two responsories differ between the hours. Cross-
checked plain common-of-a-virgin (St. Praxedes) renders the same real
content, matching the already-existing benedictus-antiphon-common-of-a-
virgin.yml precedent -- duplicated the same 7 files under that id too.

Proof: St. Apollonia's own clean year (2029-02-09, her 2026 date collides
with St. John of Matha) for Lauds, St. Praxedes's real First-Vespers
anticipation for Vespers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:14:05 -04:00
will cf34a6f945 Author common-of-an-abbot Lauds/Vespers bundle (= Confessor-Not-Bishop content)
Deploy / deploy (push) Successful in 1m18s
Same finding as common-of-a-confessor: live-verified (St. Maurus, Jan 15;
St. Odo, Feb 7) that Common-of-an-Abbot's real Lauds/Vespers content is
identical to Common-of-a-Confessor-Not-Bishop's -- an Abbot isn't
necessarily a bishop, so the traditional rite reuses the plain Confessor
common for them. Duplicated the same 7 files under this id, matching the
existing benedictus-antiphon-common-of-an-abbot.yml precedent. Proof
tests on St. Maurus (Duplex II. classis, own Lauds + real First-Vespers
anticipation Jan 14 -> 15).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:08:24 -04:00
will 60f3bcc4db Author common-of-a-confessor Lauds/Vespers bundle (= Confessor-Not-Bishop content)
Deploy / deploy (push) Successful in 1m20s
Fixed a real audit-methodology bug first: the bulk gap-audit was sampling
each saint's own date via prayVespera, which reflects whatever saint
claims First Vespers that evening (often a neighboring day's), not the
sampled saint's own category. Redid it via prayLaudes (no anticipation).

That surfaced the real finding: common-of-a-confessor (37 saints, this
app's largest remaining gap) isn't a distinct liturgical Common -- it's
common-of-a-confessor-not-bishop under a different label. St. Joseph's
and St. Thomas Aquinas's chapter is byte-identical to the already-
authored Sir 31:8-9 text, and several of the 37 saints' own file comments
already say "vide C5" (Confessor Not a Bishop) directly. Not relabeled
across the 37 saint files, since matins-hymn-common-of-a-confessor.yml
and benedictus-antiphon-common-of-a-confessor.yml already establish this
plain id as the real, working one for those saints' Matins/Benedictus
content -- duplicated the Lauds/Vespers bundle under this id too, to
match that existing convention.

Updates two existing tests whose dates collided with this fix: the
FERIAL_MONDAY constant (moved off St. Raymond Nonnatus's own day) and a
new Vespers proof on St. Joseph's First Vespers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 08:05:31 -04:00
will 716937b8f9 Author Common-of-a-Confessor-Bishop Lauds/Vespers chapter/responsory/hymn/versicle
Deploy / deploy (push) Successful in 1m22s
Third Common category (17 real users) with no Lauds/Vespers bundle
authored. Live-verified against Divinum Officium (Monastic Tridentinum
1617) votive=C4: chapter Sir 44:16-17 "Ecce sacérdos magnus," Lauds hymn
"Jesu Redémptor ómnium," shared versicle "Justum dedúxit Dóminus per vias
rectas." Found the Vespers hymn is byte-identical to Common-of-a-
Confessor-Not-Bishop's own ("Iste Conféssor Dómini sacrátus") even though
the chapter, both responsories, and the Lauds hymn all genuinely differ
between the two categories -- reused that already-authored text verbatim
rather than re-deriving it.

No real Duplex-II-classis+ saint exists in this category, so the Vespers
proof uses St. Ubald's real First-Vespers anticipation instead of a
votive-only proof. Updates one existing test (2026-04-20, Paschaltide
seasonal office) whose date turned out to also anticipate St. Anselm's
First Vespers, previously masked by this same gap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 07:50:38 -04:00
will 52d6810f3a Author Common-of-Several-Martyrs Lauds/Vespers chapter/responsory/hymn/versicle
Deploy / deploy (push) Successful in 1m24s
Next-largest remaining Common category (22 real users) with no Lauds/
Vespers bundle authored, same fix pattern as Confessor-Not-Bishop and
Martyr(-Bishop). Live-verified against Divinum Officium (Monastic
Tridentinum 1617) votive=C3: chapter Sap 3:1-3, Lauds hymn "Rex glorióse
Mártyrum," Vespers hymn "Sanctórum méritis ínclyta gáudia," shared
versicle "Exsultábunt Sancti in glória." Cross-checked votive=C3/C3a/C3b
byte-identical, confirming the Missal-collect split (plain-martyrs vs.
Pope-martyrs) doesn't carry through to this Office bundle. Proof tested
on Ss. Placid and Companions (Duplex II. classis, real un-outranked
Second Vespers).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 07:45:12 -04:00
will 7816450086 Author Common-of-a-Martyr(-Bishop) Lauds/Vespers chapter/responsory/hymn/versicle
Deploy / deploy (push) Successful in 1m18s
St. Zephyrinus (Aug 26, common-of-a-martyr-bishop, Simplex) claims First
Vespers over St. Louis's Aug 25, but that evening was showing the plain
ferial hymn instead of a real Common-of-a-Martyr hymn: no content had
been authored yet for this Common category's Lauds/Vespers bundle, so
resolveOfficeBundle's chapter lookup came back missing and the whole
bundle fell through to the ferial default.

Live-verified against Divinum Officium (Monastic Tridentinum 1617)
votive=C2b: chapter Jac 1:12, Lauds hymn "Martyr Dei, qui unicum," Vespers
hymn "Deus tuorum militum," shared versicle "Justus ut palma florebit."
Used the votive-office view rather than a real saint's own day for the
same structural reason as the earlier Confessor-Not-Bishop fix: every
real Common-of-a-Martyr-Bishop saint either has proper content of his own
(St. Lawrence, St. Stephen) or never reaches the rank needed to keep his
own Vespers (St. Zephyrinus, St. Callistus). Cross-checked votive=C2/C2a/
C2b/C2-1 all render identical text, so the same 7 files were duplicated
under the plain common-of-a-martyr id too (St. Hadrian, St. Hermes, St.
Fidelis of Sigmaringen).

Updates one existing Vespers test whose date (2026-03-03) turned out to
also anticipate St. Lucius I's First Vespers, previously masked by this
same content gap; adds two new proof tests and a TODO.md entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 07:19:02 -04:00
will 63fd319509 Fix Vespers/Compline header to use evening day, not daytime day
Deploy / deploy (push) Successful in 1m19s
day-nav.ts's header always resolved via resolveDay(date) regardless of
selected hour, so viewing Vespers/Compline on a day whose evening
anticipates tomorrow's First Vespers (e.g. Aug 25, 2026: St. Louis's day,
but St. Zephyrinus claims First Vespers per calendar/vespers.ts) still
showed today's saint in the header while the hour's actual content had
already switched to tomorrow's. Now uses resolveEveningDay for those two
hours specifically.

Also adds an explicit "First Vespers (of tomorrow)" / "Second Vespers (of
today)" status line to the Vespers header, via new
getVespersStatusLabel(), so the anticipation/commemoration result is
visible rather than only inferable from which saint's name shows up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014axryJBrRswYh2niA7WCUc
2026-08-26 07:02:21 -04:00
will 13ffce00bf Add Preces (Kyrie/Pater Noster + short litany) to Terce, Sext, and None
Deploy / deploy (push) Successful in 1m19s
Real Monastic 1617 has no Preces at the Little Hours, and that was
previously documented as intentional. Per this project's "not a
reconstruction" design, Prime was recently given Preces in the secular
Tridentine form as a deliberate deviation from Monastic — do the same
here: the reference engine's ferial minor-hour Preces (Kyrie, Pater
Noster, then "Domine Deus virtutum, converte nos..." V/R pairs) is
authored as a single shared common id and wired into all three hours
after the chapter. By explicit choice this is said every day regardless
of rank, mirroring Prime's own "every ferial day" simplification, rather
than importing the reference engine's Sunday/feast form, whose sourcing
(a V/R block reused from Compline) looks like a data quirk rather than
genuine minor-hour content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXySSbbUh1EhdN1RnAoCaP
2026-08-25 12:30:16 -04:00
will fc5c9297fa Give Prime's Preces the ferial form on Vigil days
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
2026-08-25 08:07:00 -04:00
will 472878da47 Switch Prime's Sunday/feast Preces to the secular Tridentine form
Monastic Prime's own Dominicales Preces drop the "Et ego ad te, Domine,
clamavi..." versicle block and shorten the closing "Miserere nostri,
Domine... Fiat misericordia tua..." set. Prefer the secular Tridentine
form instead (live-verified against Divinum Officium's "Tridentine -
1906" version), keeping "beato Patri nostro Benedicto" in the Confiteor
by explicit choice even though the real secular Confiteor omits it.

The Sunday/ferial by-day-kind split stays as-is; the ferial form
(prime-preces-feriales.yml) is unchanged and continues to show on every
ferial day by deliberate choice, more generously than the historical
rubric.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HWN869GrMFHqrer9fdCBbF
2026-08-25 07:55:41 -04:00
will e82f3ac018 Author Common-of-Several-Martyrs Matins psalmody, largest remaining category
Live-verified against Ss. Fabian and Sebastian's clean date (2029-01-20,
Monastic Tridentinum 1617). Genuinely distinct psalm scheme from
common-of-a-martyr (single martyr), not a duplicate. Adds Wisdom 3:1-6
to the scripture store for Nocturn 3's canticles. Swaps the matins.test.ts
ferial-fallback proof to St. Anthony Abbot now that Fabian/Sebastian
resolve through this new category instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VpKT73TSqRNnE2CSD8GpUY
2026-08-25 06:41:18 -04:00
will 449472d5aa Add Matins hymn Common-of-Saints tier and a real invitatory-antiphon mechanism
resolveMatinsHymn gained a Common-category tier (override -> octave ->
Common category -> season -> ferial), reusing matins-psalmody-
overrides.ts's own getSaintRecord(id)?.common lookup pattern. The
invitatory antiphon (invitatoryParts), previously one hardcoded fixed
text for every day of the year with only full-vs-incipit doubling
varying by rank, now goes through a real resolveMatinsInvitatoryText
(override -> Common category -> season -> ferial, no octave tier) before
the doubling step.

Content authored this pass: Common-of-an-Apostle only, both hymn
(matins-hymn-common-of-an-apostle.yml, "Aeterna Christi munera") and
invitatory antiphon (matins-invitatory-common-of-an-apostle.yml, "Regem
Apostolorum Dominum"), both read directly from the reference engine's
Commune/C1.txt. Live-verified: St. Bartholomew (2026-08-24) and St.
Andrew (2026-11-30) both now resolve to this Common's text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F25189JqjXddUhU9hM9nUS
2026-08-24 07:14:06 -04:00
will f8de318b7f Fix Matins nocturn readings leaking suppressed Sunday content
nocturnReadingIds unconditionally pooled the plain temporal-cycle and
month-week reading ids into every day's nocturn-reading pool, even when
a real feast wins the day outright with zero commemorations
(decideOccurrence's `ordinary-feria` branch, which correctly suppresses
the temporal identity entirely). St. Bartholomew (duplex-2-classis,
2026-08-24, a Monday) has no Nocturn 3 content of his own, so the
leftover 13th-Sunday-after-Pentecost and month-week readings wrongly
filled his Nocturn 3.

Gate the pooling on whether the day's own occurrence decision actually
retained the temporal identity (day.winner.kind === 'temporal', or a
`temporal`-kind entry in day.commemorations) — mirrors getDayCollects's
existing pattern for the same question.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F25189JqjXddUhU9hM9nUS
2026-08-24 07:11:09 -04:00
will 42dd4dc04b Add Te decet laus to Matins, drop Ps 129/50 from ferial Preces
Deploy / deploy (push) Successful in 1m24s
Matins now sings "Te decet laus" (Psalterium/Common/Prayers.txt's [Te
decet]) immediately after the Te Deum on every 3-nocturn day, matching
Rule of Benedict ch. 11's close of Vigils. New common text
te-decet-laus.yml, wired into both matins.ts branches that render a Te
Deum; absent on ferial days along with it.

Also drops Psalm 129 (De profundis) from the Lauds ferial Preces and
Psalm 50 (Miserere) from the Vespers ferial Preces, per direct
instruction — both were embedded inline in the live-transcribed text.
Updated the affected fixture tests to key off a versicle that survives
the removal instead of the now-absent psalm text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWzE3kaSZCs79fPBrrE1aF
2026-08-23 09:03:04 -04:00
will 72a0f8072e Import calendar-month/week Matins Nocturn 2 content for Aug-Nov
Deploy / deploy (push) Successful in 1m24s
The later post-Pentecost Sundays' real Nocturn 2 patristic content isn't
keyed by Pentecost offset in the reference engine — it's keyed by civil
calendar month/week (the old "Scriptura occurrens" cycle), which shifts
relative to post-pentecost-NN every year depending on Easter's date. Port
Date.pm's monthday() algorithm (pre-1955/Tridentine rubrics) as a new,
date-driven id, author all 20 August-November month-week files from the
reference engine's Tempora/0MN-0.txt sources, and pool them in
hours/matins.ts as a third, independent reading source alongside the
existing temporalId lookup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWzE3kaSZCs79fPBrrE1aF
2026-08-23 07:45:37 -04:00
will 25e2eeb493 Author Matins Nocturn 2/3 patristic readings for the whole post-Pentecost season
Deploy / deploy (push) Successful in 1m27s
Closes the gap the user found on 2026-08-23 (post-pentecost-13's Matins had
no patristic reading): nocturn-readings/ had content for only one temporal
id (post-pentecost-15, the original mechanism proof date). Since
resolveTemporalId maps every day in a liturgical week to the same id, one
file per id covers the whole week — authored all 23 remaining
post-pentecost-NN ids (01-14, 16-24) from the reference engine's own
Tempora/PentNN-0.txt files. Trinity Sunday (01) keeps its own full proper
office including a displaced-Sunday commemoration; 02-11 have real Nocturn
2+3 content; 12 onward are Nocturn-3-only (homily, Gospel incipit skipped
per convention), reusing the same two responsories the source itself
cross-references week to week.

Rest of the temporal cycle (Advent/Christmas/Epiphany/Septuagesima/Lent/
Easter) is a separate follow-up pass, tracked in TODO.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWzE3kaSZCs79fPBrrE1aF
2026-08-23 07:10:20 -04:00
will 7858692fa7 Wire the flexa mark into every hour's psalm-antiphon pairing
Deploy / deploy (push) Successful in 1m24s
Every psalm's opening antiphon (Lauds, Prime, Terce, Sext, None,
Vespers, Compline's callers, Matins) now runs through applyFlexaMark,
marking the boundary in both the displayed antiphon and the psalm's
first verse — e.g. today's Sunday Matins Nocturn 1: "Ant. The king
rejoices ‡" / "The king rejoices ‡ in thy strength, O Lord...".
Live-verified against the reference engine's own rendering for Ps 20.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UCvvgFLrhXNVFUThz6kanw
2026-08-23 06:30:58 -04:00
will 72b30fad80 Add flexa-mark (‡) boundary computation for antiphon/psalm text
Ports Divinum Officium's getantcross/depunct (horas.pl): finds where a
displayed antiphon's words stop matching a psalm's first verse, so the
mark can later show where the antiphon leaves off and the psalm's own
text resumes. Not yet wired into any hour builder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UCvvgFLrhXNVFUThz6kanw
2026-08-23 06:30:23 -04:00
will 9fb9cb5e24 Add Ember-day mechanism (privileged feria, not a rank contest) + real content
Deploy / deploy (push) Successful in 1m17s
Researched whether September/Advent Ember days belong in the
movable-feasts.ts table alongside Christ the King/Immaculate Heart of
Mary. Live-verified against the reference engine they don't: Ember days
are privileged ferias (a saint can still win outright against them), not
named feasts contesting the day by rank.

Two smaller pieces instead: calendar/temporal.ts's septemberEmberDayOffset
(Sunday nearest Sept 14, same "nearest" arithmetic adventStart already
used, factored into a shared nearestSunday helper) feeds a new
resolveTemporalCategory check giving those 3 dates privileged-feria-minor
(live-verified correct); calendar/ember-days.ts's applyEmberDay relabels
the day's own temporal id to the Ember day's own (so its real content is
found) when the feria itself wins, or adds a commemoration when a saint
does. Advent Ember days needed no precedence change -- Advent's own
season default already covers them.

Found and fixed a real bug along the way: matins.ts's nocturnReadingIds
only ever pulled a commemorated *sanctoral* id's own readings, never a
commemorated *temporal* one's -- so a commemorated Ember day (the common
case) would never have surfaced its own content. Same root cause as the
day.winner.id gap the IHM relocation fixed earlier, just hiding in the
commemorations loop instead.

Authored all 6 days' real collect + 3 Matins readings each, transcribed
directly from the reference engine (September: Tempora/093-{3,5,6}.txt;
Advent: Tempora/Adv3-{3,5,6}.txt, whose lessons are themselves a
cross-reference to the Annunciation's own Common, followed and
transcribed from there). Kept as 3 separate readings per day rather than
the usual combine-into-one default, since each carries its own
genuinely distinct proper responsory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZSAgRi4QE4XRTqVto93zA
2026-08-22 07:27:52 -04:00
will 5e79fb4edf Generalize non-Sunday movable feasts into one Easter-offset resolver
Deploy / deploy (push) Successful in 1m21s
The bespoke applyImmaculateHeart function (following the applyMarianSaturday/
applyChristTheKing precedent of one hand-written override function per
feast) doesn't scale: several more feasts are planned that all reduce to
"weekday N days from Easter Sunday" (Ember/Rogation days, more
Sacred-Heart-family Marian devotions, St. Joseph's pre-1955 Eastertide
feast, Lenten Friday Passion devotions), and one-off functions invite
subtle ordering bugs -- applyImmaculateHeart had to be sequenced after
applyMarianSaturday specifically or it would have been silently clobbered.

Replaced with calendar/movable-feasts.ts's generic applyMovableFeasts,
which scans calendar/temporal-feasts.ts's existing per-feast YAML records
for a new optional `easterOffset` field. Adding the next movable feast is
now a new data/calendar/temporal-feasts/<id>.yml file with `rank` and
`easterOffset` set, not a new TypeScript function. Same rank-compared,
commemorate-the-loser semantics as before, just centralized instead of
duplicated per feast.

christ-the-king, marian-saturday, and christmas-octave-sunday stay as
their own functions -- they're structurally different rules (a fixed
month-position search, a date-less fallback default, and a fixed-date-
range Sunday search, respectively), not Easter offsets, so force-fitting
them into this table wouldn't actually simplify anything.

No behavior change -- the existing Immaculate Heart of Mary test suite
passes unchanged through the new generic path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZSAgRi4QE4XRTqVto93zA
2026-08-22 06:33:15 -04:00
will cf4edfae2b Fix Matins hymn to actually keep the Assumption's octave hymn all week
Deploy / deploy (push) Successful in 1m31s
resolveMatinsHymn's own doc comment already claimed the octave "keeps
the feast's own proper hymn all week," but the code never implemented
it -- it only matched when the day's own winner literally was
`assumption` (Aug 15 itself). Every other octave day (16, 17, 19, 20,
21, none of whom has a Matins hymn of their own authored) fell straight
through to the plain ferial hymn instead.

Adds a real octave-fallback tier between the existing per-feast override
and the seasonal tier. A deliberate departure from the reference engine,
which doesn't do this either (Monastic 1617's octave days have no
[Hymnus Matutinum] override at all) -- consistent with how this project
already treats octave readings/commemorations more generously than any
one source track.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZSAgRi4QE4XRTqVto93zA
2026-08-22 06:00:42 -04:00
will 224f34908c Relocate Immaculate Heart of Mary off Aug 22 to Easter+69
Fixed Aug 22 collided outright with the Assumption's own octave-closing
day (both this project's blended calendar tracks are kept deliberately,
so one shouldn't permanently suppress the other). Moved IHM to the
Saturday after the Feast of the Sacred Heart -- its real diocesan date
from 1914 until Pius XII's 1944 fixed-date decree, and also the date the
1969 reform returned to.

Required real new mechanism, not just a data move: a new bespoke
calendar/index.ts override (applyImmaculateHeart, following the existing
applyMarianSaturday/applyChristTheKing precedent), a move from the
sanctoral saints store to the temporal-feasts store (different propers
lookup entirely), and a real bug fix in matins.ts's nocturnReadingIds,
which only ever picked up a *sanctoral* winner's own reading file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZSAgRi4QE4XRTqVto93zA
2026-08-22 05:59:33 -04:00
will 025d0e3a08 Fix Sunday Matins nocturn versicles dropping their R. line
Deploy / deploy (push) Successful in 1m13s
sundayPsalmNocturn/sundayCanticleNocturn only ever rendered a
versicle's V. line, silently missing the responsory for Sunday Matins
and every Duplex+ weekday feast using matins-psalmody-overrides. Both
now share the versicleText helper introduced for the ferial nocturn
antiphons, which combines V./R. into one block.
2026-08-21 09:31:09 -04:00
will 675b644c68 Add antiphons and versicle to plain ferial weekday Matins nocturns
Deploy / deploy (push) Successful in 1m13s
Every non-Sunday, non-Duplex+ Matins nocturn rendered bare psalms with
no antiphon at all. Sourced each psalm's antiphon from Divinum
Officium's Monastic Matins psalter and regrouped to fit
psalter-distribution.yml's own already-established (and non-historical)
weekday psalm redistribution, since the source's own per-weekday
antiphon pairings don't survive that reslice intact.
2026-08-21 09:27:13 -04:00
will b52b202909 Make octave-reading source attribution bilingual
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
2026-08-21 08:04:03 -04:00
will 945c5ef109 Default the initial hour to local time of day instead of always Prime
Deploy / deploy (push) Successful in 1m18s
Visiting the app with no hour in the URL previously always showed
Prime for today, since selectedHour was hardcoded in AppState. Now it
picks the hour whose band contains the visitor's local clock time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L3wxvz3mPXiHxkPB5JpnmD
2026-08-21 06:50:42 -04:00
will 784e0fb5ba Order hour nav by the day, not by build/declaration order
Deploy / deploy (push) Successful in 1m9s
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>
2026-08-19 09:33:58 -04:00
will 3e1fd3b9c4 Strengthen the Immaculate Conception's and Nativity BVM's octaves too
Deploy / deploy (push) Successful in 1m8s
Same `wins: duplex` override as the Assumption's and Pentecost's
octaves, applied to two more common-class octaves. Accepted side
effect: St. Damasus (Dec 11) and St. Nicholas of Tolentino (Sep 10),
both Semiduplex, no longer tie-and-win an ordinary octave day here --
they're now commemorated instead of winning outright.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 07:40:39 -04:00
will 6f8f6618b9 Strengthen the Assumption's octave against a demoted St. John Eudes
Demote St. John Eudes (Aug 19) from Duplex to Semiduplex -- too minor
a confessor to keep outranking the Assumption's octave. To make that
demotion actually cede the day, raise the Assumption's octave to
`wins: duplex` (same pattern already used by Pentecost's octave), so an
ordinary-day tie no longer automatically favors the occurring saint.

Promote St. Thomas of Canterbury (Dec 29) to Duplex so he keeps
winning against the Christmas octave stack now that ordinary-day ties
are no longer a given.

Also add a closing-day tie-break to octave-vs-octave precedence
(pickWinningOctave): when two active octaves tie in rank, the one on
its own closing day now wins the label contest, ahead of the existing
"more recently started" tie-break. Needed because Assumption's day 3
and St. Lawrence's own closing day (Aug 17) now tie at Duplex, and the
closing day should still govern that date's label.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 07:39:58 -04:00
will 9364185f70 Omit responsories' own closing Gloria Patri throughout Passiontide
A different, wider rule than the psalm/canticle Gloria (Triduum-only):
responsories drop their Gloria for the whole of Passiontide (Passion
Sunday through Holy Saturday), except on a Sancti feast's own day —
live-verified (St. Joseph, Duplex I, 2027-03-19, landing in Passiontide
that year, keeps it; an ordinary ferial day in the same window omits it,
at both Compline and Prime).

Every responsory's stored text differs, Gloria-bearing vs Gloria-free,
by exactly one fixed-wording line, so resolve-common.ts's new
resolveResponsory filters that line out rather than requiring separate
per-season files or a data migration — mechanism first, per the
project's own stated content-authoring order. Wired into the four hours
that have a real responsory (Compline, Prime, Lauds, Vespers); Lauds/
Vespers' existing hand-authored Passiontide-proper responsories were
already Gloria-free and are unaffected.
2026-08-19 06:38:21 -04:00
will e93fc4032a Drop Matins' opening (versicle, Ps 3, Invitatory, hymn) during the Sacred Triduum
Tenebrae's rubric replaces Matins' usual opening with a silently-said
Pater/Ave/Credo, live-verified against both Monastic Tridentinum 1617
and Divino Afflatu 1954 (Holy Thursday: Invitatorium and Hymnus both
"omittitur", no Ps 3 or versicle either). Since this app already never
renders that silent block on ordinary days either (data/hours/prime.yml),
the fix is a pure omission straight into Nocturn 1, using the same
isInTriduum window added for the Gloria Patri work.
2026-08-19 06:18:07 -04:00
will 7ffb9afea2 Extend closing Gloria Patri to canticles, centralize instead of baking it in
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).
2026-08-19 06:06:07 -04:00
will a9f673fef9 Label Matins nocturns and the Invitatory in the rendered ordo
Deploy / deploy (push) Successful in 1m17s
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>
2026-08-19 05:28:44 -04:00
will e7bbc7c9f5 Add bilingual day-label headers with Latin weekday/season/ordinal vocabulary
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>
2026-08-19 05:27:44 -04:00
will 01667e7926 Revert ordinary-Sunday Semiduplex to transferring, per direct instruction
Deploy / deploy (push) Successful in 1m9s
Keeps Simplex commemorating in place, but a Semiduplex feast on an
ordinary Sunday goes back to transferring off to the next open day —
this app's own design choice, not a correction against a reference
engine. The transfer mechanism is already generic: this file's `decideOccurrence`
only signals "couldn't hold onto this candidate" for the day itself;
calendar/index.ts's resolveDay is what independently checks each
neighboring day's own native occurrence for an incoming transfer and
claims it — no separate lookback logic needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 11:56:15 -04:00
will 79372fb274 Don't commemorate a Vigil alongside the very feast it anticipates
At Vespers/Compline, a day's own Vigil losing tonight to tomorrow's First
Vespers was showing up as a redundant extra commemoration alongside that
same feast (e.g. Nov 29's Compline showing both "St. Andrew" and "Vigil
of St. Andrew" for Advent I's anticipated evening).

Added an explicit `vigilOf` field to each Vigil's own saint record
(several Vigil ids don't map cleanly to their feast's id by stripping a
`vigil-of-` prefix — `vigil-of-the-assumption` vs `assumption`,
`vigil-of-st-james` vs `st-james-the-greater` — so this needs to be
explicit rather than guessed from the id or date adjacency).
calendar/vespers.ts's evening-anticipation merge now skips adding the
Vigil commemoration when tomorrow's winner or its own commemorations
already include that same feast.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 11:00:09 -04:00
will f43f030a6a Fix ordinary-Sunday precedence and unify getDayLabel's commemoration rendering
Two related fixes surfaced while chasing why St. Gregory Thaumaturgus
appeared to be misdated on Nov 18:

- calendar/commemorations.ts's `ordinary-sunday` case was transferring a
  Semiduplex (or lower) saint off the Sunday instead of commemorating it
  in place — an earlier, unverified guess. Live-verified against both
  Tridentine 1906 and Divino Afflatu 1954 (St. Gregory Thaumaturgus, St.
  Clement, St. Apollinaris, St. Thomas Becket, all real cases): the saint
  stays and is commemorated on the Sunday itself, same as Simplex, never
  pushed to the next open day. This is what was actually moving Gregory
  onto Nov 18 — not a data error. `applyChristmasOctaveSunday`'s own
  Dec 26-29 special case needed a matching adjustment (per its own
  documented intent, the displaced saint there is deliberately *not*
  commemorated in place, since he reappears in full on Dec 30 instead).

- getDayLabel had five branches, each hand-assembling its own
  filter/format logic for which commemorations to show — which is why
  the octave phrasing, the closing-day title, and a missing Sunday
  commemoration turned into three separate bugs earlier instead of one.
  Replaced with a single shared `collectCommemorations` used by every
  branch. This also exposed that the sanctoral-winner branch never
  showed any commemorated saint at all, and every other branch only
  showed the *first* one (`.find()`), silently dropping real collisions
  — both now show every commemorated saint, matching this app's own
  generous-commemoration design.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 10:59:50 -04:00