Case study · 2026-06-09

JobMail Assistant

Assistant local pour trier les mails de recrutement sans exposer la mailbox complète : filtrage local, extraction LLM seulement sur les mails pertinents, dashboard FastAPI, cleaner Thunderbird et extension WebExtension pour déléguer les actions à Thunderbird.

Pourquoi ce projet

JobMail est né d'un besoin très concret : suivre une recherche d'emploi depuis des boîtes mail réelles, très bruyantes, sans envoyer toute la messagerie à un provider cloud. Le premier invariant est donc simple : les règles locales passent avant le LLM. Les newsletters, pubs et mails hors sujet restent en local ; seuls les mails classés comme opportunités passent éventuellement à Ollama, Claude ou OpenAI selon la configuration.

Le projet a ensuite touché un point plus délicat : Thunderbird. Lire un fichier MBOX hors ligne est faisable, mais déplacer des messages pendant que Thunderbird tourne peut produire des erreurs de profil, d'index ou de verrouillage. Le chantier Codex de juin 2026 a donc pivoté vers une architecture plus saine : JobMail décide, Thunderbird exécute via une extension locale.

Architecture

Architecture
Loading diagram…
Le pont Thunderbird sépare décision et exécution : JobMail filtre/analyse, l'extension lit et déplace via les APIs Thunderbird.

La première version savait lire des MBOX Thunderbird et scanner des milliers de mails très vite côté Python. Elle reste utile pour produire des rapports : pubs anciennes, règles regex, jobs déjà extraits, doublons Orange/Gmail. Mais le mouvement vers la corbeille est mieux placé dans Thunderbird lui-même. L'extension devient donc une sorte de bras local : elle reçoit une demande, retrouve les messages par Message-Id, demande confirmation, puis utilise l'API Thunderbird pour déplacer sans suppression définitive.

Le chantier Codex

Le déclencheur a été un bug très humain : Thunderbird affichait une erreur d'enregistrement de messages après des opérations cleaner. On a d'abord sécurisé le profil : vérifier que Thunderbird était fermé, nettoyer les index .msf, déplacer les backups hors des dossiers Mail/<compte>, et stopper le serveur local qui pouvait garder du contexte actif. Puis le vrai fix est apparu : arrêter de considérer les MBOX comme l'interface d'écriture normale.

Codex a posé une extension Thunderbird minimale, puis l'a fait évoluer par boucles courtes : import de la sélection, import des non lus, CORS FastAPI, scan cleaner, délégation du dernier scan JobMail, affichage des candidats, et tableau de bord "Pont Thunderbird" dans JobMail. Chaque étape a été testée en réel dans Thunderbird, pas seulement simulée.

Ce qui a bien marché

  • Un pont local plutôt qu'un plugin magique : l'extension ne contient pas le métier. Elle lit, envoie, reçoit une décision et exécute. Le code critique reste dans JobMail, testable avec pytest.
  • Message-Id comme clé de réconciliation : les scans Python voient le MBOX, Thunderbird voit ses messages internes. Le champ commun fiable est le header Message-Id, avec fallback sujet/expéditeur/date.
  • Retour utilisateur explicite : demandé, retrouvé, déplacé, introuvable. Sans ces compteurs, impossible de savoir si "Corbeille scan JobMail" a vraiment travaillé ou seulement échoué silencieusement.
  • Une seule source de configuration : l'extension lit /cleaner/state. L'âge minimum et la limite de scan restent pilotés par JobMail, pas dupliqués dans la popup.

Limites actuelles

Le pont est volontairement prudent. Le nettoyage n'est jamais automatique, et la corbeille passe par une confirmation utilisateur. La résolution peut encore échouer si le mail a déjà été déplacé, si le Message-Id est absent, ou si Thunderbird expose un dossier inattendu. Le résultat est alors visible : mails demandés, retrouvés, déplacés et introuvables.

La leçon importante dépasse JobMail : quand une application mature comme Thunderbird possède déjà l'API qui manipule ses données, un agent IA ne devrait pas forcer l'écriture bas niveau dans ses fichiers. Il doit plutôt construire un pont propre, observable et réversible.