@@ -6908,6 +6908,54 @@ static void add_parse_partial_summary(yyjson_mut_doc *doc, yyjson_mut_val *root,
69086908 yyjson_mut_obj_add_val (doc , root , "parse_partial" , pp );
69096909}
69106910
6911+ /* The pipeline persists the complete current coverage set before this
6912+ * response is built. Prefer that set over the per-run errors so incremental
6913+ * runs that do not revisit a flagged file, and artifact bootstraps, do not
6914+ * make existing gaps appear to have vanished. By-design exclusions have
6915+ * their own response surface and are not failures. */
6916+ static bool add_persisted_failure_summaries (yyjson_mut_doc * doc , yyjson_mut_val * root ,
6917+ cbm_store_t * store , const char * project ,
6918+ const char * logfile ) {
6919+ cbm_coverage_row_t * rows = NULL ;
6920+ int row_count = 0 ;
6921+ if (cbm_store_coverage_get (store , project , & rows , & row_count ) != CBM_STORE_OK ) {
6922+ return false;
6923+ }
6924+
6925+ int failure_count = 0 ;
6926+ for (int i = 0 ; i < row_count ; i ++ ) {
6927+ const char * kind = rows [i ].kind ? rows [i ].kind : "" ;
6928+ if (strcmp (kind , "not_indexed_dir" ) != 0 && strcmp (kind , "not_indexed_file" ) != 0 ) {
6929+ failure_count ++ ;
6930+ }
6931+ }
6932+
6933+ cbm_file_error_t * failures =
6934+ failure_count > 0 ? calloc ((size_t )failure_count , sizeof (* failures )) : NULL ;
6935+ if (failure_count > 0 && !failures ) {
6936+ cbm_store_free_coverage (rows , row_count );
6937+ return false;
6938+ }
6939+
6940+ int n = 0 ;
6941+ for (int i = 0 ; i < row_count ; i ++ ) {
6942+ const char * kind = rows [i ].kind ? rows [i ].kind : "" ;
6943+ if (strcmp (kind , "not_indexed_dir" ) == 0 || strcmp (kind , "not_indexed_file" ) == 0 ) {
6944+ continue ;
6945+ }
6946+ failures [n ].path = (char * )rows [i ].rel_path ;
6947+ failures [n ].reason = (char * )rows [i ].detail ;
6948+ failures [n ].phase = (char * )rows [i ].kind ;
6949+ n ++ ;
6950+ }
6951+
6952+ add_skipped_summary (doc , root , failures , failure_count , logfile );
6953+ add_parse_partial_summary (doc , root , failures , failure_count );
6954+ free (failures );
6955+ cbm_store_free_coverage (rows , row_count );
6956+ return true;
6957+ }
6958+
69116959/* Write the FULL (uncapped) skip list to a per-run logfile — ONLY when >=1 file
69126960 * was skipped (no logfile on a clean run). Location:
69136961 * $CBM_INDEX_LOG (override) else <cache_dir>/logs/<project>-<epoch>.log
@@ -6967,8 +7015,6 @@ static bool build_index_success_response(cbm_mcp_server_t *srv, yyjson_mut_doc *
69677015 const cbm_file_error_t * file_errors , int file_error_count ,
69687016 const char * logfile ) {
69697017 add_excluded_summary (doc , root , excluded_dirs , excluded_count );
6970- add_skipped_summary (doc , root , file_errors , file_error_count , logfile );
6971- add_parse_partial_summary (doc , root , file_errors , file_error_count );
69727018 add_not_indexed_files_summary (doc , root , p );
69737019
69747020 int exp_nodes = -1 ;
@@ -6979,6 +7025,10 @@ static bool build_index_success_response(cbm_mcp_server_t *srv, yyjson_mut_doc *
69797025 const int min_floor = CBM_DUMP_VERIFY_MIN_FLOOR ;
69807026
69817027 cbm_store_t * store = resolve_store (srv , project_name );
7028+ if (!store || !add_persisted_failure_summaries (doc , root , store , project_name , logfile )) {
7029+ add_skipped_summary (doc , root , file_errors , file_error_count , logfile );
7030+ add_parse_partial_summary (doc , root , file_errors , file_error_count );
7031+ }
69827032 int nodes = 0 ;
69837033 int edges = 0 ;
69847034 bool degraded = false;
0 commit comments