diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28a2bfbb05..8ab7a699f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,9 +123,11 @@ jobs: run: | uv sync --extra treesitter-full --extra semantic --extra milvus --extra test --group dev + # evals/ too: the pre-commit hook checks it, so a type error there failed + # every local commit while this job stayed green (issue #3436). - name: Run type checking run: | - uv run ty check codebase_rag --exclude codebase_rag/tests/ + uv run ty check codebase_rag evals --exclude codebase_rag/tests/ # Again with the optional `neo4j` extra installed: its package types # every statement `LiteralString`, so a checkout with all extras saw @@ -133,7 +135,7 @@ jobs: - name: Run type checking with the neo4j extra run: | uv sync --extra treesitter-full --extra semantic --extra milvus --extra test --extra neo4j --group dev - uv run ty check codebase_rag --exclude codebase_rag/tests/ + uv run ty check codebase_rag evals --exclude codebase_rag/tests/ test-unit: name: Unit Tests (${{ matrix.os }}, py${{ matrix.python-version }}, shard ${{ matrix.shard }}) diff --git a/codebase_rag/tests/test_eval_graph_node_exists.py b/codebase_rag/tests/test_eval_graph_node_exists.py new file mode 100644 index 0000000000..41bf18e5b4 --- /dev/null +++ b/codebase_rag/tests/test_eval_graph_node_exists.py @@ -0,0 +1,33 @@ +# The in-memory store answers CYPHER_GRAPH_NODE_EXISTS for the unknown-target +# refusals in `cgr graph` and the MCP tools: a name counts as known when any +# node in any project carries it, and the answer is at most one row (#3436). +from __future__ import annotations + +from codebase_rag import constants as cs +from codebase_rag import cypher_queries as cq +from codebase_rag.graph_query import node_exists +from evals.cgr_graph import _StatefulIngestor + +QN = "other_project.pkg.mod.run" + + +def _store() -> _StatefulIngestor: + store = _StatefulIngestor() + # The same name under two labels: two nodes, so two candidate rows. + for label in (cs.NodeLabel.FUNCTION, cs.NodeLabel.METHOD): + store.ensure_node_batch(label.value, {cs.KEY_QUALIFIED_NAME: QN}) + return store + + +def test_a_name_any_node_carries_is_found_as_one_row() -> None: + store = _store() + rows = store.fetch_all(cq.CYPHER_GRAPH_NODE_EXISTS, {cs.KEY_QN: QN}) + assert rows == [{cs.KEY_QUALIFIED_NAME: QN}] + assert node_exists(store.fetch_all, QN) + + +def test_an_unknown_name_is_not_found() -> None: + store = _store() + missing = "other_project.pkg.mod.walk" + assert store.fetch_all(cq.CYPHER_GRAPH_NODE_EXISTS, {cs.KEY_QN: missing}) == [] + assert not node_exists(store.fetch_all, missing) diff --git a/evals/cgr_graph.py b/evals/cgr_graph.py index f1815baff1..7b94f4c334 100644 --- a/evals/cgr_graph.py +++ b/evals/cgr_graph.py @@ -1376,11 +1376,12 @@ def fetch_all( # Any node in any project: the unknown-target refusal asks # whether the name is in the graph at all (issue #3244). wanted = _str((params or {}).get(cs.KEY_QN)) - return [ + rows: list[ResultRow] = [ {cs.KEY_QUALIFIED_NAME: wanted} for props in self.nodes.values() if props.get(cs.KEY_QUALIFIED_NAME) == wanted - ][:1] + ] + return rows[:1] case cq.CYPHER_CLAIM_PROJECT_ROOT: return self._claim_project_root(params or {}) case cq.CYPHER_MARK_CLAIMED_PROJECT_INCOMPLETE: