From 69964ba17f6f0e27a174649ba002de63d6472311 Mon Sep 17 00:00:00 2001 From: josh Date: Sat, 9 May 2026 01:39:48 -0400 Subject: [PATCH] docs(02): capture phase context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../02-CONTEXT.md | 216 ++++++++++++ .../02-DISCUSSION-LOG.md | 311 ++++++++++++++++++ 2 files changed, 527 insertions(+) create mode 100644 .planning/phases/02-season-1-vertical-slice-soil/02-CONTEXT.md create mode 100644 .planning/phases/02-season-1-vertical-slice-soil/02-DISCUSSION-LOG.md diff --git a/.planning/phases/02-season-1-vertical-slice-soil/02-CONTEXT.md b/.planning/phases/02-season-1-vertical-slice-soil/02-CONTEXT.md new file mode 100644 index 0000000..5884952 --- /dev/null +++ b/.planning/phases/02-season-1-vertical-slice-soil/02-CONTEXT.md @@ -0,0 +1,216 @@ +# Phase 2: Season 1 Vertical Slice (Soil) - Context + +**Gathered:** 2026-05-09 +**Status:** Ready for planning + + +## 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. + + + + +## 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:** **2–3 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 1–2 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 = **~2–5 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 ~2–5min 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 4–10Hz 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 2–5min 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 ±1–2 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 4–10Hz; 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). + + + + +## 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 1–10, 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 `` 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. + + + + +## 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. + + + + +## 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. + + + + +## 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 4–7. +- **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). + + + +--- + +*Phase: 2-season-1-vertical-slice-soil* +*Context gathered: 2026-05-09* diff --git a/.planning/phases/02-season-1-vertical-slice-soil/02-DISCUSSION-LOG.md b/.planning/phases/02-season-1-vertical-slice-soil/02-DISCUSSION-LOG.md new file mode 100644 index 0000000..4e92868 --- /dev/null +++ b/.planning/phases/02-season-1-vertical-slice-soil/02-DISCUSSION-LOG.md @@ -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 (5–7 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 | +|--------|-------------|----------| +| 2–3 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. | | +| 4–6 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 | +|--------|-------------|----------| +| ~2–5 minutes | Cozy but watchable; lets a playtest session complete the loop in ~10 minutes. | ✓ | +| ~30 seconds – 2 minutes | Demo-pace; reads less 'idle'. | | +| ~15–60 minutes | True idle; hard to playtest end-to-end without a debug fast-forward. | | +| ~6–12 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 `` § "Claude's Discretion"): + +- Specific growth-duration values per plant within the 2–5min band. +- Exact fragment-count thresholds for plant unlocks and Lura beats (model is locked, values may shift ±1–2 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 4–10Hz). +- 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 ``): + +- 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.