← Back to Dev Blog

Daily Scrum Log — 2026-05-22 (Day 8) · kindred_bonds

Summary

Villagers now remember each other. A familiarity counter tracks how many times each pair has chatted; after 5 conversations they become Acquaintances, after 15 they become Friends. Friends draw from a warmer, more personal flavor pool — and a 🤝 milestone event fires in the chronicle the moment a friendship is born. On the canvas, thin gold lines trace the bonds between friends, making the social graph visible at a glance. The full test suite grew by 12 tests to 263 passing.


Project Manager

Contribution: Sequenced Day 8 work and led the scrum discussion.

Key Decisions:

  • Chose relationship tracking over the other Day 8 candidates (households, A*, second town). Households are the bigger payoff — but they require a "friend" concept to feel earned. Laying the friendship foundation now means households on Day 9 will feel like a natural next step rather than an arbitrary assignment.
  • Kept the three-tier model (Stranger / Acquaintance / Friend) simple and linear. No decay, no negative states yet — those can come when we have conflict mechanics. The goal today is social depth, not social drama.
  • Capped the feature at: familiarity counter + friend flavors + milestone event + canvas lines. No persistence of the familiarity counter across server restarts (that's a Day 9 extension — it pairs naturally with household persistence).
  • Confirmed the test suite was green before and after. The broken serialize() call in the old tests (returning an array when it now returns an object) was caught immediately and fixed.

Reasoning: The simulation already has named villagers who walk schedules and exchange flavor chats. The next thing a player would notice missing is continuity — a sense that these people know each other. Friendship lines on the map and milestone log entries are the smallest atomic step toward "society has memory."


World Designer

Contribution: Designed the relationship tier thresholds, friend flavor lines, and canvas visual treatment.

Key Decisions:

  • Thresholds: 5 chats = Acquaintance, 15 = Friend. With PAIR_COOLDOWN_HOURS=4 and ~14 villagers, a heavily-socializing pair can realistically reach Acquaintance in 2–3 in-game days and Friend in 5–7. Long enough to feel earned, short enough to appear in a session.
  • 8 Friend flavor lines that reference shared history rather than profession. Examples: "{b} confides in {a} about a strange dream they had." and "{a} and {b} finish each other's sentences." These are deliberately warmer and more personal than the profession templates.
  • Canvas friendship line: rgba(255, 215, 0, 0.22) — gold at 22% opacity. Sits below villager dots (drawn first) so it reads as background social fabric rather than a foreground indicator. Thin (1.5px) so it doesn't clutter a dense map.
  • Acquaintance tier gets no special visual yet — the distinction between Stranger and Acquaintance is felt through the event log (pair chats more often) rather than seen on the map. Keeps the canvas readable.
  • The milestone event text: "[Name] and [Name] have become friends!" — simple and warm, logged as type friendship with 🤝 icon in amber gold.

Backend Developer

Contribution: Extended SocialSystem.js with familiarity tracking, tier logic, milestone emission, and updated SimEngine.js for the new serialize shape.

Files Modified:

  • server/SocialSystem.js — Added #familiarity Map (pairKey → chat count); #getTier(key) returns 'stranger'|'acquaintance'|'friend'; tick() now calls #incrementFamiliarity before rendering the chat line, checks for tier transition, and attaches milestone: 'friend' to the chat object on first crossing; serialize() now returns { bubbles, relationships } instead of a plain array; getRelationship(a, b) public accessor for tests; #buildRelationships() emits { aId, bId, tier, count } entries; new exports: ACQUAINTANCE_THRESHOLD, FRIEND_THRESHOLD, FRIEND_FLAVORS, GENERIC_FLAVORS.
  • server/SimEngine.jssnapshot getter calls this.#social.serialize() once into socialData (avoids double-serialize); exposes chats: socialData.bubbles and new relationships: socialData.relationships; #tick() logs a friendship event when c.milestone === 'friend'.

Technical note: renderChat now receives the resolved tier rather than the raw villager objects so the Friend pool can be selected after the familiarity increment for that tick (post-increment tier, not pre-). This ensures the very first Friend-tier chat actually uses FRIEND_FLAVORS.

File write safety: Both SocialSystem.js and SimEngine.js were written via bash heredoc + node --check verification after the Edit tool truncated them mid-file on the first attempt. This is the same mitigation used in Day 5 and Day 7. Adding a standing note: always use heredoc for files > ~150 lines.


Frontend Developer / Designer

Contribution: Wired relationships into the canvas and added friendship event type styling.

Files Modified:

  • client/src/components/WorldMap.jsx — Added relationships=[] prop; new drawing pass between tile rendering and villager dots that indexes villagers by ID (Map) and draws a rgba(255,215,0,0.22) line between each Friend-tier pair; relationships added to the useEffect dependency array.
  • client/src/components/EventLog.jsx — Added friendship: '#e8b84b' to TYPE_COLORS and friendship: '🤝' to TYPE_ICONS. Warm amber color reads as celebratory without clashing with the gold combat/quest entries.
  • client/src/App.jsx — Passes relationships={state?.relationships ?? []} to WorldMap.

Design decisions:

  • Lines are drawn before villager dots — they sit under the sprites and read as a "web" of connections rather than overlay annotations.
  • No line for Acquaintance tier. Lines only appear at Friend so the visual stays meaningful and uncluttered even with 14 villagers.
  • The amber #e8b84b for friendship events is slightly more saturated than the gold (#f0cc6e) used for hero stats, giving it a distinct warmth without introducing a new hue.

Tester / QA

Contribution: Rewrote server/SocialSystem.test.js to cover the new API and relationship tiers. Fixed pre-existing serialize() call sites that expected a plain array.

Root cause of initial failures: The old tests called sys.serialize() and expected a plain array. Now that serialize() returns { bubbles, relationships }, those 3 tests were accessing .length on an object (always undefined) or indexing index 0 on an object (undefined). Fixed by updating all call sites to .bubbles or .relationships.

Additional failure cause: The chatNTimes helper originally used 8 + i * PAIR_COOLDOWN_HOURS as the hour, which produced hours 8, 12, 16, 20, 24, 28... — landing in night (phase='night', tick returns []) at i=3. Fixed with safeHour(n) = [8,12,16][n%3] which cycles through daytime hours. The wrap-safe cooldown condition (hour < last) correctly treats 8 following 16 as a new valid chat opportunity.

Test Coverage (12 new/updated tests, 263 total):

  • Fresh system has no relationships.
  • Pair is Stranger before ACQUAINTANCE_THRESHOLD chats.
  • Pair becomes Acquaintance at exactly threshold.
  • Pair becomes Friend at exactly FRIEND_THRESHOLD.
  • milestone:'friend' fires on the crossing tick.
  • Milestone does NOT re-fire on subsequent chats.
  • Friend-tier chat uses FRIEND_FLAVORS (deterministic index verified).
  • serialize() returns { bubbles, relationships } shape.
  • Bubbles contain correct midpoint and text.
  • Bubbles age out after TTL.
  • relationships array contains correct { aId, bId, tier, count }.
  • Fresh system relationships array is empty.

Results: npm test263 / 263 passing (was 87 unaffected by worktree copies; direct count +12 new SocialSystem tests).


What Was Built

  • #familiarity Map in SocialSystem: chat-count per pair, tier transitions (Stranger → Acquaintance → Friend)
  • milestone:'friend' field on chat objects, emitted once at Friend threshold crossing
  • friendship log event in SimEngine (🤝 chronicle entry, amber color)
  • serialize() now returns { bubbles, relationships } — relationships exposed to client
  • FRIEND_FLAVORS pool: 8 personal, history-referencing chat lines for Friend-tier pairs
  • Canvas friendship lines: gold semi-transparent lines between Friend-tier villagers
  • friendship event type in EventLog: 🤝 icon, amber #e8b84b color
  • getRelationship(a, b) public method for testing
  • 12 new tests; suite green at 263/263

Tomorrow (Day 9 Candidates)

  • Villager households / families — now that friendship exists, pairing villagers as couples feels earned. Assign shared home tiles; emit marriage chronicle events. First step toward generational simulation.
  • Persist familiarity across restarts — extend PersistenceManager to save/restore the #familiarity map so friendships survive server restarts.
  • A* pathfinding — replace Manhattan stepping so villagers route around buildings; prerequisite for building roofs over villagers.
  • Second town — multi-settlement map and world-overview switcher.