← Back to Dev Blog

Daily Scrum Log -- 2026-05-29 (Post Day 22) - iron_warden

Summary

This session focused on infrastructure hardening and hero movement fidelity rather than new features. Four file corruption incidents were discovered and repaired (SimEngine.js, Villager.js, App.jsx, WorldMap.jsx), a 300-line file size limit was codified to prevent future corruption, App.jsx was decomposed into four focused modules, and the hero was upgraded from seed-jitter teleportation to full A* pathfinding with per-zone random wandering. 885 tests passing across 77 test files — zero failures.


Project Manager

Contribution: Triaged session priorities; corruption repair was prerequisite to everything else.

Key Decisions:

  • Treated corruption repair as blocking — the server was crashing on startup due to orphaned code fragments outside class braces in SimEngine.js and Villager.js; WorldMap.jsx and App.jsx had silent truncations causing Vite build failures. None of the feature work was viable until these were fixed.
  • Codified the 300-line rule in CLAUDE.md with IMPORTANT: ALL FILES MUST BE BROKEN DOWN INTO SMALLER FILES IF THEY ARE OVER 300 LINES OF CODE. — this is the root cause mitigation. Agents writing large files in one shot are the proximate cause of every corruption incident observed.
  • Approved SimEngine.js decomposition (516 → 365 lines across three phases) and App.jsx decomposition (426 → 110 lines) as direct applications of the new rule.
  • Hero A* navigation was prioritized over new features because visual fidelity is foundational — players watching the hero teleport around undermines trust in the simulation.

Reasoning: Corruption is not random. It correlates strongly with file size at write time. The 300-line ceiling is conservative (the rule says 200–400 lines typical) but the IMPORTANT flag signals to agents that this is non-negotiable, not a guideline. Scrum notes should always verify file line counts before declaring work complete.


World Designer

Contribution: Specified hero movement behavior and zone target locations.

Hero Zone Targets (initial fixed, then replaced with random wander):

  • Resting in town: market centre {11, 11} — the natural gathering point for the hero between quests.
  • Thornwood Forest: NW quadrant grass tile {6, 5} — outside the NW building (y:6–8, x:6–8).
  • Stonehold Mountains: NE quadrant {18, 5} — above the NE building.
  • Blackmire Swamp: SW quadrant {6, 18} — below the SW building.
  • Crimson Wastes: SE quadrant {18, 18} — open grass in SE zone.

Random Wander Upgrade: Fixed targets were replaced with random walkable-tile sampling within each zone's bounding box (REGION_ZONES). This makes the hero feel alive rather than magnetized to one spot. The hero picks a new random destination each time they arrive, creating continuous wandering movement similar to villagers.

Visual Tweaks: Reduced night/dawn/dusk overlay from full darkness to 50% opacity — the map was unreadable at night. Lantern glow reduced from 0.55 to 0.35 alpha to match. Hero helmet icon color changed from gold (#f0cc6e) to black (#000000) for contrast against the market square tiles.


Backend Developer

Contribution: Repaired corrupted server files; refactored SimEngine.js; implemented hero A* navigation.

Corruption Repairs:

  • server/SimEngine.js: Class closed prematurely at line 516; lines 517–554 were an orphaned duplicate tail (espan, save.lifespan); through a second copy of #maybeSave, #log, #emit). Truncated to the valid class end using head -516.
  • server/Villager.js: Same pattern — valid through line 176, orphaned return NameGenerator.pickSurname(flavor); } } tail appended outside the module. Truncated to line 176.

SimEngine.js Decomposition (516 → 365 lines):

  • server/SimConstants.js (80 lines): HERO_CLASSES, TICK_MS, TICKS_PER_HOUR, HERO_VILLAGER_MESSAGES, BUILDING_VISIT_EFFECTS, pickHeroName().
  • server/CombatRunner.js (42 lines): runCombat(hero, monsters, weatherAtkPenalty) — weather-adjusted combat + loot/log assembly.
  • server/SimSnapshot.js (61 lines): buildSnapshot() — pure state serialization for the client tick payload.

Hero A Navigation:*

  • Added #heroPos, #heroPath, #heroPathTarget, #heroZoneKey private fields.
  • Added HERO_BLOCKED set: BUILDING, TREE, WATER, WALL, COTTAGE — matches Pathfinding.js blocked set plus cottages.
  • #heroZone() returns the REGION_ZONES entry for the current resting/region state.
  • #heroCurrentZoneKey() returns a string key for zone-change detection.
  • #pickHeroWanderTarget(zone) samples up to 30 random tiles within zone bounds, returns first walkable one.
  • #stepHero(): detects zone changes (clears path), picks new random target when path exhausts, advances one tile per sub-tick via astar().
  • Called on every sub-tick alongside villager movement, gated to heroTown === 'Thornhaven' (world-map travel is handled separately).
  • #heroPos saved to and restored from save.sim.heroPos.

SimSnapshot.js: Removed heroTilePosition import and call; uses heroPos param directly with { x: 11, y: 11 } fallback.


Frontend Developer

Contribution: Repaired truncated client files; decomposed App.jsx.

Corruption Repairs:

  • client/src/App.jsx: Day 22 commit dropped the closing }, for the main style plus the mapCol, mapLabel, sideCol style blocks and the object's };. All three keys were still referenced in JSX. Restored from Day 21 git history.
  • client/src/components/WorldMap.jsx: Day 22 write truncated 114 lines — chat bubble tail drawing, grave markers, day/night overlay, seasonal tint, weather overlay, the world-map useEffect, and the full JSX return were all missing. Restored from Day 21 git history.

App.jsx Decomposition (426 → 110 lines):

  • client/src/utils/simFormatters.js (36 lines): weatherIcon, weatherLabel, festivalIcon, festivalLabel, phaseColor — pure functions with no component dependencies.
  • client/src/styles/appStyles.js (123 lines): mobile and layout style objects extracted verbatim.
  • client/src/components/TopBar.jsx (63 lines): Desktop navigation bar — title, clock, weather badge, festival badge, season badge, live indicator, Dev Chronicles link.
  • client/src/components/MobileLayout.jsx (96 lines): Full mobile layout including TabBtn component and all four tab panes.
  • App.jsx retained: state hooks, SSE connection, resize listener, prop assembly, desktop layout shell.

Tester / QA

Contribution: Verified all corruption fixes pass syntax check; ran full suite after each change; confirmed Docker build produces a clean image.

Corruption Verification Method:

  • Server JS: node --check <file> — zero exit code confirms no syntax errors.
  • Client JSX: Vite build (npm run build) — if it compiles, the JSX is structurally valid.
  • Full Docker build (docker build --build-arg BERGY_NPM=...) used as the integration gate; each bug was verified by watching the specific build error resolve.

Build Failures Encountered and Fixed (in order):

  1. SimEngine.js:517 SyntaxError: Unexpected token ')' — orphaned tail outside class.
  2. Villager.js:177 SyntaxError: Illegal return statement — orphaned return outside function.
  3. App.jsx:402 Unexpected token (Vite) — truncated style object, missing closing };.
  4. WorldMap.jsx:460 Expected ')' but found EOF — 114-line truncation mid-ctx.stroke(.
  5. faBow not exported — agent-added icon doesn't exist in pro-solid v7.2.0; replaced with faBowArchery.
  6. @rolldown/binding-linux-x64-musl not found — Windows lock file missing Linux optional dep; fixed by deleting lock file inside Docker before npm install.

Final state: 885 tests / 77 files, all green. Docker image builds cleanly with only BERGY_NPM arg (Verdaccio now rewrites all FA tarball URLs after url_prefix was configured). No regressions observed in any existing system.