Release note: client OCR fallback ladder
Release · OCR · fallback · client scan · behaviour change
What changed
The client scan now loads OCR engines through an ordered ladder (`lib/ocr/engine-ladder.ts`) instead of a single engine:
- MRZ model from the browser cache (`public/tessdata/mrz.traineddata`, Doubango `tessdata_fast` int model, BSD-3-Clause).
- MRZ model re-downloaded with `cacheMethod: "refresh"` if the cache attempt failed (stale or corrupt model).
- Stock `eng` data from the CDN — last attempt only.
A shared singleton worker (`lib/ocr/shared-worker.ts`) applies the same OCR parameters to whichever engine loads, and `activeOcrEngine()` reports which one won.
Behaviour guarantees
- OCR data tests (stock `eng`) are used only when everything falls — never first, never by default.
- Functionality is maintained on the fallback: band read, candidate ranking, look-alike repairs, and send-to-Validate/Compare all work identically.
- Failed loads are never cached; every read retries the ladder from the MRZ model.
- The user is told which engine read the document and can trigger "Try the MRZ model again" at any time.
Validation
`lib/ocr/engine-ladder.test.ts` covers the ladder: first success, fallback after MRZ failure, last-error rethrow, attempt reporting, and empty-ladder rejection. All tests, `tsc --noEmit` and ESLint must stay clean.