Fix octave day labels: correct commemoration phrasing, closing-day title, and privileged-season precedence
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:
2026-08-18 08:49:51 -04:00
parent 427ba6756a
commit 3f07d6e8d3
7 changed files with 251 additions and 54 deletions
+28
View File
@@ -38,6 +38,34 @@ export function isAtLeast(rank: FeastClass, threshold: FeastClass): boolean {
return compareFeastClass(rank, threshold) >= 0;
}
/**
* The minimum rank a real sanctoral candidate needs to win each temporal
* category outright, per `decideOccurrence`'s own branches above —
* factored out so calendar/octaves.ts can ask the identical question of an
* *octave*'s own effective standing (not a real occurring saint) on a day
* where nothing else won. `undefined` where nothing ever wins outright no
* matter how high-ranked (`privileged-feria-major`/`privileged-sunday` —
* Ash Wednesday, Holy Week, a privileged Sunday — only ever get a
* commemoration at best, per those branches above). `ordinary-feria` has
* no real threshold at all (any real content wins), included here as
* `'simplex'` (the weakest real rank) only for completeness — callers
* needing that case already have their own unconditional path and don't
* need to call this. */
export function minimumOutrightWinningRank(category: TemporalCategory): FeastClass | undefined {
switch (category) {
case 'ordinary-feria':
return 'simplex';
case 'privileged-feria-minor':
return 'semiduplex';
case 'privileged-feria':
case 'ordinary-sunday':
return 'duplex';
case 'privileged-feria-major':
case 'privileged-sunday':
return undefined;
}
}
/** Vigils belong to the day *before* their feast, so an impeded vigil is
* shifted backward rather than forward like everything else — see
* calendar/transfer.ts. */