← 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
M apply.mjsA backfill-xwh-meta.mjs
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 →