[object Object]

← back to Designer Wallcoverings

auto-save: 2026-07-29T21:42:29 (1 files) — scripts/tk10002-null-sample/fix-missoni-sample.js

dd4a842f705156897f2a88e2534a691beac450f5 · 2026-07-29 21:42:35 -0700 · Steve Abrams

Files touched

Diff

commit dd4a842f705156897f2a88e2534a691beac450f5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 29 21:42:35 2026 -0700

    auto-save: 2026-07-29T21:42:29 (1 files) — scripts/tk10002-null-sample/fix-missoni-sample.js
---
 scripts/tk10002-null-sample/fix-missoni-sample.js | 63 +++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/scripts/tk10002-null-sample/fix-missoni-sample.js b/scripts/tk10002-null-sample/fix-missoni-sample.js
new file mode 100644
index 00000000..857475f8
--- /dev/null
+++ b/scripts/tk10002-null-sample/fix-missoni-sample.js
@@ -0,0 +1,63 @@
+#!/usr/bin/env node
+/**
+ * TK-10002 — fix the 2 Missoni -sample collisions BATCH-1 missed (Cody caught, live-verified).
+ * apply-residual renamed only the loser's BASE variant; the loser's -sample sibling still
+ * shares MIS-183xx-sample with the keeper. Rename loser's sample to match its new base.
+ * REASSIGN only, 0 archives. In-scope of the residual-collision-cleanup artifact.
+ * DRY-RUN by default; APPLY=1 + artifact required. Includes the live-uniqueness guard.
+ */
+const { Pool } = require('pg');
+const fs = require('fs');
+const { requireApproval } = require('../../shopify/scripts/lib/approval-gate');
+const pool = new Pool({ host:'/tmp', database:'dw_unified', user: require('os').userInfo().username });
+const TOKEN = fs.readFileSync(require('path').join(process.env.HOME,'Projects/Designer-Wallcoverings/shopify/.env'),'utf8')
+  .split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=').slice(1).join('=').replace(/['"]/g,'').trim();
+const STORE='designer-laboratory-sandbox.myshopify.com', API='2024-10';
+const APPLY = process.env.APPLY === '1';
+const sleep = ms => new Promise(r=>setTimeout(r,ms));
+const TARGETS = [
+  { pid:'6751727386675', label:'Magic Garden Cream',  old:'MIS-18313-sample', neu:'MIS-18313-W2-sample' },
+  { pid:'6753081589811', label:'Striped Sunset',      old:'MIS-18398-sample', neu:'MIS-18398-W2-sample' },
+];
+async function gql(query,variables){
+  for(let a=0;a<5;a++){
+    const r=await fetch(`https://${STORE}/admin/api/${API}/graphql.json`,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query,variables})});
+    if(r.status===429){await sleep(2000);continue;}
+    const j=await r.json();
+    if((j.errors||[]).some(e=>/throttl/i.test(e.message||''))){await sleep(2000);continue;}
+    if(j.errors) throw new Error('GQL '+JSON.stringify(j.errors));
+    return j.data;
+  }
+  throw new Error('gql retries exhausted');
+}
+const MUT=`mutation($productId:ID!,$variants:[ProductVariantsBulkInput!]!){ productVariantsBulkUpdate(productId:$productId,variants:$variants){ productVariants{ id inventoryItem{ sku } } userErrors{ field message } } }`;
+async function skuFreeLive(sku){
+  const d=await gql(`{ productVariants(first:10, query:"sku:${sku}"){ nodes{ product{ status } inventoryItem{ sku } } } }`);
+  return !(d.productVariants.nodes||[]).some(n=>String(n.product.status).toUpperCase()!=='ARCHIVED' && n.inventoryItem?.sku===sku);
+}
+async function main(){
+  const gate=requireApproval({ ticket:'TK-10002', scope:'residual-collision-cleanup', apply:APPLY });
+  console.log(`MODE: ${APPLY?'APPLY (LIVE WRITE)':'DRY-RUN'}  gate=${gate.mode}\n`);
+  const results={applied:[],skipped:[],failed:[]};
+  for(const t of TARGETS){
+    const gid=`gid://shopify/Product/${t.pid}`;
+    if(!(await skuFreeLive(t.neu))){ results.skipped.push({...t,reason:'new sku not free'}); console.log('⚠',t.label,t.neu,'NOT free — SKIP'); continue; }
+    const d=await gql(`{ product(id:"${gid}"){ variants(first:20){ nodes{ id inventoryItem{ sku } } } } }`);
+    const v=(d.product.variants.nodes||[]).find(n=>n.inventoryItem?.sku===t.old);
+    if(!v){ results.skipped.push({...t,reason:'old sku absent (already fixed?)'}); console.log('=',t.label,t.old,'absent — SKIP'); continue; }
+    console.log(`${APPLY?'':'[dry] '}${t.label}: '${t.old}' -> '${t.neu}'`);
+    if(!APPLY) continue;
+    try{
+      const r=await gql(MUT,{productId:gid,variants:[{id:v.id,inventoryItem:{sku:t.neu}}]});
+      const ue=r.productVariantsBulkUpdate.userErrors;
+      if(ue.length){ results.failed.push({...t,ue}); console.log('  ✗',JSON.stringify(ue)); }
+      else { results.applied.push({...t,got:r.productVariantsBulkUpdate.productVariants[0].inventoryItem.sku}); console.log('  ✓ ->',results.applied.at(-1).got); }
+    }catch(e){ results.failed.push({...t,err:e.message}); console.log('  ✗',e.message); }
+    await sleep(300);
+  }
+  fs.writeFileSync('/tmp/tk10002_missoni_sample_results.json',JSON.stringify(results,null,2));
+  console.log(`\nDONE: applied=${results.applied.length} skipped=${results.skipped.length} failed=${results.failed.length}`);
+  if(!APPLY) console.log('(DRY-RUN)');
+  await pool.end();
+}
+main().catch(e=>{console.error(e);process.exit(1);});

← f9d5538b TK-10002: PR base-collision regression fix (dry-run tooling,  ·  back to Designer Wallcoverings  ·  TK-10002 /yolo FIX-THEN-SHIP: fix 2 Missoni -sample collisio 1376340c →