31fa78bdfd
#13 — handleMeasurement era rientrante: saving tornava false prima della pausa di 1s di feedback, currentIndex avanzava dopo. Una seconda lettura in quella finestra salvava due volte la stessa quota e saltava la successiva. Guardia unica this.busy: true all'ingresso, false su ogni uscita della funzione; l'ingresso è bloccato anche con showCompletionOverlay, showSupervisorModal, isPaused o cycleConfirmed pendenti. Una lettura scartata non sparisce in silenzio: errorMessage "Lettura ignorata: attendere". #12 — dopo la scadenza del token CSRF (config lato server, lotto B2) il POST tornava 400 in HTML: response.json() lanciava, finiva nel catch generico e l'operatore vedeva "Errore di rete" invece che "Sessione scaduta". Aggiunto try/catch dedicato sul parsing e controllo di result.csrf_expired / session_expired, sia in handleMeasurement che in postProduction(); la lettura non salvata resta sul display. #29 — confirmCycle mostrava il ciclo chiuso anche quando il server rifiutava /cycle (produzione aperta, postProduction torna null): cadeva nel ramo "nessuna produzione", incrementando cycleCount in locale. Ora, con produzione aperta, un rifiuto del server ripristina cycleConfirmed=false, riapre l'overlay di completamento e mostra l'errore (già in productionError); il ramo locale resta riservato a quando non c'è produzione aperta. #37 punto 9 — il blocco capoturno era deciso su passFailStatus (preview client, prima del salvataggio) invece che su result.pass_fail del server; ora la risposta del server decide sia il blocco che i conteggi locali. Completa anche il lato pagina di #27: aggiunto il listener per l'evento numpad-error (dispatchato da numpad.js) cosi' la lettura rifiutata dal calibro è visibile all'operatore, non solo intercettata in silenzio. Stringhe nuove in messages.po (it/en): "Sessione scaduta: ricaricare la pagina", "Lettura ignorata: attendere", "Lettura del calibro rifiutata: valore troppo grande". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>