[object Object]

← back to Dw Yolo Loop

harden add_roll_variants: price floor (>=$10, block $4.25 sample-trap) + require -Sample SKU (skip+log fallback); 0 of approved 305 dropped, guards arm against input drift

8ff645a7517704f0eceaf0e8e22f088252f4605b · 2026-06-16 15:33:00 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit 8ff645a7517704f0eceaf0e8e22f088252f4605b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jun 16 15:33:00 2026 -0700

    harden add_roll_variants: price floor (>=$10, block $4.25 sample-trap) + require -Sample SKU (skip+log fallback); 0 of approved 305 dropped, guards arm against input drift
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 scripts/kravet-master-2026/add_roll_variants.mjs | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/scripts/kravet-master-2026/add_roll_variants.mjs b/scripts/kravet-master-2026/add_roll_variants.mjs
index 985a516..40bd98b 100644
--- a/scripts/kravet-master-2026/add_roll_variants.mjs
+++ b/scripts/kravet-master-2026/add_roll_variants.mjs
@@ -25,9 +25,13 @@ for(const line of fs.readFileSync('/tmp/kravet_305_source.txt','utf8').trim().sp
   const c=line.split('|'); if(c.length>=4) nm[c[0]]=parseFloat(c[3]);
 }
 // the 305 sample-only gids
-const targets=fs.readFileSync('/tmp/kravet_roll_dryrun.csv','utf8').trim().split('\n')
-  .filter(l=>l.includes('SAMPLE_ONLY_NEEDS_PRICE')).map(l=>{const c=l.split(','); return {gid:c[0],vendor:c[1],sku:c[2],new_map:nm[c[0]]};})
-  .filter(t=>t.new_map>0);
+// PRICE FLOOR: a ROLL MAP below this is almost certainly a sample/per-yard/garbage
+// value (the $4.25 sample-as-price trap). Reject + log rather than write it live.
+const MIN_ROLL_PRICE=10;
+const rawTargets=fs.readFileSync('/tmp/kravet_roll_dryrun.csv','utf8').trim().split('\n')
+  .filter(l=>l.includes('SAMPLE_ONLY_NEEDS_PRICE')).map(l=>{const c=l.split(','); return {gid:c[0],vendor:c[1],sku:c[2],new_map:nm[c[0]]};});
+const priceDropped=rawTargets.filter(t=>!(t.new_map>=MIN_ROLL_PRICE) || t.new_map===4.25);
+const targets=rawTargets.filter(t=>t.new_map>=MIN_ROLL_PRICE && t.new_map!==4.25);
 
 async function gql(query,variables){
   for(let a=0;a<4;a++){
@@ -48,7 +52,8 @@ const CREATE=`mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){
     userErrors{ field message } } }`;
 
 let added=0,skipped=0,failed=0,n=0;
-log(`\n=== add_roll_variants ${APPLY?'APPLY':'DRY-RUN'} limit=${LIMIT} targets=${targets.length} @ ${new Date().toISOString?.()||'now'} ===`);
+log(`\n=== add_roll_variants ${APPLY?'APPLY':'DRY-RUN'} limit=${LIMIT} targets=${targets.length} (price-floor dropped ${priceDropped.length}) @ ${new Date().toISOString?.()||'now'} ===`);
+for(const d of priceDropped) log(`DROP below-floor ${d.vendor} ${d.sku} new_map=${d.new_map}`);
 for(const t of targets){
   if(n>=LIMIT) break; n++;
   let p;
@@ -56,7 +61,11 @@ for(const t of targets){
   if(!p){ failed++; log(`FAIL no-product ${t.gid}`); continue; }
   const vs=p.variants.edges.map(e=>e.node);
   if(vs.some(v=>/Sold Per Roll/i.test(v.title))){ skipped++; log(`SKIP has-roll ${p.title}`); continue; }
-  const sample=vs.find(v=>/-sample$/i.test(v.sku||'')) || vs[0];
+  // require a real "-Sample" variant: the roll SKU is derived from it. With no
+  // -Sample match the old `|| vs[0]` fallback minted a roll variant sharing the
+  // sample's SKU — skip + log instead so it's surfaced, not silently mislabeled.
+  const sample=vs.find(v=>/-sample$/i.test(v.sku||''));
+  if(!sample){ skipped++; log(`SKIP no-sample-sku ${p.title} (skus: ${vs.map(v=>v.sku||'∅').join(',')})`); continue; }
   const rollSku=(sample.sku||'').replace(/-Sample$/i,'');
   const wm=(p.width?.value||'').match(/[\d.]+/);   // leading numeric part only ("20 in (50.8 cm)" -> "20")
   const w=wm?wm[0]:'';

← 4aeca4e collection merge+301 map (c42): 28 empties->twin, 15 no-twin  ·  back to Dw Yolo Loop  ·  Showroom Lines: scan + bulk tag/metafield scripts for single ae2c5c8 →