Files
TheLastGarden/CLAUDE.md
T
2026-05-08 21:37:11 -04:00

104 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 27 → 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 (58 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.