[object Object]

← back to Designer Wallcoverings

auto-save: 2026-07-02T11:47:40 (7 files) — pending-approval/boost-filter-consolidation-2026-06-25 shopify/scripts/cadence/data/cadence-cursor.json shopify/scripts/cadence/data/cadence-plan-am-2026-07-02.json shopify/scripts/cadence/data/upload-restore-2026-07-02.jsonl vendor-scrapers/china-seas-refresh

7568c2faa3111cb43f56264f0e5a337f3392c2cb · 2026-07-02 11:47:47 -0700 · Steve Abrams

Files touched

Diff

commit 7568c2faa3111cb43f56264f0e5a337f3392c2cb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 2 11:47:47 2026 -0700

    auto-save: 2026-07-02T11:47:40 (7 files) — pending-approval/boost-filter-consolidation-2026-06-25 shopify/scripts/cadence/data/cadence-cursor.json shopify/scripts/cadence/data/cadence-plan-am-2026-07-02.json shopify/scripts/cadence/data/upload-restore-2026-07-02.jsonl vendor-scrapers/china-seas-refresh
---
 audits/dwtt-conflict-2-before-state.jsonl          |   2 +
 shopify/scripts/cadence/data/cadence-cursor.json   |   2 +-
 .../cadence/data/cadence-plan-am-2026-07-02.json   | 220 ++++++++++-----------
 .../cadence/data/upload-restore-2026-07-02.jsonl   |  18 ++
 shopify/scripts/fix-dwtt-conflict-2-skus.py        | 151 ++++++++++++++
 5 files changed, 281 insertions(+), 112 deletions(-)

diff --git a/audits/dwtt-conflict-2-before-state.jsonl b/audits/dwtt-conflict-2-before-state.jsonl
new file mode 100644
index 00000000..4485cf88
--- /dev/null
+++ b/audits/dwtt-conflict-2-before-state.jsonl
@@ -0,0 +1,2 @@
+{"ts": "2026-07-02T11:21:57.165712", "product_id": "gid://shopify/Product/7552689897523", "mfr": "AT57882", "before": {"handle": "tate-trellis-green-anna-french", "status": "ACTIVE", "title": "Tate Trellis Green Wallcovering", "width": "27\" x 4.5yds", "length": "11 Yards", "unit_of_measure": "Full Roll"}}
+{"ts": "2026-07-02T11:21:57.165738", "product_id": "gid://shopify/Product/7552706543667", "mfr": "T41013", "before": {"handle": "dwtt-80529-designer-wallcoverings-los-angeles", "status": "ACTIVE", "title": "Hinton Tunnel Ivory | Thibaut", "width": "27 Inches", "length": "4.5 yd (13.5 ft)", "unit_of_measure": ""}}
diff --git a/shopify/scripts/cadence/data/cadence-cursor.json b/shopify/scripts/cadence/data/cadence-cursor.json
index 575ad4c7..64fd75b8 100644
--- a/shopify/scripts/cadence/data/cadence-cursor.json
+++ b/shopify/scripts/cadence/data/cadence-cursor.json
@@ -1,5 +1,5 @@
 {
- "idx": 6,
+ "idx": 12,
  "lastSlot": "am",
  "lastRun": "2026-07-02"
 }
\ No newline at end of file
diff --git a/shopify/scripts/cadence/data/cadence-plan-am-2026-07-02.json b/shopify/scripts/cadence/data/cadence-plan-am-2026-07-02.json
index cba4f975..f2a440be 100644
--- a/shopify/scripts/cadence/data/cadence-plan-am-2026-07-02.json
+++ b/shopify/scripts/cadence/data/cadence-plan-am-2026-07-02.json
@@ -1,184 +1,182 @@
 [
  {
-  "vendor": "Romo",
-  "dw_sku": "DWRM-240167",
-  "cost": 1364,
-  "retail": 2468.78,
+  "vendor": "Schumacher",
+  "dw_sku": "DWLK-807240",
+  "cost": 38,
+  "retail": 68.78,
   "sample": "4.25",
   "sampleOnly": false,
-  "title": "Bakbak, Indigo Wallcoverings | Romo",
+  "title": "Villandry Damask Print, Neutral Wallcoverings | Schumacher",
   "willActivate": true
  },
  {
-  "vendor": "Romo",
-  "dw_sku": "DWRM-240187",
-  "cost": 490.6,
-  "retail": 887.96,
+  "vendor": "Schumacher",
+  "dw_sku": "DWLK-807250",
+  "cost": 38,
+  "retail": 68.78,
   "sample": "4.25",
   "sampleOnly": false,
-  "title": "Suru, Typhoon Wallcoverings | Romo",
+  "title": "Villandry Damask Print, Black Wallcoverings | Schumacher",
   "willActivate": true
  },
  {
-  "vendor": "Romo",
-  "dw_sku": "DWRM-240194",
-  "cost": 451,
-  "retail": 816.29,
+  "vendor": "Schumacher",
+  "dw_sku": "DWLK-807260",
+  "cost": 28,
+  "retail": 50.68,
   "sample": "4.25",
   "sampleOnly": false,
-  "title": "Palm, Harvest Wallcoverings | Romo",
+  "title": "Dedra Damask, Mineral & Leaf Wallcoverings | Schumacher",
   "willActivate": true
  },
  {
-  "vendor": "Thibaut",
-  "dw_sku": "DWTT-73659",
-  "cost": 165.6,
-  "retail": 299.73,
+  "vendor": "Quadrille",
+  "dw_sku": "DWQC-600201",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Hampton, Lilac Wallcoverings | Thibaut",
+  "sampleOnly": true,
+  "title": "Shanghai, Raspberry on White Wallcoverings | Quadrille",
   "willActivate": true
  },
  {
-  "vendor": "Thibaut",
-  "dw_sku": "DWTT-73660",
-  "cost": 165.6,
-  "retail": 299.73,
+  "vendor": "Quadrille",
+  "dw_sku": "DWQC-600202",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Hampton, Forest Green Wallcoverings | Thibaut",
+  "sampleOnly": true,
+  "title": "Shanghai, Multi Greys on White Wallcoverings | Quadrille",
   "willActivate": true
  },
  {
-  "vendor": "Thibaut",
-  "dw_sku": "DWTT-73661",
-  "cost": 165.6,
-  "retail": 299.73,
+  "vendor": "Quadrille",
+  "dw_sku": "DWQC-600203",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Hampton, Mist Wallcoverings | Thibaut",
+  "sampleOnly": true,
+  "title": "Stitched Floral, Multi Blues Turquoise Lilac Wallcoverings | Quadrille",
   "willActivate": true
  },
  {
-  "vendor": "Designtex",
-  "dw_sku": "DWDX-220516",
-  "cost": 58,
-  "retail": 104.98,
+  "vendor": "Alan Campbell",
+  "dw_sku": "DWAK-700202",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Fragments, New York Wallcoverings | Designtex",
-  "willActivate": true,
-  "held": true,
-  "heldReason": "\"York Wallcoverings\" is a banned front-facing trade-parent name"
+  "sampleOnly": true,
+  "title": "Inca Gold on Tint Wallcoverings | Alan Campbell",
+  "willActivate": true
  },
  {
-  "vendor": "Designtex",
-  "dw_sku": "DWDX-220521",
-  "cost": 58,
-  "retail": 104.98,
+  "vendor": "Alan Campbell",
+  "dw_sku": "DWAK-700203",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Resurfacing, Primer Wallcoverings | Designtex",
+  "sampleOnly": true,
+  "title": "Shrimp on Tint Wallcoverings | Alan Campbell",
   "willActivate": true
  },
  {
-  "vendor": "Designtex",
-  "dw_sku": "DWDX-220522",
-  "cost": 58,
-  "retail": 104.98,
+  "vendor": "Alan Campbell",
+  "dw_sku": "DWAK-700204",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Resurfacing, Blackout Wallcoverings | Designtex",
+  "sampleOnly": true,
+  "title": "French Blue on Tint Wallcoverings | Alan Campbell",
   "willActivate": true
  },
  {
-  "vendor": "Newwall",
-  "dw_sku": "DWXW-1005721",
-  "cost": 88,
-  "retail": 159.28,
+  "vendor": "Home Couture",
+  "dw_sku": "DWHJ-900197",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Skyscraper Window, Made to Measure Mural Vinyl Wallcoverings | Newwall",
-  "willActivate": false
+  "sampleOnly": true,
+  "title": "Fern Green on Tint Wallcoverings | Home Couture",
+  "willActivate": true
  },
  {
-  "vendor": "Newwall",
-  "dw_sku": "DWXW-1005722",
-  "cost": 88,
-  "retail": 159.28,
+  "vendor": "Home Couture",
+  "dw_sku": "DWHJ-900198",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Pink Old Wall, Made to Measure Mural Vinyl Wallcoverings | Newwall",
-  "willActivate": false
+  "sampleOnly": true,
+  "title": "New Navy on Tint Wallcoverings | Home Couture",
+  "willActivate": true
  },
  {
-  "vendor": "Newwall",
-  "dw_sku": "DWXW-1005723",
-  "cost": 88,
-  "retail": 159.28,
+  "vendor": "Home Couture",
+  "dw_sku": "DWHJ-900199",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Fronton Wall, Made to Measure Mural Vinyl Wallcoverings | Newwall",
-  "willActivate": false
+  "sampleOnly": true,
+  "title": "Black on Tint Wallcoverings | Home Couture",
+  "willActivate": true
  },
  {
-  "vendor": "Brewster & York",
-  "dw_sku": "DWBR-170879",
-  "cost": 90,
-  "retail": 162.9,
+  "vendor": "Suncloth",
+  "dw_sku": "DWUX-800203",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Lindlöv, Moss Wallcoverings | Malibu Wallcovering",
+  "sampleOnly": true,
+  "title": "Magenta New Navy Wallcoverings | Suncloth",
   "willActivate": true
  },
  {
-  "vendor": "Brewster & York",
-  "dw_sku": "DWBR-170880",
-  "cost": 90,
-  "retail": 162.9,
+  "vendor": "Suncloth",
+  "dw_sku": "DWUX-800204",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Lindlöv, Taupe Wallcoverings | Malibu Wallcovering",
+  "sampleOnly": true,
+  "title": "Pacific Blue Navy Wallcoverings | Suncloth",
   "willActivate": true
  },
  {
-  "vendor": "Brewster & York",
-  "dw_sku": "DWBR-170881",
-  "cost": 90,
-  "retail": 162.9,
+  "vendor": "Suncloth",
+  "dw_sku": "DWUX-800205",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Lindlöv, Light Grey Wallcoverings | Malibu Wallcovering",
+  "sampleOnly": true,
+  "title": "Black Brown Wallcoverings | Suncloth",
   "willActivate": true
  },
  {
-  "vendor": "Kravet",
-  "dw_sku": "DWKK-102919",
-  "cost": 59.95,
-  "retail": 89.93,
+  "vendor": "Plains",
+  "dw_sku": "DWYP-1000200",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Poet Plain, Seafoam Green Wallcoverings | Kravet",
+  "sampleOnly": true,
+  "title": "Flax Wallcoverings | Plains",
   "willActivate": true
  },
  {
-  "vendor": "Kravet",
-  "dw_sku": "DWKK-102920",
-  "cost": 59.95,
-  "retail": 89.93,
+  "vendor": "Plains",
+  "dw_sku": "DWYP-1000201",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Poet Plain, Beige Wallcoverings | Kravet",
+  "sampleOnly": true,
+  "title": "Dove Wallcoverings | Plains",
   "willActivate": true
  },
  {
-  "vendor": "Kravet",
-  "dw_sku": "DWKK-102921",
-  "cost": 59.95,
-  "retail": 89.93,
+  "vendor": "Plains",
+  "dw_sku": "DWYP-1000202",
+  "cost": 0,
+  "retail": 0,
   "sample": "4.25",
-  "sampleOnly": false,
-  "title": "Poet Plain, Celadon Wallcoverings | Kravet",
+  "sampleOnly": true,
+  "title": "Natural Wallcoverings | Plains",
   "willActivate": true
  }
 ]
\ No newline at end of file
diff --git a/shopify/scripts/cadence/data/upload-restore-2026-07-02.jsonl b/shopify/scripts/cadence/data/upload-restore-2026-07-02.jsonl
index 0733979c..8c94e210 100644
--- a/shopify/scripts/cadence/data/upload-restore-2026-07-02.jsonl
+++ b/shopify/scripts/cadence/data/upload-restore-2026-07-02.jsonl
@@ -281,3 +281,21 @@
 {"table":"kravet_catalog","mfr_sku":"36649.15.0","dw_sku":"DWKK-102919","shopify_product_id":"7874188279859","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T17-40-00-924Z","ts":"2026-07-02T17:40:47.406Z"}
 {"table":"kravet_catalog","mfr_sku":"36649.16.0","dw_sku":"DWKK-102920","shopify_product_id":"7874188312627","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T17-40-00-924Z","ts":"2026-07-02T17:40:50.897Z"}
 {"table":"kravet_catalog","mfr_sku":"36649.23.0","dw_sku":"DWKK-102921","shopify_product_id":"7874188345395","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T17-40-00-924Z","ts":"2026-07-02T17:40:54.358Z"}
+{"table":"schumacher_catalog","mfr_sku":"5011751","dw_sku":"DWLK-807240","shopify_product_id":"7874222030899","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:06.137Z"}
+{"table":"schumacher_catalog","mfr_sku":"5011752","dw_sku":"DWLK-807250","shopify_product_id":"7874222129203","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:09.833Z"}
+{"table":"schumacher_catalog","mfr_sku":"5011760","dw_sku":"DWLK-807260","shopify_product_id":"7874222161971","action":"created","activated":true,"published":true,"sampleOnly":false,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:13.488Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"JF01000-06","dw_sku":"DWQC-600201","shopify_product_id":"7874222194739","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:17.474Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"JF01000-01","dw_sku":"DWQC-600202","shopify_product_id":"7874222227507","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:21.371Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"306541F","dw_sku":"DWQC-600203","shopify_product_id":"7874222293043","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:25.156Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"AC303-13","dw_sku":"DWAK-700202","shopify_product_id":"7874222391347","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:29.463Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"AC303-17","dw_sku":"DWAK-700203","shopify_product_id":"7874222456883","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:33.078Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"AC303-15","dw_sku":"DWAK-700204","shopify_product_id":"7874222555187","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:37.270Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"HC1860-02","dw_sku":"DWHJ-900197","shopify_product_id":"7874222587955","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:41.220Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"HC1860-07","dw_sku":"DWHJ-900198","shopify_product_id":"7874222653491","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:44.861Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"HC1860-09","dw_sku":"DWHJ-900199","shopify_product_id":"7874222719027","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:48.445Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"149-34SUN","dw_sku":"DWUX-800203","shopify_product_id":"7874222784563","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:52.759Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"149-35SUN","dw_sku":"DWUX-800204","shopify_product_id":"7874222817331","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:40:56.467Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"149-30SUN","dw_sku":"DWUX-800205","shopify_product_id":"7874222850099","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:41:00.094Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"HC1420-05","dw_sku":"DWYP-1000200","shopify_product_id":"7874222882867","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:41:03.962Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"HC1420-04","dw_sku":"DWYP-1000201","shopify_product_id":"7874222948403","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:41:07.455Z"}
+{"table":"quadrille_house_catalog","mfr_sku":"HC1450-01","dw_sku":"DWYP-1000202","shopify_product_id":"7874222981171","action":"created","activated":true,"published":true,"sampleOnly":true,"status":"ACTIVE","batch_id":"upload-am-2026-07-02T18-40-00-149Z","ts":"2026-07-02T18:41:10.948Z"}
diff --git a/shopify/scripts/fix-dwtt-conflict-2-skus.py b/shopify/scripts/fix-dwtt-conflict-2-skus.py
new file mode 100644
index 00000000..3e8c2f18
--- /dev/null
+++ b/shopify/scripts/fix-dwtt-conflict-2-skus.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+"""
+Fix the 2 wave-1 conflict SKUs held for manual review (DTD verdict A, 3/3,
+2026-07-02): AT57882 (double-roll "11 Yards" length -> single-roll) and
+T41013 (imperial "13.5 ft" -> metric "(4.11 m)" formatting) + the withheld
+single-roll unit_of_measure copy on both.
+
+Values re-confirmed against the LIVE thibautdesign.com PDPs 2026-07-02
+(RollLength 5.5 / 4.5, width 27) — NOT from anna_french_catalog, whose
+AT57882 length column has since drifted to a bad "11 Feet" re-scrape parse.
+
+Same write path as set-dwtt-single-roll-dimensions.py (metafieldsSet,
+global namespace, single_line_text_field). Only writes fields that differ.
+Default: --dry-run. Cost: $0 (Shopify Admin API).
+"""
+import json, os, sys, time, urllib.request, datetime
+
+TOKEN = os.environ["SHOPIFY_ADMIN_TOKEN"]
+SHOP = "designer-laboratory-sandbox.myshopify.com"
+GQL = f"https://{SHOP}/admin/api/2024-10/graphql.json"
+DRY = "--dry-run" in sys.argv or "--dry" in sys.argv
+COPY = "Priced per single roll. Single Roll Dimensions. Packed in 2 roll increments only."
+BEFORE_PATH = os.path.expanduser(
+    "~/Projects/Designer-Wallcoverings/audits/dwtt-conflict-2-before-state.jsonl")
+AUDIT_PATH = os.path.expanduser(
+    "~/Projects/Designer-Wallcoverings/shopify/scripts/audits/dwtt-conflict-2-fix.json")
+
+TARGETS = {
+    "gid://shopify/Product/7552689897523": dict(
+        mfr="AT57882",
+        width="27 in (68.58 cm)",
+        length="5.5 yd (5.03 m)",
+        unit_of_measure=COPY),
+    "gid://shopify/Product/7552706543667": dict(
+        mfr="T41013",
+        width=None,  # not flagged; wave already handled — leave untouched
+        length="4.5 yd (4.11 m)",
+        unit_of_measure=COPY),
+}
+
+NODES = """
+query($ids:[ID!]!){
+  nodes(ids:$ids){
+    ... on Product{
+      id status title handle
+      w:metafield(namespace:"global",key:"width"){value}
+      l:metafield(namespace:"global",key:"length"){value}
+      uom:metafield(namespace:"global",key:"unit_of_measure"){value}
+    }
+  }
+}
+"""
+
+SET = """
+mutation($mfs:[MetafieldsSetInput!]!){
+  metafieldsSet(metafields:$mfs){
+    metafields{ id }
+    userErrors{ field message }
+  }
+}
+"""
+
+def gql(q, v=None):
+    body = json.dumps({"query": q, "variables": v or {}}).encode()
+    req = urllib.request.Request(GQL, data=body, headers={
+        "X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json"})
+    for _ in range(8):
+        try:
+            with urllib.request.urlopen(req) as r:
+                d = json.load(r)
+            if d.get("errors"):
+                if "THROTTLED" in json.dumps(d["errors"]):
+                    time.sleep(2.5); continue
+                raise SystemExit("GQL ERR: " + json.dumps(d["errors"]))
+            return d["data"]
+        except urllib.error.HTTPError as e:
+            if e.code == 429:
+                time.sleep(2.5); continue
+            raise
+    raise SystemExit("retries exhausted")
+
+def clean(s):
+    return (s or "").replace(" ", " ").strip()
+
+def snapshot():
+    d = gql(NODES, {"ids": list(TARGETS.keys())})
+    out = {}
+    for n in d["nodes"]:
+        if not n: continue
+        out[n["id"]] = dict(
+            handle=n["handle"], status=n["status"], title=n["title"],
+            width=clean((n["w"] or {}).get("value")),
+            length=clean((n["l"] or {}).get("value")),
+            unit_of_measure=clean((n["uom"] or {}).get("value")))
+    return out
+
+def main():
+    before = snapshot()
+    os.makedirs(os.path.dirname(BEFORE_PATH), exist_ok=True)
+    with open(BEFORE_PATH, "a") as f:
+        for gid, cur in before.items():
+            f.write(json.dumps(dict(ts=datetime.datetime.now().isoformat(),
+                                    product_id=gid, mfr=TARGETS[gid]["mfr"],
+                                    before=cur)) + "\n")
+
+    writes, plan = [], []
+    for gid, t in TARGETS.items():
+        cur = before.get(gid)
+        if not cur:
+            raise SystemExit(f"product not found: {gid} ({t['mfr']})")
+        if cur["status"] == "ARCHIVED":
+            raise SystemExit(f"ARCHIVED, refusing: {t['mfr']}")
+        for key in ("width", "length", "unit_of_measure"):
+            want = t.get(key)
+            if want is None or cur[key] == want:
+                continue
+            plan.append(f"{t['mfr']}.{key}: {cur[key]!r} -> {want!r}")
+            writes.append({"ownerId": gid, "namespace": "global", "key": key,
+                           "type": "single_line_text_field", "value": want})
+
+    print("PLAN:")
+    for p in plan: print("  " + p)
+    print(f"{len(writes)} metafield writes queued")
+
+    if DRY:
+        print("DRY RUN — no writes performed.")
+        return
+
+    d = gql(SET, {"mfs": writes})
+    ue = d["metafieldsSet"]["userErrors"]
+    print(f"wrote {len(writes)} metafields, userErrors={ue or 0}")
+
+    after = snapshot()
+    ok = True
+    for gid, t in TARGETS.items():
+        for key in ("width", "length", "unit_of_measure"):
+            want = t.get(key)
+            if want is not None and after[gid][key] != want:
+                ok = False
+                print(f"VERIFY FAIL {t['mfr']}.{key}: {after[gid][key]!r}")
+    print("VERIFY: " + ("all values confirmed on Shopify" if ok else "MISMATCH — see above"))
+
+    with open(AUDIT_PATH, "w") as f:
+        json.dump(dict(generated_at=datetime.datetime.now().isoformat(),
+                       verdict="DTD A 3/3 2026-07-02", plan=plan,
+                       before=before, after=after,
+                       user_errors=ue, verified=ok), f, indent=1)
+    print(f"Audit JSON: {AUDIT_PATH}")
+
+if __name__ == "__main__":
+    main()

← a6251257 discontinued-sweep FINAL: thibaut 299 discontinued (285 dual  ·  back to Designer Wallcoverings  ·  PDP qty-inline: move quantity stepper to the right of the pr 631ae2c3 →