[object Object]

← back to Dw Yolo Loop

fix(gmc-feed-guard): variant-based sample-leak classifier — kills 50,991 false positive, reports real ~457

6108f5bd930a4a11f07e1f9db4c23d0fa654ce56 · 2026-09-01 07:40:42 -0700 · Steve Abrams

Files touched

Diff

commit 6108f5bd930a4a11f07e1f9db4c23d0fa654ce56
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 1 07:40:42 2026 -0700

    fix(gmc-feed-guard): variant-based sample-leak classifier — kills 50,991 false positive, reports real ~457
---
 package-lock.json                         |  4 +--
 scripts/gmc-feed-guard/data/latest.json   | 53 ++++++++++++++++---------------
 scripts/gmc-feed-guard/gmc-feed-guard.mjs | 26 +++++++++++----
 3 files changed, 48 insertions(+), 35 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index cfa6776..489edc0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "designerwallcoverings-ai",
-  "version": "0.1.1",
+  "version": "0.1.3",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "designerwallcoverings-ai",
-      "version": "0.1.1",
+      "version": "0.1.3",
       "dependencies": {
         "express": "^4.19.2",
         "multer": "^2.0.1"
diff --git a/scripts/gmc-feed-guard/data/latest.json b/scripts/gmc-feed-guard/data/latest.json
index be3fe77..360eb2d 100644
--- a/scripts/gmc-feed-guard/data/latest.json
+++ b/scripts/gmc-feed-guard/data/latest.json
@@ -1,31 +1,32 @@
 {
-  "generated_at": "2026-08-20T06:43:31.291Z",
+  "generated_at": "2026-09-01T14:40:30.740Z",
+  "verdict": "FAIL",
   "status": "FAIL",
-  "headline": "sample-leak(real-roll feeding $4.25 on Google)=48347 of 89825 active (target 0); sample-only-$4.25=27487 (legit)",
-  "scanned": 89825,
-  "sample_leaks": 48347,
-  "sample_only_425": 27487,
+  "headline": "sample-leak(non-sample variant <=$4.25 on Google)=457 of 91011 active (target 0); sample-only-$4.25=23565 (legit)",
+  "scanned": 91011,
+  "sample_leaks": 457,
+  "sample_only_425": 23565,
   "offenders": [
-    "venezia-glass-beads-bronzino-gls-114-glass-beads-bronzino [Glitter Walls] min=$4.25 high=$81.41",
-    "the-original-heavy-madagascar-cloth-raffia-wall-paper-mad-100 [Madagascar Walls] min=$4.25 high=$105.89",
-    "grasscloth-wiki-horizontal-aba-311 [Phillipe Romano] min=$4.25 high=$27.06",
-    "grasscloth-tightwiki-ab327-deep-wedgewood-blue-grs-ab327 [Phillipe Romano] min=$4.25 high=$42.5",
-    "grasscloth-tightwiki-ab320-peach-grasscloth-tightwiki-ab320-peach [Phillipe Romano] min=$4.25 high=$37.5",
-    "grasscloth-tightwiki-light-blue-grs-1825 [Phillipe Romano] min=$4.25 high=$42.5",
-    "grasscloth-tightwiki-chocolate-mocha-grs-1819 [Phillipe Romano] min=$4.25 high=$37.5",
-    "grasscloth-tightwiki-tight-caramel-grasscloth-tightwiki-ab305-light [Phillipe Romano] min=$4.25 high=$37.5",
-    "grasscloth-tightwiki-ab318-golden-yellow-grasscloth-tightwiki-ab318-gyell [Phillipe Romano] min=$4.25 high=$37.5",
-    "grasscloth-tightwiki-mixed-naturals-grs-1808 [Phillipe Romano] min=$4.25 high=$18.82",
-    "grasscloth-slubs-natural-ab311-grs-ab311 [Phillipe Romano] min=$4.25 high=$25.88",
-    "brown-tone-paper-weave-grass-2006-grass-2006 [Phillipe Romano] min=$4.25 high=$27.06",
-    "multicolor-sisal-weave-grass-2007-grass-2007 [Phillipe Romano] min=$4.25 high=$33.18",
-    "1970s-daisy-wall-paper-10xrl15b99 [Fentucci] min=$4.25 high=$58.76",
-    "black-and-white-paisley-wall-paper-paisley10xrv999 [Retro Walls] min=$4.25 high=$58.76",
-    "balihigh-gold-tan-natural-bamboo-grasscloth-wallpaper-grs-6262 [Phillipe Romano] min=$4.25 high=$16.76",
-    "medium-heavy-madagascar-cloth-wall-paper-mad-42022 [Madagascar Walls] min=$4.25 high=$105.89",
-    "red-grasscloth-tight-weave-wall-paper-wgrs-2254 [Phillipe Romano] min=$4.25 high=$16.71",
-    "light-mocha-grasscloth-wgrs-2016 [Phillipe Romano] min=$4.25 high=$11.71",
-    "reeded-bamboo-chinese-red-tones-qweb1000 [Phillipe Romano] min=$4.25 high=$23.76"
+    "vernazza-slate-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "vernazza-ochre-ii-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "vernazza-olive-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "manarola-ochre-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "manarola-fawn-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "manarola-slate-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "manarola-pewter-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "manarola-moss-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "riomaggiore-amber-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "riomaggiore-amber-ii-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "riomaggiore-espresso-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "riomaggiore-fawn-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "corniglia-amber-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "corniglia-sienna-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "corniglia-slate-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "corniglia-ochre-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "corniglia-olive-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "monterosso-fawn-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "monterosso-sienna-fentucci-naturals [Fentucci Naturals] non-sample-min=$0",
+    "monterosso-garnet-fentucci-naturals [Fentucci Naturals] non-sample-min=$0"
   ],
-  "elapsed_s": 260
+  "elapsed_s": 422
 }
\ No newline at end of file
diff --git a/scripts/gmc-feed-guard/gmc-feed-guard.mjs b/scripts/gmc-feed-guard/gmc-feed-guard.mjs
index 13be5ee..fd6d908 100644
--- a/scripts/gmc-feed-guard/gmc-feed-guard.mjs
+++ b/scripts/gmc-feed-guard/gmc-feed-guard.mjs
@@ -31,6 +31,14 @@ const DIR=process.env.HOME+'/Projects/dw-yolo-loop/scripts/gmc-feed-guard/data';
 const SKILL_DIR=process.env.HOME+'/.claude/skills/gmc-feed-guard/data'; // mirrors artifact so cron-fire-canary manifest discovers it
 const Q=process.env.HOME+'/.claude/yolo-queue';
 const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+// A SAMPLE variant = its SKU carries a -sample/-memo/-swatch suffix (authoritative signal), OR its
+// title/option says sample/memo/swatch. Samples often have a generic variant title, so the SKU suffix
+// is the reliable signal — NOT the price. (TK-11061, 2026-09-01: the old min<=4.25/max>4.25 heuristic
+// counted EVERY roll that merely HAD a $4.25 sample variant as a "leak" → 50,991 false alarms; a real
+// leak is a product on Google whose NON-sample variant is itself <= $4.25. Classify by variant, not price.
+// Ported from ~/.claude/skills/google-merchant-agent/check.mjs.)
+const isSampleVariant=v=>/-(sample|memo|swatch)\b|(^|[^a-z])(sample|memo|swatch)([^a-z]|$)/i
+  .test((v.sku||'')+' '+(v.title||'')+' '+(v.selectedOptions||[]).map(o=>o.value).join(' '));
 async function gql(q,v){
   for(let a=0;a<7;a++){
     try{ const r=await fetch(API,{method:'POST',headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v||{}}),signal:AbortSignal.timeout(20000)});
@@ -44,23 +52,27 @@ async function gql(q,v){
 }
 let cur=null,scanned=0,leaks=0,sampleOnly=0,pages=0; const offenders=[]; const t0=Date.now();
 do{
-  const d=await gql(`query($c:String){products(first:150,after:$c,query:"status:active"){pageInfo{hasNextPage endCursor} nodes{handle vendor onGoogle:publishedOnPublication(publicationId:"${GOOG}") variants(first:60){nodes{price}}}}}`,{c:cur});
+  const d=await gql(`query($c:String){products(first:150,after:$c,query:"status:active"){pageInfo{hasNextPage endCursor} nodes{handle vendor onGoogle:publishedOnPublication(publicationId:"${GOOG}") variants(first:60){nodes{price sku title selectedOptions{value}}}}}}`,{c:cur});
   const pg=d.data.products;
   for(const p of pg.nodes){ scanned++;
     const prices=p.variants.nodes.map(v=>parseFloat(v.price)).filter(x=>!isNaN(x));
     if(!prices.length) continue;
-    const mn=Math.min(...prices), mx=Math.max(...prices);
-    if(mn<=4.25 && p.onGoogle){
-      const hasReal = mx>4.25;           // has a real roll variant above the $4.25 sample
-      if(hasReal){ leaks++; if(offenders.length<20)offenders.push(`${p.handle} [${p.vendor}] min=$${mn} high=$${mx}`); }
-      else sampleOnly++;                  // sample-only $4.25 — legit under the new policy
+    if(p.onGoogle){
+      // Real leak = a product ON Google whose NON-sample (roll/main) variant is priced <= $4.25.
+      // Sample-only products (every variant is a sample) at $4.25 are LEGIT, not a leak.
+      const nonSample=p.variants.nodes.filter(v=>!isSampleVariant(v)&&!isNaN(parseFloat(v.price)));
+      if(nonSample.length===0){ sampleOnly++; }
+      else{
+        const mnNon=Math.min(...nonSample.map(v=>parseFloat(v.price)));
+        if(mnNon<=4.25){ leaks++; if(offenders.length<20)offenders.push(`${p.handle} [${p.vendor}] non-sample-min=$${mnNon}`); }
+      }
     }
   }
   pages++; if(pages%80===0)process.stderr.write(`  ...${scanned} scanned, sample-leaks=${leaks}\n`);
   cur=pg.pageInfo.hasNextPage?pg.pageInfo.endCursor:null;
 }while(cur);
 const verdict = leaks>0 ? 'FAIL' : 'PASS';
-const out={generated_at:new Date().toISOString(), verdict, status:verdict, headline:`sample-leak(real-roll feeding $4.25 on Google)=${leaks} of ${scanned} active (target 0); sample-only-$4.25=${sampleOnly} (legit)`, scanned, sample_leaks:leaks, sample_only_425:sampleOnly, offenders, elapsed_s:((Date.now()-t0)/1000)|0};
+const out={generated_at:new Date().toISOString(), verdict, status:verdict, headline:`sample-leak(non-sample variant <=$4.25 on Google)=${leaks} of ${scanned} active (target 0); sample-only-$4.25=${sampleOnly} (legit)`, scanned, sample_leaks:leaks, sample_only_425:sampleOnly, offenders, elapsed_s:((Date.now()-t0)/1000)|0};
 fs.mkdirSync(DIR,{recursive:true}); fs.writeFileSync(DIR+'/latest.json',JSON.stringify(out,null,2));
 fs.mkdirSync(SKILL_DIR,{recursive:true}); fs.writeFileSync(SKILL_DIR+'/latest.json',JSON.stringify(out,null,2)); // mirror for cron-fire-canary + fleet-health-rollup
 const stamp=new Date().toISOString().slice(0,10);

← 7e33d8b gmc-feed-guard TK-10952: mirror crash artifact to skill dir,  ·  back to Dw Yolo Loop  ·  refresh GMC leak guard heartbeat bd5e681 →