@@ -33,6 +33,7 @@ enum { CBM_DIR_PERMS = 0755, PL_RING = 4, PL_RING_MASK = 3, PL_SEQ_PASSES = 6, P
3333#include "foundation/compat_thread.h"
3434#include "foundation/profile.h"
3535#include "foundation/mem.h"
36+ #include "yyjson/yyjson.h"
3637
3738#include <stdint.h>
3839#include <stdio.h>
@@ -151,6 +152,66 @@ static const char *itoa_buf(int val) {
151152 return bufs [i ];
152153}
153154
155+ static const char * index_mode_name (cbm_index_mode_t mode ) {
156+ switch (mode ) {
157+ case CBM_MODE_FULL :
158+ return "full" ;
159+ case CBM_MODE_MODERATE :
160+ return "moderate" ;
161+ case CBM_MODE_FAST :
162+ return "fast" ;
163+ default :
164+ return "unknown" ;
165+ }
166+ }
167+
168+ static bool parse_index_mode (const char * properties_json , cbm_index_mode_t * out_mode ) {
169+ if (!properties_json || !out_mode ) {
170+ return false;
171+ }
172+
173+ yyjson_doc * doc = yyjson_read (properties_json , strlen (properties_json ), 0 );
174+ if (!doc ) {
175+ return false;
176+ }
177+
178+ yyjson_val * root = yyjson_doc_get_root (doc );
179+ yyjson_val * mode_value = yyjson_is_obj (root ) ? yyjson_obj_get (root , "index_mode" ) : NULL ;
180+ const char * mode_name =
181+ mode_value && yyjson_is_str (mode_value ) ? yyjson_get_str (mode_value ) : NULL ;
182+ bool parsed = true;
183+ if (mode_name && strcmp (mode_name , "full" ) == 0 ) {
184+ * out_mode = CBM_MODE_FULL ;
185+ } else if (mode_name && strcmp (mode_name , "moderate" ) == 0 ) {
186+ * out_mode = CBM_MODE_MODERATE ;
187+ } else if (mode_name && strcmp (mode_name , "fast" ) == 0 ) {
188+ * out_mode = CBM_MODE_FAST ;
189+ } else {
190+ parsed = false;
191+ }
192+
193+ yyjson_doc_free (doc );
194+ return parsed ;
195+ }
196+
197+ static bool index_mode_covers (cbm_index_mode_t stored_mode , cbm_index_mode_t requested_mode ) {
198+ return stored_mode >= CBM_MODE_FULL && stored_mode <= CBM_MODE_FAST &&
199+ requested_mode >= CBM_MODE_FULL && requested_mode <= CBM_MODE_FAST &&
200+ stored_mode <= requested_mode ;
201+ }
202+
203+ static bool read_stored_index_mode (cbm_store_t * store , const char * project ,
204+ cbm_index_mode_t * out_mode ) {
205+ cbm_node_t project_node = {0 };
206+ if (cbm_store_find_node_by_qn (store , project , project , & project_node ) != CBM_STORE_OK ) {
207+ return false;
208+ }
209+
210+ bool parsed = parse_index_mode (project_node .properties_json , out_mode );
211+ cbm_node_free_fields (& project_node );
212+ return parsed ;
213+ }
214+
154215/* Log current + peak RSS at a pipeline phase boundary (memory profiling). */
155216static void log_phase_mem (const char * phase ) {
156217 enum { PL_BYTES_PER_MB = 1024 * 1024 };
@@ -446,7 +507,11 @@ static int pass_structure(cbm_pipeline_t *p, const cbm_file_info_t *files, int f
446507 cbm_log_info ("pass.start" , "pass" , "structure" , "files" , itoa_buf (file_count ));
447508
448509 /* Project node */
449- cbm_gbuf_upsert_node (p -> gbuf , "Project" , p -> project_name , p -> project_name , NULL , 0 , 0 , "{}" );
510+ char project_props [CBM_SZ_64 ];
511+ snprintf (project_props , sizeof (project_props ), "{\"index_mode\":\"%s\"}" ,
512+ index_mode_name (p -> mode ));
513+ cbm_gbuf_upsert_node (p -> gbuf , "Project" , p -> project_name , p -> project_name , NULL , 0 , 0 ,
514+ project_props );
450515 const char * branch_qn = p -> branch_qn ? p -> branch_qn : p -> project_name ;
451516 const char * branch_name = p -> git_ctx .branch ? p -> git_ctx .branch : "working-tree" ;
452517 char branch_props [CBM_SZ_2K ];
@@ -998,17 +1063,26 @@ static int try_incremental_or_delete_db(cbm_pipeline_t *p, cbm_file_info_t *file
9981063 if (check_store && cbm_store_check_integrity (check_store )) {
9991064 cbm_file_hash_t * hashes = NULL ;
10001065 int hash_count = 0 ;
1066+ cbm_index_mode_t stored_mode = CBM_MODE_FAST ;
1067+ bool stored_mode_known = read_stored_index_mode (check_store , p -> project_name , & stored_mode );
10011068 cbm_store_get_file_hashes (check_store , p -> project_name , & hashes , & hash_count );
10021069 cbm_store_free_file_hashes (hashes , hash_count );
10031070 cbm_store_close (check_store );
1004- if (hash_count > 0 && file_count <= hash_count + (hash_count / PAIR_LEN )) {
1071+ bool mode_covered = stored_mode_known && index_mode_covers (stored_mode , p -> mode );
1072+ if (hash_count > 0 && mode_covered && file_count <= hash_count + (hash_count / PAIR_LEN )) {
10051073 cbm_log_info ("pipeline.route" , "path" , "incremental" , "stored_hashes" ,
1006- itoa_buf (hash_count ));
1007- int rc = cbm_pipeline_run_incremental (p , db_path , files , file_count );
1074+ itoa_buf (hash_count ), "stored_mode" , index_mode_name (stored_mode ),
1075+ "requested_mode" , index_mode_name (p -> mode ), "effective_mode" ,
1076+ index_mode_name (stored_mode ));
1077+ int rc = cbm_pipeline_run_incremental (p , db_path , files , file_count , stored_mode );
10081078 free (db_path );
10091079 return rc ;
10101080 }
1011- if (hash_count > 0 ) {
1081+ if (hash_count > 0 && !mode_covered ) {
1082+ cbm_log_info ("pipeline.route" , "path" , "mode_upgrade_reindex" , "stored_mode" ,
1083+ stored_mode_known ? index_mode_name (stored_mode ) : "unknown" ,
1084+ "requested_mode" , index_mode_name (p -> mode ));
1085+ } else if (hash_count > 0 ) {
10121086 cbm_log_info ("pipeline.route" , "path" , "mode_change_reindex" , "stored_hashes" ,
10131087 itoa_buf (hash_count ), "discovered" , itoa_buf (file_count ));
10141088 }
0 commit comments