2101b9b2c9
Erano fermi allo snapshot V2.0.0 di fine aprile: parlavano di Fasi rev04 da iniziare, di quattro test rotti e di uno stack che scaricava le librerie da CDN. Niente di tutto questo è più vero, e un documento di stato sbagliato è peggio di un documento di stato assente — qualcuno ci si fida. STATO_PROGETTO: i quindici punti con il loro stato e dove vivono nel codice, cosa è entrato in V3.0.0 raggruppato per tema, le dieci migrazioni, la suite a 360 pass. In fondo la sezione che conta di più, «cosa non è stato provato»: tredici punti sono in esercizio e nessuno li ha percorsi su un tablet, il punto 14 non è mai stato riprodotto su un dispositivo, il punto 12 non è mai stato provato a rete staccata. Serve a non confondere «i test passano» con «funziona in reparto». ROADMAP: riscritta intorno a quello che resta e in che ordine — il collaudo prima di tutto, perché è l'unico lavoro che non aspetta risposte da nessuno, con la tabella di cosa guardare e come si vede che è giusto. Poi il punto 4 (due o tre giorni, a decisioni chiuse), l'innesto GAIA e l'installazione di settembre. Le decisioni aperte sono ora le D-1…D-9 del documento del 28/07, non più le D-0.x di aprile: la corrispondenza è scritta, così chi torna sui vecchi documenti non si perde. Stessa cosa per le sette Fasi rev04, con dove è finita ciascuna: due assorbite, una sostituita, una ridimensionata dal fatto che una rete isolata rende l'aggiornamento automatico privo di senso. Lo snapshot V2.0.0 è conservato in docs/archive/ invece di essere sovrascritto. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
193 lines
10 KiB
Markdown
193 lines
10 KiB
Markdown
# Stato Progetto TieMeasureFlow — V3.0.0
|
||
|
||
> Snapshot al 2026-07-28. Aggiornare ad ogni milestone.
|
||
> Lo snapshot V2.0.0 è in [`../archive/STATO_PROGETTO_V2.0.0_2026-04-27.md`](../archive/STATO_PROGETTO_V2.0.0_2026-04-27.md).
|
||
|
||
## Versione corrente
|
||
|
||
**V3.0.0** (in sviluppo, branch `V3.0.0`). Versione precedente: `V2.0.0`.
|
||
|
||
In esercizio su `tieflow.tielogic.xyz` (Docker Compose + Traefik + Let's Encrypt),
|
||
schema alla migrazione `010_meas_authorisation`.
|
||
|
||
## Sintesi esecutiva
|
||
|
||
V2.0.0 aveva chiuso stazioni per-tablet, ristrutturazione monorepo con `uv` e la
|
||
tenuta multi-tablet. **V3.0.0 nasce dal sopralluogo del 28/07 e lavora sui quindici
|
||
punti** raccolti in [`TieMeasureFlow_modifiche_2026-07-28.md`](../../TieMeasureFlow_modifiche_2026-07-28.md).
|
||
|
||
Il filo che li tiene insieme: fino a V2.0.0 il sistema *registrava* misure ma non
|
||
*governava* la produzione. Le regole vivevano nell'interfaccia — una modale che si
|
||
poteva chiudere, un timer che moriva al cambio pagina, un tipo di task dedotto dalla
|
||
presenza di quote. V3.0.0 le sposta sul server, dove non si aggirano.
|
||
|
||
Tredici punti su quindici sono fatti e in esercizio. Uno è fermo sulle risposte del
|
||
cliente, uno è fuori offerta.
|
||
|
||
## I quindici punti
|
||
|
||
| # | Punto | Stato | Dove vive |
|
||
|---|---|---|---|
|
||
| 1 | Memoria della produzione in corso | ✅ | `production_runs`, `production_service` |
|
||
| 2 | Tipo di task esplicito | ✅ | `RecipeTask.task_type`, migrazione 007 |
|
||
| 3 | Loop di misura e ripetizione | ✅ | `production-clock.js`, `production_service`, migrazione 008 |
|
||
| 4 | Limite di tentativi prima del capoturno | ⛔ **Fermo su D-6, D-7, D-9** | — |
|
||
| 5 | Avanzamento solo se in tolleranza | ✅ | `measurement_service.pending_authorisation`, migrazione 010 |
|
||
| 6 | Fermo linea e Fine produzione con effetto | ✅ | `production_service`, `production_export_service` |
|
||
| 7 | Gestione delle stazioni | ✅ | `station_service`, `/admin/stations` |
|
||
| 8 | Tracciabilità obbligatoria | ✅ | `Recipe.requires_lot/serial`, migrazione 009 |
|
||
| 9 | Blocco dell'inserimento manuale | ✅ | `Recipe.allow_manual_input`, `numpad.js` |
|
||
| 10 | Interfaccia operatore: sequenza e conferme | ✅ | `/measure/start`, `task-progress`, `task_list`, `task_execute` |
|
||
| 11 | Formattazione delle descrizioni | ✅ | filtro `rich_text`, `rich-text.js` |
|
||
| 12 | Funzionamento senza internet | ✅ | `static/vendor/`, CSP in `app.py` |
|
||
| 13 | Generazione task dalla scheda tecnica con l'AI | — **Fuori offerta** | — |
|
||
| 14 | Stabilità del layout | ✅ | `.tmf-page` in `themes.css`, [`LAYOUT.md`](LAYOUT.md) |
|
||
| 15 | Statistica separata dalla registrazione | ✅ | `@role_required("Metrologist")`, test dedicato |
|
||
|
||
## Cosa è entrato in V3.0.0
|
||
|
||
### Produzione come stato, non come pulsante (punti 1, 6)
|
||
|
||
- Tabelle `production_runs` e `production_events`; una produzione si apre su una
|
||
stazione, accumula eventi (`start`, `cycle_completed`, `task_measured`,
|
||
`remeasure`, `line_stop`, `resume`, `close`) e si chiude.
|
||
- Fermo linea, ripresa e fine produzione richiedono le credenziali del capoturno e
|
||
**cambiano lo stato**: prima erano modali che si chiudevano.
|
||
- La fine produzione emette il file di statistica dell'intera produzione
|
||
(`production_export_service`), CSV con i delimitatori configurati, marcando le
|
||
misure come esportate. Il punto d'innesto verso GAIA è lì accanto, in attesa di
|
||
D-1 e D-2.
|
||
|
||
### Il ciclo di misura (punto 3)
|
||
|
||
- L'intervallo della ricetta è calcolato dal server (`seconds_to_next_measurement`,
|
||
`overdue`, `server_time`): sopravvive al cambio task, che è un ricaricamento di
|
||
pagina, e non si azzera cambiando schermata.
|
||
- Il conteggio **prosegue oltre lo zero**: il ritardo si vede, in rosso.
|
||
- A scadenza l'operatore viene riportato al primo task di misura della ricetta, con
|
||
cinque secondi di preavviso; chi sta già misurando viene lasciato finire.
|
||
- Cicalino via WebAudio, nessun file audio da caricare.
|
||
- `remeasure` distingue una seconda lettura dello stesso pezzo da un ciclo nuovo:
|
||
girare il pezzo e rimisurare non deve far ripartire l'intervallo.
|
||
|
||
### Le regole della ricetta (punti 8, 9)
|
||
|
||
- `requires_lot`, `requires_serial`, `allow_manual_input` per ricetta, verificati in
|
||
`measurement_service`: **ogni porta d'ingresso passa di lì**, barcode compreso.
|
||
- Con `allow_manual_input` a falso il tastierino non viene disegnato — non nascosto:
|
||
il markup non esiste — e il server rifiuta comunque un valore digitato a mano.
|
||
- Il rilevamento del calibro USB è stato reso più tollerante (Enter veloce), perché
|
||
con la regola stretta una lettura corta come `9.5` veniva scambiata per digitazione.
|
||
|
||
### Il fuori tolleranza (punto 5)
|
||
|
||
- Una quota fuori tolleranza e non autorizzata **blocca** il salvataggio della quota
|
||
successiva e la chiusura del ciclo (409).
|
||
- Conta l'**ultima** lettura di ogni quota: rimisurare la stessa quota resta
|
||
possibile — il calibro scivola, il pezzo si riposiziona — e una lettura dentro i
|
||
limiti scioglie il blocco senza chiamare nessuno.
|
||
- L'autorizzazione del capoturno resta scritta sulla misura (`supervisor_id`,
|
||
`authorised_at`) e finisce in due colonne del CSV di produzione: il file mostra il
|
||
guasto **e** la decisione.
|
||
- Quanti tentativi siano ammessi è il punto 4, non questo.
|
||
|
||
### L'interfaccia dell'operatore (punti 10, 11, 14, 15)
|
||
|
||
- La ricetta si apre sul primo task (`/measure/start/<id>`), non su un elenco. La
|
||
lista scende a secondo livello e mostra quali task sono rimasti incompiuti
|
||
(`GET /api/measurements/task-progress`, contato per quota e non per tentativo).
|
||
- «Fine ciclo misura» è visibile da subito e spento finché mancano quote, con
|
||
scritto quante ne mancano.
|
||
- Descrizioni con `**grassetto**` e a capo: marcatura, non HTML, così la
|
||
sanificazione è per costruzione.
|
||
- Una cornice sola per tutte le viste, allineata alla navbar — vedi [`LAYOUT.md`](LAYOUT.md).
|
||
- Nessun percorso dell'operatore porta alla statistica, ed è un test.
|
||
|
||
### Fuori dalla rete (punto 12)
|
||
|
||
- Alpine, Plotly, PDF.js (+ worker), Fabric e i font sono nell'installazione, con
|
||
versione nel nome e impronta SHA-256 verificata da un test.
|
||
- Content-Security-Policy a sola origine locale servita **dal client Flask**: prima
|
||
esisteva solo sul backend, cioè sulle risposte API e non sulle pagine.
|
||
- Tailwind fissato a `3.4.19` nel `Dockerfile.frontend`.
|
||
|
||
## Migrazioni
|
||
|
||
| # | Contenuto |
|
||
|---|---|
|
||
| 001–004 | image_path, stazioni, intervallo di misura + auto-logout, `input_duration_ms` |
|
||
| 005–006 | `production_runs`, misure legate alla produzione |
|
||
| 007 | `task_type` dichiarato |
|
||
| 008 | tipi di evento del ciclo + `task_id` sull'evento |
|
||
| 009 | `requires_lot`, `requires_serial`, `allow_manual_input` |
|
||
| 010 | `supervisor_id`, `authorised_at` sulla misura |
|
||
|
||
Le 008 e 010 usano il *batch mode* per compatibilità SQLite (i test) e sono state
|
||
verificate anche in resa MySQL (`alembic upgrade X:Y --sql`) prima del deploy.
|
||
|
||
La 009 imposta `allow_manual_input = 1` sulle ricette esistenti: conserva il
|
||
comportamento in essere invece di irrigidire di colpo ricette già in uso. È una
|
||
scelta, e va confermata dal cliente ricetta per ricetta.
|
||
|
||
## Test status
|
||
|
||
| | Test | Fail |
|
||
|---|---|---|
|
||
| Backend (`src/backend/tests/`) | 212 | 0 |
|
||
| Frontend (`src/frontend/flask_app/tests/`) | 148 | 0 |
|
||
| **Totale** | **360** | **0** |
|
||
|
||
I quattro fallimenti pre-esistenti tracciati nello snapshot V2.0.0 non ci sono più.
|
||
|
||
Tre file di test non renderizzano niente e leggono i sorgenti, perché guardano
|
||
proprietà che sopravvivono solo se qualcuno le controlla:
|
||
|
||
| File | Cosa impedisce |
|
||
|---|---|
|
||
| `test_offline.py` | Una libreria caricata dalla rete, un worker PDF.js lasciato sul CDN, una libreria sostituita senza aggiornare l'impronta |
|
||
| `test_layout_shell.py` | Una vista che torna a dichiararsi la propria larghezza |
|
||
| `test_template_js_syntax.py` | Una traduzione con l'apostrofo dentro una stringa JS a virgolette singole, che spegne Alpine su tutta la pagina |
|
||
|
||
## Cosa non è stato provato
|
||
|
||
Va detto perché non si confonda «i test passano» con «funziona in reparto».
|
||
|
||
- **Il collaudo sul campo non è mai stato percorso.** Le ricette `COLLAUDO-A` e
|
||
`COLLAUDO-B` sono state seminate sul sistema in esercizio (`scripts/seed_collaudo.py`)
|
||
insieme a un utente `capoturno`, ma nessuno ha ancora guidato il flusso su un
|
||
tablet vero.
|
||
- **Il punto 14 non è stato riprodotto su un dispositivo**: è dimostrato che il
|
||
layout *poteva* muoversi per quattro motivi e che ora non può più, non che
|
||
l'operatore vedesse esattamente quelli.
|
||
- **Il punto 12 non è stato provato a rete staccata.** È dimostrato che nessuna
|
||
risorsa esterna viene richiesta. Il giro con la rete staccata va fatto, ed è ora il
|
||
momento giusto perché la CSP è appena entrata in vigore.
|
||
- **Il cicalino** è il suono del browser. Se serva una colonnina luminosa è D-5.
|
||
|
||
## Stack
|
||
|
||
- **Backend:** FastAPI + SQLAlchemy 2.0 async + MySQL 8 + Alembic + Pydantic v2 +
|
||
WeasyPrint + Plotly/Kaleido. 12 router.
|
||
- **Frontend:** Flask + Jinja2 + Alpine.js 3.15.12 + TailwindCSS 3.4.19 +
|
||
Fabric.js 5.3.1 + PDF.js 3.11.174 + Plotly.js 2.32.0 + Flask-Babel —
|
||
**tutte copie locali**, nessun CDN.
|
||
- **Deploy:** Docker Compose. Dev = Nginx; Prod = Traefik + Let's Encrypt.
|
||
- **Tooling:** uv, pytest + pytest-asyncio + httpx + aiosqlite.
|
||
|
||
## Decisioni architetturali rilevanti
|
||
|
||
| Decisione | Stato | Note |
|
||
|---|---|---|
|
||
| Le regole di misura stanno sul server, non sullo schermo | **Confermata (V3.0.0)** | Tracciabilità, inserimento manuale, fuori tolleranza: la schermata può nasconderle, il server le rifiuta. |
|
||
| Una app di stazione per PC, dati sul server | **Decisa il 28/07** (D-3) | Validazione con l'IT di Tràfilo aperta come D-4. |
|
||
| Descrizioni in marcatura, non in HTML | **Confermata** | Niente HTML accettato in ingresso: la sanificazione è per costruzione, non per filtro. |
|
||
| Frontend Flask invece di React (deroga vs spec §8) | **Confermata** | Tablet UX server-side, calibri USB, editor Fabric.js, i18n Babel collaudato. |
|
||
| NATS messaging (spec §7) | **Skippato** | Monorepo single-host, nessun microservizio. |
|
||
| Envelope risposta `{success,data,error}` (spec §6) | **Rimandato** | Costo alto, rotture client. Eventuale API v2. |
|
||
| Header `X-API-Key` vs spec `X-Api-Key` | **Mantenuto attuale** | Rinominare è breaking per i deploy esistenti. |
|
||
|
||
## Branch git
|
||
|
||
- **Corrente:** `V3.0.0`
|
||
- **Precedenti:** `V2.0.0`, `V1.0.0` … `V1.0.7` (release storiche)
|