[object Object]

← back to Tk10630 Sku Suffix Canary

TK-10630: XWH metafield backfill (25) + base-prefix filter; XWH line 116/116 verified clean

722d3ce28da671ee4472394fd3353457843a42a1 · 2026-08-17 12:01:30 -0700 · steve

Files touched

Diff

commit 722d3ce28da671ee4472394fd3353457843a42a1
Author: steve <steve@designerwallcoverings.com>
Date:   Mon Aug 17 12:01:30 2026 -0700

    TK-10630: XWH metafield backfill (25) + base-prefix filter; XWH line 116/116 verified clean
---
 apply.mjs             |  2 ++
 backfill-xwh-meta.mjs | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/apply.mjs b/apply.mjs
index ab44e18..f3dfc06 100644
--- a/apply.mjs
+++ b/apply.mjs
@@ -13,6 +13,7 @@ const APPLY = process.argv.includes('--apply');
 const LIMIT = Number((process.argv.find(a => a.startsWith('--limit=')) || '').split('=')[1] || Infinity);
 const CONC = Number((process.argv.find(a => a.startsWith('--conc=')) || '').split('=')[1] || 5);
 const ONLY = (process.argv.find(a => a.startsWith('--only=')) || '').split('=')[1] || null;
+const BASEPFX = (process.argv.find(a => a.startsWith('--basePrefix=')) || '').split('=')[1] || null;
 const MF_ONLY = process.argv.includes('--mf-only');   // write metafields only (needs write_products)
 const VAR_ONLY = process.argv.includes('--var-only'); // write variant SKUs only (needs write_inventory)
 const PHASE = MF_ONLY ? 'mf' : VAR_ONLY ? 'var' : 'all';
@@ -34,6 +35,7 @@ if (existsSync(DONE)) for (const l of readFileSync(DONE, 'utf8').split('\n')) {
 
 let work = plan.filter(p => !done.has(p.id) && !FABRICATED.test(p.base) && !collisions.find(([b]) => b === p.base));
 if (ONLY) work = work.filter(p => p.handle.includes(ONLY));
+if (BASEPFX) work = work.filter(p => p.base.startsWith(BASEPFX));
 if (work.length > LIMIT) work = work.slice(0, LIMIT);
 
 console.log(`[apply] mode=${APPLY ? 'LIVE-WRITE' : 'DRY-RUN'} plan=${plan.length} todo=${work.length} done=${done.size} conc=${CONC}`);
diff --git a/backfill-xwh-meta.mjs b/backfill-xwh-meta.mjs
new file mode 100644
index 0000000..44ee5d5
--- /dev/null
+++ b/backfill-xwh-meta.mjs
@@ -0,0 +1,40 @@
+// Backfill dw_sku metafields = real XWH-<n> mfr code for EVERY live XWH product
+// (both vendor labels), wherever the metafield is missing or wrong. Metafield-only
+// (write_products, default token). READ-then-WRITE, idempotent.
+import { gql } from './shopify.mjs';
+const APPLY = process.argv.includes('--apply');
+
+const products = new Map();
+let cursor = null;
+while (true) {
+  const q = `query($c:String){ productVariants(first:200, query:"sku:XWH-*", after:$c){
+    pageInfo{ hasNextPage endCursor }
+    nodes{ sku product{ id handle status
+      g:metafield(namespace:"global",key:"dw_sku"){value}
+      d:metafield(namespace:"dwc",key:"dw_sku"){value}
+      variants(first:10){ nodes{ sku } } } } } }`;
+  const { data } = await gql(q, { c: cursor });
+  for (const n of data.productVariants.nodes) products.set(n.product.id, n.product);
+  if (!data.productVariants.pageInfo.hasNextPage) break;
+  cursor = data.productVariants.pageInfo.endCursor;
+}
+
+const MFS = `mutation($m:[MetafieldsSetInput!]!){ metafieldsSet(metafields:$m){ metafields{id} userErrors{field message} } }`;
+let need = 0, done = 0, err = 0;
+for (const p of products.values()) {
+  let base = null;
+  for (const v of p.variants.nodes) { const m = (v.sku || '').match(/^(XWH-\d+)/); if (m) { base = m[1]; break; } }
+  if (!base) continue;
+  const targets = [];
+  if (p.g?.value !== base) targets.push({ ownerId: p.id, namespace: 'global', key: 'dw_sku', type: 'single_line_text_field', value: base });
+  if (p.d?.value !== base) targets.push({ ownerId: p.id, namespace: 'dwc', key: 'dw_sku', type: 'single_line_text_field', value: base });
+  if (!targets.length) continue;
+  need++;
+  if (APPLY) {
+    try { const { data } = await gql(MFS, { m: targets }); const ue = data.metafieldsSet.userErrors; if (ue.length) { err++; console.log('✗', p.handle, JSON.stringify(ue)); } else done++; }
+    catch (e) { err++; console.log('✗', p.handle, e.message.slice(0, 100)); }
+  } else {
+    console.log(`would set ${p.handle} -> ${base} (${targets.length} mf)`);
+  }
+}
+console.log(`[backfill-xwh-meta] scanned=${products.size} need=${need} ${APPLY ? `done=${done} err=${err}` : '(dry-run)'}`);

← 6072853 TK-10630: Hollywood SKU consolidation tool (enumerate/apply/  ·  back to Tk10630 Sku Suffix Canary  ·  TK-10630: custom.dw_sku recovered-cohort fix complete; 507 r 9de4585 →