Files
josh 69964ba17f docs(02): capture phase context
Phase 2 (Season 1 Vertical Slice — Soil) discuss-phase output. 02-CONTEXT.md
captures 34 implementation decisions across 8 gray areas (garden geometry &
input · time density · Lura's arc · letter composition · Begin screen ·
Memory Journal · plant placeholders · Phase-2 Settings UI scope). Locks the
4×4 grid + click+inline seed picker, 2–5min growth band per plant with
auto-harvest-while-offline, 3 Lura gate visits gated by 1st/4th/8th harvest,
authored Ink letter skeleton with templated slots, full-screen letter on
≥5min absence, tasteful placeholder Begin screen (Phase 3 paints), full-
screen Journal modal revealing after first harvest, simple Phaser-primitive
plants with subtle ready-state pulse, save-management-only Settings.
Phase 2's first commits: BigQty wrapper, Zustand 5 store, tick scheduler.
Save schema is a v1 *extension*, not v1→v2.

02-DISCUSSION-LOG.md preserves the alternatives considered for human review.

Next: /gsd-plan-phase 2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 01:39:48 -04:00

217 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 2: Season 1 Vertical Slice (Soil) - Context
**Gathered:** 2026-05-09
**Status:** Ready for planning
<domain>
## Phase Boundary
Ship the complete Season 1 (Soil) vertical slice end-to-end on real authored content, with no aesthetic polish required. Player launches → presses **Begin** (gesture-gate calls `AudioContext.resume()`, AEST-07) → places a seed in an empty tile of a 4×4 garden → watches it grow (state survives refresh) → harvests one Season-1 fragment → reads it in a Memory Journal → meets Lura at the gate via Ink-authored dialogue → closes the tab → returns to a *letter from the garden* describing what bloomed → the Playwright e2e smoke proves the whole loop. The simulation advances by elapsed real time (capped at 24h, refusing negative deltas), saves fire on `visibilitychange`/`beforeunload`/Season transitions, and story progression gates on tick count (not wall time).
Phase 2's first commits land the foundations Phase 1 deliberately deferred: the **`BigQty` wrapper around `break_eternity.js`** (per CLAUDE.md "from day one of feature code"), the **Zustand 5 store** wiring scenes ↔ React UI, and the **tick scheduler / monotonic clock** (the only owner of wall-clock access; sim modules stay pure).
**Out of scope for Phase 2 (deferred to later phases):**
- Watercolor post-process, painted plants, solo cello, ambient buses (Phase 3)
- Season transitions, die-off, Roothold prestige, cross-pollination (Phase 4)
- Place-memory vignettes, Memory Storms, the Nameless Man's full arc (Phase 5)
- The Below, ecosystem planting, the Loom, the Archivist (Phase 6)
- Final binary choice + credits/coda rest state (Phase 7)
- Audio sliders (UX-04), keyboard nav (UX-06), browser-zoom guarantees (UX-07), color-redundant icons (UX-08), tab-title bloom (UX-09), Lura-not-numbers UX (UX-12) (Phase 8)
- Visual regression for the asset library (PIPE-04, Phase 8)
**Phase 2 owns 24 REQ-IDs:** CORE-02, CORE-03, CORE-11, GARD-01, GARD-02, GARD-03, GARD-04, MEMR-01, MEMR-02, MEMR-03, MEMR-04, MEMR-05, MEMR-06, STRY-01, STRY-06, STRY-07 (vacuous in S1), STRY-10, AEST-07, UX-01, UX-02, UX-10, UX-11, PIPE-02, PIPE-07.
</domain>
<decisions>
## Implementation Decisions
### Garden Geometry & Input
- **D-01:** Garden is a **4×4 fixed grid (16 tiles)**. Intimate walled-garden feel; matches cozy/contemplative tone and Phase 2's minimum-viable bias. Later Seasons add new surfaces (the Below, etc.) rather than enlarging the grid.
- **D-02:** Seed placement is **click-empty-tile → inline seed picker** (small popover anchored to the clicked tile, listing currently-unlocked plant types; single tap commits). No persistent seed sidebar — honors UX-01 "no UI clutter".
- **D-03:** **23 plant types** ship in Season 1 — enough that the player exercises real choice and different plants gate different fragments via MEMR-06. Each plant has its own growth duration and fragment pool.
- **D-04:** **Infinite seed supply from start** — anti-FOMO. The meaningful constraint is *time* (growth duration, tile count), not seed inventory. Simplest sim/store shape; cleanest cozy framing.
- **D-05:** **First plant available from start; remaining 12 unlock by fragment-count thresholds** harvested. Drip-feed of progression without ceremony. Specific thresholds are Claude's discretion within reason.
- **D-06:** Empty tile look in Phase 2 = **faint outlined tile + subtle hover state** (Phaser primitive draw, no asset work). Phase 3 paints the watercolor treatment.
- **D-07:** Post-harvest tile **returns immediately to empty** with a brief acknowledgement beat (small text snippet, gentle particle, or pause beat — Claude's discretion on form). No "spent" cooldown state in the sim or save schema.
### Time Density (Growth + Offline)
- **D-08:** First-plant active-play growth = **~25 minutes** (sprout → ready). Cozy but watchable; lets a playtester complete the full loop in ~10 minutes while still feeling idle. The 24h offline cap is meaningful, not just theoretical.
- **D-09:** **Per-plant durations vary** (short / medium / longer) within the ~25min band. Lets the player make real time-vs-yield tradeoffs and gives each plant a tonal identity. Specific durations per plant are Claude's discretion; document chosen values in PLAN.md.
- **D-10:** **Auto-harvest during offline; manual in active play.** While the player is away, plants that ripen are auto-harvested and queued into the offline event log; the *letter* narrates them. In active play, the player still chooses when to harvest. Cleanest idle-game shape and gives the letter content to describe.
- **D-11:** **24h offline cap is surfaced silently in the letter's voice** — no numeric "you were gone for 28 hours" copy. The simulation hard-caps the elapsed delta at 24h (CORE-03 + CORE-11); the letter may lightly acknowledge a long absence in voice, but never shows the cap as a system message.
### Lura's Season 1 Arc
- **D-12:** Lura is present as **discrete visits at the gate** — not a persistent chat thread. Matches the bible's framing; each visit is its own Ink scene; bounded content target for Phase 2.
- **D-13:** **3 beats in Season 1: arrival · mid · farewell.** Tight prologue arc; small authoring surface; load-bearing for tone.
- **D-14:** Beats are **gated by fragment-count thresholds** — beat 1 fires after the **1st** harvest; mid-beat after the **4th**; farewell after the **8th**. Counts come from sim ticks, not wall time, so STRY-10 holds (system-clock manipulation cannot fast-forward through beats). Specific thresholds may shift slightly during playtest but the model — fragment-count thresholds — is locked.
- **D-15:** Beat-fire UX = **subtle gate indicator + player-initiated visit.** When a beat unlocks, a soft cue at the gate signals it (glow, mark, or small text); the player walks over (clicks the gate) when they choose. The conversation opens as a **React DOM dialogue overlay** (DOM, not canvas — supports MEMR-05-style selectable text from day one). Honors A Dark Room rule.
- **D-16:** All Lura dialogue is authored in **Ink (`.ink`) under `/content/dialogue/`**, compiled to JSON via `inklecate` (already in devDependencies), runtime-loaded via `inkjs` (already in dependencies). Per STRY-06 + ROADMAP.md.
### Letter-from-the-Garden (UX-02)
- **D-17:** Letter is composed from an **authored skeleton + templated insertions**. Hand-authored Ink passages in voice with named variable slots (e.g., `{plants_bloomed}`, `{fragment_titles}`, `{lura_was_here}`); specifics flow in from the offline event log. Best balance of voice control (Lura-anchor tone is preserved by author) and reactivity (each return reads true to what happened).
- **D-18:** Letter authoring lives in **Ink (`.ink` files in `/content/dialogue/`)** — same authoring layer as Lura's beats. Single narrative-rendering path for Phase 2; reuses STRY-06 stack; no second loader path.
- **D-19:** Save schema gains a small **`offlineEvents`** block: per-plant counts of plants bloomed, list of auto-harvested fragment IDs, and a flag for any newly-unlocked Lura beat queued for first-visit. Compact and bounded; sufficient for the slot vocabulary; cleanly fits inside the existing `V1Payload` shape (see `src/save/migrations.ts`). Phase 2 must update `V1Payload` to include this block — it's a Phase-2 schema *extension*, not a `migrate_v1_to_v2`, because Phase 1's v1 has not shipped any production saves yet.
- **D-20:** Letter triggers on return when **absence ≥ 5 minutes**. Below 5 minutes, the player simply resumes (no letter). At/above the threshold, a **full-screen React DOM overlay** delivers the letter; **one tap dismisses to the live garden**. Threshold avoids letter-spam during active tab-flicking; full-screen treatment honors the tonal weight.
### Begin Screen (AEST-07 + UX-01)
- **D-21:** **Tasteful placeholder; Phase 3 paints.** Phase 2 ships a clean, restrained Begin screen — typographic title + a single "Begin" affordance, calls `AudioContext.resume()` on tap. No painted canvas illustration. Phase 3 swaps in the watercolor gesture-gate without touching the gating logic.
- **D-22:** **Begin screen shows on first run only.** Subsequent loads skip directly to the live garden; `AudioContext` enables on the first interaction (any tile click or gate click counts as a user gesture). "First run" is determined by save existence (no save → first run; save present → returning player). Acknowledged tradeoff: returning players have a brief silent moment until their first interaction; deemed acceptable for cozy-pacing.
### Memory Journal (MEMR-04, MEMR-05)
- **D-23:** Journal affordance **reveals after the player's first harvest**, then is persistent. Pre-harvest, no journal icon at all. Most A-Dark-Room-consistent; the UI grows as the player progresses.
- **D-24:** Journal layout = **full-screen modal overlay** with fragments grouped by Season; a back affordance returns to the live garden. DOM-rendered text per MEMR-05 (selectable, copy-pasteable).
- **D-25:** Newly harvested fragments (in active play) **surface immediately in a full-text reveal modal**; dismissing files them into the journal under their Season. Off-line auto-harvested fragments are surfaced via the *letter* (D-17 .. D-20) and re-readable in the journal afterwards. Creates a memorable beat-per-harvest in active play; preserves the cozy/quiet feel of the offline return.
### Visual Placeholders (Phase 2 only)
- **D-26:** Plants render as **simple Phaser-primitive shapes per growth stage, tinted by plant type**. Sprout = small dot, mature = stem, ready = bloom shape; tint differentiates plant types. No PNG asset work in Phase 2; the architectural firewall stays clean (no asset deps in `src/sim/`). Phase 3 swaps in painted sprites.
- **D-27:** Ready-state cue = **subtle glow / pulse on ready tiles** (Phaser shader pulse or alpha cycle). Reads at a glance without text; works regardless of plant type; Phase 3 paints over with a warmer light treatment.
### Phase 2 Settings UI Scope
- **D-28:** Settings menu in Phase 2 ships **save-management surfaces only** — Export to Base64 (CORE-09 UI), Import from Base64 (CORE-09 UI), Restore previous snapshot (CORE-08 UI). Persistence-result toast (CORE-05 UI) folds in. **Audio sliders + keyboard nav + browser zoom + color-redundant icons stay in Phase 8** (UX-04, UX-06, UX-07, UX-08).
- **D-29:** Settings access = **small icon in a corner of the main view + keyboard shortcut**. Persistent (returning players need to find Export/Import for save recovery); restrained enough not to clutter the cozy surface. Same pattern as the Memory Journal affordance.
- **D-30:** `navigator.storage.persist()` outcome surfaced as a **one-time soft toast in voice on first save if denied; nothing if granted** (e.g., *"the garden may forget, if your browser asks it to"*). Honors UX-01 + cozy tone; respects CORE-05's "surfaces the result respectfully if the browser declines."
### Foundations That Must Land in Phase 2 (per CLAUDE.md)
- **D-31:** **`BigQty` wrapper around `break_eternity.js`** is Phase 2's first task. Lands in `src/sim/numbers/` (or similar firewall-respecting location). All economy values from this point forward go through `BigQty`. Even if Season 1's actual scaling never demands `break_eternity`, the wrapper is in place day-one of feature code per CLAUDE.md.
- **D-32:** **Zustand 5 store** is the bridge between the Phaser scene and React UI surfaces (Begin screen, Memory Journal, Settings, Letter, Lura dialogue). Sim writes to the store; React reads from it. Sim never imports the store directly — Phase 1's ESLint boundaries (CORE-10) enforce this. Exact slice shape is Claude's discretion.
- **D-33:** **Tick scheduler / monotonic clock** is the *only* owner of wall-clock access. Sim modules in `src/sim/` stay pure (no `Date.now`, no `setInterval`); the scheduler injects `currentTime` into sim updates. Tick rate is Claude's discretion (likely 410Hz for sim updates, decoupled from `requestAnimationFrame`-driven render). Negative deltas are refused at the scheduler boundary (CORE-11); single offline catch-up clamps to 24h (CORE-03 + CORE-11).
- **D-34:** **Save extension for Phase 2** updates `V1Payload` (in `src/save/migrations.ts`) to include the Phase-2-needed fields: garden tiles with plant state, plants array with growth-state + plantedAt-tick, harvested fragment IDs (already declared), `lastTickAt` (already declared), Lura-beat progress flags, `offlineEvents` block (D-19), settings (already declared). This is a Phase-2-scope schema *extension* (not a v1→v2 migration) because Phase 1's v1 envelope has shipped no production saves; the synthetic v0→v1 demo migration in `migrations[1]` continues to work.
### Claude's Discretion
- Specific growth-duration values per plant type within the 25min band (D-08 / D-09).
- Exact fragment-count threshold values for plant-type unlocks (D-05) and Lura beats — the model is locked (1st/4th/8th) but Claude may adjust by ±12 during playtest.
- Form of the post-harvest acknowledgement beat (text snippet, particle, pause — D-07).
- Form of the gate indicator when Lura's beat unlocks (D-15) — soft glow, mark, or small text.
- Tick rate / sim cadence (likely 410Hz; not 60Hz — sim should be cheap).
- Internal shape of the Zustand store slices.
- Internal shape of the scene/state machine inside Phaser (Boot → Preloader → Garden, or simpler).
- Specific copy of the tasteful Begin screen, the persistence-denied toast, and the post-harvest acknowledgement (all must match the bible voice; user reviews).
- Choice of e2e test fast-forward mechanism (hidden dev hotkey vs URL flag vs sim-clock injection — Phase 2 needs *some* way for Playwright to fast-forward growth in PIPE-07).
- Specific copy of the Memory Journal "no fragments yet" empty state (if shown pre-first-harvest in any flow).
- Whether the offline letter's slot vocabulary is finalized in this phase or expanded incrementally (Lura's variable can be the simplest first slot; more added if needed).
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### Project-Level Source of Truth
- `.planning/PROJECT.md` — Story bible synthesis, core value ("every idle mechanic must function as a metaphor"), hard thematic constraints, Out of Scope, Key Decisions table.
- `.planning/REQUIREMENTS.md` — 77 v1 requirements with REQ-IDs. Phase 2 owns 24 (see Phase Boundary above for the complete list).
- `.planning/ROADMAP.md` §"Phase 2: Season 1 Vertical Slice (Soil)" — 5 success criteria. The plan must satisfy each.
- `.planning/STATE.md` — current position; Phase 1 verification table; carry-forward concerns.
- `CLAUDE.md` — stack lock, architectural firewall, banner concerns 110, hard thematic constraints, code style rules ("BigQty from day one of feature code", externalized strings, stable string IDs, no Date.now in sim).
### Phase 1 Outputs (load-bearing for Phase 2)
- `.planning/phases/01-foundations-and-doctrine/01-CONTEXT.md` — Phase 1 decisions (D-01..D-12) that constrain Phase 2 (save format locked, content pipeline locked, firewall locked, doctrine docs locked).
- `.planning/phases/01-foundations-and-doctrine/01-VERIFICATION.md` — Phase 1 PASS evidence; Phase 2 builds on every green REQ here.
- `.planning/anti-fomo-doctrine.md` — banned UX patterns; review checklist. **MUST be consulted at every UX decision in Phase 2.**
- `.planning/season-7-end-state.md` — the principle-level rest-state contract. Phase 2's growth-curve and Roothold-precursor decisions must not preclude the finite ceiling tied to authored content.
### Research Layer
- `.planning/research/SUMMARY.md` — stack at a glance, banner concerns, Phase 2 rationale (vertical slice that could plausibly ship as a free standalone prologue).
- `.planning/research/STACK.md` — stack rationale; especially the React-19-DOM-overlay vs Phaser-canvas split, Zustand 5 bridge, Howler.js, Ink/inkjs, idb + lz-string.
- `.planning/research/ARCHITECTURE.md` — three-layer firewall (sim → application → presentation); tick scheduler shape; save format `{schemaVersion, payload, checksum}`.
- `.planning/research/PITFALLS.md` — 14 critical pitfalls. Phase 2 directly hits #1 (story-ends-but-loop-doesn't — vertical-slice prologue must not foreclose Season 7's rest state), #2 (system-clock cheating — STRY-10 + CORE-11), #4 (tab throttling — CORE-02 + UX-10), #6 (Web Audio user-gesture — AEST-07 + Begin screen), #7 (offline catch-up — CORE-03), #10 (content/code divergence — MEMR-02 + MEMR-03 + STRY-09), #12 (anti-FOMO — UX-13 holds for every decision in Phase 2).
- `.planning/research/FEATURES.md` — must-have / should-have / defer / anti-feature classification. The "should-have" classification for Season 1 is the input to Phase 2's content authoring scope.
### Phase 1 Code Surfaces Phase 2 Will Consume
- `src/save/index.ts` — public barrel of the save layer (Phase 2 imports ONLY from this file).
- `src/save/migrations.ts``V1Payload` shape and migration registry. **Phase 2 extends `V1Payload` per D-34.**
- `src/save/envelope.ts` + `src/save/codec.ts` + `src/save/db.ts` + `src/save/snapshots.ts` + `src/save/persist.ts` — internal modules; Phase 2 does not import these directly.
- `src/content/loader.ts` + `src/content/schemas/index.ts` — Vite-native content pipeline. Phase 2 adds Season 1 fragment files under `/content/seasons/01-soil/` and Lura's Ink scenes under `/content/dialogue/`.
- `src/content/index.ts` — public barrel of the content layer.
- `src/game/main.ts` + `src/game/scenes/Boot.ts` — Phaser entry. Phase 2 expands the scene tree (Boot → Preloader → Garden, or simpler).
- `src/App.tsx` + `src/PhaserGame.tsx` — React shell + Phaser bridge. Phase 2 adds the Begin screen, HUD, Settings, Memory Journal, Lura dialogue overlay, and Letter overlay as React components beside the `<PhaserGame>` mount.
- `eslint.config.js` (Phase 1's flat config with `eslint-plugin-boundaries`) — enforces sim ↔ render/ui firewall. Phase 2 code MUST pass lint.
- `package.json scripts.ci``npm run ci` is the CI gate; Phase 2 plans must keep `npm run ci` green at every commit.
### Content Conventions
- `content/README.md` — content authoring conventions (stable string IDs, frontmatter shape, file location norms).
- `content/seasons/00-demo/fragments.yaml` — demo fragment from Phase 1. **Phase 2 removes this file** when Season 1 (`/content/seasons/01-soil/`) is authored.
### Phase 2 New Outputs (will be created during this phase)
- `/content/seasons/01-soil/` — real Season 1 fragments (Markdown + frontmatter, per MEMR-02). Fragment count and per-plant pool sizes are part of Phase 2 planning.
- `/content/dialogue/season1/` — Lura's 3 Ink scenes (arrival, mid, farewell) + the *letter from the garden* Ink passage with templated slots.
- `src/sim/numbers/big-qty.ts` (or similar) — `BigQty` wrapper around `break_eternity.js` (D-31).
- `src/store/` — Zustand 5 slices (D-32).
- `src/sim/scheduler/` — tick scheduler / monotonic clock (D-33). Single owner of wall-clock access.
- `src/sim/garden/` — garden tile state, plant state machine, harvest logic (sim-only, no DOM, no Date.now).
- `src/sim/narrative/` — Lura beat-gating logic (sim-side; reads fragment-count from sim state, not narrative-string content).
- `src/render/garden/` — Phaser scene rendering tiles, plants, ready-state pulse, gate.
- `src/ui/begin/`, `src/ui/journal/`, `src/ui/dialogue/`, `src/ui/letter/`, `src/ui/settings/` — React DOM overlays.
- `tests/e2e/` — Playwright smoke (PIPE-07) covering load → dismiss begin → plant → fast-forward → harvest → journal-shows-fragment → reload → fragment-persists.
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets (from Phase 1)
- **Save layer is complete and stable** (`src/save/`). Phase 2 imports `wrap`, `unwrap`, `migrate`, `snapshot`, `listSnapshots`, `requestPersistence`, `exportToBase64`, `importFromBase64`, `openSaveDB`, `LocalStorageDBAdapter`, `crc32hex`, `canonicalJSON` from `src/save/index.ts`. The `V1Payload` interface is the contract; Phase 2 extends it (D-34).
- **Content pipeline is Vite-native** (`src/content/loader.ts`). Phase 2 drops Markdown fragments under `/content/seasons/01-soil/fragments/*.md` and they're picked up by the existing glob; schema violations fail the build.
- **ESLint boundary rule** (`eslint.config.js`) enforces `src/sim/``src/render/`/`src/ui/` firewall. Phase 2 sim code must be pure.
- **Vitest + Playwright** are already wired (`vitest.config.ts`, `playwright.config.ts`). Phase 2 adds tests; the CI script (`npm run ci`) already runs them.
- **CRC-32 checksum + canonical JSON** for save integrity.
- **`fake-indexeddb`** is pre-installed for test environments.
- **Asset provenance gate** (`scripts/validate-assets.mjs`) — Phase 2 ships placeholder shapes via Phaser primitives, no PNG assets, so the gate is not exercised. (D-26.)
### Established Patterns (from Phase 1)
- **Single public barrel per layer** (`src/save/index.ts`, `src/content/index.ts`). Phase 2's `src/store/`, `src/sim/`, `src/render/`, `src/ui/` should each expose a `index.ts` barrel; cross-layer imports go through barrels only.
- **Stable string fragment IDs** (e.g., `season1.soil.lura_01.greeting`) — never numeric. Validated by Zod schema in `src/content/schemas/`.
- **Player-visible strings live in `/content/`**, not in TS.
- **Doc-lint pattern** (Vitest assertions over Markdown structure) — Phase 2 may extend for Lura's Ink scenes if structural invariants emerge.
- **Plan-bundle pattern** — each plan in `01-foundations-and-doctrine/` shipped a SUMMARY.md alongside its PLAN.md. Phase 2 plans should follow.
### Integration Points
- **Phaser scene ↔ React overlays via Zustand 5** (D-32). Sim writes; React reads. Phaser scenes should NOT import React; React should NOT import Phaser scenes — both go through the store.
- **Tick scheduler ↔ Phaser game loop.** The sim tick runs at its own cadence (D-33); Phaser's `requestAnimationFrame` drives rendering. The scheduler dispatches sim updates from the Phaser scene's `update()` hook (or an independent loop) but is the only place `Date.now()` is allowed.
- **`visibilitychange` + `beforeunload` save triggers** wire to the save layer's `wrap` + IndexedDB write path. Phase 2 must ensure save serialization is fast enough to complete in `beforeunload`'s tight window.
- **Begin screen → AudioContext.resume()** is the bootstrapping handshake for Howler.js (Phase 3 actually uses Howler — Phase 2 may stub or no-op the audio bus while still calling `resume()`).
- **Ink → inkjs runtime path.** `inklecate` (devDependencies) compiles `.ink``.json` at build time; `inkjs` (dependencies) loads the JSON at runtime. Phase 2 establishes this pipeline (it's a no-op stub today per `package.json scripts.compile:ink`).
- **Memory Journal text rendering** must be DOM (React), not canvas (Phaser) — MEMR-05 demands selectable/copyable text.
</code_context>
<specifics>
## Specific Ideas
- **Tone discipline:** the user pushed back on Phase 1 ceremony and prefers minimum-viable infrastructure. Apply the same lens to Phase 2: do not build a registry, framework, or DSL where a typed function table will do; do not pre-allocate scaffolding for Phase 4+ Roots/Canopy/Storm mechanics.
- **A Dark Room rule across every UX decision:** Begin screen has no clutter; Memory Journal affordance only appears after first harvest; Settings is a small corner icon; persistence-denied is a soft toast in voice. The UI grows as the player progresses.
- **Letter is a tonal load-bearing surface, not a stat dump.** UX-02 is explicit: "written in voice, not a stat dump." The authored Ink skeleton is what holds the voice; the slots are what make it true to what happened. Reviewer should read the letter Ink as if it were short fiction, not as if it were copy.
- **The vertical slice could plausibly ship as a free standalone prologue.** This is the project's escape hatch against the 7-Season scope risk (banner concern #2). Phase 2 must complete the loop end-to-end so that, in the worst case, this slice could go live alone. That includes: a satisfying first-pass arc shape (Lura's 3 beats), a real letter, working save/restore, working e2e smoke.
- **Architecture firewall is non-negotiable.** Sim is pure; render/UI talk to sim only via the Zustand store. ESLint enforces. Tick scheduler is the *only* place wall-clock enters the sim.
- **Save-schema extension, not migration.** Phase 1's `V1Payload` has not shipped any production saves; Phase 2 extends the v1 payload shape rather than adding `migrate_v1_to_v2`. The first real migration lands in Phase 4 (per Phase 1 D-04). The synthetic v0→v1 demo migration in `migrations[1]` continues to work as the proof-of-chain.
- **Tick-count gating, not wall-time gating, for narrative beats.** STRY-10 is satisfied because Lura's fragment-count thresholds count *harvest events* (which are sim ticks), not minutes elapsed. A player who manipulates their system clock cannot fast-forward through Lura's beats.
</specifics>
<deferred>
## Deferred Ideas
Items mentioned during discussion that belong in other phases:
- **Hybrid Lura presence (gate visits + ambient text-message drip)** — discussed and rejected for Phase 2 in favor of pure discrete-gate-visits (D-12). May be reconsidered in Phase 4+ if the narrative density warrants it.
- **Plant-type unlocks tied to specific authored fragments** — discussed and rejected for Phase 2 in favor of fragment-count thresholds (D-05). Phase 4+ may explore narrative-keyed unlocks.
- **Fully procedural letter from event-log templates** — discussed and rejected (D-17). Phase 2 commits to authored skeleton + slots. If the slot vocabulary turns out to be too small in playtest, expand in Phase 4 (longer Seasons, more event types).
- **Audio sliders (UX-04), keyboard nav (UX-06), browser-zoom guarantees (UX-07), color-redundant icons (UX-08), tab-title bloom (UX-09), Lura-not-numbers UX (UX-12)** — all confirmed for **Phase 8** (D-28). Settings menu in Phase 2 is save-management only.
- **Visual regression for asset library (PIPE-04)** — Phase 8.
- **Roothold prestige currency, Season transitions, die-off, finite ceiling enforcement** — Phase 4 (Season-Prestige Cycle & Season 2). Phase 2 plants nothing in the save schema for Roothold; Phase 4 owns that addition.
- **Cross-pollination, Memory Storms, place-memory vignettes, ecosystem planting, the Below, the Loom, the Archivist, Lura's full multi-Season arc, the Nameless Man** — Phase 47.
- **Watercolor post-process, painted plants, painted Begin screen, solo cello + ambient buses + crossfade, reduced-motion toggle (UX-05)** — Phase 3 (Watercolor & Cello Aesthetic).
- **Real production-volume AI assets + locked north-star reference set (Phase 1 IOU AEST-09)** — Phase 5 follow-up. Phase 2 ships zero AI-generated assets (D-26 = primitive shapes); the Phase-1 IOU is unblocked, not unblocking Phase 2.
- **Real `migrate_v1_to_v2`** — Phase 4 (when Roothold / Season-prestige state actually lands). Phase 2 only extends `V1Payload` shape (D-34); no new migration entry is added.
- **Per-plant duration variance via dynamic content authoring (e.g., player-modifiable growth times)** — out of scope; not a v1 capability; not in REQUIREMENTS.md.
- **Compost yielding seeds back** — discussed as a possible scarcity mechanic and rejected (D-04 = infinite seeds). Phase 4's cross-pollination introduces hybrid seeds, which is the proper place for seed-as-economy.
- **Persistent Settings element on Begin screen** — discussed as alt access pattern; rejected in favor of in-garden corner icon + hotkey (D-29).
</deferred>
---
*Phase: 2-season-1-vertical-slice-soil*
*Context gathered: 2026-05-09*