← back to Wallco Ai
add one-off re-screen of already-flagged candidates for large-void duds
33279370d564afe55bdcbef52fdaed2dffbdc781 · 2026-05-27 16:09:41 -0700 · Steve Abrams
Files touched
A scripts/rescreen-flagged-voids.js
Diff
commit 33279370d564afe55bdcbef52fdaed2dffbdc781
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed May 27 16:09:41 2026 -0700
add one-off re-screen of already-flagged candidates for large-void duds
---
scripts/rescreen-flagged-voids.js | 75 +++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/scripts/rescreen-flagged-voids.js b/scripts/rescreen-flagged-voids.js
new file mode 100644
index 0000000..0b7c4ba
--- /dev/null
+++ b/scripts/rescreen-flagged-voids.js
@@ -0,0 +1,75 @@
+#!/usr/bin/env node
+/**
+ * One-off: re-screen the already-flagged AUTO_HOT_CANDIDATE pool with the new
+ * open-space guard (Steve 2026-05-27 chose "add open-space guard"). Designs
+ * flagged HOT before the guard existed may contain large-void duds (#53743-type);
+ * this demotes those to the defect registry so the curator pile is clean dense
+ * all-overs only. Good (non-void) candidates keep their AUTO_HOT_CANDIDATE flag.
+ *
+ * Touches ONLY rows that carry the flag, so it never collides with the live
+ * triage decider (which only takes un-flagged rows). Resumable: a re-screened
+ * void loses its flag, a kept one gets a ' | VOID_RESCREENED_OK' note so it
+ * isn't checked twice.
+ */
+const path = require('path');
+const fs = require('fs');
+const { execSync } = require('child_process');
+const ROOT = __dirname.replace(/\/scripts$/, '');
+const { gateComposition } = require(path.join(ROOT, 'lib', 'composition-detector'));
+
+const LEDGER = path.join(ROOT, 'data', 'rescreen-voids-ledger.jsonl');
+const SLEEP_MS = 700;
+const BACKOFF_MS = 30000;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+function psql(sql) { return execSync(`psql dw_unified -At -F'\x1f' -c ${JSON.stringify(sql)}`, { encoding: 'utf8' }).trim(); }
+function exec(sql) { execSync(`psql dw_unified -q -c ${JSON.stringify(sql)}`, { stdio: 'ignore' }); }
+function esc(s) { return String(s == null ? '' : s).replace(/'/g, "''"); }
+function log(o) { try { fs.appendFileSync(LEDGER, JSON.stringify({ t: new Date().toISOString(), ...o }) + '\n'); } catch {} }
+
+function nextBatch(n = 40) {
+ const rows = psql(
+ "SELECT id, local_path, COALESCE(category,'') FROM all_designs " +
+ "WHERE notes LIKE '%AUTO_HOT_CANDIDATE%' AND notes NOT LIKE '%VOID_RESCREENED_OK%' " +
+ "AND is_published=FALSE AND (user_removed IS NULL OR user_removed=FALSE) AND local_path LIKE '%.png' " +
+ `ORDER BY id LIMIT ${n};`
+ );
+ return rows ? rows.split('\n').filter(Boolean).map(l => { const [id, lp, cat] = l.split('\x1f'); return { id: +id, lp, cat }; }) : [];
+}
+
+let kept = 0, demoted = 0, errs = 0, done = 0;
+(async () => {
+ console.log('[rescreen] start — re-checking AUTO_HOT_CANDIDATE pool for large-void duds');
+ while (true) {
+ const batch = nextBatch();
+ if (!batch.length) { console.log('[rescreen] done — all flagged re-screened.'); break; }
+ for (const d of batch) {
+ if (!fs.existsSync(d.lp)) { exec(`UPDATE all_designs SET notes=COALESCE(notes,'')||' | VOID_RESCREENED_OK' WHERE id=${d.id};`); kept++; done++; continue; }
+ let g;
+ try { g = await gateComposition(d.lp, { minInstances: 3, category: d.cat, rejectOpenSpace: true }); }
+ catch (e) {
+ const msg = (e.message || '').toLowerCase();
+ if (msg.includes('rate') || msg.includes('429') || msg.includes('quota') || msg.includes('overload')) { console.warn('[rescreen] backoff'); await sleep(BACKOFF_MS); continue; }
+ errs++; log({ id: d.id, verdict: 'error', reason: msg.slice(0, 120) });
+ exec(`UPDATE all_designs SET notes=COALESCE(notes,'')||' | VOID_RESCREENED_OK' WHERE id=${d.id};`); // don't loop forever on a hard error
+ done++; continue;
+ }
+ const r = g.result || {};
+ if (!g.ok && g.reason === 'large-empty-void') { // demote void → registry (kept, never deleted)
+ exec(`UPDATE all_designs SET is_published=FALSE, user_removed=TRUE, notes=REPLACE(COALESCE(notes,''),'AUTO_HOT_CANDIDATE','VOID_DEMOTED') WHERE id=${d.id};`);
+ exec(
+ "INSERT INTO wallco_defect_registry (design_id, category, generator, local_path, defect_type, defect_detail, prompt, seed, source) " +
+ `SELECT id, category, generator, local_path, 'auto_decide_open_space', '${esc(g.reason + ' :: ' + (r.what_you_see || ''))}', prompt, seed, 'rescreen_flagged_voids' FROM all_designs WHERE id=${d.id} ` +
+ "ON CONFLICT (design_id, defect_type) DO NOTHING;"
+ );
+ log({ id: d.id, verdict: 'demoted_void', see: r.what_you_see }); demoted++;
+ } else { // keep flagged; mark so we don't recheck
+ exec(`UPDATE all_designs SET notes=COALESCE(notes,'')||' | VOID_RESCREENED_OK' WHERE id=${d.id};`);
+ log({ id: d.id, verdict: 'kept', n: r.instance_count }); kept++;
+ }
+ done++;
+ if (done % 25 === 0) console.log(`[rescreen] ${done} re-screened · ✅${kept} kept · ❌${demoted} demoted-void · ⚠${errs} err`);
+ await sleep(SLEEP_MS);
+ }
+ }
+ console.log(`[rescreen] FINISHED — ${done} · ${kept} kept · ${demoted} voids demoted · ${errs} err`);
+})();
← 66faa13 drunk-curator: inch ruler + stretch tile to 24/36/52in print
·
back to Wallco Ai
·
flag geometric-block-leaves as bad_batch in defect registry 9449d43 →