fix(products): conservative dialect detection — canonical-distinctive columns veto Shopify (SLICE-8 review)
ci / check (push) Has been cancelled
ci / check (pull_request) Has been cancelled

Addresses adversarial-review findings: a canonical file carrying a stray
Shopify-signature name (e.g. SEO Title) no longer misdetects as Shopify and
silently drops canonical Type / corrupts the weight unit (kg->g). A
canonical-distinctive column (Description/Variant Cost/Variant Weight/Google
Product Category/Variant Volume/Tax ID/Position) now vetoes detection, so
detection leans conservative (under-detection warns; over-detection corrupts).
Also closes the dual-named-column shadow (Body (HTML)+Description) and the
stale-weight-unit-on-clear case. Tests cover all three.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 02:36:09 -07:00
parent cb6b5996f3
commit a851d3587c
4 changed files with 87 additions and 11 deletions
+10 -5
View File
@@ -72,11 +72,16 @@ mapping (mirrored by `tests/test_products_dialect_shopify.py` and
- **Detection** — `is_shopify_header()` returns `shopify` iff the header carries a
Shopify-only *signature* column (`Body (HTML)`, `Variant Grams`, `Cost per item`,
`Gift Card`, a `Google Shopping / …` column, …). Everything else is `canonical`.
Detection is by signature, not an exact header set, so extra market/region columns
don't defeat it; an ambiguous header (only columns common to both dialects) falls
through to `canonical`, which maps those shared columns identically — so it never
misparses (§7.4).
`Gift Card`, a `Google Shopping / …` column, …) **and no canonical-distinctive
column**. A canonical-distinctive name — one Shopify renames away (`Description`,
`Variant Cost`, `Variant Weight`, `Google Product Category`) or a canonical-only
column Shopify lacks (`Variant Volume`, `Variant Tax ID 1/2`, `Variant Position`) —
**vetoes** Shopify detection. Detection is by signature, not an exact header set, so
extra market/region columns don't defeat it; the veto biases it conservative, because
under-detection is safe (Shopify-named columns are warned as not-imported) while
over-detection corrupts (canonical `Type` / `Variant Weight Unit` would be dropped).
So a hybrid or ambiguous header falls through to `canonical`, which maps shared
columns identically and warns the rest — it never misparses (§7.4).
- **Mapping** — `map_shopify_header(header)` returns `(mapped, not_imported)`:
`mapped[i]` is the canonical name for column `i` (or `None` when it has no
canonical home), and `not_imported` is the warning list surfaced as the draft's