fix(pipeline): delete the old generation only when rebuilding it

Two fixes on top of the main merge.

1. try_incremental_or_delete_db removed the existing database unconditionally.

On main those two lines are only REACHED on the reindex route, because the
incremental path returns early. This branch restructured the function so there
is no early return, and the merge carried main's cleanup in verbatim -- so the
database was deleted on every route, including the no-op and the successful
incremental publish.

The failure mode was badly misleading: the pipeline genuinely succeeded and
logged pipeline.done nodes=17, and cleanup then destroyed the result, so every
later reader reported dump.verify reason=store_missing. Because the probe
suites all share ~/.cache/codebase-memory-mcp and all have the shape
"index a snippet, open the store, count nodes", one suite that indexed twice
destroyed the shared database and every probe suite after it failed at its
store-open gate.

Full suite went from 6539 passed / 836 failed to 7354 passed / 21 failed. The
narrow arena that isolates it -- test-runner mcp node_creation_probe -- went
from 83 failed to 269 passed, which is exactly main's number for that pairing.

2. pass_parallel.c sized its usage-properties buffer at 256 bytes while the
sequential twin in pass_usages.c uses 512.

esc_ref holds up to 255 bytes and the {"callee":"..."} wrapper adds 13, so a
long callable identifier truncated mid-string, cutting the closing quote-brace
and persisting malformed JSON into edge properties. The two paths must also
agree: the same repository indexed in parallel and sequentially has to produce
byte-identical edge properties. This is RED item 4 from the PR description, and
it was a live defect on main rather than only on this branch.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-08-01 03:18:23 +02:00
parent 029416fda6
commit bccd22907f
2 changed files with 15 additions and 3 deletions
+7 -1
View File
@@ -2613,7 +2613,13 @@ static void resolve_file_usages(resolve_ctx_t *rc, resolve_worker_state_t *ws,
if (!tgt || src->id == tgt->id) {
continue;
}
char uprops[CBM_SZ_256];
/* 512, matching the sequential twin in pass_usages.c. esc_ref holds up
* to 255 bytes and the {"callee":"..."} wrapper adds 13, so a 256-byte
* uprops truncates a long callable identifier mid-string -- cutting the
* closing quote-brace and persisting MALFORMED JSON. The two paths must
* also agree: the same repo indexed in parallel and sequentially has to
* produce byte-identical edge properties. */
char uprops[CBM_SZ_512];
char esc_ref[CBM_SZ_256]; /* sliced source text: escape quotes/newlines */
cbm_json_escape(esc_ref, sizeof(esc_ref), usage->ref_name);
snprintf(uprops, sizeof(uprops), "{\"callee\":\"%s\"}", esc_ref);
+8 -2
View File
@@ -1316,14 +1316,20 @@ static int try_incremental_or_delete_db(cbm_pipeline_t *p, cbm_file_info_t *file
cbm_log_info("pipeline.route", "path", "incremental_manifest");
int rc = cbm_pipeline_run_incremental(p, db_path, files, file_count, baseline_manifest,
baseline_count);
/* Delete the existing generation ONLY when we are about to rebuild it.
* On main this was guarded by an early `return rc` for the incremental
* path; this function has no such early return, so the delete must be
* conditional. Unconditionally removing it destroys the database on the
* no-op and successful-incremental routes -- the pipeline reports success
* while every later reader finds no store. */
if (rc == CBM_PIPELINE_FORCE_FULL_REINDEX) {
int adr_rc = capture_existing_adr(p, db_path);
if (adr_rc != 0) {
rc = adr_rc;
}
(void)cbm_unlink(db_path);
(void)cbm_remove_db_sidecars(db_path);
}
(void)cbm_unlink(db_path);
(void)cbm_remove_db_sidecars(db_path);
free(db_path);
return rc;
}