Unify MCP edges onto the Edge-node model (AI edges now visible to humans)#47
Merged
Conversation
…ans) The core "humans and AI as peers on one graph" promise was broken: MCP create_edge wrote a DIRECT Neo4j relationship (a)-[:TYPE]->(b), but the GraphQL server and web UI model edges as Edge NODES joined via EDGE_SOURCE / EDGE_TARGET. Proven live: an MCP-created edge produced 0 rows in the GraphQL `edges` query, so edges an AI created were invisible in the web UI (and human/web-created Edge nodes were invisible to every MCP read, which only traversed direct rels). The two layers were fully bifurcated. This migrates the ENTIRE MCP edge surface onto the canonical Edge-node model the server already uses: - Writes: createEdge, executeBulkCreateEdge (MERGE an Edge node + EDGE_SOURCE/EDGE_TARGET, idempotent); deleteEdge, executeBulkDeleteEdge (DETACH DELETE the Edge node). - Reads: getNodeDetails relationships, getGraphContext (edge count + blockers), getGraphDetails, browseGraph 'dependencies', cloneGraph edge clone — all traverse Edge nodes now. - Analytics: analyzeGraphHealth, getPriorityInsights, getBottlenecks, getContributorPriorities swap direct-rel patterns for Edge-node ones. - Variable-length: findPath and detectCycles use Neo4j 5 quantified path patterns / EDGE_SOURCE|EDGE_TARGET traversal and project back to WorkItem nodes + edge types (verified live: path A->C type DEPENDS_ON, cycle A->B->C->A len 3). Tests: new real-Neo4j contract case asserts an MCP-created edge is a web-visible Edge node (and NOT a stray direct rel), and that deleteEdge removes it. cloneGraph contract case now verifies via Edge nodes. Mock driver updated for the new relationships query + count(newE). Full CI-mode unit suite (305) green; contract suite (7) green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🧪 Comprehensive Test Suite
Full-stack smoke gate runs in the CI workflow. |
mvalancy
added a commit
that referenced
this pull request
Jun 13, 2026
…ity with web) (#49) * create_node: attach to a graph via graph_id (orphan nodes were invisible) Node-level twin of the edge parity fix (#47). The web lists nodes per-graph (workItems where graph.id = currentGraph), but MCP create_node had no graph_id parameter and never created a BELONGS_TO relationship, so every node an AI created was orphaned and shown in NO graph view. This also undermined the edge fix: an AI's nodes never appeared, so neither did the edges between them. Add an optional graph_id to the create_node tool + CreateNodeArgs; when present, MATCH the graph and MERGE (n)-[:BELONGS_TO]->(g). A graph_id that matches no graph returns a clean error instead of silently creating an unattached node. Omitting graph_id stays backward-compatible. Real-Neo4j contract test covers all three: attached (BELONGS_TO + shows in get_graph_context), and unknown graph_id errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * create_node: write canonical WorkItem fields the web reads MCP node creation also used non-canonical property names the GraphQL server/web schema don't read: priorityComputed (vs priorityComp), sphericalRadius/Theta/Phi (vs radius/theta/phi), and it never set positionX/Y/Z or priority at all. So AI-created nodes showed priority 0 at the origin in the web, and MCP was internally inconsistent (createNode + browseGraph used priorityComputed while updateNode and the priority/bottleneck analytics used priorityComp — updates were invisible to browse-by-priority and vice versa). Standardize createNode and executeBulkCreateNode on the canonical schema fields (positionX/Y/Z, radius, theta, phi, priority, priorityComp) while keeping the MCP-internal priorityExec/Indiv/Comm decomposition, and fix browseGraph by_priority to read priorityComp. Contract test now also asserts the canonical fields are set and no legacy priorityComputed remains. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug (headline finding of the skeptical audit)
GraphDone's core promise is "humans and AI agents collaborate as peers through the same graph interface." It was broken at the data layer.
create_edgewrote a direct relationship(a)-[:TYPE]->(b).EDGE_SOURCE/EDGE_TARGET(neo4j-schema.ts,seed.ts,onboarding.ts).Proven live: an MCP-created edge produced 0 rows in the GraphQL
edgesquery → invisible in the web UI. Conversely, every human/web-created Edge node was invisible to all MCP reads (which only traversed direct rels). The two layers were fully bifurcated — neither could see the other's edges.The fix
Migrate the entire MCP edge surface onto the canonical Edge-node model:
createEdge,executeBulkCreateEdge(MERGE Edge node, idempotent);deleteEdge,executeBulkDeleteEdge(DETACH DELETE)getNodeDetailsrelationships,getGraphContext(edge count + blockers),getGraphDetails,browseGraphdependencies,cloneGraphedge cloneanalyzeGraphHealth,getPriorityInsights,getBottlenecks,getContributorPrioritiesfindPath,detectCycles— Neo4j 5 quantified path patterns /EDGE_SOURCE|EDGE_TARGETtraversal, projected back to WorkItem nodes + edge typesVerification
Live probes (real Neo4j):
create_edge→ 1 Edge node (type preserved, server-style id), 0 stray direct rels;getNodeDetails&getGraphContextsee it;deleteEdgeremoves it.findPath(A,C)over a DEPENDS_ON chain →A→C, typeDEPENDS_ON(Edge-node hops correctly collapsed to logical edges).detectCyclesoverA→B→C→A→ cycleA→B→C→A, length 3.Tests:
deleteEdgeremoves it.cloneGraphcontract case now verifies cloned edges via Edge nodes.count(newE).Closes the parity gap so an AI agent and a human now genuinely operate on one shared graph.
🤖 Generated with Claude Code