[object Object]

← back to Dw Yolo Loop

Push the 59 held ROLL anomalies up to MAP (Steve-authorized 2026-06-15)

fd8f8aca0703d02db56a040a9a6a73ac7d4003a0 · 2026-06-15 10:02:10 -0700 · Steve Abrams

ROLL-unit wallcoverings currently BELOW MAP (compliance violation); raising to MAP
fixes it. Staleness-guarded (skips any whose live price drifted). Reversible.

Files touched

Diff

commit fd8f8aca0703d02db56a040a9a6a73ac7d4003a0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jun 15 10:02:10 2026 -0700

    Push the 59 held ROLL anomalies up to MAP (Steve-authorized 2026-06-15)
    
    ROLL-unit wallcoverings currently BELOW MAP (compliance violation); raising to MAP
    fixes it. Staleness-guarded (skips any whose live price drifted). Reversible.
---
 scripts/kravet-cost/push-anomalies.mjs | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/scripts/kravet-cost/push-anomalies.mjs b/scripts/kravet-cost/push-anomalies.mjs
new file mode 100644
index 0000000..9e4b57f
--- /dev/null
+++ b/scripts/kravet-cost/push-anomalies.mjs
@@ -0,0 +1,45 @@
+#!/usr/bin/env node
+/**
+ * push-anomalies.mjs — push the 59 HELD ROLL anomalies (under-priced rolls) up to MAP.
+ * Steve-authorized 2026-06-15 ("push the 59"). These are ROLL-unit wallcoverings currently
+ * BELOW MAP (a Kravet compliance violation); raising to MAP fixes it. Reversible.
+ *
+ * Safety: only touches the roll variant id already recorded; verifies the live price still
+ * equals the expected `cur` (skips if it drifted, so we never double-write or clobber a
+ * concurrently-changed price). DRY-RUN default; LIVE requires --apply --i-am-steve.
+ */
+import fs from 'node:fs';
+const SHOP='designer-laboratory-sandbox.myshopify.com',VER='2024-10';
+const TOKEN=(fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1]?.trim();
+const URL=`https://${SHOP}/admin/api/${VER}/graphql.json`;
+const args=Object.fromEntries(process.argv.slice(2).map(a=>{const[k,v]=a.replace(/^--/,'').split('=');return[k,v===undefined?true:v];}));
+const APPLY=args.apply===true&&args['i-am-steve']===true;
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+async function gql(q,v){for(let a=0;a<8;a++){let j;try{const r=await fetch(URL,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});j=await r.json();}catch(e){await sleep(1500*(a+1));continue;}if(j.errors){if(JSON.stringify(j.errors).includes('THROTTLED')){await sleep(2000*(a+1));continue;}throw new Error(JSON.stringify(j.errors));}return j.data;}throw new Error('retries');}
+
+const anomalies=JSON.parse(fs.readFileSync('data/kravet-cost/price-push-plan.json','utf8')).anomaly||[];
+console.log(`push-anomalies — mode: ${APPLY?'⚠️  LIVE APPLY':'DRY-RUN'} | held anomalies: ${anomalies.length}`);
+
+// verify live price still == cur (not stale)
+const Q=`query($ids:[ID!]!){nodes(ids:$ids){... on ProductVariant{id price}}}`;
+const fresh=[],stale=[];
+for(let i=0;i<anomalies.length;i+=100){
+  const b=anomalies.slice(i,i+100);
+  const d=await gql(Q,{ids:b.map(x=>x.vid)});
+  const live=new Map(d.nodes.filter(Boolean).map(n=>[n.id,parseFloat(n.price)]));
+  for(const x of b){const lp=live.get(x.vid);
+    if(lp!=null&&Math.abs(lp-x.cur)<0.01)fresh.push(x); else stale.push({...x,live:lp});}
+}
+console.log(`fresh (live == expected current): ${fresh.length} | drifted/skip: ${stale.length}`);
+for(const x of fresh.slice(0,12))console.log(`  ${x.mfr}: $${x.cur} -> $${x.map} (${(x.map/x.cur).toFixed(1)}x)`);
+if(stale.length)console.log('drifted (will NOT touch):',stale.map(s=>s.mfr).join(', '));
+if(!APPLY){console.log('\nDRY-RUN. To execute: node push-anomalies.mjs --apply --i-am-steve');process.exit(0);}
+
+const M=`mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$pid,variants:$variants){userErrors{field message}}}`;
+let ok=0,err=0;
+for(const x of fresh){
+  try{const d=await gql(M,{pid:x.pid,variants:[{id:x.vid,price:x.map.toFixed(2)}]});
+    const ue=d.productVariantsBulkUpdate?.userErrors||[];if(ue.length){err++;console.log('  err',x.mfr,JSON.stringify(ue));}else ok++;
+  }catch(e){err++;console.log('  EX',x.mfr,e.message);}
+}
+console.log(`\nDONE — ${ok} rolls raised to MAP, ${err} errors, ${stale.length} skipped (drifted).`);

← c50964f brands page: 3 audit fixes — promote title to <h1> (SEO), in  ·  back to Dw Yolo Loop  ·  brands: drop Zeuxis Parrhasius floral room-bg (settlement fo 2927219 →