Files
parking_solution/apps/trainer
julian f4b806a538 fix(collector,trainer): migrate an existing collector DB on open; trainer handlers answer 500 JSON
The reviewer host's collector.sqlite was created by an earlier build, before the
`kind` column. CREATE TABLE IF NOT EXISTS shapes only a new database, so every query
naming the column failed: the collector's /health (container unhealthy), every
booth ingest, and the trainer's readiness — whose stdlib server printed the
traceback and dropped the socket, which the collector could only render as
"trainer not reachable: fetch failed". Nine days like that.

- CollectorDb.#migrate(): PRAGMA table_info against the list of columns added
  since the first deploy; ALTER TABLE ADD COLUMN for each missing one (all
  nullable or defaulted). Append to that list whenever a column joins the CREATE.
  Test replays the original schema: health, ingest, stats, a legacy row reads
  back with the defaults.
- Trainer Handler._guarded(): any unexpected exception → 500 JSON naming it,
  never a dropped connection; /health keeps answering. Test drives readiness
  against an old-schema DB.
- The collector's training status proxy includes the trainer's error text.

Wiki: the incident and the schema rule (vision-review-outbox), what the message
means (bodytype-classifier-training), log. Deploy: the new collector migrates on
start; nothing manual.

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
2026-09-16 10:33:47 +02:00
..

parking-trainer

The phase-B body-type classifier job. Reads the wash collector's volume (collector.sqlite + crops/), trains a classifier on the reviewer's labels, and writes a versioned model folder the vision image bakes in — or refuses when validation is below the floor. Design and decisions: wiki/decisions/bodytype-classifier-training.md.

parking-trainer inspect  --data /data                       # what a run would train on
parking-trainer train    --data /data --out /out            # features mode (minutes)
parking-trainer train    --mode finetune --epochs 12 ...    # full fine-tune (about an hour on 4 cores)
parking-trainer evaluate --model /out/<version>/bodytype.onnx --data /data
parking-trainer publish  /out/<version> --url https://git.infra.msai.al/api/packages/mca/generic/parking-bodytype

Exit codes: 0 model written · 2 not enough labels · 3 below the floor (report written, no model) · 1 other.

A passing run writes <out>/<version>/:

file what
bodytype.onnx the classifier; input image = RGB float32 0–255 [N,3,S,S], output logits [N,K]; normalisation is inside the graph
bodytype.json sidecar: version, class list (in vocabulary order), input size, crop margin, backbone, mode, label counts, validation metrics
report.md the human report: accuracy, per-class recall/precision, confusion matrix, dropped classes, loss weights
metrics.json the same numbers, machine-readable

On the reviewer's host the image runs serve as the trainer service of the wash-collector stack: a job API (/health, /readiness, /versions, /jobs) on the compose network that the collector's Training section (/review) drives — readiness, Train / Evaluate / Publish, reports and logs. Jobs run as subprocesses of the CLI, one at a time; state and logs persist under /out/jobs/. The CLI stays for debugging: docker compose -f docker-compose.collector.yml exec trainer parking-trainer inspect.

Local dev: uv sync --extra train (CPU torch, ~200 MB), uv run pytest -q. The test suite runs without the extra (torch tests skip), matching CI.