From a095ed8e31947adb06d363002a134973a42517de Mon Sep 17 00:00:00 2001 From: Will Estes Date: Sat, 5 Sep 2026 20:06:45 -0400 Subject: [PATCH] Fix Matins occasion label reusing winner-centric day label occasionName's fallback reused getDayLabel(day), which is winner-centric -- when a sanctoral feast wins the day, it returns the winner's own full label including rank (e.g. "All Saints (Duplex I Class)"), so a reading tagged with the governing Sunday's temporal id got wrapped as "(for All Saints (Duplex I Class))" instead of naming the Sunday. Exports day-label.ts's existing temporalLabel helper (the plain ordinal Sunday/ feria name, independent of who won) and uses it instead whenever a saint has won the day. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01LGsATZvfnpQ81HQuoF5JuL --- src/calendar/day-label.ts | 9 ++++++++- src/hours/matins.ts | 9 +++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/calendar/day-label.ts b/src/calendar/day-label.ts index bd43821..08c5640 100644 --- a/src/calendar/day-label.ts +++ b/src/calendar/day-label.ts @@ -320,7 +320,14 @@ function trinitytideOverrideLabel(day: LiturgicalDay): Bi | undefined { return undefined; } -function temporalLabel(day: LiturgicalDay): Bi { +/** The plain temporal/Sunday-or-feria name only (ordinal week/season + * phrasing, e.g. "The Twenty-fourth Sunday after Pentecost"), independent of + * who actually won the day — i.e. what the day would be called if the + * temporal cycle were the winner. Exported for callers (e.g. + * `hours/matins.ts`'s `occasionName`) that want to name the governing + * Sunday/feria without repeating a sanctoral winner's own rank-qualified + * label from `getDayLabel`. */ +export function temporalLabel(day: LiturgicalDay): Bi { const trinitytideOverride = trinitytideOverrideLabel(day); if (trinitytideOverride) { return trinitytideOverride; diff --git a/src/hours/matins.ts b/src/hours/matins.ts index a422746..4c99ea3 100644 --- a/src/hours/matins.ts +++ b/src/hours/matins.ts @@ -71,7 +71,7 @@ import type { ResolvedOrdo, ResolvedPart, ResolvedText, ResolvedVerse } from './ import type { LiturgicalDay, DayWinner } from '../calendar/types'; import { resolveDay, resolveTemporalId, monthWeekId, activeOctavesFor, resolveActiveOctave, isAtLeast } from '../calendar'; import { isInTriduum } from '../calendar/temporal'; -import { getDayLabel, emberDayLabel } from '../calendar/day-label'; +import { getDayLabel, emberDayLabel, temporalLabel } from '../calendar/day-label'; import { getPsalmVerses } from '../psalter'; import { getPsalmsFor, type PsalmRef } from '../psalter/distribution'; import { getScriptureVerses } from '../scripture'; @@ -799,7 +799,12 @@ function occasionName(id: string, day: LiturgicalDay): string | undefined { if (named) return named; const monthWeek = monthWeekId(day.date); if (id === resolveTemporalId(day.date) || (monthWeek && id === `month-week-${monthWeek}`)) { - return getDayLabel(day).en; + // When a saint (not the temporal day itself) won the day, name the + // plain Sunday/feria only — `getDayLabel`'s own text is winner-centric + // (the winning saint's full name + rank), which would otherwise get + // pointlessly repeated back into its own "(for ...)" annotation, since + // the winner's name is already shown elsewhere as the day's heading. + return day.winner.kind === 'sanctoral' ? temporalLabel(day).en : getDayLabel(day).en; } return undefined; }