VisionReferenceImage (migration 013, chained after 012_vision_results):
task_id, path, station_id, device_code, calibration_snapshot,
engine_version, note, expected_json, acquired_at. station_id/device_code/
calibration_snapshot/engine_version/expected_json stay nullable - nessun
endpoint di questo task li popola ancora, arrivano coi Piani 1b/2/4.
save_reference_image salva sotto settings.upload_path (non upload_dir,
come production_export_service). preview esegue il grafo e non salva
niente - comporre non e' misurare, lo asserisce il test.
POST /api/vision/reference-images e /api/vision/preview su require_maker
(non require_measurement_tec di execute): comporre e provare sono lavoro
del Maker, non misure.
Suite intera su Python 3.11.15 locale (uv run pytest -q, SQLite
in-memory per il backend): 1 failed, 373 passed, 4 skipped in 82.35s.
Il fallimento e' quello preesistente e non correlato di
test_no_first_party_script_calls_out (fabric-debug.js locale,
git-ignored) - 371 passed prima di questo task, +2 qui.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BBnuACZSCJqXrMYC3LUMU
Aggiunge POST /api/vision/execute: route l'immagine al worker di visione,
mappa le uscite del grafo sulle quote e le salva con save_measurement -
stesso verdetto, stesso gate del fuori tolleranza di ogni altra misura.
Nuova tabella vision_results (una riga per acquisizione, non per quota) e
input_method 'camera' su measurements.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BBnuACZSCJqXrMYC3LUMU
Punto 1 del documento modifiche del 28/07, il prerequisito su cui poggiano i
punti 3, 4 e 6.
Lo stato di una produzione viveva dentro una pagina del browser: timer, conteggio
cicli e flag "produzione avviata" erano variabili Alpine di task_execute.html, e
la navigazione fra task e' un ricaricamento completo, quindi cambiando task si
perdeva tutto. Da qui il loop di misura che non reggeva, il fermo linea che non
aveva nulla da fermare e l'assenza di storico.
Nuove tabelle production_runs e production_events (migrazione 005), endpoint REST
senza stato in memoria di processo - con un'app di stazione installata su ogni PC
il database e' l'unico posto condiviso - e il frontend che legge lo stato
all'apertura invece di tenerlo in memoria.
Tre scelte di modello:
- la scadenza e' un timestamp assoluto (next_measurement_at), non un contatore:
il countdown si ricalcola da li' a ogni caricamento, e lasciarne andare la
differenza sotto zero dara' gratis il contatore del ritardo del punto 3. Al
client vanno i secondi gia' calcolati, non il timestamp: un datetime naive
verrebbe letto nel fuso del browser e il conto sarebbe sfasato dell'offset UTC;
- l'intervallo di misura e' copiato sulla produzione, non referenziato: modificare
la ricetta a produzione avviata non deve spostare una scadenza in corso;
- active_station_id rispecchia la stazione finche' la produzione e' aperta e va a
NULL alla chiusura. Con un vincolo unico sopra, "una stazione = una produzione
aperta" e' una garanzia del database e non un controllo soggetto a race; i NULL
non collidono, quindi le produzioni chiuse si accumulano senza disturbo.
Il fermo linea congela il conto alla rovescia e alla ripresa la scadenza viene
traslata della durata del fermo, non ricalcolata: un fermo non regala ne' toglie
tempo all'operatore. L'autorizzazione del capoturno passa da authenticate_user e
non da un login, che rigenererebbe la sua API key buttando giu' la sessione che ha
aperta altrove.
La migrazione e' stata eseguita davvero, non solo scritta, su uno SQLite usa e
getta: upgrade e downgrade girano e le colonne coincidono con i modelli. La prova
ha trovato un difetto - create_unique_constraint dopo create_table e' un ALTER,
che SQLite rifiuta - ora il vincolo e' dichiarato dentro create_table.
Fuori da questo commit, per stare nei confini del punto 1: l'API espone gia'
pause, resume e close, ma i pulsanti fermo linea e fine produzione restano da
collegare (punto 6), e il rientro forzato sulla misura allo scadere e' il punto 3.
Corretti due difetti trovati strada facendo: env.py non importava ne' Station ne'
ProductionRun, quindi l'autogenerate di Alembic era gia' cieco sulle stazioni; e
task_execute.html, lo schermo con piu' JavaScript dell'applicazione, non era
coperto dal test di sintassi. Aggiungerlo ha richiesto di correggere l'helper, che
validava le espressioni Alpine solo come espressione singola e bocciava
@click="a = false; b = true", forma che Alpine accetta: ora prova entrambe le
letture e fallisce solo se cadono tutte e due.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both came from the src/ restructure and only show up at runtime, so
the test suite had not caught them.
- src/backend/config.py: env_file was "../../.env", which pydantic-
settings resolves against the *cwd*, not the file. Running uvicorn
or alembic from the project root therefore looked for
../../.env one level above the repo and silently fell back to the
default DB_PASSWORD ("change_me_in_production"), hiding the real
password. Now resolved as Path(__file__).resolve().parents[2] /
".env" so the lookup is always against the project root regardless
of cwd.
- src/backend/models/orm/__init__.py: Station and
StationRecipeAssignment were never imported here, so anything that
triggers Base.metadata.create_all without first importing the
setup router (which has its own Station import) ended up with no
stations / station_recipe_assignments tables. Verified locally:
/api/setup/seed used to fail with "Table tiemeasureflow.stations
doesn't exist" before this fix.
- .gitignore: ignore src/frontend/flask_app/package.json and
package-lock.json (local npm-install artifacts; the Dockerfile
installs tailwindcss directly).
Smoke verified end-to-end: uvicorn + gunicorn + MySQL, login + admin
stations + select_recipe + admin users all 200 OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns the repo with the python-project-spec-design.md template chosen
for V2.0.0. Big move, no logic changes. The 3 pre-existing test
failures (test_recipes::test_update_recipe, test_recipes::
test_recipe_versioning, test_tasks::test_reorder_tasks, plus the
client test_save_measurement_proxy) survive unchanged.
Layout changes
- server/ -> src/backend/
- server/middleware/ -> src/backend/api/middleware/
- server/routers/ -> src/backend/api/routers/
- server/models/ -> src/backend/models/orm/
- server/schemas/ -> src/backend/models/api/
- server/uploads/ -> uploads/ (project root, mounted volume)
- server/tests/ -> src/backend/tests/
- client/ -> src/frontend/flask_app/ (Flask kept; React
deroga is documented in CLAUDE.md, justified by tablet UX, USB
caliper/barcode workflow and Fabric.js integration)
Tooling
- pyproject.toml: monorepo with [project] core deps and
optional-dependencies server / client / dev. Replaces both
server/requirements.txt and client/requirements.txt.
- uv.lock + .python-version (3.11) committed for reproducible builds.
- Dockerfile (root, backend) and Dockerfile.frontend rewritten to use
uv sync --frozen --no-dev --extra server|client; legacy Dockerfiles
preserved as Dockerfile.legacy for reference but excluded from build
context via .dockerignore.
- docker-compose.dev.yml + docker-compose.yml: build context now ".",
dockerfile pointing to the root files.
Code adjustments forced by the move
- Every "from config|database|models|schemas|services|routers|middleware
import ..." rewritten to its src.backend.* equivalent (50+ files
including indented inline imports inside test bodies).
- src/backend/migrations/env.py: insert project root into sys.path so
alembic can resolve src.backend.* imports regardless of cwd.
- src/backend/config.py: env_file ../../.env (was ../.env), upload_path
resolves project root via parents[2].
- src/backend/tests/conftest.py + tests: import ... from src.backend.*
instead of bare names; old per-directory pytest.ini files removed in
favor of root pyproject.toml [tool.pytest.ini_options].
- .gitignore: uploads/ at root, src/frontend/flask_app/static/css/
tailwind.css path; .dockerignore tightened.
- CLAUDE.md: rewrote sections "Layout del repository", "Comandi di
Sviluppo", "Database & Migrations", "Test", "i18n", and all path
references throughout the architecture sections.
Verified
- uv lock resolves 77 packages; uv sync --extra server --extra client
--extra dev installs cleanly.
- uv run pytest: 171 passed, 4 pre-existing failures.
- uv run alembic -c src/backend/migrations/alembic.ini check loads
config and metadata (errors only on the absent local MySQL).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>