diff --git a/app/core/ingestion/ingestion_processor.py b/app/core/ingestion/ingestion_processor.py index fbc5a5d..f4cef12 100644 --- a/app/core/ingestion/ingestion_processor.py +++ b/app/core/ingestion/ingestion_processor.py @@ -4,13 +4,13 @@ DESCRIPTION: Der zentrale IngestionService (Orchestrator). WP-25a: Integration der Mixture of Experts (MoE) Architektur. WP-15b: Two-Pass Workflow mit globalem Kontext-Cache. WP-20/22: Cloud-Resilienz und Content-Lifecycle integriert. - AUDIT v4.1.0: - - GOLD-STANDARD v4.1.0: Symmetrie-Integrität korrigiert (note_id, source_id, kind, target_section). + AUDIT v4.2.4: + - GOLD-STANDARD v4.2.4: Hash-basierte Change-Detection (MINDNET_CHANGE_DETECTION_MODE). + - Wiederherstellung des iterativen Abgleichs basierend auf Inhalts-Hashes. - Phase 2 verwendet exakt dieselbe ID-Generierung wie Phase 1 (inkl. target_section). - Authority-Check in Phase 2 prüft mit konsistenter ID-Generierung. - Eliminiert Duplikate durch inkonsistente ID-Generierung (Steinzeitaxt-Problem). - - Beibehaltung der strikten 2-Phasen-Strategie (Authority-First). -VERSION: 4.1.0 (WP-24c: Gold-Standard Identity v4.1.0) +VERSION: 4.2.4 (WP-24c: Hash-Integrität) STATUS: Active """ import logging @@ -212,10 +212,21 @@ class IngestionService: logger.info(f"📄 Bearbeite: '{note_id}'") - # Change Detection + # Change Detection (WP-24c v4.2.4: Hash-basierte Inhaltsprüfung) old_payload = None if force_replace else fetch_note_payload(self.client, self.prefix, note_id) c_miss, e_miss = artifacts_missing(self.client, self.prefix, note_id) - if not (force_replace or not old_payload or c_miss or e_miss): + + content_changed = True + if old_payload and not force_replace: + # Nutzt die über MINDNET_CHANGE_DETECTION_MODE gesteuerte Genauigkeit + # Mapping: 'full' -> 'full:parsed:canonical', 'body' -> 'body:parsed:canonical' + h_key = f"{self.active_hash_mode or 'full'}:parsed:canonical" + new_h = note_pl.get("hashes", {}).get(h_key) + old_h = old_payload.get("hashes", {}).get(h_key) + if new_h and old_h and new_h == old_h: + content_changed = False + + if not (force_replace or content_changed or not old_payload or c_miss or e_miss): return {**result, "status": "unchanged", "note_id": note_id} if not apply: @@ -279,12 +290,6 @@ class IngestionService: inv_kind = edge_registry.get_inverse(resolved_kind) if inv_kind and t_id != note_id: # GOLD-STANDARD v4.1.0: Symmetrie-Integrität - # - note_id: Besitzer-Wechsel zum Link-Ziel - # - source_id: Neue Quelle (Note-ID des Link-Ziels) - # - target_id: Ursprüngliche Quelle (note_id) - # - kind/relation: Invers setzen - # - target_section: Beibehalten (falls vorhanden) - # - scope: Immer "note" für Symmetrien (Note-Level Backbone) v_edge = { "note_id": t_id, # Besitzer-Wechsel: Symmetrie gehört zum Link-Ziel "source_id": t_id, # Neue Quelle ist das Link-Ziel