Skip to main content

GM Dealer Source

The quantum_dealers source fetches the US dealer roster for the four GM brands (Chevrolet, Buick, GMC, Cadillac) and feeds the dealers entity. It lives in the us market of projects/ai_gm (src/ai_gm/sources/quantum_dealers.py).

Inventory references each dealer by BAC (dealer.bac). In transformed inventory this is named dealer_code, and the consolidated tier resolves the dealer FK with the asymmetric join on: ["dealer_code>bac", "brand"]. Dealer coverage therefore bounds how many inventory rows resolve a dealer FK.

Current endpoint — quantum-dealer-locator

GM's brand sites expose a native dealer service that resolves dealer details directly by BAC — no geography, no 50-row cap:

GET https://www.<brand>.com/bypass/pcf/quantum-dealer-locator/v1/dealerId/<BAC>
Header: clientapplicationid: quantum (plus standard Accept / locale headers)
  • Records path: payload.dealers[], with address.postalCode, geolocation.{latitude,longitude}, dealerName, dealerUrl, hours, makeCodes.
  • Granularity: one BAC returns one object per brand franchise at that location, each with its own 5-digit dealerCode under the shared 6-digit bac/id.
  • Row identity: one row per brand franchise (bac + dealer_code + brand). Each brand's raw asset enumerates BACs from its brand inventory raw asset, fetches dealer details by BAC, discovers the brand makeCode from quantum_dealer_locator.js with a static fallback, and extracts the matching franchise row.
  • Fetch tolerance: this is a high-cardinality per-BAC fan-out; the raw asset allows up to 1% terminal BAC fetch failures before blocking downstream tiers. Failed BACs still surface in fetch_attempts metadata for follow-up.
  • Current FK: consolidated inventory joins inventory.dealer_code to dealers.bac and also joins on brand, so inventory does not need to carry the 5-digit franchise dealerCode.
  • A latlong+radius variant also exists (the current GMC site's "locate a dealer").

In the verification, this endpoint resolved 1,539 of 1,540 missing BACs (1 dead BAC), every one with real coordinates.

Retired endpoint — Tekion AEC-CP integrations API

POST https://<brand>.aecloud.io/api/aec-cp-integrations-api/p/v1/integrations/locate-dealers
Body: {"zipCode": "<zip>", "radius": 100}
  • Records path: data.locatorResult[] (each element wraps vendor).
  • Identity: vendor.id — the 6-digit BAC, matching inventory dealer.bac. vendor.vendorCode is a separate 5-digit GM-internal code — do not use it.
  • Auth: none; this is the one GM endpoint that needs no client header.
  • Hard limits (verified live): requires a zipCode; caps at 50 vendors per call; no national/unfiltered form.

This is the same Tekion AEC-CP platform that serves inventory and configurator.

Fan-out — hardcoded national zip sweep

fetch_dealers sweeps ai_core.geo.US_NATIONAL_ZIP_SWEEP (58 zips, one call each at 100-mile radius, DEFAULT_RADIUS), then dedups vendors by vendor.id. The zip list is a hand-picked set of metros and per-state cities — not derived from where GM dealers actually are.

Coverage verification — 2026-07-22

A hands-on review measured what this sweep actually collects versus the full dealer population implied by inventory. Method: materialized dealer_locator raw + transformed locally (reproducing production exactly), took the distinct dealer BACs that appear in that day's inventory, resolved the ones the sweep missed via the quantum-dealer-locator endpoint, and classified each miss against the sweep geometry.

The gap

SegmentDealersShare
Inventory distinct dealer BACs (full population)3,836100%
Captured by the current sweep2,29659.8%
Missing — geographic hole (>100mi from every sweep anchor)1,37135.7%
Missing — in-radius (50-cap truncation / other)1684.4%

~40% of dealers-with-inventory are never collected, and the shortfall is ~89% geographic holes, ~11% the 50-cap. The dealer locator has never had national coverage — the gap has existed since onboarding.

Root cause 1 — geographic holes (dominant)

The 58 zips at 100mi cover only 62.4% of the 40,567 populated continental-US ZIP centroids. Whole regions sit >100mi from any anchor (e.g. no Wyoming anchor; Las Vegas, and much of the Intermountain West / Great Plains uncovered). A deterministic greedy cover shows +89 zips reach 99% and +151 reach 100% CONUS coverage at 100mi.

Root cause 2 — hallucinated sweep zips (never vetted)

Three entries in US_NATIONAL_ZIP_SWEEP are not valid ZIP codes; the API silently returns zero results, so those metros have had no coverage since onboarding:

Invalid (in sweep)Intended metroValid replacement
94101San Francisco, CA94102
23201Richmond, VA23221
14601Rochester, NY14602

Root cause 3 — the 50-per-call cap (secondary)

27 of 232 sweep calls (12%) returned exactly 50 (saturated), entirely Chevrolet (20 zips) and GMC (7) — Buick and Cadillac never have enough dealers within 100mi to saturate. Saturated calls are dense metros (NYC, Boston, Philadelphia, Chicago, Detroit, …). Overlap between neighboring metro circles recaptures much of the truncated set, so net cap loss is small (~168 dealers).

Decision

Dealers resolve through quantum-dealer-locator rather than the AEC-CP zip-sweep tier: dealer BACs are enumerated from the inventory feed and each one resolved via the BAC endpoint — deterministic ~100% coverage, richer data, no holes/cap/invalid zips. The dealers asset is downstream of inventory. The old raw + transformed dealer assets are deleted outright (no freeze, no end_date, no dual-source).

Row identity

  • BAC is the inventory FK target. Dealers store the 6-digit BAC in bac because inventory's dealer.bac references that value. The 5-digit GM franchise dealerCode is retained separately as dealer_code.
  • One row per brand franchise. Quantum returns one object per brand franchise at a BAC (e.g. 330505 → 4 dealerCodes, identical address/coords). The implemented extractor filters to the requesting brand's makeCode and emits one row keyed by bac + dealer_code + brand. Consolidated inventory joins by dealer_code>bac plus brand, resolving each vehicle to the brand franchise for that BAC.

History

Old sweep records are discarded (too incomplete). Because dealer rosters are slow-moving, history is synthesized: materialize today's real quantum partition, then carry it back over historical partitions with the central fill_partition_job (ai_core/jobs/fill_partition.py, fill-partition skill), which records filled_from provenance for visibility. Trusted-history-since = the cutover date; earlier partitions are explicitly carried-forward, not independently observed.