diff --git a/specs/SD-0002-products-bulk-csv-import-export.md b/specs/SD-0002-products-bulk-csv-import-export.md index 81122ca..0b7970d 100644 --- a/specs/SD-0002-products-bulk-csv-import-export.md +++ b/specs/SD-0002-products-bulk-csv-import-export.md @@ -226,7 +226,79 @@ Scenario: BUC-4 — The catalog leaves whole, on demand ## 2. Solution Proposal -*(not yet authored — next in the section-at-a-time loop)* +**Build software:** CSV file import/export in the storefront admin — a new +**Products** surface where a merchant uploads a CSV to add and update products +in bulk, and downloads a CSV of the full catalog at any time. + +**The shape of it:** + +- **One canonical CSV format**, Shopify-flavored: one row per variant, + consecutive rows sharing a `Handle` form one product (with its options, + variants, and images). The column set is a deliberate superset of Shopify's + product CSV — adding component columns (the kits seam, #15), volume, + cost/tax fields — so a Shopify export is structurally familiar and our + format never needs to churn when siblings arrive. The full column + specification is §6. +- **Two accepted dialects, auto-detected:** the canonical format, and + Shopify's own product-CSV export (mapped onto canonical at the boundary). + The header row identifies the dialect; the merchant never picks from a + menu. This is the migrating merchant's path (BUC-1): their existing export + imports directly. +- **Import is upsert-only.** A row whose `Handle` is new creates a product; a + known `Handle` updates it (variants matched by option-value combination). + Nothing is ever deleted by an import — the mechanism is non-destructive by + construction, so a partial file can never silently destroy catalog + (BUC-2's safety, structurally). +- **Validate-all → preview → confirm.** The whole file is validated and + diffed against the catalog before anything is written. The merchant sees a + summary — N products to add, M to update, K rows in error — and can drill + into any record to see exactly which fields will change and to what. + Nothing applies until they confirm; confirming applies the valid records + and leaves the error rows listed, unapplied, and actionable (BUC-1a, + BUC-2). Unrecognized columns are warned about in the preview and ignored, + never silently dropped. +- **Synchronous, with a stated capacity** of ~5,000 rows per file. No job + queue in the MVP; the design names the async seam for when catalogs + outgrow it (§6.9). One deliberate exception rides that seam's first rail: + **images**. +- **Images are fetched and hosted.** `Image Src` URLs are downloaded into + platform object storage after the catalog rows commit, with renditions + (thumbnail / card / detail) generated at fetch. Images that can't be + fetched, aren't images, or fall below the resolution bar are **rejected** + (the product shows a placeholder), with the outcome reported per image. + The catalog is self-contained: exports reference our hosted copies, and + the storefront never depends on a third-party host staying alive. +- **Imports are first-class records.** Every import run is kept — file name, + dialect detected, who ran it, add/update/error counts, image-fetch + outcomes — with a detail view the merchant can leave and return to + (the post-commit image phase reports there and on the Products page). +- **Export is one gesture:** the complete catalog (optionally filtered by + status) as a canonical CSV. Export → re-import is lossless and idempotent — + the acceptance bar from #13 and BUC-4's round-trip guarantee. + +**Why this approach.** CSV is the medium merchant catalogs already live in: +every platform exports it, every spreadsheet tool speaks it, and the +migrating merchant (the sharpest pain, PP-2) holds one today. Accepting +Shopify's dialect directly converts the largest migration population's +existing artifact into a working catalog with zero transformation effort. +Upsert-only plus preview-confirm makes a powerful bulk mechanism safe to hand +to a non-technical merchant: the destructive operations simply don't exist, +and every change is consented to with full knowledge (OHM: consent & agency). + +**Alternatives weighed** (detail in §6.7): a per-item editing UI first +(rejected — Epic #12's explicit boundary, and it does nothing for migration); +integration APIs / direct platform connectors (rejected — heavy, narrow, and +premature before the catalog model exists); accepting spreadsheet formats +(XLSX) (rejected for MVP — CSV covers the actual artifacts merchants hold; +XLSX is an adapter behind the same boundary later); do-nothing / concierge +data entry by the operator (rejected — doesn't scale past a demo and builds +no product capability). + +**Solution-specific non-goals:** no deletion semantics of any kind in the +CSV; no scheduled/automated re-import; no foreign dialects beyond Shopify's +product CSV in the MVP; no kit *semantics* (the format carries the columns; +#15 defines their meaning); no image editing/cropping — the platform hosts +what the merchant's URLs provide, or rejects it. ## 3. Product Personas