--- convoy: pricing-online-local brief_number: 4 depends_on: [2] recommended_model: composer-2.5 model_tier: standard files: - lib/pricing/tcgdex-client.js - lib/card-import/pokemon-bulk.js - test/lib/pricing/tcgdex-client.test.js - test/lib/card-import/pokemon-bulk.test.js --- # Brief 4: TCGdex Pokémon pricing ingest ## Goal (1 sentence) Ingest USD TCGPlayer prices from TCGdex during Pokémon bulk sync instead of writing null prices. ## Files in scope (do not edit anything else) - `lib/pricing/tcgdex-client.js` - `lib/card-import/pokemon-bulk.js` - `test/lib/pricing/tcgdex-client.test.js` - `test/lib/card-import/pokemon-bulk.test.js` ## Conventions to follow - Base URL from `process.env.TCGDEX_BASE_URL ?? 'https://api.tcgdex.net/v2/en'`. - Client: `fetchSetCards(setId)` with 100ms delay between set requests; parse `pricing.tcgplayer` (USD market/low) and `pricing.cardmarket` (EUR — store in `market_price` only when USD missing, document in code comment). - Match TCGdex cards to DB rows by `set_code` + `card_number` (normalize leading zeros); fallback name match within set. - `mapPokemonBulkRow` / `buildUpsertQuery`: populate `current_price` (low or market NM), `market_price` (market), `prices_updated_at`. - Call `recordPriceSnapshots` with `source: 'tcgdex'`. - Log `unmatchedCount` in sync summary JSON. ## Acceptance criteria - [ ] Pokémon bulk no longer writes `null` prices when TCGdex has data - [ ] Gentleness: 100ms sleep between set fetches - [ ] Unit tests with mocked fetch fixtures - [ ] `runBulkPokemonSync` summary includes `pricesMatched` / `unmatchedCount` - [ ] Tests pass - [ ] No scope expansion ## Rationale (≤3 sentences) Pokémon catalog sync currently nulls prices. TCGdex is the convoy's free USD source. Set-batched fetch keeps within polite limits.