diff --git a/.planning/phases/02-season-1-vertical-slice-soil/02-03-harvest-journal-fragments-PLAN.md b/.planning/phases/02-season-1-vertical-slice-soil/02-03-harvest-journal-fragments-PLAN.md index 0726bb2..ff6bf22 100644 --- a/.planning/phases/02-season-1-vertical-slice-soil/02-03-harvest-journal-fragments-PLAN.md +++ b/.planning/phases/02-season-1-vertical-slice-soil/02-03-harvest-journal-fragments-PLAN.md @@ -370,7 +370,9 @@ wait. The wait was the thing. The flower would bloom in its own time. Most good things were like that, until they weren't. ``` -(Total: 9 in yaml + 2 in md + 1 sentinel = 12 fragments. Exceeds RESEARCH Assumption A8's "≥10" target with margin. Tags distribute: 4 warm, 3 contemplative, 3 heavy, 1 _meta = 11 plant-tagged + 1 sentinel; comfortably feeds 8th-harvest Lura threshold + plant-type unlocks.) +(W6 fix — bump warm-pool depth so a worst-case all-rosemary playthrough still has fragments left at harvest 8. + +Total: ≥14 in yaml + ≥2 in md + 1 sentinel = ≥17 fragments. Tags distribute: ≥9 warm, ≥3 contemplative, ≥3 heavy, 1 _meta. The yaml block above shows 3 warm samples; the executor authors ≥6 additional warm-tagged fragments matching the same tonal register before committing. Pool depth must satisfy the worst-case constraint: 8 harvests of rosemary alone must not exhaust the warm pool. The exhaustion sentinel still exists as a defensive fallback (Pitfall 8), but the authored pool should be deep enough that it is never reached during normal Phase-2 play.) **Step 3 — `src/sim/memory/pool.ts`** (PATTERNS Group D filter pattern): @@ -643,7 +645,8 @@ PIPE-02's lazy split is structurally verified by `scripts/check-bundle-split.mjs **Commit:** `feat(02-03): Season-1 fragments + sim/memory selector + harvest/compost commands`. Run `npm run lint && npx vitest run src/sim/ src/content/ && npm run build` before committing (npm run build proves the new fragments parse). - - `grep -c "^ - id: season1\\." content/seasons/01-soil/fragments.yaml` returns ≥9 + - `grep -c "^ - id: season1\\." content/seasons/01-soil/fragments.yaml` returns ≥14 + - `[ "$(grep -c "tags: \\[warm\\]" content/seasons/01-soil/fragments.yaml)" -ge 9 ]` (W6: warm pool ≥ 8th-harvest depth + 1 buffer) - `ls content/seasons/01-soil/fragments/*.md | wc -l` returns ≥2 - `grep -q "season1.soil._exhaustion" content/seasons/01-soil/fragments.yaml` - `grep -q "tags: \\[warm\\]\\|tags: \\[contemplative\\]\\|tags: \\[heavy\\]" content/seasons/01-soil/fragments.yaml` (multiple) @@ -858,7 +861,7 @@ export function FragmentRevealModal(): JSX.Element | null { **Step 3 — `src/ui/journal/journal-icon.tsx`** (D-23 + D-29): ```typescript -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useAppStore, selectJournalRevealed } from '../../store'; import { Journal } from './Journal'; @@ -873,6 +876,18 @@ export function JournalIcon(): JSX.Element | null { const revealed = useAppStore(selectJournalRevealed); const [open, setOpen] = useState(false); + // W2 — D-29 'j' hotkey toggles the journal. App.tsx dispatches a window + // CustomEvent so the JournalIcon owns its open/close state without lifting + // it into the store. The listener is keyed off the same revealed gate as + // the icon itself — pre-first-harvest the hotkey is a no-op (matches the + // anti-FOMO doctrine: nothing exists for the player to "discover" early). + useEffect(() => { + if (!revealed) return; + const onToggle = () => setOpen((o) => !o); + window.addEventListener('tlg:toggle-journal', onToggle); + return () => window.removeEventListener('tlg:toggle-journal', onToggle); + }, [revealed]); + if (!revealed) return null; return (