Spaces:
Sleeping
docs(migration): plan de retrait complet du legacy vers la 2.0
Browse filesDocument préparatoire à la décision : pas de cohabitation legacy +
rewrite à long terme, la 2.0 est livrée sans aucune ligne legacy.
Critère absolu : zéro bricolage, zéro semi-rendu, zéro régression
de comportement éditorial.
Plan en 12 phases (77-110 jours-personnes, 3,5-5 mois) :
- Phase 0 : foundation (harness régression + tracker + tolérances)
- Phase 1 : core/results, facts, pipeline, modules
- Phase 2 : statistics (Wilcoxon, Friedman/Nemenyi, bootstrap, …)
- Phase 3 : narrative engine (18 détecteurs + arbiter + 36 templates)
- Phase 4 : 35 mesures legacy (par cluster thématique)
- Phase 5 : 22 renderers HTML + glossaire + i18n
- Phase 6 : pipelines OCRLLMPipeline → PipelineSpec composable
- Phase 7 : modules officiels (TextToAltoMonoRegion)
- Phase 8 : importers IIIF/Gallica/eScriptorium
- Phase 9 : 9 routers web legacy → interfaces/web/
- Phase 10 : 13 commandes CLI manquantes
- Phase 11 : retrait final + bump 2.0
Le document est déclaré 'vivant' et mis à jour à chaque phase.
Définition de 'done' universelle, stratégie de régression
bit-for-bit, anti-bricolage explicite (pas de double API, pas de
shim sans date de retrait, pas de TODO sans issue, pas de
copié-collé, pas de god-module).
NOTE : ce commit livre uniquement le PLAN. L'exécution attend la
validation utilisateur avant de démarrer la Phase 0.
|
@@ -0,0 +1,353 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Plan de retrait complet du legacy — vers la 2.0
|
| 2 |
+
|
| 3 |
+
> **Décision stratégique** : pas de cohabitation legacy + rewrite à
|
| 4 |
+
> long terme. La 2.0 est livrée **sans aucune ligne legacy**.
|
| 5 |
+
> L'arborescence cible (domain → formats → evaluation → pipeline →
|
| 6 |
+
> adapters → app → reports_v2 → interfaces) est unique.
|
| 7 |
+
>
|
| 8 |
+
> **Critère absolu** : zéro bricolage, zéro semi-rendu, zéro
|
| 9 |
+
> régression de comportement éditorial. Une institution comme la
|
| 10 |
+
> BnF ne tolère pas un *partial rewrite*.
|
| 11 |
+
>
|
| 12 |
+
> **Pas de contrainte de date** : on livre quand tout est propre.
|
| 13 |
+
>
|
| 14 |
+
> **Document vivant** : ce plan est mis à jour à chaque phase
|
| 15 |
+
> achevée. Toute exception ou découverte doit être inscrite ici.
|
| 16 |
+
|
| 17 |
+
## Définition de « done » universelle
|
| 18 |
+
|
| 19 |
+
Chaque phase est terminée quand **tous** les critères suivants sont
|
| 20 |
+
remplis :
|
| 21 |
+
|
| 22 |
+
1. **Code** : les modules legacy de la phase ont été soit migrés,
|
| 23 |
+
soit déclarés sans équivalent et supprimés (avec justification).
|
| 24 |
+
2. **Tests** : tous les tests qui pointaient vers le legacy sont
|
| 25 |
+
migrés vers le rewrite ; les nouveaux tests couvrent le rewrite
|
| 26 |
+
à un niveau ≥ celui du legacy.
|
| 27 |
+
3. **Régression** : le harness `tests/regression/legacy_vs_rewrite/`
|
| 28 |
+
prouve que le rewrite produit les mêmes résultats que le legacy
|
| 29 |
+
sur les corpus de référence (tolérance ε explicite par métrique).
|
| 30 |
+
4. **Doc** : la doc utilisateur, opérationnelle et architecturale
|
| 31 |
+
ne mentionne plus le legacy de la phase ; les chemins cassés
|
| 32 |
+
`tests/architecture/test_doc_paths.py` baseline diminue.
|
| 33 |
+
5. **Lint** : `ruff check picarones/ tests/` clean.
|
| 34 |
+
6. **Suite complète** : `pytest tests/` 100 % vert sur 3 OS × 3
|
| 35 |
+
versions Python (3.11, 3.12, 3.13).
|
| 36 |
+
7. **Coverage** : ≥ 85 %, pas de dégradation > 0,5 pt vs. la phase
|
| 37 |
+
précédente.
|
| 38 |
+
|
| 39 |
+
## Phases
|
| 40 |
+
|
| 41 |
+
### Phase 0 — Foundation (en cours)
|
| 42 |
+
|
| 43 |
+
**Objectif** : poser les garde-fous qui rendent les 11 phases
|
| 44 |
+
suivantes **vérifiables** sans introduire de régression invisible.
|
| 45 |
+
|
| 46 |
+
**Livrables** :
|
| 47 |
+
|
| 48 |
+
- [x] `docs/migration/legacy-retirement-plan.md` (ce document) —
|
| 49 |
+
inventaire complet, phases, acceptance criteria.
|
| 50 |
+
- [ ] `tests/regression/legacy_vs_rewrite/` — harness qui exécute
|
| 51 |
+
legacy + rewrite sur 3 corpus de référence et compare bit-for-bit
|
| 52 |
+
(avec ε explicite par métrique).
|
| 53 |
+
- [ ] `docs/migration/regression-tolerances.md` — table des
|
| 54 |
+
tolérances acceptables par métrique (ex : CER ε = 0, narrative
|
| 55 |
+
templates ε = 0 mais ordre des facts non-significatif, etc.).
|
| 56 |
+
- [ ] Test architectural `test_no_legacy_imports_in_rewrite.py` qui
|
| 57 |
+
garantit qu'un module rewrite ne réintroduit jamais d'import
|
| 58 |
+
legacy.
|
| 59 |
+
|
| 60 |
+
**Critère de fin** : harness vert sur 3 corpus de référence pour
|
| 61 |
+
les fonctionnalités déjà migrées (5 OCR, 4 LLM, 4 VLM, vues
|
| 62 |
+
canoniques). Toute migration future doit ajouter son corpus de
|
| 63 |
+
régression.
|
| 64 |
+
|
| 65 |
+
### Phase 1 — Foundation conceptuelle (`core/`, `domain/`)
|
| 66 |
+
|
| 67 |
+
**Modules à migrer** :
|
| 68 |
+
|
| 69 |
+
| Legacy | Cible rewrite | Note |
|
| 70 |
+
|--------|---------------|------|
|
| 71 |
+
| `core/results.py` (677 LOC, `BenchmarkResult`/`EngineReport`/`DocumentResult` + 30 champs agrégés) | `domain/run_result.py` + champs agrégés en `Artifact` typés | Le plus critique |
|
| 72 |
+
| `core/facts.py` | déjà dans `domain/facts.py` | Vérifier parité |
|
| 73 |
+
| `core/pipeline.py` (legacy `PipelineSpec` + `BaseModule`) | `domain/pipeline_spec.py` + `domain/module_protocol.py` | Migration des callers |
|
| 74 |
+
| `core/modules.py` (`BaseModule`, `ArtifactType` 6 valeurs) | `domain/artifacts.py` (déjà 10 valeurs) + `domain/module_protocol.py` | Le superset |
|
| 75 |
+
| `core/metric_registry.py` + `metric_hooks.py` | `evaluation/registry/` (déjà migré) | Vérifier callers |
|
| 76 |
+
| `core/corpus.py` (`Document`, `Corpus`, `GTLevel`) | `domain/corpus.py` + `domain/documents.py` (déjà migré) | Modèle différent — convertisseur |
|
| 77 |
+
| `core/diff_utils.py` | `evaluation/utils/diff.py` (à créer) | Pure utility |
|
| 78 |
+
| `core/xml_utils.py` | `formats/xml_utils.py` (à créer) | Pure utility |
|
| 79 |
+
| `core/metrics.py` | `evaluation/metrics/_base.py` (à créer) | API helpers |
|
| 80 |
+
|
| 81 |
+
**Effort** : 5-8 jours-personnes.
|
| 82 |
+
|
| 83 |
+
**Acceptance** : aucun fichier `picarones.{adapters,evaluation,pipeline,
|
| 84 |
+
app,reports_v2,interfaces}` n'importe plus `picarones.core`. Le
|
| 85 |
+
package `core/` peut être vidé en gardant uniquement des shims
|
| 86 |
+
`DeprecationWarning` (ou directement supprimé si aucun caller
|
| 87 |
+
externe ne le lit).
|
| 88 |
+
|
| 89 |
+
### Phase 2 — Statistics (`measurements/statistics/`)
|
| 90 |
+
|
| 91 |
+
**Modules** : `wilcoxon.py`, `friedman_nemenyi.py`, `bootstrap.py`,
|
| 92 |
+
`pareto.py`, `clustering.py`, `correlation.py`, `distributions.py`,
|
| 93 |
+
`cdd_render.py`.
|
| 94 |
+
|
| 95 |
+
**Cible** : nouveau sous-package `picarones/evaluation/statistics/`.
|
| 96 |
+
|
| 97 |
+
**Effort** : 5-7 jours. Code mathématique pur, pas de couplage
|
| 98 |
+
applicatif.
|
| 99 |
+
|
| 100 |
+
**Acceptance** : régression bit-for-bit sur les outputs des 8 tests
|
| 101 |
+
statistiques + le rendu CDD SVG.
|
| 102 |
+
|
| 103 |
+
### Phase 3 — Narrative engine (`measurements/narrative/`)
|
| 104 |
+
|
| 105 |
+
**Modules** : `arbiter.py`, `renderer.py`, `registry.py` + 18
|
| 106 |
+
détecteurs en 6 familles (`ranking/`, `pareto/`, `stratum/`,
|
| 107 |
+
`quality/`, `history/`, `ensemble/`) + 36 templates YAML FR/EN +
|
| 108 |
+
`core/facts.py`.
|
| 109 |
+
|
| 110 |
+
**Cible** : `picarones/reports_v2/narrative/` (le narratif est de
|
| 111 |
+
la **présentation**, pas du domaine — il vit du côté rapport, pas
|
| 112 |
+
de l'évaluation).
|
| 113 |
+
|
| 114 |
+
**Effort** : 8-12 jours. Le bloc le plus délicat — 18 détecteurs
|
| 115 |
+
chacun avec garde-fous anti-hallucination, arbitre de cohérence
|
| 116 |
+
inter-détecteurs, renderer Jinja par templates YAML.
|
| 117 |
+
|
| 118 |
+
**Acceptance** : régression bit-for-bit sur la synthèse rendue
|
| 119 |
+
pour chaque cas-test legacy `test_sprint{16,19,29,36,39,42,44,46,
|
| 120 |
+
51,56,73,81,90,92}_*.py` (les sprints qui ont introduit chaque
|
| 121 |
+
détecteur).
|
| 122 |
+
|
| 123 |
+
### Phase 4 — 35 mesures legacy (`measurements/*.py`)
|
| 124 |
+
|
| 125 |
+
**Modules** (ordre par cluster thématique, chaque cluster = un PR/sprint) :
|
| 126 |
+
|
| 127 |
+
#### 4.A — Philologique (8 modules) — 5 jours
|
| 128 |
+
|
| 129 |
+
`mufi.py`, `abbreviations.py`, `early_modern_typography.py`,
|
| 130 |
+
`modern_archives.py`, `roman_numerals.py`, `unicode_blocks.py`,
|
| 131 |
+
`equivalence_profile.py`, `philological_hooks.py`.
|
| 132 |
+
|
| 133 |
+
#### 4.B — NER + readability (6 modules) — 4 jours
|
| 134 |
+
|
| 135 |
+
`ner.py`, `ner_backends.py`, `readability.py`, `readability_hooks.py`,
|
| 136 |
+
`searchability.py`, `searchability_hooks.py`.
|
| 137 |
+
|
| 138 |
+
#### 4.C — Structurel (5 modules) — 3 jours
|
| 139 |
+
|
| 140 |
+
`reading_order.py`, `structure.py`, `alto_metrics.py`, `line_metrics.py`
|
| 141 |
+
(legacy), `numerical_sequences.py` + `numerical_sequences_hooks.py`.
|
| 142 |
+
|
| 143 |
+
#### 4.D — Robustness, reliability, history (4 modules) — 4 jours
|
| 144 |
+
|
| 145 |
+
`robustness.py`, `reliability.py`, `history.py`, `specialization.py`.
|
| 146 |
+
|
| 147 |
+
#### 4.E — Pipeline metrics (3 modules) — 3 jours
|
| 148 |
+
|
| 149 |
+
`pipeline_benchmark.py`, `pipeline_comparison.py`,
|
| 150 |
+
`pipeline_spec_loader.py` — opèrent sur le `PipelineSpec` legacy.
|
| 151 |
+
À fondre dans la couche `app/services/` du rewrite.
|
| 152 |
+
|
| 153 |
+
#### 4.F — Utility / hooks (9 modules) — 4 jours
|
| 154 |
+
|
| 155 |
+
`builtin_hooks.py`, `builtin_metrics.py`, `metrics.py`, `char_scores.py`,
|
| 156 |
+
`cost_projection.py`, `difficulty.py`, `taxonomy.py`,
|
| 157 |
+
`taxonomy_intra_doc.py`, `runner/*` (sous-package).
|
| 158 |
+
|
| 159 |
+
**Cible** : chaque mesure migre vers `evaluation/metrics/<thème>/`
|
| 160 |
+
ou un sous-package adapté. Enregistrement via la `MetricRegistry`
|
| 161 |
+
typée (signature `(input_type, output_type) → score`).
|
| 162 |
+
|
| 163 |
+
**Effort total Phase 4** : 23-28 jours.
|
| 164 |
+
|
| 165 |
+
**Acceptance** : régression bit-for-bit sur les ~5000 tests existants
|
| 166 |
+
qui pointent vers ces métriques.
|
| 167 |
+
|
| 168 |
+
### Phase 5 — Reports HTML (`report/`)
|
| 169 |
+
|
| 170 |
+
**Modules** :
|
| 171 |
+
|
| 172 |
+
- 22 renderers thématiques : `baseline_render.py`, `calibration_render.py`,
|
| 173 |
+
`difficulty_render.py`, `error_absorption_render.py`,
|
| 174 |
+
`image_predictive_render.py`, `incremental_comparison_render.py`,
|
| 175 |
+
`inter_engine_render.py`, `levers_render.py`,
|
| 176 |
+
`lexical_modernization_render.py`, `longitudinal_render.py`,
|
| 177 |
+
`marginal_cost_render.py`, `module_audit_render.py`,
|
| 178 |
+
`multirun_stability_render.py`, `ner_render.py`,
|
| 179 |
+
`numerical_sequences_render.py`, `philological_render.py`,
|
| 180 |
+
`pipeline_dag_render.py`, `pipeline_render.py`,
|
| 181 |
+
`rare_token_recall_render.py`, `readability_render.py`,
|
| 182 |
+
`robustness_projection_render.py`, `searchability_render.py`,
|
| 183 |
+
`specialization_render.py`, `stratification_render.py`,
|
| 184 |
+
`taxonomy_comparison_render.py`, `taxonomy_cooccurrence_render.py`,
|
| 185 |
+
`taxonomy_intra_doc_render.py`, `throughput_render.py`,
|
| 186 |
+
`worst_lines_render.py`.
|
| 187 |
+
- 5 vues : `views/{advanced_taxonomy,diagnostics,economics,
|
| 188 |
+
pipeline,robustness}.py`.
|
| 189 |
+
- `generator.py` (orchestrateur), `comparison.py`, `snapshot.py`,
|
| 190 |
+
`assets.py`, `colors.py`, `render_helpers.py`, `report_data/`.
|
| 191 |
+
- `templates/` (~10 fichiers Jinja2), `glossary/` (2 YAML, 25
|
| 192 |
+
entrées), `i18n/`, `vendor/`.
|
| 193 |
+
|
| 194 |
+
**Cible** : `picarones/reports_v2/html/views/<theme>.py` + helpers
|
| 195 |
+
partagés dans `reports_v2/html/_helpers/`. Glossaire dans
|
| 196 |
+
`reports_v2/html/glossary/`. Templates Jinja2 dans
|
| 197 |
+
`reports_v2/html/templates/`.
|
| 198 |
+
|
| 199 |
+
**Effort** : 12-18 jours.
|
| 200 |
+
|
| 201 |
+
**Acceptance** : régression bit-for-bit sur le HTML produit pour
|
| 202 |
+
les 3 corpus de référence. Aucun renderer legacy laissé.
|
| 203 |
+
|
| 204 |
+
### Phase 6 — Pipelines OCR+LLM (`pipelines/`)
|
| 205 |
+
|
| 206 |
+
**Modules** : `pipelines/base.OCRLLMPipeline` (3 modes), `pipelines/
|
| 207 |
+
over_normalization.detect_over_normalization`.
|
| 208 |
+
|
| 209 |
+
**Cible** :
|
| 210 |
+
|
| 211 |
+
- Les 3 modes deviennent des `PipelineSpec` YAML composés (OCR
|
| 212 |
+
adapter → LLM adapter avec `inputs_from`).
|
| 213 |
+
- `over_normalization` devient une métrique enregistrée dans
|
| 214 |
+
`evaluation/metrics/over_normalization.py`.
|
| 215 |
+
|
| 216 |
+
**Effort** : 3-5 jours.
|
| 217 |
+
|
| 218 |
+
**Acceptance** : les 3 callers internes (`web/benchmark_utils.py`,
|
| 219 |
+
`measurements/runner/document.py`, `fixtures.py`) consomment des
|
| 220 |
+
`PipelineSpec` YAML rewrite.
|
| 221 |
+
|
| 222 |
+
### Phase 7 — Modules officiels (`modules/`)
|
| 223 |
+
|
| 224 |
+
**Module** : `modules/alto_text_to_mono_region.TextToAltoMonoRegion`
|
| 225 |
+
(310 LOC) — baseline TEXT → ALTO.
|
| 226 |
+
|
| 227 |
+
**Cible** : `picarones/formats/alto/baseline_reconstruction.py` ou
|
| 228 |
+
`picarones/evaluation/projectors/text_to_alto.py` (selon où la
|
| 229 |
+
sémantique colle le mieux).
|
| 230 |
+
|
| 231 |
+
**Effort** : 1 jour.
|
| 232 |
+
|
| 233 |
+
### Phase 8 — Importers (`extras/importers/`)
|
| 234 |
+
|
| 235 |
+
**Modules** : `iiif.py`, `gallica.py`, `escriptorium.py`, `_http.py`,
|
| 236 |
+
`_fallback_log.py`.
|
| 237 |
+
|
| 238 |
+
**Cible** : `picarones/adapters/corpus/{iiif,gallica,escriptorium}.py`
|
| 239 |
+
+ helpers partagés dans `adapters/corpus/_http.py`.
|
| 240 |
+
|
| 241 |
+
**Effort** : 3-5 jours.
|
| 242 |
+
|
| 243 |
+
### Phase 9 — Web UI riche (`web/`)
|
| 244 |
+
|
| 245 |
+
**Modules** : 9 routers (`config`, `engines`, `history`, `home`,
|
| 246 |
+
`importers`, `normalization`, `reports`, `synthesis`, `system`) +
|
| 247 |
+
utilitaires (`benchmark_utils.py`, `engine_utils.py`,
|
| 248 |
+
`corpus_utils.py`, `config_utils.py`, `state.py`, `security.py`,
|
| 249 |
+
`models.py`, `jobs.py`, `maintenance.py`, `app.py`) + templates
|
| 250 |
+
Jinja2.
|
| 251 |
+
|
| 252 |
+
**Cible** : `picarones/interfaces/web/routers/<router>.py` + utils
|
| 253 |
+
partagés dans `interfaces/web/_utils/` + templates dans
|
| 254 |
+
`interfaces/web/templates/`.
|
| 255 |
+
|
| 256 |
+
**Effort** : 8-12 jours.
|
| 257 |
+
|
| 258 |
+
**Acceptance** : régression sur tous les `tests/web/test_sprint*.py`
|
| 259 |
+
existants. L'UI riche (sélecteur moteurs dynamique, gallery,
|
| 260 |
+
stratification, narrative inline, browse corpus) doit produire les
|
| 261 |
+
mêmes pages HTML.
|
| 262 |
+
|
| 263 |
+
### Phase 10 — CLI complète (`cli/`)
|
| 264 |
+
|
| 265 |
+
**Commandes** : 13 commandes legacy non couvertes (`metrics`,
|
| 266 |
+
`engines`, `info`, `demo`, `diagnose`, `economics`, `edition`,
|
| 267 |
+
`compare`, `import` group, `serve`, `history`, `robustness`,
|
| 268 |
+
`pipeline` group avec sous-commandes `run` et `compare`).
|
| 269 |
+
|
| 270 |
+
**Cible** : `picarones/interfaces/cli/<command>.py`. L'entry point
|
| 271 |
+
`console_scripts` du `pyproject.toml` doit pointer sur
|
| 272 |
+
`picarones.interfaces.cli:cli` (à la place de `picarones.cli:cli`).
|
| 273 |
+
|
| 274 |
+
**Effort** : 4-6 jours.
|
| 275 |
+
|
| 276 |
+
### Phase 11 — Retrait final + release 2.0
|
| 277 |
+
|
| 278 |
+
- Suppression des 10 packages legacy.
|
| 279 |
+
- Suppression des shims `DeprecationWarning` introduits aux phases
|
| 280 |
+
précédentes.
|
| 281 |
+
- Mise à jour du `pyproject.toml` (`console_scripts`,
|
| 282 |
+
`[project.urls]`).
|
| 283 |
+
- Rédaction du CHANGELOG 2.0 final avec liste exhaustive des
|
| 284 |
+
breaking changes (les utilisateurs externes ont eu
|
| 285 |
+
`DeprecationWarning` à chaque phase).
|
| 286 |
+
- Génération SBOM + signature SLSA Level 3 (cf.
|
| 287 |
+
`docs/operations/supply-chain.md`).
|
| 288 |
+
- Bump `_version.py` et tag `v2.0.0`.
|
| 289 |
+
|
| 290 |
+
**Effort** : 3-5 jours.
|
| 291 |
+
|
| 292 |
+
## Estimation totale
|
| 293 |
+
|
| 294 |
+
| Phase | Effort min | Effort max |
|
| 295 |
+
|-------|------------|------------|
|
| 296 |
+
| 0 | 2 j | 3 j |
|
| 297 |
+
| 1 | 5 j | 8 j |
|
| 298 |
+
| 2 | 5 j | 7 j |
|
| 299 |
+
| 3 | 8 j | 12 j |
|
| 300 |
+
| 4 | 23 j | 28 j |
|
| 301 |
+
| 5 | 12 j | 18 j |
|
| 302 |
+
| 6 | 3 j | 5 j |
|
| 303 |
+
| 7 | 1 j | 1 j |
|
| 304 |
+
| 8 | 3 j | 5 j |
|
| 305 |
+
| 9 | 8 j | 12 j |
|
| 306 |
+
| 10 | 4 j | 6 j |
|
| 307 |
+
| 11 | 3 j | 5 j |
|
| 308 |
+
| **Total** | **77 j** | **110 j** |
|
| 309 |
+
|
| 310 |
+
Soit **3,5 à 5 mois** d'effort focalisé en mode développeur unique.
|
| 311 |
+
Aucune contrainte de date — on livre quand c'est propre.
|
| 312 |
+
|
| 313 |
+
## Stratégie de régression — invariant non négociable
|
| 314 |
+
|
| 315 |
+
À chaque phase :
|
| 316 |
+
|
| 317 |
+
1. **Avant** : exécuter le harness legacy sur 3 corpus de référence
|
| 318 |
+
(small / medium / large) → capture des outputs en JSON / HTML
|
| 319 |
+
bit-for-bit.
|
| 320 |
+
2. **Pendant** : réécrire la fonctionnalité dans le rewrite.
|
| 321 |
+
3. **Après** : exécuter le harness rewrite et **diff** vs. snapshot
|
| 322 |
+
legacy.
|
| 323 |
+
4. **Tolérance** : explicite par métrique dans
|
| 324 |
+
`docs/migration/regression-tolerances.md`. Tout écart non
|
| 325 |
+
tolerance = régression à corriger avant merge.
|
| 326 |
+
|
| 327 |
+
Cela évite le piège classique du rewrite : *« ça compile, ça tourne,
|
| 328 |
+
mais le CER a glissé de 0,002 par doc »*.
|
| 329 |
+
|
| 330 |
+
## Anti-bricolage — règles
|
| 331 |
+
|
| 332 |
+
1. **Pas de double API** : pendant la migration d'un module, on ne
|
| 333 |
+
garde **pas** le legacy en parallèle dans le code de production.
|
| 334 |
+
Soit on importe l'ancien, soit le nouveau. Le harness de
|
| 335 |
+
régression suffit pour valider.
|
| 336 |
+
2. **Pas de shim sans date de retrait** : tout `DeprecationWarning`
|
| 337 |
+
introduit doit être inscrit dans le CHANGELOG avec date de
|
| 338 |
+
retrait (la 2.0).
|
| 339 |
+
3. **Pas de TODO dans le code mergé** : un TODO = une issue ouverte
|
| 340 |
+
référencée par numéro.
|
| 341 |
+
4. **Pas de copié-collé** : si une logique apparaît dans deux
|
| 342 |
+
modules, extraire en helper partagé dès la deuxième occurrence.
|
| 343 |
+
5. **Pas de god-module** : `tests/architecture/test_file_budgets.py`
|
| 344 |
+
reste l'autorité.
|
| 345 |
+
|
| 346 |
+
## Statut
|
| 347 |
+
|
| 348 |
+
| Phase | Statut |
|
| 349 |
+
|-------|--------|
|
| 350 |
+
| 0 | 🟡 En cours |
|
| 351 |
+
| 1-11 | ⚪ À démarrer |
|
| 352 |
+
|
| 353 |
+
**Dernière mise à jour** : 2026-05.
|