286b4ba44620b06d50354a73eb10ea6f18cf6b3e
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
26eb77a216 |
feat(02-05): sim/offline + auto-harvest + letter Ink + letter-renderer
- src/sim/offline/: OfflineEventBlockSchema (Zod) + EMPTY_OFFLINE_EVENTS + aggregateOfflineEvent pure aggregator (D-19); 14 tests green - src/sim/garden/auto-harvest.ts: autoHarvestReadyPlants silent-mode branch (D-10); reuses harvest() pipeline so selector + Pitfall 10 unlocks + STRY-10 Lura gate all run identically; BLOCKER 3 invariant preserved (no lastTickAt writes); 7 tests green - simulateOneTick: ctx.silent triggers auto-harvest sweep before tick increment; active-play path unchanged (silent defaults false) - content/dialogue/season1/letter-from-the-garden.ink: authored skeleton with VAR plants_bloomed / fragment_titles / lura_was_here per D-17/D-18; bible voice, anti-FOMO compliant, 24h cap silent in voice (D-11) - ink-loader: loadInkStory union extended with letter-from-the-garden; separate letterStoryGlob for lazy code-split chunk; INK_VARIABLE_MAP gains plants_bloomed / fragment_titles / lura_was_here slots reading from session.pendingLetterEventBlock - src/ui/letter/letter-renderer.ts: pure buildLetterSlots helper — prefers fragment first-sentence body for tonal weight, slugified-id fallback; 10 tests green - npm run compile:ink emits 5 .ink.json files (was 4); Vite emits the letter as a separate lazy chunk (letter-from-the-garden.ink-*.js) - 295/295 tests green (was 264; +31 new); npm run ci exits 0 |
||
|
|
c90f8f1e5c |
feat(02-04): ink compilation pipeline + 4 authored Season-1 Ink files + runtime loader
- scripts/compile-ink.mjs: build-time inklecate runner using bundled binary (BLOCKER 4 — uses node_modules/inklecate/bin, not stale -windows/-mac path strings). Assumption A6 verified first-try on Windows; the same binary path resolution works on macOS + Linux per the wrapper's own getInklecatePath convention. - scripts/compile-ink.test.mjs: 3 Vitest cases proving the compiler runs + emits valid JSON with inkVersion. wipe=false for the test path so it can run in parallel with the ink-loader test without racing on the wipe step. - 4 Season-1 .ink files authored in voice (Lura warmth-anchor, gardener-keeper for compost): lura-arrival.ink, lura-mid.ink, lura-farewell.ink, compost-acknowledgements.ink (rewrite of Plan 02-03 scaffolded version into VAR-driven branch shape consumable by the runtime). - src/content/ink-loader.ts: loadInkStory + bindGardenStateToInk + INK_VARIABLE_MAP. Centralized snake_case slot mapping per Pitfall 4. UTF-8 BOM stripped before Story instantiation. - src/content/ink-loader.test.ts: 8 cases — Story instantiation for all 4 beats, fragment_count binding, Pitfall 4 snake_case enforcement, silent skip for stories missing declared vars. - package.json: build now runs compile:ink first; ci chain runs compile:ink before test so ink-loader.test.ts's precondition check passes. - .gitignore: src/content/compiled-ink/ excluded (regenerated on every build). npm run ci exits 0; 11 new tests green (228 total). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
572c86192f |
feat(02-03): journal + reveal modal + harvest pointer wiring
Task 2 of Plan 02-03: ship the Memory Journal UI surfaces (D-23/D-24/D-25) and wire harvest + compost pointer events through the Garden scene to the sim → store → React reveal flow. src/ui/journal/ (new module): - Journal.tsx — full-screen modal (D-24); fragments grouped by Season; DOM-rendered text with userSelect: text per MEMR-05; reads harvestedFragmentIds from the store; resolves ids against the eager `fragments` corpus (defensive — unresolvable ids skip silently). - FragmentRevealModal.tsx — D-25 active-play reveal modal; backdrop click + inner Close button dismiss; event.stopPropagation on the article body so clicking inside the text doesn't dismiss; defensive silent dismiss on unresolvable id. - journal-icon.tsx — D-23 + D-29 corner affordance; gated by selectJournalRevealed (`harvestedFragmentIds.length > 0`); local open state (no store pollution); 'j' hotkey deferred to Plan 02-05. - index.ts — barrel. - 16 new Vitest cases across 3 test files (Journal: 7 / FragmentRevealModal: 6 / journal-icon: 3); all green. src/App.tsx — mount FragmentRevealModal + JournalIcon as DOM siblings of PhaserGame. src/ui/index.ts — re-export ./journal. src/game/scenes/Garden.ts — harvest/compost pointer flow: - create() builds a SimContext from the eager `fragments` corpus filtered to Season 1; passed to every simulateOneTick call (Phase 4+ should swap to await loadSeasonFragments(currentSeason) when Season transitions land). - handleTilePointerDown branches on tile state: empty → seed picker event; ready plant → enqueue 'harvest' command; immature plant → enqueue 'compost' command (TODO Plan 02-04: render the Ink-authored compost acknowledgement beat from compost-acknowledgements.ink). - update() detects newly-appended harvestedFragmentIds and sets fragmentRevealId so the reveal modal pops with the new fragment text. - BLOCKER 3 invariant preserved — sim writes tickCount, never lastTickAt. content/dialogue/season1/compost-acknowledgements.ink — authored content for the GARD-04 + D-07 compost beat. 6 short lines in the gardener-keeper voice (NOT Lura — she's the warmth anchor; the garden's voice is the contrast). Plan 02-04 wires the inkjs runtime; Plan 02-03 ships the content so the writer can iterate independently. 214/214 tests green (was 163; +51 new this plan); npm run lint exits 0; npm run ci exits 0; npm run build exits 0 with the expected INEFFECTIVE_DYNAMIC_IMPORT warnings (eager `fragments` export still imports the Season-1 yaml/md statically alongside the lazy loadSeasonFragments path — documented in 02-02-SUMMARY.md). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
df7d687da4 |
chore(01-01): scaffold Phaser 4 + React 19 + Vite + TS template + Phase-1 deps + firewall directories
- Built equivalent React + Vite + TypeScript scaffold by hand because the official npm create @phaserjs/game@latest scaffolder is interactive-only and the documented --template/--yes flags are ignored (verified 2026-05-08 with create-game v1.3.2). Plan Step 1 explicitly authorizes this fallback. Resulting tree mirrors the official template shape: index.html, src/main.tsx, src/App.tsx, src/PhaserGame.tsx, src/game/main.ts, src/game/scenes/Boot.ts. - Installed Phase-1 production deps at versions verified in RESEARCH.md: phaser@4.1.0, react@19.2.6, react-dom@19.2.6, idb@8.0.3, lz-string@1.5.0, zod@4.4.3, crc-32@1.2.2, gray-matter@4.0.3, yaml@2.8.4, inkjs@2.4.0. - Installed Phase-1 dev deps: vite@8.0.11, @vitejs/plugin-react@6.0.1, typescript@6.0.3, @types/react@19, @types/react-dom@19, @types/node@22, vitest@4.1.5, @vitest/ui, happy-dom, fake-indexeddb@6 (for Plan 03 IDB tests), @playwright/test@1.59.1, eslint@9, eslint-plugin-boundaries@6.0.2, inklecate@1.8.1. - Created the seven architectural-firewall directories under src/ with .gitkeep markers (sim, render, ui, save, content, audio, store) — siblings to the template-provided src/game/ — so Plan 02's ESLint boundaries rule has clean targets per CLAUDE.md 'Architectural Firewall'. - Created repo-root /content/ (with /dialogue/ and /seasons/ subdirs) and /assets/ trees per CONTEXT D-11, D-12. - Pre-declared all downstream-required scripts in package.json so Plans 02–06 only edit code, not script keys: dev, build, preview, lint (--max-warnings 0 per RESEARCH CI Pitfall C), test (--passWithNoTests=false per CI Pitfall B), test:watch, validate:assets, compile:ink (no-op stub for Phase 1; Phase 2 replaces with real inklecate invocation), ci. - TypeScript strict mode enforced via tsconfig.json + tsconfig.app.json + tsconfig.node.json. - npm run build succeeds (tsc -b && vite build) producing dist/index.html and dist/assets/index-*.js (~1.5MB Phaser bundle; code-splitting deferred to Phase 2+ when actual scenes exist). |