Audit prêt-pour-bêta de GUI V6 (3 axes : parcours/UX/PII, moteurs/OnnxTR/frozen, licence/télémétrie/diffusion) → punch-list P0=7 / P1=11 / P2=6, findings vérifiés. Design validé Dom : périmètre « A + chaîne prod », P0 + P1 honnêteté UI, toggles « Données à détecter » câblés au moteur (gating par type + couplage rescan, pas de plancher dur), licence souple + binding poste (D-20.4), −2 Go via excludes PyInstaller, Linux-first TDD + un seul build Windows. Plus 3 exigences prod intégrées : diagnostics logs auto-upload scrubbé (liste-blanche), mise à jour propre (installeur), modèles embarqués hors-ligne. 6 chantiers (A-F), 2 repos. Diffusion = gate Dom. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
13 KiB
13 KiB
GUI V6 → prod bêta testeur — design
Date : 2026-06-25 · Branche : feature/q1-quarantine-mvp · HEAD de départ : 4b7c8db
Statut : design validé Dom (cadrage + décisions ci-dessous), prêt pour plan d'implémentation.
1. Objectif
Amener GUI V6 (gui_v6/) à une mise en prod bêta testeur complète : un testeur
non-technique installe l'EXE Windows → active sa licence → traite des PDF/images
médicaux → ses statistiques d'usage remontent au portail app.aivanov.eu, et l'équipe
peut récupérer ses logs de diagnostic (scrubbés) pour analyse. Le CLI est mis de côté
(pas abandonné) ; il partage le même core, donc les fixes cœur lui profitent.
2. Périmètre
Dans le périmètre (décisions Dom) :
- App finie : correction des 7 P0 + des P1 qui trompent le testeur + UI honnête de bout en bout.
- Rebuild Windows OnnxTR / torch-free (gain ~2 Go) + smoke.
- Chaîne prod : activation licence bout-en-bout + télémétrie qui remonte réellement + diffusion testée.
- Diagnostics : log fichier V6 + scrubbing PII (philosophie liste-blanche) + auto-upload portail.
- Mise à jour propre : version réelle injectée au build, upgrade en place, app fermée, config préservée.
- Modèles embarqués dans l'EXE : présents automatiquement à l'install, hors-ligne (déjà le cas, à fiabiliser).
Hors périmètre (différé, acté) :
- Enforcement licence dur (blocage), anti-VM (D-20.2/3), fingerprint multi-composants (D-20.1), vérification de signature RSA côté client + clé prod stable (P1-9).
- MAJ in-app auto-download (l'app télécharge+lance l'installeur) — la MAJ bêta se fait par téléchargement manuel de l'installeur depuis le portail.
- Onboarding profond : install validée sur site testeur, boucle de retour structurée.
- DnD réel = inclus (voir D) ; tout le reste P1 non listé + P2 non listés = itération ultérieure.
3. Décisions clés (et pourquoi)
| # | Décision | Justification |
|---|---|---|
| D1 | Licence souple + binding poste : activation câblée, statut affiché, vérif locale machine_id (D-20.4), pas de blocage du traitement. |
Valide la chaîne sans bloquer un testeur hors-ligne/mal activé. Durcissement post-bêta. |
| D2 | Toggles « Données à détecter » câblés pour de vrai, défaut tous ON. | Un contrôle visible non fonctionnel fausse le test humain. |
| D3 | Pas de plancher dur : décocher une catégorie relâche masquage et filet de sécurité pour cette catégorie ; l'utilisateur assume. | Sinon le toggle reste faussé (quarantaine systématique). EMAIL/IBAN/IPP (sans toggle) restent toujours masqués = plancher conservé. |
| D4 | −2 Go via excludes PyInstaller ; validation par taille EXE réelle. |
optimum[onnxruntime] tire torch en dép cœur ; legacy non câblé (import déjà sous try/except). |
| D5 | Linux-first TDD, un seul build Windows à la fin ; P0-5 porte à l'identique le hotfix CLI 6c6f653. |
Minimise les cycles de build chers. |
| D6 | Rebuild = autonome (build interne + smoke) ; diffusion (republish portail) = gate Dom. | Cadrage d'autonomie. |
| D7 | Logs auto-upload scrubbé : log fichier V6 rotatif + diagnostics structurés liste-blanche (type d'exception, étape, code) + redaction ciblée des identifiants de doc connus, envoyés au portail (endpoint /diagnostics, auth licence, non bloquant). |
Récupération auto pour analyse sans déranger le testeur ; RGPD garanti par la même philosophie que la télémétrie (whitelist), pas par un scrub a posteriori d'un log brut. |
| D8 | MAJ = installeur propre : version réelle injectée au build, AppId fixe (upgrade en place), CloseApplications, config/licence préservées (hors dossier install). |
Suffit en bêta ; MAJ in-app auto-download différée. |
4. Chantiers
A. Sûreté & robustesse cœur
- P0-1 — Fail-close GUI sur NER indisponible.
gui_v6/engine_bridge.py:make_process_fn(210-233) : capturer l'état deensure_loaded(); siuse_local_neret étatUNAVAILABLE(CamemBERT-bio non chargé,:173-175) → leverEngineUnavailableError. Le runner la remonte en échec (doc non livré). Réplique la garantie code-3 du CLI (scripts/anonymize_cli.py:184-198). Test : factory CamemBERT qui throw → 0 fichier en sortie + statut échec. - P0-5 — Frozen ONNX.
Pseudonymisation_Gui_V6.py:main(50-58, entrée frozen confirméeanonymisation_gui_v6_onefile.spec:145) poseos.environ.setdefault("ANON_SKIP_LEGACY_ONNX_MANAGER","1")avantfrom gui_v6.app import AnonymisationApp(l.55), et pré-charge CamemBERT. Test : ordre d'init unitaire ; confirmation au smoke EXE (non reproductible en dev Linux). - P0-7 — Lock multi-instance. File-lock
msvcrt.locking(Windows) au début demain(), message « application déjà ouverte ». Le nom du mutex est partagé avec l'installeur (D8/AppMutex). Test : unit mock.
B. Chaîne prod — licence + télémétrie
- P0-2 — URL portail.
DEFAULT_PORTAL_URL = "https://app.aivanov.eu"injectée à l'entrypoint (overrideANON_PORTAL_URL) ;gui_v6/app.py:41localhostdevient secours dev. Télémétrie et diagnostics héritent de l'URL (app.py:199). Test : entrypoint construit le client sur l'URL prod. - P0-6 — Licence souple + binding (D1). Activation câblée bout-en-bout. Au démarrage :
local_status()+ comparerpayload.machine_idaumachine_idlocal ; si mismatch → statut « licence liée à un autre poste » affiché, traitement non bloqué. Test :license.jsoncopié (machine_id ≠ local) → statut invalide, traitement fonctionne.
C. Build torch-free reproductible + modèles embarqués
- P0-3 — −2 Go (D4).
excludes=['torch','torchvision','optimum','optimum.onnxruntime','doctr']aux 3 specs ;optimum[onnxruntime]en optionnel commenté dansrequirements.txt. Validation = taille EXE réelle + grep arbo PyInstaller pourtorch. - P0-4 — Précache OnnxTR + modèles embarqués (#2). Étape avant PyInstaller dans
scripts/build_windows_oneclick.ps1:ocr_predictor(det_arch='db_resnet50', reco_arch='crnn_vgg16_bn')(télécharge les 2 poids). Confirme que le spec embarque bien CamemBERT-bio ONNX + poids OnnxTR (anonymisation_gui_v6_onefile.specdatas) → modèles présents à l'install, hors-ligne, sans téléchargement (aucun chemin de download runtime dansgui_v6/— vérifié). Doc build mise à jour.
D. Honnêteté UI (anti-faussé)
- P1-2 — Toggles « Données à détecter » câblés (D2/D3). 7 booléens (Noms→
NOM, DDN→DATE_NAISSANCE, Établissements→ETAB, Adresses→ADRESSE, NIR→NIR, Téléphones→TEL, N° adhérent→ADHERENT), défaut tous ON, fil :ConfigState→EngineSettings→ kwargs →process_document→cfg(core:2934/5678). Le moteur gate le masquage par type ; une catégorie décochée n'est plus masquée. Couplage sécurité (D3) : pour les catégories décochées du rescan résiduel (CRITICAL_PII_KEYS, core:610 — NIR, TEL, DATE_NAISSANCE), le rescan est relâché (pas de fausse quarantaine).EMAIL/IBAN/IPPrestent toujours masqués. Tests : (a) décocherETAB→ établissement non masqué, autres masqués ; (b) décocherNIR→ NIR en clair + pas de quarantaine ; (c) tous ON → non-régression. Revue Qwen obligatoire (code sécurité). - P1-1 — Glisser-déposer réel via
tkinterdnd2(tab_usage.py:103-115) + boutons conservés. - P1-3 — Import/Export config câblés (
tab_config.py:817-840), logique merge V5 (scripts/merge_params.py). - P1-4 —
config_pathfrozen : résoudredictionnaires.ymlà côté desys.executable, copie au 1er lancement, passé enconfig_path. - P1-5 — Erreurs/quarantaine lisibles : chemin sortie + mention
_*_failed/+ bouton « ouvrir le dossier ». - P2-1 / P2-2 (bonus) : étapes de progression et cartes format honnêtes (fonctionnelles ou visiblement non-cliquables).
E. Diagnostics & logs (#1, D7)
- E1 — Log fichier V6. Configurer un log loguru rotatif à chemin connu
(
%LOCALAPPDATA%/Aivanov/Anonymisation/logs/anonymisation.log) dansPseudonymisation_Gui_V6.pyavant tout import (combiné à l'ordre P0-5). Aujourd'hui V6 n'a aucun log fichier → en frozen fenêtré les diagnostics sont perdus. Test : le log est créé au chemin attendu. - E2 — Diagnostics structurés liste-blanche (RGPD). Un module
diagnosticsqui produit des événements structurés (type d'exception, étape moteur, code, durée, version,run_id,machine_id) — jamais de texte de document ni de contenu d'erreur libre. Les identifiants de documents (noms/chemins connus en entrée) sont redactés (→ ordinal/hash) partout. Même philosophie queusage_telemetry._ALLOWED_*. Tests : un run avec noms de fichiers PII → 0 nom/chemin/texte dans la charge utile diag. - E3 — Upload auto. Client diagnostic (réutilise l'auth licence/
machine_idcomme la télémétrie),POST /api/v1/diagnostics/report, non bloquant (thread daemon, spool best-effort, timeout court). Hérite de l'URL portail (P0-2). Test : échec réseau → traitement non figé, spool écrit. - E4 — Portail (
app_aivanov, repo séparé). Endpoint/api/v1/diagnostics/report(Pydantic strict, auth licence/seat, recalcul/validation serveur), stockage, vue admin « Diagnostics par client ». Migration alembic. Tests web. Revue Qwen RGPD.
F. Release, mise à jour & diffusion
- P1-7 + D8 — Version & installeur. Injecter la vraie version release (schéma
2026.MM.DD.HHMM, depuisbuild_info) dansgui_v6/__init__.pyet dansinstaller/Anonymisation.iss(AppVersion,VersionInfoVersion) — aujourd'hui codée en dur"1.0.0"/"6.0.0-g1"(3 schémas incohérents). - D8 — MAJ propre.
Anonymisation.iss: AppId fixe déjà présent (upgrade en place) ; ajouterCloseApplications=yes+AppMutex=<nom partagé avec P0-7>pour fermer l'app avant remplacement de l'EXE. Licence/config en%LOCALAPPDATA%/Aivanov/Anonymisation/(hors{app}) → préservées (vérifié). Test/checklist : installer N+1 sur N préserve licence+config, version affichée correcte. - Rebuild Windows GUI V6 (192.168.1.11 via ssh-windows) ; smoke :
--self-test; 1 PDF natif ; 1 PDF scanné (ocr_used=True) ; fail-close vérifié ; taille EXE mesurée (−2 Go ?) ; cycle MAJ N→N+1 testé. - Runbook
docs/beta/runbook-portail-beta.mdfinalisé (+ étape « vérifier URL portail embarquée ») ; republish portail + MAJ SHA dansnote-beta-client.md= gate Dom.
5. Stratégie de tests & non-régression
- TDD sur tout le testable Linux : nouveaux
tests/unit/test_gui_v6_*+ extension core (gating par type)- tests diagnostics/scrubbing. Non-régression suite existante (222+ tests) + gate
synthetic_regression.
- tests diagnostics/scrubbing. Non-régression suite existante (222+ tests) + gate
- Revue Qwen sur les fixes sécurité (P0-1, P0-6, P1-2 gating+rescan) et RGPD (F2/F4 diagnostics).
- Smoke EXE pour les concerns frozen-only (P0-4, P0-5, taille, cycle MAJ).
6. Critères d'acceptation (definition of done)
- Aucune sortie produite si le NER obligatoire échoue (fail-close GUI = CLI).
- Activation licence fonctionne contre
app.aivanov.eu; binding correct ; non bloquant. - Télémétrie d'un run réel visible au portail (RGPD : compteurs only).
- Les 7 toggles modifient réellement la sortie ; tous ON = non-régression ; décoché = relâché.
- Import/export, DnD, messages d'erreur : fonctionnels (zéro contrôle factice).
- Diagnostic d'un run visible au portail, contenant 0 nom/chemin/texte de document (RGPD vérifié).
- Modèles présents dans l'EXE : OCR + NER fonctionnent hors-ligne post-install, sans téléchargement.
- MAJ N→N+1 : upgrade propre, licence + config préservées, app fermée pendant la MAJ, version correcte.
- EXE rebuildé : self-test OK, OCR scanné OK, taille −~2 Go confirmée, 0 « cannot load module ».
- Suite verte, 0 régression, revue Qwen GO (sécurité + RGPD).
- Diffusion portail = sur GO Dom uniquement.
7. Risques & points différés
- R1 — P1-2 touche le cœur sécurité : risque de régression de masquage. Mitigation : TDD + gate
synthetic_regression+ revue Qwen ; défaut tous-ON identique à l'existant. - R2 — Binding
machine_idMAC-seule faible (contournable, fragile en VM). Accepté en souple ; durcissement (D-20.1) différé post-bêta — signalé. - R3 — P0-5 (crash frozen) non confirmable en dev : dépend du smoke EXE. Mitigation : portage identique du hotfix CLI connu-bon.
- R4 — Sans signature vérifiée côté client (différé), le binding
machine_idest éditable à la main : protège de la copie naïve, pas d'un utilisateur déterminé. Acceptable bêta. - R5 — RGPD diagnostics : un log technique peut fuiter de la PII. Mitigation = événements structurés liste-blanche (pas de texte libre) + redaction des identifiants de doc + revue Qwen RGPD + défaut conservateur (dans le doute, on ne remonte pas la ligne). C'est le risque RGPD principal du lot.
- Différé : MAJ in-app auto-download, enforcement dur, anti-VM, signature client, reste P1/P2.