# The Last Garden — Claude Code Project Guide A 7-Season browser narrative idle game in the lineage of *A Dark Room* and *Universal Paperclips*. Players tend a walled garden at the fraying edge of a world that is being forgotten; the idle loop *is* the act of remembering. ## Source of Truth Always read these before suggesting changes: - [.planning/PROJECT.md](.planning/PROJECT.md) — story bible synthesis, core value, hard constraints - [.planning/REQUIREMENTS.md](.planning/REQUIREMENTS.md) — 77 v1 requirements with REQ-IDs (CORE, GARD, MEMR, STRY, SEAS, AEST, UX, PIPE) + v2 deferrals + explicit out-of-scope - [.planning/ROADMAP.md](.planning/ROADMAP.md) — 8-phase MVP-mode roadmap (foundation → Season 1 vertical slice → aesthetic → Seasons 2–7 → launch polish) - [.planning/STATE.md](.planning/STATE.md) — current phase + next action - [.planning/research/SUMMARY.md](.planning/research/SUMMARY.md) — stack at a glance + banner concerns - [.planning/research/STACK.md](.planning/research/STACK.md), [FEATURES.md](.planning/research/FEATURES.md), [ARCHITECTURE.md](.planning/research/ARCHITECTURE.md), [PITFALLS.md](.planning/research/PITFALLS.md) ## Stack (locked, do not re-litigate) - **Engine:** Phaser 4 (`npm create @phaserjs/game@latest` — official React + Vite + TypeScript template) - **UI shell:** React 19 (DOM overlay; HUD, journal, dialogue, settings) - **State bridge:** Zustand 5 - **Numbers:** break_eternity.js (BigQty wrapper from day one) - **Narrative:** Ink + inkjs - **Audio:** Howler.js (music + ambience), Phaser native (SFX) - **Save:** IndexedDB (via `idb`) + localStorage fallback + lz-string + versioned schema + Base64 export - **Content:** Markdown+frontmatter / YAML / `.ink` in `/content/`, Zod-validated, compiled per-Season at build time - **Tests:** Vitest (sim + migrations) + Playwright (e2e smoke) ## Architectural Firewall (load-bearing) - **Phaser owns the canvas** — garden, plants, weather, particles, watercolor post-process. - **React 19 owns the UI shell** — HUD, journal, dialogue overlays, fragment viewer, settings, shop. - **Zustand bridges them.** - **Simulation core is rendering-agnostic** — `src/sim/` does not import from `src/render/` or `src/ui/`. Enforced by ESLint boundary rules in CI. ## Banner Concerns (carry forward at every phase) 1. **The story ends but the loop doesn't** — the most dangerous structural pitfall. Roothold has a *finite* ceiling tied to the narrative; design Season 7 end-state before economy code; build a credits/coda rest state. 2. **7-Season scope eats solo devs** — defended by structuring Phase 2 as a plausibly-shippable standalone Season 1 prologue and capping new mechanics at one per Season. 3. **Browser save fragility** — multi-layer (IndexedDB + localStorage), versioned, `navigator.storage.persist()` always called, Base64 export shipped at v1. 4. **System-clock cheating** — 24h offline cap, monotonic deltas, story gates on tick count. 5. **AI asset style drift** — pinned model + provenance metadata + locked north-star reference set + mandatory human curation gate, enforced from Phase 1. 6. **FOMO mechanics violate cozy tone** — anti-FOMO doctrine doc enforced at every UX review (no daily logins, streaks, limited-time, or nag). 7. **Web Audio user-gesture requirement** — first screen is a "Tend the garden / Begin" gate that calls `AudioContext.resume()`. 8. **Tab throttling** — elapsed-time progression, never `setInterval` ticking; save on `visibilitychange` + `beforeunload` + Season transitions. 9. **Tonal failure** — external readers gate every Season's tone before integration; Lura is the warmth anchor. 10. **Authored content / code divergence** — single source of truth in `/content/`, stable string IDs (e.g., `season3.canopy.lura_07.vignette`), all player-visible strings externalized day one. ## Hard Thematic Constraints (Out of Scope by Design) These are not deferred features — they are excluded because they would undermine the game's argument: - No gacha, no lootboxes, no random-drop monetization - No narrative gating behind purchase - No Season *skipping* (acceleration only) - No daily login bonuses, streaks, or limited-time content - No energy / stamina systems - No rewarded ads - No push-notification spam (Memory Storm opt-in is the *only* allowed notification) - No lore codex or encyclopedia entries (fragments do all world-building) - No generic fantasy flora (real species, slightly wrong) - No combat, no boss fights (the Archivist is not an enemy) - No multiplayer / leaderboards in v1 - No voiced dialogue in v1 - No always-online (local-first save model) - No named/personality-rich Keeper (player projects onto the system) - No hint system / objective tracker ## GSD Workflow This project uses [Get Shit Done (GSD)](https://github.com/goodlyrottenapple/get-shit-done-cc) with the following config (see [.planning/config.json](.planning/config.json)): - **Mode:** YOLO — auto-approve at decision gates - **Granularity:** Standard (5–8 phases) - **Parallelization:** plans run in parallel within a phase - **Models:** Opus for research, synthesis, roadmap (Quality profile) - **Workflow agents:** research before planning, plan-check before execution, verifier after each phase ### Commands - `/gsd-discuss-phase N` — gather context for phase N before planning - `/gsd-plan-phase N` — create the executable plan for phase N - `/gsd-execute-phase N` — execute all plans in phase N - `/gsd-verify-work` — UAT a completed phase - `/gsd-progress` — situational command: where am I, what's next? - `/gsd-stats` — project metrics ### Next Action Run `/gsd-discuss-phase 1` to begin Phase 1 (Foundations & Doctrine). ## Code Style - TypeScript strict; no `any` in production code. - Player-visible strings are externalized in `/content/`, never hardcoded. - Memory fragment IDs are stable strings (`season3.canopy.lura_07.vignette`), never numeric. - Simulation modules are pure — no `Date.now()`, no `setInterval`, no DOM, no fetch. Inject time as a parameter; the tick scheduler owns wall-clock access. - BigNumbers go through the typed `BigQty` wrapper around `break_eternity.js`. Never raw `Decimal` values in app code. - Save format always carries `{schemaVersion, payload, checksum}`. Never serialize raw state. - New AI-generated assets must carry full provenance metadata (`{model_id, checkpoint_hash, prompt, seed, sampler, params}`) and pass the curation gate before reaching the manifest. ## Tone This is a contemplative game about loss, memory, and what persists. Code comments and PR descriptions can be functional, but anything player-facing — UI text, error messages, tooltips, the "while you were away" letter — should match the bible's voice: warm, specific, intermittent, sometimes funny, sometimes devastating. Lura is the warmth anchor; write her as the contrast, not a co-griever.