[object Object]

← back to Designerwallcoverings

TK-10238/10210: verify no_roll_variant_price exclusions — 0 false positives across all 28545 (fix 0b4b249 already recovered 17534 into feed)

666e9369fde2d553288a823c7d163ae3d01cc748 · 2026-08-05 07:49:09 -0700 · Steve Abrams

Files touched

Diff

commit 666e9369fde2d553288a823c7d163ae3d01cc748
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 5 07:49:09 2026 -0700

    TK-10238/10210: verify no_roll_variant_price exclusions — 0 false positives across all 28545 (fix 0b4b249 already recovered 17534 into feed)
---
 data/google-feed/no-roll-falsepos.json          |  1 +
 scripts/google-feed/verify-no-roll-falsepos.mjs | 65 +++++++++++++++++++++++++
 2 files changed, 66 insertions(+)

diff --git a/data/google-feed/no-roll-falsepos.json b/data/google-feed/no-roll-falsepos.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/data/google-feed/no-roll-falsepos.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/scripts/google-feed/verify-no-roll-falsepos.mjs b/scripts/google-feed/verify-no-roll-falsepos.mjs
new file mode 100644
index 0000000..34d10a9
--- /dev/null
+++ b/scripts/google-feed/verify-no-roll-falsepos.mjs
@@ -0,0 +1,65 @@
+#!/usr/bin/env node
+/**
+ * verify-no-roll-falsepos.mjs — READ-ONLY.
+ * For every product excluded with reason exactly ['no_roll_variant_price'],
+ * re-query LIVE Shopify variants and apply the SAME classifier the feed uses.
+ * A TRUE FALSE POSITIVE = a live product that, under the current fixed logic,
+ * actually HAS a non-sample variant with a finite price >= ROLL_PRICE_MIN.
+ * Emits: data/google-feed/no-roll-falsepos.json  (the recoverable set)
+ *        data/google-feed/no-roll-confirmed.json (genuinely sample-only, count only)
+ */
+import fs from 'node:fs';
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const VER = '2024-10';
+const envTxt = fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8');
+const TOKEN = (envTxt.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1]?.trim();
+const URL = `https://${SHOP}/admin/api/${VER}/graphql.json`;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+const ROLL_PRICE_MIN = 5;
+const looksSample = v => /(sample|memo|swatch)/i.test([v.title, v.sku].join(' ')) || /-sample$/i.test(v.sku || '');
+const isSample = v => looksSample(v) && !(parseFloat(v.price) > ROLL_PRICE_MIN);
+
+async function gql(query, variables) {
+  for (let a = 0; a < 8; a++) {
+    let res, j;
+    try {
+      res = await fetch(URL, { method: 'POST', headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }) });
+      j = await res.json();
+    } catch (e) { await sleep(1500 * (a + 1)); continue; }
+    if (j.errors) { if (JSON.stringify(j.errors).includes('THROTTLED')) { await sleep(2000 * (a + 1)); continue; } throw new Error(JSON.stringify(j.errors)); }
+    const t = j.extensions?.cost?.throttleStatus;
+    if (t && t.currentlyAvailable < 500) await sleep(1000);
+    return j.data;
+  }
+  throw new Error('exhausted retries');
+}
+
+const NODES_Q = `query($ids:[ID!]!){ nodes(ids:$ids){ ... on Product { id title vendor status variants(first:40){ nodes{ title sku price } } } } }`;
+
+const ids = fs.readFileSync('/tmp/all_nr_ids.txt', 'utf8').trim().split('\n').map(s => `gid://shopify/Product/${s.trim()}`);
+const falsePos = [];
+let confirmed = 0, deleted = 0, notActive = 0, scanned = 0;
+
+for (let i = 0; i < ids.length; i += 50) {
+  const batch = ids.slice(i, i + 50);
+  const data = await gql(NODES_Q, { ids: batch });
+  for (const p of data.nodes) {
+    scanned++;
+    if (!p) { deleted++; continue; }
+    if (p.status !== 'ACTIVE') { notActive++; continue; }
+    const vs = p.variants?.nodes || [];
+    const rolls = vs.filter(v => !isSample(v)).map(v => parseFloat(v.price)).filter(Number.isFinite);
+    const rollPrice = rolls.length ? Math.max(...rolls) : null;
+    if (rollPrice != null && rollPrice >= ROLL_PRICE_MIN) {
+      falsePos.push({ id: p.id.split('/').pop(), vendor: p.vendor, title: p.title, rollPrice,
+        variants: vs.map(v => ({ t: v.title, sku: v.sku, price: v.price, sample: isSample(v) })) });
+    } else {
+      confirmed++;
+    }
+  }
+  if (i % 2000 === 0) process.stderr.write(`  scanned ${scanned} | falsePos ${falsePos.length} | confirmed ${confirmed} | deleted ${deleted} | notActive ${notActive}\n`);
+}
+
+fs.writeFileSync('data/google-feed/no-roll-falsepos.json', JSON.stringify(falsePos, null, 2));
+console.log(JSON.stringify({ scanned, falsePos: falsePos.length, confirmed_sample_only: confirmed, deleted, notActive }, null, 2));

← 6b7ca7c auto-save: 2026-08-05T07:40:04 (2 files) — data/orphan-clean  ·  back to Designerwallcoverings  ·  TK-10014: live DWKK price vs authoritative MAP comparator — 3f5ba6e →