# CSV → canonical adapters 0.1.0-alpha

Reviewed 2026-09-05. Sources: [Google product data specification](https://support.google.com/merchants/answer/7052112) and [Shopify product CSV documentation](https://help.shopify.com/en/manual/products/import-export/using-csv). Source columns are DIRECT; canonical transformations are DERIVED or INFERRED as noted. This is a bounded export reader, not a complete destination validator or importer. It never submits a feed.

## Google

| Source | Canonical target | Transformation / status |
|---|---|---|
| id | identifier.sku | INFERRED source-item identifier, not proof of merchant SKU equivalence; uniqueness checked separately |
| title, description | product.title, product.description | DERIVED variant title and per-record product description |
| link, image_link, additional_image_link | offer.url, media.primary_image_url, media.additional_image_urls | DERIVED URL strings, comma-separated additional image list; no retrieval |
| price | offer.price | DERIVED amount and explicit uppercase currency token; no numeric coercion or rounding |
| availability, condition | offer.availability, offer.condition | DERIVED explicit tokens, governed value validation |
| gtin, mpn | identifier.gtin, identifier.mpn | DERIVED strings, leading zeros preserved; GTIN checksum is not assignment evidence |
| brand | brand.name | DERIVED separate brand entity; no asserted product-brand relationship outside the graph contract |
| color, size | attribute.color, attribute.size | DERIVED variant attributes |
| gender, age_group, product_type, material | matching canonical attributes/product type | DERIVED at product grain; material retained as one source string inside an array, not split heuristically |
| item_group_id, google_product_category | UNKNOWN | No family axes or taxonomy version guessed; publish these explicitly in canonical JSON |

The structural profile requires id/title/description/link/image_link/availability/price headers and non-empty core cells. This bounded profile does not implement all Google conditional exceptions. Target market and seller come only from explicit user settings, never brand or language. Google currency always comes from the price token, not the Shopify currency setting.

## Shopify

The grouped adapter requires Handle/URL handle and Title headers; this is **not** Shopify's minimum import contract. Matching ignores header case. Current and legacy aliases are supported, but supplying both aliases for a field is rejected as ambiguous.

| Legacy / current source | Canonical mapping | Status / handling |
|---|---|---|
| Handle / URL handle | product grouping | DERIVED same-handle rows grouped; missing handles fail, never attached by proximity |
| Title; Body (HTML) / Description | title; description | DERIVED shared product fields carried across variants; conflicting nonblank values fail; HTML kept inert, never rendered |
| Variant SKU / SKU | identifier.sku | DERIVED optional source SKU; repeated SKU warns, no invented SKU |
| Variant Barcode / Barcode | identifier.gtin | DERIVED only numeric GTIN-length candidates; other identifier systems UNKNOWN |
| Variant Price / Price | offer.price | DERIVED requires explicit currency setting; no default zero; invalid bare amounts fail even without currency |
| Option1–3 Name / name and Value / value | variant.option_values | DERIVED option names carried across product rows; missing pairs, conflicting names and duplicate combinations fail |
| Option1–3 LinkedTo | UNKNOWN | Metaobject IDs/handles are not treated as display values |
| Image Src / Product image URL; Variant Image / Variant image URL | primary/additional image URLs | DERIVED variant image preferred, otherwise first product image; image-only continuation rows add images, not variants; URL validation only |
| Type | product.product_type | DERIVED custom product-type text |
| Product category | UNKNOWN | Export lacks a pinned taxonomy version; use canonical reference format |
| Vendor, Published, Status, inventory quantity | not inferred | UNKNOWN brand/seller/live availability; do not conflate these meanings |

Rows with a title, SKU, price, barcode or option value are candidate variants; other rows are continuation data. Image association and variant classification are bounded interpretations. Repeated image URLs are deduplicated without rewriting the original CSV. Rows with unequal width are reported and excluded from adaptation. CSV record provenance establishes the local source, not credibility or merchant verification. Shared fields/images may be inherited within the same handle group.

Offer URLs and availability remain UNKNOWN for Shopify exports; handles do not prove variant-specific purchasability. Seller, currency and target country/area are optional explicit settings. Shopify multi-market price columns, linked metafields, inventory exports, tax/shipping, sale prices and other columns are not adapted. No currency or country is derived from market display names.

## Boundaries

Canonical graphs are transient and private. File-local graph IDs change when rows are reordered. Input is at most 200,000 UTF-8 bytes / 1,000 data records. Unsupported OpenAI-compatible CSV retains the existing structural-only path and is clearly labelled. No adapter is a complete platform eligibility test.
