← Back to Dev Blog

Daily Scrum Log — 2026-05-25 (Day 13) · guild_bloom

Summary

Day 13 closed the generational loop opened by Day 10's ChildSystem and extended by Day 12's LifespanSystem. Adult children (age >= 14) are now promoted into the workforce: they leave the child roster, receive a randomly chosen profession from a weighted pool, join NPCManager as full Villagers, and register with LifespanSystem so they will age, eventually become elders, and die like any other townsperson. Alongside promotion, household surnames were introduced: when a couple marries, their household is assigned a fantasy surname (e.g. "Ashwood", "Embervale") which propagates to all children born into that family. Children carry their surname into adulthood and it appears in all chronicle messages. SAVE_VERSION was bumped to 6. The test suite grew by 25 to 692 passing across 67 files.

The disk-truncation issue struck again on PersistenceManager.js, SimEngine.js, ChildSystem.test.js, and Villager.test.js — all four recovered via bash heredoc or Python-driven rewrite.


Project Manager

Contribution: Led Day 13 scrum, selected features, set scope.

Key Decisions:

  • Headlined adult children joining the workforce — the single most critical follow-on from Day 12. Elders are dying; nothing was replacing them. Today's change gives the town genuine generational continuity.
  • Paired with surname inheritance as a zero-cost companion feature. Surnames require no new systems — they ride the existing household/child graph — but they make every chronicle message richer: "Pip Ashwood joins the town as a Merchant!" is far more evocative than just "Pip joins."
  • Deferred Day 14 candidates: memorial panel + graveyard tiles, hero visits buildings, persist villager positions. All three remain healthy backlog items for tomorrow.

Reasoning: The simulation now has a complete life-cycle: born → child → adult with a profession → ages → elder → dies. Surnames make that history legible across generations.


World Designer

Contribution: Specified visual and narrative treatment for adult promotion and surnames.

Surname list: 25 fantasy surnames added to Villager.js as FAMILY_NAMES: Ashwood, Bramble, Coldwater, Duskmantle, Embervale, Frostholm, Graystone, Hearthwick, Ironbark, Jadehollow, Kettleford, Lyremore, Moonveil, Nighthollow, Oakenshield, Pinecrest, Quickthorn, Ravenmoore, Silverbrook, Thornwall, Underhill, Vexford, Whisperwind, Yelwick, Zinderfall. Each household gets the name at index householdId % 25 so assignment is deterministic but varied.

Chronicle messages:

  • Marriage: "Alice and Bob are wed! — House of Ashwood" — surname revealed on the wedding day, the moment it is assigned.
  • Birth: "Pip Ashwood is born to Alice & Bob!" — child already carries the name.
  • Coming of age (adult): "Pip Ashwood, raised by Alice & Bob, comes of age and takes up a trade!" then "Pip Ashwood joins the town as a Merchant!"

Profession weighting: The promotion pool is weighted toward high-turnover roles (Farmer x2, Merchant x2) so the town gradually restores the economic backbone as elders die, while still occasionally producing Guards, Priests, Smiths, and others.


Backend Developer

Contribution: Implemented all backend changes across 5 files.

Files modified:

  • server/Villager.js

    • Added FAMILY_NAMES array (25 names).
    • Added optional surname param to constructor (default '').
    • Added fullName getter: "FirstName Surname" or just "FirstName".
    • Added static pickSurname() utility.
    • serialize() now includes fullName and surname fields.
  • server/HouseholdSystem.js

    • Added #surnames = new Map() (householdId -> surname).
    • tick(): when a marriage fires, assigns FAMILY_NAMES[hhId % 25] to the household if none yet; includes surname in the returned marriage object so SimEngine can log it.
    • getSurname(householdId): new accessor for ChildSystem.
    • restore(): now accepts optional surnames array.
    • serialize(): includes surnames array.
  • server/ChildSystem.js

    • #checkConceptions(): reads householdSystem.getSurname(householdId) and stores surname on the pregnancy record.
    • #advancePregnancies(): propagates surname to the child record; birth message includes the surname if present.
    • #ageChildren(): at ADULT_AGE emits a comingofage event with promote: true, childId, childName, surname, householdId, homeTile so SimEngine can act on it. Also refreshes c.surname from the household if the child was born before the parents married.
    • promoteToAdult(childId): new public method — splices child out of #children, returns the record. Second call returns null (idempotent).
  • server/NPCManager.js

    • Added PROMOTION_POOL constant: weighted list of professions for child promotion.
    • addVillager(id, name, profession, homeTile, surname): creates and appends a new Villager; updates internal #fieldIdx/#patrolIdx counters so new Farmers/Guards get sensible work tiles.
    • get nextVillagerId(): max(villager ids) + 1.
    • static pickPromotion(): random draw from PROMOTION_POOL.
  • server/SimEngine.js

    • Child events loop now checks e.promote and calls #promoteChildToAdult(e).
    • #promoteChildToAdult({ childId, childName, surname, homeTile }): calls children.promoteToAdult(childId), then npcs.addVillager(...), then lifespan.register(newId, 14), and logs a comingofage event with the full name and new profession.
    • Marriage log updated to show surname: "Alice and Bob are wed! — House of Embervale".
  • server/PersistenceManager.js (SAVE_VERSION 6)

    • save(): adds social.surnames from households.serialize().surnames.
    • restoreSocial(): passes surnames to householdSystem.restore().

Frontend Developer / Designer

Contribution: No frontend changes needed today. The fullName and surname fields are now present in the villager SSE payload, ready for the client to display when a tooltip or panel is built. The new comingofage chronicle event type was already styled on Day 10 (gold-yellow, person icon) and renders correctly for adult promotions.


Tester / QA

Contribution: Wrote 3 new test files (25 tests). Verified suite after each truncation repair.

New test files:

  • server/ChildSystem.promote.test.js (9 tests): promote:true fires at ADULT_AGE, carries surname/homeTile/childId, promoteToAdult removes child, double-call is null, unknown id is null.
  • server/NPCManager.promote.test.js (8 tests): addVillager increments roster, sets id/name/profession/surname, serializes fullName, nextVillagerId tracks correctly, removeVillager still works, pickPromotion always returns a valid profession.
  • server/HouseholdSystem.surname.test.js (7 tests): No surname before marriage, surname assigned on marriage, marriage event carries surname, surname persists, serialize includes surnames array, restore round-trips surnames.

Existing tests patched:

  • server/ChildSystem.test.js: makeHouseholdStub stub extended with getSurname: () => surname (previously caused 12 test failures).
  • server/Villager.test.js: serialize expectation updated to include fullName and surname fields; added one new serialize test.

Truncation repairs (4 files): PersistenceManager.js, SimEngine.js, ChildSystem.test.js, Villager.test.js — all repaired via bash heredoc or Python-driven rewrite. Confirmed via tail and byte-level inspection before proceeding.

Results: npm test -> 692 / 692 passing across 67 files (was 668/667; +25 new tests, +1 Villager serialize test).


What Was Built

  • FAMILY_NAMES list + surname field on Villager; fullName getter.
  • HouseholdSystem: surname assignment at marriage; getSurname(); persist/restore.
  • ChildSystem: surname propagation at birth; late surname refresh; promoteToAdult(); promote:true event at ADULT_AGE.
  • NPCManager: addVillager(), nextVillagerId, pickPromotion(), PROMOTION_POOL.
  • SimEngine: #promoteChildToAdult() wired into child event loop; marriage log shows surname.
  • PersistenceManager: SAVE_VERSION 6; surnames persisted and restored.
  • 3 new test files (25 tests); 2 existing test files patched.

Tomorrow (Day 14 Candidates)

  • Memorial panel / graveyard tiles — surface snapshot.deceased in the UI as a "Town History" sidebar panel; optionally place a GRAVE tile cluster on the map once a few deaths accumulate.
  • Hero visits buildings — give #resting state real behaviour: Inn (heal more), Temple (stat buff), Smith (ATK bump), Merchant (sell loot).
  • Persist villager positions — small infra; persist pos per villager so the map picks up where it left off on reboot.
  • Second town — second settlement on the world map with its own population; hero can travel between them; trade caravans.