{"record":{"id":"047836f7aaf60df5","repo":"666ghj/MiroFish","slug":"mirofish-entity-context-omitted-incoming-or-outgoi","errorCode":null,"errorMessage":"MiroFish entity context omitted incoming or outgoing node edges","messagePattern":"MiroFish entity context omitted incoming or outgoing node edges","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"backend/scripts/validate_zep_cloud_integration.py","lineNumber":717,"sourceCode":"                for edge in final_edges\n                if not edge.invalid_at and not edge.expired_at\n            ],\n            \"selected_node\": _node_view(selected_node),\n            \"sdk_node_edge_count\": len(sdk_node_edges),\n            \"complete_node_edge_count\": len(complete_node_edges),\n            \"entity_reader_context_edge_count\": len(entity_context.related_edges),\n            \"recent_episode_count\": len(episode_list),\n        }\n        result[\"searches\"] = {\n            \"current_state_edges\": _search_view(edge_search, final_names),\n            \"person_nodes\": _search_view(node_search, final_names),\n            \"typed_role_edges\": _search_view(typed_edge_search, final_names),\n            \"auto_context\": _search_view(auto_search, final_names),\n            \"partnership_episodes\": _search_view(episode_search, final_names),\n        }\n\n        if len(entity_context.related_edges) != len(complete_node_edges):\n            raise AssertionError(\n                \"MiroFish entity context omitted incoming or outgoing node edges\"\n            )\n\n        result[\"runtime_assertions\"] = {\n            \"edge_search_call_completed\": edge_search is not None,\n            \"node_search_call_completed\": node_search is not None,\n            \"typed_edge_search_call_completed\": typed_edge_search is not None,\n            \"auto_search_call_completed\": auto_search is not None,\n            \"episode_search_call_completed\": episode_search is not None,\n            \"node_detail_has_all_incoming_and_outgoing_edges\": True,\n            \"sdk_node_endpoint_omits_incoming_edges\": (\n                len(sdk_node_edges) < len(complete_node_edges)\n            ),\n            # The following values are observations only. Zep's extraction and\n            # retrieval quality are not runtime acceptance criteria.\n            \"search_result_counts\": {\n                \"edges\": len(edge_search.edges or []),\n                \"nodes\": len(node_search.nodes or []),","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/backend/scripts/validate_zep_cloud_integration.py#L699-L735","documentation":"A hard post-run assertion inside the MiroFish Zep Cloud integration validator. After ingesting the fixture ontology, the script selects the most-connected node, computes every edge in final_edges touching that node (both source and target side), and requires ZepEntityReader.get_entity_with_context(graph_id, node_uuid) to return exactly that many related_edges. The assertion fires when the entity-context reader returns fewer (or more) edges than the graph actually contains for the selected node.","triggerScenarios":"Running backend/scripts/validate_zep_cloud_integration.py against a Zep Cloud graph where ZepEntityReader.get_entity_with_context omits incoming edges (edges whose target_node_uuid is the selected node), filters expired/invalidated edges differently than the validator, or the Zep backend has not yet finished indexing all edges written moments earlier. Any count mismatch, not just omission, trips it because the check uses != rather than <.","commonSituations":"Zep Cloud SDK version drift where the entity reader's edge filter changes; eventual-consistency lag between graph.add and graph.read right after ingestion; Zep server-side changes that stop returning incoming edges in entity context (the script already records sdk_node_endpoint_omits_incoming_edges for the raw node endpoint, which is known to omit them); fixture ontology updated with new edge types that the reader drops.","solutions":["Re-run the validator after a short delay: if the mismatch is indexing lag, the counts converge on the second run and no code change is needed.","Inspect result['final'] (sdk_node_edge_count, complete_node_edge_count, entity_reader_context_edge_count) printed by the script to see whether entity_context is missing exactly the incoming edges; if so, confirm the Zep SDK/Cloud version changed behavior and pin or upgrade zep-cloud to the version the validator was written against.","Diff the UUIDs: log [e.uuid for e in entity_context.related_edges] vs complete_node_edges to identify which edge class (expired_at/invalid_at set, custom edge name, self-loop) the reader excludes, then align the validator's complete_node_edges computation (e.g. skip edges with invalid_at/expired_at) if the reader's filtering is the intended contract.","If Zep Cloud genuinely regressed and now omits incoming edges from entity context, file it with Zep and mark this assertion as a known-failing acceptance criterion rather than deleting it."],"exampleFix":"// before (validator, line 716)\nif len(entity_context.related_edges) != len(complete_node_edges):\n    raise AssertionError(\n        \"MiroFish entity context omitted incoming or outgoing node edges\"\n    )\n\n# after: diagnose which edges are missing before failing\nmissing = {e.uuid for e in complete_node_edges} - {\n    e.uuid for e in entity_context.related_edges\n}\nif missing:\n    raise AssertionError(\n        \"MiroFish entity context omitted \"\n        f\"{len(missing)} node edges: {sorted(missing)}\"\n    )","handlingStrategy":"validation","validationCode":"# before asserting, reconcile the two edge sets and report the delta\ncontext_uuids = {e.uuid for e in entity_context.related_edges}\nexpected_uuids = {e.uuid for e in complete_node_edges}\nmissing = expected_uuids - context_uuids\nextra = context_uuids - expected_uuids\nif missing or extra:\n    # log/diagnose instead of failing blind: indexing lag vs reader regression\n    print(f\"missing={sorted(missing)} extra={sorted(extra)}\")","typeGuard":"from typing import Any\n\ndef has_related_edges(obj: Any) -> bool:\n    \"\"\"Narrow a Zep entity-context object before counting its edges.\"\"\"\n    edges = getattr(obj, \"related_edges\", None)\n    return edges is not None and isinstance(edges, list)","tryCatchPattern":"try:\n    run_validation(...)  # executes the assertion at line 716\nexcept AssertionError as exc:\n    if \"omitted incoming or outgoing\" not in str(exc):\n        raise\n    # tolerate transient indexing lag once, then surface diagnostics\n    time.sleep(10)\n    run_validation(...)","preventionTips":["Run the validator against a freshly seeded graph twice, a minute apart, before treating an edge-count mismatch as a regression.","Pin the zep-cloud SDK version in backend requirements so reader behavior cannot drift silently.","Keep the assertion message actionable by including both counts (entity_reader_context_edge_count vs complete_node_edge_count) at minimum."],"tags":["zep","knowledge-graph","assertion","validation","integration-test"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}