From 5312402a7b1110cd73ba5b221898640f5133414b Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sat, 28 Feb 2026 22:11:15 -0600 Subject: [PATCH] =?UTF-8?q?fix:=20improve=20auto-edge=20reliability=20?= =?UTF-8?q?=E2=80=94=20error=20visibility,=20similarity-based=20strength,?= =?UTF-8?q?=20heuristic=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Surface auto-edge errors in store response (auto_edge_error key) instead of silently swallowing exceptions, making failures diagnosable - Pass similarity score as edge strength so higher-confidence matches get stronger edges instead of always defaulting to 0.8 - Document why same-type pairs and REQUIRES/FOLLOWS are excluded from the heuristic table vs edge-proposer.py Co-Authored-By: Claude Opus 4.6 --- mcp_server.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/mcp_server.py b/mcp_server.py index ec1d757..e7ad8c7 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -22,6 +22,9 @@ SYNC_SCRIPT = Path(__file__).parent / "scripts" / "memory-git-sync.sh" # Auto-edge heuristics: (new_type, match_type) -> (rel_type, direction) # direction "ab" = new->match, "ba" = match->new +# Subset of edge-proposer.py's TYPE_HEURISTICS — excludes: +# - Same-type pairs (decision/decision, fix/fix, solution/solution): too noisy for auto-edges +# - REQUIRES/FOLLOWS pairs: need stronger signal than title recall provides AUTO_EDGE_HEURISTICS = { ("fix", "problem"): ("SOLVES", "ab"), ("solution", "problem"): ("SOLVES", "ab"), @@ -555,7 +558,8 @@ def _auto_create_edges( Uses recall to find similar memories and heuristic type-pairs to choose relationship types. Returns list of created edge info dicts. - Never raises — failures are silently swallowed so store always succeeds. + Never raises — returns partial results or empty list with error info so + store always succeeds. Returns (edges_list, error_string_or_None). """ try: # Build recall query from title + tags for better precision (#4) @@ -613,11 +617,15 @@ def _auto_create_edges( from_title = title if from_id == memory_id else match_title to_title = match_title if to_id == match_id else title desc = f"Auto-edge: {from_title} → {to_title}" + # Use similarity as edge strength when available (default 0.8) + sim = result.get("similarity") + strength = round(sim, 2) if sim is not None else 0.8 edge_id = client.relate( from_id=from_id, to_id=to_id, rel_type=rel_type, description=desc, + strength=strength, ) if edge_id: # Empty string means duplicate @@ -630,9 +638,9 @@ def _auto_create_edges( } ) - return created_edges - except Exception: - return [] + return created_edges, None + except Exception as e: + return [], f"{type(e).__name__}: {e}" def handle_tool_call(tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]: @@ -669,16 +677,19 @@ def handle_tool_call(tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any tags=tags, ) episode_logged = True - auto_edges = _auto_create_edges(client, memory_id, title, mem_type, tags) - _trigger_git_sync() - return ok( - { - "success": True, - "memory_id": memory_id, - "episode_logged": episode_logged, - "auto_edges": auto_edges, - } + auto_edges, auto_edge_error = _auto_create_edges( + client, memory_id, title, mem_type, tags ) + _trigger_git_sync() + result = { + "success": True, + "memory_id": memory_id, + "episode_logged": episode_logged, + "auto_edges": auto_edges, + } + if auto_edge_error: + result["auto_edge_error"] = auto_edge_error + return ok(result) elif tool_name == "memory_recall": results = client.recall(