Skip to main content

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_id is 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_id and null trimline columns until the carline feed publishes it. trimline_id / trimline_name are 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 spells
  • option_trim_code — the token a vehicle's own option codes spell
  • offered_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

Sourcemodel_code / sales_model_yeartrim
OneGraph carlinesmodelId.code; year in the carline nametrim_code from its trimline; "" for the first entry of a model code in the first trimline
catalog featuresfrom the catalog idcatalog_trim_code from the id; trim_code in enriched
OneGraph StockCarSearchmodel.id.code; model.salesModelyearcatalog_trim_code from the id; trim_code in enriched
accessaudi ordersstated directlyoption_trim_code from the order's codes; trim_code in enriched
accessaudi product modelsstated directlyoffered_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 — WPS a 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 carries WPS, so once its year ages out the fan-out publishes a WPS row that is really the default trim, and the WPS-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 two model_ids.
  • A line publishing no trims does not either. 4NL5SA offers WPT as the Executive Plus Package, so it would gain a WPT model 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.