[object Object]

← back to Groundworks Activation

Bucket-3 butterfly publish script: 3 Flutter Sisal net-new (Steve settlement sign-off), new DWKK, authoritative MAP

52e3d5d9386f22c5d5eb0759164c338b52e51190 · 2026-07-22 08:41:03 -0700 · Steve Abrams

Files touched

Diff

commit 52e3d5d9386f22c5d5eb0759164c338b52e51190
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 22 08:41:03 2026 -0700

    Bucket-3 butterfly publish script: 3 Flutter Sisal net-new (Steve settlement sign-off), new DWKK, authoritative MAP
---
 data/publish-bucket2-results.json | 104 ++++++++++++++---------------
 data/publish-bucket3-results.json |  26 ++++++++
 scripts/publish-bucket3.js        | 135 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 213 insertions(+), 52 deletions(-)

diff --git a/data/publish-bucket2-results.json b/data/publish-bucket2-results.json
index af590fd..5e6de97 100644
--- a/data/publish-bucket2-results.json
+++ b/data/publish-bucket2-results.json
@@ -2,261 +2,261 @@
   {
     "mfr_sku": "GWP-3417.816.0",
     "dwSku": "DWKK-106908",
+    "pid": "7896278335539",
     "title": "Channels Paper Ebony/Ivory | Groundworks",
     "price": "747.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Each",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3302.516 NEW.0",
     "dwSku": "DWKK-106890",
+    "pid": "7896278401075",
     "title": "Channels Paper Periwinkle Oat | Groundworks",
     "price": "747.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Each",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3417.516.0",
     "dwSku": "DWKK-106907",
+    "pid": "7896278433843",
     "title": "Channels Paper Periwinkle/Oat | Groundworks",
     "price": "747.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Each",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3417.911.0",
     "dwSku": "DWKK-106909",
+    "pid": "7896278499379",
     "title": "Channels Paper Plum/Oatmeal | Groundworks",
     "price": "747.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Each",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3304.816.0",
     "dwSku": "DWKK-106897",
+    "pid": "7896278532147",
     "title": "Crescent Paper Ebony/Cream | Groundworks",
     "price": "897.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3304.48.0",
     "dwSku": "DWKK-106895",
+    "pid": "7896278564915",
     "title": "Crescent Paper Ebony/Gold | Groundworks",
     "price": "897.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3304.416.0",
     "dwSku": "DWKK-106894",
+    "pid": "7896278630451",
     "title": "Crescent Paper Gold/Ivory | Groundworks",
     "price": "897.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3304.316.0",
     "dwSku": "DWKK-106893",
+    "pid": "7896278663219",
     "title": "Crescent Paper Lake/Cream | Groundworks",
     "price": "897.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3304.611.0",
     "dwSku": "DWKK-106896",
+    "pid": "7896278695987",
     "title": "Crescent Paper Taupe/Putty | Groundworks",
     "price": "897.00",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 10,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3413.101.0",
     "dwSku": "DWKK-106899",
+    "pid": "7896278728755",
     "title": "Hutch Cream | Groundworks",
     "price": "742.50",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 8,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3413.10.0",
     "dwSku": "DWKK-106898",
+    "pid": "7896278761523",
     "title": "Hutch Lavender | Groundworks",
     "price": "742.50",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 8,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3413.17.0",
     "dwSku": "DWKK-106902",
+    "pid": "7896278794291",
     "title": "Hutch Pink | Groundworks",
     "price": "742.50",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3735.1311.0",
     "dwSku": "DWKK-106976",
+    "pid": "7896278827059",
     "title": "Orchard Paper Aqua/Stone | Groundworks",
     "price": "549.68",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27.5In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3735.55.0",
     "dwSku": "DWKK-106978",
+    "pid": "7896278859827",
     "title": "Orchard Paper Blue/Denim | Groundworks",
     "price": "549.68",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27.5In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3735.421.0",
     "dwSku": "DWKK-106977",
+    "pid": "7896278892595",
     "title": "Orchard Paper Pebble/Coin | Groundworks",
     "price": "549.68",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  27.5In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3727.635.0",
     "dwSku": "DWKK-106963",
+    "pid": "7896278925363",
     "title": "Roulade Paper Aqua/Dune | Groundworks",
     "price": "707.18",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3727.640.0",
     "dwSku": "DWKK-106964",
+    "pid": "7896278958131",
     "title": "Roulade Paper Citron/Stone | Groundworks",
     "price": "707.18",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3727.630.0",
     "dwSku": "DWKK-106962",
+    "pid": "7896278990899",
     "title": "Roulade Paper Juniper/Buff | Groundworks",
     "price": "707.18",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3727.355.0",
     "dwSku": "DWKK-106961",
+    "pid": "7896279056435",
     "title": "Roulade Paper Navy/Teal | Groundworks",
     "price": "707.18",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3727.73.0",
     "dwSku": "DWKK-106965",
+    "pid": "7896279089203",
     "title": "Roulade Paper Rose/Leaf | Groundworks",
     "price": "707.18",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3726.503.0",
     "dwSku": "DWKK-106958",
+    "pid": "7896279121971",
     "title": "Twister Paper Blue/Leaf | Groundworks",
     "price": "689.85",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3726.355.0",
     "dwSku": "DWKK-106956",
+    "pid": "7896279154739",
     "title": "Twister Paper Denim/Aqua | Groundworks",
     "price": "689.85",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3726.352.0",
     "dwSku": "DWKK-106955",
+    "pid": "7896279187507",
     "title": "Twister Paper Kiwi/Slate | Groundworks",
     "price": "689.85",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3726.357.0",
     "dwSku": "DWKK-106957",
+    "pid": "7896279220275",
     "title": "Twister Paper Petal/Teal | Groundworks",
     "price": "689.85",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3726.512.0",
     "dwSku": "DWKK-106959",
+    "pid": "7896279253043",
     "title": "Twister Paper Sky/Tang | Groundworks",
     "price": "689.85",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   },
   {
     "mfr_sku": "GWP-3726.86.0",
     "dwSku": "DWKK-106960",
+    "pid": "7896279285811",
     "title": "Twister Paper Tobacco/Noir | Groundworks",
     "price": "689.85",
     "map_src": "authoritative_new_map",
-    "unit": "Sold Per Roll -  28.35In Wide",
-    "tags": 9,
+    "active": true,
     "before_vendor": "Kravet(registry)"
   }
 ]
\ No newline at end of file
diff --git a/data/publish-bucket3-results.json b/data/publish-bucket3-results.json
new file mode 100644
index 0000000..d6a6cc7
--- /dev/null
+++ b/data/publish-bucket3-results.json
@@ -0,0 +1,26 @@
+[
+  {
+    "mfr_sku": "GWP-3746.194.0",
+    "dwSku": "DWKK-158364",
+    "title": "Flutter Sisal Rainbow | Groundworks",
+    "price": "2101.05",
+    "map_src": "authoritative_new_map",
+    "unit": "Sold Per Roll -  33.5In Wide"
+  },
+  {
+    "mfr_sku": "GWP-3746.712.0",
+    "dwSku": "DWKK-158374",
+    "title": "Flutter Sisal Sunset | Groundworks",
+    "price": "2101.05",
+    "map_src": "authoritative_new_map",
+    "unit": "Sold Per Roll -  33.5In Wide"
+  },
+  {
+    "mfr_sku": "GWP-3746.513.0",
+    "dwSku": "DWKK-158384",
+    "title": "Flutter Sisal The Blues | Groundworks",
+    "price": "2101.05",
+    "map_src": "authoritative_new_map",
+    "unit": "Sold Per Roll -  33.5In Wide"
+  }
+]
\ No newline at end of file
diff --git a/scripts/publish-bucket3.js b/scripts/publish-bucket3.js
new file mode 100644
index 0000000..eb668fd
--- /dev/null
+++ b/scripts/publish-bucket3.js
@@ -0,0 +1,135 @@
+#!/usr/bin/env node
+/**
+ * Bucket 3 — 3 butterfly Flutter Sisal (GWP-3746.194/.712/.513). Settlement verdict was
+ * NEEDS REVIEW (Part B butterflies, no full Part A) — Steve has given the human sign-off the
+ * settlement rule requires. Publish net-new with the full 5-field gate + MAP (authoritative).
+ * These are NOT in the registry, so mint NEW DWKK numbers.
+ *
+ * Usage: node publish-bucket3.js [--dry]
+ */
+const { Client } = require('pg');
+const fs = require('fs');
+
+const DRY = process.argv.includes('--dry');
+const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const TOKEN = (fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8')
+  .split('\n').find(l => l.startsWith('SHOPIFY_ADMIN_TOKEN=')) || '').split('=').slice(1).join('=').trim();
+if (!TOKEN) { console.error('no SHOPIFY_ADMIN_TOKEN'); process.exit(2); }
+
+const GQL = `https://${DOMAIN}/admin/api/${API}/graphql.json`;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+const BUTTERFLIES = ['GWP-3746.194.0', 'GWP-3746.712.0', 'GWP-3746.513.0'];
+
+async function shopify(query, variables) {
+  for (let attempt = 0; attempt < 6; attempt++) {
+    const res = await fetch(GQL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOKEN }, body: JSON.stringify({ query, variables }) });
+    if (res.status === 429 || res.status >= 500) { await sleep(2500 * (attempt + 1)); continue; }
+    const j = await res.json();
+    if (j.errors) throw new Error('GQL: ' + JSON.stringify(j.errors).slice(0, 400));
+    const cost = j.extensions?.cost?.throttleStatus;
+    if (cost && cost.currentlyAvailable < 250) await sleep(1500);
+    return j.data;
+  }
+  throw new Error('shopify retries exhausted');
+}
+function titleCase(s) { return (s || '').toLowerCase().replace(/\b\w/g, c => c.toUpperCase()).replace(/\bWc\b/gi, '').replace(/\bWp\b/gi, '').replace(/\s{2,}/g, ' ').trim(); }
+function parsePattern(name) { const idx = name.lastIndexOf(' - '); if (idx < 0) return { pattern: titleCase(name), color: '' }; return { pattern: titleCase(name.slice(0, idx)), color: titleCase(name.slice(idx + 3)) }; }
+function buildTitle(row) { const { pattern, color } = parsePattern(row.name); const rc = color || row.color_name || ''; if (pattern && rc) return `${pattern} ${rc} | Groundworks`; if (pattern) return `${pattern} | Groundworks`; return `${row.mfr_sku} | Groundworks`; }
+function unitLabel(row) { const w = row.width ? `${row.width}` : ''; if (row.uom === 'Yard') return `Sold Per Yard${w ? ` -  ${w}In Wide` : ''}`; if (row.uom === 'Each') return `Sold Each`; return `Sold Per Roll${w ? ` -  ${w}In Wide` : ''}`; }
+function buildDescription(row) {
+  const { pattern, color } = parsePattern(row.name);
+  let h = `<p>${pattern}${color ? ` in ${color}` : ''} is a Groundworks wallcovering`;
+  if (row.designer) h += ` designed by ${row.designer}`;
+  if (row.collection) h += `, from the ${titleCase(row.collection)} collection`;
+  h += '.';
+  const specs = [];
+  if (row.composition) specs.push(`Composition: ${row.composition}`);
+  if (row.made_in) specs.push(`Made in ${row.made_in}`);
+  if (row.repeat_v || row.repeat_h) { const rp = [row.repeat_v && `${row.repeat_v}" vertical`, row.repeat_h && `${row.repeat_h}" horizontal`].filter(Boolean).join(', '); specs.push(`Repeat: ${rp}`); }
+  if (row.width) specs.push(`Width: ${row.width}"`);
+  if (specs.length) h += ` ${specs.join('. ')}.`;
+  return h + '</p>';
+}
+function buildTags(row) {
+  const { pattern, color } = parsePattern(row.name);
+  const t = new Set(['Wallcovering', 'Groundworks']);
+  if (pattern) t.add(pattern); if (color) t.add(color);
+  if (row.color_name) t.add(row.color_name); if (row.color_bucket) t.add(row.color_bucket);
+  if (row.collection) t.add(titleCase(row.collection)); if (row.designer) t.add(row.designer);
+  t.add(row.mfr_sku); if (row.width) t.add(`${row.width}In`);
+  return [...t].filter(Boolean);
+}
+async function nextDwkkStart(pg) {
+  const r = await pg.query(`SELECT COALESCE(MAX((regexp_replace(dw_sku,'^DWKK-',''))::bigint),0) AS mx FROM dw_sku_registry WHERE dw_sku ~ '^DWKK-[0-9]+$'`);
+  return Number(r.rows[0].mx);
+}
+
+async function main() {
+  const pg = new Client({ host: '/tmp', database: 'dw_unified' });
+  await pg.connect();
+  const rows = (await pg.query(`SELECT a.*, k.new_map AS auth_map FROM groundworks_catalog a
+    LEFT JOIN kravet_authoritative_pricing k ON k.mfr_sku=a.mfr_sku
+    WHERE a.mfr_sku = ANY($1) ORDER BY a.name`, [BUTTERFLIES])).rows;
+  console.log(`Bucket-3 (butterflies): ${rows.length} SKUs  DRY=${DRY}`);
+  let counter = await nextDwkkStart(pg);
+  const results = [];
+  for (const row of rows) {
+    // NEVER-DUPLICATE re-check
+    const dup = await pg.query(`SELECT 1 FROM shopify_products WHERE mfr_sku=$1 UNION SELECT 1 FROM dw_sku_registry WHERE mfr_sku=$1 LIMIT 1`, [row.mfr_sku]);
+    if (dup.rowCount) { results.push({ mfr_sku: row.mfr_sku, skip: 'dup-appeared' }); continue; }
+    counter += 10;
+    const dwSku = `DWKK-${counter}`;
+    const title = buildTitle(row);
+    const MAP = (row.auth_map != null && Number(row.auth_map) > 0) ? Number(row.auth_map) : Number(row.map_price);
+    const mapSrc = (row.auth_map != null && Number(row.auth_map) > 0) ? 'authoritative_new_map' : 'staging_map';
+    const price = Number(MAP).toFixed(2);
+    if (!(Number(price) > 0)) { results.push({ mfr_sku: row.mfr_sku, dwSku, skip: 'no-price' }); continue; }
+    if (!row.image_url) { results.push({ mfr_sku: row.mfr_sku, dwSku, skip: 'no-image' }); continue; }
+    const input = { title, vendor: 'Groundworks', productType: 'Wallcovering', descriptionHtml: buildDescription(row), tags: buildTags(row), handle: dwSku.toLowerCase() };
+    if (DRY) { results.push({ mfr_sku: row.mfr_sku, dwSku, title, price, map_src: mapSrc, unit: unitLabel(row) }); continue; }
+
+    const cr = await shopify(`mutation($input:ProductInput!){productCreate(input:$input){product{id handle} userErrors{field message}}}`, { input: { ...input, status: 'DRAFT' } });
+    if (cr.productCreate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, err: JSON.stringify(cr.productCreate.userErrors) }); continue; }
+    const pid = cr.productCreate.product.id; const numericPid = pid.split('/').pop();
+
+    let imgOk = false;
+    try {
+      const os = require('os'); const { execSync } = require('child_process');
+      const tmp = `${os.tmpdir()}/gw_${dwSku}.jpg`;
+      execSync(`curl -sL -A "Mozilla/5.0" -e "https://www.kravet.com/" "${row.image_url}" -o "${tmp}"`, { stdio: 'ignore' });
+      execSync(`sips -Z 2000 "${tmp}" --out "${tmp}" >/dev/null 2>&1`);
+      const b64 = fs.readFileSync(tmp).toString('base64');
+      const ir = await fetch(`https://${DOMAIN}/admin/api/${API}/products/${numericPid}/images.json`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOKEN }, body: JSON.stringify({ image: { attachment: b64, filename: `${row.mfr_sku}.jpg` } }) });
+      const ij = await ir.json(); if (ij.image && ij.image.id) imgOk = true;
+      fs.unlinkSync(tmp);
+    } catch (e) {}
+
+    const sellLabel = unitLabel(row);
+    const prodQ = await shopify(`query($id:ID!){product(id:$id){variants(first:5){nodes{id}}options{id name optionValues{id name}}}}`, { id: pid });
+    const optId = prodQ.product.options[0].id, optValId = prodQ.product.options[0].optionValues[0].id, defVarId = prodQ.product.variants.nodes[0].id;
+    const rn = await shopify(`mutation($pid:ID!,$opt:OptionUpdateInput!,$vals:[OptionValueUpdateInput!]!){productOptionUpdate(productId:$pid,option:$opt,optionValuesToUpdate:$vals){userErrors{field message}}}`, { pid, opt: { id: optId, name: 'Size' }, vals: [{ id: optValId, name: sellLabel }] });
+    if (rn.productOptionUpdate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, err: 'optRename:' + JSON.stringify(rn.productOptionUpdate.userErrors) }); continue; }
+    const su = await shopify(`mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$pid,variants:$variants){userErrors{field message}}}`, { pid, variants: [{ id: defVarId, price, inventoryPolicy: 'CONTINUE', inventoryItem: { sku: dwSku, tracked: false } }] });
+    if (su.productVariantsBulkUpdate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, err: 'sellUpd:' + JSON.stringify(su.productVariantsBulkUpdate.userErrors) }); continue; }
+    const sc = await shopify(`mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkCreate(productId:$pid,variants:$variants){userErrors{field message}}}`, { pid, variants: [{ price: '4.25', inventoryPolicy: 'CONTINUE', inventoryItem: { sku: `${dwSku}-Sample`, tracked: false }, optionValues: [{ optionName: 'Size', name: 'Sample' }] }] });
+    if (sc.productVariantsBulkCreate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, err: 'sampleCreate:' + JSON.stringify(sc.productVariantsBulkCreate.userErrors) }); continue; }
+
+    if (imgOk) await shopify(`mutation($input:ProductInput!){productUpdate(input:$input){userErrors{field message}}}`, { input: { id: pid, status: 'ACTIVE' } });
+    else await shopify(`mutation($input:ProductInput!){productUpdate(input:$input){userErrors{field message}}}`, { input: { id: pid, status: 'DRAFT', tags: [...input.tags, 'Needs-Image'] } });
+
+    await pg.query(`INSERT INTO dw_sku_registry(dw_sku,vendor_prefix,vendor_name,mfr_sku,shopify_product_id,shopify_handle,status) VALUES($1,'DWKK','Groundworks',$2,$3,$4,'active') ON CONFLICT (dw_sku) DO NOTHING`, [dwSku, row.mfr_sku, numericPid, cr.productCreate.product.handle]);
+    await pg.query(`UPDATE groundworks_catalog SET dw_sku=$1, shopify_product_id=$2 WHERE mfr_sku=$3`, [dwSku, numericPid, row.mfr_sku]);
+    await pg.query(`INSERT INTO kravet_master_price(mfr_sku,whls_cost,map_price,brand,source) VALUES($1,$2,$3,'Groundworks','groundworks-activation-b3') ON CONFLICT (mfr_sku) DO UPDATE SET map_price=EXCLUDED.map_price,brand='Groundworks'`, [row.mfr_sku, row.wholesale, MAP]);
+
+    results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, title, price, map_src: mapSrc, active: imgOk });
+    console.log(`  ✓ ${dwSku}  ${title}  $${price}  ${imgOk ? 'ACTIVE' : 'DRAFT(Needs-Image)'}`);
+    await sleep(700);
+  }
+  await pg.end();
+  fs.writeFileSync(__dirname + '/../data/publish-bucket3-results.json', JSON.stringify(results, null, 2));
+  const ok = results.filter(r => r.pid || (DRY && r.dwSku && !r.skip && !r.err));
+  console.log(`\nDONE: created=${ok.length} errors=${results.filter(r => r.err).length} skipped=${results.filter(r => r.skip).length}`);
+  results.filter(r => r.err).forEach(r => console.log('ERR', JSON.stringify(r)));
+}
+main().catch(e => { console.error(e); process.exit(1); });

← 4129d03 Bucket-2 publish script: reuse existing DWKK, re-point Krave  ·  back to Groundworks Activation  ·  chore: session-close artifacts + v1.0.1 977bda4 →