← 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
A verify-mfr-exact.mjsA verify-mfr-rest.mjsA verify-mfr.mjs
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 →