Root cause: ffmpeg is absent in the prod worker, so vinpin.ocr cropScale silently
returns the full 1600x900 frame — the tiny centered Rpartstore launch-error modal
is illegible, so a DOWN Rpartstore is misread as a spinner and thrashes the seat.
- Dockerfile: install ffmpeg so cropScale actually crops+3x-upscales (restores all
clipped OCR: modal-crop error detect, Fiat modal, Renault header).
- ensureRpartstore: gate the "already open" short-circuit on a CONTENT token
(rpartstoreLoaded), not rpartstoreOpen which false-matches the flyout/title word
"Rpartstore"; click the flyout entry when the submenu is up over the grid; keep a
late open-window branch so a spinner/vehicle-page window still counts as present.
- closeRpartstoreTab: never click windowClose(1298,14) (it hits the language
selector and wedges the grid); gate the retry on a content token; Escape after.
- cleanTeardown: dismiss any blocking modal BEFORE "Çıkış yap" so logout is a clean
RDS log-off (not a dirty channel disconnect); recover a Disconnected dialog via
disconnectedClose(953,505) + relaunch VINPIN app for a fresh grid.
- Rpartstore-down cooldown (10m): a launch-error / repeated load-failure routes
Renault decodes straight to Dialogys (skip reopening a down catalog); a confirmed
load clears it.
- ensureBrandGrid: dismiss a wedging launch-error modal + short-retry instead of
burning the 84s dead-wait.
- parseRenaultHeader: ignore the status-bar license-expiry date when reading the
model year; sessionAlive matches RDST01/RDST02 (seat load-balances).
- Keeps never-throw, VINPIN_DECODE_BUDGET_MS, sessionPoisoned, the acquire cap,
spinner-guard, relogin-cap and the Fiat/Dialogys fallbacks intact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
VINPIN köprüsüne Fiat'ın yanına Renault/Dacia desteği eklendi.
- Marka yönlendirme: selectVinpinBrandFlow(vin) WMI'den akışı seçer
(Renault/Dacia → Rpartstore/Dialogys, geri kalan → mevcut Fiat ePER,
Fiat davranışı byte-identical). isVinpinBrandAllowed allowlist'i
fiat + renault + dacia'ya genişletildi.
- Renault akışı durum-toleranslı: koltuk son katalogu (Rpartstore)
sunucu-taraflı hatırlayıp açık resume ediyor; ensureRpartstore grid /
submenu / açık-pencere / yükleniyor durumlarını tanıyıp kendini
toparlıyor. Rpartstore ana akış, Dialogys best-effort fallback.
- Ortak ensureBrandGrid artık resume olmuş yabancı katalog penceresini
(Rpartstore/Dialogys/ePER) kapatıp grid'e dönüyor → Renault↔Fiat
ardışık decode'ları (tek koltuk) artık kırılmıyor.
- parseRenaultHeader/isUsableRenaultParse: OCR başlığından model+marka
(RENAULT/DACIA) + yıl; bilinen-token allowlist ile contiguous model
koşusu. Matcher + processor kararlaştırılan markaya göre PL24
catalog_vehicle eşliyor (Fiat varsayılan korunur).
- Testler: Renault parser + marka-yönlendirme/allowlist birim testleri.
Flag-gated (VINPIN_ENABLED). Canlı doğrulama: 5/5 Renault/Dacia VIN
(Kadjar, Clio II, Clio IV, Dacia Duster, Latitude) doğru decode; Fiat
Egea spot-check korunuyor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A failed ePER VIN lookup ("vehicle not found") whose Cyrillic "не найден" alert
OCRs to garbage ("He HaaeHs") slipped the not-found check; the parser then
grabbed the operator's last-browsed catalog from the breadcrumb
("FIAT » TIPO-EGEA") and false-mapped it to a catalog — NM4131 landed on the
ancient "Tipo 1100-1370-1580 (1987-1993)", plus 2× PANDA. Every genuine decode
carries a SINCOM; the breadcrumb false positives never do.
- parser: isUsableParse now requires a SINCOM (a model alone is not a decode)
- matcher: modelTokens keeps 4-digit engine displacements (1100/1370/1580) —
only 1950-2039 count as years — so ancient catalogs no longer collapse to
["TIPO"] and win the fewest-extra-tokens tiebreak
- matcher: recency tiebreak on equal score — never fall back to an ancient
generation for a bare / year-less model
15 unit tests (parser + matcher) pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When PL24/pcat/emex can't decode a Fiat VIN, decode it via the Vinpin ePER
web catalog (warm-session Playwright worker, single seat, BullMQ concurrency 1),
cache the exact vehicle in vinpin_decodes, match it to PL24's existing
catalog_vehicle for that model, and serve the parts from there. Vinpin = decode
oracle only; PL24 already holds the parts (e.g. Egea/Linea/Doblo).
Strictly gated behind VINPIN_ENABLED (default false) + a Fiat-only brand
allowlist: with the flag off, decodeVin behaviour is byte-identical and the
queue is never touched (covered by tests). Coordinates/selectors in
vinpin.constants.ts are marked TUNE-AGAINST-LIVE-PAID-SEAT.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>