Claude commited on
Commit
a7ee8c2
·
unverified ·
1 Parent(s): ed02e58

docs(audit): Phase 12 — CHANGELOG v2.0 (Unreleased) — synthèse audit

Browse files

Section ``[Unreleased] — Audit code-quality (mai 2026)`` qui
récapitule les 12 phases du chantier audit, documente les **5
ruptures API v2.0** sans dépréciation, et liste les invariants CI
mis en place.

Format ``Keep a Changelog`` : BREAKING / Added / Changed / Removed
/ Fixed / Security / Stats.

Ruptures API documentées (avec exemples de migration) :

1. ``POST /api/benchmark/start`` retiré → ``/api/benchmark/run``
2. ``BenchmarkRequest`` retiré → ``BenchmarkRunRequest``
3. ``max_workers`` retiré → ``CorpusRunner.max_in_flight``
4. ``expand_legacy_keys`` retiré (JSON pré-v2.0 invalidés)
5. Paramètres morts (``text_hint``)

Features inachevées débloquées (3) :
- Sur-normalisation agrégée corpus-wide
- Journal de fallbacks importer end-to-end
- Profils de normalisation YAML versionnables (CLI + API)

Architecture & lisibilité (8 changements majeurs) :
- 8 ``__init__.py`` de couche renumérotés
- ``benchmark_runner.py`` : 1700 → 1584 LOC
- ``robustness.py`` : 850 → 578 LOC
- ``PipelineMode`` unifié (3 définitions → 1)
- Validation chemin web factorisée
- eScriptorium anti-SSRF
- Tesseract injection CLI bloquée
- 7 nouveaux tests d'architecture (ratchet)

Stats : +158 tests (4 686 → 4 784), ~600 LOC mortes supprimées,
12 nouveaux modules créés.

Ruff propre, bandit 0 MEDIUM, pip-audit vert, 0 régression.

La section ``[Unreleased] — Chantier post-rewrite`` historique
reste juste en dessous pour préserver le récit chronologique.
Cible : promotion en ``[2.0.0]`` au prochain tag git.

Files changed (1) hide show
  1. CHANGELOG.md +189 -0
CHANGELOG.md CHANGED
@@ -7,6 +7,195 @@ La numérotation de version suit [Semantic Versioning](https://semver.org/lang/f
7
 
8
  ---
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  ## [Unreleased] — Chantier post-rewrite (mai 2026)
11
 
12
  Branche `claude/fix-module-rewiring-MHssX` — réconciliation des chemins
 
7
 
8
  ---
9
 
10
+ ## [Unreleased] — Audit code-quality (mai 2026)
11
+
12
+ Branche `claude/code-quality-audit-EeY0r` — audit implacable du repo
13
+ suite à la migration v2.0, suivi de 12 sprints correctifs (Phases 0
14
+ à 12 du plan d'audit).
15
+
16
+ ### BREAKING — ruptures API v2.0
17
+
18
+ Toutes ces ruptures sont **immédiates**, sans calendrier de
19
+ dépréciation (règle de l'audit : « soit on supprime, soit on garde
20
+ et on développe »). Migration directe documentée ci-dessous.
21
+
22
+ - **`POST /api/benchmark/start` retiré** au profit de
23
+ `POST /api/benchmark/run`. Le modèle Pydantic
24
+ `BenchmarkRequest` (liste de moteurs plats) est remplacé par
25
+ `BenchmarkRunRequest` (liste de `PipelineConfig`). Helpers
26
+ associés supprimés : `_legacy_request_to_run_request`,
27
+ `run_benchmark_thread` (v1 ; `run_benchmark_thread_v2` reste).
28
+ Migration :
29
+
30
+ ```python
31
+ # avant (v1.x)
32
+ POST /api/benchmark/start
33
+ {"corpus_path": "...", "engines": ["tesseract", "pero_ocr"]}
34
+
35
+ # après (v2.0)
36
+ POST /api/benchmark/run
37
+ {
38
+ "corpus_path": "...",
39
+ "competitors": [
40
+ {"name": "tesseract", "engine_name": "tesseract"},
41
+ {"name": "pero_ocr", "engine_name": "pero_ocr"},
42
+ ],
43
+ }
44
+ ```
45
+
46
+ - **`run_benchmark_via_service(..., max_workers=4)` retiré** —
47
+ paramètre absorbé sans effet via `# noqa: ARG001`. Le rewrite
48
+ passe par `CorpusRunner.max_in_flight` directement.
49
+
50
+ - **`expand_legacy_keys()` retiré** de `picarones.domain.artifacts` —
51
+ 0 caller en production. Le dict `LEGACY_VALUE_ALIASES` reste
52
+ vivant pour le canonicalisation des manifests legacy.
53
+
54
+ - **JSON `BenchmarkResult` pré-v2.0 plus relisibles** — le retrait
55
+ de `expand_legacy_keys` interdit le round-trip depuis des sorties
56
+ v1.x. Régénérer les benchmarks de référence.
57
+
58
+ - **Paramètre `text_hint` retiré** de
59
+ `picarones.evaluation.synthetic._make_placeholder_png` (était
60
+ jamais lu).
61
+
62
+ ### Added — features inachevées débloquées
63
+
64
+ - **Agrégation sur-normalisation LLM corpus-wide** :
65
+ `aggregate_over_normalization` câblée via
66
+ `@register_corpus_aggregator(name="over_normalization", ...)`
67
+ (profils `philological`, `diagnostics`, `full`). Le hook
68
+ extrait depuis `DocumentResult.pipeline_metadata["over_normalization"]`
69
+ et alimente `EngineReport.aggregated_over_normalization`.
70
+ Round-trip JSON préservé.
71
+
72
+ - **Journal de fallbacks importer end-to-end** : la chaîne
73
+ `record_fallback → consume_fallback_log → BenchmarkResult.metadata
74
+ → build_report_data → narrative.detect_importer_fallback` est
75
+ désormais branchée. Un fallback HTR-United mode démo apparaît
76
+ dans la synthèse narrative avec traçabilité (URL, raison).
77
+
78
+ - **Profils de normalisation YAML versionnables** :
79
+ - CLI : `picarones run --normalization-profile <ID-OR-PATH>` —
80
+ accepte un identifiant builtin ou un fichier `.yaml`
81
+ versionné dans git.
82
+ - API : `POST /api/normalization/profiles/preview` — valide un
83
+ YAML utilisateur et retourne le profil sérialisé (preview, pas
84
+ de persistance). Limite 64 KiB côté Pydantic.
85
+
86
+ - **`register_default_metrics()` exposée publiquement** —
87
+ remplace le side-effect `import picarones.evaluation.metrics`
88
+ opaque en tête de `picarones/__init__.py`. Idempotente
89
+ (`sys.modules` cache). Auto-déclenchement préservé pour
90
+ rétrocompat.
91
+
92
+ - **`RunResult` accessible depuis `picarones.pipeline.run_result`** —
93
+ déplacé de `app.results` (compat shim conservé) vers la couche 4
94
+ pour respecter l'orientation des couches (`reports/` ne peut
95
+ plus importer depuis `app/`).
96
+
97
+ ### Changed — architecture & lisibilité
98
+
99
+ - **8 `__init__.py` de couche** : renumérotation `Cercle N`
100
+ (incohérent, max 5, doublons) → `Couche N` (1 à 8, ordre du
101
+ manifeste).
102
+ - **`benchmark_runner.py`** : 1 700 → 1 584 LOC. Extractions :
103
+ - `_benchmark_ner.py` (NER aggregation, ~100 LOC).
104
+ - `_benchmark_persistence.py` (sérialisation JSON, ~15 LOC).
105
+ Budget `test_file_budgets` resserré de 1 750 à 1 620.
106
+ - **`robustness.py`** : 850 → 578 LOC. Suppression des 5 helpers
107
+ pure-Python `_apply_*` et du stub `_degrade_pure_python` (Pillow
108
+ est dep obligatoire, fallback sans valeur).
109
+ - **`PipelineMode` unifié** : source unique
110
+ `picarones.domain.pipeline_spec.PipelineMode`. Les 3 alias
111
+ historiques (`OCRLLMMode`, `OCRLLMPipelineMode`, `PipelineMode`)
112
+ deviennent des re-exports.
113
+ - **Validation chemin web factorisée** : helpers
114
+ `validated_user_path` / `validated_user_output_dir` dans
115
+ `interfaces/web/_path_helpers.py` ; 2 routers migrés.
116
+ - **eScriptorium anti-SSRF** : `_get`, `_post` et le téléchargement
117
+ d'image utilisent désormais `validate_http_url` et
118
+ `download_url` (cohérence avec IIIF/Gallica/HTR-United).
119
+ - **Tesseract `lang` validation** : regex
120
+ `^[a-zA-Z]{3,}(\+[a-zA-Z]{3,})*$` rejette les injections CLI
121
+ (`fra --user-words /etc/passwd`).
122
+ - **CI : sync compteurs bloquant** — nouveau job ``sync-counters``
123
+ exécute `scripts/gen_readme_tables.py --check`.
124
+ - **CI : 7 nouveaux tests d'architecture** verrouillent les
125
+ invariants pour bloquer la régression :
126
+ - `test_no_zombie_skips.py` (interdit `pytest.skip` sur dep
127
+ obligatoire).
128
+ - `test_no_legacy_imports_in_rewrite.py` refondu — test actif
129
+ contre la résurrection des paquets legacy supprimés (au lieu
130
+ d'un `LEGACY_PACKAGES = ()` vacuement vrai).
131
+ - `test_no_broad_pytest_raises.py` — refuse les
132
+ `pytest.raises(Exception)` (ratchet, baseline 24).
133
+ - `test_logger_prefix.py` — refuse les logs sans préfixe
134
+ `[<module>]` (ratchet, baseline 46).
135
+ - `test_live_test_markers.py` — chaque fonction dans
136
+ `tests/integration/live/` porte `@pytest.mark.live`.
137
+ - `test_reports_layer_strict.py` — interdit les imports
138
+ `reports/ → {adapters, app, interfaces}`.
139
+ - `test_pipeline_mode_single_source.py` — refuse toute nouvelle
140
+ redéfinition de `Literal["text_only", "text_and_image", "zero_shot"]`.
141
+ - `test_api_stable_modules_exist.py` — chaque module cité dans
142
+ `api-stable.md` doit s'importer.
143
+
144
+ ### Removed
145
+
146
+ - **`POST /api/benchmark/start`** + helpers (cf. BREAKING).
147
+ - **`BenchmarkRequest`** modèle Pydantic v1 (cf. BREAKING).
148
+ - **`max_workers`**, **`text_hint`** paramètres morts.
149
+ - **`expand_legacy_keys()`** (cf. BREAKING).
150
+ - **5 helpers `_apply_*`** + **`_degrade_pure_python`** dans
151
+ robustness.py (~300 LOC).
152
+ - **7 `pytest.skip("click non installé")`** zombies dans
153
+ `tests/integration/test_chantier{4,5}.py` (click est dep
154
+ obligatoire — skip vacuement vrai).
155
+ - **4 modules fantômes** retirés de `docs/reference/api-stable.md`
156
+ (`pipeline.legacy_runner`, `pipeline.legacy_pipeline_benchmark`,
157
+ `pipeline.legacy_pipeline_comparison`,
158
+ `evaluation.metrics.pipeline_spec_loader`).
159
+
160
+ ### Fixed
161
+
162
+ - **`app.py`** : entry point HuggingFace cassé
163
+ (`picarones.web.app:app` → `picarones.interfaces.web.app:app`).
164
+ - **8 `except: pass` silencieux** : tous remplacés par
165
+ `logger.warning("[<module>] ...")` ou `logger.debug(...)` selon
166
+ criticité (friedman_nemenyi, image_quality, iiif, clustering,
167
+ path_security, benchmark_runner, job_store, robustness).
168
+ - **10 `pytest.raises(Exception)` trop larges** : précisés en
169
+ `FrozenInstanceError` ou `pydantic.ValidationError`.
170
+ - **README.md** : retrait du paragraphe « Legacy paths still
171
+ present as shims » (faux depuis v2.0) ; `mypy picarones/core/`
172
+ → `mypy picarones/domain/`.
173
+ - **CLAUDE.md** : compteur tests synchronisé (4 700 → réel),
174
+ auto-contradiction « 12 vs 9 skipped » résolue, 18 → 20
175
+ détecteurs, 22 → 28 renderers.
176
+ - **Faux positifs bandit B608** documentés avec `# nosec` et
177
+ commentaire de justification (sites SQL où les `fields`
178
+ interpolés sont des littéraux internes, valeurs via `?`).
179
+ - **eScriptorium** : urlretrieve sans validation → `download_url`
180
+ avec anti-SSRF.
181
+
182
+ ### Security
183
+
184
+ - **SSRF résiduel eScriptorium** fermé (`_get`, `_post`,
185
+ téléchargement images).
186
+ - **Injection CLI Tesseract** bloquée (regex sur `lang`).
187
+ - **Aucune CVE** introduite (pip-audit vert).
188
+
189
+ ### Stats
190
+
191
+ - **+158 nouveaux tests** (4 686 → 4 784 passing). Ruff propre,
192
+ bandit propre (1 LOW résiduel inoffensif), 0 régression.
193
+ - **12 nouveaux modules** créés (helpers extraits + tests
194
+ d'invariant).
195
+ - **~600 LOC mortes supprimées**.
196
+
197
+ ---
198
+
199
  ## [Unreleased] — Chantier post-rewrite (mai 2026)
200
 
201
  Branche `claude/fix-module-rewiring-MHssX` — réconciliation des chemins