hermes-tt: garde-fou de portee /search (mitigation bug D) + timeout aligne sur le nouveau plafond
Garde-fou : /search renvoie parfois le contenu de la Boite de reception pour un folder_path donne, en HTTP 200 sans erreur (bug D, cause non corrigee cote serveur). Verifie avant d'implementer qu'AUCUN signal structure n'existe dans la reponse (top-level query/count/items ; par item raw_text/aria_label/elem_id/attrs) -- en exposer un imposerait de modifier hermes-mail-browser, donc session dediee. Signal retenu faute de mieux : la derniere ligne de raw_text porte le libelle du dossier. Fiabilite mesuree sur 5 dossiers de types differents avant implementation : uniforme a 100% dans les 5 cas (81/81, 37/37, 6/6, 84/84, 77/77), correspondant dans les 4 cas sains, divergent dans le seul cas verole -- fiable ET discriminant, ce qui justifie un blocage dur plutot qu'un simple avertissement. Conception conservatrice : ne bloque que sur preuve positive de mauvaise portee ; journalise et laisse passer quand il ne peut pas conclure (aucun item, libelles heterogenes, noyau comparable vide). Un faux positif bloquerait une ingestion legitime, ce qui serait pire que le defaut couvert. HermesFolderScopeError herite de HermesMailClientError -> comportement correct chez les deux appelants de production sans les modifier (checkpoint 'error' cote pipeline, None cote download_attachments). Verifie en conditions reelles depuis le container : dossier sain 81 items ingeres, dossier verole bloque. Non-regression du mock de test verifiee. Timeout : effet de bord du fix B (plafond 20 -> 200). La duree de /search croit avec le volume demande (scroll progressif) -- 40 a 240 s mesures, contre timeout=60 code en dur. Les gros dossiers echouaient en timeout. SEARCH_TIMEOUT_S = 300. + 1 regle PROTOCOL-INFRA (plafond de pagination et timeout sont couples).
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
**Date** : 10/08/2026
|
||||
**Instance** : hermes-tt
|
||||
**Statut** : A et B CORRIGÉS + déployés · C diagnostiqué (correctif non appliqué) · **D NON RÉSOLU — bloquant backfill**
|
||||
**Statut** : A et B CORRIGÉS + déployés · C diagnostiqué (correctif non appliqué) · **D NON RÉSOLU** — garde-fou de portée déployé en mitigation (erreur visible au lieu de corruption silencieuse)
|
||||
**Fichiers** : `/volume1/docker/nyora-notes-tt/hermes_mail_client.py` (`_parse_item`, `search_folder`) · `/volume1/docker/hermes-mail-browser/app/main.py` (bug C, non modifié)
|
||||
|
||||
Quatre défauts distincts de la même chaîne d'extraction, découverts ensemble. Ils partagent une
|
||||
@@ -172,7 +172,7 @@ réel : rebuild et retest Playwright d'un **second** container.
|
||||
|
||||
## Bug D — `/search` renvoie le contenu de la Boîte de réception pour certains `folder_path`, en HTTP 200
|
||||
|
||||
**Découvert en auditant le bug B. NON RÉSOLU — bloquant pour tout backfill.**
|
||||
**Découvert en auditant le bug B. Cause NON RÉSOLUE (session dédiée) — mitigé côté client par un garde-fou de portée, cf plus bas.**
|
||||
|
||||
### Symptôme
|
||||
Deux `folder_path` différents renvoient ~77 items dont 63 en commun, et **chaque ligne porte le
|
||||
@@ -214,10 +214,69 @@ introuvable, jamais si la navigation échoue **silencieusement** après un clic
|
||||
3. **Bloquant pour le backfill** : ingérer à grande échelle avec une portée fausse polluerait la
|
||||
base massivement.
|
||||
|
||||
### Contrôle de portée à systématiser
|
||||
La dernière ligne de `raw_text` porte le libellé du dossier du message. C'est un **contrôle de
|
||||
portée gratuit** : après un `/search` scopé, si ce libellé ne correspond pas au dossier demandé,
|
||||
la navigation a échoué. À câbler dans `search_folder` avant toute ingestion.
|
||||
### Mitigation déployée : garde-fou de portée côté client (10/08/2026)
|
||||
|
||||
Le correctif de fond reste à faire côté `hermes-mail-browser`. En attendant, un contrôle a été
|
||||
câblé dans `hermes_mail_client.search_folder` pour transformer la **corruption silencieuse en
|
||||
erreur visible**.
|
||||
|
||||
**Aucun signal structuré n'existe** — vérifié avant d'implémenter, conformément à la règle
|
||||
« préférer un attribut structuré au texte rendu ». La réponse `/search` expose au top-level
|
||||
`query`, `count`, `items` ; par item `raw_text`, `aria_label`, `elem_id`, `attrs`. **Aucun** ne
|
||||
porte le dossier réellement atteint. En exposer un imposerait de modifier `hermes-mail-browser`,
|
||||
donc relève de la session dédiée au bug D. Faute de mieux, le signal retenu est la **dernière
|
||||
ligne de `raw_text`**, qui porte le libellé du dossier du message.
|
||||
|
||||
**Fiabilité mesurée avant implémentation** (même méthode que le recensement PUA), 5 dossiers de
|
||||
types différents :
|
||||
|
||||
| Type | Items | Libellé uniforme | Correspond au dossier demandé |
|
||||
|---|---|---|---|
|
||||
| normal Réception (`00 2026\|⏱️ suivi`) | 81 | 81/81 | ✅ `⏱️ Suivi` |
|
||||
| veille/notif (`00 2026\|🤖 veille`) | 37 | 37/37 | ✅ `🤖 Veille` |
|
||||
| profond niveau 5 (`…\|🤖 projet auto`) | 6 | 6/6 | ✅ `🤖 Projet Auto` |
|
||||
| ancien 2024 (`02 2024\|DR Zone Sud\|Tozeur`) | 84 | 84/84 | ✅ `Tozeur` |
|
||||
| **vérolé** (`00 2026\|Relance marchés RLA`) | 77 | 77/77 | ❌ `Boîte de réception` |
|
||||
|
||||
Signal **uniforme à 100 % dans les 5 cas**, correspondant dans les 4 cas sains, divergent dans le
|
||||
seul cas vérolé : fiable **et** discriminant. Le blocage dur est donc justifié par la mesure.
|
||||
|
||||
**Conception — volontairement conservatrice.** Le contrôle ne bloque que sur une *preuve positive*
|
||||
de mauvaise portée. Quand il ne peut pas conclure (aucun item, libellé absent, libellés
|
||||
hétérogènes sous le seuil de 90 %, noyau comparable vide), il **journalise et laisse passer** : un
|
||||
faux positif bloquerait une ingestion légitime, ce qui serait pire que le défaut couvert. La
|
||||
comparaison se fait sur un « noyau » alphanumérique en minuscules (glyphes, emoji et ponctuation
|
||||
retirés), car la même entité s'écrit de trois façons : chemin stocké (`00 2026|Suivi`), chemin réel
|
||||
(`⏱️ suivi`), libellé rendu (`⏱️ Suivi`).
|
||||
|
||||
`HermesFolderScopeError` hérite de `HermesMailClientError`, ce qui lui donne le bon comportement
|
||||
chez les **deux appelants de production sans les modifier** : `pipeline_processor.process_folder`
|
||||
la reçoit dans son `except Exception` → checkpoint en statut `error` (visible, re-tentable) au lieu
|
||||
d'ingérer ; `download_attachments_phase1._fetch_folder_or_none` renvoie `None` (« on ne sait pas, à
|
||||
retenter »), sans rien marquer.
|
||||
|
||||
**Vérifié en conditions réelles**, depuis le container après rebuild :
|
||||
```
|
||||
SAIN '00 2026|⏱️ suivi' -> 81 items INGERES
|
||||
VEROLE '00 2026|Relance marchés RLA' -> BLOQUE
|
||||
Portee /search non respectee : dossier demande '00 2026|Relance marchés RLA',
|
||||
mais les messages recus appartiennent a 'Boîte de réception' (bug D).
|
||||
Ingestion refusee pour eviter un classement errone.
|
||||
```
|
||||
Non-régression vérifiée sur les `raw_text` du mock de test (`test_api_mock_server.py`), qui ne
|
||||
portent pas de libellé de dossier : le contrôle les laisse passer.
|
||||
|
||||
### Effet de bord du fix B : le timeout de 60 s devenait trop court
|
||||
|
||||
Constaté en testant le garde-fou. `/search` accumule ses résultats par **scroll clavier progressif**
|
||||
dans une liste virtualisée : la durée croît avec le nombre d'items demandé. Passer le plafond de 20
|
||||
à 200 (fix B) a fait passer les appels de ~30 s à **40–240 s** selon le dossier, alors que
|
||||
`search_folder` gardait `timeout=60` codé en dur — les gros dossiers échouaient donc en timeout,
|
||||
et le pipeline les aurait tous marqués `error`.
|
||||
|
||||
`SEARCH_TIMEOUT_S = 300` désormais. **Leçon** : relever un plafond de pagination change le profil
|
||||
temporel des appels — vérifier les timeouts en même temps, ils sont dimensionnés pour l'ancien
|
||||
volume.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user