diff --git a/src/mcp/mcp.c b/src/mcp/mcp.c index c6024a99d..25bcb5d72 100644 --- a/src/mcp/mcp.c +++ b/src/mcp/mcp.c @@ -4995,6 +4995,9 @@ static char *handle_index_repository(cbm_mcp_server_t *srv, const char *args) { yyjson_mut_obj_add_str(doc, root, "project", project_name); 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)); + yyjson_mut_obj_add_bool(doc, root, "graph_changed", graph_changed); if (rc == 0) { cbm_store_t *store = resolve_store(srv, project_name); diff --git a/src/pipeline/pipeline.c b/src/pipeline/pipeline.c index 02a2a0f67..ccf980196 100644 --- a/src/pipeline/pipeline.c +++ b/src/pipeline/pipeline.c @@ -403,6 +403,23 @@ 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_kind_name(cbm_pipeline_publish_kind_t kind) { + switch (kind) { + case CBM_PIPELINE_PUBLISH_NONE: + return "none"; + case CBM_PIPELINE_PUBLISH_FULL: + return "full"; + case CBM_PIPELINE_PUBLISH_INCREMENTAL_NOOP: + return "incremental_noop"; + case CBM_PIPELINE_PUBLISH_INCREMENTAL_EXACT: + return "incremental_exact"; + case CBM_PIPELINE_PUBLISH_INCREMENTAL_CONTAINMENT: + return "incremental_containment"; + default: + return "unknown"; + } +} + void cbm_pipeline_set_graph_changed(cbm_pipeline_t *p, bool changed) { if (p) { p->graph_changed = changed; diff --git a/src/pipeline/pipeline.h b/src/pipeline/pipeline.h index 0a280b4be..23646355b 100644 --- a/src/pipeline/pipeline.h +++ b/src/pipeline/pipeline.h @@ -141,6 +141,7 @@ bool cbm_pipeline_graph_changed(const cbm_pipeline_t *p); /* Last publish route for the most recent run. This is observability for callers * 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); /* ── Index lock (prevents concurrent pipeline runs on same DB) ──── */ diff --git a/tests/test_incremental.c b/tests/test_incremental.c index d95215a48..87ce541c2 100644 --- a/tests/test_incremental.c +++ b/tests/test_incremental.c @@ -2174,6 +2174,21 @@ TEST(tool_index_mode_fast) { PASS(); } +TEST(tool_index_publish_metadata) { + double ms; + char *r = call_tool_timed("index_repository", &ms, "{\"repo_path\":\"%s\",\"mode\":\"fast\"}", + g_repodir); + ASSERT(r != NULL); + ASSERT(strstr(r, "\\\"publish_kind\\\":\\\"") != NULL); + ASSERT(strstr(r, "\\\"graph_changed\\\":") != NULL); + ASSERT(strstr(r, "\\\"publish_kind\\\":\\\"full\\\"") != NULL || + strstr(r, "\\\"publish_kind\\\":\\\"incremental_noop\\\"") != NULL || + strstr(r, "\\\"publish_kind\\\":\\\"incremental_exact\\\"") != NULL || + strstr(r, "\\\"publish_kind\\\":\\\"incremental_containment\\\"") != NULL); + free(r); + PASS(); +} + TEST(tool_index_invalid_path) { double ms; char *r = call_tool_timed("index_repository", &ms, "{\"repo_path\":\"/nonexistent/path/xyz\"}"); @@ -3264,6 +3279,7 @@ SUITE(incremental) { /* Phase 19: index_repository params */ RUN_TEST(tool_index_mode_fast); + RUN_TEST(tool_index_publish_metadata); RUN_TEST(tool_index_invalid_path); RUN_TEST(tool_index_missing_param); diff --git a/tests/test_pipeline.c b/tests/test_pipeline.c index 999132571..6aaf61676 100644 --- a/tests/test_pipeline.c +++ b/tests/test_pipeline.c @@ -10013,6 +10013,19 @@ TEST(pipeline_unit_threshold_setters_clamp_invalid_values) { PASS(); } +TEST(pipeline_publish_kind_names_are_stable) { + ASSERT_STR_EQ(cbm_pipeline_publish_kind_name(CBM_PIPELINE_PUBLISH_NONE), "none"); + ASSERT_STR_EQ(cbm_pipeline_publish_kind_name(CBM_PIPELINE_PUBLISH_FULL), "full"); + ASSERT_STR_EQ(cbm_pipeline_publish_kind_name(CBM_PIPELINE_PUBLISH_INCREMENTAL_NOOP), + "incremental_noop"); + ASSERT_STR_EQ(cbm_pipeline_publish_kind_name(CBM_PIPELINE_PUBLISH_INCREMENTAL_EXACT), + "incremental_exact"); + ASSERT_STR_EQ(cbm_pipeline_publish_kind_name(CBM_PIPELINE_PUBLISH_INCREMENTAL_CONTAINMENT), + "incremental_containment"); + ASSERT_STR_EQ(cbm_pipeline_publish_kind_name((cbm_pipeline_publish_kind_t)999), "unknown"); + PASS(); +} + TEST(pipeline_apply_config_sets_all_thresholds) { char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cbm_pipeline_cfg_XXXXXX"); @@ -11175,6 +11188,7 @@ SUITE(pipeline) { /* Resource management & internal helper tests */ RUN_TEST(pipeline_empty_path); RUN_TEST(pipeline_project_name_content); + RUN_TEST(pipeline_publish_kind_names_are_stable); RUN_TEST(pipeline_cancel_sets_flag); RUN_TEST(pipeline_double_cancel); RUN_TEST(pipeline_double_free_prevention);