---
from: claude (Agent A)
to: dom
date: 2026-06-05T19:30:00+02:00
topic: planA-gui-v6-architecture
status: open
priority: high
nature: PLANIFICATION (lecture seule — aucun code modifié, aucun commit)
references:
  - plan: docs/coordination/inbox/for-dom/2026-06-05_claude_plan-v11-5-parallele.md (57aa0f0)
  - mockup: docs/ui_mockup_v6.html (validé 2026-05-06)
  - wip: backup/windows-wip-2026-06-05 (b8c9c41)
  - base_wip: 0124457
gardefou: "98 tests unit doivent rester verts — le moteur ne bouge pas"
---

# Plan A — Architecture GUI v6

Sous-plan détaillé de la transposition GUI v6. **Document de conception
uniquement.** Aucun fichier de code n'a été touché.

## 0. Constat majeur (corrige une hypothèse du plan v11.5)

Le plan v11.5 décrit le WIP `backup/windows-wip-2026-06-05` comme « +1250 lignes
de GUI v6 ». **Vérification faite, ce n'est pas une GUI v6 :**

- Le diff `0124457..b8c9c41` (+1148/-102) ajoute **profils métier, masques PDF
  réutilisables, paramètres avancés** — features qui sont **déjà toutes dans le
  `Pseudonymisation_Gui_V5.py` actuel** (v5.5).
- Le WIP est en **tkinter pur** : aucune trace de `customtkinter`/`ctk`/`CTk`.
- Le fichier de travail actuel est en fait **en avance** sur le WIP : `git diff
  backup/windows-wip-2026-06-05 -- Pseudonymisation_Gui_V5.py` = seulement
  24 insertions / 5 suppressions, et ce delta = les fixes D-11 (VLM masqué hors
  admin), D-13 (tag « MODE ADMIN » dans le titre) et RGPD (`CHCB`→`CHUXX`,
  `chcb_strict`→`chuxx_strict`) que le WIP **n'a pas encore**.

**Conséquences pour l'Agent A :**
1. Le WIP n'est **pas** une base de départ v6 — c'est l'ancêtre de la v5.5 actuelle.
   La « matière première » réelle de la GUI v6 = le **mockup HTML v6** + la **v5.5
   actuelle** (logique métier déjà écrite et fonctionnelle).
2. La GUI v6 = **réécriture de la couche présentation** (tkinter → customtkinter,
   2 onglets → 3 onglets + sous-onglets) **en réutilisant telle quelle toute la
   logique métier** (worker, profils, masques, params, contrat moteur).
3. La sauvegarde Gitea (section 0 du plan v11.5) est **déjà faite** : la branche
   existe sur `remotes/gitea/backup/windows-wip-2026-06-05`. Risque « disque
   unique » levé. ⚠️ reste à vérifier : que la v5.5 *actuelle* (en avance sur le
   WIP) soit elle aussi sauvegardée hors disque avant de démarrer le codage v6.

`customtkinter` **n'est ni installé dans `.venv` ni listé dans les requirements**
→ à ajouter comme dépendance v11.5 (impact PyInstaller à anticiper avec Agent D).

---

## 1. Inventaire de l'existant

### 1.1 GUI v5.5 (`Pseudonymisation_Gui_V5.py`, 2894 lignes)

**Stack :** tkinter + ttk, thème `sv_ttk` optionnel (fallback `clam`), PIL pour
logo/icônes (dégradation si absent). Palette magenta/pêche dérivée du logo
(`CLR_PRIMARY=#E91E63`, etc.). Onglets *custom* faits main (pas `ttk.Notebook`).

**Structure actuelle — 3 onglets plats :**

| Onglet | Contenu |
|---|---|
| **Anonymisation** | Étape 1 (choisir dossier OU fichier) → Étape 2 (info formats : raster PDF + .txt) → checkbox VLM (si admin) → bouton Lancer/Arrêter → progress → résultats (3 cartes stats + badge fuites + perf + ouvrir dossier + journal repliable) |
| **Paramètres** | Whitelist / Blacklist / Stop-words (3 listes éditables) ; masques PDF réutilisables (ouvrir éditeur, combo modèle, dossier modèles) ; export/import JSON ; sauvegarder |
| **Profils** | Profil actif (combo + actualiser), description éditable, flags (masque obligatoire, désactiver VLM), masque mémorisé, actions (nouveau/enregistrer/renommer/défaut/supprimer), panneau résumé |

**Briques techniques déjà en place (à conserver intégralement) :**
- `App` (classe monolithique), `UiMessage`/`MsgType` (file worker→UI), `ToolTip`.
- Worker threadé (`_run` → `threading.Thread(_worker)`), pompe `_pump_logs`
  (`root.after(60)`).
- Détection police/dark-mode, résolution assets/config compatible PyInstaller
  (`_asset`, `_app_dir`, `_exe_dir`, `_resolve_config`, `_resolve_profiles_config`).
- 4 managers NER chargés en interne (ONNX, EDS-Pseudo, CamemBERT, VLM optionnel).
- Mode admin (`admin_mode.is_admin`) : masque le VLM + annote le titre.

### 1.2 Mockup v6 (`docs/ui_mockup_v6.html`, 898 lignes) — cible UX validée

**3 onglets principaux :**
1. **📄 Utilisation** — dropzone glisser-déposer + liste fichiers, bouton Go,
   barre progression « Fichier 1/3 », 4 cartes résultats (Documents, PII masqués,
   Durée, Qualité), bandeau « Aucune fuite détectée », journal.
2. **⚙️ Configuration** — **4 sous-onglets** :
   - **⚙️ Réglages** : catégories PII activables (Noms, Dates naissance,
     Établissements, Adresses/CP, N° sécu, Tél/email, N° mutuelle) + choix moteur
     (CamemBERT-bio RAPIDE / EDS-Pseudo PRÉCIS / GLiNER OPTIONNEL).
   - **🎭 Masquage** : couleur rectangles, libellés placeholders par type
     (NOM, Date naissance, Établissement…), marges/coins arrondis, **éditeur de
     masques PDF intégré** (canvas, zoom, DPI, compteur masques, template).
   - **🔄 Partage** : export/import config (whitelist/blacklist).
   - **🛡️ Règles** : table de règles personnalisées (Label, Type, Cible→Résultat,
     Statut) + simulateur (texte test → sortie).
3. **ℹ️ À propos** — version, thème, build.

**Thèmes :** sélecteur (cf. roadmap mémoire : 4 thèmes).

### 1.3 WIP backup (`b8c9c41`)
Ancêtre de la v5.5 (cf. §0). Sert de **référence de lecture** pour les libellés
français et l'organisation des écrans Profils/Masques, **pas de base à merger**.

---

## 2. Architecture cible GUI v6 (customtkinter)

### 2.1 Principe directeur
**Séparer présentation et logique.** La v5.5 mélange les deux dans une classe
`App` de 2894 lignes. La v6 extrait la logique métier (déjà testée, déjà
fonctionnelle) dans un *contrôleur* réutilisable, et réécrit uniquement la
couche vue en customtkinter selon le mockup.

### 2.2 Arborescence proposée

```
Pseudonymisation_Gui_V6.py          # point d'entrée : main(), bootstrap ctk, App
gui_v6/
├── __init__.py
├── app.py                          # AppV6(ctk.CTk) : shell, header, nav 3 onglets
├── theme.py                        # palette + 4 thèmes ctk + tokens (couleurs/polices)
├── widgets/                        # composants réutilisables
│   ├── dropzone.py                 # zone glisser-déposer + liste fichiers
│   ├── stat_card.py                # carte statistique résultat
│   ├── phrase_list.py              # liste éditable (whitelist/blacklist/stopwords)
│   ├── tooltip.py                  # ToolTip (porté depuis v5)
│   └── tabview.py                  # onglets/sous-onglets stylés
├── tabs/
│   ├── tab_use.py                  # onglet Utilisation
│   ├── tab_config.py              # onglet Configuration (host des 4 sous-onglets)
│   ├── config_reglages.py         # sous-onglet Réglages (PII + moteur)
│   ├── config_masquage.py         # sous-onglet Masquage + éditeur masques intégré
│   ├── config_partage.py          # sous-onglet Partage (export/import)
│   ├── config_regles.py           # sous-onglet Règles + simulateur  [zone B]
│   └── tab_about.py               # onglet À propos + état licence  [zone C]
├── controller.py                   # AnonymController : SEULE porte vers le moteur
├── worker.py                       # worker threadé + file UiMessage (porté de v5)
└── assets_v6/                      # logo, icônes (réutilise assets/ existant)
```

**Note de packaging :** `gui_v6/` est un package neuf (frontière propre Agent A,
cf. plan v11.5 §3). Aucun fichier du périmètre bêta n'est modifié. Ajouter
`gui_v6/` et `customtkinter` au `.spec` PyInstaller = tâche post-GO bêta (Agent D).

### 2.3 Mapping mockup → modules

| Onglet mockup | Module v6 | Réutilise (v5.5) |
|---|---|---|
| Utilisation | `tabs/tab_use.py` | `_run`/`_worker`, stats, badge fuites, journal |
| Config → Réglages | `tabs/config_reglages.py` | sélection moteur NER, seuils (nouveau : cases PII) |
| Config → Masquage | `tabs/config_masquage.py` | combo masques + `pdf_mask_designer` |
| Config → Partage | `tabs/config_partage.py` | `_export_params`/`_import_params` |
| Config → Règles | `tabs/config_regles.py` | nouveau (zone B) |
| À propos | `tabs/tab_about.py` | `_version_long`, build_info (+ licence zone C) |
| (Profils v5) | intégré dans Config ou onglet dédié | tout l'appareil `profile_defaults` |

**Décision à trancher avec Dom :** le mockup v6 n'a **pas** d'onglet « Profils »
distinct (v5 en a un). Deux options : (a) garder un 4ᵉ onglet principal
« Profils », ou (b) intégrer la sélection de profil en bandeau dans Utilisation +
gestion dans Config. Recommandation : **option (b)** pour coller au mockup validé,
avec un sélecteur de profil en haut de l'onglet Utilisation.

---

## 3. Liste des écrans / workflows

**Workflow principal (Utilisation) :**
1. Glisser-déposer OU parcourir (dossier/fichier) → liste fichiers.
2. (option) choisir profil métier + masque manuel.
3. Lancer → progress par fichier → cartes résultats + badge fuites → ouvrir dossier.
4. Arrêter en cours possible ; journal détaillé repliable.

**Workflows Configuration :**
- Réglages : activer/désactiver catégories PII, choisir moteur NER.
- Masquage : couleur/placeholders/marges + dessiner et enregistrer un masque PDF.
- Partage : exporter config (JSON pour email) / importer config reçue.
- Règles : créer une règle perso (cible→résultat), tester via simulateur.

**Workflows transverses :** profils (CRUD + défaut), thème (4 thèmes),
état licence (bandeau).

---

## 4. Contrat minimal avec le moteur (GARDE-FOU — le moteur ne bouge pas)

La GUI v6 consomme **exactement les mêmes API que la v5.5**. Aucune signature
moteur ne change ⇒ les 98 tests unit restent verts. Tout passe par
`gui_v6/controller.py` (point d'entrée unique vers le backend).

### 4.1 Fonction moteur centrale (à appeler à l'identique)

```python
# anonymizer_core_refactored_onnx.py
process_document(doc_path, out_dir, **kwargs) -> Dict[str, str]   # multi-formats
process_pdf(pdf_path, out_dir, ...) -> Dict[str, str]            # fallback PDF
```
kwargs effectivement passés par le worker v5 (à reproduire tels quels) :
`make_vector_redaction=False`, `also_make_raster_burn=True`, `config_path`,
`use_hf`, `ner_manager`, `ner_thresholds`, `ogc_label`, `vlm_manager`,
`camembert_manager`. Sélection via `getattr(core, 'process_document', None) or
core.process_pdf` + clé `doc_path`/`pdf_path`. **Retour = dict chemins de sortie**
(clés `audit`, etc.) — la v6 lit ces clés à l'identique (comptage audit, badge fuites).

### 4.2 Managers NER (instanciés et chargés comme en v5)
- `ner_manager_onnx.NerModelManager(cache_dir)` + `NerThresholds` — `.is_loaded()`,
  `.load(model_id)`, `.models_catalog()`.
- `eds_pseudo_manager.EdsPseudoManager(cache_dir)` — idem.
- `camembert_ner_manager.CamembertNerManager()` — `.is_loaded()`, `.load()`.
- `vlm_manager.VlmManager` / `VlmConfig` — **masqué hors admin** (D-11),
  `.is_loaded()`.

### 4.3 Modules support (réutilisés sans modification)
- `config_defaults` : `load_effective_dictionaries_dict`, `load_effective_param_lists`,
  `deep_merge_dict`, `read_*_text`, `ensure_runtime_dictionaries_config`.
- `gui_batch_paths` : `list_supported_documents`, `build_batch_output_dir`,
  `iter_pseudonymized_texts`.
- `manual_masking` : `ensure_mask_templates_dir`, `list_mask_templates`,
  `mask_template_label`, `resolve_manual_mask_pdf`, `append_jsonl_file`.
- `profile_defaults` : `list_effective_profiles`, `save_runtime_profile`,
  `delete_runtime_profile`, `set_runtime_default_profile`, `get_default_profile_key`,
  `ensure_runtime_profiles_config`.
- `pdf_mask_designer` : `Template`, `load_template_yaml`, `apply_template_vector`,
  `MaskDesignerApp` (intégrer dans le sous-onglet Masquage plutôt que Toplevel).
- `format_converter.SUPPORTED_EXTENSIONS`.
- `admin_mode.is_admin` / `admin_required`.
- `build_info` (BUILD_DATE/COMMIT/BRANCH).

### 4.4 Construction de la config par profil (logique worker à porter telle quelle)
Le worker v5 fabrique un **YAML temporaire** fusionnant config effective +
`param_lists` du profil + overlay, puis le passe en `config_path`. Cette mécanique
(`deep_merge_dict` + `tempfile.mkstemp` à côté de la config) **est reportée à
l'identique** dans `gui_v6/worker.py`. Le moteur reçoit donc le même intrant
qu'aujourd'hui → sortie inchangée → audit qualité ≥ baseline.

**Règle d'or :** `controller.py`/`worker.py` ne contiennent **aucune** logique de
détection. Ils orchestrent. Toute tentation de « pré-traiter » le texte côté GUI
= violation du garde-fou.

---

## 5. Stratégie de migration progressive (v5 → v6 sans casser)

1. **Cohabitation.** v6 = fichier neuf `Pseudonymisation_Gui_V6.py` + package
   `gui_v6/`. La v5.5 reste l'entrée par défaut tant que la v6 n'a pas passé le
   smoke test et l'audit qualité. Bascule par défaut = dernière étape (Agent D).
2. **Extraction d'abord, vue ensuite.** Étape 1 : extraire worker + contrôleur
   depuis la v5.5 **sans changer de toolkit** (refactor pur, testable). Étape 2 :
   réécrire la vue en customtkinter par-dessus ce contrôleur. Ça découple le risque
   « moteur » du risque « UI ».
3. **Parité fonctionnelle par onglet.** Migrer Utilisation → Configuration →
   Profils dans cet ordre ; à chaque onglet, vérifier que le workflow produit les
   **mêmes sorties** que la v5 sur un même lot (diff des dossiers `anonymise/`).
4. **Tests conservés.** `gui_batch_paths` / `manual_masking` ont déjà leurs tests :
   ne pas y toucher. Ajouter un **smoke test de lancement** v6 + un test de
   **non-régression du contrat** (mocked managers, vérifier que le worker appelle
   `process_document` avec exactement les kwargs attendus).
5. **Garde-fou n°1 permanent.** `pytest tests/unit` (98) doit rester vert à chaque
   commit v6. Si un test moteur casse ⇒ la v6 a franchi sa frontière, rollback.
6. **Rétro-port RGPD/admin.** La v6 doit naître au niveau de la v5.5 **actuelle**
   (CHUXX, admin tag, VLM masqué), pas du WIP `b8c9c41` qui est en retard.

---

## 6. Zones de contact

### 6.1 Avec Agent B (D-13 — Paramètres avancés / Profils techniques)
- **Fichiers partagés :** sous-onglets « avancés » de Config (`config_reglages.py`,
  `config_regles.py`) + onglet/bandeau Profils.
- **Contrat attendu de B (avant que A code ces écrans) :**
  - liste des réglages **protégés admin** (cachés/désactivés en non-admin) ;
  - API `admin_mode.admin_required(feature)` pour verrouiller une action ;
  - règle de sauvegarde : config sensible **bloquée** en non-admin.
- **A fournit :** des conteneurs/onglets prêts où B injecte ses contrôles +
  un helper `is_admin()` déjà câblé dans le shell (titre annoté, sections
  masquées). A réserve le sous-onglet « Règles » comme zone B.
- **À écrire :** contrat A↔B avant tout code (plan v11.5 §3).

### 6.2 Avec Agent C (Licence — affichage état)
- **Emplacement UI réservé par A :** bandeau d'état en haut du shell (sous le
  header) + bloc dédié dans l'onglet **À propos** (statut, expiration, grace).
- **API attendue de C (`license.py`, à créer) :** une fonction de statut du type
  `get_license_status() -> {valid, expires_at, grace_days, machine_id, message}`
  que A appelle au démarrage et affiche (vert/orange/rouge). A **n'implémente
  aucune crypto** ; A consomme le statut.
- **Dégradation :** si `license.py` absent (dev), le bandeau s'efface
  silencieusement (même pattern que `admin_mode`/`vlm_manager` en try/except).

---

## 7. Risques spécifiques GUI v6 + mitigations

| Risque | Mitigation |
|---|---|
| customtkinter absent du venv/spec | Ajouter dépendance + tester build EXE tôt avec Agent D |
| Éditeur de masques (`MaskDesignerApp`) conçu pour Toplevel tk | L'intégrer en frame dans le sous-onglet Masquage, ou le garder en fenêtre détachée v1 |
| Glisser-déposer natif (mockup) absent de tkinter pur | `tkinterdnd2` ou fallback « Parcourir » ; à valider avec Dom |
| Régression silencieuse moteur via worker | Test contrat (kwargs `process_document`) + 98 tests verts |
| v6 part du WIP en retard (CHCB/admin) | Naître de la v5.5 actuelle (§5.6) |
| Dérive de portée (refonte logique) | controller/worker = orchestration pure, zéro détection |

---

## Résumé (5-8 lignes)

Le « WIP +1250 lignes » n'est **pas** une GUI v6 : c'est l'ancêtre tkinter de la
v5.5 actuelle (profils/masques/params déjà livrés), et la v5.5 est même **en
avance** dessus (fixes CHUXX/admin/VLM). La vraie matière première v6 = le
**mockup HTML validé** + la **logique métier v5.5** à réutiliser telle quelle.
Architecture cible : `Pseudonymisation_Gui_V6.py` + package `gui_v6/`
(customtkinter, **à ajouter au venv/spec**), 3 onglets (Utilisation / Configuration
à 4 sous-onglets / À propos), éditeur de masques intégré, 4 thèmes. **Garde-fou
n°1 :** tout passe par `gui_v6/controller.py`+`worker.py` qui appellent
`process_document(...)` avec les **mêmes kwargs** qu'aujourd'hui → le moteur ne
bouge pas, les 98 tests unit restent verts. Migration progressive : extraire
contrôleur d'abord, réécrire la vue ensuite, cohabitation v5/v6, bascule par
défaut en dernier (Agent D). Zones de contact : **B** sur les sous-onglets
Réglages/Règles + Profils (contrat admin écrit avant code), **C** sur le bandeau
d'état + bloc À propos (A consomme `get_license_status()`, A n'implémente aucune
crypto).

— Claude (Agent A)
