Files
TheLastGarden/.planning/STATE.md
T
josh de3f55b1c4 docs(02-04): complete lura-gate-beats plan
- 02-04-lura-gate-beats-SUMMARY.md created. Documents:
  - 3 Lura Ink beats authored in bible voice (warmth anchor, contrast not co-griever)
  - Build-time inklecate compile pipeline with bundled binary (BLOCKER 4 mitigated)
  - RESEARCH Assumption A6 verified first-try on Windows
  - 47 new Vitest cases (264/264 total green); npm run ci exits 0
  - sim/narrative gating pure-state; STRY-10 mechanically defended
  - sim/* contains zero inkjs imports; ESLint sim-purity rule still green
  - 4 lazy code-split chunks emitted for compiled Ink JSON
  - Compost-toast UI deferred to Plan 02-05 (folded into persistence-toast surface)
  - 5 auto-fix deviations documented (Rule 1 + Rule 3); 2 tightenings; 0 architectural changes
- STATE.md updated: progress 18% → 19%; Phase 2 plans 3/5 → 4/5; 217 → 264 tests;
  per-phase metrics updated (Phase 2 4/5 plans, ~66min, ~16min/plan).
- ROADMAP.md: Plan 02-04 marked complete with duration; progress table updated.
- REQUIREMENTS.md: STRY-01 / STRY-06 / STRY-07 / STRY-10 marked complete with
  full traceability annotations.

Plan 02-05 (offline catchup + letter + Settings + Playwright e2e) is the only
remaining Phase-2 work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 10:41:24 -04:00

16 KiB
Raw Blame History

gsd_state_version, milestone, milestone_name, status, stopped_at, last_updated, last_activity, progress
gsd_state_version milestone milestone_name status stopped_at last_updated last_activity progress
1.0 v1.0 milestone in_progress Phase 2 Wave 2 first plan (Plan 02-04 lura-gate-beats) executed in sequential mode. 3 atomic commits: c90f8f1 (ink compilation pipeline + 4 authored Season-1 .ink files + runtime loader), 7b79d11 (sim/narrative — Lura beat gating 1/4/8 harvest, STRY-10), 661f990 (Lura dialogue overlay + Ink runtime + gate visual + Garden scene wiring). 264/264 tests green (was 217; +47 new); npm run ci exits 0 end-to-end with compile:ink integrated into the chain BEFORE test. STRY-01 / STRY-06 / STRY-07 / STRY-10 requirements satisfied. Lura goes on the record as the warmth anchor — 3 authored Ink beats (lura-arrival/mid/farewell) gated at 1st/4th/8th harvest counts (D-14); STRY-10 mechanically defended by FakeClock-24h-no-harvest test. RESEARCH Assumption A6 verified first-try on Windows via the bundled inklecate binary at node_modules/inklecate/bin/. Vite emits 4 lazy code-split chunks for compiled Ink JSON. Sim purity firewall holds: src/sim/narrative/ contains zero inkjs imports. Compost-toast UI deferred to Plan 02-05's persistence-toast surface (compost-acknowledgements.ink rewritten in VAR-driven branch shape, ready for the runtime). Wave 2 final plan (02-05 letter-settings-e2e) is the only remaining Phase-2 work. Next: /gsd-execute-phase 2 to continue with Plan 02-05. 2026-05-09T14:32:00.000Z 2026-05-09
total_phases completed_phases total_plans completed_plans percent
8 1 8 11 19

Project State

Project Reference

See: .planning/PROJECT.md (updated 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. Current focus: Phase 02 — Season 1 Vertical Slice (Soil) — Wave 0 done; Wave 1 done (02-02 + 02-03); Wave 2 first plan (02-04) DONE; 02-05 is the only remaining Phase-2 work

Current Position

Phase: 02 (season-1-vertical-slice-soil) — 4/5 plans complete (Wave 0 + Wave 1 + first Wave 2 plan) Plans: 5 of 5 created (3 waves); Wave 0 (02-01) DONE; Wave 1 (02-02 + 02-03) DONE; Wave 2 first plan (02-04 lura-gate-beats) DONE; Wave 2 final plan (02-05 letter-settings-e2e) queued Status: Plan 02-04 lura-gate-beats executed in sequential mode — 3 atomic commits, 47 new tests (264/264 total green), npm run ci exits 0 with compile:ink integrated into the chain BEFORE test. STRY-01 / STRY-06 / STRY-07 (vacuous) / STRY-10 satisfied end-to-end. Lura's 3 Season-1 beats authored in voice + ship via the inklecate compile pipeline + inkjs runtime; gate visual indicator + DOM dialogue overlay both green. RESEARCH Assumption A6 verified first-try on Windows. Sim purity firewall holds: src/sim/narrative/ contains zero inkjs imports. Last activity: 2026-05-09 -- Plan 02-04 execute complete

Progress: [██░░░░░░░░] 19%

Verification Results

Phase 1 Overall Verdict: PASSED

REQ-ID Status
CORE-01 PASS — scaffold builds; <5s measurement is Phase 2
CORE-04 PASS — IDB + localStorage fallback; 4 tests green
CORE-05 PASS — navigator.storage.persist() all 4 scenarios
CORE-06 PASS — versioned envelope + CRC-32; tamper detection
CORE-07 PASS — forward-only migration chain; synthetic v0→v1
CORE-08 PASS — last-3 snapshot retention; 5-then-3 invariant
CORE-09 PASS — Base64 codec + 50MB DoS cap; round-trip test
CORE-10 PASS — ESLint boundaries rule + Vitest proof
PIPE-01 PASS — Vite-native loader; schema violation fails build
PIPE-03 PASS — asset provenance gate; refused-sample fixture
PIPE-05 PASS — both doctrine docs authored + doc-lint tests
PIPE-06 PASS — ci.yml; 53 tests on every push
AEST-08 PASS — ProvenanceSchema 6 fields; CI gate in place
AEST-09 PASS (IOU) — curation gate exists; human decision recorded
STRY-09 PASS (vacuous) — /content/ convention established
UX-13 PASS — anti-fomo-doctrine.md; review-enforced

Gates run: lint (exit 0), test (53/53 green, 12 files), validate:assets (2 assets valid), build (exit 0), compile:ink (exit 0), ci (exit 0).

Performance Metrics

Velocity:

  • Total plans completed: 10 (1 partial — 01-05 Task 2 deferred via IOU)
  • Average duration: ~5 min (Wave 1 baseline 6min; Wave 2 plans 48min; Plan 07 ~2min; Plan 02-01 ~12min; Plan 02-02 ~18min; Plan 02-03 ~12min — Phase-2 vertical-slice plans are heaviest)
  • Total execution time: ~70 min across Phase 1 + Phase 2 Wave 0 + Wave 1 (both plans)

By Phase:

Phase Plans Total Avg/Plan
1. Foundations & Doctrine 7/7 (complete) ~30 min ~5 min
2. Season 1 Vertical Slice (Soil) 4/5 (Wave 0 + Wave 1 + first Wave 2 plan complete) ~66 min ~16 min

Recent Trend:

  • Last 5 plans: [01-07 ci-workflow · 02-01 foundations · 02-02 begin-plant-grow · 02-03 harvest-journal-fragments · 02-04 lura-gate-beats — all green]
  • Trend: → (02-04 was 24 min — heaviest Phase-2 plan to date; first real player-narrative integration in the project covers a build pipeline + runtime + sim gate + UI overlay + canvas indicator + Garden scene wiring + 4 authored Ink files all in one plan; +47 new tests for 264/264 total green)

Updated after each plan completion

Accumulated Context

Decisions

Decisions are logged in PROJECT.md Key Decisions table. Recent decisions affecting current work:

  • Phase 1 will land all retrofit-hostile foundations (versioned saves, content/asset pipelines, sim/render firewall, anti-FOMO doctrine, Season 7 end-state design) before any feature code — research from all four researchers converged on this ordering. COMPLETE.
  • Phase 2 will ship Season 1 as a complete vertical slice that could plausibly ship as a free standalone prologue ahead of Seasons 2-7, defending against the 7-Season scope risk.
  • Plan 02-01 (Wave 0): BLOCKER 3 lastTickAt-vs-tickCount split landed — SimState carries TWO time fields with strict ownership (lastTickAt = wall-clock, app-only writes; tickCount = sim-internal monotonic). simAdapter.applyTickCount is the canonical sim → store path. Pinned by 3 store tests + 1 migrations test.
  • Plan 02-01 (Wave 0): V1Payload extended in place per D-34 (no migrations[2]) — Phase-1's v1 has shipped zero production saves so adding fields with defaults in migrations[1] is cleaner. Regression-defense test asserts Object.keys(migrations).sort() === ['1'].
  • Plan 02-01 (Wave 0): ESLint sim-purity rule (Block 3 of eslint.config.js) is the mechanical defense for D-33 — bans Date.now() and setInterval in src/sim/** with src/sim/scheduler/clock.ts as the lone exception. Programmatic Vitest test against the date-now-violator fixture proves the rule fires; negative test on clock.ts proves the exception holds.
  • Plan 02-02 (Wave 1): GRID_LAYOUT origin-centering math corrected during execution — gridOriginX=296 / gridOriginY=168 (not the plan's 240/144 hedged "≈" values). True-centered in 1024×768.
  • Plan 02-02 (Wave 1): Phaser 4 cannot be imported under happy-dom — its boot probe checkInverseAlpha calls canvas.getContext('2d') which returns null. SeedPicker test mocks src/game/event-bus to avoid pulling Phaser into the test runtime; Phaser scene behavioral coverage is the Plan 02-05 Playwright e2e's job (RESEARCH Validation Architecture explicitly states render-tier needs a real canvas).
  • Plan 02-02 (Wave 1): Audio bootstrap is module-level state (not React useState) so the click handler can call it synchronously — Pitfall 5 (iOS Safari requires AudioContext construction inside the gesture, not just resume) is mitigated structurally.
  • Plan 02-03 (Wave 1): Pool-exhaustion behavior chosen — sentinel fallback (season1.soil._exhaustion), NOT repeat-most-recent. Repeat-most-recent would silently re-grow harvestedFragmentIds past the corpus size, breaking the no-dup invariant downstream consumers (Journal de-dup, Lura beat counters, letter slot vocabulary) depend on. Authored warm pool ≥9 makes the sentinel structurally unreachable in normal Phase-2 play; it's a defensive structural fallback only.
  • Plan 02-03 (Wave 1): Plant-type unlock thresholds finalized — rosemary @ 0 / yarrow @ 3 / winter-rose @ 6. Spaced before Lura's mid-beat (4th harvest) and farewell beat (8th harvest) per D-14, so unlocks land in tonal alignment with the arc's turns. Pitfall 10 mitigation: thresholds checked AFTER the harvest commit (3 explicit boundary tests).
  • Plan 02-03 (Wave 1): Garden scene loads fragments via the EAGER fragments corpus filtered to Season 1, NOT via await loadSeasonFragments(1). Trade-off: simpler synchronous create() vs. INEFFECTIVE_DYNAMIC_IMPORT warnings inherited from Plan 02-02. Lazy plumbing is structurally proven by scripts/check-bundle-split.mjs; Phase 4+ should swap to lazy when Season transitions land.
  • Plan 02-03 (Wave 1): Compost beat content shipped in content/dialogue/season1/compost-acknowledgements.ink ahead of Plan 02-04's Ink runtime; Garden.ts compost branch carries a TODO at the wiring point. The split lets the writer iterate on voice independently of runtime work.
  • Plan 02-03 (Wave 1): PIPE-02 verifier scripts/check-bundle-split.mjs is structured as Vitest-importable Node ESM (runCheck() exported, CLI gated by import.meta.url). Pattern reusable for Phase 4 Season-2 onboarding (extend known-content list) and Phase 8 visual-regression baselines (different filename heuristics, same export shape).
  • Plan 02-04 (Wave 2): Direct binary invocation chosen over the inklecate npm wrapper API. The wrapper's executableHandler swallows non-zero exit codes silently, the stderr capture surface is undocumented. compile-ink.mjs uses execFileSync(node_modules/inklecate/bin/inklecate{.exe}) directly so failure modes are loud (full stderr/stdout in the throw). The bundled binary IS stable; the wrapper isn't.
  • Plan 02-04 (Wave 2): BLOCKER 4 mitigation — script uses node_modules/inklecate/bin/inklecate{.exe}, NOT the stale inklecate-windows//inklecate-mac/ per-platform-folder strings. The wrapper ships a single bin/ directory with the .NET self-contained executable + DLLs. Verified via ls node_modules/inklecate/bin/. RESEARCH Assumption A6 verified first-try on Windows.
  • Plan 02-04 (Wave 2): compileAllInk has a wipe toggle (default true for CLI; passed false from the test path) so compile-ink.test.mjs and src/content/ink-loader.test.ts don't race on the wipe step under Vitest's parallel test execution. CI's compile:ink-before-test ordering still guarantees a fully-populated directory.
  • Plan 02-04 (Wave 2): compost-beat UI wiring deferred to Plan 02-05's persistence-toast surface (compost is a thinner toast variant separate from Lura's full-screen overlay; Plan 02-05 lands the toast UX alongside CORE-05's persistence-denied surface). Plan 02-04 ships the AUTHORED CONTENT (compost-acknowledgements.ink rewritten in VAR-driven branch shape) + the loadInkStory('compost-acknowledgements') path; only the toast component is missing.
  • Plan 02-04 (Wave 2): STRY-07 satisfied vacuously for Phase 2 — zero .ink files contain Keeper-spoken lines. The gardener-keeper voice in compost beats acknowledges the player's actions but is never personified. Phase 7's binary choice surface (SEAS-09 / STRY-08) re-evaluates.
  • Plan 02-04 (Wave 2): Cadence values: DEFAULT_DELAY_MS=1500, PER_CHAR_MS=20, MAX_DELAY_MS=4000. Calibrated against typical 80-char line (3.1s) feeling close to a thoughtful texted reply, vs short "Oh." (1.56s) feeling like a beat. Tunable in playtest by editing src/ui/dialogue/ink-runtime.ts; constants exported for the Phase 8 UX-05 reduced-motion hook.
  • Plan 02-04 (Wave 2): Lura's last_plant_type derives from the most-recently-harvested fragment's tonal-register tag (warm → rosemary, contemplative → yarrow, heavy → winter-rose). The harvest pipeline doesn't currently store source plant type per harvest — Plan 02-05 may add that to offlineEvents. The tag-based proxy is sufficient for Phase 2's voice; Lura's branch on plant type is flavor, not a gate.
  • Phases 4-7 deliver the remaining six Seasons in mechanic-introducing pairs (Season 2 alone with prestige, Seasons 3-4, Seasons 5-6, Season 7 alone) — at most one new mechanic per Season per the scope-defense doctrine.
  • Plan 01-01: scaffolded by hand (the official npm create @phaserjs/game@latest is interactive-only — --template react-ts --yes flags are silently ignored as of create-game v1.3.2); plan's documented fallback path was used. Vite 8 + TS 6 referenced-projects tsconfig layout adopted; build runs tsc -b && vite build so strict-TS gates every build. ESLint 9 installed → Plan 02 must use flat config (eslint.config.js), not legacy .eslintrc.*.
  • Plan 01-01: pre-installed fake-indexeddb@^6 here so Plan 03 doesn't have to re-edit package.json. All Phase-1 dep versions match RESEARCH.md exactly within their ^ ranges.
  • Plan 01-07: minimum-viable CI workflow (49 lines) running npm ci + npm run ci on push/PR to main; ubuntu-latest only, Node 22 only, no matrix per CONTEXT user pushback against ceremony. The workflow's role is to refuse merges that break local npm run ci, nothing more. New CI gates (Phase 2 e2e, Phase 8 visual regression) are added by editing package.json scripts.ci (or new dedicated workflow files), not by editing ci.yml — the workflow stays stable across all future phases.
  • Plan 01-07: npm ci (lockfile-strict) chosen over npm install per RESEARCH § Security Domain; npm audit deferred to Phase 8. actions/setup-node@v4 with cache: 'npm' per RESEARCH CI Pitfall A — never cache node_modules/ directly.
  • Plan 01-05 Task 2 (north-star images): Deferred via Path C per 01-05-IOU.md. User decision: "I don't really want to deal with creating the art for this." Two placeholder PNGs committed with valid provenance sidecars. Real north-star curation deferred to Phase 5 when production-volume asset generation begins.

Pending Todos

  • Plan 01-05 Task 2 (human curation) — Phase 5 follow-up: 10-20 hand-curated AI generations committed to assets/north-stars/ with provenance sidecars. Non-blocking for Phase 2 (no production AI assets until Phase 5+). Recorded in 01-05-IOU.md with resolution path.

Blockers/Concerns

Carry-forward banner concerns from research:

  • 7-Season scope risk is the project's biggest risk; defended by the standalone-Season-1 escape hatch (Phase 2) and the one-mechanic-per-Season cap.
  • Story ends but the loop doesn't — Season 7 end-state design landed in Phase 1 (.planning/season-7-end-state.md, PIPE-05 ✓); finite Roothold ceiling enforcement deferred to Phase 4 (SEAS-04); credits/coda rest-state to Phase 7 (SEAS-10).
  • AI asset style drift — provenance schema + CI gate + refused-sample fixture landed in Phase 1 (Plan 01-05 Task 1, PIPE-03 + AEST-08 ✓); locked 1020-image north-star reference set deferred to Phase 5 per IOU (AEST-09 IOU); production-volume asset generation begins Phase 5+; visual regression testing in Phase 8 (PIPE-04).

Deferred Items

Items acknowledged and carried forward:

Category Item Status Deferred At
AEST-09 10-20 real north-star reference images for visual regression baseline IOU — Phase 5 follow-up Phase 1 (01-05-IOU.md)

Session Continuity

Last session: 2026-05-09 Stopped at: Phase 2 Wave 2 first plan (Plan 02-04 lura-gate-beats) executed in sequential mode — 3 atomic commits (c90f8f1, 7b79d11, 661f990), 47 new tests, 264/264 total green, npm run ci exits 0 (compile:ink integrated into the CI chain BEFORE test). STRY-01/STRY-06/STRY-07/STRY-10 satisfied end-to-end. Lura's 3 Season-1 beats authored in voice via the inklecate compile pipeline + inkjs runtime; gate visual indicator + DOM dialogue overlay both functional. RESEARCH Assumption A6 verified first-try on Windows via the bundled inklecate binary. Sim purity firewall holds: src/sim/narrative/ contains zero inkjs imports; ESLint sim-purity rule still green. Vite emits 4 lazy code-split chunks for compiled Ink JSON. SUMMARY at .planning/phases/02-season-1-vertical-slice-soil/02-04-lura-gate-beats-SUMMARY.md. Next action: /gsd-execute-phase 2 to continue with Plan 02-05 (offline catchup + letter + Settings + Playwright e2e — UX-02, UX-10, CORE-03, CORE-11, PIPE-07). Plan 02-05 will fold the compost-toast UI surface alongside the persistence-denied toast (compost-acknowledgements.ink runtime path is already wired; only the toast component is missing). Plan 02-05 is the final Phase-2 plan; completing it means Phase 2 is shippable as a free standalone Season-1 prologue.