fix(vinpin): stop budget-burn + seat-poison on undecodable Renault when Rpartstore down
FIX A (runRenaultFlow): when Rpartstore is UNAVAILABLE (down-cooldown or it
never loaded, so `primary` is only a placeholder ambiguous), a CLEAN Dialogys
not_found is now DEFINITIVE. The old gate required BOTH catalogs to say
not_found, so every undecodable Renault while Rpartstore was down got
downgraded to ambiguous → 3x retry → 180s budget → sessionPoisoned, which
then degraded later decodes. A genuinely-ambiguous Dialogys (unreachable)
still retries. When Rpartstore actually ran, both-must-agree is preserved.
runDialogysSearch now logs its outcome + truncated OCR header so this class
is diagnosable from prod logs.
FIX B (vinpin.constants): add old R-number + TR-badge Renault model tokens
(R5/R9/R11/R12/R19/R21/R25, Europa/Broadway/Toros/Flash). R-prefixed form
only — no bare numerics that could false-match year/engine digits.
FIX C (vin-validator extractModelYear): the position-10 year code repeats every
30 years ("T" = 1996 or 2026) with no clean VIN-only rule. New optional
{modelResolved:false} signal: for a brand-only decode of an old-shaped Renault
VIN (Renault WMI + numeric-led VDS type code) whose code pins to the current
cycle's leading edge, roll back one 30-year cycle so a ~1996 R19 isn't labelled
2026. Narrow: model-resolved or modern-shaped VINs are unchanged. Corgi's
WMI-only decoder wired to pass modelResolved:false.
Keeps never-throw / VINPIN_DECODE_BUDGET_MS / sessionPoisoned semantics and the
Fiat + working Renault paths intact. tsc clean; vinpin + corgi + extractModelYear
tests green (new tests cover A and C).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -257,12 +257,55 @@ const VIN_YEAR_CODE_ANCHOR: Record<string, number> = {
|
||||
* tahminidir. Servis (PL24/EMEX/NHTSA vb.) bir model yılı veriyorsa HER ZAMAN o
|
||||
* değer kullanılmalıdır; bu fonksiyon onun yerine geçmez.
|
||||
*/
|
||||
export function extractModelYear(vin: string, referenceYear?: number): number | null {
|
||||
export interface ExtractModelYearOptions {
|
||||
/**
|
||||
* Decode SADECE markayı çözebildiyse (model çözülmediyse) `false` geçilir.
|
||||
* Pozisyon-10 yıl kodu 30 yılda bir tekrarlar; "T" hem 2026 hem 1996'dır ve
|
||||
* VIN-only kesin bir ayrım YOKTUR. Yalnızca decode brand-only kaldığında VE VIN
|
||||
* eski Renault sayısal tip-kodu şeklindeyken, güncel döngünün ön kenarına düşen
|
||||
* (yani "şimdi/gelecek yıl" gibi görünen) belirsiz bir yılı bir önceki döngüye
|
||||
* çekeriz — böylece 1996 model bir R19 yanlışlıkla 2026 olmaz. Model çözülen ya
|
||||
* da modern-şekilli VIN'ler ETKİLENMEZ (varsayılan davranış korunur).
|
||||
*/
|
||||
modelResolved?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eski Renault/Dacia (pre-2000) VIN şekli: Renault akış WMI'si (VF1/VF2/VF6/VF7
|
||||
* veya Dacia UU1) + sayısal-öncüllü tip kodu (VDS ilk karakteri RAKAM — ör. "553",
|
||||
* "5R4"). Modern Renault VDS tip kodları harf-öncüllüdür (RJ/RF/RH gibi), bu yüzden
|
||||
* bu heuristik modern araçları dışarıda bırakır. Spec kuralı değildir — bu yüzden
|
||||
* yalnızca brand-only decode ile birlikte (aşağıda) tetiklenir.
|
||||
*/
|
||||
function isOldRenaultShape(upperVin: string): boolean {
|
||||
const isRenaultWmi = /^VF[1267]/.test(upperVin) || upperVin.startsWith("UU1");
|
||||
if (!isRenaultWmi) return false;
|
||||
return /^[0-9]$/.test(upperVin[3] ?? "");
|
||||
}
|
||||
|
||||
export function extractModelYear(
|
||||
vin: string,
|
||||
referenceYear?: number,
|
||||
opts?: ExtractModelYearOptions,
|
||||
): number | null {
|
||||
if (!vin || vin.length < 10) return null;
|
||||
const anchor = VIN_YEAR_CODE_ANCHOR[vin.toUpperCase()[9]];
|
||||
const upper = vin.toUpperCase();
|
||||
const anchor = VIN_YEAR_CODE_ANCHOR[upper[9]];
|
||||
if (anchor == null) return null;
|
||||
const cutoff = (referenceYear ?? new Date().getFullYear()) + 1;
|
||||
const ref = referenceYear ?? new Date().getFullYear();
|
||||
const cutoff = ref + 1;
|
||||
let year = anchor;
|
||||
while (year > cutoff) year -= 30; // imkânsız geleceği bir önceki 30-yıllık döngüye çek
|
||||
|
||||
// 30-yıllık döngü belirsizliği (kanıtlı vaka: VF1553K05TR596166 = ~1996 Renault 19,
|
||||
// pozisyon-10 "T"). Kod hem yakın-geçmiş hem ~30 yıl öncesine denk gelir ve VIN-only
|
||||
// kesin bir kural yoktur. SADECE elimizde aracın eski olduğuna dair pozitif kanıt
|
||||
// olduğunda — decode brand-only kaldı (model yok) VE VIN eski Renault sayısal
|
||||
// tip-kodu şeklinde — güncel döngünün ön kenarına (year >= ref, yani "şimdi/gelecek")
|
||||
// pinlenmiş yılı bir önceki döngüye çekeriz. Aksi halde eski bir R19 yanlış 2026 olur.
|
||||
// DAR tutulur: model çözülen ya da modern-şekilli VIN'lerin yılı asla değişmez.
|
||||
if (opts?.modelResolved === false && year >= ref && isOldRenaultShape(upper)) {
|
||||
return year - 30;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user