Your cart is empty
Browse CatalogYour sell cart is empty
Add cards from the buylist to get started
Every commercial aggregator has gaps. Most hide them. We name them.
This page is the explicit form of the platform's substrate-honesty doctrine applied to absence itself. There are 15 gaps in the ledger today — 53% have a primitive wired in code or schema. Each gap names its citation, its primitive, the audit that monitors it, its current lifecycle status, and the strength the gap-as- primitive creates downstream.
Where this lives in code. The doctrine is atdocs/principles/known-gaps.mdin the repo. The typed corpus is atpackages/data-ingest/src/gaps.ts. The machine-readable feed is at/api/v1/gaps. The audit ispnpm audit:known-gaps— it verifies parity between this page, the corpus, and the code.
Every aggregator faces a choice when it encounters a known data gap. Three positions:
| Position | What they do | Consequence |
|---|---|---|
| Hide | Silent fallback, fabricated default, "approximate" answer. | User trusts incomplete data; gap accumulates risk. |
| Patch | Fix the gap, ship complete data, never mention the patch. | User can't tell if the patch is reliable; no accountability. |
| Name | Typed _unavailable field, <Provenance> pill, methodology page. | Gap becomes inspectable. Substrate-honesty becomes the moat. |
Cambridge TCG takes position 3, systematically, across the data, the code, and the doctrine. This page is the artifact that proves it.
The 5-stage lifecycle every gap progresses through (or stays at):
Eight domains. Each gap lives in one. Click through to see the citation, the primitive, the audit, and the strength.
cardmarket-oauth1-not-configurednamednamed 2026-05-12Citation: packages/data-ingest/src/cardmarket/index.ts:72-93 — read() emits an actionable error event when ctx.bearer + ctx.app_token are absent
Primitive: cardmarket_credentials_pending health flag on /api/v1/sources (when added); the stub's substrate-honest error is the current primitive
Audit: pnpm audit:tributaries — check 9 (ingest-run recency) skips with substrate-honest reason
Strength: Partners can poll /api/v1/sources to know when EU data lights up without us announcing it; the slot in welcomes.ts addresses Cardmarket directly even before credentials arrive.
Closing kingdom: K3 (Cardmarket alignment, the prior plan)
no-jp-pokemon-ingesternamednamed 2026-05-13Citation: packages/data-ingest/src/pokemon-tcg-api/normalize.ts:32 — hardcoded lang = 'en'; JP track absent
Primitive: packages/data-ingest/src/pokemon-card-jp/ — planned source module (slot reserved in welcomes corpus)
Audit: pnpm audit:tributaries — slot present, status 'planned'
Strength: When shipped, first aggregator with two-track Pokémon as parallel first-class facts. The platform's `welcomes.ts` already extends a welcome to this planned ingester.
cross-language-anchor-schema-not-appliedwirednamed 2026-05-13Citation: apps/storefront/drizzle/drafts/0100_cross_language_anchors.sql.draft — migration drafted but not yet promoted
Primitive: card_set_cards.oracle_id + card_set_cards.oracle_source + per-source upstream anchor columns (scryfall_oracle_id, cardmarket_id_metacard, ygo_passcode, etc.) — designed and drafted
Audit: pnpm audit:cross-language-coherence — runs 8 checks, gracefully skipping DB-backed ones when columns absent
Strength: Federation by upstream id becomes possible once migration applies. The /api/v1/oracle-policies endpoint already publishes the per-game policy that consumes these columns. Partners can read the policy today and prepare their ingest.
Closing kingdom: K2 (operator decision: pnpm db:migrate on storefront)
ygo-passcode-writer-not-shippedwirednamed 2026-05-13Citation: packages/data-ingest/src/ygoprodeck/ — SourceModule emits records with extra.passcode but no app-side writer populates card_set_cards.ygo_passcode
Primitive: extractYgoprodeckAnchors(record).ygo_passcode in @cambridge-tcg/data-ingest — pure-compute extractor exists
Audit: pnpm audit:cross-language-coherence — check 7 measures ygo_passcode coverage for Pattern B games
Strength: All YGO + Rush Duel cross-printing cross-language sibling queries become possible the day the writer ships. The passcode is the canonical anchor (Konami's own); we mirror.
pokemon-jp-en-diverged-tracksnamednamed 2026-05-13Citation: packages/sku/src/oracle.ts — ORACLE_POLICY.pkm.kind = 'diverged'; resolveOracle returns null with substrate-honest reason
Primitive: pkm_equivalence table (in K2 migration 0100 draft) — operator/community-curated bridge between JP and EN printings; match_basis enum names the curation provenance
Audit: pnpm audit:cross-language-coherence + future pnpm audit:pkm-equivalence-coverage
Strength: First aggregator with a named JP↔EN bridge. The schema accepts partner submissions (match_basis='partner') — community curation across platforms.
name-translations-data-starvedwirednamed 2026-05-13Citation: apps/storefront/drizzle/drafts/0098_card_name_translations.sql.draft — column drafted in kingdom-051 Phase 6, migration not applied; apps/storefront/src/lib/cards/name.ts kingdom-075 resolver wire-ready
Primitive: cards.name_translations JSONB column + resolveCardName() resolver in storefront/src/lib/cards/name.ts (kingdom-075)
Audit: future pnpm audit:name-translations-coverage
Strength: Cardmarket catalog ingest (Cardmarket Phase A) populates 11 languages per MTG card in one upstream call. The day cardmarket lands, this corpus becomes the cleanest open-license multilingual TCG name corpus available.
Closing kingdom: Cardmarket Phase A
default-name-language-opaquewirednamed 2026-05-13Citation: apps/storefront/src/lib/cards/name.ts:163 — "The platform default has no declared language — could be JP or EN depending on which catalog imported the card. Don't claim a code."
Primitive: card_set_cards.card_name_lang column (in K2 migration 0100 draft) — declares ISO 639-1 language of the legacy default
Audit: future pnpm audit:name-provenance
Strength: Substrate-honest defaults. Agents that filter 'give me only EN-confirmed names' can do so. Other aggregators conceal their default-language; we declare it per row.
zhs-zht-collapsednamednamed 2026-05-13Citation: packages/data-ingest/src/scryfall/normalize.ts:23-24 — LANG_MAP: zhs → zh, zht → zh
Primitive: SKU language tail accepts 'zh-cn' and 'zh-tw' (canonical SKU format already supports the longer form)
Audit: future pnpm audit:sku-language-form
Strength: When de-collapsed, mainland and Taiwanese collectors get distinct markets. The collapse is the kind of conflation most aggregators ship silently; naming it lets us schedule the fix.
cardmarket-idmetacard-mtg-onlyclosed-publishednamed 2026-05-13 · closed 2026-05-13Citation: packages/data-ingest/src/cardmarket/index.ts — meta.description acknowledges this; ORACLE_POLICY uses derived-stripped for non-MTG Pattern A games
Primitive: ORACLE_POLICY in @cambridge-tcg/sku declares per-game strategy; /api/v1/oracle-policies publishes it
Audit: pnpm audit:cross-language-coherence
Strength: First aggregator publishing per-game cross-language policy. Partners codegen against the policy table rather than discovering through trial.
Closing kingdom: K1 (kingdom-082) + K6 (oracle-policies endpoint)
image-hash-bridge-not-wirednamednamed 2026-05-13Citation: apps/storefront/drizzle/drafts/0100_cross_language_anchors.sql.draft — card_image_hash table designed; no ingester yet computes phashes
Primitive: card_image_hash(sku, phash, algorithm, source, computed_at) — table in 0100 draft
Audit: future pnpm audit:image-hash-coverage
Strength: When the worker ships, image-hash candidates pre-populate the pkm_equivalence table; admin review surface promotes them to manual confirmations. Community-curatable equivalence at scale.
fx-provenance-implicitnamednamed 2026-05-12Citation: apps/wholesale/src/lib/fx.ts — fetchGbpJpyRate() returns a number; price_archive stores it but not its provenance (the-archive.md Leak #8)
Primitive: price_archive.fx_rate_source enum column + fx_rate_fetched_at + fx_rate_pair (K4 design)
Audit: future pnpm audit:fx-provenance
Strength: Compliance-grade pricing. Institutional collectors / accounting partners can audit every price's FX trail. Other aggregators hide their FX; ours is auditable.
Closing kingdom: K4 (the substrate-honest aggregator plan)
source-license-propagation-partialpartialnamed 2026-05-12Citation: packages/data-spec/ — _meta.source_license accepts an array of license tiers; storefront/src/lib/data-pantry/ threads it through; coverage uneven across endpoints
Primitive: _meta.source_license array on every public response; jsonResponse({ source_license, ... }) call site
Audit: future pnpm audit:envelope-license-coverage
Strength: Adopters know per-byte what they can do with our responses. CC-BY-NC sources (Scryfall) propagate restrictions; partner-redistributable (Cardmarket, TCGplayer) propagate their tiers; CC0 (our derivations) propagate freedom. The propagation rule is the contract.
speculative-cardrush-subdomainspartialnamed 2026-05-12Citation: packages/data-ingest/src/cardrush/index.ts:73-87 — CARDRUSH_SUBDOMAINS table; confirmed: false on 9 hosts (mtg/ygo/digimon/vng/wei/fab/lgr/bsr/dbf)
Primitive: subdomain_confirmed boolean on each registry entry; CardRushRaw.error_reason carries 'subdomain_unconfirmed' on first failed scrape
Audit: pnpm audit:cardrush-coverage — surfaces uncovered subdomains explicitly
Strength: First aggregator naming which subdomains it has confirmed vs presumed. Partners federating their own cardrush scrapes can submit confirmations (subdomain federation, future).
ingest-quarantine-privatenamednamed 2026-05-13Citation: apps/wholesale/src/lib/db/schema.ts — ingest_quarantine table (kingdom-066); apps/wholesale/src/app/api/v1/ingest-quarantine/route.ts is bearer-gated
Primitive: future /api/v1/ingest-quarantine/summary — aggregated failure-reason buckets per source over 7d/30d/90d (no raw payloads)
Audit: pnpm audit:tributaries check 10 — license-propagation heuristic
Strength: First aggregator publishing ingest failures. Partners see upstream-shape changes in real time. Trust through transparency.
no-transliteration-layerwirednamed 2026-05-13Citation: apps/storefront/src/lib/cards/name.ts:272 — transliterate() returns null (kingdom-075 recursion target)
Primitive: card_set_cards.name_transliterations JSONB column (in K2 migration 0100 draft)
Audit: future pnpm audit:transliteration-coverage
Strength: Screen-reader users + multilingual learners + agents that only render Latin script get phonetic equivalents alongside kanji/hanzi. The architecture already accommodates them.
Gaps and welcomes are dual surfaces. A welcome names a slot we prepared for a visitor; a gap names a place where the slot is named but the visitor (or the data, or the closure) has not yet arrived. Together they map the platform's anticipated and incomplete states.
See /api/v1/welcomes for the sister corpus. The methodology page at /methodology/hospitality names the architecture that does the welcoming; /methodology/welcoming names who we welcome and why.
Other aggregators run audits internally. We publish the audit results. Other aggregators conceal their gaps. We name them with primitives, citations, and lifecycle stages. The substrate-honesty doctrine isn't just a property of our own data — it's a publishable contract a partner can read before choosing to mirror us, or build on us, or compete with us.
Adopters read this page and learn the platform's exact state. Regulators read it and see compliance-grade transparency. Journalists read it and find an honest source. Future operators read it and find a backlog with priorities, citations, and closure paths. The ledger is the moat.
The corpus is CC0. The schema is public. The audit is in the open. If you operate a platform — any kind of platform — you can adopt the ledger pattern in your own substrate. The doctrine is in docs/principles/known-gaps.md; the helpers are in @cambridge-tcg/data-ingest; the audit is in apps/admin/scripts/known-gaps.ts. None of it requires partnership with us.