mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-10-29 07:13:44 +08:00
feat(mcp): report incremental containment reasons
Add pipeline-owned publish_reason metadata and surface it in index_repository responses when incremental indexing falls back to containment. The reason is set on existing exact-route skip/fallback paths and cleared on exact publish/full run reset, so MCP and CLI JSON callers can distinguish a safe containment rebuild from an opaque incremental result. Update the affected-frontier benchmark harness to prefer structured publish_reason while keeping stderr parsing for older binaries. Add focused pipeline assertions and an MCP tool test for incremental_containment with missing_existing_ownership. Validated with git diff --check, bash scripts/check-source-safety.sh, bash scripts/test-source-safety.sh, python3 -m py_compile scripts/benchmark-incremental-speed.py, escalated make -j8 -f Makefile.cbm build/c/test-runner, escalated CBM_ONLY_SUITE=pipeline build/c/test-runner, escalated CBM_ONLY_SUITE=mcp build/c/test-runner, escalated make -j8 -f Makefile.cbm cbm, and escalated persistent MCP affected-frontier matrix 8/8 pass. Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
@@ -274,6 +274,11 @@ def response_publish_kind(data: dict[str, Any]) -> str:
|
||||
return publish_kind if isinstance(publish_kind, str) else ""
|
||||
|
||||
|
||||
def response_publish_reason(data: dict[str, Any]) -> str:
|
||||
publish_reason = data.get("publish_reason")
|
||||
return publish_reason if isinstance(publish_reason, str) else ""
|
||||
|
||||
|
||||
def is_incremental_publish_kind(publish_kind: str) -> bool:
|
||||
return publish_kind in {
|
||||
PUBLISH_INCREMENTAL_NOOP,
|
||||
@@ -337,6 +342,7 @@ def build_index_result(
|
||||
"incremental_done": parse_logged_elapsed_ms(stderr, "incremental.done"),
|
||||
}
|
||||
indexed_ms = indexed_work_elapsed_ms(logged_elapsed_ms)
|
||||
publish_reason = response_publish_reason(data)
|
||||
result: dict[str, Any] = {
|
||||
"elapsed_ms": elapsed_ms_int,
|
||||
"indexed_work_elapsed_ms": indexed_ms,
|
||||
@@ -357,7 +363,7 @@ def build_index_result(
|
||||
or is_incremental_publish_kind(publish_kind),
|
||||
},
|
||||
"logged_elapsed_ms": logged_elapsed_ms,
|
||||
"exact_reason": parse_exact_reason(stderr),
|
||||
"exact_reason": publish_reason or parse_exact_reason(stderr),
|
||||
"stderr_tail": log_tail(stderr),
|
||||
}
|
||||
if include_logs:
|
||||
|
||||
@@ -4969,6 +4969,7 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) {
|
||||
int rc = cbm_pipeline_run(p);
|
||||
bool graph_changed = cbm_pipeline_graph_changed(p);
|
||||
cbm_pipeline_publish_kind_t publish_kind = cbm_pipeline_publish_kind(p);
|
||||
const char *publish_reason = cbm_pipeline_publish_reason(p);
|
||||
srv->active_pipeline = NULL;
|
||||
cbm_pipeline_unlock();
|
||||
|
||||
@@ -4997,6 +4998,9 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) {
|
||||
yyjson_mut_obj_add_str(doc, root, "status", rc == 0 ? "indexed" : "error");
|
||||
yyjson_mut_obj_add_str(doc, root, "publish_kind",
|
||||
cbm_pipeline_publish_kind_name(publish_kind));
|
||||
if (publish_reason && publish_reason[0]) {
|
||||
yyjson_mut_obj_add_str(doc, root, "publish_reason", publish_reason);
|
||||
}
|
||||
yyjson_mut_obj_add_bool(doc, root, "graph_changed", graph_changed);
|
||||
|
||||
if (rc == 0) {
|
||||
|
||||
@@ -118,6 +118,7 @@ struct cbm_pipeline {
|
||||
int committed_edges;
|
||||
bool graph_changed;
|
||||
cbm_pipeline_publish_kind_t publish_kind;
|
||||
char *publish_reason;
|
||||
};
|
||||
|
||||
/* ── Global pkgmap (one active pipeline at a time) ─────────────── */
|
||||
@@ -189,6 +190,7 @@ cbm_pipeline_t *cbm_pipeline_new(const char *repo_path, const char *db_path,
|
||||
p->committed_edges = -1;
|
||||
p->graph_changed = false;
|
||||
p->publish_kind = CBM_PIPELINE_PUBLISH_NONE;
|
||||
p->publish_reason = NULL;
|
||||
atomic_init(&p->cancelled, 0);
|
||||
|
||||
return p;
|
||||
@@ -325,6 +327,7 @@ void cbm_pipeline_free(cbm_pipeline_t *p) {
|
||||
free(p->repo_path);
|
||||
free(p->db_path);
|
||||
free(p->project_name);
|
||||
free(p->publish_reason);
|
||||
cbm_discover_free_excluded(p->excluded_dirs, p->excluded_count);
|
||||
p->excluded_dirs = NULL;
|
||||
p->excluded_count = 0;
|
||||
@@ -403,6 +406,10 @@ cbm_pipeline_publish_kind_t cbm_pipeline_publish_kind(const cbm_pipeline_t *p) {
|
||||
return p ? p->publish_kind : CBM_PIPELINE_PUBLISH_NONE;
|
||||
}
|
||||
|
||||
const char *cbm_pipeline_publish_reason(const cbm_pipeline_t *p) {
|
||||
return p ? p->publish_reason : NULL;
|
||||
}
|
||||
|
||||
const char *cbm_pipeline_publish_kind_name(cbm_pipeline_publish_kind_t kind) {
|
||||
switch (kind) {
|
||||
case CBM_PIPELINE_PUBLISH_NONE:
|
||||
@@ -432,6 +439,15 @@ void cbm_pipeline_set_publish_kind(cbm_pipeline_t *p, cbm_pipeline_publish_kind_
|
||||
}
|
||||
}
|
||||
|
||||
void cbm_pipeline_set_publish_reason(cbm_pipeline_t *p, const char *reason) {
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
char *next = (reason && reason[0]) ? cbm_strdup(reason) : NULL;
|
||||
free(p->publish_reason);
|
||||
p->publish_reason = next;
|
||||
}
|
||||
|
||||
static bool resolve_db_path_buf(const cbm_pipeline_t *p, char *path, size_t path_sz) {
|
||||
if (!p || !path || path_sz == 0) {
|
||||
return false;
|
||||
@@ -1272,6 +1288,7 @@ int cbm_pipeline_run(cbm_pipeline_t *p) {
|
||||
}
|
||||
p->graph_changed = false;
|
||||
p->publish_kind = CBM_PIPELINE_PUBLISH_NONE;
|
||||
cbm_pipeline_set_publish_reason(p, NULL);
|
||||
p->committed_nodes = -1;
|
||||
p->committed_edges = -1;
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ bool cbm_pipeline_graph_changed(const cbm_pipeline_t *p);
|
||||
* that need derived-view policy decisions; it does not change graph contents. */
|
||||
cbm_pipeline_publish_kind_t cbm_pipeline_publish_kind(const cbm_pipeline_t *p);
|
||||
const char *cbm_pipeline_publish_kind_name(cbm_pipeline_publish_kind_t kind);
|
||||
const char *cbm_pipeline_publish_reason(const cbm_pipeline_t *p);
|
||||
|
||||
/* ── Index lock (prevents concurrent pipeline runs on same DB) ──── */
|
||||
|
||||
|
||||
@@ -881,6 +881,7 @@ static int incr_try_exact_delete_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
|
||||
const char *rel_path = deleted[0];
|
||||
if (!rel_path || !rel_path[0]) {
|
||||
cbm_pipeline_set_publish_reason(p, "missing_rel_path");
|
||||
cbm_log_info("incremental.exact.delete.fallback", "reason", "missing_rel_path");
|
||||
return CBM_STORE_OK;
|
||||
}
|
||||
@@ -901,6 +902,7 @@ static int incr_try_exact_delete_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
CBM_PIPELINE_EXACT_DELTA_MAX_AFFECTED_PATHS,
|
||||
&plan);
|
||||
if (rc != CBM_STORE_OK || plan.route != CBM_PIPELINE_DELTA_ROUTE_EXACT_CANDIDATE) {
|
||||
cbm_pipeline_set_publish_reason(p, plan.reason ? plan.reason : "plan_error");
|
||||
cbm_log_info("incremental.exact.delete.fallback", "reason",
|
||||
plan.reason ? plan.reason : "plan_error");
|
||||
cbm_pipeline_file_delta_plan_free(&plan);
|
||||
@@ -911,6 +913,7 @@ static int incr_try_exact_delete_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
int64_t generation = 0;
|
||||
rc = cbm_store_reserve_index_generation(store, project, NULL, NULL, &generation);
|
||||
if (rc != CBM_STORE_OK || generation <= 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "reserve_generation");
|
||||
cbm_log_info("incremental.exact.delete.fallback", "reason", "reserve_generation", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
return CBM_STORE_OK;
|
||||
@@ -921,6 +924,7 @@ static int incr_try_exact_delete_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
CBM_PIPELINE_EXACT_DELTA_MAX_AFFECTED_PATHS, &plan);
|
||||
if (rc != CBM_STORE_OK || plan.route != CBM_PIPELINE_DELTA_ROUTE_EXACT_CANDIDATE) {
|
||||
incr_mark_generation_failed(store, project, generation);
|
||||
cbm_pipeline_set_publish_reason(p, plan.reason ? plan.reason : "apply_error");
|
||||
cbm_log_info("incremental.exact.delete.fallback", "reason",
|
||||
plan.reason ? plan.reason : "apply_error");
|
||||
cbm_pipeline_file_delta_plan_free(&plan);
|
||||
@@ -932,6 +936,7 @@ static int incr_try_exact_delete_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
cbm_store_count_edges(store, project));
|
||||
cbm_pipeline_set_graph_changed(p, true);
|
||||
cbm_pipeline_set_publish_kind(p, CBM_PIPELINE_PUBLISH_INCREMENTAL_EXACT);
|
||||
cbm_pipeline_set_publish_reason(p, NULL);
|
||||
if (cbm_pipeline_repo_path(p) && cbm_artifact_exists(cbm_pipeline_repo_path(p))) {
|
||||
(void)cbm_artifact_export(db_path, cbm_pipeline_repo_path(p), project, CBM_ARTIFACT_FAST);
|
||||
}
|
||||
@@ -954,14 +959,15 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
if (deleted_count != 0 || changed_count > CBM_PIPELINE_EXACT_DELTA_MAX_CHANGED_PATHS ||
|
||||
cbm_pipeline_get_mode(p) < CBM_MODE_FAST ||
|
||||
changed_count > CBM_PIPELINE_EXACT_DELTA_MAX_AFFECTED_PATHS) {
|
||||
cbm_log_info("incremental.exact.skip", "reason",
|
||||
deleted_count != 0
|
||||
? "has_deletes"
|
||||
: (changed_count > CBM_PIPELINE_EXACT_DELTA_MAX_CHANGED_PATHS
|
||||
? "changed_batch_too_large"
|
||||
: (cbm_pipeline_get_mode(p) < CBM_MODE_FAST
|
||||
? "global_derived_edges"
|
||||
: "frontier_too_large")));
|
||||
const char *reason =
|
||||
deleted_count != 0
|
||||
? "has_deletes"
|
||||
: (changed_count > CBM_PIPELINE_EXACT_DELTA_MAX_CHANGED_PATHS
|
||||
? "changed_batch_too_large"
|
||||
: (cbm_pipeline_get_mode(p) < CBM_MODE_FAST ? "global_derived_edges"
|
||||
: "frontier_too_large"));
|
||||
cbm_pipeline_set_publish_reason(p, reason);
|
||||
cbm_log_info("incremental.exact.skip", "reason", reason);
|
||||
return CBM_STORE_OK;
|
||||
}
|
||||
|
||||
@@ -983,11 +989,13 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
scratch = cbm_gbuf_new(project, cbm_pipeline_repo_path(p));
|
||||
registry = cbm_registry_new();
|
||||
if (!changed_paths || !deltas || !delta_ptrs || !result_cache || !scratch || !registry) {
|
||||
cbm_pipeline_set_publish_reason(p, "alloc");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "alloc");
|
||||
goto cleanup;
|
||||
}
|
||||
for (int i = 0; i < changed_count; i++) {
|
||||
if (!changed_files[i].rel_path) {
|
||||
cbm_pipeline_set_publish_reason(p, "missing_rel_path");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "missing_rel_path");
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -999,6 +1007,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
store, scratch, registry, project, changed_paths, changed_count);
|
||||
CBM_PROF_END_N("incremental_exact", "1_seed_scratch", t_exact_seed, changed_count);
|
||||
if (rc != CBM_STORE_OK) {
|
||||
cbm_pipeline_set_publish_reason(p, "scratch_seed");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "scratch_seed", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1030,6 +1039,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = cbm_pipeline_ensure_file_structure(scratch, project, structure_root_qn,
|
||||
changed_files[i].rel_path, NULL);
|
||||
if (rc != 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "ensure_structure");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "ensure_structure", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1041,6 +1051,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
CBM_PROF_END_N("incremental_exact", "3_extract_resolve", t_exact_extract_resolve,
|
||||
changed_count);
|
||||
if (rc != 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "extract_resolve");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "extract_resolve", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1049,6 +1060,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = cbm_pipeline_pass_k8s(&ctx, changed_files, changed_count);
|
||||
CBM_PROF_END_N("incremental_exact", "4_k8s", t_exact_k8s, changed_count);
|
||||
if (rc != 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "k8s");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "k8s", "rc", itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1056,6 +1068,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = run_postpasses(&ctx, changed_files, changed_count, project);
|
||||
CBM_PROF_END_N("incremental_exact", "5_postpasses", t_exact_postpasses, changed_count);
|
||||
if (rc != 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "postpasses");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "postpasses", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1067,6 +1080,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = cbm_pipeline_pass_httplinks(&ctx);
|
||||
CBM_PROF_END_N("incremental_exact", "7_httplinks", t_exact_httplinks, changed_count);
|
||||
if (rc != 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "httplinks");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "httplinks", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1080,6 +1094,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = cbm_pipeline_build_file_delta_from_gbuf(scratch, project, changed_files[i].rel_path,
|
||||
CBM_PIPELINE_COMPAT_GENERATION, &deltas[i]);
|
||||
if (rc != CBM_STORE_OK) {
|
||||
cbm_pipeline_set_publish_reason(p, "build_delta");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "build_delta", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1088,6 +1103,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
&changed_files[i],
|
||||
pass_fingerprint);
|
||||
if (rc != CBM_STORE_OK) {
|
||||
cbm_pipeline_set_publish_reason(p, "metadata");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "metadata", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1101,6 +1117,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
CBM_PIPELINE_EXACT_DELTA_MAX_AFFECTED_PATHS, &plan);
|
||||
CBM_PROF_END_N("incremental_exact", "10_plan_delta", t_exact_plan, changed_count);
|
||||
if (rc != CBM_STORE_OK || plan.route != CBM_PIPELINE_DELTA_ROUTE_EXACT_CANDIDATE) {
|
||||
cbm_pipeline_set_publish_reason(p, plan.reason ? plan.reason : "plan_error");
|
||||
cbm_log_info("incremental.exact.fallback", "reason",
|
||||
plan.reason ? plan.reason : "plan_error");
|
||||
goto cleanup;
|
||||
@@ -1111,6 +1128,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = cbm_store_reserve_index_generation(store, project, NULL, NULL, &generation);
|
||||
CBM_PROF_END("incremental_exact", "11_reserve_generation", t_exact_reserve);
|
||||
if (rc != CBM_STORE_OK || generation <= 0) {
|
||||
cbm_pipeline_set_publish_reason(p, "reserve_generation");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "reserve_generation", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1120,6 +1138,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
rc = cbm_pipeline_file_delta_stamp_generation(&deltas[i], generation);
|
||||
if (rc != CBM_STORE_OK) {
|
||||
incr_mark_generation_failed(store, project, generation);
|
||||
cbm_pipeline_set_publish_reason(p, "stamp_generation");
|
||||
cbm_log_info("incremental.exact.fallback", "reason", "stamp_generation", "rc",
|
||||
itoa_buf_incr(rc));
|
||||
goto cleanup;
|
||||
@@ -1133,6 +1152,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
CBM_PROF_END_N("incremental_exact", "13_apply_delta", t_exact_apply, changed_count);
|
||||
if (rc != CBM_STORE_OK || plan.route != CBM_PIPELINE_DELTA_ROUTE_EXACT_CANDIDATE) {
|
||||
incr_mark_generation_failed(store, project, generation);
|
||||
cbm_pipeline_set_publish_reason(p, plan.reason ? plan.reason : "apply_error");
|
||||
cbm_log_info("incremental.exact.fallback", "reason",
|
||||
plan.reason ? plan.reason : "apply_error");
|
||||
goto cleanup;
|
||||
@@ -1142,6 +1162,7 @@ static int incr_try_exact_upsert_route(cbm_pipeline_t *p, cbm_store_t *store, co
|
||||
cbm_store_count_edges(store, project));
|
||||
cbm_pipeline_set_graph_changed(p, true);
|
||||
cbm_pipeline_set_publish_kind(p, CBM_PIPELINE_PUBLISH_INCREMENTAL_EXACT);
|
||||
cbm_pipeline_set_publish_reason(p, NULL);
|
||||
if (cbm_pipeline_repo_path(p) && cbm_artifact_exists(cbm_pipeline_repo_path(p))) {
|
||||
(void)cbm_artifact_export(db_path, cbm_pipeline_repo_path(p), project, CBM_ARTIFACT_FAST);
|
||||
}
|
||||
@@ -1526,6 +1547,9 @@ int cbm_pipeline_run_incremental(cbm_pipeline_t *p, const char *db_path, cbm_fil
|
||||
if (persist_rc == 0) {
|
||||
cbm_pipeline_set_graph_changed(p, true);
|
||||
cbm_pipeline_set_publish_kind(p, CBM_PIPELINE_PUBLISH_INCREMENTAL_CONTAINMENT);
|
||||
if (!cbm_pipeline_publish_reason(p)) {
|
||||
cbm_pipeline_set_publish_reason(p, "containment_rebuild");
|
||||
}
|
||||
}
|
||||
incr_classification_free(&cls);
|
||||
cbm_gbuf_free(existing);
|
||||
|
||||
@@ -758,6 +758,7 @@ atomic_int *cbm_pipeline_cancelled_ptr(cbm_pipeline_t *p);
|
||||
void cbm_pipeline_set_committed_counts(cbm_pipeline_t *p, int nodes, int edges);
|
||||
void cbm_pipeline_set_graph_changed(cbm_pipeline_t *p, bool changed);
|
||||
void cbm_pipeline_set_publish_kind(cbm_pipeline_t *p, cbm_pipeline_publish_kind_t kind);
|
||||
void cbm_pipeline_set_publish_reason(cbm_pipeline_t *p, const char *reason);
|
||||
|
||||
/* Parse a gRPC stub call "<service-stub>.<method>" into the canonical proto
|
||||
* service name + method. Returns true ONLY when a recognized gRPC stub/client
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "test_framework.h"
|
||||
#include <cli/cli.h>
|
||||
#include <mcp/mcp.h>
|
||||
#include <pipeline/pipeline.h>
|
||||
#include <store/store.h>
|
||||
#include <sqlite3.h>
|
||||
#include <yyjson/yyjson.h>
|
||||
@@ -1647,6 +1648,81 @@ TEST(tool_index_repository_missing_path) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(tool_index_repository_reports_incremental_containment_reason) {
|
||||
char *repo_tmp = th_mktempdir("cbm_mcp_publish_reason_repo");
|
||||
if (!repo_tmp) {
|
||||
PASS();
|
||||
}
|
||||
char repo[CBM_PATH_MAX];
|
||||
int n = snprintf(repo, sizeof(repo), "%s", repo_tmp);
|
||||
ASSERT(n >= 0 && (size_t)n < sizeof(repo));
|
||||
|
||||
char *cache_tmp = th_mktempdir("cbm_mcp_publish_reason_cache");
|
||||
ASSERT_NOT_NULL(cache_tmp);
|
||||
char cache[CBM_PATH_MAX];
|
||||
n = snprintf(cache, sizeof(cache), "%s", cache_tmp);
|
||||
ASSERT(n >= 0 && (size_t)n < sizeof(cache));
|
||||
|
||||
const char *saved = getenv("CBM_CACHE_DIR");
|
||||
char *saved_copy = saved ? cbm_strdup(saved) : NULL;
|
||||
cbm_setenv("CBM_CACHE_DIR", cache, 1);
|
||||
|
||||
cbm_config_t *cfg = cbm_config_open(cache);
|
||||
ASSERT_NOT_NULL(cfg);
|
||||
ASSERT_EQ(cbm_config_set(cfg, CBM_CONFIG_INCREMENTAL_REINDEX, "always"), 0);
|
||||
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "go.mod"), "module example.com/pubreason\n\ngo 1.22\n"),
|
||||
0);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "main.go"), "package main\n\nfunc main() {}\n"), 0);
|
||||
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
ASSERT_NOT_NULL(srv);
|
||||
cbm_mcp_server_set_config(srv, cfg);
|
||||
|
||||
char req[CBM_SZ_4K];
|
||||
n = snprintf(req, sizeof(req),
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":41,\"method\":\"tools/call\","
|
||||
"\"params\":{\"name\":\"index_repository\","
|
||||
"\"arguments\":{\"repo_path\":\"%s\",\"mode\":\"fast\"}}}",
|
||||
repo);
|
||||
ASSERT(n >= 0 && (size_t)n < sizeof(req));
|
||||
char *resp = cbm_mcp_server_handle(srv, req);
|
||||
ASSERT_NOT_NULL(resp);
|
||||
ASSERT_NOT_NULL(strstr(resp, "indexed"));
|
||||
free(resp);
|
||||
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "pkg/file_created.go"),
|
||||
"package pkg\n\nfunc Created() int {\n\treturn 7\n}\n"),
|
||||
0);
|
||||
|
||||
n = snprintf(req, sizeof(req),
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":42,\"method\":\"tools/call\","
|
||||
"\"params\":{\"name\":\"index_repository\","
|
||||
"\"arguments\":{\"repo_path\":\"%s\",\"mode\":\"fast\"}}}",
|
||||
repo);
|
||||
ASSERT(n >= 0 && (size_t)n < sizeof(req));
|
||||
resp = cbm_mcp_server_handle(srv, req);
|
||||
ASSERT_NOT_NULL(resp);
|
||||
char *inner = extract_text_content(resp);
|
||||
ASSERT_NOT_NULL(inner);
|
||||
ASSERT_NOT_NULL(strstr(inner, "\"publish_kind\":\"incremental_containment\""));
|
||||
ASSERT_NOT_NULL(strstr(inner, "\"publish_reason\":\"missing_existing_ownership\""));
|
||||
|
||||
free(inner);
|
||||
free(resp);
|
||||
cbm_mcp_server_free(srv);
|
||||
cbm_config_close(cfg);
|
||||
if (saved_copy) {
|
||||
cbm_setenv("CBM_CACHE_DIR", saved_copy, 1);
|
||||
free(saved_copy);
|
||||
} else {
|
||||
cbm_unsetenv("CBM_CACHE_DIR");
|
||||
}
|
||||
th_cleanup(repo);
|
||||
th_cleanup(cache);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(tool_get_code_snippet_missing_qn) {
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
|
||||
@@ -3457,6 +3533,7 @@ SUITE(mcp) {
|
||||
|
||||
/* Pipeline-dependent tool handlers */
|
||||
RUN_TEST(tool_index_repository_missing_path);
|
||||
RUN_TEST(tool_index_repository_reports_incremental_containment_reason);
|
||||
RUN_TEST(tool_get_code_snippet_missing_qn);
|
||||
RUN_TEST(tool_get_code_snippet_not_found);
|
||||
RUN_TEST(tool_search_code_missing_pattern);
|
||||
|
||||
@@ -8694,6 +8694,7 @@ TEST(incremental_fast_falls_back_for_inbound_transitive_complexity_and_matches_f
|
||||
ASSERT(strstr(logs, "msg=incremental.exact.fallback reason=inbound_edges_require_full") !=
|
||||
NULL);
|
||||
ASSERT_EQ(cbm_pipeline_publish_kind(p), CBM_PIPELINE_PUBLISH_INCREMENTAL_CONTAINMENT);
|
||||
ASSERT_STR_EQ(cbm_pipeline_publish_reason(p), "inbound_edges_require_full");
|
||||
cbm_pipeline_free(p);
|
||||
|
||||
char diff_err[CBM_SZ_8K] = {0};
|
||||
@@ -8760,6 +8761,7 @@ TEST(incremental_fast_three_file_batch_falls_back_to_full_rebuild_parity) {
|
||||
cbm_pipeline_apply_config(p, cfg);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
ASSERT_EQ(cbm_pipeline_publish_kind(p), CBM_PIPELINE_PUBLISH_INCREMENTAL_CONTAINMENT);
|
||||
ASSERT_STR_EQ(cbm_pipeline_publish_reason(p), "changed_batch_too_large");
|
||||
cbm_pipeline_free(p);
|
||||
|
||||
ASSERT(pipeline_store_has_function_name(g_incr_dbpath, project, "NewMain"));
|
||||
|
||||
Reference in New Issue
Block a user