Skip to content

Unify MCP edges onto the Edge-node model (AI edges now visible to humans)#47

Merged
mvalancy merged 1 commit into
developfrom
fix/mcp-edge-node-parity
Jun 13, 2026
Merged

Unify MCP edges onto the Edge-node model (AI edges now visible to humans)#47
mvalancy merged 1 commit into
developfrom
fix/mcp-edge-node-parity

Conversation

@mvalancy

Copy link
Copy Markdown
Member

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.

  • MCP create_edge wrote a direct relationship (a)-[:TYPE]->(b).
  • The GraphQL server + web UI model edges as Edge NODES joined via EDGE_SOURCE / EDGE_TARGET (neo4j-schema.ts, seed.ts, onboarding.ts).

Proven live: an MCP-created edge produced 0 rows in the GraphQL edges query → 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:

Area Methods
Writes createEdge, executeBulkCreateEdge (MERGE Edge node, idempotent); deleteEdge, executeBulkDeleteEdge (DETACH DELETE)
Core reads getNodeDetails relationships, getGraphContext (edge count + blockers), getGraphDetails, browseGraph dependencies, cloneGraph edge clone
Analytics analyzeGraphHealth, getPriorityInsights, getBottlenecks, getContributorPriorities
Variable-length findPath, detectCycles — Neo4j 5 quantified path patterns / EDGE_SOURCE|EDGE_TARGET traversal, projected back to WorkItem nodes + edge types

Verification

Live probes (real Neo4j):

  • create_edge1 Edge node (type preserved, server-style id), 0 stray direct rels; getNodeDetails & getGraphContext see it; deleteEdge removes it.
  • findPath(A,C) over a DEPENDS_ON chain → A→C, type DEPENDS_ON (Edge-node hops correctly collapsed to logical edges).
  • detectCycles over A→B→C→A → cycle A→B→C→A, length 3.

Tests:

  • New real-Neo4j contract case: an MCP-created edge is a web-visible Edge node and not a direct rel; deleteEdge removes it.
  • cloneGraph contract case now verifies cloned edges via Edge nodes.
  • Mock driver updated for the new relationships query + count(newE).
  • Full CI-mode unit suite (305) green; contract suite (7) green.

Closes the parity gap so an AI agent and a human now genuinely operate on one shared graph.

🤖 Generated with Claude Code

…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>
@github-actions

Copy link
Copy Markdown

🧪 Comprehensive Test Suite

  • Unit suites (Node 18.x & 20.x) — core, web, server, mcp-server: ✅ passed
  • Installer & deploy config: ✅ passed

Full-stack smoke gate runs in the CI workflow.

@mvalancy mvalancy merged commit 2c489a8 into develop Jun 13, 2026
16 checks passed
@mvalancy mvalancy deleted the fix/mcp-edge-node-parity branch June 13, 2026 21:00
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant