← back to Dw Yolo Loop

scripts/inverse-mislabel-disambig/showroom-negctrl.mjs

38 lines

// showroom-negctrl — READ-ONLY, $0. Cycle 85 NEGATIVE CONTROL for inverse-mislabel-disambig.
// The disambig signal = "untagged-Showroom iff in showroom collection OR has custom.showroom_line".
// That signal is only meaningful if the DELIBERATE Showroom-Lines set (tag ~ /showroom/) actually
// CARRIES those two markers. If tagged-showroom products are themselves tag-only (no collection,
// no metafield), then 0/238 having them proves nothing. This samples tagged-showroom products and
// measures collection-membership + metafield coverage = the control that validates the disambig signal.
import fs from 'fs';
const ENV=fs.readFileSync('/Users/macstudio3/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 sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v){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: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;}return null;}
// query tagged-showroom products directly via Shopify search; sample first ~600
const Q=`query($cursor:String){ products(first:60, after:$cursor, query:"status:active tag:showroom*"){ pageInfo{hasNextPage endCursor}
  edges{node{ vendor tags
    metafield(namespace:"custom", key:"showroom_line"){ value }
    collections(first:30){ edges{ node{ handle } } } } } } }`;
let cursor=null,pages=0,n=0,inColl=0,hasMeta=0,either=0,both=0,neither=0;
const SHOWROOM_COLL=/showroom/i; const CAP=Number(process.env.CAP||600);
while(n<CAP){ const j=await gql(Q,{cursor}); if(!j?.data)break; const c=j.data.products;
  for(const e of c.edges){ const p=e.node; n++;
    const ic=(p.collections?.edges||[]).some(x=>SHOWROOM_COLL.test(x.node.handle||''));
    const hm=!!(p.metafield&&p.metafield.value);
    if(ic)inColl++; if(hm)hasMeta++; if(ic||hm)either++; if(ic&&hm)both++; if(!ic&&!hm)neither++;
  }
  pages++; if(!c.pageInfo.hasNextPage)break; cursor=c.pageInfo.endCursor; await sleep(400);
}
console.log(`=== showroom-negctrl (READ-ONLY, $0) — deliberate Showroom-Lines marker coverage ===`);
console.log(`tagged-showroom active products sampled: ${n} (${pages}p)`);
console.log(`  in a showroom COLLECTION: ${inColl} (${(100*inColl/n).toFixed(1)}%)`);
console.log(`  has custom.showroom_line METAFIELD: ${hasMeta} (${(100*hasMeta/n).toFixed(1)}%)`);
console.log(`  collection OR metafield (the disambig signal): ${either} (${(100*either/n).toFixed(1)}%)`);
console.log(`  collection AND metafield: ${both} (${(100*both/n).toFixed(1)}%)`);
console.log(`  NEITHER (tag-only, would break the signal): ${neither} (${(100*neither/n).toFixed(1)}%)`);
console.log(`\nINTERPRETATION: if 'collection OR metafield' coverage is HIGH, then 0/238 carrying it is a`);
console.log(`strong genuine-mislabel signal (the 238 lack what real showroom products have). If LOW,`);
console.log(`the signal is weak and the 238 can't be dispositioned on collection/metafield alone.`);