fix(anonymizer): handle FC14 practitioner OGC rules

This commit is contained in:
2026-06-08 12:03:51 +02:00
parent 21a408a9e4
commit 94f7903af3
12 changed files with 759 additions and 3 deletions

View File

@@ -0,0 +1,67 @@
---
from: claude
to: dom
date: 2026-06-08T00:00:00+02:00
topic: blocage-rebuild-c40441d
status: open
priority: blocker
references:
- mission: docs/coordination/inbox/for-claude/2026-06-08_11-12_dom-via-codex_rebuild-hotfix-perf-c40441d.md
- commit: c40441d
- decision: docs/coordination/decisions/2026-06-05_dom_d16-test-windows-avant-diffusion.md
---
# Blocage rebuild Windows c40441d — commit non poussé sur Gitea
## Diagnostic (vérifié, pas déduit)
| Élément | État |
|---|---|
| `c40441d` en local Linux | ✅ présent, = HEAD de `feature/q1-quarantine-mvp` |
| `c40441d` sur un remote | ❌ **sur aucun remote** (`git branch -r --contains c40441d` vide) |
| `gitea/feature/q1-quarantine-mvp` | à `15f73f8` (base bêta), **7 commits derrière** le local |
| Relation local↔distant | **fast-forward** (non destructif) — le distant est ancêtre direct |
| HEAD machine Windows (192.168.1.11) | `15f73f8`, branche `feature/q1-quarantine-mvp`, working tree propre |
| `c40441d` côté Windows | ❌ `fatal: Not a valid object name` |
| Remote Gitea côté Windows | `http://192.168.1.40:3100/Dom/anonymisation.git` (accessible) |
**Conclusion** : impossible de builder `c40441d` côté Windows tant qu'il n'est pas
poussé sur Gitea. Conformément à la mission (« ne pas faire de push destructif »,
contrainte « pas de push sans demande explicite »), **je n'ai pas poussé**. Je
signale et propose la commande.
## Résolution proposée (2 étapes, GO Dom requis pour l'étape 1)
**Étape 1 — push Linux → Gitea** (fast-forward, non destructif) :
```bash
cd /home/dom/ai/anonymisation
git push gitea feature/q1-quarantine-mvp
```
Pousse 7 commits (6 docs coordination + le hotfix perf `c40441d`). Aucune
réécriture d'historique, aucun force.
**Étape 2 — récupération côté Windows** (je peux la lancer dès l'étape 1 faite) :
```cmd
cd C:\Users\dom\ai\anonymisation
git fetch gitea && git merge --ff-only gitea/feature/q1-quarantine-mvp
git cat-file -t c40441d :: doit retourner "commit"
```
## Prêt à enchaîner dès le commit disponible côté Windows
Au GO, je lance le rebuild EXE interne (via `schtasks` + log, builds longs — le MCP
SSH tue les process au retour de commande), **build interne de mesure uniquement,
aucun upload / diffusion / package public** (D-16). Puis je relève temps total,
CPU moyen/pic, RAM pic, lignes `torch threads config`, lignes `PERF`, mode raster
(`threads`/`sequential`/`processes`), présence/absence de fallback raster — et je
teste ton PDF lent si tu me le rends accessible (sinon je prépare le build candidat
et te le réclame).
## Décision attendue de Dom
1. **GO pour `git push gitea feature/q1-quarantine-mvp`** (par toi/Codex, ou tu
m'autorises explicitement à le faire) ?
2. Le **PDF lent** de ton test Windows : chemin sur 192.168.1.11, ou tu me le
déposes quelque part d'accessible ?
— Claude

View File

@@ -0,0 +1,184 @@
---
from: qwen
to: dom
date: 2026-06-08T11:55:00+02:00
topic: validation-independante-c40441d
status: open
priority: high
references:
- commit: c40441d
- mission: docs/coordination/inbox/for-qwen/2026-06-08_11-12_dom-via-codex_validation-c40441d-perf-fp.md
---
# Validation independante c40441d
## 1. Resume du commit
`c40441d fix(perf): apply MVP threading hotfix` — 120 lignes modifiees dans
`anonymizer_core_refactored_onnx.py`, 1 ligne dans `config/dictionnaires.default.yml`,
3 lignes dans `config/profiles.yml`, 20 fichiers archives (coordination).
### Modifications code
| Zone | Changement | Lignes |
|---|---|---|
| Env vars multi-cœur (H1) | `os.environ.setdefault` pour OMP/MKL/OPENBLAS/NUMEXPR/VECLIB AVANT imports lourds | 10 |
| `_configure_torch_threads()` | Idempotent, `set_num_threads(n_cpus)` + `set_num_interop_threads(min(n,8))` avec guard | 27 |
| `_get_doctr_model()` | Cache + appel `_configure_torch_threads()` avant chargement | 6 |
| `_run_ner_on_original_text()` | Appel `_configure_torch_threads()` (PDF natif sans OCR) | 5 |
| `redact_pdf_raster()` | ThreadPoolExecutor en frozen + fallback + log PERF + env `ANON_DISABLE_RASTER_THREADS` | ~40 |
| `dictionnaires.default.yml` | CONCERTATION retire des force_terms | 1 |
| `profiles.yml` | Force_terms redondants retires | 3 |
## 2. Validation des risques techniques
### 2.1 Env vars posees avant imports lourds
**✅ VERIFIE** — `os.environ.setdefault` aux lignes 32-34, premier import lourd
(pdfplumber) ligne 45. PIL ligne 48, fitz ligne 51, numpy ligne 1299.
L'ordre est correct : les variables sont lues par numpy/torch/onnxruntime a
leur initialisation, donc elles doivent etre posees avant tout import transif.
C'est le cas.
### 2.2 Idempotence `torch.set_num_interop_threads`
**✅ VERIFIE** — Le flag global `_TORCH_THREADS_CONFIGURED` empeche un 2e appel.
`set_num_interop_threads` est dans un `try/except` interne avec `pass` si
l'API refuse (deja figuree par un travail torch anterieur).
Test empirique : 3 appels successifs sans exception, log unique
`torch threads config: intra=32 inter=8 (CPUs=32)`.
### 2.3 Appel H1 pour OCR et PDF natif/NER
**✅ VERIFIE** — Deux points d'appel :
- `_get_doctr_model()` → chemin OCR (PDF scanne, texte extrait via docTR)
- `_run_ner_on_original_text()` → chemin PDF natif (texte riche, OCR saute)
Les deux chemins couvrent les deux modes de traitement. L'appel dans
`_run_ner_on_original_text()` est un complement de Claude par rapport a ma
version initiale (qui ne couvrait que l'OCR). C'est necessaire car le NER
torch (EDS-Pseudo, GLiNER) tourne sur le texte original et serait mono-thread
sans cet appel.
### 2.4 Rollback `ANON_DISABLE_RASTER_THREADS=1`
**✅ VERIFIE** — Le code lit :
```python
disable_threads = os.getenv("ANON_DISABLE_RASTER_THREADS", "").lower() in {"1", "true", "yes", "on"}
```
5 valeurs reconnues. En mode frozen avec cette variable, le raster revient
en sequentiel. Le log indique `reason=env_disabled`.
### 2.5 Risque thread-safety PyMuPDF/Pillow en frozen
**⚠️ ATTENTION — risque identifie**
`ThreadPoolExecutor` partage le meme processus. PyMuPDF (`fitz`) et Pillow
sont-ils thread-safe ?
- **PyMuPDF** : la doc officielle dit que chaque objet `fitz.Document` et
`fitz.Page` doit etre utilise dans un seul thread. Le code raster utilise
un `fitz.open()` **par thread** (dans `_rasterize_page`), donc pas de
partage d'objet entre threads. ✅ OK.
- **Pillow** : `Image.frombytes` et `Image.save` sont thread-safe pour des
operations independantes sur des objets separes. ✅ OK.
- **GIL Python** : les operations lourdes (rasterisation PyMuPDF, encodage
PNG Pillow) liberent le GIL car ce sont des extensions C. Le
multi-threading apporte donc un vrai gain parallele. ✅ OK.
**Conclusion** : le risque est mitigé par l'isolation des objets `fitz` par
thread. Aucun conflit identifie.
### 2.6 Absence de changement de detection PII
**✅ VERIFIE** — Le diff ne modifie aucune logique de detection. Seuls les
points suivants changent :
- CONCERTATION retire de `dictionnaires.default.yml` (force_terms)
- Force-terms redondants retires de `profiles.yml`
- Commentaire mis a jour dans `_kv_value_only_mask` (`CHUXX, sigle local...`)
Aucune modification des regex, NER, gazetteers, ou logique de propagation.
## 3. Tests unitaires
**98 passed, 0 failed** avec `.venv/bin/python -m pytest tests/unit -q`.
Le test 009 (Biarritz, pyahocorasick) passe dans le venv car la dependance
est installee. Mon test precedent avec `python3` systeme (97 passed) etait
un artefact d'environnement, pas une regression.
## 4. Mini-corpus pdf_natif (6 PDF natifs)
| Fichier | PII hits | Force terms | CONCERTATION |
|---|---|---|---|
| FC14.pdf | 45 | 0 | ✅ absent |
| FC16.pdf | 45 | 0 | ✅ absent |
| FC17.pdf | 45 | 0 | ✅ absent |
| FC19.pdf | 45 | 0 | ✅ absent |
| FC21.pdf | 45 | 0 | ✅ absent |
| FC8.pdf | 44 | 0 | ✅ absent |
**Evaluateur qualite** :
```
SCORE GLOBAL : 100.0/100 [A+]
Leak score : 100.0/100
FP score : 100/100
Fuites noms audit : 0
Fuites regex (PII) : 0
Noms INSEE (contexte fort) : 0
Termes médicaux masqués : 0
Alertes sur-masquage : 0
```
**CONCERTATION** : ✅ Aucun force-term genere sur les 6 PDF. Le retrait du
dictionnaire est valide.
## 5. Matrice de validation Windows
### Scenarios de test
| # | Scenario | Fichier attendu | Mesures |
|---|---|---|---|
| 1 | PDF natif court (<5 pages) | FC8.pdf ou equivalent | Temps <5s, CPU >30%, RAM <4 Go |
| 2 | PDF natif moyen (10-30 pages) | FC14.pdf (4 pages) ou plus long | Temps proportionnel, CPU >30% |
| 3 | PDF scanne court (<5 pages) | PDF scanne disponible | Temps <30s, CPU >30%, mode raster=threads |
| 4 | PDF reel lent Dom | Fourni par Dom | Temps avant/apres, CPU, RAM, mode raster |
### Mesures attendues dans `anonymisation.log`
| Ligne log | Valeur attendue (avant) | Valeur attendue (apres) |
|---|---|---|
| `torch threads config: intra=N inter=M (CPUs=X)` | Absente | `intra=8 inter=8 (CPUs=8)` (machine Dom) |
| `Raster PDF: mode=threads pages=N workers=W dpi=D frozen=1` | Absente (sequential) | Present si PDF >2 pages |
| `PERF ... stage=...` | Present | Present (unchanged) |
| `Raster PDF: mode=sequential ... reason=env_disabled` | N/A | Si `ANON_DISABLE_RASTER_THREADS=1` |
| CPU processus | ~12% | >40% |
| RAM pic | 16 Go | Similaire ou legerement superieur |
### Criteres GO/NO-GO
| Critere | GO | NO-GO |
|---|---|---|
| Leak score | 100/100 | <100 |
| FP score | 100/100 | <95 |
| Temps total PDF lent | <50% du temps precedent | >= temps precedent |
| CPU moyen | >30% | <20% |
| Crash/erreur | Aucun | 1+ |
| RAM pic | <20 Go | >24 Go |
## 6. Avis
**GO conditionnel** pour rebuild Windows, sous reserve que :
1. Le push `c40441d` vers Gitea soit fait (bloquant pour Windows)
2. Le test PDF reel de Dom confirme le gain CPU/temps
3. Aucun crash thread-safety ne remonte
**NO-GO** si le PDF reel montre :
- Une regression leak (force_term retire trop tot pour un cas non teste)
- Un crash PyMuPDF en mode threads (rare mais possible avec certains PDF)
- Un gain CPU negligible (<10%) malgre la config threads