From 0116afbba9dce0d3d0560e78f7b547718893dae2 Mon Sep 17 00:00:00 2001 From: Ben Stull Date: Thu, 11 Jun 2026 13:10:09 -0700 Subject: [PATCH] =?UTF-8?q?spec(SD-0002=20=C2=A7=C2=A712=E2=80=9314):=20tr?= =?UTF-8?q?aceability=20matrix,=20decisions=20log=20D-1..13=20+=20Q-1..3,?= =?UTF-8?q?=20glossary;=20self-review=20fixes;=20v0.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- ...SD-0002-products-bulk-csv-import-export.md | 77 ++++++++++++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/specs/SD-0002-products-bulk-csv-import-export.md b/specs/SD-0002-products-bulk-csv-import-export.md index 562f1ef..6509aca 100644 --- a/specs/SD-0002-products-bulk-csv-import-export.md +++ b/specs/SD-0002-products-bulk-csv-import-export.md @@ -14,6 +14,7 @@ | Date | Version | Change | By | | --- | --- | --- | --- | | 2026-06-11 | v0.1.0 | Initial draft — §1 Business Context (session ecomm-0030) | Claude + Ben | +| 2026-06-11 | v0.2.0 | Full draft complete, §§1–14, authored section-at-a-time with operator review at each boundary (session ecomm-0030) | Claude + Ben | --- @@ -1135,7 +1136,8 @@ spec, then replan. canonical path; INV-10/11/13/14/15/18 enforced + unit/integration suites green; E2E green: `e2e_import_preview_confirm`, `e2e_import_errors_actionable`, `e2e_import_file_rejected`, - `e2e_import_cancel_no_trace`; TEL-1/2 emitting (§9); DOC-1 updated. + `e2e_import_cancel_no_trace`; TEL-1/2/6 emitting + ALR-2 live (§9); + DOC-1 updated; DOC-3 sample shipped. #### SLICE-6 — Export & the round-trip lock → completes PUC-9, PUC-10 @@ -1332,12 +1334,79 @@ SLICE-8 ships. Docs update in the same PR/slice as the change they describe ## 12. Traceability matrix -*(not yet authored)* +| Pain | Business UC | Product UC | Slice | Tests | Measured by | +| --- | --- | --- | --- | --- | --- | +| PP-1 dead-end storefront | BUC-1 | PUC-1, PUC-2, PUC-3, PUC-4 | SLICE-5 | `e2e_import_preview_confirm`; bootstrap test (§6.8) | BO-1 / PM-1 ← TEL-1, TEL-2 | +| PP-2 re-keying to migrate | BUC-1, BUC-1a | PUC-5, PUC-5a, PUC-6, PUC-11 | SLICE-5, SLICE-8 | `e2e_import_shopify_dialect`, `e2e_import_errors_actionable`, `e2e_import_file_rejected` | BO-1 / PM-2 ← TEL-1, TEL-2 | +| PP-3 no batch gesture | BUC-2, BUC-3 | PUC-3, PUC-3a, PUC-4, PUC-10 | SLICE-5, SLICE-6 | `e2e_import_cancel_no_trace`, `e2e_roundtrip_noop`; INV-12 property test | BO-2 / PM-1 ← TEL-2 | +| PP-3 (imagery upkeep) | BUC-1a | PUC-7 | SLICE-7 | `e2e_image_outcomes`; fetch-resume + SSRF integration | PM-3 ← TEL-4, TEL-5 | +| PP-4 builders blocked | BUC-1 | PUC-1…PUC-11 (the mechanism itself) | SLICE-5–8 | bootstrap test end-to-end | BO-4 (tracker record) | +| PP-5 trapped data | BUC-4 | PUC-9, PUC-10 | SLICE-6 | `e2e_export_download`, `e2e_roundtrip_noop` | BO-3 ← TEL-3 | + +Audit: every PUC lands in a slice (PUC-11's sample in SLICE-5, finalized +SLICE-8); every BUC is realized; every BO has a §8.1 source; every +TEL event is consumed (§9.1); both alerts have runbooks (§9.3 → §10.1). ## 13. Open Questions & Decisions log -*(not yet authored)* +Question/decision numbering is per-spec (SD-0001's D-1…8 and Q-1…3 are its +own; cite cross-spec as "SD-0002 D-13"). + +**Open** + +| # | Question | Owner | Blocks | +| --- | --- | --- | --- | +| Q-1 | Currency: prices are bare decimals with one implicit currency. When does a storefront get a currency setting, and what happens to existing imports then? | Ben | nothing in this MVP; revisit before checkout/orders work | +| Q-2 | Bucket backup: no cross-region backup of image objects pre-v1 (accepted risk, §10.3). Decide backup + restore drill at prod stand-up. | Ben | prod hardening, not this Feature | +| Q-3 | The exact image resolution bar (minimum dimensions for `rejected_low_res`) — pinned in SLICE-7's implementation plan against real merchant fixtures. | SLICE-7 session | SLICE-7 only | + +**Resolved** (operator decisions, brainstorming session ecomm-0030; technical +rationale for engineering choices lives in §6.7) + +| # | Decision | Resolution | Date | +| --- | --- | --- | --- | +| D-1 | Canonical column set | Shopify-flavored superset, operator-specified (§6.5.1): options/variants, images, inventory, cost/weight/volume/tax, kit-seam `Component` columns | 2026-06-11 | +| D-2 | Foreign dialects | Canonical + Shopify product CSV, auto-detected by header set; merchant never picks | 2026-06-11 | +| D-3 | Deletion semantics | Upsert-only; the CSV mechanism never deletes (INV-10) | 2026-06-11 | +| D-4 | Kits scope | Seam only: format reserves `Type` values + `Component` columns; semantics are #15's | 2026-06-11 | +| D-5 | Import flow | Validate-all → preview with per-record drill-in diffs → confirm; sync now (~5k rows), async seam named | 2026-06-11 | +| D-6 | Error delivery | On-screen (preview + run detail) only; no email summaries | 2026-06-11 | +| D-7 | UI home | A Products page created by this design; #14 fills in the list | 2026-06-11 | +| D-8 | Variant identity | Option-value combination; SKU is correctable data (diverges from corpus 11-05-0035, §6.7) | 2026-06-11 | +| D-9 | Images | Fetch-and-host with renditions at fetch; post-commit background phase; **reject** low-res (placeholder) rather than accept-with-warning | 2026-06-11 | +| D-10 | Export shape | Full catalog + status filter; canonical format; hosted image URLs | 2026-06-11 | +| D-11 | Format learning | Sample CSV + column reference, app-served (DOC-2/3) | 2026-06-11 | +| D-12 | Re-import of images | Platform-hosted and already-fetched URLs are never re-fetched; fetch only new URLs + retries of failed/rejected (round-trip integrity, INV-12) | 2026-06-11 | +| D-13 | Storage & serving | Catalog in existing Cloud SQL (additive); images in a per-env private GCS bucket, app-served with immutable caching; CDN named as the #24 seam (§6.3.1) | 2026-06-11 | ## 14. Glossary & References -*(not yet authored)* +- **Handle** — a product's stable identifier in the CSV and the catalog: + lowercase letters, numbers, dashes (INV-13). +- **Variant** — one purchasable form of a product, identified by its + option-value combination; every product has ≥ 1 (a no-option product has + exactly one). +- **Option** — a named axis of variation ("Size"); names live on the + product, values on the variant. +- **Canonical row model** — the internal representation every dialect maps + to before validation (INV-17). +- **Dialect** — an accepted CSV header-shape: canonical or Shopify product + CSV. +- **Draft vs run** — a *draft* is an unconfirmed, expiring preview (no + trace if canceled); a *run* is the durable record of a confirmed import. +- **Rendition** — a derived image size (thumb / card / detail), generated at + fetch, immutable. +- **Hosted URL** — an `Image Src` pointing at this platform's image-serving + route; resolves to an existing image record, never fetched. +- **Upsert** — add-or-update by identity; this mechanism's only mutation. +- **Round-trip** — export → re-import of the unmodified file diffs to + nothing (INV-12). +- **References:** anchor [#13](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm/issues/13) · + epic [#12](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm/issues/12) · + siblings [#14](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm/issues/14), + [#15](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm/issues/15) · + [SD-0001](./SD-0001-mvp-sign-up-and-single-storefront.md) · + corpus [rfcs 11-05-0032–0037](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm-prototype-content/src/branch/main/rfcs) · + [research/shopify/products](https://git.wiggleverse.org/wiggleverse/wiggleverse-ecomm-prototype-content/src/branch/main/research/shopify/products/products.md) · + Shopify product-CSV documentation (external; the dialect fixture is the + binding artifact, §6.5.1) · handbook §§3.3, 4.3, 8.5, 9.