[object Object]

← back to Rentv 2026

harden(brief): stale-lock TTL — a hard-killed run (SIGKILL/OOM) that leaves the brief-gen lock behind can't silently disable brief refresh forever; a run >15min old (≫ any real run) is stolen with a WARN, else a live concurrent run holds it → skip. Closes the one lock caveat Cody raised on the concurrent-run fix.

04680821cc9c067d02d8885dda1523c2a7b09eb6 · 2026-08-06 17:02:29 -0700 · Steve

Files touched

Diff

commit 04680821cc9c067d02d8885dda1523c2a7b09eb6
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 17:02:29 2026 -0700

    harden(brief): stale-lock TTL — a hard-killed run (SIGKILL/OOM) that leaves the brief-gen lock behind can't silently disable brief refresh forever; a run >15min old (≫ any real run) is stolen with a WARN, else a live concurrent run holds it → skip. Closes the one lock caveat Cody raised on the concurrent-run fix.
---
 scripts/gen-deal-brief.mjs | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/scripts/gen-deal-brief.mjs b/scripts/gen-deal-brief.mjs
index 14f18ce3..7a16f80e 100644
--- a/scripts/gen-deal-brief.mjs
+++ b/scripts/gen-deal-brief.mjs
@@ -9,7 +9,7 @@
 //
 // Boomer is the on-air PERSONA ("This is Boom…") — never "Bloom" in any customer-facing text. The script is
 // factual-only (no editorializing) so an auto-published brief can't misstate a deal.
-import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from 'fs';
+import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, statSync } from 'fs';
 import { join, dirname } from 'path';
 import { fileURLToPath } from 'url';
 
@@ -92,7 +92,17 @@ export async function generateBrief() {
   // Lock so two overlapping runs (cron + a manual invocation) can't both pass the cap check and double-spend.
   const lockPath = join(AUDIO, 'brief-gen.lock');
   try { writeFileSync(lockPath, String(process.pid), { flag: 'wx' }); }
-  catch { console.log('another brief-gen in progress (lock held) — SKIP, no spend'); return; }
+  catch {
+    // Lock held. Steal it ONLY if stale (>15 min = a crashed run left it) so a hard-kill can't silently
+    // disable brief refresh forever; otherwise a real concurrent run holds it → skip. 15 min ≫ any real
+    // run, so this never races a live run.
+    try {
+      if (Date.now() - statSync(lockPath).mtimeMs > 15 * 60 * 1000) {
+        unlinkSync(lockPath); writeFileSync(lockPath, String(process.pid), { flag: 'wx' });
+        console.error('WARN: stole a stale brief-gen lock (>15min old — prior run likely crashed)');
+      } else { console.log('another brief-gen in progress (lock held) — SKIP, no spend'); return; }
+    } catch { console.log('brief-gen lock contended — SKIP, no spend'); return; }
+  }
   try {
     // HARD MONTHLY SPEND CAP — a dollar backstop beyond the top-deal-change dedupe (bounds spend to
     // MONTHLY_CAP/month ≈ $6). Ledger in rsync-excluded public/audio (prod-owned, survives deploys).

← 3e79a22d harden(agents review): spend-safety + robustness pass on thi  ·  back to Rentv 2026  ·  auto-data-snapshot: 2026-08-06T17:04:50 (7 data files) — dat 9911142f →