From 2235af41db31338b45dfc251c4db7106df598410 Mon Sep 17 00:00:00 2001 From: SunneeYang Date: Fri, 14 Aug 2026 17:31:31 +0800 Subject: [PATCH 1/3] fix(mcp): preserve exact-path coverage when ignored records truncate Signed-off-by: SunneeYang --- src/mcp/mcp.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/mcp/mcp.c b/src/mcp/mcp.c index a0d3269f0..2f5865067 100644 --- a/src/mcp/mcp.c +++ b/src/mcp/mcp.c @@ -4213,7 +4213,8 @@ static void coverage_add_row_json(yyjson_mut_doc *doc, yyjson_mut_val *array, static const char *coverage_status(const cbm_coverage_row_t *rows, int count, const char *requested_path, const char *recording_status, - bool generation_matches, bool lookup_ok) { + bool generation_matches, bool lookup_ok, + bool exact_path_verified) { if (!lookup_ok) { return "coverage_unavailable"; } @@ -4241,7 +4242,11 @@ static const char *coverage_status(const cbm_coverage_row_t *rows, int count, } } } - if (!generation_matches || !recording_status || strcmp(recording_status, "complete") != 0) { + bool recording_complete = + recording_status && strcmp(recording_status, "complete") == 0; + bool truncated_exact_path_verified = exact_path_verified && recording_status && + strcmp(recording_status, "truncated") == 0; + if (!generation_matches || (!recording_complete && !truncated_exact_path_verified)) { return "coverage_unavailable"; } return "no_recorded_issue"; @@ -4358,9 +4363,12 @@ static char *handle_check_index_coverage(cbm_mcp_server_t *srv, const char *args bool outside = false; const char *freshness = coverage_path_freshness( store, project, have_project ? proj.root_path : NULL, rel, &outside); + bool exact_path_verified = have_meta && meta.hash_records_complete && + strcmp(freshness, "metadata_match") == 0; const char *status = outside ? "outside_project" : coverage_status(rows, row_count, rel, recording_status, - generation_matches, lookup_ok); + generation_matches, lookup_ok, + exact_path_verified); yyjson_mut_obj_add_strcpy(doc, item, "status", status); yyjson_mut_obj_add_strcpy(doc, item, "freshness", freshness); yyjson_mut_obj_add_strcpy(doc, item, "recommended_action", From b5c89ee58e4e7efcd17822916b4dd0d941d96547 Mon Sep 17 00:00:00 2001 From: SunneeYang Date: Fri, 14 Aug 2026 17:32:13 +0800 Subject: [PATCH 2/3] test(mcp): cover truncated ignored catalogs for exact paths Signed-off-by: SunneeYang --- tests/test_mcp.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/test_mcp.c b/tests/test_mcp.c index 1d99d71ad..8e48e06e8 100644 --- a/tests/test_mcp.c +++ b/tests/test_mcp.c @@ -2531,6 +2531,72 @@ static int write_coverage_meta(cbm_store_t *store, const char *generation, return cbm_store_coverage_replace_ex(store, "test-project", NULL, 0, &meta); } +TEST(tool_check_index_coverage_accepts_truncated_ignored_catalog_for_fresh_path_issue1613) { + char tmp[256]; + cbm_mcp_server_t *srv = setup_snippet_server(tmp, sizeof(tmp)); + ASSERT_NOT_NULL(srv); + cbm_store_t *store = cbm_mcp_server_store(srv); + ASSERT_NOT_NULL(store); + char source_path[512]; + snprintf(source_path, sizeof(source_path), "%s/project/main.go", tmp); + struct stat source_stat; + ASSERT_EQ(stat(source_path, &source_stat), 0); +#ifdef __APPLE__ + int64_t source_mtime_ns = + ((int64_t)source_stat.st_mtimespec.tv_sec * (int64_t)CBM_NSEC_PER_SEC) + + (int64_t)source_stat.st_mtimespec.tv_nsec; +#elif defined(_WIN32) + int64_t source_mtime_ns = + (int64_t)source_stat.st_mtime * (int64_t)CBM_NSEC_PER_SEC; +#else + int64_t source_mtime_ns = + ((int64_t)source_stat.st_mtim.tv_sec * (int64_t)CBM_NSEC_PER_SEC) + + (int64_t)source_stat.st_mtim.tv_nsec; +#endif + ASSERT_EQ(cbm_store_upsert_file_hash(store, "test-project", "main.go", "", + source_mtime_ns, source_stat.st_size), + CBM_STORE_OK); + cbm_project_t project = {0}; + ASSERT_EQ(cbm_store_get_project(store, "test-project", &project), CBM_STORE_OK); + cbm_coverage_meta_t meta = { + .generation = project.indexed_at, + .index_mode = "fast", + .recorded_at = "2026-07-12T00:00:00Z", + .recording_status = "truncated", + .ignored_files_stored = 2000, + .ignored_files_total = 2001, + .coverage_version = 1, + .hash_records_complete = true, + }; + ASSERT_EQ(cbm_store_coverage_replace_ex(store, "test-project", NULL, 0, &meta), + CBM_STORE_OK); + cbm_project_free_fields(&project); + + char *response = cbm_mcp_handle_tool( + srv, "check_index_coverage", + "{\"project\":\"test-project\",\"paths\":[\"main.go\"],\"scopes\":[\".\"]}"); + ASSERT_NOT_NULL(response); + char *inner = extract_text_content(response); + ASSERT_NOT_NULL(inner); + yyjson_doc *doc = yyjson_read(inner, strlen(inner), 0); + ASSERT_NOT_NULL(doc); + yyjson_val *root = yyjson_doc_get_root(doc); + yyjson_val *path = yyjson_arr_get(yyjson_obj_get(root, "paths"), 0); + ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(path, "status")), "no_recorded_issue"); + ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(path, "freshness")), "metadata_match"); + ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(path, "recommended_action")), + "use_graph_with_best_effort_caveat"); + yyjson_val *scope = yyjson_arr_get(yyjson_obj_get(root, "scopes"), 0); + ASSERT_STR_EQ(yyjson_get_str(yyjson_obj_get(scope, "status")), "coverage_unavailable"); + + yyjson_doc_free(doc); + free(inner); + free(response); + cbm_mcp_server_free(srv); + cleanup_snippet_dir(tmp); + PASS(); +} + TEST(tool_check_index_coverage_rejects_stale_generation) { char tmp[256]; cbm_mcp_server_t *srv = setup_snippet_server(tmp, sizeof(tmp)); @@ -10611,6 +10677,7 @@ SUITE(mcp) { RUN_TEST(tool_check_index_coverage_finds_path_beyond_status_cap); RUN_TEST(tool_check_index_coverage_reports_paths_scopes_and_ranges); RUN_TEST(tool_check_index_coverage_preserves_multiple_scope_labels); + RUN_TEST(tool_check_index_coverage_accepts_truncated_ignored_catalog_for_fresh_path_issue1613); RUN_TEST(tool_check_index_coverage_rejects_stale_generation); RUN_TEST(tool_check_index_coverage_requires_source_when_file_metadata_changed); RUN_TEST(tool_check_index_coverage_surfaces_lookup_errors); From a8283e930d852771524f57d0da8a78e315f2c196 Mon Sep 17 00:00:00 2001 From: SunneeYang Date: Fri, 14 Aug 2026 18:26:29 +0800 Subject: [PATCH 3/3] chore(mcp): apply clang-format to coverage fix Signed-off-by: SunneeYang --- src/mcp/mcp.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/mcp/mcp.c b/src/mcp/mcp.c index 2f5865067..622a72f8b 100644 --- a/src/mcp/mcp.c +++ b/src/mcp/mcp.c @@ -4242,10 +4242,9 @@ static const char *coverage_status(const cbm_coverage_row_t *rows, int count, } } } - bool recording_complete = - recording_status && strcmp(recording_status, "complete") == 0; - bool truncated_exact_path_verified = exact_path_verified && recording_status && - strcmp(recording_status, "truncated") == 0; + bool recording_complete = recording_status && strcmp(recording_status, "complete") == 0; + bool truncated_exact_path_verified = + exact_path_verified && recording_status && strcmp(recording_status, "truncated") == 0; if (!generation_matches || (!recording_complete && !truncated_exact_path_verified)) { return "coverage_unavailable"; } @@ -4363,12 +4362,12 @@ static char *handle_check_index_coverage(cbm_mcp_server_t *srv, const char *args bool outside = false; const char *freshness = coverage_path_freshness( store, project, have_project ? proj.root_path : NULL, rel, &outside); - bool exact_path_verified = have_meta && meta.hash_records_complete && - strcmp(freshness, "metadata_match") == 0; - const char *status = outside ? "outside_project" - : coverage_status(rows, row_count, rel, recording_status, - generation_matches, lookup_ok, - exact_path_verified); + bool exact_path_verified = + have_meta && meta.hash_records_complete && strcmp(freshness, "metadata_match") == 0; + const char *status = + outside ? "outside_project" + : coverage_status(rows, row_count, rel, recording_status, + generation_matches, lookup_ok, exact_path_verified); yyjson_mut_obj_add_strcpy(doc, item, "status", status); yyjson_mut_obj_add_strcpy(doc, item, "freshness", freshness); yyjson_mut_obj_add_strcpy(doc, item, "recommended_action",