---
name: scan-quality
description: AI scan kwaliteit analyse en monitoring. Gebruik bij "scan kwaliteit", "AI herkent niet goed", "te lage confidence", "verkeerde namen", "wrong prices".
argument-hint: "[check|fix|stats]"
user-invocable: true
allowed-tools: Bash, Read, Grep, Glob, Edit, Write
---

Scan kwaliteit analyse + verbetering. Mode: $ARGUMENTS

## Wat doet de scan pipeline

```
foto.jpg → /api/admin/scan-proxy
  → Claude Sonnet 4.6 (cascade naar Sonnet 4.5 → Haiku 4.5)
  → buildProductResponseWithPricing()
    → validateProduct() — naam corrections, platform validation
    → correctGameName() — match tegen 616 games in database
    → lookupPrice() — PriceCharting + franchise markup
  → response { product, confidence, side, productCode, pricing }
```

## Veelvoorkomende kwaliteitsproblemen

### A. Naam = "Onbekend"
**Symptom:** scanResult.name === "Onbekend"
**Oorzaak:**
- Beeld te onscherp/donker/wazig
- Label verwijderd of beschadigd
- Reflectie blokkeert tekst

**Auto-detectie:** confidence wordt verlaagd naar 0.30 in scan/route.ts (line ~120)

**Fix:**
- Lenn maakt nieuwe foto met betere belichting
- OF Lenn vult handmatig naam in via ScanResultEditor

### B. Pokemon zonder subtitel
**Symptom:** name = "Pokemon Mystery Dungeon" (zonder ": Red Rescue Team")
**Impact:** €15-30 prijsverschil (verkeerde game in database)

**Prompt regel** (scan-prompt.ts line ~88):
> Pokemon subtitels VERPLICHT — "Pokemon" alleen is NOOIT een volledige naam

### C. Console zonder kleur+variant
**Symptom:** name = "Nintendo 3DS" zonder " — [Kleur]"
**Auto-detectie:** confidence verlaagd naar 0.40 (NIET auto-approved)
**Fix:** ScanResultEditor — kleur veld toevoegen

### D. Sealed prijs te laag
**Symptom:** sealed game voor €14.95
**Auto-detectie:** scan/route.ts line ~120 — sealed minimum €29.95 (2.5x multiplier)

### E. Verkeerde regio
**Symptom:** PAL game gemarkeerd als NTSC-U
**Auto-detectie:** scan-validate.ts gebruikt productcode prefix als priority bron

## Quick checks

### Hoeveel items hebben lage confidence?
```bash
curl -s "https://gameshopenter.com/api/admin/upload-queue" \
  -H "Authorization: Bearer gameshop-admin-2024" | python3 -c "
import json, sys
d = json.load(sys.stdin)
items = d.get('items', [])
buckets = {'high': 0, 'mid': 0, 'low': 0, 'none': 0}
for i in items:
  sr = i.get('scanResult')
  if not sr: buckets['none'] += 1
  else:
    c = sr.get('confidence', 0)
    if c >= 0.75: buckets['high'] += 1
    elif c >= 0.50: buckets['mid'] += 1
    else: buckets['low'] += 1
print(f'Total: {len(items)}')
print(f'  ≥75% (auto-approve): {buckets[\"high\"]}')
print(f'  50-75% (review):     {buckets[\"mid\"]}')
print(f'  <50% (skip):         {buckets[\"low\"]}')
print(f'  Nog niet gescand:    {buckets[\"none\"]}')
"
```

### Welke items hebben "Onbekend" als naam?
```bash
curl -s "https://gameshopenter.com/api/admin/upload-queue" \
  -H "Authorization: Bearer gameshop-admin-2024" | python3 -c "
import json, sys
d = json.load(sys.stdin)
unknown = [i for i in d.get('items', []) if i.get('scanResult', {}).get('name', '').lower() == 'onbekend']
for i in unknown: print(f'  {i[\"id\"]}: {i.get(\"filename\", \"?\")}')
print(f'Total: {len(unknown)}')
"
```

### Welke models worden gebruikt?
```bash
curl -s "https://gameshopenter.com/api/admin/upload-queue" \
  -H "Authorization: Bearer gameshop-admin-2024" | python3 -c "
import json, sys
d = json.load(sys.stdin)
models = {}
for i in d.get('items', []):
  m = i.get('scanModel', 'unknown')
  models[m] = models.get(m, 0) + 1
for k, v in sorted(models.items(), key=lambda x: -x[1]):
  print(f'  {v}x {k}')
"
```

## Confidence thresholds (huidige instelling)

| Niveau | Confidence | Wat gebeurt |
|--------|------------|-------------|
| 🟢 Hoog | ≥0.75 | Auto-approve in scanOne |
| 🟡 OK | 0.50-0.75 | "Doe alles" approvet, anders review |
| 🟠 Laag | <0.50 | "Doe alles" SKIPT met waarschuwing |
| 🔴 Falen | 0.30 | Onbruikbare naam fallback |

Constanten in `src/app/admin/upload/page.tsx`:
```ts
const AUTO_APPROVE_THRESHOLD = 0.75;
const MANUAL_APPROVE_THRESHOLD = 0.50;
```

## Scan prompt belangrijkste regels

(`src/lib/scan-prompt.ts` — 427 regels totaal)

1. **titleReadLetterByLetter VERPLICHT** — geen verzonnen namen
2. **name = leestekst** — niet beschrijving
3. **Subtitels na ":" verplicht** (Mario & Luigi: Dream Team)
4. **Pokemon volledig** (Mystery Dungeon: Red Rescue Team)
5. **Console: variant + kleur** (3DS XL — Metallic Blauw)
6. **Productcode lezen** (AGB-BPEE = Pokemon Emerald)
7. **Sealed check** — transparant plastic = isWrapped: true
8. **PAL voorkeur** — Lenn verkoopt vanuit Nederland

## Database verbeteren

Als bepaalde games vaak verkeerd herkend worden:
1. Voeg toe aan `src/data/game-database.ts`
2. Met juiste productcodes (DMG/CGB/AGB/NTR/CTR/HAC prefix)
3. Met aliases die de AI vaak gebruikt
4. Met `palPrice` (huidige marktprijs)

```ts
{
  name: 'Pokemon Mystery Dungeon: Red Rescue Team',
  platform: 'Game Boy Advance',
  codes: ['AGB-B24E', 'AGB-B24P'],
  aliases: ['Pokemon Mystery Dungeon Red', 'Mystery Dungeon Red Rescue'],
  palPrice: 49.95,
}
```

## Wat NIET te doen

- ❌ Confidence threshold verlagen onder 0.50 (slechte data wordt gepushed)
- ❌ Auto-approve threshold verhogen boven 0.85 (te weinig items worden geapproved)
- ❌ Pokemon zonder subtitel als auto-approved laten
- ❌ Console zonder kleur als auto-approved laten
- ❌ Scan-proxy response op top-level lezen (data zit in `product` object!)
