molecule-core/canvas
Hongming Wang 32fc77bad4 fix(canvas): A2ATopologyOverlay re-fetch storm hammering /activity → 429
Selector instability caused fetchAndUpdate to recreate on every Zustand
nodes[] mutation (status flips, position drags, peer-discovery writes,
heartbeats — typically ~5/sec). Each recreation invalidated the
useEffect deps so the 60s polling fan-out fired on every update,
hammering /workspaces/<id>/activity?type=delegation 5×N requests/sec
until the edge rate-limit returned 429. User-reported via browser
console showing infinite uE→ux→uE→ux render loop and 429s repeating
across every visible workspace ID.

Root cause:
  const nodes = useCanvasStore((s) => s.nodes);
  const visibleIds = useMemo(() => nodes.filter(...).map(...), [nodes]);
  // useMemo dep recreates on every store update, even when ID set unchanged

Fix: select a STABLE STRING KEY (sorted CSV of visible IDs) from
Zustand. The selector's shallow-equal short-circuit prevents re-renders
when the actual visible-ID set is unchanged, so visibleIds reference
stays stable, fetchAndUpdate keeps its identity, and the useEffect
only re-fires when the visible-ID-set genuinely changes.

Tests:
- New regression test "does not re-fetch when nodes[] reference
  changes but visible IDs are the same"
- Discipline-verified: pre-fix code emits 4 fetches (2 mount + 2
  re-fetch storm), post-fix emits exactly 2
- Companion test "re-fetches when the visible ID set actually changes"
  pins the desired behavior so future "stabilization" doesn't suppress
  legitimate updates

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:39:36 -07:00
..
e2e canvas/e2e: surface admin-orgs row + workspace body on failure 2026-05-03 14:01:50 -07:00
public
src fix(canvas): A2ATopologyOverlay re-fetch storm hammering /activity → 429 2026-05-03 23:39:36 -07:00
.env.example
.gitignore
components.json fix(canvas): restore text-white on saturated buttons + close zinc gaps 2026-05-03 02:04:20 -07:00
Dockerfile
next.config.ts
package-lock.json fix(canvas): regenerate lockfile with cross-platform optional deps 2026-05-03 01:52:42 -07:00
package.json feat(canvas): warm-paper theme + Tailwind v4 migration 2026-05-03 01:43:55 -07:00
playwright.config.ts
playwright.staging.config.ts
postcss.config.js feat(canvas): warm-paper theme + Tailwind v4 migration 2026-05-03 01:43:55 -07:00
tsconfig.json
vitest.config.ts feat(canvas): vitest coverage instrumentation (#1815, no CI gate yet) 2026-04-26 23:44:07 -07:00