Carry over unreached Advent bible-plan readings into Christmastide
Deploy / deploy (push) Successful in 1m55s
Deploy / deploy (push) Successful in 1m55s
Advent's own reading plan (advent-1-sunday through advent-4-saturday, 28 rows for a full 4-week Advent) is always longer than real Advent runs. Advent's last real day is always Dec 23 (Dec 24 is the Vigil of Christmas's own temporal id), while Advent 1 Sunday lands anywhere from Nov 27 to Dec 3 -- even the longest possible Advent is one row short of the full plan, and the shortest is a whole week short. Every year, at least the plan's last row never reaches its own natural date. Added adventCarryoverReadings to propers/bible-plan.ts: whatever doesn't land on its own natural date now carries forward one reading per day starting Christmas Day, pooled alongside whatever Christmastide's own calendar-dated rows already have -- never replacing them. This is the one place in bible-plan.ts that needs real calendar-year awareness (calendar/temporal.ts's adventStart). 4 new tests covering the shortest (2028) and a long (2027) Advent, pooling behavior, and the Dec-24 boundary. npm test (2029 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:
@@ -6022,3 +6022,36 @@ real `Lev 1` responsory text, `palm-sunday`/Sunday resolves real `Num 25-27` tex
|
|||||||
audit re-run, clean. Remaining 10, the true final gap: `rom` plus 9 books with no citation
|
audit re-run, clean. Remaining 10, the true final gap: `rom` plus 9 books with no citation
|
||||||
anywhere in Tempora or Sancti (`bar`, Josue, Judices, Ruth, 1-2 Chronicles, Ezra, Nehemiah,
|
anywhere in Tempora or Sancti (`bar`, Josue, Judices, Ruth, 1-2 Chronicles, Ezra, Nehemiah,
|
||||||
Deuteronomy).
|
Deuteronomy).
|
||||||
|
|
||||||
|
### Advent reading-plan carryover into Christmastide (2026-09-05)
|
||||||
|
|
||||||
|
User-flagged wrinkle in `data/hours/bible-plan/*.yml`'s own Isaiah plan: it's authored as a full
|
||||||
|
4-week Advent (`advent-1-sunday` through `advent-4-saturday`, 28 rows), but real Advent is never
|
||||||
|
that long. Advent's last real day is always Dec 23 (Dec 24 is the Vigil of Christmas's own
|
||||||
|
temporal id, `calendar/temporal-id.ts`), while Advent 1 Sunday itself — the Sunday nearest Nov 30
|
||||||
|
— falls anywhere from Nov 27 to Dec 3. Even the *longest* possible Advent (Nov 27, 27 real days)
|
||||||
|
is one short of the full 28-row plan; the *shortest* (Dec 3, 21 real days) is a whole week short.
|
||||||
|
Every single year, at least the plan's last row never lands on its own natural date — not just
|
||||||
|
"most years," as the request first put it.
|
||||||
|
|
||||||
|
Fixed in `propers/bible-plan.ts`'s new `adventCarryoverReadings`: whatever doesn't land on its own
|
||||||
|
natural date now carries forward one reading per day starting Christmas Day itself, pooled
|
||||||
|
alongside whatever Christmastide's own calendar-dated rows already have for that day (never
|
||||||
|
replacing them) — same "every contributing source pools, none override" design already used
|
||||||
|
throughout this file and `hours/matins.ts`'s own reading-pool assembly. Computed live from
|
||||||
|
`calendar/temporal.ts`'s `adventStart(year)` for each date's own year (the one place in this file
|
||||||
|
that needs real calendar-year awareness, since every other lookup here is year-independent); no
|
||||||
|
new content authoring needed, since the Isaiah rows were already there, just never reachable on
|
||||||
|
their own natural date in a short year.
|
||||||
|
|
||||||
|
User's own framing accepted as the trade-off: "for advent only, we need to show all of those
|
||||||
|
chapters" — i.e. every planned reading should surface somewhere, even if that means spilling into
|
||||||
|
Christmastide, rather than silently dropping the tail end of a short year's Advent plan.
|
||||||
|
|
||||||
|
Live-verified across the full spread of possible Advent-1-Sunday dates (`adventStart` for
|
||||||
|
2020-2035 spans exactly the Nov 27 - Dec 3 range): 2028 (Advent 1 Sunday = Dec 3, the latest/
|
||||||
|
shortest case) carries all 7 of week 4's Isaiah readings forward across Dec 25-31 in order; 2027/
|
||||||
|
2032 (Advent 1 Sunday = Nov 28, near-earliest/longest) carry only the last 2. Added 4 permanent
|
||||||
|
tests to `tests/propers/bible-plan.test.ts` covering both extremes, the pooling-not-replacing
|
||||||
|
behavior, and that nothing carries over before Christmas Day itself. `npm test` (2029 passed,
|
||||||
|
+4 new) and `tsc --noEmit` both pass.
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import { resolvePassages, type ScriptureCitation } from './octave-readings';
|
|||||||
import { getResponsoryForBook } from './matins-responsories';
|
import { getResponsoryForBook } from './matins-responsories';
|
||||||
import { getBookIncipit } from './bible-book-incipits';
|
import { getBookIncipit } from './bible-book-incipits';
|
||||||
import { canonicalBook } from '../scripture';
|
import { canonicalBook } from '../scripture';
|
||||||
|
import { adventStart } from '../calendar/temporal';
|
||||||
|
import { addDays, daysBetween } from '../calendar/date-math';
|
||||||
|
import type { Weekday } from '../calendar/types';
|
||||||
|
|
||||||
const GOSPEL_BOOKS = new Set(['matt', 'mark', 'luke', 'john']);
|
const GOSPEL_BOOKS = new Set(['matt', 'mark', 'luke', 'john']);
|
||||||
|
|
||||||
@@ -154,10 +157,54 @@ for (const mod of Object.values(modules)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WEEKDAYS: Weekday[] = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
||||||
|
|
||||||
|
/** Advent's own reading plan (`advent-1-sunday` through `advent-4-saturday`,
|
||||||
|
* 28 rows, one per day of a full 4-week Advent) is longer than Advent
|
||||||
|
* itself ever actually runs: Advent's last real day is Dec 23 (Dec 24 is
|
||||||
|
* the Vigil of Christmas's own temporal id, calendar/temporal-id.ts) but
|
||||||
|
* Advent 1 Sunday falls anywhere from Nov 27 to Dec 3 (the Sunday nearest
|
||||||
|
* Nov 30) — even in the longest possible Advent (Advent 1 Sunday = Nov 27,
|
||||||
|
* 27 real days before Dec 24) the 28th planned reading never lands on its
|
||||||
|
* own natural date, and in the shortest (Advent 1 Sunday = Dec 3, 21 real
|
||||||
|
* days) the entire last week's worth (7 readings) never does. Rather than
|
||||||
|
* silently drop that content, whatever didn't land on its own natural date
|
||||||
|
* carries forward one reading per day starting Christmas Day itself,
|
||||||
|
* pooled alongside whatever Christmastide's own calendar-dated rows
|
||||||
|
* already have for that day (never replacing them) — same "every
|
||||||
|
* contributing source pools, none override" design as everywhere else in
|
||||||
|
* this file and in hours/matins.ts's own reading-pool assembly. User
|
||||||
|
* direction, 2026-09-05: "for advent only, we need to show all of those
|
||||||
|
* chapters," accepting the spillover into Christmastide as the trade-off.
|
||||||
|
*
|
||||||
|
* Real day-by-day for `date`'s own year: the pool has no calendar-year
|
||||||
|
* awareness anywhere else (temporal ids are already year-independent), so
|
||||||
|
* this is the one place in this file that computes real dates. */
|
||||||
|
function adventCarryoverReadings(date: string): BiblePlanReading[] {
|
||||||
|
const year = Number(date.slice(0, 4));
|
||||||
|
const christmasDay = `${year}-12-25`;
|
||||||
|
const daysAfterChristmas = daysBetween(christmasDay, date);
|
||||||
|
if (daysAfterChristmas < 0) return [];
|
||||||
|
|
||||||
|
const vigilOfChristmas = `${year}-12-24`;
|
||||||
|
const start = adventStart(year);
|
||||||
|
const skippedKeys: string[] = [];
|
||||||
|
for (let i = 0; i < 28; i++) {
|
||||||
|
if (addDays(start, i) >= vigilOfChristmas) {
|
||||||
|
const week = Math.floor(i / 7) + 1;
|
||||||
|
skippedKeys.push(`advent-${week}-${WEEKDAYS[i % 7]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const key = skippedKeys[daysAfterChristmas];
|
||||||
|
return key ? (readingsByTemporalKey.get(key) ?? []) : [];
|
||||||
|
}
|
||||||
|
|
||||||
/** Every reading this plan contributes to Matins' pooled reading list for
|
/** Every reading this plan contributes to Matins' pooled reading list for
|
||||||
* `date` — both the (temporalId, weekday) row and the fixed-calendar-date
|
* `date` — the (temporalId, weekday) row, the fixed-calendar-date row (if
|
||||||
* row (if any), pooled together rather than one overriding the other, since
|
* any), and (Christmas Day onward) any Advent reading bumped past its own
|
||||||
* both can genuinely apply to the same date (see this file's header).
|
* natural date by a short Advent that year (see `adventCarryoverReadings`)
|
||||||
|
* — all pooled together rather than one overriding another, since more
|
||||||
|
* than one can genuinely apply to the same date (see this file's header).
|
||||||
* Empty array, not undefined, when nothing's authored for this day yet —
|
* Empty array, not undefined, when nothing's authored for this day yet —
|
||||||
* every caller already treats "no readings" as a valid, renderable state
|
* every caller already treats "no readings" as a valid, renderable state
|
||||||
* (see hours/matins.ts), so there's no separate "missing entirely" signal
|
* (see hours/matins.ts), so there's no separate "missing entirely" signal
|
||||||
@@ -165,5 +212,6 @@ for (const mod of Object.values(modules)) {
|
|||||||
export function getBiblePlanReadings(temporalId: string, weekday: string, date: string): BiblePlanReading[] {
|
export function getBiblePlanReadings(temporalId: string, weekday: string, date: string): BiblePlanReading[] {
|
||||||
const byTemporal = readingsByTemporalKey.get(`${temporalId}-${weekday}`) ?? [];
|
const byTemporal = readingsByTemporalKey.get(`${temporalId}-${weekday}`) ?? [];
|
||||||
const byCalendarDate = readingsByCalendarDate.get(date.slice(5)) ?? [];
|
const byCalendarDate = readingsByCalendarDate.get(date.slice(5)) ?? [];
|
||||||
return [...byTemporal, ...byCalendarDate];
|
const carryover = adventCarryoverReadings(date);
|
||||||
|
return [...byTemporal, ...byCalendarDate, ...carryover];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,3 +38,40 @@ describe('getBiblePlanReadings label uses the same book-alias normalization as v
|
|||||||
expect(readings[0]?.label?.la).toBe('Léctio libri Judith');
|
expect(readings[0]?.label?.la).toBe('Léctio libri Judith');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The Advent reading plan has 28 rows (advent-1-sunday through
|
||||||
|
// advent-4-saturday, a full 4-week Advent), but real Advent is never that
|
||||||
|
// long -- its last real day is always Dec 23 (Dec 24 is the Vigil of
|
||||||
|
// Christmas's own temporal id), while Advent 1 Sunday itself lands anywhere
|
||||||
|
// from Nov 27 to Dec 3. Even the longest possible Advent (Nov 27) is 27
|
||||||
|
// real days, one short of the full plan; the shortest (Dec 3) is 21, a
|
||||||
|
// whole week short. Whatever doesn't land on its own natural date carries
|
||||||
|
// forward one reading per day starting Christmas Day, pooled alongside
|
||||||
|
// whatever Christmastide's own calendar-dated rows already have. User
|
||||||
|
// direction, 2026-09-05: "for advent only, we need to show all of those
|
||||||
|
// chapters," accepting the spillover into Christmastide as the trade-off.
|
||||||
|
describe('Advent reading-plan carryover into Christmastide', () => {
|
||||||
|
it('a short Advent (2028: Advent 1 Sunday = Dec 3, latest possible) carries all 7 of week 4\'s readings forward, one per day', () => {
|
||||||
|
const isaiahCitations = [25, 26, 27, 28, 29, 30, 31].map(
|
||||||
|
(day) => getBiblePlanReadings('christmas-octave-sunday', 'monday', `2028-12-${day}`).find((r) => r.citation.la?.startsWith('Isa'))?.citation.la,
|
||||||
|
);
|
||||||
|
expect(isaiahCitations).toEqual(['Isa 46-48', 'Isa 49-51', 'Isa 52-54', 'Isa 55-57', 'Isa 58-60', 'Isa 61-63', 'Isa 64-66']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('a long Advent (2027: Advent 1 Sunday = Nov 28, near-earliest) carries only the last 2 readings forward', () => {
|
||||||
|
const isaiahCitations = [25, 26, 27, 28].map(
|
||||||
|
(day) => getBiblePlanReadings('christmas-octave-sunday', 'monday', `2027-12-${day}`).find((r) => r.citation.la?.startsWith('Isa'))?.citation.la,
|
||||||
|
);
|
||||||
|
expect(isaiahCitations).toEqual(['Isa 61-63', 'Isa 64-66', undefined, undefined]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('carried-over readings pool alongside Christmastide\'s own calendar-dated rows, never replacing them', () => {
|
||||||
|
const readings = getBiblePlanReadings('christmas-octave-sunday', 'friday', '2028-12-25');
|
||||||
|
expect(readings.map((r) => r.citation.la)).toEqual(['Cant 1', 'Isa 46-48']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not carry anything over before Christmas Day', () => {
|
||||||
|
const readings = getBiblePlanReadings('vigil-of-christmas', 'thursday', '2028-12-24');
|
||||||
|
expect(readings.find((r) => r.citation.la?.startsWith('Isa'))).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user