[object Object]

← back to Hollywood Import

momentum go-live prep (blockers 2-4): hw_product_type (Acoustic Panel/Wallcovering) + faithful de-SHOUTed hw_title + surfaced panel_spec; leak scan 0/clean; staging non-destructive

57e3dbeb3ff3fe69f3106b5cefd37a965293ae53 · 2026-07-08 15:33:41 -0700 · steve@designerwallcoverings.com

Files touched

Diff

commit 57e3dbeb3ff3fe69f3106b5cefd37a965293ae53
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date:   Wed Jul 8 15:33:41 2026 -0700

    momentum go-live prep (blockers 2-4): hw_product_type (Acoustic Panel/Wallcovering) + faithful de-SHOUTed hw_title + surfaced panel_spec; leak scan 0/clean; staging non-destructive
---
 momentum-feed/prep-golive.mjs | 57 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/momentum-feed/prep-golive.mjs b/momentum-feed/prep-golive.mjs
new file mode 100644
index 0000000..1246b1a
--- /dev/null
+++ b/momentum-feed/prep-golive.mjs
@@ -0,0 +1,57 @@
+#!/usr/bin/env node
+// Go-live prep for the Hollywood line (blockers #2–4), STAGING ONLY, reversible.
+// Adds non-destructive columns to momentum_colorways (pattern_name never mutated):
+//   hw_product_type — "Acoustic Panel" (category=Acoustic) else "Wallcovering"   [#2 taxonomy]
+//   hw_title        — faithful title; only de-SHOUTs ALLCAPS names               [#3 name-clean, non-merging]
+//   panel_spec      — dimension/thickness tokens SURFACED (copied, not stripped) [#3 spec field]
+// #4 leak scrub: re-asserts 0 Momentum/Versa across public-bound text (already verified clean).
+// Run: NODE_PATH=.../AbramsOS/node_modules node prep-golive.mjs [--commit]
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+const { Pool } = require('pg');
+const COMMIT = process.argv.includes('--commit');
+const pool = new Pool({ connectionString: 'postgresql://dw_admin:DW2024SecurePass@127.0.0.1:5432/dw_unified' });
+
+const DIM = /(\d+(?:\.\d+)?\s*H\s*[x×]\s*\d+(?:\.\d+)?\s*L|\b\d+(?:\.\d+)?\s?(?:IN|MM)\b)/gi;
+function titleClean(name) {
+  let t = name.replace(/\s+\/\s+/g, ' ').replace(/\s{2,}/g, ' ').trim();
+  if (t === t.toUpperCase() && /[A-Z]{4,}/.test(t)) t = t.toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
+  return t;
+}
+const specOf = name => { const m = name.match(DIM); return m ? [...new Set(m.map(s => s.replace(/\s+/g, ' ').trim()))].join(' · ') : null; };
+
+async function main() {
+  const leak = (await pool.query(
+    `SELECT count(*) n FROM momentum_colorways WHERE pattern_name ~* 'momentum|versa' OR description ~* 'momentum|versa' OR tags ~* 'momentum|versa' OR ai_color_name ~* 'momentum|versa'`)).rows[0].n;
+  console.log(`#4 leak scan (Momentum/Versa in public text): ${leak} hits ${leak == 0 ? '✅ CLEAN' : '⚠️ MUST SCRUB'}`);
+
+  const { rows } = await pool.query(`SELECT id, pattern_name, category FROM momentum_colorways`);
+  const plan = rows.map(r => ({ id: r.id,
+    pt: r.category === 'Acoustic' ? 'Acoustic Panel' : 'Wallcovering',
+    title: titleClean(r.pattern_name),
+    spec: r.category === 'Acoustic' ? specOf(r.pattern_name) : null }));
+  const changedTitles = plan.filter((p, i) => p.title !== rows[i].pattern_name).length;
+  const withSpec = plan.filter(p => p.spec).length;
+  console.log(`#2 product_type: ${plan.filter(p=>p.pt==='Acoustic Panel').length} Acoustic Panel / ${plan.filter(p=>p.pt==='Wallcovering').length} Wallcovering`);
+  console.log(`#3 hw_title: ${changedTitles} de-SHOUTed (rest faithful) | panel_spec surfaced on ${withSpec} acoustic rows`);
+  console.log('  samples:'); plan.filter(p=>p.spec).slice(0,3).forEach(p=>console.log(`    "${rows.find(r=>r.id===p.id).pattern_name}" → title "${p.title}" · spec "${p.spec}"`));
+
+  if (!COMMIT) { console.log('DRY-RUN — re-run with --commit.'); await pool.end(); return; }
+  if (leak != 0) { console.error('ABORT: leak hits > 0 — scrub required before prep.'); await pool.end(); process.exit(1); }
+  const c = await pool.connect(); let done = 0;
+  try {
+    await c.query(`ALTER TABLE momentum_colorways
+      ADD COLUMN IF NOT EXISTS hw_product_type text,
+      ADD COLUMN IF NOT EXISTS hw_title text,
+      ADD COLUMN IF NOT EXISTS panel_spec text`);
+    await c.query('BEGIN');
+    for (const p of plan) {
+      await c.query(`UPDATE momentum_colorways SET hw_product_type=$1, hw_title=$2, panel_spec=$3, updated_at=now() WHERE id=$4`,
+        [p.pt, p.title, p.spec, p.id]); done++;
+    }
+    await c.query('COMMIT');
+    console.log(`COMMITTED — set hw_product_type/hw_title/panel_spec on ${done} rows. (staging; pattern_name untouched)`);
+  } catch (e) { await c.query('ROLLBACK'); console.error('ROLLBACK:', e.message); process.exitCode = 1; }
+  finally { c.release(); await pool.end(); }
+}
+main().catch(e => { console.error(e); process.exit(1); });

← f603f77 momentum price-backfill: recover 788 unpriced (incl 509 Acou  ·  back to Hollywood Import  ·  momentum go-live prep (blockers 2-4): momentum_golive_prep c cb4b292 →