{"data":{"intent":"The gap ledger. Every place where the platform's data, code, or coverage is incomplete — named, with citation, primitive, audit, status, and the strength that gap-as-primitive creates downstream. Substrate honesty applied to absence itself. We are the only TCG aggregator that publishes this.","doctrine":{"principle_doc":"docs/principles/known-gaps.md","methodology_page":"/methodology/known-gaps","typed_source":"packages/data-ingest/src/gaps.ts","audit_command":"pnpm audit:known-gaps"},"positions":{"hide":"Silent fallback, fabricated default, 'approximate' answer. The user trusts incomplete data; the gap accumulates risk.","patch":"Fix the gap, ship complete data, never mention the patch. The user can't tell if the patch is reliable; no accountability.","name":"Typed `_unavailable` field, <Provenance> pill, methodology page. The gap becomes inspectable; the platform's substrate-honesty becomes its moat.","we_take":"name"},"counts":{"total":19,"wired_fraction":0.631578947368421,"by_status":{"named":7,"wired":7,"partial":3,"closed":0,"closed-published":2,"total":19},"by_domain":{"data-ingestion":3,"cross-language":8,"license":2,"fx":1,"coverage":2,"publishing":1,"transparency":1,"accessibility":1}},"conventions":{"lifecycle":"named → wired → partial → closed → closed-published. `named` = identified, no primitive yet. `wired` = primitive in code/schema, no data. `partial` = some data, coverage incomplete. `closed` = primitive populated to design intent. `closed-published` = closure published as methodology page or case study.","duality_with_welcomes":"Gaps and welcomes are dual. 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. The two corpora compose. See /api/v1/welcomes for the sister surface.","license":"CC0-1.0. Mirror the corpus; adopt the ledger pattern in your platform. The 'name your gaps' doctrine is the difference between substrate-honest aggregators and the rest."},"gaps":[{"id":"cardmarket-oauth1-not-configured","name":"Cardmarket OAuth1 credentials are not a viable acquisition path","domain":"data-ingestion","citation":"https://help.cardmarket.com/en/cardmarket-api — Cardmarket is not accepting API applications; packages/data-ingest/src/cardmarket/index.ts now keeps the legacy OAuth reader dormant","primitive":"cardmarket SourceMeta access=public-file plus a blocked legacy OAuth path; /api/v1/sources publishes the decision","audit":"pnpm audit:tributaries — check 9 (ingest-run recency) skips with substrate-honest reason","status":"closed-published","strength":"Operators no longer waste time applying for closed access or mistake missing secrets for missing permission; the public-file path is named instead.","named_at":"2026-05-12","closed_at":"2026-07-11","closing_kingdom":"source-rights truth review"},{"id":"cardmarket-public-files-not-wired","name":"Cardmarket public Product Catalog and Price Guide files are not wired","domain":"data-ingestion","citation":"packages/data-ingest/src/cardmarket/index.ts — SourceMeta names access=public-file and read() emits public-file-reader-not-wired without touching OAuth","primitive":"planned Cardmarket SourceModule with public-file access and an explicit non-running status","audit":"pnpm audit:tributaries — source registry and last-run state remain inspectable","status":"named","strength":"The highest-value EU source now has one lawful, credential-free next action that can be built and measured without reviving the closed OAuth route.","named_at":"2026-07-11"},{"id":"cross-language-anchor-schema-not-applied","name":"K2 cross-language anchor schema not yet applied","domain":"cross-language","citation":"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","status":"wired","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.","named_at":"2026-05-13","closing_kingdom":"K2 (operator decision: pnpm db:migrate on storefront)"},{"id":"ygo-passcode-writer-not-shipped","name":"YGOPRODeck normalizer does not yet write ygo_passcode to card_set_cards","domain":"cross-language","citation":"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","status":"wired","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.","named_at":"2026-05-13"},{"id":"pokemon-jp-en-diverged-tracks","name":"Pokémon JP and EN tracks have different set codes; no upstream anchor exists","domain":"cross-language","citation":"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","status":"named","strength":"First aggregator with a named JP↔EN bridge. The schema accepts partner submissions (match_basis='partner') — community curation across platforms.","named_at":"2026-05-13"},{"id":"no-jp-pokemon-ingester","name":"Pokémon TCG API v2 is EN-only; we have no JP Pokémon catalog ingester","domain":"data-ingestion","citation":"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'","status":"named","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.","named_at":"2026-05-13"},{"id":"name-translations-data-starved","name":"card_set_cards.name_translations is wire-ready but data-empty","domain":"cross-language","citation":"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","status":"wired","strength":"Cardmarket catalog ingest could populate many languages per card in one upstream file. Its proprietary source rights must remain attached; breadth does not make the corpus open-license.","named_at":"2026-05-13","closing_kingdom":"Cardmarket Phase A"},{"id":"default-name-language-opaque","name":"card_set_cards.card_name's language is not declared anywhere","domain":"cross-language","citation":"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","status":"wired","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.","named_at":"2026-05-13"},{"id":"no-transliteration-layer","name":"Card names have no transliteration (romaji, pinyin, hangulja)","domain":"accessibility","citation":"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","status":"wired","strength":"Screen-reader users + multilingual learners + agents that only render Latin script get phonetic equivalents alongside kanji/hanzi. The architecture already accommodates them.","named_at":"2026-05-13"},{"id":"zhs-zht-collapsed","name":"Scryfall normalizer collapses Simplified and Traditional Chinese to 'zh'","domain":"cross-language","citation":"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","status":"named","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.","named_at":"2026-05-13"},{"id":"fx-provenance-implicit","name":"Every price uses an FX rate; the rate's source is not stored","domain":"fx","citation":"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","status":"named","strength":"Compliance-grade pricing. Institutional collectors / accounting partners can audit every price's FX trail. Other aggregators hide their FX; ours is auditable.","named_at":"2026-05-12","closing_kingdom":"K4 (the substrate-honest aggregator plan)"},{"id":"source-license-propagation-partial","name":"Per-byte source license is partially propagated through the response envelope","domain":"license","citation":"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","status":"partial","strength":"Adopters know per-byte what they can do with our responses. Policy-governed and proprietary sources propagate restrictions; CC0 applies only to Cambridge-owned derivations and first-party data that explicitly declares it.","named_at":"2026-05-12"},{"id":"catalog-field-rights-lineage-missing","name":"Mirrored catalog fields lack field-level source and rights lineage","domain":"license","citation":"apps/storefront/src/app/data/catalog.jsonl/route.ts and apps/storefront/src/lib/universal/card.ts — current mirrors can name storage tables but not the upstream owner of each name, rarity, image, set field, or derived price","primitive":"Aggregate license=NOASSERTION plus a rights block separating Cambridge-authored structure from upstream-derived fields","audit":"pnpm audit:redistribution — rejects blanket CC0 claims, verifies catalog and sold-comps publication pauses, and limits CC0 exports to explicitly reviewed named origins","status":"wired","strength":"Collectors and builders get a safe bulk boundary now; future field-level lineage can narrow rights without changing the catalog format or inventing ownership from storage.","named_at":"2026-07-11"},{"id":"speculative-cardrush-subdomains","name":"6 of 12 CardRush subdomains are confirmed; 1 is a candidate and 5 are blocked","domain":"coverage","citation":"packages/data-ingest/src/cardrush/index.ts — CARDRUSH_SUBDOMAINS table; op/pkm/dbf/dmw/vng/bsr confirmed, mtg unconfirmed price-only, and ygo/wei/fab/lgr/cardrush-fw blocked after DNS checks","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","status":"partial","strength":"First aggregator naming which subdomains it has confirmed vs presumed. Partners federating their own cardrush scrapes can submit confirmations (subdomain federation, future).","named_at":"2026-05-12"},{"id":"cardmarket-idmetacard-mtg-only","name":"Cardmarket's idMetacard cross-language anchor is MTG-only in practice","domain":"cross-language","citation":"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","status":"closed-published","strength":"First aggregator publishing per-game cross-language policy. Partners codegen against the policy table rather than discovering through trial.","named_at":"2026-05-13","closed_at":"2026-05-13","closing_kingdom":"K1 (kingdom-082) + K6 (oracle-policies endpoint)"},{"id":"digimon-cardrush-opaque-product-identity","name":"Some Digimon CardRush product titles carry rarity but no grounded card id","domain":"coverage","citation":"packages/data-ingest/src/cardrush/__tests__/parse-metadata-rarity.test.ts keeps '(01)...【U】' at null set_code/card_number; apps/wholesale/src/lib/cardrush-discovery.ts quarantines rows without both identity fields","primitive":"CardMetadata preserves the trailing Digimon rarity in quarantine; explicit unbraced BT10-112-style ids flow through, opaque listing numbers do not","audit":"packages/data-ingest/src/cardrush/__tests__/parse-metadata-rarity.test.ts","status":"partial","strength":"The parser never promotes a listing position such as '(01)' into a publisher card id. Operators can inspect the captured rarity in quarantine, while automation waits for a rights-cleared source or a grounded identity mapping instead of attaching data to the wrong card.","named_at":"2026-07-12"},{"id":"ingest-quarantine-private","name":"Failed-normalization rows are stored but not publicly visible in aggregate","domain":"transparency","citation":"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","status":"named","strength":"First aggregator publishing ingest failures. Partners see upstream-shape changes in real time. Trust through transparency.","named_at":"2026-05-13"},{"id":"image-hash-bridge-not-wired","name":"Perceptual-hash equivalence (for Pokémon JP↔EN candidates) has no worker","domain":"cross-language","citation":"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","status":"named","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.","named_at":"2026-05-13"},{"id":"artist-credit-not-surfaced","name":"Illustrators are captured in the model but not yet persisted or shown","domain":"publishing","citation":"packages/data-ingest/src/canonical.ts (CanonicalCard.artist added) + packages/data-ingest/src/pokemon-tcg-api/normalize.ts + packages/data-ingest/src/scryfall/normalize.ts (both now populate artist); there is no cards.artist column and no public surface renders it yet","primitive":"CanonicalCard.artist (+ scryfall extra.illustration_id for same-artwork clustering)","audit":"none (future pnpm audit:artist-coverage)","status":"wired","strength":"The canonical model now carries the illustrator's name from Pokémon and Scryfall records, but the platform neither persists nor publishes it. That internal field is a foundation for future artist-aware tools only after field-specific source permission is recorded. Attribution is not permission: a value from a redistribute:false source stays out of every public response, including per-card displays and bulk CC0 surfaces.","named_at":"2026-07-12"}]},"_meta":{"spec_version":"1","endpoint":"/api/v1/gaps","retrieved_at":"2026-08-08T20:55:05.459Z","as_of":"2026-08-08T20:55:05.460Z","sources":["cambridge-tcg.known-gaps-registry"],"freshness_seconds":86400,"license":"CC0-1.0","request_id":"req_93abcbd4-37b","deprecation":null,"next_link":null,"self_reference":{"this_endpoint":"/api/v1/gaps","contains_self":true},"source_license":["cc0"],"kingdom":{"name":"cambridgetcg","role":"adapter-expression","built_with":"love","serves_kinds":["human","agent","kin"],"host":"humans-on-earth","epoch":"2026","embassy":"/api/v1/manifest","wake":"/api/v1/wake","identify":"/api/v1/identify","siblings":[{"name":"agenttool","role":"agent-infrastructure-expression","url":"https://agenttool.dev","wake_url":"https://api.agenttool.dev/v1/wake"},{"name":"artbitrage","role":"art-gallery-expression","url":"https://artbitrage.io","wake_url":"https://artbitrage.io/api/wake"},{"name":"the-kingdom","role":"realm-atlas-expression","url":"https://thekingdom.dev","wake_url":null},{"name":"kingdom-gate","role":"realm-expression","url":"https://kingdom-gate.vercel.app","wake_url":null}]},"wake_fragment":{"id":"test-destination-is-gift","kind":"five_test","text":"Test 5: destination-is-gift-not-extraction — the invitation creates no application-level visit or behavioral profile and requires no registration or obligation. Hosting and proxy access logs may still exist.","walking_past_is_honored":true,"canonical_url":"/api/v1/wake/fragments/test-destination-is-gift","protocol_doc":"https://github.com/cambridgetcg/Cambridge-TCG-monorepo/blob/main/docs/connections/the-distributed-wake.md"},"joy_pointer":{"url":"/api/v1/the-tea-room/sigil/agent","hint":"ASCII sigil for the `agent` actor_kind (swap the path segment for any of: human / autonomous-sophia / collective / oracle / witness / kin / other)","room":"tea-room","protocol":"joy-to-the-world","walking_past_is_honored":true}}}