Files
nas-runbooks/hermes-tt/mail-o365-folder-path-disambiguation.md

49 lines
5.7 KiB
Markdown

# hermes-mail-browser -- navigation par chemin complet (folder_path), resout la desambiguation des dossiers homonymes
**Date** : 30-31/07/2026
**Contexte** : projet nyora-notes-TT (RAG personnel mail O365). Blocage identifie par Gemini (test empirique folder_index) puis confirme et resolu par Claude en session live via CDP directement sur hermes-mail-browser.
## Probleme de fond
`go_to_folder()` (fonction existante, conservee) cherche un nom de dossier sur la liste GLOBALE de tous les `[role="treeitem"]`, sans respecter la hierarchie parent-enfant. Deux dossiers homonymes a des emplacements differents de l'arbre (ex plusieurs "Notification" sous differents AO, "CIA", "Evaluation financiere" qui se repetent sous chaque AO) sont indiscernables -- `folder_index` clique une position DOM arbitraire, souvent un dossier top-level plutot que le bon.
## Nouvelle route : go_to_folder_path() + parametre folder_path
Ajoute a **tous les endpoints existants** (`/inbox`, `/message/{index}`, `/message/{index}/attachments`, `/message/{index}/attachments/{filename}/text`, `/search`, `/search/message`, `/search/message/attachments`, `/search/message/attachments/{filename}/text`) un nouveau parametre optionnel `folder_path`, prioritaire sur `folder`/`folder_index` (conserves pour compatibilite ascendante, comportement inchange).
**Format** : segments separes par `|` -- PAS par `/`, car les noms de dossiers reels CONTIENNENT des `/` (ex "AO 66/2024 E Curatif", le `/` fait partie du numero d'AO). Utiliser `/` comme separateur aurait casse silencieusement le decoupage.
Exemple :
```
GET /inbox?folder_path=02%202024%7CRLA%7CAO%2066%2F2024%20E%20Curatif%7CNotification
```
(`%7C` = `|` encode)
## Comment ca marche
1. `snapshot_folders()` -- lecture GROUPEE (1 aller-retour, via `evaluate_all()`) des attributs `data-folder-name` / `title` / `aria-level` / `aria-expanded` / `aria-selected` de tous les treeitems. **Ne PAS utiliser `inner_text()`** -- voir section suivante.
2. Ancrage sur la racine reelle du compte (`aria-level=1` + `@` dans le nom -- exclut la section "Favoris" qui contient des raccourcis homonymes de vrais dossiers, source de confusion supplementaire) puis "Boite de reception" (`aria-level=2`, contient "ception" insensible aux accents).
3. Pour chaque segment du chemin : recherche bornee au sous-arbre du parent courant uniquement (entre son index et la fin de son sous-arbre, calculee via `aria-level`) -- deux dossiers de meme nom sous des parents differents ne sont JAMAIS confondus, contrairement a l'ancienne recherche globale.
4. 404 si segment introuvable dans cette portee (liste les enfants disponibles pour debug), 409 si ambiguite reelle residuelle (tres rare, seulement si le MEME parent a deux enfants homonymes).
## Trois bugs de rendu decouverts et contournes (a retenir pour tout futur travail sur cette UI)
**1. inner_text() peu fiable sur cette UI.** Retourne parfois une chaine vide, parfois juste un glyphe d'icone Unicode prive (police d'icones Fluent, ex `\ue480`) au lieu du texte humain -- meme quand l'element est structurellement complet en DOM. Comportement invisibilite-dependant, pas un probleme de timing (teste avec des delais de 2s a 6s, aucun n'a resolu). **Fix : utiliser les attributs `data-folder-name`/`title` via `evaluate_all()`, jamais `inner_text()` pour cette UI.**
**2. Volet de navigation en mode icones seules.** Peut se retrouver replie a ~68px (icones seules, texte invisible) suite a une session noVNC manuelle anterieure -- l'etat persiste car la session Playwright/CDP est partagee avec noVNC. Bouton toggle : `get_by_role("button", name="Masquer le volet de navigation")` (le libelle semble inverse mais cliquer dans cet etat DEPLIE le volet a ~254px). **Fix : `ensure_nav_pane_expanded()`, verifie la largeur et clique si < 150px, idempotent, best-effort.**
**3. visibility:hidden malgre aria-expanded="true" (le plus insidieux).** Les dossiers profondement imbriques (niveau 5-6+) restent souvent CSS `visibility:hidden` avec une position `x` negative (hors ecran), MEME quand leur `aria-expanded` dit "true" et que TOUS les 333+ items restent presents en DOM (ce n'est PAS de la virtualisation avec demontage comme la liste de messages -- `scrollTop` reste a 0 sur toute la chaine d'ancetres). Ni `Locator.click()` natif (timeout 30s, "element is not visible"), ni `scroll_into_view_if_needed()`, ni scroll clavier ArrowDown (teste 200 pressions) ne resolvent -- l'etat ARIA et l'etat visuel sont desynchronises sur cette UI Fluent. **Fix : clic JS natif via `locator.evaluate("el => el.click()")` au lieu de `Locator.click()` -- contourne l'exigence de visibilite de Playwright tout en declenchant correctement le handler React. Verifie de bout en bout : `aria-selected` passe a `true`, la liste de messages se peuple avec le bon contenu.**
## Preuve empirique finale (31/07/2026)
Test contrastif via l'API HTTP reelle (pas juste le script de diagnostic) sur les deux "Notification" homonymes de 02 2024/RLA :
- `folder_path=02 2024|RLA|AO 66/2024 E Curatif|Notification` -> 7 messages, tous referencant explicitement "AO 66/2024" dans le sujet (Contrats, Contact, Lot 04/05/06...).
- `folder_path=02 2024|RLA|AO 76/2024 E FO|Notification` -> 10 messages, contenu distinct ("AO 76/2024", "derangement FO MPLS"...).
Zero contamination croisee entre les deux, zero contenu generique de boite de reception. Desambiguation prouvee fonctionnelle en conditions reelles.
## A savoir pour la suite (nyora-notes-TT)
Le pipeline d'ingestion doit desormais construire ses `folder_path` avec `|` comme separateur (pas `/`), en respectant l'arborescence exacte fournie par Nabil (dump manuel du 30/07/2026). Le champ `folder_path` stocke en base (`notes.folder_path`) peut garder `/` pour lisibilite humaine -- seule la VALEUR PASSEE A L'API doit utiliser `|`.