689 Commits

Author SHA1 Message Date
1fade89df4 fix(vinpin): prefer exact model over wrong-market catalog in matcher
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
The Vinpin catalog matcher could route a European Renault VIN
(VF1RFE00653633190, decoded "KADJAR") onto the China-market catalog
"KADJAR ÇİN" (source XZH, 22 categories) instead of the correct European
"KADJAR" (XFE, 44 categories), serving wrong parts. Root cause: the
Turkish "ÇİN" lost its Ç/İ to the ASCII token strip and collapsed to a
dropped 1-char "N", so "KADJAR ÇİN" tokenized identically to "KADJAR" —
the market qualifier was invisible to the scorer.

Fixes:
- modelTokens now folds diacritics (NFD + combining-mark strip) so
  "ÇİN" survives as the ASCII token "CIN".
- Scorer prefers an EXACT normalized model match (no extra tokens) over
  a superset, via a bonus kept smaller than the year-range swing so
  multi-generation routing (2022 TIPO-EGEA → MCA) is unaffected.
- New MARKET_QUALIFIERS set (ÇİN/CIN, CHINA, CHINE, RUSYA, ... grounded
  in the real Renault/Dacia catalog rows) heavily penalizes candidates
  whose EXTRA tokens are region qualifiers; generation tokens are not
  penalized, so generations stay matchable.
- Richer-catalog (categoryCount) tiebreak among equal-score candidates;
  match() query selects the category count via a correlated subquery.

Adds unit tests incl. the KADJAR case, CLIO/DUSTER generation cases, and
diacritic-folding. Fiat behavior unchanged; all 39 vinpin tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:47:55 +03:00
7069ceaeae fix(web): cover cold Vinpin decode + drop hardcoded Fiat no-catalog copy
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Two Vinpin decode-flow UX bugs in the search page:

1. Poll window (VINPIN_MAX_POLLS 6→30, still 3s interval) now covers a
   cold Vinpin VDI decode (~60-100s) instead of bailing at 18s. On
   poll-exhaust we no longer fall through to the no-catalog dead-end;
   instead a reassuring brand-neutral "still working, ready shortly"
   card (decodePendingHint) — the decode caches server-side so a later
   re-submit returns instantly.

2. The poll-exhaust fallback previously forced brandName:"Fiat" onto the
   no-catalog prompt, so a Renault VIN read "Fiat". Removed. The
   no-catalog copy is brand-aware via the API's noCatalog.brandName, with
   brand-neutral fallback keys (noCatalogHintNeutral / browseCatalogCtaNeutral)
   when no brand is available. A Renault VIN never says "Fiat".

Web-only, no flag. New i18n keys added to tr.json + en.json. No API change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:21:43 +03:00
eaa500ddd5 perf(vinpin): in-session Renault→Fiat swap + OCR-poll waits + input micro-trims
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Latency optimizations in the flag-gated (VINPIN_ENABLED) Vinpin decode driver.
Every change keeps the existing fixed-wait value as a fallback cap, so
worst-case behaviour and robustness are unchanged — only the common case is
faster. No live seat session was run (prod holds the single Horizon seat).

#2 Renault→Fiat symmetric swap: ensureReady no longer tears the whole browser
down on a Renault→Fiat flow switch (was a ~60-80s cold restart). It now keeps
the authenticated Horizon/VinPower session, returns to the brand grid via the
existing ensureBrandGrid (which closes a resumed foreign catalog window) and
reopens Fiat via the normal tile flow — symmetric with the Fiat→Renault
direction. Guarded fallback: if the in-session swap can't reach the grid /
throws, it falls back to close()+cold re-establish.

#3 OCR-poll-until-ready: replaced big fixed post-action sleeps whose completion
is OCR-detectable with a capped poll (screenshot→ocrRegion→regex every ~700ms,
return on match, cap == old fixed wait). Converted: afterFiatOpen(12s),
afterVinSubmit(7s, Fiat modal), afterRpartstoreSubmit(8s),
afterRenaultCatalogOpen(12s, ×2), plus cold-path afterLogin(20s) and
afterVinPowerLogin(18s). Left afterVinpinLaunch(14s) fixed — the VinPower login
dialog has no reliable OCR marker (detected via DOM), and it doubles as a
generic connecting-screen settle. Left afterDialogysSubmit fixed (out of scope).

#4 Micro-trims: field-clear Backspace burst 40→5 (VINPIN_FIELD_CLEAR_BACKSPACES),
key-type delay 45-50ms→20ms (VINPIN_TYPE_DELAY_MS, 17-char VIN ~850→~340ms),
afterAlertDismiss 700→250ms.

New pure/injectable pollForText helper in vinpin.ocr.ts (unit-tested:
early-return, cap-never-exceeded, first-read match). typecheck + biome clean;
31 vinpin unit tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 09:57:40 +03:00
77c93af9a0 feat(vinpin): Renault/Dacia decode akışı (Rpartstore + Dialogys)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
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>
2026-07-14 08:44:09 +03:00
bf49900b57 fix(catalog): Fiat "Birleşik" stays the canonical view, not the raw pcat tree
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Correction to #216: "Birleşik" must fold the catalog into OUR canonical 20-bucket
structure (pcat + eper), not show parts-catalogs' raw tree. Only "Orijinal
Katalog" swaps to the native ePER tree for Fiat.

- treeSource → eper-pekidi for Fiat (Orijinal); canonical tab renders CanonicalView
- reverts #216's "canonical tab → pcat source tree"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 07:11:50 +03:00
5a3e1a2299 feat(catalog): map the two Fiat catalog tabs onto eper/pcat, drop the extra switcher
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Simplify the previous change: instead of a third source-switcher pill, reuse the
two tabs that are already there. For an eper (Fiat) vehicle:
  "Orijinal Katalog" → the native ePER tree (default)
  "Birleşik"         → parts-catalogs
Non-eper vehicles keep the original source/canonical tabs untouched.

- web: derive treeSource from browseMode when the vehicle has eper; canonical
  tab renders the pcat source tree (not CanonicalView) for eper vehicles
- removes the ePER/Diğer switcher, SOURCE_LABELS, and the activeSource state
Net −27 lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 06:56:12 +03:00
de960a0dd4 feat(catalog): eper as the default Fiat catalog source, pcat one tap away
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Fiat dealers know the native ePER tree; parts-catalogs' broader-but-lazy folders
are where they hit gaps (a motor OEM missing under "Motor" — the reason we
crawled pekidi ePER). So for a multi-source decoded vehicle, default the
"Orijinal Katalog" view to eper-pekidi — fully populated with parts + diagrams —
instead of showing every source's roots mixed together.

- categories.service.getCategoryTree(vehicleId, source?): prefer eper-pekidi when
  present; ?source= overrides; cache keyed per source
- categories: getCatalogSources(vehicleId) → sources present, default (eper) first
- controller: tree/:id/sources endpoint + ?source query
- web: source switcher pill (ePER ⇄ Diğer) in source mode when >1 source;
  defaults to the first (eper). pcat never hidden, just one tap away.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 06:42:04 +03:00
70d901f9f5 fix(vinpin): reject stale-breadcrumb decodes (require SINCOM) + harden matcher
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
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>
2026-07-13 12:08:40 +03:00
731ac93a55 Merge pull request 'public-api: katalog gezinme (VIN'siz araç ağacı) endpoint'leri /v1/catalog/* — marka→model→motor→kategori, API-key auth' (#212) from dev into main
Reviewed-on: #212
2026-07-10 12:44:09 +03:00
oyp
0e8b2ab294 public-api: katalog gezinme (VIN'siz araç ağacı) endpoint'leri /v1/catalog/* — marka→model→motor→kategori, API-key auth
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:27:36 +03:00
c51b7b4e78 feat(internal-admin): extend limiti 90 → 3650 gün
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Panelin 'Bonus süre ekle (goodwill)' aksiyonu 360 gün gibi süreler
gerektiriyor; 90 gün sınırı iş ihtiyacını karşılamıyordu. Üst sınır
3650 (10 yıl) parmak-kayması koruması olarak kalıyor. Panel tarafı
(sp) aynı limite güncellendi.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 10:35:28 +03:00
ff81b244b7 Merge pull request 'fix: şerit-bazlı rate bütçesi' (#209) from dev into main 2026-07-09 10:01:55 +03:00
8dd2d223e9 fix(prefetch): şerit-bazlı kaynak bütçesi — fast lane açlığını bitir
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Fast worker canlıydı ama pcat'in 90/dk penceresini ana worker yiyordu → fast
işler sürekli 18s source-rate defer (BMW init'i slot bulamadı). checkSourceRate
artık şerit-bazlı: fast = total/3 (pcat 30), main = kalan (60); toplam
değişmedi → upstream yükü aynı. Cooldown pause'u da doğru worker'ı duraklatıyor.
Spec fast-queue davranışına güncellendi (17/17).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 10:01:47 +03:00
36ed37d5c0 Merge pull request 'feat: prefetch fast kuyruk' (#208) from dev into main 2026-07-09 09:13:34 +03:00
c8803ada35 feat(prefetch): ayrı FAST kuyruk — taze araçlar backlog'la yarışmasın
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Kök neden: ana kuyruk saatlerce üretim>tüketim rejiminde (children patlaması);
lifo'da sonradan eklenen HER iş bir öncekini gömer → taze araç init'leri bile
açlığa düşer (BMW 320i 58dk 0 parça). Fix: catalog-prefetch-fast kuyruğu +
ikinci worker (conc=4, 120/dk) — decode-tetiği ve fast:true işler (children
zinciri dahil) buraya akar; per-source redis rate gate iki worker'ı da ortak
sınırlar (upstream korunur). vehicles.service fast kuyruğa bağlandı.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 09:13:26 +03:00
ccc2cd75a5 Merge pull request 'feat: birleşikte görünüm değiştiriciler' (#207) from dev into main 2026-07-09 08:11:24 +03:00
f2a37b78cb feat(web): Birleşik görünümde grid/ağaç/kolon değiştiricileri çalışsın
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
CanonicalView viewMode prop'u: tree/columns için kanonik veriden sentetik
hiyerarşi (kova → alt-yaprak → gerçek kategoriler; sentetik düğümler children'lı
geldiğinden lazy-fetch tetiklenmez, gerçek kategoriler children'sız geçip normal
drill yapar; tpl-* placeholder'lar bu görünümlere alınmaz — grid'de kalır).
vehicles sayfasında CategoryViewToggle artık canonical'da da görünür; katalog
sayfası viewMode'u geçirir.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 08:11:16 +03:00
0e2871c964 Merge pull request 'feat: katalog sayfası Birleşik toggle' (#206) from dev into main 2026-07-09 08:07:21 +03:00
116def2c79 feat(web): pl24 katalog sayfasına Birleşik/Orijinal toggle
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
/dashboard/catalog/:brand/:modelId sayfasında birleşik görünüm yoktu — backend
endpoint'i (/catalog/vehicles/:id/canonical) hazırdı ama web'e hiç bağlanmamıştı.
CanonicalView'a endpoint/catalogMode/brandName/variantSearch prop'ları eklendi
(varyant qs dahil); sayfaya vehicles/$id ile aynı toggle. Varsayılan Orijinal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 08:07:13 +03:00
bcf8bb38cd Merge pull request 'fix: kanonik sayaç + entry cap' (#205) from dev into main 2026-07-09 07:45:09 +03:00
d38bcaa36f fix(canonical): kova başlık sayacı subgroup'ları da saysın + entry cap 40/kova
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
(1) Placeholder'lar subgroup içinde olduğundan başlık "0 kategori" diyordu —
sayaç artık flat+subgroup birleşik unique. (2) Kalabalık markalar (pl24 BMW
elektrik 537 entry) görünümü boğuyordu — kova başına en-paylaşılan 40 entry
(vehicle_count desc).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 07:45:02 +03:00
25965717c3 Merge pull request 'fix: entries ambiguity' (#204) from dev into main 2026-07-09 07:28:15 +03:00
671e9af15b fix(canonical-backfill): entries GROUP BY'da name'i c.name ile nitele (parent join ambiguity)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 07:28:07 +03:00
da95b0484d Merge pull request 'canonical: şablon entry-seviyesi' (#203) from dev into main 2026-07-09 07:23:51 +03:00
5ed3959a12 feat(canonical): şablon ENTRY-seviyesi — decode anında tam gezinilebilir yapı
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Kullanıcının orijinal tasarımı: aynı (kaynak, marka)da kategori yapısı birebir
aynı — değişen yalnız parça kodları/şemalar. Önceki şablon sadece kova/alt-yaprak
BAŞLIKLARINI overlay ediyordu → taze araçta "Aydınlatma — 0 kategori". Yeni:
canonical_template_entries (migration 0031) — markanın paylaşılan kaynak-kategori
ADLARI (≥2 araç); backfill'de rebuild. overlayTemplate eksik adları disabled
placeholder olarak basar (unavailable=true, CategoryGrid zaten soluk+tıklanamaz
render ediyor) → decode anında her kova tam adlarıyla dolu; drill tamamlanınca
placeholder'lar gerçek satıra dönüşür.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 07:23:43 +03:00
eb92c5c5f8 Merge pull request 'fix: orijinal default' (#202) from dev into main 2026-07-09 07:12:00 +03:00
8a079d84f8 fix(web): Orijinal Katalog tekrar varsayılan — taze araçta boş kanonik kovalar çözülene dek
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 07:11:52 +03:00
1c4c896d64 Merge pull request 'feature/vin-ocr' (#201) from feature/vin-ocr into main
Reviewed-on: #201
2026-07-09 07:05:10 +03:00
3c6c27cb00 fix(vin-ocr): sunucu hata mesajını toast'ta göster
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
"Fotoğraf okunamadı" genel metni gerçek nedeni (örn. 503 "görselden şase
okuma şu anda kullanılamıyor", 400 "desteklenmeyen tür") yutuyordu — dev'de
OPENROUTER_API_KEY boş kaldığında teşhisi zorlaştırdı. ApiError mesajı artık
toast açıklamasında; 429 için Türkçe "çok fazla deneme" metni; PostHog
reason artık http_<status> taşıyor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 06:52:14 +03:00
8d11ae7400 feat: fotoğraftan şase okuma (VIN foto-OCR)
Dashboard şase kutusunun sağına kamera butonu: kullanıcı fotoğraf yükler
(mobilde kamera), görsel istemcide ~1800px'e küçültülüp JPEG'e çevrilir,
POST /vehicles/decode/ocr OpenRouter üzerinden vision modeline gönderir
(varsayılan google/gemini-2.5-flash; bulamazsa openai/gpt-5.4-nano yedeği),
dönen VIN kutuya yazılır. Decode HER ZAMAN kullanıcının "Şase Çöz"
tıklamasıyla başlar — OCR yalnızca yazma zahmetini alır.

Model seçimi 2026-07-09 canlı bake-off'una dayanır (Commons'tan gerçek VIN
fotoğrafları): flash plaka/etiketleri doğru okudu; flash-lite rakam bozdu,
Haiku 4.5 olmayan VIN uydurdu, OCR.space damgalı şasede 8→S hatası verdi.
Sunucu tarafı katı doğrulama (17 karakter, I→1 O→0 Q→9, aksan ayrıştırma)
"regex'ten geçen ama yanlış" VIN'leri keser; okunaksızsa vin:null döner.

- throttle: kullanıcı başına 10 istek/dk; görsel sınırı 5MB (mevcut multipart
  middleware'iyle uyumlu), sadece JPEG/PNG/WebP
- compose: OPENROUTER_API_KEY api VE worker bloklarına eklendi (worker çeviri
  işçisi bu anahtarı bekliyordu ama compose'a hiç enjekte edilmemişti),
  VIN_OCR_MODEL / VIN_OCR_FALLBACK_MODEL api bloğuna eklendi
- analitik: vin_ocr_used eventi (success/reason/confidence/duration_ms),
  query_source="ocr"
- i18n: search.ocr.* (tr+en)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 06:41:33 +03:00
cafe206001 Merge pull request 'Merge branch 'feature/widget-canonical' into dev' (#199) from dev into main 2026-07-08 06:48:39 +03:00
Semih
8054e2faf3 fix(vinpin): query_logs muhasebesi — poll'lar failure satırı yazmasın
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Vinpin fallback'i failure logQuery'sinden ÖNCE denenir oldu:

- Pending decode'un her 3sn'lik frontend poll'u 'No catalog — identified
  as Fiat' failure satırı yazıyordu: vinpin'in 10sn'de ÇÖZDÜĞÜ tek Egea
  VIN'i 17 failure satırı üretti, ham 24h success -20.7pp okundu
  (2026-07-07, 4 kullanıcı/4 VIN, gerçek infra hatası 0).
- Resolved VIN'lerde her decode kalıcı olarak fail+success çifti
  yazıyordu (failure log vinpin lookup'tan önceydi ve negatif cache
  vinpin çözümünden haberdar değil).

Yeni muhasebe: VIN başına TEK coverage-gap failure satırı (ilk görüş,
enqueue anı — coverage backlog sinyali korunur); pending poll'lar hiç
loglanmaz; resolved decode tek success satırı loglar; not_found/failed
eskisi gibi caller'ın failure log'una düşer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 06:40:23 +03:00
7956fc9e9b feat: kategori sağlık paketi — dedup+index, prefetch throughput, v1 taxonomy, Birleşik default
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
(1) migration 0030: 57.780 duplike kategori merge (prod'da uygulandı, idempotent)
+ categories unique index NULLS NOT DISTINCT → decode retry duplike üretimi
KALICI olarak durdu (onConflictDoNothing artık NULL sınıflarında da çalışıyor).
(2) prefetch throughput: backlog %89 pcat + net-büyüyordu (82k→94k). pcat
20→90/dk (DataImpulse rotating gateway), pace 5s→1.5s, conc 3→8, global 180.
pl24/emex dokunulmadı (ban riski).
(3) GET /v1/taxonomy: araçtan bağımsız kanonik yapı kontratı (B2C navigasyon).
(4) Birleşik tekrar varsayılan görünüm (edge-case'ler çözüldü).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 06:26:54 +03:00
394e71e269 feat(widget): kanonik (birleşik) kategori görünümü
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Widget artık /categories varsayılan kanonik cevabını render eder (view=raw
kaldırıldı): kaynak-bağımsız kova → alt-grup → yaprak kategori → parça gezinme.
Frame-tabanlı breadcrumb (her seviye kendi render'ını taşır), ortak gridEl/grid
çizici, leafItem drill'i /categories/:id ile parça+şema. Dashboard'daki
'Birleşik' varsayılanıyla aynı deneyim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:32:55 +03:00
df04784ddb fix(widget): kategori ağacını ?view=raw ile çağır (API default kanonik oldu)
/v1/vehicles/:id/categories artık varsayılan kanonik kova görünümü döndüğü
için, ham ağaç şeklini render eden widget shell'i açıkça view=raw ister —
yoksa {canonical:{name},categories[]} şeklini {name,partCount,id} sanıp
kırılırdı. Kanonik-kova görünümünü widget'a getirmek ayrı iş.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:02:08 +03:00
ae31a7b61a feat(jobs): kanonik backfill gecelik cron + artımlı (keep-fresh) mod
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Faz 2 — decode edilen yeni araçların kategorilerini materyalize tutar:
- Processor'a incremental mod: step-2 yalnız HENÜZ map satırı olmayan yeni
  isimleri sınıflandırır (tüm ~113k değil). Materialize (step-5) differential +
  inheritance NULL-only olduğundan, adı zaten map'li yeni kategori de id'sini
  alır — hiçbir şey atlanmaz. is_manual korunur. reset varsa yok sayılır
  (reset tam yeniden-eşleme demek).
- Gecelik cron: 05:00 İstanbul, { incremental: true }. Dev+prod'da güvenli
  (harici yan-etki yok, kendi DB'sine yazar). Lexicon iyileştirmesi sonrası
  tam yeniden-eşleme hâlâ ELLE { reset:true } global run.
- Dokümante edilen 'backfill trigger yok' boşluğunu kapatır; serving zaten
  anlık lexicon fallback yapıyordu, bu cron materyalizasyonu güncel tutar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:59:19 +03:00
dc65c61440 feat(api): /v1 kategori ağacı varsayılan birleşik (kanonik) + source-strip
Faz 1 — public API kategori ucunu kanonik taksonomiye bağlar:
- GET /v1/vehicles/:id/categories artık VARSAYILAN olarak birleşik (kanonik)
  görünüm döner (kaynak-bağımsız 20 kova + alt-kategoriler,
  getCanonicalTree; materyalize %96,9 L1 + anlık lexicon fallback).
- ?view=raw ile ham kaynak ağacı (kaçış kapısı — plaka-kodu yapraklı yeni
  PSA/Renault kataloglarında birleşik zayıfsa).
- GÜVENLİK: iki görünüm de public-api.sanitize ile temizlenir — 'source'
  (upstream sağlayıcı: parts-catalogs/emex/pl24) ve 'linkWid' (upstream widget
  id'si) ARTIK SIZMAZ. Önceki ham-ağaç ucu bunları sızdırıyordu (Faz 1 widget
  işinde açılmış mevcut sızıntı, burada kapatıldı).
- Swagger: view param + iki görünümün şeması dokümante.
- 6 birim test (sanitizer beyaz-liste + source-drop).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:52:03 +03:00
28720c7d7d fix(canonical): kova → doğal alt-kategori seviyesi (entry-children, subtree count)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Önceki hibrit sadece derin parça-yapraklarını + kök/dal girişlerini gösteriyordu;
kısmen drill edilmiş kovada ("Fren sistemi" subtree'de 39 parça) sistem-kökü
skip ediliyor + 0-parçalı kardeşleri düşüyordu → Fren'de 14 yerine 2. Yeni model
(buildCanonicalInput): her kovanın "entry container" köklerinin AYNI-KOVA
çocuklarını SUBTREE parça sayısıyla göster (drilled olsun olmasın). Audi Fren →
11 alt-kategori (Disk frenler/Fren Kaliyeri/El fren…), orijinal katalog gibi.
On-the-fly bucketing (materialize edilmemiş taze kategoriler için).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:15:52 +03:00
7fb3608dea fix(canonical): kova→alt-kategori doğrudan (redundant "sistem" hop'unu kaldır)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
(1) Serving: "Fren sistemi" gibi sistem-container dalını çocuklarıyla değiştir
(Fren Kaliyeri/Disk frenler/Balata…) → kova doğrudan alt-parçaları içerir.
(2) Frontend: CanonicalView artık subgroups'u (2. seviye: Balata/Disk/Kaliper)
render ediyor — önce sadece flat categories gösteriyordu, alt-seviye işi hiç
görünmüyordu. Böylece Fren'e tıkla → alt-kategoriler doğrudan gelir.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:59:19 +03:00
5a933fec9d fix(canonical-tree): dal-girişi filtresini yaprakları da kapsayacak şekilde düzelt
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Debriyaj sistemi / İç Aydınlatma Sistemi drill-edilmemiş YAPRAK (0 çocuk) —
önceki "çocuğu olmalı" şartı bunları dışlıyordu → hâlâ 0. Şart: mapped + kök-
değil + 0-parça + entry-point (parent farklı kova). Yaprak/dal fark etmez.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:42:25 +03:00
f5d7a27ea3 fix(canonical-tree): drill-edilmemiş dal-girişlerini de göster (Debriyaj/Aydınlatma)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Sorun: aracın "Debriyaj sistemi"/"Aydınlatma Sistemi" kategorisi var + doğru
kovaya map olmuş ama parçası drill edilmemiş → hibrit sadece parça-yaprak +
drill-edilmemiş KÖK gösterdiğinden bu DALLAR düşüyordu → "0 kategori". Fix:
bucket'a map olan, çocuğu olan ama subtree'de parçası olmayan dal-girişlerini de
input'a ekle (parent farklı kova = entry point). Böylece kova boş görünmez,
tıklanınca on-demand drill olur (prefetch beklemez).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:40:33 +03:00
e5fd44820a fix(prefetch): decode-tetiğini fast-lane (lifo) yap — backlog'u atla
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Sorun: yeni decode edilen araç prefetch'i normal öncelikle (5dk delay, fast yok)
enqueue ediliyordu → 82k'lık deep-drill backlog'unun ARKASINA düşüp hiç
işlenmiyordu → taze araçlarda kanonik view boş kalıyordu. Fix: fast:true + lifo
→ taze araç saniyeler içinde drill olur, eski children backlog'unu atlar.
Per-source rate gate upstream'i hâlâ korur.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:19:30 +03:00
f25ef2d54f feat(canonical): marka/platform yapı şablonu — decode anında tam kanonik yapı
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
canonical_structure_template(source, brand, bucket, sub, vehicle_count): drill
edilmiş araçlardan her (kaynak, marka) için görülen kova/alt-yaprak kümesi.
Backfill sonunda yeniden üretilir (global run). Serving overlayTemplate ile
aracın ağacına markanın bilinen ama bu araçta henüz drill edilmemiş kova/alt-
yapraklarını BOŞ ekler → VIN decode olur olmaz FULL kanonik yapı görünür;
parçalar backfill ile dolar. migration 0029. vehicle + catalog akışı.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:46:59 +03:00
7b1daffe97 feat(canonical-tree): hibrit — tüm üst kovalar decode anında görünür
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Sorun: bir araçta sadece TEK bir kategori (drill edilmiş olan) görünüyordu —
"herhangi parça varsa leaf-mode" mantığı drill-edilmemiş kökleri gizliyordu.
Fix: parça-tutan yapraklar + drill-edilmemiş kökler (subtree'sinde parça yok)
birlikte gösterilir. Kök yapı decode'da oluştuğundan tüm 20 kova ANINDA görünür;
parçalar backfill deep-drill ile dolar. vehicle + catalog akışı.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:32:28 +03:00
013cd43e7e fix: Orijinal Katalog varsayılan + canonical duplike-kök dedupe
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
(1) Araç sayfası varsayılanı "Orijinal Katalog" (source) — canonical view'ın
edge-case'leri (duplike/drill-edilmemiş kökler) çözülene kadar.
(2) canonical grouping ad-bazlı dedupe: bazı decode'lar kökü iki kez üretiyor
("Motor, Motor"; Seat Ateca'da her kök 2×) → aynı adı en zengin kopyayla
(parça, sonra çocuk) birleştir. Önceki boş-kök filtresi GERİ ALINDI (drill-
edilmemiş kökleri de siliyordu = "2 kategori" regresyonu).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:06:08 +03:00
34d97b47ac fix(canonical-tree): boş kökleri (çocuksuz+parçasız) root-fallback'ta gösterme
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
emex bazen aynı kökü iki kez decode ediyor (biri boş) → "Motor → Motor, Motor"
gibi redundant başlık. Root-fallback artık sadece çocuğu VEYA parçası olan
kökleri gösteriyor. vehicle + catalog akışlarının ikisinde de.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 07:54:39 +03:00
6f8e9ff618 feat(canonical-lexicon): 3 yanlış-kova override daha (IC Motor prefix + türbo şarj)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
"motor soğutma"→Soğutma (2516, engine cooling — "motor" subsystem qualifier),
"türbo şarj"→Motor (684, "şarj"=charge Elektrik'e çekmiş), "tahrik kayışı"→
Triger (767, drive belt). pcat "IC Motor <sistem>" prefix örüntüsü. 32 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 07:39:32 +03:00
6e556e198a feat(canonical-lexicon): phrase-override katmanı — adjective+head yanlış-kovalar
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Skordan önce yüksek-güven phrase kuralları: "<sistem> yağı$" → Yağlar (end-
anchor, "motor yağı filtresi/pompası" hariç), "(cam|silecek|kalorifer) motoru"
→ Elektrik, "zamanlama (dişli|zincir|kayış)" → Triger. ~1400 motor/şanzıman
yanlış-kovası + ~230 silecek-motoru + ~790 zamanlama düzelir. 29/29 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:48:40 +03:00
75dbadcb47 feat(canonical-sublexicon): SP kuyruğunu küçültmek için yüksek-frekans alt-terimler
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Kuyruğun tepesindeki net-eşlenebilir adları otomatikleştir: gösterge/anten→
Modül, korna→Korna(yeni), silecek→Silecek(yeni), güneşlik→Tavan, havalandırma/
ısıtma/eşanjör→Kalorifer, soğutucu→Radyatör, absorber→Amortisör, dingil→Aks,
pim→Burç, kontrol kol→Salıncak, yağ tava/keçe/basınç→Yağlama, motor takımı→
Komple, iç kontrol→Vites. +2 yaprak (silecek/korna), migration 0028. 6/6 test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 06:28:06 +03:00
1aaf2d8d8b feat(canonical): emex düşük-marka deep-dive terimleri
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
emex Dacia/Toyota/BMW/Kia eşleşmeyenlerinden tek-token distinctive terimler
(üst-lexicon token-bazlı): buharlastirici/kurutucu→sogutma, aktuator/selenoid/
anten/ses→elektrik, lambda/rolanti→yakit, kanat/kiris→panjur, burkma→susp. +
ilgili sub keyword'ler. 2-kelime adlar (su pompası) token-matcher'da eşleşmez,
atlandı → SP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:15:51 +03:00