@@ -4321,10 +4321,11 @@ def _fast_register_cloned_instances(self, instances_to_clone: List[importlib.met
43214321 # 7. Execute all registrations in a single transaction.
43224322 pipe.execute()
43234323
4324+
43244325 def _synchronize_knowledge_base_with_reality(self, verbose: bool = False) -> List[importlib.metadata.Distribution]:
43254326 """
4326- (UPGRADED - THE REPAIR BOT v7) Now uses skip_existing_checksums=True during
4327- targeted rebuilds to avoid processing already-registered instances .
4327+ (UPGRADED - THE REPAIR BOT v7) Now uses resolved paths consistently
4328+ for hash generation to prevent ghost/rebuild loops .
43284329 """
43294330
43304331 if self._check_and_run_pending_rebuild():
@@ -4369,24 +4370,19 @@ def _synchronize_knowledge_base_with_reality(self, verbose: bool = False) -> Lis
43694370 # ========================================================================
43704371
43714372 # Step 1: Build map of disk instances by their installation hashes
4372- safe_print(" -> [DIAGNOSTIC] Building map of all instances found on disk...")
43734373 disk_instance_map_by_hash = {}
43744374 for dist in all_discovered_dists:
43754375 try:
43764376 c_name = canonicalize_name(dist.metadata['Name'])
4377- raw_path_str = str(dist._path)
43784377 resolved_path_str = str(dist._path.resolve())
43794378 unique_instance_identifier = f"{resolved_path_str}::{dist.version}"
43804379 instance_hash = hashlib.sha256(unique_instance_identifier.encode()).hexdigest()[:12]
43814380
4382- safe_print(f" - DISK: Found '{c_name}=={dist.version}'. Path: '{raw_path_str}'. Resolved Path: '{resolved_path_str}'. -> HASH: {instance_hash}")
4383-
43844381 disk_instance_map_by_hash[instance_hash] = dist
43854382 except Exception:
43864383 continue
43874384
43884385 # Step 2: Get all KB instance keys and extract their installation hashes
4389- safe_print(" -> [DIAGNOSTIC] Getting all instance hashes from Knowledge Base (Redis)...")
43904386 kb_instance_keys = set(self.cache_client.keys(self.redis_key_prefix.replace(':pkg:', ':inst:') + '*'))
43914387
43924388 kb_hashes_to_keys = {} # Map: installation_hash -> redis_key
@@ -4398,53 +4394,41 @@ def _synchronize_knowledge_base_with_reality(self, verbose: bool = False) -> Lis
43984394 for key, stored_hash in zip(kb_instance_keys, results):
43994395 if stored_hash:
44004396 kb_hashes_to_keys[stored_hash] = key
4401- safe_print(f" - KB: Found Key '{key}' -> HASH: {stored_hash}")
4402- else:
4403- safe_print(f" - KB WARNING: Key '{key}' is missing its 'installation_hash' field!")
44044397
44054398 # Step 3: Identify instances that need registration (hash not in KB)
44064399 disk_hashes = set(disk_instance_map_by_hash.keys())
44074400 kb_hashes = set(kb_hashes_to_keys.keys())
44084401
44094402 hashes_needing_registration = disk_hashes - kb_hashes
44104403 instances_to_rebuild = [disk_instance_map_by_hash[hash_val] for hash_val in hashes_needing_registration]
4411-
4412- if instances_to_rebuild:
4413- safe_print(f" -> [DIAGNOSTIC] The following {len(hashes_needing_registration)} hashes are NEW (on disk, but not in KB):")
4414- for h in hashes_needing_registration:
4415- dist = disk_instance_map_by_hash.get(h)
4416- safe_print(f" - NEW HASH: {h} (corresponds to disk path: '{dist._path.resolve() if dist else '??'}')")
44174404
44184405 # Step 4: Clean up ghost instances (KB hashes that don't exist on disk)
44194406 ghost_hashes = kb_hashes - disk_hashes
4420- if ghost_hashes:
4421- safe_print(f" -> [DIAGNOSTIC] The following {len(ghost_hashes)} hashes are GHOSTS (in KB, but not found on disk):")
4422- for h in ghost_hashes:
4423- safe_print(f" - GHOST HASH: {h} (corresponds to KB key: '{kb_hashes_to_keys.get(h)}')")
4424-
4425- safe_print(f" -> 👻 Removing {len(ghost_hashes)} ghost instance(s) from KB...")
4426- ghost_keys = [kb_hashes_to_keys[hash_val] for hash_val in ghost_hashes]
4427- if ghost_keys:
4428- self.cache_client.delete(*ghost_keys)
4407+
4408+ # Only print if we found discrepancies
4409+ if instances_to_rebuild or ghost_hashes:
4410+ if instances_to_rebuild:
4411+ safe_print(f" -> 🔍 Found {len(instances_to_rebuild)} new instance(s) to register")
4412+ if ghost_hashes:
4413+ safe_print(f" -> 👻 Removing {len(ghost_hashes)} ghost instance(s) from KB")
44294414
44304415 if instances_to_rebuild:
4431- safe_print(f" -> 🔍 Found {len(instances_to_rebuild)} unregistered instance(s) at new paths.")
4432-
44334416 if verbose:
44344417 for dist in instances_to_rebuild:
44354418 try:
44364419 safe_print(f" - {dist.metadata['Name']}=={dist.version} at {dist._path}")
44374420 except Exception:
44384421 pass
44394422
4440- # --- THIS IS THE FIX ---
4441- # Instead of creating generic specs and re-discovering, we pass the
4442- # exact list of new Distribution objects directly to the builder.
4443- safe_print(f" -> 🧠 Surgically rebuilding KB for {len(instances_to_rebuild)} new instance(s)...")
4444-
4423+ safe_print(f" -> 🧠 Rebuilding KB for {len(instances_to_rebuild)} instance(s)...")
44454424 gatherer.run(pre_discovered_distributions=instances_to_rebuild)
44464425 self._installed_packages_cache = None
4447-
4426+
4427+ if ghost_hashes:
4428+ ghost_keys = [kb_hashes_to_keys[hash_val] for hash_val in ghost_hashes]
4429+ if ghost_keys:
4430+ self.cache_client.delete(*ghost_keys)
4431+
44484432 if instances_to_rebuild or ghost_hashes:
44494433 safe_print(" -> ✅ Instance-level healing complete.")
44504434
@@ -4510,8 +4494,14 @@ def _synchronize_knowledge_base_with_reality(self, verbose: bool = False) -> Lis
45104494 safe_print(_(' ✅ Sync and repair complete.'))
45114495
45124496 return all_discovered_dists
4513-
4514-
4497+
4498+
4499+
4500+
4501+
4502+
4503+
4504+
45154505 def _get_disk_specs_for_context(self, python_version: str) -> set:
45164506 """
45174507 (V3 - ROBUST PATH FIX) A lightweight, READ-ONLY function to get the ground truth
0 commit comments