fix(pipeline): accept regenerated file-owned inbound edges

Allow exact-delta planning to handle inbound edges whose source node has no file owner when the edge itself is owned by a path in the delta batch and the replacement batch regenerates the same source/target/type edge.

Keep the final planner authoritative so stale unowned-source edges still fall back to full/containment handling. Add paired pipeline canaries for regenerated versus stale file-owned unowned-source CALLS edges.

Validation: build/c/test-runner rebuild rc=0; CBM_ONLY_SUITE=pipeline rc=0 (306/306); source-safety rc=0; escalated CBM_ONLY_SUITE=incremental CBM_ONLY_TEST=exact rc=0 (1/1); self-dogfood cap64 rc=0 moved first blocker to frontier_too_large; cap128/cap256 benchmark evidence recorded in notes.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
Andrew Hundt
2026-07-02 14:35:00 -04:00
parent 179d0bb498
commit 2b05dc5a03
3 changed files with 126 additions and 5 deletions
+4 -4
View File
@@ -905,11 +905,11 @@ static bool delta_batch_contains_edge(const cbm_pipeline_file_delta_t *const *de
return false;
}
static bool delta_unowned_inbound_edge_is_regenerated(
static bool delta_inbound_edge_is_regenerated_by_batch(
const cbm_store_inbound_edge_t *edge, const cbm_pipeline_file_delta_t *const *deltas,
int delta_count) {
return edge && edge->source_rel_path && edge->source_rel_path[0] == '\0' && edge->type &&
strcmp(edge->type, cbm_delta_edge_contains_file) == 0 &&
return edge && edge->source_rel_path && edge->source_rel_path[0] == '\0' &&
delta_path_in_batch(edge->edge_rel_path, deltas, delta_count) &&
delta_batch_contains_edge(deltas, delta_count, edge->source_qn, edge->target_qn,
edge->type);
}
@@ -936,7 +936,7 @@ static bool delta_inbound_edges_supported(cbm_store_t *store,
bool ok = true;
for (int i = 0; i < edge_count; i++) {
if (!delta_path_in_batch(edges[i].source_rel_path, deltas, delta_count) &&
!delta_unowned_inbound_edge_is_regenerated(&edges[i], deltas, delta_count) &&
!delta_inbound_edge_is_regenerated_by_batch(&edges[i], deltas, delta_count) &&
!delta_owned_inbound_edge_is_deleted(&edges[i], delta)) {
ok = false;
break;
+9 -1
View File
@@ -1022,6 +1022,13 @@ static bool incr_empty_source_inbound_edge_is_structure(const cbm_store_inbound_
strcmp(edge->type, CBM_PIPELINE_EDGE_CONTAINS_FILE) == 0;
}
static bool incr_inbound_edge_owner_is_exact_file(const cbm_store_inbound_edge_t *edge,
const cbm_file_info_t *exact_files,
int exact_count) {
return edge && edge->source_rel_path && edge->source_rel_path[0] == '\0' &&
incr_file_info_has_rel_path(exact_files, exact_count, edge->edge_rel_path);
}
static int incr_expand_exact_inbound_frontier(cbm_store_t *store, const char *project,
const cbm_file_info_t *all_files, int all_file_count,
cbm_file_info_t *exact_files, int *exact_count,
@@ -1052,7 +1059,8 @@ static int incr_expand_exact_inbound_frontier(cbm_store_t *store, const char *pr
for (int i = 0; i < edge_count; i++) {
const char *source_rel_path = edges[i].source_rel_path;
if (!source_rel_path || source_rel_path[0] == '\0') {
if (incr_empty_source_inbound_edge_is_structure(&edges[i])) {
if (incr_empty_source_inbound_edge_is_structure(&edges[i]) ||
incr_inbound_edge_owner_is_exact_file(&edges[i], exact_files, *exact_count)) {
continue;
}
if (out_reason) {
+113
View File
@@ -3548,6 +3548,37 @@ static int pipeline_delta_seed_existing_ownership(cbm_store_t *s, const char *pr
: CBM_STORE_ERR;
}
static int pipeline_delta_seed_file_owned_unowned_source_edge(
cbm_store_t *s, const char *project, const char *rel_path, const char *source_qn,
const char *target_qn, const char *edge_type) {
enum { PIPELINE_DELTA_TEST_BASE_GENERATION = 1 };
int64_t target_id = pipeline_delta_seed_existing_ownership_id(s, project, rel_path, target_qn);
if (target_id <= CBM_STORE_NO_NODE_ID) {
return CBM_STORE_ERR;
}
cbm_node_t source = {.project = (char *)project,
.label = "Module",
.name = "module",
.qualified_name = (char *)source_qn,
.file_path = "",
.properties_json = "{}"};
int64_t source_id = cbm_store_upsert_node(s, &source);
if (source_id <= CBM_STORE_NO_NODE_ID) {
return CBM_STORE_ERR;
}
cbm_edge_t edge = {.project = (char *)project,
.source_id = source_id,
.target_id = target_id,
.type = (char *)edge_type,
.properties_json = "{}"};
int64_t edge_id = cbm_store_insert_edge(s, &edge);
if (edge_id <= CBM_STORE_NO_NODE_ID) {
return CBM_STORE_ERR;
}
return cbm_store_upsert_edge_owner(s, project, edge_id, rel_path, NULL,
PIPELINE_DELTA_TEST_BASE_GENERATION);
}
static int pipeline_delta_seed_project_node(cbm_store_t *s, const char *project) {
cbm_node_t project_node = {.project = (char *)project,
.label = "Project",
@@ -4925,6 +4956,86 @@ TEST(pipeline_file_delta_plan_accepts_regenerated_structural_inbound_edge) {
PASS();
}
TEST(pipeline_file_delta_plan_accepts_regenerated_file_owned_unowned_source_edge) {
const char *project = "test";
const char *rel_path = "src/main.go";
const char *source_qn = "test.src.module";
const char *target_qn = "test.src.main.Run";
cbm_store_t *s = cbm_store_open_memory();
ASSERT_NOT_NULL(s);
ASSERT_EQ(cbm_store_upsert_project(s, project, "/tmp/test"), CBM_STORE_OK);
ASSERT_EQ(pipeline_delta_seed_file_owned_unowned_source_edge(
s, project, rel_path, source_qn, target_qn, "CALLS"),
CBM_STORE_OK);
cbm_node_t replacement_node = {.project = (char *)project,
.label = "Function",
.name = "Run",
.qualified_name = (char *)target_qn,
.file_path = (char *)rel_path,
.properties_json = "{}"};
cbm_store_delta_edge_t replacement_edge = {.source_qn = source_qn,
.target_qn = target_qn,
.type = "CALLS",
.properties_json = "{}",
.derived_kind = CBM_STORE_DERIVED_KIND_DIRECT};
cbm_pipeline_file_delta_t delta = {.delta = {.project = project,
.rel_path = rel_path,
.nodes = &replacement_node,
.node_count = 1,
.edges = &replacement_edge,
.edge_count = 1}};
cbm_file_hash_t hash = {0};
cbm_file_state_t state = {0};
pipeline_delta_attach_test_metadata(&delta, &hash, &state);
cbm_pipeline_file_delta_plan_t plan = {0};
ASSERT_EQ(cbm_pipeline_plan_file_delta(s, &delta, CBM_SZ_4, &plan), CBM_STORE_OK);
ASSERT_EQ(plan.route, CBM_PIPELINE_DELTA_ROUTE_EXACT_CANDIDATE);
ASSERT_STR_EQ(plan.reason, "candidate");
ASSERT_EQ(pipeline_delta_plan_contains_path(&plan, rel_path), 1);
cbm_pipeline_file_delta_plan_free(&plan);
cbm_store_close(s);
PASS();
}
TEST(pipeline_file_delta_plan_falls_back_on_stale_file_owned_unowned_source_edge) {
const char *project = "test";
const char *rel_path = "src/main.go";
const char *source_qn = "test.src.module";
const char *target_qn = "test.src.main.Run";
cbm_store_t *s = cbm_store_open_memory();
ASSERT_NOT_NULL(s);
ASSERT_EQ(cbm_store_upsert_project(s, project, "/tmp/test"), CBM_STORE_OK);
ASSERT_EQ(pipeline_delta_seed_file_owned_unowned_source_edge(
s, project, rel_path, source_qn, target_qn, "CALLS"),
CBM_STORE_OK);
cbm_node_t replacement_node = {.project = (char *)project,
.label = "Function",
.name = "Run",
.qualified_name = (char *)target_qn,
.file_path = (char *)rel_path,
.properties_json = "{}"};
cbm_pipeline_file_delta_t delta = {.delta = {.project = project,
.rel_path = rel_path,
.nodes = &replacement_node,
.node_count = 1}};
cbm_file_hash_t hash = {0};
cbm_file_state_t state = {0};
pipeline_delta_attach_test_metadata(&delta, &hash, &state);
cbm_pipeline_file_delta_plan_t plan = {0};
ASSERT_EQ(cbm_pipeline_plan_file_delta(s, &delta, CBM_SZ_4, &plan), CBM_STORE_OK);
ASSERT_EQ(plan.route, CBM_PIPELINE_DELTA_ROUTE_FALLBACK);
ASSERT_STR_EQ(plan.reason, "inbound_edges_require_full");
cbm_pipeline_file_delta_plan_free(&plan);
cbm_store_close(s);
PASS();
}
TEST(pipeline_file_delta_plan_falls_back_without_file_metadata) {
cbm_store_t *s = cbm_store_open_memory();
ASSERT_NOT_NULL(s);
@@ -11920,6 +12031,8 @@ SUITE(pipeline) {
RUN_TEST(pipeline_file_delta_plan_falls_back_on_new_folder_structure_edge);
RUN_TEST(pipeline_file_delta_apply_inserts_and_prunes_new_folder_context);
RUN_TEST(pipeline_file_delta_plan_accepts_regenerated_structural_inbound_edge);
RUN_TEST(pipeline_file_delta_plan_accepts_regenerated_file_owned_unowned_source_edge);
RUN_TEST(pipeline_file_delta_plan_falls_back_on_stale_file_owned_unowned_source_edge);
RUN_TEST(pipeline_file_delta_plan_falls_back_without_file_metadata);
RUN_TEST(pipeline_file_delta_plan_falls_back_on_unsupported_edges);
RUN_TEST(pipeline_file_delta_plan_falls_back_on_delete);