Model Identity — Keys and Resolution Across the US Feeds
How the Audi US tiers identify a model, dedup on that identity, and associate entities across feeds. What each source states is on its own page: public API, OneGraph, accessaudi.
The identity problem
An Audi catalog id is
{model_code}{model_version}{extensions}_{catalog_model_year} (e.g.
4NL5SA1S0DWPS_2024). Two of its four parts are unstable: the version changes
arbitrarily, and the catalog model year changes too (less often), so a mismatch
on either does not mean a different model. accessaudi states no catalog id at
all.
The feeds also count years differently. OneGraph's modelId.year is the year
that goes into the catalog id and is stored as catalog_model_year; the year
accessaudi, the carline name and the VIN agree on is the sales model
year, which can run a year ahead — the "2027 Audi SQ6 e-tron" carline has
modelId.year 2026. sales_model_year is therefore its own column on models,
inventory and features. What the catalog year tracks beyond the id it belongs to
is unknown, so nothing downstream reads meaning into it.
The trim key
Of a variant's extensions, at most one names its trim, and the vocabulary is
fixed: WPS is Premium Plus and WPT is Prestige. accessaudi spells the
same trims among its option codes, occasionally in an XP* form (XPP for
Premium Plus, XPR for Prestige). No other extension or option says anything
about trim — WPD (Convenience) and WPR (Technology) are ordinary packages
that merely share the prefix.
A model's lowest trim is never spelled: an id (or an order) with no trim token
is the model's default trim, and which trim that is varies by model — usually
Premium, but e.g. Premium Plus on a model that offers nothing lower. So
trim_code is the token itself — "", "WPS" or "WPT" — where "" is a
known value rather than an unknown, with several tokens resolving to the
alphabetically latest (ai_audi.entities.trim_code). Which token a given model
carries is onegraph_models' to say; see below.
Keys
(sales_model_year, model_code, trim_code)is the model identity: the dedup key of every models asset, the merge key of consolidated models, and the key every foreign key into models joins on.- Features dedup and merge on that identity plus
(pr3_id, parent_package_code). - Non-trim extensions never participate in identity.
model_catalog_idis a descriptive column: consolidated models carry the latest observed id, the one the configurator accepts. - Nothing is synthesized: an accessaudi-only model has a null
model_catalog_idand null trimline columns until the carline feed publishes it.trimline_id/trimline_nameare carline-stated attributes, not identity.
A trimline can list one model code twice — OneGraph sometimes nests an elevated
trim's models under trimline_default. The first entry of a code there is that
code's default trim; later entries keep the trim their own extensions spell.
Treating every model in the first trimline as the default collapses those onto
one key, and the transformed dedup then prunes the loser and its catalog id.
Who decides the trim
Only the carlines feed states which token designates a trim on a given model. The
same code means different things elsewhere: GHASE8's default trimline carries
WPS in its id and has trim "", while 4NL5SA publishes one trimline and
offers WPT as the Executive Plus Package. Reading a token off a vehicle or a
product and calling it the trim gets both wrong.
So a source that holds only a catalog id, or only a flat option list, states the token it saw and nothing more:
catalog_trim_code— the token a catalog id spellsoption_trim_code— the token a vehicle's own option codes spelloffered_trim_codes— the tokens among an accessaudi product's options, on its model row and on each of its feature rows
trim_code is the trim a model has. The carlines feed states it directly, from
its trimline list; every other source gets it in the enriched tier, from
consolidated/models — which carries that feed's verdict for the model years it
publishes, and the accessaudi fan-out's rows for the years it has dropped.
Per-source derivation
| Source | model_code / sales_model_year | trim |
|---|---|---|
| OneGraph carlines | modelId.code; year in the carline name | trim_code from its trimline; "" for the first entry of a model code in the first trimline |
| catalog features | from the catalog id | catalog_trim_code from the id; trim_code in enriched |
| OneGraph StockCarSearch | model.id.code; model.salesModelyear | catalog_trim_code from the id; trim_code in enriched |
| accessaudi orders | stated directly | option_trim_code from the order's codes; trim_code in enriched |
| accessaudi product models | stated directly | offered_trim_codes; one row per published trim in enriched |
Enriched tier
ai_audi.components.TrimResolutionComponent emits
enriched/accessaudi_{models,features,inventory} and
enriched/onegraph_inventory; ai_audi.components.CatalogEnrichmentComponent
emits enriched/catalog_features. Every one of them but
enriched/accessaudi_models resolves trim against the same partition's
consolidated/models, which publishes the sales year as model_year and drops
catalog_trim_code, so the enriched tier restores both — the token by parsing it
back out of model_catalog_id, which was composed from the same extensions.
enriched/accessaudi_models reads transformed/onegraph_models instead. It is a
source of consolidated/models, so resolving against it would close a cycle;
Dagster rejects one with a CircularDependencyError when the global asset job
resolves, whatever partition mapping the edge carries.
A row stating a token is matched to its model on (model_code, sales_model_year, token), against the token that model's own id spells, and
takes that model's trim_code. So a vehicle whose id carries WPS on a model
whose floor is Premium Plus resolves to "", and a token no model of that line
spells resolves to "" rather than inventing a trim. Matching the identity pair
plus the token, rather than the whole id, ignores extensions that say nothing
about trim — which is how a vehicle id the catalog never published still reaches
its model. Where several trimlines spell a token the same way the match is
ambiguous and does not resolve.
A model year the carline feed has dropped leaves consolidated/models holding
only the accessaudi fan-out's rows, which state no catalog id and so spell no
token to match. For those the token is taken at face value where the line
publishes a trim by that name, and refused where it does not — so a 2025 vehicle
spelling WPS on a line offering Premium Plus reaches it, while a 4NL5SA
carrying the Executive Plus Package still falls to "".
accessaudi products fan out to one row per trim the published models list for
their model line, so a product offering a trim-shaped package gets no second
model. A line it does not publish falls back to the row's own tokens — the
coverage accessaudi exists for — and takes no trimline name, which only the
carline feed states. Its models and its features run the same fan-out, so the two
cannot disagree about which trims exist. Every option reaches every trim of its
model: product_models scopes nothing, and a trim-shaped code is itself an
option on the trims that do not take their name from it.
A vehicle writes its trim token in its catalog id, among its own option codes, or
in neither — never two different ones — so the first non-empty of the two is the
token, and an empty id is an absence rather than a claim on the default trim.
GHASE80_2027 names no trim in its id, so a WPT among its features places it on
the Prestige model. A token no model of that line spells buys no trim, so a
4NL5SA vehicle carrying the Executive Plus Package falls to "" — the default
trim every line has. So does a vehicle that spells no token at all.
A vehicle naming no model_code states no trim: trim_code stays null rather
than "", since "" is a claim about a model line and this vehicle names none.
The gateway drops a vehicle's model node now and then, and consolidated
resolves fields across feeds by coalesce — so a confident "" from the feed that
lost the node would beat the real trim from the feed that did place the vehicle.
enriched/catalog_features
(ai_audi.components.CatalogEnrichmentComponent): the transformed tier keys
catalog features on the id — one row per fetch — so revisions sharing a merge key
collapse here to the newest catalog year, then version (the advisory
catalog_revisions_agree check reports what collapsed). The fan-out requests one
id per onegraph_models variant, so a feature's model_catalog_id names one
model row and takes both its sales year and its trim_code from it. The token
match is the fallback for an id no current carline feed states — a partition
backfilled from the BigQuery history, whose ids came from the carline feed of the
day.
onegraph_models states the year only where the carline name carries it, so
inventory
dates the rest: a vehicle states its own sales model year, and where its feed
does not, VIN position 10 does. An id neither states stays null — the catalog
id's own year is never substituted, since it runs a year behind on any carline
that sells a year ahead of it. Such a model has no inventory to measure, and
accessaudi states what can be ordered independently.
Consolidated
Consolidated reads the enriched feeds for every source whose trim
onegraph_models decides, and the transformed feeds for the carlines and PSS, merging models and
features on the identity above with
OneGraph ahead of accessaudi on priority: accessaudi covers model years and
vehicles the public feed has not published yet, while OneGraph stays
authoritative for everything both carry.
Inventory matches on vin first and order_number second, and is keyed on
both. A pre-production order has no VIN, so it is identified by its order
number until one is assigned — at which point the row matches OneGraph on VIN
and its inventory_id changes, since the id hashes the first populated key
field. Only accessaudi populates order_number; OneGraph's factory commission
number is a different identifier domain and stays in commission_number, so
the second match set never pairs rows across the two feeds, and accessaudi's
number survives the merge that gives the order its VIN.
Model years the carline feed no longer covers
Audi drops older model years from the carline feed, and once a year is gone no
row states which of its tokens are trims. consolidated/models still lists the
year, because enriched/accessaudi_models fans a product out to the tokens it
offers when the carline feed publishes no trims for its line. Inventory of that
year takes its token at face value against those rows, which is how a 2025
vehicle spelling WPS reaches Premium Plus.
That fallback trusts a token to name a trim, which is the one thing the carline feed exists to decide, so it is right exactly as often as the token happens to be the trim:
- A line whose floor is the lowest trim —
WPSa real step above a Premium floor — fans out correctly, and its vehicles resolve correctly. - A line whose floor is elevated does not.
GHASE8's floor is Premium Plus and its default trimline's own id carriesWPS, so once its year ages out the fan-out publishes aWPSrow that is really the default trim, and theWPS-spelling vehicles join to it instead of to"". The""row keeps the vehicles whose ids spell no token, so one real trim ends up split across twomodel_ids. - A line publishing no trims does not either.
4NL5SAoffersWPTas the Executive Plus Package, so it would gain aWPTmodel it does not have.
Nothing in a vehicle recovers this. The two tokens agreeing does not help — a
token in the catalog id is variant-defining but need not be the trim — and
consolidated/inventory drops model_catalog_id, catalog_trim_code and
option_trim_code, so the evidence is not there to re-split on later.
What the fan-out rows can be told apart by is trimline_name, which only the
carline feed states: a row carrying one is that feed's verdict, and a row with
null is the fallback's guess. consolidated/models keeps the column, so the
split above is recoverable as a mapping between model_ids — "" and WPS both
naming what was one trim — rather than as per-vehicle forensics.