diff --git a/CLAUDE.md b/CLAUDE.md
index 71427fe..e70e5eb 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -72,30 +72,40 @@ conversione/
├── deps.py # _check_deps() — verifica opendataloader-pdf e Java
├── checker.py # check_pdf() — validazione PDF
├── converter.py # convert_pdf() — wrapper opendataloader
- ├── transforms.py # apply_transforms() + tutte le _t_* (~920 righe)
├── structure.py # analyze() + rilevamento lingua e struttura
├── report.py # build_report() → report.json
├── runner.py # run() — orchestrazione 4 fasi
- └── validator.py # validate() + _score() + _grade()
+ ├── validator.py # validate() + _score() + _grade()
+ └── transforms/ # Package pulizia strutturale
+ ├── __init__.py # Espone apply_transforms()
+ ├── _apply.py # Orchestratore: lista _transforms in ordine semantico
+ ├── _constants.py# Regex compilate e mappe statiche condivise
+ ├── _encoding.py # Gruppo 1: PUA font Symbol, accenti LaTeX, simboli SI
+ ├── _artifacts.py# Gruppo 2: immagini, BR, footnote, URL, righe ricorrenti, watermark
+ ├── _headers.py # Gruppo 3: normalizzazione livelli, concat, bold, ALLCAPS
+ ├── _structure.py# Gruppo 4: TOC, ALLCAPS→##, sezioni numerate, math, articoli
+ ├── _text.py # Gruppo 5: merge paragrafi, whitespace, poesia, versi
+ ├── _finish.py # Gruppo 6: header vuoti/garbage, formula-header, frontmatter
+ └── _helpers.py # Funzioni pure condivise (es. _sentence_case)
```
### `__main__.py` — entry point unificato
CLI con due modalità: conversione (default, `--stem`, `--force`) e validazione (subcommand `validate`, con stems opzionali e `--detail`). Aggiunge `conversione/` a `sys.path` e delega a `_pipeline`. Uso: `python conversione/ [--stem X] [--force]` oppure `python conversione/ validate [X] [--detail]`.
-### `_pipeline/transforms.py` — cuore della pipeline
+### `_pipeline/transforms/` — cuore della pipeline
-Contiene ~35 trasformazioni atomiche (`_t_*`) e l'orchestratore `apply_transforms(text) -> (text, stats)`. Le trasformazioni sono tenute in un unico file perché hanno dipendenze incrociate dense e il loro **ordine è semantico** — non modificarlo senza capire le dipendenze.
+Package con ~35 trasformazioni atomiche (`_t_*`). L'orchestratore `_apply.py::apply_transforms(text) -> (text, stats)` le chiama in ordine semantico fisso — **non modificare l'ordine senza capire le dipendenze tra gruppi**.
-Ordine logico dei gruppi (non separare):
-1. **Encoding** — PUA font Symbol, accenti backtick LaTeX, moltiplicazione, micro
-2. **Pulizia artefatti** — immagini, `
`, footnote superscript, URL, box symbol, righe ricorrenti, watermark
-3. **Struttura header** — fix header+body concatenati, Capitolo inline, normalizzazione livelli numerati, `####`→`###`, bold, ALL-CAPS
-4. **Costruzione struttura** — TOC rimosso, ALL-CAPS→`##`, sezioni numerata→`###`, ambienti matematici, articoli
-5. **Testo** — merge paragrafi spezzati, whitespace, blank lines, poesia, versi
-6. **Rifinitura** — header vuoti, garbage header, merge titoli isolati, frontmatter
+Ogni `_t_*` vive nel modulo corrispondente al suo gruppo. Le costanti regex (compilate una volta) stanno tutte in `_constants.py` e vengono importate dove servono.
-Costanti di modulo (compilate una volta): `_SYMBOL_PUA_MAP`, `_SYMBOL_PUA_RE`, `_TABSEP_RE`, `_FM_RE`, `_VERSE_NUM_RE`, `_NUMBERED_HDR_RE`, `_BIB_MARKERS_RE`, `_WATERMARK_RE`, `_SUPERSCRIPT_RE`.
+Ordine logico dei gruppi in `_apply.py`:
+1. **Encoding** (`_encoding.py`) — PUA font Symbol, accenti backtick LaTeX, moltiplicazione, micro
+2. **Pulizia artefatti** (`_artifacts.py`) — immagini, `
`, footnote superscript, URL, box symbol, righe ricorrenti, watermark
+3. **Struttura header** (`_headers.py`) — fix header+body concatenati, Capitolo inline, normalizzazione livelli numerati, `####`→`###`, bold, ALL-CAPS
+4. **Costruzione struttura** (`_structure.py`) — TOC rimosso, ALL-CAPS→`##`, sezioni numerate→`###`, ambienti matematici, articoli
+5. **Testo** (`_text.py`) — merge paragrafi spezzati, whitespace, blank lines, poesia, versi
+6. **Rifinitura** (`_finish.py`) — header vuoti, garbage header, merge titoli isolati, frontmatter
Flag automatico: se il testo contiene "Esercizi/Problems/Homework", `_t_numbered_sections` non converte `- N. testo` in header (sono numerazioni di esercizi, non titoli).
@@ -146,8 +156,8 @@ Assegna un voto 0–100 (A/B/C/D/F) leggendo `report.json`. Penalità principali
Quando si aggiunge una trasformazione in `apply_transforms()`:
- Ogni `_t_*` deve restituire `(testo, n_modifiche)` — il contatore alimenta `report.json`.
-- Aggiungere la coppia `("stat_key", _t_nuova)` nella lista `_transforms` nel punto logicamente corretto (rispettare i gruppi sopra).
-- Compilare i pattern regex a livello di modulo come costanti, non dentro la funzione.
+- Implementare la funzione nel modulo del gruppo corretto (`_encoding.py`, `_artifacts.py`, ecc.), importarla in `_apply.py` e inserire la coppia `("stat_key", _t_nuova)` nella lista `_transforms` nel punto logicamente corretto.
+- Compilare i pattern regex in `_constants.py` come costanti di modulo, non dentro la funzione.
- Testare con `.venv/bin/python conversione/ --stem --force` e confrontare `report.json`.
- Un nuovo tipo di artefatto: prima aggiungerlo come residuo in `report.py` (`_scan`), poi implementare la `_t_*` che lo rimuove.