josh 7b79d11584 feat(02-04): sim/narrative — Lura beat gating (1/4/8 harvest, STRY-10)
- src/sim/narrative/beat-queue.ts: LuraBeatId / LuraBeatProgress contracts
  matching V1Payload.luraBeatProgress + NarrativeSlice; INITIAL frozen.
- src/sim/narrative/lura-gate.ts: LURA_BEAT_THRESHOLDS = {1: arrival,
  4: mid, 8: farewell}; advanceLuraBeatProgress / resolvePendingLuraBeat /
  isLuraBeatPending — pure, no inkjs import, no Date.now (sim-purity rule
  green). The gate counts harvest events, never wall-clock time, so STRY-10
  holds.
- src/sim/narrative/lura-gate.test.ts: 17 cases including the load-bearing
  STRY-10 case (24 hours of FakeClock advance with 0 harvests leaves all
  flags + pending false). Pitfall 10 boundaries pinned at 3/4/5 and 7/8/9.
  pending-set-already + already-visited carry-throughs covered.
- src/sim/garden/commands.ts: harvest() now calls advanceLuraBeatProgress
  AFTER the harvest commit (Pitfall 10 — same-tick boundary). The new
  luraBeatProgress field flows through the returned SimState and into the
  store via the existing Garden.update() path.
- src/sim/garden/commands.test.ts: +5 cases pinning the harvest → beat
  gate edges (1st→arrival, 4th→mid, 8th→farewell, between-threshold
  no-fire, pending preservation when player hasn't visited).
- src/sim/index.ts: re-export ./narrative.

67/67 sim tests green; npm run lint + build exit 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 10:27:06 -04:00
2026-05-08 21:37:11 -04:00
S
Description
No description provided
1.4 MiB
Languages
TypeScript 91.2%
JavaScript 6.7%
Ink 1.8%
CSS 0.2%
HTML 0.1%