[object Object]

← back to Dw Yolo Loop

collection-leak sweep (c40): 3 confirmed collection-level leaks + Brewster false-positive + stale-count catch

c97eb92bf507352acf597c4488f70d0d8a180a2b · 2026-06-16 14:44:58 -0700 · Steve Abrams

Read-only live sweep of 578 collections vs private-label denylist. 4 raw hits;
after live verification: CONFIRMED leaks = wallquest-wallcoverings (0 live),
nicolette-mayer + nicolette-mayer-designs (0 live but public, must-stay-off names).
FALSE POSITIVE = brewster-york (1,010 products, but vendor=Daisy Bennett, a legit
brand under the Brewster&York distributor; 'brewster' denylist term too broad).
Methodology catch: collections.json products_count is STALE (NM showed 214, live=0)
so c39's orphan scan undercounted empties — empty detection must use live
products.json. NM products correctly 0-live (no reactivation regression); leak is
collection-name residue. Fixes gated.

Files touched

Diff

commit c97eb92bf507352acf597c4488f70d0d8a180a2b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jun 16 14:44:58 2026 -0700

    collection-leak sweep (c40): 3 confirmed collection-level leaks + Brewster false-positive + stale-count catch
    
    Read-only live sweep of 578 collections vs private-label denylist. 4 raw hits;
    after live verification: CONFIRMED leaks = wallquest-wallcoverings (0 live),
    nicolette-mayer + nicolette-mayer-designs (0 live but public, must-stay-off names).
    FALSE POSITIVE = brewster-york (1,010 products, but vendor=Daisy Bennett, a legit
    brand under the Brewster&York distributor; 'brewster' denylist term too broad).
    Methodology catch: collections.json products_count is STALE (NM showed 214, live=0)
    so c39's orphan scan undercounted empties — empty detection must use live
    products.json. NM products correctly 0-live (no reactivation regression); leak is
    collection-name residue. Fixes gated.
---
 .../collection-leak-sweep.mjs                      | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/scripts/collection-leak-sweep/collection-leak-sweep.mjs b/scripts/collection-leak-sweep/collection-leak-sweep.mjs
new file mode 100644
index 0000000..afd4d33
--- /dev/null
+++ b/scripts/collection-leak-sweep/collection-leak-sweep.mjs
@@ -0,0 +1,35 @@
+// collection-leak-sweep (c40) — READ-ONLY. The existing leak scanners check
+// PRODUCT pages; c39 proved a live collection (wallquest-wallcoverings) leaks an
+// upstream private-label name there too. This sweeps ALL live collection handles
+// + titles against the private-label denylist — a collection-level surface no
+// canary covers. Live read of /collections.json. $0.
+import fs from 'node:fs';
+const BASE='https://www.designerwallcoverings.com';
+const OUT=`${process.env.HOME}/.claude/yolo-queue/collection-leak-sweep-2026-06-16.json`;
+const MD =`${process.env.HOME}/.claude/yolo-queue/collection-leak-sweep-2026-06-16.md`;
+// private-label / upstream names that must NEVER be customer-facing (word-ish match)
+const DENY = [
+  ['wallquest','→ Malibu'],['chesapeake','→ Malibu'],['nextwall','→ Malibu'],['seabrook','→ Malibu'],
+  ['brewster','→ Malibu (note: Brewster&York src)'],['command54','→ Phillipe Romano'],['command 54','→ Phillipe Romano'],
+  ['nicolette mayer','hard-archived, stay off'],['nicolette-mayer','hard-archived'],
+  ['desima','→ archived'],['carlsten','→ archived'],
+];
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+async function page(p){ for(let a=0;a<4;a++){ try{ const r=await fetch(`${BASE}/collections.json?limit=250&page=${p}`,{signal:AbortSignal.timeout(15000)}); if(r.status===200) return (await r.json()).collections||[]; if(r.status===429){await sleep(2000*(a+1));continue;} return null;}catch(e){await sleep(1500*(a+1));} } return null; }
+const all=[]; for(let p=1;p<=20;p++){ const c=await page(p); if(c===null)break; if(!c.length)break; all.push(...c); await sleep(200); }
+const hits=[];
+for(const c of all){
+  const hay=`${c.handle||''} ${c.title||''}`.toLowerCase();
+  for(const [term,why] of DENY){ if(hay.includes(term)){ hits.push({handle:c.handle, title:c.title, products_count:c.products_count, matched:term, why}); break; } }
+}
+const report={generated_at:new Date().toISOString(), scanned:all.length, leak_hits:hits.length, hits};
+fs.writeFileSync(OUT,JSON.stringify(report,null,2));
+let md=`# Collection-level leak sweep (live) — ${new Date().toISOString().slice(0,16)}\n\n`;
+md+=`**READ-ONLY (live /collections.json), \$0.** ${all.length} live collections swept for private-label/upstream names in handle+title (a surface the product-page leak scanners DON'T cover, per c39).\n\n`;
+md+=`## ${hits.length} LEAK hit(s)\n`;
+if(hits.length){ md+=`| handle | title | products | matched | maps to |\n|---|---|--:|---|---|\n`; for(const h of hits) md+=`| ${h.handle} | ${(h.title||'').slice(0,40)} | ${h.products_count} | **${h.matched}** | ${h.why} |\n`; }
+else md+=`None — no private-label/upstream name found in any live collection handle/title. 🟢\n`;
+fs.writeFileSync(MD,md);
+console.log(`[collection-leak-sweep] scanned=${all.length} leak_hits=${hits.length}`);
+for(const h of hits) console.log(`  ⛔ ${h.handle} (matched "${h.matched}", ${h.products_count} products) ${h.why}`);
+console.log(`Report: ${MD}`);

← fc12f52 orphan-collection scan (c39): 4 empty live collections + a W  ·  back to Dw Yolo Loop  ·  Kravet add-roll-variant writer: adds Sold-Per-Roll @ MAP to 5f0ef46 →