Files
anonymisation/docs/superpowers/specs/2026-06-25-gui-v6-beta-prod-design.md
Domi31tls 6554a6d590 docs(beta): audit prêt-bêta GUI V6 + design de mise en prod bêta testeur
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>
2026-06-25 16:43:04 +02:00

13 KiB
Raw Blame History

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 de ensure_loaded() ; si use_local_ner et état UNAVAILABLE (CamemBERT-bio non chargé, :173-175) → lever EngineUnavailableError. 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ée anonymisation_gui_v6_onefile.spec:145) pose os.environ.setdefault("ANON_SKIP_LEGACY_ONNX_MANAGER","1") avant from 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 de main(), 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 (override ANON_PORTAL_URL) ; gui_v6/app.py:41 localhost devient 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() + comparer payload.machine_id au machine_id local ; si mismatch → statut « licence liée à un autre poste » affiché, traitement non bloqué. Test : license.json copié (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é dans requirements.txt. Validation = taille EXE réelle + grep arbo PyInstaller pour torch.
  • 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.spec datas) → modèles présents à l'install, hors-ligne, sans téléchargement (aucun chemin de download runtime dans gui_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 : ConfigStateEngineSettings → kwargs → process_documentcfg (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/IPP restent toujours masqués. Tests : (a) décocher ETAB → établissement non masqué, autres masqués ; (b) décocher NIR → 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_path frozen : résoudre dictionnaires.yml à côté de sys.executable, copie au 1er lancement, passé en config_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) dans Pseudonymisation_Gui_V6.py avant 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 diagnostics qui 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 que usage_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_id comme 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, depuis build_info) dans gui_v6/__init__.py et dans installer/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) ; ajouter CloseApplications=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.md finalisé (+ étape « vérifier URL portail embarquée ») ; republish portail + MAJ SHA dans note-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.
  • 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)

  1. Aucune sortie produite si le NER obligatoire échoue (fail-close GUI = CLI).
  2. Activation licence fonctionne contre app.aivanov.eu ; binding correct ; non bloquant.
  3. Télémétrie d'un run réel visible au portail (RGPD : compteurs only).
  4. Les 7 toggles modifient réellement la sortie ; tous ON = non-régression ; décoché = relâché.
  5. Import/export, DnD, messages d'erreur : fonctionnels (zéro contrôle factice).
  6. Diagnostic d'un run visible au portail, contenant 0 nom/chemin/texte de document (RGPD vérifié).
  7. Modèles présents dans l'EXE : OCR + NER fonctionnent hors-ligne post-install, sans téléchargement.
  8. MAJ N→N+1 : upgrade propre, licence + config préservées, app fermée pendant la MAJ, version correcte.
  9. EXE rebuildé : self-test OK, OCR scanné OK, taille ~2 Go confirmée, 0 « cannot load module ».
  10. Suite verte, 0 régression, revue Qwen GO (sécurité + RGPD).
  11. 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_id MAC-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_id est éditable à la main : protège de la copie naïve, pas d'un utilisateur déterminé. Acceptable bêta.
  • R5RGPD 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.