77d95fdcc4
#4 - api/routers/tasks.py: update_task, delete_task, create_subtask, update_subtask, delete_subtask controllavano solo is_current, non version_has_measurements: la versione con misure veniva modificata sul posto, cambiando i limiti di quote gia' giudicate pass/fail. Nuovi helper _task_for_write / _subtask_for_write riusano recipe_service.create_new_version (lo stesso copy-on-write di create_task): se la versione ha misure creano una nuova versione, ritrovano il task per order_index e la quota per marker_number (chiavi che _copy_tasks_to_version preserva), e la scrittura va sulla copia. La versione misurata resta intatta. Trovato e corretto nello stesso punto: il task ritrovato nella copia non ha .version precaricato (new_version.tasks viene da selectinload, che non popola il backref figlio->genitore), e TaskResponse.recipe_id legge task.version.recipe_id - senza assegnare esplicitamente candidate.version = new_version, update_task va in MissingGreenlet (lazy load fuori da un await) invece di rispondere 200. #26 - models/api/task.py: nuovo validate_tolerance_order (ltl<=lwl<=nominal<= uwl<=utl sui soli campi valorizzati), usato come model_validator su SubtaskCreate e richiamato dal router in update_subtask sui valori risultanti (esistenti + parziali) prima del flush, perche' SubtaskUpdate non ha un insieme fisso di campi da validare da solo. Test: src/backend/tests/test_tasks.py, TestCopyOnWriteOnEdit (5) e TestToleranceOrder (4); adattato test_update_subtask esistente (nominal 12.0 violava il nuovo ordine contro utl=10.5 invariato). PROVA: uv run --extra server --extra dev pytest src/backend/tests/test_tasks.py -q -> 16 passed Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>