diff --git a/TODO.md b/TODO.md index c6d3efe..c124152 100644 --- a/TODO.md +++ b/TODO.md @@ -478,18 +478,59 @@ uncertainty can't be resolved at all. Used the text anyway (better than nothing, and plausibly correct), just honestly flagged rather than claimed as confirmed. -All 15 now have real content — none left "honestly missing." St. -Frances of Rome and St. Bibiana (the original, pre-Kalendaria-table -109-saint pass) are still left missing and are good candidates for this -exact same fix — this pass didn't touch them since their own fileref -was never derived (they predate the Kalendaria-table work this session -built), not because they're any less resolvable in principle. Ss. +All 15 now have real content — none left "honestly missing." Ss. Ursula and Companions (the section above) is the one case that's actually different in kind: no other calendar member shares her `C6b`/plural-Virgins-and-Martyrs category to cross-check a Common against, so even a clean file read wouldn't have anything solid to verify itself against. +### A full sweep for the same pattern elsewhere, and 2 more real fixes (2026-08) + +Same feedback, applied more broadly: swept every saint in +`data/calendar/saints/*.yml` (not just the 94-batch's own lineage) for +a still-`missing` collect or Benedictus antiphon, via a synthetic-winner +smoke test. Found 4 hits; 2 were real, 2 were false positives from the +sweep's own synthetic-day methodology (forcing a saint to be `day.winner` +when in reality it's *never* the winner doesn't correctly exercise its +real content path): + +- **St. Frances of Rome and St. Bibiana** (Benedictus antiphon) — + exactly the fix from the section above, just predating the + Kalendaria-table work (no fileref had been derived for either yet). + Both resolve to `common-of-a-virgin` once one is: Bibiana's file + points to `C6-1` directly; Frances of Rome's points to `C7a` (a + whole-file inherit of `C7`, just the `[Officium]` title relabeled "of + a Widow" — fitting, since she's a widow, not a virgin — with no + override of the same `[Ant 1]` cross-reference, so it resolves + identically). +- **St. Agatha and St. Lucy** (collect) — a real, older gap, from + *before* `collectCommon` existed at all: both saints' own file + comments already documented "no unique collect... resolves honestly + missing... despite propers being set," written back when that was the + only option. Agatha's own `[Oratio]` is `@Commune/C6::s/N\./Agathæ/` — + confirmed identical to the already-existing `collect-c6.yml` template + — so `collectCommon: collect-c6` was simply wired in. Lucy's own + `[Oratio]` is `@Commune/C7a`, *unedited* (unlike `collect-c6a.yml`'s + own C7a-derived text, which has a `"Vírginis tuæ"` insertion baked + in) — her own `[Name]` section supplies the whole descriptive phrase + instead ("Lúciæ Vírginis et Mártyris tuæ", not just her bare name), + making her resolved text effectively proper even though the + underlying formula is a shared Common one; written as her own + `st-lucy-collect.yml` (she already had `propers: "st-lucy"` set) + rather than a new single-use `collect-c7a` template. No English + `[Oratio]` in the source for either saint's own unedited form, so + Lucy's stays `status.en: draft`. +- **St. Clare** (false positive) — the sweep's synthetic day forced her + to be `day.winner`, but she never actually is one (always + commemorated under St. Lawrence's octave); her real content lives in + `st-clare-commemoration.yml`, used via a completely different code + path (`sanctoralCommemorationPart`, not `getBenedictusAntiphon`) that + the sweep doesn't exercise. Already fully authored and live-verified; + no gap. +- **Ss. Ursula and Companions** (already covered above) — the one + genuine, currently-irresolvable case. + Two vigils (Vigil of St. John the Baptist, Vigil of St. Lawrence) didn't resolve via this file-based method at all (their particular file format wasn't handled by this pass's parser) — no impact, both already had diff --git a/src/data/calendar/saints/st-agatha.yml b/src/data/calendar/saints/st-agatha.yml index 8bdbd02..19bf9a8 100644 --- a/src/data/calendar/saints/st-agatha.yml +++ b/src/data/calendar/saints/st-agatha.yml @@ -1,10 +1,17 @@ # Verified against Divinum Officium (Monastic Tridentinum 1617), live kalendar # check (2029-02-05, no Sunday collision) -- winner outright, Semiduplex. Own -# proper antiphon (see propers/common/st-agatha-antiphon.yml) but no unique -# collect of her own -- Common of a Virgin Martyr supplies that instead, so -# getDayCollect resolves honestly missing for her despite propers being set. +# proper antiphon (see propers/common/st-agatha-antiphon.yml). No unique +# collect of her own -- Common of a Virgin Martyr supplies that instead +# (her own [Oratio] is @Commune/C6::s/N\./Agathæ/, a plain name-substituted +# reference, confirmed identical to collect-c6.yml's own template) -- 2026-08, +# wired via collectCommon now that mechanism exists (it postdates this +# saint's own original authoring, hence the gap). id: st-agatha name: "St. Agatha, Virgin and Martyr" rank: semiduplex common: common-of-a-virgin-martyr propers: "st-agatha" +collectCommon: collect-c6 +collectName: + la: ["Agathæ"] + en: ["Agatha"] diff --git a/src/data/calendar/saints/st-bibiana.yml b/src/data/calendar/saints/st-bibiana.yml index fdde434..c0187fd 100644 --- a/src/data/calendar/saints/st-bibiana.yml +++ b/src/data/calendar/saints/st-bibiana.yml @@ -9,3 +9,8 @@ name: "St. Bibiana, Virgin and Martyr" rank: simplex common: common-of-a-virgin-martyr propers: "st-bibiana" +# No [Ant 1] of its own in the source (12-02.txt) -- [Rule]/[Rank] point to +# Commune/C6-1 instead, the same C-number family already live-query-verified +# for common-of-a-virgin.yml (see that section of TODO.md). +minorHoursCommon: common-of-a-virgin +benedictusCommon: common-of-a-virgin diff --git a/src/data/calendar/saints/st-frances-of-rome.yml b/src/data/calendar/saints/st-frances-of-rome.yml index ed9bc86..30b2358 100644 --- a/src/data/calendar/saints/st-frances-of-rome.yml +++ b/src/data/calendar/saints/st-frances-of-rome.yml @@ -10,3 +10,12 @@ name: "St. Frances of Rome, Widow" rank: simplex common: common-of-a-widow propers: "st-frances-of-rome" +# No [Ant 1] of its own in the source (03-09.txt) -- [Rule]/[Rank] point to +# Commune/C7a instead, the same C-number family already live-query-verified +# for common-of-a-virgin.yml (see that section of TODO.md). Her file's own +# C7a is a whole-file inherit of C7 (just the [Officium] title relabeled +# "Commune non Virginum non Martyrum" -- fitting, since she's a widow, not a +# virgin) with no override of its own [Ant 1] either, so the same cross- +# reference resolution applies unchanged. +minorHoursCommon: common-of-a-virgin +benedictusCommon: common-of-a-virgin diff --git a/src/data/propers/common/st-lucy-collect.yml b/src/data/propers/common/st-lucy-collect.yml new file mode 100644 index 0000000..e12592d --- /dev/null +++ b/src/data/propers/common/st-lucy-collect.yml @@ -0,0 +1,25 @@ +# Read directly from her own source file (Sancti/12-13.txt): [Oratio] is +# @Commune/C7a (unedited -- unlike collect-c6a.yml's own C7a-derived +# text, no "Vírginis tuæ" insertion), so her own [Name] section supplies +# the whole descriptive phrase ("Lúciæ Vírginis et Mártyris tuæ", not +# just her bare name) that a plain name field would otherwise need the +# template to supply. Written as her own file rather than a new shared +# collect-c7a template, since she's currently the only saint on this +# calendar using the unedited form. No English Oratio in the source at +# all; translated in the same register as collect-c6a.yml's own English +# (itself adapted the same way, also draft), so marked draft here too. +id: st-lucy-collect +text: + la: | + Orémus. + Exáudi nos, Deus, salutáris noster: ut sicut de beátæ Lúciæ Vírginis et Mártyris tuæ festivitáte gaudémus; ita piæ devotiónis erudiámur afféctu. + Per Dóminum nostrum Jesum Christum, Fílium tuum: qui tecum vivit et regnat in unitáte Spíritus Sancti, Deus, per ómnia sǽcula sæculórum. + R. Amen. + en: | + Let us pray. + Graciously hear us, O God of our salvation: that as we rejoice in the festivity of blessed Lucy, thy Virgin and Martyr, we may be instructed in the affections of a loving devotion. + Through Jesus Christ, thy Son our Lord, Who liveth and reigneth with thee, in the unity of the Holy Ghost, God, world without end. + R. Amen. +status: + la: verified + en: draft diff --git a/tests/calendar/february-sanctoral.test.ts b/tests/calendar/february-sanctoral.test.ts index 8a50053..c289155 100644 --- a/tests/calendar/february-sanctoral.test.ts +++ b/tests/calendar/february-sanctoral.test.ts @@ -17,7 +17,11 @@ describe('February sanctoral pull (first pass)', () => { expect(getDayCollect(day).status.en).toBe('verified'); }); - it("a saint with a proper antiphon but no proper collect resolves the collect as honestly missing", () => { + it('a saint with a proper antiphon but no proper collect resolves the collect via its Common category (collectCommon), not missing', () => { + // Her own [Oratio] is @Commune/C6::s/N\./Agathæ/ -- a name-substituted + // reference, not unique text -- confirmed identical to collect-c6.yml's + // own template; wired via collectCommon (2026-08), postdating this + // saint's own original authoring, which is why this used to be missing. const day = resolveDay('2029-02-05'); // St. Agatha expect(day.winner).toEqual({ kind: 'sanctoral', @@ -25,7 +29,9 @@ describe('February sanctoral pull (first pass)', () => { name: 'St. Agatha, Virgin and Martyr', rank: 'semiduplex', }); - expect(getDayCollect(day).status.en).toBe('missing'); + const collect = getDayCollect(day); + expect(collect.status.en).toBe('verified'); + expect(collect.text.en).toContain('Agatha'); }); it('the two Chair-of-Peter feasts (Rome, Antioch) are separate records with independently authored, matching content', () => { diff --git a/tests/hours/resolve-common.test.ts b/tests/hours/resolve-common.test.ts index 99d1068..6cf4368 100644 --- a/tests/hours/resolve-common.test.ts +++ b/tests/hours/resolve-common.test.ts @@ -35,6 +35,20 @@ describe('getDayCollect', () => { expect(collect.status.la).toBe('verified'); }); + it("resolves St. Lucy's own collect, read directly from her source file rather than a shared template (2026-08)", () => { + // Her own [Oratio] is @Commune/C7a, unedited -- unlike + // collect-c6a.yml's own C7a-derived text, no "Vírginis tuæ" insertion + // -- so her own [Name] section supplies the whole descriptive phrase, + // making her file's own text effectively proper even though it's + // built from a Common formula underneath. No English in the source at + // all, so status.en stays draft. + const day = resolveDay('2025-12-13'); + expect(day.winner).toMatchObject({ kind: 'sanctoral', id: 'st-lucy' }); + const collect = getDayCollect(day); + expect(collect.status).toEqual({ la: 'verified', en: 'draft' }); + expect(collect.text.en).toContain('Lucy'); + }); + it('resolves as honestly missing for a saint with neither a proper collect nor a Common fallback authored', () => { // Every real saint in data/calendar/saints/*.yml now has either a // proper collect or a collectCommon fallback (see TODO.md's @@ -73,6 +87,17 @@ describe('genuinely unwinnable stubs still get a Benedictus antiphon (2026-08)', expect(ben.status).toEqual({ la: 'verified', en: 'verified' }); }); + it('resolves via the same fallback for a saint from the *original*, pre-Kalendaria-table 109-saint pass', () => { + // St. Frances of Rome and St. Bibiana predate the 94-saint batch's + // collectCommon/minorHoursCommon mechanism entirely, but the same + // file-read method applies once a fileref is derived for them too -- + // Frances of Rome's own file points to C7a (a whole-file inherit of + // C7, just relabeled "of a Widow"), resolving to the same + // common-of-a-virgin text as St. Felicitas above. + const ben = getBenedictusAntiphon(syntheticSanctoralDay('st-frances-of-rome')); + expect(ben.status).toEqual({ la: 'verified', en: 'verified' }); + }); + it("resolves its own file's [Ant 1] as draft, not verified, when it can't be checked against a live rendering", () => { // Ss. Euphemia, Lucy, and Geminian -- her own file *does* have an // [Ant 1], but St. Alexius (a winnable saint checked earlier in this