← back to Wallco Ai
scripts: --start/--count CLI flags on geometric-block-leaves generator + pilot scorer
50fe0af7906a979c450a14985460f4587ae97324 · 2026-05-26 08:25:59 -0700 · Steve Abrams
Files touched
M scripts/generate-geometric-block-leaves.jsA scripts/score-geometric-block-leaves-pilot.js
Diff
commit 50fe0af7906a979c450a14985460f4587ae97324
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 26 08:25:59 2026 -0700
scripts: --start/--count CLI flags on geometric-block-leaves generator + pilot scorer
---
scripts/generate-geometric-block-leaves.js | 20 ++++-
scripts/score-geometric-block-leaves-pilot.js | 103 ++++++++++++++++++++++++++
2 files changed, 120 insertions(+), 3 deletions(-)
diff --git a/scripts/generate-geometric-block-leaves.js b/scripts/generate-geometric-block-leaves.js
index cd325fe..a6509a3 100755
--- a/scripts/generate-geometric-block-leaves.js
+++ b/scripts/generate-geometric-block-leaves.js
@@ -11,6 +11,17 @@ const path = require('path');
const ROOT = path.join(__dirname, '..');
+// Minimal CLI flag support: --start=N --count=M (1-indexed inclusive start)
+function arg(name, fallback) {
+ const m = process.argv.find(a => a.startsWith('--' + name + '='));
+ if (m) return m.slice(name.length + 3);
+ const i = process.argv.indexOf('--' + name);
+ if (i >= 0 && process.argv[i + 1] && !process.argv[i + 1].startsWith('--')) return process.argv[i + 1];
+ return fallback;
+}
+const START = parseInt(arg('start', '1'), 10);
+const COUNT = parseInt(arg('count', '0'), 10);
+
const SHAPES = [
'uniform hexagonal honeycomb tiles, edge-to-edge tessellation',
'vertical diamond rhombi stacked in identical orientation, tightly grid-locked',
@@ -43,17 +54,20 @@ const NEGATIVE_SUFFIX =
'Architectural, minimalist, brutalist-modern, screen-print aesthetic, single-ink woodblock print feeling. ' +
'Archival paper texture. No text, no watermark, no signature.';
-console.log('Generating ' + SHAPES.length + ' geometric-block leaf designs');
+const startIdx = Math.max(0, START - 1);
+const endIdx = COUNT > 0 ? Math.min(SHAPES.length, startIdx + COUNT) : SHAPES.length;
+
+console.log('Generating ' + (endIdx - startIdx) + ' / ' + SHAPES.length + ' geometric-block leaf designs (range ' + (startIdx+1) + '..' + endIdx + ')');
console.log('Backend: ' + (process.env.GEN_BACKEND || 'replicate') + ' | Category: geometric-block-leaves\n');
let success = 0, fail = 0;
const ids = [];
-for (let i = 0; i < SHAPES.length; i++) {
+for (let i = startIdx; i < endIdx; i++) {
const shape = SHAPES[i];
const prompt = 'Geometric block leaf pattern - ' + shape + '. ' + NEGATIVE_SUFFIX;
- console.log('[' + (i+1) + '/' + SHAPES.length + '] ' + shape.slice(0, 60));
+ console.log('[' + (i+1) + '/' + endIdx + '] ' + shape.slice(0, 60));
const r = spawnSync('node', [
path.join(__dirname, 'generate_designs.js'),
'--n', '1',
diff --git a/scripts/score-geometric-block-leaves-pilot.js b/scripts/score-geometric-block-leaves-pilot.js
new file mode 100644
index 0000000..792717d
--- /dev/null
+++ b/scripts/score-geometric-block-leaves-pilot.js
@@ -0,0 +1,103 @@
+#!/usr/bin/env node
+// Score the geometric-block-leaves pilot: for every row in category that was
+// created since START_TS_UNIX (set by env), capture
+// - id
+// - primitive (parsed from prompt)
+// - settlement-gate verdict (text-side, deterministic)
+// - ghost-detector verdict (analyzeGhostLayerStable, 3-call majority vote)
+// - pass/fail (settlement=OK AND ghost.hasGhostLayer=false)
+// and write JSONL to data/geometric-block-leaves-pilot-<ISO>.jsonl plus a
+// markdown table next to it.
+
+require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
+const fs = require('fs');
+const path = require('path');
+const { spawnSync } = require('child_process');
+const { checkPrompt } = require('./settlement-gate');
+const { analyzeGhostLayerStable } = require('../lib/ghost-detector');
+
+const ROOT = path.join(__dirname, '..');
+const SINCE = process.env.PILOT_SINCE_TS;
+if (!SINCE) {
+ console.error('PILOT_SINCE_TS env var required (postgres-castable timestamp, e.g. 2026-05-26T18:00:00)');
+ process.exit(1);
+}
+
+function psql(sql) {
+ const r = spawnSync('psql', ['dw_unified', '-At', '-F', '\t', '-q'], { input: sql, encoding: 'utf8', maxBuffer: 50_000_000 });
+ if (r.status !== 0) throw new Error(r.stderr || r.stdout || 'psql failed');
+ return r.stdout;
+}
+
+function parsePrimitive(prompt) {
+ // Strip the canonical "Geometric block leaf pattern - <SHAPE>. " prefix
+ const m = String(prompt || '').match(/Geometric block leaf pattern - ([^.]+)\./);
+ return m ? m[1].trim() : null;
+}
+
+(async () => {
+ const sql = `SELECT id, prompt, local_path, image_url
+ FROM spoon_all_designs
+ WHERE category='geometric-block-leaves'
+ AND created_at >= '${SINCE.replace(/'/g, "''")}'::timestamptz
+ ORDER BY id ASC;`;
+ const rows = psql(sql).trim().split('\n').filter(Boolean).map(line => {
+ const [id, prompt, local_path, image_url] = line.split('\t');
+ return { id: parseInt(id, 10), prompt, local_path, image_url };
+ });
+ console.log(`Found ${rows.length} pilot rows since ${SINCE}.`);
+ if (!rows.length) process.exit(0);
+
+ const results = [];
+ for (const row of rows) {
+ const primitive = parsePrimitive(row.prompt);
+ const sg = checkPrompt(row.prompt || '');
+ let ghost;
+ try {
+ ghost = await analyzeGhostLayerStable(row.local_path);
+ } catch (e) {
+ ghost = { error: e.message };
+ }
+ const pass = sg.verdict === 'OK' && ghost && ghost.hasGhostLayer === false;
+ const rec = {
+ id: row.id,
+ primitive,
+ local_path: row.local_path,
+ image_url: row.image_url,
+ settlement: {
+ verdict: sg.verdict,
+ a1: sg.partA?.a1, a2: sg.partA?.a2, a3: sg.partA?.a3,
+ partA: sg.partA?.satisfied,
+ partB: sg.partB?.satisfied,
+ },
+ ghost: ghost.error ? { error: ghost.error } : {
+ hasGhostLayer: ghost.hasGhostLayer,
+ confidence: ghost.confidence,
+ unanimous: ghost.unanimous,
+ votes: ghost.votes,
+ reason: ghost.reason,
+ },
+ pass,
+ };
+ results.push(rec);
+ const ghostStr = ghost.error ? `ERR(${ghost.error})` : `${ghost.hasGhostLayer ? 'GHOST' : 'clean'} (${ghost.confidence?.toFixed?.(2) ?? '?'}${ghost.unanimous ? ' unanimous' : ''})`;
+ console.log(`#${row.id.toString().padStart(5)} · settlement=${sg.verdict.padEnd(13)} · ghost=${ghostStr.padEnd(28)} · ${pass ? 'PASS' : 'FAIL'} · ${primitive?.slice(0, 50) || ''}`);
+ }
+
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
+ const outBase = path.join(ROOT, 'data', `geometric-block-leaves-pilot-${stamp}`);
+ fs.writeFileSync(outBase + '.jsonl', results.map(r => JSON.stringify(r)).join('\n') + '\n');
+
+ // markdown table
+ const lines = ['# Geometric-block-leaves pilot — re-run with stable ghost-detector', '', `Since: \`${SINCE}\` · Rows: ${results.length}`, '', '| id | primitive | settlement | ghost | pass |', '|---|---|---|---|---|'];
+ for (const r of results) {
+ const g = r.ghost.error ? `ERR` : `${r.ghost.hasGhostLayer ? 'GHOST' : 'clean'} c=${(r.ghost.confidence ?? 0).toFixed(2)}${r.ghost.unanimous ? ' ✓' : ''}`;
+ lines.push(`| ${r.id} | ${(r.primitive || '').slice(0, 60)} | ${r.settlement.verdict} | ${g} | ${r.pass ? '✅' : '❌'} |`);
+ }
+ const passCount = results.filter(r => r.pass).length;
+ lines.push('', `**Tally:** ${passCount}/${results.length} PASS · ${results.length - passCount} FAIL`);
+ fs.writeFileSync(outBase + '.md', lines.join('\n') + '\n');
+
+ console.log(`\nWrote ${outBase}.jsonl + .md`);
+ console.log(`Final: ${passCount}/${results.length} PASS`);
+})();
← e69a345 gitignore: exclude data/edges-scan-report-*.md (regeneratabl
·
back to Wallco Ai
·
pm2: ecosystem.wallco-ai-9878.config.js with restart-loop gu 4f3993d →