From b0c9ce9a655dd82d5ae36101cfc69479ba1f7222 Mon Sep 17 00:00:00 2001 From: Dvir Dukhan <12258836+DvirDukhan@users.noreply.github.com> Date: Wed, 27 May 2026 12:15:45 +0300 Subject: [PATCH] bench: fix find_symbol exact-match against nested properties.name Smoke #3 revealed cg find-symbol --name returned [] for symbols the graph clearly contained (cg auto-complete --prefix found the same symbol with full file:line+docstring). Root cause: the filter compared item['name'] to the requested name, but the /api/auto_complete payload nests the symbol name under item['properties']['name'] (FalkorDB node properties), so the top-level lookup always returned None and nothing matched. Fix: prefer item['properties']['name'], fall back to item['name'] for flatter shapes the unit tests pass in. Added a regression test that uses the real payload structure. Verified end-to-end against the live FastAPI service: cg find-symbol --repo pytest-dev__pytest-6202__code_graph \ --name getmodpath # -> [{id:2714, labels:[Function], properties:{name,path,doc,...}}] This was the bug that made the smoke #3 code_graph agent burn 3 of 5 cg calls retrying exact-name lookups before falling back to auto-complete. With this fix, an agent doing the natural workflow (find-symbol -> get-neighbors -> note-edit) should land far fewer wasted calls. Also: norecursedirs in [tool.pytest.ini_options] to keep pytest from walking into per-instance bench worktrees that ship their own pytest sources (was breaking host pytest's AST rewriter on import). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- bench/agents/code_graph_adapter.py | 17 +++++++++++++---- pyproject.toml | 4 ++++ tests/test_bench_code_graph_adapter.py | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/bench/agents/code_graph_adapter.py b/bench/agents/code_graph_adapter.py index f3e6e633..96f5cfdf 100644 --- a/bench/agents/code_graph_adapter.py +++ b/bench/agents/code_graph_adapter.py @@ -102,15 +102,24 @@ def find_symbol(self, repo: str, name: str) -> list[dict[str, Any]]: auto_complete returns prefix matches; the agent often wants exact matches. Doing this client-side keeps the FastAPI surface untouched. + + The auto_complete payload nests the symbol name under + `item["properties"]["name"]` (FalkorDB node properties), so we look + there first and only fall back to a top-level `name` for older / + flatter shapes the tests may pass in. """ payload = self.auto_complete(repo, name) results = payload.get("completions") or payload.get("results") or payload if isinstance(results, dict): results = results.get("items", []) - return [ - item for item in (results or []) - if isinstance(item, dict) and item.get("name") == name - ] + out: list[dict[str, Any]] = [] + for item in (results or []): + if not isinstance(item, dict): + continue + props = item.get("properties") if isinstance(item.get("properties"), dict) else {} + if props.get("name") == name or item.get("name") == name: + out.append(item) + return out def note_edit(self, repo: str, path: str) -> dict[str, Any]: """Tell code-graph the agent just edited `path`; trigger an diff --git a/pyproject.toml b/pyproject.toml index cd840469..6f007651 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,10 @@ bench = [ markers = [ "slow: marks tests that spawn external subprocesses (LSP servers, FalkorDB, etc.); skip with -m 'not slow'", ] +# Keep pytest from walking into per-instance bench worktrees that contain +# their own copies of pytest source — collecting from those breaks the +# host pytest's AST rewriter. +norecursedirs = ["bench/cache", ".venv", "node_modules", "build", "dist", "*.egg-info"] [tool.setuptools.packages.find] where = ["."] diff --git a/tests/test_bench_code_graph_adapter.py b/tests/test_bench_code_graph_adapter.py index 192695ed..6b5ee4a1 100644 --- a/tests/test_bench_code_graph_adapter.py +++ b/tests/test_bench_code_graph_adapter.py @@ -80,6 +80,26 @@ def test_find_symbol_filters_for_exact_match(): assert all(item["name"] == "Foo" for item in out) +def test_find_symbol_reads_nested_properties_name(): + # Regression: the real /api/auto_complete payload nests `name` under + # `properties` (FalkorDB node properties). Before this fix every + # exact-name lookup returned [], so the agent fell back to bash grep. + resp = httpx.Response( + 200, + json={"completions": [ + {"id": 1, "labels": ["Function"], + "properties": {"name": "FooBar", "path": "/a.py"}}, + {"id": 2, "labels": ["Function"], + "properties": {"name": "Foo", "path": "/b.py"}}, + {"id": 3, "labels": ["Function"], + "properties": {"name": "Foo", "path": "/c.py"}}, + ]}, + ) + with _client_with({"POST /api/auto_complete": resp}) as c: + out = c.find_symbol("r", "Foo") + assert [item["id"] for item in out] == [2, 3] + + def test_note_edit_calls_analyze_folder_and_reports_path(): resp = httpx.Response(200, json={"status": "ok"}) with _client_with({"POST /api/analyze_folder": resp}) as c: