[object Object]

← back to Wallco Ai

feat(ticks): rsync-pull prod bad-aesthetic-patterns.jsonl at top of each tick

75c5fe06fc3bfa1355eeeccb64a1344c29cc9e71 · 2026-05-24 23:57:44 -0700 · Steve Abrams

The bad-aesthetic learn loop was wired (913798d) but starved — cron lives
on Mac2 because ComfyUI is local, but the Ghost Review UI's bulk-delete
endpoint writes the source-of-truth file on prod. Local file empty →
LEARNED-AVOID addendum never fired → the loop was effectively dead.

scripts/_pull-bad-aesthetic.sh — tiny rsync wrapper, 4s SSH connect
timeout, fails open. Pulls /root/public-projects/wallco-ai/data/
bad-aesthetic-patterns.jsonl → local. ~0.4s when prod reachable.

Both ticks (generator_tick.js, drunk_animals_tick.js) call it BEFORE
loading lib/bad-aesthetic. If SSH hiccups the existing local copy
(however stale) survives; if no local copy exists, lib returns 0 rows
and the tick proceeds as if no failures (safe default).

Verified locally: deleting local file, running pull, reading lib →
4 cactus-pine-scenic entries appear.

Files touched

Diff

commit 75c5fe06fc3bfa1355eeeccb64a1344c29cc9e71
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun May 24 23:57:44 2026 -0700

    feat(ticks): rsync-pull prod bad-aesthetic-patterns.jsonl at top of each tick
    
    The bad-aesthetic learn loop was wired (913798d) but starved — cron lives
    on Mac2 because ComfyUI is local, but the Ghost Review UI's bulk-delete
    endpoint writes the source-of-truth file on prod. Local file empty →
    LEARNED-AVOID addendum never fired → the loop was effectively dead.
    
    scripts/_pull-bad-aesthetic.sh — tiny rsync wrapper, 4s SSH connect
    timeout, fails open. Pulls /root/public-projects/wallco-ai/data/
    bad-aesthetic-patterns.jsonl → local. ~0.4s when prod reachable.
    
    Both ticks (generator_tick.js, drunk_animals_tick.js) call it BEFORE
    loading lib/bad-aesthetic. If SSH hiccups the existing local copy
    (however stale) survives; if no local copy exists, lib returns 0 rows
    and the tick proceeds as if no failures (safe default).
    
    Verified locally: deleting local file, running pull, reading lib →
    4 cactus-pine-scenic entries appear.
---
 scripts/_pull-bad-aesthetic.sh | 32 ++++++++++++++++++++++++++++++++
 scripts/drunk_animals_tick.js  |  6 ++++++
 scripts/generator_tick.js      |  4 ++++
 3 files changed, 42 insertions(+)

diff --git a/scripts/_pull-bad-aesthetic.sh b/scripts/_pull-bad-aesthetic.sh
new file mode 100755
index 0000000..6aaf32b
--- /dev/null
+++ b/scripts/_pull-bad-aesthetic.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Pull the source-of-truth bad-aesthetic-patterns.jsonl from prod (where the
+# Ghost Review UI's bulk-delete endpoint and learn-and-purge-flagged.js write
+# it) so the local generator ticks read fresh failure data.
+#
+# Architecture note: cron lives on Mac2 (this box) because ComfyUI/SDXL runs
+# locally. But the UI that produces the learn-from-this-failure rows runs on
+# prod (Kamatera). Without this pull, the local lib/bad-aesthetic.js reads an
+# empty local file and the LEARNED-AVOID addendum never fires.
+#
+# Fails open: if SSH hiccups or the remote file is missing, exits 0 and leaves
+# whatever stale local copy exists (or nothing) intact. The generators are
+# designed to tolerate a missing/empty file.
+#
+# Invoked from the top of generator_tick.js and drunk_animals_tick.js.
+
+set -u  # no -e; failure is acceptable
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+REMOTE_HOST="my-server"
+REMOTE_PATH="/root/public-projects/wallco-ai/data/bad-aesthetic-patterns.jsonl"
+LOCAL_PATH="$ROOT/data/bad-aesthetic-patterns.jsonl"
+
+# Tight timeouts so a slow SSH session never delays a tick by more than ~6s.
+rsync -az \
+  --timeout=5 \
+  -e 'ssh -o ConnectTimeout=4 -o ServerAliveInterval=2 -o BatchMode=yes' \
+  "${REMOTE_HOST}:${REMOTE_PATH}" \
+  "${LOCAL_PATH}" \
+  2>/dev/null
+
+exit 0
diff --git a/scripts/drunk_animals_tick.js b/scripts/drunk_animals_tick.js
index 665c763..35d75c8 100644
--- a/scripts/drunk_animals_tick.js
+++ b/scripts/drunk_animals_tick.js
@@ -61,6 +61,12 @@ function freshPrompt() {
 // Steve killed the 18:00→05:59 window on 2026-05-13.
 
 const ROOT = path.join(__dirname, '..');
+
+// Pull source-of-truth bad-aesthetic-patterns.jsonl from prod (the Ghost
+// Review UI writes there; cron runs locally because ComfyUI is local). Fails
+// open — local stale copy survives if SSH hiccups. ~0.4s when prod reachable.
+try { execSync(path.join(__dirname, '_pull-bad-aesthetic.sh'), { stdio: 'ignore', timeout: 8000 }); } catch {}
+
 const { isCategoryCooldown, getAvoidanceAddendum, getCategoryFailureCount } = require('../lib/bad-aesthetic');
 
 // ── Bad-aesthetic cooldown ───────────────────────────────────────────────
diff --git a/scripts/generator_tick.js b/scripts/generator_tick.js
index b773e50..7375e8c 100644
--- a/scripts/generator_tick.js
+++ b/scripts/generator_tick.js
@@ -119,6 +119,10 @@ function main() {
   // data/bad-aesthetic-patterns.jsonl (written by the Ghost Review UI's
   // bulk-delete endpoint and learn-and-purge-flagged.js), skip the tick
   // entirely. Stops Gemini/SDXL burn on an aesthetic that keeps failing.
+  //
+  // Source of truth lives on prod (UI writes there); pull a fresh copy before
+  // reading. Fails open if SSH hiccups — stale local copy survives.
+  try { execSync(path.join(__dirname, '_pull-bad-aesthetic.sh'), { stdio: 'ignore', timeout: 8000 }); } catch {}
   const { isCategoryCooldown, getAvoidanceAddendum, getCategoryFailureCount } = require('../lib/bad-aesthetic');
   const COOLDOWN_THRESHOLD = parseInt(process.env.GENERATOR_COOLDOWN || '50', 10);
   const category = (recipe.style_tags && recipe.style_tags[0]) || 'mixed';

← b50dfe2 Joint-fix gallery viewer  ·  back to Wallco Ai  ·  Unified /api/bg-textures — title-keyword classification + de 091fbfa →