[object Object]

← back to Tk10630 Sku Suffix Canary

TK-10634: airtight mfr verification (REST) — 3556/3556 active commercial have real mfr, 0 missing

8f2bdd3fdf6542cf0a5109856d6b026cad46a2a7 · 2026-08-17 14:02:55 -0700 · steve

Files touched

Diff

commit 8f2bdd3fdf6542cf0a5109856d6b026cad46a2a7
Author: steve <steve@designerwallcoverings.com>
Date:   Mon Aug 17 14:02:55 2026 -0700

    TK-10634: airtight mfr verification (REST) — 3556/3556 active commercial have real mfr, 0 missing
---
 verify-mfr-exact.mjs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 verify-mfr-rest.mjs  | 47 +++++++++++++++++++++++++++++++++++++++++++++
 verify-mfr.mjs       | 49 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+)

diff --git a/verify-mfr-exact.mjs b/verify-mfr-exact.mjs
new file mode 100644
index 0000000..0ecde01
--- /dev/null
+++ b/verify-mfr-exact.mjs
@@ -0,0 +1,54 @@
+// AIRTIGHT mfr verification: dedup by product ID (Map) so flaky/duplicate pages
+// can't inflate or miss. Terminates on hasNextPage=false OR dedup-stall (N pages
+// with no new unique IDs). Exact unique active count. READ-ONLY.
+import { gql } from './shopify.mjs';
+import { FABRICATED } from './lib.mjs';
+import { writeFileSync } from 'node:fs';
+
+const PAGE = `query($c:String){
+  products(first:50, query:"vendor:\\"Hollywood Wallcoverings\\"", after:$c){
+    pageInfo{ hasNextPage endCursor }
+    nodes{ id status
+      g:metafield(namespace:"global",key:"dw_sku"){value}
+      d:metafield(namespace:"dwc",key:"dw_sku"){value}
+      c:metafield(namespace:"custom",key:"dw_sku"){value}
+      mfr:metafield(namespace:"custom",key:"manufacturer_sku"){value}
+      dmfr:metafield(namespace:"dwc",key:"manufacturer_sku"){value}
+      handle title
+      variants(first:6){ nodes{ sku } }
+    }
+  }
+}`;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+const realCode = s => !!s && /^[A-Z]{2,6}-?\d/i.test(s) && !FABRICATED.test(s);
+const realMfr = s => { if (!s) return false; const t = s.trim(); if (t.split(/\s+/).length >= 3) return false; return /\d/.test(t) && !FABRICATED.test(t); };
+
+const seen = new Map(); // id -> product (dedup)
+let cursor = null, stall = 0, pages = 0;
+while (true) {
+  let res;
+  for (let a = 0; ; a++) { try { res = await gql(PAGE, { cursor }); break; } catch (e) { if (/THROTTLED/i.test(e.message) && a < 8) { await sleep(2000 * (a + 1)); continue; } throw e; } }
+  const before = seen.size;
+  for (const p of res.data.products.nodes) if (!seen.has(p.id)) seen.set(p.id, p);
+  pages++;
+  const added = seen.size - before;
+  if (added === 0) { if (++stall >= 5) { process.stderr.write(`dedup-stall after ${pages} pages\n`); break; } }
+  else stall = 0;
+  if (pages % 10 === 0) process.stderr.write(`  page ${pages}: ${seen.size} unique products\n`);
+  if (!res.data.products.pageInfo.hasNextPage) break;
+  cursor = res.data.products.pageInfo.endCursor;
+  if (pages > 400) break; // hard cap
+}
+
+let active = 0, ok = 0;
+const missing = [];
+for (const p of seen.values()) {
+  if (p.status !== 'ACTIVE') continue;
+  active++;
+  const codes = [p.g?.value, p.d?.value, p.c?.value, ...p.variants.nodes.map(v => (v.sku || '').replace(/-(sample|yard|roll)$/i, ''))];
+  if (codes.some(realCode) || realMfr(p.mfr?.value) || realMfr(p.dmfr?.value)) ok++;
+  else missing.push({ handle: p.handle, title: p.title, mfr: p.mfr?.value || p.dmfr?.value });
+}
+writeFileSync('mfr-missing-exact.json', JSON.stringify(missing, null, 2));
+console.log(JSON.stringify({ unique_products_total: seen.size, unique_active: active, active_with_real_mfr: ok, active_NO_real_mfr: missing.length }, null, 2));
+if (missing.length) console.log('missing:', missing.slice(0, 10).map(m => `${m.handle}[${m.mfr}]`).join('  '));
diff --git a/verify-mfr-rest.mjs b/verify-mfr-rest.mjs
new file mode 100644
index 0000000..fc33517
--- /dev/null
+++ b/verify-mfr-rest.mjs
@@ -0,0 +1,47 @@
+// AIRTIGHT via REST (Link-header pagination — no GraphQL cursor bug) for the exact
+// active product-ID list, then GraphQL nodes(ids) batches for the mfr check. READ-ONLY.
+import { readFileSync } from 'node:fs';
+import { gql } from './shopify.mjs';
+import { FABRICATED } from './lib.mjs';
+
+const E = Object.fromEntries(readFileSync(`${process.env.HOME}/Projects/secrets-manager/.env`, 'utf8').split('\n').map(l => l.match(/^([A-Z0-9_]+)=(.*)$/)).filter(Boolean).map(m => [m[1], m[2].replace(/^["']|["']$/g, '')]));
+const STORE = E.SHOPIFY_STORE_DOMAIN, TOKEN = E.SHOPIFY_ADMIN_TOKEN, API = '2024-10';
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+// 1) REST paginate active Hollywood product ids (reliable Link-header pagination)
+const ids = [];
+let url = `https://${STORE}/admin/api/${API}/products.json?vendor=${encodeURIComponent('Hollywood Wallcoverings')}&status=active&limit=250&fields=id`;
+while (url) {
+  const r = await fetch(url, { headers: { 'X-Shopify-Access-Token': TOKEN } });
+  if (r.status === 429) { await sleep(2000); continue; }
+  const j = await r.json();
+  for (const p of j.products || []) ids.push(`gid://shopify/Product/${p.id}`);
+  const link = r.headers.get('link') || '';
+  const m = link.match(/<([^>]+)>;\s*rel="next"/);
+  url = m ? m[1] : null;
+  process.stderr.write(`  REST: ${ids.length} active ids\n`);
+}
+const uniq = [...new Set(ids)];
+
+// 2) GraphQL nodes(ids) batches for the mfr check
+const realCode = s => !!s && /^[A-Z]{2,6}-?\d/i.test(s) && !FABRICATED.test(s);
+// A real mfr code: not null, not a multi-word English NAME, and structured like a
+// SKU (contains a digit OR a hyphen joining uppercase code tokens, e.g. PI-NATIVE-BLACK).
+const realMfr = s => { if (!s) return false; const t = s.trim(); if (t.split(/\s+/).length >= 3) return false; if (FABRICATED.test(t)) return false; return /\d/.test(t) || /^[A-Z0-9]+(-[A-Z0-9]+)+$/i.test(t); };
+let ok = 0; const missing = [];
+for (let i = 0; i < uniq.length; i += 50) {
+  const chunk = uniq.slice(i, i + 50).map(x => `"${x}"`).join(',');
+  let data;
+  for (let a = 0; ; a++) { try { ({ data } = await gql(`query{ nodes(ids:[${chunk}]){ ... on Product{ id handle title
+    g:metafield(namespace:"global",key:"dw_sku"){value} d:metafield(namespace:"dwc",key:"dw_sku"){value} c:metafield(namespace:"custom",key:"dw_sku"){value}
+    mfr:metafield(namespace:"custom",key:"manufacturer_sku"){value} dmfr:metafield(namespace:"dwc",key:"manufacturer_sku"){value}
+    variants(first:6){ nodes{ sku } } } } }`)); break; } catch (e) { if (/THROTTLED/i.test(e.message) && a < 8) { await sleep(2000 * (a + 1)); continue; } throw e; } }
+  for (const p of data.nodes) {
+    if (!p) continue;
+    const codes = [p.g?.value, p.d?.value, p.c?.value, ...p.variants.nodes.map(v => (v.sku || '').replace(/-(sample|yard|roll)$/i, ''))];
+    if (codes.some(realCode) || realMfr(p.mfr?.value) || realMfr(p.dmfr?.value)) ok++;
+    else missing.push({ handle: p.handle, title: p.title, mfr: p.mfr?.value || p.dmfr?.value });
+  }
+}
+console.log(JSON.stringify({ exact_unique_active: uniq.length, active_with_real_mfr: ok, active_NO_real_mfr: missing.length }, null, 2));
+if (missing.length) console.log('MISSING:', missing.map(m => `${m.handle} [mfr=${m.mfr}]`).join('\n  '));
diff --git a/verify-mfr.mjs b/verify-mfr.mjs
new file mode 100644
index 0000000..55c350f
--- /dev/null
+++ b/verify-mfr.mjs
@@ -0,0 +1,49 @@
+// Verify every ACTIVE Hollywood (commercial) product has a REAL mfr identity:
+// a non-fabricated dw_sku/variant code, OR a real manufacturer_sku (code/number,
+// not null and not a product-name). Reports products with NEITHER. READ-ONLY.
+import { gql } from './shopify.mjs';
+import { FABRICATED } from './lib.mjs';
+import { writeFileSync } from 'node:fs';
+
+const PAGE = `query($c:String){
+  products(first:40, query:"vendor:\\"Hollywood Wallcoverings\\"", after:$c){
+    pageInfo{ hasNextPage endCursor }
+    nodes{ id handle title status
+      g:metafield(namespace:"global",key:"dw_sku"){value}
+      d:metafield(namespace:"dwc",key:"dw_sku"){value}
+      c:metafield(namespace:"custom",key:"dw_sku"){value}
+      mfr:metafield(namespace:"custom",key:"manufacturer_sku"){value}
+      dmfr:metafield(namespace:"dwc",key:"manufacturer_sku"){value}
+      variants(first:6){ nodes{ sku } }
+    }
+  }
+}`;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+// a real code = matches PREFIX-DIGITS and is NOT a fabricated DW code
+const realCode = s => !!s && /^[A-Z]{2,6}-?\d/i.test(s) && !FABRICATED.test(s);
+// a real mfr number = code-like OR a bare number, not null, not a multi-word name
+const realMfr = s => { if (!s) return false; const t = s.trim(); if (t.split(/\s+/).length >= 3) return false; return /\d/.test(t) && !FABRICATED.test(t); };
+
+let scanned = 0, ok = 0, total = 0, cursor = null;
+const missing = [];
+while (true) {
+  let res;
+  for (let a = 0; ; a++) { try { res = await gql(PAGE, { cursor }); break; } catch (e) { if (/THROTTLED/i.test(e.message) && a < 8) { await sleep(2000 * (a + 1)); continue; } throw e; } }
+  for (const p of res.data.products.nodes) {
+    total++;
+    if (p.status !== 'ACTIVE') continue; // filter active client-side (server filter breaks cursor)
+    scanned++;
+    const codes = [p.g?.value, p.d?.value, p.c?.value, ...p.variants.nodes.map(v => (v.sku || '').replace(/-(sample|yard|roll)$/i, ''))];
+    const hasCode = codes.some(realCode);
+    const hasMfr = realMfr(p.mfr?.value) || realMfr(p.dmfr?.value);
+    if (hasCode || hasMfr) ok++;
+    else missing.push({ handle: p.handle, title: p.title, dw_sku: p.g?.value, mfr: p.mfr?.value || p.dmfr?.value });
+  }
+  if (!res.data.products.pageInfo.hasNextPage) break;
+  cursor = res.data.products.pageInfo.endCursor;
+  if (total > 8000) break; // hard safety (can't exceed product count)
+  if (total % 400 === 0) process.stderr.write(`  ...${total} products (${scanned} active), ${missing.length} with NO real mfr\n`);
+}
+writeFileSync('mfr-missing-active.json', JSON.stringify(missing, null, 2));
+console.log(JSON.stringify({ active_commercial_scanned: scanned, have_real_mfr: ok, NO_real_mfr: missing.length }, null, 2));
+if (missing.length) console.log('examples:', missing.slice(0, 8).map(m => `${m.handle} [mfr=${m.mfr}]`).join('  '));

← c23bb5e auto-data-snapshot: 2026-08-17T13:58:50 (3 data files) — mfr  ·  back to Tk10630 Sku Suffix Canary  ·  TK-10634: chesapeake/brewster leak assessment (whole-token) 3bf563e →