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>
10 KiB
Audit prêt-pour-bêta — GUI V6 (HEAD 4b7c8db, 2026-06-25)
Périmètre : GUI V6 (gui_v6/) + chaîne prod (licence, télémétrie, diffusion) pour
une mise en bêta testeur. Cible : un testeur installe l'EXE Windows → active sa
licence → traite des PDF/images médicaux → ses stats remontent au portail.
Audit en 3 axes (parcours/UX/PII, moteurs/OnnxTR/frozen, licence/télémétrie/diffusion). Findings vérifiés dans le code (lignes citées) ; les 2 plus lourds recontrôlés par Claude.
À FAIRE par Dom : valider / élaguer cette liste avant tout code (cocher / barrer).
P0 — Bloqueurs bêta (à corriger avant toute diffusion)
P0-1 — Fuite PII silencieuse : la GUI ne fait pas fail-close si le NER obligatoire échoue
gui_v6/engine_bridge.py:222-231appelleensure_loaded()sans vérifier l'état ; si CamemBERT-bio ne charge pas (:173-175→UNAVAILABLE,camembert=None), le traitement continue en regex + gazetteers seuls et le document sort compté « Réussi ». Le rescan résiduel ne couvre PAS les noms de personnes.- Le CLI de prod fait l'inverse :
scripts/anonymize_cli.py:184-198fail-close (code 3, aucune sortie) si le modèle obligatoire manque. - Impact : modèle ONNX corrompu/absent (antivirus, ressource manquante) → PDF qui paraît traité, statut OK, mais noms patients potentiellement en clair. Risque #1.
- Fix : dans
process_fn, siuse_local_neret étatUNAVAILABLE→ lever une exception explicite (refus de traitement), le runner la remonte en échec. Aligne la GUI sur la garantie code-3 du CLI.
P0-2 — Chaîne prod cassée : la GUI pointe http://localhost, jamais le portail réel
Pseudonymisation_Gui_V6.py:57→AnonymisationApp()sans client →gui_v6/app.py:41LicenseClient("http://localhost"). La télémétrie réutilise cette URL (app.py:199). Aucune env var / config / injection build ne fixe l'URL portail (grep exhaustif = 0).- Impact : sur un poste testeur, activation licence ET télémétrie POSTent sur
localhost→ rien n'écoute → activation impossible, stats jamais remontées. Toute la « chaîne prod » demandée est non fonctionnelle. Le portail tourne surapp.aivanov.eu(vérifié live, HTTPS 200, artefact servi). - Fix : injecter l'URL réelle (
DEFAULT_PORTAL_URL="https://app.aivanov.eu"ou env var),localhostdevient le secours dev. Impose un rebuild (l'EXE déjà publié est inutilisable pour l'activation).
P0-3 — Gain −2 Go non réalisé : optimum réintroduit torch en dur
requirements.txt:3=optimum[onnxruntime]>=2.0.0; optimum déclaretorch>=1.11en dépendance cœur (vérifié). Le build installe requirements → torch dans le venv → PyInstaller le bundle malgré le retrait des hiddenimports (4b7c8db). EXE ~+2 Go.optimumn'est importé que parner_manager_onnx.py(legacy ONNX non câblé à la GUI),Pseudonymisation_Gui_V5.py(V5 legacy) et un script de finetune — jamais pargui_v6/(le NER GUI =camembert_ner_manageren onnxruntime brut).- Fix : retirer
optimum(et hiddenimportsoptimum*) du build frozen GUI ; valider « torch-free » par la taille EXE réelle + grep de l'arbo PyInstaller, pas par le diff du spec.
P0-4 — Build non reproductible : pas de précache des poids OnnxTR
anonymisation_gui_v6_onefile.spec:50-67raiseFileNotFoundErrorsi les poidsdb_resnet50+crnn_vgg16_bnabsents du cache ;scripts/build_windows_oneclick.ps1ne télécharge jamais ces poids. Le build « marche » seulement grâce au cache résiduel de la machine 192.168.1.11.- Fix : étape pré-PyInstaller :
python -c "from onnxtr.models import ocr_predictor; ocr_predictor(det_arch='db_resnet50', reco_arch='crnn_vgg16_bn')"+ doc.
P0-5 — Crash frozen ONNX probable : le hotfix CLI 6c6f653 non reporté au launcher GUI
- Le launcher V6 ne pose pas
ANON_SKIP_LEGACY_ONNX_MANAGER=1(lu à l'import du core,anonymizer_core_refactored_onnx.py:211) et ne pré-charge pas CamemBERT — alors que le hotfix CLI (scripts/anonymize_cli.py:87) le fait pour éviter « cannot load module more than once per process » en frozen Windows. - Fix : poser le flag tout en haut de
Pseudonymisation_Gui_V6.py+ pré-charger CamemBERT avant le 1er import du core. À confirmer sur l'EXE (non reproductible en dev).
P0-6 — Contrôle de licence purement décoratif (décision Dom requise)
- Enforcement nul :
tab_usage.py:205active « Lancer » selon le nombre de documents seulement ; aucune gate sur le statut licence (lu pour affichage uniquement). - Pas de vérif locale :
license_client.py:208-213litlicense.jsonsans vérifier signature nimachine_id→ fichier copiable sur N postes = N licences « actives ». machine_id= MAC seule 12 chars (machine_id.py:12-14) = clonable, change en VM.- D-20.1 (fingerprint multi-composants) et D-20.4 (vérif locale machine_id) non implémentés. La bêta est une fenêtre idéale (aucune activation prod existante à migrer).
- Décision Dom : niveau d'enforcement bêta (souple / dur / différé) — voir question.
P0-7 — Pas de protection multi-instance (lock perdu vs V5)
Pseudonymisation_Gui_V6.py:50-59lancemainloop()sans file-lockmsvcrt.locking(présent en V5). Double-clic testeur → 2 instances, 2 chargements ONNX, 2 écritures dans le même dossier sortie.- Fix : réintroduire le lock V5 + message « application déjà ouverte ».
P1 — Forte friction testeur / écarts (à arbitrer pour la bêta)
- P1-1 — Dropzone décorative (
tab_usage.py:103-115) : zone « déposez vos fichiers » sans glisser-déposer réel. Confusion dès la 1re étape. → câbler DnD (tkinterdnd2) ou retirer la métaphore. - P1-2 — 7 toggles « Données à détecter » non câblés (
tab_config.py:351-357) : les interrupteurs (Noms, DDN, Établissements…) n'ont ni variable ni callback → aucun effet. Promesse UI non tenue. → câbler au moteur ou rendre informatifs (lecture seule). - P1-3 — Import/Export config désactivés « à venir » (
tab_config.py:817-840) alors que le Partage est mis en avant. Régression vs workflow email V5. → câbler ou retirer. - P1-4 —
config_pathjamais transmis (app.py:155-162,tab_usage.py:53,176) : ledictionnaires.ymlexterne éditable n'est pas garanti chargé en frozen (pas de copie au 1er lancement). Personnalisations établissement potentiellement ignorées. - P1-5 — Erreurs/quarantaine sans pointeur (
tab_usage.py:268,282) : « échec » / « quarantaine » sans indiquer où est le doc non livré (_*_failed/). → afficher le chemin sortie + bouton « ouvrir le dossier ». - P1-6 — Pas de validation d'inscriptibilité du dossier de sortie (
processing_runner.py:214) : dossier en lecture seule → tout le lot échoue par doc avec message cryptique. → test d'écriture en amont. - P1-7 — Version app incohérente :
gui_v6/__init__.py:11=6.0.0-g1vs artefact/note bêta2026.06.18.1203(remonte aussi en télémétrie). → aligner sur le schéma de release. - P1-8 — Runbook non exécutable tel quel : republier impose un rebuild après P0-2 (le
SHA de
note-beta-client.mddeviendra caduc). → ajouter étape « vérifier URL portail embarquée » au runbook. - P1-9 — Clé de signature licence = clé dev auto-générée (
app_aivanov/app/signing.py) : KEY_ID « dev », paire RSA régénérée si volume recréé → licences invalidées. → provisionner une clé prod stable avant d'activer la vérif de signature côté client. - P1-10 — Politiques VM (D-20.2) / dossiers partagés (D-20.3) non implémentées (décision Dom, marquées « DECISION REQUIS » dans D-20). Acceptable de différer en bêta interne — à acter.
- P1-11 — Audit JSONL
original="docTR"(anonymizer_core_refactored_onnx.py:5356) : trace de provenance périmée (OCR = OnnxTR) visible dans le livrable d'audit. →"OnnxTR".
P2 — Polish (best-effort)
- P2-1 — Étapes de progression statiques (
tab_usage.py:144-147) : pastilles Extraction/Détection/Masquage/PDF décoratives ; pas de feedback intra-document. - P2-2 — Cartes format de sortie décoratives (
tab_usage.py:118-127) : suggèrent un choix alors que la sortie est toujours PDF+TXT. → présenter comme « sorties produites ». - P2-3 — Commentaires/docstrings docTR périmés (
anonymizer_core_refactored_onnx.py:55,102) : cosmétique, comportement OK. - P2-4 —
os_infonon envoyé à l'activation (license_client.py:161-164) : perte d'info diagnostique mineure. - P2-5 — Contact / canal de retour vide dans
docs/beta/note-beta-client.md:36. - P2-6 —
/api/v1/versionne déclenche pas de download (tab_about.py:164-178) : le testeur va manuellement sur le portail. Acceptable bêta.
Verdicts positifs (ne pas re-traiter)
- Télémétrie RGPD : irréprochable. Double filtrage liste-blanche (
usage_telemetry.py:72,_ALLOWED_DOC_KEYS) + recalcul serveur Pydantic strict. Aucun nom/chemin/texte ne peut fuiter. Non bloquante (thread daemon, timeout 4 s, spool best-effort). App utilisable hors-ligne. - Pipeline cœur fail-closed correct : quarantaine moteur → échec,
SEUIL_RESCAN_RESIDUEL=0,redact_pdf_rasterpas dans unexcept: pass. La fuite P0-1 vient du wiring GUI, pas du cœur. - engine_bridge / affichage moteurs : logique « n'afficher que les moteurs embarqués » saine et cohérente avec OnnxTR (OCR) + CamemBERT-bio (NER onnxruntime). 0 référence docTR dans les chaînes affichées à l'utilisateur.
- Câblage
ONNXTR_CACHE_DIRfrozen : chemins concordants, fail-closed si OnnxTR absent. - Portail déployé et fonctionnel :
app.aivanov.euHTTPS, artefact servi, download authentifié (licence active requise). Le serveur n'est pas le problème — le client n'y est pas connecté.
Décompte : P0 = 7 · P1 = 11 · P2 = 6
Synthèse pour décision
Le « complet pour prod bêta » est plus que rebuild + ship : la chaîne prod est aujourd'hui non fonctionnelle bout-en-bout (P0-2 localhost, P0-6 licence décorative), il y a un risque PII #1 (P0-1 fail-open), et le −2 Go n'est pas acquis (P0-3 optimum). Rien de rédhibitoire : findings nets, fixes ciblés. C'est un vrai chantier de finition + câblage, pas une réécriture.