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

5.7 KiB

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 |.