diff options
| -rw-r--r-- | docs/superpowers/plans/2026-07-27-lectio-ef-calendar-engine.md | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-07-27-lectio-ef-calendar-engine.md b/docs/superpowers/plans/2026-07-27-lectio-ef-calendar-engine.md new file mode 100644 index 0000000..c74e737 --- /dev/null +++ b/docs/superpowers/plans/2026-07-27-lectio-ef-calendar-engine.md @@ -0,0 +1,47 @@ +# EF (1962) Calendar Engine Implementation Plan + +> REQUIRED SUB-SKILL: superpowers:executing-plans. Spec: docs/superpowers/specs/2026-07-27-lectio-ef-calendar-engine-design.md. + +**Goal:** Compute the 1962 (EF) liturgical day offline, form-aware in the existing engine (`sel.Form == "old"`), validated against the missalemeum per-date API. + +**Global constraints:** `calendar` stays stdlib-pure; `Compute` total. Both OF and EF oracles must pass (OF: 0 season mismatches; EF: 0 season mismatches). Reuse Computus/merge/INI/result + custom layers. + +--- + +### Task 1: Rank int enum → unified string token (OF refactor, oracle-gated) +**Files:** `internal/calendar/types.go`, `precedence.go`, `calendar.go`, `temporal.go`, `internal/cli/liturgy.go`, tests. +- [ ] `type Rank string`; consts `RankFerial="ferial"`, `RankOptional="optional"`, `RankMemorial="memorial"`, `RankFeast="feast"`, `RankSolemnity="solemnity"`. `ParseRank` validates → `Rank(s)` or `RankFerial`. +- [ ] Add `func ofRankOrder(r Rank) int` (ferial0<optional1<memorial2<feast3<solemnity4). Replace the two `cel.Rank < RankSolemnity` / `< RankFeast` sites in `calendar.go` with `ofRankOrder(...)` comparisons. Equality checks (`== RankSolemnity`) stay as-is (strings). +- [ ] `rankLabel` (cli) switches on the string values. +- [ ] Build; `go test ./internal/calendar/ ./internal/cli/`; **re-run OF oracle → 0 season mismatches**. Commit. + +### Task 2: EF seasons + `temporalEF` +**Files:** `internal/calendar/types.go` (season consts), `internal/calendar/temporal_ef.go` (new); Test `temporal_ef_test.go`. +- [ ] Add Season consts: `Septuagesima`, `Passiontide`, `TimeAfterEpiphany`, `TimeAfterPentecost` (advent/christmas/lent/easter/triduum shared). +- [ ] `func temporalEF(date time.Time) temporalDay`: Computus + `adventStart` → the 1962 seasons (Advent; Christmastide + octaves; Time after Epiphany; Septuagesima/Sexagesima/Quinquagesima; Lent; Passiontide; Holy Week/Triduum; Paschaltide + octaves; Time after Pentecost incl. Trinity/Corpus/Sacred Heart, resumed Epiphany Sundays, Christ the King = last Sun of October, Last Sunday). EF ranks on temporal cels (`class-1` Sundays of Advent/Lent, etc.). +- [ ] Unit tests for a handful of dates (Septuagesima Sunday `easter-63`; a Sunday after Pentecost; Christ the King = last Sun of Oct). Season boundaries validated wholesale by Task 6's oracle. Commit. + +### Task 3: `precedenceEF` (1960 rubrics) +**Files:** `internal/calendar/precedence_ef.go` (new); Test `precedence_ef_test.go`. +- [ ] `efRankOrder(Rank) int` (class-1 highest … ferial/commemoration lowest). `precedenceEF(candidate) int` + `pickEF` mirroring the OF pattern but by 1960 class/occurrence. Impeded I-class transfer handled in the EF Compute branch. +- [ ] Unit tests: a I-class feast beats a III-class feast beats a IV-class feria; commemoration lowest. Commit. + +### Task 4: `Compute` form branch +**Files:** `internal/calendar/calendar.go`; Test `calendar_test.go`. +- [ ] `Compute` branches on `sel.Form`: `"old"` → `temporalEF` + EF candidate build + `precedenceEF`/`pickEF` + EF transfer; else existing OF path. Shared: merge, buildCelebration, result assembly, cycles (cycles are OF-only concepts → leave SundayCycle/WeekdayCycle empty for EF). +- [ ] Test: an EF layer's `class-1` feast observed on its date; OF path unchanged. Commit. + +### Task 5: `caldata.Tridentine()` + form-aware `Stack` +**Files:** `internal/caldata/tridentine-calendar.ini` (new, initial subset), `internal/caldata/caldata.go`; Modify `internal/cli/liturgy.go`; Tests. +- [ ] Embed `tridentine-calendar.ini` (a real initial 1962 sanctoral subset: the great feasts with `class-1…4`, EF dates/colours/names). `Tridentine() calendar.Layer`; `Base(form) calendar.Layer` (old→Tridentine, else Universal); `Stack(form, dir, use)` prepends `Base(form)`. +- [ ] Update `runLiturgy` to `caldata.Stack(cfg.Selection().Form, dir, cfg.Use)`; **remove the EF guard**; form-aware `rankLabel`. +- [ ] Tests: `Tridentine()` loads; `Stack("old",…)` base is the EF layer. Commit. + +### Task 6: missalemeum EF oracle +**Files:** `scripts/build-oracle-ef.sh` (new), `internal/calendar/testdata/oracle-ef.json` (generated), `internal/calendar/oracle_ef_test.go` (new). Iterate `temporal_ef.go` + data. +- [ ] `build-oracle-ef.sh`: fetch `https://www.missalemeum.com/en/api/v5/proper/{date}` per day over 2024–2028; map `tempora`→season/week, `rank`(1–4)→`class-N`, `colors`→colour; write `{date:{season,rank,colour}}`. Not run by `go test`. +- [ ] `oracle_ef_test.go`: compute each day (Form=old, `[Tridentine()]`), assert **season strictly** (SKIP if snapshot absent); rank/colour informational. +- [ ] Iterate until 0 season mismatches (document exceptions). Commit snapshot + fixes. + +### Task 7: full verification + version bump +- [ ] `config.Version` → `0.28.0`. `go build/vet/test ./...`; both oracles pass; calendar still stdlib-pure. Live check: `lectio 2025-08-15 --lectionary trad -L` computes the EF Assumption. Commit. |
