- 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