Conversation
… 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.
Contributor
🚫 This PR is missing its before/after evidenceEvery pull request here has to show its work. Screenshots or a short screen recording, before the change and after it.
How to fix it: edit the description, keep the 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 |
adikeshri
marked this pull request as draft
September 15, 2026 21:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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()inOfficeFloor.tsxre-runs on everyagentsarray change, which includes the pty parser's per-chunk status/action writes
— i.e. very often while any agent is active.
addCharacter()is async (itawaits 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:
claimSeat().Characterand added it to the scene.Only the last call to finish ever landed in
runtimes(the map key getsoverwritten); 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 whereverit 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'scollision 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 iscorrect once a
Characteris actually ticked. The defect is purely themissing re-entrancy guard around
addCharacter.Fix: added a
pendingAddsset so at most oneaddCharacter()call isever in flight per agent id, regardless of how often
syncAgentsfiresduring the async
getFrames()gap.Closes #524
Type of change
Evidence
Before
After
How I tested it
syncAgents→addCharacter→runtimescontrol flow andconfirmed there was no re-entrancy guard around the async gap.
office.tmj's collision layer and ran BFS from the entrance toevery named desk spawn point — all reachable — then traced
Character.moveTo/walkToDeskAndSitto confirm the walk-to-deskstate machine is correct once a
Characteris actually ticked.npm run typecheck— passes clean.npm run test:focused— 827/831 pass; the 4 failures are unrelated tothis change (native
node-pty/Electron binary not built in myenvironment) and don't touch
OfficeFloor.tsx,Character.ts, or theseat-assignment path.
npm run build— succeeds clean.Credit (optional)
Discord:
X:
Checklist
npm run typecheckpasses.npm run test:focusedpasses.npm run buildsucceeds.output, commented-out code, or unrelated formatting churn in it.
DESIGN.md/tokens.ts— no ad-hoc colors,spacing, or fonts.
ATTRIBUTION.md.