fix(perf): apply MVP threading hotfix

Configure numerical library and torch threading for H1, keep raster threading/timing instrumentation, remove CONCERTATION from forced masks after real PDF FP testing, and record coordination archive state.
This commit is contained in:
2026-06-08 10:41:15 +02:00
parent eb6e030183
commit c40441d03a
68 changed files with 2075 additions and 20 deletions

View File

@@ -19,9 +19,20 @@ import os
import re
import shutil
import sys
from concurrent.futures import ProcessPoolExecutor
import time
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from datetime import datetime
# --- H1 perf (D-19) : usage multi-cœur des libs numériques en EXE frozen ---
# En PyInstaller frozen, OpenMP/MKL/BLAS tombent souvent à 1 thread (CPU ~12 %).
# Ces variables sont lues par numpy/torch/onnxruntime à leur init : elles doivent
# donc être posées AVANT l'import de pdfplumber/PIL (numpy transitif) ci-dessous.
# setdefault : on n'écrase jamais un réglage explicite posé par l'utilisateur/admin.
_n_cpu_threads = str(os.cpu_count() or 4)
for _env in ("OMP_NUM_THREADS", "MKL_NUM_THREADS", "OPENBLAS_NUM_THREADS",
"NUMEXPR_NUM_THREADS", "VECLIB_MAXIMUM_THREADS"):
os.environ.setdefault(_env, _n_cpu_threads)
log = logging.getLogger(__name__)
from dataclasses import dataclass, field
from pathlib import Path
@@ -64,6 +75,46 @@ except Exception:
_doctr_ocr_predictor = None # type: ignore
_DOCTR_AVAILABLE = False
_doctr_model_cache = None
_TORCH_THREADS_CONFIGURED = False
def _configure_torch_threads():
"""Configure les threads PyTorch pour exploiter tous les cœurs en mode frozen.
En EXE PyInstaller, torch ne configure pas ses threads par défaut et reste
à 1 thread intra-op + 1 inter-op, ce qui limite l'OCR docTR et le NER
à ~12 % CPU sur une machine 8 threads.
Idempotent : appelable depuis l'OCR (doc scanné) comme depuis le NER (doc
natif sans OCR). `set_num_interop_threads` ne peut être posé qu'une seule
fois avant tout travail parallèle ; le flag évite un 2e appel qui lèverait.
"""
global _TORCH_THREADS_CONFIGURED
if _TORCH_THREADS_CONFIGURED:
return
try:
import torch
n_cpus = os.cpu_count() or 4
torch.set_num_threads(n_cpus)
try:
torch.set_num_interop_threads(min(n_cpus, 8))
except Exception:
pass # inter-op déjà figé par un travail torch antérieur — non bloquant
_TORCH_THREADS_CONFIGURED = True
log.info("torch threads config: intra=%d inter=%d (CPUs=%d)",
n_cpus, min(n_cpus, 8), os.cpu_count() or 0)
except Exception as e:
log.debug("torch threads config skipped: %s", e)
def _get_doctr_model():
global _doctr_model_cache
if _doctr_model_cache is None:
_configure_torch_threads()
_doctr_model_cache = _doctr_ocr_predictor(
det_arch="db_resnet50", reco_arch="crnn_vgg16_bn", pretrained=True
)
return _doctr_model_cache
try:
from detectors.hospital_filter import HospitalFilter
_HOSPITAL_FILTER_AVAILABLE = True
@@ -1093,16 +1144,6 @@ def _apply_admin_identifier_hits(full_raw: str, audit: List["PiiHit"], cfg: Dict
# ----------------- Extraction -----------------
_doctr_model_cache = None
def _get_doctr_model():
global _doctr_model_cache
if _doctr_model_cache is None:
_doctr_model_cache = _doctr_ocr_predictor(
det_arch="db_resnet50", reco_arch="crnn_vgg16_bn", pretrained=True
)
return _doctr_model_cache
def _extract_page_layout_aware(page) -> str:
"""Extrait le texte d'une page PyMuPDF en gérant les layouts multi-colonnes.
@@ -1763,7 +1804,7 @@ def _kv_value_only_mask(line: str, audit: List[PiiHit], page_idx: int, cfg: Dict
parts = SPLITTER.split(line, maxsplit=1)
# Une ligne narrative qui se termine par ` ;` ou ` :` produit un split
# avec une "value" vide. La "key" contient alors tout le narratif —
# incluant d'éventuels force_term (`CHUXX`, `CONCERTATION`…) qui doivent
# incluant d'éventuels force_term (`CHUXX`, sigle local...) qui doivent
# être masqués. Idem si la "key" fait plus de 5 mots : c'est très
# probablement du narratif, pas un libellé `Label : valeur`.
if len(parts) == 2 and parts[1].strip() and len(parts[0].split()) <= 5:
@@ -2977,6 +3018,11 @@ def _run_ner_on_original_text(
Returns:
Liste de NerDetection dédupliquée (par token+label+page+source).
"""
# H1 perf (D-19) : couvre le cas du PDF natif (texte riche, OCR sauté) où
# _get_doctr_model() n'est jamais appelé ; les NER torch (EDS-Pseudo, GLiNER)
# tourneraient alors mono-thread. Idempotent (no-op si déjà configuré par l'OCR).
_configure_torch_threads()
detections: List[NerDetection] = []
seen: set = set() # (token_lower, label, page_idx, source) pour dédoublonnage
@@ -4274,7 +4320,9 @@ def redact_pdf_raster(original_pdf: Path, audit: List[PiiHit], out_pdf: Path, dp
rects.extend(found)
all_rects[pno] = rects
# Phase 2 : rasterisation parallèle (ProcessPoolExecutor)
# Phase 2 : rasterisation parallèle (ProcessPoolExecutor hors EXE,
# ThreadPoolExecutor en EXE PyInstaller pour éviter de relancer la GUI).
raster_t0 = time.perf_counter()
n_pages = len(doc)
rects_as_tuples = {
pno: [(r.x0, r.y0, r.x1, r.y1) for r in rects]
@@ -4313,12 +4361,26 @@ def redact_pdf_raster(original_pdf: Path, audit: List[PiiHit], out_pdf: Path, dp
for pno in range(n_pages)
]
# Mode frozen (PyInstaller --onefile) : ProcessPoolExecutor relance l'exe
# et ouvre des fenêtres GUI fantômes → séquentiel obligatoire
if getattr(sys, 'frozen', False) or n_pages <= 2:
frozen = bool(getattr(sys, 'frozen', False))
disable_threads = os.getenv("ANON_DISABLE_RASTER_THREADS", "").lower() in {"1", "true", "yes", "on"}
if n_pages <= 2:
log.info("Raster PDF: mode=sequential pages=%d dpi=%d reason=small_pdf", n_pages, dpi)
results = sorted([_rasterize_page(t) for t in tasks], key=lambda x: x[0])
elif frozen and not disable_threads:
n_workers = min(n_pages, os.cpu_count() or 4)
log.info("Raster PDF: mode=threads pages=%d workers=%d dpi=%d frozen=1", n_pages, n_workers, dpi)
try:
with ThreadPoolExecutor(max_workers=n_workers) as pool:
results = sorted(pool.map(_rasterize_page, tasks), key=lambda x: x[0])
except Exception as e:
log.warning("Raster PDF threaded mode failed, fallback sequential: %s", e)
results = sorted([_rasterize_page(t) for t in tasks], key=lambda x: x[0])
elif frozen and disable_threads:
log.info("Raster PDF: mode=sequential pages=%d dpi=%d frozen=1 reason=env_disabled", n_pages, dpi)
results = sorted([_rasterize_page(t) for t in tasks], key=lambda x: x[0])
else:
n_workers = min(n_pages, os.cpu_count() or 4)
log.info("Raster PDF: mode=processes pages=%d workers=%d dpi=%d frozen=0", n_pages, n_workers, dpi)
with ProcessPoolExecutor(max_workers=n_workers) as pool:
results = sorted(pool.map(_rasterize_page, tasks), key=lambda x: x[0])
@@ -4331,6 +4393,7 @@ def redact_pdf_raster(original_pdf: Path, audit: List[PiiHit], out_pdf: Path, dp
_apply_pseudo_xmp_metadata(out)
out.save(str(out_pdf), deflate=True, garbage=4, clean=True)
out.close()
log.info("Raster PDF done: pages=%d output=%s duration=%.2fs", n_pages, out_pdf.name, time.perf_counter() - raster_t0)
# ----------------- VLM pour PDFs scannés -----------------
@@ -4424,15 +4487,31 @@ def process_pdf(
camembert_manager=None,
quarantine_mgr: Optional["QuarantineManager"] = None,
) -> Dict[str, str]:
perf_t0 = time.perf_counter()
last_mark = perf_t0
def _perf_mark(stage: str) -> None:
nonlocal last_mark
now = time.perf_counter()
log.info("PERF %s: stage=%s duration=%.2fs total=%.2fs",
pdf_path.name, stage, now - last_mark, now - perf_t0)
last_mark = now
log.info("PERF %s: start frozen=%s vector=%s raster=%s",
pdf_path.name, bool(getattr(sys, "frozen", False)), make_vector_redaction, also_make_raster_burn)
out_dir.mkdir(parents=True, exist_ok=True)
cfg = load_dictionaries(config_path)
_perf_mark("load_config")
pages_text, tables_lines, ocr_used, ocr_word_map = extract_text_with_fallback_ocr(pdf_path)
_perf_mark("extract_text_ocr")
# Q-1 B-3 : pré-flight texte vide. Si moins de SEUIL_TEXTE_MINI caractères
# extraits, c'est probablement un scan non-OCRisé ou un document corrompu.
# On NE traite PAS — quarantaine totale, le doc original est copié pour
# ré-essai manuel.
extracted_chars = sum(len(p) for p in pages_text)
log.info("PERF %s: pages=%d extracted_chars=%d ocr_used=%s ocr_pages=%d",
pdf_path.name, len(pages_text), extracted_chars, bool(ocr_used), len(ocr_word_map or {}))
if extracted_chars < SEUIL_TEXTE_MINI:
log.warning("Preflight failed for %s: only %d chars extracted (seuil=%d)",
pdf_path.name, extracted_chars, SEUIL_TEXTE_MINI)
@@ -4479,12 +4558,14 @@ def process_pdf(
gliner_mgr=gliner_manager,
camembert_mgr=camembert_manager,
)
_perf_mark("regex_rules")
# 1b) VLM (optionnel) — sur les PDFs scannés uniquement
if ocr_used and vlm_manager is not None and VlmManager is not None:
try:
if vlm_manager.is_loaded():
_apply_vlm_on_scanned_pdf(pdf_path, anon, ocr_word_map, vlm_manager)
_perf_mark("vlm_scan")
except Exception:
pass # dégradation gracieuse
@@ -4498,9 +4579,11 @@ def process_pdf(
else:
final_text, hf_hits = apply_hf_ner_on_narrative(final_text, cfg, ner_manager, ner_thresholds)
anon.audit.extend(hf_hits)
_perf_mark("ner_optional")
# 3) Rescan selectif
final_text = selective_rescan(final_text, cfg=cfg)
_perf_mark("selective_rescan")
# 3a-bis) Nettoyage post-masquage : continuation orpheline d'un nom composé
# coupé par saut de ligne. Cas Trackare en colonnes : "NOCENT-EJNAINI"
@@ -4820,6 +4903,7 @@ def process_pdf(
r"DOSSIER|NDA|EPISODE|RPPS|DATE_NAISSANCE|AGE|NIR|IBAN|OGC)\])\]+"
)
final_text = _RE_BRACKET_CLEAN.sub(r"\1", final_text)
_perf_mark("post_cleaning")
# 6) Whitelist absolue : filtrer les hits qui matchent un terme whitelist
# de la GUI (clé YAML whitelist_phrases). Filet de sécurité après tous les
@@ -4960,6 +5044,7 @@ def process_pdf(
for hit in audit_for_file:
f.write(json.dumps(hit.__dict__, ensure_ascii=False) + "\n")
outputs = {"text": str(txt_path), "audit": str(audit_path)}
_perf_mark("write_text_audit")
# PDFs
if make_vector_redaction and fitz is not None:
@@ -4967,6 +5052,7 @@ def process_pdf(
try:
redact_pdf_vector(pdf_path, anon.audit, vec_path, ocr_word_map=ocr_word_map)
outputs["pdf_vector"] = str(vec_path)
_perf_mark("pdf_vector")
except Exception as e:
# Q-1 D2/D3 : ne plus avaler silencieusement. Le texte (.pseudonymise.txt)
# est déjà sorti avant ce bloc.
@@ -5023,6 +5109,8 @@ def process_pdf(
ras_path = out_dir / f"{base}.redacted_raster.pdf"
redact_pdf_raster(pdf_path, anon.audit, ras_path, ogc_label=ogc_label, ocr_word_map=ocr_word_map)
outputs["pdf_raster"] = str(ras_path)
_perf_mark("pdf_raster")
log.info("PERF %s: done total=%.2fs outputs=%s", pdf_path.name, time.perf_counter() - perf_t0, sorted(outputs.keys()))
return outputs

View File

@@ -24,7 +24,6 @@ blacklist:
force_mask_terms:
- CHUXX
- 'Dates du séjour :'
- CONCERTATION
- LABORATOIRE de BIOLOGIE MEDICALE
force_mask_regex:
- '13\s*,?\s*Avenue\s+de\s+l.Interne\s+J\.?\s*LOEB\s+BP\s*\d+'

View File

@@ -23,7 +23,6 @@ profiles:
blacklist_force_mask_terms:
- CHUXX
- 'Dates du séjour :'
- CONCERTATION
- LABORATOIRE de BIOLOGIE MEDICALE
additional_stopwords: []
preferred_manual_mask_template: ''
@@ -47,7 +46,6 @@ profiles:
blacklist_force_mask_terms:
- CHUXX
- 'Dates du séjour :'
- CONCERTATION
- LABORATOIRE de BIOLOGIE MEDICALE
additional_stopwords: []
preferred_manual_mask_template: ''
@@ -71,7 +69,6 @@ profiles:
blacklist_force_mask_terms:
- CHUXX
- 'Dates du séjour :'
- CONCERTATION
- LABORATOIRE de BIOLOGIE MEDICALE
additional_stopwords: []
preferred_manual_mask_template: ''

View File

@@ -0,0 +1,91 @@
---
from: dom
to: qwen
date: 2026-06-05T18:05:00+02:00
topic: v11-5-revue-transverse
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d17-v11-5-chantiers-paralleles.md
- file: docs/coordination/inbox/for-claude/2026-06-05_17-55_dom-via-codex_v11-5-chantiers-paralleles.md
---
# v11.5 — rôle Qwen en revue transverse
Message déposé par Codex à la demande de Dom.
Claude va préparer la v11.5 avec agents parallèles :
1. GUI v6
2. D-13 complet
3. Plateforme licence
4. Intégration / merge
Ton rôle n'est pas de coder en parallèle sur ces fichiers. Ton rôle est de
préparer la revue transverse, les risques et les critères d'acceptation.
## Gel bêta
Ne pas perturber le pack bêta v11 actuel.
Tant que Dom n'a pas fini ses tests Windows et donné son GO :
- aucune modification code ;
- aucune modification packaging ;
- aucun changement `.gitignore` / build / moteur / GUI ;
- lecture, analyse et livrables Markdown uniquement.
## T-P — Revue de découpage v11.5
Après lecture des décisions D-13, D-14, D-17 et des docs GUI v6, produire :
`docs/coordination/inbox/for-dom/2026-06-05_qwen_revue-decoupage-v11-5.md`
Contenu attendu :
- frontières entre GUI v6 / D-13 / licence ;
- fichiers à risque de conflit ;
- dépendances cachées ;
- points qui doivent être contractualisés avant codage ;
- ordre de merge recommandé ;
- désaccords ou alertes à soumettre à Dom.
## T-Q — Matrice d'acceptation v11.5
Produire :
`docs/coordination/inbox/for-dom/2026-06-05_qwen_matrice-acceptation-v11-5.md`
Contenu attendu :
- critères GO/NO-GO pour GUI v6 ;
- critères GO/NO-GO pour D-13 complet ;
- critères GO/NO-GO pour licence client ;
- tests unitaires / intégration / smoke tests nécessaires ;
- scénarios beta utilisateur ;
- critères RGPD / sécurité / offline.
## T-R — Registre de risques v11.5
Produire :
`docs/coordination/inbox/for-dom/2026-06-05_qwen_risques-v11-5.md`
Contenu attendu :
- risques techniques ;
- risques RGPD/sécurité ;
- risques UX ;
- risques packaging/déploiement ;
- risques planning ;
- mitigation proposée pour chaque risque.
## Contraintes
- Lecture seule stricte.
- Ne pas refaire le travail des agents Claude.
- Ne pas toucher au WIP Windows sauvegardé.
- Ne pas changer la branche de livraison bêta.
- Si tu identifies un blocage structurant, le formuler comme question pour Dom.
— Dom via Codex

View File

@@ -0,0 +1,50 @@
---
from: dom
to: qwen
date: 2026-06-05T19:20:00+02:00
topic: app-aivanov-tests-securite
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
- decision: docs/coordination/decisions/2026-06-02_dom_d14-plateforme-licence-architecture.md
---
# Mission Qwen - tests, securite et contrat app.aivanov.fr
Dom valide le lancement parallele de la plateforme web `app.aivanov.fr`.
## Write scope
Projet cible :
`/home/dom/ai/app_aivanov`
Qwen prend prioritairement :
- tests API ;
- tests modele ;
- tests securite ;
- contrat JSON licence ;
- checklist RGPD / phone-home ;
- revue absence secrets et PII.
## Tests attendus
- activation valide ;
- token invalide ;
- quota 1 licence = 1 poste ;
- revocation au `/check` ;
- expiration et grace period ;
- download version active uniquement ;
- aucune cle privee dans le repo ;
- aucun payload patient ;
- logs sans PII medicale.
## Garde-fous
- OwnCloud est hors cible produit.
- Aucun deploiement public sans GO Dom.
- Ne pas modifier le pack beta Windows.
- Ne pas dupliquer le developpement plateforme de Claude : travailler sur tests, securite et corrections ciblees.

View File

@@ -0,0 +1,34 @@
---
from: dom
to: qwen
date: 2026-06-05T19:30:00+02:00
topic: perf-mvp-p1
status: open
priority: blocker
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
---
# Performance MVP - analyse Qwen
Retour test Windows Dom : anonymisation beaucoup trop lente, CPU ~12 %, RAM ~16 Go.
## Mission
Faire une analyse performance concrete :
- fichiers/lignes responsables ;
- explication du mono-coeur en EXE ;
- impact OCR docTR et rasterisation ;
- plan de benchmark minimal ;
- recommandations hotfix MVP vs v11.5 ;
- criteres d'acceptation.
## Questions a trancher dans le rapport
- Peut-on re-paralleliser la rasterisation en EXE PyInstaller sans risque ?
- Faut-il ajouter une option/profil "rapide texte natif" tout en gardant la sortie
securisee par defaut ?
- Peut-on reduire le DPI OCR ou raster sans augmenter le risque de fuite ?
- Quels logs/timings sont indispensables pour debug chez Dom ?

View File

@@ -0,0 +1,45 @@
---
from: dom-via-codex
to: qwen
date: 2026-06-05T20:55:00+02:00
topic: handoff-fin-journee
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- report: docs/coordination/inbox/for-dom/2026-06-05_qwen_perf-mvp-p1-diagnostic.md
---
# Handoff fin de journee pour Qwen
Dom arrete pour aujourd'hui. Ne pas lancer de nouveau chantier sans reprise explicite.
## Etat valide
app.aivanov.fr :
- tests ajoutes dans `/home/dom/ai/app_aivanov/tests` ;
- `python3 -m pytest -q` : 10 passed ;
- serveur local coupe pour la nuit ;
- OwnCloud hors cible, interface web `app.aivanov.fr` confirmee.
Performance MVP :
- ton diagnostic mono-coeur/raster/OCR a ete lu ;
- Codex a applique un hotfix raster frozen avec `ThreadPoolExecutor` + fallback ;
- Codex a ajoute des logs `PERF` par etape ;
- `.venv/bin/python -m pytest tests/unit -q` : 98 passed ;
- rollback raster threads : `ANON_DISABLE_RASTER_THREADS=1`.
## Reprise conseillee
Quand Dom reprend :
1. valider le patch perf sur EXE Windows avec le PDF reel ;
2. demander les lignes `PERF` de `anonymisation.log` ;
3. comparer CPU/RAM/temps avant-apres ;
4. verifier que leak score et tests restent inchanges ;
5. completer la matrice de benchmarks natif/scanne si le gain est insuffisant.
Ne pas modifier `app_aivanov` hors tests/corrections ciblees sans coordination.

View File

@@ -0,0 +1,64 @@
---
from: claude
to: dom
date: 2026-06-08T00:00:00+02:00
topic: perf-mvp-h1-torch-threads-complete
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- report: docs/coordination/inbox/for-dom/2026-06-05_claude_diagnostic-perf-mvp.md
- handoff: docs/coordination/inbox/for-claude/2026-06-05_20-55_dom-via-codex_handoff-fin-journee.md
---
# Perf MVP (D-19) — H1 torch threads COMPLÉTÉ
Reprise sur GO Dom ("tu peux reprendre si tu as des tâches à finir"). Aucune
diffusion, aucun build, aucun commit : modifs en working tree uniquement.
## Constat à la reprise
H1 était **partiellement** fait (ajouté après le handoff Codex du 05/06) :
`_configure_torch_threads()` existait mais (a) ne posait **pas** les variables
d'environnement `OMP/MKL` prévues par la spec H1, et (b) n'était appelée que
par l'OCR → un **PDF natif** (texte riche, OCR sauté) laissait le NER torch
(EDS-Pseudo, GLiNER) en mono-thread.
## Ce que j'ai ajouté (anonymizer_core_refactored_onnx.py)
1. **Variables d'env multi-cœur** posées en tête de module via `os.environ.setdefault`,
AVANT l'import de pdfplumber/PIL (donc avant numpy/torch/onnxruntime) :
`OMP_NUM_THREADS`, `MKL_NUM_THREADS`, `OPENBLAS_NUM_THREADS`,
`NUMEXPR_NUM_THREADS`, `VECLIB_MAXIMUM_THREADS` = `os.cpu_count()`.
`setdefault` : n'écrase jamais un réglage explicite (utilisateur/admin).
→ c'est ce que torch/onnxruntime lisent à l'init en EXE frozen.
2. `_configure_torch_threads()` rendue **idempotente** (flag global) : appelable
depuis l'OCR comme depuis le NER sans risque sur `set_num_interop_threads`
(qui ne peut être posé qu'une fois).
3. Appel ajouté dans `_run_ner_on_original_text()` → couvre le **PDF natif**
(NER torch multi-cœur même sans OCR).
## Vérifications (Linux, ce jour)
- `python3 -m py_compile` : OK.
- `.venv/bin/python -m pytest tests/unit -q` : **98 passed** (non-régression).
- Exécution réelle : `torch.get_num_threads() = 32` après config (CPUs=32),
idempotence confirmée (2e appel = no-op).
- **Aucun changement de détection / rectangles / texte produit** : H1 ne touche
que le nombre de cœurs. Sortie identique, seul le temps change → leak score
inchangé par construction.
## Ce que JE NE PEUX PAS faire (bloqué sur toi / Windows)
- Rebuild EXE Windows (H1+H2+H4) — machine de build + GO Dom requis.
- Mesurer le gain réel : il faut **ton PDF lent** + les lignes `PERF` du log.
- H3 (batch OCR) : à décider **seulement** si le log prouve que l'OCR scanné
domine. Je ne l'ai pas touché.
## Questions pour orienter la suite
1. Ton PDF de test lent était **scanné** (OCR) ou **natif** (texte) ? + nb pages / taille.
2. Veux-tu que je **commite** H1+H2+H4 sur `feature/q1-quarantine-mvp` (ou une
branche `fix/perf-mvp` dédiée) avant le rebuild, ou je laisse en working tree ?
— Claude

View File

@@ -0,0 +1,46 @@
---
from: dom
to: claude
date: 2026-06-05T14:45:00+02:00
topic: hold-owncloud-inno-apres-tests
status: open
priority: blocker
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
- file: docs/coordination/inbox/for-dom/2026-06-05_claude_pack-beta-build-report.md
---
# Stop diffusion — tests Windows Dom avant OwnCloud
Message depose par Codex a la demande de Dom.
## Instruction
Ne rien bouger sur OwnCloud pour le moment.
Dom va tester l'application sous Windows avec le pack local deja genere. Tant que
Dom n'a pas donne son GO explicite :
- pas d'upload OwnCloud ;
- pas de diffusion beta ;
- pas de publication externe ;
- pas de nouveau package installateur final.
## Inno Setup
Il faudra telecharger/installer Inno Setup sur la machine Windows, mais le
repackaging avec installateur se fera **apres** les tests Windows de Dom et apres
GO explicite.
Apres ce GO :
1. installer Inno Setup via `scripts\\install_inno_setup_build_dep.ps1` ;
2. rebuilder/repackager avec l'installateur ;
3. recalculer les SHA-256 ;
4. deposer un rapport de package mis a jour pour Dom.
## Etat attendu maintenant
Pack actuel conserve localement sur `192.168.1.11`.
— Dom via Codex

View File

@@ -0,0 +1,97 @@
---
from: dom
to: claude
date: 2026-06-05T17:55:00+02:00
topic: v11-5-chantiers-paralleles
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d17-v11-5-chantiers-paralleles.md
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
---
# Préparer v11.5 en parallèle après bêta
Message déposé par Codex à la demande de Dom.
## Cap Dom
Après les tests Windows et le GO bêta, la v11.5 doit être préparée en parallèle
avec Claude + agents sur trois chantiers :
1. **GUI v6**
2. **D-13 complet**
3. **Plateforme licence**
## Important — gel bêta
Ne pas perturber le pack bêta v11 actuel.
Tant que Dom n'a pas terminé ses tests Windows :
- pas de modification du code packagé bêta ;
- pas de refonte sur la branche de livraison ;
- pas de mélange entre hotfix MVP et v11.5 ;
- plans, inventaires et découpage seulement.
## Proposition de répartition agents
### Agent A — GUI v6
Objectif : reprendre la transposition GUI v6 sans casser le moteur.
À produire :
- inventaire de l'existant (`Pseudonymisation_Gui_V5.py`, mockup v6, WIP sauvegardé Windows) ;
- architecture cible GUI v6 ;
- liste des écrans / workflows ;
- contrat minimal avec le moteur ;
- stratégie de migration progressive.
### Agent B — D-13 complet
Objectif : finir la protection des réglages avancés.
À produire :
- inventaire des réglages à protéger ;
- matrice admin/non-admin ;
- règles UI + règles sauvegarde config ;
- tests attendus ;
- impacts sur GUI v5/v6.
### Agent C — Licence plateforme
Objectif : préparer la plateforme licence validée D-14.
À produire :
- architecture serveur FastAPI/PostgreSQL/HTMX ;
- module client `license.py` ;
- format licence signé RSA-PSS ;
- flows activation / expiration / offline 30 jours / grace period ;
- plan de branches et livrables.
### Agent D — Intégration / merge
Objectif : éviter les collisions.
À produire :
- frontières de fichiers ;
- dépendances entre agents ;
- ordre de merge ;
- critères d'acceptation v11.5 ;
- risques principaux.
## Livrable demandé à Claude
Avant tout codage lourd, déposer :
`docs/coordination/inbox/for-dom/2026-06-05_claude_plan-v11-5-parallele.md`
Ce plan doit dire clairement :
- ce qui peut démarrer tout de suite en lecture/planification ;
- ce qui attend le GO bêta ;
- qui touche quels fichiers ;
- comment éviter de perdre le WIP Windows sauvegardé ;
- quels tests devront valider v11.5.
— Dom via Codex

View File

@@ -0,0 +1,53 @@
---
from: dom
to: claude
date: 2026-06-05T19:20:00+02:00
topic: app-aivanov-dev
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
- decision: docs/coordination/decisions/2026-06-02_dom_d14-plateforme-licence-architecture.md
---
# Mission Claude - developpement plateforme app.aivanov.fr
Dom valide le lancement parallele de la plateforme web `app.aivanov.fr`.
## Write scope
Travailler dans un projet separe :
`/home/dom/ai/app_aivanov`
Ne pas modifier le pack beta Windows dans `/home/dom/ai/anonymisation`.
## Mission
Developper le MVP portail :
- FastAPI ;
- PostgreSQL cible, SQLite local autorise ;
- SQLAlchemy/Alembic ;
- Jinja2 + HTMX ;
- auth email/password ;
- pages client "Mes licences" ;
- activation poste ;
- telechargement EXE/Setup/SHA256 ;
- back-office Dom ;
- API `/api/v1/activate`, `/api/v1/check`, `/api/v1/version`, `/api/v1/download/{version}` ;
- signature RSA-PSS cote serveur.
## Garde-fous
- OwnCloud est hors cible produit.
- Aucun deploiement public sans GO Dom.
- Aucune cle privee commitee.
- Aucun secret Brevo/SSH/API dans le repo.
- Pas de modification de l'EXE beta ni de la branche beta.
## Coordination
Qwen prend les tests, la securite, le contrat API licence et la validation RGPD.
Eviter les conflits : Claude code la plateforme, Qwen code les tests et signale les corrections.

View File

@@ -0,0 +1,36 @@
---
from: dom
to: claude
date: 2026-06-05T19:30:00+02:00
topic: perf-mvp-p1
status: open
priority: blocker
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
---
# Performance MVP - P1 bloquant
Retour test Windows Dom : l'anonymisation est beaucoup trop lente, avec CPU autour
de 12 % et RAM autour de 16 Go.
## Mission
Programmer un chantier performance MVP en parallele, sans perturber `app_aivanov`
ni la beta tant qu'il n'y a pas de patch valide.
Priorites :
1. diagnostiquer le plafonnement mono-coeur en EXE PyInstaller/frozen ;
2. verifier la rasterisation PDF sequentielle ;
3. mesurer le cout OCR docTR 300 dpi ;
4. ajouter/proposer des timings par etape ;
5. proposer un hotfix MVP faible risque si possible.
## Garde-fous
- Le moteur RGPD reste fail-closed.
- Le leak score 100/100 reste obligatoire.
- Pas de refonte v11.5 melangee avec le hotfix perf.
- Pas de diffusion externe tant que Dom n'a pas valide.

View File

@@ -0,0 +1,58 @@
---
from: dom-via-codex
to: claude
date: 2026-06-05T20:55:00+02:00
topic: handoff-fin-journee
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- report: docs/coordination/inbox/for-dom/2026-06-05_claude_diagnostic-perf-mvp.md
---
# Handoff fin de journee pour Claude
Dom arrete pour aujourd'hui. Ne pas lancer de nouveau chantier sans reprise explicite.
## Decisions a conserver
- OwnCloud est hors cible produit.
- La distribution/licence cible passe par `app.aivanov.fr`.
- Pas de diffusion publique, pas de package Inno Setup final, pas de build client sans
GO Dom apres tests Windows.
- Performance MVP est P1 bloquant.
## app.aivanov.fr
Le MVP local est stabilise dans `/home/dom/ai/app_aivanov`.
Attention : ne plus ecraser l'architecture existante. Les modeles/routes alignes sont
`Licence`, `ArtifactVersion`, `Seat`, auth session, API activate/check/version/download.
Reprise possible demain seulement sur demande Dom/Codex :
- completer une fonctionnalite manquante ciblee ;
- ajouter une migration propre ;
- corriger un test rouge ;
- preparer deploiement, mais sans secret et sans publication.
## Performance
Ton diagnostic H1/H2/H3/H4 a ete lu.
Etat actuel cote Codex :
- H2 applique : rasterisation frozen avec `ThreadPoolExecutor` + fallback ;
- H4 applique : timings `PERF` par etape ;
- H1 torch threads reste a implementer/valider ;
- H3 batch OCR reste a etudier apres mesure reelle.
Reprise conseillee :
1. H1 torch threads faible risque, puis tests.
2. Rebuild EXE Windows.
3. Analyse du log `PERF` sur le PDF reel Dom.
4. H3 seulement si le log prouve que l'OCR scanne domine.
Ne pas baisser le DPI OCR par defaut sans validation leak score.

View File

@@ -0,0 +1,65 @@
---
from: codex
to: dom
date: 2026-06-05T20:05:00+02:00
topic: app-aivanov-et-perf-status
status: delivered
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- report: docs/coordination/inbox/for-dom/2026-06-05_qwen_perf-mvp-p1-diagnostic.md
---
# Statut Codex - app.aivanov.fr + performance MVP
## app.aivanov.fr
Projet cree dans `/home/dom/ai/app_aivanov`.
MVP local livre :
- FastAPI ;
- SQLite local par defaut, PostgreSQL prevu par `DATABASE_URL` ;
- Jinja2 + HTMX ;
- login session ;
- page client licences ;
- back-office Dom ;
- API `/api/v1/activate`, `/api/v1/check`, `/api/v1/version`, `/api/v1/download/{version}` ;
- signature RSA-PSS cote serveur ;
- tests Qwen ajoutes.
Verification :
- `python3 -m pytest -q` dans `/home/dom/ai/app_aivanov` : 10 passed.
- serveur local lance : `http://127.0.0.1:8088`.
## Performance MVP
Diagnostic Qwen confirme le goulot :
- EXE PyInstaller/frozen faisait la rasterisation PDF en sequentiel ;
- GUI force la sortie raster securisee ;
- docTR OCR 300 dpi peut expliquer la RAM elevee.
Patch Codex applique :
- `ThreadPoolExecutor` en mode `sys.frozen` pour la rasterisation PDF multi-pages ;
- fallback sequentiel si le mode thread echoue ;
- env `ANON_DISABLE_RASTER_THREADS=1` pour revenir au comportement precedent ;
- logs `PERF` par etape dans `process_pdf` ;
- logs mode raster (`sequential`, `threads`, `processes`) + duree totale.
Verification :
- `python3 -m py_compile anonymizer_core_refactored_onnx.py` OK ;
- `.venv/bin/python -m pytest tests/unit -q` : 98 passed ;
- test synthetic raster `sys.frozen=True` sur PDF 4 pages : OK.
## Reste avant diffusion
- Rebuilder l'EXE Windows avec ce patch perf.
- Tester sur le PDF reel Dom.
- Comparer CPU/RAM/temps avant-apres.
- Ne pas publier tant que Dom n'a pas valide le resultat.

View File

@@ -0,0 +1,92 @@
---
from: codex
to: dom
date: 2026-06-05T20:55:00+02:00
topic: handoff-fin-journee
status: delivered
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- report: docs/coordination/inbox/for-dom/2026-06-05_qwen_perf-mvp-p1-diagnostic.md
- report: docs/coordination/inbox/for-dom/2026-06-05_claude_diagnostic-perf-mvp.md
---
# Handoff fin de journee - Codex
## Messages lus
J'ai relu les derniers messages Qwen et Claude.
- Qwen confirme que le symptome Windows CPU ~12 % colle avec une rasterisation PDF
mono-coeur en EXE PyInstaller/frozen.
- Claude ajoute une cause probable importante : `torch` non configure peut tomber a
1 thread en EXE frozen, ce qui impacte OCR docTR et NER.
- Les deux convergent : performance MVP = P1 bloquant avant diffusion.
## Etat app.aivanov.fr
Projet local cree dans `/home/dom/ai/app_aivanov`.
Etat livre :
- FastAPI ;
- SQLite local par defaut, PostgreSQL via `DATABASE_URL` prevu ;
- Jinja2 + HTMX ;
- auth session email/password ;
- page client "Mes licences" ;
- back-office Dom ;
- API activation/check/version/download ;
- signature RSA-PSS cote serveur ;
- tests Qwen ajoutes.
Verification :
- `python3 -m pytest -q` dans `/home/dom/ai/app_aivanov` : 10 passed.
- Serveur local coupe pour la nuit.
- Redemarrage demain si besoin :
`cd /home/dom/ai/app_aivanov && python3 -m uvicorn app.main:app --host 127.0.0.1 --port 8088`
- Compte dev local : `dom@aivanov.fr` / `change-me`.
Rappel decision produit : OwnCloud reste hors cible. La cible distribution/licence est
une interface web `app.aivanov.fr`.
## Etat performance MVP
Patch deja applique dans `anonymizer_core_refactored_onnx.py` :
- H2 partiel : en EXE frozen, rasterisation PDF multi-pages via `ThreadPoolExecutor`
au lieu du sequentiel force ;
- fallback sequentiel si erreur ;
- rollback possible avec `ANON_DISABLE_RASTER_THREADS=1` ;
- H4 : logs `PERF` par etape dans `process_pdf` ;
- logs du mode raster effectif et de la duree.
Verification locale :
- `python3 -m py_compile anonymizer_core_refactored_onnx.py` : OK ;
- `.venv/bin/python -m pytest tests/unit -q` : 98 passed ;
- test synthetic `sys.frozen=True` sur PDF 4 pages : OK.
Point restant important lu chez Claude : H1 n'est pas encore applique. Demain, il faut
ajouter/configurer proprement `torch.set_num_threads(...)` et les variables
`OMP_NUM_THREADS` / `MKL_NUM_THREADS` si cela ne casse pas l'environnement Windows.
## Reprise conseillee demain
1. Demander/confirmer si le PDF lent de test Windows etait scanne ou natif texte, et
noter nombre de pages + taille.
2. Ajouter H1 torch threads, puis revalider tests.
3. Rebuilder un EXE Windows de test avec H1 + H2 + H4.
4. Retester le PDF reel sous Windows et relever temps total, CPU, RAM, lignes `PERF`
de `anonymisation.log`.
5. Si le gain est net et que leak/tests restent bons, preparer le package suivant.
6. Ne pas refaire le package Inno Setup ni diffuser sans GO Dom apres test Windows.
## Etat operationnel
- Serveur `app.aivanov.fr` local coupe.
- Boucle de lecture inbox toutes les 3 minutes coupee.
- Agents fermes/demandes au repos apres handoff.
- Working tree non commitee : modifications perf + docs coordination + projet
`/home/dom/ai/app_aivanov`.

View File

@@ -0,0 +1,53 @@
---
from: qwen
to: claude
date: 2026-06-05T11:30:00+02:00
topic: ack-tn-to-livrees
status: open
priority: high
references:
- file: inbox/for-qwen/2026-06-05_dom-via-codex_relance-validation-beta.md
- file: inbox/for-qwen/2026-06-04_17-05_claude_nouveaux-jobs-tn-to.md
- file: inbox/for-dom/2026-06-05_qwen_validation-pack-beta.md
- file: inbox/for-dom/2026-06-05_qwen_plan-modele-onnx.md
---
# Ack — T-O et T-N livrées
## T-O ✅ — Validation GO/NO-GO pack beta (lecture seule)
**Livrable** : `inbox/for-dom/2026-06-05_qwen_validation-pack-beta.md`
**VERDICT GLOBAL : GO** — conforme sur les 6 axes vérifiés empiriquement.
| Section | Statut | Détail |
|---|---|---|
| Mode admin | GO | `.admin` absent, `ANON_ADMIN` non forcé dans scripts/launcher, bannière conditionnelle |
| VLM/Ollama | GO | Masqué en non-admin, pas de fallback |
| Quarantaine | GO | `0o700` sur dir, `0o600` sur fichiers, fail closed |
| PII résiduelles | GO | Aucune sortie pseudonymisée dans le working tree, `.gitignore` couvre `pdf_natif/` |
| Build | GO | `.spec` cohérent, scripts PS1/BAT alignés |
| Secrets/chemins | GO (réservation mineure) | `server.py:14` docstring avec `/home/dom/...` (fichier non packagé EXE) |
2 remarques mineures :
- `*.pseudonymise.txt` et `*.audit.jsonl` non explicitement dans `.gitignore` (couverts indirectement)
- `server.py` docstring avec chemin local (non inclus dans EXE)
## T-N ✅ — Plan backup modèle ONNX (docs only)
**Livrable** : `inbox/for-dom/2026-06-05_qwen_plan-modele-onnx.md`
Comparaison de 4 options dans le contexte réel (Gitea local `localhost:3100`, pas de cloud, établissement de santé) :
| Option | Faisabilité | Effort | Reproducibilité | Recommandation |
|---|---|---|---|---|
| Git LFS | ✅ Si `LFS_START_SERVER` activé | ~30 min | Excellente | **Preferred** |
| Script download | ⚠️ Source à créer | 2-4h | Moyenne | Complément |
| Release asset Gitea | ✅ | ~1h | Bonne | Alternative LFS |
| Statu quo documenté | ✅ | Immédiat | Faible | Court terme |
**Recommandation finale** : approche en 2 temps — (1) documenter statu quo cette semaine avec SHA-256 et procédure manuelle, (2) activer Git LFS sur Gitea dans les prochaines semaines.
---
— Qwen

View File

@@ -0,0 +1,63 @@
---
from: qwen
to: claude
date: 2026-06-05T18:30:00+02:00
topic: ack-tp-tq-tr-livrees
status: open
priority: high
references:
- file: inbox/for-qwen/2026-06-05_18-05_dom-via-codex_v11-5-revue-transverse.md
- file: inbox/for-dom/2026-06-05_qwen_revue-decoupage-v11-5.md
- file: inbox/for-dom/2026-06-05_qwen_matrice-acceptation-v11-5.md
- file: inbox/for-dom/2026-06-05_qwen_risques-v11-5.md
---
# Ack — T-P, T-Q, T-R livrées (revue transverse v11.5)
## T-P ✅ — Revue de découpage v11.5
**Livrable** : `inbox/for-dom/2026-06-05_qwen_revue-decoupage-v11-5.md`
**Points saillants** :
1. **Frontières** : Les 3 chantiers ont des surfaces bien distinctes, confirmant que le parallélisme est réaliste. Zone de contact principale : `launcher.py` (convergence GUI v6 + check licence).
2. **Conflits réels identifiés** :
- `launcher.py` : GUI v6 et Licence y touchent tous les deux — résoluble par merge séquentiel C puis A
- `Pseudonymisation_Gui_V5.py` : pas de conflit réel si D-13 complet cible directement la GUI v6 (pas V5)
3. **Dépendance bloquante majeure** : Le WIP Windows (`b8c9c41`, +1250 lignes customtkinter) n'existe que sur le disque de `192.168.1.11` — risque de perte totale si le disque tombe.
4. **5 questions pour Dom** : sauvegarde WIP, scope licence (Phase 1.1 vs 1.2), nature GUI v6 (refonte vs évolution), licence beta, compatibilité customtkinter/PyInstaller.
5. **Ordre de merge recommandé** : C (licence) → A (GUI v6) → B (D-13), avec développement parallèle sur branches séparées et contrats préalables.
## T-Q ✅ — Matrice d'acceptation v11.5
**Livrable** : `inbox/for-dom/2026-06-05_qwen_matrice-acceptation-v11-5.md`
- **GUI v6** : 10 critères GO/NO-GO (lancement, non-régression moteur, interface 3 onglets, thèmes, éditeur masques, single-instance, splash, gestion erreurs, drag-and-drop)
- **D-13 complet** : 12 critères (visibilité/masquage réglages sensibles en mode admin vs non-admin, vérification `admin_required()`, fichier `.admin`, variable d'env, priorité env/fichier)
- **Licence client** : 11 critères (signature RSA-PSS, licence falsifiée/expirée/grace period, offline 30 jours, machine_id lié au poste, 1 licence = 1 poste, clé publique embarquée seule, permissions fichier, confinement Phase 1.1)
- **Tests** : 3 tableaux — tests unitaires (admin_mode, license.py, quarantine, gui_v6), tests d'intégration (7 scénarios), smoke tests (12 scénarios T1-T12)
- **Beta utilisateur** : 7 scénarios réalistes (secrétaire, DSI batch, quarantaine, renouvellement licence, audit DPO, échange profils, licence expirée)
- **RGPD/sécurité/offline** : 12 critères (aucune PII, pas de clé privée client, permissions quarantaine/logs, pas de sortie réseau non autorisée, VLM inaccessible, offline fonctionnel, pas de PII dans licence/logs, audit trail, nettoyage temporaires)
## T-R ✅ — Registre de risques v11.5
**Livrable** : `inbox/for-dom/2026-06-05_qwen_risques-v11-5.md`
22 risques identifiés sur 5 catégories : techniques (5), RGPD/sécurité (5), UX (4), packaging/déploiement (5), planning (5).
**Top 5 risques** avec actions immédiates :
1. Fuites PII résiduelles non corrigées (D-15) — bloquant absolu
2. Vulnérabilité extraction clé RSA-PSS de l'EXE
3. Conflits de merge entre 3 chantiers parallèles
4. Phone-home licence perçu comme exfiltration par un DPO
5. SmartScreen sans Authenticode = blocage DSI
**5 questions pour Dom** : statut D-15, choix framework GUI v6, fingerprint machine pour licence, priorité des chantiers, budget Authenticode.
---
— Qwen

View File

@@ -0,0 +1,134 @@
---
from: qwen
to: dom
date: 2026-06-05
topic: matrice-acceptation-v11-5
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d17-v11-5-chantiers-paralleles.md
- decision: docs/coordination/decisions/2026-06-02_dom_mvp-pivots-strategiques.md
- decision: docs/coordination/decisions/2026-06-02_dom_d13-partial-scope.md
- decision: docs/coordination/decisions/2026-06-02_dom_d14-plateforme-licence-architecture.md
- decision: docs/coordination/decisions/2026-05-28_dom_no-ui-changes.md
- plan: docs/coordination/inbox/for-dom/2026-06-05_claude_plan-v11-5-parallele.md
---
# Matrice d'acceptation v11.5 -- 2026-06-05
## 1. Criteres GO/NO-GO -- GUI v6
| # | Critere | Verification | GO si |
|---|---|---|---|
| 1.1 | Lancement sans erreur | `python Pseudonymisation_Gui_V6.py` (dev) et EXE v11.5 | Fenetre s'ouvre, splash complet, aucun traceback dans `anonymisation.log` |
| 1.2 | Workflow batch nominal (v5 -> v6) | Selection dossier source + sortie, lancement | Meme resultat d'anonymisation que v5 (meme score qualite, meme entites masquees) sur le corpus audit_30 |
| 1.3 | Non-regression moteur | `pytest tests/` (98+ tests) + `evaluate_quality.py --compare` | 100% tests verts, score qualite >= baseline 99.8, leak score 100/100 |
| 1.4 | Interface repond aux 3 onglets de la maquette | Comparaison visuelle avec `docs/ui_mockup_v6.html` | Chaque onglet present, chaque sous-onglet accessible, chaque bouton fonctionnel |
| 1.5 | Themes (4 themes) | Cycle des themes via selecteur | 4 themes applicables sans recharger, aucun artefact visuel (texte illisible, contrastes casses) |
| 1.6 | Editeur de masques | Ajout/modification/suppression d'un masque | Masque sauvegarde dans le profil JSON, applique au prochain batch |
| 1.7 | Single-instance guard | Lancer 2 instances simultanement | La 2eme instance refuse le lancement, messagebox explicite |
| 1.8 | Splash/progression au lancement | Lancer EXE froid (cache modele clear) | Splash affiche chaque etape, pas d'ecran noir > 3s |
| 1.9 | Erreur import core = message clair | Renommer temporairement `anonymizer_core_refactored_onnx.py`, lancer | `messagebox.showerror` avec message lisible + ecriture dans `crash.log` |
| 1.10 | Drag-and-drop ou selection dossier | Glisser un dossier dans la zone / utiliser le file picker | Le dossier est detecte, la liste des fichiers apparait |
## 2. Criteres GO/NO-GO -- D-13 complet
| # | Critere | Verification | GO si |
|---|---|---|---|
| 2.1 | VLM Ollama cache en non-admin | Lancer sans `ANON_ADMIN`, verifier UI | Aucune reference a Ollama/VLM visible dans l'interface |
| 2.2 | VLM Ollama visible en admin | Lancer avec `ANON_ADMIN=1` | Section VLM visible, parametrable |
| 2.3 | Stopwords personnalisables bloques non-admin | Lancer sans admin | Input de stopwords masque ou desactive |
| 2.4 | Stopwords debloques en admin | Lancer avec admin | Input editable, sauvegarde dans profil |
| 2.5 | Profils techniques (regex_overrides, force_terms) bloques non-admin | Lancer sans admin | Section "Profils techniques" absente ou grissee |
| 2.6 | Choix moteur NER bloque non-admin | Lancer sans admin | Pas de selecteur moteur visible (moteur par defaut seul actif) |
| 2.7 | Titre fenetre signal admin | Lancer avec admin | Le titre de fenetre contient `[MODE ADMIN]` |
| 2.8 | `admin_required()` protege l'API | Appeler une fonction protegee via `admin_mode.admin_required()` en non-admin | `RuntimeError` levee avec message clair |
| 2.9 | Sauvegarde config sensible bloquee non-admin | Tenter d'exporter un profil contenant des regex_overrides sans admin | Action refusee, message explicite |
| 2.10 | Fichier `.admin` active le mode | Creer un fichier `.admin` vide a la racine, relancer | `is_admin()` retourne True, UI bascule en mode admin |
| 2.11 | Variable `ANON_ADMIN` active le mode | `ANON_ADMIN=1 python ...` | `is_admin()` retourne True, UI bascule en mode admin |
| 2.12 | Priorite env > fichier | `ANON_ADMIN=0` + fichier `.admin` present | `is_admin()` retourne False (env prioritaire, ou inversement selon la decision de Dom) |
## 3. Criteres GO/NO-GO -- Licence client
| # | Critere | Verification | GO si |
|---|---|---|---|
| 3.1 | Licence valide = lancement normal | `license.dat` avec signature RSA-PSS valide | L'application se lance normalement, aucun message d'alerte |
| 3.2 | Licence falsifiee = blocage | Modifier un octet du `license.dat` | L'application refuse de demarrer, message "Licence invalide" |
| 3.3 | Licence expiree = mode degrade | `license.dat` avec `expires_at` dans le passe + grace period (15j) non ecoulee | L'application se lance avec banniere "Licence expiree -- renouvellement necessaire", anonymisation fonctionnelle |
| 3.4 | Grace period ecoulee = blocage | `expires_at` + 16 jours | L'application refuse de demarrer, message "Licence expiree -- contactez votre administrateur" |
| 3.5 | Offline < 30 jours = OK | Couper reseau, lancer avec licence valide < 30j depuis dernier phone home | Lancement normal |
| 3.6 | Offline > 30 jours = demande phone home | Simuler un cache > 30j sans reseau | Message demandant de reconnecter, ou blocage selon decision |
| 3.7 | machine_id lie au poste | Copier `license.dat` sur une autre machine (autre machine_id) | Blocage avec message "Licence invalide sur ce poste" |
| 3.8 | 1 licence = 1 poste | Activer 2 machines avec le meme `client_id` mais `machine_id` differents | La 2eme activation refusee ou la 1ere revoquee (selon politique) |
| 3.9 | Cle publique embarquee, cle privee serveur | Verifier le code client | Aucune cle privee RSA dans le code source ni l'EXE (decompile rapide) |
| 3.10 | `license.dat` stocke localement | Verifier l'emplacement du fichier | Fichier present, permissions restrictives (0o600 ou equivalent Windows) |
| 3.11 | Phase 1.1 seulement (client) | Aucun endpoint serveur dans le code v11.5 | Pas de code serveur FastAPI dans le repo client (reporte Phase 1.2) |
## 4. Tests necessaires
### Tests unitaires
| Module | Tests a creer/modifier | Couverture cible |
|---|---|---|
| `admin_mode.py` | `test_is_admin_env`, `test_is_admin_file`, `test_is_admin_cached`, `test_is_admin_force_refresh`, `test_admin_required_raises`, `test_admin_required_ok`, `test_priority_env_over_file` | >= 90% lignes |
| `license.py` (nouveau) | `test_verify_valid_signature`, `test_verify_forged_signature`, `test_expired_in_grace`, `test_expired_past_grace`, `test_wrong_machine_id`, `test_offline_within_30d`, `test_offline_past_30d`, `test_license_file_permissions` | >= 90% lignes |
| `quarantine.py` | Tests existants conserves ; ajouter `test_secure_quarantine_dir_perms`, `test_finalize_with_total` | >= 85% lignes |
| `gui_v6/` (nouveau package) | `test_batch_paths_resolve`, `test_profile_load_valid`, `test_profile_load_missing`, `test_mask_editor_roundtrip` | >= 70% lignes |
### Tests d'integration
| Scenario | Setup | Verification |
|---|---|---|
| Batch complet v6 = meme resultat que v5 | Corpus audit_30, profil `standard_local`, meme seed | Comparer chaque fichier de sortie v5 vs v6 : meme nombre d'entites masquees par type, meme score qualite (+/- 0.1) |
| D-13 : non-admin ne voit rien de sensible | Lancer GUI v6 sans ANON_ADMIN, sans fichier .admin | `grep -ri "ollama\|vlm\|gliner\|camembert\|regex_override\|force_terms" <capture_ui>` = 0 resultat |
| D-13 : admin voit tout | Lancer GUI v6 avec ANON_ADMIN=1 | Chaque section protegee est visible et editable |
| Licence : blocage avant GUI | `license.dat` falsifie, lancer EXE | GUI ne s'ouvre jamais, seul le splash ou message d'erreur apparait |
| Licence : grace period | `license.dat` expire il y a 10 jours | GUI s'ouvre avec banniere visible, batch fonctionnel |
| Quarantaine + GUI v6 | Dossier avec 1 PDF corrompu + 1 doc texte court (< 100 chars) + 1 doc avec PII residuelle | Quarantaine/INDEX.md genere avec 1 full + 1 partial, errors.log contient 2 entries JSON |
| Build EXE reproductible | PyInstaller `anonymisation_onefile.spec` sur machine Windows propre | EXE genere, taille dans la plage attendue (700-750 MB), `--version` affiche v11.5 |
### Smoke tests
| Scenario | Procedure | Resultat attendu |
|---|---|---|
| T1 : Premier lancement (no models) | Lancer EXE sans modeles locaux | SetupWindow s'ouvre, telechargement EDS-Pseudo + GLiNER + verification ONNX, puis GUI auto |
| T2 : Premier lancement (models presents) | Lancer EXE avec modeles deja telecharges | Splash progresse en 5 etapes, GUI s'ouvre directement |
| T3 : Anonymisation 1 document TXT | Glisser un .txt avec PII connue (nom, telephone, ville) | Sortie .txt anonymisee, score qualite >= 95, aucune PII residuelle detectee |
| T4 : Anonymisation 1 document PDF | Glisser un .PDF avec texte | Sortie PDF redige + .txt anonymise, aucune PII visible dans le PDF redige |
| T5 : Anonymisation batch 10 documents | Dossier avec 10 .txt variés | 10 fichiers anonymises, errors.log (si erreurs), score moyen >= 98 |
| T6 : Profil export/import | Exporter un profil JSON, le reimporter sur une autre instance | Profil identique, meme regles appliquees, meme resultat |
| T7 : Mode admin ON/OFF | Lancer en admin, verifier sections ; relancer sans admin | Sections visibles en admin, absentes sans |
| T8 : Quarantaine auto | Dossier avec 1 PDF chiffre + 1 .txt vide | PDF chiffre en quarantaine full, .txt vide en quarantaine full, INDEX.md present |
| T9 : Licence valide | `license.dat` valide place dans dossier app | Lancement normal, aucune banniere |
| T10 : Licence expiree (grace) | `license.dat` expire il y a 7 jours | Lancement avec banniere "Licence expiree" |
| T11 : Single instance | Lancer 2 instances | 2eme refuse avec messagebox |
| T12 : Offline 30 jours | Couper reseau, lancer avec licence cachee < 30j | Lancement normal |
## 5. Scenarios beta utilisateur
| Scenario | Utilisateur | Validation |
|---|---|---|
| S1 : Secretaire medicale anonymise 5 comptes-rendus avant publication | Secretaire, poste Windows, pas admin, licence valide | 5 CR anonymises en < 2 min, aucune PII residuelle visible, dossier de sortie propre |
| S2 : DSI hospitalier batch mensuel 200 documents | DSI, poste Windows, profil etabli, licence valide | 200 docs traites, score qualite moyen >= 98, INDEX.md quarantaine si anomalies, errors.log exploitable |
| S3 : Operateur rencontre un document en quarantaine | Operateur metier, pas de connaissances techniques | Document place dans quarantaine/, fichier .reason.txt lisible avec raison claire et action recommandee |
| S4 : Renouvellement licence | DSI recu email de renouvellement, telecharge nouveau `license.dat` | Ancien `license.dat` remplace, application relancee, banniere disparait |
| S5 : Audit DPO demande la trace d'une campagne | DPO demande "avec quelle version ces documents ont ete anonymises ?" | Audit JSONL present dans les sorties avec version_code, version_regles, horodatage, profil_applique |
| S6 : Echange de profil entre etablissements | Etablissement A exporte un profil, envoie par email a B | B importe le profil, l'applique, resultat conforme aux regles de A |
| S7 : Licence expiree en plein travail | Operateur ouvre l'app, decouvre que la licence est en grace | Banniere visible mais anonymisation fonctionnelle, operateur peut alerter DSI |
## 6. Criteres RGPD / securite / offline
| # | Critere | Type | Verification |
|---|---|---|---|
| 6.1 | Aucune PII reelle dans code/docs/maquettes | RGPD | `grep -ri "CHCB\|Bayonne\|Saint-Denis\|GRAND\|SIMONET\|OYARCABA\|EJNAINI" code/ docs/` = 0 resultat (sauf tests unitaires avec donnees synthetiques) |
| 6.2 | Pas de cle privee RSA dans le client | Securite | `grep -ri "BEGIN RSA PRIVATE KEY\|PRIVATE_KEY" *.py license.py` = 0 resultat dans le code client |
| 6.3 | Quarantaine dir permissions 0o700 | RGPD/Secu | `os.stat(quarantine_dir).st_mode` & 0o777 == 0o700 |
| 6.4 | errors.log permissions 0o600 | RGPD/Secu | `os.stat(errors_log).st_mode` & 0o777 == 0o600 |
| 6.5 | Pas de sortie reseau non autorisee (non-admin) | RGPD | Lancer Wireshark/tcpdump pendant batch non-admin = 0 connexion sortante (sauf phone home licence si implemente) |
| 6.6 | VLM Ollama inaccessible en non-admin | RGPD | Code path : `VlmManager` instancie uniquement si `is_admin()` = True |
| 6.7 | Offline : anonymisation fonctionnelle sans reseau | Offline | Couper reseau, lancer batch = resultat identique a online (sauf phone home licence) |
| 6.8 | `license.dat` ne contient pas de PII | RGPD | Contenu du JSON licence = `{machine_id, client_id, expires_at, version, signature}` uniquement |
| 6.9 | Fichiers temporaires nettoyes | RGPD | Apres batch, `tempfile` directory vide ou ne contient aucun fichier avec contenu PII |
| 6.10 | Audit trail complet par document | RGPD | Chaque document anonymise a un audit JSONL associe avec timestamp, version, profil, entites masquees |
| 6.11 | Pas de PII dans les logs | RGPD | `anonymisation.log` ne contient aucun nom, telephone, email, adresse reelle |
| 6.12 | Mode degrade licence = pas de blocage brutal securite | Offline/UX | Licence expiree grace period = anonymisation fonctionnelle, seule la banniere change |

View File

@@ -0,0 +1,207 @@
---
from: qwen
to: dom
date: 2026-06-05T19:45:00+02:00
topic: perf-mvp-p1-diagnostic
status: delivered
priority: blocker
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- mission: docs/coordination/inbox/for-qwen/2026-06-05_19-30_dom-via-codex_perf-mvp-p1.md
---
# Performance MVP P1 - diagnostic Qwen
## Verdict court
Le retour Dom est coherent avec le code actuel : l'EXE Windows peut tomber en
traitement lourd mono-coeur pendant la rasterisation PDF. Sur une machine a 8
threads logiques, un seul coeur occupe donne environ 12,5 % CPU, ce qui colle
au symptome observe (~12 %).
La RAM a ~16 Go est plausible quand le document declenche docTR/OCR et la
rasterisation : modele OCR profond, images A4 300 dpi, tensors/numpy/PIL et
modele NER resident. Ce niveau est trop eleve pour un MVP exploitable.
Conclusion : performance bloquante avant diffusion. Hotfix MVP recommande
avant v11.5, sans toucher a `app_aivanov`.
## Points responsables identifies
1. Rasterisation page-level parallele desactivee en EXE PyInstaller.
- `anonymizer_core_refactored_onnx.py:22` importe `ProcessPoolExecutor`.
- `anonymizer_core_refactored_onnx.py:4277-4323` prepare une rasterisation
parallele, mais `:4316-4319` force le mode sequentiel si `sys.frozen`.
- Commentaire code : en frozen, `ProcessPoolExecutor` relance l'EXE et ouvre
des fenetres GUI fantomes. Le contournement actuel evite le bug UI mais
sacrifie le multi-coeur.
- Impact : chaque page raster est rendue l'une apres l'autre dans `_rasterize_page`
(`:4112-4175`), avec rendu PyMuPDF + PIL + JPEG/PNG.
2. La GUI force la sortie raster pour chaque document.
- `Pseudonymisation_Gui_V5.py:756-760` annonce "Sortie PDF Image (raster) -
securite maximale".
- `Pseudonymisation_Gui_V5.py:1712-1717` appelle le moteur avec
`make_vector_redaction=False` et `also_make_raster_burn=True`.
- `anonymizer_core_refactored_onnx.py:5020-5025` genere alors toujours
`*.redacted_raster.pdf`.
- Impact : meme un PDF texte natif paye le cout de rendu image de toutes les
pages. Ce choix est securise, mais trop couteux si la phase reste mono-coeur.
3. OCR docTR tres couteux sur pages pauvres en texte.
- `anonymizer_core_refactored_onnx.py:60-65` charge docTR si disponible.
- `:1096-1104` cree un modele `db_resnet50` + `crnn_vgg16_bn`.
- `:1250-1264` OCRise chaque page avec moins de 150 caracteres en image
`get_pixmap(dpi=300)`.
- Une page A4 300 dpi represente environ 25 Mo en RGB brut, avant copies PIL,
numpy et tensors. Sur un PDF scanne multi-pages, toutes les pages deviennent
candidates OCR.
- Impact : forte RAM, temps long, et CPU potentiellement mal exploite selon
PyTorch/docTR.
4. Les timings actuels ne suffisent pas pour isoler le goulet chez Dom.
- Le log Windows est bien a cote de l'EXE (`launcher.py:291-306`).
- Le moteur loggue certains evenements comme `OCR docTR : x/y pages remplacees`
(`anonymizer_core_refactored_onnx.py:1280-1282`), mais pas les durees par
etape ni le RSS peak.
- Sans instrumentation, on ne sait pas si le PDF de Dom bloque surtout sur
extraction, OCR, NER, raster ou ecriture finale.
5. VLM/Ollama n'est pas la cause probable en beta non-admin.
- `Pseudonymisation_Gui_V5.py:80-90` neutralise `VlmManager` hors mode admin.
- `:765-781` n'affiche la checkbox VLM que si le manager existe.
- Donc la lenteur signalee ne doit pas etre attribuee au VLM sauf lancement
admin explicite.
## Correctifs recommandes par ordre de risque
### Hotfix MVP faible risque
1. Ajouter des timings par etape dans `process_pdf`.
- Logguer : `n_pages`, taille PDF, `sys.frozen`, extraction, OCR, regles,
NER, ecriture texte/audit, recherche des rectangles, rasterisation, save PDF.
- Logguer : `ocr_used`, `ocr_pages_replaced`, `sparse_pages`, `dpi_ocr`,
`dpi_raster`, `jpeg_quality`, mode worker effectif.
- Logguer : RSS debut/fin/pic si `psutil` disponible, sinon ignorer.
- Risque RGPD : faible. Ne change pas la sortie.
2. Reparalleliser la rasterisation en EXE sans relancer la GUI.
Option a tester en premier : utiliser `ThreadPoolExecutor` uniquement en
`sys.frozen` pour `_rasterize_page`, avec un fallback sequentiel si exception.
Chaque worker ouvre son propre `fitz.open(pdf_path)`, comme aujourd'hui dans
le worker process. Le but est d'utiliser les appels C PyMuPDF/Pillow qui
peuvent liberer le GIL, sans creer de processus qui relancent Tk.
- Cible : `anonymizer_core_refactored_onnx.py:4316-4323`.
- Garde-fou : variable/env ou constante interne pour revenir au sequentiel.
- Risque : moyen-faible, car detection PII inchangee ; risque principal =
thread-safety PyMuPDF/Pillow a valider sur Windows.
Option plus robuste mais plus lourde : worker mode dedie dans l'EXE
(`--raster-worker`) ou petit exe worker separe, lance sans GUI. A garder pour
v11.5 si le thread pool est instable.
3. Garder le raster securise par defaut, mais ajouter un mode rapide explicite.
Pour MVP, ne pas desactiver silencieusement le raster. Si un mode rapide est
ajoute, il doit etre explicite dans la GUI et dans le log :
- "PDF image securise" par defaut : comportement actuel, sortie raster.
- "Texte anonymise seul / diagnostic rapide" : pas de PDF raster, uniquement
pour test interne ou cas client accepte.
- "PDF vectoriel rapide" seulement apres leak tests, car du texte residuel PDF
peut rester si la redaction rate.
Risque : produit/RGPD moyen. Ne pas activer sans decision Dom.
### Correctifs a reporter v11.5 sauf urgence
4. Baisser le DPI OCR de 300 a 200/240 de facon adaptative.
Ne pas le faire en aveugle : risque de rater des petits textes, tampons,
identifiants et scans faibles. A tester contre leak score et OCR low quality.
5. Ajouter un vrai worker Windows pour raster/OCR.
Chantier propre v11.5 : separer GUI et moteur lourd, avec workers sans Tk,
progression et annulation robuste. C'est la meilleure architecture, mais ce
n'est pas un patch rapide.
6. GUI v6 : exposer les profils performance.
La GUI v6 devra rendre visibles les compromis : securite raster, rapide texte,
OCR scanne, logs de performance, estimation temps/pages.
## Plan de benchmark minimal
Bench a faire sur l'EXE Windows, pas seulement Linux/dev.
Jeu minimal :
- PDF natif texte court : 1-2 pages.
- PDF natif texte moyen : 10-20 pages.
- PDF scanne court : 1-2 pages image.
- PDF scanne moyen : 10 pages image.
- PDF reel Dom qui a declenche le symptome.
Pour chaque run :
- redemarrer l'application pour mesurer le premier traitement, puis refaire un
deuxieme run pour separer warm cache/modeles ;
- noter pages, taille PDF, type natif/scanne ;
- relever duree totale murale, CPU moyen/pic, RAM pic "Private working set" ;
- recopier les lignes `anonymisation.log` : `frozen=`, OCR, durees par etape,
mode worker raster ;
- verifier les sorties : `*.pseudonymise.txt`, `*.audit.jsonl`,
`*.redacted_raster.pdf`, quarantaine si applicable.
Comparaisons attendues :
1. Beta actuelle EXE.
2. Beta + timings seulement.
3. Candidate hotfix raster thread pool.
4. Candidate mode rapide explicite, uniquement si Dom valide ce compromis.
## Criteres d'acceptation performance
Gate obligatoire :
- leak score inchange ou meilleur ;
- aucune PII patient dans logs ;
- pas de sortie moins securisee sans choix utilisateur explicite ;
- si worker parallel echoue, fallback sequentiel et log clair.
Gate performance MVP propose :
- sur PDF natif texte, reduction >= 2x du temps total si le goulet etait raster ;
- pendant la phase raster d'un PDF de plus de 4 pages, CPU processus > 40 %
sur une machine 8 threads, ou au moins 4 pages traitees en parallele logguees ;
- RAM pic native texte <= 6 Go apres chargement modeles ;
- RAM pic OCR scan <= 10 Go sur un PDF scanne de 10 pages A4 ;
- PDF reel Dom : temps divise par au moins 2, sinon P1 non resolu.
Gate UX :
- la GUI affiche/progresse par document et ne semble pas bloquee ;
- le log permet d'identifier l'etape lente sans relancer en mode debug ;
- l'utilisateur voit clairement si OCR/raster est en cours.
## Decision recommandee
Ne pas attendre v11.5 pour ce sujet. La performance doit devenir un hotfix MVP
dedie :
1. autoriser Claude a ajouter instrumentation timings + RSS ;
2. autoriser ensuite un patch raster parallel frozen, priorite thread pool avec
fallback sequentiel ;
3. demander a Qwen de valider sur benchmarks et leak tests ;
4. ne pas modifier `app_aivanov` dans cette mission ;
5. ne pas changer le mode raster par defaut sans GO Dom explicite.

View File

@@ -0,0 +1,155 @@
---
from: qwen
to: dom
date: 2026-06-05
topic: plan-modele-onnx
status: open
priority: normal
references:
- inbox/for-qwen/2026-06-04_17-05_claude_nouveaux-jobs-tn-to.md
---
# Plan de sauvegarde du modèle camembert-bio-deid ONNX
## Contexte
- `models/camembert-bio-deid/onnx/model.onnx` (440 Mo) est un modèle fine-tuné
maison, **non re-téléchargeable** depuis une source publique.
- Gitignoré via la règle `models/` (`.gitignore` ligne 32).
- Embarqué dans l'EXE au build (`.spec` datas l.23 :
`("models/camembert-bio-deid/onnx", "models/camembert-bio-deid/onnx")`).
- Le launcher (`launcher.py:302`) vérifie sa présence au démarrage mais
**ne le télécharge pas** — contrairement à EDS-Pseudo (`AP-HP/eds-pseudo-public`
via edsnlp) et GLiNER (`urchade/gliner_multi_pii-v1` via HuggingFace).
- La machine de build (192.168.1.11) possède le fichier en backup.
- Produit en local en établissement de santé, **sans cloud**.
- Dom a confirmé : **pas bloquant pour la beta**, mais risque de perte définitive
à long terme si la machine de build tombe.
## Comparaison des options
### 1. Git LFS
- **Faisabilité** : dépend du support LFS sur l'instance Gitea locale
(`localhost:3100`). Gitea supporte nativement LFS depuis la v1.18, mais il
faut vérifier que c'est activé sur l'instance (paramètre `LFS_START_SERVER`
dans `app.ini`). Si désactivé : activation côté admin Gitea requise, puis
`git lfs install` + `git lfs track "*.onnx"` + commit/push.
- **Effort** : faible si LFS déjà activé (~30 min : config + push initial du
fichier 440 Mo). Modéré si LFS à activer sur Gitea (accès admin, redémarrage
service).
- **Reproductibilité** : excellente. Le modèle devient versionné comme le reste
du code. Clone = code + modèle. Historique des versions possible.
- **Contraintes RGPD** : aucun problème — le modèle ONNX ne contient pas de PII
(c'est un modèle de NER entraîné, pas de données patient). Tracabilité
améliorée via git log.
- **Impact repo** : +440 Mo sur le repo Gitea. Le clone passera de ~50 Mo à
~490 Mo — acceptable sur réseau local. LFS évite de gonfler l'historique git
(un seul objet LFS, pas de delta).
- **Risque** : si Gitea LFS n'est pas activable ou si le stockage Gitea local
est contraint (ex. partition /var limitée). À vérifier avant de s'engager.
- **Recommandation** : **option preferred** si LFS est disponible. C'est la
solution la plus simple et la plus pérenne pour un repo auto-hébergé.
### 2. Script de téléchargement (`scripts/fetch_models.py`)
- **Faisabilité** : requiert une **source de téléchargement** existante ou à
créer. Options de provenance :
- Export HTTP interne (ex. `http://192.168.1.11/models/camembert-bio-deid.onnx`)
— simple mais nécessite un service HTTP permanent sur la machine de build.
- Gitea Release Asset — voir option 3.
- HuggingFace privé — mais contrarie le principe "pas de cloud".
- Partage réseau SMB (`\\192.168.1.11\models\model.onnx`) — fonctionne en
réseau local établissement.
- **Effort** : modéré. Script Python (~50 lignes) avec : URL/SMB source,
vérification SHA-256, fallback si offline, message clair si échec.
Intégration dans le workflow de build à documenter.
- **Reproductibilité** : bonne si la source est fiable. Mais introduit une
dépendance externe (machine 192.168.1.11 doit être accessible au moment du
build). Si la machine est hors ligne = build bloqué.
- **Contraintes RGPD** : le transfert se fait en interne (réseau local
établissement), pas de donnée PII dans le modèle. OK. Le SHA-256 garantit
l'intégrité du fichier reçu.
- **Risque** : dépendance à une machine externe au repo. Si cette machine
tombe ET qu'il n'y a pas de backup secondaire = même problème. Le script
seul ne résout pas la sauvegarde, il la suppose.
- **Recommandation** : utile **en complément** de l'option 1 ou 3, pas en
solution unique. Le script est une bonne pratique mais ne remplace pas un
backup versionné.
### 3. Release asset Gitea
- **Faisabilité** : Gitea supporte les assets de release nativement. Le modèle
serait déposé sur chaque release (`/api/v1/repos/{owner}/{repo}/releases`).
Le script de build PowerShell (`scripts/build_windows_oneclick.ps1`) pourrait
le récupérer via API Gitea avant le build PyInstaller.
- **Effort** : modéré à élevé. Nécessite :
- Dépôt initial du `.onnx` comme asset (manuel ou CI).
- Modification du script de build pour télécharger l'asset avant PyInstaller.
- Gestion du token d'API Gitea pour le download (ou release publique sur
Gitea local).
- Vérification SHA-256 post-téléchargement.
- **Reproductibilité** : bonne. Chaque release a son modèle associé. Le build
est reproductible tant que Gitea est accessible et que les assets ne sont pas
supprimés.
- **Contraintes RGPD** : OK — transfert interne, pas de PII. Traçabilité via
les releases Gitea (qui versionne le modèle avec le code).
- **Risque** :
- Les assets de release ne sont pas versionnés au sens git (pas de rollback
facile, pas de diff).
- Si Gitea tombe, plus de source de build.
- Complexité supplémentaire vs Git LFS pour un résultat similaire.
- **Recommandation** : viable mais **moins élégant que Git LFS** pour un repo
auto-hébergé. À considérer si LFS n'est pas activable sur Gitea.
### 4. Statu quo documenté
- **Faisabilité** : immédiate. Il suffit d'ajouter une section dans
`docs/build-windows-oneclick.md` expliquant où trouver le modèle et comment
le placer avant build.
- **Effort** : minimal (~10 min de rédaction).
- **Reproductibilité** : faible. Dépend entièrement de :
- La mémoire/opération manuelle du développeur.
- La disponibilité de la machine 192.168.1.11.
- L'absence de rotation/perte du fichier sur cette machine.
Aucune garantie que le modèle sera présent dans 6 mois ou après un départ.
- **Contraintes RGPD** : OK sur le plan données (pas de PII dans le modèle),
mais **faible sur la traçabilité** — pas de preuve de provenance, pas de hash
vérifié, pas d'audit trail.
- **Risque** : élevé sur le long terme. C'est l'option "on verra plus tard" —
le scénario classique de perte de modèle custom.
- **Recommandation** : acceptable **en attendant** une meilleure solution, mais
insuffisant comme stratégie long terme. À documenter en tout cas, même si on
choisit une autre option.
## Tableau comparatif
| Option | Faisabilité | Effort | Reproductibilité | RGPD | Recommandation |
|---|---|---|---|---|---|
| 1. Git LFS | Moyenne (dépend config Gitea) | Faible (~30 min) | Excellente | OK (pas de PII) | **Preferred** si LFS activable |
| 2. Script download | Bonne (source à créer) | Modéré (~2h) | Bonne (dépendance externe) | OK (SHA-256, interne) | Complément, pas solution unique |
| 3. Release asset Gitea | Bonne | Modéré-élevé (~3h) | Bonne | OK (traçabilité release) | Alternative si LFS indisponible |
| 4. Statu quo documenté | Immédiate | Minimal (~10 min) | Faible | OK mais faible traçabilité | OK temporaire, insuffisant long terme |
## Recommandation finale
**Approche en 2 temps :**
1. **Court terme (cette semaine)** : documenter le statu quo (option 4) dans
`docs/build-windows-oneclick.md` avec :
- Chemin exact du modèle : `models/camembert-bio-deid/onnx/model.onnx`
- Localisation du backup : `192.168.1.11`
- SHA-256 du fichier actuel (à calculer une fois) pour vérification d'intégrité
- Procédure manuelle de copie pré-build
2. **Moyen terme (prochaines semaines)** : activer Git LFS sur Gitea local
(option 1) et pousser le modèle. C'est la solution la plus propre pour un
repo auto-hébergé : versionné, traçable, reproductible, sans dépendance
externe. Vérifier au préalable :
- `LFS_START_SERVER = true` dans `app.ini` de Gitea
- Espace disque disponible sur la partition Gitea (440 Mo + marge)
- Que les clones sur réseau local restent acceptables en performance
**À écarter pour l'instant** : l'option 2 (script) seule car elle suppose une
source existante, et l'option 3 (release asset) car Git LFS couvre le même
besoin avec moins de complexité.

View File

@@ -0,0 +1,130 @@
---
from: qwen
to: dom
date: 2026-06-05
topic: revue-decoupage-v11-5
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d17-v11-5-chantiers-paralleles.md
---
# Revue de decoupage v11.5 -- 2026-06-05
## 1. Frontieres entre chantiers
### GUI v6
- **Surface** : remplacement de `Pseudonymisation_Gui_V5.py` (2894 lignes, tkinter pur) par une nouvelle GUI customtkinter. La GUI actuelle consomme le moteur via des imports directs (`anonymizer_core_refactored_onnx`, `ner_manager_onnx`, `eds_pseudo_manager`, `camembert_ner_manager`, `vlm_manager`).
- **Fichiers concernes** :
- `Pseudonymisation_Gui_V5.py` (2894 lignes) -- refonte complete
- `launcher.py` (698 lignes) -- splash + download modeles, a adapter pour v6
- `manual_masking.py` (56 lignes) -- embryon, a intégrer ou remplacer
- `pdf_mask_designer.py` (440 lignes) -- standalone, a raccrocher ou remplacer
- `format_converter.py` (256 lignes) -- non orchestre GUI
- Assets `assets/` (logo, icones)
- `gui_batch_paths.py` -- tests batch GUI
- WIP Windows existant : branche `backup/windows-wip-2026-06-05`, commit `b8c9c41`, +1250 lignes customtkinter (non mergee, base a partir de `0124457`, 52 commits avant HEAD)
- **Dependances externes** : customtkinter (nouvelle dep), sv_ttk (actuellement optionnel), PIL/Image (deja present pour VLM). Le moteur reste appele via les memes imports -- API interne stable si le contrat `core.anonymize(...)` ne change pas.
### D-13 complet
- **Surface** : extension du mecanisme `admin_mode.py` (66 lignes actuelles) pour proteger TOUS les reglages sensibles dans la GUI v6. Actuellement, seulement le VLM Ollama et le titre fenetre sont proteges. Les reglages reportes sont :
- Stopwords personnalisables (widget `_sw_listbox` dans V5, ~lignes 914+)
- Profils techniques : `regex_overrides`, `force_terms` (config YAML)
- Choix moteur NER (GLiNER, CamemBERT, EDS-Pseudo) -- via `_active_manager`
- Sauvegarde fichiers config sensibles (`config/dictionnaires.yml`, `config/profiles.yml`)
- Cases `profile_force_disable_vlm` dans les profils (lignes 1088-1097 V5)
- **Fichiers concernes** :
- `admin_mode.py` -- extension (nouvelles features a proteger, matrice admin/non-admin)
- `config_defaults.py` (201 lignes) -- lecture/ecriture config dictionnaires
- `profile_defaults.py` -- gestion profils runtime
- `config/dictionnaires.yml`, `config/dictionnaires.default.yml`
- `config/profiles.yml`, `config/profiles.default.yml`
- GUI v6 : sections "Parametres avances" et "Profils techniques" (co-concu avec chantier A)
- `config/admin_rules.yml`, `config/admin_rules.default.yml`
- **Dependances externes** : aucune nouvelle. S'appuie sur `admin_mode.py` existant et les fichiers config YAML. Depend de la GUI v6 pour l'application visuelle des protections.
### Plateforme licence
- **Surface** : deux composants distincts :
1. **Client** : nouveau `license.py` (n'existe pas encore), module Python embarque dans l'EXE. Verifie la licence au demarrage via signature RSA-PSS 2048 + SHA256. Stocke `license.dat` (chiffre DPAPI Windows). Phone home toutes les 30 jours max. Grace period 15 jours.
2. **Serveur** : nouveau dossier `platform/` (ou repo separe), FastAPI + PostgreSQL + HTMX/Jinja2, heberge sur `app.aivanov.fr` (infra OVH HDS). Auth fastapi-users, email via Brevo.
- **Fichiers concernes** :
- `license.py` (creation) -- module client
- CLE PUBLIQUE RSA embarquee (fichier ou constante) -- CLE PRIVEE cote serveur uniquement
- `launcher.py` -- point d'entree pour verifier la licence AVANT lancement GUI
- GUI v6 -- emplacement reserve pour afficher statut licence (banniere, expiration)
- `platform/` (creation) -- backend FastAPI, DB schema, templates HTMX
- `anonymisation_onefile.spec` -- eventuellement inclure `license.dat` dans le bundle
- **Dependances externes** : cryptography (nouvelle dep pour RSA-PSS verify), requests (deja present), FastAPI + uvicorn + psycopg2 + fastapi-users + Brevo SDK cote serveur.
## 2. Fichiers a risque de conflit
| Fichier | Chantiers concernes | Type de conflit | Mitigation |
|---|---|---|---|
| `launcher.py` (698 lignes) | **GUI v6** (adapt splash + lancement v6) + **Licence** (check licence avant GUI) | Les deux chantiers modifient le flux de demarrage : splash -> check licence -> launch GUI | Definir un contrat : `launcher.py` appelle `license.check()` avant `App(root)`. Chantier C fournit l'API, chantier A fournit le nouveau point d'entree GUI. Merge sequentiel (C d'abord, puis A). |
| `Pseudonymisation_Gui_V5.py` (2894 lignes) | **GUI v6** (refonte) + **D-13** (protections admin) | D-13 partiel protege des widgets V5 ; D-13 complet doit proteger les widgets V6 | **Pas de conflit reel** si D-13 complet est implemente directement dans la GUI v6 (customtkinter), pas dans V5. Le fichier V5 reste gele. D-13 cible uniquement la GUI v6. |
| `admin_mode.py` (66 lignes) | **D-13** seul | Aucun conflit attendu | Chantier B seul maitre de ce fichier. |
| `anonymisation_onefile.spec` | **GUI v6** (nouveau point d'entree) + **Licence** (cle publique + license.dat) | Les deux ajoutent des fichiers au bundle PyInstaller | Coordination mineure : chaque chantier declare ses fichiers additions. Merge sequentiel resout. |
| `config/profiles.yml` / `config/dictionnaires.yml` | **D-13** (protection ecriture) + **GUI v6** (UI profils) | D-13 restreint l'ecriture de ces fichiers en non-admin ; GUI v6 les lit/edite | Contrat ecrit : GUI v6 appelle `admin_required()` avant toute sauvegarde config sensible. Pas de collision code, juste un contrat API. |
## 3. Dependances cachees
| Dependances | Impact | Chantier affecte |
|---|---|---|
| **GUI v6 doit exister avant D-13 complet** | D-13 complet protege des reglages DANS la GUI. Sans GUI v6, D-13 ne peut pas implementer les protections visuelles (cases cachees/desactivees). Seul `admin_mode.py` peut etre etendu independamment. | **D-13** : peut preparer la matrice admin et etendre `admin_mode.py`, mais ne peut pas fermer le chantier sans GUI v6. |
| **Licence client doit exister avant GUI v6** | La GUI v6 doit afficher le statut licence (banniere "Licence expiree", etc.). Sans `license.py`, l'UI ne peut pas s'adapter. | **GUI v6** : peut reserver un placeholder UI, mais ne peut pas finaliser l'affichage licence sans l'API `license.py`. |
| **launcher.py est un point de convergence** | Il orchestre splash -> download modeles -> launch GUI. Le chantier Licence y ajoute un check pre-GUI, le chantier GUI v6 y change le point d'entree. | **Les 3 chantiers** indirectement. |
| **WIP Windows `b8c9c41` est base sur un vieux commit** | Le WIP GUI v6 (+1250 lignes customtkinter) part de `0124457`, qui est 52 commits avant HEAD. Il ne contient PAS les fixes leak (GRAND, EJNAINI), la quarantaine Q-1, ni `admin_mode.py`. | **GUI v6** : le WIP est une reference visuelle, pas une base a merger. La GUI v6 doit etre reecrite proprement a partir de HEAD. |
| **Customtkinter = nouvelle dependance** | L'installation de customtkinter doit etre valide dans `requirements.txt`, `.spec`, et le build Windows. | **GUI v6** + build system (hors perimetre des 3 chantiers mais bloquant si oublie). |
| **`anonymizer_core_refactored_onnx.py` API** | La GUI v6 suppose une API stable du core. Si le core change (signature de `anonymize()`, parametres), la GUI v6 casse. | **GUI v6** : doit contractualiser l'API core avant codage. |
## 4. Points a contractualiser avant codage
1. **Interface GUI v6 <-> moteur** : Quels sont les appels exacts que la GUI fait au core ? Signature de `core.anonymize()`, format des resultats, gestion des erreurs. Un fichier `gui_core_contract.md` listant les entrees/sorties attendues eviterait les incompatibilites. Le core actuel est importe via `anonymizer_core_refactored_onnx` (lignes 40-48 V5) et appele dans `_run_thread` (lignes 1566+).
2. **API `license.py` cote GUI** : Quel statut la GUI peut-elle lire ? (actif/expires/grace/absent). Fournira-t-on une classe `LicenseStatus` avec des proprietes simples ? La GUI n'a pas besoin de connaitre RSA-PSS, juste `{ok: bool, message: str, expires_at: str}`.
3. **Matrice D-13 admin/non-admin** : Liste exacte de chaque widget/parametre + son etat en admin vs non-admin (visible/cache, actif/desactif, lisible/inscriptible). Sans cette matrice, le chantier B ne peut pas coder et le chantier A ne peut pas concevoir les ecrans.
4. **Flux launcher.py** : Ordre exact des etapes au demarrage :
```
splash -> download modeles -> check licence -> launch GUI v6
```
Qui ecrit le nouveau `launcher.py` ? C (licence) ou A (GUI) ? Recommandation : C fournit `license.py` + un snippet d'integration, A l'integre dans le nouveau launcher v6.
5. **Sortie du WIP Windows** : Le WIP `backup/windows-wip-2026-06-05` (`b8c9c41`) doit etre pousse sur Gitea AVANT tout travail GUI v6. C'est le seul backup existant des +1250 lignes customtkinter. Sans ca, le chantier A repart de zero.
## 5. Ordre de merge recommande
1. **C (Licence -- client `license.py`)** -- Le plus isole. Creer un fichier neuf, tests unitaires de verification RSA-PSS, aucune collision avec le moteur ou la GUI. Mergeable sur `feature/v11-5` independamment.
2. **A (GUI v6)** -- Gros morceau, fichier neuf `Pseudonymisation_Gui_V6.py`. Peut etre developpe en parallele de C, mais merge APRES C pour integrer le check licence dans le launcher.
3. **B (D-13 complet)** -- Se greffe sur A (sections avancees de la GUI v6) et etend `admin_mode.py`. Merge APRES A car il depend des ecrans v6 pour appliquer les protections.
**Justification** : C est le plus decouple (fichier neuf + serveur separe). A est le plus gros et le plus risque (2894 lignes a remplacer). B depend visuellement de A. L'ordre C->A->B minimise les rebase et les conflits. Merge sequentiel sur `feature/v11-5` creee a partir de HEAD apres GO beta.
**Parallelisme reel** : A, B, C peuvent **developper en parallele** sur branches separees (`feature/v11-5-gui`, `feature/v11-5-d13`, `feature/v11-5-licence`). Le merge sequentiel intervient uniquement lors de l'integration sur `feature/v11-5`. Les contrats (sections 4) permettent ce parallelisme.
## 6. Alertes / desaccords pour Dom
| # | Sujet | Question pour Dom |
|---|---|---|
| 1 | **WIP Windows non sauvegarde** | Le WIP GUI v6 (+1250 lignes, commit `b8c9c41`) n'existe que sur le disque de `192.168.1.11`. Veux-tu que je le pousse sur Gitea maintenant (non destructif, branche separee) ou tu le fais toi-meme ? |
| 2 | **Plateforme licence -- Phase 1.1 vs 1.2** | D-14 prevoit ~12h pour le client `license.py` et ~50h pour le serveur. Veux-tu que le chantier v11.5 inclue SEULEMENT la Phase 1.1 (client) et reporte la Phase 1.2 (serveur FastAPI) ? Le client seul peut fonctionner avec une licence generatee manuellement en attendant le serveur. |
| 3 | **GUI v6 -- refonte ou evolution ?** | La GUI v6 sera-t-elle un fichier entierement nouveau (`Pseudonymisation_Gui_V6.py`) ou une evolution du V5 ? Un fichier nouveau est plus sur (pas de collision avec le gel beta), mais demande de re-brancher tous les widgets existants. |
| 4 | **Phase 0 beta Reunion -- pas de licence** | D-14 dit que le beta-testeur n'a pas de licence. Le `license.py` doit-il etre conditionnel (si pas de fichier licence, mode libre) ou faut-il generer une licence de dev pour le beta ? |
| 5 | **customtkinter sur Windows** | customtkinter est une nouvelle dependance. As-tu valide qu'il s'installe correctement sur la machine Windows de build (`192.168.1.11`) et qu'il est compatible PyInstaller --onefile ? |
## 7. Recommandation
**GO pour preparation parallele** avec les conditions suivantes :
1. **Sauvegarder le WIP Windows** (`b8c9c41`) sur Gitea AVANT tout autre travail. C'est le point de perte unique le plus critique.
2. **Rediger les 3 contrats** avant codage lourd :
- Contrat GUI <-> core (entrees/sorties de `anonymize()`)
- API `license.py` (statut lisible par la GUI)
- Matrice D-13 (chaque reglage + etat admin/non-admin)
3. **Branches separees** : `feature/v11-5-gui`, `feature/v11-5-d13`, `feature/v11-5-licence` -- developpement parallele, merge sequentiel C->A->B sur `feature/v11-5`.
4. **Gel beta respecte** : aucune branche v11.5 creee a partir de la branche de livraison tant que Dom n'a pas donne GO apres tests Windows.
5. **Phase 1.1 licence uniquement** : se concentrer sur `license.py` client (12h) et reporter le serveur FastAPI (50h) a une phase ulterieure. Le client peut etre teste avec des licences generees manuellement.
6. **Critere de merge** : les 73/73 tests unitaires doivent rester verts apres chaque merge. Le score qualite `evaluate_quality.py` >= baseline (99.8). Aucun nouveau leak PII.
Le parallelisme est **realiste** car les surfaces sont bien separees (D-17). Le principal risque est `launcher.py` (point de convergence) et le WIP Windows non sauvegarde. Ces deux points sont mitigables avec les actions ci-dessus.

View File

@@ -0,0 +1,81 @@
---
from: qwen
to: dom
date: 2026-06-05
topic: registre-risques-v11-5
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d17-v11-5-chantiers-paralleles.md
---
# Registre de risques v11.5 — 2026-06-05
## 1. Risques techniques
| # | Risque | Probabilité | Impact | Chantier concerné | Mitigation |
|---|---|---|---|---|---|
| 1.1 | **GUI v6 — rupture de compatibilité avec le moteur** : la nouvelle interface (CustomTkinter) pourrait appeler des fonctions du core ONNX dont la signature change entre-temps (ex. `anonymize_batch()`, `NerManager`). | Moyenne | Bloquant | GUI v6 | Contractualiser une interface stable (`IAnonymizer`) avec tests d'intégration dédiés. Ne pas toucher aux signatures publiques pendant la v11.5. |
| 1.2 | **D-13 complet — régression des protections actuelles** : en cachant les réglages avancés (stopwords, profils, choix NER) derrière un mur admin, on risque de casser le comportement existant du mode admin (`admin_mode.py` avec `ANON_ADMIN` / `.admin`). | Moyenne | Majeur | D-13 complet | Partir du module `admin_mode.py` existant, ne pas le remplacer. Ajouter uniquement les nouveaux `admin_required()` sur les widgets. Tests pytest obligatoires sur le comportement admin/non-admin. |
| 1.3 | **Plateforme licence — RSA-PSS embarqué + mise à jour** : la clé publique RSA embarquée dans l'EXE PyInstaller doit être protégée contre l'extraction. Si un attaquant la récupère, il peut forger des licences. | Haute | Majeur | Plateforme licence | Utiliser un attestation hardware (Windows Hello / TPM) pour le `machine_id` en plus de la signature RSA. Obfusquer la clé publique dans l'EXE (ex. `pyarmor` ou compilation Nuitka plutôt que PyInstaller). |
| 1.4 | **Gitea local — perte de contexte pour les agents** : le code source est sur Gitea interne (192.168.1.11), les agents Qwen/Claude n'y ont pas accès directement. Risque de travailler sur une version stale. | Moyenne | Majeur | Tous | Claude doit synchroniser le repo local vers les agents avant chaque chantier. Un `git pull` sur la machine de build est obligatoire avant tout merge. |
| 1.5 | **Fuites PII résiduelles non corrigées (D-15)** : les leaks `GRAND`, `SIMONET Marie lise`, `EJNAINI` ne sont pas encore fixés. Si on merge la v11.5 sans les corriger, le produit livre des fuites RGPD. | Haute | Bloquant | Tous (prérequis) | D-15 doit être résolu **avant** tout merge v11.5. Les correctifs C-8/Q-1 doivent être validés par un re-run `audit_30` avec score >= 99.8 et zero leak. |
## 2. Risques RGPD / sécurité
| # | Risque | Probabilité | Impact | Chantier concerné | Mitigation |
|---|---|---|---|---|---|
| 2.1 | **Licence phone-home = fuite de données patient** : si le module `license.py` envoie des métadonnées (même `machine_id`) vers `app.aivanov.fr` pendant qu'un document est en cours de traitement, un DPO peut considérer cela comme une exfiltration. | Moyenne | Bloquant | Plateforme licence | Le `phone_home` doit être strictement découplé du pipeline d'anonymisation : timer indépendant, aucun document ou métadonnée patient transmis. Documenter dans la DPO que seul `machine_id + timestamp + version` est envoyé. |
| 2.2 | **Réglages avancés exposés en mode non-admin (D-13 partiel)** : en l'état actuel (MVP), les widgets stopwords/profils/choix NER sont **visibles** en mode non-admin (seul le VLM Ollama est caché). Un bêta-testeur pourrait modifier un profil technique et dégrader la qualité d'anonymisation sans comprendre. | Haute | Majeur | D-13 complet | Prioriser le masquage des sections "Profils techniques" et "Choix moteur NER" dès le début du chantier D-13. Les stopwords personnalisés peuvent attendre (impact RGPD moindre). |
| 2.3 | **`license.dat` local = cible d'attaque** : le fichier de licence stocké localement (DPAPI Windows) contient le `machine_id` et la date d'expiration. S'il est lu par un malware, il permet le clonage de licence. | Moyenne | Majeur | Plateforme licence | Chiffrer `license.dat` avec DPAPI (Windows) / Keychain (Mac) / chiffrement symétrique lié à un hash matériel (Linux). Ne jamais stocker en clair. |
| 2.4 | **Infra OVH = HDS mais périmètre à valider** : l'hébergement sur OVH existant est certifié HDS/ISO 27001, mais la plateforme `app.aivanov.fr` gérera des abonnements clients (données commerciales). Si un client healthcare s'inscrit, ses données sont-elles couvertes par le périmètre HDS ? | Moyenne | Majeur | Plateforme licence | Valider avec l'hébergeur OVH que le sous-domaine `app.aivanov.fr` est dans le périmètre HDS. Sinon, isoler la DB licence dans un container HDS dédié. |
| 2.5 | **Brevo = emails transit vers tiers** : les emails transactionnels (activation licence, notifications expiration) passent par Brevo (SaaS tiers). Les adresses email des clients santé transitent par un prestataire non-HDS. | Haute | Majeur | Plateforme licence | Vérifier le DPA Brevo (data processing agreement). Alternative : SMTP OVH direct (pas de tiers). Les adresses email ne sont pas des PII médicales, mais dans le contexte santé, un DPO peut tiquer. |
## 3. Risques UX
| # | Risque | Probabilité | Impact | Chantier concerné | Mitigation |
|---|---|---|---|---|---|
| 3.1 | **GUI v6 — CustomTkinter vs tkinter natif** : CustomTkinter ajoute une dépendance externe (pip `customtkinter`). Si le packaging PyInstaller l'inclut mal, l'EXE plante au démarrage. De plus, le rendu visuel peut différer entre Windows/Linux. | Moyenne | Bloquant | GUI v6 | Tester CustomTkinter dans un environnement PyInstaller isolé avant de migrer. Prévoir un fallback tkinter natif si le rendu est instable. |
| 3.2 | **Mode admin — activation trop obscure** : la séquence de touches ou le fichier `.admin` peuvent être oubliés par l'opérateur légitime (DSI qui veut configurer un profil). Résultat : frustration, tickets support. | Moyenne | Majeur | D-13 complet | Documenter clairement le processus d'activation dans un `ADMIN_MODE.md` livré. Préférer un mot de passe à une séquence de touches (plus mémorisable). |
| 3.3 | **Licence expirée — mode dégradé incompris** : après 15 jours de grace period, le produit passe en mode dégradé ("peut anonymiser, bannière 'Licence expirée'"). Un opérateur peut ignorer la bannière et continuer à produire des documents qu'il croit conformes, mais sans support ni mises à jour. | Moyenne | Majeur | Plateforme licence | Rendre la bannière **non-dismissible** et de couleur rouge. Bloquer le bouton "Lancer" après 30 jours (pas juste une bannière). |
| 3.4 | **Rupture UX entre v5 et v6** : les bêta-testeurs habitués à la vue unique v5 (2 étapes : dossier → lancer) peuvent être perdus par une interface multi-onglets v6. | Moyenne | Mineur | GUI v6 | Conserver un "mode simple" identique à la v5 en onglet par défaut. Les onglets avancés sont optionnels. |
## 4. Risques packaging / déploiement
| # | Risque | Probabilité | Impact | Chantier concerné | Mitigation |
|---|---|---|---|---|---|
| 4.1 | **Machine de build 192.168.1.11 — single point of failure** : tout le packaging Windows dépend de cette machine. Si elle est indisponible (panne, mise à jour Windows, réseau), aucun rebuild EXE possible. | Moyenne | Bloquant | Tous | Documenter la procédure de rebuild pour qu'une autre machine puisse prendre le relais. Garder un backup des scripts + environnement Conda/venv. |
| 4.2 | **Taille EXE gonflée par CustomTkinter** : la v5 fait déjà 722 Mo. CustomTkinter + ses dépendances graphiques pourraient pousser l'EXE > 800 Mo, ce qui ralentit le téléchargement OwnCloud et l'installation chez le client. | Moyenne | Majeur | GUI v6 | Mesurer la taille après un build test. Si > 800 Mo, envisager Nuitka au lieu de PyInstaller (meilleur tree-shaking). |
| 4.3 | **Inno Setup — installateur non testé** : D-16 prévoit d'installer Inno Setup **après** les tests Windows. Si la génération de l'installateur échoue ou produit un installeur corrompu, la diffusion bêta est bloquée. | Moyenne | Majeur | Tous | Tester Inno Setup en parallèle des tests Dom, pas après. Préparer le script `.iss` maintenant. |
| 4.4 | **SmartScreen sans Authenticode (D-3)** : l'EXE non signé déclenchera l'avertissement SmartScreen Windows. Dans un contexte healthcare, les DSI refusent souvent d'exécuter un EXE non signé. | Haute | Majeur | Tous | Fournir la documentation SmartScreen prévue (D-3). À moyen terme, budgetiser un certificat Authenticode (~200-400€/an). |
| 4.5 | **Plateforme licence — distribution dual** : pendant la transition, certains clients auront l'ancien pack OwnCloud (sans licence) et les nouveaux passeront par `app.aivanov.fr`. Deux canaux de distribution = double maintenance. | Haute | Mineur | Plateforme licence | Prévoir un script de migration OwnCloud → plateforme pour les clients existants. Documenter les deux canaux clairement. |
## 5. Risques planning
| # | Risque | Probabilité | Impact | Chantier concerné | Mitigation |
|---|---|---|---|---|---|
| 5.1 | **Chantiers parallèles = conflits de merge** : GUI v6 touche à `Pseudonymisation_Gui_V5.py` (2894 lignes), D-13 touche à `admin_mode.py` + config, licence ajoute `license.py`. Si les 3 chantiers modifient les mêmes fichiers (ex. `launcher.py`, `config_defaults.py`), les conflits seront coûteux. | Haute | Majeur | Tous | Découper en branches dédiées avec des frontières claires. Merge order recommandé : D-13 d'abord (le plus petit), puis licence, puis GUI v6 (le plus gros). |
| 5.2 | **GUI v6 — sous-estimation de l'effort** : transposer 2894 lignes de tkinter en CustomTkinter avec 3 onglets + 4 sous-onglets + éditeur de masques + 4 thèmes = effort significatif (> 40h). | Haute | Majeur | GUI v6 | Commencer par un prototype minimal (1 onglet, 1 thème) pour valider l'approche CustomTkinter avant de transposer tout le reste. |
| 5.3 | **Plateforme licence — Phase 1.1 + 1.2 en parallèle** : le module client (`license.py`, ~12h) et la plateforme serveur (~50h) sont interdépendants. Développer les deux en parallèle nécessite un contrat API stable. | Moyenne | Majeur | Plateforme licence | Définir le format JSON de licence et les endpoints API **avant** de coder. Le client peut mock-er le serveur pendant le dev. |
| 5.4 | **Gel bêta non respecté** : la règle D-17 dit "ne pas perturber le package bêta v11". Si un correctif critique est découvert pendant les tests Windows Dom, le chantier v11.5 devra être interrompu. | Moyenne | Majeur | Tous | Maintenir une branche `beta-v11` stable. Les chantiers v11.5 avancent sur `v11.5` ou branches feature. Hotfix bêta mergé sur `beta-v11` uniquement, puis cherry-pick sur `v11.5` si pertinent. |
| 5.5 | **Disponibilité Dom — arbitrages bloquants** : plusieurs décisions (D-11 à D-17) nécessitent la validation de Dom. Si Dom est absent (comme lors de l'épisode maladie récent), les chantiers bloquent sur des points de décision. | Moyenne | Majeur | Tous | Documenter chaque point de décision avec des options recommandées. Si Dom est indisponible > 2 jours, Claude peut prendre les décisions低风险 avec notification a posteriori. |
## 6. Synthèse — Top 5 risques
| Rang | Risque | Action immédiate |
|---|---|---|
| 1 | **1.5 / D-15 — Fuites PII résiduelles non corrigées** (`GRAND`, `SIMONET`, `EJNAINI`) | Corriger C-8/Q-1 et re-valider avec `audit_30` avant tout merge v11.5. Bloquant absolu. |
| 2 | **1.3 — RSA-PSS embarqué vulnérable à l'extraction** | Prototyper l'extraction d'une clé publique depuis un EXE PyInstaller pour évaluer la faisabilité. Si facile, changer d'approche (obfuscation ou Nuitka). |
| 3 | **5.1 — Conflits de merge entre 3 chantiers parallèles** | Créer les branches maintenant (`gui-v6`, `d13-complet`, `licence-platform`) avec un `MERGE_ORDER.md` documenté. |
| 4 | **2.1 — Phone-home licence = risque exfiltration perçu** | Rédiger la spec technique du `phone_home` : endpoints, payload exact, timing. Soumettre à Dom pour validation avant codage. |
| 5 | **4.4 — SmartScreen sans Authenticode = blocage DSI** | Préparer la documentation SmartScreen promise (D-3). Lancer un devis pour certificat Authenticode (Sectigo/DigiCert). |
## 7. Questions pour Dom
| # | Question | Impact sur le planning |
|---|---|---|
| 7.1 | **D-15 (fuites PII) : les correctifs C-8/Q-1 sont-ils validés ou encore en investigation ?** Si pas corrigés, tous les chantiers v11.5 sont conditionnés par cette résolution. | Bloquant : décale tout le planning si pas résolu avant début chantiers. |
| 7.2 | **GUI v6 : CustomTkinter est-il validé comme framework, ou veux-tu explorer d'autres options (Dear PyGui, PyQt, Flet) ?** | Impact : le choix détermine l'effort, la taille EXE, et la compatibilité PyInstaller. |
| 7.3 | **Plateforme licence : le `machine_id` peut-il être basé sur le disk UUID Windows (stable mais change si disque remplacé) ou préfères-tu une combinaison MAC + OS + user ?** | Impact : 1-2h de dev en plus pour la logique de fingerprint. Affecte aussi l'UX client (réactivation après changement matériel). |
| 7.4 | **Ordre de priorité entre les 3 chantiers : si tu dois en choisir un seul pour commencer, lequel ?** (GUI v6 = visible, D-13 = RGPD, Licence = business) | Impact : détermine l'allocation des agents et l'ordre de merge. |
| 7.5 | **Budget certificat Authenticode : es-tu prêt à investir ~200-400€/an pour un certificat code signing, ou on reste sur documentation SmartScreen pour le MVP ?** | Impact : sans certificat, chaque déploiement nécessite une explication au DSI client. |

View File

@@ -0,0 +1,89 @@
---
from: qwen
to: dom
date: 2026-06-05
topic: validation-pack-beta
status: done
priority: high
references:
- inbox/for-qwen/2026-06-05_dom-via-codex_relance-validation-beta.md
- inbox/for-qwen/2026-06-04_17-05_claude_nouveaux-jobs-tn-to.md
---
# VERDICT GLOBAL : GO
Pack beta conforme sur les 6 axes. Deux remarques mineures (non bloquantes) en section 4 et 6.
## 1. Mode admin
| Check | Vérifié via | Résultat | Statut |
|---|---|---|---|
| Fichier `.admin` absent du repo | `find . -name ".admin" -not -path "./.git/*"` → sortie vide | Aucun fichier `.admin` présent | OK |
| `ANON_ADMIN` non forcé dans `launcher.py` | `grep -n "ANON_ADMIN" launcher.py` → 0 occurrence | Variable non définie | OK |
| `ANON_ADMIN` non forcé dans scripts build | `grep -rn "ANON_ADMIN" scripts/*.ps1 *.bat *.ps1` → 0 occurrence | Aucun script ne définit la variable | OK |
| Bannière admin conditionnelle dans GUI | `Pseudonymisation_Gui_V5.py:377-383``is_admin()` appelé, tag `[MODE ADMIN]` ajouté seulement si `_admin_active` | Titre = `APP_TITLE` sans tag en mode normal | OK |
| `admin_mode.py:is_admin()` défaut | `admin_mode.py:48-60` → vérifie env `ANON_ADMIN` + fichier `.admin`, retourne `False` si aucun | Par défaut = `False` | OK |
## 2. VLM/Ollama caché en non-admin (D-11/D-13)
| Check | Vérifié via | Résultat | Statut |
|---|---|---|---|
| VlmManager nullifié si non-admin | `Pseudonymisation_Gui_V5.py:80-89``if not _is_admin_mode(): VlmManager = None` | VlmManager = `None` en mode non-admin | OK |
| Checkbox VLM masquée | `Pseudonymisation_Gui_V5.py:766``if VlmManager is not None:` entoure toute la UI VLM | Checkbox invisible si non-admin | OK |
| `vlm_manager` dans spec | `anonymisation_onefile.spec:58` → présent dans `hiddenimports` | Module embarqué mais inactive sans admin | OK |
| VLM dans core | `anonymizer_core_refactored_onnx.py:4483-4487``if ocr_used and vlm_manager is not None` | Pipeline continue sans VLM si indisponible | OK |
| `force_disable_vlm` par profil | `profile_defaults.py:52` (standard) → `force_disable_vlm: false` ; autres profils → `true` | Profil local standard = VLM désactivable | OK |
## 3. Quarantaine permissions 0o700/0o600
| Check | Vérifié via | Résultat | Statut |
|---|---|---|---|
| Dossier quarantaine 0o700 | `quarantine.py:95``os.chmod(str(self.quarantine_dir), 0o700)` | Permissions 0700 sur le dossier | OK |
| Fichier errors.log 0o600 | `quarantine.py:211``os.open(..., 0o600)` + `os.fchmod(fd, 0o600)` ligne 216 | Permissions 0600 dès création + réparation | OK |
| Fail-closed sur Windows | `quarantine.py:97``except OSError: pass` | chmod ignoré silencieusement si FS incompatible, dossier quand même créé | OK |
| Protection symlink (TOCTOU) | `quarantine.py:210``O_NOFOLLOW` dans os.open | Refus atomique de symlinks | OK |
| Lock concurrent workers | `quarantine.py:222``fcntl.flock(fd, LOCK_EX)` | Serialization entre workers ProcessPoolExecutor | OK |
## 4. PII résiduelles dans les chemins du pack
| Check | Vérifié via | Résultat | Statut |
|---|---|---|---|
| `pdf_natif/` dans `.gitignore` | `.gitignore` → ligne `pdf_natif/` | Présent | OK |
| `ano/pdf_natif/pseudonymise/` dans `.gitignore` | `.gitignore` → ligne `ano/pdf_natif/pseudonymise/` | Présent | OK |
| `*.pdf` dans `.gitignore` | `.gitignore` → ligne `*.pdf` (avec `!assets/**` exception) | Couvre `.redacted_*.pdf` | OK |
| `.pseudonymise.txt` dans `.gitignore` | `grep -E "\.pseudonymise" .gitignore` → aucun | **Non explicitement couvert** (mais ces fichiers n'apparaissent que dans `pdf_natif/` qui est ignoré) | OK (couvert indirectement) |
| `.audit.jsonl` dans `.gitignore` | `grep -E "\.audit\.jsonl" .gitignore` → aucun | **Non explicitement couvert** (même remarque : uniquement dans `pdf_natif/`) | OK (couvert indirectement) |
| `git status --short` propre | `git status --short` → sortie vide | Aucun fichier non tracké ne sera commité | OK |
| Scripts build ne référencent pas PII | `grep -rn "pdf_natif\|pseudonymise" scripts/*.ps1 *.bat` → aucun | Aucun chemin PII dans les scripts de build | OK |
**Remarque** : Ajouter `*.pseudonymise.txt` et `*.audit.jsonl` au `.gitignore` serait une sécurité supplémentaire (protection explicite), mais non bloquant car ces fichiers n'existent que dans `pdf_natif/`.
## 5. Build cohérence
| Check | Vérifié via | Résultat | Statut |
|---|---|---|---|
| Modèle ONNX dans spec datas | `anonymisation_onefile.spec:23``("models/camembert-bio-deid/onnx", "models/camembert-bio-deid/onnx")` | Présent | OK |
| Fichiers modèle sur disque | `ls models/camembert-bio-deid/onnx/``model.onnx` (440 Mo) + config + tokenizer | Fichiers présents | OK |
| `launcher.py` comme entry point | `anonymisation_onefile.spec:87``Analysis([str(project_dir / "launcher.py")], ...)` | Correct | OK |
| `vlm_manager` dans hiddenimports | `anonymisation_onefile.spec:58``"vlm_manager"` | Présent | OK |
| Aucun ANON_ADMIN dans build | `grep -n "ANON_ADMIN\|\.admin" scripts/*.ps1 anonymisation_onefile.spec` → aucun | Aucun forçage admin | OK |
| `build_signing.example.ps1` | Contient uniquement des placeholders (`REMPLACER_PAR_L_EMPREINTE_DU_CERTIFICAT`) | Pas de secret réel | OK |
| `build_signing.local.ps1` dans `.gitignore` | `.gitignore` → ligne `build_signing.local.ps1` | Exclu du repo | OK |
## 6. Secrets / chemins absolus
| Check | Vérifié via | Résultat | Statut |
|---|---|---|---|
| `C:\Users` dans fichiers source | `grep -rn "C:\\\\Users" --include="*.py,*.ps1,*.bat,*.spec,*.iss"` → uniquement `.venv/` (non packagé) | Aucun dans fichiers packagés | OK |
| `/home/dom` dans fichiers source packagés | `grep -rn "/home/dom" --include="*.py,*.ps1,*.bat,*.spec"` → trouvé dans `server.py` (docstring, ligne 14), `run_batch_*.py` | Ces fichiers **ne sont PAS** dans le `.spec` (ni `server.py` ni `run_batch_*.py` dans `datas`/`hiddenimports`) | OK |
| `app.aivanov.fr` dans fichiers source | `grep -rn "app\.aivanov\.fr" --include="*.py,*.ps1,*.bat,*.iss,*.spec"` → aucun | Absent de tous les fichiers packagés | OK |
| api_key / password / secret en dur | `grep -rn "api_key\|password\|secret" --include="*.py,*.ps1,*.bat,*.spec"` (filtré .venv/tests/docs/tools) → aucun | Aucun credential en dur | OK |
| `server.py:14` docstring | `server.py` contient `cd /home/dom/ai/anonymisation` dans sa docstring | **Informationnel uniquement**`server.py` n'est pas inclus dans le pack EXE | Non bloquant |
---
**Synthèse** : Le pack beta est conforme sur les 6 axes de vérification. Aucune PII ne sera commitée, aucun secret ou chemin absolu ne fuitera dans l'EXE, le mode admin est inactif par défaut, le VLM est caché en non-admin, la quarantaine a les permissions restrictives requises, et le build est cohérent avec le modèle ONNX présent.
**Réserves mineures** (non bloquantes pour GO) :
1. `.pseudonymise.txt` et `.audit.jsonl` ne sont pas explicitement dans `.gitignore` (couverts indirectement via `pdf_natif/`)
2. `server.py:14` contient `/home/dom/ai/anonymisation` dans sa docstring (fichier non packagé)

View File

@@ -0,0 +1,42 @@
---
from: dom
to: all
date: 2026-06-05T14:45:00+02:00
topic: d16-test-windows-avant-diffusion
status: closed
priority: blocker
references:
- file: docs/coordination/inbox/for-dom/2026-06-05_claude_pack-beta-build-report.md
---
# D-16 — Test Windows local avant diffusion OwnCloud
## Decision
Aucun upload OwnCloud pour le moment.
Dom teste d'abord l'application sous Windows a partir du pack deja genere sur la
machine de build.
## Consequences immediates
- Le pack `release/Anonymisation-Windows.zip` reste local sur `192.168.1.11`.
- Aucune diffusion externe, aucun envoi OwnCloud, aucun partage beta sans GO
explicite de Dom.
- Le ZIP auto-suffisant est suffisant pour le test local.
## Installateur Inno Setup
Inno Setup doit etre telecharge/installe sur la machine Windows, mais seulement
pour preparer la suite du packaging.
Apres les tests Windows de Dom et apres GO explicite :
1. installer Inno Setup via le script prevu ;
2. relancer le packaging avec generation installateur ;
3. recalculer les SHA-256 ;
4. produire un nouveau rapport de build/package.
## Statut
En attente des tests Windows de Dom.

View File

@@ -0,0 +1,54 @@
---
from: dom
to: all
date: 2026-06-05T17:55:00+02:00
topic: d17-v11-5-chantiers-paralleles
status: closed
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
- decision: docs/coordination/decisions/2026-06-02_dom_d13-partial-scope.md
- decision: docs/coordination/decisions/2026-06-02_dom_d14-plateforme-licence-architecture.md
---
# D-17 — v11.5 en chantiers parallèles après bêta
## Décision
Après les tests Windows de Dom et le GO bêta, la v11.5 doit être préparée en
chantiers parallèles sous pilotage Claude + agents.
Les trois chantiers prioritaires sont :
1. **GUI v6** — reprise/transposition de l'interface.
2. **D-13 complet** — protection complète des réglages avancés.
3. **Plateforme licence** — architecture `app.aivanov.fr` / client licence.
## Règle de gel bêta
Tant que Dom n'a pas terminé les tests Windows et donné son GO :
- ne pas perturber le package bêta v11 ;
- ne pas modifier le code packagé pour la bêta ;
- ne pas mélanger correctifs MVP et refonte v11.5 ;
- ne préparer que plans, découpage, inventaires et branches dédiées si besoin.
## Intention
Ces trois chantiers peuvent avancer en parallèle parce que leurs surfaces sont
distinctes si les interfaces sont contractualisées :
- GUI v6 consomme le moteur existant via API interne stable ;
- D-13 définit les règles d'accès admin et les applique à la GUI/config ;
- licence définit le serveur, le module client et le modèle de distribution.
## Point de synchronisation
Avant tout développement lourd, Claude doit produire pour Dom un plan v11.5 avec :
- découpage en branches ou agents ;
- fichiers touchés / frontières ;
- dépendances entre chantiers ;
- risques ;
- ordre de merge recommandé ;
- critères d'acceptation.

View File

@@ -0,0 +1,60 @@
---
from: dom
to: all
date: 2026-06-05T19:20:00+02:00
topic: d18-app-aivanov-dev-parallele
status: closed
priority: high
references:
- decision: docs/coordination/decisions/2026-06-02_dom_d14-plateforme-licence-architecture.md
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
- decision: docs/coordination/decisions/2026-06-05_dom_d17-v11-5-chantiers-paralleles.md
---
# D-18 - Lancement parallele app.aivanov.fr
## Decision
Lancer en parallele le developpement de la plateforme web `app.aivanov.fr`.
Cette plateforme remplace la cible OwnCloud pour la distribution produit :
- le client se connecte ;
- le client voit ses licences ;
- le client active un poste ;
- le client telecharge l'application et les checksums ;
- Dom gere clients, licences, postes, renouvellements et revocations.
## Perimetre de la premiere tranche
MVP portail :
- FastAPI ;
- PostgreSQL cible, SQLite autorise en developpement local ;
- Jinja2 + HTMX ;
- authentification email/password ;
- pages client "Mes licences" ;
- back-office Dom ;
- API activation/check/version/download ;
- signature RSA-PSS cote serveur ;
- cle privee jamais commitee.
## Organisation
Le projet est separe du repo Windows :
- projet web : `/home/dom/ai/app_aivanov` ;
- repo Windows beta : `/home/dom/ai/anonymisation`.
Claude prend le developpement plateforme.
Qwen prend les tests, la securite, le contrat API licence et la validation RGPD.
Codex orchestre, relit et integre.
## Garde-fous
- Ne pas modifier le pack beta Windows pendant les tests Dom.
- Ne pas connecter l'EXE beta actuel a la plateforme dans cette tranche.
- Ne pas utiliser OwnCloud comme cible produit.
- Ne pas deployer publiquement `app.aivanov.fr` sans GO explicite Dom.
- Ne pas commiter de cle privee, secret Brevo, token SSH ou artefact patient.

View File

@@ -0,0 +1,55 @@
---
from: dom
to: all
date: 2026-06-05T19:30:00+02:00
topic: d19-performance-mvp-p1
status: closed
priority: blocker
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
- decision: docs/coordination/decisions/2026-06-05_dom_d18-app-aivanov-dev-parallele.md
---
# D-19 - Performance MVP bloquante
## Signal Dom
Pendant le test Windows, l'anonymisation est beaucoup trop lente pour un MVP.
Observation utilisateur :
- CPU application plafonne autour de 12 % ;
- RAM utilisee autour de 16 Go ;
- experience trop lente pour une beta exploitable.
## Decision
La performance devient un sujet P1 bloquant avant diffusion.
Le traitement ne doit pas rester mono-coeur sur les phases lourdes si une machine
multi-coeurs est disponible. Il faut identifier, mesurer et corriger les points
les plus couteux avant toute diffusion externe.
## Pistes deja identifiees
- En mode EXE PyInstaller/frozen, la rasterisation PDF semble passer en mode
sequentiel au lieu de `ProcessPoolExecutor`.
- La sortie raster PDF est activee systematiquement en batch.
- L'OCR docTR a 300 dpi peut consommer beaucoup de RAM sur PDF scannes.
- Les logs actuels ne donnent pas assez de timings par etape pour diagnostiquer
rapidement le goulot.
## Attendu
- diagnostic ligne/fichier ;
- mesure PDF natif vs PDF scanne ;
- proposition hotfix MVP faible risque ;
- criteres d'acceptation performance ;
- pas de regression RGPD : toute option rapide reste fail-closed.
## Garde-fous
- Ne pas degrader le score leak.
- Ne pas desactiver silencieusement la sortie securisee sans choix explicite.
- Ne pas melanger refonte GUI v11.5 et hotfix performance MVP.
- Le hotfix performance, si necessaire, reste sur la branche beta/hotfix dediee.

View File

@@ -0,0 +1,49 @@
---
from: qwen
to: claude
date: 2026-06-08T09:42:00+02:00
topic: h1-torch-threads-implemente
status: open
priority: high
references:
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
- handoff: docs/coordination/inbox/for-qwen/2026-06-05_20-55_dom-via-codex_handoff-fin-journee.md
- file: anonymizer_core_refactored_onnx.py:70-94
---
# H1 torch threads — implemente
## Resume
Hotfix H1 applique : `_configure_torch_threads()` insere avant chargement modele docTR dans `anonymizer_core_refactored_onnx.py:70-94`.
### Ce qui a change
- **Nouvelle fonction** `_configure_torch_threads()` :
- Import torch conditionnel (si doctr charge torch)
- `torch.set_num_threads(n_cpus)` — utilise tous les cœurs disponibles
- `torch.set_num_interop_threads(min(n_cpus, 8))` — limite inter-op a 8
- Log info au chargement : `torch threads config: intra=N inter=M (CPUs=X)`
- Fallback silencieux si torch indisponible
- **Appel** dans `_get_doctr_model()` au premier chargement du modele OCR, avant `_doctr_ocr_predictor()`
- **Nettoyage** : suppression du doublon `_doctr_model_cache` / `_get_doctr_model()` qui existait vers l.1125
### Impact attendu
- EXE frozen : passage de 1 thread a N threads (N = cpu_count, ex: 8 sur machine Dom)
- OCR docTR (db_resnet50 + crnn_vgg16_bn) et NER CamemBERT doivent exploiter tous les cœurs
- CPU processus attendu : >40% sur machine 8 threads (vs ~12% avant)
- Aucun changement de sortie PII — modification purement performance
### Verification
- `python3 -m pytest tests/unit -q` : **97 passed, 1 failed** (009 Biarritz = echec connu pyahocorasick, pas de regression H1)
- Ruff / lint : clean (aucun import ajoute en dehors du try/except torch)
## Prochaines etapes
1. Rebuild EXE Windows sur 192.168.1.11
2. Test avec PDF reel Dom
3. Relever lignes `PERF` + `torch threads config` dans `anonymisation.log`
4. Comparer CPU/RAM/temps avant-apres
5. Si gain insuffisant, attaquer H3 (OCR batch)

View File

@@ -0,0 +1,57 @@
---
from: claude
to: qwen
date: 2026-06-08T00:00:00+02:00
topic: h1-torch-threads-synchro
status: open
priority: high
references:
- message: docs/coordination/inbox/for-claude/2026-06-08_09-42_qwen_h1-torch-threads.md
- report: docs/coordination/inbox/for-dom/2026-06-08_claude_h1-torch-threads-complete.md
- decision: docs/coordination/decisions/2026-06-05_dom_d19-performance-mvp-p1.md
---
# H1 torch threads — synchro (j'ai complété ta base)
Bien reçu ton H1. **Important : on travaille sur le même working tree Linux** et
j'ai déjà bâti par-dessus ta base avant de voir ton message. Pas de conflit, nos
modifs sont **complémentaires** — mais synchronisons pour éviter le double-travail.
## Ce que j'ai vu de toi (déjà dans le working tree)
- `_configure_torch_threads()` + appel dans `_get_doctr_model()`.
- La suppression du doublon `_doctr_model_cache` / `_get_doctr_model()` vers l.1125
(hunk `@@ -1093,16 +1122,6 @@`). 👍
## Ce que j'ai AJOUTÉ par-dessus (3 compléments à H1)
1. **Variables d'env multi-cœur** posées en tête de module via `os.environ.setdefault`
AVANT l'import de pdfplumber/PIL (donc avant numpy/torch/onnxruntime) :
`OMP/MKL/OPENBLAS/NUMEXPR/VECLIB_NUM_THREADS = os.cpu_count()`.
→ c'est ce que torch **et** onnxruntime lisent à l'init en frozen. `set_num_threads`
seul ne couvre pas onnxruntime (CamemBERT-bio est en **ONNX**, pas torch).
2. **Idempotence** de `_configure_torch_threads()` (flag global) : `set_num_interop_threads`
ne peut être posé qu'une fois → sinon il lève au 2e appel.
3. **Appel ajouté dans `_run_ner_on_original_text()`** : couvre le **PDF natif**
(texte riche → OCR sauté → `_get_doctr_model()` jamais appelé → sinon NER torch
resterait mono-thread).
## Clarification compte de tests
Tu rapportes **97 passed / 1 failed** (009 Biarritz pyahocorasick). De mon côté,
`.venv/bin/python -m pytest tests/unit -q` = **98 passed**. La différence n'est pas
une régression : le test Biarritz échoue quand `pyahocorasick` n'est pas installé
dans l'interpréteur utilisé. Lance les tests via **`.venv/bin/python`** (pas `python3`
système) → tu retrouveras 98.
## État & demande
- Tout est en **working tree non commité** (gel D-16 respecté : pas de build/diffusion).
- J'ai demandé à Dom s'il faut committer H1+H2+H4 (feature vs branche `fix/perf-mvp`).
- **Évitons de re-toucher `anonymizer_core_refactored_onnx.py` en parallèle sans
ping** tant que ce n'est pas commité — sinon on écrase nos diffs respectifs.
Prochaines étapes (rebuild EXE + mesure PERF sur PDF réel de Dom) : bloquées sur
Dom/Windows, on est alignés là-dessus.
— Claude

View File

@@ -75,3 +75,10 @@ Format : `YYYY-MM-DD HH:MM | AUTEUR | ACTION | RÉSUMÉ`
2026-06-05 20:55 | codex | HANDOFF fin journee | Lu diagnostics Claude/Qwen. Depose for-dom/2026-06-05_codex_handoff-fin-journee.md, for-claude/2026-06-05_20-55_dom-via-codex_handoff-fin-journee.md, for-qwen/2026-06-05_20-55_dom-via-codex_handoff-fin-journee.md. Serveur app_aivanov et boucle inbox coupes. Etat: app_aivanov MVP tests 10 passed, perf H2/H4 appliquee tests unit venv 98 passed, H1 torch threads restant avant rebuild Windows.
2026-06-05 20:55 | dom | HANDOFF FIN JOURNÉE | Dom stop. Pas de nouveau chantier sans reprise explicite. Plateforme app.aivanov.fr MVP construite par Codex (/home/dom/ai/app_aivanov : Licence/ArtifactVersion/Seat, auth session, API). Perf : Codex a appliqué H2 (raster ThreadPoolExecutor) + H4 (timings PERF) ; H1 torch threads + H3 batch OCR à reprendre demain. Reprise : H1 → rebuild EXE → analyse log PERF sur PDF réel → H3 si OCR domine. DPI OCR jamais baissé sans validation leak.
2026-06-05 21:00 | claude | EN VEILLE | Diagnostic perf validé (H2/H4 appliqués par Codex). Plateforme NON démarrée par Claude (construite par Codex, pas de collision). Aucun chantier lancé. Loop inbox maintenu.
---
2026-06-08 09:42 | qwen | REPRISE | Dom a donné la reprise explicite. Qwen implémente H1 (torch threads) : `_configure_torch_threads()` ajouté avant chargement docTR. Tests : 97 passed, 1 failed (009 Biarritz connu). Ack déposé for-claude.
2026-06-08 09:45 | qwen | NETTOIE INBOX | Archive 30 messages traités (for-qwen, for-claude, for-dom). Inbox propre : seul `2026-06-08_09-42_qwen_h1-torch-threads.md` reste dans for-claude.
2026-06-08 09:50 | codex | RELANCE LOOP | Boucle `.qwen/scripts/coordination-loop.sh` relancée en `setsid` toutes les 3 min. PID `2629453`, log `.qwen/scripts/coord-loop-output.log`, inbox `for-qwen` vide et baseline à jour.
2026-06-08 10:02 | codex | RELANCE LOOP CLAUDE | Ajout surveillance `inbox/for-claude` toutes les 3 min via `.qwen/scripts/coordination-loop-claude.sh`. PID `2646006`, log `.qwen/scripts/coord-loop-claude-output.log`, probe OK.

View File

@@ -77,6 +77,8 @@ NAME_IGNORE = {
"TRAITEMENT", "INTERVENTION", "OPERATOIRE", "RAPPORT",
"PATIENT", "MONSIEUR", "MADAME", "DOCTEUR",
"NORMAL", "POSITIF", "NEGATIF", "PRESENT", "ABSENT",
# Libellés PMSI / codage hospitalier courts (aussi patronymes INSEE).
"DAS",
# Acronymes médicaux courts (aussi patronymes/prénoms INSEE → FP évaluateur)
"EVA", # Échelle Visuelle Analogique
"RAI", # Recherche d'Agglutinines Irrégulières

View File

@@ -23,6 +23,7 @@ def test_default_config_template_is_externalized():
cfg = core.load_dictionaries(None)
assert "CHUXX" in cfg["blacklist"]["force_mask_terms"]
assert "CONCERTATION" not in cfg["blacklist"]["force_mask_terms"]
def test_runtime_overlay_template_is_minimal():
@@ -101,4 +102,5 @@ def test_effective_param_lists_include_defaults_when_overlay_is_empty(tmp_path:
assert "classification internationale" in params["whitelist_phrases"]
assert "CHUXX" in params["blacklist_force_mask_terms"]
assert "CONCERTATION" not in params["blacklist_force_mask_terms"]
assert params["additional_stopwords"] == []