Compare commits

..

101 Commits

Author SHA1 Message Date
will f8bd7f3140 Dedupe Matins nocturn readings pooled from two ids on the same day
Deploy / deploy (push) Successful in 2m12s
post-pentecost-15 and month-week-092 land on the same Sunday
(2026-09-06) and independently reuse the identical Gregory the Great
Moralia in Job excerpt -- a real duplication in the reference engine's
own Tempora files, not a vu authoring error. buildReadingPool pooled
both ids and rendered the reading twice; now dedupes by text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKC2QQGLYo2UTiDjDyWEkW
2026-09-05 07:22:53 -04:00
will 6ecd1485c4 Add jdc (Judges) responsory, composed from Gideon's cry at Jdc 7:17-20
Deploy / deploy (push) Successful in 1m52s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKC2QQGLYo2UTiDjDyWEkW
2026-09-05 07:14:07 -04:00
will 9e66ad10e4 Add josue (Joshua) responsory, composed from Josue 24:15-16
Deploy / deploy (push) Successful in 1m56s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKC2QQGLYo2UTiDjDyWEkW
2026-09-05 07:04:25 -04:00
will dcea19015f Add 1esdr (Ezra) responsory, composed from 1 Esdr 8:21-23
Deploy / deploy (push) Successful in 1m56s
No liturgical precedent exists for this book; the verse's antithesis
("the hand of our God is upon all them that seek him in goodness: and
his power and strength, and wrath upon all them that forsake him") is
reordered so the hopeful clause repeats after the V., not the wrath
clause. Closes the per-book pool at 68/72 -- the last 4 have no
sourceable material at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALE7phKTAnmRWvWMU9dZa7
2026-09-05 06:39:18 -04:00
will c98abaebb9 Drop parenthetical gloss from Nehemiah responsory V.
Deploy / deploy (push) Successful in 2m12s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALE7phKTAnmRWvWMU9dZa7
2026-09-05 06:24:21 -04:00
will 1d566ec6e0 Add 2esdr (Nehemiah), correcting a prior mis-diagnosis
Deploy / deploy (push) Successful in 2m2s
Nehemiah was wrongly bundled in with ezra's "ambiguous, not acted on"
reasoning and left unseeded. It actually has a real, unambiguous
citation in the Missa (Ember Wednesday of September's Lectio/Communio,
Neh 8:1-10) that a prior pass never checked. R. from v.3, V. from v.9,
both from that accented Missa Lectio.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALE7phKTAnmRWvWMU9dZa7
2026-09-05 06:14:20 -04:00
will c2a15d94dd Add ruth -- composed with no liturgical precedent at all
Deploy / deploy (push) Successful in 1m51s
User-directed: compose Ruth's own responsory from Ruth 1:16-17 ("thy
people shall be my people, and thy God my God") on the verse's own
merit. Unlike deut/rom/bar, this text has no liturgical precedent
anywhere in the reference source (no Nuptial Mass file even exists
there) -- flagged that distinction before proceeding; user confirmed
explicitly.

Since data/scripture/ruth-1.yml (the usual bulk import) carries no
accents, the Latin was hand-accented rather than transcribed, but
cross-checked word-by-word against already-accented occurrences of
the same words elsewhere in the corpus rather than guessed freehand.

66 of 72 bible-plan books now seeded. npm test (2029 passed) and
tsc --noEmit pass; live-verified through getBiblePlanReadings.
Canonical-key audit re-run, clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 22:01:15 -04:00
will 3ee87098c0 Note user's explanation for the Ezra Introit citation mismatch
Deploy / deploy (push) Successful in 1m53s
Vulgate "Esdras" numbering is genuinely ambiguous: 1/2 Esdras is
canonical Ezra/Nehemiah, but there's also a separate non-canonical
Greek "Esdras A" (the Vulgate appendix's own 3 Esdras, sometimes
itself called "1 Esdras" in Septuagint/Orthodox numbering) -- a
different text this project has never digitized. The reference
engine's own citation never disambiguates, so this can't be
confirmed, but it cleanly explains why the Pasc7-2.txt Introit's
"Esdr 2:36-37" citation doesn't match our canonical Ezra text.
Practical outcome unchanged (ezra stays unseeded), now documented
with a clear reason rather than an open mystery.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 21:51:06 -04:00
will e52c285f99 Add bar (Baruch), found in the Missa propers, not the Horas
Deploy / deploy (push) Successful in 1m54s
User pointed to missa/Latin/Tempora/Quad6-6.txt -- the Mass propers, a
directory tree this whole responsory search had never once checked
(everything so far searched horas/, the Divine Office proper). The
Easter Vigil Mass's own fourth prophecy ([Proph_Baruch3], Bar 3:9-38,
"Audi, Israel, mandata vitae") is genuine, real, accented liturgical
Latin -- just never part of Matins at all.

Composed bar the same way as deut/rom: built from that prophecy's own
climactic close ("Hic est Deus noster... Post haec in terris visus
est, et cum hominibus conversatus est" -- read at the Vigil because
early exegetes saw "seen upon earth, and conversed with men" as a
prophecy of the Incarnation).

Extended the Missa check to the other 6 already-absent books too --
genuinely zero citations anywhere. Ezra turned up one thin, ambiguous
Missa Introit citation that doesn't match its own cited verse; left
unseeded rather than force it.

65 of 72 bible-plan books now seeded -- this closes the per-book
responsory pool. npm test (2029 passed) and tsc --noEmit pass;
live-verified through getBiblePlanReadings. Canonical-key audit
re-run, clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 21:36:48 -04:00
will 2b7495fc91 Close the Matins per-book responsory pool: rom is the last book
Deploy / deploy (push) Successful in 1m53s
Composed rom from Rom 13:11-12 ("the night is past, the day is at
hand... let us cast off the works of darkness, and put on the armour
of light") -- the standing year-round ferial Lauds Capitulum, also
used at Advent I's own Capitulum. Genuine accented Vulgate/
Douay-Rheims text, never historically assembled into its own Matins
responsory, same approach as deut. User-confirmed before adding.

64 of 72 bible-plan books now seeded -- every book actually used in
the bible-plan has a real responsory, whether transcribed (62) or
composed from genuine already-liturgical text (deut, rom). The
remaining 8 have no citation anywhere in the source at all; there's
nothing further to check.

npm test (2029 passed) and tsc --noEmit pass; live-verified through
getBiblePlanReadings. Canonical-key audit re-run, clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 21:14:03 -04:00
will afa187032b Add deut, this pool's first genuine original composition
Deploy / deploy (push) Successful in 1m55s
User found a lead reading a patristic homily (English/Sancti/01-02.txt)
that quotes Deut 32:11's eagle image. Deuteronomy has no historical
Matins responsory anywhere in the reference source, so unlike every
other entry in this pool, this one is composed rather than
transcribed: a real R./V. pairing built from the Canticle of Moses'
own genuine verses (Latin/Psalterium/Psalmorum/Psalm226.txt, sung at
Lauds), verified against data/scripture/deut-32.yml to confirm the
citation is standard verse 11 and not the breviary's own internal
chant-verse-numbering (32:15-16) for the same text. Documented plainly
in the data file's own header as vu's composition, not presented as
historically attested.

63 of 72 bible-plan books now seeded. npm test (2029 passed) and
tsc --noEmit pass; live-verified through getBiblePlanReadings.
Canonical-key audit re-run, clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 20:06:48 -04:00
will d39359e5ac Carry over unreached Advent bible-plan readings into Christmastide
Deploy / deploy (push) Successful in 1m55s
Advent's own reading plan (advent-1-sunday through advent-4-saturday,
28 rows for a full 4-week Advent) is always longer than real Advent
runs. Advent's last real day is always Dec 23 (Dec 24 is the Vigil of
Christmas's own temporal id), while Advent 1 Sunday lands anywhere
from Nov 27 to Dec 3 -- even the longest possible Advent is one row
short of the full plan, and the shortest is a whole week short. Every
year, at least the plan's last row never reaches its own natural date.

Added adventCarryoverReadings to propers/bible-plan.ts: whatever
doesn't land on its own natural date now carries forward one reading
per day starting Christmas Day, pooled alongside whatever
Christmastide's own calendar-dated rows already have -- never
replacing them. This is the one place in bible-plan.ts that needs
real calendar-year awareness (calendar/temporal.ts's adventStart).

4 new tests covering the shortest (2028) and a long (2027) Advent,
pooling behavior, and the Dec-24 boundary. npm test (2029 passed) and
tsc --noEmit pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 19:17:24 -04:00
will 52052d737e Find Canticles, Leviticus, and Numbers in Sancti, not Tempora
Deploy / deploy (push) Successful in 1m51s
User pushback on Canticles being "confirmed absent" -- right instinct
to check the Commune files, which redirected productively: the same
Lectio-citation survey run against Sancti (individual saint/feast
files) rather than just Tempora found real, genuine content:

- cant: the Visitation (July 2) reads straight through the Song of
  Songs, blending it with the Visitation narrative itself.
- lev: the Purification of the BVM's own Leviticus purification-law
  reading is the feast's actual reason for being.
- num: the Exaltation of the Holy Cross's bronze-serpent/Cross
  typology (Num 21:6-9).

Re-checked the other 9 already-"confirmed absent" books against
Sancti too -- still genuinely absent, no alternate abbreviation
missed.

62 of 72 bible-plan books now seeded -- the real final count. Only
Romans and 9 books with no citation anywhere (Tempora or Sancti)
remain. npm test (2025 passed) and tsc --noEmit pass; live-verified
through getBiblePlanReadings. Canonical-key audit re-run, clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 19:03:01 -04:00
will e96d201346 Close out the Matins per-book responsory pool: 59 of 72 books
Deploy / deploy (push) Successful in 1m49s
Asked to translate the 15 remaining "real Latin, no English" books.
None needed translation: the whole post-Epiphany reading season
(Galatians/Ephesians/Philippians/Colossians/1-2 Timothy/Titus/
Philemon/2 Thessalonians) turns out to have no distinct responsory
content of its own -- every feria file references 2 Corinthians' own
week (already bilingual) by weekday position. Same for 2 Peter/2-3
John/Jude, which reference Ascension Day's own responsories (act).

Only Romans genuinely has nothing: every citing file borrows an
unrelated feast's own proper responsory (Circumcision, Christmas Day,
a January saint), never anything Romans-specific. Left unseeded --
using one would repeat the Sacred Heart/Jeremiah mismatch this pool
already avoids elsewhere.

59 of 72 books now seeded -- every book with any usable Latin content
in the source at all. The remaining 13 (Romans plus 12 books with no
citation anywhere) are the real, final gap.

Ran the full canonical-key audit again; confirmed clean. npm test
(2025 passed) and tsc --noEmit pass; live-verified through
getBiblePlanReadings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 18:50:56 -04:00
will 1867f73080 Add R./V./repeat labels to per-book responsory pool output
Deploy / deploy (push) Successful in 1m54s
getResponsoryForBook's joinResponsory concatenated the r/v text with a
bare newline and no "R."/"V." labels at all, unlike every other
responsory source in the app (octave-readings' own responsory field,
nocturn-readings' inline responsory, and the new per-Common-category
pool) which all include the full "R. ...\n* ...\nV. ...\nR. ..."
convention. User caught this looking at the rendered Judith entry.

Fixed by computing the repeat line from the existing "*" marker
already present in the pool's r text. npm test (2025 passed) and
tsc --noEmit pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 18:44:38 -04:00
will ff87aff240 Add 1 Cor, 2 Cor, 1 Peter, 1 John; fix a repeated canonical-key bug
Deploy / deploy (push) Successful in 1m52s
Confirmed Leviticus/Numbers/Deuteronomy genuinely absent from Tempora
(precise awk-based survey, not a loose grep). Added 1cor (Maundy
Thursday's own Institution-narrative Nocturn 3), 2cor (Epiphany II
Sunday), 1pet and 1joan (Paschaltide Sunday sets already partly used
for jac/jas).

Caught a repeat of the same canonical-key mistake from the previous
commit: the jas entry should have been keyed jac (BOOK_ALIASES maps
jas->jac). Fixed, and this time audited every single key in the file
programmatically against BOOK_ALIASES rather than just the keys
touched this pass -- confirmed clean.

45 of 72 books now seeded. npm test (2025 passed) and tsc --noEmit
pass; live-verified through getBiblePlanReadings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 18:31:13 -04:00
will c32fabb54d Add Acts, Apocalypse, James, Hebrews, and the 4 Gospels to responsory pool
Deploy / deploy (push) Successful in 1m48s
Surveyed Tempora precisely with an awk one-liner reading the line after
every [LectioN] tag (two lines deep for Gospel citations) rather than a
loose grep-for-book-prefix, which had already produced one false
positive this session.

Added act (Ascension Day's own Matins), marc (Easter Sunday's own),
heb (Good Friday's own Tenebrae), apoc (Easter III Sunday), jas
(Easter IV Sunday), and luc/matt/joan (Advent I/II/III Sundays, one
entry each). 41 of 72 bible-plan books now seeded.

Confirmed no English exists anywhere in the reference engine for
Galatians/Ephesians/Philippians/Colossians/Titus/Philemon or Romans --
real Latin exists, no bilingual source; left unseeded.

npm test (2025 passed) and tsc --noEmit pass; live-verified through
the real getBiblePlanReadings call chain (not a direct
getResponsoryForBook call, per the previous commit's lesson).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 18:20:44 -04:00
will f0b771a02a Fix real correctness bug: pool keys must match canonical book form
Deploy / deploy (push) Successful in 1m50s
bible-plan.ts normalizes a reading's book through canonicalBook()
(scripture/index.ts's BOOK_ALIASES map) before calling
getResponsoryForBook -- so the pool must be keyed by the canonical
form, not the bible-plan YAML's own raw book code. 12 of the 33 keys
seeded earlier today used the raw form and would have silently
resolved to undefined in the real app despite passing an initial
"live verification" that called getResponsoryForBook directly
(bypassing the normalization the real call site always applies).

Renamed: 1macc->1mach, 2macc->2mach, 1sam->1reg, 2sam->2reg,
1kgs->3reg, 2kgs->4reg, jdt->jdth, zeph->soph, obad->abd, jonah->jon,
hos->osee, mic->mich.

Re-verified through the actual getBiblePlanReadings call chain this
time (2026-06-04, post-pentecost-01/Thursday, "1sam 9-10" now
resolves a real responsory). npm test (2025 passed) and
tsc --noEmit pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 18:11:36 -04:00
will 3b46a18d63 Add 14 more books to Matins responsory pool, fix 2 dropped entries
Deploy / deploy (push) Successful in 1m52s
User caught jdt (Judith) listed as unseeded despite having real
responsory content directly visible in the reference source -- and
already flagged present in this same session's own citation dump,
never followed up on. Self-audit after that catch found job had also
been extracted earlier this session but never committed to the file.

Added jdt, esth, job, eccl, hab, joel, zeph, obad, jonah, mal, hos, mic,
nah, and lam (Maundy Thursday's own Tenebrae responsories). 33 of 72
bible-plan books now seeded, up from 19.

npm test (2025 passed) and tsc --noEmit pass; live-verified via
getResponsoryForBook resolving real text for every new key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 17:43:43 -04:00
will 816dcadf0d Fix wrong book-count estimate in Matins responsory pool notes (66 -> 72)
Deploy / deploy (push) Successful in 1m52s
"~66" was an eyeballed guess from a sort|uniq -c list, never actually
counted -- and coincidentally echoes the 66-book Protestant canon count,
a bad number to leave uncorrected in a Catholic office project's own
docs. The real count (grep -c distinct `book:` values across
data/hours/bible-plan/*.yml) is 72.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 17:28:22 -04:00
will 7cee7a1e8a Fix Matins responsory extraction bug, unblock 8 more books
Deploy / deploy (push) Successful in 1m50s
The per-book pool extraction script only matched a bare [ResponsoryN]
tag, missing the reference engine's underscore-suffixed [ResponsoryN_]
template convention that holds the real text (a bare tag often just
@:-references it, alongside a Cistercian-rubric variant). This wrongly
made 1sam/2sam/1kgs/2kgs/2macc look untranslated in English.

Fixed the script and hand-followed @Tempora/X:ResponsoryN cross-file
references to add 1sam, 2sam, 1kgs, 2kgs, 2macc, zach, amos, and agg --
19 of ~66 bible-plan books now seeded (up from 11).

npm test (2025 passed) and tsc --noEmit pass; live-verified via
getResponsoryForBook resolving real text for every new key at every
pool index.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 17:13:13 -04:00
will 91dcc2c972 Seed 8 more books in the Matins per-book responsory pool
Deploy / deploy (push) Successful in 1m59s
Added exod, prov, sir, sap, tob, ezek, dan, 1macc (11 of ~66 total now
seeded), each sourced from a genuine, non-overridden reference-engine
Tempora file with real bilingual Responsory text. sir/sap and dan reuse
prov's/ezek's own entries respectively, confirmed by the reference
engine's own week files @-referencing that same pool -- real evidence
this app's loose book-level responsory match is historically authentic,
not invented.

Skipped 1sam (macro-only responsory text, not worth resolving for one
book) and 2sam/1kgs/2kgs/2macc/zach/amos/agg (real Latin but no
reference-engine English translation at all).

npm test (2025 passed) and tsc --noEmit pass; live-verified via
getResponsoryForBook resolving real text for every new key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 16:59:50 -04:00
will a570266028 Add per-Common-category Matins responsory pool, expand per-book pool
Deploy / deploy (push) Successful in 1m52s
The per-book responsory pool (Nocturn 1 scripture readings) only had `isa`
seeded; the much bigger, previously untracked gap was that 272 of 355
nocturn-readings/*.yml files (Nocturn 2/3 patristic readings) had no
responsory at all. Added a sibling per-Common-category pool, sourced from
the reference engine's static Commune text files, covering all 19 of vu's
real `common:` category ids (several confirmed aliases of the same
underlying file). Wired into matins.ts's reading assembly as a fallback
when a reading has no proper responsory of its own. Also expanded the
per-book pool with `gen` and `jer`.

Live-verified end-to-end (St. Francis de Sales, Ss. Cornelius/Cyprian, Ss.
Euphemia/Lucy/Geminianus). npm test (2025 passed) and tsc --noEmit pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 16:47:17 -04:00
will 2a98386bdd Log the Suffragium Paschale mechanism fix in TODO.md
Deploy / deploy (push) Successful in 1m52s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 15:37:59 -04:00
will 58b201dc23 Build the Paschaltide "Suffragium Paschale" mechanism
Throughout Paschaltide (Eastertide/Ascensiontide/Pentecost), two
things happen wherever they'd otherwise show: the ordinary
5-suffrage set (Cross/BVM/Joseph/Apostles/Peace) collapses into one
alleluia-form "Suffragium Paschale" (Lauds and Vespers each have
their own distinct antiphon here, unlike the ordinary suffrages,
which share identical text at both hours), and the ferial-Preces
litany drops entirely rather than being replaced. Live-verified
against the reference source's own [Suffragium] macro directly,
byte-identical between Monastic Tridentinum 1617 and Tridentine 1906.

Exported isPaschaltide(season) from resolve-common.ts (wraps the
pre-existing PASCHALTIDE_SEASONS set). resolveSuffrages gained a
third hourId parameter to pick the right antiphon; the Preces
omission is a small new check gated on the same isFerialOrVigil
predicate that already selects the ferial litany form.

Authored lauds-suffrage-paschale.yml and vespers-suffrage-
paschale.yml -- English on the Vespers antiphon is this project's own
translation (the reference engine leaves it untranslated) and marked
draft; everything else verified.

Closes the last item in TODO.md's "Mechanism gaps" priority tier.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 15:37:53 -04:00
will b90789cab3 Log the Lent Vespers verification in TODO.md
Deploy / deploy (push) Successful in 1m50s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 15:12:11 -04:00
will 7551d205b7 Mark the rest of Lent's Vespers/Lauds hymn content verified
Same shape as the earlier Lauds capitulum/responsory/versicle fix
this session: real content, just never live-query-verified. Live-
verified Vespers' whole bundle (capitulum/responsory/hymn/versicle,
including the hymn's own claimed Monastic word-swap) against a plain
Lenten Wednesday (2026-03-04). Lauds' own hymn and both hours'
doxology-per-annum files were already seen live earlier the same
session (checking lauds-hymn-saturday.yml's real content against a
Lenten Ember Saturday) -- byte-identical, just needed the status
flipped. No more draft Lent content anywhere in this app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 15:12:04 -04:00
will 25dc73da1f Add Lent's still-draft Vespers/hymn content to the Open Work list
Deploy / deploy (push) Successful in 1m54s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 15:00:53 -04:00
will 4864992cc0 Log Ember recombine/Lent verification/Trinity Sunday fixes in TODO.md
Deploy / deploy (push) Successful in 1m54s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 14:50:39 -04:00
will aba89dc250 Model Trinity Sunday as a real feast, not just a cosmetic label
Trinity Sunday wasn't modeled at all: getDayLabel had a purely
cosmetic "Trinity Sunday" label overlay for the Sunday after
Pentecost, but resolveTemporalId just fell back to post-pentecost-01
(ordinary-sunday category), so any Duplex+ saint assigned that date
(e.g. the Queenship of the BVM, fixed May 31) won outright, real
Trinity Sunday content never appearing anywhere.

Added a real temporal-feasts/trinity-sunday.yml record (Easter+56,
Duplex II. classis, live-verified). Per direct instruction,
unconditional: true (same shape as christ-the-king.yml) -- a
principal Sunday shouldn't lose a same-rank fixed-date collision, and
the one real collision case (Queenship of the BVM) isn't
independently verifiable against Monastic 1617 at all (that calendar
doesn't carry the Queenship feast -- May 31 there is St. Petronilla
instead).

Authored the full office and registered trinity-sunday in
ALWAYS_OVERRIDE_TEMPORAL_IDS -- the same generic mechanism already
built for the Pentecost-cluster fix, no new code needed: collect,
Benedictus/Magnificat antiphons, Matins hymn ("Summæ Deus cleméntiæ"),
Lauds/Vespers chapter (shared, Rom 11:33) and hymn (shared, "O lux
beáta Trínitas", already the pre-Urban VIII text), each hour's own
distinct responsory/versicle -- all live-verified 2026-05-31.

Fixed 3 pre-existing tests written around the old cosmetic-label
behavior, two of which had never-verified guesses baked in as if
correct: day-label.test.ts's "names the anchor day itself" test and
its "outranks an active octave" test (was asserting "Duplex I Class",
a guess -- the real live-verified rank is Duplex II);
transfer.test.ts's St. Felix I test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 14:50:31 -04:00
will 2a611cc17b Mark Lent's own Lauds capitulum/responsory/versicle verified
Not actually homeless, contrary to an earlier session's claim: these
3 files already had the real content (Isa 58:1 / "Ipse liberávit me"
/ "Ángelis suis"), just marked draft since never live-verified.
Live-verified against both a Lenten Ember Saturday (2026-02-28) and a
plain Lenten Wednesday (2026-03-04) -- identical, confirming this is
genuinely season-wide content, not weekday-varying.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 14:50:08 -04:00
will 3d230b34f2 Recombine the remaining 5 Ember-day nocturn-readings files
Same fix as ember-september-friday.yml (already done): each file's
Lectio1-3 turned out to be one continuous, unbroken homily (Bede,
Gregory, or Ambrose's Commentary on Luke for the two Advent cases,
cross-referenced from the Annunciation's own Common and confirmed
contiguous by reading the source file directly), split only to fill
the fixed 3-lesson slot count. Recombined ember-september-wednesday/
-saturday and ember-advent-wednesday/-friday/-saturday into single
readings, keeping each one's first lesson's own responsory.

All 6 Ember days now recombined -- no remaining split-for-no-reason
readings in this family. Updated ember-days.test.ts's lesson-count
table accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 14:49:59 -04:00
will cefff90a34 Log the Ascension collect/Corpus Christi/Sacred Heart fixes in TODO.md
Deploy / deploy (push) Successful in 1m49s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 12:55:00 -04:00
will 30b3971190 Give Corpus Christi and Sacred Heart their own hymnody, all octave
Same gap and same generic mechanism fix as Ascension/Pentecost --
just needed the ids added to ALWAYS_OVERRIDE_TEMPORAL_IDS plus real
content. Both feasts have 3 genuinely different hymns, one per hour:
Corpus Christi's own are widely known outside this context too
("Sacris solémniis" at Matins, "Verbum supérnum pródiens" at Lauds
whose last 2 stanzas are "O Salutaris Hostia", "Pange, lingua,
gloriósi Córporis mystérium" at Vespers whose last 2 stanzas are
"Tantum Ergo").

Sacred Heart needed one source-track correction: Monastic Tridentinum
1617 doesn't observe this feast on this date at all (shows St.
Barnabas instead) -- used Divino Afflatu 1954 instead, this project's
own stated rubric track. Also live-verified a genuine structural
difference: Sacred Heart's own Lauds/Vespers have no responsory at
all in the source (straight chapter to hymn) -- not an authoring gap.
Fixed lauds.ts's and vespers.ts's own resolveOffice to omit the
responsory part entirely when its status is missing in both
languages, instead of always rendering an empty section.

Authored matins-hymn-{corpus-christi,sacred-heart}.yml,
lauds-{capitulum,responsory,hymn,versicle}-corpus-christi.yml,
vespers-{responsory,hymn,versicle}-corpus-christi.yml,
lauds-{capitulum,hymn,versicle}-sacred-heart.yml, and
vespers-{hymn,versicle}-sacred-heart.yml -- all live-verified,
2026-06-04 and 2026-06-12.

Fixed 2 pre-existing tests whose own dates now land inside these
newly-modeled octaves and were asserting the old fall-through
behavior as if correct: lauds.test.ts's FERIAL_MONDAY (moved off
2026-06-08) and vespers.test.ts's St. Margaret of Scotland test
(2026-06-09) -- its own title already anticipated this fix, just had
the wrong expected values until now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 12:54:55 -04:00
will 98aa411a1b Author Ascension's own collect
A separate, pre-existing gap: getDayCollect already looked up
ascension-collect via resolveOfficeWinner (the same mechanism this
session's earlier fix already covers), but the content file itself
never existed. Live-verified against Divinum Officium (Monastic
Tridentinum 1617), 2026-05-14.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 12:54:35 -04:00
will 8dd04ab318 Log the Pentecost hymnody fix in TODO.md
Deploy / deploy (push) Successful in 1m50s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 12:07:54 -04:00
will 5d500c4e1f Give Pentecost its own hymnody at Matins/Lauds/Vespers, all octave
Same gap as Ascension (pentecost-sunday is a temporal-feast id, not a
saint record) -- the mechanism fix from that commit is generic, so
this only needed the id added to ALWAYS_OVERRIDE_TEMPORAL_IDS plus
real content.

Unlike Ascension, Pentecost has 3 genuinely different hymns, one per
hour: "Jam Christus astra ascénderat" (Matins), "Beáta nobis gáudia"
(Lauds), "Veni Creátor Spíritus" (Vespers), each with its own real,
distinct chapter/responsory/versicle at Lauds vs. Vespers.

Authored matins-hymn-pentecost-sunday.yml, lauds-{capitulum,
responsory,hymn,versicle}-pentecost-sunday.yml, and
vespers-{responsory,hymn,versicle}-pentecost-sunday.yml (Vespers
reuses Lauds' chapter via the existing cross-hour fallback) -- all
live-verified against Divinum Officium (Monastic Tridentinum 1617),
2026-05-24. Confirmed the whole octave end-to-end via resolveOrdo,
including a real winning saint within the window (St. Augustine of
Canterbury) keeping his own hymn at Lauds/Vespers unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 12:07:48 -04:00
will 374d27858a Log the Ascension hymnody fix in TODO.md
Deploy / deploy (push) Successful in 1m52s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 11:08:16 -04:00
will 44dd4bb47a Give Ascension its own hymnody at Matins/Lauds/Vespers, all octave
`ascension` is a temporal-feast id, not a saint record, so
resolveOfficeWinner's octave-synthesis (which already handles
saint-based octaves like Assumption's/St. Lawrence's) never
recognized it -- Ascension Day itself and its whole octave silently
rendered the generic Paschaltide hymn at every hour.

Fixed the mechanism: added `ascension` to ALWAYS_OVERRIDE_TEMPORAL_IDS
(covers the feast day itself), and taught resolveOfficeWinner to also
synthesize a {kind:'temporal'} winner from an active octave with no
saint record but a real temporal-feast record (covers ordinary-feria
octave days; a real winning saint within the octave still correctly
keeps priority).

A third gap surfaced testing this: octaveGoverningPrivilegedDay's
correct-by-design "a privileged Sunday never accepts an octave
override" rule is live-verified wrong specifically for "Dominica
infra Octavam Ascensionis" (keeps the octave's own hymn/responsory,
just with its own distinct proper chapter). Rather than loosen that
shared gate for every octave, gave resolveLaudsHymn/resolveVespersHymn
a narrow, hymn-only intercept guarded to saint-less octaves, checked
before the season/weekday default.

Authored matins-hymn-ascension.yml, lauds-{capitulum,responsory,hymn,
versicle}-ascension.yml, and vespers-{responsory,hymn,versicle}-
ascension.yml (Vespers reuses Lauds' chapter via the existing
cross-hour fallback) -- all live-verified against Divinum Officium
(Monastic Tridentinum 1617), 2026-05-14.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 11:08:11 -04:00
will ab8d1d452d Log the Sunday/seasonal Matins hymn work in TODO.md
Deploy / deploy (push) Successful in 1m54s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 10:38:10 -04:00
will e7e19c530a Author Sunday's own Matins hymn and every season's (Advent-Paschaltide)
The "genuinely more complex, not-yet-understood two-hymn mechanism"
flagged for Sunday turned out simple: Day0 Hymnus ("Nocte surgéntes")
is just Sunday's plain per-annum hymn, and Day0 Hymnus1 ("Primo
diérum ómnium") is Septuagesima-tide's own separate seasonal hymn --
the same shape Advent/Lent/Passiontide/Paschaltide each already have.
Live-verified these 4 don't vary by weekday within their season (a
Lenten Sunday and a Lenten feria render identically). None of the 6
were authored at all before this -- every Sunday and every season day
not otherwise overridden by a winning saint/octave silently fell
through to Monday's own ferial placeholder text.

Authored matins-hymn-{sunday,septuagesima,advent,lent,passiontide,
paschaltide}.yml, all live-verified, self-contained with their own
real closing doxology baked in. Septuagesima is checked directly by
day.season in resolveMatinsHymn itself, deliberately not folded into
the shared seasonalOfficeSuffix (Lauds/Vespers have no matching
content yet -- resolveOfficeBundle there is all-or-nothing per feast,
so adding it would silently blank their chapter/hymn instead of
falling through to their current weekday default).

Known, flagged simplification: the generic Paschaltide hymn wrongly
applies during the Ascension octave, whose own real hymn isn't
authored anywhere yet -- a same-shape gap Lauds already has, not a
new regression. Updated 2 FERIAL_DATE (a Tuesday in Advent) hymn
assertions that had been asserting Tuesday's own ferial text --
Advent's own seasonal hymn correctly outranks the weekday cycle now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 10:38:05 -04:00
will 8307526f69 Log today's Ember Friday/Matins-hymn/Marian Saturday work in TODO.md
Deploy / deploy (push) Successful in 1m45s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 09:34:26 -04:00
will 0b6a0ad1cc Complete Marian Saturday: proper Matins/Lauds hymns, ferial antiphons
Design (direct instruction): Marian Saturday is its own rank tier,
between simplex and vigil in vu's FeastClass scale -- beats a plain
Simplex saint (commemorated in return), loses cleanly with zero trace
to anything Vigil-or-higher. Deliberately not the full historical
Officium Sabbatorum B.M.V. ("no little office"): ferial psalmody is
kept everywhere, only the antiphons swap to Marian text; Matins and
Lauds each get a real proper Marian hymn; the Marian collect applies
whenever the day wins.

The calendar-side mechanism (calendar/index.ts's applyMarianSaturday)
and a collect/Benedictus antiphon already existed from an earlier
session -- authored what was still missing: matins-hymn-marian-
saturday.yml (Matins had nothing at all) and lauds-{capitulum,
responsory,hymn,versicle}-marian-saturday.yml.

Also found and fixed 3 real bugs from that earlier, unverified pass:
Lauds' own hymn ("O gloriósa Dómina") was sitting under the plain
ferial lauds-hymn-saturday id instead of its own marian-saturday id;
that id's capitulum/responsory/versicle siblings were live-verified
from a different, privileged Lenten Ember Saturday, not a genuine
per-annum ferial Saturday -- corrected to the real content ("Auróra
jam spargit polum" / Rom 13:12-13 / "Sana ánimam meam" / "Repléti
sumus mane misericórdia tua", live-verified against a clean September
Ember Saturday); and the Lauds psalmody override used the real
historical proper psalm set (92/99/62) instead of the ferial one,
rolled back per direct instruction to plain ferial psalms (50, 142,
the split Canticle of Moses) with only the antiphons swapped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 09:32:25 -04:00
will b8cb636446 Give the Matins ferial hymn its own text per weekday, not one for all
matins-hymn-ferial.yml turned out to already be Monday's own hymn
("Somno reféctis ártubus"), mislabeled as a single fixed year-round
text and used unconditionally for every weekday -- the source
(Psalterium/Special/Matutinum Special.txt) genuinely varies this hymn
Monday through Saturday, the classic "week of hymns". Caught first on
Friday ("Tu Trinitátis Únitas" expected, Monday's text shown), then
again on a real Ember Saturday ("Summæ Deus cleméntiæ" expected).

Authored matins-hymn-ferial-{tuesday,wednesday,thursday,friday,
saturday}.yml (Monastic 1617 text, live-verified against the CGI
instance directly, including Friday's own real M-track word-swaps) and
wired resolveMatinsHymn's final fallback tier to pick by day.weekday.
Sunday is left on the existing placeholder, documented as a real,
more complex not-yet-understood gap (a two-hymn source split).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 09:32:05 -04:00
will 4b6d55d56c Recombine Ember September Friday's split Matins homily into one reading
Lectio1-3 in ember-september-friday.yml were one continuous, unbroken
Gregory homily on Luke 7:36-50, split only to fill the fixed 3-lesson
Nocturn slot count -- the "source doesn't genuinely split" case this
store's own established default (post-pentecost-15.yml) already
recombines elsewhere. User caught this live on /2026-09-18/matins
showing 3 identically-labeled lessons in a row. Keeps Lectio1's own
responsory per the "first one, when several collapse" convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 09:31:51 -04:00
will f7ff8fc0ae Fix Ezekiel spelling in the generated book-incipit heading
Deploy / deploy (push) Successful in 2m28s
The ezek incipit's English heading read "...Prophecy of Ezechiel" --
the Vulgate/Douay-Rheims spelling, inconsistent with this app's own
generated heading text elsewhere. Left every other in-corpus
"Ezechiel" alone (quoted Douay-Rheims verse/martyrology/homily prose,
where the traditional spelling is correct as source text).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 07:13:17 -04:00
will 3c8ccc3cbb Stop pooling the governing Sunday's own Matins content on a weekday
nocturnReadingIds pooled the governing Sunday's own nocturn-readings
id (temporalId) and its month-week-N sibling whenever any temporal
commemoration survived on a 3-nocturn day, with no weekday check --
so a Duplex+ saint winning outright on a plain weekday that also
carried a temporal commemoration pulled in a whole second homily's
worth of unrelated Sunday content. Live-verified against the
reference engine (Tridentine 1906, St. Joseph of Cupertino's day):
real Nocturn III there is just the winning saint's own Common homily
plus a single "Commemoratio Feriae" lesson -- no Sunday content at
all. Gates temporalKept to day.weekday === 'sunday'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 07:13:07 -04:00
will 290bbac1b5 Route bible-plan incipit/responsory lookup through the book-alias table
bible-plan.ts's *.yml import uses English-style book abbreviations
("jdt", "1kgs", "luke", ...) for ~30 books where that diverges from
this store's canonical form ("jdth", "3reg", "luc", ...).
scripture/index.ts's own BOOK_ALIASES already normalized this for
verse-text lookup, but the incipit (bible-book-incipits.ts) and
responsory (matins-responsories.ts) lookups used the raw abbreviation
directly, silently missing on any canonically-keyed book and falling
back to a bare "Reading" heading. The 3 Gospel aliases self-healed via
a separate citation-regex fallback that only exists for Gospels; every
other aliased book (Judith, Kings, Samuel, Esdras, Osee, Jonas, Josue,
Micheas, Abdias, Judges, the Johannine epistles, James, Jude, Philemon,
Titus, Zacharias, Sophonias) had no such fallback. Exports
canonicalBook from scripture/index.ts and routes both lookups through
it -- one shared normalization instead of two independently-aliased
ones that can drift again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 07:12:55 -04:00
will 88e8a5b404 Name Ember days in the day label, demoted or winning outright
calendar/day-label.ts never surfaced an Ember day's own identity
anywhere: collectCommemorations only matched a temporal commemoration
against resolveTemporalId(day.date), never an Ember-relabeled id, and
the winning-outright fallback used the generic ordinal weekday label
with no id awareness at all. Adds a standalone emberDayLabel lookup,
deliberately kept out of temporal-feasts.ts since that table doubles
as isFerialOrVigil's bareness check and would have silently turned
off Ember days' own ferial Preces.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 07:12:32 -04:00
will 03d39dc911 Author Ember days' own Benedictus/Magnificat antiphons
Live-verified (Monastic Tridentinum 1617) that an Ember day's Lauds
psalmody stays on the plain ferial default throughout -- only the
Benedictus antiphon (and, Wednesday/Friday only, the Magnificat
antiphon) is genuinely proper to the day. Adds the 6 Benedictus +
4 Magnificat antiphon files (Ember Saturday's Second Vespers is
always ceded to the following Sunday on both quarters, so no
Magnificat content exists to author there).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-04 07:12:01 -04:00
will bcbc93732f Log the Sunday Matins Gospel-pericope sweep in TODO.md
Deploy / deploy (push) Successful in 1m54s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 17:07:14 -04:00
will 750bea4cff Add full Gospel pericopes to post-Pentecost Sunday Matins readings
Closes the gap this whole sweep started from: Nocturn 3 on
post-pentecost-13/14 showed St. Augustine's homily with no Gospel
citation or text above it. Fixes all remaining post-pentecost-NN ids
(01-14, 16-24; -15 already had this from the original mechanism proof)
the same way as the rest of the sweep, plus Trinity Sunday's second
Nocturn 3 pair — the "Commemoration of the First Sunday after
Pentecost," whose Gospel (Luke 6:36-42) has no live date to source from
since Trinity always occupies that slot, so it's sourced from the
reference engine's preserved pre-displacement Mass propers file
instead. Also fixes a stray-space formatting glitch in
post-pentecost-19's existing homily text, found while sourcing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 17:07:10 -04:00
will 1dc8e3700a Add full Gospel pericopes to Eastertide Sunday Matins readings
Same gap and fix as the Advent/Christmas and post-Epiphany sweeps, for
easter-octave, easter-3 through -6, and sunday-after-ascension. Also
fixes stray leading "v." verse-marker artifacts left over in three of
the existing English homily texts (easter-4/5/6), found while sourcing
the paired Gospel text alongside them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 17:06:46 -04:00
will 01ffb25057 Add full Gospel pericopes to post-Epiphany through Palm Sunday Matins readings
Same gap as the Advent/Christmas sweep: Nocturn 3's patristic homily
appeared with no Gospel pericope before it. Authors the full text for
post-epiphany-1 through -6, septuagesima/sexagesima/quinquagesima,
lent-1 through -4, passion-sunday, and palm-sunday.

Palm Sunday needed a different source than the rest: its Missale
[Evangelium] tag is the Passion narrative (Matt 26-27) proclaimed at
Mass, not the entry-into-Jerusalem pericope (Matt 21:1-9) its own
Nocturn 3 homily is actually about — that shorter Gospel is instead
read at the Blessing of Palms, sourced from there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 17:06:41 -04:00
will b7b5a63756 Add full Gospel pericopes to Advent/Christmas Sunday Matins readings
Nocturn 3 previously dropped the bare "Et réliqua" Gospel incipit and
kept only the patristic homily, so the Gospel citation and text never
appeared. Authors the full pericope before the homily for advent-1
through advent-4 and christmas-octave-sunday, sourced from the
reference engine's Missale Gospel files. Authoring Advent 4's Gospel
surfaces a genuine second Matins Gospel on the rare Dec-24-as-Advent-4
collision (2023/2028), so the affected test now expects both readings
instead of one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 17:06:33 -04:00
will d8bb32bf45 Label unattributed hagiographic Matins readings by saint, not "Reading"
Deploy / deploy (push) Successful in 1m54s
A saint's vita reading (Nocturn 2, no patristic `source` attribution)
fell through to the UI's generic "Reading" heading. Add
fallbackHagiographicLabel, which looks up the contributing id's own
saint/temporal-feast record and produces "On St. Pius X, Pope and
Confessor" style labels instead — same plain-string convention as an
authored `source`.

Updates two matins.test.ts assertions that used label shape (string
vs object) as a proxy for "not a bible-plan reading" — no longer
reliable now that vita readings also carry a string label, so they
key off citation presence instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 16:16:46 -04:00
will 34c585726d Fix scripture-plan Gospel readings labeled generic "Gospel"
Deploy / deploy (push) Successful in 1m51s
Bible-plan Gospel entries in buildReadingPool never had an authored
label, so they fell back to the UI's generic "Gospel" heading. Reuse
getGospelIncipitFromCitation (already used for proper nocturn-reading
Gospels) to derive the real "A reading from the Holy Gospel
according to..." incipit from the citation when no label was
authored.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 16:07:26 -04:00
will 359c7bfc49 Log the seasonal hymn-doxology work in TODO.md
Deploy / deploy (push) Successful in 2m2s
Closes the "unexamined gap" open-work item and records both passes (the
source-marker audit, then the deliberate broadening) as dated build-log
entries, including the extraction-script bug caught and reverted before
anything was committed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 15:00:26 -04:00
will 46b34493e2 Wire seasonal hymn-doxology into every plain-default Lauds hymn
Lauds' Monastic source never carries the "*" substitution marker anywhere
(audited), so this is purely the same deliberate broadening as the Vespers
commit: every plain-default hymn (Sunday, Mon-Sat, Advent, Lent,
Passiontide, Paschaltide) now routes its closing doxology through
getHymnDoxologyId/appendDoxology instead of a fixed ending, split into
matching per-annum companion files that reproduce the original text
whenever no season/feast override applies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 15:00:17 -04:00
will 0736c36494 Wire seasonal hymn-doxology into every plain-default Vespers hymn
Sunday's hymn ("Lucis Creator") is the one case whose Monastic source text
carries a real "*" seasonal-substitution marker (live-checked); every other
key (Mon-Sat, Advent, Lent, Passiontide, Paschaltide) is wired the same way
anyway as a deliberate broadening beyond the source, at the user's request
("the doxology is a neat way to stay tuned in to the season... we're doing a
remix, not a copy"). Each hymn's own natural ending is split into a matching
per-annum companion file, so a season/feast with no override reproduces the
original text exactly; only Nat/Epi/Pasch/Asc/Pent/Corp/Heart and the two
feast/date overrides from the previous commit actually change anything.

Real visible effect: hymns shown during Christmastide/Epiphanytide/Corpus
Christi/Sacred Heart (none of which get a distinct seasonal hymn, so these
were always the plain weekday defaults) now pick up the right doxology
instead of a generic ending; Ascensiontide and the Pentecost octave now
diverge from the rest of Paschaltide's doxology mid-season.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 15:00:06 -04:00
will aeda043577 Wire seasonal hymn-doxology into the Matins ferial hymn
The plain ferial hymn's Monastic source (Day1-6 Hymnus,
Psalterium/Special/Matutinum Special.txt) carries a real "*" seasonal-
substitution marker, live-checked against the reference engine. Splits the
hymn's own natural doxology ending into a new per-annum companion file and
swaps it via getHymnDoxologyId/appendDoxology, same pattern already used by
Prime and Compline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 14:59:49 -04:00
will 44feb8c6b1 Widen seasonal hymn-doxology to cover Marian Saturday and the Assumption octave
getHymnDoxologyId now takes the whole LiturgicalDay (needed for a date-window
check) and checks a feast override for a temporal winner's id too, not just
sanctoral — matching Divinum Officium's own Common-of-the-BVM `Doxology=Nat`
rule, which is why Marian Saturdays get the Nativity doxology regardless of
season. Also ports the source's Aug 16-22 (Assumption octave) hardcoded
Nativity-doxology window, confirmed live not excluded for the Monastic/
Tridentine 1617 track.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 14:59:32 -04:00
will be4759d413 Split date picker/audio out of TODO.md's mechanism gaps
Deploy / deploy (push) Successful in 1m48s
They're new product surface, not something blocking a content pass —
give them their own "New features" tier instead of "Mechanism gaps".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 14:10:46 -04:00
will 998c549e51 Replace generic "Reading"/"Gospel" headings with real incipits
Deploy / deploy (push) Successful in 1m50s
Bible-plan readings now carry a generated bilingual incipit label
(bible-book-incipits.ts) instead of falling through to the UI's generic
"Reading" heading. Gospel pericopes (whether from the user's own plan
or nocturn-readings.ts's proper content) get the same treatment via a
new label field on the 'gospel' ResolvedPart, recovering the book from
the reading's own citation when no raw book code is available.

Updates 3 matins.test.ts assertions that used "no label" as a stand-in
for "this is a bible-plan reading" — no longer true now that these
readings carry a real label, so they now key off the label's shape
(string = patristic attribution, object = generated incipit) instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 14:02:58 -04:00
will 56321ab276 Add traditional book incipits for scripture reading labels
Maps every book abbreviation under src/data/scripture to its bilingual
liturgical incipit (e.g. "Léctio libri Sapiéntiæ" / "A reading from the
book of Wisdom"), sourced from the reference engine's own headers where
available. English uses "Saint Paul"/"Saint James"/etc. rather than
"blessed" per common modern usage; Latin keeps the traditional "beáti".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 14:02:49 -04:00
will 64a5634557 Collapse consecutive bible-plan chapters into a citation range
Deploy / deploy (push) Successful in 1m51s
Multi-chapter readings (e.g. Job 34-37, authored as one YAML entry per
chapter) were rendering as "Job 34; Job 35; Job 36; Job 37" instead of
the compact "Job 34-37". Verse-qualified and non-consecutive citations
are unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGjUyhUZJaSjiniEmnLdak
2026-09-03 11:46:34 -04:00
will d660f9e531 Give post-pentecost-15's homily reading its own Duo Séraphim responsory
Deploy / deploy (push) Successful in 1m46s
Every other post-Pentecost week's own file already attaches "Duo
Séraphim" (the closing responsory before Te Deum) to the hour's *last*
reading, overriding the usual "first one, when several collapse"
convention for exactly this responsory. post-pentecost-15 was the only
week not following that pattern -- its last (homily) reading kept
Lectio 10's own responsory instead. Swapped it in, matching the other
23 weeks; removes the now-resolved "needs an hour-level responsory
slot" note from TODO.md, which was chasing a problem that didn't
actually exist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 07:42:02 -04:00
will 10fd8515b9 Correct and relocate the Duo Seraphim and temporal-categories notes
Deploy / deploy (push) Successful in 1m51s
Duo Seraphim: the old note claimed it "isn't modeled" as a blanket
statement, but it's actually authored as a regular reading's own
responsory in 23 of 24 post-Pentecost weeks -- only post-pentecost-15
lacks it (its 3 readings don't include the one it historically closes).
Rewrote to state the real scope and moved to Mechanism gaps: the actual
gap is the schema having no hour-level closing-responsory slot, not a
missing content item.

temporal-categories.yml: the old note said the whole season->category
mapping was unverified, but the 2026-09-01 sweep (see that file's own
header) already fully live-verified `bySeason`. What's still open is
narrower -- a few privileged-period tier classifications (Ash Wednesday/
Holy Week's major tier on prior knowledge, Vigil of Christmas/Pentecost
Ember days defaulted as a guess, Pentecost Ember days' precedence
flagged in-file as a real unverified gap).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 07:38:13 -04:00
will 1725ce9186 Further trim TODO.md's Open Work per follow-up feedback
Deploy / deploy (push) Successful in 1m51s
- Removed the getDayCollects multi-collect-rendering note entirely.
- Moved the suffrage/ferial-Preces Paschaltide-substitution gap into
  Mechanism gaps, trimmed to just the actual gap (the alleluia-form
  "Suffragium Paschale" swap isn't modeled) rather than the prior
  verification-status framing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 07:34:50 -04:00
will 3794903c68 Alias common-of-a-confessor-pope to common-of-a-confessor-bishop
Deploy / deploy (push) Successful in 1m51s
While auditing the Lauds/Vespers Common-category psalmody note for
staleness, found 3 saints (St. Pius X, St. Leo II, St. Pius V) use
common: common-of-a-confessor-pope, which had no alias in either
category file -- unlike common-of-a-confessor-doctor, which already
does. Live-verified (votive=C4 vs C4b, both Vespers and Lauds,
Monastic Tridentinum 1617): psalms/antiphons are byte-identical to
Confessor-Bishop's, only the Magnificat antiphon and collect differ
(both already handled per-saint, not through this table).

Note: this doesn't change any currently-visible content -- all 3
saints are ranked duplex/semiduplex, below the duplex-majus+ threshold
this psalmody-override tier is gated at, so they correctly fall to the
plain ferial default regardless. It closes a real data-completeness
gap for whenever a duplex-majus+ Confessor Pope is added.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 07:26:19 -04:00
will c412086e0a Clean up TODO.md's Open Work section on request
- Removed the bogus St. Michael Sep 29 note from section 3 (user: "it's
  bogus").
- Removed the ferial-Preces note from section 3 -- it documents a design
  decision, not a simplification; moved to CLAUDE.md's "Not a
  reconstruction" list and a new memory entry instead.
- Moved the hymn-doxology note from section 3 to section 1 (mechanism
  gaps) and broadened its scope: checked which hours actually wire in
  the existing getHymnDoxologyId/appendDoxology mechanism (only Prime
  and Compline do), confirmed Terce/Sext/None are genuinely fine as
  documented, and found Lauds/Vespers/Matins are all equally
  unexamined -- not just the "5 of 30 Vespers hymns" the old note
  undersold this as.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 07:26:11 -04:00
will 2e39fd508a Remove completed items from TODO.md's Open Work section
Deploy / deploy (push) Successful in 1m56s
The dated log at the bottom already carries full detail for each; the
Open Work section should list only what's actually open, not linger on
struck-through DONE bullets (Easter's own octave, bilingual header
nameLa, Matins temporal-cycle readings, bulk scripture import).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 07:14:14 -04:00
will aa287ab806 Recover isolated single-verse scripture gaps that were transient, not real
Deploy / deploy (push) Successful in 1m57s
33 chapters from the first-pass "Unbound Bible" import each had one lone
missing English verse (matt-12:43's in-file "Transfer interrupted!"
download-corruption artifact among them). Rather than assume these were
genuine source gaps, checked every one against drbo.org's per-verse
text: 33 of 34 turned out to be real, present verses the old scrape had
simply dropped, now filled in.

The remaining 10 are confirmed genuine: drbo.org's own Douay-Rheims
numbering for these chapters stops one verse short of the Vulgate's own
count too (checked each chapter's actual max verse number there) -- a
real versification difference, not a scrape artifact, so these stay
honestly missing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 06:52:40 -04:00
will b29ce0551f Re-source the corrupted/missing scripture chapters from drbo.org
Deploy / deploy (push) Successful in 1m53s
The "Unbound Bible" source used for the previous pass had real defects
in 12 chapters (Douay-Rheims verses shifted/merged under the wrong
number, e.g. "Isaias 25:1" actually holding 25:11's text) and was
missing Daniel 4-14/Esther 11-16 outright.

Found a better source: drbo.org's combined "DR + LV" page per chapter
keeps each verse's Latin and English in the same HTML block, making
the earlier misalignment structurally impossible. Fetched and parsed
all 28 real chapters from it (bar-2; isa-5/25; joan-15; rom-9/11;
sir-10/19/23/41/51; dan-4..14; esth-11..16), spot-checked several
against known-correct text, and confirmed sir-52 isn't a real chapter
at all (Sirach only has 51 -- the old source's "52" was bogus).

Store now at 1185 chapter files with zero remaining gaps against every
book/chapter citation the app actually uses. Repoints
scripture.test.ts's "unauthored chapter" case from dan-14 (now
authored) to gen-51 (Genesis only has 50 chapters).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 06:41:17 -04:00
will 4bc5fdd77f Complete the bulk Vulgate/Douay-Rheims scripture import from the real source
Deploy / deploy (push) Successful in 1m49s
Found 137 chapters missing from src/data/scripture/ despite the recent
bulk import, scattered oddly across ~30 otherwise-covered books. Located
the original "Unbound Bible" source archives (douay_rheims.zip/latinv.zip)
the earlier import must have used but never preserved in-repo, and
re-parsed directly from them.

118 new chapters authored, plus the 3 hand-authored short books (Jude,
Obadiah, Philemon) replaced with source-derived text — small wording
differences from the hand-typed drafts confirm going back to source was
worth it. Store now at 1157 chapter files.

Deliberately left unauthored rather than risk bad content:
- Daniel 4-14 and Esther 11-16: this digitization's Latin side only has
  Daniel 1-3 and Esther 1-10 at all (English has the rest).
- 12 chapters where the Douay-Rheims side has a corrupted, misnumbered
  block (e.g. isa-25's tagged verse 1 actually carries verse 11's text)
  rather than simply missing content -- importing under the source's own
  wrong verse numbers would silently attach mismatched text to real
  citations, worse than leaving them missing.

Widened bulk-import.test.ts's invariant to tolerate up to 2 genuinely
isolated missing-English verses per chapter (extends the pre-existing
isa-33/sir-36 pointing-split-half-verse allowance to also cover true
source gaps, e.g. an in-file "Transfer interrupted!" download-corruption
artifact at matt-12:43), while still failing loudly on anything
resembling the corrupted-block pattern above.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 06:13:56 -04:00
will cd53805c83 Author Jude, Obadiah, and Philemon — books the bulk scripture import missed
Deploy / deploy (push) Successful in 1m45s
Three single-chapter books referenced by bible-plan.ts's readings had no
corresponding file in src/data/scripture/ at all, unlike everything else
covered by the Vulgate/Douay-Rheims bulk import — apparently dropped by
whatever produced the source. Authored by hand (Clementine Vulgate /
Douay-Rheims 1899 American Edition) and wired via new BOOK_ALIASES
entries (jude->jud, obad->abd, philem->phlm) so bible-plan's existing
citations keep their familiar English display form.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 05:51:20 -04:00
will 493c532469 Drop verse-number prefixes from scripture-sourced Matins readings
Deploy / deploy (push) Successful in 1m47s
resolvePassages() prepended each verse's number ("1 Et factum est...")
when joining scripture verses into continuous reading text — visible
today in Job's Matins reading. Not wanted; join the verse text alone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 05:43:12 -04:00
will 63f48d26d2 Fix Matins bible-plan readings stuck on stale "translation pending"
resolveReading() in bible-plan.ts preferred each YAML row's hardcoded
status: { la: missing, en: missing } over computing status from the
actually-resolved text. That field was accurate before the Vulgate/
Douay-Rheims bulk import but was never cleared once real text landed,
so every Matins bible-plan reading kept showing as untranslated
regardless. Strip the stale field from all 368 files so status falls
through to the real compute-from-resolved-text path.

Also alias book abbreviations in scripture/index.ts's lookup: the
bulk-imported store uses Vulgate-numbering codes (sap, jac, joan,
1reg..4reg, 1par/2par, 1esdr/2esdr, ...) matching octave-readings.ts's
existing convention, while bible-plan.ts's TSV import used common
English abbreviations (kgs, sam, chr, john, jas, luke, mark, ...) for
the ~30 books where the two conventions diverge. Alias at the lookup
layer so citations keep their familiar English display form.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 05:43:07 -04:00
will 5ebb856de7 Bulk-import the Vulgate/Douay-Rheims scripture text
Deploy / deploy (push) Successful in 1m54s
Adds ~1017 chapter YAML files under src/data/scripture/ carrying verified
bilingual (Latin Vulgate / Douay-Rheims English) verse text, closing out
the bulk scripture import TODO.md has tracked as pending bulk-content
work. Daniel 14 (the Greek deuterocanonical addition) has no counterpart
in the Latin source and stays unauthored.

Raises the PWA precache size cap (8 -> 24 MiB) to fit the larger bundle,
repoints scripture.test.ts's "unauthored chapter" case from Genesis 1
(now authored) to Daniel 14, and adds bulk-import.test.ts asserting every
imported chapter has verified, gap-free, ordered verse text in both
languages -- tolerating the pre-existing isa-33/sir-36 pointing-split
half-verses, whose English intentionally isn't split to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Je2mXNHoXmi1DS3xEL67rH
2026-09-03 05:31:11 -04:00
will 90665429ae Author Easter Octave Wednesday-Saturday's shared Matins antiphons
Deploy / deploy (push) Successful in 1m28s
All 4 weekdays share the exact same 2 antiphons verbatim ("Jesum qui
crucifíxus est quǽritis, allelúja..." / "Veníte et vidéte locum ubi
pósitus erat Dóminus, allelúja, allelúja."), live-verified against
Pofficium.pl (Monastic Tridentinum 1617, prayMatutinum) across
2026-04-08/09/10/11 — despite each day drawing on a different Gospel
reading in the nocturn-readings pool, the antiphon layer doesn't vary
by day.

Layered onto vu's own already-fixed per-weekday ferial psalm numbers
rather than the reference engine's own differently-sized (Rule of
Benedict summer-contracted) psalm set for these days, consistent with
this project's deliberate rejection of that contraction.

This closes "Easter's own octave" fully: calendar precedence
(2026-09-01), Sunday/Monday/Tuesday's shared proper psalmody, and now
Wednesday-Saturday's antiphons (both 2026-09-02).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X26BkFT3ARbtUGXuBmzUmD
2026-09-02 07:51:34 -04:00
will f78222ce9e Let a temporal winner's ferial branch use its own named antiphons
Ferial-side counterpart to the Sunday-branch temporal-override
mechanism added earlier today: hours/matins.ts's ferialAntiphonedNocturn
now checks a new matins-ferial-named-antiphon-overrides.yml store first,
before falling back to the plain per-weekday default. An override's
antiphon list is distributed evenly across the weekday's own
already-fixed psalm-group count rather than tied to specific psalm
numbers, since one entry is meant to be reused across several
different weekdays (each with a different psalm set) instead of
authored per-day. Same optional weekdays field as the Sunday-side
override restricts which days of a multi-day temporalId span actually
share the content.

General-purpose, not scoped to any one feast — content added next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X26BkFT3ARbtUGXuBmzUmD
2026-09-02 07:51:06 -04:00
will 57acbeefe9 Author Easter Sunday/Monday/Tuesday's shared proper Matins
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
2026-09-02 07:42:39 -04:00
will c2b24ae77d Let a temporal winner reach Matins' 3-nocturn proper psalmody
hours/matins.ts's threeNocturns gate and Sunday-branch dispatch only
ever fired for a literal Sunday or a sanctoral winner — a temporal
winner (e.g. Easter Monday/Tuesday, sharing Easter Sunday's own
winner id) had no path into 3-nocturn proper content at all, even
with a matching named override authored for it.

Generalizes hours/matins-sunday-named-nocturn-overrides.yml's lookup
so a temporal winner with an authored entry is eligible too, reusing
the same sundayPsalmNocturn/sundayCanticleNocturn render functions
the Sunday and saint-proper branches already share. A new optional
weekdays field lets one entry restrict itself to specific days of a
temporalId that spans more calendar days than actually share its
content (needed by the Easter content this unblocks, added next).

Built as a general-purpose feature, not scoped to Easter specifically
— any temporal feast that wants its own proper psalms/lessons can now
reach this path by adding a named-override entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X26BkFT3ARbtUGXuBmzUmD
2026-09-02 07:42:06 -04:00
will f4d4eb3738 Lower Matins 3-nocturn threshold from Duplex to Semiduplex
Deploy / deploy (push) Successful in 1m31s
Semiduplex and Duplex should share the same 3-nocturn/3-psalms-each
shape (distinct from Sunday/Duplex-majus+'s 6/6/3-plus-canticles
scheme) — the previous Duplex floor meant a Semiduplex winner like
St. Stephen of Hungary wrongly fell all the way through to the
1-nocturn ferial branch.

New threshold is a separate check from isDoubleOrHigher, which drives
the unrelated antiphon-doubling rule elsewhere and stays floored at
Duplex — the two thresholds only coincided by accident before this
change. No new content authoring was needed: ferialPsalmodyThreeNocturns
already chunks the ferial 9-psalm pool into 3/3/3 as the fallback for
a plain-Duplex winner with no override authored, and Semiduplex
winners now route into that same already-working branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X26BkFT3ARbtUGXuBmzUmD
2026-09-02 07:07:12 -04:00
will 9da5a74301 Remove spurious mid-nocturn versicle from ferial Matins
The 1-nocturn ferial branch was rendering a V./R. versicle stuck
mid-list, as if marking a nocturn boundary that doesn't exist in a
single continuous nocturn. Live-verified against the reference engine
(Pofficium.pl, Monastic Tridentinum 1617) for Wednesday and Thursday:
the real office runs straight from the last psalm to the Capitulum,
with no versicle anywhere in the psalmody. The embedded mid-list V./R.
line in the source Psalmi matutinum.txt is inert there and was never
actually emitted by the engine — matins-ferial-antiphons.yml's
versicle field was a documented-but-unverified assumption, not real
content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X26BkFT3ARbtUGXuBmzUmD
2026-09-02 07:06:21 -04:00
will 9d39d162ec Strip stray Vulgate verse-number markers from psalm text
Deploy / deploy (push) Successful in 1m28s
Several psalm files carried leftover inline markers like (6a), (17a),
(32) from the source text (e.g. Ps 67:2-19). vu doesn't number verses
inline anywhere else, so these were artifacts, not content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015yHHNsbzD8jLkkmVuJpAcC
2026-09-02 06:31:08 -04:00
will b54fcb30e6 Reclassify Passion week ferias from ordinary-feria to privileged-feria
Deploy / deploy (push) Successful in 1m35s
Passion week's ferias (Mon-Sat before Palm Sunday) fell through to
passiontide's bare ordinary-feria default, letting any real feast win
outright regardless of rank. Live-verified against the reference engine:
swept every date that can land in this window across a century of Easter
dates and found zero sub-Duplex winners -- every real winner is Duplex+.
This was letting vu's own low-ranked calendar saints (e.g. Ss. Tiburtius/
Valerian/Maximus, St. Anicetus) trivially win there and wrongly restore
the responsory's closing Gloria on what should stay a penitential day.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015yHHNsbzD8jLkkmVuJpAcC
2026-09-02 06:21:02 -04:00
will 3f9b748e4a Fix Matins test part-index offsets for new opening versicle
Deploy / deploy (push) Successful in 1m28s
Inserting the new Domine-labia versicle shifted every downstream
ordo.parts index/destructure by one. Pure index-shift fixups, no
behavioral assertions changed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015yHHNsbzD8jLkkmVuJpAcC
2026-09-02 05:54:15 -04:00
will db23523a5d Add Domine labia mea aperies (x3) to Matins opening
The Monastic-rite versicle "V. Dómine, lábia mea apéries. R. Et os
meum annuntiábit laudem tuam" was entirely absent from Matins' opening
block — only the shared "Deus in adjutórium" versicle was modeled.
Authored per the reference engine's "iij" rubric (recited three times,
after Deus in adjutórium, before Psalm 3), with the English rendered
as imperative ("O Lord, open Thou my lips") rather than future
indicative.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015yHHNsbzD8jLkkmVuJpAcC
2026-09-02 05:54:10 -04:00
will 5b3dc2e9d5 Redesign the Easter-octave backlog: floor-gated, one per day
Deploy / deploy (push) Successful in 1m28s
Replaced the multi-day cascading queue with the actual intended design:
only candidates at or above duplex-majus are worth carrying out of the
Palm-Sunday-through-Low-Sunday span at all - below that, a candidate
simply lapses for the year. Of those that clear the floor, the Nth day
after Low Sunday celebrates the Nth one (native-date order), winning
outright with no rank fight, demoting whatever's natively there to a
commemoration - one per day, positional, not a free-day search.

Caught a real bug while re-verifying: the ordinary single-hop transfer
check was independently leaking Low Sunday's own signal into the next
day, letting a below-floor candidate bypass the new gate. Fixed by
excluding the whole span from that path.

Inventoried the only duplex-majus+ candidates that can ever fall in the
reachable window (St. Benedict, St. Gabriel the Archangel, the
Annunciation, St. Mark, Ss. Philip & James), then bumped St. Patrick's
own rank to duplex-majus by direct instruction, adding him as the 6th.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 15:06:55 -04:00
will fc4ac33903 Fix privileged-feria-major: nothing is ever commemorated, any rank
Deploy / deploy (push) Successful in 1m32s
The Easter octave cascade built last commit reused the existing
"duplex-majus+ still gets a bare commemoration" branch unchanged,
trusting its own citation (St. Mark within the Easter Octave). That
citation was a misreading. Live-verified: the Annunciation (Duplex I.
classis) transfers with zero commemoration on Holy Thursday itself, and
Chair of St. Peter at Antioch does the same on Ash Wednesday - the
exception was wrong everywhere this category is used, not just Holy
Week. privileged-feria-major now matches privileged-sunday exactly:
unconditional transfer, no exceptions, any rank.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 14:08:15 -04:00
will d6c8e8b7a8 Build the Easter octave transfer cascade
Deploy / deploy (push) Successful in 1m32s
Closes the tabled "Easter's own octave" mechanism gap. resolveDay only
ever checked yesterday/tomorrow for an inbound transfer - a single hop -
so anything impeded in Holy Week just vanished rather than reappearing
after the Octave. Live-verified the real behavior first (2033 proof
year): a candidate skips the whole privileged span and keeps walking
forward, FIFO by native date, until it lands; a transferred-in candidate
never gets the "still gets a nod" commemoration a native occurrence of
the same rank would along the way (confirmed with the Annunciation, zero
commemoration anywhere in the span). Also fixed Low Sunday itself, which
needed the same privileged-sunday treatment as Easter Day.

Built resolveForwardTransferLanding: a bounded queue simulation that
reuses applyIncomingTransfer unchanged per hop - its existing
privileged-feria-major no-op was already the right single-hop refusal,
it just needed a real cascade around it instead of a dead end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 12:25:21 -04:00
will 4adbb71ddf Full precedence sweep of temporal-categories.yml, two more real bugs
Deploy / deploy (push) Successful in 1m27s
Following the Ascensiontide fix, swept every remaining unverified Sunday/
feria precedence tier against the live reference engine:

- commemorations.ts's privileged-sunday rule was itself wrong: a
  duplex-majus+ candidate was given a bare commemoration instead of
  transferring, never actually verified. Live-checked the Immaculate
  Conception (transfers off Advent II) and the Annunciation (transfers
  off Palm/Easter Sunday) - confirmed correct by direct instruction.
  privileged-sunday now transfers every rank unconditionally.

- eastertide's own Sunday tier was backwards (privileged-sunday instead
  of ordinary-sunday) for the ordinary Sundays after Easter - live-
  verified twice (Finding of the Holy Cross, St. Mark both win outright).
  Easter Day itself still needs the stronger tier, now special-cased
  directly since Sundays never consult the offset table.

Six existing tests updated to reflect real, live-verified behavior - each
had the old wrong "commemorated in place" assumption baked into its
expected value. Remaining unverified entries (Vigil of Pentecost/
Christmas, Corpus Christi/Sacred Heart) documented as such rather than
silently assumed correct.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 11:45:04 -04:00
will 6b66999908 Fix Ascensiontide Sunday precedence and give it its own Matins proper
Deploy / deploy (push) Successful in 1m27s
The Sunday within the Octave of Ascension was letting a Duplex saint win
outright (ascensiontide's Sunday was miscategorized ordinary-sunday
instead of privileged-sunday, the only "great octave" season not already
so classed). Live-verified the reference engine keeps the Sunday primary
and transfers the saint instead. Fixed the category, then authored the
Sunday's own wholly proper Matins (a different psalm scheme entirely, not
a seasonal overlay) via a new per-temporalId override layer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 09:58:40 -04:00
will 05f82a3556 Give ordinary Paschaltide Sundays their own Matins Nocturn I-III content
Deploy / deploy (push) Successful in 1m26s
Same seasonal-overlay shape as Advent, live-verified against the reference
engine across 5 Paschaltide Sundays. Generalized sundayNocturnFor to a
season-keyed lookup covering both Advent and Paschaltide.

Confirmed two related non-gaps while investigating: Christmastide/post-
Epiphany Sundays have no seasonal block in the source, and a Duplex+ saint
winning outright on a Sunday deliberately still uses the fixed Sunday
psalmody per this app's own documented design (readings still surface the
saint via the separate pool) — neither needs a fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 09:31:22 -04:00
will c3e540f6c2 Give Advent Sundays their own Matins Nocturn I-III content
Deploy / deploy (push) Successful in 1m27s
Nocturns I-II had no seasonal override mechanism at all, and Nocturn III's
own Septuagesima-Palm override didn't cover Advent — live-verified against
the reference engine that Advent has its own seasonal antiphons/versicles
across all three nocturns, identical on all 4 Advent Sundays.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 09:07:46 -04:00
will 20b1401919 Log Sunday Nocturn 3 seasonal fix, close the flagged gap
Deploy / deploy (push) Successful in 1m27s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 08:17:18 -04:00
will 3d221c2a45 Give Sunday Matins Nocturn III its own antiphon Septuagesima-Palm Sunday
It was hardcoded to "Allelúja, allelúja, allelúja" year-round. Each of
the 9 Sundays from Septuagesima through Palm Sunday has its own
Gospel-tied antiphon instead, live-verified against Monastic Tridentinum
1617; Lent 1-4 also swap in their own canticles, and Lent 1-4 plus
Passion/Palm Sunday swap in their own versicle. New
matins-sunday-nocturn3-overrides.yml, keyed by temporalId and merged
over the base nocturn3 group by hours/matins.ts's new
sundayNocturn3For — only the real-Sunday branch uses it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 08:17:14 -04:00
will 36e2cc60cd Add Jeremiah 14, Lamentations 5, and Ezekiel 36 scripture chapters
Lent 1-4's Sunday Matins Nocturn III canticles, transcribed from a live
Divinum Officium query (Monastic Tridentinum 1617). First content under
the `lam` book code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XMSokiTD2Qc5vPP2YQu3Q
2026-09-01 08:17:04 -04:00
will 97b5a5d7f4 Log ferial antiphon fix and flag Sunday Nocturn 3 Alleluia bug
Deploy / deploy (push) Successful in 1m28s
Records the sourcing detail for the one-antiphon-per-psalm change in
the dated build log, and adds a mechanism-gap entry for Sunday
Matins' Nocturn 3 antiphon: hardcoded to "Allelúja" year-round but
confirmed live against Monastic Tridentinum 1617 to need its own
proper antiphon on each of the 9 Sundays from Septuagesima through
Palm Sunday — collected but not yet authored.
2026-09-01 07:30:44 -04:00
will afff77f75a Split ferial Matins nocturn antiphons to one per psalm
Every cross-psalm pair previously shared one antiphon under just the
first psalm, which read inconsistently once psalter-distribution.yml's
reslice left some pairs intact and split others. Each pair is now two
single-psalm groups: the psalm whose own words the shared antiphon
actually quoted keeps it, and its former partner gets a real antiphon
borrowed from elsewhere in the reference corpus where one exists, else
one composed fresh from its own opening verse.
2026-09-01 07:30:35 -04:00
1777 changed files with 182324 additions and 1974 deletions
+1832 -55
View File
File diff suppressed because it is too large Load Diff
+32 -12
View File
@@ -143,11 +143,22 @@ export function decideOccurrence(
}; };
case 'privileged-feria-major': case 'privileged-feria-major':
// Ash Wednesday, Holy Week, the Easter Octave, the Vigil of // Ash Wednesday, Holy Week, the Easter Octave, Low Sunday, the
// Christmas — never displaced, same as privileged-sunday. // Vigils of Christmas/Pentecost — never displaced, and never gives
if (isAtLeast(sanctoral.rank, 'duplex-majus')) { // even a bare commemoration to any rank, same as privileged-sunday.
return { winner: temporalWinner, commemorations: [sanctoralCommemoration(sanctoral)] }; // The "duplex-majus+ still gets a nod" branch this case used to
} // have was wrong, not just unverified: the one case that seemed to
// support it (St. Mark, Duplex II. classis, "merely commemorated"
// within the Easter Octave) turns out to have been a misreading —
// live-verified 2026-09-01, direct instruction ("you dont
// commemorate -anything- from palm sunday through low sunday"),
// confirmed with two more dates on top of the Annunciation/Palm-
// Easter-Sunday evidence already in the privileged-sunday case
// below: the Annunciation (Duplex I. classis) transfers with zero
// commemoration on Holy Thursday itself in years it lands there
// (2027/2032/2043), and Chair of St. Peter at Antioch (Duplex
// majus) transfers off Ash Wednesday the same way (2034) — so the
// exception was wrong for Ash Wednesday too, not just Holy Week.
return { return {
winner: temporalWinner, winner: temporalWinner,
commemorations: [], commemorations: [],
@@ -188,13 +199,22 @@ export function decideOccurrence(
}; };
case 'privileged-sunday': case 'privileged-sunday':
if (isAtLeast(sanctoral.rank, 'duplex-majus')) { // A privileged Sunday wants nothing at all, no matter how high the
// Never displaced, but a sufficiently high feast still gets a nod. // candidate ranks — not even the bare commemoration an ordinary
return { winner: temporalWinner, commemorations: [sanctoralCommemoration(sanctoral)] }; // Sunday would allow a Simplex, and no exception for duplex-majus+ —
} // everything transfers. Live-verified 2026-09-01, three independent
// Below duplex-majus, a privileged Sunday wants nothing at all — // dates: the Immaculate Conception (Duplex I. classis) transfers off
// not even the bare commemoration an ordinary Sunday would allow a // Advent II with no commemoration at all (real dates 2024/2030/2041),
// Simplex — so everything here transfers. // and the Annunciation (also Duplex I. classis) transfers off both
// Palm Sunday and Easter Sunday itself the same way (2029/2035/2040/
// 2046) — confirmed by direct instruction as the correct behavior,
// correcting an earlier, never-actually-verified "duplex-majus+
// still gets a nod" guess. (Where a transferred candidate actually
// lands is a separate, still-generic `transferDirectionOf`/next-
// open-day mechanism — the Annunciation's own real rule of jumping
// *past* the whole Easter Octave rather than landing inside it is a
// known follow-on, blocked on the not-yet-built Easter-octave
// mechanism; see TODO.md.)
return { return {
winner: temporalWinner, winner: temporalWinner,
commemorations: [], commemorations: [],
+38 -1
View File
@@ -50,6 +50,38 @@ function joinBi(items: Bi[], sep = ' — '): Bi {
return { en: items.map((i) => i.en).join(sep), la: items.map((i) => i.la).join(sep) }; return { en: items.map((i) => i.en).join(sep), la: items.map((i) => i.la).join(sep) };
} }
/**
* The 6 Ember-day ids (calendar/ember-days.ts) are deliberately *not* in
* temporal-feasts.ts's named-feast table — they're privileged ferias, not
* rank-contesting movable feasts (see that file's own doc comment), and
* that table's `getTemporalFeastRecord` also doubles as resolve-
* common.ts's `isFerialOrVigil` bareness check, so adding them there
* would silently turn off their own ferial Preces (their `[Rule]` block
* is literally "Preces Feriales"). A small standalone lookup here instead
* — Latin names read straight from each day's own `[Officium]` header
* (Tempora/093-{3,5,6}.txt, Tempora/Adv3-{3,5,6}.txt).
*
* Without this, an Ember day never named itself anywhere in the UI: not
* as the primary label when it wins outright (fell through to the plain
* "Wednesday in the Nth week after Trinity"/"In the Nth week of Advent"
* wording with no Ember mention at all), and not as a commemoration when
* demoted (collectCommemorations's temporal branch only ever matched
* `resolveTemporalId(day.date)`, never an Ember-relabeled id) — live-
* verified as a real, user-visible gap, 2026-09-03.
*/
const EMBER_DAY_NAMES: Record<string, Bi> = {
'ember-september-wednesday': bi('Ember Wednesday in September', 'Feria Quarta Quattuor Temporum Septembris'),
'ember-september-friday': bi('Ember Friday in September', 'Feria Sexta Quattuor Temporum Septembris'),
'ember-september-saturday': bi('Ember Saturday in September', 'Sabbato Quattuor Temporum Septembris'),
'ember-advent-wednesday': bi('Ember Wednesday in Advent', 'Feria IV Quattuor Temporum in Adventu'),
'ember-advent-friday': bi('Ember Friday in Advent', 'Feria VI Quattuor Temporum in Adventu'),
'ember-advent-saturday': bi('Ember Saturday in Advent', 'Sabbato Quattuor Temporum in Adventu'),
};
function emberDayLabel(id: string): Bi | undefined {
return EMBER_DAY_NAMES[id];
}
function capitalize(text: string): string { function capitalize(text: string): string {
return text.charAt(0).toUpperCase() + text.slice(1); return text.charAt(0).toUpperCase() + text.slice(1);
} }
@@ -458,6 +490,11 @@ function collectCommemorations(
} else if (c.kind === 'temporal') { } else if (c.kind === 'temporal') {
if (c.id === temporalId) { if (c.id === temporalId) {
temporal.push(anchorDayName(day, false) ?? temporalLabel(day)); temporal.push(anchorDayName(day, false) ?? temporalLabel(day));
} else {
const emberName = emberDayLabel(c.id);
if (emberName) {
temporal.push(emberName);
}
} }
} else if (opts.showOctaves && c.id !== opts.excludeOctaveId) { } else if (opts.showOctaves && c.id !== opts.excludeOctaveId) {
const octave = activeOctaves.find((a) => a.id === c.id); const octave = activeOctaves.find((a) => a.id === c.id);
@@ -663,7 +700,7 @@ export function getDayLabel(day: LiturgicalDay): Partial<Record<string, string>>
} }
const temporalRank = TEMPORAL_CATEGORY_RANK_LABELS[day.temporalCategory]; const temporalRank = TEMPORAL_CATEGORY_RANK_LABELS[day.temporalCategory];
const label = temporalLabel(day); const label = (day.winner.kind === 'temporal' ? emberDayLabel(day.winner.id) : undefined) ?? temporalLabel(day);
const temporal = bi(`${label.en} (${temporalRank.en})`, `${label.la} (${temporalRank.la})`); const temporal = bi(`${label.en} (${temporalRank.en})`, `${label.la} (${temporalRank.la})`);
const { sanctoral } = collectCommemorations(day, { showOctaves: false }); const { sanctoral } = collectCommemorations(day, { showOctaves: false });
return withTransferNote(day, joinBi([temporal, ...sanctoral])); return withTransferNote(day, joinBi([temporal, ...sanctoral]));
+112 -18
View File
@@ -1,9 +1,9 @@
import type { Commemoration, DayWinner, LiturgicalDay, SanctoralIdentity, TemporalCategory } from './types'; import type { Commemoration, DayWinner, FeastClass, LiturgicalDay, SanctoralIdentity, TemporalCategory } from './types';
import { weekdayOf } from './weekday'; import { weekdayOf } from './weekday';
import { resolveSeason, resolveTemporalCategory } from './temporal'; import { resolveSeason, resolveTemporalCategory, easterOffsetOf } from './temporal';
import { resolveTemporalId } from './temporal-id'; import { resolveTemporalId } from './temporal-id';
import { getSanctoralCandidatesFor } from './feasts'; import { getSanctoralCandidatesFor } from './feasts';
import { decideOccurrence, compareFeastClass, type OccurrenceResult } from './commemorations'; import { decideOccurrence, compareFeastClass, isAtLeast, type OccurrenceResult } from './commemorations';
import { resolveCollision } from './collision'; import { resolveCollision } from './collision';
import { addDays, toIsoDate } from './date-math'; import { addDays, toIsoDate } from './date-math';
import { easterSunday } from './easter'; import { easterSunday } from './easter';
@@ -13,10 +13,13 @@ import { applyEmberDay } from './ember-days';
/** /**
* A day's occurrence considered on its own — no awareness of what an * A day's occurrence considered on its own — no awareness of what an
* adjacent day might be trying to transfer in. `resolveDay` calls this for * adjacent day might be trying to transfer in. `resolveDay` calls this
* the date itself *and* for yesterday/tomorrow (to check for an inbound * for the date itself and for yesterday/tomorrow (to check for an
* transfer) without ever recursing into their own adjacent-day checks — * inbound transfer) without ever recursing into their own adjacent-day
* that's what keeps this from being mutually recursive. * checks — that's what keeps this from being mutually recursive. The one
* exception, `easterOctaveBacklogFor`, deliberately calls this for up to
* 15 fixed days (Palm Sunday through Low Sunday) regardless of `isoDate`
* — see its own doc comment.
*/ */
function resolveNativeOccurrence(isoDate: string): { temporalCategory: TemporalCategory; result: OccurrenceResult } { function resolveNativeOccurrence(isoDate: string): { temporalCategory: TemporalCategory; result: OccurrenceResult } {
const weekday = weekdayOf(isoDate); const weekday = weekdayOf(isoDate);
@@ -59,13 +62,17 @@ function applyIncomingTransfer(
fromDate: string, fromDate: string,
): DayWinner { ): DayWinner {
if (temporalCategory === 'privileged-feria-major') { if (temporalCategory === 'privileged-feria-major') {
// Deferred: a transfer landing on one of these (the concrete case is // Never a landing spot for any rank, and never even a bare
// Holy Week, right after Palm Sunday) needs its own Easter-date-keyed // commemoration (commemorations.ts's own case already guarantees
// lookup table, the same way the reference engine handles it — not // this — a `winner.kind === 'sanctoral'` day never actually happens
// modeled yet. The transfer is simply not delivered here rather than // in this category, so `decideOccurrence` alone would already
// guessed at. The lesser privileged-feria tier doesn't need this — // produce this same no-op; kept explicit as a documented short-
// ordinary collision/transfer logic is fine there, same as any // circuit). A candidate impeded here — Holy Week, the Easter Octave,
// ordinary-sunday landing. // Low Sunday — doesn't chain forward day-by-day the way an ordinary
// transfer does; see `applyEasterOctaveBacklog` in `resolveDay` for
// its own dedicated mechanism instead. The lesser privileged-feria
// tier doesn't need any of this — ordinary collision/transfer logic
// is fine there, same as any ordinary-sunday landing.
return winner; return winner;
} }
// Tags the returned winner with where it transferred from, but only when // Tags the returned winner with where it transferred from, but only when
@@ -88,9 +95,10 @@ function applyIncomingTransfer(
// not just take over because nothing else was assigned here. // not just take over because nothing else was assigned here.
const decided = decideOccurrence(temporalCategory, winner.id, candidate); const decided = decideOccurrence(temporalCategory, winner.id, candidate);
commemorations.push(...tagCommemorations(decided.commemorations)); commemorations.push(...tagCommemorations(decided.commemorations));
// If the candidate fails here too (decided.transfer set), that's a // If the candidate fails here too (decided.transfer set), it's simply
// transfer chain — not modeled, same "not delivered rather than // not delivered — every category reaching this branch only ever
// guessed at" stance as the privileged-feria-major case above. // needs a single hop (see `applyEasterOctaveBacklog` for the one
// multi-day case, handled separately, not through here).
return tagIfLanded(decided.winner); return tagIfLanded(decided.winner);
} }
const collision = resolveCollision(candidate, winner); const collision = resolveCollision(candidate, winner);
@@ -98,6 +106,74 @@ function applyIncomingTransfer(
return tagIfLanded(collision.winner); return tagIfLanded(collision.winner);
} }
// The minimum rank worth carrying out of the Palm-Sunday-through-Low-
// Sunday span at all (see `applyEasterOctaveBacklog` below) — anything
// weaker than this just lapses for the year, same as it already gets no
// trace *within* the span itself (commemorations.ts's `privileged-feria-
// major`/`privileged-sunday` cases). Per direct instruction (2026-09-01):
// this is the same threshold the sibling in-place-commemoration rule
// used before it was found wrong — that number wasn't the mistake, only
// which question it was answering.
const EASTER_OCTAVE_BACKLOG_FLOOR: FeastClass = 'duplex-majus';
/**
* Every candidate impeded anywhere from Palm Sunday through Low Sunday
* inclusive (`easterOffsetOf` -7 through +7) that clears
* `EASTER_OCTAVE_BACKLOG_FLOOR`, in native-date order — the backlog
* `applyEasterOctaveBacklog` draws from. Judged only by each day's own
* `resolveNativeOccurrence`, never a fully `resolveDay`-resolved one
* (same convention `applyIncomingTransfer` already uses).
*/
function easterOctaveBacklogFor(year: number): { candidate: SanctoralIdentity; fromDate: string }[] {
const easterIso = toIsoDate(easterSunday(year));
const backlog: { candidate: SanctoralIdentity; fromDate: string }[] = [];
for (let offset = -7; offset <= 7; offset++) {
const date = addDays(easterIso, offset);
const candidate = resolveNativeOccurrence(date).result.transfer?.candidate;
if (candidate && isAtLeast(candidate.rank, EASTER_OCTAVE_BACKLOG_FLOOR)) {
backlog.push({ candidate, fromDate: date });
}
}
return backlog;
}
// Generous headroom over any realistic backlog length (the whole span is
// 15 days; vu's own calendar has never produced more than a handful of
// duplex-majus+ candidates in it) — exists purely so a date far removed
// from any Easter doesn't pay for an `easterOctaveBacklogFor` computation
// it could never need.
const MAX_EASTER_OCTAVE_BACKLOG_DAYS = 60;
/**
* The Nth day after Low Sunday celebrates the Nth candidate in that
* year's backlog (native-date order) — winning outright, no rank fight
* against whatever would have natively been there, which is demoted to a
* commemoration instead — one candidate per day until the backlog is
* exhausted. Per direct instruction (2026-09-01): not a generic multi-day
* cascade/queue (this app already has real precedent for stacking
* commemorations, so a spread-out cascade wasn't the right shape), and
* not a same-day batch either (only one candidate surfaces per day, same
* as any other transfer landing in this app) — a fixed, deterministic
* position for each backlog candidate rather than a "first day not
* already spoken for" search.
*/
function applyEasterOctaveBacklog(isoDate: string, winner: DayWinner, commemorations: Commemoration[]): DayWinner {
const offset = easterOffsetOf(isoDate);
if (offset <= 7 || offset > 7 + MAX_EASTER_OCTAVE_BACKLOG_DAYS) {
return winner;
}
const n = offset - 7;
const backlog = easterOctaveBacklogFor(Number(isoDate.slice(0, 4)));
const entry = backlog[n - 1];
if (!entry) {
return winner;
}
if (winner.kind === 'sanctoral') {
commemorations.push({ kind: 'sanctoral', id: winner.id, name: winner.name, nameLa: winner.nameLa, rank: winner.rank });
}
return { kind: 'sanctoral', ...entry.candidate, transferredFrom: entry.fromDate };
}
/** /**
* Resolves everything about a given day *except* hour content — weekday, * Resolves everything about a given day *except* hour content — weekday,
* season, temporal precedence category, and the day's real winner plus * season, temporal precedence category, and the day's real winner plus
@@ -114,7 +190,19 @@ export function resolveDay(isoDate: string): LiturgicalDay {
const yesterdayIso = addDays(isoDate, -1); const yesterdayIso = addDays(isoDate, -1);
const yesterday = resolveNativeOccurrence(yesterdayIso); const yesterday = resolveNativeOccurrence(yesterdayIso);
if (yesterday.result.transfer?.direction === 'forward') { // A candidate impeded anywhere from Palm Sunday through Low Sunday
// (`easterOffsetOf` -7 through +7) is exclusively
// `applyEasterOctaveBacklog`'s to place — skipped here so it isn't
// *also* picked up by this ordinary single-hop check (real bug found
// 2026-09-01: Low Sunday's own transfer was leaking into the very next
// day's ordinary yesterday-check, landing/commemorating a candidate
// below the backlog's own rank floor). Scoped to that specific span,
// not `privileged-sunday` generally — an ordinary privileged Sunday
// elsewhere (Advent, SeptuagesimaPassiontide) still uses this normal
// single-hop path same as before.
const yesterdayOffset = easterOffsetOf(yesterdayIso);
const yesterdayInEasterSpan = yesterdayOffset >= -7 && yesterdayOffset <= 7;
if (yesterday.result.transfer?.direction === 'forward' && !yesterdayInEasterSpan) {
winner = applyIncomingTransfer(yesterday.result.transfer.candidate, temporalCategory, winner, commemorations, yesterdayIso); winner = applyIncomingTransfer(yesterday.result.transfer.candidate, temporalCategory, winner, commemorations, yesterdayIso);
} }
const tomorrowIso = addDays(isoDate, 1); const tomorrowIso = addDays(isoDate, 1);
@@ -122,6 +210,12 @@ export function resolveDay(isoDate: string): LiturgicalDay {
if (tomorrow.result.transfer?.direction === 'backward') { if (tomorrow.result.transfer?.direction === 'backward') {
winner = applyIncomingTransfer(tomorrow.result.transfer.candidate, temporalCategory, winner, commemorations, tomorrowIso); winner = applyIncomingTransfer(tomorrow.result.transfer.candidate, temporalCategory, winner, commemorations, tomorrowIso);
} }
// The one case that single yesterday-check can't reach: anything
// impeded within Holy Week/Easter Day/the Octave/Low Sunday itself
// (all `privileged-feria-major`/`privileged-sunday`, which never
// accept a landing at all) needs its own dedicated mechanism — see
// `applyEasterOctaveBacklog`'s own doc comment.
winner = applyEasterOctaveBacklog(isoDate, winner, commemorations);
winner = applyOctaves(isoDate, winner, commemorations); winner = applyOctaves(isoDate, winner, commemorations);
winner = applyEmberDay(isoDate, winner, commemorations); winner = applyEmberDay(isoDate, winner, commemorations);
+22 -1
View File
@@ -202,12 +202,33 @@ export function adventEmberDayOffset(isoDate: string): number | undefined {
* the Christmas vigil (Dec 24 can land on a Sunday) — and a Sunday's own * the Christmas vigil (Dec 24 can land on a Sunday) — and a Sunday's own
* privileged/ordinary status should win in that case regardless, so * privileged/ordinary status should win in that case regardless, so
* Sundays are resolved straight from `bySeason` without consulting the * Sundays are resolved straight from `bySeason` without consulting the
* overrides at all. * overrides at all. The deliberate exceptions are Easter Sunday itself
* (offset 0) and Low Sunday (offset 7, the Octave's own closing day):
* `eastertide`'s own `bySeason` default is `ordinary-sunday` (live-
* verified for the *ordinary* Sundays from the 2nd Sunday after Easter
* on — see that entry's own comment), but both bookend Sundays of the
* Easter Octave need the stronger `privileged-sunday` tier instead.
* Easter Day: live-verified 2026-09-01, the Annunciation (Duplex I.
* classis) transfers off it in years they coincide, the same "transfers,
* no exception" behavior as every other privileged-sunday case. Low
* Sunday: not independently collision-verified (no Monastic-track saint
* — `SanctiM/MM-DD.txt` — ever falls there across the real range of
* Easter dates, so no live test case exists), but its own formal rank in
* the reference source ("Dominica in Albis in Octava Paschæ ~ Duplex I.
* classis") matches Easter Day's own exactly, and no `Transfer:` note
* ever shows there even for a same-named non-Monastic-track saint
* (St. Isidore of Seville, 2027; St. Vincent Ferrer, 2043) that a real
* Monastic-track day would display — treated the same as Easter Day on
* that basis. Both checked directly here rather than via `offsets`,
* since offsets are never consulted for a Sunday.
*/ */
export function resolveTemporalCategory(isoDate: string, season: Season, weekday: Weekday): TemporalCategory { export function resolveTemporalCategory(isoDate: string, season: Season, weekday: Weekday): TemporalCategory {
const bySeasonEntry = temporalCategories.bySeason[season]; const bySeasonEntry = temporalCategories.bySeason[season];
const base = bySeasonEntry ? (weekday === 'sunday' ? bySeasonEntry.sunday : bySeasonEntry.feria) : 'ordinary-feria'; const base = bySeasonEntry ? (weekday === 'sunday' ? bySeasonEntry.sunday : bySeasonEntry.feria) : 'ordinary-feria';
if (weekday === 'sunday') { if (weekday === 'sunday') {
if (season === 'eastertide' && [0, 7].includes(easterOffsetOf(isoDate))) {
return 'privileged-sunday';
}
return base; return base;
} }
+21 -13
View File
@@ -76,12 +76,16 @@ export type FeastClass =
// 1617 engine, which the original single-tier model wrongly forbade) — // 1617 engine, which the original single-tier model wrongly forbade) —
// see calendar/commemorations.ts's rules for `privileged-feria` (behaves // see calendar/commemorations.ts's rules for `privileged-feria` (behaves
// like `ordinary-sunday`: Duplex+ wins outright) vs `privileged-feria-major` // like `ordinary-sunday`: Duplex+ wins outright) vs `privileged-feria-major`
// (behaves like `privileged-sunday`: never displaced at all, confirmed by // (behaves like `privileged-sunday`: never displaced, and — corrected
// checking St. Mark, Duplex II. classis, merely commemorated rather than // 2026-09-01 — never gets even a bare commemoration at any rank either;
// winning within the Easter Octave). See data/calendar/temporal- // an earlier claim that a duplex-majus+ candidate is "merely commemorated"
// categories.yml for exactly which days fall in which tier — some of that // there, citing St. Mark within the Easter Octave, turned out to be a
// split (e.g. whether Pentecost's own Ember days share Lent's lesser tier // misreading — direct instruction confirmed nothing is ever commemorated
// or Easter's major one) is still a reconstructed guess, not verified. // anywhere from Palm Sunday through Low Sunday, and live-checking Ash
// Wednesday found the same). See data/calendar/temporal-categories.yml
// for exactly which days fall in which tier — some of that split (e.g.
// whether Pentecost's own Ember days share Lent's lesser tier or
// Easter's major one) is still a reconstructed guess, not verified.
// `privileged-feria-minor` is a third, weaker tier, added during the // `privileged-feria-minor` is a third, weaker tier, added during the
// December sanctoral pull after finding Advent's own ordinary (non-Ember) // December sanctoral pull after finding Advent's own ordinary (non-Ember)
// ferias have real standing of their own in the live engine — a Simplex // ferias have real standing of their own in the live engine — a Simplex
@@ -216,13 +220,17 @@ export interface LiturgicalDay {
/** Set when this date's own native sanctoral candidate couldn't be kept /** Set when this date's own native sanctoral candidate couldn't be kept
* here at all (calendar/commemorations.ts's `decideOccurrence` `transfer` * here at all (calendar/commemorations.ts's `decideOccurrence` `transfer`
* signal) and moved to a later/earlier date instead. No landing date is * signal) and moved to a later/earlier date instead. No landing date is
* recorded here — `resolveDay` only ever checks the immediate adjacent * recorded here — this exists purely so the display can say "not an
* date, but a real landing can chain further than that (an unmodeled * omission, this office moved elsewhere" without needing the caller to
* case noted in `applyIncomingTransfer`, e.g. a transfer running into * separately go find where; the actual landing date is discoverable
* Holy Week), so naming a specific "to" date risked asserting a wrong * from the landing day's own winner/commemoration, tagged with
* one; this exists purely so the display can say "not an omission, this * `transferredFrom`. An ordinary transfer (calendar/index.ts's
* office moved elsewhere" without claiming to know where. Distinct from * `applyIncomingTransfer`, called from `resolveDay`) only ever needs
* — and not implied by — `Commemoration`, since a transferred-away * the immediately adjacent day; anything impeded within Holy Week/the
* Easter Octave/Low Sunday goes through the separate, dedicated
* `applyEasterOctaveBacklog` mechanism instead, which can place a
* candidate many days later. Distinct from — and not implied by —
* `Commemoration`, since a transferred-away
* candidate gets no commemoration on its own native date at all. */ * candidate gets no commemoration on its own native date at all. */
transferredAway?: { candidate: SanctoralIdentity }; transferredAway?: { candidate: SanctoralIdentity };
} }
+9 -3
View File
@@ -1,12 +1,18 @@
# Read directly from the reference engine: untagged/default [Rank] # Reference source: untagged/default [Rank] block of
# block of `web/www/horas/Latin/Sancti/03-17.txt` -- "vide C4;;Duplex;;3;;". # `web/www/horas/Latin/Sancti/03-17.txt` -- "vide C4;;Duplex;;3;;" (plain
# Duplex). Rank bumped to duplex-majus here by direct instruction
# (2026-09-01) -- a deliberate departure from the source, not a
# transcription of it (see CLAUDE.md's "Not a reconstruction" -- this app
# doesn't take the reference's own rank at face value everywhere, e.g.
# the existing late-canonization deflation convention; this is the same
# kind of deliberate move in the other direction).
# Own proper [Oratio]; no [Ant 1] of its own, so the Benedictus antiphon # Own proper [Oratio]; no [Ant 1] of its own, so the Benedictus antiphon
# falls back to Common of a Confessor Bishop (C4), matching # falls back to Common of a Confessor Bishop (C4), matching
# st-andrew-corsini.yml. No Lectio7-9 in the source (6-lesson office). # st-andrew-corsini.yml. No Lectio7-9 in the source (6-lesson office).
id: st-patrick id: st-patrick
name: "St. Patrick, Bishop and Confessor" name: "St. Patrick, Bishop and Confessor"
nameLa: "Sanctus Patricius, Episcopus et Confessor" nameLa: "Sanctus Patricius, Episcopus et Confessor"
rank: duplex rank: duplex-majus
common: common-of-a-confessor-bishop common: common-of-a-confessor-bishop
propers: "st-patrick" propers: "st-patrick"
minorHoursCommon: common-of-a-confessor-bishop minorHoursCommon: common-of-a-confessor-bishop
+68 -5
View File
@@ -48,6 +48,19 @@
# fixability but currently fall to the plain `ordinary-feria` default # fixability but currently fall to the plain `ordinary-feria` default
# instead (see the offsetRanges comment below) — unverified, a real gap, # instead (see the offsetRanges comment below) — unverified, a real gap,
# not a guess dressed up as one. # not a guess dressed up as one.
#
# 2026-09-01 full sweep: every `bySeason` entry now live-verified or
# explicitly noted as not (see each entry's own comment below) — found
# and fixed two real bugs in the process (`eastertide`'s and
# `ascensiontide`'s own `sunday` tiers were each backwards), and a third,
# deeper one in `commemorations.ts` itself (`privileged-sunday`'s
# duplex-majus+ candidates were wrongly given a bare commemoration
# instead of transferring like every other rank there). Every `feria`
# entry in `bySeason` besides the ones with their own verification
# comment is the trivial `ordinary-feria` default (no rank threshold at
# all — any real content wins outright) — not worth a dedicated live
# collision test on top of the hundreds of ordinary weekdays already
# exercising it correctly across this app's whole test suite.
bySeason: bySeason:
# Advent's ordinary ferias verified `privileged-feria-minor` during the # Advent's ordinary ferias verified `privileged-feria-minor` during the
# December sanctoral pull (see calendar/types.ts's TemporalCategory doc # December sanctoral pull (see calendar/types.ts's TemporalCategory doc
@@ -81,8 +94,35 @@ bySeason:
septuagesima: { sunday: privileged-sunday, feria: ordinary-feria } septuagesima: { sunday: privileged-sunday, feria: ordinary-feria }
lent: { sunday: privileged-sunday, feria: ordinary-feria } lent: { sunday: privileged-sunday, feria: ordinary-feria }
passiontide: { sunday: privileged-sunday, feria: ordinary-feria } passiontide: { sunday: privileged-sunday, feria: ordinary-feria }
eastertide: { sunday: privileged-sunday, feria: ordinary-feria } # The ordinary Sundays after Easter (i.e. not Easter Day itself, not
ascensiontide: { sunday: ordinary-sunday, feria: ordinary-feria } # within its Octave — both handled elsewhere) are `ordinary-sunday`,
# NOT `privileged-sunday` — originally miscoded, live-verified
# 2026-09-01, two independent Duplex-II-classis collisions on "Dominica
# IV Post Pascha" (the Finding of the Holy Cross, 2026-05-03; St. Mark,
# 2027-04-25): both win outright, with the Sunday itself only noted as
# a `Tempora` commemoration underneath — exactly `ordinary-sunday`'s
# Duplex+ rule, the reverse of `privileged-sunday`'s (which would keep
# the Sunday primary and transfer the feast entirely, per the
# Ascensiontide/Advent/Palm-Sunday/Easter-Day evidence above and
# below). Easter Day itself (offset 0) and its Octave (offsets 1-6,
# `privileged-feria-major` via offsetRanges below) are unaffected by
# this — only the ordinary Sundays past the Octave use this default.
eastertide: { sunday: ordinary-sunday, feria: ordinary-feria }
# The Sunday within the Octave of Ascension is `privileged-sunday`, not
# `ordinary-sunday` — live-verified 2026-09-01: the reference engine
# titles it "Dominica infra Octavam Ascensionis ~ Semiduplex Dominica
# minor" and gives it its own proper Matins content (own antiphons, Ps 8
# opening Nocturn I — not the generic fixed Sunday scheme), with St.
# Paschal Baylon (Duplex, real May 17) nowhere in it; `ordinary-sunday`'s
# Duplex+ threshold would wrongly let that saint win outright instead.
# Matches the same "great octave" pattern as eastertide/pentecost above,
# both already `privileged-sunday`.
ascensiontide: { sunday: privileged-sunday, feria: ordinary-feria }
# Pentecost Sunday itself — no fixed-date saint collision found/
# attempted to independently verify this specific entry, but consistent
# with the confirmed "great octave Sundays are privileged-sunday"
# pattern (Easter Day, Ascension's own octave Sunday) — Pentecost
# outranks both.
pentecost: { sunday: privileged-sunday, feria: ordinary-feria } pentecost: { sunday: privileged-sunday, feria: ordinary-feria }
trinitytide: { sunday: ordinary-sunday, feria: ordinary-feria } trinitytide: { sunday: ordinary-sunday, feria: ordinary-feria }
# Corpus Christi/Sacred Heart are themselves "feasts of the Lord" that # Corpus Christi/Sacred Heart are themselves "feasts of the Lord" that
@@ -90,7 +130,14 @@ bySeason:
# privileged-feria-major (rather than injecting a separate synthetic # privileged-feria-major (rather than injecting a separate synthetic
# FeastClass) since with the small sanctoral calendar this app has, a # FeastClass) since with the small sanctoral calendar this app has, a
# real clash is unlikely and the practical effect (temporal wins) is the # real clash is unlikely and the practical effect (temporal wins) is the
# same either way. # same either way. Not independently live-verified (no fixed-date saint
# collision found/attempted), but same "feast of the Lord" reasoning
# already confirmed for Ash Wednesday/Holy Week/the Easter Octave.
# `sunday` is checked here for shape-consistency with every other
# season entry, but genuinely unreachable in practice — per
# data/calendar/easter-offsets.yml's `days:` table, both are single
# fixed weekdays (Corpus Christi always a Thursday, Sacred Heart always
# the following Friday), never a Sunday.
corpus-christi: { sunday: ordinary-sunday, feria: privileged-feria-major } corpus-christi: { sunday: ordinary-sunday, feria: privileged-feria-major }
sacred-heart: { sunday: ordinary-sunday, feria: privileged-feria-major } sacred-heart: { sunday: ordinary-sunday, feria: privileged-feria-major }
@@ -99,9 +146,25 @@ offsets:
-39: privileged-feria # Lent Ember Wednesday (Wed after 1st Sunday of Lent) — verified: Duplex wins outright here -39: privileged-feria # Lent Ember Wednesday (Wed after 1st Sunday of Lent) — verified: Duplex wins outright here
-37: privileged-feria # Lent Ember Friday -37: privileged-feria # Lent Ember Friday
-36: privileged-feria # Lent Ember Saturday — verified: Duplex wins outright here (St. Gregory the Great) -36: privileged-feria # Lent Ember Saturday — verified: Duplex wins outright here (St. Gregory the Great)
48: privileged-feria-major # Vigil of Pentecost — unverified, defaulted to the stricter tier 48: privileged-feria-major # Vigil of Pentecost — no live saint-collision test found/attempted (rare by construction), but corroborated 2026-09-01 by its own formal rank in the reference source (Sancti/Pentecost Vigil file's own [Rank]: "Semiduplex I classis" — a genuinely first-class day, consistent with the stricter tier)
offsetRanges: offsetRanges:
# Ferias of Passion week (Monday-Saturday before Palm Sunday) -- was
# falling through to passiontide's own `ordinary-feria` season default
# (no rank threshold at all), which let any real feast, however
# low-ranked, win outright. Live-verified 2026-09-02 against the
# reference engine (Monastic Tridentinum 1617): swept every calendar
# date that can ever land in this window across 2020-2119 (the full
# range Easter's date can produce) and found zero sub-Duplex winners --
# every real winner is Duplex or higher (St. Joseph, St. Leo I, St.
# Francis of Paola, the Transitus of St. Benedict, the Annunciation, St.
# Gregory the Great) -- while an ordinary Lent feria outside this window
# (Ss. Quadraginta Martyrum, Semiduplex, March 10) does win outright,
# confirming Semiduplex+ is a real threshold for Lent generally but not
# for Passion week specifically. `privileged-feria` (Duplex+ wins
# outright, matching Lent's Ember days above) is the right tier, not
# `ordinary-feria`.
- { fromOffset: -13, toOffset: -8, category: privileged-feria } # ferias of Passion week
- { fromOffset: -6, toOffset: -1, category: privileged-feria-major } # ferias of Holy Week - { fromOffset: -6, toOffset: -1, category: privileged-feria-major } # ferias of Holy Week
- { fromOffset: 1, toOffset: 6, category: privileged-feria-major } # Easter octave — verified: even Duplex II. classis (St. Mark) only gets commemorated, never wins - { fromOffset: 1, toOffset: 6, category: privileged-feria-major } # Easter octave — verified: even Duplex II. classis (St. Mark) only gets commemorated, never wins
# Pentecost's own Octave (offsets 50-55) is deliberately absent from # Pentecost's own Octave (offsets 50-55) is deliberately absent from
@@ -120,4 +183,4 @@ offsetRanges:
# existing one, not introduced by this change. # existing one, not introduced by this change.
fixedDates: fixedDates:
"12-24": privileged-feria-major # Vigil of Christmas — unverified, defaulted to the stricter tier "12-24": privileged-feria-major # Vigil of Christmas — no live saint-collision test found/attempted, but corroborated 2026-09-01 by its own formal rank in the reference source (Sancti/12-24.txt's own [Rank]: "Duplex I classis", consistent with the stricter tier)
@@ -0,0 +1,31 @@
# Read from the reference engine: web/www/horas/Latin/Tempora/Pent01-0.txt
# ("In Festo Sanctissimæ Trinitatis"), Duplex II. classis — live-verified
# 2026-05-31 (Trinity Sunday itself that year). Previously not modeled as
# its own record at all: `resolveTemporalId` just fell back to
# `post-pentecost-01`, so this Sunday — one of the principal Sundays of
# the whole year — displayed as a generic ordinary post-Pentecost Sunday,
# with whatever saint happened to be assigned that date (if any) winning
# outright per the plain `ordinary-sunday` rule. Found and fixed 2026-09-04
# after the user asked about a display-only-looking gap that turned out to
# be this much bigger mechanism gap.
id: trinity-sunday
name: "The Most Holy Trinity"
# Reference engine's own [Officium] title is a prepositional phrase ("In
# Festo Sanctissimæ Trinitatis"); adapted to this app's nominative-noun-
# phrase convention.
nameLa: "Sanctissima Trinitas"
rank: duplex-2-classis
anchor:
kind: easter
offset: 56
# Per direct instruction (2026-09-04): always wins, same as christ-the-
# king.yml's own `unconditional: true` — one of the year's principal
# Sundays shouldn't lose to a same-rank fixed-date collision. The one
# real case this matters for: the Queenship of the BVM (1954 addition,
# fixed May 31, also Duplex II. classis) lands on the same date as
# Trinity Sunday in rare years (e.g. 2026) — not verifiable against the
# Monastic Tridentinum 1617 track directly, since that calendar doesn't
# carry the Queenship feast at all (confirmed live: May 31 1617-track
# shows St. Petronilla instead, this app's own blended-calendar-only
# collision).
unconditional: true
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 12 } - { book: isa, chapter: 12 }
- { book: isa, chapter: 13 } - { book: isa, chapter: 13 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 5 } - { book: sap, chapter: 5 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 4 } - { book: isa, chapter: 4 }
- { book: isa, chapter: 5 } - { book: isa, chapter: 5 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 1 } - { book: sap, chapter: 1 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 14 } - { book: isa, chapter: 14 }
- { book: isa, chapter: 15 } - { book: isa, chapter: 15 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 6 } - { book: sap, chapter: 6 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 1 } - { book: isa, chapter: 1 }
- { book: isa, chapter: 2 } - { book: isa, chapter: 2 }
- { book: isa, chapter: 3 } - { book: isa, chapter: 3 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 10 } - { book: isa, chapter: 10 }
- { book: isa, chapter: 11 } - { book: isa, chapter: 11 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 4 } - { book: sap, chapter: 4 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 6 } - { book: isa, chapter: 6 }
- { book: isa, chapter: 7 } - { book: isa, chapter: 7 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 2 } - { book: sap, chapter: 2 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 8 } - { book: isa, chapter: 8 }
- { book: isa, chapter: 9 } - { book: isa, chapter: 9 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 3 } - { book: sap, chapter: 3 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 27 } - { book: isa, chapter: 27 }
- { book: isa, chapter: 28 } - { book: isa, chapter: 28 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 11 } - { book: sap, chapter: 11 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 19 } - { book: isa, chapter: 19 }
- { book: isa, chapter: 20 } - { book: isa, chapter: 20 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 7 } - { book: sap, chapter: 7 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 29 } - { book: isa, chapter: 29 }
- { book: isa, chapter: 30 } - { book: isa, chapter: 30 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 12 } - { book: sap, chapter: 12 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 16 } - { book: isa, chapter: 16 }
- { book: isa, chapter: 17 } - { book: isa, chapter: 17 }
- { book: isa, chapter: 18 } - { book: isa, chapter: 18 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 25 } - { book: isa, chapter: 25 }
- { book: isa, chapter: 26 } - { book: isa, chapter: 26 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 10 } - { book: sap, chapter: 10 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 21 } - { book: isa, chapter: 21 }
- { book: isa, chapter: 22 } - { book: isa, chapter: 22 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 8 } - { book: sap, chapter: 8 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 23 } - { book: isa, chapter: 23 }
- { book: isa, chapter: 24 } - { book: isa, chapter: 24 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 9 } - { book: sap, chapter: 9 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 42 } - { book: isa, chapter: 42 }
- { book: isa, chapter: 43 } - { book: isa, chapter: 43 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 17 } - { book: sap, chapter: 17 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 34 } - { book: isa, chapter: 34 }
- { book: isa, chapter: 35 } - { book: isa, chapter: 35 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 13 } - { book: sap, chapter: 13 }
status: { la: missing, en: missing }
@@ -5,8 +5,6 @@ readings:
- passages: - passages:
- { book: isa, chapter: 44 } - { book: isa, chapter: 44 }
- { book: isa, chapter: 45 } - { book: isa, chapter: 45 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 18 } - { book: sap, chapter: 18 }
- { book: sap, chapter: 19 } - { book: sap, chapter: 19 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 31 } - { book: isa, chapter: 31 }
- { book: isa, chapter: 32 } - { book: isa, chapter: 32 }
- { book: isa, chapter: 33 } - { book: isa, chapter: 33 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 40 } - { book: isa, chapter: 40 }
- { book: isa, chapter: 41 } - { book: isa, chapter: 41 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 16 } - { book: sap, chapter: 16 }
status: { la: missing, en: missing }
@@ -18,7 +18,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 36 } - { book: isa, chapter: 36 }
- { book: isa, chapter: 37 } - { book: isa, chapter: 37 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 14 } - { book: sap, chapter: 14 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: isa, chapter: 38 } - { book: isa, chapter: 38 }
- { book: isa, chapter: 39 } - { book: isa, chapter: 39 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: sap, chapter: 15 } - { book: sap, chapter: 15 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 61 } - { book: isa, chapter: 61 }
- { book: isa, chapter: 62 } - { book: isa, chapter: 62 }
- { book: isa, chapter: 63 } - { book: isa, chapter: 63 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 49 } - { book: isa, chapter: 49 }
- { book: isa, chapter: 50 } - { book: isa, chapter: 50 }
- { book: isa, chapter: 51 } - { book: isa, chapter: 51 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 64 } - { book: isa, chapter: 64 }
- { book: isa, chapter: 65 } - { book: isa, chapter: 65 }
- { book: isa, chapter: 66 } - { book: isa, chapter: 66 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 46 } - { book: isa, chapter: 46 }
- { book: isa, chapter: 47 } - { book: isa, chapter: 47 }
- { book: isa, chapter: 48 } - { book: isa, chapter: 48 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 58 } - { book: isa, chapter: 58 }
- { book: isa, chapter: 59 } - { book: isa, chapter: 59 }
- { book: isa, chapter: 60 } - { book: isa, chapter: 60 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 52 } - { book: isa, chapter: 52 }
- { book: isa, chapter: 53 } - { book: isa, chapter: 53 }
- { book: isa, chapter: 54 } - { book: isa, chapter: 54 }
status: { la: missing, en: missing }
@@ -6,4 +6,3 @@ readings:
- { book: isa, chapter: 55 } - { book: isa, chapter: 55 }
- { book: isa, chapter: 56 } - { book: isa, chapter: 56 }
- { book: isa, chapter: 57 } - { book: isa, chapter: 57 }
status: { la: missing, en: missing }
@@ -3,8 +3,6 @@ calendarDate: "01-01"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 8 } - { book: cant, chapter: 8 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: rom, chapter: 7 } - { book: rom, chapter: 7 }
- { book: rom, chapter: 8 } - { book: rom, chapter: 8 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: rom, chapter: 9 } - { book: rom, chapter: 9 }
- { book: rom, chapter: 10 } - { book: rom, chapter: 10 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: rom, chapter: 11 } - { book: rom, chapter: 11 }
- { book: rom, chapter: 12 } - { book: rom, chapter: 12 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: rom, chapter: 13 } - { book: rom, chapter: 13 }
- { book: rom, chapter: 14 } - { book: rom, chapter: 14 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: rom, chapter: 15 } - { book: rom, chapter: 15 }
- { book: rom, chapter: 16 } - { book: rom, chapter: 16 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: heb, chapter: 1 } - { book: heb, chapter: 1 }
- { book: heb, chapter: 2 } - { book: heb, chapter: 2 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: heb, chapter: 3 } - { book: heb, chapter: 3 }
- { book: heb, chapter: 4 } - { book: heb, chapter: 4 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: heb, chapter: 5 } - { book: heb, chapter: 5 }
- { book: heb, chapter: 6 } - { book: heb, chapter: 6 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: heb, chapter: 7 } - { book: heb, chapter: 7 }
- { book: heb, chapter: 8 } - { book: heb, chapter: 8 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: heb, chapter: 9 } - { book: heb, chapter: 9 }
- { book: heb, chapter: 10 } - { book: heb, chapter: 10 }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ readings:
- passages: - passages:
- { book: heb, chapter: 11 } - { book: heb, chapter: 11 }
- { book: heb, chapter: 12 } - { book: heb, chapter: 12 }
status: { la: missing, en: missing }
@@ -3,4 +3,3 @@ calendarDate: "01-13"
readings: readings:
- passages: - passages:
- { book: heb, chapter: 13 } - { book: heb, chapter: 13 }
status: { la: missing, en: missing }
@@ -3,4 +3,3 @@ calendarDate: "12-25"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 1 } - { book: cant, chapter: 1 }
status: { la: missing, en: missing }
@@ -3,4 +3,3 @@ calendarDate: "12-26"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 2 } - { book: cant, chapter: 2 }
status: { la: missing, en: missing }
@@ -3,4 +3,3 @@ calendarDate: "12-27"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 3 } - { book: cant, chapter: 3 }
status: { la: missing, en: missing }
@@ -3,4 +3,3 @@ calendarDate: "12-28"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 4 } - { book: cant, chapter: 4 }
status: { la: missing, en: missing }
@@ -3,8 +3,6 @@ calendarDate: "12-29"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 5 } - { book: cant, chapter: 5 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: rom, chapter: 1 } - { book: rom, chapter: 1 }
- { book: rom, chapter: 2 } - { book: rom, chapter: 2 }
status: { la: missing, en: missing }
@@ -3,8 +3,6 @@ calendarDate: "12-30"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 6 } - { book: cant, chapter: 6 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: rom, chapter: 3 } - { book: rom, chapter: 3 }
- { book: rom, chapter: 4 } - { book: rom, chapter: 4 }
status: { la: missing, en: missing }
@@ -3,8 +3,6 @@ calendarDate: "12-31"
readings: readings:
- passages: - passages:
- { book: cant, chapter: 7 } - { book: cant, chapter: 7 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: rom, chapter: 5 } - { book: rom, chapter: 5 }
- { book: rom, chapter: 6 } - { book: rom, chapter: 6 }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 30 } - { book: deut, chapter: 30 }
- { book: deut, chapter: 31 } - { book: deut, chapter: 31 }
- { book: deut, chapter: 32 } - { book: deut, chapter: 32 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 13 } - { book: act, chapter: 13 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 6, verses: "22-44" } - { book: john, chapter: 6, verses: "22-44" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: deut, chapter: 19 } - { book: deut, chapter: 19 }
- { book: deut, chapter: 20 } - { book: deut, chapter: 20 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 9 } - { book: act, chapter: 9 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 4, verses: "31-54" } - { book: john, chapter: 4, verses: "31-54" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: deut, chapter: 33 } - { book: deut, chapter: 33 }
- { book: deut, chapter: 34 } - { book: deut, chapter: 34 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 14 } - { book: act, chapter: 14 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 6, verses: "45-71" } - { book: john, chapter: 6, verses: "45-71" }
status: { la: missing, en: missing }
@@ -4,7 +4,5 @@ weekday: sunday
readings: readings:
- passages: - passages:
- { book: deut, chapter: 18 } - { book: deut, chapter: 18 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 8 } - { book: act, chapter: 8 }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 27 } - { book: deut, chapter: 27 }
- { book: deut, chapter: 28 } - { book: deut, chapter: 28 }
- { book: deut, chapter: 29 } - { book: deut, chapter: 29 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 12 } - { book: act, chapter: 12 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 6, verses: "1-21" } - { book: john, chapter: 6, verses: "1-21" }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 21 } - { book: deut, chapter: 21 }
- { book: deut, chapter: 22 } - { book: deut, chapter: 22 }
- { book: deut, chapter: 23 } - { book: deut, chapter: 23 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 10 } - { book: act, chapter: 10 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 5, verses: "1-24" } - { book: john, chapter: 5, verses: "1-24" }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 24 } - { book: deut, chapter: 24 }
- { book: deut, chapter: 25 } - { book: deut, chapter: 25 }
- { book: deut, chapter: 26 } - { book: deut, chapter: 26 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 11 } - { book: act, chapter: 11 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 5, verses: "25-47" } - { book: john, chapter: 5, verses: "25-47" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 9 } - { book: josh, chapter: 9 }
- { book: josh, chapter: 10 } - { book: josh, chapter: 10 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 20 } - { book: act, chapter: 20 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 9, verses: "1-23" } - { book: john, chapter: 9, verses: "1-23" }
status: { la: missing, en: missing }
@@ -4,10 +4,7 @@ weekday: monday
readings: readings:
- passages: - passages:
- { book: josh, chapter: 2 } - { book: josh, chapter: 2 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 16 } - { book: act, chapter: 16 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 7, verses: "1-27" } - { book: john, chapter: 7, verses: "1-27" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 11 } - { book: josh, chapter: 11 }
- { book: josh, chapter: 12 } - { book: josh, chapter: 12 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 21 } - { book: act, chapter: 21 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 9, verses: "24-41" } - { book: john, chapter: 9, verses: "24-41" }
status: { la: missing, en: missing }
@@ -4,7 +4,5 @@ weekday: sunday
readings: readings:
- passages: - passages:
- { book: josh, chapter: 1 } - { book: josh, chapter: 1 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 15 } - { book: act, chapter: 15 }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 7 } - { book: josh, chapter: 7 }
- { book: josh, chapter: 8 } - { book: josh, chapter: 8 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 19 } - { book: act, chapter: 19 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 8, verses: "28-59" } - { book: john, chapter: 8, verses: "28-59" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 3 } - { book: josh, chapter: 3 }
- { book: josh, chapter: 4 } - { book: josh, chapter: 4 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 17 } - { book: act, chapter: 17 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 7, verses: "28-53" } - { book: john, chapter: 7, verses: "28-53" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 5 } - { book: josh, chapter: 5 }
- { book: josh, chapter: 6 } - { book: josh, chapter: 6 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 18 } - { book: act, chapter: 18 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 8, verses: "1-27" } - { book: john, chapter: 8, verses: "1-27" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 21 } - { book: josh, chapter: 21 }
- { book: josh, chapter: 22 } - { book: josh, chapter: 22 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 27 } - { book: act, chapter: 27 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 12, verses: "1-28" } - { book: john, chapter: 12, verses: "1-28" }
status: { la: missing, en: missing }
@@ -4,10 +4,7 @@ weekday: monday
readings: readings:
- passages: - passages:
- { book: josh, chapter: 14 } - { book: josh, chapter: 14 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 23 } - { book: act, chapter: 23 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 10, verses: "1-23" } - { book: john, chapter: 10, verses: "1-23" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 23 } - { book: josh, chapter: 23 }
- { book: josh, chapter: 24 } - { book: josh, chapter: 24 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 28 } - { book: act, chapter: 28 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 12, verses: "29-50" } - { book: john, chapter: 12, verses: "29-50" }
status: { la: missing, en: missing }
@@ -4,7 +4,5 @@ weekday: sunday
readings: readings:
- passages: - passages:
- { book: josh, chapter: 13 } - { book: josh, chapter: 13 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 22 } - { book: act, chapter: 22 }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 19 } - { book: josh, chapter: 19 }
- { book: josh, chapter: 20 } - { book: josh, chapter: 20 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 26 } - { book: act, chapter: 26 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 11, verses: "30-57" } - { book: john, chapter: 11, verses: "30-57" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 15 } - { book: josh, chapter: 15 }
- { book: josh, chapter: 16 } - { book: josh, chapter: 16 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 24 } - { book: act, chapter: 24 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 10, verses: "24-42" } - { book: john, chapter: 10, verses: "24-42" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: josh, chapter: 17 } - { book: josh, chapter: 17 }
- { book: josh, chapter: 18 } - { book: josh, chapter: 18 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 25 } - { book: act, chapter: 25 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 11, verses: "1-29" } - { book: john, chapter: 11, verses: "1-29" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: judg, chapter: 8 } - { book: judg, chapter: 8 }
- { book: judg, chapter: 9 } - { book: judg, chapter: 9 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jas, chapter: 5 } - { book: jas, chapter: 5 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 15 } - { book: john, chapter: 15 }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: judg, chapter: 3 } - { book: judg, chapter: 3 }
- { book: judg, chapter: 4 } - { book: judg, chapter: 4 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jas, chapter: 2 } - { book: jas, chapter: 2 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 13, verses: "1-20" } - { book: john, chapter: 13, verses: "1-20" }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: judg, chapter: 10 } - { book: judg, chapter: 10 }
- { book: judg, chapter: 11 } - { book: judg, chapter: 11 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 16 } - { book: john, chapter: 16 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: judg, chapter: 1 } - { book: judg, chapter: 1 }
- { book: judg, chapter: 2 } - { book: judg, chapter: 2 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jas, chapter: 1 } - { book: jas, chapter: 1 }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: judg, chapter: 5 } - { book: judg, chapter: 5 }
- { book: judg, chapter: 6 } - { book: judg, chapter: 6 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jas, chapter: 3 } - { book: jas, chapter: 3 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 13, verses: "21-38" } - { book: john, chapter: 13, verses: "21-38" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: judg, chapter: 7 } - { book: judg, chapter: 7 }
- { book: judg, chapter: 8 } - { book: judg, chapter: 8 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jas, chapter: 4 } - { book: jas, chapter: 4 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 14 } - { book: john, chapter: 14 }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 13 } - { book: deut, chapter: 13 }
- { book: deut, chapter: 14 } - { book: deut, chapter: 14 }
- { book: deut, chapter: 15 } - { book: deut, chapter: 15 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 6 } - { book: act, chapter: 6 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 3, verses: "19-36" } - { book: john, chapter: 3, verses: "19-36" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: deut, chapter: 2 } - { book: deut, chapter: 2 }
- { book: deut, chapter: 3 } - { book: deut, chapter: 3 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 2 } - { book: act, chapter: 2 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 1, verses: "1-28" } - { book: john, chapter: 1, verses: "1-28" }
status: { la: missing, en: missing }
@@ -5,10 +5,7 @@ readings:
- passages: - passages:
- { book: deut, chapter: 16 } - { book: deut, chapter: 16 }
- { book: deut, chapter: 17 } - { book: deut, chapter: 17 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 7 } - { book: act, chapter: 7 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 4, verses: "1-30" } - { book: john, chapter: 4, verses: "1-30" }
status: { la: missing, en: missing }
@@ -4,7 +4,5 @@ weekday: sunday
readings: readings:
- passages: - passages:
- { book: deut, chapter: 1 } - { book: deut, chapter: 1 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 1 } - { book: act, chapter: 1 }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 10 } - { book: deut, chapter: 10 }
- { book: deut, chapter: 11 } - { book: deut, chapter: 11 }
- { book: deut, chapter: 12 } - { book: deut, chapter: 12 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 5 } - { book: act, chapter: 5 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 3, verses: "1-18" } - { book: john, chapter: 3, verses: "1-18" }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 4 } - { book: deut, chapter: 4 }
- { book: deut, chapter: 5 } - { book: deut, chapter: 5 }
- { book: deut, chapter: 6 } - { book: deut, chapter: 6 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 3 } - { book: act, chapter: 3 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 1, verses: "29-51" } - { book: john, chapter: 1, verses: "29-51" }
status: { la: missing, en: missing }
@@ -6,10 +6,7 @@ readings:
- { book: deut, chapter: 7 } - { book: deut, chapter: 7 }
- { book: deut, chapter: 8 } - { book: deut, chapter: 8 }
- { book: deut, chapter: 9 } - { book: deut, chapter: 9 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: act, chapter: 4 } - { book: act, chapter: 4 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: john, chapter: 2 } - { book: john, chapter: 2 }
status: { la: missing, en: missing }
@@ -4,7 +4,5 @@ weekday: friday
readings: readings:
- passages: - passages:
- { book: ezra, chapter: 10 } - { book: ezra, chapter: 10 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: hag, chapter: 2, verses: "20-23" } - { book: hag, chapter: 2, verses: "20-23" }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: ezra, chapter: 3 } - { book: ezra, chapter: 3 }
- { book: ezra, chapter: 4 } - { book: ezra, chapter: 4 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: hag, chapter: 1, verses: "12-15" } - { book: hag, chapter: 1, verses: "12-15" }
status: { la: missing, en: missing }
@@ -4,4 +4,3 @@ weekday: saturday
readings: readings:
- passages: - passages:
- { book: ezra, chapter: 10 } - { book: ezra, chapter: 10 }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: ezra, chapter: 1 } - { book: ezra, chapter: 1 }
- { book: ezra, chapter: 2 } - { book: ezra, chapter: 2 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: hag, chapter: 1, verses: "1-11" } - { book: hag, chapter: 1, verses: "1-11" }
status: { la: missing, en: missing }
@@ -4,7 +4,5 @@ weekday: thursday
readings: readings:
- passages: - passages:
- { book: ezra, chapter: 9 } - { book: ezra, chapter: 9 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: hag, chapter: 2, verses: "20-23" } - { book: hag, chapter: 2, verses: "20-23" }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: ezra, chapter: 5 } - { book: ezra, chapter: 5 }
- { book: ezra, chapter: 6 } - { book: ezra, chapter: 6 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: hag, chapter: 2, verses: "1-9" } - { book: hag, chapter: 2, verses: "1-9" }
status: { la: missing, en: missing }
@@ -5,7 +5,5 @@ readings:
- passages: - passages:
- { book: ezra, chapter: 7 } - { book: ezra, chapter: 7 }
- { book: ezra, chapter: 8 } - { book: ezra, chapter: 8 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: hag, chapter: 2, verses: "10-19" } - { book: hag, chapter: 2, verses: "10-19" }
status: { la: missing, en: missing }
@@ -6,8 +6,6 @@ readings:
- { book: exod, chapter: 15 } - { book: exod, chapter: 15 }
- { book: exod, chapter: 16 } - { book: exod, chapter: 16 }
- { book: exod, chapter: 17 } - { book: exod, chapter: 17 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jer, chapter: 11 } - { book: jer, chapter: 11 }
- { book: jer, chapter: 12 } - { book: jer, chapter: 12 }
status: { la: missing, en: missing }
@@ -6,8 +6,6 @@ readings:
- { book: exod, chapter: 3 } - { book: exod, chapter: 3 }
- { book: exod, chapter: 4 } - { book: exod, chapter: 4 }
- { book: exod, chapter: 5 } - { book: exod, chapter: 5 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jer, chapter: 3 } - { book: jer, chapter: 3 }
- { book: jer, chapter: 4 } - { book: jer, chapter: 4 }
status: { la: missing, en: missing }
@@ -6,8 +6,6 @@ readings:
- { book: exod, chapter: 18 } - { book: exod, chapter: 18 }
- { book: exod, chapter: 19 } - { book: exod, chapter: 19 }
- { book: exod, chapter: 20 } - { book: exod, chapter: 20 }
status: { la: missing, en: missing }
- passages: - passages:
- { book: jer, chapter: 13 } - { book: jer, chapter: 13 }
- { book: jer, chapter: 14 } - { book: jer, chapter: 14 }
status: { la: missing, en: missing }

Some files were not shown because too many files have changed in this diff Show More