7b79d115842868f5f8de4bad7eb3e87a31214b2c
- 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>
Description
No description provided
Languages
TypeScript
91.2%
JavaScript
6.7%
Ink
1.8%
CSS
0.2%
HTML
0.1%