diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f4966..11428a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [3.2.1] - 2026-08-30 + +### 🧹 Cleanup des ontologies S3 orphelines + +- **`storage_check`** ne considère plus automatiquement tous les objets contenant `_ontology_` comme légitimes : seules les ontologies encore référencées par une mémoire existante sont protégées. +- **`storage_cleanup`** peut désormais détecter et supprimer les ontologies laissées sur S3 après la suppression d'une mémoire, y compris les copies obsolètes d'une ontologie. +- **Compatibilité legacy** : les mémoires anciennes sans `ontology_uri` restent protégées par une correspondance stricte sur leur identifiant et leur nom d'ontologie. +- Correctif de l'issue [#31](https://github.com/Cloud-Temple/graph-memory/issues/31), validé sur Docker local : 5 ontologies orphelines détectées puis supprimées, second contrôle à 0 orphelin. + ## [3.2.0] - 2026-06-04 ### 🧠`source_path` exposé dans la recherche Graph-first diff --git a/Dockerfile b/Dockerfile index 74932ee..5fb45ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ FROM python:3.11.12-slim # Métadonnées LABEL maintainer="Cloud Temple" LABEL description="MCP Memory Service - Knowledge Graph Memory for AI Agents" -LABEL version="3.2.0" +LABEL version="3.2.1" # Variables d'environnement Python ENV PYTHONDONTWRITEBYTECODE=1 diff --git a/README.en.md b/README.en.md index de29c72..9bc2a99 100644 --- a/README.en.md +++ b/README.en.md @@ -14,9 +14,9 @@ Built by **[Cloud Temple](https://www.cloud-temple.com)**. ## 📋 Changelog -See **[CHANGELOG.md](CHANGELOG.md)** for the full version history (v0.5.0 → v3.2.0). +See **[CHANGELOG.md](CHANGELOG.md)** for the full version history (v0.5.0 → v3.2.1). -**Latest**: v3.2.0 (June 4, 2026) — `source_path` exposed in Graph-first search: `memory_search` and `memory_query` now return the canonical source path (`source_path`) and a derived `repo_path` for every document/chunk, letting an agent open the Git file immediately without a full `document_list`. Enriched via a retroactive graph join (no re-ingestion). `document_get`/`document_list` tools aligned. Previously: v3.1.1 (`/admin` "⚡ Ingest Jobs" console). +**Latest**: v3.2.1 (August 30, 2026) — `storage_check` and `storage_cleanup` now detect S3 ontology objects orphaned after memory deletion, while preserving ontologies still referenced by existing memories and legacy memories without an `ontology_uri`. Fixes [#31](https://github.com/Cloud-Temple/graph-memory/issues/31). Previously: v3.2.0 (`source_path` exposed in Graph-first search). --- diff --git a/README.md b/README.md index bac6c4b..19d4440 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,9 @@ Développé par **[Cloud Temple](https://www.cloud-temple.com)**. ## 📋 Changelog -Voir **[CHANGELOG.md](CHANGELOG.md)** pour l'historique complet des versions (v0.5.0 → v3.2.0). +Voir **[CHANGELOG.md](CHANGELOG.md)** pour l'historique complet des versions (v0.5.0 → v3.2.1). -**Dernière version** : v3.2.0 (4 juin 2026) — `source_path` exposé dans la recherche Graph-first : `memory_search` et `memory_query` renvoient désormais le chemin source canonique (`source_path`) et un `repo_path` dérivé pour chaque document/chunk, permettant à un agent d'ouvrir immédiatement le fichier Git sans `document_list` complet. Enrichissement par jointure graphe rétroactive (aucune ré-ingestion). Outils `document_get`/`document_list` alignés. Précédemment : v3.1.1 (console `/admin` « ⚡ Ingest Jobs »). +**Dernière version** : v3.2.1 (30 août 2026) — `storage_check` et `storage_cleanup` détectent désormais les ontologies S3 devenues orphelines après la suppression d'une mémoire, tout en protégeant les ontologies encore référencées et les mémoires legacy sans `ontology_uri`. Correctif de l'issue [#31](https://github.com/Cloud-Temple/graph-memory/issues/31). Précédemment : v3.2.0 (`source_path` exposé dans la recherche Graph-first). --- @@ -932,4 +932,4 @@ Développé par **[Cloud Temple](https://www.cloud-temple.com)**. --- -*Graph Memory v3.2.0 — Juin 2026* +*Graph Memory v3.2.1 — Août 2026* diff --git a/VERSION b/VERSION index a4f52a5..e4604e3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.0 \ No newline at end of file +3.2.1 diff --git a/src/mcp_memory/__init__.py b/src/mcp_memory/__init__.py index d2c3ec8..0d14752 100644 --- a/src/mcp_memory/__init__.py +++ b/src/mcp_memory/__init__.py @@ -15,5 +15,5 @@ python -m src.mcp_memory.server --port 8002 """ -__version__ = "3.2.0" +__version__ = "3.2.1" __author__ = "Cloud Temple" diff --git a/src/mcp_memory/server.py b/src/mcp_memory/server.py index 81b7b7b..ea47f8d 100644 --- a/src/mcp_memory/server.py +++ b/src/mcp_memory/server.py @@ -26,6 +26,7 @@ from .auth.middleware import AuthMiddleware, LoggingMiddleware, StaticFilesMiddleware from .auth.context import check_memory_access, check_write_permission, check_admin_permission, get_allowed_memory_ids, current_auth from .core.validators import validate_memory_id, validate_filename, validate_document_size, validate_entity_name, validate_backup_id as validate_backup_id_format, check_bootstrap_key_safety +from .storage_consistency import collect_referenced_ontology_keys, is_referenced_ontology_key # ============================================================================= @@ -2226,13 +2227,21 @@ async def storage_check( return admin_err # 1. Récupérer les mémoires à vérifier + all_memories = await get_graph().list_memories() if memory_id: - memory = await get_graph().get_memory(memory_id) + memory = next((mem for mem in all_memories if mem.id == memory_id), None) if not memory: return {"status": "error", "message": f"Mémoire '{memory_id}' non trouvée"} memories = [memory] else: - memories = await get_graph().list_memories() + memories = all_memories + + # Les ontologies ne sont légitimes que si une mémoire existante les + # référence. Les mémoires legacy sans ontology_uri sont protégées par + # un fallback strict sur leur préfixe et leur nom d'ontologie. + referenced_ontology_keys, legacy_ontology_patterns = collect_referenced_ontology_keys( + all_memories, get_storage()._parse_key + ) # 2. Collecter toutes les URIs des documents référencés dans le graphe graph_uris = set() # URIs référencées dans Neo4j @@ -2275,7 +2284,6 @@ async def storage_check( all_graph_uris = set(graph_uris) # Commencer avec celles du scope if memory_id: # Charger les URIs des autres mémoires aussi - all_memories = await get_graph().list_memories() for mem in all_memories: if mem.id == memory_id: continue # Déjà chargé @@ -2310,9 +2318,11 @@ async def storage_check( if key.startswith("_backups/"): continue - # Ignorer les ontologies (fichiers légitimes) - # Le pattern est {hash[:8]}__ontology_{name}.yaml (double _ car hash + _ontology) - if "_ontology_" in key: + # Ignorer uniquement les ontologies encore référencées par une + # mémoire existante. Une ontologie de mémoire supprimée est orpheline. + if is_referenced_ontology_key( + key, referenced_ontology_keys, legacy_ontology_patterns + ): continue # Si la clé n'est pas référencée dans le graphe → orphelin diff --git a/src/mcp_memory/static/admin.html b/src/mcp_memory/static/admin.html index bd94be1..fddb168 100644 --- a/src/mcp_memory/static/admin.html +++ b/src/mcp_memory/static/admin.html @@ -4,7 +4,7 @@