← 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
A .gitignoreA scan-catalog.js
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 →