# Tolérances de régression — legacy ↔ rewrite > **Audience** : développeur qui migre une fonctionnalité legacy > vers le rewrite, reviewer qui relit la PR. > > **Référence** : [`legacy-retirement-plan.md`](legacy-retirement-plan.md). > > **Contrat** : le harness `tests/regression/legacy_vs_rewrite/` > exécute legacy + rewrite sur les mêmes corpus de référence et > compare leurs sorties. Toute divergence au-delà de la tolérance > ε définie ici est une **régression à corriger avant merge**. > > Une régression peut être : > > - **Intentionnelle** : la phase de migration corrige un bug > historique → la tolérance est temporairement relâchée AVEC > commentaire pointant vers l'issue. > - **Inattendue** : c'est ce que ce document est censé empêcher. ## Principe général Pour une fonctionnalité donnée, la sortie du rewrite **doit être égale** à celle du legacy à la tolérance ε près. L'égalité est : - **Bit-for-bit** quand l'output est déterministe (texte, hash, JSON). - **Sémantique** quand l'output structurel a des libertés (ordre des éléments d'un set, indentation HTML, ordre des facts narratifs équivalents). ## Table des tolérances par type d'output ### Métriques numériques | Métrique | ε | Justification | |----------|---|---------------| | `cer_raw`, `cer_nfc`, `cer_caseless`, `cer_diplomatic` | **0** (bit-for-bit) | jiwer est déterministe ; toute différence = changement de pré/post-processing | | `wer`, `mer`, `wil` | **0** | idem | | `bleu`, `chrf` | **1e-9** | flottants — réordonnancements internes acceptables | | `precision`, `recall`, `f1` (NER) | **1e-9** | flottants | | `mufi_coverage`, `abbreviation_expansion_score` | **0** | comptage entier sur ensembles fermés | | `roman_numerals_accuracy` | **0** | parsing déterministe | | `unicode_blocks_accuracy` | **0** | tables Unicode déterministes | | `reading_order_f1` (ICDAR 2015) | **1e-9** | algorithme déterministe, flottants | | `layout_f1` | **1e-9** | flottants | | `confusion_matrix.entries` | **0** | comptage entier | | `taxonomy.error_class_*` | **0** | classification déterministe sur règles | ### Tests statistiques | Test | ε | Justification | |------|---|---------------| | Wilcoxon `p_value` | **1e-9** | scipy `wilcoxon` est déterministe à entrée constante | | Friedman `chi2`, `p_value` | **1e-9** | idem | | Nemenyi (matrice p-values) | **1e-9** | dérivé de Friedman | | Bootstrap CI 95 % | **1e-3** | random seed FIXÉ explicitement (cf. `bootstrap.py` du legacy : `seed=42`) ; la tolérance laisse une marge minuscule pour les ré-implémentations qui itéreraient dans un ordre différent à seed identique | | Pareto front (set d'engines dominants) | **0** (bit-for-bit en tant qu'ensemble) | dominance Pareto stable sur entrées identiques | | CDD (Critical Difference Diagram) coordonnées SVG | **1e-3** sur les positions (px) | rendu Matplotlib peut varier sur des sub-pixels selon backend | | Clustering (labels) | **0** sur l'**ensemble** des classes (l'étiquetage interne 0/1/2 peut différer mais la partition doit être identique) | un test custom compare les partitions, pas les labels | | Corrélation Spearman / Pearson | **1e-9** | flottants | ### Calibration | Output | ε | Justification | |--------|---|---------------| | ECE, MCE | **1e-9** | flottants, pas d'aléatoire | | Reliability diagram (bins, freq, conf) | **0** sur les bins, **1e-9** sur les valeurs | binning déterministe | ### Confidences sidecar (S50 sur Tesseract) | Output | ε | |--------|---| | `tokens[].text` | **0** (string identique) | | `tokens[].confidence` | **0** | Tesseract retourne un entier 0-100 ; division exacte par 100 → flottant binairement identique en IEEE-754 | | `extractor`, `model_version` | **0** | ### HTML (rapport `reports/html/render.py`) Le diff HTML est **structurel**, pas lexical : - Mêmes éléments DOM avec mêmes attributs sémantiques (`data-*`, `aria-*`, `id`, `class`). - Mêmes valeurs textuelles dans les nœuds de texte. - L'**ordre** des sections doit être identique. - L'indentation et le whitespace inter-éléments sont **ignorés**. - Le contenu d'un `