@@ -462,116 +462,127 @@ pub async fn run_consumer(
462462 . to_string_lossy ( )
463463 . to_string ( ) ;
464464
465- let store_guard = store. lock ( ) . await ;
466- match indexer:: rename_file ( & old_rel, & new_rel, & store_guard) {
467- Ok ( ( ) ) => {
468- tracing:: info!( from = %old_rel, to = %new_rel, "renamed file in index" ) ;
469- // Track the file_id for edge rebuild
470- if let Ok ( Some ( record) ) = store_guard. get_file ( & new_rel) {
471- affected_file_ids. push ( record. id ) ;
472- }
465+ // Phase 1: Store operations under lock
466+ let needs_frontmatter_strip = {
467+ let store_guard = store. lock ( ) . await ;
468+ match indexer:: rename_file ( & old_rel, & new_rel, & store_guard) {
469+ Ok ( ( ) ) => {
470+ tracing:: info!( from = %old_rel, to = %new_rel, "renamed file in index" ) ;
471+ // Track the file_id for edge rebuild
472+ if let Ok ( Some ( record) ) = store_guard. get_file ( & new_rel) {
473+ affected_file_ids. push ( record. id ) ;
474+ }
473475
474- // Placement correction detection
475- if let Ok ( content) = std:: fs:: read_to_string ( to) {
476- let actual_folder = std:: path:: Path :: new ( & new_rel)
477- . parent ( )
478- . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
479- . unwrap_or_default ( ) ;
480-
481- match placement:: detect_correction_from_frontmatter (
482- & content,
483- & actual_folder,
484- ) {
485- Some ( correction) => {
486- tracing:: info!(
487- file = %new_rel,
488- suggested = %correction. suggested_folder,
489- actual = %correction. actual_folder,
490- "placement correction detected"
491- ) ;
476+ // Placement correction detection
477+ if let Ok ( content) = std:: fs:: read_to_string ( to) {
478+ let actual_folder = std:: path:: Path :: new ( & new_rel)
479+ . parent ( )
480+ . map ( |p| p. to_string_lossy ( ) . to_string ( ) )
481+ . unwrap_or_default ( ) ;
492482
493- // Compute mean vector from file chunks
494- if let Ok ( Some ( file) ) = store_guard. get_file ( & new_rel)
495- && let Ok ( vectors) =
496- store_guard. get_chunk_vectors_for_file ( file. id )
497- && !vectors. is_empty ( )
498- {
499- let dim = vectors[ 0 ] . len ( ) ;
500- let mut mean = vec ! [ 0.0f32 ; dim] ;
501- for v in & vectors {
502- for ( i, val) in v. iter ( ) . enumerate ( ) {
503- mean[ i] += val;
483+ match placement:: detect_correction_from_frontmatter (
484+ & content,
485+ & actual_folder,
486+ ) {
487+ Some ( correction) => {
488+ tracing:: info!(
489+ file = %new_rel,
490+ suggested = %correction. suggested_folder,
491+ actual = %correction. actual_folder,
492+ "placement correction detected"
493+ ) ;
494+
495+ // Compute mean vector from file chunks
496+ if let Ok ( Some ( file) ) = store_guard. get_file ( & new_rel)
497+ && let Ok ( vectors) =
498+ store_guard. get_chunk_vectors_for_file ( file. id )
499+ && !vectors. is_empty ( )
500+ {
501+ let dim = vectors[ 0 ] . len ( ) ;
502+ let mut mean = vec ! [ 0.0f32 ; dim] ;
503+ for v in & vectors {
504+ for ( i, val) in v. iter ( ) . enumerate ( ) {
505+ mean[ i] += val;
506+ }
507+ }
508+ let n = vectors. len ( ) as f32 ;
509+ for val in & mut mean {
510+ * val /= n;
504511 }
505- }
506- let n = vectors. len ( ) as f32 ;
507- for val in & mut mean {
508- * val /= n;
509- }
510512
511- // Adjust centroids: boost actual, decay suggested
512- if let Err ( e) = store_guard. adjust_folder_centroid (
513- & correction. actual_folder ,
514- & mean,
515- true ,
516- ) {
517- tracing:: warn!( error = %e, "failed to adjust actual folder centroid" ) ;
513+ // Adjust centroids: boost actual, decay suggested
514+ if let Err ( e) = store_guard. adjust_folder_centroid (
515+ & correction. actual_folder ,
516+ & mean,
517+ true ,
518+ ) {
519+ tracing:: warn!( error = %e, "failed to adjust actual folder centroid" ) ;
520+ }
521+ if let Err ( e) = store_guard. adjust_folder_centroid (
522+ & correction. suggested_folder ,
523+ & mean,
524+ false ,
525+ ) {
526+ tracing:: warn!( error = %e, "failed to adjust suggested folder centroid" ) ;
527+ }
518528 }
519- if let Err ( e) = store_guard. adjust_folder_centroid (
529+
530+ // Log the correction
531+ if let Err ( e) = store_guard. insert_placement_correction (
532+ & new_rel,
520533 & correction. suggested_folder ,
521- & mean,
522- false ,
534+ & correction. actual_folder ,
523535 ) {
524- tracing:: warn!( error = %e, "failed to adjust suggested folder centroid " ) ;
536+ tracing:: warn!( error = %e, "failed to log placement correction " ) ;
525537 }
526- }
527538
528- // Log the correction
529- if let Err ( e) = store_guard. insert_placement_correction (
530- & new_rel,
531- & correction. suggested_folder ,
532- & correction. actual_folder ,
533- ) {
534- tracing:: warn!( error = %e, "failed to log placement correction" ) ;
535- }
536-
537- // Strip placement frontmatter and write atomically
538- let stripped =
539- placement:: strip_placement_frontmatter ( & content) ;
540- if stripped != content {
541- let tmp = to. with_extension ( "md.tmp" ) ;
542- if let Err ( e) = std:: fs:: write ( & tmp, & stripped)
543- . and_then ( |_| std:: fs:: rename ( & tmp, to) )
544- {
545- tracing:: warn!( error = %e, "failed to strip placement frontmatter" ) ;
546- let _ = std:: fs:: remove_file ( & tmp) ;
547- }
548- }
549- }
550- None => {
551- // Check if it's a confirmation (suggested == actual) — just strip
552- let has_suggested = content. contains ( "suggested_folder:" ) ;
553- if has_suggested {
539+ // Signal that frontmatter strip is needed (done outside lock)
554540 let stripped =
555541 placement:: strip_placement_frontmatter ( & content) ;
556542 if stripped != content {
557- let tmp = to. with_extension ( "md.tmp" ) ;
558- if let Err ( e) = std:: fs:: write ( & tmp, & stripped)
559- . and_then ( |_| std:: fs:: rename ( & tmp, to) )
560- {
561- tracing:: warn!( error = %e, "failed to strip placement frontmatter on confirmation" ) ;
562- let _ = std:: fs:: remove_file ( & tmp) ;
543+ Some ( stripped)
544+ } else {
545+ None
546+ }
547+ }
548+ None => {
549+ // Check if it's a confirmation (suggested == actual) — just strip
550+ let has_suggested = content. contains ( "suggested_folder:" ) ;
551+ if has_suggested {
552+ let stripped =
553+ placement:: strip_placement_frontmatter ( & content) ;
554+ if stripped != content {
555+ Some ( stripped)
556+ } else {
557+ None
563558 }
559+ } else {
560+ None
564561 }
565562 }
566563 }
564+ } else {
565+ None
567566 }
568567 }
568+ Err ( e) => {
569+ tracing:: warn!( from = %old_rel, to = %new_rel, error = %e, "failed to rename file" ) ;
570+ None
571+ }
569572 }
570- Err ( e) => {
571- tracing:: warn!( from = %old_rel, to = %new_rel, error = %e, "failed to rename file" ) ;
573+ } ; // store_guard dropped here
574+
575+ // Phase 2: Frontmatter file I/O without store lock.
576+ // The write triggers a Changed event that gets re-indexed anyway.
577+ if let Some ( stripped) = needs_frontmatter_strip {
578+ let tmp = to. with_extension ( "md.tmp" ) ;
579+ if let Err ( e) = std:: fs:: write ( & tmp, & stripped)
580+ . and_then ( |_| std:: fs:: rename ( & tmp, to) )
581+ {
582+ tracing:: warn!( error = %e, "failed to strip placement frontmatter" ) ;
583+ let _ = std:: fs:: remove_file ( & tmp) ;
572584 }
573585 }
574- drop ( store_guard) ;
575586 }
576587
577588 WatchEvent :: FullRescan => {
0 commit comments