fix(builder): Auto-cleanup invalid/orphan edges during graph operations - #12113
fix(builder): Auto-cleanup invalid/orphan edges during graph operations#12113Otto-AGPT wants to merge 6 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughBackend now prunes invalid/orphan links during graph validation; frontend syncs its edge store to the backend's authoritative links after save and validates/inserts links only when their nodes exist. Changes
Sequence DiagramsequenceDiagram
actor User
participant Frontend
participant Backend
participant EdgeStore
User->>Frontend: Save graph (create/update)
Frontend->>Backend: Send graph payload (nodes, links)
Backend->>Backend: _validate_graph_structure()
Backend->>Backend: prune_invalid_links(graph)
Backend-->>Frontend: Return saved graph with authoritative links
Frontend->>EdgeStore: syncEdgesWithBackend(links)
EdgeStore->>EdgeStore: map links -> edges, filter invalid/orphan
EdgeStore-->>Frontend: Edge store updated
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔍 PR Overlap DetectionThis check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early. 🟢 Low Risk — File Overlap OnlyThese PRs touch the same files but different sections (click to expand)
Summary: 0 conflict(s), 0 medium risk, 3 low risk (out of 3 PRs with file overlap) Auto-generated on push. Ignores: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@autogpt_platform/backend/backend/data/graph.py`:
- Around line 870-922: The docstring for prune_invalid_links mentions pruning
links with invalid pin names but the function never checks pin names; either
remove that bullet or implement pin validation here: inside prune_invalid_links
(not in _validate_graph_structure) after obtaining source_block and sink_block,
verify that link.source_pin exists on source_block and link.sink_pin exists on
sink_block (use the block's pin/port descriptors or methods the codebase
provides for pin lookup), log a warning and skip the link if either pin is
missing, then append valid links as before; ensure warnings reference
link.source_id/sink_id and link.source_pin/link.sink_pin so diagnostics match
existing logs.
🧹 Nitpick comments (4)
autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts (1)
138-151: Consider thataddEdgepushes a history entry per link — bulk loads may flood the undo stack.Each call to
get().addEdge(...)inside the loop (Line 150) records an independent history snapshot viauseHistoryStore.getState().pushState(prevState). When loading a graph with many links, this produces one undo entry per edge, which is likely unintended.This is pre-existing behavior and not introduced by this PR, but the new validation logic in
addLinksmakes this a good time to note it. A batch approach (collect valid edges, thenupsertMany+ single history push) would be cleaner.autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts (1)
25-28: JSDoc comment could be trimmed.The coding guidelines state to avoid comments unless the code is very complex. The function name
syncEdgesWithBackendis already self-documenting. Consider removing or minimizing the JSDoc. As per coding guidelines: "Avoid comments at all times unless the code is very complex."autogpt_platform/backend/backend/data/graph.py (2)
898-912:get_block()instantiates a new block object per call — consider caching within this method.
get_block(block_id)callscls()each time (per the relevant snippet fromblocks/__init__.py). If multiple links share the same source/sink nodes, this creates redundant block instances. You could reuse thenode_mapto cache block lookups.♻️ Cache block lookups
node_map = {v.id: v for v in graph.nodes} + block_cache: dict[str, "AnyBlockSchema | None"] = {} original_count = len(graph.links) valid_links = [] for link in graph.links: source_node = node_map.get(link.source_id) sink_node = node_map.get(link.sink_id) # Skip if either node doesn't exist if not source_node or not sink_node: logger.warning(...) continue # Skip if source block doesn't exist - source_block = get_block(source_node.block_id) + if source_node.block_id not in block_cache: + block_cache[source_node.block_id] = get_block(source_node.block_id) + source_block = block_cache[source_node.block_id] if not source_block: logger.warning(...) continue # Skip if sink block doesn't exist - sink_block = get_block(sink_node.block_id) + if sink_node.block_id not in block_cache: + block_cache[sink_node.block_id] = get_block(sink_node.block_id) + sink_block = block_cache[sink_node.block_id] if not sink_block: logger.warning(...) continue
924-928: Validation now has a mutation side-effect — document this for callers.
_validate_graph_structureis a validation method, but it now mutatesgraph.linksviaprune_invalid_links. This is intentional per the PR objectives, but callers likefork_graph(Line 1536) might not expect validation to alter the graph. Consider adding a brief note in thevalidate_graphdocstring (Line 674) to make this side-effect explicit.📝 Suggested docstring update
def validate_graph( self, for_run: bool = False, nodes_input_masks: Optional["NodesInputMasks"] = None, ): """ Validate graph structure and raise `ValueError` on issues. For structured error reporting, use `validate_graph_get_errors`. + + Note: This method has side effects — it prunes invalid/orphan links + from the graph as part of structural validation. """
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
autogpt_platform/backend/backend/data/graph.pyautogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
🧰 Additional context used
📓 Path-based instructions (15)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Use Node.js 21+ with pnpm package manager for frontend development
Always run 'pnpm format' for formatting and linting code in frontend development
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/frontend/**/*.{tsx,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/frontend/**/*.{tsx,ts}: Use function declarations for components and handlers (not arrow functions) in React components
Only use arrow functions for small inline lambdas (map, filter, etc.) in React components
Use PascalCase for component names and camelCase with 'use' prefix for hook names in React
Use Tailwind CSS utilities only for styling in frontend components
Use design system components from 'src/components/' (atoms, molecules, organisms) in frontend development
Never use 'src/components/legacy/' in frontend code
Only use Phosphor Icons (@phosphor-icons/react) for icons in frontend components
Use generated API hooks from '@/app/api/generated/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/'
Use React Query for server state (via generated hooks) in frontend development
Default to client components ('use client') in Next.js; only use server components for SEO or extreme TTFB needs
Use '' component for rendering errors in frontend UI; use toast notifications for mutation errors; use 'Sentry.captureException()' for manual exceptions
Separate render logic from data/behavior in React components; keep comments minimal (code should be self-documenting)
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/frontend/**/*.{ts,tsx}: No barrel files or 'index.ts' re-exports in frontend code
Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)
autogpt_platform/frontend/src/**/*.{ts,tsx}: Fully capitalize acronyms in symbols, e.g.graphID,useBackendAPI
Use function declarations (not arrow functions) for components and handlers
Separate render logic (.tsx) from business logic (use*.tshooks)
Use shadcn/ui (Radix UI primitives) with Tailwind CSS styling for UI components
Use Phosphor Icons only for icons
Use ErrorCard for render errors, toast for mutations, and Sentry for exceptions
Use design system components fromsrc/components/(atoms, molecules, organisms)
Never usesrc/components/__legacy__/*components
Use generated API hooks from@/app/api/__generated__/endpoints/with patternuse{Method}{Version}{OperationName}
Use Tailwind CSS only for styling, with design tokens
Do not useuseCallbackoruseMemounless asked to optimize a given function
Never type withanyunless a variable/attribute can ACTUALLY be of any type
autogpt_platform/frontend/src/**/*.{ts,tsx}: Structure components asComponentName/ComponentName.tsx+useComponentName.ts+helpers.tsand use design system components fromsrc/components/(atoms, molecules, organisms)
Use generated API hooks from@/app/api/__generated__/endpoints/with patternuse{Method}{Version}{OperationName}and regenerate withpnpm generate:api
Use function declarations (not arrow functions) for components and handlers
Separate render logic from business logic with component.tsx + useComponent.ts + helpers.ts structure
Colocate state when possible, avoid creating large components, use sub-components in local/componentsfolder
Avoid large hooks, abstract logic intohelpers.tsfiles when sensible
Use arrow functions only for callbacks, not for component declarations
Avoid comments at all times unless the code is very complex
Do not useuseCallbackoruseMemounless asked to optimize a given function
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/frontend/src/**/*use*.ts
📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)
Do not type hook returns, let TypeScript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}: Format frontend code usingpnpm format
Never use components fromsrc/components/__legacy__/*
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use Tailwind CSS only for styling, use design tokens, and use Phosphor Icons only
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/frontend/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not type hook returns, let Typescript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never type with
any, if no types available useunknown
Files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
autogpt_platform/backend/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development
Files:
autogpt_platform/backend/backend/data/graph.py
autogpt_platform/backend/backend/data/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
All data access in backend requires user ID checks; verify this for any 'data/*.py' changes
Files:
autogpt_platform/backend/backend/data/graph.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/data/graph.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/data/graph.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/data/graph.py
autogpt_platform/**/data/*.py
📄 CodeRabbit inference engine (AGENTS.md)
For changes touching
data/*.py, validate user ID checks or explain why not needed
Files:
autogpt_platform/backend/backend/data/graph.py
🧠 Learnings (9)
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{tsx,ts} : Use generated API hooks from '@/app/api/__generated__/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/*'
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.tsautogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Use generated API hooks from `@/app/api/__generated__/endpoints/` with pattern `use{Method}{Version}{OperationName}` and regenerate with `pnpm generate:api`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
📚 Learning: 2026-02-04T16:50:33.615Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:33.615Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Use generated API hooks from `@/app/api/__generated__/endpoints/` with pattern `use{Method}{Version}{OperationName}`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
📚 Learning: 2026-02-04T16:50:33.615Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:33.615Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Fully capitalize acronyms in symbols, e.g. `graphID`, `useBackendAPI`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Avoid large hooks, abstract logic into `helpers.ts` files when sensible
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/frontend/src/app/(platform)/**/*.tsx : If adding protected frontend routes, update `frontend/lib/supabase/middleware.ts`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
📚 Learning: 2026-02-04T16:50:33.615Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:33.615Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : No barrel files or 'index.ts' re-exports in frontend code
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts
🧬 Code graph analysis (3)
autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts (2)
autogpt_platform/backend/backend/data/graph.py (1)
GraphModel(403-1015)autogpt_platform/frontend/src/app/(platform)/build/components/helper.ts (1)
linkToCustomEdge(70-80)
autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts (1)
autogpt_platform/frontend/src/app/(platform)/build/components/helper.ts (1)
customEdgeToLink(82-89)
autogpt_platform/backend/backend/data/graph.py (1)
autogpt_platform/backend/backend/blocks/__init__.py (1)
get_block(130-132)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: types
- GitHub Check: Seer Code Review
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: end-to-end tests
- GitHub Check: Check PR Status
🔇 Additional comments (4)
autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts (1)
123-136: LGTM — defensive filtering before save.Clean approach: building a
Setof node IDs once and filtering in a single pass is efficient. Theconsole.warnis helpful for debugging orphan edge issues without being noisy in production.autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts (3)
25-35: Good: module-level helper avoids unnecessary re-renders and closure dependencies.The function correctly replaces all frontend edges with the backend-authoritative state. The
undefinedguard on Line 30 is appropriate.One minor note: if the backend ever returns
links: undefineddue to a serialization issue, the frontend will silently keep stale edges. Consider logging a warning in theelsebranch if this is unexpected.
80-82: LGTM — post-create sync.Correctly syncs edges after the graph is created on the backend.
120-122: LGTM — post-update sync.Correctly syncs edges after the graph is updated on the backend.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
|
Re: Sentry Bug Prediction (comment r2807474296) Good catch on the history pollution issue with My changes in this PR actually improve the situation:
The |
|
Fixed the history pollution issue raised by Sentry. Changed Before: Each |
|
Fixed the race condition identified by Sentry. Problem: Two separate
Since links can be available before nodes, Fix: Combined both into a single
This ensures the correct ordering and prevents silent connection loss. |
|
Re: Sentry Bug Prediction (comment r2807845064) - Edge changes lost during save This is a valid UX concern but out of scope for this PR. The issue exists because:
The suggested fix (locking the entire editor during save) is a significant UX change that should be addressed in a dedicated PR. It requires:
For now, the save operation is intentionally non-blocking to keep the editor responsive. If edge loss during save becomes a user-reported issue, we can prioritize the locking feature. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@autogpt_platform/frontend/src/app/`(platform)/build/components/FlowEditor/Flow/useFlow.ts:
- Around line 138-150: The effect currently clears nodes via
useNodeStore.getState().setNodes([]) but only clears edges inside the
graph?.links guard, so stale edges can persist when graph.links is nullish;
update the useEffect (the block handling customNodes and graph?.links) to always
call useEdgeStore.getState().setEdges([]) alongside
useNodeStore.getState().setNodes([]) before adding nodes/links (i.e., move the
edge reset out of the if (graph?.links) guard), then call addLinks(graph.links)
only if graph?.links exists; keep addNodes(customNodes) as-is.
🧹 Nitpick comments (1)
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts (1)
150-150:graph?.linksas a dependency triggers re-runs on every query refetch
graph?.linksis an array reference — React'suseEffectcompares dependencies by reference. Each time React Query refetches or returns a newgraphobject (e.g., window refocus, cache invalidation),graph.linkswill be a new array even if the content is identical, causing this effect to re-run and re-add all nodes and edges unnecessarily.Consider using a stable derived value (e.g.,
linkCountwhich is already computed on line 230, or a JSON-serialized key) as the dependency instead, or rely ongraphobject identity alone if React Query's structural sharing keeps it stable for your use case.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
🧰 Additional context used
📓 Path-based instructions (10)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Use Node.js 21+ with pnpm package manager for frontend development
Always run 'pnpm format' for formatting and linting code in frontend development
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/**/*.{tsx,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/frontend/**/*.{tsx,ts}: Use function declarations for components and handlers (not arrow functions) in React components
Only use arrow functions for small inline lambdas (map, filter, etc.) in React components
Use PascalCase for component names and camelCase with 'use' prefix for hook names in React
Use Tailwind CSS utilities only for styling in frontend components
Use design system components from 'src/components/' (atoms, molecules, organisms) in frontend development
Never use 'src/components/legacy/' in frontend code
Only use Phosphor Icons (@phosphor-icons/react) for icons in frontend components
Use generated API hooks from '@/app/api/generated/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/'
Use React Query for server state (via generated hooks) in frontend development
Default to client components ('use client') in Next.js; only use server components for SEO or extreme TTFB needs
Use '' component for rendering errors in frontend UI; use toast notifications for mutation errors; use 'Sentry.captureException()' for manual exceptions
Separate render logic from data/behavior in React components; keep comments minimal (code should be self-documenting)
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/frontend/**/*.{ts,tsx}: No barrel files or 'index.ts' re-exports in frontend code
Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)
autogpt_platform/frontend/src/**/*.{ts,tsx}: Fully capitalize acronyms in symbols, e.g.graphID,useBackendAPI
Use function declarations (not arrow functions) for components and handlers
Separate render logic (.tsx) from business logic (use*.tshooks)
Use shadcn/ui (Radix UI primitives) with Tailwind CSS styling for UI components
Use Phosphor Icons only for icons
Use ErrorCard for render errors, toast for mutations, and Sentry for exceptions
Use design system components fromsrc/components/(atoms, molecules, organisms)
Never usesrc/components/__legacy__/*components
Use generated API hooks from@/app/api/__generated__/endpoints/with patternuse{Method}{Version}{OperationName}
Use Tailwind CSS only for styling, with design tokens
Do not useuseCallbackoruseMemounless asked to optimize a given function
Never type withanyunless a variable/attribute can ACTUALLY be of any type
autogpt_platform/frontend/src/**/*.{ts,tsx}: Structure components asComponentName/ComponentName.tsx+useComponentName.ts+helpers.tsand use design system components fromsrc/components/(atoms, molecules, organisms)
Use generated API hooks from@/app/api/__generated__/endpoints/with patternuse{Method}{Version}{OperationName}and regenerate withpnpm generate:api
Use function declarations (not arrow functions) for components and handlers
Separate render logic from business logic with component.tsx + useComponent.ts + helpers.ts structure
Colocate state when possible, avoid creating large components, use sub-components in local/componentsfolder
Avoid large hooks, abstract logic intohelpers.tsfiles when sensible
Use arrow functions only for callbacks, not for component declarations
Avoid comments at all times unless the code is very complex
Do not useuseCallbackoruseMemounless asked to optimize a given function
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/src/app/(platform)/**/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)
Put sub-components in local
components/folder within feature directories
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/src/**/*use*.ts
📄 CodeRabbit inference engine (autogpt_platform/frontend/CLAUDE.md)
Do not type hook returns, let TypeScript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx}: Format frontend code usingpnpm format
Never use components fromsrc/components/__legacy__/*
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use Tailwind CSS only for styling, use design tokens, and use Phosphor Icons only
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/frontend/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not type hook returns, let Typescript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
autogpt_platform/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never type with
any, if no types available useunknown
Files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
🧠 Learnings (3)
📚 Learning: 2026-02-04T16:50:33.615Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:33.615Z
Learning: Use xyflow/react for visual graph editor in Workflow Builder
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
📚 Learning: 2026-02-04T16:50:33.615Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/CLAUDE.md:0-0
Timestamp: 2026-02-04T16:50:33.615Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
📚 Learning: 2026-02-04T16:50:51.495Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-04T16:50:51.495Z
Learning: Applies to autogpt_platform/frontend/src/**/*.{ts,tsx} : Structure components as `ComponentName/ComponentName.tsx` + `useComponentName.ts` + `helpers.ts` and use design system components from `src/components/` (atoms, molecules, organisms)
Applied to files:
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts
🧬 Code graph analysis (1)
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts (2)
autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts (1)
useNodeStore(123-691)autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts (1)
useEdgeStore(40-257)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: types
- GitHub Check: Seer Code Review
- GitHub Check: end-to-end tests
- GitHub Check: Analyze (python)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: Check PR Status
🔇 Additional comments (1)
autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts (1)
136-148: Good fix: combining node and link initialization eliminates the race condition.Sequencing the node addition before link addition within a single effect is the right approach to ensure nodes exist in the store when
addLinksvalidates link endpoints. The bulksetEdges([])+addLinks()pattern also avoids per-edge history pollution.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
I think we can remove the changes from this file — this will create a race condition with useEffect in useFlow.ts, and there’s no need for it tbh.
There was a problem hiding this comment.
Done! Removed syncEdgesWithBackend and all its call sites. The backend-returned data still flows through the query cache and triggers the useFlow.ts useEffect naturally, so this was indeed redundant.
There was a problem hiding this comment.
I think we can create a new file called linkValidations.ts and add these two validations there: filterValidEdges() and filterValidLinks().
but in the best-case scenario, our backend should not send a broken agent to the frontend.
There was a problem hiding this comment.
Done! Created linkValidations.ts with filterValidEdges() and filterValidLinks(), and updated edgeStore.ts to use them.
Agreed on the backend point — the backend's prune_invalid_links() in graph.py should catch these before they reach the frontend. The frontend validation is defense-in-depth for edge cases (e.g., concurrent edits, stale cache).
Fixes graph desync issues where edge deletions don't persist, causing stale connections that users cannot remove. Frontend changes: - edgeStore: Validate links during addLinks() - skip edges referencing non-existent nodes - edgeStore: Filter invalid edges in getBackendLinks() before save - useSaveGraph: Sync edge store with authoritative backend state after save to prevent desync Backend changes: - graph.py: Add prune_invalid_links() method that removes: - Links referencing non-existent nodes - Links with invalid block IDs - Called during graph validation to auto-cleanup orphan edges This ensures: 1. Invalid edges are filtered out when loading a graph 2. Invalid edges are not sent to backend during save 3. Frontend syncs with backend state after save 4. Backend cleans up any orphan edges that slip through Closes: SECRT-1959
Changed addLinks to collect all valid edges first and add them in a single set() call with one history push, instead of calling addEdge for each link which would push to history for every edge individually. Fixes: Sentry bug prediction about undo/redo history pollution
Combined the separate useEffect hooks for adding nodes and links into a single effect that ensures nodes are added first. Previously, links could be processed before nodes existed in the store, causing all connections to be silently filtered out. Fixes: Sentry bug prediction about race condition on graph load
Moved setEdges([]) outside the graph?.links guard to prevent stale edges from persisting when loading a graph with no links.
…syncEdgesWithBackend - Remove syncEdgesWithBackend from useSaveGraph.ts to avoid race condition with useFlow.ts useEffect (per Abhi's review) - Extract filterValidEdges() and filterValidLinks() into linkValidations.ts for cleaner separation of concerns (per Abhi's review) - Update edgeStore.ts to use extracted validation functions
e19b6e4 to
dee131b
Compare
Otto-AGPT
left a comment
There was a problem hiding this comment.
Review from Otto 🐙
Overall: Solid defensive fix for a real user-facing bug. The multi-layer approach (backend pruning + frontend validation + load-time ordering) is appropriate for this kind of desync issue.
What's good
- Backend
prune_invalid_links()— clean implementation, good logging, correctly placed before structure validation - useFlow.ts race condition fix — combining node+edge loading into one
useEffectis the right call. The original two separate effects had different dependency timing - linkValidations.ts extraction — clean separation per Abhi's feedback
- History pollution fix — bulk-adding edges with a single history push instead of individual
addEdgecalls
Minor observations
-
prune_invalid_linkscallsget_block()twice per link (once for source, once for sink). For graphs with many links sharing the same blocks, a block cache could avoid redundant lookups. Not a blocker — theget_block()function likely already caches via the block registry. -
useFlow.ts dependency array includes
graph?.links— this is a reference comparison on an array. If the parent query refetches and returns a new array object with the same data, this effect will re-run (clearing and re-adding all nodes/edges). Works correctly but worth noting if performance becomes a concern on large graphs. -
The
useEdgeStore.getState().setEdges([])is now always called alongside node reset even whengraph?.linksis nullish — good, this was the CodeRabbit suggestion and prevents stale edges.
No blocking issues. Looks good to me. 👍
Summary
Fixes graph desync issues where edge deletions don't persist, causing stale connections that users cannot remove. The fix auto-removes invalid edges at multiple points to ensure graphs stay clean.
Problem
Edge deletion in the Agent Builder is not persisting correctly:
Solution
Frontend Changes
edgeStore.ts:addLinks(): Validate links during load - skip edges referencing non-existent nodesgetBackendLinks(): Filter invalid edges before sending to backend during saveuseSaveGraph.ts:Backend Changes
graph.py:prune_invalid_links()method that removes:_validate_graph_structure()to auto-cleanup orphan edges before saveTesting
Related Issues
Closes SECRT-1959
Greptile Overview
Greptile Summary
This PR implements comprehensive edge cleanup to fix persistent desync issues where deleted edges would reappear after save/reload. The fix adds validation at three key points:
Backend (
graph.py):prune_invalid_links()method to remove orphan edges referencing non-existent nodes or blocksFrontend (
edgeStore.ts):getBackendLinks()now filters out invalid edges before sending to backendaddLinks()validates links during load and skips edges with missing nodesSync Logic (
useSaveGraph.ts):syncEdgesWithBackend()The changes implement defense-in-depth by validating at multiple layers (frontend pre-save, backend validation, frontend post-save sync, frontend load). This ensures orphan edges are caught and removed regardless of where they originate.
Confidence Score: 4/5
prune_invalid_links()method, though the existing validation flow has test coverage.Sequence Diagram
sequenceDiagram participant User participant Frontend participant edgeStore participant useSaveGraph participant Backend participant graph.py User->>Frontend: Delete node/edge Frontend->>edgeStore: Update local state User->>Frontend: Click Save Frontend->>useSaveGraph: saveGraph() useSaveGraph->>edgeStore: getBackendLinks() edgeStore->>edgeStore: Filter invalid edges<br/>(check node existence) edgeStore-->>useSaveGraph: Valid links only useSaveGraph->>Backend: POST/PUT graph with links Backend->>graph.py: _validate_graph_structure() graph.py->>graph.py: prune_invalid_links()<br/>(remove orphan edges) graph.py->>graph.py: Validate remaining links Backend-->>useSaveGraph: Return cleaned graph useSaveGraph->>useSaveGraph: syncEdgesWithBackend() useSaveGraph->>edgeStore: setEdges(backend links) edgeStore->>edgeStore: Replace all edges User->>Frontend: Reload page Frontend->>Backend: GET graph Backend-->>Frontend: Return graph with links Frontend->>edgeStore: addLinks(links) edgeStore->>edgeStore: Filter invalid links<br/>(check node existence) edgeStore->>edgeStore: Add valid edges onlyLast reviewed commit: 6cb794c