[object Object]

← back to Dw Yolo Loop

cycle 69: multi-variant $0 full-catalog GraphQL sweep — default-$0=0, but 33 select-to-free-ROLL products (P2) + orphan straggler (single $0 111->112)

6526738b341d88ab7bb167c67ed18c7c124145a0 · 2026-06-17 18:56:30 -0700 · Steve Abrams

Files touched

Diff

commit 6526738b341d88ab7bb167c67ed18c7c124145a0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jun 17 18:56:30 2026 -0700

    cycle 69: multi-variant $0 full-catalog GraphQL sweep — default-$0=0, but 33 select-to-free-ROLL products (P2) + orphan straggler (single $0 111->112)
---
 .../multivariant-zero-sweep.mjs                    | 91 ++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/scripts/multivariant-zero-sweep/multivariant-zero-sweep.mjs b/scripts/multivariant-zero-sweep/multivariant-zero-sweep.mjs
new file mode 100644
index 0000000..134fec3
--- /dev/null
+++ b/scripts/multivariant-zero-sweep/multivariant-zero-sweep.mjs
@@ -0,0 +1,91 @@
+// multivariant-zero-sweep — READ-ONLY, $0. Cycle 69 (DTD-picked A, unanimous).
+// c67/c68 bounded the SINGLE-variant $0-"Sample" free-checkout bug to 111 RL. The officer
+// named the uncovered population: MULTI-variant products whose DEFAULT/position-1 variant
+// is $0 — would render $0 in cart-add defaults + per-variant feed offers but never matched
+// the single-variant signature. Swept via READ-ONLY Shopify Admin products GraphQL
+// (cursor pagination = full active catalog, NO page-100 cap → also closes c68's 6% orphan
+// gap). For every ACTIVE product reads each variant's price/position/inventoryPolicy.
+// Classify:
+//   DEFAULT-ZERO   — position-1 variant price == 0 (renders $0 by default = real exposure)
+//   SINGLE-ZERO    — single variant price == 0 (the c67/c68 RL set — COVERAGE GATE positive)
+//   NONDEFAULT-ZERO— some variant $0 but position-1 priced (lower: only if shopper selects it)
+//   healthy        — otherwise
+// COVERAGE GATE (negative control): SINGLE-ZERO must reproduce ~111 Ralph Lauren; if not,
+// coverage incomplete → totals are lower bounds.
+// ONLY a read query is ever sent — never a mutation. EXCLUDES Phillip Jeffries.
+import fs from 'fs';
+const ENV=fs.readFileSync('/Users/stevestudio2/Projects/secrets-manager/.env','utf8');
+const TOK=(ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1]?.replace(/['"\r]/g,'').trim();
+const GQL='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
+const PJ=/phillip[- ]?jeffries/i;
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+if(!TOK){ console.error('no token'); process.exit(1); }
+
+async function gql(query,variables){
+  for(let a=0;a<6;a++){
+    const r=await fetch(GQL,{method:'POST',headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json'},body:JSON.stringify({query,variables})});
+    if(r.status===429){ await sleep(2000*(a+1)); continue; }
+    const j=await r.json();
+    if(j.errors && JSON.stringify(j.errors).includes('Throttled')){ await sleep(2500*(a+1)); continue; }
+    return j;
+  }
+  return null;
+}
+
+const Q=`query($cursor:String){
+  products(first:100, after:$cursor, query:"status:active"){
+    pageInfo{ hasNextPage endCursor }
+    edges{ node{ handle vendor
+      variants(first:15){ edges{ node{ price position inventoryPolicy } } } } }
+  }
+}`;
+
+console.log('=== multivariant-zero-sweep (READ-ONLY Admin GraphQL, $0) ===\nscanning ACTIVE products, cursor-paginated (no page-100 cap)\n');
+let cursor=null, scanned=0, pages=0, pjSkip=0;
+let defaultZero=0, singleZero=0, nondefaultZero=0, healthy=0;
+const vendDefault={}, vendSingle={}, defaultExamples=[], singleExamples=[];
+while(true){
+  const j=await gql(Q,{cursor});
+  if(!j||!j.data){ console.log('query failed at page',pages, JSON.stringify(j?.errors||'').slice(0,200)); break; }
+  const conn=j.data.products;
+  for(const e of conn.edges){
+    const n=e.node;
+    if(PJ.test(n.vendor||'')){ pjSkip++; continue; }
+    scanned++;
+    const vs=(n.variants?.edges||[]).map(x=>({price:parseFloat(x.node.price),pos:x.node.position,ip:x.node.inventoryPolicy})).filter(v=>!isNaN(v.price));
+    if(vs.length===0){ healthy++; continue; }
+    const p1=vs.find(v=>v.pos===1)||vs.slice().sort((a,b)=>a.pos-b.pos)[0];
+    const anyZero=vs.some(v=>v.price===0);
+    if(vs.length===1){
+      if(vs[0].price===0){ singleZero++; vendSingle[n.vendor]=(vendSingle[n.vendor]||0)+1; if(singleExamples.length<5) singleExamples.push({h:n.handle,vendor:n.vendor}); }
+      else healthy++;
+    } else { // multi-variant
+      if(p1 && p1.price===0){ defaultZero++; vendDefault[n.vendor]=(vendDefault[n.vendor]||0)+1; if(defaultExamples.length<10) defaultExamples.push({h:n.handle,vendor:n.vendor,ip:p1.ip,nVar:vs.length}); }
+      else if(anyZero){ nondefaultZero++; }
+      else healthy++;
+    }
+  }
+  pages++; scanned;
+  const cost=j.extensions?.cost?.throttleStatus;
+  if(pages%20===0) console.log(`  page ${pages} scanned=${scanned} default-zero=${defaultZero} single-zero=${singleZero} nondefault-zero=${nondefaultZero} (bucket ${cost?Math.round(cost.currentlyAvailable):'?'})`);
+  if(!conn.pageInfo.hasNextPage) break;
+  cursor=conn.pageInfo.endCursor;
+  // throttle: if bucket low, pause for restore
+  if(cost && cost.currentlyAvailable<800) await sleep(2000); else await sleep(250);
+}
+
+console.log(`\n=== RESULTS (ACTIVE products scanned: ${scanned}; pages: ${pages}; PJ-skip: ${pjSkip}) ===`);
+console.log(`  *** DEFAULT-ZERO (multi-variant, position-1 = $0 → renders $0 by default): ${defaultZero} ***`);
+console.log(`  SINGLE-ZERO (single $0 variant — c67/c68 set, COVERAGE GATE): ${singleZero}`);
+console.log(`  NONDEFAULT-ZERO (multi, $0 only on a non-default variant): ${nondefaultZero}`);
+console.log(`  healthy: ${healthy}`);
+console.log(`\n  vendors with DEFAULT-ZERO (multi-variant $0 default):`);
+Object.entries(vendDefault).sort((a,b)=>b[1]-a[1]).slice(0,15).forEach(([v,n])=>console.log(`    ${v}: ${n}`));
+if(defaultExamples.length){ console.log(`  default-zero examples:`); defaultExamples.forEach(x=>console.log(`    ${x.h.slice(0,44)} [${x.vendor}] variants=${x.nVar} pos1.invPolicy=${x.ip}`)); }
+const rlSingle=Object.entries(vendSingle).filter(([v])=>/ralph/i.test(v)).reduce((s,[,n])=>s+n,0);
+console.log(`\n-- COVERAGE GATE (negative control) --`);
+console.log(`  single-zero Ralph Lauren reproduced: ${rlSingle} (c68 found 111) → ${rlSingle>=100?'COVERAGE OK':rlSingle>0?'PARTIAL (lower bound)':'FAILED'}`);
+console.log(`  single-zero by vendor:`); Object.entries(vendSingle).sort((a,b)=>b[1]-a[1]).forEach(([v,n])=>console.log(`    ${v}: ${n}`));
+
+fs.writeFileSync('/tmp/multivariant-zero-sweep.json',JSON.stringify({ts:new Date().toISOString(),scanned,pages,defaultZero,singleZero,nondefaultZero,healthy,vendDefault,vendSingle,defaultExamples,singleExamples,rlSingle},null,2));
+console.log('\nwrote /tmp/multivariant-zero-sweep.json');

← 816dc6e cycle 68: officer CONFIRMED + scope sharpened (single-varian  ·  back to Dw Yolo Loop  ·  cycle 69: officer CONFIRMED + position-model re-verified (0/ 5fadfac →