Skip to content

Commit 2833ef5

Browse files
author
Tommy Corbett
committed
fix(incremental): name File nodes by basename, matching the full pipeline
The incremental path passed changed_files[i].rel_path as the File node NAME, while the full pipeline names File nodes by basename (pipeline.c "Create File node"). Node upserts match by qualified name, so an incremental re-index of a touched file RENAMED its File node in place ("applications.py" -> "fastapi/applications.py"). File nodes are USAGE-edge sources, so: - the same tree yields different graphs depending on whether it was indexed fresh or incrementally, and the divergence grows as files are edited (add AND modify both rename); - name-based traversal dead-ends at any touched file's node (e.g. walking inbound USAGE transitively misses everything that reaches a target through a recently added/modified class). Mirror the full pipeline's node shape at the incremental call site: basename as the name, and the same {"extension": ...} props (previously "{}"). Adds incr_file_node_name_stays_basename to the incremental suite: modify a file, incremental re-index, then assert no File node name contains '/'. Red without the fix, green with it; full incremental suite 162/162. Signed-off-by: Tommy Corbett <spde89@gmail.com>
1 parent 8c9ac98 commit 2833ef5

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

‎src/pipeline/pipeline_incremental.c‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,8 +848,17 @@ int cbm_pipeline_run_incremental(cbm_pipeline_t *p, const char *db_path, cbm_fil
848848
for (int i = 0; i < ci; i++) {
849849
char *file_qn = cbm_pipeline_fqn_compute(project, changed_files[i].rel_path, "__file__");
850850
if (file_qn) {
851-
cbm_gbuf_upsert_node(existing, "File", changed_files[i].rel_path, file_qn,
852-
changed_files[i].rel_path, 0, 0, "{}");
851+
/* #994: the name must be the BASENAME with extension props,
852+
* mirroring the full build's File node (pipeline.c) — upserts
853+
* match by QN, so any other name here renames the node in place
854+
* and the incremental graph diverges from a full build. */
855+
const char *rel = changed_files[i].rel_path;
856+
const char *slash = strrchr(rel, '/');
857+
const char *basename = slash ? slash + SKIP_ONE : rel;
858+
char props[CBM_SZ_256];
859+
const char *ext = strrchr(basename, '.');
860+
snprintf(props, sizeof(props), "{\"extension\":\"%s\"}", ext ? ext : "");
861+
cbm_gbuf_upsert_node(existing, "File", basename, file_qn, rel, 0, 0, props);
853862
free(file_qn);
854863
}
855864
}

‎tests/test_incremental.c‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,36 @@ static int resp_lacks_key(const char *resp, const char *key) {
964964

965965
/* ── list_projects ─────────────────────────────────────────────── */
966966

967+
TEST(incr_file_node_name_stays_basename) {
968+
/* #994: File nodes must keep their basename NAME through an incremental
969+
* re-index (the bug renamed touched files' nodes to rel_path, diverging
970+
* from a full build). Modify a file, re-index, assert no File node name
971+
* contains a path separator. */
972+
char path[512];
973+
snprintf(path, sizeof(path), "%s/fastapi/applications.py", g_repodir);
974+
FILE *f = fopen(path, "a");
975+
ASSERT(f != NULL);
976+
fprintf(f, "\n# incr_file_node_name_stays_basename touch\n");
977+
fclose(f);
978+
979+
char *resp = index_repo();
980+
ASSERT(resp != NULL);
981+
free(resp);
982+
983+
double ms;
984+
char *r = call_tool_timed("query_graph", &ms,
985+
"{\"project\":\"%s\",\"format\":\"json\","
986+
"\"query\":\"MATCH (n:File) WHERE n.name CONTAINS '/'"
987+
" RETURN n.name LIMIT 5\"}",
988+
g_project);
989+
TOOL_OK(r, ms);
990+
/* No File node may carry a path separator in its name; the query engine
991+
* returns an empty rows array when nothing matches. */
992+
ASSERT(strstr(r, "\"rows\":[]") != NULL || strstr(r, "\\\"rows\\\":[]") != NULL);
993+
free(r);
994+
PASS();
995+
}
996+
967997
TEST(tool_list_projects_basic) {
968998
double ms;
969999
char *r = call_tool_timed("list_projects", &ms, "{}");
@@ -2970,6 +3000,7 @@ SUITE(incremental) {
29703000

29713001
/* Phase 3: Incremental deltas */
29723002
RUN_TEST(incr_modify_file);
3003+
RUN_TEST(incr_file_node_name_stays_basename);
29733004
RUN_TEST(incr_formatter_run);
29743005
RUN_TEST(incr_add_file);
29753006
RUN_TEST(incr_delete_file);

0 commit comments

Comments
 (0)