- 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>
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>