[object Object]

← back to Dw Yolo Loop

cycle 78: harden zero-dollar canary per officer REVISE — (1) inventoryQuantity in predicate (DENY+stock>0 false-clean hole), (2) variants first:100 + truncation observable, (3) coverage gate decoupled from RL to completeness-floor (survives the reprice), (4) token-guard; re-verified 145 active + 8 draft, gate PASS, 0 trunc

43256c6159d69445a964c8917c2a960ee22e2115 · 2026-06-18 01:06:14 -0700 · Steve Abrams

Files touched

Diff

commit 43256c6159d69445a964c8917c2a960ee22e2115
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jun 18 01:06:14 2026 -0700

    cycle 78: harden zero-dollar canary per officer REVISE — (1) inventoryQuantity in predicate (DENY+stock>0 false-clean hole), (2) variants first:100 + truncation observable, (3) coverage gate decoupled from RL to completeness-floor (survives the reprice), (4) token-guard; re-verified 145 active + 8 draft, gate PASS, 0 trunc
---
 .../zero-dollar-orderable-canary.mjs               | 40 ++++++++++++++--------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/scripts/zero-dollar-orderable-canary/zero-dollar-orderable-canary.mjs b/scripts/zero-dollar-orderable-canary/zero-dollar-orderable-canary.mjs
index 595b72c..8999583 100644
--- a/scripts/zero-dollar-orderable-canary/zero-dollar-orderable-canary.mjs
+++ b/scripts/zero-dollar-orderable-canary/zero-dollar-orderable-canary.mjs
@@ -19,43 +19,53 @@
 // EXCLUDES Phillip Jeffries. Read-only Admin GraphQL; never a mutation.
 import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url';
 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 TOK=((ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m)||[])[1]||'').replace(/['"\r]/g,'').trim(); // c78-officer: guard missing token
 const GQL='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
 const PJ=/phillip[- ]?jeffries/i;
+const MIN_ACTIVE_SCAN=Number(process.env.MIN_ACTIVE_SCAN||50000); // completeness floor (real active ~71.7k); survives the RL reprice (decoupled from $0 content)
 const BASE_DIR=path.dirname(fileURLToPath(import.meta.url)); const DATA_DIR=path.join(BASE_DIR,'data');
 const sleep=ms=>new Promise(r=>setTimeout(r,ms));
 async function gql(q,v){for(let a=0;a<6;a++){try{const r=await fetch(GQL,{method:'POST',headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});if(r.status===429||r.status>=500){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;}catch(e){await sleep(1500*(a+1));}}return null;}
 
-const Q=`query($cursor:String,$q:String!){ products(first:100, after:$cursor, query:$q){ pageInfo{hasNextPage endCursor} edges{node{handle vendor status variants(first:30){edges{node{price position inventoryPolicy availableForSale}}}}} } }`;
+// c78-officer fix: fetch inventoryQuantity (DENY+stock>0 IS orderable — was a false-clean hole);
+// variants(first:100) + pageInfo so a >100-variant product's truncation is OBSERVABLE, not silent.
+const Q=`query($cursor:String,$q:String!){ products(first:100, after:$cursor, query:$q){ pageInfo{hasNextPage endCursor} edges{node{handle vendor status variants(first:100){pageInfo{hasNextPage} edges{node{price position inventoryPolicy availableForSale inventoryQuantity}}}}} } }`;
+// orderable iff price 0 AND can actually transact: oversell allowed OR has stock OR storefront says available
+const isOrderableZero=v=> parseFloat(v.price)===0 && (v.inventoryPolicy==='CONTINUE' || (Number.isFinite(v.inventoryQuantity)&&v.inventoryQuantity>0) || v.availableForSale===true);
 
 async function scanStatus(statusQ){
-  let cursor=null, pages=0, scanned=0, complete=true; const hits=[];
+  let cursor=null, pages=0, scanned=0, complete=true, truncated=0; const hits=[];
   while(true){
     const j=await gql(Q,{cursor,q:statusQ});
     if(!j||!j.data){ complete=false; break; }
     const c=j.data.products;
     for(const e of c.edges){ const n=e.node; if(PJ.test(n.vendor||'')) continue; scanned++;
       const vs=(n.variants?.edges||[]).map(x=>x.node);
-      const z=vs.filter(v=>parseFloat(v.price)===0 && (v.availableForSale===true || v.inventoryPolicy==='CONTINUE'));
+      if(n.variants?.pageInfo?.hasNextPage) truncated++; // >100 variants: a $0 past 100 is unverified
+      const z=vs.filter(isOrderableZero);
       if(z.length){ hits.push({h:n.handle,vendor:n.vendor,status:n.status,n:z.length,pos:z.map(v=>v.position)}); }
     }
     pages++; if(!c.pageInfo.hasNextPage) break; cursor=c.pageInfo.endCursor;
     const cost=j.extensions?.cost?.throttleStatus; if(cost&&cost.currentlyAvailable<800) await sleep(1500); else await sleep(220);
   }
-  return {scanned, pages, complete, hits};
+  return {scanned, pages, complete, truncated, hits};
 }
 
 console.log(`=== zero-dollar-orderable-canary (READ-ONLY Admin GraphQL, $0) ===`);
+if(!TOK){ console.log('🔴 INCONCLUSIVE: SHOPIFY_ADMIN_TOKEN missing — cannot scan'); fs.mkdirSync(DATA_DIR,{recursive:true}); fs.writeFileSync(path.join(DATA_DIR,'latest.json'),JSON.stringify({ts:new Date().toISOString(),verdict:'INCONCLUSIVE',reason:'no token'},null,2)); process.exitCode=2; process.exit(2); }
 const act=await scanStatus('status:active');
 const dft=await scanStatus('status:draft');
 const complete = act.complete && dft.complete;
-console.log(`active: scanned ${act.scanned} (${act.pages}p, complete=${act.complete}) | orderable-$0 hits ${act.hits.length}`);
-console.log(`draft:  scanned ${dft.scanned} (${dft.pages}p, complete=${dft.complete}) | orderable-$0 hits ${dft.hits.length}`);
+console.log(`active: scanned ${act.scanned} (${act.pages}p, complete=${act.complete}, >100-var trunc=${act.truncated}) | orderable-$0 hits ${act.hits.length}`);
+console.log(`draft:  scanned ${dft.scanned} (${dft.pages}p, complete=${dft.complete}, >100-var trunc=${dft.truncated}) | orderable-$0 hits ${dft.hits.length}`);
 
-// coverage gate (negative control): the known RL $0 set must be reproduced
-const rlActive=act.hits.filter(h=>/ralph/i.test(h.vendor||'')).length;
-const coverageOk = rlActive>=100;
-console.log(`\nCOVERAGE GATE: active Ralph Lauren orderable-$0 = ${rlActive} (c68/c69 = 111) → ${coverageOk?'PASS':'FAIL (scan may be blind)'}`);
+// COVERAGE GATE decoupled from the remediated content (c78-officer): a scan-completeness FLOOR,
+// not an RL fingerprint (RL=111 would brick to INCONCLUSIVE the moment Steve fixes the RL set).
+// Trust the result iff the crawl completed AND the active scan saw a plausible full catalog.
+const coverageOk = complete && act.scanned>=MIN_ACTIVE_SCAN;
+const rlActive=act.hits.filter(h=>/ralph/i.test(h.vendor||'')).length; // informational context only, NOT a gate
+console.log(`\nCOVERAGE GATE: complete=${complete} + activeScanned ${act.scanned} >= floor ${MIN_ACTIVE_SCAN} → ${coverageOk?'PASS':'FAIL (scan incomplete/blind)'}  [RL $0 = ${rlActive}, info only]`);
+if(act.truncated+dft.truncated>0) console.log(`  ⚠️ ${act.truncated+dft.truncated} products have >100 variants → a $0 past position 100 is UNVERIFIED (observable, not silent)`);
 
 // vendor/status breakdown
 function vb(hits){ const m={}; hits.forEach(h=>{const k=`${h.vendor}|${h.status}`; m[k]=(m[k]||0)+1;}); return Object.entries(m).sort((a,b)=>b[1]-a[1]).slice(0,12); }
@@ -63,14 +73,14 @@ console.log(`\nactive orderable-$0 by vendor:`); vb(act.hits).forEach(([k,n])=>c
 if(dft.hits.length){ console.log(`DRAFT orderable-$0 (latent activation landmine) by vendor:`); vb(dft.hits).forEach(([k,n])=>console.log(`  ${k}: ${n}`)); }
 
 const alerts=[];
-if(!complete) alerts.push(`INCONCLUSIVE: scan did not complete (active ${act.complete}, draft ${dft.complete}) — a "0 found" cannot be trusted`);
+if(!coverageOk) alerts.push(`INCONCLUSIVE: scan incomplete/blind (complete=${complete}, activeScanned=${act.scanned} vs floor ${MIN_ACTIVE_SCAN}) — a "0 found" cannot be trusted`);
 else {
-  if(!coverageOk) alerts.push(`COVERAGE FAIL: RL orderable-$0=${rlActive} (<100) — scan likely blind, do not trust`);
   if(act.hits.length>0) alerts.push(`LIVE FREE-CHECKOUT: ${act.hits.length} ACTIVE products with an orderable zero-dollar variant (P1 — gated reprice pending)`);
-  if(dft.hits.length>0) alerts.push(`LATENT: ${dft.hits.length} DRAFT products with an orderable zero-dollar variant (would go live-free on activation)`);
+  if(dft.hits.length>0) alerts.push(`LATENT: ${dft.hits.length} DRAFT products with an orderable zero-dollar variant (the $0 is already in the draft; activation EXPOSES it to checkout)`);
+  if(act.truncated+dft.truncated>0) alerts.push(`UNVERIFIED: ${act.truncated+dft.truncated} products >100 variants — $0 past position 100 not checked`);
 }
 let verdict;
-if(!complete || (complete && !coverageOk)) verdict='INCONCLUSIVE';
+if(!coverageOk) verdict='INCONCLUSIVE';
 else if(act.hits.length>0) verdict='ALERT';
 else verdict='HEALTHY';
 console.log(`\n=== VERDICT: ${verdict} ===`);

← fefd684 cycle 78: BUILD zero-dollar-orderable canary (P1 free-checko  ·  back to Dw Yolo Loop  ·  cycle 78: officer REVISE sign-off appended (3 false-clean bl 4fdf1b1 →