2020* IDF weighting causes rare, discriminative terms (e.g. ``jwt``) to rank
2121 higher than ubiquitous terms (e.g. ``function``).
2222* The engine degrades gracefully — empty query, missing db, missing
23- ``symbols `` table, and ``top_k=0`` all return well-formed responses
23+ ``graph_nodes `` table, and ``top_k=0`` all return well-formed responses
2424 rather than raising.
2525* The cache invalidates on db mtime change, so a re-scan picks up new
2626 symbols.
5959def _make_db (workspace : str , symbols ):
6060 """Create a fake ``.codelens/codelens.db`` with the given symbol rows.
6161
62- ``symbols`` is a list of dicts with keys: name, kind, file_path,
63- line_start, language, signature, extra_json (dict, will be
64- JSON-encoded).
62+ Mirrors what ``scan`` actually produces: rows in the ``graph_nodes``
63+ table (issue #188). The engine reads from ``graph_nodes``, not the
64+ legacy ``symbols`` table declared by ``persistent_registry`` (which
65+ the scan flow never populates).
66+
67+ ``symbols`` is a list of dicts with keys: name, kind (maps to
68+ node_type), file_path (maps to file), line_start (maps to line),
69+ language, signature, extra_json (dict, will be JSON-encoded).
6570 """
6671 codelens_dir = os .path .join (workspace , ".codelens" )
6772 os .makedirs (codelens_dir , exist_ok = True )
6873 db_path = os .path .join (codelens_dir , "codelens.db" )
6974 conn = sqlite3 .connect (db_path )
7075 try :
76+ # Schema mirrors scripts/graph_model.py:_CREATE_GRAPH_NODES so the
77+ # engine's SELECT AS mapping has real columns to read from.
7178 conn .execute (
7279 """
73- CREATE TABLE symbols (
80+ CREATE TABLE graph_nodes (
7481 id INTEGER PRIMARY KEY AUTOINCREMENT,
82+ node_id TEXT NOT NULL UNIQUE,
83+ node_type TEXT NOT NULL DEFAULT 'function',
7584 name TEXT NOT NULL,
76- kind TEXT NOT NULL DEFAULT 'function',
77- file_path TEXT,
78- line_start INTEGER,
79- line_end INTEGER,
80- language TEXT,
81- signature TEXT,
82- hash TEXT,
85+ file TEXT,
86+ line INTEGER,
8387 extra_json TEXT
8488 )
8589 """
8690 )
8791 for i , s in enumerate (symbols , start = 1 ):
8892 extra = s .get ("extra_json" ) or {}
93+ name = s ["name" ]
94+ file_path = s .get ("file_path" , "" )
95+ line = s .get ("line_start" ) or 0
96+ # Synthesize a node_id matching the flat-registry convention
97+ # (file:line) — what populate_graph_tables would store. This
98+ # keeps tests realistic and would let future graph-traversal
99+ # integration reuse the same fixtures.
100+ node_id = s .get ("id" ) or f"{ file_path } :{ line } :{ name } "
89101 conn .execute (
90102 """
91- INSERT INTO symbols
92- (id, name, kind, file_path, line_start, line_end,
93- language, signature, hash, extra_json)
94- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
103+ INSERT INTO graph_nodes
104+ (id, node_id, node_type, name, file, line, extra_json)
105+ VALUES (?, ?, ?, ?, ?, ?, ?)
95106 """ ,
96107 (
97108 i ,
98- s [ "name" ] ,
109+ node_id ,
99110 s .get ("kind" , "function" ),
100- s .get ("file_path" , "" ),
101- s .get ("line_start" ),
102- s .get ("line_end" ),
103- s .get ("language" , "" ),
104- s .get ("signature" , "" ),
105- s .get ("hash" , "" ),
111+ name ,
112+ file_path ,
113+ line ,
106114 json .dumps (extra ) if extra else None ,
107115 ),
108116 )
@@ -255,7 +263,7 @@ class TestSemanticQuery:
255263 def test_finds_auth_symbol_by_concept (self , auth_workspace ):
256264 """The core issue #11 use case: a query token that doesn't appear
257265 in any symbol NAME still surfaces relevant symbols because file
258- paths, signatures, and kinds are all part of the TF-IDF document.
266+ paths and kinds are part of the TF-IDF document.
259267
260268 Here, the query ``"auth"`` doesn't appear in any symbol name
261269 (``verify_jwt_claims``, ``loginUser``, ``format_date``, etc.), but
@@ -380,13 +388,14 @@ def test_cache_invalidates_on_mtime_change(self, workspace):
380388 idx1 = build_index (db_path )
381389 assert len (idx1 .symbols ) == 1
382390
383- # Force mtime change by sleeping then writing new symbols
391+ # Force mtime change by sleeping then writing a new graph_nodes
392+ # row (what a real re-scan would do).
384393 time .sleep (0.05 )
385394 conn = sqlite3 .connect (db_path )
386395 try :
387396 conn .execute (
388- "INSERT INTO symbols (name, kind, file_path, line_start, language ) "
389- "VALUES ('bar', 'function', 'b.py', 2, 'python' )"
397+ "INSERT INTO graph_nodes (node_id, node_type, name, file, line ) "
398+ "VALUES ('b.py:2: bar', 'function', 'bar', ' b.py', 2)"
390399 )
391400 conn .commit ()
392401 finally :
@@ -413,8 +422,9 @@ def test_missing_db_returns_empty_results(self, workspace):
413422 assert result ["stats" ]["total_symbols" ] == 0
414423 assert result ["stats" ]["returned" ] == 0
415424
416- def test_db_without_symbols_table_returns_empty (self , workspace ):
417- # Create the db file but no `symbols` table
425+ def test_db_without_graph_nodes_table_returns_empty (self , workspace ):
426+ # Create the db file but no `graph_nodes` table — mirrors a
427+ # workspace that has been `codelens init`-ed but never scanned.
418428 codelens_dir = os .path .join (workspace , ".codelens" )
419429 os .makedirs (codelens_dir , exist_ok = True )
420430 db_path = os .path .join (codelens_dir , "codelens.db" )
@@ -427,7 +437,7 @@ def test_db_without_symbols_table_returns_empty(self, workspace):
427437 assert result ["status" ] == "ok"
428438 assert result ["results" ] == []
429439
430- def test_empty_symbols_table_returns_empty (self , workspace ):
440+ def test_empty_graph_nodes_table_returns_empty (self , workspace ):
431441 _make_db (workspace , [])
432442 result = semantic_query (workspace , "anything" , top_k = 10 )
433443 assert result ["status" ] == "ok"
0 commit comments