[object Object]

← back to Tk10656 Empty Collections

TK-10656: empty published-collection hunt — 19 live pages show shoppers 0 products + verified Versa private-label leak + colefax dup

ebe5b42cf354767ea8eddd06e375e03d83a16c1a · 2026-08-18 00:53:39 -0700 · steve

Files touched

Diff

commit ebe5b42cf354767ea8eddd06e375e03d83a16c1a
Author: steve <steve@designerwallcoverings.com>
Date:   Tue Aug 18 00:53:39 2026 -0700

    TK-10656: empty published-collection hunt — 19 live pages show shoppers 0 products + verified Versa private-label leak + colefax dup
---
 .gitignore                            |   5 +
 data/empty-published-collections.json |   1 +
 data/empty-to-shopper.json            |  97 +++++++++++++++++
 data/empty-triage.json                | 196 ++++++++++++++++++++++++++++++++++
 data/scan-result.json                 |   7 ++
 data/storefront-empty-result.json     | 102 ++++++++++++++++++
 scan-collections.mjs                  |  32 ++++++
 storefront-empty-scan.mjs             |  30 ++++++
 8 files changed, 470 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1389fd5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+node_modules/
+.env*
+*.log
+.DS_Store
+data/collections-dump.jsonl
diff --git a/data/empty-published-collections.json b/data/empty-published-collections.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/data/empty-published-collections.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/data/empty-to-shopper.json b/data/empty-to-shopper.json
new file mode 100644
index 0000000..5e390e4
--- /dev/null
+++ b/data/empty-to-shopper.json
@@ -0,0 +1,97 @@
+[
+  {
+    "handle": "phillipe-romano-faux-leathers-upholstery-more",
+    "title": "Phillipe Romano Faux Leathers Upholstery & More",
+    "admin_count": 1338
+  },
+  {
+    "handle": "hollywood-sunset-collection",
+    "title": "Hollywood Sunset Vinyl Collection",
+    "admin_count": 243
+  },
+  {
+    "handle": "phillipe-romano-ultimo",
+    "title": "Phillipe Romano Ultimo",
+    "admin_count": 20
+  },
+  {
+    "handle": "as-seen-in-showrooms",
+    "title": "As Seen in Showrooms",
+    "admin_count": 42
+  },
+  {
+    "handle": "paul-montgomery-murals",
+    "title": "Paul Montgomery Murals",
+    "admin_count": 443
+  },
+  {
+    "handle": "paul-montgomery-all-products",
+    "title": "Paul Montgomery - All Products",
+    "admin_count": 1538
+  },
+  {
+    "handle": "colefax-and-fowler",
+    "title": "Colefax and Fowler",
+    "admin_count": 361
+  },
+  {
+    "handle": "armani-casa-wallcoverings",
+    "title": "Armani Casa",
+    "admin_count": 243
+  },
+  {
+    "handle": "phillipe-romano-fabrics",
+    "title": "Phillipe Romano Fabrics",
+    "admin_count": 176
+  },
+  {
+    "handle": "carlisle-co",
+    "title": "Carlisle & Co",
+    "admin_count": 441
+  },
+  {
+    "handle": "colefax-fowler",
+    "title": "Colefax & Fowler",
+    "admin_count": 361
+  },
+  {
+    "handle": "borastapeter",
+    "title": "Borastapeter",
+    "admin_count": 100
+  },
+  {
+    "handle": "et-cie",
+    "title": "Et Cie",
+    "admin_count": 487
+  },
+  {
+    "handle": "holly-hunt",
+    "title": "Holly Hunt",
+    "admin_count": 1260
+  },
+  {
+    "handle": "versa-designed-surfaces-wallcoverings",
+    "title": "Versa Designed Surfaces Wallcoverings",
+    "admin_count": 42
+  },
+  {
+    "handle": "hermes",
+    "title": "Hermes",
+    "admin_count": 29
+  },
+  {
+    "handle": "hygge-and-west",
+    "title": "Hygge & West Wallcoverings",
+    "admin_count": 11
+  },
+  {
+    "handle": "borastapeter-scandinavian-wallpapers",
+    "title": "Borastapeter Scandinavian Wallcoverings",
+    "admin_count": 1
+  },
+  {
+    "handle": "collezione-italia",
+    "title": "Collezione Italia",
+    "admin_count": 413
+  }
+]
\ No newline at end of file
diff --git a/data/empty-triage.json b/data/empty-triage.json
new file mode 100644
index 0000000..fd6cf17
--- /dev/null
+++ b/data/empty-triage.json
@@ -0,0 +1,196 @@
+[
+  {
+    "handle": "phillipe-romano-faux-leathers-upholstery-more",
+    "title": "Phillipe Romano Faux Leathers Upholstery & More",
+    "admin_count": 1338,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "hollywood-sunset-collection",
+    "title": "Hollywood Sunset Vinyl Collection",
+    "admin_count": 243,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "phillipe-romano-ultimo",
+    "title": "Phillipe Romano Ultimo",
+    "admin_count": 20,
+    "sampled": 20,
+    "status": {
+      "ARCHIVED": 20
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "as-seen-in-showrooms",
+    "title": "As Seen in Showrooms",
+    "admin_count": 42,
+    "sampled": 42,
+    "status": {
+      "DRAFT": 10,
+      "ARCHIVED": 32
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "paul-montgomery-murals",
+    "title": "Paul Montgomery Murals",
+    "admin_count": 443,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "paul-montgomery-all-products",
+    "title": "Paul Montgomery - All Products",
+    "admin_count": 1538,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "colefax-and-fowler",
+    "title": "Colefax and Fowler",
+    "admin_count": 361,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "armani-casa-wallcoverings",
+    "title": "Armani Casa",
+    "admin_count": 243,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 99,
+      "DRAFT": 1
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "phillipe-romano-fabrics",
+    "title": "Phillipe Romano Fabrics",
+    "admin_count": 176,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "carlisle-co",
+    "title": "Carlisle & Co",
+    "admin_count": 441,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "colefax-fowler",
+    "title": "Colefax & Fowler",
+    "admin_count": 361,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "borastapeter",
+    "title": "Borastapeter",
+    "admin_count": 100,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "et-cie",
+    "title": "Et Cie",
+    "admin_count": 487,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 99,
+      "DRAFT": 1
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "holly-hunt",
+    "title": "Holly Hunt",
+    "admin_count": 1260,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 100
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "versa-designed-surfaces-wallcoverings",
+    "title": "Versa Designed Surfaces Wallcoverings",
+    "admin_count": 42,
+    "sampled": 42,
+    "status": {
+      "ARCHIVED": 42
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "hermes",
+    "title": "Hermes",
+    "admin_count": 29,
+    "sampled": 29,
+    "status": {
+      "ARCHIVED": 29
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "hygge-and-west",
+    "title": "Hygge & West Wallcoverings",
+    "admin_count": 11,
+    "sampled": 11,
+    "status": {
+      "ARCHIVED": 11
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "borastapeter-scandinavian-wallpapers",
+    "title": "Borastapeter Scandinavian Wallcoverings",
+    "admin_count": 1,
+    "sampled": 1,
+    "status": {
+      "ARCHIVED": 1
+    },
+    "likely": "ALL_ARCHIVED_unpublish_or_dedupe"
+  },
+  {
+    "handle": "collezione-italia",
+    "title": "Collezione Italia",
+    "admin_count": 413,
+    "sampled": 100,
+    "status": {
+      "ARCHIVED": 40,
+      "DRAFT": 60
+    },
+    "likely": "STAGED_DRAFTS_maybe_activate"
+  }
+]
\ No newline at end of file
diff --git a/data/scan-result.json b/data/scan-result.json
new file mode 100644
index 0000000..87937f9
--- /dev/null
+++ b/data/scan-result.json
@@ -0,0 +1,7 @@
+{
+  "total_collections": 707,
+  "published_empty_ZERO_products": 0,
+  "unpublished_empty": 49,
+  "published_nonempty": 577,
+  "sample": []
+}
diff --git a/data/storefront-empty-result.json b/data/storefront-empty-result.json
new file mode 100644
index 0000000..4d128ba
--- /dev/null
+++ b/data/storefront-empty-result.json
@@ -0,0 +1,102 @@
+{
+  "published_collections_checked": 577,
+  "EMPTY_to_shopper": 19,
+  "fetch_errors": 0,
+  "sample": [
+    {
+      "handle": "phillipe-romano-faux-leathers-upholstery-more",
+      "title": "Phillipe Romano Faux Leathers Upholstery & More",
+      "admin_count": 1338
+    },
+    {
+      "handle": "hollywood-sunset-collection",
+      "title": "Hollywood Sunset Vinyl Collection",
+      "admin_count": 243
+    },
+    {
+      "handle": "phillipe-romano-ultimo",
+      "title": "Phillipe Romano Ultimo",
+      "admin_count": 20
+    },
+    {
+      "handle": "as-seen-in-showrooms",
+      "title": "As Seen in Showrooms",
+      "admin_count": 42
+    },
+    {
+      "handle": "paul-montgomery-murals",
+      "title": "Paul Montgomery Murals",
+      "admin_count": 443
+    },
+    {
+      "handle": "paul-montgomery-all-products",
+      "title": "Paul Montgomery - All Products",
+      "admin_count": 1538
+    },
+    {
+      "handle": "colefax-and-fowler",
+      "title": "Colefax and Fowler",
+      "admin_count": 361
+    },
+    {
+      "handle": "armani-casa-wallcoverings",
+      "title": "Armani Casa",
+      "admin_count": 243
+    },
+    {
+      "handle": "phillipe-romano-fabrics",
+      "title": "Phillipe Romano Fabrics",
+      "admin_count": 176
+    },
+    {
+      "handle": "carlisle-co",
+      "title": "Carlisle & Co",
+      "admin_count": 441
+    },
+    {
+      "handle": "colefax-fowler",
+      "title": "Colefax & Fowler",
+      "admin_count": 361
+    },
+    {
+      "handle": "borastapeter",
+      "title": "Borastapeter",
+      "admin_count": 100
+    },
+    {
+      "handle": "et-cie",
+      "title": "Et Cie",
+      "admin_count": 487
+    },
+    {
+      "handle": "holly-hunt",
+      "title": "Holly Hunt",
+      "admin_count": 1260
+    },
+    {
+      "handle": "versa-designed-surfaces-wallcoverings",
+      "title": "Versa Designed Surfaces Wallcoverings",
+      "admin_count": 42
+    },
+    {
+      "handle": "hermes",
+      "title": "Hermes",
+      "admin_count": 29
+    },
+    {
+      "handle": "hygge-and-west",
+      "title": "Hygge & West Wallcoverings",
+      "admin_count": 11
+    },
+    {
+      "handle": "borastapeter-scandinavian-wallpapers",
+      "title": "Borastapeter Scandinavian Wallcoverings",
+      "admin_count": 1
+    },
+    {
+      "handle": "collezione-italia",
+      "title": "Collezione Italia",
+      "admin_count": 413
+    }
+  ]
+}
diff --git a/scan-collections.mjs b/scan-collections.mjs
new file mode 100644
index 0000000..9454018
--- /dev/null
+++ b/scan-collections.mjs
@@ -0,0 +1,32 @@
+// TK-10656 — READ-ONLY scan of ALL collections: capture handle/title/productsCount +
+// whether published to the Online Store. Flags the customer-facing dead-page class:
+// published to Online Store AND productsCount == 0 (a live collection page showing shoppers 0 products).
+import { readFileSync, writeFileSync, appendFileSync } from 'node:fs';
+const txt=readFileSync(`${process.env.HOME}/Projects/secrets-manager/.env`,'utf8');
+const env={}; for(const l of txt.split('\n')){const m=l.match(/^([A-Z0-9_]+)=(.*)$/); if(m) env[m[1]]=m[2].replace(/^["']|["']$/g,'');}
+const STORE=env.SHOPIFY_STORE_DOMAIN, TOKEN=env.SHOPIFY_FULL_ACCESS_TOKEN, API='2024-10';
+const ONLINE='gid://shopify/Publication/22208643184';
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+async function gql(q,v={}){for(let a=0;a<10;a++){try{const res=await fetch(`https://${STORE}/admin/api/${API}/graphql.json`,{method:'POST',headers:{'Content-Type':'application/json','X-Shopify-Access-Token':TOKEN},body:JSON.stringify({query:q,variables:v})});const tx=await res.text();let j;try{j=JSON.parse(tx);}catch{await sleep(1500*(a+1));continue;}if(j.errors){if(/THROTTLED/i.test(JSON.stringify(j.errors))){await sleep(1500*(a+1));continue;}throw new Error(JSON.stringify(j.errors));}return j.data;}catch(e){if(a<9){await sleep(1500*(a+1));continue;}throw e;}}}
+const DUMP='data/collections-dump.jsonl'; writeFileSync(DUMP,'');
+let cursor=null, n=0, publishedEmpty=0, unpublishedEmpty=0, publishedNonEmpty=0;
+const emptyPublished=[];
+while(true){
+  const d=await gql(`query($c:String){ collections(first:100, after:$c){ pageInfo{ hasNextPage endCursor }
+    nodes{ id handle title productsCount{count} publishedOnPublication(publicationId:"${ONLINE}") } } }`,{c:cursor});
+  const buf=[];
+  for(const c of d.collections.nodes){ n++;
+    const rec={handle:c.handle,title:c.title,count:c.productsCount?.count??null,pub:c.publishedOnPublication};
+    buf.push(JSON.stringify(rec));
+    if(rec.count===0){ if(rec.pub){ publishedEmpty++; if(emptyPublished.length<500) emptyPublished.push(rec); } else unpublishedEmpty++; }
+    else if(rec.pub) publishedNonEmpty++;
+  }
+  appendFileSync(DUMP, buf.join('\n')+'\n');
+  if(n%500===0) process.stderr.write(`  scanned ${n} collections, published-empty ${publishedEmpty}\n`);
+  if(!d.collections.pageInfo.hasNextPage) break;
+  cursor=d.collections.pageInfo.endCursor;
+}
+writeFileSync('data/empty-published-collections.json', JSON.stringify(emptyPublished,null,2));
+console.log(JSON.stringify({total_collections:n, published_empty_ZERO_products:publishedEmpty,
+  unpublished_empty:unpublishedEmpty, published_nonempty:publishedNonEmpty,
+  sample:emptyPublished.slice(0,15)},null,2));
diff --git a/storefront-empty-scan.mjs b/storefront-empty-scan.mjs
new file mode 100644
index 0000000..ce9b0e8
--- /dev/null
+++ b/storefront-empty-scan.mjs
@@ -0,0 +1,30 @@
+// TK-10656 — customer-truth scan: for every collection PUBLISHED to Online Store, fetch the
+// storefront /collections/<handle>/products.json (published products only). Flag those that
+// return 0 products = a live collection page that shows the SHOPPER nothing (hidden dead page
+// even if the Admin productsCount>0, because all members are archived/draft/unpublished).
+import { readFileSync, writeFileSync } from 'node:fs';
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+const BASE='https://www.designerwallcoverings.com';
+const rows=readFileSync('data/collections-dump.jsonl','utf8').trim().split('\n').map(JSON.parse).filter(c=>c.pub);
+let checked=0, emptyToShopper=0, errs=0; const empties=[];
+for(const c of rows){
+  let ok=false;
+  for(let a=0;a<4;a++){
+    try{
+      const r=await fetch(`${BASE}/collections/${c.handle}/products.json?limit=1`,{headers:{'User-Agent':'Mozilla/5.0 dw-audit'}});
+      if(r.status===429||r.status>=500){ await sleep(1500*(a+1)); continue; }
+      const j=await r.json().catch(()=>null);
+      if(!j){ await sleep(1000*(a+1)); continue; }
+      const nprod=(j.products||[]).length;
+      if(nprod===0){ emptyToShopper++; empties.push({handle:c.handle,title:c.title,admin_count:c.count}); }
+      ok=true; break;
+    }catch(e){ await sleep(1000*(a+1)); }
+  }
+  if(!ok) errs++;
+  checked++;
+  if(checked%100===0) process.stderr.write(`  checked ${checked}/${rows.length}, empty-to-shopper ${emptyToShopper}, errs ${errs}\n`);
+  await sleep(120); // gentle
+}
+writeFileSync('data/empty-to-shopper.json', JSON.stringify(empties,null,2));
+console.log(JSON.stringify({published_collections_checked:checked, EMPTY_to_shopper:emptyToShopper, fetch_errors:errs,
+  sample:empties.slice(0,25)},null,2));

(oldest)  ·  back to Tk10656 Empty Collections  ·  TK-10656 Cody fixes: Playwright rendered-DOM proof (holly-hu 62bdcca →