[object Object]

← back to Majilite Onboard

Publish 153/160 Majilite $4.25 samples to live store (active, unpublished-to-channel); 7 handle-collisions + channel-publish blocked on token read_products/read_publications merchant approval

001730f47c3da800519cbf8494403a5bb96cba8e · 2026-08-10 10:27:35 -0700 · Steve Abrams

Files touched

Diff

commit 001730f47c3da800519cbf8494403a5bb96cba8e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 10 10:27:35 2026 -0700

    Publish 153/160 Majilite $4.25 samples to live store (active, unpublished-to-channel); 7 handle-collisions + channel-publish blocked on token read_products/read_publications merchant approval
---
 data/published.json        | 1106 ++++++++++++++++++++++++++++++++++++++++++++
 scripts/probe7.js          |   17 +
 scripts/publish_shopify.js |  153 ++++++
 scripts/reconcile.js       |   42 ++
 4 files changed, 1318 insertions(+)

diff --git a/data/published.json b/data/published.json
new file mode 100644
index 0000000..7fb1f2b
--- /dev/null
+++ b/data/published.json
@@ -0,0 +1,1106 @@
+{
+  "DWMJ-600001": {
+    "productId": "gid://shopify/Product/7916891701299",
+    "handle": "majilite-stature-storm",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600001-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:05.079Z"
+  },
+  "DWMJ-600002": {
+    "productId": "gid://shopify/Product/7916892946483",
+    "handle": "majilite-apollo-silver-icon",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600002-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:45.573Z"
+  },
+  "DWMJ-600003": {
+    "productId": "gid://shopify/Product/7916893077555",
+    "handle": "majilite-chinchilla-ice-floe",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600003-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:47.721Z"
+  },
+  "DWMJ-600004": {
+    "productId": "gid://shopify/Product/7916893175859",
+    "handle": "majilite-metallic-paragon-silver",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600004-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:49.860Z"
+  },
+  "DWMJ-600005": {
+    "productId": "gid://shopify/Product/7916893274163",
+    "handle": "majilite-metallic-raindrop-stainless-steel",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600005-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:52.367Z"
+  },
+  "DWMJ-600006": {
+    "productId": "gid://shopify/Product/7916893339699",
+    "handle": "majilite-brushed-finesse-pewter",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600006-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:54.504Z"
+  },
+  "DWMJ-600007": {
+    "productId": "gid://shopify/Product/7916893438003",
+    "handle": "majilite-reflection-aluminum",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600007-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:56.708Z"
+  },
+  "DWMJ-600008": {
+    "productId": "gid://shopify/Product/7916893536307",
+    "handle": "majilite-reflection-aluminum-icon",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600008-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:14:58.848Z"
+  },
+  "DWMJ-600009": {
+    "productId": "gid://shopify/Product/7916893601843",
+    "handle": "majilite-legacy-pearl-black-icon-gold-glitter",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600009-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:01.063Z"
+  },
+  "DWMJ-600010": {
+    "productId": "gid://shopify/Product/7916893667379",
+    "handle": "majilite-radiance-dove",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600010-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:03.446Z"
+  },
+  "DWMJ-600011": {
+    "productId": "gid://shopify/Product/7916893732915",
+    "handle": "majilite-finesse-mercury",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600011-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:05.526Z"
+  },
+  "DWMJ-600012": {
+    "productId": "gid://shopify/Product/7916893831219",
+    "handle": "majilite-shimmer-grey-pearl",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600012-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:07.689Z"
+  },
+  "DWMJ-600013": {
+    "productId": "gid://shopify/Product/7916893896755",
+    "handle": "majilite-infinity-onyx",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600013-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:09.939Z"
+  },
+  "DWMJ-600014": {
+    "productId": "gid://shopify/Product/7916893995059",
+    "handle": "majilite-legacy-pearl-black",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600014-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:12.157Z"
+  },
+  "DWMJ-600015": {
+    "productId": "gid://shopify/Product/7916894093363",
+    "handle": "majilite-stature-granite-icon",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600015-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:14.274Z"
+  },
+  "DWMJ-600016": {
+    "productId": "gid://shopify/Product/7916894158899",
+    "handle": "majilite-eclipse-fx-mercury",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600016-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:16.455Z"
+  },
+  "DWMJ-600017": {
+    "productId": "gid://shopify/Product/7916894257203",
+    "handle": "majilite-drizzle-black",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600017-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:18.520Z"
+  },
+  "DWMJ-600018": {
+    "productId": "gid://shopify/Product/7916894322739",
+    "handle": "majilite-techno-black",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600018-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:20.740Z"
+  },
+  "DWMJ-600019": {
+    "productId": "gid://shopify/Product/7916894421043",
+    "handle": "majilite-finesse-pearl-black",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600020": {
+    "productId": "gid://shopify/Product/7916894486579",
+    "handle": "majilite-presto-silver",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600021": {
+    "productId": "gid://shopify/Product/7916894584883",
+    "handle": "majilite-finesse-fx-silver",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600021-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:27.005Z"
+  },
+  "DWMJ-600022": {
+    "productId": "gid://shopify/Product/7916894650419",
+    "handle": "majilite-infinity-iceberg",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600023": {
+    "productId": "gid://shopify/Product/7916894715955",
+    "handle": "majilite-infinity-silver",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600023-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:29.804Z"
+  },
+  "DWMJ-600024": {
+    "productId": "gid://shopify/Product/7916894781491",
+    "handle": "majilite-legacy-mercury",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600025": {
+    "productId": "gid://shopify/Product/7916894847027",
+    "handle": "majilite-pony-moonbeam",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600025-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:32.664Z"
+  },
+  "DWMJ-600026": {
+    "productId": "gid://shopify/Product/7916894912563",
+    "handle": "majilite-eclipse-fx-silver",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600027": {
+    "productId": "gid://shopify/Product/7916894978099",
+    "handle": "majilite-pony-granite-grey",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600027-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:35.518Z"
+  },
+  "DWMJ-600028": {
+    "productId": "gid://shopify/Product/7916895043635",
+    "handle": "majilite-drizzle-black-silver",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600029": {
+    "productId": "gid://shopify/Product/7916895141939",
+    "handle": "majilite-echo-silver-black",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600029-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:38.104Z"
+  },
+  "DWMJ-600030": {
+    "productId": "gid://shopify/Product/7916895174707",
+    "handle": "majilite-shimmer-mercury",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600031": {
+    "productId": "gid://shopify/Product/7916895207475",
+    "handle": "majilite-starlite-stainless-steel",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600031-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:40.857Z"
+  },
+  "DWMJ-600032": {
+    "productId": "gid://shopify/Product/7916895273011",
+    "handle": "majilite-eclipse-fx-carbon",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600033": {
+    "productId": "gid://shopify/Product/7916895305779",
+    "handle": "majilite-nuance-nickel",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600033-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:43.602Z"
+  },
+  "DWMJ-600034": {
+    "productId": "gid://shopify/Product/7916895371315",
+    "handle": "majilite-status-silver-black",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600035": {
+    "productId": "gid://shopify/Product/7916895469619",
+    "handle": "majilite-burnished-metal-pewter",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600035-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:46.487Z"
+  },
+  "DWMJ-600036": {
+    "productId": "gid://shopify/Product/7916895535155",
+    "handle": "majilite-brushed-finesse-stainless-steel",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600037": {
+    "productId": "gid://shopify/Product/7916895633459",
+    "handle": "majilite-shimmer-charcoal",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600037-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:49.085Z"
+  },
+  "DWMJ-600038": {
+    "productId": "gid://shopify/Product/7916895666227",
+    "handle": "majilite-pony-black",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600039": {
+    "productId": "gid://shopify/Product/7916895731763",
+    "handle": "majilite-apollo-black-icon",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600039-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:51.814Z"
+  },
+  "DWMJ-600040": {
+    "productId": "gid://shopify/Product/7916895830067",
+    "handle": "majilite-chinchilla-black",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600041": {
+    "productId": "gid://shopify/Product/7916895895603",
+    "handle": "majilite-charm-white",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600041-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:54.390Z"
+  },
+  "DWMJ-600043": {
+    "productId": "gid://shopify/Product/7916896026675",
+    "handle": "majilite-capricorn-bright-star",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600043-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:57.185Z"
+  },
+  "DWMJ-600044": {
+    "productId": "gid://shopify/Product/7916896092211",
+    "handle": "majilite-farro-pearl-white-gold-stardust",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600045": {
+    "productId": "gid://shopify/Product/7916896190515",
+    "handle": "majilite-shimmer-polar-white",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600045-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:15:59.917Z"
+  },
+  "DWMJ-600046": {
+    "productId": "gid://shopify/Product/7916896256051",
+    "handle": "majilite-apollo-snow",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600047": {
+    "productId": "gid://shopify/Product/7916896321587",
+    "handle": "majilite-chinchilla-snow",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600047-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:02.594Z"
+  },
+  "DWMJ-600049": {
+    "productId": "gid://shopify/Product/7916896452659",
+    "handle": "majilite-shimmer-polar-white-gab-paraweave",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600049-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:05.501Z"
+  },
+  "DWMJ-600050": {
+    "productId": "gid://shopify/Product/7916896518195",
+    "handle": "majilite-finesse-pearl-white",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600051": {
+    "productId": "gid://shopify/Product/7916896583731",
+    "handle": "majilite-shimmer-ivory",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600051-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:08.117Z"
+  },
+  "DWMJ-600052": {
+    "productId": "gid://shopify/Product/7916896649267",
+    "handle": "majilite-legacy-white",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600053": {
+    "productId": "gid://shopify/Product/7916896714803",
+    "handle": "majilite-nuance-glacier",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600053-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:11.038Z"
+  },
+  "DWMJ-600054": {
+    "productId": "gid://shopify/Product/7916896813107",
+    "handle": "majilite-eclipse-hx-snow-white",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600055": {
+    "productId": "gid://shopify/Product/7916896911411",
+    "handle": "majilite-nuance-silver",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600055-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:13.650Z"
+  },
+  "DWMJ-600056": {
+    "productId": "gid://shopify/Product/7916896976947",
+    "handle": "majilite-nuance-snow",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600057": {
+    "productId": "gid://shopify/Product/7916897042483",
+    "handle": "majilite-finesse-hx-pure-white-silver-saffa",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600057-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:16.417Z"
+  },
+  "DWMJ-600058": {
+    "productId": "gid://shopify/Product/7916897140787",
+    "handle": "majilite-legacy-white-gabardine-black-saffa",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600059": {
+    "productId": "gid://shopify/Product/7916897206323",
+    "handle": "majilite-nuance-platinum",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600059-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:19.173Z"
+  },
+  "DWMJ-600060": {
+    "productId": "gid://shopify/Product/7916897271859",
+    "handle": "majilite-palisades-igloo",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600061": {
+    "productId": "gid://shopify/Product/7916897337395",
+    "handle": "majilite-finesse-pearl-gelato",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600061-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:21.783Z"
+  },
+  "DWMJ-600063": {
+    "productId": "gid://shopify/Product/7916897501235",
+    "handle": "majilite-pony-pearl-gelato",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600063-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:24.790Z"
+  },
+  "DWMJ-600064": {
+    "productId": "gid://shopify/Product/7916897632307",
+    "handle": "majilite-palisades-salt-1",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600064-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:27.038Z"
+  },
+  "DWMJ-600066": {
+    "productId": "gid://shopify/Product/7916897763379",
+    "handle": "majilite-charm-featherbed",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600066-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:30.263Z"
+  },
+  "DWMJ-600067": {
+    "productId": "gid://shopify/Product/7916897828915",
+    "handle": "majilite-sandwash-salt",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600068": {
+    "productId": "gid://shopify/Product/7916897927219",
+    "handle": "majilite-starlite-pearl-gelato",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600068-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:33.291Z"
+  },
+  "DWMJ-600069": {
+    "productId": "gid://shopify/Product/7916897959987",
+    "handle": "majilite-classic-platinum",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600070": {
+    "productId": "gid://shopify/Product/7916898058291",
+    "handle": "majilite-legacy-platinum",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600070-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:35.954Z"
+  },
+  "DWMJ-600071": {
+    "productId": "gid://shopify/Product/7916898123827",
+    "handle": "majilite-nuance-white-gold",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600072": {
+    "productId": "gid://shopify/Product/7916898189363",
+    "handle": "majilite-brushed-finesse-pearl-gelato",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600072-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:38.566Z"
+  },
+  "DWMJ-600073": {
+    "productId": "gid://shopify/Product/7916898222131",
+    "handle": "majilite-nuance-gold-leaf",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600074": {
+    "productId": "gid://shopify/Product/7916898287667",
+    "handle": "majilite-legacy-pearl-gelato",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600074-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:41.576Z"
+  },
+  "DWMJ-600075": {
+    "productId": "gid://shopify/Product/7916898385971",
+    "handle": "majilite-charm-ii-platinum",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600076": {
+    "productId": "gid://shopify/Product/7916898451507",
+    "handle": "majilite-hammered-metal-parchment",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600076-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:44.261Z"
+  },
+  "DWMJ-600077": {
+    "productId": "gid://shopify/Product/7916898517043",
+    "handle": "majilite-beton-platinum",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600078": {
+    "productId": "gid://shopify/Product/7916898582579",
+    "handle": "majilite-stature-icicle",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600078-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:47.030Z"
+  },
+  "DWMJ-600079": {
+    "productId": "gid://shopify/Product/7916898648115",
+    "handle": "majilite-eclipse-fx-icicle",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600080": {
+    "productId": "gid://shopify/Product/7916898713651",
+    "handle": "majilite-charm-pashmina",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600080-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:49.702Z"
+  },
+  "DWMJ-600081": {
+    "productId": "gid://shopify/Product/7916898779187",
+    "handle": "majilite-chinchilla-fawn",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600082": {
+    "productId": "gid://shopify/Product/7916898844723",
+    "handle": "majilite-charm-pearl-gelato",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600082-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:52.499Z"
+  },
+  "DWMJ-600083": {
+    "productId": "gid://shopify/Product/7916898877491",
+    "handle": "majilite-finesse-pearl-eggshell-gab-paraweave",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600084": {
+    "productId": "gid://shopify/Product/7916898975795",
+    "handle": "majilite-legacy-pearl-gelato-icon-gold-glitter",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600084-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:55.021Z"
+  },
+  "DWMJ-600085": {
+    "productId": "gid://shopify/Product/7916899041331",
+    "handle": "majilite-radiance-sand",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600086": {
+    "productId": "gid://shopify/Product/7916899106867",
+    "handle": "majilite-brushed-finesse-fawn",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600086-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:57.652Z"
+  },
+  "DWMJ-600087": {
+    "productId": "gid://shopify/Product/7916899237939",
+    "handle": "majilite-chinchilla-gold-leaf-1",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600087-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:16:59.773Z"
+  },
+  "DWMJ-600089": {
+    "productId": "gid://shopify/Product/7916899401779",
+    "handle": "majilite-linea-ash",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600089-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:02.696Z"
+  },
+  "DWMJ-600091": {
+    "productId": "gid://shopify/Product/7916899565619",
+    "handle": "majilite-charm-silver",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600091-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:05.710Z"
+  },
+  "DWMJ-600092": {
+    "productId": "gid://shopify/Product/7916899663923",
+    "handle": "majilite-nuance-fawn",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600093": {
+    "productId": "gid://shopify/Product/7916899729459",
+    "handle": "majilite-cross-hatch-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600093-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:08.295Z"
+  },
+  "DWMJ-600094": {
+    "productId": "gid://shopify/Product/7916899762227",
+    "handle": "majilite-infinity-pebble",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600095": {
+    "productId": "gid://shopify/Product/7916899794995",
+    "handle": "majilite-starlite-fawn",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600095-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:10.937Z"
+  },
+  "DWMJ-600096": {
+    "productId": "gid://shopify/Product/7916899827763",
+    "handle": "majilite-finesse-pearl-cr-me",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600097": {
+    "productId": "gid://shopify/Product/7916899860531",
+    "handle": "majilite-shimmer-mist-pique",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600097-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:13.666Z"
+  },
+  "DWMJ-600098": {
+    "productId": "gid://shopify/Product/7916899958835",
+    "handle": "majilite-splendor-sand",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600099": {
+    "productId": "gid://shopify/Product/7916899991603",
+    "handle": "majilite-sandwash-cream-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600099-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:16.602Z"
+  },
+  "DWMJ-600100": {
+    "productId": "gid://shopify/Product/7916900122675",
+    "handle": "majilite-metallic-raindrop-fawn",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600101": {
+    "productId": "gid://shopify/Product/7916900188211",
+    "handle": "majilite-shimmer-wheat",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600101-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:19.118Z"
+  },
+  "DWMJ-600103": {
+    "productId": "gid://shopify/Product/7916900286515",
+    "handle": "majilite-finesse-pearl-eggshell",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600103-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:22.156Z"
+  },
+  "DWMJ-600104": {
+    "productId": "gid://shopify/Product/7916900384819",
+    "handle": "majilite-eclipse-fx-wheat",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600105": {
+    "productId": "gid://shopify/Product/7916900450355",
+    "handle": "majilite-legacy-beige",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600105-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:24.797Z"
+  },
+  "DWMJ-600106": {
+    "productId": "gid://shopify/Product/7916900548659",
+    "handle": "majilite-legacy-natural",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600107": {
+    "productId": "gid://shopify/Product/7916900614195",
+    "handle": "majilite-pony-pearl-buff",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600107-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:27.659Z"
+  },
+  "DWMJ-600108": {
+    "productId": "gid://shopify/Product/7916900679731",
+    "handle": "majilite-apollo-chardonnay",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600109": {
+    "productId": "gid://shopify/Product/7916900745267",
+    "handle": "majilite-vibes-eggshell-gold-stardust",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600109-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:30.236Z"
+  },
+  "DWMJ-600110": {
+    "productId": "gid://shopify/Product/7916900810803",
+    "handle": "majilite-drizzle-buff",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600111": {
+    "productId": "gid://shopify/Product/7916900876339",
+    "handle": "majilite-hive-antique-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600111-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:32.847Z"
+  },
+  "DWMJ-600112": {
+    "productId": "gid://shopify/Product/7916900941875",
+    "handle": "majilite-orion-gold-coin",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600113": {
+    "productId": "gid://shopify/Product/7916901040179",
+    "handle": "majilite-chinchilla-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600113-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:35.534Z"
+  },
+  "DWMJ-600114": {
+    "productId": "gid://shopify/Product/7916901105715",
+    "handle": "majilite-finesse-gold",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600115": {
+    "productId": "gid://shopify/Product/7916901171251",
+    "handle": "majilite-legacy-sand",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600115-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:38.426Z"
+  },
+  "DWMJ-600116": {
+    "productId": "gid://shopify/Product/7916901204019",
+    "handle": "majilite-nuance-sand",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600117": {
+    "productId": "gid://shopify/Product/7916901302323",
+    "handle": "majilite-eclipse-hx-mystic-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600117-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:41.145Z"
+  },
+  "DWMJ-600118": {
+    "productId": "gid://shopify/Product/7916901367859",
+    "handle": "majilite-legacy-tan",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600119": {
+    "productId": "gid://shopify/Product/7916901498931",
+    "handle": "majilite-echo-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600119-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:44.268Z"
+  },
+  "DWMJ-600120": {
+    "productId": "gid://shopify/Product/7916901564467",
+    "handle": "majilite-fortuna-white-gold",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600121": {
+    "productId": "gid://shopify/Product/7916901630003",
+    "handle": "majilite-nuance-petal",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600121-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:46.965Z"
+  },
+  "DWMJ-600122": {
+    "productId": "gid://shopify/Product/7916901761075",
+    "handle": "majilite-palisades-pearl-eggshell",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600123": {
+    "productId": "gid://shopify/Product/7916901826611",
+    "handle": "majilite-stature-rose-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600123-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:49.712Z"
+  },
+  "DWMJ-600124": {
+    "productId": "gid://shopify/Product/7916901892147",
+    "handle": "majilite-chinchilla-rose-gold",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600125": {
+    "productId": "gid://shopify/Product/7916901957683",
+    "handle": "majilite-bijoux-pink",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600125-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:52.390Z"
+  },
+  "DWMJ-600126": {
+    "productId": "gid://shopify/Product/7916902023219",
+    "handle": "majilite-finesse-pearl-dark-powder",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600127": {
+    "productId": "gid://shopify/Product/7916902088755",
+    "handle": "majilite-chinchilla-blossom",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600127-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:54.948Z"
+  },
+  "DWMJ-600128": {
+    "productId": "gid://shopify/Product/7916902154291",
+    "handle": "majilite-finesse-metallic-rose",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600129": {
+    "productId": "gid://shopify/Product/7916902252595",
+    "handle": "majilite-infinity-white-zinfandel",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600129-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:17:57.719Z"
+  },
+  "DWMJ-600130": {
+    "productId": "gid://shopify/Product/7916902318131",
+    "handle": "majilite-brushed-finesse-champagne",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600131": {
+    "productId": "gid://shopify/Product/7916902383667",
+    "handle": "majilite-chinchilla-wheat",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600131-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:00.632Z"
+  },
+  "DWMJ-600132": {
+    "productId": "gid://shopify/Product/7916902449203",
+    "handle": "majilite-infinity-sand-icon-silver-glitter",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600133": {
+    "productId": "gid://shopify/Product/7916902514739",
+    "handle": "majilite-capricorn-gold-leaf",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600133-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:03.312Z"
+  },
+  "DWMJ-600134": {
+    "productId": "gid://shopify/Product/7916902580275",
+    "handle": "majilite-presto-fawn",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600135": {
+    "productId": "gid://shopify/Product/7916902613043",
+    "handle": "majilite-infinity-sand",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600135-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:05.985Z"
+  },
+  "DWMJ-600136": {
+    "productId": "gid://shopify/Product/7916902678579",
+    "handle": "majilite-heirloom-soft-gold",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600137": {
+    "productId": "gid://shopify/Product/7916902744115",
+    "handle": "majilite-metallic-paragon-ivory",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600137-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:08.841Z"
+  },
+  "DWMJ-600138": {
+    "productId": "gid://shopify/Product/7916902809651",
+    "handle": "majilite-burnished-metal-penny",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600139": {
+    "productId": "gid://shopify/Product/7916902842419",
+    "handle": "majilite-legacy-cameo",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600139-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:11.413Z"
+  },
+  "DWMJ-600140": {
+    "productId": "gid://shopify/Product/7916902907955",
+    "handle": "majilite-shimmer-mist-legend",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600141": {
+    "productId": "gid://shopify/Product/7916902973491",
+    "handle": "majilite-echo-white-silver",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600141-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:14.392Z"
+  },
+  "DWMJ-600142": {
+    "productId": "gid://shopify/Product/7916903039027",
+    "handle": "majilite-eclipse-fx-diamond",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600143": {
+    "productId": "gid://shopify/Product/7916903137331",
+    "handle": "majilite-palisades-metallic-taupe",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600143-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:17.293Z"
+  },
+  "DWMJ-600144": {
+    "productId": "gid://shopify/Product/7916903202867",
+    "handle": "majilite-infinity-mist",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600145": {
+    "productId": "gid://shopify/Product/7916903268403",
+    "handle": "majilite-distinction-grey",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600145-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:19.897Z"
+  },
+  "DWMJ-600146": {
+    "productId": "gid://shopify/Product/7916903333939",
+    "handle": "majilite-nuance-grey",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600147": {
+    "productId": "gid://shopify/Product/7916903432243",
+    "handle": "majilite-burnished-metal-mercury",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600147-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:22.589Z"
+  },
+  "DWMJ-600148": {
+    "productId": "gid://shopify/Product/7916903497779",
+    "handle": "majilite-nuance-pewter",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600149": {
+    "productId": "gid://shopify/Product/7916903563315",
+    "handle": "majilite-charm-metallic-taupe",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600149-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:25.167Z"
+  },
+  "DWMJ-600150": {
+    "productId": "gid://shopify/Product/7916903661619",
+    "handle": "majilite-shimmer-slate-1",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600150-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:27.316Z"
+  },
+  "DWMJ-600151": {
+    "productId": "gid://shopify/Product/7916903759923",
+    "handle": "majilite-finesse-pearl-platinum-1",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600151-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:29.368Z"
+  },
+  "DWMJ-600152": {
+    "productId": "gid://shopify/Product/7916903858227",
+    "handle": "majilite-charm-antique-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600152-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:31.848Z"
+  },
+  "DWMJ-600153": {
+    "productId": "gid://shopify/Product/7916903956531",
+    "handle": "majilite-eclipse-fx-metallic-taupe",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600153-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:34.012Z"
+  },
+  "DWMJ-600154": {
+    "productId": "gid://shopify/Product/7916904087603",
+    "handle": "majilite-infinity-grey",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600154-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:36.142Z"
+  },
+  "DWMJ-600155": {
+    "productId": "gid://shopify/Product/7916904218675",
+    "handle": "majilite-metallic-paragon-slate",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600155-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:38.240Z"
+  },
+  "DWMJ-600156": {
+    "productId": "gid://shopify/Product/7916904316979",
+    "handle": "majilite-attache-rose-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600156-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:40.404Z"
+  },
+  "DWMJ-600157": {
+    "productId": "gid://shopify/Product/7916904382515",
+    "handle": "majilite-shimmer-pewter",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600158": {
+    "productId": "gid://shopify/Product/7916904415283",
+    "handle": "majilite-stature-gold",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600158-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:43.284Z"
+  },
+  "DWMJ-600159": {
+    "productId": "gid://shopify/Product/7916904513587",
+    "handle": "majilite-finesse-pearl-pewter",
+    "status": "ACTIVE",
+    "note": "pre-existing"
+  },
+  "DWMJ-600160": {
+    "productId": "gid://shopify/Product/7916904611891",
+    "handle": "majilite-hammered-metal-midnight",
+    "status": "ACTIVE",
+    "sample_sku": "DWMJ-600160-Sample",
+    "image": "ok",
+    "at": "2026-08-10T17:18:46.291Z"
+  }
+}
\ No newline at end of file
diff --git a/scripts/probe7.js b/scripts/probe7.js
new file mode 100644
index 0000000..ab65314
--- /dev/null
+++ b/scripts/probe7.js
@@ -0,0 +1,17 @@
+const fs=require('fs'),path=require('path');
+const STORE='designer-laboratory-sandbox.myshopify.com';
+const TOKEN=(fs.readFileSync(path.join(process.env.HOME,'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1].replace(/["']/g,'').trim();
+const GQL=`https://${STORE}/admin/api/2024-10/graphql.json`;
+const skus=['DWMJ-600042','DWMJ-600048','DWMJ-600062','DWMJ-600065','DWMJ-600088','DWMJ-600090','DWMJ-600102'];
+async function gql(q,v){const r=await fetch(GQL,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});return r.json();}
+(async()=>{
+  for(const base of skus){
+    const sku=base+'-Sample';
+    const d=await gql(`query($q:String!){ productVariants(first:5, query:$q){ nodes{ sku product{ id handle vendor status featuredMedia{id} } } } }`,{q:`sku:${sku}`});
+    if(d.errors){console.log(base,'ERR',JSON.stringify(d.errors).slice(0,90));continue;}
+    const n=d.data.productVariants.nodes;
+    if(!n.length){console.log(base,'→ NO variant with that sku (product at handle is broken/foreign)');}
+    else n.forEach(x=>console.log(base,'→ FOUND', x.product.handle, x.product.vendor, x.product.status,'img:'+(x.product.featuredMedia?'y':'n'), x.product.id));
+    await new Promise(r=>setTimeout(r,150));
+  }
+})();
diff --git a/scripts/publish_shopify.js b/scripts/publish_shopify.js
new file mode 100644
index 0000000..015a861
--- /dev/null
+++ b/scripts/publish_shopify.js
@@ -0,0 +1,153 @@
+#!/usr/bin/env node
+/**
+ * Publish Majilite Metallic Specialties I as $4.25 SAMPLE products on the live
+ * DW Shopify store (designer-laboratory-sandbox = production). Mirrors the DW
+ * house structure: one "Sample" variant @ $4.25, dwc + custom spec metafields,
+ * display_variant tag, local swatch image via staged upload.
+ *
+ * Idempotent: skips a SKU whose "<dw_sku>-Sample" variant already exists.
+ * PG-first-then-Shopify: majilite_catalog is already staged; this creates the
+ * authoritative Shopify product and records the id back to data/published.json.
+ *
+ * Usage:
+ *   node scripts/publish_shopify.js --canary 1            # first N only
+ *   node scripts/publish_shopify.js --from 2 --to 160     # range
+ *   node scripts/publish_shopify.js --all                 # everything (skips done)
+ *   node scripts/publish_shopify.js --status draft        # default: active
+ *   node scripts/publish_shopify.js --dry                 # no writes, plan only
+ */
+const fs = require('fs');
+const path = require('path');
+
+const ROOT = path.resolve(__dirname, '..');
+const API = '2024-10';
+const STORE = process.env.SHOPIFY_STORE || 'designer-laboratory-sandbox.myshopify.com';
+const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN || (() => {
+  const env = fs.readFileSync(path.join(process.env.HOME, 'Projects/secrets-manager/.env'), 'utf8');
+  const m = env.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m); return m ? m[1].replace(/["']/g, '').trim() : '';
+})();
+const GQL = `https://${STORE}/admin/api/${API}/graphql.json`;
+
+const args = process.argv.slice(2);
+const opt = (k, d) => { const i = args.indexOf('--' + k); return i >= 0 ? (args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : true) : d; };
+const DRY = !!opt('dry', false);
+const STATUS = (opt('status', 'active') === 'draft') ? 'DRAFT' : 'ACTIVE';
+const products = JSON.parse(fs.readFileSync(path.join(ROOT, 'data/products.json'), 'utf8'));
+let range;
+if (opt('canary')) range = products.slice(0, Number(opt('canary')));
+else if (opt('all')) range = products;
+else { const f = Number(opt('from', 1)), t = Number(opt('to', products.length)); range = products.filter(p => p.seq >= f && p.seq <= t); }
+
+const PUB_PATH = path.join(ROOT, 'data/published.json');
+const published = fs.existsSync(PUB_PATH) ? JSON.parse(fs.readFileSync(PUB_PATH, 'utf8')) : {};
+const savePub = () => fs.writeFileSync(PUB_PATH, JSON.stringify(published, null, 2));
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+async function gql(query, variables) {
+  for (let attempt = 0; attempt < 5; attempt++) {
+    const res = await fetch(GQL, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }) });
+    const j = await res.json();
+    if (j.errors) {
+      const throttled = JSON.stringify(j.errors).includes('THROTTLED');
+      if (throttled) { await sleep(1500 * (attempt + 1)); continue; }
+      throw new Error('GQL errors: ' + JSON.stringify(j.errors).slice(0, 300));
+    }
+    return j.data;
+  }
+  throw new Error('GQL throttled after retries');
+}
+
+const slugify = s => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
+
+async function existingVariant(sku) {
+  const d = await gql(`query($q:String!){ productVariants(first:1, query:$q){ nodes{ id product{ id handle status } } } }`, { q: `sku:${sku}` });
+  return d.productVariants.nodes[0] || null;
+}
+
+async function uploadImage(filePath, filename) {
+  const d = await gql(`mutation($input:[StagedUploadInput!]!){ stagedUploadsCreate(input:$input){ stagedTargets{ url resourceUrl parameters{ name value } } userErrors{ field message } } }`,
+    { input: [{ filename, mimeType: 'image/png', resource: 'IMAGE', httpMethod: 'POST' }] });
+  const err = d.stagedUploadsCreate.userErrors; if (err && err.length) throw new Error('stagedUpload: ' + JSON.stringify(err));
+  const t = d.stagedUploadsCreate.stagedTargets[0];
+  const form = new FormData();
+  for (const p of t.parameters) form.append(p.name, p.value);
+  form.append('file', new Blob([fs.readFileSync(filePath)], { type: 'image/png' }), filename);
+  const up = await fetch(t.url, { method: 'POST', body: form });
+  if (!(up.status >= 200 && up.status < 300)) throw new Error('staged POST ' + up.status);
+  return t.resourceUrl;
+}
+
+function metafields(p) {
+  const S = (ns, key, value, type = 'single_line_text_field') => ({ namespace: ns, key, type, value: String(value) });
+  // Only keys proven to exist as single_line_text_field on this store (from a live
+  // DWMA product). Full spec (weight/thickness/durability/FR) lives in descriptionHtml
+  // to avoid colliding with typed metafield definitions (e.g. custom.weight = weight type).
+  return [
+    S('dwc', 'brand', p.vendor), S('dwc', 'pattern_name', p.pattern), S('dwc', 'color', p.color),
+    S('dwc', 'collection', p.collection), S('dwc', 'manufacturer_sku', p.mfr_sku), S('dwc', 'width', p.spec.width),
+    S('custom', 'brand', p.vendor), S('custom', 'pattern_name', p.pattern), S('custom', 'color', p.color),
+    S('custom', 'collection_name', p.collection), S('custom', 'manufacturer_sku', p.mfr_sku),
+    S('custom', 'width', p.spec.width), S('custom', 'color_hex', p.color_hex),
+    S('custom', 'product_class', 'Wallcovering'), S('custom', 'material_category', 'Faux Leather (Nytek)'),
+  ];
+}
+
+function bodyHtml(p) {
+  const s = p.spec;
+  return `<p><strong>${p.pattern} — ${p.color}</strong>. ${p.vendor} ${p.brand}® faux leather from the ${p.collection} collection — a specially engineered Nylon Fiber Matrix, luxurious, breathable and durable, ideal for wall coverings, seating, flat surfaces and displays.</p>`
+    + `<ul><li>Width: ${s.width}</li><li>Weight: ${s.weight}</li><li>Thickness: ${s.thickness}</li>`
+    + `<li>Durability: ${s.durability}</li><li>Flammability: ${s.flammability_inherent}</li>`
+    + `<li>Cleanability: ${s.cleanability_code} · Crocking ${s.crocking}</li>`
+    + `<li>PFAS-, PVC- and plasticizer-free · no off-gassing</li></ul>`
+    + `<p><em>Sample swatch. Sold by the yard (54&quot; wide) — contact us for yardage pricing.</em></p>`;
+}
+
+async function publishOne(p) {
+  const sampleSku = `${p.dw_sku}-Sample`;
+  if (published[p.dw_sku] && published[p.dw_sku].productId) return { skip: 'recorded' };
+  const exists = await existingVariant(sampleSku);
+  if (exists) { published[p.dw_sku] = { productId: exists.product.id, handle: exists.product.handle, status: exists.product.status, note: 'pre-existing' }; savePub(); return { skip: 'exists' }; }
+  if (DRY) return { dry: true, title: p.title, sku: sampleSku };
+
+  const handle = `majilite-${slugify(p.pattern + '-' + p.color)}`;
+  const tags = Array.from(new Set([...p.tags, p.dw_sku, 'display_variant', 'Sample'])).join(', ');
+
+  const input = {
+    handle, title: p.title, vendor: p.vendor, productType: p.product_type, status: STATUS,
+    descriptionHtml: bodyHtml(p), tags: tags.split(', '),
+    productOptions: [{ name: 'Type', values: [{ name: 'Sample' }] }],
+    variants: [{ optionValues: [{ optionName: 'Type', name: 'Sample' }], price: '4.25', sku: sampleSku, inventoryPolicy: 'CONTINUE', taxable: true }],
+    metafields: metafields(p),
+  };
+  const d = await gql(`mutation($input:ProductSetInput!){ productSet(synchronous:true, input:$input){ product{ id handle status } userErrors{ field message } } }`, { input });
+  const ue = d.productSet.userErrors; if (ue && ue.length) throw new Error('productSet: ' + JSON.stringify(ue));
+  const prod = d.productSet.product;
+
+  // attach swatch image
+  let media = 'none';
+  try {
+    const resourceUrl = await uploadImage(path.join(ROOT, 'public', p.image), `${p.dw_sku}.png`);
+    const m = await gql(`mutation($id:ID!,$media:[CreateMediaInput!]!){ productCreateMedia(productId:$id, media:$media){ media{ status } mediaUserErrors{ field message } } }`,
+      { id: prod.id, media: [{ originalSource: resourceUrl, alt: `${p.title} — ${p.vendor} ${p.brand}`, mediaContentType: 'IMAGE' }] });
+    const me = m.productCreateMedia.mediaUserErrors; media = (me && me.length) ? 'err:' + JSON.stringify(me) : 'ok';
+  } catch (e) { media = 'img-fail:' + e.message.slice(0, 80); }
+
+  published[p.dw_sku] = { productId: prod.id, handle: prod.handle, status: prod.status, sample_sku: sampleSku, image: media, at: new Date().toISOString() };
+  savePub();
+  return { created: prod.id, handle: prod.handle, image: media };
+}
+
+(async () => {
+  console.log(`Publish → ${STORE}  status=${STATUS}  ${DRY ? '(DRY)' : ''}  targets=${range.length}`);
+  let created = 0, skipped = 0, failed = 0;
+  for (const p of range) {
+    try {
+      const r = await publishOne(p);
+      if (r.skip) { skipped++; process.stdout.write(`· ${p.dw_sku} skip(${r.skip})\n`); }
+      else if (r.dry) { process.stdout.write(`plan ${p.dw_sku} ${p.title} → ${r.sku}\n`); }
+      else { created++; process.stdout.write(`✓ ${p.dw_sku} ${p.title} → ${r.handle} [img:${r.image}]  (${created})\n`); }
+    } catch (e) { failed++; process.stdout.write(`✗ ${p.dw_sku} ${p.title} — ${e.message.slice(0, 160)}\n`); }
+    await sleep(350);
+  }
+  console.log(`\nDone. created=${created} skipped=${skipped} failed=${failed} | published.json has ${Object.keys(published).length} records`);
+})();
diff --git a/scripts/reconcile.js b/scripts/reconcile.js
new file mode 100644
index 0000000..f33b8bb
--- /dev/null
+++ b/scripts/reconcile.js
@@ -0,0 +1,42 @@
+#!/usr/bin/env node
+/**
+ * Reconcile the live store against products.json using productByHandle (strongly
+ * consistent, unlike variant-sku search which lags). For each of the 160:
+ *  - already recorded in published.json -> OK
+ *  - handle resolves to OUR product (vendor Majilite, DWMJ sample sku) -> record it
+ *  - handle resolves to a DIFFERENT product -> report collision (needs unique handle)
+ *  - handle missing -> report as genuinely absent (to be (re)published)
+ * Read-only: only records confirmed-ours into published.json; never creates/deletes.
+ */
+const fs = require('fs'); const path = require('path');
+const ROOT = path.resolve(__dirname, '..');
+const API = '2024-10'; const STORE = process.env.SHOPIFY_STORE || 'designer-laboratory-sandbox.myshopify.com';
+const TOKEN = (fs.readFileSync(path.join(process.env.HOME,'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1].replace(/["']/g,'').trim();
+const GQL = `https://${STORE}/admin/api/${API}/graphql.json`;
+const products = JSON.parse(fs.readFileSync(path.join(ROOT,'data/products.json'),'utf8'));
+const PUB = path.join(ROOT,'data/published.json');
+const pub = fs.existsSync(PUB) ? JSON.parse(fs.readFileSync(PUB,'utf8')) : {};
+const slug = s => s.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');
+const sleep = ms => new Promise(r=>setTimeout(r,ms));
+async function gql(q,v){ for(let a=0;a<5;a++){ const r=await fetch(GQL,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})}); const j=await r.json(); if(j.errors){ if(JSON.stringify(j.errors).includes('THROTTLED')){await sleep(1500*(a+1));continue;} throw new Error(JSON.stringify(j.errors).slice(0,200)); } return j.data; } throw new Error('throttled'); }
+
+(async()=>{
+  let ok=0, recorded=0, collided=[], missing=[], noimg=[];
+  for(const p of products){
+    if(pub[p.dw_sku] && pub[p.dw_sku].productId){ ok++; continue; }
+    const handle = `majilite-${slug(p.pattern+'-'+p.color)}`;
+    const d = await gql(`query($h:String!){ productByHandle(handle:$h){ id handle status vendor variants(first:1){nodes{sku price}} featuredMedia{id} } }`, {h:handle});
+    const prod = d.productByHandle;
+    if(!prod){ missing.push(p.dw_sku); continue; }
+    const v = prod.variants.nodes[0]||{};
+    const ours = prod.vendor==='Majilite' && v.sku===`${p.dw_sku}-Sample`;
+    if(ours){ pub[p.dw_sku]={productId:prod.id,handle:prod.handle,status:prod.status,sample_sku:v.sku,image:prod.featuredMedia?'ok':'none',reconciled:true}; recorded++; if(!prod.featuredMedia) noimg.push(p.dw_sku); }
+    else collided.push({sku:p.dw_sku, handle, owner_vendor:prod.vendor, owner_variant:v.sku});
+    await sleep(120);
+  }
+  fs.writeFileSync(PUB, JSON.stringify(pub,null,2));
+  console.log(`already_ok=${ok} newly_recorded=${recorded} total_published=${Object.keys(pub).length}`);
+  console.log(`genuinely_missing=${missing.length}${missing.length?': '+missing.join(','):''}`);
+  console.log(`collisions_with_other_products=${collided.length}`); if(collided.length) console.log(JSON.stringify(collided,null,1));
+  console.log(`recorded_without_image=${noimg.length}${noimg.length?': '+noimg.join(','):''}`);
+})();

← 63a1a27 Renumber to DWMJ-600000 block; onboard all 160 as sellable $  ·  back to Majilite Onboard  ·  auto-data-snapshot: 2026-08-10T10:27:30 (1 data files) — tas bfc60b4 →