From 27acee1930dc728b286c655cb66bd5e44804df6f Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Tue, 18 Aug 2026 16:02:17 +0200 Subject: fix(ef): Christmas Time runs to 13 January (RG 72) RG 72: "Tempus natalicium decurrit a I Vesperis Nativitatis Domini usque ad diem 13 ianuarii INCLUSIVE." 6-13 January is the Epiphany section OF Christmas Time, not the start of Time after Epiphany. RG 119(a) backs the same boundary from the colour side, white "usque ad expletum tempus Epiphaniae". lectio ended Christmas Time on 5 January, so eight days a year came out green that should be white, and Epiphany itself reported the wrong season. NOT a one-line boundary change, which is why an earlier attempt was reverted rather than shipped. efSeason both named the slugs and reported the season, so moving the boundary renamed every slug in the window -- ef-time-after-epiphany-* became ef-christmas-* -- and orphaned the ef-time-after-epiphany-sunday-1 lectionary key. The two are now separate axes: efSlugSeason names slugs and numbers weeks; unchanged behaviour efSeason the liturgical season, which the colour follows; RG 72's The split is not a new idea in this file. The resumed-Sundays branch already does exactly this and says so -- "Season stays time-after-pentecost (calendrical); the Epiphany slug only routes the readings" -- it simply had no name, so the one place that needed it could not reuse it. Result, checked against colitur across all 365 days of 2026: SEASON now agrees on every single day, where it previously differed on eight. Colour differences drop to nine, every one a Saturday and every one the BVM Saturday Office (RG 78, colitur C17) which lectio does not build. Slugs and week numbers are byte-identical to before. The oracle's season axis now consults the allow-list and excludes excused days from its threshold. Neither was true before, because no cited divergence had ever BEEN a season difference -- the January window showed up as a colour one, since the wrong season produced the wrong colour too. With the season right, what is left is a season difference against missalemeum, and an axis whose threshold is a t.Fatalf would have failed regardless of any allow-list. TestTemporalEFChristmastideBoundary pins the boundary AND the split, mutation- tested both ways: reverting the boundary reddens it, and re-merging the two seasons so the slug is built from the liturgical one reddens it differently, naming the orphaned lectionary key. --- internal/calendar/oracle_ef_test.go | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'internal/calendar/oracle_ef_test.go') diff --git a/internal/calendar/oracle_ef_test.go b/internal/calendar/oracle_ef_test.go index b74d935..b765a34 100644 --- a/internal/calendar/oracle_ef_test.go +++ b/internal/calendar/oracle_ef_test.go @@ -249,11 +249,18 @@ var efAllowList = []efAllow{ // defects, and not touched here: efSeason is a season-boundary // function with much wider reach than any of the seven, and changing // it is out of this task's scope. + // CORRECTED 2026-08-18: lectio now follows RG 72 and reads 6-13 January + // as Christmas Time, so this divergence has CHANGED SIDES. It is + // missalemeum that calls those days "time after Epiphany" now, and + // what this excuses is a SEASON difference rather than the colour one + // it was written for -- the colour follows correctly once the season + // is right, which is why the six colour rows this used to cover are + // simply gone. match: func(date string, day time.Time, o efOracleDay) bool { return day.Month() == time.January && day.Day() >= 6 && day.Day() <= 13 }, - field: "colour", - reason: "RG 72-73/RG 119(a): 6-13 Jan is Christmastide, not lectio's time-after-Epiphany reading (efSeason, not one of the seven defects; see colitur rules-register.md §3c item 1)", + field: "season", + reason: "RG 72: Christmas Time runs 'usque ad diem 13 ianuarii inclusive', so 6-13 Jan is the Epiphany section OF Christmas Time; missalemeum reads it as time after Epiphany (colitur C1)", }, { // RG 33: "Vigilia II aut III classis penitus omittitur, si occurrat in @@ -330,7 +337,7 @@ func TestOracleEF(t *testing.T) { } sort.Strings(dates) - var seasonMiss, rankMiss, colourMiss, rankAllowed, colourAllowed, skipped, total int + var seasonMiss, seasonAllowed, rankMiss, colourMiss, rankAllowed, colourAllowed, skipped, total int shownSeason, shownRank, shownColour := 0, 0, 0 for _, date := range dates { od := oracle[date] @@ -356,10 +363,25 @@ func TestOracleEF(t *testing.T) { if want == "" { skipped++ } else if got.Season != want { - seasonMiss++ - if shownSeason < 40 { - t.Errorf("%s: EF season got %q want %q (from %q)", date, got.Season, want, src) - shownSeason++ + // The season axis consults the allow-list, and an excused day is + // NOT counted in seasonMiss. Neither was true before: until the + // RG 72 boundary was fixed no cited divergence had ever been a + // SEASON one -- the 6-13 January window showed up as a colour + // difference, because the wrong season produced the wrong colour + // too. With the season right the colour follows, and what is left + // is a season difference with no way to say "this one is known". + // Counting an excused day in seasonMiss would be worse than + // useless: the threshold below is a t.Fatalf, so the allow-list + // would print a reason and fail the suite anyway. + if ok, why := efAllowed(date, day, od, "season"); ok { + seasonAllowed++ + t.Logf("[allow-listed] %s: EF season got %q want %q — %s", date, got.Season, want, why) + } else { + seasonMiss++ + if shownSeason < 40 { + t.Errorf("%s: EF season got %q want %q (from %q)", date, got.Season, want, src) + shownSeason++ + } } } @@ -419,6 +441,7 @@ func TestOracleEF(t *testing.T) { t.Logf("EF oracle: %d checked, %d skipped(unmapped), %d season mismatches, "+ "%d rank mismatches (%d allow-listed), %d colour mismatches (%d allow-listed)", total, skipped, seasonMiss, rankMiss, rankAllowed, colourMiss, colourAllowed) + t.Logf("EF oracle: %d season mismatches allow-listed", seasonAllowed) if seasonMiss > 0 { t.Fatalf("%d/%d EF season mismatches vs missalemeum — temporalEF is wrong", seasonMiss, total) } -- cgit v1.3