Skip to content

Fix clone_graph: throws on most graphs and corrupts every edge type#44

Merged
mvalancy merged 1 commit into
developfrom
fix/clone-graph-broken
Jun 13, 2026
Merged

Fix clone_graph: throws on most graphs and corrupts every edge type#44
mvalancy merged 1 commit into
developfrom
fix/clone-graph-broken

Conversation

@mvalancy

Copy link
Copy Markdown
Member

Summary

Found during the skeptical live-app audit by exercising the clone_graph MCP tool against a real Neo4j. It has two real, AI-reachable bugs:

1. clone_graph throws ParameterMissing: teamId for most graphs

Neo4j does not store null-valued properties, so a graph created without a teamId simply has no teamId property. cloneGraph read it back as undefined and passed it as a query parameter — and the driver rejects undefined param values. So clone threw for essentially every graph created via the MCP API (createGraph stores teamId: null → property absent).

THREW: Expected parameter(s): teamId | Neo.ClientError.Statement.ParameterMissing

Fix: coalesce teamId (and type/isShared/settings defensively) to non-undefined values with ?? null.

2. Every cloned edge becomes DEPENDS_ON

The edge-clone query matched all relationship types but hard-coded CREATE (newW)-[:DEPENDS_ON {...}]->(newTargetW), so a BLOCKS / RELATES_TO / CONTAINS / PART_OF edge was silently rewritten to DEPENDS_ON on clone — corrupting the graph's dependency semantics.

Fix: CALL apoc.create.relationship(newW, type(r), {...}, newTargetW) to preserve the real type. (APOC 5.26 ships with the project's Neo4j.)

Verification

Before (probe): clone_graph threw ParameterMissing(teamId).
After (probe): clones 3 nodes + 2 edges, types preserved as BLOCKS and RELATES_TO.

Tests

New real-Neo4j contract case (MCP Real-Neo4j Contract CI job): clone a graph containing a BLOCKS and a RELATES_TO edge → assert all nodes copy and both edge types survive (not collapsed to DEPENDS_ON).

The existing mock unit tests passed straight through both bugs — they can't reproduce real-DB null-property/relationship-type behavior, which is exactly why the contract test exists.

🤖 Generated with Claude Code

Two real, AI-reachable bugs in the clone_graph MCP tool:

1. ParameterMissing(teamId): Neo4j does not persist null-valued
   properties, so a graph created without a teamId has NO teamId property.
   cloneGraph read it back as `undefined` and passed it straight to the
   driver, which rejects undefined param values — so clone threw for
   essentially every graph created via the API. Coalesce teamId (and
   type/isShared/settings defensively) to non-undefined values.

2. Edge types silently collapsed to DEPENDS_ON: the edge-clone query
   matched every relationship type but hard-coded `CREATE (a)-[:DEPENDS_ON
   ...]->(b)`, rewriting BLOCKS / RELATES_TO / CONTAINS / PART_OF into
   DEPENDS_ON on clone. Use apoc.create.relationship(newW, type(r), ...)
   to preserve the real type (APOC 5.x ships with the project's Neo4j).

Adds a real-Neo4j contract case: clone a graph with BLOCKS + RELATES_TO
edges and assert all nodes copy and both edge types survive. The existing
mock unit tests passed through both bugs — they can't reproduce real-DB
param/relationship-type behavior, which is exactly why the contract test
exists.

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 1750f46 into develop Jun 13, 2026
16 checks passed
@mvalancy mvalancy deleted the fix/clone-graph-broken branch June 13, 2026 17:57
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