← 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
M scripts/kravet-master-2026/add_roll_variants.mjs
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 →