← All entries

Daily Scrum Log — 2026-05-19 (Day 4)

Summary

Day 4 delivered the first web interface: an Express SSE backend wrapping the simulation engine, and a Vite + React frontend with a live canvas-based town map. The hero now moves visibly across a 24x24 tile map as the simulation ticks every 3.5 seconds. Test count grew from 37 to 47 (all passing), including 10 new smoke tests for the town map generator.


Project Manager

Contribution: Led scrum, determined pivot to web stack, locked today's scope.

Key Decisions:

  • Days 1–3 produced a solid headless simulation engine. Day 4's mandate from CLAUDE.md is clear: get a map rendering. Scope locked to Express + SSE backend, Vite + React frontend, canvas tile map.
  • Deferred TypeScript conversion and PostgreSQL/PG-BOSS to Day 5+. Getting something visible in the browser is higher priority than infrastructure purity.
  • No inventory system today — that's backlogged. The web transition is the critical path item.
  • Decided server and client stay in the same repo (no monorepo tooling) for now. Single package.json at root with server and client scripts.
  • Approved 3.5s tick interval: fast enough to feel live, slow enough to read what's happening.

Reasoning: CLAUDE.md explicitly states the end goal is sprites on a world map. Every day we don't have a browser target is a day we're building the wrong interface. The headless simulation is mature enough to wrap — doing it now rather than at Day 6 avoids a painful late migration.


Backend Developer

Contribution: Implemented the full server layer.

Files Created:

  • server/TownMap.js — 24x24 tile map generator. Eight tile types (GRASS, PATH, BUILDING, TREE, WATER, MARKET, FIELD, WALL). Fixed town layout: cross-shaped main paths, central market square (9–14, 9–14), four 3x3 buildings (Inn, Blacksmith, Temple, Merchant Hall), water feature, decorative fields. heroTilePosition(region, isResting, seed) maps simulation state to tile coordinates via deterministic jitter within region zones.
  • server/SimEngine.jsEventEmitter subclass. Async tick-based simulation wrapper running every 3.5 seconds via setInterval. On each tick: spawn encounter, delegate to CombatSystem.resolve(), infer combat summary from HP delta (avoids re-implementing Logger), run quest progress via World.recordKills(), roll rest chance, advance region every 5 turns. Maintains 80-entry ring buffer of typed events (encounter, combat, quest, rest, travel, death). snapshot getter serialises full state for SSE broadcast.
  • server/index.js — Express server on port 3001. Three routes: GET /api/map (static tile map JSON), GET /api/state (snapshot), GET /api/events (SSE stream). All SSE clients registered in a Set; each tick event broadcasts data: <JSON>\n\n to all subscribers. Map generated once at startup.

Key Technical Decisions:

  • SSE over WebSockets: no extra dependency, works with EventSource API natively in browsers, sufficient for one-way server→client state push.
  • SimEngine extends EventEmitter rather than holding a callback list — clean integration with Node's event model.
  • #runCombat infers outcomes from HP delta instead of intercepting Logger — preserves existing Logger behaviour and avoids coupling server layer to presentation code.
  • heroTilePosition uses (seed * prime + offset) % zone_width for deterministic but varied hero movement across the map without storing extra state.

Bug Fixed: package.json kept getting silently truncated by the file write tool (JSON became invalid). Switched to bash heredoc writes for all JSON/long files; confirmed with Python JSON parser before each server start.


Frontend Developer / Designer

Contribution: Designed and built the complete React frontend.

Files Created:

  • client/src/index.css — Dark fantasy theme. CSS variables: --bg-dark #0d0d0f, --gold #c9a84c, --gold-light #f0cc6e, --green #4caf72, --red #c94c4c, --blue #4c84c9, --purple #9c6dc9. Georgia serif body font.
  • client/src/App.jsx — Root component. Fetches /api/map once on mount. Subscribes to /api/events SSE stream via EventSource. Layout: top bar (title + live indicator), left column (map), right column (hero panel + event log). Green/red live indicator dot shows connection state.
  • client/src/components/WorldMap.jsx — Canvas renderer (24×24 tiles, 22px each = 528×528px). Eight tile colors with subtle top-edge highlight for depth. Semi-transparent grid lines. Region zone labels at map edges (THORNWOOD, MOUNTAINS, BLACKMIRE, CRIMSON WASTES). Building name labels (Inn, Smith, Temple, Market, Square). Hero indicator: radial glow + gold filled circle + ⚔ icon. Smooth position updates with each SSE tick.
  • client/src/components/HeroPanel.jsx — Hero status panel. Shows name/class/level badge, region + turn, HP bar (color-coded: green/amber/red), XP bar (blue), stat grid (ATK/DEF/DEX/Gold/Quests), active quest with purple progress bar.
  • client/src/components/EventLog.jsx — Scrolling chronicle. Color-coded by event type with emoji icons. Auto-scrolls to bottom on new events. 80-entry ring buffer visible.
  • client/vite.config.js — Proxy /api/* to http://localhost:3001 for dev server, eliminating CORS issues during development.

Design Decisions:

  • Dark fantasy palette chosen to match the game's tone — not a generic dashboard look.
  • Canvas over CSS grid for the map: gives pixel-level control for future sprite rendering (the map canvas is already set up as the foundation for sprite layers).
  • Region zone labels on map edges orient the player without cluttering the town center.
  • imageRendering: pixelated on canvas prepares for sprite scaling when pixel art assets are added.

Tester / QA

Contribution: Expanded test suite and caught two critical bugs during the session.

Files Created:

  • server/TownMap.test.js — 10 tests covering: 24x24 grid dimensions, tree borders on all four corners, market tile at center, path tiles on main horizontal and vertical crosses, building tile at Inn location, water feature presence, heroTilePosition within map bounds, resting hero in town zone, active hero in correct region zone, deterministic output for same seed.

Bugs Caught:

  1. Field cells overwriting main paths[11,5] and [12,5] were in the field decoration list, sitting exactly on the horizontal path. Test has path tiles on the main cross caught this. Fixed by moving field cells off the path lines and adding a guard (if grid[y][x] === GRASS) before placing field tiles.
  2. package.json truncation — The file write tool silently truncated package.json at line 14, producing invalid JSON and the ERR_INVALID_PACKAGE_CONFIG error. Caught by running python3 -c "json.load(...)" after every write. Workaround: bash heredoc for all writes to JSON and long JS files.
  3. Vitest .bin wrapper incompatible with Node 22node_modules/.bin/vitest shebang failed. Fixed by invoking node_modules/vitest/vitest.mjs directly in the test script.

Results: 47/47 tests passing across 5 test files. Server boots and serves all three endpoints. SSE stream emits valid JSON snapshots every 3.5 seconds.

Risk Log:

  • The Write tool truncates files silently at ~100 lines — all future long file writes must use bash heredoc.
  • SimEngine and App.jsx were written via the Write tool and should be audited line-count vs expected before the next session.

What Was Built

  • server/TownMap.js: 24x24 tile map with town layout and region-aware hero positioning
  • server/SimEngine.js: async tick-based simulation engine with SSE event broadcasting
  • server/index.js: Express server with /api/map, /api/state, /api/events endpoints
  • client/src/App.jsx: React root with SSE subscription and two-column layout
  • client/src/components/WorldMap.jsx: Canvas tile renderer with hero indicator
  • client/src/components/HeroPanel.jsx: Hero status panel with progress bars
  • client/src/components/EventLog.jsx: Scrolling color-coded chronicle
  • 10 new TownMap tests (47 total, all passing)

Verified: Server starts on port 3001. /api/state returns valid hero, region, heroPosition, activeQuest, and events. SSE stream broadcasts updates every 3.5 seconds. Vite dev server proxies /api/* correctly. All 47 tests pass.


Tomorrow (Day 5 Plan)

  • TypeScript migration: convert src/ entities and server files to .ts
  • PostgreSQL setup: schema for heroes, events, quest_log tables
  • PG-BOSS worker: replace setInterval in SimEngine with a pg-boss job that fires every minute
  • Multi-hero support: run 3–5 heroes simultaneously, each visible as a different color dot on the map
  • Town name + world map stub: named town (e.g. "Ashenvale"), world map overlay with region labels