---
from: claude
to: dom
date: 2026-06-03
topic: ejnaini-root-cause (T-H)
status: RÉSOLU — fix F5 implémenté + validé audit_30
references:
  - file: docs/coordination/inbox/for-dom/2026-06-02_qwen_ejnaini-investigation.md
  - doc: trackare-BA127127-23135726
---

# EJNAINI — cause racine confirmée (et conclusion de Qwen corrigée)

## Test empirique (moteur actuel, paranames + stopwords nettoyés)

Re-traitement du PDF source `433_23135726/trackare-BA127127...pdf` :

| Token | Avant | Maintenant |
|---|---|---|
| `NOCENT` | 7 fuites | **0** ✅ |
| `EJNAINI` | 7 fuites | **7** ❌ |

Pattern résiduel **déterministe** (7×) :
```
[NOM]-
EJNAINI
```

## Conclusion de Qwen RÉFUTÉE

Qwen concluait : « paranames résoudra EJNAINI ». **Faux.**
- `EJNAINI` **est** dans paranames (`noms_famille_world.txt.gz`) et chargé dans le core (vérifié).
- Il reste pourtant non masqué.

## Vraie cause racine

Deux chemins de masquage parallèles dans le moteur :
1. **Spans NER** → remplacement direct du span détecté. Capte `Cécilia NOCENT-EJNAINI` **là où il est intact** (166 hits NOM).
2. **NameCandidates** (regex) → cross-validation (NER/INSEE/paranames) → `safe_names` → remplacement global **+ redaction raster** (`NOM_GLOBAL`).

En zone tableau Trackare, le nom est éclaté sur deux lignes : `NOCENT-` en fin de ligne, `EJNAINI` orphelin plus bas.
- Le span intact `Cécilia NOCENT-EJNAINI` n'existe pas → chemin 1 ne le voit pas.
- Aucun candidat regex ne propose `EJNAINI` seul → chemin 2 ne le voit pas (donc paranames jamais consulté pour lui).
- F1 ne décompose que les tokens **uniques** à trait d'union déjà dans `names`, pas les **spans multi-mots** NER.

`NOCENT` finit masqué (`[NOM]-`) par un autre artefact de remplacement, mais `EJNAINI`, n'étant ni dans un span intact ni dans `safe_names`, fuit.

## Fix recommandé (robuste, corrige texte ET PDF)

Quand le NER détecte un nom **multi-mots ou à trait d'union** (PER), **décomposer** le span et injecter ses tokens composants (≥4 chars, non stop-word, confirmés gazetteer/NER) comme **NameCandidates haute confiance** → ils entrent dans `safe_names` → remplacement global + `NOM_GLOBAL` (raster).

C'est une généralisation de F1 aux spans NER multi-mots. Avantage : masque `EJNAINI` partout (texte + raster), pas un patch cosmétique sur le `.txt`.

## Fix implémenté (F5) — post-passe orpheline

Au lieu de la décomposition des spans NER (le span n'existe pas en zone tableau),
la solution retenue cible directement le pattern résiduel : après `selective_rescan`,
on masque le token majuscule orphelin qui suit immédiatement un `[NOM]-` (cas
`[NOM]-\nEJNAINI`). Implémenté dans `process_pdf` (étape 3a-bis), même style que le
nettoyage des codes postaux orphelins. Masque le texte **et** le raster (via `NOM_GLOBAL`).

## Validation (2026-06-03)

- BA127127 re-traité : `NOCENT=0, EJNAINI=0` ✅
- `tests/unit` : 85 passed
- **audit_30 complet** (29 docs, 1 quarantiné) :
  - **Score global 98.5/100 (A+)** (baseline 97.0, +1.5)
  - **Leak score 100/100** — 0 fuite audit, 0 regex, 0 INSEE contexte fort
  - FP score 95 (+5 vs baseline), 0 terme médical masqué
  - **Aucune régression**

EJNAINI était la dernière fuite de l'audit_30. **Leak score = 100%.**

— Claude

