@@ -181,6 +181,12 @@ func (p *Pipeline) IndexURL(ctx context.Context, rootURL string, opts IndexOptio
181181
182182// indexFile processes a single file through Phases 1-2.
183183func (p * Pipeline ) indexFile (ctx context.Context , path string , opts IndexOptions ) error {
184+ absPath , err := filepath .Abs (path )
185+ if err != nil {
186+ return fmt .Errorf ("resolve absolute path: %w" , err )
187+ }
188+ path = absPath
189+
184190 data , err := os .ReadFile (path )
185191 if err != nil {
186192 return err
@@ -550,6 +556,17 @@ func (p *Pipeline) Finalize(ctx context.Context, verbose bool) error {
550556 if err != nil {
551557 return fmt .Errorf ("load relationships: %w" , err )
552558 }
559+
560+ // Check if finalization can be skipped: communities already exist and
561+ // entity/relationship counts haven't changed since last run.
562+ existingEntities , existingRels , existingComms , fpErr := p .store .GraphFingerprint (ctx )
563+ if fpErr == nil && existingComms > 0 &&
564+ existingEntities == len (entities ) && existingRels == len (rels ) {
565+ slog .Info ("⏭️ skipping finalization — graph unchanged since last run" ,
566+ "entities" , len (entities ), "relationships" , len (rels ), "communities" , existingComms )
567+ return nil
568+ }
569+
553570 slog .Info ("🧩 Phase 3: running Louvain community detection" ,
554571 "entities" , len (entities ), "relationships" , len (rels ))
555572
@@ -640,6 +657,12 @@ func (p *Pipeline) Finalize(ctx context.Context, verbose bool) error {
640657 }
641658 }
642659
660+ if len (workItems ) == 0 {
661+ slog .Warn ("⚠️ all communities filtered out by min_community_size" ,
662+ "min_community_size" , p .cfg .Community .MinCommunitySize ,
663+ "hint" , "lower community.min_community_size in config or index more documents" )
664+ return nil
665+ }
643666 slog .Info ("🧩 Phase 4: summarising communities" , "communities" , len (workItems ))
644667
645668 type commResult struct {
0 commit comments