← back to Wallco Ai
drunk-animals: replace launchd plist with pm2 daemon for precise cadence
530832434f508762e2955e74f34934348e8e1bd4 · 2026-05-12 20:59:42 -0700 · SteveStudio2
macOS launchd was coalescing the StartInterval=240s firings into
~20-min intervals (system idle → power-saving deferred wake). pm2
daemon uses setInterval(4*60*1000) so cadence is exact regardless
of system idle state.
- scripts/drunk_animals_daemon.js: long-lived process, fires
scripts/drunk_animals_tick.js every 240s; skips if previous tick
is still running; bails outside 18:00→06:00 PT window
- launchctl unloaded com.steve.wallco-drunk-animals.plist
- pm2 started as 'wallco-drunk-animals' with PATH including
/opt/homebrew/bin so PIL chain works
- pm2 save dumped so daemon survives reboot
- Manual tick fired: design #1838 generated cleanly, 55 total
Files touched
A scripts/drunk_animals_daemon.js
Diff
commit 530832434f508762e2955e74f34934348e8e1bd4
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Tue May 12 20:59:42 2026 -0700
drunk-animals: replace launchd plist with pm2 daemon for precise cadence
macOS launchd was coalescing the StartInterval=240s firings into
~20-min intervals (system idle → power-saving deferred wake). pm2
daemon uses setInterval(4*60*1000) so cadence is exact regardless
of system idle state.
- scripts/drunk_animals_daemon.js: long-lived process, fires
scripts/drunk_animals_tick.js every 240s; skips if previous tick
is still running; bails outside 18:00→06:00 PT window
- launchctl unloaded com.steve.wallco-drunk-animals.plist
- pm2 started as 'wallco-drunk-animals' with PATH including
/opt/homebrew/bin so PIL chain works
- pm2 save dumped so daemon survives reboot
- Manual tick fired: design #1838 generated cleanly, 55 total
---
scripts/drunk_animals_daemon.js | 61 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/scripts/drunk_animals_daemon.js b/scripts/drunk_animals_daemon.js
new file mode 100644
index 0000000..0044838
--- /dev/null
+++ b/scripts/drunk_animals_daemon.js
@@ -0,0 +1,61 @@
+#!/usr/bin/env node
+/**
+ * Drunk-animals daemon — runs as a pm2-managed long-lived process and fires
+ * scripts/drunk_animals_tick.js exactly every TICK_MS milliseconds. Replaces
+ * the launchd plist which got deferred from 4-min to 20-min cadence by
+ * macOS power-coalescing on Apple Silicon (idle system + StartInterval is
+ * "best effort", not guaranteed).
+ *
+ * Stops itself past STOP_HOUR PT (default 6 AM) — does not exit, but
+ * suppresses tick firings. Resumes the next evening if the daemon is still
+ * up (the tick script also has its own outside-window guard, redundant).
+ */
+
+const { spawn } = require('child_process');
+const path = require('path');
+
+const TICK_MS = parseInt(process.env.TICK_MS || '240000', 10); // 4 min
+const STOP_HOUR = parseInt(process.env.STOP_HOUR || '6', 10);
+const ROOT = path.join(__dirname, '..');
+const TICK_SCRIPT = path.join(__dirname, 'drunk_animals_tick.js');
+
+function nowPT() {
+ return new Date(new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
+}
+
+function inWindow() {
+ const t = nowPT();
+ const h = t.getHours();
+ // Active window: 18:00 → 05:59 PT next day. Quiet 06:00 → 17:59.
+ return h >= 18 || h < STOP_HOUR;
+}
+
+let running = false;
+
+function fire() {
+ if (running) {
+ console.log(`[${new Date().toISOString()}] previous tick still running, skipping`);
+ return;
+ }
+ if (!inWindow()) {
+ const h = nowPT().getHours();
+ if (h % 4 === 0) console.log(`[${new Date().toISOString()}] outside window (PT hour ${h}), idle`);
+ return;
+ }
+ running = true;
+ const t0 = Date.now();
+ const child = spawn('node', [TICK_SCRIPT], { stdio: 'inherit', cwd: ROOT });
+ child.on('exit', code => {
+ running = false;
+ console.log(`[${new Date().toISOString()}] tick done in ${((Date.now()-t0)/1000).toFixed(1)}s (exit ${code})`);
+ });
+ child.on('error', err => {
+ running = false;
+ console.error(`[${new Date().toISOString()}] tick spawn error:`, err.message);
+ });
+}
+
+console.log(`[${new Date().toISOString()}] drunk-animals daemon up · TICK_MS=${TICK_MS} STOP_HOUR=${STOP_HOUR}PT`);
+console.log(`[${new Date().toISOString()}] first tick in ${TICK_MS/1000}s`);
+setInterval(fire, TICK_MS);
+// Don't fire on boot — give pm2 a moment, then ride the setInterval cadence
← 73f6770 /compare: ETag + 304 keyed on (catalog lastmod, view, ordere
·
back to Wallco Ai
·
drunk-animals: cross-tick prompt dedup against last 5 PG row 83c5b98 →