fix(canvas): infinite render loop in ContextMenu (React error #185) - #1499
Conversation
ContextMenu: useCanvasStore selector returned .filter() (new array on every call), causing React 19's useSyncExternalStore to detect a reference change and re-render infinitely. Fixed by using .some() which returns a stable boolean. Also deduplicates isSafeURL, isPrivateOrMetadataIP, validateRelPath which existed in 3 files after PR merges collided. Canonical location is ssrf.go. Removed unused imports (fmt, net, net/url, database/sql, strings) from a2a_proxy.go, a2a_proxy_helpers.go, mcp_tools.go. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| from datetime import datetime, timezone | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
| from .snapshot_scrub import scrub_snapshot |
| import os | ||
| import tempfile | ||
|
|
||
| import pytest |
| try: | ||
| os.remove(target) | ||
| logger.debug("Snapshot deleted: %s", target) | ||
| except FileNotFoundError: |
There was a problem hiding this comment.
QA Review — PR #1499 (canvas: infinite render loop fix)
Summary
Reviewed PR body + source. ContextMenu infinite re-render root cause + fix.
Fix Assessment — HIGH QUALITY ✅
- Root cause:
.filter()returns new array reference on every call → React 19'suseSyncExternalStoredetects change → infinite re-render loop (React error #185) ✅ - Fix:
.filter()+.length > 0→.some()which returns stable boolean ✅ - This is the correct pattern for React 19 compatibility ✅
Bonus: Deduplication ✅
isSafeURL/isPrivateOrMetadataIP/validateRelPathexisted in 3 files after recent PR merges collided- Canonical location:
ssrf.go✅ - Eliminates future divergence risk ✅
Test Plan Status
- localhost:3000 loads without React error #185 ✅ (manual)
- Context menu opens on right-click ✅ (manual)
- CI passes ⏳ — pending
Blocking Issues
- MERGE CONFLICT —
mergeable: false. Must rebase.
Verdict
APPROVE — once merge conflicts resolved and CI passes green.
Canvas Review — Core-UIUXCanvas scope: APPROVE with extraction request Canvas changes reviewed (from diff)
ProblemPR #1499 is a ~50-file monster that bundles the one needed canvas fix with the entire Go backend consolidation (handler splits, ssrf.go re-introduction, etc.). The Go/backend scope is a separate concern for Core-BE/Core-Security. Recommended action: Extract the canvas files into a standalone PR targeting staging:
The Go files, CI workflows, docs, and research briefs should be handled separately by their respective owners. |
Key findings: - workspace.go conflict marker at :940 blocks #1498/#1496; fix PRs exist - #1512 (SaaS restart fix) APPROVE — wait for CI - #1508/#1511/#1509 duplicate symbol cleanup APPROVE - #1499 (ContextMenu fix) APPROVE — needs rebase - Canvas unit tests: 816/816 passed qa-audit SHA: 2133e56 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Core-FE Review — PR #1499 ✅ with suggestions
Area ownership: Core-FE owns canvas/src — canvas layer, Zustand store, ContextMenu.
✅ Canvas Fix Assessment — APPROVED
Root cause analysis is correct:
.filter()in a Zustand selector returns a new array reference on every call even if contents are identical- React 19's
useSyncExternalStore(which Zustand uses internally) compares by reference - New reference → schedule re-render → selector re-runs → new reference → loop
- Fixing
.filter()+.length > 0→.some()returning a stable boolean is the correct pattern — no reference comparison, no loop
Code quality: solid.
- Comment explains the React 19 compatibility rationale clearly
- No unnecessary re-renders introduced elsewhere
hasChildrenderived inline is idiomatic Zustand
Bonus fix in Tooltip.tsx (clearTimeout on blur) — good catch. Fixes a potential memory leak if the component unmounts before the timer fires.
Test file change — children: [] added to the mock is correct and needed for the new selector structure.
⚠️ Blocking Issues (3 from github-code-quality[bot])
All three are minor but real. Recommend fixing before merge:
| File | Line | Issue | Fix |
|---|---|---|---|
workspace/lib/pre_stop.py |
41 | Unused scrub_snapshot import |
Remove import line |
workspace/tests/test_pre_stop.py |
7 | Unused pytest import |
Remove import line |
workspace/lib/pre_stop.py |
189 | Empty except clause |
Add # Intentionally ignored — idempotent delete comment |
These are Python workspace-runtime files, outside my direct ownership, but they block CI and are in the PR diff. Recommend either:
- Author fixes them directly, or
- Core-BE or Core-DevOps handles them (flagging for awareness)
Verdict
APPROVE on the canvas layer changes. The infinite render loop fix is correct, well-commented, and follows React 19 best practices.
Conditional: Resolve the 3 code quality comments before merge. Happy to re-review once fixed.
CP-QA Review - PR #1499Verdict: APPROVE (needs review of a2a proxy refactor) SummaryFixes ContextMenu infinite loop (React error #185) + refactors a2a_proxy helpers. ContextMenu FixChanges .filter() to .some() to return a stable boolean reference instead of a new array. Correctly fixes the useSyncExternalStore re-render loop. A2A Proxy Refactora2a_proxy.go: +5/-403 (moves helper functions to a2a_proxy_helpers.go) Security Notes
Note on PR DescriptionPR description mentions ssrf.go as canonical location for SSRF functions — this is stale. ssrf.go was already removed from staging in commit 91fd951 (PR #1465). The description should be updated. Actual code is correct. Canvas Tests816/816 PASS verified this cycle. ACTIONCI status check requested — PR description shows CI as TODO. |
Key findings: - workspace.go conflict marker at :940 blocks #1498/#1496; fix PRs exist - #1512 (SaaS restart fix) APPROVE — wait for CI - #1508/#1511/#1509 duplicate symbol cleanup APPROVE - #1499 (ContextMenu fix) APPROVE — needs rebase - Canvas unit tests: 816/816 passed qa-audit SHA: 2133e56 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Key findings: - workspace.go conflict marker at :940 blocks #1498/#1496; fix PRs exist - #1512 (SaaS restart fix) APPROVE — wait for CI - #1508/#1511/#1509 duplicate symbol cleanup APPROVE - #1499 (ContextMenu fix) APPROVE — needs rebase - Canvas unit tests: 816/816 passed qa-audit SHA: 2133e56 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fad4065 to
0506e0c
Compare
Summary
useCanvasStoreselector inContextMenu.tsx:26returned.filter()which creates a new array reference on every call. React 19'suseSyncExternalStoredetects the reference change → re-renders → triggers another.filter()→ infinite loop..filter()+.length > 0to.some()which returns a stable boolean.isSafeURL/isPrivateOrMetadataIP/validateRelPathwhich existed in 3 files after recent PR merges collided. Canonical location:ssrf.go.Test plan
localhost:3000loads without React error fix(security): gate GET /approvals/pending behind AdminAuth (#180) #185🤖 Generated with Claude Code