← back to Designer Wallcoverings
color cleaner v2: (1) tri-state safety — removal now requires explicit COLOR=NO; ambiguous/empty verdicts KEEP+retry, never remove (guards the false-remove class of bug); (2) bake exact collection handles into COLOR_COLLECTIONS (incl pink, was null) + resolver prefers cfg.handle — robust on any store, handles are inconsistent (purple/black/coral vs blue-wallpaper-collection)
df60b0ab88d42fa3df18807b43287635aaf7295f · 2026-06-12 15:22:52 -0700 · Steve Abrams
Files touched
M DW-Programming/color-collection-cleaner-v2.js
Diff
commit df60b0ab88d42fa3df18807b43287635aaf7295f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jun 12 15:22:52 2026 -0700
color cleaner v2: (1) tri-state safety — removal now requires explicit COLOR=NO; ambiguous/empty verdicts KEEP+retry, never remove (guards the false-remove class of bug); (2) bake exact collection handles into COLOR_COLLECTIONS (incl pink, was null) + resolver prefers cfg.handle — robust on any store, handles are inconsistent (purple/black/coral vs blue-wallpaper-collection)
---
DW-Programming/color-collection-cleaner-v2.js | 45 ++++++++++++++++-----------
1 file changed, 27 insertions(+), 18 deletions(-)
diff --git a/DW-Programming/color-collection-cleaner-v2.js b/DW-Programming/color-collection-cleaner-v2.js
index ac65a218..5e3a5c92 100644
--- a/DW-Programming/color-collection-cleaner-v2.js
+++ b/DW-Programming/color-collection-cleaner-v2.js
@@ -44,19 +44,19 @@ const LEDGER = path.join(DATA_DIR, 'color-clean-ledger.jsonl');
const BANNER_QUEUE = path.join(DATA_DIR, 'banner-recrawl-queue.jsonl');
const COLOR_COLLECTIONS = {
- blue: { collectionId: 95278104641, tags:['blue','navy','sky','indigo','cobalt','azure','cerulean','sapphire'], word:'BLUE', consider:'navy, indigo, cobalt, sky blue, teal-blue, sapphire, royal blue, periwinkle', notColors:'brown, gold, silver, black, white, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
- green: { collectionId: 164060037171, tags:['green','emerald','olive','sage','forest','mint','lime','jade','hunter'], word:'GREEN', consider:'emerald, olive, sage, forest, mint, lime, jade, hunter, moss, fern', notColors:'brown, gold, silver, black, white, blue, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
- yellow: { collectionId: 283180367923, tags:['yellow','mustard','lemon','saffron','amber','marigold','sunflower','buttercup'], word:'YELLOW', consider:'mustard, lemon, saffron, amber, marigold, sunflower, buttercup, canary', notColors:'brown, silver, black, white, blue, red, bronze, copper, beige, tan, grey, cork, natural, cream, green, pink, purple, orange' },
+ blue: { collectionId: 95278104641, handle:'blue-wallpaper-collection', tags:['blue','navy','sky','indigo','cobalt','azure','cerulean','sapphire'], word:'BLUE', consider:'navy, indigo, cobalt, sky blue, teal-blue, sapphire, royal blue, periwinkle', notColors:'brown, gold, silver, black, white, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
+ green: { collectionId: 164060037171, handle:'green-wallpaper-collection', tags:['green','emerald','olive','sage','forest','mint','lime','jade','hunter'], word:'GREEN', consider:'emerald, olive, sage, forest, mint, lime, jade, hunter, moss, fern', notColors:'brown, gold, silver, black, white, blue, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
+ yellow: { collectionId: 283180367923, handle:'yellow-wallpaper-collection', tags:['yellow','mustard','lemon','saffron','amber','marigold','sunflower','buttercup'], word:'YELLOW', consider:'mustard, lemon, saffron, amber, marigold, sunflower, buttercup, canary', notColors:'brown, silver, black, white, blue, red, bronze, copper, beige, tan, grey, cork, natural, cream, green, pink, purple, orange' },
cream: { collectionId: 283307828659, tags:['cream','ivory','off-white','vanilla','eggshell','champagne'], word:'CREAM or IVORY', consider:'off-white, vanilla, eggshell, champagne, warm white', notColors:'brown, silver, black, blue, red, bronze, copper, tan, grey, cork, green, yellow, pink, purple, orange, navy, bright white' },
- pink: { collectionId: null, tags:['pink','rose','blush','fuchsia','magenta','salmon','coral pink','mauve'], word:'PINK', consider:'rose, blush, fuchsia, magenta, salmon, hot pink, mauve, dusty rose', notColors:'brown, gold, silver, black, white, blue, green, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, purple, orange' },
- red: { collectionId: 264122269747, tags:['red','crimson','burgundy','scarlet','ruby','cherry','maroon','wine'], word:'RED', consider:'crimson, burgundy, scarlet, ruby, cherry, maroon, wine, vermilion', notColors:'brown, gold, silver, black, white, blue, green, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
- purple: { collectionId: 283307573299, tags:['purple','violet','lavender','plum','amethyst','lilac','aubergine','mauve'], word:'PURPLE', consider:'violet, lavender, plum, amethyst, lilac, aubergine, mauve, eggplant', notColors:'brown, gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, orange' },
- gold: { collectionId: 164059906099, tags:['gold','golden','gilded','brass'], word:'GOLD or GOLDEN', consider:'brass, gilded, antique gold, warm metallic gold', notColors:'silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange, brown' },
- grey: { collectionId: 164059938867, tags:['grey','gray','charcoal','slate','silver','pewter','ash','graphite'], word:'GREY/GRAY', consider:'charcoal, slate, silver, pewter, ash, graphite, gunmetal', notColors:'brown, gold, black, white, blue, green, red, bronze, copper, beige, tan, cork, natural, cream, yellow, pink, purple, orange' },
- black: { collectionId: 283285651507, tags:['black','ebony','onyx','jet','noir'], word:'BLACK', consider:'ebony, onyx, jet, noir, charcoal-black', notColors:'brown, gold, silver, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
- brown: { collectionId: 283307868211, tags:['brown','chocolate','cocoa','mocha','coffee','walnut','chestnut','espresso','umber'], word:'BROWN', consider:'chocolate, cocoa, mocha, coffee, walnut, chestnut, espresso, umber, sienna', notColors:'gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
- orange: { collectionId: 283307999283, tags:['orange','tangerine','burnt orange','rust','terracotta','sienna','pumpkin'], word:'ORANGE', consider:'tangerine, burnt orange, rust, terracotta, sienna, pumpkin, paprika', notColors:'brown, gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple' },
- coral: { collectionId: 282281377843, tags:['coral','peach','apricot'], word:'CORAL or PEACH', consider:'warm salmon-orange, apricot, soft orange-pink', notColors:'brown, gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple' },
+ pink: { collectionId: null, handle:'pink-wallcoverings', tags:['pink','rose','blush','fuchsia','magenta','salmon','coral pink','mauve'], word:'PINK', consider:'rose, blush, fuchsia, magenta, salmon, hot pink, mauve, dusty rose', notColors:'brown, gold, silver, black, white, blue, green, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, purple, orange' },
+ red: { collectionId: 264122269747, handle:'red-wallcoverings', tags:['red','crimson','burgundy','scarlet','ruby','cherry','maroon','wine'], word:'RED', consider:'crimson, burgundy, scarlet, ruby, cherry, maroon, wine, vermilion', notColors:'brown, gold, silver, black, white, blue, green, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
+ purple: { collectionId: 283307573299, handle:'purple', tags:['purple','violet','lavender','plum','amethyst','lilac','aubergine','mauve'], word:'PURPLE', consider:'violet, lavender, plum, amethyst, lilac, aubergine, mauve, eggplant', notColors:'brown, gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, orange' },
+ gold: { collectionId: 164059906099, handle:'gold-wallpaper', tags:['gold','golden','gilded','brass'], word:'GOLD or GOLDEN', consider:'brass, gilded, antique gold, warm metallic gold', notColors:'silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange, brown' },
+ grey: { collectionId: 164059938867, handle:'grey-wallcovering', tags:['grey','gray','charcoal','slate','silver','pewter','ash','graphite'], word:'GREY/GRAY', consider:'charcoal, slate, silver, pewter, ash, graphite, gunmetal', notColors:'brown, gold, black, white, blue, green, red, bronze, copper, beige, tan, cork, natural, cream, yellow, pink, purple, orange' },
+ black: { collectionId: 283285651507, handle:'black', tags:['black','ebony','onyx','jet','noir'], word:'BLACK', consider:'ebony, onyx, jet, noir, charcoal-black', notColors:'brown, gold, silver, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
+ brown: { collectionId: 283307868211, handle:'brown-wallpaper-collection', tags:['brown','chocolate','cocoa','mocha','coffee','walnut','chestnut','espresso','umber'], word:'BROWN', consider:'chocolate, cocoa, mocha, coffee, walnut, chestnut, espresso, umber, sienna', notColors:'gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple, orange' },
+ orange: { collectionId: 283307999283, handle:'orange', tags:['orange','tangerine','burnt orange','rust','terracotta','sienna','pumpkin'], word:'ORANGE', consider:'tangerine, burnt orange, rust, terracotta, sienna, pumpkin, paprika', notColors:'brown, gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple' },
+ coral: { collectionId: 282281377843, handle:'coral', tags:['coral','peach','apricot'], word:'CORAL or PEACH', consider:'warm salmon-orange, apricot, soft orange-pink', notColors:'brown, gold, silver, black, white, blue, green, red, bronze, copper, beige, tan, grey, cork, natural, cream, yellow, pink, purple' },
};
function shopifyREST(method, p) {
@@ -122,7 +122,12 @@ Answer on ONE line EXACTLY: "COLOR=YES|NO (dominant) BANNER=YES|NO"` },
generationConfig: { temperature: 0.1, maxOutputTokens: 60, thinkingConfig: { thinkingBudget: 0 } } });
for (let a = 1; a <= 5; a++) {
try { const v = await callGemini(body);
- return { color: /COLOR=YES/i.test(v), banner: /BANNER=YES/i.test(v), raw: v };
+ // Tri-state: only an EXPLICIT "COLOR=NO" removes. A "COLOR=YES" keeps. Anything
+ // else (empty/garbled/unexpected) is null=uncertain → caller keeps + retries,
+ // never removes on ambiguity. This is the belt-and-suspenders guard against the
+ // thinking-budget class of bug that caused mass false-removes. (2026-06-12)
+ const yes = /COLOR\s*[=:]\s*YES/i.test(v), no = /COLOR\s*[=:]\s*NO/i.test(v);
+ return { color: yes ? true : (no ? false : null), banner: /BANNER\s*[=:]\s*YES/i.test(v), raw: v };
} catch (e) {
if (e.code === 429) { await new Promise(r => setTimeout(r, Math.min(e.retryAfter || 15, 60) * 1000)); continue; }
if (e.code >= 500 && a < 3) { await new Promise(r => setTimeout(r, 4000)); continue; }
@@ -153,7 +158,7 @@ async function resolveCollectionId(color, cfg) {
if (r && r.collection && r.collection.id) return (_cidCache[color] = cfg.collectionId);
console.log(` [${color}] hardcoded id ${cfg.collectionId} not on this store — resolving by handle…`);
}
- const handles = [color, `${color}-wallpaper`, `${color}-wallcovering`, `${color}-wallcoverings`];
+ const handles = [cfg.handle, color, `${color}-wallpaper`, `${color}-wallcovering`, `${color}-wallcoverings`, `${color}-wallpaper-collection`].filter(Boolean);
for (const h of handles) {
for (const type of ['smart_collections', 'custom_collections']) {
const r = await shopifyREST('GET', `/${type}.json?handle=${encodeURIComponent(h)}&fields=id,handle,title`);
@@ -179,7 +184,7 @@ async function processColor(color) {
if (LIMIT) products = products.slice(0, LIMIT);
console.log(` to process: ${products.length} (already done ${done.size})`);
const tagSet = new Set(cfg.tags.map(t => t.toLowerCase()));
- let kept = 0, removed = 0, errors = 0, skipped = 0, banners = 0, idx = 0;
+ let kept = 0, removed = 0, errors = 0, skipped = 0, banners = 0, uncertain = 0, idx = 0;
const queue = [...products];
async function worker() {
@@ -192,10 +197,10 @@ async function processColor(color) {
if (res.banner) {
banners++; append(BANNER_QUEUE, { ts: new Date().toISOString(), color, product_id: p.id, title: p.title, vendor: p.vendor, image_src: src, raw: res.raw });
}
- if (res.color) {
+ if (res.color === true) {
kept++; markProcessed(color, p.id);
if (n <= 15 || n % 100 === 0) console.log(` [${n}] KEEP ${p.title.slice(0,42)} — ${res.raw}`);
- } else {
+ } else if (res.color === false) {
const tags = (p.tags || '').split(',').map(t => t.trim()).filter(Boolean);
const cleaned = tags.filter(t => !tagSet.has(t.toLowerCase()));
const removedTags = tags.filter(t => tagSet.has(t.toLowerCase()));
@@ -211,11 +216,15 @@ async function processColor(color) {
markProcessed(color, p.id);
console.log(` [${n}] ${DRY_RUN ? 'DRY-REMOVE' : 'REMOVE'} ${p.title.slice(0,40)} — ${res.raw} — [${removedTags.join(', ')}]`);
} else { kept++; markProcessed(color, p.id); }
+ } else {
+ // res.color === null → uncertain verdict. NEVER remove on ambiguity.
+ // Leave unmarked so it is re-evaluated on the next run. (guard, 2026-06-12)
+ uncertain++; if (n % 25 === 0) console.log(` [${n}] UNCERTAIN(keep) ${p.title.slice(0,40)} — ${res.raw}`);
}
}
}
await Promise.all(Array.from({ length: CONC }, worker));
- const sum = ` ${color.toUpperCase()}: kept ${kept}, removed ${removed}, banners ${banners}, skipped ${skipped}, errors ${errors}`;
+ const sum = ` ${color.toUpperCase()}: kept ${kept}, removed ${removed}, banners ${banners}, skipped ${skipped}, uncertain ${uncertain}, errors ${errors}`;
console.log(sum); return sum;
}
← 88eb7001 make cleaner importable (require.main guard + module.exports
·
back to Designer Wallcoverings
·
color cleaner v2: model-fallback chain (flash-lite→flash→pro 3fd98379 →