From 3378068884001a33cc6e6e23dfb3e6456c97d626 Mon Sep 17 00:00:00 2001 From: josh Date: Fri, 8 May 2026 21:27:24 -0400 Subject: [PATCH] docs: define v1 requirements --- .planning/REQUIREMENTS.md | 201 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 .planning/REQUIREMENTS.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md new file mode 100644 index 0000000..0f29802 --- /dev/null +++ b/.planning/REQUIREMENTS.md @@ -0,0 +1,201 @@ +# Requirements: The Last Garden + +**Defined:** 2026-05-08 +**Core Value:** Every idle mechanic must function as a metaphor that the player absorbs without being told. When economy and meaning conflict, meaning wins. + +## v1 Requirements + +Requirements for initial release. Each maps to roadmap phases. All are user-centric, testable, atomic. + +### CORE — Engine, Simulation, Save Persistence + +- [ ] **CORE-01**: Player loads the game in a modern browser (Chrome, Firefox, Safari, Edge — last 2 stable releases) and reaches a playable state in under 5 seconds on a 25 Mbps connection. +- [ ] **CORE-02**: Game runs a deterministic, fixed-timestep simulation that advances by elapsed real time (not `setInterval` ticks), so a player who switches tabs or sleeps their device returns to a correctly-advanced garden. +- [ ] **CORE-03**: Player who closes the game and returns finds the garden has progressed by the elapsed time (capped at 24 hours) — *no progression resumes from a stale snapshot*. +- [ ] **CORE-04**: Player's progress saves to IndexedDB (with localStorage fallback), surviving browser refresh, browser updates, and at least 30 days of inactivity on Chrome and Firefox. +- [ ] **CORE-05**: Game requests persistent storage via `navigator.storage.persist()` on first save and surfaces the result respectfully if the browser declines. +- [ ] **CORE-06**: Saves are versioned (`{schemaVersion, payload, checksum}`) and the game refuses to load a save with a checksum mismatch, presenting the player with a recovery option. +- [ ] **CORE-07**: Game runs a `migrate_vN_to_vN+1` chain on load, so a save from any prior version of the game upgrades cleanly to the current shape (validated by Vitest tests for every shipped migration). +- [ ] **CORE-08**: Game keeps the last 3 pre-migration save snapshots and offers the player a "restore previous save" option in settings. +- [ ] **CORE-09**: Player can export their save as a Base64 text blob via Settings → Export and import it back into the same or a fresh browser via Settings → Import. +- [ ] **CORE-10**: Game's simulation core (`src/sim/`) imports nothing from `src/render/` or `src/ui/` — enforced by ESLint boundary rules in CI. +- [ ] **CORE-11**: Simulation refuses negative time deltas (system-clock cheat defense) and caps any single offline progression at 24 hours, regardless of wall-clock claim. + +### GARDEN — Planting, Growing, Harvesting + +- [ ] **GARD-01**: Player can plant a seed from their seed inventory into an unoccupied tile of the garden. +- [ ] **GARD-02**: Each plant has a visible growth state (sprout → mature → ready-to-harvest) that updates from save data on load and advances over time. +- [ ] **GARD-03**: Player can harvest a mature plant to receive a memory fragment; harvesting empties the tile. +- [ ] **GARD-04**: Player can compost an immature or unwanted plant, returning a portion of resources and triggering a tonal beat (acknowledging the choice to let go). +- [ ] **GARD-05**: Player unlocks new plant types as they progress through Seasons, with each plant type having distinct growth time, harvest yield, and visual identity. +- [ ] **GARD-06**: Tree plantings (Season 3+) are slow and expensive but produce place-memory vignettes when harvested. +- [ ] **GARD-07**: Cross-pollination (Season 2+): adjacent compatible plants can produce hybrid seeds with mixed memory traits. +- [ ] **GARD-08**: Ecosystem planting (Season 5+): clusters of compatible species produce yield bonuses, encouraging the player to think in ecosystems rather than individual crops. +- [ ] **GARD-09**: Memory Storms (Season 4+): periodic events accelerate decay of unprotected plants, requiring the player to plant resilient species, build windbreaks, or time harvests around them. +- [ ] **GARD-10**: Player sees the garden as a Phaser 4 canvas with watercolor-styled, hand-painted-feeling plants (no generic fantasy flora). + +### MEMORY — Fragments, Journal, Selection + +- [ ] **MEMR-01**: Each harvest yields exactly one memory fragment, drawn from the authored content pool gated by current Season and unlocked progression. +- [ ] **MEMR-02**: Memory fragments are authored in plain text (Markdown with frontmatter) in the project's `/content/` tree and compiled per-Season at build time. +- [ ] **MEMR-03**: Each fragment has a stable string ID (e.g., `season3.canopy.lura_07.vignette`) — never numeric — so re-ordering or re-authoring does not break references. +- [ ] **MEMR-04**: Player can open a Memory Journal (React DOM panel) listing every fragment they have collected, organized by Season. +- [ ] **MEMR-05**: Player can read any collected fragment in full at any time, including selecting and copying its text (DOM-based, not canvas-rendered). +- [ ] **MEMR-06**: Fragments are selected by a deterministic selector that respects authored gating rules (Season requirement, story-state requirement, player-progression requirement) and avoids duplicates within a single playthrough until the pool is exhausted. +- [ ] **MEMR-07**: Place-memory vignettes (Season 3+) deliver a fragment as a short interactive scene the player can walk through, not just a text block. + +### STORY — Characters, Dialogue, Choice + +- [ ] **STRY-01**: Lura (the audience-surrogate carpenter from a Remembered town) appears at the garden gate during Season 1 and reacts to early fragments with text-message-cadence dialogue authored in Ink. +- [ ] **STRY-02**: Lura's dialogue continues across all 7 Seasons, contextualizes major story beats, and reflects player progression in Ink-driven branches tied to Zustand variables. +- [ ] **STRY-03**: The Nameless Man appears in Season 2, his dialogue progressively shortens and confuses across Seasons 2-4, and he vanishes mid-sentence in Season 4 with no fanfare or cutscene. +- [ ] **STRY-04**: The Archivist appears in Season 6, never gendered (they/them), speaks softly and reflectively, and asks the player a thematic question without forcing an answer. +- [ ] **STRY-05**: The Archivist responds (mechanically and tonally) when the player feeds the Loom a memory containing both joy and grief — the Loom holds the contradiction, ending the Unremembering's advance. +- [ ] **STRY-06**: All authored dialogue uses Ink (`.ink` files) compiled to JSON for runtime via inkjs. +- [ ] **STRY-07**: The Keeper (player character) has no name, no backstory, and no dialogue beyond the final binary choice in Season 7. +- [ ] **STRY-08**: The final scene of Season 7 presents the player with a binary narrative choice (*"They help us remember"* / *"They help us grow"*); both endings display the line *"The garden persists."* and both are tonally complete; neither unlocks alternate post-credits content. +- [ ] **STRY-09**: Every player-visible string is externalized in `/content/` (not hardcoded in TypeScript), so localization can be retrofitted in v2 without code refactor. +- [ ] **STRY-10**: Story progression gates on tick count, not on wall time — players cannot fast-forward through authored beats by manipulating their system clock. + +### SEAS — Seasons, Prestige, Roothold + +- [ ] **SEAS-01**: The game presents the seven Seasons (Soil, Roots, Canopy, Storm, Depth, Loom, Return) as an authored sequence; the player progresses through them in order. +- [ ] **SEAS-02**: Each Season ends with a die-off event (frost in Season 1, escalating tonally; pulled-edit-out-of-existence in Season 3) that wipes surface plantings and triggers a Season transition. +- [ ] **SEAS-03**: Roothold persists across every Season transition; it is the only number that can never decrease. +- [ ] **SEAS-04**: Roothold has a *finite* ceiling tied to the narrative argument; the curve flattens deliberately as the player approaches the end of the authored arc. +- [ ] **SEAS-05**: Each Season introduces *at most one* new mechanic (composting, cross-pollination, place-memory vignettes, Memory Storms, ecosystem planting, the Loom interface, Return-mode expansion) per the scope-defense doctrine. +- [ ] **SEAS-06**: Season transitions crossfade visual palette and audio bed (Howler.js), shifting from golden/autumnal (early) → deep green/storm (middle) → dawn/silver (late). +- [ ] **SEAS-07**: The Below (accessible Season 5+) lets the player grow root structures into ancient memory layers and deliver content from a pre-Archivist civilization. +- [ ] **SEAS-08**: Season 6 shifts the primary gameplay loop to the Below, where the player feeds memories to the Loom rather than harvesting fragments. +- [ ] **SEAS-09**: Season 7 ("Return") shifts to a long, satisfying late-game in which collected memories become seeds that automatically reconstitute the world; the Pale recedes; the Heartsoil expands beyond the garden walls. +- [ ] **SEAS-10**: When the player reaches the end of the authored arc, the game transitions to a credits/coda *rest* state — not infinite prestige tiers — that the player can return to indefinitely without grinding. + +### AEST — Visual & Audio Aesthetic + +- [ ] **AEST-01**: The garden renders in a watercolor-adjacent visual style (hand-painted textures, plants that look like real species made slightly wrong) on a Phaser 4 canvas with a watercolor post-process filter. +- [ ] **AEST-02**: The Pale renders as overexposed white silence — luminous, pearlescent, *too bright* — with a faint tinnitus-like high tone in audio. +- [ ] **AEST-03**: The main musical theme is a solo cello, looped and crossfaded across Seasons via Howler.js. +- [ ] **AEST-04**: Ambient garden sounds (wind, birdsong, the creak of a gate) thin and fade as the Unremembering draws closer to the player's region. +- [ ] **AEST-05**: Audio crossfades, never hard-cuts, between Seasons; the cello and ambient layers are independent buses with separate volume controls. +- [ ] **AEST-06**: Color palette shifts deliberately by Season — golden/autumnal → deep green/storm → dawn/silver. +- [ ] **AEST-07**: The first screen of the game is a hand-painted "Tend the garden" / "Begin" gesture gate that satisfies the Web Audio user-gesture requirement and explicitly calls `AudioContext.resume()`. +- [ ] **AEST-08**: All AI-assisted assets carry persisted provenance metadata (`{model_id, checkpoint_hash, prompt, seed, sampler, params}`) and are produced from a pinned model and a locked north-star reference set. +- [ ] **AEST-09**: All shipped assets pass a mandatory human curation gate before integration; no asset reaches the production manifest unreviewed. + +### UX — Onboarding, Settings, Accessibility, Return + +- [ ] **UX-01**: First-time player sees a single, painted "Begin" screen with no UI clutter; the garden reveals itself as the player interacts (A Dark Room rule). +- [ ] **UX-02**: Player who returns after time away receives a "while you were away" *letter from the garden* — written in voice, not a stat dump — describing what grew, what bloomed, what the wind brought. +- [ ] **UX-03**: Player can buy plants/upgrades in multi-buy increments (×1 / ×10 / ×100 / Max) when the option is meaningful for the current scaling. +- [ ] **UX-04**: Player can adjust separate Music, Ambient, and SFX volume sliders, with a master mute keybind; settings persist in saves. +- [ ] **UX-05**: Player can toggle a reduced-motion option (respects `prefers-reduced-motion` system setting by default) that disables non-essential particles and animation. +- [ ] **UX-06**: Player can navigate all menus by keyboard (Tab, Enter, Escape, arrow keys) and the focus indicator is always visible. +- [ ] **UX-07**: All UI text is selectable, copy-pasteable, and supports browser zoom up to 200% without breaking layout. +- [ ] **UX-08**: Color is never the sole carrier of information — icons, labels, or patterns provide a redundant channel for color-blind players. +- [ ] **UX-09**: Tab title and favicon update to reflect a backgrounded state (e.g., a small bloom appears when a fragment is ready). +- [ ] **UX-10**: Game saves state on `visibilitychange` to hidden, on `beforeunload`, and on Season transitions; behavior is identical between "tab backgrounded" and "tab closed." +- [ ] **UX-11**: Numbers display in human-readable formats (1.2K, 4.5M, 8.9B, scientific notation past notation thresholds). +- [ ] **UX-12**: Game surfaces *what Lura said yesterday* in returning-player UI affordances — never *fragments per hour* or *optimization metrics* (mechanic-as-metaphor doctrine). +- [ ] **UX-13**: No daily login bonuses, no streaks, no limited-time content, no nag notifications, no loss-aversion copy — anti-FOMO doctrine is enforced in every UX review. + +### PIPE — Content Build & Asset Pipelines + +- [ ] **PIPE-01**: Project ships a build step that compiles `/content/**/*.{md,yaml,ink}` into per-Season JSON chunks via Zod-validated schemas; build fails on any schema violation. +- [ ] **PIPE-02**: Player loads only the content for their current Season at runtime (lazy chunk loading); future Seasons are not in the initial bundle. +- [ ] **PIPE-03**: Project ships an AI asset pipeline that records provenance per asset and refuses to integrate an asset missing required provenance fields. +- [ ] **PIPE-04**: Project ships visual regression testing for the asset library that flags style drift before any model migration is merged. +- [ ] **PIPE-05**: Project ships an `anti-FOMO doctrine` document and a `Season 7 end-state` design document in `.planning/` (or `docs/`) before economy code is written. +- [ ] **PIPE-06**: Project ships unit tests (Vitest) covering all save migrations and core economy formulas, run on every CI build. +- [ ] **PIPE-07**: Project ships an end-to-end smoke test (Playwright) that loads the game, plants a seed, harvests a fragment, and verifies persistence across a page reload. + +## v2 Requirements + +Deferred to v1.x or v2.0. Tracked but not in current roadmap. + +### MONE — Cosmetic Monetization + +- **MONE-01**: Player can purchase cosmetic-only items (planters, walls, gates, tool skins) via fixed-price catalog (no gacha, no random drops). +- **MONE-02**: Player can purchase a one-time "Keeper's Journal" premium upgrade unlocking annotation, organization, and personal-scrapbook features for collected fragments. +- **MONE-03**: Player can purchase Season acceleration that *accelerates waiting* — never *skips story beats*; UI explicitly labels the distinction. +- **MONE-04**: Game uses a server-authoritative entitlements backend (Cloudflare Worker + Stripe webhooks) — no localStorage entitlement claims. +- **MONE-05**: Cosmetic catalog items are permanent (never time-limited), priced transparently, and aesthetic-coherent with the garden setting. + +### PWA — Offline-First & Notifications + +- **PWA-01**: Player can install the game as a PWA with a service worker manifest. +- **PWA-02**: Player can opt in to push notifications for Memory Storm events only — no daily/marketing/streak notifications, ever. +- **PWA-03**: Service worker caches static assets and serves them offline; saves continue to work without a network connection. + +### CLOU — Cloud Save Sync + +- **CLOU-01**: Player can sign in (anonymous or email) and sync their save across devices via PocketBase or equivalent backend. +- **CLOU-02**: Cloud save uses the same versioned snapshot format as local save; conflict resolution prompts the player rather than silently overwriting. + +### CONT — Post-Launch Content Patches + +- **CONT-01**: Project ships free additive content patches post-launch (Hollow Knight model) — additional place-memory vignettes, garden cosmetics, and ambient-only Seasons that fit between authored beats. + +### PORT — Steam & Mobile Native Ports + +- **PORT-01**: Game ships on Steam (PC/Mac) using Phaser 4 + Electron or equivalent wrapper. +- **PORT-02**: Game ships on iOS and Android as native or hybrid wrappers, with on-device save sync via cloud. + +### LOC — Localization + +- **LOC-01**: Game supports localization (EN baseline, additional languages added based on community demand) — externalized strings already shipped in v1, only translation pass needed. + +### MOD — Modding & Community Content + +- **MOD-01**: Project documents a community-content authoring spec that lets community writers contribute fragments respecting tone and provenance discipline. +- **MOD-02**: Project ships a community-content gallery (off-canon, opt-in) where players can browse and load community-authored gardens. + +### SOC — Garden Visiting (cautious — must not break tone) + +- **SOC-01**: Player can opt in to visiting other players' gardens in read-only mode, viewing what fragments they have collected (without spoilers for unvisited Seasons). + +## Out of Scope + +Explicit exclusions. Documented to prevent scope creep. **Anti-features tied to the project's thematic constraints — these are not deferred features, they are excluded by design.** + +| Feature | Reason | +|---------|--------| +| Gacha mechanics | Directly contradicts the game's thematic argument that complex things cannot be reduced to simple transactions. | +| Lootboxes | Same reason as gacha — undermines the story's monetization-as-meaning argument. | +| Narrative gating behind purchase | The story IS the product; story content is never paid. | +| Season *skipping* (vs. *accelerating*) | Players must never miss authored story beats; acceleration is allowed, skipping is forbidden. | +| Daily login bonuses | FOMO mechanic; violates cozy/contemplative tone. | +| Login streaks | FOMO mechanic; same reason. | +| Limited-time / time-limited content | FOMO mechanic; same reason. | +| Energy / stamina systems | Anti-cozy gating that interrupts contemplative play. | +| Rewarded ads | Anti-cozy; tonally incoherent with a contemplative grief-narrative. | +| Push notification spam | Memory Storm opt-in is the *only* allowed notification — no daily/marketing/streak/re-engagement nags. | +| Lore codex / encyclopedia entries | Players should always feel *almost* understanding; world-building emerges through fragments alone. | +| Generic fantasy flora | Plants must look like real-world species made slightly wrong; no D&D-style fictional flora. | +| Combat / boss fights | The Archivist is not a boss; there is no enemy. Combat would violate the entire thematic frame. | +| Multiplayer / leaderboards (v1) | Solitary, contemplative experience; reconsider for v2 only if it does not break tone. | +| Voiced dialogue (v1) | Tone is "a friend texting you while you're at work"; text fits the medium. Reconsider v2+ only if cello-and-silence soundscape benefits. | +| Always-online | Local-first save model; game must work offline. | +| Named/personality-rich Keeper | Keeper is a presence, not a personality; player projects onto the system. | +| Hint system / objective tracker | Discovery-driven progression (A Dark Room rule); explicit objectives violate the tone. | +| Time-skip purchases that bypass real-time | Real-time *is* the metaphor for memory; skip-time purchases would violate mechanic-as-metaphor doctrine. | +| Unity / Godot / non-web engines (v1) | Web-first per PROJECT.md and lineage of A Dark Room / Universal Paperclips; install friction kills the audience. | +| Generic cosmetic items | Cosmetics must reinforce, not dilute, the garden aesthetic. No generic skins. | +| Random-drop cosmetics | All cosmetics must be deterministic catalog purchases. No RNG monetization. | +| Mobile-style nag UX | Cozy audience expects respect; nag patterns will tank reviews. | + +## Traceability + +Empty initially. Populated by gsd-roadmapper during roadmap creation. + +| Requirement | Phase | Status | +|-------------|-------|--------| +| (Pending roadmap creation) | — | — | + +**Coverage:** +- v1 requirements: 78 total +- Mapped to phases: 0 (pending roadmap) +- Unmapped: 78 ⚠️ — will resolve in roadmap step + +--- +*Requirements defined: 2026-05-08* +*Last updated: 2026-05-08 after initial definition*