canvas: harden org-map against cyclic parent chains and mount ErrorBoundary #2896
Reference in New Issue
Block a user
Delete Branch "fix/2601-org-map-resilience-hardening"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #2601 (mechanism-2 follow-up).
P0 hardening:
canvas-topology.ts:sortParentsBeforeChildrennow detects cyclic parent chains and bails by returning the input unchanged instead of hanging.buildNodesAndEdgesthrowsTopologyCycleErroron a cycle.canvas.ts:hydratecatchesTopologyCycleErrorand surfaces a retryablehydrationErrorwhile preserving the existing node tree.isDescendant,absOf, anddepthOfnow use ancestor seen-sets so drag/arrange/nest actions cannot wedge on corrupt data.P1 hardening:
ErrorBoundaryaroundAuthGatechildren inlayout.tsxso a render crash degrades to a reloadable fallback instead of a blank screen.Verification:
npm run test: 3489 passed.npm run lint: 0 errors (pre-existing warnings only).npx tsc --noEmit: clean on changed files.Ready for 2-genuine review. Do not self-merge.
5-axis review — APPROVE. head
09f4400(#2601 mechanism-2 follow-up)visiting(grey) detects back-edges,visited(black) memoizes.sortParentsBeforeChildrenfails closed (returns input unchanged);buildNodesAndEdgesthrowsTopologyCycleErrorup tohydrate, which catches it. The iterative walks (absOf/depthOf/isDescendant) all gain aseenset and break/return on revisit.isDescendantcorrectly checks=== ancestorIdbefore the cycle-guard, so a legitimate ancestor inside a cycle is still found. Tests cover each path (incl. the load-bearinghydrate→hydrationError+ nodes-preserved assertion).ErrorBoundaryaroundAuthGatedegrades a render crash to a reloadable fallback (DOM-level test added via jsdom + RTL). Cycle data no longer hangs drag/arrange/nest.parent_id(from DB corruption or a crafted value) previously hung the canvas — a client-side DoS/hang. This closes it. No new input/auth surface.TopologyCycleError, clear fail-closed comments.Non-blocking notes (none gate this PR):
hydratesuccess path never clearshydrationError— it's only ever set (line ~982 catch) and initialized null; no success-path reset. After in-place recovery (e.g. a WS-driven re-hydrate once the cycle is fixed server-side), the error banner sticks until a full page reload, so the stated "retryable" recovery is incomplete. Fail-safe today (the reload button works), but recommendset({ nodes, edges, hydrationError: null })on the success branch (and/or clear at hydrate entry). This is the one I'd most like addressed.hydratecatch maps any non-TopologyCycleErrorto a generic "corrupt topology" message and returns, swallowing the stack. Aconsole.error(err)before thesetwould keep unrelatedbuildNodesAndEdgesbugs observable in dev.absOf/depthOfreturn a partial value on cyclic data (break-on-revisit). Fine as defense-in-depth sincehydratefail-closes cycles at load — just noting the degraded value is intentional.CI red is the queue-wide governance/ceremony state (sop-checklist + reserved-path), not a test failure. Canvas-only, well-tested (author reports 3489 passing). Approving on merits.