Skip to content

fix(office): stop duplicate addCharacter calls from starving the seat… - #534

Draft
adikeshri wants to merge 1 commit into
chaitanyagiri:mainfrom
adikeshri:fix/office-desk-seating-race
Draft

adikeshri wants to merge 1 commit into
chaitanyagiri:mainfrom
adikeshri:fix/office-desk-seating-race

Conversation

@adikeshri

Copy link
Copy Markdown

What & why

Working agents on the office floor stood wherever they happened to be —
doorways, next to furniture, on top of desk monitors — instead of walking to
and sitting at an empty desk. Every non-god agent was affected; only the
CEO/god agent ("Michael") reliably sat down.

Root cause: syncAgents() in OfficeFloor.tsx re-runs on every agents
array change, which includes the pty parser's per-chunk status/action writes
— i.e. very often while any agent is active. addCharacter() is async (it
awaits sprite frame loading) but had no guard against being re-invoked for
the same agent id before its first call finished registering in runtimes.

Each duplicate call:

  • Claimed another seat from the finite seat pool via claimSeat().
  • Spawned its own Character and added it to the scene.

Only the last call to finish ever landed in runtimes (the map key gets
overwritten); every earlier duplicate was silently orphaned — never added to
runtimes, so the per-frame ticker (for (const rt of runtimes.values()) rt.character.update(dt)) never advanced it again. It froze exactly wherever
it was, typically still at the entrance, mid walk-to-desk — which is why
agents appeared stuck in doorways and furniture instead of at an empty desk.
Once the seat pool was drained by duplicate claims, subsequently-added agents
fell back to the entrance tile as their "desk" entirely.

The god agent's seat claim is idempotent (if (agent.isGod) { seatClaims.add(GOD_SEAT); return GOD_SEAT; })
— every duplicate call resolves to the same desk — so the same race never
produced a visible symptom for it.

I independently ruled out a map/pathfinding data issue: parsed office.tmj's
collision layer and confirmed every named desk spawn point is BFS-reachable
from the entrance on the static walkability grid, and traced
Character.moveTo / walkToDeskAndSit — the walk-to-desk state machine is
correct once a Character is actually ticked. The defect is purely the
missing re-entrancy guard around addCharacter.

Fix: added a pendingAdds set so at most one addCharacter() call is
ever in flight per agent id, regardless of how often syncAgents fires
during the async getFrames() gap.

Closes #524

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs
  • Build / CI

Evidence

Before

After

How I tested it

  • OS: macOS (Darwin)
  • Steps:
    1. Traced the syncAgentsaddCharacterruntimes control flow and
      confirmed there was no re-entrancy guard around the async gap.
    2. Parsed office.tmj's collision layer and ran BFS from the entrance to
      every named desk spawn point — all reachable — then traced
      Character.moveTo / walkToDeskAndSit to confirm the walk-to-desk
      state machine is correct once a Character is actually ticked.
    3. npm run typecheck — passes clean.
    4. npm run test:focused — 827/831 pass; the 4 failures are unrelated to
      this change (native node-pty/Electron binary not built in my
      environment) and don't touch OfficeFloor.tsx, Character.ts, or the
      seat-assignment path.
    5. npm run build — succeeds clean.

Credit (optional)

Discord:

X:

Checklist

  • Before and after evidence is attached above, under both headings.
  • npm run typecheck passes.
  • npm run test:focused passes.
  • npm run build succeeds.
  • This PR is one change. Unrelated fixes belong in their own PR.
  • I read the diff myself before opening this, and there is no debug
    output, commented-out code, or unrelated formatting churn in it.
  • Any new UI derives from DESIGN.md / tokens.ts — no ad-hoc colors,
    spacing, or fonts.
  • If I added art, it's my own or compatibly licensed, and listed in
    ATTRIBUTION.md.

… pool

syncAgents() re-runs on every agents-array change, including the pty
parser's per-chunk status/action writes, so it fires very often while
any agent is active. addCharacter() is async (it awaits sprite frame
loading) but had no guard against being re-invoked for the same agent
id before its first call finished registering in `runtimes`.

Each duplicate call claimed another desk from the finite seat pool and
spawned its own Character; only the last one to finish ever made it
into `runtimes`; earlier ones were silently orphaned and never ticked
again, freezing wherever they last stood (often still at the entrance,
mid walk-to-desk). Once the pool was drained, later agents fell back
to the entrance tile as their "desk" — explaining agents stuck in
doorways/furniture instead of walking to an empty desk (chaitanyagiri#524).

Add a pendingAdds set so at most one addCharacter() call is ever in
flight per agent id.
@github-actions

Copy link
Copy Markdown
Contributor

🚫 This PR is missing its before/after evidence

Every pull request here has to show its work. Screenshots or a short screen recording, before the change and after it.

  • Before — no image or video under that heading
  • After — no image or video under that heading

How to fix it: edit the description, keep the ### Before and ### After headings from the template, and drag an image or video under each. GitHub uploads it inline. This check re-runs the moment you save.

A bug fix with no visible surface still needs it: show the failing behaviour, then the same steps passing. A terminal recording is fine.

Genuinely nothing to show — a CI tweak, a typo, a dependency bump? A maintainer can apply the no-visual-change label. Please don't ask unless it truly has no observable effect.

📖 CONTRIBUTING.md → Evidence is mandatory

@adikeshri
adikeshri marked this pull request as draft September 15, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Working agents stop in the middle of the floor instead of walking to a desk

1 participant