← back to Yolo Agent
snapshot: 93 file(s) changed, +45 new, ~48 modified
d0902e977dd110de2fba3d1d64367886026c4299 · 2026-05-13 08:58:44 -0700 · Steve
Files touched
M package-lock.jsonM package.jsonM server.jsM tasks/done/00_create-79-missing-collections.mdA tasks/done/00_create-79-missing-collections.md.pre-scrub-2026-05-07.bakM tasks/done/00_crop-grs-images-to-square.mdA tasks/done/00_crop-grs-images-to-square.md.pre-scrub-2026-05-07.bakM tasks/done/00_fix-fabrics-remove-trim-new-arrivals.mdA tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md.pre-scrub-2026-05-07.bakM tasks/done/00_full-monte-phase3-all.mdA tasks/done/00_full-monte-phase3-all.md.pre-scrub-2026-05-07.bakM tasks/done/00_hex-color-gemini-blitz.mdA tasks/done/00_hex-color-gemini-blitz.md.pre-scrub-2026-05-07.bakM tasks/done/00a_maya-room-settings.mdA tasks/done/00a_maya-room-settings.md.pre-scrub-2026-05-07.bakM tasks/done/01_ai-enrich-romo.mdA tasks/done/01_ai-enrich-romo.md.pre-scrub-2026-05-07.bakM tasks/done/01_cleanup-37-empty-collections.mdA tasks/done/01_cleanup-37-empty-collections.md.pre-scrub-2026-05-07.bakM tasks/done/01_enrich-prl-direct.mdA tasks/done/01_enrich-prl-direct.md.pre-scrub-2026-05-07.bakM tasks/done/01_interior-design-tags-all-vendors.mdA tasks/done/01_interior-design-tags-all-vendors.md.pre-scrub-2026-05-07.bakM tasks/done/01_newmor-full-monty-test.mdA tasks/done/01_newmor-full-monty-test.md.pre-scrub-2026-05-07.bakM tasks/done/02_activate-versace-drafts.mdA tasks/done/02_activate-versace-drafts.md.pre-scrub-2026-05-07.bakM tasks/done/02_ai-enrich-villa-nova.mdA tasks/done/02_ai-enrich-villa-nova.md.pre-scrub-2026-05-07.bakM tasks/done/02_build-as-creation-agent.mdA tasks/done/02_build-as-creation-agent.md.pre-scrub-2026-05-07.bakM tasks/done/02_enrich-remaining-gaps.mdA tasks/done/02_enrich-remaining-gaps.md.pre-scrub-2026-05-07.bakM tasks/done/02_full-store-collection-audit.mdA tasks/done/02_full-store-collection-audit.md.pre-scrub-2026-05-07.bakM tasks/done/02_newmor-bulk-phase3-ai.mdA tasks/done/02_newmor-bulk-phase3-ai.md.pre-scrub-2026-05-07.bakM tasks/done/02_update-vcc-spec-standards.mdA tasks/done/02_update-vcc-spec-standards.md.pre-scrub-2026-05-07.bakM tasks/done/03_03_gls-tag-cleanup.mdA tasks/done/03_03_gls-tag-cleanup.md.pre-scrub-2026-05-07.bakM tasks/done/03_ai-enrich-mark-alexander.mdA tasks/done/03_ai-enrich-mark-alexander.md.pre-scrub-2026-05-07.bakM tasks/done/03_fix-spec-gaps.mdA tasks/done/03_fix-spec-gaps.md.pre-scrub-2026-05-07.bakM tasks/done/03_orphan-product-audit.mdA tasks/done/03_orphan-product-audit.md.pre-scrub-2026-05-07.bakM tasks/done/03_p2-06-interior-design-tagger.mdA tasks/done/03_p2-06-interior-design-tagger.md.pre-scrub-2026-05-07.bakM tasks/done/04_ai-enrich-arte.mdA tasks/done/04_ai-enrich-arte.md.pre-scrub-2026-05-07.bakM tasks/done/04_china-seas-hex-colors.mdA tasks/done/04_china-seas-hex-colors.md.pre-scrub-2026-05-07.bakM tasks/done/04_gemini-texture-classify-17k.mdA tasks/done/04_gemini-texture-classify-17k.md.pre-scrub-2026-05-07.bakM tasks/done/04_recrawl-low-spec-vendors.mdA tasks/done/04_recrawl-low-spec-vendors.md.pre-scrub-2026-05-07.bakM tasks/done/04_run-all-vendor-crawls-high-to-low.mdA tasks/done/04_run-all-vendor-crawls-high-to-low.md.pre-scrub-2026-05-07.bakM tasks/done/05_ai-enrich-remaining.mdA tasks/done/05_ai-enrich-remaining.md.pre-scrub-2026-05-07.bakM tasks/done/05_china-seas-interior-tagger.mdA tasks/done/05_china-seas-interior-tagger.md.pre-scrub-2026-05-07.bakM tasks/done/05_crawl-report-for-steve.mdA tasks/done/05_crawl-report-for-steve.md.pre-scrub-2026-05-07.bakM tasks/done/05_final-audit-report.mdA tasks/done/05_final-audit-report.md.pre-scrub-2026-05-07.bakM tasks/done/06_hollywood-gemini-hex.mdA tasks/done/06_hollywood-gemini-hex.md.pre-scrub-2026-05-07.bakM tasks/done/06_hollywood-hex-extraction.mdA tasks/done/06_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bakM tasks/done/08_phase3-enrichment-run.mdA tasks/done/08_phase3-enrichment-run.md.pre-scrub-2026-05-07.bakM tasks/done/08_slack-notify-completion.mdA tasks/done/08_slack-notify-completion.md.pre-scrub-2026-05-07.bakM tasks/done/09_ai-enrich-zoffany.mdA tasks/done/09_ai-enrich-zoffany.md.pre-scrub-2026-05-07.bakM tasks/done/10_catalog-spec-fill-audit.mdA tasks/done/10_catalog-spec-fill-audit.md.pre-scrub-2026-05-07.bakM tasks/done/11_interior-design-tagger.mdA tasks/done/11_interior-design-tagger.md.pre-scrub-2026-05-07.bakM tasks/done/12_weekly-relink-orphans.mdA tasks/done/12_weekly-relink-orphans.md.pre-scrub-2026-05-07.bakM tasks/done/13_hollywood-hex-extraction.mdA tasks/done/13_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bakM tasks/done/19_zero-repeat-textures.mdA tasks/done/19_zero-repeat-textures.md.pre-scrub-2026-05-07.bakM tasks/done/25_zero-repeat-textures-r2.mdA tasks/done/25_zero-repeat-textures-r2.md.pre-scrub-2026-05-07.bakM tasks/done/29_texture-classify-remaining-r3.mdA tasks/done/29_texture-classify-remaining-r3.md.pre-scrub-2026-05-07.bakM tasks/done/AQ_hollywood-imageclean-and-spin.mdA tasks/done/AQ_hollywood-imageclean-and-spin.md.pre-scrub-2026-05-07.bak
Diff
commit d0902e977dd110de2fba3d1d64367886026c4299
Author: Steve <steve@designerwallcoverings.com>
Date: Wed May 13 08:58:44 2026 -0700
snapshot: 93 file(s) changed, +45 new, ~48 modified
---
package-lock.json | 12 ++-
package.json | 3 +-
server.js | 3 +
tasks/done/00_create-79-missing-collections.md | 4 +-
...missing-collections.md.pre-scrub-2026-05-07.bak | 88 +++++++++++++++++
tasks/done/00_crop-grs-images-to-square.md | 2 +-
...rs-images-to-square.md.pre-scrub-2026-05-07.bak | 75 +++++++++++++++
.../00_fix-fabrics-remove-trim-new-arrivals.md | 2 +-
...e-trim-new-arrivals.md.pre-scrub-2026-05-07.bak | 54 +++++++++++
tasks/done/00_full-monte-phase3-all.md | 2 +-
...ll-monte-phase3-all.md.pre-scrub-2026-05-07.bak | 52 ++++++++++
tasks/done/00_hex-color-gemini-blitz.md | 4 +-
...-color-gemini-blitz.md.pre-scrub-2026-05-07.bak | 29 ++++++
tasks/done/00a_maya-room-settings.md | 2 +-
..._maya-room-settings.md.pre-scrub-2026-05-07.bak | 43 +++++++++
tasks/done/01_ai-enrich-romo.md | 2 +-
.../01_ai-enrich-romo.md.pre-scrub-2026-05-07.bak | 19 ++++
tasks/done/01_cleanup-37-empty-collections.md | 4 +-
...7-empty-collections.md.pre-scrub-2026-05-07.bak | 83 ++++++++++++++++
tasks/done/01_enrich-prl-direct.md | 2 +-
...1_enrich-prl-direct.md.pre-scrub-2026-05-07.bak | 14 +++
tasks/done/01_interior-design-tags-all-vendors.md | 2 +-
...gn-tags-all-vendors.md.pre-scrub-2026-05-07.bak | 87 +++++++++++++++++
tasks/done/01_newmor-full-monty-test.md | 2 +-
...mor-full-monty-test.md.pre-scrub-2026-05-07.bak | 63 +++++++++++++
tasks/done/02_activate-versace-drafts.md | 2 +-
...vate-versace-drafts.md.pre-scrub-2026-05-07.bak | 59 ++++++++++++
tasks/done/02_ai-enrich-villa-nova.md | 2 +-
...i-enrich-villa-nova.md.pre-scrub-2026-05-07.bak | 9 ++
tasks/done/02_build-as-creation-agent.md | 2 +-
...d-as-creation-agent.md.pre-scrub-2026-05-07.bak | 51 ++++++++++
tasks/done/02_enrich-remaining-gaps.md | 2 +-
...rich-remaining-gaps.md.pre-scrub-2026-05-07.bak | 9 ++
tasks/done/02_full-store-collection-audit.md | 4 +-
...re-collection-audit.md.pre-scrub-2026-05-07.bak | 76 +++++++++++++++
tasks/done/02_newmor-bulk-phase3-ai.md | 2 +-
...wmor-bulk-phase3-ai.md.pre-scrub-2026-05-07.bak | 89 +++++++++++++++++
tasks/done/02_update-vcc-spec-standards.md | 2 +-
...-vcc-spec-standards.md.pre-scrub-2026-05-07.bak | 64 +++++++++++++
tasks/done/03_03_gls-tag-cleanup.md | 2 +-
..._03_gls-tag-cleanup.md.pre-scrub-2026-05-07.bak | 9 ++
tasks/done/03_ai-enrich-mark-alexander.md | 2 +-
...rich-mark-alexander.md.pre-scrub-2026-05-07.bak | 9 ++
tasks/done/03_fix-spec-gaps.md | 2 +-
.../03_fix-spec-gaps.md.pre-scrub-2026-05-07.bak | 67 +++++++++++++
tasks/done/03_orphan-product-audit.md | 4 +-
...rphan-product-audit.md.pre-scrub-2026-05-07.bak | 105 +++++++++++++++++++++
tasks/done/03_p2-06-interior-design-tagger.md | 2 +-
...erior-design-tagger.md.pre-scrub-2026-05-07.bak | 33 +++++++
tasks/done/04_ai-enrich-arte.md | 2 +-
.../04_ai-enrich-arte.md.pre-scrub-2026-05-07.bak | 9 ++
tasks/done/04_china-seas-hex-colors.md | 2 +-
...ina-seas-hex-colors.md.pre-scrub-2026-05-07.bak | 32 +++++++
tasks/done/04_gemini-texture-classify-17k.md | 2 +-
...exture-classify-17k.md.pre-scrub-2026-05-07.bak | 46 +++++++++
tasks/done/04_recrawl-low-spec-vendors.md | 2 +-
...wl-low-spec-vendors.md.pre-scrub-2026-05-07.bak | 46 +++++++++
tasks/done/04_run-all-vendor-crawls-high-to-low.md | 2 +-
...-crawls-high-to-low.md.pre-scrub-2026-05-07.bak | 86 +++++++++++++++++
tasks/done/05_ai-enrich-remaining.md | 2 +-
...ai-enrich-remaining.md.pre-scrub-2026-05-07.bak | 10 ++
tasks/done/05_china-seas-interior-tagger.md | 2 +-
...eas-interior-tagger.md.pre-scrub-2026-05-07.bak | 27 ++++++
tasks/done/05_crawl-report-for-steve.md | 2 +-
...wl-report-for-steve.md.pre-scrub-2026-05-07.bak | 43 +++++++++
tasks/done/05_final-audit-report.md | 2 +-
..._final-audit-report.md.pre-scrub-2026-05-07.bak | 38 ++++++++
tasks/done/06_hollywood-gemini-hex.md | 2 +-
...ollywood-gemini-hex.md.pre-scrub-2026-05-07.bak | 14 +++
tasks/done/06_hollywood-hex-extraction.md | 2 +-
...wood-hex-extraction.md.pre-scrub-2026-05-07.bak | 21 +++++
tasks/done/08_phase3-enrichment-run.md | 2 +-
...ase3-enrichment-run.md.pre-scrub-2026-05-07.bak | 13 +++
tasks/done/08_slack-notify-completion.md | 2 +-
...k-notify-completion.md.pre-scrub-2026-05-07.bak | 11 +++
tasks/done/09_ai-enrich-zoffany.md | 2 +-
...9_ai-enrich-zoffany.md.pre-scrub-2026-05-07.bak | 12 +++
tasks/done/10_catalog-spec-fill-audit.md | 2 +-
...log-spec-fill-audit.md.pre-scrub-2026-05-07.bak | 12 +++
tasks/done/11_interior-design-tagger.md | 2 +-
...erior-design-tagger.md.pre-scrub-2026-05-07.bak | 20 ++++
tasks/done/12_weekly-relink-orphans.md | 2 +-
...ekly-relink-orphans.md.pre-scrub-2026-05-07.bak | 46 +++++++++
tasks/done/13_hollywood-hex-extraction.md | 2 +-
...wood-hex-extraction.md.pre-scrub-2026-05-07.bak | 17 ++++
tasks/done/19_zero-repeat-textures.md | 4 +-
...ero-repeat-textures.md.pre-scrub-2026-05-07.bak | 50 ++++++++++
tasks/done/25_zero-repeat-textures-r2.md | 4 +-
...-repeat-textures-r2.md.pre-scrub-2026-05-07.bak | 50 ++++++++++
tasks/done/29_texture-classify-remaining-r3.md | 2 +-
...assify-remaining-r3.md.pre-scrub-2026-05-07.bak | 61 ++++++++++++
tasks/done/AQ_hollywood-imageclean-and-spin.md | 4 +-
...imageclean-and-spin.md.pre-scrub-2026-05-07.bak | 59 ++++++++++++
93 files changed, 1979 insertions(+), 55 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 4b43973..bf49e39 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,8 @@
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2",
- "express-basic-auth": "^1.2.1"
+ "express-basic-auth": "^1.2.1",
+ "helmet": "^8.1.0"
}
},
"node_modules/accepts": {
@@ -422,6 +423,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/helmet": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz",
+ "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
diff --git a/package.json b/package.json
index 4b87673..8d3ce18 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
},
"dependencies": {
"express": "^4.18.2",
- "express-basic-auth": "^1.2.1"
+ "express-basic-auth": "^1.2.1",
+ "helmet": "^8.1.0"
}
}
diff --git a/server.js b/server.js
index 852aba3..9f567c0 100644
--- a/server.js
+++ b/server.js
@@ -8,6 +8,7 @@
// ============================================================================
const express = require('express');
+const helmet = require('helmet');
const { execSync, spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
@@ -447,6 +448,8 @@ function sleep(ms) {
// ── Express Server ─────────────────────────────────────────────────────────
const app = express();
+// Security headers via helmet (added 2026-05-04 overnight YOLO loop)
+app.use(helmet({ contentSecurityPolicy: false }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
diff --git a/tasks/done/00_create-79-missing-collections.md b/tasks/done/00_create-79-missing-collections.md
index 98c7e69..b6a0c8f 100644
--- a/tasks/done/00_create-79-missing-collections.md
+++ b/tasks/done/00_create-79-missing-collections.md
@@ -73,7 +73,7 @@ Format:
```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"✅ *Collections Created*\n• Created: X new collections\n• Skipped: Y (internal/private)\n• Failed: Z\n• Report: /root/DW-Agents/yolo-agent/logs/collections-created-report.json"}' \
- "https://hooks.slack.com/services/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"
+ "${SLACK_WEBHOOK_URL}"
```
## DO NOT
@@ -85,4 +85,4 @@ curl -X POST -H 'Content-type: application/json' \
## Credentials
- Shopify API: <redacted:SHOPIFY_ORDERS_TOKEN>
- Store: designer-laboratory-sandbox.myshopify.com
-- Slack: https://hooks.slack.com/services/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
+- Slack: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/00_create-79-missing-collections.md.pre-scrub-2026-05-07.bak b/tasks/done/00_create-79-missing-collections.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..98c7e69
--- /dev/null
+++ b/tasks/done/00_create-79-missing-collections.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,88 @@
+# Create Smart Collections for 79 Vendors Missing Dedicated Collection Pages
+
+## Ralph Clarifying Questions (Self-Check Before Executing)
+
+1. **Is the collection audit report still current?**
+ Read: `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json`
+ Verify it has a "missing" array with vendor names and product counts.
+
+2. **Are any of these vendors private label or internal?**
+ Skip these — do NOT create public collections for:
+ - "Designer Laboratory" (internal)
+ - "Steve Abrams Studios" (internal)
+ - "DW Home" (internal brand)
+ - "Sancar" (distributor — NEVER show distributor names publicly)
+ - Any vendor in the private_label system
+
+3. **What's the smart collection rule pattern?**
+ Use: `vendor equals "{Vendor Name}"` — this auto-populates with all matching products.
+
+## What To Do
+
+### Step 1: Read the audit report
+```bash
+cat /root/DW-Agents/yolo-agent/logs/collection-audit-report.json
+```
+Extract the "missing" array.
+
+### Step 2: For each vendor (skip internal/private label), create a smart collection:
+```bash
+curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/smart_collections.json" \
+ -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "smart_collection": {
+ "title": "{Vendor Name} Wallcoverings",
+ "rules": [{"column": "vendor", "relation": "equals", "condition": "{Vendor Name}"}],
+ "published": true,
+ "sort_order": "best-selling"
+ }
+ }'
+```
+
+**Title rules:**
+- If vendor sells wallcoverings: "{Vendor} Wallcoverings" (e.g., "Versa Designed Surfaces Wallcoverings")
+- If vendor sells trim: "{Vendor} Trim" (e.g., "Schumacher Trim")
+- If vendor sells fabric: "{Vendor} Fabrics"
+- If unclear: just "{Vendor}" with no suffix
+
+### Step 3: Rate limiting
+- Shopify API: max 2 requests/second
+- Add 600ms delay between each collection creation
+- Log each creation: vendor name, collection ID, product count
+
+### Step 4: Verify
+After creating all collections, verify each returns HTTP 200:
+```bash
+curl -s -o /dev/null -w "%{http_code}" "https://www.designerwallcoverings.com/collections/{handle}"
+```
+
+### Step 5: Save results
+Save to: `/root/DW-Agents/yolo-agent/logs/collections-created-report.json`
+Format:
+```json
+{
+ "timestamp": "...",
+ "created": [{"vendor": "X", "collectionId": 123, "handle": "x", "products": 50}],
+ "skipped": [{"vendor": "Y", "reason": "internal/private label"}],
+ "failed": [{"vendor": "Z", "error": "..."}]
+}
+```
+
+### Step 6: Slack notification
+```bash
+curl -X POST -H 'Content-type: application/json' \
+ --data '{"text":"✅ *Collections Created*\n• Created: X new collections\n• Skipped: Y (internal/private)\n• Failed: Z\n• Report: /root/DW-Agents/yolo-agent/logs/collections-created-report.json"}' \
+ "https://hooks.slack.com/services/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"
+```
+
+## DO NOT
+- Create collections for Sancar, Designer Laboratory, Steve Abrams Studios, DW Home
+- Create collections for vendors with < 3 active products (not worth an SEO page)
+- Delete any existing collections
+- Modify any products
+
+## Credentials
+- Shopify API: <redacted:SHOPIFY_ORDERS_TOKEN>
+- Store: designer-laboratory-sandbox.myshopify.com
+- Slack: https://hooks.slack.com/services/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
diff --git a/tasks/done/00_crop-grs-images-to-square.md b/tasks/done/00_crop-grs-images-to-square.md
index 549dc52..37f1897 100644
--- a/tasks/done/00_crop-grs-images-to-square.md
+++ b/tasks/done/00_crop-grs-images-to-square.md
@@ -67,7 +67,7 @@ After processing, spot-check 5 products to verify:
```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"✂️ *GRS Image Crop Complete*\n• Products processed: X\n• Images cropped: Y\n• Already square: Z"}' \
- "https://hooks.slack.com/services/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"
+ "${SLACK_WEBHOOK_URL}"
```
## Credentials
diff --git a/tasks/done/00_crop-grs-images-to-square.md.pre-scrub-2026-05-07.bak b/tasks/done/00_crop-grs-images-to-square.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..549dc52
--- /dev/null
+++ b/tasks/done/00_crop-grs-images-to-square.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,75 @@
+# Crop GRS- Product Images to Largest Square
+
+## DO NOT ask clarifying questions — proceed autonomously.
+
+## What
+GRS- products (grasscloth/Designer Wallcoverings vendor) have manufacturer logos in a strip at the top or bottom of their images. Crop ALL GRS- product images to the largest possible square to remove the logo strip.
+
+## How
+
+### Step 1: Find all GRS products on Shopify
+Search for products with "GRS" in handle, tags, or SKU:
+```bash
+# Use GraphQL to find all products with GRS in handle
+curl -s -X POST "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json" \
+ -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ADMIN_TOKEN>" \
+ -H "Content-Type: application/json" \
+ -d '{"query":"{ products(first: 250, query: \"tag:grs OR sku:GRS\") { edges { node { id title handle images(first: 5) { edges { node { id src width height } } } } } } }"}'
+```
+
+Also search by vendor "Designer Wallcoverings" with GRS in handle:
+```
+GET /products.json?vendor=Designer+Wallcoverings&limit=250
+```
+Filter for handles containing "grs".
+
+### Step 2: For each product image, crop to square
+1. Download the image
+2. Get dimensions (W x H)
+3. If already square, skip
+4. Crop to the largest square:
+ - If W < H (portrait): crop center vertically → (0, (H-W)/2, W, (H-W)/2 + W)
+ - If W > H (landscape): crop center horizontally → ((W-H)/2, 0, (W-H)/2 + H, H)
+5. Use ImageMagick or Python Pillow:
+```bash
+# ImageMagick center crop to square
+convert input.jpg -gravity center -crop WxW+0+0 +repage output.jpg
+# Where W = min(width, height)
+```
+
+### Step 3: Upload cropped image back to Shopify
+```bash
+# Delete old image
+DELETE /admin/api/2024-01/products/{product_id}/images/{image_id}.json
+
+# Upload new cropped image
+POST /admin/api/2024-01/products/{product_id}/images.json
+{"image": {"attachment": "BASE64_DATA", "filename": "product.jpg"}}
+```
+
+### Step 4: Rate limiting
+- 500ms between API calls
+- Process in batches of 50
+- Write progress to /tmp/grs-crop-progress.log
+
+## DO NOT
+- Crop images that are already square (aspect ratio ~1.0)
+- Delete the original without uploading the replacement first
+- Process non-GRS products
+
+## Verification
+After processing, spot-check 5 products to verify:
+- Image is square
+- No logo visible
+- Image quality preserved
+
+## Slack Notification
+```bash
+curl -X POST -H 'Content-type: application/json' \
+ --data '{"text":"✂️ *GRS Image Crop Complete*\n• Products processed: X\n• Images cropped: Y\n• Already square: Z"}' \
+ "https://hooks.slack.com/services/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"
+```
+
+## Credentials
+- Shopify: <redacted:SHOPIFY_ADMIN_TOKEN>
+- Store: designer-laboratory-sandbox.myshopify.com
diff --git a/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md b/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md
index 4adbe4c..653680f 100644
--- a/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md
+++ b/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md
@@ -51,4 +51,4 @@ After all fixes:
## Credentials
- Shopify: <redacted:SHOPIFY_ADMIN_TOKEN>
- Store: designer-laboratory-sandbox.myshopify.com
-- Slack: https://hooks.slack.com/services/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
+- Slack: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md.pre-scrub-2026-05-07.bak b/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..4adbe4c
--- /dev/null
+++ b/tasks/done/00_fix-fabrics-remove-trim-new-arrivals.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,54 @@
+# Fix Fabric Titles + Remove Trim from New Arrivals
+
+## DO NOT ask clarifying questions — proceed autonomously.
+
+## Task 1: Remove ALL Trim products from New Arrivals collection
+
+New Arrivals collection ID: 167327760435
+
+1. Get all products in New Arrivals with product_type "Trim" using Shopify REST API pagination
+2. For each Trim product, remove it from the New Arrivals collection using the Collect API:
+ - GET /admin/api/2024-01/collects.json?collection_id=167327760435&product_id={id}
+ - DELETE /admin/api/2024-01/collects/{collect_id}.json
+3. If it's a smart collection, the rule may auto-include them — check the collection rules:
+ - GET /admin/api/2024-01/smart_collections/167327760435.json
+ - If the rules don't exclude Trim, add a rule: product_type NOT_EQUALS Trim
+4. Rate limit: 500ms between API calls
+5. Report: how many trim products removed
+
+## Task 2: Fix 80+ Fabric titles with "Fabrics" in the name
+
+1. Search: GET /products.json?product_type=Fabric&limit=250 (paginate)
+2. Filter for titles containing "Fabrics" (the word, not "Fabric")
+3. For each, fix title: replace "Fabrics" with "Fabric"
+ - "Akio Fabrics – Black | Thibaut" → "Akio - Black Fabric | Thibaut"
+ - "Ralph Lauren Fabrics" in title → keep "Ralph Lauren" as vendor, remove "Fabrics" from pattern name
+4. PUT /products/{id}.json with corrected title
+5. Rate limit: 500ms
+
+## Task 3: Fix 50 Fabric products with "Wallcovering" in title
+
+These are products with product_type "Fabric" but "Wallcovering" in the title — wrong label.
+1. Check if they're actually fabrics or wallcoverings:
+ - Wolf Gordon: could be BOTH fabric and wallcovering — check tags
+ - Schumacher: check tags for "Fabric" tag
+ - If tagged Fabric and product_type is Fabric → replace "Wallcovering" with "Fabric" in title
+ - If it's actually a wallcovering miscategorized → change product_type to "Wallcovering"
+2. The Adriano products (Phillipe Romano) with "Wallcovering" in title but product_type "Fabric" are likely wallcoverings — change product_type to "Wallcovering"
+
+## Title Format Rules
+- Pattern: `{PatternName} - {Color} {ProductType} | {Vendor}`
+- ProductType for fabrics: "Fabric" (not "Fabrics")
+- NEVER "Wallcovering" in a Fabric product title
+- NEVER "Fabrics" (plural) — always "Fabric" (singular)
+
+## Verification
+After all fixes:
+- Count products in New Arrivals with product_type Trim (should be 0)
+- Count products store-wide with "Fabrics" in title and product_type Fabric (should be 0)
+- Send Slack notification with results
+
+## Credentials
+- Shopify: <redacted:SHOPIFY_ADMIN_TOKEN>
+- Store: designer-laboratory-sandbox.myshopify.com
+- Slack: https://hooks.slack.com/services/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
diff --git a/tasks/done/00_full-monte-phase3-all.md b/tasks/done/00_full-monte-phase3-all.md
index d1d5be9..f426e8d 100644
--- a/tasks/done/00_full-monte-phase3-all.md
+++ b/tasks/done/00_full-monte-phase3-all.md
@@ -43,7 +43,7 @@ node full-monte-batch.js --vendor maya_romanoff --phase 3 --limit 100
```
## Rules
-- Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Gemini key: ${GOOGLE_API_KEY}
- Rate limit: 600ms between calls
- Dedup via enrichment_tracking — zero duplicate calls
- Image type detection included (scan_swatch, photo_full, etc.)
diff --git a/tasks/done/00_full-monte-phase3-all.md.pre-scrub-2026-05-07.bak b/tasks/done/00_full-monte-phase3-all.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..d1d5be9
--- /dev/null
+++ b/tasks/done/00_full-monte-phase3-all.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,52 @@
+# Full Monte Phase 3 — AI Enrichment ALL Vendors
+
+Run Phase 3 (Gemini AI vision) on ALL vendors in priority order. Budget: $227 max.
+Uses enrichment_tracking dedup — will NOT re-process already enriched products.
+
+## Execution
+```bash
+cd /root/DW-Agents/full-monte
+
+# Tier 1 first (already on Shopify, high value)
+node full-monte-batch.js --vendor kravet --phase 3 --limit 10000
+node full-monte-batch.js --vendor thibaut --phase 3 --limit 6000
+node full-monte-batch.js --vendor schumacher --phase 3 --limit 5200
+node full-monte-batch.js --vendor phillip_jeffries --phase 3 --limit 4400
+node full-monte-batch.js --vendor cole_son --phase 3 --limit 1000
+
+# Tier 2 (large catalogs)
+node full-monte-batch.js --vendor brewster --phase 3 --limit 9000
+node full-monte-batch.js --vendor york --phase 3 --limit 5500
+node full-monte-batch.js --vendor arte --phase 3 --limit 2000
+node full-monte-batch.js --vendor elitis --phase 3 --limit 1200
+node full-monte-batch.js --vendor romo --phase 3 --limit 2600
+node full-monte-batch.js --vendor koroseal --phase 3 --limit 2600
+
+# Anna French (specifically requested)
+node full-monte-batch.js --vendor anna_french --phase 3 --limit 600
+
+# Tier 3 — remaining large vendors
+node full-monte-batch.js --vendor marburg --phase 3 --limit 9300
+node full-monte-batch.js --vendor as_creation --phase 3 --limit 6200
+node full-monte-batch.js --vendor designtex --phase 3 --limit 2900
+node full-monte-batch.js --vendor holly_hunt --phase 3 --limit 2100
+node full-monte-batch.js --vendor maharam --phase 3 --limit 1600
+node full-monte-batch.js --vendor fabricut --phase 3 --limit 1400
+node full-monte-batch.js --vendor ralph_lauren --phase 3 --limit 3100
+node full-monte-batch.js --vendor graham_brown --phase 3 --limit 3200
+node full-monte-batch.js --vendor andrew_martin --phase 3 --limit 1000
+node full-monte-batch.js --vendor harlequin --phase 3 --limit 800
+node full-monte-batch.js --vendor carlisle --phase 3 --limit 800
+node full-monte-batch.js --vendor sandberg --phase 3 --limit 900
+node full-monte-batch.js --vendor wolf_gordon --phase 3 --limit 300
+node full-monte-batch.js --vendor maya_romanoff --phase 3 --limit 100
+```
+
+## Rules
+- Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Rate limit: 600ms between calls
+- Dedup via enrichment_tracking — zero duplicate calls
+- Image type detection included (scan_swatch, photo_full, etc.)
+- Run Tier 1 first, then 2, then 3
+- If budget exceeded ($227), stop and report
+- Log all progress to /root/DW-Agents/logs/full-monte-phase3.log
diff --git a/tasks/done/00_hex-color-gemini-blitz.md b/tasks/done/00_hex-color-gemini-blitz.md
index 91927a7..a565036 100644
--- a/tasks/done/00_hex-color-gemini-blitz.md
+++ b/tasks/done/00_hex-color-gemini-blitz.md
@@ -15,7 +15,7 @@ Many products have images but no color_hex. Use Gemini vision to analyze product
### Approach:
1. For each vendor, find products with image_url but no color_hex
2. Use Gemini 2.0 Flash vision API to analyze each image:
- - Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct8QejMo`
+ - Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- Prompt: "What is the dominant background color of this wallpaper/wallcovering? Reply with ONLY a hex code like #A5B2C3"
- Pass the image_url as an image part
3. Update color_hex in the catalog table
@@ -24,6 +24,6 @@ Many products have images but no color_hex. Use Gemini vision to analyze product
### CRITICAL:
- Use Gemini for ALL image analysis (NEVER Claude vision)
-- API key for analysis: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct8QejMo
+- API key for analysis: ${GOOGLE_API_KEY}
- Do NOT push to Shopify
- If Gemini returns a color name instead of hex, map it using color_hex_map table
diff --git a/tasks/done/00_hex-color-gemini-blitz.md.pre-scrub-2026-05-07.bak b/tasks/done/00_hex-color-gemini-blitz.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..91927a7
--- /dev/null
+++ b/tasks/done/00_hex-color-gemini-blitz.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,29 @@
+## Hex Color Gemini Blitz — Fill Missing color_hex via AI Vision
+
+Many products have images but no color_hex. Use Gemini vision to analyze product images and extract the dominant color as a hex code.
+
+### Priority vendors (most missing hex):
+- Marburg: 7,652 missing (have images)
+- Newwall: 7,602 missing
+- Cowtan & Tout: 5,011 missing (specs only vendor but color is a spec)
+- Schumacher: 3,894 missing
+- Scalamandre: 4,055 missing
+- PJ: 4,294 missing
+- Rebel Walls: 1,934 missing
+- Milton King: 2,464 missing
+
+### Approach:
+1. For each vendor, find products with image_url but no color_hex
+2. Use Gemini 2.0 Flash vision API to analyze each image:
+ - Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct8QejMo`
+ - Prompt: "What is the dominant background color of this wallpaper/wallcovering? Reply with ONLY a hex code like #A5B2C3"
+ - Pass the image_url as an image part
+3. Update color_hex in the catalog table
+4. Rate limit: 10 req/sec (Gemini allows 60 RPM on free tier)
+5. Start with smallest vendors first for quick wins
+
+### CRITICAL:
+- Use Gemini for ALL image analysis (NEVER Claude vision)
+- API key for analysis: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct8QejMo
+- Do NOT push to Shopify
+- If Gemini returns a color name instead of hex, map it using color_hex_map table
diff --git a/tasks/done/00a_maya-room-settings.md b/tasks/done/00a_maya-room-settings.md
index 4f73358..f003d2f 100644
--- a/tasks/done/00a_maya-room-settings.md
+++ b/tasks/done/00a_maya-room-settings.md
@@ -15,7 +15,7 @@ For EACH new product (WHERE dw_sku LIKE 'DWMR-8%' AND room_setting_images IS NUL
API: Gemini 2.5 Flash Image
- Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent
-- Key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Key: ${GOOGLE_API_KEY}
- Config: generationConfig: { responseModalities: ['TEXT', 'IMAGE'] }
- Rate limit: 600ms between calls
diff --git a/tasks/done/00a_maya-room-settings.md.pre-scrub-2026-05-07.bak b/tasks/done/00a_maya-room-settings.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..4f73358
--- /dev/null
+++ b/tasks/done/00a_maya-room-settings.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,43 @@
+# Maya Romanoff — Room Settings + Spin Viewers for 224 New Products
+
+## Context
+224 new Maya Romanoff products were just imported into maya_catalog (dw_sku LIKE 'DWMR-8%'). They need room setting images and spin viewer assets generated before Shopify push.
+
+## Task 1: Room Settings (Gemini Image Generation)
+Generate room setting images for the new Maya Romanoff products using Gemini 2.5 Flash Image generation.
+
+For EACH new product (WHERE dw_sku LIKE 'DWMR-8%' AND room_setting_images IS NULL):
+1. Fetch the product image from image_url
+2. Send to Gemini with prompt: "Create a photorealistic interior room visualization showing this wallcovering installed on the main wall. Show a luxurious {room_type} with complementary furniture and decor. The wallcovering should be the focal point covering the full back wall. Photorealistic, professional interior design photography, warm natural lighting."
+3. Generate 3 room types per product: living room, dining room, hotel lobby
+4. Save generated images to /root/DW-Agents/room-settings/maya-romanoff/
+5. Upload to Shopify CDN and store URLs in maya_catalog.room_setting_images as JSON array
+
+API: Gemini 2.5 Flash Image
+- Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent
+- Key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Config: generationConfig: { responseModalities: ['TEXT', 'IMAGE'] }
+- Rate limit: 600ms between calls
+
+Since 224 products × 3 rooms = 672 images is expensive, prioritize:
+- First batch: 50 most popular collections (Ajiro, Beadazzled, Craze, Island Weaves, Fleece Veil)
+- Skip products where image_url returns non-200
+
+## Task 2: Spin Viewers
+For each new product with an image:
+1. Generate 8 rotation frames using CSS transform perspective
+2. Create an HTML spin viewer at /root/DW-Agents/spin-viewers/maya-romanoff/{dw_sku}.html
+3. Each viewer shows the wallcovering pattern on a 3D-perspective wall that rotates
+
+Use the existing spin viewer template at /root/DW-Agents/vendor-command-center/spin-viewer-template.html if it exists, otherwise create a simple CSS 3D transform viewer.
+
+## Task 3: Update Shopify Push Task
+After room settings are generated, the 00_maya-romanoff-shopify-push.md task should include room_setting_images in the Shopify product payload as additional product images.
+
+## DB Connection
+postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
+
+## Important
+- Use Gemini for ALL image generation — never DALL-E or other providers
+- Save images locally first, then reference in DB
+- Log progress every 10 products
diff --git a/tasks/done/01_ai-enrich-romo.md b/tasks/done/01_ai-enrich-romo.md
index 1b94b76..6725ae1 100644
--- a/tasks/done/01_ai-enrich-romo.md
+++ b/tasks/done/01_ai-enrich-romo.md
@@ -14,6 +14,6 @@ If that script doesn't exist or errors, use the enrich-ai-tags approach:
4. Update vendor_catalog with results
5. Mark in enrichment_tracking table
-Use Gemini API key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Use Gemini API key: ${GOOGLE_API_KEY}
Model: gemini-2.0-flash
Limit to 200 products per run to stay within rate limits.
diff --git a/tasks/done/01_ai-enrich-romo.md.pre-scrub-2026-05-07.bak b/tasks/done/01_ai-enrich-romo.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..1b94b76
--- /dev/null
+++ b/tasks/done/01_ai-enrich-romo.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,19 @@
+# AI Enrich — Romo (454 products missing AI colors)
+
+Run Phase 3 AI enrichment on Romo products missing ai_colors in vendor_catalog.
+
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --vendor romo --phase 3 --limit 200
+```
+
+If that script doesn't exist or errors, use the enrich-ai-tags approach:
+1. Query vendor_catalog for romo products where ai_colors IS NULL AND image_url IS NOT NULL
+2. For each product, call Gemini 2.0 Flash vision API with the image
+3. Extract: colors (with hex + percentages), background color, styles, patterns, image type
+4. Update vendor_catalog with results
+5. Mark in enrichment_tracking table
+
+Use Gemini API key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Model: gemini-2.0-flash
+Limit to 200 products per run to stay within rate limits.
diff --git a/tasks/done/01_cleanup-37-empty-collections.md b/tasks/done/01_cleanup-37-empty-collections.md
index 2165b7f..3b2507b 100644
--- a/tasks/done/01_cleanup-37-empty-collections.md
+++ b/tasks/done/01_cleanup-37-empty-collections.md
@@ -69,7 +69,7 @@ Save to: `/root/DW-Agents/yolo-agent/logs/empty-collections-cleanup-report.json`
```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"🧹 *Empty Collections Cleanup*\n• Unpublished: X collections\n• Skipped (linked): Y\n• Failed: Z\n• Report: /root/DW-Agents/yolo-agent/logs/empty-collections-cleanup-report.json"}' \
- "https://hooks.slack.com/services/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"
+ "${SLACK_WEBHOOK_URL}"
```
## DO NOT
@@ -80,4 +80,4 @@ curl -X POST -H 'Content-type: application/json' \
## Credentials
- Shopify API: <redacted:SHOPIFY_ORDERS_TOKEN>
- Store: designer-laboratory-sandbox.myshopify.com
-- Slack: https://hooks.slack.com/services/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
+- Slack: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/01_cleanup-37-empty-collections.md.pre-scrub-2026-05-07.bak b/tasks/done/01_cleanup-37-empty-collections.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2165b7f
--- /dev/null
+++ b/tasks/done/01_cleanup-37-empty-collections.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,83 @@
+# Clean Up 37 Empty Collections (0 Active Products)
+
+## Ralph Clarifying Questions (Self-Check Before Executing)
+
+1. **Which collections are empty and why?**
+ Read: `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json`
+ The "empty" array has collections with 0 active products. For each:
+ - Are ALL products archived? (vendor discontinued)
+ - Is the collection rule wrong? (no matching products)
+ - Was the vendor removed from the store?
+
+2. **Are any of these collections linked from the homepage or navigation?**
+ Check: `curl -s https://www.designerwallcoverings.com/ | grep "{collection_handle}"`
+ If linked from homepage/nav, DON'T delete — flag for Steve.
+
+3. **Should empty collections be unpublished or deleted?**
+ - **Unpublish** = keeps the collection but removes from storefront/search (reversible)
+ - **Delete** = permanent removal
+ - Default: **UNPUBLISH** (safer — can re-publish if products come back)
+
+## What To Do
+
+### Step 1: Read the audit report
+```bash
+cat /root/DW-Agents/yolo-agent/logs/collection-audit-report.json
+```
+Extract the "empty" array with collection IDs and handles.
+
+### Step 2: For each empty collection, check if it's linked anywhere important
+```bash
+# Check homepage
+curl -s "https://www.designerwallcoverings.com/" | grep -i "{handle}"
+# Check navigation menus via API
+curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
+ "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/menus.json"
+```
+
+### Step 3: Unpublish empty collections (NOT delete)
+```bash
+# For smart collections:
+curl -s -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/smart_collections/{id}.json" \
+ -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
+ -H "Content-Type: application/json" \
+ -d '{"smart_collection": {"published": false}}'
+
+# For custom collections:
+curl -s -X PUT "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/custom_collections/{id}.json" \
+ -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
+ -H "Content-Type: application/json" \
+ -d '{"custom_collection": {"published": false}}'
+```
+
+### Step 4: Rate limiting
+- 600ms between API calls
+- Log each action
+
+### Step 5: Save results
+Save to: `/root/DW-Agents/yolo-agent/logs/empty-collections-cleanup-report.json`
+```json
+{
+ "timestamp": "...",
+ "unpublished": [{"id": 123, "handle": "x", "title": "Y", "reason": "0 active products"}],
+ "skippedLinked": [{"id": 456, "handle": "z", "linkedFrom": "homepage"}],
+ "failed": []
+}
+```
+
+### Step 6: Slack notification
+```bash
+curl -X POST -H 'Content-type: application/json' \
+ --data '{"text":"🧹 *Empty Collections Cleanup*\n• Unpublished: X collections\n• Skipped (linked): Y\n• Failed: Z\n• Report: /root/DW-Agents/yolo-agent/logs/empty-collections-cleanup-report.json"}' \
+ "https://hooks.slack.com/services/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"
+```
+
+## DO NOT
+- DELETE any collections (unpublish only — reversible)
+- Remove collections that are linked from homepage or main navigation
+- Modify any products or product data
+
+## Credentials
+- Shopify API: <redacted:SHOPIFY_ORDERS_TOKEN>
+- Store: designer-laboratory-sandbox.myshopify.com
+- Slack: https://hooks.slack.com/services/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
diff --git a/tasks/done/01_enrich-prl-direct.md b/tasks/done/01_enrich-prl-direct.md
index 2d0afd7..dd038d6 100644
--- a/tasks/done/01_enrich-prl-direct.md
+++ b/tasks/done/01_enrich-prl-direct.md
@@ -9,6 +9,6 @@ Run Gemini Vision directly on each product:
4. UPDATE vendor_catalog SET ai_colors, ai_background_color, ai_styles, ai_patterns, ai_tags, ai_description
5. Track in enrichment_tracking
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Gemini key: ${GOOGLE_API_KEY}
Model: gemini-2.0-flash
Limit: 100 per run, 500ms between calls
diff --git a/tasks/done/01_enrich-prl-direct.md.pre-scrub-2026-05-07.bak b/tasks/done/01_enrich-prl-direct.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2d0afd7
--- /dev/null
+++ b/tasks/done/01_enrich-prl-direct.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,14 @@
+# AI Enrich — PRL Products (186 missing in vendor_catalog)
+
+PRL vendor_code has 186 products in vendor_catalog with images but no ai_colors. There's no dedicated PRL catalog table.
+
+Run Gemini Vision directly on each product:
+1. Query: `SELECT id, mfr_sku, image_url FROM vendor_catalog WHERE vendor_code = 'PRL' AND ai_colors IS NULL AND image_url IS NOT NULL LIMIT 100`
+2. For each, call Gemini 2.0 Flash with the image_url
+3. Extract: colors (name, hex, percentage), background_color, styles, patterns, image_type
+4. UPDATE vendor_catalog SET ai_colors, ai_background_color, ai_styles, ai_patterns, ai_tags, ai_description
+5. Track in enrichment_tracking
+
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Model: gemini-2.0-flash
+Limit: 100 per run, 500ms between calls
diff --git a/tasks/done/01_interior-design-tags-all-vendors.md b/tasks/done/01_interior-design-tags-all-vendors.md
index 5dc6290..85a86d3 100644
--- a/tasks/done/01_interior-design-tags-all-vendors.md
+++ b/tasks/done/01_interior-design-tags-all-vendors.md
@@ -27,7 +27,7 @@ ORDER BY id
1. **Gemini analysis** — send image URL, get colors + hex + styles + patterns
```
-POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}
```
Prompt: "Analyze this wallcovering. Return ONLY valid JSON: { dominantColor, hexCode (#XXXXXX), allColors (ALL visible), styles, patterns, material }"
diff --git a/tasks/done/01_interior-design-tags-all-vendors.md.pre-scrub-2026-05-07.bak b/tasks/done/01_interior-design-tags-all-vendors.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..5dc6290
--- /dev/null
+++ b/tasks/done/01_interior-design-tags-all-vendors.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,87 @@
+# Interior Design Tags — PostgreSQL First, Then Shopify
+
+Run Gemini AI analysis on ALL products in PostgreSQL catalog tables. Save tags + hex codes to DB.
+Then push to Shopify only for products already on Shopify.
+
+## FLOW: PostgreSQL → Gemini → PostgreSQL → Shopify
+
+## TAG RULES
+- **NO "Background Color" prefix** — just the color name directly
+- **ALL colors detected** — tag every visible color
+- **Hex code** → save to `color_hex` column in catalog table + `global.color_hex` metafield on Shopify
+- **NEVER use "wallpaper"** — always "wallcovering"
+
+## Step 1: Analyze ALL catalog tables in PostgreSQL
+
+For each vendor catalog table that has an `image_url` column:
+
+```sql
+SELECT id, mfr_sku, pattern_name, color_name, image_url, material, collection
+FROM {catalog_table}
+WHERE image_url IS NOT NULL AND image_url <> ''
+AND (color_hex IS NULL OR color_hex = '')
+ORDER BY id
+```
+
+### For EACH product with image:
+
+1. **Gemini analysis** — send image URL, get colors + hex + styles + patterns
+```
+POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+```
+Prompt: "Analyze this wallcovering. Return ONLY valid JSON: { dominantColor, hexCode (#XXXXXX), allColors (ALL visible), styles, patterns, material }"
+
+2. **Save to PostgreSQL** — update the catalog row:
+```sql
+UPDATE {catalog_table} SET
+ color_hex = '{hexCode}',
+ ai_colors = '{allColors as JSON array}',
+ ai_styles = '{styles as JSON array}',
+ ai_patterns = '{patterns as JSON array}',
+ ai_background_color = '{dominantColor}',
+ ai_tags = '{computed tags as JSON array}',
+ updated_at = NOW()
+WHERE id = {id}
+```
+
+Add missing columns if needed:
+```sql
+ALTER TABLE {table} ADD COLUMN IF NOT EXISTS color_hex VARCHAR(7);
+ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_colors JSONB;
+ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_styles JSONB;
+ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_patterns JSONB;
+ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_background_color VARCHAR(100);
+ALTER TABLE {table} ADD COLUMN IF NOT EXISTS ai_tags JSONB;
+```
+
+## Step 2: Push to Shopify (only products already on Shopify)
+
+For products with `shopify_product_id IS NOT NULL`:
+
+1. Build tag string from ai_tags + brand + pattern + material + "Wallcovering" + "Commercial" + "Architectural"
+2. Push hex to `global.color_hex` metafield via GraphQL
+3. Update Shopify product tags via REST
+4. Publish to all 14 channels if not already
+
+## Vendor catalog tables to process (in order of priority):
+1. versace_catalog (136 products)
+2. black_edition_catalog (191)
+3. kirkby_catalog (152)
+4. zinc_catalog (100)
+5. villa_nova_catalog (387)
+6. arte_catalog (1991)
+7. romo_catalog (2595)
+8. scalamandre_catalog (4819)
+9. thibaut_catalog (5703)
+10. All other catalogs with image_url
+
+## Rate limits
+- Gemini: 1 req/sec (max 15 RPM on free tier — use batch API for 100+ products)
+- Shopify: 2 req/sec
+- Log progress every 25 products per vendor
+
+## CRITICAL
+- PostgreSQL FIRST, Shopify SECOND
+- Save ALL Gemini results to DB before touching Shopify
+- color_hex must be valid hex format (#XXXXXX)
+- Preserve display_variant tag on Shopify
diff --git a/tasks/done/01_newmor-full-monty-test.md b/tasks/done/01_newmor-full-monty-test.md
index 5fce44c..1cfe695 100644
--- a/tasks/done/01_newmor-full-monty-test.md
+++ b/tasks/done/01_newmor-full-monty-test.md
@@ -58,6 +58,6 @@ Print a clear summary of what was found:
### IMPORTANT RULES
- ALL specs go in metafields, NEVER in body_html
- body_html = description ONLY, 3 sentences max
-- Use Gemini analysis key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Use Gemini analysis key: ${GOOGLE_API_KEY}
- Track costs via /root/DW-Agents/shared/gemini-cost-tracker.js
- Do NOT push to Shopify yet — this is Phase 3 test only
diff --git a/tasks/done/01_newmor-full-monty-test.md.pre-scrub-2026-05-07.bak b/tasks/done/01_newmor-full-monty-test.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..5fce44c
--- /dev/null
+++ b/tasks/done/01_newmor-full-monty-test.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,63 @@
+# NEWMOR Full Monty — Test 1 Product
+
+## Context
+NEWMOR has 1,285 products in `newmor_catalog` PostgreSQL table. 653 have width. Zero have AI enrichment. Zero on Shopify.
+Steve wants a 1-product Full Monty test to validate the pipeline before running the full batch.
+
+## DB Connection
+`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+
+## Steps
+
+### Step 1: Pick a good test product
+Query `newmor_catalog` for a product with image_url, width, and mfr_sku populated. Prefer one with material and fire_rating too. Good candidate: mfr_sku = 'CHARCOAL' (pattern: Mid-Century Modern Floral Geo, width: 70cm, material: Non-Woven 460gsm, fire_rating: Type II).
+
+### Step 2: Run Phase 3 (AI Tags) on 1 product
+```bash
+cd /root/DW-Agents/vendor-scrapers
+node enrich-ai-tags.js newmor --limit 1
+```
+This runs Gemini Vision on the product image to extract colors, hex codes, styles, patterns, background color, and image type.
+
+### Step 3: Verify AI enrichment results
+Query the database to confirm AI data was written:
+```sql
+SELECT mfr_sku, pattern_name, color_name, ai_colors, ai_styles, ai_patterns, ai_background_color, ai_tags, color_hex
+FROM newmor_catalog
+WHERE ai_colors IS NOT NULL
+LIMIT 5;
+```
+
+Also check enrichment_tracking:
+```sql
+SELECT * FROM enrichment_tracking WHERE vendor_code = 'newmor' AND phase3_ai_at IS NOT NULL LIMIT 5;
+```
+
+### Step 4: Run Phase 4 (Silas Validation) on the enriched product
+```bash
+curl -s -u admin:DWSecure2024! http://localhost:9674/api/validate/newmor | head -100
+```
+
+### Step 5: Generate body_html description (3 sentences max, NO specs)
+Write a 3-sentence product description for the test product. Format:
+- Sentence 1: What it is (pattern/color/vendor)
+- Sentence 2: Style/mood
+- Sentence 3: Recommended use
+Store in the `body_html` column of `newmor_catalog`.
+
+### Step 6: Report Results
+Print a clear summary of what was found:
+- Product tested (SKU, pattern, color)
+- AI colors detected (with hex codes)
+- AI styles and patterns
+- Background color
+- Image type classification
+- Body HTML generated
+- Whether the product passes FULLPRODUCT gate (image + width + mfr_sku)
+
+### IMPORTANT RULES
+- ALL specs go in metafields, NEVER in body_html
+- body_html = description ONLY, 3 sentences max
+- Use Gemini analysis key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Track costs via /root/DW-Agents/shared/gemini-cost-tracker.js
+- Do NOT push to Shopify yet — this is Phase 3 test only
diff --git a/tasks/done/02_activate-versace-drafts.md b/tasks/done/02_activate-versace-drafts.md
index fd69a91..e4aaf39 100644
--- a/tasks/done/02_activate-versace-drafts.md
+++ b/tasks/done/02_activate-versace-drafts.md
@@ -12,7 +12,7 @@ FROM versace_catalog WHERE shopify_product_id IS NOT NULL
### 2. Generate Gemini AI description
For each product with an image, call Gemini to generate a 2-3 sentence luxury wallcovering description.
-API key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+API key: ${GOOGLE_API_KEY}
Model: gemini-2.0-flash
Push result to Shopify body_html (description only, NO specs table).
diff --git a/tasks/done/02_activate-versace-drafts.md.pre-scrub-2026-05-07.bak b/tasks/done/02_activate-versace-drafts.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..fd69a91
--- /dev/null
+++ b/tasks/done/02_activate-versace-drafts.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,59 @@
+# Activate 136 Versace Draft Products on Shopify
+
+136 Versace VI products are on Shopify as DRAFT. They need enrichment + activation.
+
+## Steps for EACH product:
+
+### 1. Find all Versace drafts
+```sql
+SELECT mfr_sku, dw_sku, pattern_name, color_name, shopify_product_id, width, material, collection, image_url
+FROM versace_catalog WHERE shopify_product_id IS NOT NULL
+```
+
+### 2. Generate Gemini AI description
+For each product with an image, call Gemini to generate a 2-3 sentence luxury wallcovering description.
+API key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Model: gemini-2.0-flash
+Push result to Shopify body_html (description only, NO specs table).
+
+### 3. Push metafields via GraphQL metafieldsSet
+For each product, push ALL available specs to global.* namespace:
+- global.width (from DB width column)
+- global.manufacturer_sku (from mfr_sku)
+- global.Brand = "Versace"
+- global.Collection = "Versace VI"
+- global.Contents = "Non-woven"
+- global.application = "Paste the wall"
+- global.repeat (if pattern, not texture — check pattern_name, textures have no repeat)
+
+Use GraphQL metafieldsSet mutation — ONE call per product (not REST).
+Endpoint: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json
+Token: <redacted:SHOPIFY_ADMIN_TOKEN>
+
+### 4. Silas validate-activation
+POST http://127.0.0.1:9674/api/validate-activation
+Body: { shopify_product_id }
+Only activate if Silas approves.
+
+### 5. Activate
+PUT product status to 'active' via REST API.
+Only if Silas approved AND metafields are confirmed.
+
+### 6. Skip products without images
+67 of 136 have images. Products without images CANNOT be activated (Silas will block).
+Log skipped products.
+
+## Rate limits
+- Gemini: 1 req/sec
+- Shopify GraphQL: 2 req/sec
+- Shopify REST: 2 req/sec
+- Silas: no limit (local)
+
+## Report
+Total drafts, enriched with description, metafields pushed, activated, blocked by Silas, skipped (no image).
+
+## CRITICAL
+- Body HTML = description ONLY, no specs tables
+- All specs in metafields exclusively (global.* namespace)
+- NEVER use word "wallpaper" — always "wallcovering"
+- Use Gemini for image analysis, NEVER Claude Vision
diff --git a/tasks/done/02_ai-enrich-villa-nova.md b/tasks/done/02_ai-enrich-villa-nova.md
index 5525699..d189bab 100644
--- a/tasks/done/02_ai-enrich-villa-nova.md
+++ b/tasks/done/02_ai-enrich-villa-nova.md
@@ -6,4 +6,4 @@ Same as Romo task but for villa_nova vendor_code. Limit 200 per run.
cd /root/DW-Agents/full-monte
node full-monte-batch.js --vendor villa_nova --phase 3 --limit 200
```
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
+Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/02_ai-enrich-villa-nova.md.pre-scrub-2026-05-07.bak b/tasks/done/02_ai-enrich-villa-nova.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..5525699
--- /dev/null
+++ b/tasks/done/02_ai-enrich-villa-nova.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,9 @@
+# AI Enrich — Villa Nova (357 products missing AI colors)
+
+Same as Romo task but for villa_nova vendor_code. Limit 200 per run.
+
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --vendor villa_nova --phase 3 --limit 200
+```
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
diff --git a/tasks/done/02_build-as-creation-agent.md b/tasks/done/02_build-as-creation-agent.md
index 245fa45..494d9eb 100644
--- a/tasks/done/02_build-as-creation-agent.md
+++ b/tasks/done/02_build-as-creation-agent.md
@@ -47,5 +47,5 @@ Fall back to sitemap approach:
## Slack Notification — REQUIRED
```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"AS CREATION AGENT BUILT: Ace on port 9656. [X] products crawled. US distributor: Sancar."}' "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"AS CREATION AGENT BUILT: Ace on port 9656. [X] products crawled. US distributor: Sancar."}' "${SLACK_WEBHOOK_URL}"
```
diff --git a/tasks/done/02_build-as-creation-agent.md.pre-scrub-2026-05-07.bak b/tasks/done/02_build-as-creation-agent.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..245fa45
--- /dev/null
+++ b/tasks/done/02_build-as-creation-agent.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,51 @@
+# Build AS Creation Agent — Ace
+
+Build a scraper agent for AS Creation at https://products.as-creation.com/en/Collections/
+
+## Key Facts
+- **Platform**: Shopware 6 (NOT Shopify, NOT WooCommerce)
+- **Codename**: Ace (A = AS Creation)
+- **Port**: 9656
+- **Directory**: /root/DW-Agents/ace-agent/
+- **Sitemap**: https://products.as-creation.com/sitemap.xml (contains gzipped product sitemap)
+- **API**: Shopware Store API at /store-api/product requires `sw-access-key` header
+- **US Distribution**: Sancar is US distributor. Astek may also rep their lines.
+
+## Find the sw-access-key
+The access key is usually in the page source HTML. Look for it:
+```bash
+curl -sk 'https://products.as-creation.com/en/' | grep -i 'access.key\|sw-access\|salesChannel'
+```
+Or check the JavaScript files for the key.
+
+## Build Steps
+1. Create /root/DW-Agents/ace-agent/ directory
+2. Create package.json with express and pg dependencies
+3. Build server.js following the pattern of marco-agent (/root/DW-Agents/marco-agent/server.js) but adapted for Shopware:
+ - POST to /store-api/product with sw-access-key header
+ - Paginate with `page` and `limit` params
+ - Extract all product attributes/properties for specs
+4. Create as_creation_catalog table with ALL spec columns including:
+ - us_distributor (default: 'Sancar Wallcoverings')
+ - showroom_locations
+ - All standard spec columns (width, length, repeat_v, repeat_h, material, fire_rating, finish, etc.)
+5. npm install
+6. Register in vendor_registry
+7. Open firewall: `sudo ufw allow from 76.33.146.135 to any port 9656 proto tcp`
+8. Start with PM2: `pm2 start server.js --name ace-agent`
+9. Trigger 3h continuous crawl
+
+## If Store API doesn't work:
+Fall back to sitemap approach:
+1. Download the gzipped sitemap
+2. Extract product URLs
+3. Curl each product page and parse the HTML for specs
+4. Use execFileSync('curl', ['-sk', url]) to avoid SSL issues
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
+
+## Slack Notification — REQUIRED
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"AS CREATION AGENT BUILT: Ace on port 9656. [X] products crawled. US distributor: Sancar."}' "https://hooks.slack.com/services/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"
+```
diff --git a/tasks/done/02_enrich-remaining-gaps.md b/tasks/done/02_enrich-remaining-gaps.md
index 06bd691..ef16a19 100644
--- a/tasks/done/02_enrich-remaining-gaps.md
+++ b/tasks/done/02_enrich-remaining-gaps.md
@@ -5,5 +5,5 @@
For Arte (9 missing): Check arte_catalog for matching products and sync. If not in arte_catalog, run Gemini directly.
For Black Edition (4 missing, SKUs W924/01-04): Not in black_edition_catalog. Run Gemini Vision directly.
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Gemini key: ${GOOGLE_API_KEY}
Model: gemini-2.0-flash
diff --git a/tasks/done/02_enrich-remaining-gaps.md.pre-scrub-2026-05-07.bak b/tasks/done/02_enrich-remaining-gaps.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..06bd691
--- /dev/null
+++ b/tasks/done/02_enrich-remaining-gaps.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,9 @@
+# AI Enrich — Remaining Gaps (Arte 9, Black Edition 4)
+
+13 products still missing ai_colors in vendor_catalog.
+
+For Arte (9 missing): Check arte_catalog for matching products and sync. If not in arte_catalog, run Gemini directly.
+For Black Edition (4 missing, SKUs W924/01-04): Not in black_edition_catalog. Run Gemini Vision directly.
+
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Model: gemini-2.0-flash
diff --git a/tasks/done/02_full-store-collection-audit.md b/tasks/done/02_full-store-collection-audit.md
index 04b7f19..d86c5c3 100644
--- a/tasks/done/02_full-store-collection-audit.md
+++ b/tasks/done/02_full-store-collection-audit.md
@@ -55,7 +55,7 @@ Format for JSON:
```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"📊 *Collection Audit Complete*\n• Total vendors: X\n• Matched: X\n• Missing collection: X\n• Empty collection: X\n• Report: /root/DW-Agents/yolo-agent/logs/collection-audit-summary.md"}' \
- "https://hooks.slack.com/services/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"
+ "${SLACK_WEBHOOK_URL}"
```
## DO NOT
@@ -73,4 +73,4 @@ curl -X POST -H 'Content-type: application/json' \
## Credentials
- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
- GraphQL endpoint: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
-- Slack webhook: https://hooks.slack.com/services/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
+- Slack webhook: ${SLACK_WEBHOOK_URL}
diff --git a/tasks/done/02_full-store-collection-audit.md.pre-scrub-2026-05-07.bak b/tasks/done/02_full-store-collection-audit.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..04b7f19
--- /dev/null
+++ b/tasks/done/02_full-store-collection-audit.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,76 @@
+# Full Store-Wide Collection Audit — Every Vendor Has a Collection
+
+## Ralph Clarifying Questions (Self-Check Before Executing)
+
+1. **How many unique vendors exist in the store?**
+ ```bash
+ curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
+ -H "Content-Type: application/json" \
+ -d '{"query":"{ shop { productVendors(first: 250) { edges { node } } } }"}' \
+ "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json"
+ ```
+ List all vendor names. Count them.
+
+2. **How many custom + smart collections exist?**
+ - Count custom_collections and smart_collections via API
+ - This gives us the denominator for "% of vendors with collections"
+
+3. **What's the mapping strategy?**
+ - Most vendor collections use the handle pattern: `vendor-name-lowercase-dashed`
+ - Some use alternate handles (e.g., `arte` not `arte-international`)
+ - Check for both exact and partial matches
+
+## What To Do
+
+### Phase 1: Build the vendor→collection map
+1. Fetch ALL unique vendors via GraphQL (shop.productVendors)
+2. Fetch ALL collections (custom + smart) via REST API with pagination
+3. For each vendor, check if a collection exists where:
+ - Collection title contains the vendor name, OR
+ - Collection handle matches the vendor handle pattern, OR
+ - Collection has a rule `vendor equals {vendor_name}`
+
+### Phase 2: Identify gaps
+Create a report with three categories:
+- **MATCHED**: Vendor has a collection (with handle and product count)
+- **MISSING**: Vendor has active products but NO collection
+- **EMPTY**: Vendor has a collection but 0 visible products
+
+### Phase 3: Save the report
+Save to: `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json`
+Also save a human-readable summary to: `/root/DW-Agents/yolo-agent/logs/collection-audit-summary.md`
+
+Format for JSON:
+```json
+{
+ "timestamp": "2026-03-23T...",
+ "totalVendors": 250,
+ "matched": [{"vendor": "Arte", "collection": "arte", "products": 150}],
+ "missing": [{"vendor": "Phyllis Morris", "activeProducts": 28}],
+ "empty": [{"vendor": "Phillipe Romano", "collection": "phillipe-romano-faux-leathers", "reason": "all archived"}]
+}
+```
+
+### Phase 4: Send Slack notification
+```bash
+curl -X POST -H 'Content-type: application/json' \
+ --data '{"text":"📊 *Collection Audit Complete*\n• Total vendors: X\n• Matched: X\n• Missing collection: X\n• Empty collection: X\n• Report: /root/DW-Agents/yolo-agent/logs/collection-audit-summary.md"}' \
+ "https://hooks.slack.com/services/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"
+```
+
+## DO NOT
+- Create any new collections (just report — Steve will review)
+- Delete any collections
+- Modify any products
+- This task is AUDIT ONLY — read operations only
+
+## Verification
+- Report file exists and has data
+- Total vendors matches GraphQL count
+- matched + missing + empty = total vendors
+- Slack notification sent
+
+## Credentials
+- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
+- GraphQL endpoint: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
+- Slack webhook: https://hooks.slack.com/services/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
diff --git a/tasks/done/02_newmor-bulk-phase3-ai.md b/tasks/done/02_newmor-bulk-phase3-ai.md
index 2873bc4..18e1ce3 100644
--- a/tasks/done/02_newmor-bulk-phase3-ai.md
+++ b/tasks/done/02_newmor-bulk-phase3-ai.md
@@ -79,7 +79,7 @@ Print summary:
- Next steps needed before Shopify push
## IMPORTANT RULES
-- Use Gemini analysis key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Use Gemini analysis key: ${GOOGLE_API_KEY}
- Track costs via /root/DW-Agents/shared/gemini-cost-tracker.js
- Check enrichment_tracking BEFORE every Gemini call (dedup)
- Do NOT push to Shopify — that requires VCC approval (Phase 5)
diff --git a/tasks/done/02_newmor-bulk-phase3-ai.md.pre-scrub-2026-05-07.bak b/tasks/done/02_newmor-bulk-phase3-ai.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2873bc4
--- /dev/null
+++ b/tasks/done/02_newmor-bulk-phase3-ai.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,89 @@
+# NEWMOR — Bulk Phase 3 AI Enrichment (All Products)
+
+## Context
+After width re-scraping, run Gemini Vision AI enrichment on ALL NEWMOR products. The test run on CHARCOAL validated the pipeline. Now run the full batch.
+
+Currently ~3 products already enriched from the test run. ~1,282 remaining.
+
+## DB Connection
+`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+
+## Steps
+
+### Step 1: Check how many still need AI enrichment
+```sql
+SELECT
+ COUNT(*) as total,
+ COUNT(CASE WHEN ai_colors IS NOT NULL THEN 1 END) as has_ai,
+ COUNT(CASE WHEN ai_colors IS NULL THEN 1 END) as needs_ai,
+ COUNT(CASE WHEN width IS NOT NULL AND length(width) > 0 THEN 1 END) as has_width
+FROM newmor_catalog;
+```
+
+### Step 2: Run Phase 3 (AI Tags) via Full Monte CLI
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --vendor newmor --phase 3 --limit 1300
+```
+
+This calls `enrich-ai-tags.js` which:
+- Queries newmor_catalog for products with images but no AI tags
+- Checks enrichment_tracking to prevent duplicate Gemini calls
+- Sends each product image to Gemini Vision for analysis
+- Extracts: colors (with hex + percentages), background color, styles, patterns, image type, tags
+- Writes results to newmor_catalog and marks enrichment_tracking
+
+### Step 3: Monitor progress (check periodically)
+```sql
+SELECT
+ COUNT(*) as total,
+ COUNT(CASE WHEN ai_colors IS NOT NULL THEN 1 END) as enriched,
+ COUNT(CASE WHEN ai_colors IS NULL THEN 1 END) as remaining
+FROM newmor_catalog;
+```
+
+### Step 4: Check enrichment tracking
+```sql
+SELECT COUNT(*) as phase3_done
+FROM enrichment_tracking
+WHERE vendor_code = 'newmor' AND phase3_ai_at IS NOT NULL;
+```
+
+### Step 5: Check for errors
+```sql
+SELECT COUNT(*) as with_errors, SUM(error_count) as total_errors
+FROM enrichment_tracking
+WHERE vendor_code = 'newmor' AND error_count > 0;
+```
+
+### Step 6: Generate body_html for all enriched products
+For products that have AI data but no body_html, generate 3-sentence descriptions:
+- Sentence 1: What it is (pattern/color/vendor)
+- Sentence 2: Style/mood
+- Sentence 3: Recommended use
+NEVER include specs in body_html.
+
+### Step 7: Final Full Monte status
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --status --vendor newmor
+```
+
+### Step 8: Report results
+Print summary:
+- Total products enriched
+- Sample of 5 products with their AI colors and tags
+- Error count
+- Cost estimate (products * $0.0006)
+- Updated Full Monte status table
+- Next steps needed before Shopify push
+
+## IMPORTANT RULES
+- Use Gemini analysis key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Track costs via /root/DW-Agents/shared/gemini-cost-tracker.js
+- Check enrichment_tracking BEFORE every Gemini call (dedup)
+- Do NOT push to Shopify — that requires VCC approval (Phase 5)
+- body_html = description ONLY, 3 sentences max, NO specs
+- ALL specs go in metafields exclusively
+- If rate limited by Gemini, back off and retry
+- This may take 15-30 minutes for 1,200+ products
diff --git a/tasks/done/02_update-vcc-spec-standards.md b/tasks/done/02_update-vcc-spec-standards.md
index 1447dc9..8d813ee 100644
--- a/tasks/done/02_update-vcc-spec-standards.md
+++ b/tasks/done/02_update-vcc-spec-standards.md
@@ -59,6 +59,6 @@ Update it to enforce that ALL vendor agents must capture these fields:
## Slack Notification — REQUIRED
When this task is complete, send a Slack message to Steve with the results summary:
```bash
-curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "${SLACK_WEBHOOK_URL}"
```
Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/02_update-vcc-spec-standards.md.pre-scrub-2026-05-07.bak b/tasks/done/02_update-vcc-spec-standards.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..1447dc9
--- /dev/null
+++ b/tasks/done/02_update-vcc-spec-standards.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,64 @@
+# Update Vendor Command Center — Enforce ALL Specs Standard
+
+Read the Vendor Command Center (Victor) at `/root/DW-Agents/vendor-command-center/server.js` (port 9660).
+
+Update it to enforce that ALL vendor agents must capture these fields:
+
+## Required Catalog Columns (ALL vendors)
+- `mfr_sku` — manufacturer SKU (UNIQUE)
+- `dw_sku` — Designer Wallcoverings internal SKU
+- `pattern_name` — pattern/design name
+- `color_name` — colorway name
+- `collection` — collection name
+- `product_type` — wallpaper, wallcovering, fabric, etc.
+- `width` — roll width
+- `length` — roll length
+- `repeat_v` — vertical repeat
+- `repeat_h` — horizontal repeat
+- `match_type` — straight, offset, random, seamless
+- `material` — vinyl, non-woven, grasscloth, etc.
+- `finish` — matte, satin, textured, etc.
+- `application` — paste-the-wall, paste-the-paper, peel-and-stick
+- `coverage` — square footage per roll
+- `features` — washable, strippable, breathable, etc.
+- `fire_rating` — ASTM E84 Class A, Class 1, etc.
+- `design` — floral, geometric, stripe, etc.
+- `rooms` — bedroom, living room, bathroom, etc.
+- `color_primary` — primary color
+- `color_secondary` — secondary color
+- `price_retail` — retail price
+- `price_currency` — USD, EUR, GBP
+- `image_url` — primary image
+- `all_images` — ALL images pipe-separated (|)
+- `product_url` — source product page URL
+- `in_stock` — boolean
+- `vendor_name` — vendor name
+- `brand` — brand if different from vendor
+- `tags` — tags/categories
+- `body_html` — full product description HTML
+- `short_description` — short description
+- `about_vendor` — vendor company info (about us)
+- `us_distributor` — US distribution info
+- `showroom_locations` — physical showroom locations
+
+## What to Add to VCC Dashboard
+1. Add a "Spec Completeness" column to the vendor table showing % of required fields filled
+2. Add a "Missing Specs" alert for vendors below 50% spec fill rate
+3. Add `about_vendor`, `us_distributor`, and `showroom_locations` columns to all catalog tables
+
+## Important
+- Do NOT restart the VCC if it's currently running well — just update the code and restart after
+- Test the dashboard still loads after changes
+- Auth: admin / DWSecure2024!
+
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY. Downloading product data FROM other vendors' Shopify stores for catalog data is fine and expected. product-scheduler and schedule-engine are STOPPED intentionally. Do NOT restart them.**
+
+
+## Slack Notification — REQUIRED
+When this task is complete, send a Slack message to Steve with the results summary:
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "https://hooks.slack.com/services/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"
+```
+Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/03_03_gls-tag-cleanup.md b/tasks/done/03_03_gls-tag-cleanup.md
index 9e60075..bd16713 100644
--- a/tasks/done/03_03_gls-tag-cleanup.md
+++ b/tasks/done/03_03_gls-tag-cleanup.md
@@ -6,4 +6,4 @@ Find ALL products on Shopify with GLS in the SKU (across ALL vendors — Glitter
Shopify API: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/
Token: <redacted:SHOPIFY_ADMIN_TOKEN>
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
\ No newline at end of file
+Gemini key: ${GOOGLE_API_KEY}
\ No newline at end of file
diff --git a/tasks/done/03_03_gls-tag-cleanup.md.pre-scrub-2026-05-07.bak b/tasks/done/03_03_gls-tag-cleanup.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..9e60075
--- /dev/null
+++ b/tasks/done/03_03_gls-tag-cleanup.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,9 @@
+Find ALL products on Shopify with GLS in the SKU (across ALL vendors — Glitter Walls, Glass Beaded, Phillipe Romano). For each:
+1. Remove tags: brand vinyl, composition vinyl, Vinyl (the tag, not material)
+2. Run interior design tagger via Gemini Vision to generate proper style/color/pattern tags
+3. Add a 2-3 sentence product description if body_html is empty
+4. Crop any text/logos from the primary product image
+
+Shopify API: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/
+Token: <redacted:SHOPIFY_ADMIN_TOKEN>
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
\ No newline at end of file
diff --git a/tasks/done/03_ai-enrich-mark-alexander.md b/tasks/done/03_ai-enrich-mark-alexander.md
index 75b3dad..c4a2f3c 100644
--- a/tasks/done/03_ai-enrich-mark-alexander.md
+++ b/tasks/done/03_ai-enrich-mark-alexander.md
@@ -6,4 +6,4 @@ Same as Romo task but for mark_alexander vendor_code. Limit 200 per run.
cd /root/DW-Agents/full-monte
node full-monte-batch.js --vendor mark_alexander --phase 3 --limit 200
```
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
+Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/03_ai-enrich-mark-alexander.md.pre-scrub-2026-05-07.bak b/tasks/done/03_ai-enrich-mark-alexander.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..75b3dad
--- /dev/null
+++ b/tasks/done/03_ai-enrich-mark-alexander.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,9 @@
+# AI Enrich — Mark Alexander (260 products missing AI colors)
+
+Same as Romo task but for mark_alexander vendor_code. Limit 200 per run.
+
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --vendor mark_alexander --phase 3 --limit 200
+```
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
diff --git a/tasks/done/03_fix-spec-gaps.md b/tasks/done/03_fix-spec-gaps.md
index b7d930c..87e5181 100644
--- a/tasks/done/03_fix-spec-gaps.md
+++ b/tasks/done/03_fix-spec-gaps.md
@@ -63,5 +63,5 @@ ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS all_images TEXT DEFAULT '';
## Slack Notification — REQUIRED
```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"SPEC GAPS FIXED: Extracted specs from body_html for [X] vendors. [summary]"}' "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"SPEC GAPS FIXED: Extracted specs from body_html for [X] vendors. [summary]"}' "${SLACK_WEBHOOK_URL}"
```
diff --git a/tasks/done/03_fix-spec-gaps.md.pre-scrub-2026-05-07.bak b/tasks/done/03_fix-spec-gaps.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..b7d930c
--- /dev/null
+++ b/tasks/done/03_fix-spec-gaps.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,67 @@
+# Fix Spec Gaps — Extract specs from body_html for all vendors
+
+Many vendors have body_html but empty spec columns. Parse body_html to extract specs.
+
+## Vendors with 0% specs that have body_html:
+Run this query to find them:
+```sql
+PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
+SELECT relname, n_live_tup FROM pg_stat_user_tables
+WHERE relname LIKE '%_catalog' AND n_live_tup > 100
+ORDER BY n_live_tup DESC;"
+```
+
+For EACH catalog table with > 100 products:
+1. Check how many have body_html populated
+2. Check how many have empty width, repeat_v, material
+3. If body_html has data but specs are empty, extract with regex patterns:
+
+### Common spec patterns in body_html:
+```sql
+-- Width patterns
+UPDATE xxx_catalog SET width = TRIM(match[1])
+FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Width|Roll Width|Dimensions)[:\s]*([^<,]+)', 'i') as match FROM xxx_catalog WHERE (width IS NULL OR width = '') AND body_html <> '') sub
+WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
+
+-- Repeat patterns
+UPDATE xxx_catalog SET repeat_v = TRIM(match[1])
+FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Repeat|Pattern Repeat|Vertical Repeat)[:\s]*([^<,]+)', 'i') as match FROM xxx_catalog WHERE (repeat_v IS NULL OR repeat_v = '') AND body_html <> '') sub
+WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
+
+-- Material patterns
+UPDATE xxx_catalog SET material = TRIM(match[1])
+FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Material|Substrate|Composition)[:\s]*([^<,]+)', 'i') as match FROM xxx_catalog WHERE (material IS NULL OR material = '') AND body_html <> '') sub
+WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
+
+-- Fire rating patterns
+UPDATE xxx_catalog SET fire_rating = TRIM(match[1])
+FROM (SELECT id, REGEXP_MATCH(body_html, '(?:Fire|Flame|ASTM|Class\s*[A1]|NFPA)[:\s]*([^<]+)', 'i') as match FROM xxx_catalog WHERE body_html <> '') sub
+WHERE xxx_catalog.id = sub.id AND sub.match IS NOT NULL;
+```
+
+4. Add missing columns first:
+```sql
+ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS fire_rating VARCHAR(255) DEFAULT '';
+ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS finish VARCHAR(255) DEFAULT '';
+ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS application VARCHAR(255) DEFAULT '';
+ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS match_type VARCHAR(100) DEFAULT '';
+ALTER TABLE xxx_catalog ADD COLUMN IF NOT EXISTS all_images TEXT DEFAULT '';
+```
+
+## Priority order (most products first):
+1. kravet_catalog (9,780)
+2. brewster_catalog (8,715)
+3. cowtan_tout_catalog (8,466)
+4. bespoke_catalog (6,262)
+5. thibaut_catalog (5,557)
+6. york_catalog (5,304)
+7. schumacher_catalog (5,182) — already has 3,144 fire ratings
+8. pj_catalog (4,376)
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
+
+## Slack Notification — REQUIRED
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"SPEC GAPS FIXED: Extracted specs from body_html for [X] vendors. [summary]"}' "https://hooks.slack.com/services/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"
+```
diff --git a/tasks/done/03_orphan-product-audit.md b/tasks/done/03_orphan-product-audit.md
index e9f9f5d..b032ca7 100644
--- a/tasks/done/03_orphan-product-audit.md
+++ b/tasks/done/03_orphan-product-audit.md
@@ -78,7 +78,7 @@ Format:
```bash
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"🔍 *Orphan Product Audit Complete*\n• Total active products: X\n• Products with NO collection: X (Y%)\n• Vendors with orphans: Z\n• Top 5 vendors: ...\n• Report: /root/DW-Agents/yolo-agent/logs/orphan-product-summary.md"}' \
- "https://hooks.slack.com/services/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"
+ "${SLACK_WEBHOOK_URL}"
```
## DO NOT
@@ -96,7 +96,7 @@ curl -X POST -H 'Content-type: application/json' \
## Credentials
- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
- GraphQL: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
-- Slack: https://hooks.slack.com/services/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
+- Slack: ${SLACK_WEBHOOK_URL}
## Rate Limiting
- GraphQL: 1000 points/second on standard plan
diff --git a/tasks/done/03_orphan-product-audit.md.pre-scrub-2026-05-07.bak b/tasks/done/03_orphan-product-audit.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..e9f9f5d
--- /dev/null
+++ b/tasks/done/03_orphan-product-audit.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,105 @@
+# Orphan Product Audit — Every Active Product Has a Browsable Collection
+
+## Ralph Clarifying Questions (Self-Check Before Executing)
+
+1. **How many total active products are in the store?**
+ ```bash
+ curl -s -H "X-Shopify-Access-Token: <redacted:SHOPIFY_ORDERS_TOKEN>" \
+ -H "Content-Type: application/json" \
+ -d '{"query":"{ productsCount(query: \"status:active\") { count } }"}' \
+ "https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json"
+ ```
+
+2. **Is the collection audit (task 02) done?**
+ - Check if `/root/DW-Agents/yolo-agent/logs/collection-audit-report.json` exists
+ - If not, this task depends on it — use the report to know which vendors lack collections
+ - If the file doesn't exist, use the "missing" vendors from the audit as a starting point
+
+3. **What does "orphan" mean in this context?**
+ - An active product whose vendor has NO collection at all
+ - OR an active product that isn't included in ANY collection (custom or smart)
+ - The second definition is harder to check — start with the first (vendor-level)
+
+## What To Do
+
+### Phase 1: Vendor-level orphan detection (fast)
+Using the collection audit report (task 02), for each vendor in the "missing" list:
+1. Count active products for that vendor via GraphQL
+2. List up to 10 example products (title, handle, SKU)
+3. This gives us "X products from Y vendors have no collection"
+
+### Phase 2: Product-level orphan check (thorough)
+For a more thorough check:
+1. Get all collection IDs and their product lists via Shopify Collect API
+2. Get all active product IDs
+3. Find product IDs that don't appear in ANY collection
+4. Group orphaned products by vendor for the report
+
+NOTE: This can be expensive API-wise. Use GraphQL bulk operations if product count > 10,000:
+```graphql
+{
+ products(first: 250, query: "status:active") {
+ edges {
+ node {
+ id
+ title
+ vendor
+ handle
+ collections(first: 10) {
+ edges { node { id title } }
+ }
+ }
+ }
+ pageInfo { hasNextPage endCursor }
+ }
+}
+```
+Paginate through all products. Any product with collections.edges = [] is an orphan.
+
+### Phase 3: Save report
+Save to: `/root/DW-Agents/yolo-agent/logs/orphan-product-report.json`
+Summary to: `/root/DW-Agents/yolo-agent/logs/orphan-product-summary.md`
+
+Format:
+```json
+{
+ "timestamp": "2026-03-23T...",
+ "totalActiveProducts": 50000,
+ "totalOrphans": 500,
+ "orphansByVendor": [
+ {"vendor": "Phyllis Morris", "count": 28, "examples": ["product-1", "product-2"]},
+ {"vendor": "Another Vendor", "count": 15, "examples": ["product-a"]}
+ ],
+ "percentOrphaned": "1.0%"
+}
+```
+
+### Phase 4: Slack notification
+```bash
+curl -X POST -H 'Content-type: application/json' \
+ --data '{"text":"🔍 *Orphan Product Audit Complete*\n• Total active products: X\n• Products with NO collection: X (Y%)\n• Vendors with orphans: Z\n• Top 5 vendors: ...\n• Report: /root/DW-Agents/yolo-agent/logs/orphan-product-summary.md"}' \
+ "https://hooks.slack.com/services/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"
+```
+
+## DO NOT
+- Create collections (audit only — Steve reviews the report)
+- Archive or delete any products
+- Modify any product data
+- This is READ-ONLY
+
+## Verification
+- Report files exist
+- orphansByVendor entries have non-zero counts
+- totalOrphans + products-in-collections ≈ totalActiveProducts
+- Slack notification sent
+
+## Credentials
+- Shopify API token: <redacted:SHOPIFY_ORDERS_TOKEN>
+- GraphQL: https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-01/graphql.json
+- Slack: https://hooks.slack.com/services/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
+
+## Rate Limiting
+- GraphQL: 1000 points/second on standard plan
+- products(first:250) costs ~252 points per call
+- For 50K products = ~200 pages = ~200 calls = ~4 points/sec = safe
+- Add 500ms delay between pages to be conservative
diff --git a/tasks/done/03_p2-06-interior-design-tagger.md b/tasks/done/03_p2-06-interior-design-tagger.md
index d20e61d..52e59af 100644
--- a/tasks/done/03_p2-06-interior-design-tagger.md
+++ b/tasks/done/03_p2-06-interior-design-tagger.md
@@ -28,6 +28,6 @@ python3 /root/DW-Agents/scripts/tag-interior-design.py --vendor <VENDOR_CODE> --
## Key
- Uses Gemini 2.0 Flash for vision analysis
-- API key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- API key: ${GOOGLE_API_KEY}
- Adds style tags (Modern, Traditional), color tags, pattern tags, room application tags
- Tags go on Shopify product tags field
diff --git a/tasks/done/03_p2-06-interior-design-tagger.md.pre-scrub-2026-05-07.bak b/tasks/done/03_p2-06-interior-design-tagger.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..d20e61d
--- /dev/null
+++ b/tasks/done/03_p2-06-interior-design-tagger.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,33 @@
+# P2-06: Interior Design Tagger Automation
+
+Run the Gemini-based interior design tagger on products that are missing AI tags.
+
+## What to do
+
+1. Find products missing AI tags:
+```sql
+psql "postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified" -c "
+SELECT vendor, COUNT(*) as total,
+ COUNT(CASE WHEN ai_tags IS NOT NULL AND ai_tags != '' THEN 1 END) as has_tags
+FROM shopify_products
+WHERE UPPER(status) = 'ACTIVE'
+GROUP BY vendor
+HAVING COUNT(CASE WHEN ai_tags IS NULL OR ai_tags = '' THEN 1 END) > 50
+ORDER BY total DESC
+LIMIT 15;"
+```
+
+2. For the top vendors missing tags, run the tagger script:
+```
+python3 /root/DW-Agents/scripts/tag-interior-design.py --vendor <VENDOR_CODE> --limit 200
+```
+
+3. Focus on vendors that already went through Full Monty (they got tags via Gemini but shopify_products table may not be updated)
+
+4. Report: tags added per vendor
+
+## Key
+- Uses Gemini 2.0 Flash for vision analysis
+- API key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Adds style tags (Modern, Traditional), color tags, pattern tags, room application tags
+- Tags go on Shopify product tags field
diff --git a/tasks/done/04_ai-enrich-arte.md b/tasks/done/04_ai-enrich-arte.md
index 487b4e5..6a64571 100644
--- a/tasks/done/04_ai-enrich-arte.md
+++ b/tasks/done/04_ai-enrich-arte.md
@@ -6,4 +6,4 @@ Same approach for arte vendor_code. Limit 200 per run.
cd /root/DW-Agents/full-monte
node full-monte-batch.js --vendor arte --phase 3 --limit 200
```
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
+Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/04_ai-enrich-arte.md.pre-scrub-2026-05-07.bak b/tasks/done/04_ai-enrich-arte.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..487b4e5
--- /dev/null
+++ b/tasks/done/04_ai-enrich-arte.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,9 @@
+# AI Enrich — Arte (204 products missing AI colors)
+
+Same approach for arte vendor_code. Limit 200 per run.
+
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --vendor arte --phase 3 --limit 200
+```
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
diff --git a/tasks/done/04_china-seas-hex-colors.md b/tasks/done/04_china-seas-hex-colors.md
index 12751a6..e2620ed 100644
--- a/tasks/done/04_china-seas-hex-colors.md
+++ b/tasks/done/04_china-seas-hex-colors.md
@@ -14,7 +14,7 @@ LIMIT 200;
For each product, call Gemini:
```bash
-curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo" \
+curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"contents": [{"parts": [
diff --git a/tasks/done/04_china-seas-hex-colors.md.pre-scrub-2026-05-07.bak b/tasks/done/04_china-seas-hex-colors.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..12751a6
--- /dev/null
+++ b/tasks/done/04_china-seas-hex-colors.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,32 @@
+## Extract Hex Colors for China Seas via Gemini Vision
+
+Use Gemini 2.0 Flash to analyze China Seas wallpaper product images and extract dominant hex colors.
+
+Query china_seas_catalog for all wallpaper products (mfr_sku LIKE '%WP%') that have image_url but no color_hex:
+```sql
+SELECT id, mfr_sku, pattern_name, color_name, image_url
+FROM china_seas_catalog
+WHERE image_url IS NOT NULL AND image_url != ''
+AND (color_hex IS NULL OR color_hex = '')
+AND mfr_sku LIKE '%WP%'
+LIMIT 200;
+```
+
+For each product, call Gemini:
+```bash
+curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "contents": [{"parts": [
+ {"text": "Analyze this wallpaper image. Return ONLY a single hex color code (e.g. #8B4513) representing the dominant/primary color. Just the hex code, nothing else."},
+ {"inlineData": {"mimeType": "image/jpeg", "data": "BASE64_IMAGE"}}
+ ]}]
+ }'
+```
+
+OR if image is a URL, use fileUri approach. Update the DB:
+```sql
+UPDATE china_seas_catalog SET color_hex = '#XXXXXX' WHERE id = {id};
+```
+
+Process in batches of 50. Report total updated.
diff --git a/tasks/done/04_gemini-texture-classify-17k.md b/tasks/done/04_gemini-texture-classify-17k.md
index 3009890..80522b8 100644
--- a/tasks/done/04_gemini-texture-classify-17k.md
+++ b/tasks/done/04_gemini-texture-classify-17k.md
@@ -20,7 +20,7 @@ LIMIT 5000;
```
2. For each product, call Gemini 2.0 Flash:
- - API key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - API key: `${GOOGLE_API_KEY}`
- Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=API_KEY`
- Send image via URL in the request (download image, base64 encode, send as inline_data)
- Prompt: "Classify this wallcovering image. TEXTURE = solid color, natural fiber, faux finish, no repeating decorative pattern. PATTERN = has a decorative design that repeats (florals, stripes, geometric, damask, etc). Respond with ONLY one word: texture or pattern"
diff --git a/tasks/done/04_gemini-texture-classify-17k.md.pre-scrub-2026-05-07.bak b/tasks/done/04_gemini-texture-classify-17k.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..3009890
--- /dev/null
+++ b/tasks/done/04_gemini-texture-classify-17k.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,46 @@
+# Gemini AI Texture Classification — Unclassified Products
+
+## Goal
+Classify products with no `repeat_classification` using Gemini Vision.
+Textures don't need repeat data. Patterns do. This closes the classification gap.
+
+## Step 1: Write and run a Node.js script
+
+Create `/root/DW-Agents/scripts/gemini-texture-classify.js` that does:
+
+1. Query DB for unclassified products:
+```sql
+SELECT id, vendor_code, mfr_sku, image_url
+FROM vendor_catalog
+WHERE on_shopify = true
+ AND (repeat_classification IS NULL OR repeat_classification = '')
+ AND image_url IS NOT NULL AND image_url != ''
+ORDER BY id DESC
+LIMIT 5000;
+```
+
+2. For each product, call Gemini 2.0 Flash:
+ - API key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=API_KEY`
+ - Send image via URL in the request (download image, base64 encode, send as inline_data)
+ - Prompt: "Classify this wallcovering image. TEXTURE = solid color, natural fiber, faux finish, no repeating decorative pattern. PATTERN = has a decorative design that repeats (florals, stripes, geometric, damask, etc). Respond with ONLY one word: texture or pattern"
+
+3. Parse response, update DB:
+```sql
+UPDATE vendor_catalog SET repeat_classification = $1, updated_at = NOW() WHERE id = $2;
+```
+
+4. Rate limit: max 10 requests/second, exponential backoff on 429s
+5. Log progress every 100 products
+6. Batch size: process 5000 per run (YOLO can re-queue for more)
+7. If Gemini returns anything other than "texture" or "pattern", mark as "unknown"
+
+## DB Connection
+`postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+
+## Run
+```bash
+cd /root/DW-Agents/scripts && timeout 900 node gemini-texture-classify.js
+```
+
+Report: total classified, texture count, pattern count, errors.
diff --git a/tasks/done/04_recrawl-low-spec-vendors.md b/tasks/done/04_recrawl-low-spec-vendors.md
index 9f886e1..3690ddf 100644
--- a/tasks/done/04_recrawl-low-spec-vendors.md
+++ b/tasks/done/04_recrawl-low-spec-vendors.md
@@ -38,7 +38,7 @@ ORDER BY n_live_tup DESC;"
## Slack Notification — REQUIRED
```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"VENDOR RE-CRAWL COMPLETE: [X] vendors re-scanned. Total catalog: [Y] products."}' "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"VENDOR RE-CRAWL COMPLETE: [X] vendors re-scanned. Total catalog: [Y] products."}' "${SLACK_WEBHOOK_URL}"
```
## EXCLUSIONS — DO NOT TOUCH IMAGES
diff --git a/tasks/done/04_recrawl-low-spec-vendors.md.pre-scrub-2026-05-07.bak b/tasks/done/04_recrawl-low-spec-vendors.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..9f886e1
--- /dev/null
+++ b/tasks/done/04_recrawl-low-spec-vendors.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,46 @@
+# Re-crawl Low Spec Vendors — Trigger fresh scans
+
+After fixing image and spec columns, re-trigger scans on vendors that need fresh data.
+
+## Re-scan these agents (check they're online first):
+```bash
+# Check each agent, restart if needed, then trigger scan
+for agent in "elise:9623" "dara:9632" "theo:9645" "cleo:9622" "artie:9624" \
+ "sasha:9620" "pj:9627" "maya:9626" "oscar:9640" "ines:9628" \
+ "knox:9621" "kira:9625" "rex:9635" "etta:9631" \
+ "dex:9633" "faye:9634" "graham:9617" \
+ "bjorn:9638" "milo:9639" "hank:9637" "beau:9629" \
+ "wendy:9642" "kurt:9636" "stu:9641"; do
+ IFS=: read name port <<< "$agent"
+ status=$(curl -s -u admin:DWSecure2024! http://127.0.0.1:$port/health 2>/dev/null)
+ if [ -n "$status" ]; then
+ echo "Scanning $name on $port..."
+ curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:$port/api/scan
+ sleep 5 # small gap between triggers
+ else
+ echo "OFFLINE: $name on $port"
+ fi
+done
+```
+
+Wait for all to complete, then run a final audit:
+```sql
+PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
+SELECT relname as catalog,
+ n_live_tup as products
+FROM pg_stat_user_tables
+WHERE relname LIKE '%_catalog' AND n_live_tup > 0
+ORDER BY n_live_tup DESC;"
+```
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
+
+## Slack Notification — REQUIRED
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"VENDOR RE-CRAWL COMPLETE: [X] vendors re-scanned. Total catalog: [Y] products."}' "https://hooks.slack.com/services/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"
+```
+
+## EXCLUSIONS — DO NOT TOUCH IMAGES
+- **Phillip Jeffries (PJ, port 9627)** — Do NOT pull images. Specs only.
+- **Cowtan & Tout** — Do NOT pull images. Skip image fields entirely.
diff --git a/tasks/done/04_run-all-vendor-crawls-high-to-low.md b/tasks/done/04_run-all-vendor-crawls-high-to-low.md
index e47f1e2..c7eb67e 100644
--- a/tasks/done/04_run-all-vendor-crawls-high-to-low.md
+++ b/tasks/done/04_run-all-vendor-crawls-high-to-low.md
@@ -81,6 +81,6 @@ Report a summary at the end: vendor name, products crawled, spec completeness %,
## Slack Notification — REQUIRED
When this task is complete, send a Slack message to Steve with the results summary:
```bash
-curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "${SLACK_WEBHOOK_URL}"
```
Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/04_run-all-vendor-crawls-high-to-low.md.pre-scrub-2026-05-07.bak b/tasks/done/04_run-all-vendor-crawls-high-to-low.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..e47f1e2
--- /dev/null
+++ b/tasks/done/04_run-all-vendor-crawls-high-to-low.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,86 @@
+# Run Vendor Command Center Crawls — High-End to Low-End
+
+Trigger vendor crawls through the Vendor Command Center (Victor, port 9660) in order from highest-end luxury vendors down to mass market. For each vendor, trigger a scan and wait for completion before moving to the next.
+
+## Vendor Order (High-End → Low-End)
+
+### Tier 1 — Ultra Luxury
+1. **Elitis** (Elise, port 9623) — French luxury wallcoverings
+2. **Dedar** (Dara, port 9632) — Italian luxury
+3. **Timorous Beasties** (Theo, port 9645) — Scottish luxury
+4. **Cole & Son** (Cleo, port 9622) — Heritage British luxury
+5. **Arte International** (Artie, port 9624) — Belgian luxury
+
+### Tier 2 — Premium
+6. **Schumacher** (Sasha, port 9620) — American heritage luxury
+7. **Phillip Jeffries** (PJ, port 9627) — Natural materials specialist
+8. **Maya Romanoff** (Maya, port 9626) — Artisan handcrafted
+9. **Osborne & Little** (Oscar, port 9640) — British premium
+10. **Innovations** (Ines, port 9628) — Contract/commercial premium
+
+### Tier 3 — Upper Mid
+11. **Kravet** (Knox, port 9621) — Large design house
+12. **Koroseal** (Kira, port 9625) — Commercial wallcovering
+13. **Romo** (Rex, port 9635) — British design
+14. **1838 Wallcoverings** (Etta, port 9631) — Heritage prints
+15. **Ralph Lauren** (Ralph, port 9608) — Lifestyle luxury
+
+### Tier 4 — Mid Market
+16. **Thibaut** (Thibaut, port 9603) — American classic
+17. **York** (York Contract, port 9618) — American traditional
+18. **Designtex** (Dex, port 9633) — Contract/commercial
+19. **Fabricut** (Faye, port 9634) — Multi-category
+20. **Graham & Brown** (Graham, port 9617) — British modern
+
+### Tier 5 — Specialty & Niche
+21. **BN Walls** (Bjorn, port 9638) — Dutch wallcovering
+22. **Mind the Gap** (Milo, port 9639) — Eclectic design
+23. **Hygge & West** (Hank, port 9637) — Modern artisan
+24. **Bespoke** (Beau, port 9629) — Custom wallcovering
+25. **WallQuest** (Wendy, port 9642) — Decorative
+26. **Knoll** (Kurt, port 9636) — Contract furniture/wall
+27. **Stout Textiles** (Stu, port 9641) — Textile specialist
+
+### Tier 6 — Mass Market / Other
+28. **Brewster** (Brewster, port 9600) — Mass market
+29. **Contrado** (Carlo, port 9643) — Print-on-demand
+30. **Mural Source** (Murray, port 9644) — Custom murals
+31. **Arteriors** (Ari, port 9646) — Home accessories
+32. **Folia Fabrics** (Flora, port 9647) — Fabric specialist
+
+## How to Run Each
+For each vendor, check if the agent is online first:
+```bash
+pm2 list | grep <pm2-name>
+curl -s -u admin:DWSecure2024! http://127.0.0.1:<port>/api/status
+```
+
+If agent is online, trigger a scan:
+```bash
+curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:<port>/api/scan
+```
+
+Or via VCC:
+```bash
+curl -s -u admin:DWSecure2024! -X POST http://127.0.0.1:9660/api/vendors/<vendor_code>/scan
+```
+
+Wait for each to complete (check /api/status until scan_running=false) before starting the next.
+
+**ALL agents must capture: ALL specs, ALL images (pipe-separated in all_images), ALL catalog data.**
+
+Skip any vendors that are offline or erroring — just note them and move on.
+
+Report a summary at the end: vendor name, products crawled, spec completeness %, images captured.
+
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY. Downloading product data FROM other vendors' Shopify stores for catalog data is fine and expected. product-scheduler and schedule-engine are STOPPED intentionally. Do NOT restart them.**
+
+
+## Slack Notification — REQUIRED
+When this task is complete, send a Slack message to Steve with the results summary:
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "https://hooks.slack.com/services/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"
+```
+Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/05_ai-enrich-remaining.md b/tasks/done/05_ai-enrich-remaining.md
index 08b0a3a..fa19224 100644
--- a/tasks/done/05_ai-enrich-remaining.md
+++ b/tasks/done/05_ai-enrich-remaining.md
@@ -7,4 +7,4 @@ For each vendor_code in [black_edition, PRL, zinc_textile, kirkby_design]:
cd /root/DW-Agents/full-monte
node full-monte-batch.js --vendor {vendor} --phase 3 --limit 200
```
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
+Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
diff --git a/tasks/done/05_ai-enrich-remaining.md.pre-scrub-2026-05-07.bak b/tasks/done/05_ai-enrich-remaining.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..08b0a3a
--- /dev/null
+++ b/tasks/done/05_ai-enrich-remaining.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,10 @@
+# AI Enrich — Remaining Vendors (Black Edition 195, PRL 186, Zinc 100, Kirkby 76)
+
+Run Phase 3 enrichment for these 4 smaller vendors. Process all of them in sequence.
+
+For each vendor_code in [black_edition, PRL, zinc_textile, kirkby_design]:
+```bash
+cd /root/DW-Agents/full-monte
+node full-monte-batch.js --vendor {vendor} --phase 3 --limit 200
+```
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
diff --git a/tasks/done/05_china-seas-interior-tagger.md b/tasks/done/05_china-seas-interior-tagger.md
index 2cf98fc..e91e213 100644
--- a/tasks/done/05_china-seas-interior-tagger.md
+++ b/tasks/done/05_china-seas-interior-tagger.md
@@ -4,7 +4,7 @@ Analyze all China Seas wallpaper products and tag them with interior design term
For each wallpaper product on Shopify (china_seas_catalog WHERE shopify_product_id IS NOT NULL AND mfr_sku LIKE '%WP%'):
-Use Gemini 2.0 Flash (key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo) to analyze the product image and generate tags.
+Use Gemini 2.0 Flash (key: ${GOOGLE_API_KEY}) to analyze the product image and generate tags.
Prompt for Gemini:
"Analyze this wallpaper image as an interior designer. Provide:
diff --git a/tasks/done/05_china-seas-interior-tagger.md.pre-scrub-2026-05-07.bak b/tasks/done/05_china-seas-interior-tagger.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2cf98fc
--- /dev/null
+++ b/tasks/done/05_china-seas-interior-tagger.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,27 @@
+## Run Interior Design Tagger on China Seas Wallpapers
+
+Analyze all China Seas wallpaper products and tag them with interior design terminology.
+
+For each wallpaper product on Shopify (china_seas_catalog WHERE shopify_product_id IS NOT NULL AND mfr_sku LIKE '%WP%'):
+
+Use Gemini 2.0 Flash (key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo) to analyze the product image and generate tags.
+
+Prompt for Gemini:
+"Analyze this wallpaper image as an interior designer. Provide:
+1. Style period (e.g., Mid-Century Modern, Art Deco, Contemporary, Traditional, Chinoiserie, Botanical)
+2. Pattern type (e.g., Floral, Geometric, Damask, Toile, Animal Print, Stripe, Abstract)
+3. Color family (e.g., Warm Neutrals, Cool Blues, Earth Tones, Jewel Tones)
+4. Recommended rooms (e.g., Living Room, Bedroom, Dining Room, Powder Room, Hallway)
+5. Design mood (e.g., Elegant, Playful, Dramatic, Serene, Bold)
+Return as JSON: {style, pattern, colorFamily, rooms, mood}"
+
+Save results to the `design` column and update Shopify tags to include the new design tags.
+
+Use Shopify REST API to update tags:
+PUT /admin/api/2024-01/products/{id}.json
+{"product":{"id":ID,"tags":"existing,tags,New Style Tag,New Pattern Tag"}}
+
+Token: <redacted:SHOPIFY_ADMIN_TOKEN>
+Store: designer-laboratory-sandbox.myshopify.com
+
+Process first 100 products. Report results.
diff --git a/tasks/done/05_crawl-report-for-steve.md b/tasks/done/05_crawl-report-for-steve.md
index 0d265ce..02e6765 100644
--- a/tasks/done/05_crawl-report-for-steve.md
+++ b/tasks/done/05_crawl-report-for-steve.md
@@ -38,6 +38,6 @@ Save the report to `/root/DW-Agents/logs/overnight-crawl-report.md`
## Slack Notification — REQUIRED
When this task is complete, send a Slack message to Steve with the results summary:
```bash
-curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "${SLACK_WEBHOOK_URL}"
```
Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/05_crawl-report-for-steve.md.pre-scrub-2026-05-07.bak b/tasks/done/05_crawl-report-for-steve.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..0d265ce
--- /dev/null
+++ b/tasks/done/05_crawl-report-for-steve.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,43 @@
+# Morning Report for Steve — Crawl Results Summary
+
+Generate a comprehensive report of ALL overnight crawl activity. Steve is waking up and wants to see results.
+
+## Check ALL catalog tables
+```sql
+PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
+SELECT table_name, n_live_tup as row_count
+FROM pg_stat_user_tables
+WHERE table_name LIKE '%_catalog'
+ORDER BY n_live_tup DESC;"
+```
+
+## For each catalog, report:
+- Total products
+- Products with images (all_images not empty)
+- Products with width
+- Products with repeat_v
+- Products with material
+- Products with fire_rating
+- Price range
+
+## Also check:
+1. Are Sara, Nadia, Marco still running or completed?
+2. Any PM2 agents that crashed overnight?
+3. Disk space and memory usage
+4. Any errors in logs?
+
+## Format as a clean summary table that's easy to scan.
+
+Save the report to `/root/DW-Agents/logs/overnight-crawl-report.md`
+
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY. Downloading product data FROM other vendors' Shopify stores for catalog data is fine and expected. product-scheduler and schedule-engine are STOPPED intentionally. Do NOT restart them.**
+
+
+## Slack Notification — REQUIRED
+When this task is complete, send a Slack message to Steve with the results summary:
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d "{\"text\":\"TASK COMPLETE: [task name here]\\n\\n[brief results summary]\"}" "https://hooks.slack.com/services/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"
+```
+Replace [task name] and [results summary] with actual values. Keep it concise — 3-5 lines max.
diff --git a/tasks/done/05_final-audit-report.md b/tasks/done/05_final-audit-report.md
index 605d8e7..90c6c48 100644
--- a/tasks/done/05_final-audit-report.md
+++ b/tasks/done/05_final-audit-report.md
@@ -31,7 +31,7 @@ FROM xxx_catalog;
## Slack Notification — REQUIRED (send FULL summary)
```bash
-curl -s -X POST -H "Content-Type: application/json" -d '{"text":"MORNING AUDIT COMPLETE\n\nTotal catalogs: [X]\nTotal products: [Y]\nWith images: [Z]%\nWith specs: [W]%\nWith fire ratings: [F]%\n\nFull report: /root/DW-Agents/logs/morning-audit-report.md"}' "https://hooks.slack.com/services/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"
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"MORNING AUDIT COMPLETE\n\nTotal catalogs: [X]\nTotal products: [Y]\nWith images: [Z]%\nWith specs: [W]%\nWith fire ratings: [F]%\n\nFull report: /root/DW-Agents/logs/morning-audit-report.md"}' "${SLACK_WEBHOOK_URL}"
```
## CRITICAL RULE
diff --git a/tasks/done/05_final-audit-report.md.pre-scrub-2026-05-07.bak b/tasks/done/05_final-audit-report.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..605d8e7
--- /dev/null
+++ b/tasks/done/05_final-audit-report.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,38 @@
+# Final Audit Report — Full Catalog Status
+
+Generate a comprehensive audit of ALL catalog tables and send to Steve via Slack.
+
+## Run this audit:
+```sql
+PGPASSWORD=DW2024SecurePass psql -h 127.0.0.1 -U dw_admin -d dw_unified -c "
+SELECT relname as catalog, n_live_tup as products
+FROM pg_stat_user_tables
+WHERE relname LIKE '%_catalog' AND n_live_tup > 0
+ORDER BY n_live_tup DESC;"
+```
+
+## For the top 20 catalogs, check spec fill rates:
+For each table, run:
+```sql
+SELECT
+ COUNT(*) as total,
+ COUNT(NULLIF(image_url,'')) as has_image,
+ COUNT(NULLIF(all_images,'')) as has_all_images,
+ COUNT(NULLIF(width,'')) as has_width,
+ COUNT(NULLIF(repeat_v,'')) as has_repeat,
+ COUNT(NULLIF(material,'')) as has_material,
+ COUNT(NULLIF(fire_rating,'')) as has_fire_rating,
+ COUNT(NULLIF(body_html,'')) as has_body_html
+FROM xxx_catalog;
+```
+
+## Save report to:
+`/root/DW-Agents/logs/morning-audit-report.md`
+
+## Slack Notification — REQUIRED (send FULL summary)
+```bash
+curl -s -X POST -H "Content-Type: application/json" -d '{"text":"MORNING AUDIT COMPLETE\n\nTotal catalogs: [X]\nTotal products: [Y]\nWith images: [Z]%\nWith specs: [W]%\nWith fire ratings: [F]%\n\nFull report: /root/DW-Agents/logs/morning-audit-report.md"}' "https://hooks.slack.com/services/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"
+```
+
+## CRITICAL RULE
+**DO NOT import anything INTO the DW Shopify store. PostgreSQL catalog tables ONLY.**
diff --git a/tasks/done/06_hollywood-gemini-hex.md b/tasks/done/06_hollywood-gemini-hex.md
index f6054f5..f444f9c 100644
--- a/tasks/done/06_hollywood-gemini-hex.md
+++ b/tasks/done/06_hollywood-gemini-hex.md
@@ -1,7 +1,7 @@
Hollywood Gemini hex color extraction — same pattern as DWPR.
DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Gemini key: ${GOOGLE_API_KEY}
Model: gemini-2.0-flash
1. Check how many Hollywood products have image_url but no hex color data:
diff --git a/tasks/done/06_hollywood-gemini-hex.md.pre-scrub-2026-05-07.bak b/tasks/done/06_hollywood-gemini-hex.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..f6054f5
--- /dev/null
+++ b/tasks/done/06_hollywood-gemini-hex.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,14 @@
+Hollywood Gemini hex color extraction — same pattern as DWPR.
+
+DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Model: gemini-2.0-flash
+
+1. Check how many Hollywood products have image_url but no hex color data:
+ SELECT COUNT(*) FROM hollywood_catalog WHERE image_url IS NOT NULL AND image_url != '' AND (color_primary IS NULL OR color_primary = '');
+2. Find the DWPR Gemini hex extraction script in /root/DW-Agents/vendor-scrapers/ (gemini-color*.py or similar)
+3. Adapt it for hollywood_catalog — send each product image to Gemini vision, extract dominant hex color
+4. Save hex to color_primary column
+5. Use Gemini Batch API if >100 products (50% cheaper)
+6. Rate limit: max 15 req/min for sync Gemini calls
+7. Report how many products got hex colors.
diff --git a/tasks/done/06_hollywood-hex-extraction.md b/tasks/done/06_hollywood-hex-extraction.md
index 2a43bc3..8c6320d 100644
--- a/tasks/done/06_hollywood-hex-extraction.md
+++ b/tasks/done/06_hollywood-hex-extraction.md
@@ -10,7 +10,7 @@ Sessions #99 and #102 noted that Hollywood Wallcoverings (4,770 products) need G
5. Generate color tags from the hex values
6. Report: how many products processed, hex codes found, any errors
-Gemini API key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+Gemini API key: `${GOOGLE_API_KEY}`
Model: `gemini-2.0-flash`
Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={key}`
diff --git a/tasks/done/06_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bak b/tasks/done/06_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2a43bc3
--- /dev/null
+++ b/tasks/done/06_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,21 @@
+## Hollywood Gemini Hex Extraction
+
+Sessions #99 and #102 noted that Hollywood Wallcoverings (4,770 products) need Gemini hex color extraction — same process that was completed for Phillipe Romano (1,622 products).
+
+### Tasks:
+1. Check how many Hollywood products already have hex data in `product_colors` table
+2. Query `hollywood_catalog` for active products with images (image_url IS NOT NULL)
+3. Use Gemini 2.0 Flash to analyze product images and extract dominant hex color
+4. Store results in `product_colors` table (same schema as DWPR products)
+5. Generate color tags from the hex values
+6. Report: how many products processed, hex codes found, any errors
+
+Gemini API key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+Model: `gemini-2.0-flash`
+Endpoint: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={key}`
+
+Reference script: Look at how DWPR hex extraction was done in session #99
+DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+
+IMPORTANT: This is a large batch (4,770 products). Use rate limiting (max 10 req/s to Gemini).
+If batch is >100 products, consider using Gemini Batch API for 50% cost savings.
diff --git a/tasks/done/08_phase3-enrichment-run.md b/tasks/done/08_phase3-enrichment-run.md
index b786897..9481378 100644
--- a/tasks/done/08_phase3-enrichment-run.md
+++ b/tasks/done/08_phase3-enrichment-run.md
@@ -5,7 +5,7 @@ Connect to PostgreSQL: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_
1. Find the catalog tables: SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name LIKE '%catalog%' ORDER BY table_name;
2. For each catalog table, find products with image_url but NULL ai_colors. Limit to 20 products per batch.
3. For each product image, call Gemini Vision API:
- curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo" \
+ curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"contents":[{"parts":[{"text":"Analyze this wallcovering image. Return JSON with: colors (array of {name, hex, percentage}), background_color ({name, hex}), styles (array of strings like Traditional, Modern, etc), patterns (array of strings), image_type (scan_swatch, photo_full, etc). Be precise with hex codes."},{"inline_data":{"mime_type":"image/jpeg","data":"BASE64_HERE"}}]}]}'
4. Download each image with curl, base64 encode it, send to Gemini
diff --git a/tasks/done/08_phase3-enrichment-run.md.pre-scrub-2026-05-07.bak b/tasks/done/08_phase3-enrichment-run.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..b786897
--- /dev/null
+++ b/tasks/done/08_phase3-enrichment-run.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,13 @@
+Run Phase 3 AI enrichment on products that have images but no AI color data.
+
+Connect to PostgreSQL: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
+
+1. Find the catalog tables: SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name LIKE '%catalog%' ORDER BY table_name;
+2. For each catalog table, find products with image_url but NULL ai_colors. Limit to 20 products per batch.
+3. For each product image, call Gemini Vision API:
+ curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo" \
+ -H "Content-Type: application/json" \
+ -d '{"contents":[{"parts":[{"text":"Analyze this wallcovering image. Return JSON with: colors (array of {name, hex, percentage}), background_color ({name, hex}), styles (array of strings like Traditional, Modern, etc), patterns (array of strings), image_type (scan_swatch, photo_full, etc). Be precise with hex codes."},{"inline_data":{"mime_type":"image/jpeg","data":"BASE64_HERE"}}]}]}'
+4. Download each image with curl, base64 encode it, send to Gemini
+5. Update the catalog table with the AI results
+6. Report how many products were enriched
diff --git a/tasks/done/08_slack-notify-completion.md b/tasks/done/08_slack-notify-completion.md
index 8353279..9ca1e09 100644
--- a/tasks/done/08_slack-notify-completion.md
+++ b/tasks/done/08_slack-notify-completion.md
@@ -3,7 +3,7 @@
After all previous tasks are done, send a summary to Slack.
```bash
-curl -s -X POST 'https://hooks.slack.com/services/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' \
+curl -s -X POST '${SLACK_WEBHOOK_URL}' \
-H 'Content-Type: application/json' \
-d '{
"text": "✅ *Agent Abrams Content Overhaul — COMPLETE*\n\n*Blog:*\n• 134 junk posts deleted\n• 18 posts rewritten (professional, no jargon)\n• Astro site rebuilt and live\n• Blog generator fixed (dedup + daily limit + sanitization)\n\n*Videos:*\n• HTML video pipeline built (Puppeteer + CSS animations)\n• 18 new videos created (landscape + vertical)\n• All uploaded to YouTube\n• Old videos unlisted\n\n*All content sanitized. Zero leaks.*\n\nCheck goodquestion.ai and youtube.com/@AgentAbrams"
diff --git a/tasks/done/08_slack-notify-completion.md.pre-scrub-2026-05-07.bak b/tasks/done/08_slack-notify-completion.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..8353279
--- /dev/null
+++ b/tasks/done/08_slack-notify-completion.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,11 @@
+# Send Completion Report to Slack
+
+After all previous tasks are done, send a summary to Slack.
+
+```bash
+curl -s -X POST 'https://hooks.slack.com/services/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' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "text": "✅ *Agent Abrams Content Overhaul — COMPLETE*\n\n*Blog:*\n• 134 junk posts deleted\n• 18 posts rewritten (professional, no jargon)\n• Astro site rebuilt and live\n• Blog generator fixed (dedup + daily limit + sanitization)\n\n*Videos:*\n• HTML video pipeline built (Puppeteer + CSS animations)\n• 18 new videos created (landscape + vertical)\n• All uploaded to YouTube\n• Old videos unlisted\n\n*All content sanitized. Zero leaks.*\n\nCheck goodquestion.ai and youtube.com/@AgentAbrams"
+ }'
+```
diff --git a/tasks/done/09_ai-enrich-zoffany.md b/tasks/done/09_ai-enrich-zoffany.md
index 9e5bafc..6e087cf 100644
--- a/tasks/done/09_ai-enrich-zoffany.md
+++ b/tasks/done/09_ai-enrich-zoffany.md
@@ -8,5 +8,5 @@ SELECT table_name FROM information_schema.tables WHERE table_name ILIKE '%zoff%'
Find the Zoffany catalog table and run Phase 3 AI enrichment on all products with images but no ai_colors.
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
+Gemini key: ${GOOGLE_API_KEY} | Model: gemini-2.0-flash
Limit 200 per run.
diff --git a/tasks/done/09_ai-enrich-zoffany.md.pre-scrub-2026-05-07.bak b/tasks/done/09_ai-enrich-zoffany.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..9e5bafc
--- /dev/null
+++ b/tasks/done/09_ai-enrich-zoffany.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,12 @@
+# AI Enrich — Zoffany (0 of 302 Shopify products enriched)
+
+Zoffany products are in w1838_catalog table (vendor_code w1838 in vendor_registry, but actual catalog table is w1838_catalog).
+Wait — Zoffany has its OWN table. Check:
+```sql
+SELECT table_name FROM information_schema.tables WHERE table_name ILIKE '%zoff%';
+```
+
+Find the Zoffany catalog table and run Phase 3 AI enrichment on all products with images but no ai_colors.
+
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo | Model: gemini-2.0-flash
+Limit 200 per run.
diff --git a/tasks/done/10_catalog-spec-fill-audit.md b/tasks/done/10_catalog-spec-fill-audit.md
index f7ccc48..a9b7366 100644
--- a/tasks/done/10_catalog-spec-fill-audit.md
+++ b/tasks/done/10_catalog-spec-fill-audit.md
@@ -7,6 +7,6 @@ Working directory: /root/DW-Agents
3. Calculate fill percentages per vendor per field
4. Identify the 10 worst-performing catalogs (most empty fields)
5. Save a markdown report to /root/DW-Agents/logs/catalog-spec-audit-$(date +%Y%m%d).md
-6. Send a Slack notification summary via: curl -X POST -H 'Content-type: application/json' --data '{"text":"Catalog Spec Audit Complete - see logs/catalog-spec-audit report"}' https://hooks.slack.com/services/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
+6. Send a Slack notification summary via: curl -X POST -H 'Content-type: application/json' --data '{"text":"Catalog Spec Audit Complete - see logs/catalog-spec-audit report"}' ${SLACK_WEBHOOK_URL}
Focus on actionable insights — which vendors need the most scraper improvements.
diff --git a/tasks/done/10_catalog-spec-fill-audit.md.pre-scrub-2026-05-07.bak b/tasks/done/10_catalog-spec-fill-audit.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..f7ccc48
--- /dev/null
+++ b/tasks/done/10_catalog-spec-fill-audit.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,12 @@
+Run a comprehensive spec fill rate audit across all vendor catalog tables.
+
+Working directory: /root/DW-Agents
+
+1. Connect to postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
+2. Query every table ending in '_catalog' to check fill rates for: image_url, all_images, width, material, repeat_v, fire_rating, body_html, mfr_sku
+3. Calculate fill percentages per vendor per field
+4. Identify the 10 worst-performing catalogs (most empty fields)
+5. Save a markdown report to /root/DW-Agents/logs/catalog-spec-audit-$(date +%Y%m%d).md
+6. Send a Slack notification summary via: curl -X POST -H 'Content-type: application/json' --data '{"text":"Catalog Spec Audit Complete - see logs/catalog-spec-audit report"}' https://hooks.slack.com/services/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
+
+Focus on actionable insights — which vendors need the most scraper improvements.
diff --git a/tasks/done/11_interior-design-tagger.md b/tasks/done/11_interior-design-tagger.md
index 92b9967..d735639 100644
--- a/tasks/done/11_interior-design-tagger.md
+++ b/tasks/done/11_interior-design-tagger.md
@@ -4,7 +4,7 @@ From Session 101: The interior-design-tagger skill exists at /root/.claude/skill
DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
Tables: justindavid_catalog, phillipe_romano_catalog (~9,392 products total)
-Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Gemini key: ${GOOGLE_API_KEY}
Model: gemini-2.0-flash (vision)
1. Read the skill file to understand the tagging logic
diff --git a/tasks/done/11_interior-design-tagger.md.pre-scrub-2026-05-07.bak b/tasks/done/11_interior-design-tagger.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..92b9967
--- /dev/null
+++ b/tasks/done/11_interior-design-tagger.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,20 @@
+Interior design tagger automation — build and run on justindavid + phillipe_romano catalogs.
+
+From Session 101: The interior-design-tagger skill exists at /root/.claude/skills/interior-design-tagger/SKILL.md but NO automated pipeline runs it.
+
+DB: postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified
+Tables: justindavid_catalog, phillipe_romano_catalog (~9,392 products total)
+Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+Model: gemini-2.0-flash (vision)
+
+1. Read the skill file to understand the tagging logic
+2. Count products needing tagging in both tables (interior_design_status IS NULL or != 'done')
+3. Build a Python script /root/DW-Agents/vendor-scrapers/interior-design-tagger.py that:
+ - Queries products with image_url but no interior_design_status
+ - Sends image to Gemini vision with the skill prompt
+ - Parses response for design tags (style, room type, color palette, pattern)
+ - Updates DB with tags and sets interior_design_status = 'done'
+ - Rate limits to 15 req/min
+4. Run on a small batch first (--limit 20)
+5. If working, run full batch (use Gemini Batch API for 50% savings if >100)
+6. Report: how many tagged, sample output
diff --git a/tasks/done/12_weekly-relink-orphans.md b/tasks/done/12_weekly-relink-orphans.md
index 4a31345..ed03954 100644
--- a/tasks/done/12_weekly-relink-orphans.md
+++ b/tasks/done/12_weekly-relink-orphans.md
@@ -24,7 +24,7 @@
4. If orphan count dropped by >10 since last run, post a Slack notification via:
```
- curl -X POST https://hooks.slack.com/services/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 \
+ curl -X POST ${SLACK_WEBHOOK_URL} \
-H 'Content-Type: application/json' \
-d '{"text": "Weekly relink: relinked N rows, drift now M.MM%"}'
```
diff --git a/tasks/done/12_weekly-relink-orphans.md.pre-scrub-2026-05-07.bak b/tasks/done/12_weekly-relink-orphans.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..4a31345
--- /dev/null
+++ b/tasks/done/12_weekly-relink-orphans.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,46 @@
+# Weekly Relink Orphans Run
+
+**Context**: The `relink-orphans.js` script matches Shopify orphan products to their vendor catalog rows by mfr_sku. It should run weekly to catch new drift before it accumulates. The last manual LIVE run (2026-04-10) relinked 3,979 rows.
+
+**Goal**: Run the relink script in LIVE mode, report the results, log to Slack if significant changes.
+
+## Steps
+
+1. Run the relink script LIVE:
+ ```bash
+ cd /root/DW-Agents && timeout 600 node scripts/relink-orphans.js --live 2>&1 | tee /root/DW-Agents/logs/relink-weekly-$(date +%Y%m%d).log
+ ```
+
+2. Parse the output to extract:
+ - Total orphans processed
+ - Can auto-relink count
+ - Updated catalog rows count
+ - Failures
+
+3. Run the drift audit to see the post-relink state:
+ ```bash
+ timeout 60 node /root/DW-Agents/scripts/drift-audit.js
+ ```
+
+4. If orphan count dropped by >10 since last run, post a Slack notification via:
+ ```
+ curl -X POST https://hooks.slack.com/services/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 \
+ -H 'Content-Type: application/json' \
+ -d '{"text": "Weekly relink: relinked N rows, drift now M.MM%"}'
+ ```
+
+5. Log result to `/root/DW-Agents/logs/relink-weekly.log` with date and counts.
+
+## Success Criteria
+- Script runs to completion
+- drift_audit_log has a fresh row
+- Slack notification sent (if threshold met)
+- Log file updated
+
+## References
+- Relink script: `/root/DW-Agents/scripts/relink-orphans.js`
+- Drift audit: `/root/DW-Agents/scripts/drift-audit.js`
+- Previous session commits: fa093bd59, dff14da82, ac1481da7, 4423ead47, 6538f96fd
+- Current state (2026-04-10): 1,690 orphans (3.27%), target <5%
+
+**Time budget**: 10 minutes.
diff --git a/tasks/done/13_hollywood-hex-extraction.md b/tasks/done/13_hollywood-hex-extraction.md
index 0270973..c17c05e 100644
--- a/tasks/done/13_hollywood-hex-extraction.md
+++ b/tasks/done/13_hollywood-hex-extraction.md
@@ -12,6 +12,6 @@ Sessions #99 and #102: Hollywood's 4,770 products need Gemini hex color extracti
7. For batches >100, use Gemini Batch API (50% cheaper)
8. Report: processed count, hex codes found, errors
-Gemini API key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+Gemini API key: `${GOOGLE_API_KEY}`
Model: `gemini-2.0-flash`
DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/13_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bak b/tasks/done/13_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..0270973
--- /dev/null
+++ b/tasks/done/13_hollywood-hex-extraction.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,17 @@
+## Hollywood Wallcoverings Gemini Hex Extraction
+
+Sessions #99 and #102: Hollywood's 4,770 products need Gemini hex color extraction (same as completed for Phillipe Romano).
+
+### Tasks:
+1. Check `product_colors` table for existing Hollywood entries
+2. Query `hollywood_catalog` for active products with `image_url IS NOT NULL`
+3. For each product image, call Gemini 2.0 Flash to extract dominant hex color
+4. Insert into `product_colors` table with vendor='Hollywood Wallcoverings'
+5. Generate color tags from hex values
+6. Use rate limiting: max 10 req/s to Gemini API
+7. For batches >100, use Gemini Batch API (50% cheaper)
+8. Report: processed count, hex codes found, errors
+
+Gemini API key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+Model: `gemini-2.0-flash`
+DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
diff --git a/tasks/done/19_zero-repeat-textures.md b/tasks/done/19_zero-repeat-textures.md
index 87cf0cf..3453364 100644
--- a/tasks/done/19_zero-repeat-textures.md
+++ b/tasks/done/19_zero-repeat-textures.md
@@ -27,7 +27,7 @@ For these, set repeat_v = '0' and repeat_h = '0' directly — name match is suff
For products that still have no repeat AND didn't match the name pattern:
1. Send the product's `image_url` to Gemini 2.0 Flash vision:
- - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
2. If Gemini says TEXTURE → set repeat_v = '0', repeat_h = '0'
@@ -43,7 +43,7 @@ Run for: `versace_catalog`, `thibaut_catalog`, `kravet_catalog`, `schumacher_cat
## Important
- Run AFTER tasks 13-17 (scraping) complete — this handles the leftovers
-- Gemini analysis key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+- Gemini analysis key: `${GOOGLE_API_KEY}`
- Rate limit Gemini to 10 req/s (flash is generous)
- Track Gemini costs with the shared tracker
- Report per vendor: how many set to zero by name, how many by Gemini, how many still unknown
diff --git a/tasks/done/19_zero-repeat-textures.md.pre-scrub-2026-05-07.bak b/tasks/done/19_zero-repeat-textures.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..87cf0cf
--- /dev/null
+++ b/tasks/done/19_zero-repeat-textures.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,50 @@
+# Set Repeat = 0 for Texture Products (No Pattern = No Repeat)
+
+## Context
+- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+- Many products missing repeat aren't patterns — they're TEXTURES (grasscloth, linen, stucco, metallic, plain, solid)
+- Textures have zero repeat. If a scraper can't find a repeat, AND the image confirms it's a texture, set repeat_v = '0' and repeat_h = '0'
+
+## Task
+For each vendor catalog table that has products still missing repeats AFTER the scraper tasks run:
+
+### Step 1: Identify likely textures from names/descriptions
+```sql
+-- Products missing repeat that are likely textures based on name/description
+SELECT id, sku, name, product_url, image_url
+FROM {catalog_table}
+WHERE (repeat_v IS NULL OR repeat_v = '' )
+ AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
+ AND (
+ LOWER(name) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave|burlap|faux |jute|suede|cork|hemp|raffia|sand|stone|concrete|plaster|canvas)'
+ OR LOWER(COALESCE(description, '')) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave)'
+ );
+```
+
+For these, set repeat_v = '0' and repeat_h = '0' directly — name match is sufficient.
+
+### Step 2: For remaining unknown products, use Gemini Vision
+For products that still have no repeat AND didn't match the name pattern:
+
+1. Send the product's `image_url` to Gemini 2.0 Flash vision:
+ - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
+
+2. If Gemini says TEXTURE → set repeat_v = '0', repeat_h = '0'
+3. If Gemini says PATTERN → leave repeat as NULL (needs scraping or manual entry)
+4. Log all Gemini calls and track cost via `require('/root/DW-Agents/shared/gemini-cost-tracker.js')`
+
+### Step 3: Update DB
+```sql
+UPDATE {catalog_table} SET repeat_v = '0', repeat_h = '0', updated_at = NOW() WHERE id = $1;
+```
+
+Run for: `versace_catalog`, `thibaut_catalog`, `kravet_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
+
+## Important
+- Run AFTER tasks 13-17 (scraping) complete — this handles the leftovers
+- Gemini analysis key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+- Rate limit Gemini to 10 req/s (flash is generous)
+- Track Gemini costs with the shared tracker
+- Report per vendor: how many set to zero by name, how many by Gemini, how many still unknown
+- Do NOT ask questions — run autonomously
diff --git a/tasks/done/25_zero-repeat-textures-r2.md b/tasks/done/25_zero-repeat-textures-r2.md
index 39bfc6f..5f1efef 100644
--- a/tasks/done/25_zero-repeat-textures-r2.md
+++ b/tasks/done/25_zero-repeat-textures-r2.md
@@ -26,7 +26,7 @@ For these, set repeat_v = '0' and repeat_h = '0' directly — name match is suff
For products that still have no repeat AND didn't match the name pattern:
1. Send the product's `image_url` to Gemini 2.0 Flash vision:
- - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
2. If Gemini says TEXTURE -> set repeat_v = '0', repeat_h = '0'
@@ -43,7 +43,7 @@ Run for: `thibaut_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_cata
## Important
- Run AFTER tasks 21-23 (scraping) complete — this handles the leftovers
-- Gemini analysis key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+- Gemini analysis key: `${GOOGLE_API_KEY}`
- Rate limit Gemini to 10 req/s (flash is generous)
- Track Gemini costs with the shared tracker
- Report per vendor: how many set to zero by name, how many by Gemini, how many still unknown
diff --git a/tasks/done/25_zero-repeat-textures-r2.md.pre-scrub-2026-05-07.bak b/tasks/done/25_zero-repeat-textures-r2.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..39bfc6f
--- /dev/null
+++ b/tasks/done/25_zero-repeat-textures-r2.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,50 @@
+# Set Repeat = 0 for Texture Products — Round 2 (No Pattern = No Repeat)
+
+## Context
+- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+- Many products missing repeat aren't patterns — they're TEXTURES (grasscloth, linen, stucco, metallic, plain, solid)
+- Textures have zero repeat. If a scraper can't find a repeat, AND the image confirms it's a texture, set repeat_v = '0' and repeat_h = '0'
+
+## Task
+For each vendor catalog table that has products still missing repeats AFTER the R2 scraper tasks run:
+
+### Step 1: Identify likely textures from names/descriptions
+```sql
+SELECT id, sku, name, product_url, image_url
+FROM {catalog_table}
+WHERE (repeat_v IS NULL OR repeat_v = '' )
+ AND shopify_product_id IS NOT NULL AND shopify_product_id != ''
+ AND (
+ LOWER(name) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave|burlap|faux |jute|suede|cork|hemp|raffia|sand|stone|concrete|plaster|canvas)'
+ OR LOWER(COALESCE(description, '')) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave)'
+ );
+```
+
+For these, set repeat_v = '0' and repeat_h = '0' directly — name match is sufficient.
+
+### Step 2: For remaining unknown products, use Gemini Vision
+For products that still have no repeat AND didn't match the name pattern:
+
+1. Send the product's `image_url` to Gemini 2.0 Flash vision:
+ - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
+
+2. If Gemini says TEXTURE -> set repeat_v = '0', repeat_h = '0'
+3. If Gemini says PATTERN -> leave repeat as NULL (needs scraping or manual entry)
+4. Log all Gemini calls and track cost via `require('/root/DW-Agents/shared/gemini-cost-tracker.js')`
+
+### Step 3: Update DB
+```sql
+UPDATE {catalog_table} SET repeat_v = '0', repeat_h = '0', updated_at = NOW() WHERE id = $1;
+```
+
+Run for: `thibaut_catalog`, `schumacher_catalog`, `brewster_catalog`, `york_catalog`
+(Skip kravet_catalog and versace_catalog — completed in round 1)
+
+## Important
+- Run AFTER tasks 21-23 (scraping) complete — this handles the leftovers
+- Gemini analysis key: `AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+- Rate limit Gemini to 10 req/s (flash is generous)
+- Track Gemini costs with the shared tracker
+- Report per vendor: how many set to zero by name, how many by Gemini, how many still unknown
+- Do NOT ask questions — run autonomously
diff --git a/tasks/done/29_texture-classify-remaining-r3.md b/tasks/done/29_texture-classify-remaining-r3.md
index 845ecbf..670be04 100644
--- a/tasks/done/29_texture-classify-remaining-r3.md
+++ b/tasks/done/29_texture-classify-remaining-r3.md
@@ -36,7 +36,7 @@ ORDER BY id;
```
2. Send image to Gemini 2.0 Flash:
- - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}`
- Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
3. If TEXTURE → `UPDATE SET repeat_h = '0', match_type = 'Texture'`
diff --git a/tasks/done/29_texture-classify-remaining-r3.md.pre-scrub-2026-05-07.bak b/tasks/done/29_texture-classify-remaining-r3.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..845ecbf
--- /dev/null
+++ b/tasks/done/29_texture-classify-remaining-r3.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,61 @@
+# Classify Remaining Unknown Products as Texture or Pattern (446 thibaut + 13 brewster)
+
+## Context
+- DB: `postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified`
+- After R3 match_type scraping, some products will STILL have NULL match_type
+- These need Gemini Vision classification: TEXTURE → repeat_h = '0', PATTERN → leave for manual review
+- Also handles 13 brewster products with NULL match_type
+
+## Task
+
+### Step 1: Quick name-based classification
+```sql
+-- Products missing repeat_h that are likely textures based on name
+UPDATE {catalog_table}
+SET repeat_h = '0', match_type = COALESCE(match_type, 'Texture'), updated_at = NOW()
+WHERE (repeat_h IS NULL OR repeat_h = '')
+ AND shopify_product_id IS NOT NULL
+ AND (
+ LOWER(name) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave|burlap|faux |jute|suede|cork|hemp|raffia|sand|stone|concrete|plaster|canvas)'
+ OR LOWER(COALESCE(description, '')) ~ '(grasscloth|texture|linen|stucco|metallic|plain|solid|sisal|weave)'
+ );
+```
+
+Run for: `thibaut_catalog`, `brewster_catalog`
+
+### Step 2: Gemini Vision for remaining unknowns
+For products that still have no repeat_h AND didn't match the name pattern:
+
+1. Query remaining:
+```sql
+SELECT id, name, image_url FROM {catalog_table}
+WHERE (repeat_h IS NULL OR repeat_h = '')
+ AND shopify_product_id IS NOT NULL
+ AND image_url IS NOT NULL AND image_url != ''
+ORDER BY id;
+```
+
+2. Send image to Gemini 2.0 Flash:
+ - API: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo`
+ - Prompt: "Is this wallcovering image a repeating pattern or a non-repeating texture? If it's a texture (grasscloth, linen, plain, solid, stucco, metallic, stone, concrete, weave, etc.), respond 'TEXTURE'. If it has a visible repeating pattern (florals, stripes, damask, geometric, medallion, etc.), respond 'PATTERN'. Respond with only one word: TEXTURE or PATTERN."
+
+3. If TEXTURE → `UPDATE SET repeat_h = '0', match_type = 'Texture'`
+4. If PATTERN → `UPDATE SET match_type = 'Unknown Pattern'` (leave repeat_h NULL for manual)
+
+### Step 3: Final cleanup — assume Straight for remaining
+Any products STILL without repeat_h after Gemini classification:
+```sql
+UPDATE {catalog_table}
+SET repeat_h = '0', match_type = COALESCE(match_type, 'Assumed Straight'), updated_at = NOW()
+WHERE (repeat_h IS NULL OR repeat_h = '')
+ AND shopify_product_id IS NOT NULL;
+```
+
+This ensures 100% completion. Products tagged 'Unknown Pattern' or 'Assumed Straight' can be manually reviewed later.
+
+## Important
+- Run AFTER task 27 (match_type scraping) completes
+- Gemini rate limit: 10 req/s
+- Track costs via `require('/root/DW-Agents/shared/gemini-cost-tracker.js')`
+- Report: how many by name, how many by Gemini (texture vs pattern), how many assumed
+- Do NOT ask questions — run autonomously
diff --git a/tasks/done/AQ_hollywood-imageclean-and-spin.md b/tasks/done/AQ_hollywood-imageclean-and-spin.md
index 2fc0c75..353d015 100644
--- a/tasks/done/AQ_hollywood-imageclean-and-spin.md
+++ b/tasks/done/AQ_hollywood-imageclean-and-spin.md
@@ -12,7 +12,7 @@ For each product:
1. Fetch product from Shopify: GET /admin/api/2024-01/products/{id}.json
2. Download the PRIMARY image (position 1, the original swatch) as base64
3. Call Gemini 2.0 Flash vision to check if image has text/logos/watermarks:
- - Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+ - Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GOOGLE_API_KEY}
- Prompt: "Does this image contain text, logos, watermarks, rulers, or website URLs? Answer YES or NO."
- mimeType: auto-detect from magic bytes (0xFF 0xD8 = image/jpeg, 0x89 0x50 = image/png)
- Strip any "data:image/...;base64," prefix before sending to Gemini
@@ -51,7 +51,7 @@ For each product (after clean step):
## Config
- Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
- Store: designer-laboratory-sandbox.myshopify.com
-- Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+- Gemini key: ${GOOGLE_API_KEY}
After writing the script, start it with PM2:
pm2 start /root/DW-Agents/scripts/hollywood-abaca-clean-spin.js --name hollywood-clean-spin --no-autorestart
diff --git a/tasks/done/AQ_hollywood-imageclean-and-spin.md.pre-scrub-2026-05-07.bak b/tasks/done/AQ_hollywood-imageclean-and-spin.md.pre-scrub-2026-05-07.bak
new file mode 100644
index 0000000..2fc0c75
--- /dev/null
+++ b/tasks/done/AQ_hollywood-imageclean-and-spin.md.pre-scrub-2026-05-07.bak
@@ -0,0 +1,59 @@
+Run image cleaning (crop text/logos) and swatch spin generation for ALL 75 Hollywood Abaca Grasscloth products.
+
+The room settings script (hollywood-rooms) is already running but it ONLY does rooms. This task handles the 2 missing steps: imageClean + spin.
+
+Product IDs are in /tmp/hollywood-ids.txt (76 total, skip 1496353341552 which already has both).
+
+Write a Node.js script at /root/DW-Agents/scripts/hollywood-abaca-clean-spin.js that processes each product:
+
+## STEP 1: Image Clean (crop text/logos from swatch)
+
+For each product:
+1. Fetch product from Shopify: GET /admin/api/2024-01/products/{id}.json
+2. Download the PRIMARY image (position 1, the original swatch) as base64
+3. Call Gemini 2.0 Flash vision to check if image has text/logos/watermarks:
+ - Endpoint: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+ - Prompt: "Does this image contain text, logos, watermarks, rulers, or website URLs? Answer YES or NO."
+ - mimeType: auto-detect from magic bytes (0xFF 0xD8 = image/jpeg, 0x89 0x50 = image/png)
+ - Strip any "data:image/...;base64," prefix before sending to Gemini
+4. If DIRTY (YES):
+ a. Save image to /tmp/clean_in_{id}.jpg
+ b. Use ImageMagick to crop: bottom 15%, top 5%, sides 2%:
+ - convert input.jpg -gravity South -chop 0x15% out1.jpg
+ - convert out1.jpg -gravity North -chop 0x5% out2.jpg
+ - convert out2.jpg -gravity West -chop 2%x0 out3.jpg
+ - convert out3.jpg -gravity East -chop 2%x0 -quality 92 final.jpg
+ c. Re-check with Gemini — if still dirty, crop more aggressively (bottom 25%, top 10%)
+ d. Upload cleaned image to Shopify as NEW image at position 1 (replacing old primary)
+ - PUT /admin/api/2024-01/products/{id}/images/{imageId}.json with { image: { attachment: base64 } }
+ e. Clean up /tmp files
+5. If CLEAN (NO): skip, log "clean"
+
+## STEP 2: Swatch Spin
+
+For each product (after clean step):
+1. Check if product already has a spin image (alt text contains "Spin" or "Swatch" or src contains "spin")
+2. If no spin exists, run the spin script:
+ - node /root/Projects/Designer-Wallcoverings/scripts/shopify-swatch-spin.js --product-id {id}
+ - Timeout: 120 seconds
+3. Log result
+
+## Rate Limiting
+- 500ms between Shopify API calls
+- 2s between Gemini calls
+- 3s between products
+- 15s timeout for each ImageMagick command
+
+## Logging
+- Log: "Product X/75: {title} — clean:{dirty|clean|skipped} spin:{done|exists|error}"
+- At end: "COMPLETE: X cleaned, Y spins generated, Z errors"
+
+## Config
+- Shopify token: <redacted:SHOPIFY_ADMIN_TOKEN>
+- Store: designer-laboratory-sandbox.myshopify.com
+- Gemini key: AIzaSyAO0rLKwtJUKcf3zVmKstBS4udct4QejMo
+
+After writing the script, start it with PM2:
+pm2 start /root/DW-Agents/scripts/hollywood-abaca-clean-spin.js --name hollywood-clean-spin --no-autorestart
+
+Report the PM2 name so it can be monitored.
← 0ca26b2 untrack node_modules per standing rule (was tracking 633 fil
·
back to Yolo Agent
·
fix: pgrep -c flag missing on macOS BSD pgrep (was flooding 4bf7765 →