← 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
M scripts/zero-dollar-orderable-canary/zero-dollar-orderable-canary.mjs
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 →