[object Object]

← back to Rentv 2026

feat(brief): hard monthly spend cap (BRIEF_MONTHLY_CAP, default 40 ≈ $6/mo) — a dollar backstop beyond the top-deal-change dedupe. Even if the top deal flaps, spend is bounded; ledger in rsync-excluded public/audio (prod-owned, survives deploys). Verified: cap=0 skips before any TTS call (no mp3, no spend).

1a6901b599d8cbec26ffca670c9fcaec1aa1a9a6 · 2026-08-06 16:38:11 -0700 · Steve

Files touched

Diff

commit 1a6901b599d8cbec26ffca670c9fcaec1aa1a9a6
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 16:38:11 2026 -0700

    feat(brief): hard monthly spend cap (BRIEF_MONTHLY_CAP, default 40 ≈ $6/mo) — a dollar backstop beyond the top-deal-change dedupe. Even if the top deal flaps, spend is bounded; ledger in rsync-excluded public/audio (prod-owned, survives deploys). Verified: cap=0 skips before any TTS call (no mp3, no spend).
---
 scripts/gen-deal-brief.mjs | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/scripts/gen-deal-brief.mjs b/scripts/gen-deal-brief.mjs
index ceefafe8..32ea27d6 100644
--- a/scripts/gen-deal-brief.mjs
+++ b/scripts/gen-deal-brief.mjs
@@ -18,6 +18,7 @@ const DATA = join(ROOT, 'data');
 const AUDIO = join(ROOT, 'public', 'audio');
 const VOICE_ID = process.env.RENTV_VOICE_ID || 'czUPGx5phxE6qpQ46nRS';
 const RATE_PER_1K = 0.30; // ElevenLabs Creator-tier estimate, $/1000 chars
+const MONTHLY_CAP = parseInt(process.env.BRIEF_MONTHLY_CAP || '40', 10); // hard spend backstop (~$6/mo)
 const FORCE = process.argv.includes('--force');
 
 function elevenKey() {
@@ -85,9 +86,22 @@ export async function generateBrief() {
 
   const meta = { deal_id: top.id, title: top.title, acquirer: top.acquirer || null, amount_label: top.amount_label || null, url: top.url || null, script: text, chars, generated_at: new Date().toISOString() };
   if (process.env.DRY_RUN) { meta.dry_run = true; writeFileSync(metaPath, JSON.stringify(meta, null, 1)); console.log('DRY_RUN=1 → wrote metadata, NO TTS call, $0'); return; }
+
+  // HARD MONTHLY SPEND CAP — a dollar backstop beyond the top-deal-change dedupe. Even if the top deal
+  // flaps, this bounds spend to MONTHLY_CAP generations/month (default 40 ≈ $6/mo). Ledger lives in the
+  // rsync-excluded public/audio (prod-owned, survives deploys). --force overrides.
+  const ledgerPath = join(AUDIO, 'brief-ledger.json');
+  const month = new Date().toISOString().slice(0, 7); // YYYY-MM
+  let ledger = {}; try { ledger = JSON.parse(readFileSync(ledgerPath, 'utf8')); } catch {}
+  if ((ledger[month] || 0) >= MONTHLY_CAP && !FORCE) {
+    console.log(`monthly cap reached (${ledger[month] || 0}/${MONTHLY_CAP} for ${month}) — SKIP, no spend`); return;
+  }
+
   await tts(text, join(AUDIO, 'deal-brief.mp3'));
+  ledger[month] = (ledger[month] || 0) + 1;
+  writeFileSync(ledgerPath, JSON.stringify(ledger, null, 1));
   writeFileSync(metaPath, JSON.stringify(meta, null, 1));
-  console.log(`✓ /public/audio/deal-brief.mp3 written · actual ~$${est.toFixed(3)}`);
+  console.log(`✓ /public/audio/deal-brief.mp3 written · actual ~$${est.toFixed(3)} · ${ledger[month]}/${MONTHLY_CAP} this month`);
 }
 
 // Run standalone only when invoked directly (not when imported by pull-deals.mjs).

← 35d978b2 auto-data-snapshot: 2026-08-06T16:33:58 (8 data files) — .de  ·  back to Rentv 2026  ·  fix(firm): profile KPIs drill deeper (every-datum-hrefs rule 9c83657e →