← back to Wallco Ai
server: passesAestheticGate runtime filter in loadDesigns (neon + ≥4 hues + rainbow tags)
c1c8a8973a5e0d9e305480548d2a0fec0098849c · 2026-05-19 23:35:44 -0700 · Steve
Files touched
Diff
commit c1c8a8973a5e0d9e305480548d2a0fec0098849c
Author: Steve <steve@designerwallcoverings.com>
Date: Tue May 19 23:35:44 2026 -0700
server: passesAestheticGate runtime filter in loadDesigns (neon + ≥4 hues + rainbow tags)
---
server.js | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 81 insertions(+), 2 deletions(-)
diff --git a/server.js b/server.js
index dde93f7..f022e67 100644
--- a/server.js
+++ b/server.js
@@ -237,11 +237,90 @@ function loadBlankIds() {
}
}
+// ── Aesthetic gate (Steve 2026-05-19) ───────────────────────────────────────
+// Defence-in-depth against neon + multi-hue patterns. The DB-level purge
+// (scripts/purge-neon-and-multicolor.js) already removed the offenders, but
+// this runtime filter protects against any reseed / refresh_designs_snapshot
+// run that might re-introduce a row matching the rule. Drops:
+// - design where any palette entry is neon (HSL S≥70, L 45-75, pct≥5)
+// - design where palette has ≥ 4 distinct hue buckets
+// - design where dominant_hex alone is neon (single-color fallback for
+// rows without a full palette)
+// - design where prompt / motifs / tags match the neon/rainbow regex
+function _hexToHsl(hex) {
+ if (!hex || typeof hex !== 'string') return null;
+ const m = hex.trim().match(/^#?([0-9a-f]{6})$/i);
+ if (!m) return null;
+ const n = parseInt(m[1], 16);
+ const r = ((n >> 16) & 0xff) / 255;
+ const g = ((n >> 8) & 0xff) / 255;
+ const b = ( n & 0xff) / 255;
+ const mx = Math.max(r, g, b), mn = Math.min(r, g, b);
+ const l = (mx + mn) / 2;
+ let h = 0, s = 0;
+ if (mx !== mn) {
+ const d = mx - mn;
+ s = l > 0.5 ? d / (2 - mx - mn) : d / (mx + mn);
+ switch (mx) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h *= 60;
+ }
+ return { h, s: s * 100, l: l * 100 };
+}
+function _hueBucket(hex) {
+ const hsl = _hexToHsl(hex);
+ if (!hsl) return 'unknown';
+ if (hsl.s < 12 || hsl.l < 10 || hsl.l > 90) return 'neutral';
+ const h = hsl.h;
+ if (h < 15) return 'red'; if (h < 45) return 'orange'; if (h < 65) return 'yellow';
+ if (h < 90) return 'chartreuse'; if (h < 150) return 'green'; if (h < 180) return 'teal';
+ if (h < 210) return 'cyan'; if (h < 250) return 'blue'; if (h < 280) return 'indigo';
+ if (h < 310) return 'violet'; if (h < 340) return 'magenta'; return 'red';
+}
+function _isNeonHex(hex, pct) {
+ const hsl = _hexToHsl(hex);
+ if (!hsl) return false;
+ return hsl.s >= 70 && hsl.l >= 45 && hsl.l <= 75 && (pct == null || pct >= 5);
+}
+const _AESTHETIC_GATE_RE = /\b(neon|fluorescent|electric|rainbow|day[- ]?glo|highlighter|fluoro)\b/i;
+function passesAestheticGate(d) {
+ if (!d) return false;
+ // 1) palette pass (if present)
+ const palette = Array.isArray(d.palette) ? d.palette : null;
+ if (palette) {
+ const buckets = new Set();
+ for (const p of palette) {
+ const b = _hueBucket(p.hex);
+ if (b !== 'neutral' && b !== 'unknown') buckets.add(b);
+ if (_isNeonHex(p.hex, p.pct)) return false;
+ }
+ if (buckets.size >= 4) return false;
+ } else if (d.dominant_hex) {
+ // 2) single-color fallback when no palette is available
+ if (_isNeonHex(d.dominant_hex, 100)) return false;
+ }
+ // 3) tag / prompt / motifs regex pass
+ const hay = [
+ d.prompt || '',
+ Array.isArray(d.tags) ? d.tags.join(' ') : '',
+ Array.isArray(d.motifs) ? d.motifs.join(' ') : '',
+ ].join(' ');
+ if (_AESTHETIC_GATE_RE.test(hay)) return false;
+ return true;
+}
+
function loadDesigns() {
try {
const raw = JSON.parse(fs.readFileSync(DATA_FILE, 'utf8'));
- // Hide soft-removed designs from all listings/sorts.
- DESIGNS = raw.filter(d => !d.user_removed);
+ // Hide soft-removed designs, then drop anything that fails the aesthetic
+ // gate (neon / >3 hues / rainbow tags).
+ const beforeGate = raw.filter(d => !d.user_removed);
+ DESIGNS = beforeGate.filter(passesAestheticGate);
+ const dropped = beforeGate.length - DESIGNS.length;
+ if (dropped > 0) console.log(`[aesthetic-gate] dropped ${dropped} designs at load (neon/multi-hue/rainbow)`);
let max = 0;
for (const d of DESIGNS) {
const t = Date.parse(d.created_at || '');
← dec2cf7 generator hardening: tone-on-tone suffix + neon/rainbow nega
·
back to Wallco Ai
·
reconcile generator suffix → empirical professional palette 9abdaaa →