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>
This commit is contained in:
2026-05-09 01:39:48 -04:00
parent 49ba411a00
commit 69964ba17f
2 changed files with 527 additions and 0 deletions
@@ -0,0 +1,216 @@
# 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*
@@ -0,0 +1,311 @@
# Phase 2: Season 1 Vertical Slice (Soil) - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-05-09
**Phase:** 2 — Season 1 Vertical Slice (Soil)
**Areas discussed:** Garden geometry & input · Time density (growth + offline) · Lura's Season 1 arc shape · Letter-from-the-garden composition · Begin screen treatment · Memory Journal access + layout · Visual placeholder treatment for plants · Phase 2 Settings UI scope
---
## Garden Geometry & Input
### Garden shape
| Option | Description | Selected |
|--------|-------------|----------|
| Small fixed grid (4×4 = 16 tiles) | Intimate walled-garden feel; matches cozy/contemplative tone and Phase 2 minimum-viable bias. | ✓ |
| Medium fixed grid (6×6 = 36 tiles) | More room to experiment and more strategic depth, but Season 1 will feel emptier with sparse early plantings and authored-content scope grows. | |
| Freeform / non-grid (57 named planting points) | Bespoke, painterly walled-garden feel. More work upfront and harder to cleanly extend in Seasons 2+. | |
### Seed placement input
| Option | Description | Selected |
|--------|-------------|----------|
| Click empty tile → inline seed picker | Clicking an unoccupied tile pops a small inline picker. Honors UX-01 'no UI clutter'. | ✓ |
| Persistent seed sidebar + click empty tile | More discoverable but introduces a permanent UI element that fights the A Dark Room rule. | |
| Drag-from-inventory to tile | Tactile, but more failure modes and more Phase 3 polish to feel right. | |
### Plant type count
| Option | Description | Selected |
|--------|-------------|----------|
| 23 plant types | Player exercises real choice; small enough to author + concept-art under Phase 2's minimum-viable bias. | ✓ |
| Just 1 plant type | Most minimum-viable possible; risks slice feeling like a single-mechanic demo. | |
| 46 plant types | Richer Season 1 with more variety, but ≈4× the authored-content + concept-art surface. | |
### Seed supply
| Option | Description | Selected |
|--------|-------------|----------|
| Infinite seeds from start | Anti-FOMO; meaningful constraint is *time*, not seed inventory. | ✓ |
| Harvest yields seeds + fragment | Adds a light economy; risks turning the cozy slice into a resource-management loop. | |
| Limited starter seeds + replenish via composting | Couples composting and planting tightly; risks player feeling soft-locked. | |
### Plant unlock progression
| Option | Description | Selected |
|--------|-------------|----------|
| First plant from start, others unlock by fragment-count | Drip-feeds discovery without ceremony. | ✓ |
| All available from start | Simplest sim; loses small but real beat of progression. | |
| Unlocks tied to specific authored fragments | Most narrative; brittle if deterministic selector reorders things. | |
### Empty tile look in Phase 2
| Option | Description | Selected |
|--------|-------------|----------|
| Faint outlined tile + subtle hover state | Readable but unembellished; Phaser primitive; no asset work. | ✓ |
| Visible 'empty plot' placeholder texture | More legible at a glance, but bakes in a visual Phase 3 will throw away. | |
| Invisible tiles (only become visible on hover) | Maximum tonal restraint; risks confusing first-time players. | |
### Post-harvest tile state
| Option | Description | Selected |
|--------|-------------|----------|
| Returns to empty + brief acknowledgement | Fastest loop, cleanest sim, honors cozy 'release' feel. | ✓ |
| Leaves a 'spent' state for a short cooldown | Adds an extra plant lifecycle state to sim and save schema. | |
| Fragment must be 'taken to the journal' first | Tactile but couples sim and UI tightly and adds dangling state. | |
---
## Time Density (Growth + Offline)
### First-plant growth duration in active play
| Option | Description | Selected |
|--------|-------------|----------|
| ~25 minutes | Cozy but watchable; lets a playtest session complete the loop in ~10 minutes. | ✓ |
| ~30 seconds 2 minutes | Demo-pace; reads less 'idle'. | |
| ~1560 minutes | True idle; hard to playtest end-to-end without a debug fast-forward. | |
| ~612 hours (long-form idle) | Anchors 24h cap; risks empty active play; demands strong return-screen. | |
### Per-plant duration variance
| Option | Description | Selected |
|--------|-------------|----------|
| Vary (short / medium / longer) within band | Real time-vs-yield tradeoffs; tonal identity per plant. | ✓ |
| All identical growth times | Simplest sim; loses 'patience-is-rewarded' beat. | |
| All identical for Phase 2; vary later | Avoids re-litigating; means Season 1 plants feel interchangeable. | |
### Ready-state behavior
| Option | Description | Selected |
|--------|-------------|----------|
| Auto-harvest during offline; manual in active play | Cleanest idle-game shape; lets the letter tell a story. | ✓ |
| Wait indefinitely — manual always | Maximum agency; offline payoff feels thin. | |
| Decay if unharvested past a threshold | Adds tension and soft FOMO undercurrent — likely runs into anti-FOMO doctrine. | |
### 24h offline cap surfacing
| Option | Description | Selected |
|--------|-------------|----------|
| Capped silently in the letter's voice | Anti-FOMO; tonally consistent. | ✓ |
| Explicit cap notice in the letter | Honest about the cap; risks reading as a system message. | |
| Cap silently and never mention it | Cleanest tone; player returning after 3 days might be confused. | |
---
## Lura's Season 1 Arc Shape
### Lura form
| Option | Description | Selected |
|--------|-------------|----------|
| Discrete visits at the gate | Bounded each visit; matches bible's 'she appears at the garden gate' framing. | ✓ |
| Single ongoing text-message thread | More 'friend texting,' but loses gate-arrival beat. | |
| Hybrid: gate + occasional texts between visits | Two narrative-state mechanisms; risks Phase-2 scope creep. | |
### Beat count
| Option | Description | Selected |
|--------|-------------|----------|
| 3 beats: arrival · mid · farewell | Tight Season 1 arc; small authoring surface; load-bearing for tone. | ✓ |
| 5 beats (arrival, two mid, late, farewell) | More texture; doubles authoring + Ink-state plumbing surface. | |
| Just 1 arrival beat in Phase 2 | Simplest; Season 1 loses tonal anchor and prologue feels unfinished. | |
### Beat gating
| Option | Description | Selected |
|--------|-------------|----------|
| Fragment count thresholds (1st / 4th / 8th harvest) | Simple, content-coupled, robust to varying play sessions. STRY-10 satisfied. | ✓ |
| Specific authored-fragment IDs unlock specific beats | Most narratively rich; brittle if selector reorders things. | |
| Pure tick-count thresholds | Cleanest against STRY-10; least responsive to player action. | |
### Beat-fire UX
| Option | Description | Selected |
|--------|-------------|----------|
| Subtle gate indicator + player-initiated visit | Honors A Dark Room rule and player's pace. | ✓ |
| Auto-opens dialogue when beat fires | Disrupts gardening flow; less cozy. | |
| Queues silently; player finds Lura by clicking gate when curious | Most A-Dark-Room; risks player missing all 3 beats. | |
---
## Letter-from-the-Garden Composition
### Composition method
| Option | Description | Selected |
|--------|-------------|----------|
| Authored skeleton + templated insertions | Best balance of voice and reactivity. | ✓ |
| Fully procedural from event-log templates | Risks tonal drift; voice depends on templates not authoring. | |
| Fully hand-authored prose, conditional inclusion only | Maximum voice control; least reactive. | |
### Authoring format
| Option | Description | Selected |
|--------|-------------|----------|
| Ink (.ink files in /content/dialogue/) | Reuses STRY-06 stack — one runtime path, one tooling path. | ✓ |
| Markdown with frontmatter (in /content/letters/) | Aligns with fragment authoring; adds a second narrative-rendering path. | |
| Both — Ink for branching, Markdown for prose passages | Maximum power; introduces a third loader path. | |
### Offline event log scope
| Option | Description | Selected |
|--------|-------------|----------|
| Compact summary (counts + IDs of bloomed/harvested + any unfired Lura beat) | Compact, sufficient, bounded in size. | ✓ |
| Full ordered timeline (every state transition with timestamp) | Most powerful; biggest schema surface and storage cost. | |
| Just last/biggest event(s) | Smallest schema; letter has less to say across multiple returns. | |
### Letter UX (when shown / dismiss)
| Option | Description | Selected |
|--------|-------------|----------|
| On return after ≥5 minutes; full-screen overlay; one tap to dismiss | Avoids letter-spam; full-screen honors tonal weight. | ✓ |
| Always show on tab return regardless of duration | Most consistent; risks feeling intrusive. | |
| Inline pane (slides in from a corner) | Less disruptive; loses 'sit with this' tonal beat. | |
---
## Begin Screen Treatment
### Phase 2 vs Phase 3 split
| Option | Description | Selected |
|--------|-------------|----------|
| Tasteful placeholder; Phase 3 paints | Honors no-aesthetic-polish bias; avoids Phase 3 rework. | ✓ |
| Real painted gesture-gate now | Strongest first impression today; pulls Phase 3 work earlier and risks scope creep. | |
| Title text only (no visual treatment) | Most-A-Dark-Room; risks reading as unfinished. | |
### Subsequent-load behavior
| Option | Description | Selected |
|--------|-------------|----------|
| Every load (browser autoplay policy demands it) | Honest with browser constraints; Recommended option. | |
| Only first run; subsequent loads skip to garden + audio enables on first interaction | Smoother return; brief silent moment until first interaction. | ✓ |
| Only first run; explicit 'enable sound' prompt later | Two surfaces to maintain; risks system-message feel. | |
**Notes:** User picked the smoother-return path, accepting the brief silent moment until the first interaction as a worthwhile tradeoff for cozy pacing. CORE-05 + AEST-07 are both still satisfied — the gesture happens on first run; subsequent loads use any first interaction as the gesture.
---
## Memory Journal Access + Layout
### Open mechanism
| Option | Description | Selected |
|--------|-------------|----------|
| Small persistent icon + keyboard shortcut | Most discoverable without violating UX-01; Recommended option. | |
| Reveals after first harvest, then persistent | Most A-Dark-Room (UI grows as player progresses). | ✓ |
| Hidden until journal hotkey is discovered | Maximum tonal restraint; high risk players miss it. | |
### Layout
| Option | Description | Selected |
|--------|-------------|----------|
| Slide-in side panel; click a fragment to expand inline | Garden stays visible; lets player feel rooted; Recommended option. | |
| Full-screen modal overlay; back button to return to garden | More tonal weight per visit; player can't see plants ripening while reading. | ✓ |
| Dedicated 'journal view' you navigate to, like a separate room | Most narratively rich; biggest implementation lift. | |
### New-fragment surfacing in active play
| Option | Description | Selected |
|--------|-------------|----------|
| Immediate full-text reveal modal; dismiss to return to garden | Honors harvest as a small event; creates a memorable beat. | ✓ |
| Quiet deposit — small acknowledgement, find it in journal later | Most A-Dark-Room; risks player never reading the prose. | |
| Inline reveal at the harvested tile; tap to dismiss | Tactile but fragile (DOM positioned over Phaser canvas resizes). | |
---
## Visual Placeholder Treatment for Plants
### Plant look per growth stage
| Option | Description | Selected |
|--------|-------------|----------|
| Simple Phaser-primitive shape per stage, single color per type | No PNG asset work; firewall stays clean; Phase 3 swaps in painted sprites. | ✓ |
| Programmer-art sprite per stage per plant (PNG placeholders) | Differentiates more legibly; produces throwaway assets that go through provenance gate. | |
| Text labels at the tile instead of any visual | Most A-Dark-Room; reads as severe. | |
### Ready-state cue
| Option | Description | Selected |
|--------|-------------|----------|
| Subtle glow / pulse on ready tiles | Reads at a glance without text; Phase 3 paints over with warmer light. | ✓ |
| Bright color shift on the placeholder shape | Most legible; bakes a specific color Phase 3 will rework. | |
| Text indicator at tile ('ready') | Very explicit; least cozy. | |
---
## Phase 2 Settings UI Scope
### Settings UI scope
| Option | Description | Selected |
|--------|-------------|----------|
| Save management only (Export/Import + Restore prior snapshot) | Smallest scope that completes deferred Phase-1 surfaces; audio + accessibility stay in Phase 8. | ✓ |
| Save management + audio sliders | Doesn't fit 'no aesthetic polish' framing; risks dragging in keyboard nav and accessibility. | |
| No settings menu at all in Phase 2 | Smallest Phase-2 lift; deferred items still aren't player-reachable. | |
### Access
| Option | Description | Selected |
|--------|-------------|----------|
| Small icon in a corner of the main view + keyboard shortcut | Same restraint pattern as Memory Journal affordance. | ✓ |
| Hidden in a 'cog' on Begin screen + accessible from Lura's gate | Risks burying critical save-recovery affordances. | |
| Only accessible via hotkey (no visual affordance) | Risks players losing access to save recovery in a crash recovery moment. | |
### `navigator.storage.persist()` outcome surfacing
| Option | Description | Selected |
|--------|-------------|----------|
| One-time soft toast on first save if denied; nothing if granted | Honors UX-01 + cozy tone; respects 'surfaces the result respectfully'. | ✓ |
| Always show 'persistent storage: yes/no' indicator in Settings only | Player won't know save is at risk if persistence denied. | |
| Modal dialog if denied | Strongest call to attention; reads anti-cozy. | |
---
## Claude's Discretion
Areas where the user explicitly deferred to implementation choice (full list in CONTEXT.md `<decisions>` § "Claude's Discretion"):
- Specific growth-duration values per plant within the 25min band.
- Exact fragment-count thresholds for plant unlocks and Lura beats (model is locked, values may shift ±12 in playtest).
- Form of the post-harvest acknowledgement beat (text / particle / pause).
- Form of Lura's gate indicator on beat-unlock.
- Tick rate / sim cadence (likely 410Hz).
- Internal Zustand store slice shapes.
- Internal Phaser scene tree.
- Specific copy of Begin screen, persistence-denied toast, post-harvest acknowledgement (must match bible voice; user reviews).
- e2e fast-forward mechanism (hidden hotkey vs URL flag vs sim-clock injection).
- Memory Journal empty-state copy.
## Deferred Ideas
Items mentioned during discussion that belong in other phases (full list in CONTEXT.md `<deferred>`):
- Hybrid Lura presence (gate + ambient drip) — possibly Phase 4+.
- Plant-type unlocks tied to specific authored fragments — possibly Phase 4+.
- Fully procedural letter from event-log templates — reconsider if slot vocabulary too small in playtest.
- Audio sliders, keyboard nav, browser-zoom guarantees, color-redundant icons, tab-title bloom, Lura-not-numbers UX — Phase 8.
- Visual regression for asset library — Phase 8.
- Roothold prestige, Season transitions, die-off, finite ceiling enforcement, cross-pollination — Phase 4.
- Memory Storms, place-memory vignettes, 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.
- Watercolor post-process, painted plants, painted Begin screen, solo cello + ambient buses, reduced-motion toggle — Phase 3.
- Real production-volume AI assets + locked north-star reference set — Phase 5 (Phase 1 IOU AEST-09).
- Real `migrate_v1_to_v2` — Phase 4 (when Roothold lands). Phase 2 only extends `V1Payload` shape.
- Compost yielding seeds back — rejected; Phase 4's cross-pollination is the proper place for seed-as-economy.
- Persistent Settings element on Begin screen — rejected in favor of in-garden corner icon + hotkey.