The OCR engine fallback ladder
Client scan · OCR · fallback · user involvement
The problem
The scan flow reads the MRZ band with `tesseract.js` in the browser. Two OCR data sources exist: the self-hosted MRZ model (`public/tessdata/mrz.traineddata`, Doubango `tessdata_fast` int model, BSD-3-Clause) and Tesseract's stock `eng` data from the CDN. On the 66-band fixture benchmark the MRZ model validates10/66 reads vs 1/66 for stock `eng`. The `eng` data must therefore never be the default — it is a last resort, not a data test to run first.
How the ladder works
`lib/ocr/engine-ladder.ts` runs engine attempts in order and returns the first success:
- Load the MRZ model from the browser cache (`langPath: "/tessdata"`).
- If that fails, re-download the MRZ model with `cacheMethod: "refresh"` to get past a stale or corrupt cached file.
- Only if both MRZ attempts failed, load the stock `eng` engine as the fallback.
Every engine gets the same OCR parameters (MRZ character whitelist, PSM 6 single uniform block of text), so the band is read the same way whichever engine wins. Failed loads are never cached: the next read retries the full ladder from the MRZ model.
Functionality is maintained
When only the `eng` fallback loads, scanning still works: the MRZ band is read, candidates are ranked by check digits passed, look-alike repairs are applied, and results can be sent to Validate or Compare. The only difference is accuracy — `eng` is weaker on MRZ text, which is why the user must be told.
User involvement
The scan UI exposes the engine that produced the current result via `activeOcrEngine()` (`lib/ocr/shared-worker.ts`). When the weak `eng` fallback was used:
- The result screen says the fallback engine was used instead of the MRZ model.
- A "Try the MRZ model again" action drops the worker (`resetOcrWorker()`) and retries the ladder from the MRZ model.
Nothing is degraded silently. Covered by `lib/ocr/engine-ladder.test.ts` (six cases: first success, fallback after MRZ failure, last-error rethrow, attempt reporting, empty ladder).