← back to Dw Yolo Loop
cycle 85: DISAMBIGUATE c84 238 inverse-mislabels (DTD unanimous A) — full active scan 0/238 in showroom collection + 0/238 have custom.showroom_line; NEG-CONTROL proves 100% of 600 deliberate Showroom-Lines carry the metafield (0% tag-only) -> signal validated, 'untagged Showroom batch' hypothesis REFUTED; genuine mislabels not tag; fix=relabel/unpublish (all 238) + SPLIT where cost exists (~74 DW private-label + Lee Jofa; PR 139 SPLIT-blocked no price sheet); 5 unit-named outliers hand-review
b1534378495aeba2140932ef46fde81f665f03e9 · 2026-06-18 08:18:57 -0700 · Steve Abrams
Files touched
A scripts/inverse-mislabel-disambig/FINDING.mdA scripts/inverse-mislabel-disambig/inverse-mislabel-disambig.mjsA scripts/inverse-mislabel-disambig/showroom-negctrl.mjs
Diff
commit b1534378495aeba2140932ef46fde81f665f03e9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jun 18 08:18:57 2026 -0700
cycle 85: DISAMBIGUATE c84 238 inverse-mislabels (DTD unanimous A) — full active scan 0/238 in showroom collection + 0/238 have custom.showroom_line; NEG-CONTROL proves 100% of 600 deliberate Showroom-Lines carry the metafield (0% tag-only) -> signal validated, 'untagged Showroom batch' hypothesis REFUTED; genuine mislabels not tag; fix=relabel/unpublish (all 238) + SPLIT where cost exists (~74 DW private-label + Lee Jofa; PR 139 SPLIT-blocked no price sheet); 5 unit-named outliers hand-review
---
scripts/inverse-mislabel-disambig/FINDING.md | 39 +++++++++++
.../inverse-mislabel-disambig.mjs | 75 ++++++++++++++++++++++
.../inverse-mislabel-disambig/showroom-negctrl.mjs | 37 +++++++++++
3 files changed, 151 insertions(+)
diff --git a/scripts/inverse-mislabel-disambig/FINDING.md b/scripts/inverse-mislabel-disambig/FINDING.md
new file mode 100644
index 0000000..d886013
--- /dev/null
+++ b/scripts/inverse-mislabel-disambig/FINDING.md
@@ -0,0 +1,39 @@
+# c84 238 inverse-mislabels DISAMBIGUATED — they are NOT an untagged Showroom-Lines batch (officer hypothesis REFUTED via negative control); genuine mislabels → relabel/SPLIT, NOT tag — cycle 85
+
+**DTD verdict A (unanimous 3/3 — Claude + Codex + Qwen).** READ-ONLY (Admin GraphQL full active scan + a negative-control sample; no mutation), **$0**. Resolves the disambiguation the c84 officer REQUIRED before any fix: are the 238 an UNTAGGED Showroom-Lines batch (cheap fix = add the tag/metafield) or GENUINE mislabels (fix = SPLIT, gated on cost)?
+
+## 🔴 Answer: GENUINE mislabels, NOT untagged-Showroom — 0/238 carry the showroom signature that 100% of real showroom products carry
+Full active scan (71,733, complete=true), all 238 re-matched:
+- **0/238 are in a showroom collection.**
+- **0/238 carry the `custom.showroom_line` metafield.**
+- **238/238 = NEITHER** → none match the deliberate Showroom-Lines signature.
+
+## 🔬 The skeptical check that makes this load-bearing — NEGATIVE CONTROL validates the signal (not a tautology)
+"0/238 carry the marker" is meaningless UNLESS real Showroom-Lines actually carry it. So I sampled the deliberate set (`tag:showroom*`, 600 active products):
+- **`custom.showroom_line` metafield: 600/600 = 100.0%**
+- in a showroom collection: 445/600 = 74.2%
+- **collection OR metafield (the disambig signal): 600/600 = 100.0%**
+- **tag-only (no collection, no metafield) — the case that would BREAK the signal: 0/600 = 0.0%**
+→ Every deliberate Showroom-Lines product carries the metafield (and 74% also the collection). The signal is **fully validated**: the 238 lack what 100% of real showroom products have. The "one untagged Showroom-Lines batch that never got tagged" hypothesis (the c84 officer's leading guess) is **REFUTED** — an untagged-but-intended batch would still carry the metafield/collection (the tag is the LAST marker applied, not the first); these carry none of the three. They were never set up as showroom items at all.
+
+## 📋 So the tag-vs-SPLIT decision the officer asked for: NOT tag. They are genuine mislabels.
+- **Cheap "tag-as-Showroom" fix is WRONG for all 238** — they don't match the showroom signature; tagging them would misfile genuine products as intentional $5 swatches.
+- These are what they looked like in c84: **single-variant products priced $5 (sample-scale) but titled as the full wallcovering/grasscloth/faux-leather/fabric, with NO showroom intent marker** = the inverse mislabel. A shopper adds "$5 Akiko Classic Grasscloth" and receives a swatch.
+- **Presentation reinforces "genuine product, not swatch":** of 60 sampled, **17 have full-product copy (body ≥200 chars)** and several have rich PDPs — e.g. *Byzantium Flock Velvet* (11 images, 1,059 chars), *Lizzie Lizard* (982 chars), *Aquarium Fish Tank* (972 chars), *Pleated Piazza* (859 chars). You do not write 1,000 chars + 11 images for a $5 swatch — these were built as **full products that never received a roll variant/price** ($5 is a never-completed placeholder). 24/60 are swatch-sparse (body <60, 1 image) — the ambiguous tail.
+
+## Vendor split = who can be fixed vs who is blocked (the actionable cut)
+| Group | Vendors | n | SPLIT executable? |
+|---|---|---|---|
+| **Phillipe Romano (PR + PR Faux Leather)** | Phillipe Romano 129 + PR Faux Leather 10 | **139** | ❌ NO — PR = Pointe Intl private-label, per-pattern quotes only, **no bulk price sheet** (`phillipe-romano-pointe-pricing`). Roll price not computable → relabel/unpublish only until a quote. |
+| **DW-owned private-label** | Designer Wallcoverings 27, Hollywood 12, British Walls 10, Malibu 10, LA Walls 6, LEED 2, Glass Beaded 2, Primo 2, Surface Stick 3 | ~74 | ✅ in principle — DW SETS these prices (own lines, `private-label-va-agents-no-scraper`); SPLIT with a DW-set roll price is doable. |
+| **Branded tail** | Fentucci 7, Versace 2, Missoni 2, Lee Jofa 2, … | ~25 | mixed — Lee Jofa = Kravet-MAP (computable); Fentucci = benign $5 sample family (c82); decode per-vendor. |
+- **Variant title:** 233/238 "Default Title" (the never-split core) + **5 outliers** (Sold Per Single Roll, "15x16.5", Square-Polyester 24x24, Sold Per Unit, Sold Per Yard) — the 5 are already partly-structured (a unit named) → hand-review separately (a real $5 unit-good may exist).
+
+## Recommendation (GATED — Steve's call; the disambiguation is now resolved)
+1. **Do NOT tag-as-Showroom** (refuted). These are genuine mislabels, not intentional swatches.
+2. **Ungated, cheap, honest-now fix (no roll price needed):** relabel so the $5 stops masquerading as the full product — either rename the single variant/title to disclose "Sample/Memo" OR unpublish until a roll variant exists. This is the O&L-officer move (relabel makes the existing price honestly presented; doesn't fabricate a number). Safe for all 238 including the PR 139.
+3. **SPLIT (add a real roll variant + price) where cost exists:** the ~74 DW-owned private-label (DW sets price) + Lee Jofa (Kravet-MAP) are SPLIT-executable; **the PR 139 are SPLIT-BLOCKED** (no price sheet) → relabel/unpublish only. Use the non-default-consuming variant path (`shopify-default-variant-consumes-sample`).
+4. **Sequence:** lower priority than the at-MAP-verified Cole&Son/Kravet roll-mislabels (authoritative price). For the 238 the immediate safe action is the cheap relabel/unpublish (ungated); SPLIT follows where a price exists.
+5. **c75 canary:** the inverse signature is now confirmed real + distinct from Showroom-Lines — add it to the basis-aware guard with the **metafield/collection negative-control built in** (flag single-variant + sample-priced + title-not-"Sample" + **NOT carrying `custom.showroom_line`**), so it never re-flags a real showroom product and never mis-routes a genuine mislabel to tag-as-Showroom.
+
+_Cycle 85. DTD-picked A (unanimous 3/3). Disambiguated the c84 238 inverse-mislabels per the c84 officer's requirement: full active scan (71,733) shows 0/238 in a showroom collection + 0/238 with `custom.showroom_line`, while a negative control proves 100% of 600 deliberate Showroom-Lines carry the metafield (0% tag-only) → the signal is validated and the "untagged Showroom-Lines batch" hypothesis is REFUTED. The 238 are genuine mislabels (single-variant $5 titled as the full product, no showroom marker; 17/60 have full-product PDPs ⇒ built as products, never given a roll/price). Tag-vs-SPLIT answer: NOT tag. Fix = relabel/unpublish (ungated, cheap, all 238) and/or SPLIT-with-real-roll where cost exists (~74 DW private-label + Lee Jofa executable; the PR 139 SPLIT-BLOCKED, no price sheet). 5 unit-named outliers hand-reviewed. PJ excluded by construction. Gated._
diff --git a/scripts/inverse-mislabel-disambig/inverse-mislabel-disambig.mjs b/scripts/inverse-mislabel-disambig/inverse-mislabel-disambig.mjs
new file mode 100644
index 0000000..a743f5d
--- /dev/null
+++ b/scripts/inverse-mislabel-disambig/inverse-mislabel-disambig.mjs
@@ -0,0 +1,75 @@
+// inverse-mislabel-disambig — READ-ONLY, $0. Cycle 85. Disambiguates the c84 238 inverse-mislabels
+// (single-variant, price<=THRESH, title NOT /sample/i, NOT showroom-tagged) the c84 officer REQUIRED
+// before any fix: are these an UNTAGGED Showroom-Lines batch (cheap fix = add the tag/metafield) or
+// GENUINE mislabels (fix = SPLIT, unexecutable for PR with no price sheet)?
+//
+// Read-only signals captured PER product (the officer's exact asks):
+// 1. showroom-lines COLLECTION membership — in the collection but missing the tag? => untagged-Showroom
+// 2. custom.showroom_line METAFIELD present — has the metafield but missing the tag? => untagged-Showroom
+// 3. variant title "Default Title" vs other — separates the 233 core from the 5 outliers
+// 4. images count + body_html length — swatch-vs-full-product presentation proxy (a real swatch
+// PDP tends to be sparse; a full-product PDP has rich copy/imagery)
+// Decision per product: untagged-Showroom (collection OR metafield present) => TAG fix (cheap, safe);
+// else genuine-mislabel => SPLIT (gated on real cost; unexecutable for PR). EXCLUDES PJ.
+import fs from 'fs';
+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 GQL='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
+const PJ=/phillip[- ]?jeffries/i;
+const THRESH=Number(process.env.THRESH||10);
+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;}
+const isSample=t=>/sample/i.test(t||'');
+// Capture the disambiguation signals. collections(first:30) handles, the showroom_line metafield,
+// image count via images(first:11) (cap+1 to observe truncation), body_html length.
+const Q=`query($cursor:String){ products(first:60, after:$cursor, query:"status:active"){ pageInfo{hasNextPage endCursor}
+ edges{node{ id title vendor tags productType
+ descriptionHtml
+ metafield(namespace:"custom", key:"showroom_line"){ value }
+ collections(first:30){ edges{ node{ handle } } }
+ images(first:11){ edges{ node{ id } } }
+ variants(first:3){ edges{ node{ title price } } } } } } }`;
+let cursor=null,pages=0,scanned=0,complete=true;
+let matched=0;
+// disposition buckets
+let bColl=0, bMeta=0, bCollOrMeta=0, bNeither=0;
+const byVendor={}, byVariantTitle={}, sample=[];
+const SHOWROOM_COLL=/showroom/i;
+while(true){ const j=await gql(Q,{cursor}); if(!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); if(vs.length!==1)continue; const v=vs[0]; const p=parseFloat(v.price);
+ if(isNaN(p)||p>THRESH) continue;
+ if(isSample(n.title)||isSample(v.title)) continue;
+ if((n.tags||[]).some(t=>/showroom/i.test(t))) continue; // tagged showroom already excluded (the 2,768)
+ // === this node is one of the c84 238 ===
+ matched++;
+ const inShowroomColl=(n.collections?.edges||[]).some(x=>SHOWROOM_COLL.test(x.node.handle||''));
+ const hasMeta=!!(n.metafield&&n.metafield.value);
+ const imgN=(n.images?.edges||[]).length; // 11 means >=11 (truncated)
+ const bodyLen=(n.descriptionHtml||'').replace(/<[^>]+>/g,'').trim().length;
+ if(inShowroomColl) bColl++;
+ if(hasMeta) bMeta++;
+ if(inShowroomColl||hasMeta) bCollOrMeta++; else bNeither++;
+ byVendor[n.vendor]=byVendor[n.vendor]||{n:0,collOrMeta:0}; byVendor[n.vendor].n++; if(inShowroomColl||hasMeta) byVendor[n.vendor].collOrMeta++;
+ byVariantTitle[v.title]=(byVariantTitle[v.title]||0)+1;
+ if(sample.length<40 || !inShowroomColl) { if(sample.length<60) sample.push({t:n.title.slice(0,42),vendor:n.vendor,p,vt:v.title,coll:inShowroomColl,meta:hasMeta,img:imgN,body:bodyLen}); }
+ }
+ pages++; if(!c.pageInfo.hasNextPage)break; cursor=c.pageInfo.endCursor;
+ const cost=j.extensions?.cost?.throttleStatus; if(cost&&cost.currentlyAvailable<400) await sleep(1800); else await sleep(300);
+ if(pages%100===0) console.log(` ${pages}p scanned=${scanned} matched=${matched}`);
+}
+console.log(`\n=== inverse-mislabel-disambig (READ-ONLY, $0) ===`);
+console.log(`active scanned: ${scanned} (complete=${complete}, ${pages}p)`);
+console.log(`c84 inverse-mislabels re-matched: ${matched}`);
+console.log(`\nDISPOSITION SIGNAL (the officer's tag-vs-SPLIT question):`);
+console.log(` in a showroom COLLECTION (untagged-showroom signal): ${bColl}`);
+console.log(` has custom.showroom_line METAFIELD (untagged-showroom signal): ${bMeta}`);
+console.log(` collection OR metafield present => UNTAGGED-SHOWROOM (cheap TAG fix): ${bCollOrMeta}`);
+console.log(` NEITHER => genuine-mislabel candidate (SPLIT, gated on cost): ${bNeither}`);
+console.log(`\nby vendor (top 15) [n / of-which-untagged-showroom]:`);
+Object.entries(byVendor).sort((a,b)=>b[1].n-a[1].n).slice(0,15).forEach(([v,o])=>console.log(` ${v}: ${o.n} / ${o.collOrMeta} untagged-showroom`));
+console.log(`\nby variant-title:`); Object.entries(byVariantTitle).sort((a,b)=>b[1]-a[1]).slice(0,8).forEach(([t,n])=>console.log(` "${t}": ${n}`));
+console.log(`\nsamples (coll=in-showroom-collection meta=has-metafield img=#images body=plaintext-chars):`);
+sample.forEach(x=>console.log(` "${x.t}" [${x.vendor}] $${x.p} v="${x.vt}" coll=${x.coll} meta=${x.meta} img=${x.img} body=${x.body}`));
+fs.writeFileSync('/tmp/inverse-mislabel-disambig.json',JSON.stringify({ts:new Date().toISOString(),scanned,complete,thresh:THRESH,matched,bColl,bMeta,bCollOrMeta,bNeither,byVendor,byVariantTitle,sample},null,2));
+console.log(`\nwrote /tmp/inverse-mislabel-disambig.json`);
diff --git a/scripts/inverse-mislabel-disambig/showroom-negctrl.mjs b/scripts/inverse-mislabel-disambig/showroom-negctrl.mjs
new file mode 100644
index 0000000..fe27d7f
--- /dev/null
+++ b/scripts/inverse-mislabel-disambig/showroom-negctrl.mjs
@@ -0,0 +1,37 @@
+// 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/stevestudio2/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.`);
← b112a34 cycle 84: consistency fix — closing line reflects re-scoped
·
back to Dw Yolo Loop
·
cycle 85: officer vp-dw-commerce CONFIRMED + 2 non-blocking 99fbc92 →