From 07abd48b505632fbe3e4fc300903509520ca4a89 Mon Sep 17 00:00:00 2001 From: Ben Stull Date: Thu, 11 Jun 2026 12:42:37 -0700 Subject: [PATCH] =?UTF-8?q?spec(SD-0002=20=C2=A77):=20delivery=20plan=20?= =?UTF-8?q?=E2=80=94=20SLICE-5..8,=20spine-first=20sequencing,=20rollout?= =?UTF-8?q?=20via=20pipeline,=20risks?= 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 | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/specs/SD-0002-products-bulk-csv-import-export.md b/specs/SD-0002-products-bulk-csv-import-export.md index 64a81e0..f644f3e 100644 --- a/specs/SD-0002-products-bulk-csv-import-export.md +++ b/specs/SD-0002-products-bulk-csv-import-export.md @@ -1100,7 +1100,107 @@ merchants having imported real data (which INV-10/12 protect). ## 7. Delivery Plan -*(not yet authored)* +Slice numbering continues app-wide from SD-0001 (SLICE-1…4), like the +invariants: this design owns **SLICE-5…8**. + +### 7.1 Approach / strategy + +**Spine first, infra second, compatibility last.** The codec + diff engine is +the design's novel heart and its riskiest logic — it lands first, with the +full preview/confirm UX, so the consent model (INV-11) is real from day one. +Export lands second and immediately locks the round-trip property (INV-12) +while the data model is still images-light — cheaper to fix losslessness bugs +before the image pipeline complicates identity. Images land third (the +infra-heaviest slice: first GCS bucket, first background work). The Shopify +dialect lands last: by then it is a pure mapping at a stable, well-tested +boundary (INV-17), not a moving target. SLICE-8 can run in parallel with +SLICE-7 if sessions overlap — it touches only the dialect adapter and docs. + +Execution convention per the standard: each slice is its own coding session — +just-in-time implementation plan → execute → verify → ship through the §9 +pipeline → merge + version bump — in dependency order. The design pass +happens once, here; if executing a slice proves this spec wrong, amend the +spec, then replan. + +### 7.2 Slicing plan + +#### SLICE-5 — Import spine (canonical, no images) → completes PUC-1, PUC-2, PUC-3 (+3a), PUC-4, PUC-5 (+5a), PUC-8, and PUC-11's sample download + +- **Depends on:** — (SD-0001 shipped surfaces only) +- **Ships:** `products` domain + §6.3 tables (images table present; + fetch phase stubbed to no-op); canonical parser, validation, diff engine, + draft/confirm/run endpoints (§6.4); Products page, upload, preview with + drill-in, run detail (no images section), import history; sample CSV. +- **Definition of done:** BUC-1/1a/2 acceptance criteria met for the + 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. + +#### SLICE-6 — Export & the round-trip lock → completes PUC-9, PUC-10 + +- **Depends on:** SLICE-5 +- **Ships:** canonical serializer (one codec, two directions), streamed + export endpoint + status filter UI, `nothing_to_apply` no-op preview + behavior, the INV-12 property test over text-field catalogs. +- **Definition of done:** BUC-3/4 acceptance criteria met; INV-12 property + test green in CI; E2E green: `e2e_export_download`, `e2e_roundtrip_noop`; + TEL-3 emitting; DOC-1 updated. + +#### SLICE-7 — Images pipeline end-to-end → completes PUC-7 + +- **Depends on:** SLICE-6 (export must exist so hosted-URL round-trip is + testable the day hosted URLs exist) +- **Ships:** `platform/objectstore` (local + GCS adapters) and + `platform/images`; **the per-environment media bucket — provisioned via + the flotilla `provision-bucket` gesture when this slice reaches PPE + (§6.3.1, handbook §8.5)**; fetch task with SSRF guard, bounds, and + restart recovery; image-serving endpoint; hosted-URL resolution in diff + and export (INV-12 extended over images); run-detail images section, + Products-page notice band; INV-16 in force. +- **Definition of done:** PUC-7 acceptance met; INV-12 property test + extended over image URLs; integration: fetch-resume after kill, SSRF + fixtures; E2E green: `e2e_image_outcomes` (fixture image host); TEL-4/5 + + ALR-3 live; DOC-1 updated (bucket gesture documented in the operator + guide). + +#### SLICE-8 — Shopify dialect + public format docs → completes PUC-6, PUC-11 + +- **Depends on:** SLICE-5 (codec boundary); parallelizable with SLICE-7 +- **Ships:** header-set dialect detection, the Shopify→canonical mapping + with its exhaustive fixture table (§6.5.1), preview's "mapped" banner + + not-imported warnings; the **column reference** public doc + final sample + CSV (DOC-2/3 — the §7.3 launch gate for the migration path). +- **Definition of done:** BUC-1 acceptance met for an unmodified real + Shopify export fixture; E2E green: `e2e_import_shopify_dialect`; DOC-2/3 + published; traceability matrix (§12) audited complete. + +### 7.3 Rollout / launch plan + +Via the mandatory pipeline (handbook §9), per slice: localhost + E2E → PPE +(`https://ecomm-ppe.wiggleverse.org`) + E2E → prod. **Prod does not exist +yet** (SD-0001's prod stand-up under `market.wiggleverse.org` is the cursor's +next stop): any slice completing before it lands ships to a green PPE and +rides the prod stand-up when it arrives — the PPE gate is never skipped +either way. Infra provisioned on demand at the stage that needs it +(§8.5-handbook): the media bucket at SLICE-7's PPE arrival (and again at its +prod arrival), operator-run. Prod is a single in-place slot (no blue/green +yet, per SD-0001). Rollback per slice = redeploy prior version (additive +migrations keep it safe, §6.10). **Non-code launch gates:** DOC-2/3 (column +reference + sample) published before SLICE-8 exposes the migration path; +§9.3 monitoring live with its slice; support path = tracker, pre-v1 (§10.2). + +### 7.4 Risks & mitigations + +| Risk | Likelihood / impact | Mitigation | +| --- | --- | --- | +| Diff-engine edge cases (blank-vs-absent, option matching, no-option variants) breed subtle catalog corruption | M / H | Property tests + per-rule fixtures (§6.8); INV-11 preview faithfulness makes any miss visible before it applies; INV-10 caps the blast radius (nothing deletes) | +| 5k-row validate+diff misses the < 10 s target on the shared VM | M / M | Measure at SLICE-5 with a 5k fixture; the cap is config, can drop without spec change (INV-18); async seam already named | +| Shopify export format drifts or has regional variants | M / M | Detection by exact header set fails *honestly* to `unknown_dialect` (never misparses); mapping is fixture-pinned; unknown columns warn rather than fail | +| Image fetch is an attack surface (SSRF, decompression bombs) | M / H | §6.6 guard (public IPs only, no cross-guard redirects, size/time/type bounds, Pillow decode caps) + integration fixtures | +| Deploy cadence kills in-process fetch tasks mid-run | M / L | Restart recovery is designed-in (§6.9) and integration-tested; per-image idempotency | +| Bucket provisioning gesture stalls SLICE-7 at PPE (operator availability) | L / L | Gesture named in the slice DoD; flotilla command handed to operator at session start, not discovery at deploy time | +| Scope creep toward in-app editing ("just one edit form") | L / M | §1.7 boundary + INV-10; editing is a future epic, not a slice | ## 8. Success Metrics & Measurement