[object Object]

← back to Dw Settlement Audit

initial scaffold — DW settlement catalog audit, stage 1 text pre-filter

2fca7eb702fae6d6dffedb2a8712c2985f4f70ff · 2026-05-18 17:20:01 -0700 · SteveStudio2

Files touched

Diff

commit 2fca7eb702fae6d6dffedb2a8712c2985f4f70ff
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Mon May 18 17:20:01 2026 -0700

    initial scaffold — DW settlement catalog audit, stage 1 text pre-filter
---
 .gitignore      |  8 ++++++++
 scan-catalog.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9da8d21
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+candidates.jsonl
+progress.txt
+images/
diff --git a/scan-catalog.js b/scan-catalog.js
new file mode 100644
index 0000000..09e39e1
--- /dev/null
+++ b/scan-catalog.js
@@ -0,0 +1,59 @@
+#!/usr/bin/env node
+/* DW settlement audit — stage 1: cheap text pre-filter.
+ *
+ * Paginates all ~122k products and flags any whose title / tags / product_type
+ * mention banana-leaf / palm / tropical motifs — the settlement-protected
+ * pattern family. Output (candidates.jsonl) feeds stage 2 (vision verify).
+ *
+ *   SHOPIFY_ADMIN_TOKEN=... node scan-catalog.js
+ */
+'use strict';
+const fs = require('fs');
+const TOKEN = process.env.SHOPIFY_ADMIN_TOKEN;
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+if (!TOKEN) { console.error('SHOPIFY_ADMIN_TOKEN not set'); process.exit(1); }
+
+// Targeted — banana-leaf / palm / tropical only. Deliberately NOT "leaf/leaves/
+// foliage" (too broad — most florals have leaves). Candidates get vision-verified.
+const KW = /\b(banana|palm|palms|frond|fronds|tropical|tropics|monstera|jungle|palmette|martinique|plantain|bird of paradise|areca|rainforest|palm leaf|banana leaf)\b/i;
+
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+(async () => {
+  let url = `https://${SHOP}/admin/api/2024-01/products.json?limit=250&fields=id,handle,title,tags,product_type,status,published_at`;
+  const out = fs.createWriteStream('candidates.jsonl');
+  let scanned = 0, flagged = 0, page = 0;
+  while (url) {
+    let res;
+    for (let attempt = 0; attempt < 5; attempt++) {
+      res = await fetch(url, { headers: { 'X-Shopify-Access-Token': TOKEN } });
+      if (res.status === 429) { await sleep(2000); continue; }
+      break;
+    }
+    const data = await res.json();
+    for (const p of (data.products || [])) {
+      scanned++;
+      const hay = [p.title, p.tags, p.product_type].filter(Boolean).join(' | ');
+      const m = hay.match(KW);
+      if (m) {
+        flagged++;
+        out.write(JSON.stringify({
+          id: p.id, handle: p.handle, title: p.title, status: p.status,
+          published: !!p.published_at, match: m[0].toLowerCase(),
+          product_type: p.product_type, tags: p.tags,
+        }) + '\n');
+      }
+    }
+    page++;
+    fs.writeFileSync('progress.txt', `page ${page} | scanned ${scanned} | flagged ${flagged} | ${new Date().toISOString()}\n`);
+    const link = res.headers.get('link') || '';
+    url = null;
+    for (const part of link.split(',')) {
+      if (part.includes('rel="next"')) url = part.split(';')[0].trim().replace(/^<|>$/g, '');
+    }
+    await sleep(550); // ~2 req/s — within Shopify REST leaky bucket
+  }
+  out.end();
+  fs.writeFileSync('progress.txt', `DONE | scanned ${scanned} | flagged ${flagged} | ${new Date().toISOString()}\n`);
+  console.log(`scan complete — scanned ${scanned}, flagged ${flagged}`);
+})();

(oldest)  ·  back to Dw Settlement Audit  ·  settlement audit stage 2: Gemini vision verifier (resumable, e577f6f →