6.8 KiB
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 — story bible synthesis, core value, hard constraints
- .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 — 8-phase MVP-mode roadmap (foundation → Season 1 vertical slice → aesthetic → Seasons 2–7 → launch polish)
- .planning/STATE.md — current phase + next action
- .planning/research/SUMMARY.md — stack at a glance + banner concerns
- .planning/research/STACK.md, FEATURES.md, ARCHITECTURE.md, 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 /
.inkin/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 fromsrc/render/orsrc/ui/. Enforced by ESLint boundary rules in CI.
Banner Concerns (carry forward at every phase)
- 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.
- 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.
- Browser save fragility — multi-layer (IndexedDB + localStorage), versioned,
navigator.storage.persist()always called, Base64 export shipped at v1. - System-clock cheating — 24h offline cap, monotonic deltas, story gates on tick count.
- AI asset style drift — pinned model + provenance metadata + locked north-star reference set + mandatory human curation gate, enforced from Phase 1.
- FOMO mechanics violate cozy tone — anti-FOMO doctrine doc enforced at every UX review (no daily logins, streaks, limited-time, or nag).
- Web Audio user-gesture requirement — first screen is a "Tend the garden / Begin" gate that calls
AudioContext.resume(). - Tab throttling — elapsed-time progression, never
setIntervalticking; save onvisibilitychange+beforeunload+ Season transitions. - Tonal failure — external readers gate every Season's tone before integration; Lura is the warmth anchor.
- 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) with the following config (see .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
anyin 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(), nosetInterval, no DOM, no fetch. Inject time as a parameter; the tick scheduler owns wall-clock access. - BigNumbers go through the typed
BigQtywrapper aroundbreak_eternity.js. Never rawDecimalvalues 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.