114 lines
6.2 KiB
Markdown
114 lines
6.2 KiB
Markdown
# 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.
|
|
|
|
```python
|
|
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 `, `N°`, `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 :
|
|
|
|
```sql
|
|
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).
|