[object Object]

← back to Dw Pairs Well

5x sweep 5: color+texture lock in clipSimilarTop (CLIP cosine × hex ΔE gate, cosine+color re-rank, backfill never blanks); PUB_SQL boot probe so a mirror without online_store_published degrades loudly instead of 500ing; clickthrough re-expand + designed-404 tolerance

0d36db39a16c070b0597ac378b5bcebe8058db43 · 2026-07-13 01:24:06 -0700 · Steve Abrams

Files touched

Diff

commit 0d36db39a16c070b0597ac378b5bcebe8058db43
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jul 13 01:24:06 2026 -0700

    5x sweep 5: color+texture lock in clipSimilarTop (CLIP cosine × hex ΔE gate, cosine+color re-rank, backfill never blanks); PUB_SQL boot probe so a mirror without online_store_published degrades loudly instead of 500ing; clickthrough re-expand + designed-404 tolerance
---
 5x/clickthrough.mjs | 16 ++++++++++++++--
 server.js           | 51 +++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/5x/clickthrough.mjs b/5x/clickthrough.mjs
index ed1ab9f..66d80c3 100644
--- a/5x/clickthrough.mjs
+++ b/5x/clickthrough.mjs
@@ -90,16 +90,28 @@ await step('second sample chip → grid swaps with real cards', async () => {
     const t = document.querySelector('#product-title');
     return t && t.textContent.trim().length > 3 && t.textContent !== prev;
   }, prevTitle, { timeout: 25000 });
+  // loadSource collapses the Design-Coordinate panel for the new source — re-expand
+  const cta = page.locator('#pairs-well-with .pairs-well__cta');
+  if ((await cta.getAttribute('aria-expanded')) !== 'true') await cta.click();
   await page.waitForFunction(() => {
     const g = document.getElementById('catalog-grid');
     return g && !g.hidden && g.querySelectorAll('a[href*="/products/"], img').length > 0;
   }, null, { timeout: 25000 });
 });
 
-await step('unknown SKU → graceful error state, no console errors', async () => {
+await step('unknown SKU → graceful error state, no page exceptions', async () => {
+  const before = errors.length;
   await page.fill('#sku', 'NOPE-0000000');
   await page.locator('#load').click();
-  await page.waitForTimeout(2500);   // UI must absorb the 404 without throwing
+  await page.waitForTimeout(2500);
+  // The page DESIGNEDLY console.errors its fetch failures ("[preview] … http 404",
+  // resource-load 404s). Those are the graceful path, not defects. Real exceptions
+  // (pageerror / anything else) still fail this step.
+  const fresh = errors.slice(before);
+  const unexpected = fresh.filter(e =>
+    !/http 404|Failed to load resource.*404|\[preview\].*fetch failed/i.test(e));
+  errors.length = before;                       // absorb the expected noise
+  if (unexpected.length) throw new Error(`unexpected error: ${unexpected[0]}`.slice(0, 150));
 });
 
 await browser.close();
diff --git a/server.js b/server.js
index f746c93..71aa948 100644
--- a/server.js
+++ b/server.js
@@ -42,6 +42,18 @@ if (!DATABASE_URL) {
 
 const pool = new Pool({ connectionString: DATABASE_URL });
 
+// "Link works on the live store" predicate. Probed at boot: if this DB's
+// shopify_products lacks online_store_published (schema drift between the Mac2
+// mirror and Kamatera), degrade to status-only filtering and say so loudly —
+// a weaker guarantee beats 500ing every suggestion endpoint.
+let PUB_SQL = 'AND sp.online_store_published IS TRUE';
+pool.query('SELECT online_store_published FROM shopify_products LIMIT 0')
+  .then(() => console.log('[link-guarantee] online_store_published present — full ACTIVE+published gate on'))
+  .catch(() => {
+    PUB_SQL = '';
+    console.error('[link-guarantee] WARNING: shopify_products.online_store_published MISSING in this DB — degrading to status-only filtering');
+  });
+
 const app = express();
 app.disable('x-powered-by');
 
@@ -137,7 +149,7 @@ async function loadCandidates(source, limit = 600) {
     FROM shopify_products AS sp
     LEFT JOIN shopify_color_enrichment sce ON sce.shopify_id = sp.shopify_id
     WHERE sp.status = 'ACTIVE'
-      AND sp.online_store_published IS TRUE
+      ${PUB_SQL}
       AND sp.image_url IS NOT NULL
       AND sp.handle IS NOT NULL
       AND sp.dw_sku IS NOT NULL
@@ -274,7 +286,7 @@ async function loadRowsByDwSkus(skus) {
        LEFT JOIN shopify_color_enrichment sce ON sce.shopify_id = sp.shopify_id
        WHERE sp.dw_sku = ANY($1)
          AND sp.status = 'ACTIVE'
-         AND sp.online_store_published IS TRUE
+         ${PUB_SQL}
          AND sp.handle IS NOT NULL`;
   const r = await pool.query(q, [skus]);
   const seen = new Set(), out = [];
@@ -293,9 +305,9 @@ async function clipSimilarTop(source, k) {
   if (!source.dw_sku) return null;
   if (clipCircuitOpen()) return null;                   // service recently failed → skip, no latency hit
   try {
-    // Over-fetch 2x: some results get dropped by the ACTIVE+published gate below,
-    // and the strip should still fill to k.
-    const body = { dw_sku: source.dw_sku, hex: source.dominant_hex || '', k: Math.min(k * 2, 50) };
+    // Over-fetch 4x: results get dropped by BOTH the ACTIVE+published gate and the
+    // color lock below, and the strip should still fill to k.
+    const body = { dw_sku: source.dw_sku, hex: source.dominant_hex || '', k: Math.min(k * 4, 50) };
     const r = await fetch(`${VISUAL_SEARCH_URL}/similar`, {
       method: 'POST',
       headers: { 'Content-Type': 'application/json' },
@@ -317,25 +329,40 @@ async function clipSimilarTop(source, k) {
     // HARD LINK GUARANTEE: only products verified ACTIVE + Online-Store-published
     // (loadRowsByDwSkus filters to exactly those) may be suggested — every /products/
     // link the UI renders must resolve on the live store.
-    const out = [];
+    //
+    // COLOR + TEXTURE LOCK (Steve, 2026-07-13): CLIP cosine locks the texture/style;
+    // the hex palette locks the color. When the source has hex enrichment, a candidate
+    // must pick up ≥1 source color (ΔE ≤ 35, paletteOverlap ≥ 1) to qualify; survivors
+    // re-rank by cosine + a small color term so tighter palette matches rise. If the
+    // lock would empty the strip (rare), pure-cosine order backfills — never blank.
+    const scored = [];
     for (const x of d.results) {
-      if (out.length >= k) break;
       const row = rowByS.get(x.dw_sku);
       if (!row || !row.handle) continue;
       const candPalette = paletteOf(row);
       const overlap = paletteOverlap(sourcePalette, candPalette);
       const why = [`Visual match (CLIP · cosine ${Number(x.score).toFixed(3)})`];
       if (overlap.pickedUp.length) why.push(`Picks up: ${overlap.pickedUp.slice(0, 3).join(', ')}`);
-      out.push({
+      scored.push({
         dw_sku: x.dw_sku,
         handle: row.handle,
         title: row.title || x.pattern || x.dw_sku,
         vendor: row.vendor || x.vendor || null,
         image_url: row.image_url || x.image || null,
         pattern_name: row.pattern_name || x.pattern || null,
-        _score: x.score, _palette: candPalette, _dE: overlap.avgDE, _why: why
+        _score: x.score, _palette: candPalette, _dE: overlap.avgDE, _why: why,
+        _colorLocked: overlap.score >= 1,
+        _combined: Number(x.score) + 0.02 * Math.min(overlap.score, 8)
       });
     }
+    const hasSourcePalette = sourcePalette.length > 0;
+    const locked = hasSourcePalette ? scored.filter(s => s._colorLocked) : scored;
+    locked.sort((a, b) => b._combined - a._combined);
+    const out = locked.slice(0, k);
+    if (out.length < k) {           // backfill from unlocked, best cosine first
+      const rest = scored.filter(s => !out.includes(s)).sort((a, b) => b._score - a._score);
+      out.push(...rest.slice(0, k - out.length));
+    }
     return out.length ? out : null;
   } catch (e) {
     clipTrip();                                         // unreachable/slow → open the breaker
@@ -480,7 +507,7 @@ async function loadSimilarCandidates(source, limit = 1200) {
     FROM shopify_products AS sp
     LEFT JOIN shopify_color_enrichment sce ON sce.shopify_id = sp.shopify_id
     WHERE sp.status = 'ACTIVE'
-      AND sp.online_store_published IS TRUE
+      ${PUB_SQL}
       AND sp.image_url IS NOT NULL
       AND sp.handle IS NOT NULL
       AND sp.dw_sku IS NOT NULL
@@ -763,7 +790,7 @@ app.get('/api/colorways', async (req, res) => {
              sp.image_url, sp.metafields, sce.color_family, sce.dominant_hex, sce.background_hex
       FROM shopify_products sp
       LEFT JOIN shopify_color_enrichment sce ON sce.shopify_id = sp.shopify_id
-      WHERE sp.status = 'ACTIVE' AND sp.online_store_published IS TRUE
+      WHERE sp.status = 'ACTIVE' ${PUB_SQL}
         AND sp.title IS NOT NULL AND sp.handle IS NOT NULL
         AND sp.dw_sku IS NOT NULL
         AND lower(regexp_replace(coalesce(sp.vendor,'∅'),'[^a-zA-Z0-9]+','-','g')) LIKE $1
@@ -896,7 +923,7 @@ app.get('/api/collection-grouped', async (req, res) => {
     const fam = familyKey(vendor) || familyKey(collHandle);
 
     const params = [];
-    let where = `sp.status = 'ACTIVE' AND sp.online_store_published IS TRUE AND sp.title IS NOT NULL AND sp.handle IS NOT NULL AND sp.dw_sku IS NOT NULL AND sp.image_url IS NOT NULL`;
+    let where = `sp.status = 'ACTIVE' ${PUB_SQL} AND sp.title IS NOT NULL AND sp.handle IS NOT NULL AND sp.dw_sku IS NOT NULL AND sp.image_url IS NOT NULL`;
     if (fam && !all) {
       params.push(fam + '%');
       where += ` AND regexp_replace(lower(sp.vendor), '[^a-z0-9]', '', 'g') LIKE $${params.length}`;

← f2f2ae6 5x sweep 4 (contrarian FIX FIRST): no-store on all product-l  ·  back to Dw Pairs Well  ·  5x: final report — sweep 5 clean at cap 2805347 →