← back to Wallco Ai
drunk-animals: cross-tick prompt dedup against last 5 PG rows
83c5b98fc041ad8333a0114c983afba2d935f0ae · 2026-05-12 21:31:30 -0700 · SteveStudio2
Daemon was firing repetitive prompts (4 of last 6 were orangutan x3 +
bullfrog x2) because buildPrompt() picks randomly with no memory of
recent ticks. Now tick reads the 5 most-recent drunk-animal prompts
from PG, extracts which ANIMAL + STYLE phrases they used, and rolls
candidate prompts up to 40 times until one dodges both sets. Falls
back to 'avoid last animal only' if everything's been used, then
plain buildPrompt as last resort.
Verified: when recent 5 = [sloth(*), orangutan, frog, elephant, frog],
fresh-pick returned 'starry-eyed sloth holding a wine glass' on first
try (* sloth wasn't actually in recent at test time).
Files touched
M scripts/drunk_animals_tick.js
Diff
commit 83c5b98fc041ad8333a0114c983afba2d935f0ae
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 21:31:30 2026 -0700
drunk-animals: cross-tick prompt dedup against last 5 PG rows
Daemon was firing repetitive prompts (4 of last 6 were orangutan x3 +
bullfrog x2) because buildPrompt() picks randomly with no memory of
recent ticks. Now tick reads the 5 most-recent drunk-animal prompts
from PG, extracts which ANIMAL + STYLE phrases they used, and rolls
candidate prompts up to 40 times until one dodges both sets. Falls
back to 'avoid last animal only' if everything's been used, then
plain buildPrompt as last resort.
Verified: when recent 5 = [sloth(*), orangutan, frog, elephant, frog],
fresh-pick returned 'starry-eyed sloth holding a wine glass' on first
try (* sloth wasn't actually in recent at test time).
---
scripts/drunk_animals_tick.js | 50 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/scripts/drunk_animals_tick.js b/scripts/drunk_animals_tick.js
index 6feba15..e40719e 100644
--- a/scripts/drunk_animals_tick.js
+++ b/scripts/drunk_animals_tick.js
@@ -8,9 +8,53 @@
*
* Usage: node scripts/drunk_animals_tick.js
*/
-const { spawnSync } = require('child_process');
+const { spawnSync, execSync } = require('child_process');
const path = require('path');
-const { buildPrompt } = require('./drunk_animal_prompts');
+const { buildPrompt, ANIMALS, STYLES } = require('./drunk_animal_prompts');
+
+// Cross-tick dedup — read the last N drunk-animal prompts from PG and
+// extract the animal phrase + style phrase from each. Reject any candidate
+// prompt that reuses a recent animal/style. Prevents the "orangutan x3 in
+// a row" pattern we saw at 04:20/04:24/04:28.
+function recentlyUsed(n = 5) {
+ try {
+ const raw = execSync(
+ `psql dw_unified -At -F'|' -c "SELECT prompt FROM spoon_all_designs ` +
+ `WHERE category='drunk-animals' ORDER BY id DESC LIMIT ${n};"`,
+ { encoding: 'utf8' }
+ );
+ const animals = new Set();
+ const styles = new Set();
+ for (const line of raw.split('\n').filter(Boolean)) {
+ for (const a of ANIMALS) if (line.includes(a)) { animals.add(a); break; }
+ for (const s of STYLES) if (line.includes(s)) { styles.add(s); break; }
+ }
+ return { animals, styles };
+ } catch { return { animals: new Set(), styles: new Set() }; }
+}
+
+function freshPrompt() {
+ const used = recentlyUsed(5);
+ // Try up to 40 times to dodge both recent animals AND recent styles.
+ // Fall back to plain buildPrompt if everything's been used (unlikely).
+ for (let i = 0; i < 40; i++) {
+ const p = buildPrompt();
+ const a = ANIMALS.find(x => p.includes(x));
+ const s = STYLES.find(x => p.includes(x));
+ if (a && used.animals.has(a)) continue;
+ if (s && used.styles.has(s)) continue;
+ return p;
+ }
+ // Last resort — just avoid the most-recent animal
+ for (let i = 0; i < 20; i++) {
+ const p = buildPrompt();
+ const a = ANIMALS.find(x => p.includes(x));
+ const lastAnimal = [...used.animals].pop();
+ if (a && a === lastAnimal) continue;
+ return p;
+ }
+ return buildPrompt();
+}
const STOP_HOUR = parseInt(process.env.STOP_HOUR || '6', 10); // 6 AM
const STOP_MIN = parseInt(process.env.STOP_MIN || '0', 10);
@@ -28,7 +72,7 @@ if (!inRunWindow) {
}
const ROOT = path.join(__dirname, '..');
-const prompt = buildPrompt();
+const prompt = freshPrompt();
console.log(`[tick ${new Date().toISOString()}] ${prompt.slice(0, 100)}…`);
const r = spawnSync('node', [
← 5308324 drunk-animals: replace launchd plist with pm2 daemon for pre
·
back to Wallco Ai
·
drunk-animals: /drunk-animals/live realtime viewer + JSON fe 25d68d1 →