Files
nas-runbooks/hermes-tt/nyora-notes-tt-ao-reference-endpoint-pj-archive-fix-08-08-2026.md

6.2 KiB

nyora-notes-tt : suite liaison Mail<->OneDrive -- endpoint ao_reference, fix /mnt/pj-archive, 1er match content_hash

Date : 08/08/2026 Instance : hermes-tt Contexte : suite directe de nyora-notes-tt-attachments-liaison-onedrive-07-08-2026.md et nyora-notes-tt-sweep-livelock-fix-08-08-2026.md (meme session, 08/08/2026 ~00h-01h).

1. Endpoint search_linked_documents (mcp_server.py)

Le rapprochement par reference AO n'etait qu'une requete Python ad hoc testee une fois le 07/08. Productionise en outil MCP reutilisable.

AO_REF_PATTERN = re.compile(r"\d{1,3}/\d{4}")

def normalize_ao_references(raw): ...  # extrait toutes les occurrences NN/AAAA d'un texte libre

@mcp.tool()
def search_linked_documents(ao_reference=None, attachment_id=None, note_id=None, limit=10):
    ...

Trois modes d'entree (un seul a la fois) :

  • ao_reference : recherche directe (ex '21/2026', normalise automatiquement une entree du type 'AO N°21/2026').
  • attachment_id : rapprochement depuis une PJ mail connue -- lit son ao_reference, normalise, cherche les documents OneDrive avec au moins une reference commune.
  • note_id : idem mais pour toutes les PJ d'un email.

Un champ ao_reference source ou cible peut contenir plusieurs references separees par virgule et des prefixes variables (AO , , A/O N°, DRT ...) -- on extrait TOUTES les occurrences du motif plutot qu'un parsing strict, comme valide le 07/08.

Retour : documents tries par nombre de references communes decroissant (matched_refs),

  • source_refs (les references normalisees utilisees comme cle) + source_attachments (detail des PJ source si attachment_id/note_id utilise).

Test reel : attachment_id='att_c3348f48e710' (PJ avec 6 references AO, dossier CODIR Medenine) -> 1082 correspondances totales dans onedrive_documents, les 10 premieres triees par pertinence (documents partageant 3-4 references communes en tete). Confirme que la logique validee a la main le 07/08 (4/14 PJ avec au moins 1 match) fonctionne a l'echelle.

Deploiement : rebuild + up -d pendant une fenetre de pause du sweep (entre deux lots, aucun docker exec actif) pour ne pas interrompre un telechargement en cours -- toujours verifier tail du log du sweep avant tout rebuild/redeploy de nyora-notes-tt tant qu'un sweep tourne en fond.

2. /mnt/pj-archive elucide -- bug de donnees, pas de montage manquant

3 lignes attachments avaient local_path sous /mnt/pj-archive/..., absent des volumes de docker-compose.yml (qui monte ./attachments:/app/attachments). Hypothese de depart (montage retire) infirmee : /mnt/pj-archive n'existe nulle part sur l'hote NAS, ni comme dossier /volume1/..., ni dans les mounts actuels du conteneur.

Verification : les 3 fichiers physiques existaient bel et bien, sous le bon chemin /app/attachments/... (/volume1/docker/nyora-notes-tt/attachments/... cote hote), avec exactement les memes noms. Seule la colonne local_path en base pointait vers l'ancien prefixe -- residu d'une constante ATTACH_ROOT differente utilisee au moment de ces 3 telechargements (02-03/08/2026, avant le bootstrap Gitea du 05/08 qui fixe ATTACH_ROOT = "/app/attachments" dans le script actuel). Pas de trace Git anterieure au bootstrap pour confirmer l'ancienne valeur exacte, sans consequence puisque les fichiers etaient au bon endroit.

Fix : UPDATE attachments SET local_path = REPLACE(local_path, '/mnt/pj-archive', '/app/attachments') WHERE local_path LIKE '/mnt/pj-archive%', puis calcul content_hash (MD5, meme methode que onedrive_catalog.py:hash_file()) sur les 3 fichiers desormais accessibles au bon chemin.

Piege rencontre : UPDATE/commit() a echoue une premiere fois avec database table is locked -- le sweep tournait en parallele et ecrivait activement (UPDATE notes SET attachments_scanned/scan_fail_count). Fix : PRAGMA busy_timeout=15000 avant la transaction (au lieu d'echouer immediatement, sqlite attend que le verrou se libere). Volontairement PAS de PRAGMA wal_checkpoint(TRUNCATE) cette fois : c'est une simple UPDATE de donnees (pas un DDL), aucun redemarrage de conteneur ne suit immediatement, donc pas besoin de forcer un checkpoint qui aurait lui-meme risque un nouveau conflit avec le sweep actif en ecriture WAL.

3. Premier match content_hash exact confirme

Apres le fix ci-dessus (20/20 PJ initiales ont desormais un content_hash), re-test du JOIN exact :

SELECT a.id, a.original_filename, o.id, o.filename
FROM attachments a JOIN onedrive_documents o ON a.content_hash = o.content_hash
WHERE a.content_hash IS NOT NULL

1 correspondance exacte (Fiche validation Fourniture Bureautique 2024.pdf, PJ mail vs document OneDrive 2024/2024 Hors RLA/.../Consultation Kebili/...) -- premiere fois que ce JOIN renvoie un resultat non-vide (0/17 le 07/08). Confirme l'hypothese du runbook du 07/08 : le JOIN exact se resorbe progressivement a mesure que l'archive OneDrive rattrape les PJ mail recentes (sync delta SharePoint actif, cf sharepoint-delta-sync.md) et que le volume de PJ traitees augmente (sweep en cours). La cle de liaison principale reste ao_reference (approximative mais large couverture des 2024 events); content_hash devient une confirmation forte en complement quand disponible, pas un remplacement.

Etat en fin de session (08/08/2026 ~00h50)

  • Sweep toujours en cours en fond (log batch_sweep_allyears_20260808_001346.log), plus aucun blocage depuis le fix du livelock -- 49 -> 29 notes restantes au moment de la redaction, continue au rythme normal (lots de 20, pause 180s).
  • search_linked_documents deploye et operationnel.
  • /mnt/pj-archive : mystere resolu, 20/20 PJ initiales ont desormais un content_hash.
  • JOIN exact content_hash : 1 match confirme (structurellement amene a augmenter).

A refaire plus tard (pas cette session)

  • Une fois le sweep complet et Phase 2/embedding relances sur le volume complet de PJ (pas seulement les 20 initiales, cf objectif 1 du prompt de session), refaire un audit content_hash + ao_reference sur l'ensemble pour mesurer le taux de couverture reel.
  • Envisager un plafond/purge sur scan_fail_count si le nombre de notes bloquees augmente significativement au-dela des 3 actuelles (pas necessaire aujourd'hui).