Add Matins psalmody overrides for a Duplex+ weekday feast

matins.ts's three-nocturn gate correctly governed nocturn count
(Sunday, or Duplex+) but wrongly reused the literal Sunday psalmody as
content for every three-nocturn day, including a weekday feast. Live-
verified (Monastic Tridentinum 1617) that a Duplex+ weekday feast has
its own genuinely different psalmody -- confirmed per-Common-category,
not per-saint, by cross-checking St. Lawrence against St. Ignatius of
Antioch (a different Martyr sub-category, nearly identical psalm
numbers, different antiphons).

New mechanism (matins-psalmody-overrides.ts, mirroring the Lauds one
but keyed per-category) with St. Lawrence authored as the live-verified
proof; falls back to the plain ferial weekday table, redistributed into
3 nocturns, for every other Duplex+ id until its own category's content
is authored. Also generalizes the canticle shape to support a citation
spanning multiple scripture chapters under one heading (Lawrence's own
Nocturn 3), and adds the new Sirach/Jeremiah chapters his canticles cite.
This commit is contained in:
2026-08-18 06:11:52 -04:00
parent 1fc4bd7160
commit 3e58f2258d
9 changed files with 399 additions and 7 deletions
+61
View File
@@ -0,0 +1,61 @@
/** Matins' own analogue of hours/lauds-psalmody-overrides.ts, but keyed
* by Common category rather than per-saint: live-verified (see
* data/hours/matins-psalmody-overrides/st-lawrence.yml's own header)
* that the psalm *numbers* for Nocturns 1-2 are shared across saints of
* the same Common category (a plain-Duplex Martyr and a plain-Duplex
* Martyr-Bishop share all but one of the same twelve psalm numbers) —
* only the antiphon text varies, proper to the individual saint when
* authored, generic Common-of-* text otherwise. Deliberately the
* opposite indexing choice from Lauds' own per-feast override (see that
* file's own doc comment) — Lauds' 5-antiphon pool is shared across
* every Duplex-majus+ feast regardless of category, so per-feast is the
* natural key there; Matins' pool varies *by* category, so per-category
* is the natural key here.
*
* `id` is looked up against `hours/resolve-common.ts`'s
* `resolveOfficeWinner(day).id` when it's sanctoral, exactly like every
* other per-feast/per-category content lookup in this app — but the
* `id` values populated so far are individual saint ids (St. Lawrence
* proper), not Common-category ids, since only the live-verified proof
* has been authored; extending this to a real Common-category key
* (`common-of-a-martyr`, etc., matching `SaintRecord.common`) with
* per-saint antiphon overrides layered on top is the deferred bulk-
* content work (see TODO.md) — not built yet, so a saint without their
* own entry here falls back to the plain ferial weekday table
* (matins.ts's own fallback), not to some other saint's proper
* antiphons. */
export interface MatinsPsalmodyGroup {
psalms: number[];
antiphon: Partial<Record<string, string>>;
}
export interface ScriptureCanticleRef {
book: string;
chapter: number;
verses?: string;
}
export interface MatinsPsalmodyNocturn {
groups?: MatinsPsalmodyGroup[];
canticles?: { refs: ScriptureCanticleRef[] }[];
antiphon?: Partial<Record<string, string>>;
versicle: { v: Partial<Record<string, string>>; r: Partial<Record<string, string>> };
}
export interface MatinsPsalmodyOverride {
id: string;
nocturn1: MatinsPsalmodyNocturn;
nocturn2: MatinsPsalmodyNocturn;
nocturn3: MatinsPsalmodyNocturn;
}
const modules = import.meta.glob<{ default: MatinsPsalmodyOverride }>(
'../data/hours/matins-psalmody-overrides/*.yml',
{ eager: true },
);
const overrides = new Map<string, MatinsPsalmodyOverride>();
for (const mod of Object.values(modules)) {
overrides.set(mod.default.id, mod.default);
}
export function getMatinsPsalmodyOverride(id: string | undefined): MatinsPsalmodyOverride | undefined {
return id ? overrides.get(id) : undefined;
}