Skip to content

[C5][C42] react/engine: mounting or unmounting a box corrupts the simulation — registration is never reconciled #12

Description

@GFier

From the 2026-07-06 codebase audit (docs/audits/codebase-audit-2026-07-06.md, in-repo). Finding IDs are stable — cite them in fixes.

Severity: Critical (C5) + Medium (C42) · Status: CONFIRMED (independently re-verified)

Problem

Engine bodies have no identity beyond their array index, and the engine's only population path is the destructive initialCondition (C42, elastica.ts:126-205). The DOM layer maps Map-insertion-order → index and never reconciles: addBox/removeBox only mutate the boxesRefs Map, and the init effect deps are [elastica, sectionRect] (dom-elastica.tsx:159-184) — registration changes never re-run it.

Failure scenarios

  • Remove: 5 boxes running; box #0 unmounts. Next frame boxes.length === 4 but positions[0..3] still hold the old bodies — every remaining element inherits its predecessor's position/velocity/angle (all visually teleport); box Bump next from 16.1.5 to 16.1.7 in /website in the npm_and_yarn group across 1 directory #4's state is orphaned.
  • Add: a 6th BoundaryBox mounts later. positions[5] is undefined → skipped by every engine guard → the new element never moves and never collides until an unrelated resize re-inits. A README-style update callback without guards (velocity[1] += ..., readme.md:183) throws TypeError every frame.
  • Remount/reorder: React remounts a middle child (key change — and the README's canonical example uses key={i}) → Map delete+set moves it to the end → index-shift corruption for everything after it.

Canvas mode "fixes" this by nuking the world on every register/unregister (C14, separate issue) — the two modes disagree on the same contract.

Direction

Short-term: re-run init on registration-set change (matches canvas semantics, still teleports — document it) and say in docs that children must be a static set. Real fix (audit design tension 2): engine-level addBody()/removeBody() returning stable handles with an internal free list (SoA stays internal), React maps child → handle. C5, C14, C15, C26 and C42 all collapse into that one abstraction. Audit open question 4: are dynamic element lists in scope for v1?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    auditFinding from a codebase auditbugSomething isn't workingcriticalCritical severityenginepackages/enginereactpackages/react

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions