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
This commit is contained in:
2026-09-04 16:47:17 -04:00
parent 2a98386bdd
commit a570266028
5 changed files with 482 additions and 11 deletions
+40
View File
@@ -2,6 +2,11 @@
// data/hours/matins-responsories-by-book.yml's own header for why matching
// is book-level, not citation-level, and why this is a seeded subset, not
// a full pool.
//
// This file also holds the sibling per-Common-category pool (see
// data/hours/matins-responsories-by-common.yml's own header) — used to
// fill in a real responsory for a saint's own Nocturn 2/3 patristic
// reading when that reading has no proper `responsory` of its own.
import type { LanguageCode, TranslationStatus } from '../psalter/types';
interface BilingualText {
@@ -56,3 +61,38 @@ export function getResponsoryForBook(book: string, index: number): MatinsRespons
if (!pool || pool.length === 0) return undefined;
return joinResponsory(pool[index % pool.length]!);
}
interface FullTextResponsory {
la: string;
en: string;
}
const commonModules = import.meta.glob<{ default: Record<string, FullTextResponsory[]> }>(
'../data/hours/matins-responsories-by-common.yml',
{ eager: true },
);
const poolByCommon = new Map<string, FullTextResponsory[]>();
for (const mod of Object.values(commonModules)) {
for (const [commonId, entries] of Object.entries(mod.default)) {
poolByCommon.set(commonId, entries);
}
}
/** Same cycling convention as getResponsoryForBook, but the pooled text is
* already the full "R. ...\n* ...\nV. ...\nR. ..." block (matching the
* inline `responsory` convention every nocturn-readings/*.yml file uses),
* ready to drop straight into a reading's `responsory` field — not the
* split {r, v} shape the book pool uses. Undefined when the saint has no
* `common` category, or that category isn't seeded (e.g. angel/evangelist/
* vigil — see the data file's own header for why those are out of scope). */
export function getResponsoryForCommon(
commonId: string | undefined,
index: number,
): Partial<Record<LanguageCode, string>> | undefined {
if (!commonId) return undefined;
const pool = poolByCommon.get(commonId);
if (!pool || pool.length === 0) return undefined;
const entry = pool[index % pool.length]!;
return { la: entry.la, en: entry.en };
}