[object Object]

← back to Designer Wallcoverings

auto-save: 2026-06-23T16:53:06 (9 files) — shopify/scripts/cadence/data/cadence-cursor.json shopify/scripts/cadence/data/cadence-plan-pm-2026-06-23.json shopify/scripts/cadence/data/upload-restore-2026-06-23.jsonl vendor-scrapers/china-seas-refresh audits/designtex-uom-fix/backfill-mirror-metafields.mjs

407a1762e6a034c1d1a087725a758e97f6ac8342 · 2026-06-23 16:53:14 -0700 · Steve Abrams

Files touched

Diff

commit 407a1762e6a034c1d1a087725a758e97f6ac8342
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jun 23 16:53:14 2026 -0700

    auto-save: 2026-06-23T16:53:06 (9 files) — shopify/scripts/cadence/data/cadence-cursor.json shopify/scripts/cadence/data/cadence-plan-pm-2026-06-23.json shopify/scripts/cadence/data/upload-restore-2026-06-23.jsonl vendor-scrapers/china-seas-refresh audits/designtex-uom-fix/backfill-mirror-metafields.mjs
---
 .../backfill-mirror-metafields.mjs                 |  80 +++++++++
 shopify/audit/wpb-grid-fixed.png                   | Bin 0 -> 6298130 bytes
 shopify/collection-hero-fix/inspect-grid-dom.js    | 122 ++++++++++++++
 shopify/collection-hero-fix/inspect-grid-wiring.js |  73 +++++++++
 .../collection-hero-fix/inspect-slider-handler.js  |  81 +++++++++
 shopify/scripts/cadence/data/cadence-cursor.json   |   2 +-
 .../cadence/data/cadence-plan-pm-2026-06-23.json   | 182 ++++++++++-----------
 .../cadence/data/upload-restore-2026-06-23.jsonl   |  18 ++
 8 files changed, 466 insertions(+), 92 deletions(-)

diff --git a/audits/designtex-uom-fix/backfill-mirror-metafields.mjs b/audits/designtex-uom-fix/backfill-mirror-metafields.mjs
new file mode 100644
index 00000000..5bb8eec7
--- /dev/null
+++ b/audits/designtex-uom-fix/backfill-mirror-metafields.mjs
@@ -0,0 +1,80 @@
+#!/usr/bin/env node
+/**
+ * backfill-mirror-metafields.mjs
+ * Backfills the dw_unified.shopify_products.metafields jsonb column for Designtex
+ * products from their LIVE Shopify metafields (the source of truth).
+ *
+ * Why: sync-shopify-mirror.js freshens core fields only (title/status/tags/etc.) and
+ * never carries metafields, so after the Designtex UOM/spec fix the mirror's
+ * metafields->'global'->'unit_of_measure' was still blank. This closes that gap.
+ *
+ * Reads ALL metafields per product, rebuilds the {namespace:{key:value}} shape, and
+ * merges it into the existing jsonb (shallow top-level merge — fresh namespaces win,
+ * untouched namespaces preserved). Customer-facing Shopify is unchanged (read-only there).
+ *
+ * Run ON Kamatera (canonical dw_unified + SHOPIFY_ADMIN_TOKEN in /root/.env):
+ *   set -a && . /root/.env && set +a && node backfill-mirror-metafields.mjs [--dry-run]
+ */
+import https from 'https';
+import pg from 'pg';
+
+const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN;
+const SHOP = process.env.SHOPIFY_SHOP || 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const DRY = process.argv.includes('--dry-run');
+const DB_URL = 'postgresql://dw_admin@127.0.0.1:5432/dw_unified';
+
+if (!TOKEN) { console.error('FATAL: SHOPIFY_ADMIN_TOKEN undefined — source /root/.env first'); process.exit(1); }
+
+const pool = new pg.Pool({ connectionString: DB_URL, max: 4 });
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+const ts = () => new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles', hour12: false });
+const log = m => console.log(`[${ts()} PT] ${m}`);
+
+function getJSON(path, retries = 4) {
+  return new Promise((resolve, reject) => {
+    const attempt = n => {
+      https.get({ host: SHOP, path, headers: { 'X-Shopify-Access-Token': TOKEN } }, res => {
+        let data = '';
+        res.on('data', c => (data += c));
+        res.on('end', () => {
+          if (res.statusCode === 429 && n < retries) return setTimeout(() => attempt(n + 1), 2000 * n);
+          if (res.statusCode >= 400) return reject(new Error(`HTTP ${res.statusCode}`));
+          try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
+        });
+      }).on('error', e => (n < retries ? setTimeout(() => attempt(n + 1), 1500 * n) : reject(e)));
+    };
+    attempt(1);
+  });
+}
+
+(async () => {
+  const { rows } = await pool.query(
+    "SELECT shopify_id, title FROM shopify_products WHERE vendor ILIKE '%designtex%' AND shopify_id IS NOT NULL ORDER BY shopify_id"
+  );
+  log(`Designtex products in mirror: ${rows.length}${DRY ? '  (DRY RUN — no writes)' : ''}`);
+  let updated = 0, errors = 0, withUom = 0;
+  for (const r of rows) {
+    try {
+      const res = await getJSON(`/admin/api/${API}/products/${r.shopify_id}/metafields.json?limit=250`);
+      const mfs = res.metafields || [];
+      const obj = {};
+      for (const m of mfs) (obj[m.namespace] ||= {})[m.key] = m.value;
+      if (obj?.global?.unit_of_measure) withUom++;
+      if (!DRY) {
+        await pool.query(
+          "UPDATE shopify_products SET metafields = COALESCE(metafields,'{}'::jsonb) || $2::jsonb, synced_at = NOW() WHERE shopify_id = $1",
+          [r.shopify_id, JSON.stringify(obj)]
+        );
+      }
+      updated++;
+      if (updated % 50 === 0) log(`  ${updated}/${rows.length} (uom-present: ${withUom})`);
+      await sleep(300);
+    } catch (e) {
+      errors++;
+      log(`  ERR ${r.shopify_id} (${r.title?.slice(0, 40)}): ${e.message}`);
+    }
+  }
+  log(`DONE: ${updated}${DRY ? ' would-update' : ' updated'}, ${withUom} carry global.unit_of_measure, ${errors} errors of ${rows.length}`);
+  await pool.end();
+})();
diff --git a/shopify/audit/wpb-grid-fixed.png b/shopify/audit/wpb-grid-fixed.png
new file mode 100644
index 00000000..d6b6cbbb
Binary files /dev/null and b/shopify/audit/wpb-grid-fixed.png differ
diff --git a/shopify/collection-hero-fix/inspect-grid-dom.js b/shopify/collection-hero-fix/inspect-grid-dom.js
new file mode 100644
index 00000000..aec801d1
--- /dev/null
+++ b/shopify/collection-hero-fix/inspect-grid-dom.js
@@ -0,0 +1,122 @@
+#!/usr/bin/env node
+/* Inspect the LIVE /collections/all Boost grid: container element, computed
+   grid-template-columns, actual column count, tile rendered vs natural dims,
+   and how the DW density slider wires in. Desktop (1440) + iPhone-13 (390). */
+const { chromium, devices } = require('playwright');
+
+const URL = 'https://www.designerwallcoverings.com/collections/all';
+
+async function probe(page, label) {
+  // wait for Boost product grid to hydrate
+  await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
+  // Boost renders client-side; wait for product images to appear
+  try {
+    await page.waitForSelector('.boost-sd__product-image-img, .boost-sd__product-item, [class*="boost-sd__product"]', { timeout: 30000 });
+  } catch (e) {
+    console.log(`[${label}] no boost product selector appeared in 30s`);
+  }
+  // give Boost layout a beat to settle after hydration
+  await page.waitForTimeout(2500);
+
+  const data = await page.evaluate(() => {
+    const out = { label: null, candidates: [], firstImg: null, sliderInfo: null, bodyClasses: document.body.className };
+
+    // find likely grid container(s)
+    const sels = [
+      '.boost-sd__product-list',
+      '.boost-sd__product-list-grid',
+      '.boost-sd__product-list-container',
+      '[class*="boost-sd__product-list"]',
+      '.boost-sd__refined-toolbar ~ * [class*="product-list"]',
+    ];
+    const seen = new Set();
+    for (const sel of sels) {
+      document.querySelectorAll(sel).forEach(el => {
+        if (seen.has(el)) return; seen.add(el);
+        const cs = getComputedStyle(el);
+        // count direct children that look like product items
+        const items = el.querySelectorAll(':scope > *');
+        out.candidates.push({
+          selector: sel,
+          className: el.className,
+          display: cs.display,
+          gridTemplateColumns: cs.gridTemplateColumns,
+          gap: cs.gap || cs.gridGap,
+          inlineStyle: el.getAttribute('style') || '',
+          directChildren: items.length,
+          width: Math.round(el.getBoundingClientRect().width),
+          // CSS custom props that might be the density var
+          varCols: cs.getPropertyValue('--cols').trim() || cs.getPropertyValue('--dw-cols').trim() || cs.getPropertyValue('--columns').trim() || '',
+        });
+      });
+    }
+
+    // product item width (track size) — pick first product item
+    const item = document.querySelector('.boost-sd__product-item, [class*="boost-sd__product-item"]');
+    if (item) {
+      const r = item.getBoundingClientRect();
+      out.itemBox = { w: Math.round(r.width), h: Math.round(r.height), className: item.className };
+    }
+
+    // first product image: natural vs rendered
+    const img = document.querySelector('.boost-sd__product-image-img--main, .boost-sd__product-image-img, img[class*="boost-sd__product-image"]');
+    if (img) {
+      const r = img.getBoundingClientRect();
+      const cs = getComputedStyle(img);
+      out.firstImg = {
+        natural: img.naturalWidth + 'x' + img.naturalHeight,
+        rendered: Math.round(r.width) + 'x' + Math.round(r.height),
+        objectFit: cs.objectFit,
+        src: (img.currentSrc || img.src || '').slice(0, 120),
+      };
+    }
+
+    // density slider — find the DW density control
+    const slider = document.querySelector('input[type="range"]');
+    if (slider) {
+      out.sliderInfo = {
+        id: slider.id, name: slider.name, className: slider.className,
+        value: slider.value, min: slider.min, max: slider.max,
+        // what does its change handler set? inspect nearby label
+        outerHTML: slider.outerHTML.slice(0, 300),
+      };
+      // what CSS var, if any, is on :root or a wrapper?
+      const root = document.documentElement;
+      const rcs = getComputedStyle(root);
+      out.rootVars = {
+        cols: rcs.getPropertyValue('--cols').trim(),
+        dwCols: rcs.getPropertyValue('--dw-cols').trim(),
+        columns: rcs.getPropertyValue('--columns').trim(),
+        gridCols: rcs.getPropertyValue('--grid-cols').trim(),
+      };
+    }
+
+    // also report any element whose computed display is grid in the product area
+    return out;
+  });
+
+  data.label = label;
+  return data;
+}
+
+(async () => {
+  const browser = await chromium.launch();
+
+  // Desktop 1440
+  const ctxD = await browser.newContext({ viewport: { width: 1440, height: 1000 }, userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36' });
+  const pD = await ctxD.newPage();
+  const d = await probe(pD, 'desktop-1440');
+  console.log(JSON.stringify(d, null, 2));
+  await pD.screenshot({ path: __dirname + '/screenshots/grid-before-desktop.png', fullPage: false });
+  await ctxD.close();
+
+  // iPhone 13
+  const ctxM = await browser.newContext({ ...devices['iPhone 13'] });
+  const pM = await ctxM.newPage();
+  const m = await probe(pM, 'iphone-13');
+  console.log(JSON.stringify(m, null, 2));
+  await pM.screenshot({ path: __dirname + '/screenshots/grid-before-mobile.png', fullPage: false });
+  await ctxM.close();
+
+  await browser.close();
+})().catch(e => { console.error(e); process.exit(1); });
diff --git a/shopify/collection-hero-fix/inspect-grid-wiring.js b/shopify/collection-hero-fix/inspect-grid-wiring.js
new file mode 100644
index 00000000..354d9dda
--- /dev/null
+++ b/shopify/collection-hero-fix/inspect-grid-wiring.js
@@ -0,0 +1,73 @@
+#!/usr/bin/env node
+/* Deeper probe: (1) capture the grid state at multiple times after load to catch
+   the 3-col->4-col hydration race; (2) find where the density slider's value is
+   applied (which element gets which inline style / class); (3) read the slider's
+   change handler effect by setting different values and re-measuring. Desktop only. */
+const { chromium } = require('playwright');
+const URL = 'https://www.designerwallcoverings.com/collections/all';
+
+(async () => {
+  const browser = await chromium.launch();
+  const ctx = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
+  const page = await ctx.newPage();
+  await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
+
+  // Sample the grid template at several points to catch any hydration flip
+  const samples = [];
+  for (const t of [800, 1500, 3000, 5000]) {
+    await page.waitForTimeout(t === 800 ? 800 : t - samples.reduce((a,s)=>0,0));
+    const snap = await page.evaluate(() => {
+      const el = document.querySelector('.boost-sd__product-list');
+      if (!el) return { none: true };
+      const cs = getComputedStyle(el);
+      return {
+        cls: el.className,
+        gtc: cs.gridTemplateColumns,
+        inline: el.getAttribute('style') || '',
+        children: el.querySelectorAll(':scope > *').length,
+      };
+    });
+    samples.push({ approxMs: t, ...snap });
+  }
+  console.log('=== hydration samples ===');
+  console.log(JSON.stringify(samples, null, 2));
+
+  // Now find the density slider + how it drives the grid.
+  // Read the DW slider script: search inline scripts for densitySlider handler.
+  const wiring = await page.evaluate(() => {
+    const r = {};
+    const slider = document.getElementById('densitySlider');
+    r.sliderExists = !!slider;
+    // Search all <style> + <script> text for densitySlider / --cols references
+    const scripts = [...document.querySelectorAll('script')].map(s => s.textContent || '').join('\n');
+    r.mentionsDensitySliderInScript = /densitySlider/.test(scripts);
+    // extract a window around densitySlider usage
+    const idx = scripts.indexOf('densitySlider');
+    r.scriptSnippet = idx >= 0 ? scripts.slice(Math.max(0, idx - 200), idx + 600) : '';
+    // What does the grid wrapper look like — is there a --cols var target?
+    const styles = [...document.querySelectorAll('style')].map(s => s.textContent || '').join('\n');
+    r.mentionsColsVarInStyle = /--cols|--dw-cols|grid-template-columns:\s*repeat\(var/.test(styles);
+    const sidx = styles.search(/--cols|--dw-cols/);
+    r.styleSnippet = sidx >= 0 ? styles.slice(Math.max(0, sidx - 200), sidx + 400) : '';
+    return r;
+  });
+  console.log('=== slider wiring ===');
+  console.log(JSON.stringify(wiring, null, 2));
+
+  // Drive the slider to value 6 and 8, re-measure grid columns
+  for (const v of ['6', '8', '4']) {
+    await page.evaluate((val) => {
+      const s = document.getElementById('densitySlider');
+      if (s) { s.value = val; s.dispatchEvent(new Event('input', { bubbles: true })); s.dispatchEvent(new Event('change', { bubbles: true })); }
+    }, v);
+    await page.waitForTimeout(600);
+    const after = await page.evaluate(() => {
+      const el = document.querySelector('.boost-sd__product-list');
+      const cs = getComputedStyle(el);
+      return { cls: el.className, gtc: cs.gridTemplateColumns, inline: el.getAttribute('style') || '' };
+    });
+    console.log(`=== after slider=${v} ===`, JSON.stringify(after));
+  }
+
+  await browser.close();
+})().catch(e => { console.error(e); process.exit(1); });
diff --git a/shopify/collection-hero-fix/inspect-slider-handler.js b/shopify/collection-hero-fix/inspect-slider-handler.js
new file mode 100644
index 00000000..4d73b1ac
--- /dev/null
+++ b/shopify/collection-hero-fix/inspect-slider-handler.js
@@ -0,0 +1,81 @@
+#!/usr/bin/env node
+/* Dump the FULL densitySlider handler from the live page + the Boost grid CSS
+   rules that set the column widths, so we know exactly what the slider targets
+   and what CSS we must override. Also re-measure at narrower desktop widths to
+   reproduce the "3 fat tiles upscaled" symptom. */
+const { chromium } = require('playwright');
+const URL = 'https://www.designerwallcoverings.com/collections/all';
+
+(async () => {
+  const browser = await chromium.launch();
+  const ctx = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
+  const page = await ctx.newPage();
+  await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
+  await page.waitForSelector('.boost-sd__product-list', { timeout: 30000 });
+  await page.waitForTimeout(2000);
+
+  const handler = await page.evaluate(() => {
+    const scripts = [...document.querySelectorAll('script')].map(s => s.textContent || '').join('\n');
+    const i = scripts.indexOf('densitySlider');
+    // grab a big window to capture the whole IIFE
+    return scripts.slice(Math.max(0, i - 50), i + 2200);
+  });
+  console.log('=== FULL densitySlider handler ===');
+  console.log(handler);
+
+  // Find which Boost CSS rules set the grid-template-columns for --3-col/4-col etc.
+  const boostRules = await page.evaluate(() => {
+    const hits = [];
+    for (const sheet of document.styleSheets) {
+      let rules;
+      try { rules = sheet.cssRules; } catch (e) { continue; }
+      if (!rules) continue;
+      for (const rule of rules) {
+        const t = rule.cssText || '';
+        if (/boost-sd__product-list/.test(t) && /grid-template-columns|grid-template/.test(t)) {
+          hits.push({ href: sheet.href ? sheet.href.slice(-60) : 'inline', text: t.slice(0, 300) });
+        }
+        // also catch media-query-wrapped rules
+        if (rule.cssRules) {
+          for (const inner of rule.cssRules) {
+            const it = inner.cssText || '';
+            if (/boost-sd__product-list/.test(it) && /grid-template/.test(it)) {
+              hits.push({ href: sheet.href ? sheet.href.slice(-60) : 'inline', media: rule.conditionText || rule.media?.mediaText || '', text: it.slice(0, 300) });
+            }
+          }
+        }
+      }
+    }
+    return hits;
+  });
+  console.log('\n=== Boost grid-template-columns CSS rules ===');
+  console.log(JSON.stringify(boostRules, null, 2));
+
+  await browser.close();
+
+  // Reproduce symptom at narrower desktop widths
+  for (const w of [1280, 1100, 1000, 990]) {
+    const b2 = await chromium.launch();
+    const c2 = await b2.newContext({ viewport: { width: w, height: 900 } });
+    const p2 = await c2.newPage();
+    await p2.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
+    try { await p2.waitForSelector('.boost-sd__product-image-img', { timeout: 20000 }); } catch {}
+    await p2.waitForTimeout(2000);
+    const r = await p2.evaluate(() => {
+      const el = document.querySelector('.boost-sd__product-list');
+      const cs = getComputedStyle(el);
+      const img = document.querySelector('.boost-sd__product-image-img--main, .boost-sd__product-image-img');
+      const ir = img ? img.getBoundingClientRect() : null;
+      return {
+        cls: el.className.replace('boost-sd__product-list ', ''),
+        gtc: cs.gridTemplateColumns,
+        gridW: Math.round(el.getBoundingClientRect().width),
+        imgNatural: img ? img.naturalWidth + 'x' + img.naturalHeight : null,
+        imgRendered: ir ? Math.round(ir.width) + 'x' + Math.round(ir.height) : null,
+        upscaled: img && ir ? (ir.width > img.naturalWidth + 2) : null,
+      };
+    });
+    console.log(`\n=== viewport ${w}px ===`, JSON.stringify(r));
+    await b2.close();
+  }
+})().catch(e => { console.error(e); process.exit(1); });
diff --git a/shopify/scripts/cadence/data/cadence-cursor.json b/shopify/scripts/cadence/data/cadence-cursor.json
index b0da392b..27534d1c 100644
--- a/shopify/scripts/cadence/data/cadence-cursor.json
+++ b/shopify/scripts/cadence/data/cadence-cursor.json
@@ -1,5 +1,5 @@
 {
- "idx": 6,
+ "idx": 4,
  "lastSlot": "pm",
  "lastRun": "2026-06-23"
 }
\ No newline at end of file
diff --git a/shopify/scripts/cadence/data/cadence-plan-pm-2026-06-23.json b/shopify/scripts/cadence/data/cadence-plan-pm-2026-06-23.json
index ed065a26..f63bedc2 100644
--- a/shopify/scripts/cadence/data/cadence-plan-pm-2026-06-23.json
+++ b/shopify/scripts/cadence/data/cadence-plan-pm-2026-06-23.json
@@ -1,164 +1,164 @@
 [
  {
-  "vendor": "Romo",
-  "dw_sku": "DWRM-240010",
-  "cost": 467.502,
-  "retail": 846.16,
+  "vendor": "Kravet",
+  "dw_sku": "DWKK-102043",
+  "cost": 81.95,
+  "retail": 122.93,
   "sample": "4.25",
-  "title": "Abaca, Indigo Wallcoverings | Romo",
+  "title": "Highhope, Off-White Wallcoverings | Kravet",
   "willActivate": true
  },
  {
-  "vendor": "Romo",
-  "dw_sku": "DWRM-240015",
-  "cost": 610.5015,
-  "retail": 1104.98,
+  "vendor": "Kravet",
+  "dw_sku": "DWKK-102044",
+  "cost": 81.95,
+  "retail": 122.93,
   "sample": "4.25",
-  "title": "Savanna, Stone Wallcoverings | Romo",
+  "title": "Highhope, White Wallcoverings | Kravet",
   "willActivate": true
  },
  {
-  "vendor": "Romo",
-  "dw_sku": "DWRM-240018",
-  "cost": 475.2,
-  "retail": 860.09,
+  "vendor": "Kravet",
+  "dw_sku": "DWKK-102045",
+  "cost": 92.95,
+  "retail": 139.43,
   "sample": "4.25",
-  "title": "Raffia, Sepia Wallcoverings | Romo",
+  "title": "Bodenham, Off-White Wallcoverings | Kravet",
   "willActivate": true
  },
  {
-  "vendor": "Thibaut",
-  "dw_sku": "DWTT-73408",
-  "cost": 61.2,
-  "retail": 110.77,
+  "vendor": "Schumacher",
+  "dw_sku": "DWLK-805170",
+  "cost": 58,
+  "retail": 104.98,
   "sample": "4.25",
-  "title": "Donavin Diamond, Green Wallcoverings | Thibaut",
+  "title": "Andromeda, Sand Wallcoverings | Schumacher",
   "willActivate": true
  },
  {
-  "vendor": "Thibaut",
-  "dw_sku": "DWTT-73409",
-  "cost": 45.9,
-  "retail": 83.08,
+  "vendor": "Schumacher",
+  "dw_sku": "DWLK-805180",
+  "cost": 58,
+  "retail": 104.98,
   "sample": "4.25",
-  "title": "Jules, Blue Wallcoverings | Thibaut",
+  "title": "Andromeda, Pink Wallcoverings | Schumacher",
   "willActivate": true
  },
  {
-  "vendor": "Thibaut",
-  "dw_sku": "DWTT-73410",
-  "cost": 63.9,
-  "retail": 115.66,
+  "vendor": "Schumacher",
+  "dw_sku": "DWLK-805190",
+  "cost": 38,
+  "retail": 68.78,
   "sample": "4.25",
-  "title": "Kahna, Black Wallcoverings | Thibaut",
+  "title": "Hellene Mylar, Gold Wallcoverings | Schumacher",
   "willActivate": true
  },
  {
-  "vendor": "Zoffany",
-  "dw_sku": "DWZF-187007",
-  "cost": 281,
-  "retail": 508.6,
+  "vendor": "Romo",
+  "dw_sku": "DWRM-240019",
+  "cost": 475.2,
+  "retail": 860.09,
   "sample": "4.25",
-  "title": "Hawksmoor, Antique Bronze Wallcoverings | Zoffany",
+  "title": "Raffia, Ochre Wallcoverings | Romo",
   "willActivate": true
  },
  {
-  "vendor": "Zoffany",
-  "dw_sku": "DWZF-187011",
-  "cost": 281,
-  "retail": 508.6,
+  "vendor": "Romo",
+  "dw_sku": "DWRM-240021",
+  "cost": 475.2,
+  "retail": 860.09,
   "sample": "4.25",
-  "title": "Abstract 1928, Mineral Wallcoverings | Zoffany",
+  "title": "Raffia, Teal Wallcoverings | Romo",
   "willActivate": true
  },
  {
-  "vendor": "Zoffany",
-  "dw_sku": "DWZF-187012",
-  "cost": 216,
-  "retail": 390.95,
+  "vendor": "Romo",
+  "dw_sku": "DWRM-240023",
+  "cost": 542.304,
+  "retail": 981.55,
   "sample": "4.25",
-  "title": "Shagreen, Como Blue Wallcoverings | Zoffany",
+  "title": "Akata, Sepia Wallcoverings | Romo",
   "willActivate": true
  },
  {
-  "vendor": "Designtex",
-  "dw_sku": "DWDX-220309",
-  "cost": 22,
-  "retail": 39.82,
+  "vendor": "Thibaut",
+  "dw_sku": "DWTT-73411",
+  "cost": 63.9,
+  "retail": 115.66,
   "sample": "4.25",
-  "title": "Pebble, Hematite Wallcoverings | Designtex",
+  "title": "Kahna, Metallic Gold and Silver Wallcoverings | Thibaut",
   "willActivate": true
  },
  {
-  "vendor": "Designtex",
-  "dw_sku": "DWDX-220310",
-  "cost": 22,
-  "retail": 39.82,
+  "vendor": "Thibaut",
+  "dw_sku": "DWTT-73412",
+  "cost": 63.9,
+  "retail": 115.66,
   "sample": "4.25",
-  "title": "Pebble, Alabaster Wallcoverings | Designtex",
+  "title": "Kahna, Blue Wallcoverings | Thibaut",
   "willActivate": true
  },
  {
-  "vendor": "Designtex",
-  "dw_sku": "DWDX-220311",
-  "cost": 22,
-  "retail": 39.82,
+  "vendor": "Thibaut",
+  "dw_sku": "DWTT-73413",
+  "cost": 63.9,
+  "retail": 115.66,
   "sample": "4.25",
-  "title": "Pebble, Starfish Wallcoverings | Designtex",
+  "title": "Kahna, Cream and Robin's Egg Wallcoverings | Thibaut",
   "willActivate": true
  },
  {
-  "vendor": "Newwall",
-  "dw_sku": "DWXW-1005017",
-  "cost": 156,
-  "retail": 282.35,
+  "vendor": "Zoffany",
+  "dw_sku": "DWZF-187014",
+  "cost": 216,
+  "retail": 390.95,
   "sample": "4.25",
-  "title": "Uzbek Black, Black Wallcoverings | Newwall",
+  "title": "Spun Silk, Antique Bronze Wallcoverings | Zoffany",
   "willActivate": true
  },
  {
-  "vendor": "Newwall",
-  "dw_sku": "DWXW-1005018",
-  "cost": 156,
-  "retail": 282.35,
+  "vendor": "Zoffany",
+  "dw_sku": "DWZF-187015",
+  "cost": 140,
+  "retail": 253.39,
   "sample": "4.25",
-  "title": "Uzbek Clay, Clay Wallcoverings | Newwall",
+  "title": "Persian Tulip, Quartz Grey Wallcoverings | Zoffany",
   "willActivate": true
  },
  {
-  "vendor": "Newwall",
-  "dw_sku": "DWXW-1005019",
-  "cost": 156,
-  "retail": 282.35,
+  "vendor": "Zoffany",
+  "dw_sku": "DWZF-187016",
+  "cost": 208,
+  "retail": 376.47,
   "sample": "4.25",
-  "title": "Uzbek Green, Green Wallcoverings | Newwall",
+  "title": "Guinea, Blue Stone Wallcoverings | Zoffany",
   "willActivate": true
  },
  {
-  "vendor": "Brewster & York",
-  "dw_sku": "DWBR-170670",
-  "cost": 69.99,
-  "retail": 126.68,
+  "vendor": "Designtex",
+  "dw_sku": "DWDX-220312",
+  "cost": 22,
+  "retail": 39.82,
   "sample": "4.25",
-  "title": "Adrian, Honey Wallcoverings | Brewster & York",
+  "title": "Pebble, Conifer Wallcoverings | Designtex",
   "willActivate": true
  },
  {
-  "vendor": "Brewster & York",
-  "dw_sku": "DWBR-170671",
-  "cost": 35,
-  "retail": 63.35,
+  "vendor": "Designtex",
+  "dw_sku": "DWDX-220313",
+  "cost": 22,
+  "retail": 39.82,
   "sample": "4.25",
-  "title": "Adrian, Plum Wallcoverings | Brewster & York",
-  "willActivate": true
+  "title": "Pebble, Pine Wallcoverings | Designtex",
+  "willActivate": false
  },
  {
-  "vendor": "Brewster & York",
-  "dw_sku": "DWBR-170672",
-  "cost": 69.99,
-  "retail": 126.68,
+  "vendor": "Designtex",
+  "dw_sku": "DWDX-220314",
+  "cost": 22,
+  "retail": 39.82,
   "sample": "4.25",
-  "title": "Elsie, Sky Blue Wallcoverings | Brewster & York",
+  "title": "Pebble, Waterfall Wallcoverings | Designtex",
   "willActivate": true
  }
 ]
\ No newline at end of file
diff --git a/shopify/scripts/cadence/data/upload-restore-2026-06-23.jsonl b/shopify/scripts/cadence/data/upload-restore-2026-06-23.jsonl
index 1c890746..334e8757 100644
--- a/shopify/scripts/cadence/data/upload-restore-2026-06-23.jsonl
+++ b/shopify/scripts/cadence/data/upload-restore-2026-06-23.jsonl
@@ -70,3 +70,21 @@
 {"table":"brewster_catalog","mfr_sku":"2657-22214","dw_sku":"DWBR-170670","shopify_product_id":"7866903134259","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T22-40-05-101Z","ts":"2026-06-23T22:41:07.451Z"}
 {"table":"brewster_catalog","mfr_sku":"2657-22213","dw_sku":"DWBR-170671","shopify_product_id":"7866903167027","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T22-40-05-101Z","ts":"2026-06-23T22:41:11.106Z"}
 {"table":"brewster_catalog","mfr_sku":"2657-22215","dw_sku":"DWBR-170672","shopify_product_id":"7866903199795","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T22-40-05-101Z","ts":"2026-06-23T22:41:14.532Z"}
+{"table":"kravet_catalog","mfr_sku":"35301.12.0","dw_sku":"DWKK-102043","shopify_product_id":"7424885456947","action":"linked-existing","activated":false,"batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:04.041Z"}
+{"table":"kravet_catalog","mfr_sku":"35301.15.0","dw_sku":"DWKK-102044","shopify_product_id":"7424885489715","action":"linked-existing","activated":false,"batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:04.914Z"}
+{"table":"kravet_catalog","mfr_sku":"35302.15.0","dw_sku":"DWKK-102045","shopify_product_id":"7424885522483","action":"linked-existing","activated":false,"batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:05.697Z"}
+{"table":"schumacher_catalog","mfr_sku":"5010574","dw_sku":"DWLK-805170","shopify_product_id":"7866911916083","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:11.082Z"}
+{"table":"schumacher_catalog","mfr_sku":"5010575","dw_sku":"DWLK-805180","shopify_product_id":"7866911948851","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:15.659Z"}
+{"table":"schumacher_catalog","mfr_sku":"5010611","dw_sku":"DWLK-805190","shopify_product_id":"7866911981619","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:20.228Z"}
+{"table":"romo_catalog","mfr_sku":"MW103/04","dw_sku":"DWRM-240019","shopify_product_id":"7866912014387","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:24.992Z"}
+{"table":"romo_catalog","mfr_sku":"MW103/07","dw_sku":"DWRM-240021","shopify_product_id":"7866912079923","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:29.041Z"}
+{"table":"romo_catalog","mfr_sku":"MW104/02","dw_sku":"DWRM-240023","shopify_product_id":"7866912112691","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:32.588Z"}
+{"table":"thibaut_catalog","mfr_sku":"AT78779","dw_sku":"DWTT-73411","shopify_product_id":"7866912145459","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:37.237Z"}
+{"table":"thibaut_catalog","mfr_sku":"AT78780","dw_sku":"DWTT-73412","shopify_product_id":"7866912178227","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:41.788Z"}
+{"table":"thibaut_catalog","mfr_sku":"AT78781","dw_sku":"DWTT-73413","shopify_product_id":"7866912210995","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:46.135Z"}
+{"table":"zoffany_catalog","mfr_sku":"ZRHW312897","dw_sku":"DWZF-187014","shopify_product_id":"7866912243763","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:50.708Z"}
+{"table":"zoffany_catalog","mfr_sku":"ZHIW312995","dw_sku":"DWZF-187015","shopify_product_id":"7866912276531","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:54.782Z"}
+{"table":"zoffany_catalog","mfr_sku":"ZKEM312648","dw_sku":"DWZF-187016","shopify_product_id":"7866912309299","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:40:58.365Z"}
+{"table":"designtex_catalog","mfr_sku":"8300651","dw_sku":"DWDX-220312","shopify_product_id":"7866912342067","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:41:02.462Z"}
+{"table":"designtex_catalog","mfr_sku":"8300701","dw_sku":"DWDX-220313","shopify_product_id":"7866912407603","action":"created","activated":false,"published":false,"status":"DRAFT","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:41:04.823Z"}
+{"table":"designtex_catalog","mfr_sku":"8300751","dw_sku":"DWDX-220314","shopify_product_id":"7866912440371","action":"created","activated":true,"published":true,"status":"ACTIVE","batch_id":"upload-pm-2026-06-23T23-40-02-490Z","ts":"2026-06-23T23:41:09.031Z"}

← fff4983c Add gated publish-5.1-to-live script (Steve runs to swap liv  ·  back to Designer Wallcoverings  ·  Record Gemini key hygiene progress: 4/6 files env-ified, 2 h 6e0add2e →