From 2101b9b2c9e44533594b9578160c4297cedf461c Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Tue, 28 Jul 2026 22:03:40 +0000 Subject: [PATCH] docs: porta stato e roadmap ai fatti del 28/07 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- docs/README.md | 12 +- docs/architecture/ROADMAP.md | 201 ++++++++----- docs/architecture/STATO_PROGETTO.md | 267 ++++++++++-------- .../STATO_PROGETTO_V2.0.0_2026-04-27.md | 145 ++++++++++ 4 files changed, 437 insertions(+), 188 deletions(-) create mode 100644 docs/archive/STATO_PROGETTO_V2.0.0_2026-04-27.md diff --git a/docs/README.md b/docs/README.md index ea9d68b..77aefae 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,8 +6,11 @@ Indice della documentazione del progetto. | Documento | Scopo | |---|---| -| [`architecture/STATO_PROGETTO.md`](architecture/STATO_PROGETTO.md) | Cosa è fatto oggi (V2.0.0). Snapshot del sistema, componenti e capacità. | -| [`architecture/ROADMAP.md`](architecture/ROADMAP.md) | Cosa resta da fare. Fasi 2-7 della migrazione rev04 verso V1.1.0/M1 demo cliente. | +| [`../TieMeasureFlow_modifiche_2026-07-28.md`](../TieMeasureFlow_modifiche_2026-07-28.md) | **Il piano di lavoro corrente**: i quindici punti del sopralluogo del 28/07 e le decisioni in attesa del cliente (D-1…D-9). | +| [`architecture/STATO_PROGETTO.md`](architecture/STATO_PROGETTO.md) | Cosa è fatto oggi (V3.0.0), punto per punto, e cosa non è ancora stato provato sul campo. | +| [`architecture/ROADMAP.md`](architecture/ROADMAP.md) | Cosa resta, in ordine: collaudo, punto 4, innesto GAIA, installazione a Tràfilo. Include la mappatura con le Fasi rev04 di aprile. | +| [`architecture/LAYOUT.md`](architecture/LAYOUT.md) | La cornice delle pagine: perché il layout si spostava fra le viste e la regola che lo tiene fermo. | +| [`../src/frontend/flask_app/static/vendor/VERSIONS.md`](../src/frontend/flask_app/static/vendor/VERSIONS.md) | Librerie di terze parti in locale: versioni, impronte SHA-256, come aggiornarle. | ## Riferimenti operativi @@ -18,7 +21,10 @@ Indice della documentazione del progetto. | [`USER_GUIDE.md`](USER_GUIDE.md) | Manuale utente (operatore, maker, metrologist, admin). | | [`I18N_SETUP.md`](I18N_SETUP.md) | Setup e workflow traduzioni (Flask-Babel + Alpine.js). | -## Piani dettagliati TDD (rev04) +## Piani dettagliati TDD (rev04) — storici + +> Il piano rev04 di aprile è **superato** dal documento del 28/07. La mappatura fra +> le sue sette Fasi e i quindici punti correnti è in fondo a `architecture/ROADMAP.md`. | Documento | Scopo | |---|---| diff --git a/docs/architecture/ROADMAP.md b/docs/architecture/ROADMAP.md index e430954..3d5cf84 100644 --- a/docs/architecture/ROADMAP.md +++ b/docs/architecture/ROADMAP.md @@ -1,95 +1,146 @@ -# Roadmap TieMeasureFlow — V2.0.0 → V1.1.0 (rev04 / M1 demo cliente) +# Roadmap TieMeasureFlow — V3.0.0 → installazione a Tràfilo -> Aggiornare ad ogni Fase chiusa. +> Aggiornata al 2026-07-28. Aggiornare a ogni punto chiuso. -## Riferimenti +## Dove siamo -- Master plan dettagliato: [`../superpowers/plans/2026-04-17-rev04-master-roadmap.md`](../superpowers/plans/2026-04-17-rev04-master-roadmap.md) -- Spec sorgente: [`../specs/2026-04-16-schema-sviluppo-rev04.docx`](../specs/2026-04-16-schema-sviluppo-rev04.docx) -- Stato corrente: [`STATO_PROGETTO.md`](STATO_PROGETTO.md) +La roadmap rev04 di aprile (Fasi 1-7, milestone M1/M2) è **superata dai fatti**: il +sopralluogo del 28/07 ha prodotto un elenco di quindici punti concreti che è oggi il +piano di lavoro. Buona parte delle vecchie Fasi 2 e 4 è dentro quei punti ed è già +fatta; il resto è confluito o decaduto. La mappatura è in fondo, per non perdere il +filo con i documenti di aprile. -## Strategia: due milestone +- Piano corrente: [`../../TieMeasureFlow_modifiche_2026-07-28.md`](../../TieMeasureFlow_modifiche_2026-07-28.md) +- Stato di dettaglio: [`STATO_PROGETTO.md`](STATO_PROGETTO.md) +- Piano rev04 (storico): [`../superpowers/plans/2026-04-17-rev04-master-roadmap.md`](../superpowers/plans/2026-04-17-rev04-master-roadmap.md) -| Milestone | Scope | Obiettivo | +**Scadenza che comanda tutto: l'installazione on-premise a Tràfilo è prevista per +settembre 2026.** + +## I quindici punti + +| # | Punto | Stato | |---|---|---| -| **M1 — Demo cliente** | Fasi 1-5 + deploy "demo" | Sistema testabile end-to-end col cliente per raccogliere feedback | -| **M2 — Produzione** | Fasi 6-7 + correzioni post-feedback + GAIA live | Rollout su tablet/PC reali | +| 1 | Memoria della produzione in corso | ✅ fatto | +| 2 | Tipo di task esplicito | ✅ fatto | +| 3 | Loop di misura e ripetizione | ✅ fatto | +| 4 | Limite di tentativi prima del capoturno | ⛔ **fermo** su D-6, D-7, D-9 | +| 5 | Avanzamento solo se in tolleranza | ✅ fatto | +| 6 | Fermo linea e Fine produzione con effetto | ✅ fatto (innesto GAIA fermo su D-1, D-2) | +| 7 | Gestione delle stazioni | ✅ fatto | +| 8 | Tracciabilità obbligatoria | ✅ fatto | +| 9 | Blocco dell'inserimento manuale | ✅ fatto | +| 10 | Interfaccia operatore: sequenza e conferme | ✅ fatto | +| 11 | Formattazione delle descrizioni | ✅ fatto | +| 12 | Funzionamento senza internet | ✅ fatto | +| 13 | Generazione task dalla scheda tecnica con l'AI | — fuori offerta (quotazione su D-8) | +| 14 | Stabilità del layout | ✅ fatto | +| 15 | Statistica separata dalla registrazione | ✅ fatto | -## Stato Fasi (M1) +## Cosa resta, in ordine -| Fase | Scope | Stato | Branch / Commit | +### 1. Collaudo sul campo — *da fare adesso, non serve nessuna risposta* + +È il lavoro più urgente rimasto, ed è l'unico che non dipende da nessuno. Tredici +punti sono in esercizio e **nessuno li ha percorsi su un tablet**. + +Le ricette `COLLAUDO-A` e `COLLAUDO-B` sono già sul sistema, con un utente +`capoturno` (`scripts/seed_collaudo.py`). Da verificare, in quest'ordine: + +| Cosa | Come si vede che è giusto | +|---|---| +| Ciclo di misura (3) | Il conto alla rovescia sopravvive al cambio task, prosegue in rosso oltre lo zero, riporta alla misura | +| Fuori tolleranza (5) | Con `12.00` sulla prima quota non si passa alla seconda finché il capoturno non autorizza, o finché non si rimisura dentro | +| Tracciabilità (8) | Su `COLLAUDO-B` l'avvio non parte finché lotto e seriale non ci sono | +| Inserimento manuale (9) | Su `COLLAUDO-B` il tastierino numerico non c'è affatto | +| Sequenza (10) | La ricetta si apre sul primo task; un task lasciato a metà appare «Incompiuto 1/2» nella lista | +| Senza rete (12) | Staccare la rete e percorrere login → ricetta → task → annotazione → statistiche → report, console aperta | +| Layout (14) | **Da un monitor da scrivania**, non solo dal tablet: è lì che il difetto si vedeva | + +Il giro del punto 12 va fatto **ora**, perché la Content-Security-Policy è appena +entrata in vigore: se una libreria avesse bisogno di un permesso non concesso, il +sintomo compare adesso. + +### 2. Punto 4 — appena arrivano le risposte + +Numero di tentativi prima del capoturno. Serve sapere **D-7** (quanti, e se uguali +per tutte le ricette), **D-6** (password, PIN o badge) e **D-9** (se cambiare i +parametri crea una nuova versione). Il resto dell'impianto è pronto: il blocco per +fuori tolleranza e l'autorizzazione del capoturno esistono già, manca il contatore. + +Stima, a decisioni chiuse: **2-3 giorni**. + +### 3. Innesto verso GAIA — fermo su D-1 e D-2 + +Avvio produzione, fermo linea e fine produzione hanno già lo stato e gli eventi; la +fine produzione emette già il file di statistica. Manca solo il canale verso il +gestionale, e non si può nemmeno disegnare finché non si sa **come** si parla con +GAIA (D-1) e **da dove** (D-2). + +Stima, a protocollo noto: **1-2 settimane**, molto dipendente dalla risposta. + +### 4. Installazione a Tràfilo — settembre + +| Cosa | Blocco | +|---|---| +| Macchina server, spazio disco | D-4 | +| Immagini Docker portate in reparto, non repository da compilare sul posto | — | +| Colonnina luminosa, se serve | D-5 | +| Validazione rete e macchine con l'IT | D-4 | + +Nota: **la costruzione delle immagini richiede rete** (npm, apt, uv); è l'esecuzione +a non richiederla. In reparto va portata l'immagine già costruita. + +## Decisioni in attesa del cliente + +Da girare a Tràfilo tramite Menoncin. Le prime due e la D-4 hanno l'orizzonte di +settembre; D-6 e D-7 bloccano lavoro che sappiamo già fare. + +| ID | Decisione | Blocca | Chi risponde | |---|---|---|---| -| **1** | Stazioni + identità per-tablet | ✅ **COMPLETATA** | `V2.0.0` (merge `ea8e468` da `feature/rev04-phase1-stations`) | -| 2 | Ruolo Capoturno (Supervisor) + override token breve | ⏳ Da iniziare | — | -| 3 | Editor ricetta a blocchi (preparation + measurement) | ⏳ Da iniziare | — | -| 4 | Workflow operatore (retry/timer/autologout/avvio produzione) | ⏳ Da iniziare | — | -| 5 (M1) | `ImportOnlyGaiaClient` + UI import dati cliente reali | ⏳ Da iniziare | — | -| Deploy M1 | VPS demo (compose + Traefik + LE, no registry) | ⏳ Da iniziare | — | +| **D-1** | Protocollo del gestionale GAIA: servizi web, database condiviso, file? | innesto GAIA (punto 6) | IT Tràfilo + fornitore GAIA | +| **D-2** | Rete e credenziali per raggiungere GAIA | come sopra | IT Tràfilo | +| ~~D-3~~ | ~~Una app per stazione o una sola?~~ **Decisa il 28/07**: dati sul server, app di stazione su ogni PC | — | chiusa; validazione in D-4 | +| **D-4** | Server: quale macchina, quanto spazio disco (database **e** disegni) | installazione | IT Tràfilo | +| **D-5** | Cicalino: basta il suono del browser o serve una colonnina luminosa? | punto 3 (completamento) | Tràfilo | +| **D-6** | Autorizzazione capoturno: password come oggi, o PIN / badge? Venti volte al giorno la password è attrito | punti 4, 5, 6 | Tràfilo | +| **D-7** | Quanti tentativi prima del capoturno, e uguali per tutte le ricette? | punto 4 | Tràfilo | +| **D-8** | Schede tecniche: quante, formato standard, conversione una-tantum o funzione permanente? | punto 13 e la sua quotazione | Tràfilo | +| **D-9** | Cambiare i parametri di una ricetta (timer, tentativi) crea una nuova versione? | punti 3, 4 | noi, con conferma cliente | -## Stato Fasi (M2) +Una decisione già presa che va **confermata**: la migrazione 009 ha impostato +`allow_manual_input = 1` su tutte le ricette esistenti, per conservare il +comportamento in essere invece di irrigidire di colpo ricette già in uso. Va deciso +ricetta per ricetta quali devono passare a solo calibro. -| Fase | Scope | Stato | -|---|---|---| -| 5 (M2) | GAIA reale (protocollo TBD, polling, comandi produzione) | ⏳ Bloccata da decisioni cliente (D-0.1, D-0.2) | -| 6 | Deploy B industriale (registry privato + Watchtower + STATION_ID per-tablet + CI release) | ⏳ Pianificata | -| 7 | Hardening, security review, E2E sito pilota, docs aggiornati, i18n delta | ⏳ Pianificata | - -## Decisioni aperte (bloccanti per M2 / future fasi) - -Da: master plan §0 "Precondizioni e Decisioni Aperte". Da risolvere col cliente prima della Fase 5/6. - -| ID | Decisione | Stato | Bloccante per | -|---|---|---|---| -| D-0.1 | Protocollo integrazione GAIA (REST / DB shared / OPC-UA / file) | **Aperta** | Fase 5 reale (M2) | -| D-0.2 | Credenziali e rete GAIA (VPN / firewall / whitelist IP) | **Aperta** | Fase 5 reale (M2) | -| D-0.3 | Target hardware "tablet" (Windows / Linux industriale / Android) | **Aperta** | Fase 6 (deploy B) | -| D-0.4 | Cicalino/luce avviso (audio HTML5 / hardware USB / entrambi) | **Rimandata a M2** | Fase 4 finale | -| D-0.5 | Parametri runtime modificabili vs versione immutabile | **Aperta** (raccomandato B: separare volatili) | Fase 3 | -| D-0.6 | Auth capoturno durante override (modale / PIN / RFID) | **Aperta** | Fase 2 | -| D-0.7 | Timeout auto-logout | **Risolta** | — | -| D-0.8 | Naming ruolo capoturno | **Proposta:** `Supervisor` | Fase 2 | -| D-0.9 | Tag versione immagine docker | **Proposta:** SemVer + `latest` | Fase 6 | -| D-0.10 | Registry esposto su Internet o solo VPN | **Proposta:** solo VPN cliente | Fase 6 | - -## Tech debt da chiudere +## Tech debt | Item | Priorità | Note | |---|---|---| -| 3 test backend pre-esistenti rotti (`test_recipes`, `test_tasks`) | Media | Investigare prima di Fase 3 (toccano recipe + task router). | -| 1 test client pre-esistente rotto (`test_save_measurement_proxy`) | Bassa | Probabilmente CSRF/payload. Risolvere con Fase 4. | -| Pagina `task_complete` riepilogo: utente segnala riga vuota in alcuni scenari | Media | Da debuggare (rendering corretto via curl ma utente vede vuoto in browser, possibile interazione con sessione lot/serial). | -| `.env` rename a convenzione spec (SERVICE_NAME, SERVICE_DOMAIN, API_KEY) | Bassa | Rinviato (impatto deploy). | -| Header `X-API-Key` rename a `X-Api-Key` | Bassa | Vedere se M2 lo richiede. | -| Envelope risposta `{success,data,error}` | Bassa | Eventuale API v2 in M2. | -| `Dockerfile.frontend`: `pybabel compile` via `uv run` non testato in build reale | Alta | Verificare al primo `docker compose build`. | -| Smoke test in container Docker (non solo locale uvicorn+gunicorn) | Alta | Validare che i Dockerfile riscritti con `uv` buildino e girino correttamente prima di chiudere V2.0.0. | +| `components/barcode_scanner.html` dipende da `html5-qrcode`, mai caricata | Media | Codice morto: il componente non è incluso da nessuna parte e il lettore che l'operatore usa è un campo di testo. Da rimuovere, o da completare portando la libreria in `static/vendor/` | +| Pagina `task_complete`: riga vuota segnalata in alcuni scenari | Media | Segnalazione di aprile mai riprodotta. Da verificare durante il collaudo, ora che il flusso è cambiato | +| `.env` rename a convenzione spec (SERVICE_NAME, SERVICE_DOMAIN, API_KEY) | Bassa | Rinviato: impatta i deploy esistenti | +| Header `X-API-Key` → `X-Api-Key` | Bassa | Breaking per i deploy esistenti | +| Envelope risposta `{success,data,error}` | Bassa | Eventuale API v2 | +| Test di carico a 20 tablet reali | Bassa | La capacità è dimensionata ma mai misurata sotto carico vero | -## Open per scelta utente prima della prossima sessione +I quattro test rotti tracciati nello snapshot V2.0.0 non risultano più: la suite è a +360 pass, 0 fail. -1. **Quale fase iniziare adesso?** Opzioni: Fase 2 (Supervisor — sblocca workflow), Fase 3 (block editor — independente), Fase 5 (import GAIA dati reali — sblocca demo). -2. **Revisione decisioni aperte col cliente** — D-0.1 / D-0.2 / D-0.3 / D-0.6 prima di pianificare Fase 5 e 6. -3. **Smoke test Docker** della nuova struttura V2.0.0 (`docker compose -f docker-compose.dev.yml up --build`) per validare i Dockerfile riscritti. -4. **Test di carico** (k6/locust) a 20 VU su `/measure/save-measurement` per validare la scalatura worker (capacità annunciata: 20-30 tablet contemporanei). +## Mappatura con la roadmap rev04 di aprile -## Stima tempi residui M1 (post-Fase 1) +Per chi torna sui documenti di aprile e non ritrova le Fasi. -| Task | Stima full-time | +| Fase rev04 | Che fine ha fatto | |---|---| -| Fase 2 — Supervisor + override | 1 settimana | -| Fase 3 — Block editor | 1.5 settimane | -| Fase 4 — Workflow operatore | 2 settimane | -| Fase 5 (M1) — Import-only GAIA | 1 settimana | -| Deploy M1 demo | 0.5 settimane | -| **Totale M1 residuo** | **~6 settimane** | +| 1 — Stazioni per-tablet | Chiusa in V2.0.0, ampliata dal punto 7 | +| 2 — Ruolo Capoturno + override | Assorbita dai punti 5 e 6: il ruolo `Supervisor` esiste e l'autorizzazione resta scritta sulla misura. L'override a token breve dipende da D-6 | +| 3 — Editor ricetta a blocchi | Sostituita dal punto 2 (tipo di task dichiarato), che risolve il problema vero senza riscrivere l'editor | +| 4 — Workflow operatore | Assorbita dai punti 3, 4, 10: timer e sequenza sono fatti, i tentativi sono il punto 4 | +| 5 — Import GAIA | Diventata l'innesto del punto 6, ferma su D-1 e D-2 | +| 6 — Deploy industriale (registry + Watchtower) | Ridimensionata: con una rete isolata l'aggiornamento automatico non ha senso. Restano immagini versionate portate a mano | +| 7 — Hardening | Parzialmente assorbita: CSP, versioni congelate e impronte sono entrate col punto 12 | -## Stima tempi M2 (dopo feedback) - -| Task | Stima | -|---|---| -| Aggiustamenti post-feedback | variabile (1-2 sett.) | -| Fase 5 reale GAIA | 1-2 settimane | -| Fase 6 deploy B | 1 settimana | -| Fase 7 hardening | 1-2 settimane | -| **Totale M2** | **~4-7 settimane** | - -**Totale fino a produzione:** ~10-13 settimane full-time da oggi (2026-04-25), assumendo decisioni aperte risolte in tempo utile. +Le vecchie decisioni `D-0.x` sono confluite nelle `D-x` qui sopra: D-0.1→D-1, +D-0.2→D-2, D-0.4→D-5, D-0.5→D-9, D-0.6→D-6. D-0.8 (nome del ruolo capoturno) è +chiusa: si chiama `Supervisor`. D-0.7 (auto-logout) era già risolta. diff --git a/docs/architecture/STATO_PROGETTO.md b/docs/architecture/STATO_PROGETTO.md index 2691426..f177153 100644 --- a/docs/architecture/STATO_PROGETTO.md +++ b/docs/architecture/STATO_PROGETTO.md @@ -1,145 +1,192 @@ -# Stato Progetto TieMeasureFlow — V2.0.0 +# Stato Progetto TieMeasureFlow — V3.0.0 -> Snapshot al 2026-04-27. Aggiornare ad ogni milestone. +> 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 -**V2.0.0** (in sviluppo, branch `V2.0.0` come default su `git.tielogic.xyz`). +**V3.0.0** (in sviluppo, branch `V3.0.0`). Versione precedente: `V2.0.0`. -Versione precedente di produzione: `V1.0.7`. +In esercizio su `tieflow.tielogic.xyz` (Docker Compose + Traefik + Let's Encrypt), +schema alla migrazione `010_meas_authorisation`. ## Sintesi esecutiva -Il sistema base (V1.0.7) è completo e collaudato: ricette, task, misurazioni, SPC, report PDF, gestione utenti, dashboard metrologist. La V2.0.0 in corso aggiunge il primo blocco della migrazione **rev04** (stazioni per-tablet) e ristruttura l'intero monorepo secondo lo standard `python-project-spec-design.md` (uv + `src/backend/` + `src/frontend/flask_app/`). +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). -## Cosa funziona oggi (V2.0.0 — branch corrente) +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. -### Funzionalità ereditate da V1.0.7 -- Autenticazione username/password + API key per-utente, ruoli combinabili (Maker, MeasurementTec, Metrologist) + flag `is_admin`. -- Recipe versioning copy-on-write: una nuova versione si crea solo se la corrente ha già measurements; altrimenti update in-place. -- Editor ricette (Maker) con annotation editor Fabric.js (~1200 LOC, collaudato su tablet). -- Workflow operatore tablet: select_recipe → task_list → task_execute → task_complete, con barcode scanner e numpad touch (input USB calibro con burst detection). -- Calcolo pass/fail con limiti UTL/UWL/LWL/LTL. -- Dashboard SPC: capability (Cp/Cpk/Pp/Ppk), control chart (UCL/LCL = mean ± 3σ), istogramma con curva normale, calcoli puro stdlib (no numpy). -- Report PDF (WeasyPrint + Kaleido SVG). -- Setup page protetta da `SETUP_PASSWORD` per inizializzazione DB e seed. -- i18n IT/EN (Flask-Babel + Alpine.js JSON). -- Tema light/dark via `Alpine.store('theme')` + localStorage. +Tredici punti su quindici sono fatti e in esercizio. Uno è fermo sulle risposte del +cliente, uno è fuori offerta. -### Aggiunte V2.0.0 (rev04 Fase 1 — Stazioni per-tablet) -- Tabelle `stations` + `station_recipe_assignments` (Alembic migration `002_add_stations.py`). -- Modelli ORM: `Station`, `StationRecipeAssignment` con vincolo unique `(station_id, recipe_id)`. -- Schemas Pydantic: `StationCreate/Update/Response`, `StationRecipeAssignmentCreate/Response`, `RecipeSummary`. -- Service `station_service` (CRUD + assegnazioni + cascade delete). -- Router `/api/stations` con CRUD admin + endpoint operatore `GET /api/stations/by-code/{code}/recipes`. -- Seed automatico `ST-DEFAULT` con tutte le ricette esistenti (idempotente). -- Variabile env client `STATION_CODE` letta da `Config`, helper `APIClient.get_station_recipes()`. -- Filtro `select_recipe`: il client mostra solo le ricette assegnate alla propria stazione, errore se `STATION_CODE` non configurato. -- **GUI admin completa** in `/admin/stations`: tabella con search, modal create/edit, modal gestione assegnazioni ricette, conferma eliminazione, link in navbar (desktop + mobile). -- 47 nuovi test (32 server + 15 client) tutti pass. +## I quindici punti -### Aggiunte V2.0.0 (performance + multi-utente) -- Gunicorn 5 workers × 4 thread (gthread) — capacità ~20 richieste concorrenti Flask, regge 20+ tablet. -- Uvicorn 4 workers + `--proxy-headers --forwarded-allow-ips='*'`. -- Rate limit middleware: identificazione IP reale via `X-Forwarded-For` → `X-Real-IP` → `request.client.host`. -- Rate limit general 100 → 300 req/min/IP (per-tablet ora, non più condiviso). -- Flask `ProxyFix(x_for=1, x_proto=1, x_host=1)` per IP reale dietro Nginx. -- `APIClient` propaga `X-Forwarded-For` + `X-Real-IP` (sia JSON che multipart). -- 12 test aggiuntivi (7 server + 5 client). +| # | 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 | -### Aggiunte V2.0.0 (struttura monorepo) -- `pyproject.toml` unico con extra `server`/`client`/`dev`. Niente più `requirements.txt`. -- `uv.lock` (77 pacchetti) + `.python-version` (3.11) committati per build riproducibili. -- Layout `src/backend/` + `src/frontend/flask_app/` (vedi sotto). -- `Dockerfile` (root) + `Dockerfile.frontend` riscritti con `uv sync --frozen --no-dev --extra server|client`. -- `docker-compose.{dev,}.yml` con build context `.`. -- Alembic env.py aggiunge project root a `sys.path`; `script_location = %(here)s` resta valido. -- `.dockerignore` aggiornato. +## Cosa è entrato in V3.0.0 -### Hardening post-restructure (smoke test 2026-04-26) +### Produzione come stato, non come pulsante (punti 1, 6) -Sequenza di smoke test in locale (uvicorn + gunicorn + MySQL Docker) ha fatto emergere quattro regressioni che sarebbero rimaste invisibili al test suite: +- 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. -- **`src/backend/config.py`**: `env_file` era cwd-relative (`../../.env`). Rotto fuori da `src/backend/`. Risolto con percorso assoluto `Path(__file__).resolve().parents[2] / ".env"`. -- **`src/backend/models/orm/__init__.py`**: `Station` e `StationRecipeAssignment` non erano esportati, quindi `Base.metadata.create_all` non creava le tabelle stations. Aggiunti agli import. -- **`.env.example`**: `UPLOAD_DIR=server/uploads` era residuo della vecchia struttura → file landavano fuori dall'albero di progetto. Aggiornato a `UPLOAD_DIR=uploads`. -- **Apostrofi italiani in template Alpine** (`l'utente`, `nell'eliminazione`, `nell'assegnazione`): chiudevano prematuramente JS string literals dentro `x-text` e blocchi `