Living Word Live-Ops Phase 1 — Christian Calendar + Holy Week Event + Streak Foundation
Status. Canonical acceptance spec. Engineering builds against this file. The founder reviews before any code lands. What this spec is. A build brief for Phase 1 of Living Word's live-ops layer — the three-feast Christian calendar default, the Holy Week six-scene headline drop, and the multi-streak telemetry foundation. What this spec is NOT. It is not a strategic narrative (see
drafts/2026-05-23-living-word-liveops-research.md). It is not the UGC spec (seeacceptance/living-word-ugc-phase1.md). It is not the lead-gen spec. Prayer circles, Sunday school class mode, family rooms, the inter-tradition forum, and the Sabbath streak are explicitly out of Phase 1; they are named in §14. Load-bearing constraints. Decision 4 fromdrafts/2026-05-23-living-word-founder-decisions.mdis quoted verbatim in §1 and is not up for re-negotiation. The voice rule from research §1 is quoted verbatim in §1 and is enforced as a copy-bug threshold (any "evolves" in any surface = ship-blocking bug). The AI Bridge Principle (knowledge/architecture/ai-bridge-principle.md) governs every player-facing surface. The 5-Act framework (drafts/2026-05-23-gods-restorative-kingdom-framework.md) is the spine the calendar deepens, never competes with.
§1 — Founder-locked decision + voice rule (verbatim)
These two constraints, locked on 2026-05-23 by the founder, govern the entire spec. Engineering cannot re-litigate either. If engineering reads this spec and disagrees, it raises the disagreement to the founder — it does not silently re-shape the spec.
Decision 4 — Liturgical calendar default
Founder answer: D (Christmas + Easter + Pentecost on for everyone; full liturgical calendar opts in via tradition lens)
"Christmas + Easter + Pentecost are observed across all Christian traditions (even most non-denominational evangelicals). Defaulting to these three gives every player the retention benefit without imposing tradition-specific observances. The full liturgical calendar (Advent, Lent, Holy Week, Christ the King, saints' days) opts in via the tradition lens — Catholic/Orthodox/Anglican players who pick their lens automatically get the full calendar; Baptist/non-denominational players don't unless they explicitly turn it on."
Implication for this spec. Three events ship as "universal scope" (Christmas, Easter / Eastertide, Pentecost) — every player sees them regardless of tradition. The Western liturgical year (Advent, Lent, Christ the King, Ordinary Time saints' days) is scoped to lens-gated traditions. The Eastern Orthodox liturgical year is Phase 2 (Pascha calculation diverges). Holy Week is the one cross-cutting case — the six-scene event itself is universal-scope (everyone gets it) because Holy Week leading to Easter is universally observed; the gameplay-side fasting and disciplines tracker around Lent is lens-gated to Phase 2.
Voice rule (research §1 — verbatim)
The retention copy verbs are deepens, grows, unfolds, is always becoming, is always new — never "evolves." This is a hard founder-voice rule, not a stylistic preference.
Implication for this spec. Every surface this spec touches — UI copy, modal text, badge labels, settings labels, error messages, audit-log action names, internal telemetry event names that may ever surface in customer-facing analytics — uses the approved verbs. The string "evolves" appearing anywhere player-facing is a ship-blocking copy bug. The string "evolves" appearing in code comments or PR descriptions is a soft signal that the author has not internalized the voice rule and is asked to revise. A lint pass (scripts/living-word/voice-rule-check.mjs) is added in Phase 1A that greps the src/app/living-word/ tree for the banned verb and fails the build on match; "is always new" / "deepens" / "grows" / "unfolds" are the substitutions.
§2 — Scope (what Phase 1 ships)
Three deliverables, in dependency order. Sub-phasing is in §12.
1. Liturgical calendar engine
Server-side date resolver that, given a date (and optionally the player's tradition lens), returns the active liturgical event (if any) and the next three upcoming events. Phase 1 ships THE THREE-FEAST DEFAULT (Christmas Dec 25 → Jan 5 Christmastide; Easter Sunday + Eastertide 50 days; Pentecost) for ALL players. The full Western liturgical year (Advent + Lent + Holy Week + Christ the King + Ordinary Time saints' days) is tradition-lens-gated (player picks Catholic / Orthodox / Anglican / Lutheran / Methodist → full calendar active; Baptist / non-denominational / evangelical → three-feast only). Eastern Orthodox calendar is Phase 2.
2. Holy Week six-scene event (the headline drop)
Six new short scenes (~5-10 min each) unlocking one per day across Holy Week (Palm Sunday → Holy Saturday). NEW content shipping with Phase 1 and playable only during the 7-day window with a 14-day grace period for catch-up afterward. Easter Sunday completion of all 6 unlocks a special relic + Resurrection cinematic + a temporary "Holy Week 2027 — completed" profile badge.
3. Streak telemetry foundation
Data model + minimal UI for tracking multiple streak types (Scripture reading, prayer, scene completion, helping another player). Phase 1 ships THE DATA + ONE VISIBLE STREAK (scene completion) + the toggle that lets players opt out. The Sabbath streak (which rewards NOT playing on rest days), streak gifting, and the additional visible streaks are Phase 2 — they plug into the Phase 1 data model without schema changes.
What Phase 1 explicitly does NOT do (deferred — engineering MUST refuse to build any of these inside Phase 1's scope)
- Prayer circles — Phase 2 (social mechanics)
- Sunday school class mode — Phase 2 (requires
lw_classroomsspec, not yet authored) - Family rooms — Phase 2
- Inter-tradition discussion forum — Phase 3+ (highest moderation overhead in the research doc)
- Friend visiting / scribe library annotations — Phase 3+
- Advent + Lent + Christ the King events — Phase 2 (real, but lens-gated and lower-impact than the three universal feasts)
- Streak gifting (a friend can freeze your streak for you) — Phase 2 (requires friend graph)
- Multiple visible streaks — Phase 2 (Phase 1 ships data for all 4 streak types; only
scene_completionrenders) - Sabbath streak (counter-cultural rest-rewarding streak) — Phase 2
- Eastern Orthodox calendar — Phase 2 (Pascha not Western Easter)
- Push notifications of any kind — Phase 3 minimum, with strict opt-in and no-urgency-copy rule
- All Saints / Christ the King / minor feasts — Phase 2+
§3 — Liturgical calendar engine
The engine is a pure server-side function with no database state. The Phase 1 calendar is hard-coded in src/lib/living-word/calendar.ts. The DB tables in §7 store per-player preferences (opt-outs, completion records), not the calendar itself. This keeps the calendar deterministic and replicable on any environment without seed data.
Function signature
type LiturgicalEvent = {
id: string; // stable id e.g., 'easter-2027', 'holy-week-2027'
name: string; // 'Easter', 'Christmastide', 'Holy Week', 'Pentecost'
season: string; // 'easter' | 'christmas' | 'pentecost' | 'advent' | 'lent' | 'ordinary' | 'holy_week'
scope: 'universal' | 'western_liturgical' | 'orthodox' | 'six_scene_event';
started_at: Date; // UTC; inclusive
ends_at: Date; // UTC; inclusive
};
type ActiveEventResult = {
active: LiturgicalEvent | null; // null when no event is active for the given date+lens
upcoming: LiturgicalEvent[]; // the next 3 events the player would see, in chronological order
};
export function resolveActiveEvent(
date: Date,
traditionLensId?: string, // when omitted or 'universal', only universal-scope events surface
): ActiveEventResult;
Phase 1 hard-coded calendar
| Event | Window | Scope | Lens-gating |
|---|---|---|---|
| Christmas | Dec 25 → Jan 5 (Christmastide) | universal | None (every player) |
| Easter Sunday + Eastertide | Easter Sunday → Pentecost Sunday (50 days) | universal | None (every player) |
| Pentecost | Easter + 49 days, the Sunday after Eastertide ends | universal | None (every player) |
| Holy Week | Palm Sunday → Holy Saturday (7 days) + 14-day grace | six_scene_event | None (universal scope — every player) |
| Advent | 4 Sundays before Christmas → Dec 24 | western_liturgical | Active only when player's lens ∈ {Catholic, Anglican, Lutheran, Methodist, Eastern Orthodox*} |
| Lent | Ash Wednesday → Holy Saturday | western_liturgical | Same lens set; Phase 1 ships the dates only — the gameplay-side fasting/disciplines tracker is Phase 2 |
*Eastern Orthodox lens reads the Advent + Lent windows from the Western calendar in Phase 1 with a flagged TODO; correct Orthodox date math lands in Phase 2.
Western Easter calculation (Computus)
Easter Sunday for Phase 1 is calculated using the Anonymous Gregorian Algorithm (the standard Computus algorithm; see the Wikipedia Computus article for the canonical pseudo-code). The function lives in src/lib/living-word/calendar.ts as a private helper computeWesternEaster(year: number): Date. It MUST be implemented from the algorithm, not pulled from an external library (the algorithm is ~20 lines of integer arithmetic; an external dependency is unnecessary risk for a load-bearing date function).
From Easter Sunday, derived dates are computed by simple offset:
- Palm Sunday = Easter − 7 days
- Maundy Thursday = Easter − 3 days
- Good Friday = Easter − 2 days
- Holy Saturday = Easter − 1 day
- Pentecost Sunday = Easter + 49 days
- Ash Wednesday = Easter − 46 days
- First Sunday of Advent = the Sunday closest to (Nov 30, but no earlier than Nov 27); deterministic helper required
The function MUST be unit-tested against a hand-verified table of Easter dates for years 2025-2035; see §11.
Edge cases the engine MUST handle correctly
- Overlap. When two events overlap (Holy Week IS during Lent for lens-gated traditions), the engine returns the more-specific event as
activeand the broader season as the first item inupcomingafter it ends. Holy Week takes precedence over Lent. Easter takes precedence over Eastertide on Easter Sunday. - Day-boundary semantics. All windows are interpreted in UTC for the date math, but rendering uses the player's local date for "today" determination at the API layer. A player in Sydney on Palm Sunday morning sees the Palm Sunday scene unlock; a player in Los Angeles 8 hours behind UTC still on Holy Saturday does not. Implementation note: the API takes the date as a string
YYYY-MM-DDinterpreted at the player's IANA timezone (defaulting to UTC if unknown). - Lens-gating fallthrough. If no event is active for the player's lens, but a universal-scope event is active, the universal event is returned. The lens-gating is additive, never subtractive — a Reformed player whose lens does not normally surface Advent still sees Christmas Dec 25 → Jan 5.
§4 — Holy Week six-scene event (the headline drop)
The Holy Week event is the Phase 1 headline content drop. It is the single most theologically dense and emotionally resonant week in the Christian year, and the live-ops research §4 names it as our equivalent of Hay Day's December event.
The six scenes
| # | Day | Scene | Scripture | Quest summary | Playtime | Lens treatment |
|---|---|---|---|---|---|---|
| 1 | Palm Sunday | Triumphal Entry | Matt 21:1-11 (cf. Mark 11:1-11, Luke 19:28-44, John 12:12-19) | Identify the messianic expectations in the crowd; the cloaks-and-palms gesture and the "Hosanna" cry both quote prophecy (Zech 9:9, Psalm 118:25-26). Player chooses how to read the moment alongside three voices in the crowd. | 5-7 min | Default shows the three theological-readings rotation (suffering-king / prophetic-fulfillment / political-tension). Specific tradition lens surfaces its preferred reading first. |
| 2 | Holy Monday | Cleansing the Temple | Mark 11:15-19 (cf. Matt 21:12-17, Luke 19:45-48, John 2:13-22 — note the Johannine timing difference) | Theological tension between Jesus's prophetic act and the political stakes; Player must weigh "zeal for God's house" (Psalm 69:9 quoted in John 2:17) against the rising authorities' resolve. | 5-7 min | Lens-aware; the Johannine-vs-Synoptic timing question gets a one-line scholar note in scholar mode (lens-gated to Reformed / Catholic / Anglican / Lutheran intermediate+). |
| 3 | Holy Tuesday | Teaching in the Temple | Matt 22-25 (the Olivet Discourse selections) + Mark 12:28-34 (the greatest commandment) | Engage one parable of judgment (Matt 25:31-46 — sheep and goats by default) AND the question about the greatest commandment. Player connects the love command to the Restorative-Kingdom thread (Act III → Act V). | 7-10 min | Tradition lens surfaces its preferred parable: Catholic/Orthodox/Anglican = sheep-and-goats; Reformed = wise/foolish virgins; Anabaptist = talents-as-stewardship. Player can switch via the lens chip. |
| 4 | Holy Wednesday (Spy Wednesday) | Anointing at Bethany + Judas's Betrayal | Matt 26:6-16 (cf. Mark 14:3-11, John 12:1-8) | Contrast Mary's devotion with Judas's calculation in the same chapter. Player makes the side-by-side comparison explicit; the scene's centerpiece is the alabaster-jar moment. | 5-7 min | Mary of Bethany (vs. Mary Magdalene — common conflation) is identified correctly; lens-aware footnote available. |
| 5 | Maundy Thursday | Last Supper + Gethsemane | John 13:1-17 (foot-washing) + John 13-17 (farewell discourse selections) + Matt 26:36-46 (Gethsemane) | Foot-washing as theological inversion of power — the Lord-as-servant gesture sets up everything that follows. Player walks through the cup and the bread (the Eucharist origin); lens treatment carefully serves Catholic real-presence, Reformed memorial, Lutheran sacramental-union, Anglican ambiguity, Anabaptist ordinance without privileging one. Then to Gethsemane and the "not my will but yours." | 7-10 min | Highest-lens-sensitivity scene of the six. Founder MUST personally review the Eucharist treatment for every represented tradition. |
| 6 | Good Friday | Crucifixion | John 19 + Matt 27:45-54 + Luke 23:34-46 + Mark 15:33-39 | The seven last words (drawn from all four gospels, traditionally enumerated). The rending of the temple veil (Matt 27:51). Player walks the via dolorosa as a contemplative sequence; mini-game is identifying which word came from which gospel + reflecting on what the veil-rending signifies (Hebrews 10:19-20 as cross-reference). | 7-10 min | Tradition lens shapes which "last word" gets foregrounded (Catholic / Orthodox / Anglican traditions historically emphasize different words). |
There is no separate seventh scene. Completing all 6 of the above on Easter Sunday triggers the Easter Sunday Resurrection cinematic + a permanent relic in the player's library + a temporary "Holy Week 2027 — completed" badge on the player's profile. The cinematic is authored separately (founder voice work) and integrates with the existing LegacyBloomCinematic.tsx pattern for production quality.
Authoring boundary (engineering vs. content)
The six scenes are CONTENT. The founder authors them personally OR a contractor authors under founder review. UGC is explicitly NOT a source for these scenes — they are canonical. Authoring is NOT engineering scope.
Engineering scope for §4 is the infrastructure, not the content:
- The event-window mechanic (lock pre-event, unlock day-by-day during event, grace period, re-lock after grace)
- The unlock-state computation (which scenes are playable today for this player)
- The completion tracking (which of the 6 has this player completed; on Easter Sunday — was the 6th completion the one that crossed the threshold)
- The Resurrection cinematic trigger
- The relic award + profile-badge persistence
- The HUD badge ("Holy Week — Day X of 6")
- The CalendarEventModal listing the 6 scenes with their unlock status
The six scene authoring is tracked separately as a founder action item (§15).
Event window mechanic (precise rules)
For year Y, given Palm Sunday = palm and Holy Saturday = holy_saturday:
-
Pre-event (any date <
palm): The 6 Holy Week scenes are not visible in the scene picker. Direct URL access to a scene is permitted IF the player has previously played it in a past year (i.e., they're allowed to re-read what they've already experienced), but a fresh player cannot reach an un-played Holy Week scene before Palm Sunday. -
During event (
palm≤ today ≤holy_saturday): On daypalm + N(0 ≤ N ≤ 5), scenes 1 through N+1 are playable. By Holy Saturday all 6 are playable. Direct URL access to a not-yet-unlocked scene 404s with a "this scene unlocks [date]" message. -
Easter Sunday (
palm + 7): All 6 remain playable. Completing all 6 on or before Easter Sunday triggers the Resurrection cinematic. Completing the 6th scene at any point during the 14-day grace period also triggers the cinematic — the player does not need to finish on Easter Sunday exactly. -
Grace period (
palm + 8throughpalm + 21, i.e., 14 days post-Easter): All 6 scenes remain playable for catch-up. -
Post-grace (
palm + 22onward, until next year's Palm Sunday): Holy Week scenes lock again. Players who completed them retain the scenes in their library as completed; un-completed scenes are not playable until next year. (Replays of completed scenes are always allowed, year-round, per the broader library design.)
The HUD badge ("Holy Week — Day X of 6", or during grace "Holy Week — catch up by [date]") appears whenever Holy Week is active per the engine and the player has not yet completed all 6 scenes. After all 6 are completed, the badge is replaced by the relic icon (temporarily for ~7 days, then folded into the standard library view).
§5 — Streak telemetry foundation
The streak mechanic is the single most dangerous live-ops mechanic to port to a Bible-curriculum product. The naive Duolingo port creates a "faith-as-habit-tracker" frame: lose your streak → lose your relationship with God. This is theologically corrosive and pastorally harmful. The seven rules below from research §7 are the guardrails. Engineering MUST NOT relax any of them; if a product question seems to require a workaround, escalate to the founder.
The seven rules (verbatim from research §7)
Streaks measure activity, not state. "You read Scripture for 30 days straight" is celebratable. "Your faith level is 30" is not. Every streak in the UI is verb-and-noun ("30 days of Scripture reading"), never adjective ("you are a 30-day faithful one"). Founder should review every streak-related copy line for this.
Streak recovery without shame. When a player breaks a streak, next open is welcoming: "Welcome back. You're here. That's what matters. The story is still here for you." Counter resets without an ominous animation. We explicitly invert Duolingo's anxiety frame.
Multiple meaningful streak types. Five small streaks (Scripture reading, prayer, scene completion, NPC chat, Lectio Divina), not one monolithic streak. A player in a busy week keeps one going while letting others rest — matches the actual rhythm of spiritual life.
Streak freezes given as gifts. A player can freeze a friend's streak (one freeze per week) — an act of love-of-neighbor, not commerce. No paid freezes ever.
Sabbath streak. A weekly opt-in: pick one day as sabbath. On that day the streak does not advance, by design. The single most counter-cultural mechanic in the doc — a streak that encourages rest. The Mosaic command built into the engagement engine.
Optional, always. Settings: "Turn off streaks if they don't serve you." AI Bridge applied to mechanics: we are a bridge to spiritual formation, never more important than the formation itself.
No streak push notifications. Duolingo's "Don't lose your streak!" is the most-studied dark-pattern in mobile gaming. We do not send it. Our notifications (if opted in) are parable-shaped: "A passage you walked is glowing in the library today" — calmer, never threatening.
Phase 1 scope within rules 1-7
| Rule | Phase 1 implementation |
|---|---|
| 1 | All streak copy is verb-and-noun. Audit happens at the §11 test layer; CI lint greps for adjective patterns. |
| 2 | The next-open-after-break copy is hard-coded. No "you lost X days" framing anywhere. |
| 3 | Data model supports four streak types in Phase 1 (scene_completion, scripture_reading, prayer, helping_others). ONE — scene_completion — is visible in the HUD. The others accumulate silently; their visible UI is Phase 2. The 5th type from the research (Lectio Divina) is reserved as a future enum value but not implemented in Phase 1 (Lectio needs its own mechanic spec first). |
| 4 | Data model supports streak freezes (a streak_event of type gifted_freeze is recorded). The UI to gift a freeze is Phase 2 (requires friend graph). Phase 1 ships the data hook so Phase 2 plugs in without migration. |
| 5 | Sabbath streak is Phase 2. Phase 1 stubs the field (sabbath_day_of_week INT NULL) on lw_player_streaks so the Phase 2 mechanic can populate it without migration. |
| 6 | Opt-out toggle ships in Phase 1. One-click in the StreakDisplay modal AND a global toggle in /living-word/settings. Honored immediately at the API layer (subsequent reads return opt_out=true and the UI hides the display). |
| 7 | Phase 1 ships ZERO notifications of any kind. The parable-shaped notification copy is designed in Phase 3 with explicit opt-in. |
Streak default state (Phase 1 answer)
The spec recommends scene-completion streak ON by default for new players, with prominent opt-out in onboarding. Rationale: the founder-decisions doc Decision 4 chose the "compounding-retention-for-everyone" default for the calendar; consistency argues the same default for streaks. The opt-out must be one-click and visible enough that a player who finds the streak shame-shaped exits cleanly. The §15 founder-handoff section asks the founder to confirm this default before Phase 1B ships.
Note for the founder. The live-ops research §11 Decision 2 recommended OFF-by-default for streaks. This spec departs from that recommendation in favor of ON-by-default with prominent opt-out. The reasoning: research §10's Q1 KPI is "D7 retention ≥35%" — opt-in defaults rarely deliver that. The founder is asked to confirm or override in §15.
UI surface — StreakDisplay.tsx
A stub of this file exists today at churchwiseai-web/src/app/living-word/components/StreakDisplay.tsx (verified 2026-05-23). Engineering MUST extend that stub, not overwrite it. The current stub reads from a /api/living-word/streak endpoint and renders a simple "X days streak" badge. The Phase 1 extension:
- Reads from the new
/api/living-word/streaksendpoint (plural; Phase 1 endpoint shape is in §3 of this section). - Visually subordinate to the existing 5-Act HUD chip (positioned beside or below
FiveActsHudChip; never displaces it). - Badge shows: a flame OR seed icon (founder picks; recommend seed — gentler, more theologically resonant) + the current scene-completion streak count + a subtle pulse on first-open-of-day-with-increment.
- Tap → modal showing: streak type label ("Scenes walked"), current length, longest length to date, last activity date, opt-out toggle, "What this measures" copy.
- The "What this measures" copy MUST honor rule #1 verbatim: e.g., "Scenes walked: the number of days in a row you have walked through at least one scene. This is not a measure of your faith — it is a measure of your engagement with the story."
API contract
POST /api/living-word/streaks/update — called by LivingWordAdventure on scene completion. Idempotent: same scene + same player + same calendar day = no double-increment. Body: { streak_type: 'scene_completion' | 'scripture_reading' | 'prayer' | 'helping_others', scene_id?: string, date?: string (YYYY-MM-DD, defaults to player-local today) }. Returns the updated streak state for that player + streak type.
GET /api/living-word/streaks — returns all of the player's current streak states (Phase 1 returns 4 rows but only scene_completion is rendered). Body: { streaks: [{ type, current_length, longest_length, last_activity_at, opt_out, paused_at }] }.
POST /api/living-word/streaks/opt-out — body { streak_type, reason? }. Sets opt_out=true immediately; subsequent GET returns opt_out=true and the StreakDisplay hides itself for that streak type.
Anonymous-player support
Phase 1 streaks MUST work for anonymous players (no auth required) via session/localStorage. On login, the anonymous streak state migrates to the user account — a lw_player_streaks row is created or updated, taking the higher of (anonymous streak, account streak) for current_length and longest_length. The migration runs at the first authenticated GET /api/living-word/streaks after login.
Migration is one-way: once an anonymous session's data is migrated to an account, the localStorage state is cleared. This prevents drift if the player later logs out and back in with a different account.
What MUST NEVER happen (load-bearing — restated from anti-goals)
- No push notification fires when a streak is at risk of breaking.
- No streak-display copy uses "lost", "broken", or "failed". The vocabulary is "paused", "starting fresh", "today is a new day".
- No streak counter displays in red, orange, or any anxiety-coded color.
- No comparison to friends ("Sarah's streak is 30 days").
- No reset animation. The counter quietly returns to 0 (or 1 if a same-day session reopens the streak).
- No streak earns currency, premium content, or any reward beyond the visible counter itself.
§6 — Pricing gate
All Phase 1 live-ops features are free to all players. The liturgical calendar, Holy Week event, and scene-completion streak ship without paywalls. This is intentional and strategically load-bearing. The lead-gen spec's frame is "Living Word IS the ad" — gating retention mechanics undercuts that frame and breaks the funnel.
Phase 3 premium tier ($4.95/mo) will add: scholar-mode quizzes for Holy Week scenes, the optional Lent disciplines tracker (when shipped in Phase 2), the additional visible streaks (Phase 2 ships the data model; Phase 3 reveals the additional visible surfaces as a premium feature). The premium tier deliberately deepens features that already exist for free; it never gates a feature that previously existed for free.
The free / paid boundary is enforced at the API layer for the Phase 3 features when they ship; in Phase 1 there is no boundary to enforce. Engineering MUST NOT add a paywall check anywhere in the Phase 1 surfaces.
§7 — Data model (DB tables)
All tables live in churchwiseai-web/migrations/2026-MM-DD-lw-events-and-streaks.sql. All tables under RLS. Owner: churchwiseai-web (per the post-decouple migration-ownership rule). Migration is idempotent (CREATE TABLE IF NOT EXISTS, CREATE INDEX IF NOT EXISTS). Founder applies post-merge.
lw_player_streaks
Per-player streak state. One row per (user_id, streak_type).
| Column | Type | Notes |
|---|---|---|
id | UUID PK | gen_random_uuid() |
user_id | UUID NOT NULL | FK to auth.users.id (anonymous players use a synthesized UUID stored in localStorage; migrates on login) |
streak_type | TEXT NOT NULL CHECK (streak_type IN ('scene_completion','scripture_reading','prayer','helping_others')) | Phase 1 implements scene_completion only; others are reserved enum values |
current_length | INT NOT NULL DEFAULT 0 | days |
longest_length | INT NOT NULL DEFAULT 0 | days |
last_activity_at | TIMESTAMPTZ NULL | the timestamp of the most recent increment |
started_at | TIMESTAMPTZ NULL | the timestamp the current run started; null when current_length=0 |
paused_at | TIMESTAMPTZ NULL | non-null when on a Sabbath day (Phase 2 mechanic; field included now for forward compatibility) |
sabbath_day_of_week | INT NULL CHECK (sabbath_day_of_week BETWEEN 0 AND 6) | 0=Sunday; Phase 2; field included now for forward compatibility |
opt_out | BOOLEAN NOT NULL DEFAULT false | honored at API layer immediately |
opt_out_reason | TEXT NULL | optional free-text; useful for product learning |
UNIQUE (user_id, streak_type).
Index: (user_id, opt_out) for the dashboard read.
RLS:
- Player can SELECT, UPDATE their own rows (
user_id = auth.uid()). - Server-only service-role client can SELECT all (for analytics + the founder dashboard).
lw_streak_events
Append-only event log for streak transitions.
| Column | Type | Notes |
|---|---|---|
id | UUID PK | |
user_id | UUID NOT NULL | FK to auth.users.id |
streak_type | TEXT NOT NULL CHECK (streak_type IN ('scene_completion','scripture_reading','prayer','helping_others')) | |
event_type | TEXT NOT NULL CHECK (event_type IN ('incremented','broken','froze','gifted_freeze','opted_out','opted_in','migrated_from_anon')) | |
occurred_at | TIMESTAMPTZ NOT NULL DEFAULT now() | |
metadata | JSONB NOT NULL DEFAULT '{}' | structured details (scene_id for increments, previous length for breaks, gift-sender user_id for gifted_freeze) |
Indexes: (user_id, occurred_at DESC) for the player history view; (streak_type, event_type, occurred_at DESC) for the founder analytics view.
RLS:
- Player can SELECT rows where
user_id = auth.uid(). - INSERT performed via server-only service-role client (no client-side INSERT).
lw_event_completions
Records when a player completes a scene that is part of a liturgical event (Phase 1: Holy Week only).
| Column | Type | Notes |
|---|---|---|
id | UUID PK | |
user_id | UUID NOT NULL | FK to auth.users.id |
event_id | TEXT NOT NULL | the LiturgicalEvent.id from the calendar engine (e.g., 'holy-week-2027') |
scene_id | TEXT NOT NULL | the canonical scene id |
completed_at | TIMESTAMPTZ NOT NULL DEFAULT now() | |
badge_earned | TEXT NULL | non-null when this completion triggered a badge (e.g., 'holy-week-2027-complete') |
UNIQUE (user_id, event_id, scene_id).
Indexes: (user_id, event_id) for the Resurrection-cinematic-trigger check.
RLS:
- Player can SELECT their own rows.
- INSERT via server-only client.
lw_player_calendar_prefs
Per-event opt-out (e.g., a player who finds the Christmas event jarring can hide it).
| Column | Type | Notes |
|---|---|---|
id | UUID PK | |
user_id | UUID NOT NULL | FK to auth.users.id |
event_id_pattern | TEXT NOT NULL | wildcards permitted: christmas-* opts out of all years of Christmas; holy-week-2027 opts out of only that year |
opted_out_at | TIMESTAMPTZ NOT NULL DEFAULT now() | |
opted_out_reason | TEXT NULL | optional free-text |
UNIQUE (user_id, event_id_pattern).
RLS:
- Player can SELECT, INSERT, DELETE their own rows.
School-admin RLS (deferred)
The research doc references school-admin reads (a teacher seeing their students' streaks). This is Phase 2 and depends on lw_classrooms (not yet specified). Phase 1 RLS is player-only + service-role-only.
§8 — UX surfaces
Four new surfaces, all coherent with the existing parchment + sacred-gold + navy design tokens already in use by FiveActsHudChip.tsx and FiveActsHudPanel.tsx.
1. Calendar event badge in HUD
Appears adjacent to the existing 5-Act HUD chip when ANY active liturgical event is in window for the player. Shows event name + (for Holy Week specifically) day-counter ("Holy Week — Day 3 of 6"). Color treatment uses sacred gold for the universal feasts; a subtle navy for lens-gated events when Phase 2 ships them. Click → opens CalendarEventModal.
Mobile: stacks below the 5-Act chip rather than beside it.
Hide rules: hidden when no event is active for the player; hidden when the player has opted out of the specific event via lw_player_calendar_prefs; hidden during Holy Week ONLY after all 6 scenes are completed (replaced by the temporary relic icon).
2. CalendarEventModal
Centered modal (full-screen on mobile). Sections, top to bottom:
- Event name + season + window dates in the player's local format.
- One short scripture anchor for the event (1-2 verses; for Holy Week, today's reading from the day's scene). Sourced from the canonical scripture corpus, not authored bespoke.
- For Holy Week specifically: the 6-scene list with unlock status (locked / available / completed). Tappable rows route to the scene.
- A contemplative one-paragraph framing of the event in narrator voice (NOT pastoral authority voice — per AI Bridge). Authored per event by the founder or under founder review.
- Upcoming events list (next 3) for context.
- A small "hide this event" link in the footer that adds to
lw_player_calendar_prefs.
All copy honors §1 voice rule.
3. StreakDisplay (per §5)
Per §5. Re-summarized: small HUD badge + tap-modal with history + opt-out toggle + "what this measures" copy. Visually subordinate to the 5-Act chip.
4. Settings — Calendar + Streak preferences
A new section in /living-word/settings (or its equivalent route in current Living Word build — verify path during implementation):
- Calendar events. A toggle for each of the three universal events (Christmas / Easter / Pentecost) — default ON, one-click off. Phase 1 ships these three; the lens-gated Western liturgical events appear in this list when Phase 2 ships.
- Streaks. A toggle for each streak type. Phase 1 shows scene_completion only; Phase 2 adds the others. A "Turn off all streaks" master toggle.
- (Future — Phase 2) Sabbath day picker, lens override for calendar events.
Cross-cutting UX requirements
All four surfaces MUST:
- Honor the §1 voice rule (deepens / grows / unfolds / is always new — never evolves).
- Honor the AI Bridge Principle (no NPC-voiced sales speak; calendar copy is contemplative narrator voice, not pastoral authority).
- Use the existing parchment + sacred gold + navy design tokens.
- Be mobile responsive at 320-768px.
- Be dismissible (any badge can be tapped away; any modal has an X).
- Persist user opt-outs across sessions (anonymous via localStorage; authenticated via
lw_player_calendar_prefs). - NEVER auto-open or auto-pop. The badge appears in the HUD; the modal opens only on user tap.
- NEVER interrupt mid-scene gameplay — the HUD badge transitions in only between scenes or at scene picker.
§9 — Anti-goals (load-bearing — what Phase 1 will NOT do)
Engineering MUST refuse to implement any of the following inside Phase 1's scope. Each is a retention mechanic common in mobile games that would break Living Word's brand or audience.
- No artificial scarcity / FOMO copy. "Holy Week ends in 4 hours!" / "Last day to play this scene!" — flat NO. The real liturgical calendar is its own honest scarcity; no manufactured urgency.
- No push notifications about anything. Phase 1 ships zero notifications. Reminders, streak warnings, event-launch alerts — all Phase 2 minimum, all with strict opt-in and no-urgency-copy rule.
- No "lose your streak" shame copy. "Don't lose your 30-day streak!" — flat NO. The vocabulary is paused, starting fresh, today is a new day.
- No gacha / loot box mechanics. Holy Week completion earns a fixed relic, not a random one. No randomized rewards anywhere.
- No PvP leaderboards. No public ranking. No "Sarah is 3 scenes ahead of you" copy. No bracket-style competitions.
- No social-pressure mechanics. "Your friend just got a 30-day streak!" — flat NO. Friend graph doesn't exist in Phase 1 anyway; this rule pre-empts the next-spec naive add.
- No mandatory account. Phase 1 streaks work for anonymous players via session/localStorage. The opt-in for account creation is a feature ("sync your library across devices"), not a wall ("create an account to track streaks").
- No mid-cinematic interruptions. The Resurrection cinematic gets its full uninterrupted beat. The HUD badge does not animate or update during the cinematic.
- No NPC voiced sales of any kind. Per the lead-gen spec — restated here because the Holy Week scenes will be high-traffic and the temptation to cross-sell at the most-engaged moment will be real. NPCs in Holy Week scenes NEVER mention ChurchWiseAI, the chatbot, the voice agent, or any monetized surface.
- No content that implies completion = righteousness. "You're 7 days holier!" / "You've mastered Scripture!" — flat NO. The frame is always "you've walked", "you've encountered", "you've heard", never "you've mastered" or "you've grown in faith because of this game."
- No reading-completion certificates that imply mastery of Scripture. (See research §9 anti-pattern #20.)
- No daily-login escalating-reward streaks ("come back 7 days in a row for the big reward"). The scene-completion streak is the alternative — same retention math, no compulsion engineering.
- No premium currency. No gold, gems, or scrolls-you-pay-for tied to live-ops events.
- No "energy" systems that cap play during events.
- No NPC dialogue that uses banned-vocabulary ("evolves", "level up your faith", etc.). The §1 voice rule is enforced in scene authoring.
§10 — Acceptance criteria (engineering tests against)
The system MUST satisfy each criterion below. Each maps to a test in §11.
Functional
resolveActiveEvent(date)returns the correct universal-scope event for any date in the year (the engine is tested against a 24-date table — one per month + each major feast).resolveActiveEvent(date, traditionLensId)returns the correct lens-gated event when the lens demands it (e.g., Catholic lens on Ash Wednesday returns Lent; Baptist lens returns null).- Western Easter date is calculated correctly via Computus for years 2025-2035 (verified against an authoritative church calendar at test-write time; the §11 test asserts the exact dates).
- Holy Week scenes are NOT visible / NOT playable before Palm Sunday.
- Holy Week scenes unlock one per day during Palm Sunday → Holy Saturday.
- All 6 Holy Week scenes are playable on Holy Saturday.
- 14-day grace period: scenes still playable through day 21 post-Palm-Sunday (i.e., 14 days after Easter Sunday).
- After the grace period, Holy Week scenes lock again until next year's Palm Sunday.
- Easter Sunday completion of all 6 scenes triggers the Resurrection cinematic + permanent relic + temporary profile badge.
- The Resurrection cinematic also fires for catch-up completions during the grace period (not Easter-Sunday-exclusive).
- Scene-completion streak increments on first scene completion of the day, exactly once per player per day, regardless of how many scenes they complete.
- Streak does NOT increment on quiz failure / scene re-play of an already-completed scene / no-scene-completion engagement (NPC chat alone doesn't count for
scene_completion— that's thenpc_chatstreak in Phase 2). - Opt-out is honored immediately: the next
GET /api/living-word/streaksreturnsopt_out=true; theStreakDisplayhides for that streak type. - Anonymous-player streaks persist via localStorage across page reloads.
- Anonymous-player streaks migrate to the user account on login (taking the max of anon and account values for
current_lengthandlongest_length). - Calendar event badge appears in the HUD only when an event is active for that player.
CalendarEventModalopens only on user tap (never auto-opens).- Per-event opt-out via
lw_player_calendar_prefsimmediately hides the badge for that event.
Anti-tests (the following MUST NOT happen — automated assertions where possible)
- No HTTP request to any notification provider is fired by any Phase 1 surface (assert by mocking the provider client in tests).
- No streak-display copy in the codebase contains the strings "lost", "broken", "failed", "you missed". CI lint asserts.
- No copy in the
src/app/living-word/tree contains the string "evolves" or "evolve" (the voice-rule lint). - No Holy Week scene file is loadable via the scene-loader before its unlock date (assert with a mock-date test).
- No event drop interrupts active gameplay — the HUD badge MUST update only at scene boundaries.
- No Phase 1 surface introduces a paywall check (assert by code review + grep for
requireSubscriptionor equivalent gates).
§11 — Test plan
Engineering uses this section as the test build sheet. Every test below MUST exist before the corresponding sub-phase ships.
Unit tests (Vitest, server-side — tests/lib/living-word/)
resolveActiveEventfor 24 hand-picked dates across the year. One per month plus each major feast. Each row asserts the returned event id and scope.- Western Easter Computus correctness. A test table for years 2025-2035 with hand-verified dates. The dates: 2025 Apr 20; 2026 Apr 5; 2027 Mar 28; 2028 Apr 16; 2029 Apr 1; 2030 Apr 21; 2031 Apr 13; 2032 Mar 28; 2033 Apr 17; 2034 Apr 9; 2035 Mar 25. (Engineering MUST re-verify these against an authoritative source — e.g., the United States Naval Observatory or a published church calendar — at test-write time; if any date disagrees, fix the test data, not the algorithm.)
- Lens-gating fallthrough. A Baptist player on Ash Wednesday returns null for Lent (lens-gated). The same player on Dec 25 returns Christmas (universal). The same player on Holy Saturday returns Holy Week (universal six-scene event).
- Overlap resolution. During Lent for a Catholic player, the engine returns Lent. During Holy Week for the same player, the engine returns Holy Week (more specific wins). On Easter Sunday for the same player, the engine returns Easter, not Eastertide.
- Streak increment idempotency. Same scene + same player + same calendar day = no double-increment. Test with two consecutive
POST /api/living-word/streaks/updatecalls. - Streak increment cross-day. Scene completion on day N + scene completion on day N+1 =
current_length=2. Gap day =current_lengthresets to 1 on the next completion. - Opt-out is immediate.
POST /api/living-word/streaks/opt-out→ subsequentGET /api/living-word/streaksreturnsopt_out=true. No further increments mutate state for that streak type. - Anonymous → account migration. Anonymous streak of length 10 + account streak of length 5 + login → account streak = 10. (Take the max.)
Integration tests (Vitest, server-side)
- Holy Week scene visibility matches event window. Mock the date to each of: 1 day pre-Palm-Sunday (scenes hidden); Palm Sunday (scene 1 only); Tuesday (scenes 1-3); Holy Saturday (all 6); Easter Sunday (all 6); day 21 post-Palm (all 6); day 22 post-Palm (scenes hidden again). Assert the scene-picker output for each.
- Easter Sunday all-six completion triggers cinematic. Seed completions for scenes 1-5; submit scene 6 on Easter Sunday; assert the cinematic-trigger flag + relic + badge.
- Grace-period completion also triggers cinematic. Seed completions for scenes 1-5; submit scene 6 on day 14 post-Easter; assert the cinematic-trigger flag fires.
- RLS — player cannot read another player's streak. Direct Postgres query as player B for player A's
lw_player_streaksrow returns zero rows.
E2E tests (Playwright)
- Holy Week badge visible on Palm Sunday 2027. Mock the date to Palm Sunday 2027, load
/living-word, confirm the Holy Week badge is visible in the HUD and Palm Sunday scene is in the picker. - All 6 scenes playable on Holy Saturday 2027. Mock the date to Holy Saturday 2027; confirm all 6 scenes are in the picker.
- Easter Sunday completion triggers Resurrection cinematic. Mock to Easter Sunday 2027; complete all 6 scenes in sequence; confirm the Resurrection cinematic plays + relic appears in library + badge appears on profile.
- Opt-out hides StreakDisplay immediately. Open the StreakDisplay modal, tap opt-out; confirm the badge disappears from the HUD without a page refresh.
- Calendar event badge persists across page reloads. During Christmastide, load
/living-word, confirm badge visible; reload; confirm badge still visible.
Voice-rule lint (CI)
- No "evolves" / "evolve" in
src/app/living-word/. A grep-based CI step in the existing knowledge-sync or build pipeline. Fails the build on match. - No shame vocabulary in streak surfaces. CI grep against
StreakDisplay.tsx, the streak API responses, and the streak modal copy for "lost", "broken", "failed", "you missed". Fails the build on match.
Manual founder review (no test framework)
- The founder personally walks through the Holy Week 6-scene event with a mocked date (Palm Sunday 2027) before the event ships to production. The founder confirms: the 5-scene sequence reads correctly, the cinematic fires correctly, the relic is themed correctly, the badge copy is correct.
- The founder personally reviews all streak-related copy before Phase 1B ships. Every string is auditable against rule #1 (verb-and-noun, not adjective).
§12 — Migration plan (phasing within Phase 1)
Three sub-phases. Each has fixed scope, an engineering-effort estimate, a founder validation step, and a green-light gate.
Phase 1A — Liturgical calendar engine + UI (months 1-2 from kickoff)
Scope shipped.
src/lib/living-word/calendar.tswithresolveActiveEvent+ Western Computus.migrations/2026-MM-DD-lw-events-and-streaks.sql(full schema — streak tables ship now even though streaks UI lands in 1B; avoids a second migration)./api/living-word/events/activeendpoint.CalendarEventModal.tsx.- HUD badge integration (calendar badge appears beside the 5-Act chip).
/living-word/settingscalendar opt-out toggles for the three universal events.- Voice-rule CI lint (§11 test 18).
- Tests 1-4, 13, 17, 18.
Scope NOT shipped in 1A. Holy Week scenes (Phase 1C content). Streak UI (Phase 1B). Lens-gated Western liturgical events (Phase 2; the engine supports them but no test data ships).
Engineering effort. ~2 weeks single-engineer.
Founder validates.
- Calendar badge appears on the right dates for at least 5 distinct dates (one per universal feast + 1 non-event date as a negative).
- Modal copy reads in the contemplative narrator voice (NOT pastoral authority).
- The opt-out toggles work and persist across sessions.
Green-light gate to 1B. Calendar engine passes tests 1-4 + founder approves modal copy.
Phase 1B — Streak telemetry foundation + visible StreakDisplay (months 2-3)
Scope shipped.
src/lib/living-word/streaks.tswith all four streak-type increment helpers (only scene_completion is wired to a UI; the others write data silently)./api/living-word/streaks/update,/api/living-word/streaks,/api/living-word/streaks/opt-out.- Extended
StreakDisplay.tsx(per §5; verify and extend the existing stub). - StreakDisplay modal with history + opt-out + "what this measures" copy.
- Anonymous-player localStorage support + migration on login.
/living-word/settingsstreak opt-out toggles.- Shame-vocabulary CI lint (§11 test 19).
- Tests 5-8, 12, 16, 19.
Engineering effort. ~2 weeks single-engineer.
Founder validates.
- Founder plays Living Word as a new player for 7 days, confirms the streak copy lands as gentle, not shame-shaped.
- Opt-out usage rate at week 4 is acceptable (target: <20% — see §13).
- Anonymous-to-account migration works (founder tests by playing anonymously, then logging in).
Green-light gate to 1C. Streak system passes tests 5-8 + founder confirms copy + opt-out rate <30% (the kill threshold per §13).
Phase 1C — Holy Week six-scene event (months 3-4, gated on content authoring)
Prerequisite. Six Holy Week scene files authored (founder voice work — see §15).
Scope shipped.
- Six Holy Week scene data files in the existing scenes-*-rebuilt.ts pattern.
- Event-window mechanic (lock/unlock/grace) wired to the scene loader.
- Resurrection cinematic + relic + profile badge.
- HUD day-counter ("Holy Week — Day X of 6").
lw_event_completionswrites on each Holy Week scene completion.- Tests 9-11, 14, 15, 20.
Engineering effort. ~3 weeks engineering + parallel content authoring (founder estimates content time separately — likely 4-8 weeks at founder pace given theological precision required).
Critical timing. Phase 1C MUST ship to production by mid-March 2027 to land for Holy Week 2027 (Palm Sunday Mar 28 → Holy Saturday Apr 3). If the timeline slips past mid-February 2027, the founder is asked to decide between:
- Ship a reduced "Three Days" event (Maundy Thursday + Good Friday + Easter Sunday — the Triduum, a defensible liturgical scoping) for 2027, with the full six-scene event delayed to Holy Week 2028.
- Hold the full event for Holy Week 2028 entirely and ship Phase 1C without a 2027 event window.
The §15 founder-handoff section asks the founder to pre-commit to a decision rule so the engineering team doesn't stall awaiting input under time pressure.
Founder validates.
- All 6 scenes read at the founder's theological standard (founder-authored = self-validates).
- The Resurrection cinematic plays cleanly and emotionally lands.
- The relic is themed coherently with the existing library art.
§13 — Success metrics + kill/scale thresholds
Phase 1A (calendar engine)
| Metric | Definition | Scale | Kill / Rework |
|---|---|---|---|
| Calendar modal open rate | % of player sessions during an active event that open the CalendarEventModal | ≥20% = engine surfacing the right events at the right times | <5% = badge is invisible or unclear; rework the badge |
| Universal-event opt-out rate | % of players who opt out of any of the three universal feasts | <5% = founder's Decision-4 thesis is validated | >15% = the events feel intrusive even at the universal-feast level; founder revisits Decision 4 |
Phase 1B (streak telemetry)
| Metric | Definition | Scale | Kill / Rework |
|---|---|---|---|
| 7-day scene-completion streak rate | % of returning players who have a current 7+ day streak by month 3 | ≥15% = retention engine working | <5% = streaks aren't earning their UI surface; revisit |
| 30-day scene-completion streak rate | % of returning players with a 30+ day streak by month 3 | ≥3% (rare and aspirational) | — |
| Streak opt-out rate | % of players who opt out of the scene_completion streak | <20% | >30% = the mechanic feels shame-shaped despite the rules; pause and re-spec. This is the load-bearing kill threshold for the streak system. |
| Welcome-back copy strikes | Number of player complaints about "you lost your streak" or shame-coded copy | 0 | Any non-zero = audit and rewrite immediately |
Phase 1C (Holy Week event)
| Metric | Definition | Scale | Kill / Rework |
|---|---|---|---|
| Holy Week event reach | % of active players who play ≥1 Holy Week scene during the 7-day window or grace period | ≥40% = the event resonates; design Easter 2028 with confidence | ≤10% = the event format isn't landing; reconsider for 2028 |
| Six-scene completion rate | % of players who started Holy Week who completed all 6 (and triggered the Resurrection cinematic) | ≥25% | <10% = the 6-scene arc is too long; shorten to Triduum for 2028 |
| Cinematic delivery rate | % of all-6-completers who saw the Resurrection cinematic fire correctly | 100% (this is a correctness metric, not engagement) | Any non-100% = production bug; P0 |
| Session minutes during Holy Week | Total active player session-minutes during the 7-day window vs. the equivalent prior 7-day baseline | +50% lift = the event compounds engagement | <+10% lift = the event isn't drawing additional sessions |
Cross-cutting kill signals
- Any "evolves" string surfaces in production copy = ship-blocking, hotfix-immediate.
- Any push notification fires from Phase 1 surfaces = ship-blocking; engineering audit how the notification path was added.
- Any paywall check appears in a Phase 1 file = ship-blocking; remove.
- Any shame-vocabulary string appears in a streak surface = ship-blocking; rewrite.
§14 — Open questions deferred to Phase 2+
Engineering MUST NOT try to solve any of these inside Phase 1. They are listed here so the spec is honest about its boundaries.
- Eastern Orthodox calendar. Pascha calculation diverges from Western Easter (Julian-vs-Gregorian + lunar-vs-paschal-full-moon). Full Orthodox liturgical year (Apostles' Fast, Dormition Fast, etc.) is Phase 2.
- Advent + Lent gameplay mechanics. Daily Advent candle lighting, Lent fasting tracker, "scene fasting" (contemplative replay mode) — Phase 2.
- Streak gifting. A friend freezing your streak as an act of love-of-neighbor — Phase 2 (requires friend graph, which is Phase 3).
- Multiple visible streaks. Phase 2 ships Scripture-reading + prayer + helping-others visible surfaces (the data is collected in Phase 1).
- Sabbath streak. Phase 2. The data-model fields are in place (
sabbath_day_of_week,paused_at). - Push notifications of any kind. Phase 3 minimum, with strict opt-in and parable-shaped no-urgency copy.
- Family rooms / Sunday school class mode. Phase 2 — depends on
lw_classroomsspec which does not yet exist. - Prayer circles. Phase 2.
- All Saints / Christ the King / Trinity Sunday / Transfiguration / minor feasts. Phase 2.
- Reformation Day + All Saints Eve dual-tradition handling. Phase 2 (the lens system handles it but the event-design work is Phase 2).
- Liturgical-event-specific scene atmospherics (Lent's purple, Easter's white-and-gold visual treatment). Phase 2.
- Today's wandering scribe (Animal Crossing-style daily NPC rotation). Phase 3.
- Inter-tradition discussion forum. Phase 3+ — highest moderation overhead in the research doc.
§15 — Founder-handoff actions (post-merge)
When Phase 1A merges, the founder is asked to do the following — in order, before Phase 1B kicks off:
- Apply the migration.
migrations/2026-MM-DD-lw-events-and-streaks.sqlto production Supabase. (Idempotent.) - Begin authoring the six Holy Week scenes (separate content work). Estimated 4-8 weeks at founder pace. Founder may choose to draft 1-2 and assign a contractor under voice review for the rest. The §4 scene table is the build brief; the existing scenes-*-rebuilt.ts pattern is the file shape.
- Decide: streaks default ON or OFF for new players. The spec recommends ON-by-default with prominent opt-out in onboarding (departing from the research §11 recommendation of OFF-by-default). The founder confirms or overrides before Phase 1B ships.
- Confirm Western Easter is the right anchor. The alternative is to let the player's tradition lens determine which Easter date (Orthodox Pascha vs. Western Easter). Phase 1 ships Western for everyone; Orthodox Pascha is Phase 2. The founder confirms this is the right Phase 1 default for the Orthodox-lens minority.
- Pre-commit to the Phase 1C slip-date decision rule. If Holy Week scene authoring slips past mid-February 2027, does engineering ship the reduced Triduum event (Maundy Thursday + Good Friday + Easter Sunday — 3 scenes instead of 6) for 2027, OR hold the full event for 2028? The decision rule MUST be in place before mid-January 2027 so engineering can act without stalling.
- Approve all calendar event copy before Phase 1A ships — every modal copy line for Christmas, Easter / Eastertide, Pentecost.
- Approve all streak copy before Phase 1B ships — the StreakDisplay badge text, the modal copy, the "what this measures" copy, the opt-out confirmation copy, the welcome-back-after-break copy.
- Approve the Resurrection cinematic + relic design before Phase 1C ships. The cinematic uses the existing
LegacyBloomCinematicpattern for production quality but the content is bespoke for Easter.
Definition of done for this spec. Engineering reads this end-to-end. The Phase 1A migration is authored against §7 verbatim. The calendar engine is built against §3 — including the Computus implementation from algorithm (not from a library). The streak system is built against §5 with all 7 rules honored. The Holy Week event is built against §4 with the six-scene window mechanic correct. All tests in §11 exist and pass before each sub-phase ships. The founder approves the §12 milestones, performs the §15 handoff actions, and is the editorial reviewer for all player-facing copy. Every promotion from one sub-phase to the next requires the green-light gate to be met.