Fix octave day labels: correct commemoration phrasing, closing-day title, and privileged-season precedence
Deploy / deploy (push) Successful in 1m4s
Deploy / deploy (push) Successful in 1m4s
Three related day-label bugs, all found via Aug 19's mislabeled Assumption octave commemoration: - A sanctoral winner that displaces the only active octave now shows the octave's real "Nth Day within the Octave of X" phrasing (no rank, since it's riding along under the winning feast), not a bare feast name. - An octave's own closing day now titles itself "Octave of X", matching the real DO's "in octava" vs. "infra octavam" distinction, instead of "8th Day within the Octave of X". - A sufficiently-ranked octave can now outrank a privileged temporal season (e.g. the Immaculate Conception's octave outright winning several of its days against Advent, live-verified against Divino Afflatu 1954), via a new shared octaveGoverningPrivilegedDay helper used by both the day label and the actual office-content resolver. Deliberately excludes Christmastide, whose own stacked octaves are structurally already that season's temporal content rather than a foreign add-on. Updates a Matins test fixture that had unknowingly relied on the Dec 15 bug (Advent ferial + a lone commemorated saint) and splits it into a clean ferial case plus a dedicated commemorated-saint case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+42
-2
@@ -8,11 +8,11 @@
|
||||
// contest is decided, it just adds commemorations on top of whatever that
|
||||
// contest already produced, and occasionally overrides the winner when a
|
||||
// too-minor saint would otherwise have taken the day from it.
|
||||
import type { FeastClass, OctaveConfig } from './types';
|
||||
import type { FeastClass, LiturgicalDay, OctaveConfig } from './types';
|
||||
import { getSanctoralCandidatesFor, getSaintRecord } from './feasts';
|
||||
import { getTemporalFeastRecord, temporalFeastIdsStartingOn } from './temporal-feasts';
|
||||
import { addDays, daysBetween } from './date-math';
|
||||
import { compareFeastClass } from './commemorations';
|
||||
import { compareFeastClass, minimumOutrightWinningRank } from './commemorations';
|
||||
|
||||
export interface ActiveOctave {
|
||||
id: string;
|
||||
@@ -131,6 +131,46 @@ export function resolveActiveOctave(isoDate: string): ActiveOctave | undefined {
|
||||
return pickWinningOctave(activeOctavesFor(isoDate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether an active octave's own effective rank clears the bar a real
|
||||
* sanctoral candidate would need to win `day`'s temporal category outright
|
||||
* — i.e. whether the octave governs the day's content/label even though
|
||||
* no *saint* occurring there was strong enough to (a "foreign" octave
|
||||
* superimposed on a season it doesn't belong to, e.g. the Immaculate
|
||||
* Conception's own octave running Dec 8-15, squarely inside Advent).
|
||||
* Live-verified against Divino Afflatu 1954: Dec 9/10/12/14 (ordinary
|
||||
* octave days, Semiduplex) and Dec 15 (the octave's own elevated closing
|
||||
* day, Duplex majus) all win outright over Advent's own privileged-feria-
|
||||
* minor ferias, exactly the same threshold `decideOccurrence` already
|
||||
* uses for a real Semiduplex+ saint there (St. Nicholas, Dec 6).
|
||||
*
|
||||
* Deliberately excludes `ordinary-feria` (unconditional there already —
|
||||
* any active octave governs regardless of rank, handled separately by
|
||||
* each caller via `resolveActiveOctave` directly) and `christmastide`
|
||||
* (Christmas's own stacked octaves — Christmas, St. Stephen, St. John,
|
||||
* Holy Innocents — are structurally already that season's own temporal
|
||||
* content, not a foreign add-on contesting it the way the Immaculate
|
||||
* Conception's octave contests Advent: live-verified, Dec 30 keeps
|
||||
* "Dominica Infra Octavam Nativitatis" as the real DO title regardless of
|
||||
* actual weekday or which of the four octaves is active, never an octave
|
||||
* name — see calendar/day-label.ts's and hours/resolve-common.ts's own
|
||||
* Dec-30 doc comments for the fuller reasoning already established
|
||||
* there). */
|
||||
export function octaveGoverningPrivilegedDay(day: LiturgicalDay): ActiveOctave | undefined {
|
||||
if (day.temporalCategory === 'ordinary-feria' || day.season === 'christmastide') {
|
||||
return undefined;
|
||||
}
|
||||
const minRank = minimumOutrightWinningRank(day.temporalCategory);
|
||||
if (!minRank) {
|
||||
return undefined;
|
||||
}
|
||||
const octave = resolveActiveOctave(day.date);
|
||||
if (octave && compareFeastClass(octave.wins, minRank) >= 0) {
|
||||
return octave;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The comparison itself, factored out from resolveActiveOctave so the
|
||||
* precedence rule (rank, then recency) is directly unit-testable against
|
||||
* synthetic ActiveOctave data — no real equal-rank overlap exists yet in
|
||||
|
||||
Reference in New Issue
Block a user