fix(vision): self-heal local real ANPR — dev scripts sync the alpr extra

The dev box runs vision as bare `uv run uvicorn`, and a plain uv run/uv sync
re-resolves the venv to the lockfile DEFAULTS, stripping fast-alpr/onnxruntime.
So after any `pnpm dev` real ANPR silently degraded to "snapshot, no plate"
(diagnosed 2026-06-25: real reads through 06-22, venv frozen lean since 06-19,
no other env with fast_alpr). The BOOTH was never affected — it runs the Docker
image, which bakes `uv sync --frozen --extra alpr` at build (immutable, weights
pre-warmed); a booth ModuleNotFoundError is a STALE image (fix: booth.sh update).

Vision package.json dev/start/recognize now run `uv sync --extra alpr &&` first
so pnpm dev is self-healing; added a dev:stub escape hatch for a lean run.
Documented in wiki/decisions/vision-service-packaging.md ("Two runtimes, one
fragile") + a log entry.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-06-26 08:11:17 +02:00
parent b3cb67188e
commit 40ffa90dac
3 changed files with 47 additions and 4 deletions
+27 -1
View File
@@ -2,7 +2,7 @@
type: decision
tags: [parking, decisions, vision, anpr, monorepo, packaging]
sources: []
updated: 2026-06-19
updated: 2026-06-25
status: settled
---
@@ -114,3 +114,29 @@ The skeleton is **built and wired** (no recognizer models yet):
> **Resolved 2026-06-22 → [[container-deployment]]:** the vision service now ships as the
> `parking-vision` Docker image (uv base, `--extra alpr`), model weights **pre-warmed into the image
> layer** at build (offline-first), and runs under **docker-compose** (base + per-env override).
## Two runtimes, one fragile (the `uv run` strips-the-extra trap) — 2026-06-25
Real ANPR runs **completely differently on the two machines**, and only the dev path was fragile:
- **Booth (deployment) = the Docker image.** The `Dockerfile` runs `uv sync --frozen --extra alpr`
at build, so fast-alpr/onnxruntime are **baked into an immutable image layer** and the weights are
pre-warmed in. `docker-compose.prod.yml` forces `VISION_RECOGNIZER=fast_alpr`. Nothing at runtime
re-resolves the venv → **the booth's real ANPR cannot silently degrade.** (A booth
`ModuleNotFoundError: fast_alpr` is a STALE image, not this bug — fix with `booth.sh update` to pull
the current image.)
- **Dev machine = bare `uv run uvicorn …`** against `apps/vision/.venv`. **This is the trap:** a plain
`uv run` (or `uv sync` with no `--extra alpr`) re-resolves the venv to the lockfile **defaults** and
**REMOVES** the alpr stack — leaving the model weights orphaned in `~/.cache/open-image-models` but
no recognizer in the venv. So a dev box that ran real ANPR (weights downloaded, plate reads
recorded) silently degrades to "**snapshot captured but no plate**" after the next `pnpm dev`. This
exactly explains a gap observed 2026-06-25: real reads on 06-22, then nothing — the venv (frozen
since 06-19, lean) had been stripped, while the Docker/compose work (06-23) was an innocent
coincidence, not the cause.
**Fix (2026-06-25):** the vision `package.json` `dev`/`start`/`recognize` scripts now run
`uv sync --extra alpr &&` FIRST, so `pnpm dev` is **self-healing** — the recognizer survives every
run. A `dev:stub` script is the lean, model-free escape hatch. The booth (Docker) is untouched.
**Implication:** local real-ANPR and booth real-ANPR are now both reliable; CI/light contributors who
don't want the heavy stack use `dev:stub` or run the suite (tests are stub-mode, offline). See
[[opencv-anpr-service]].
+15
View File
@@ -1621,3 +1621,18 @@ passes `-v` (would wipe the signed [[append-only-event-chain|ledger]] volume); `
short-circuit before any Docker/.env requirement. Verified: prod `config` renders Caddy:80 + internal
server + pinned images + `fast_alpr`; dev `config` renders `:dev` images + `stub` + published ports.
Documented in [[container-deployment]] ("Booth operator wrapper").
## [2026-06-25] fix | Local ANPR silently degraded — `uv run` strips the alpr extra
Diagnosed via the live DB (read-only `VACUUM INTO` copy) why entry `26799912337` recorded a snapshot
but no plate: the dev box's vision service was running **stub**, and earlier real ANPR had stopped.
Root cause (NOT the Docker/compose work, which was an innocent coincidence): the dev machine runs vision
as **bare `uv run uvicorn`** against `apps/vision/.venv`, and a plain `uv run`/`uv sync` re-resolves the
venv to the lockfile **defaults**, **stripping** fast-alpr/onnxruntime — so after any `pnpm dev` the
recognizer vanishes (weights orphaned in `~/.cache`, no module in the venv) and ANPR silently becomes
"snapshot, no plate". Evidence: 28 real reads through 06-22 (yolo-v9 model, ~99% conf), venv frozen lean
since 06-19, no other env with fast_alpr on the box. **The BOOTH was never affected** — it runs the
Docker image, which bakes `uv sync --frozen --extra alpr` at build (immutable, weights pre-warmed); a
booth `ModuleNotFoundError` is a STALE image (fix: `booth.sh update`). **Fix:** vision `package.json`
`dev`/`start`/`recognize` now `uv sync --extra alpr &&` first (self-healing), `.env` set to `fast_alpr`,
+ a `dev:stub` escape hatch. Restored real ANPR locally (`/health` → `fast_alpr` ready, model loaded from
cache, no download). Documented in [[vision-service-packaging]] ("Two runtimes, one fragile").