mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-03 08:03:38 +08:00
fix(mcp): report overlay freshness for schema resource
Make codebase://schema disclose canonical-only freshness when ready overlay rows exist, matching the resource behavior added for codebase://architecture. The schema content remains canonical, while freshness metadata reports the ready overlay/tombstone counts and warning text instead of silently omitting overlay-only labels. Add an MCP resources/read canary that publishes a ready overlay replacement, verifies the canonical Function schema remains visible, verifies the overlay-only Class label is not reported as canonical schema, and checks canonical_only freshness metadata. Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
@@ -9214,6 +9214,21 @@ static void build_resource_schema(yyjson_mut_doc *doc, yyjson_mut_val *root,
|
||||
}
|
||||
yyjson_mut_obj_add_val(doc, root, "edge_types", type_arr);
|
||||
cbm_store_schema_free(&schema);
|
||||
|
||||
bool overlay_limitation_reported = add_canonical_only_overlay_freshness(
|
||||
doc, root, store, proj,
|
||||
"codebase://schema reads canonical graph schema counts; ready overlay rows are not "
|
||||
"included until active schema resource views or compaction are available.");
|
||||
int dirty_pending = 0;
|
||||
int dirty_overlay_ready = 0;
|
||||
if (get_dirty_file_counts(store, proj, &dirty_pending, &dirty_overlay_ready)) {
|
||||
add_dirty_file_freshness_counts(doc, root, dirty_pending, dirty_overlay_ready);
|
||||
if (!overlay_limitation_reported) {
|
||||
add_response_warning(doc, root,
|
||||
"codebase://schema reads canonical graph schema counts; dirty "
|
||||
"file changes may be absent until overlay or reindex completes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* CBM_CONFIG_KEY_FUNCTIONS_EXCLUDE defined in constants section at top of file */
|
||||
|
||||
@@ -2636,6 +2636,57 @@ TEST(resource_architecture_reports_ready_overlay_canonical_only) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(resource_schema_reports_ready_overlay_canonical_only) {
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
ASSERT_NOT_NULL(srv);
|
||||
cbm_store_t *st = cbm_mcp_server_store(srv);
|
||||
ASSERT_NOT_NULL(st);
|
||||
|
||||
const char *proj = "resource-schema-overlay";
|
||||
ASSERT_EQ(cbm_store_upsert_project(st, proj, "/tmp/resource-schema-overlay"), CBM_STORE_OK);
|
||||
cbm_mcp_server_set_project(srv, proj);
|
||||
|
||||
cbm_node_t old_fn = {.project = proj,
|
||||
.label = "Function",
|
||||
.name = "OldResourceSchema",
|
||||
.qualified_name = "resource.schema.OldResourceSchema",
|
||||
.file_path = "src/main.c"};
|
||||
ASSERT_GT(cbm_store_upsert_node(st, &old_fn), 0);
|
||||
|
||||
int64_t overlay_generation = 0;
|
||||
ASSERT_EQ(cbm_store_reserve_overlay_generation(st, proj, 1, &overlay_generation),
|
||||
CBM_STORE_OK);
|
||||
cbm_node_t fresh_class = {.project = proj,
|
||||
.label = "Class",
|
||||
.name = "FreshResourceSchema",
|
||||
.qualified_name = "resource.schema.FreshResourceSchema",
|
||||
.file_path = "src/main.c",
|
||||
.properties_json = "{}"};
|
||||
cbm_store_file_delta_t delta = {.project = proj,
|
||||
.rel_path = "src/main.c",
|
||||
.generation = 1,
|
||||
.nodes = &fresh_class,
|
||||
.node_count = 1};
|
||||
ASSERT_EQ(cbm_store_publish_overlay_file_delta(st, &delta, overlay_generation),
|
||||
CBM_STORE_OK);
|
||||
|
||||
char *resp = cbm_mcp_server_handle(
|
||||
srv, "{\"jsonrpc\":\"2.0\",\"id\":100,\"method\":\"resources/read\","
|
||||
"\"params\":{\"uri\":\"codebase://schema\"}}");
|
||||
ASSERT_NOT_NULL(resp);
|
||||
ASSERT_NOT_NULL(strstr(resp, "\"contents\""));
|
||||
ASSERT_NOT_NULL(strstr(resp, "\\\"label\\\":\\\"Function\\\""));
|
||||
ASSERT_NULL(strstr(resp, "\\\"label\\\":\\\"Class\\\""));
|
||||
ASSERT_NOT_NULL(strstr(resp, "codebase://schema reads canonical graph schema counts"));
|
||||
ASSERT_NOT_NULL(strstr(resp, "ready overlay rows are not included"));
|
||||
ASSERT_NOT_NULL(strstr(resp, "\\\"read_model\\\":\\\"canonical_only\\\""));
|
||||
ASSERT_NOT_NULL(strstr(resp, "\\\"active_file_tombstones\\\":1"));
|
||||
|
||||
free(resp);
|
||||
cbm_mcp_server_free(srv);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(tool_get_architecture_path_scoping) {
|
||||
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
|
||||
ASSERT_NOT_NULL(srv);
|
||||
@@ -4987,6 +5038,7 @@ SUITE(mcp) {
|
||||
RUN_TEST(tool_get_architecture_uses_overlay_active_routes);
|
||||
RUN_TEST(tool_get_architecture_uses_overlay_active_file_summaries);
|
||||
RUN_TEST(resource_architecture_reports_ready_overlay_canonical_only);
|
||||
RUN_TEST(resource_schema_reports_ready_overlay_canonical_only);
|
||||
RUN_TEST(tool_get_architecture_path_scoping);
|
||||
RUN_TEST(tool_query_graph_missing_query);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user