[object Object]

← back to Ventura Corridor

iter 159: /api/magazine/gen-queue + 'Up next' rail on today.html — surfaces what watchdog will tackle

4b6a73d0d609a005d857b1fe76dd06e1baca235d · 2026-05-06 19:34:50 -0700 · SteveStudio2

Files touched

Diff

commit 4b6a73d0d609a005d857b1fe76dd06e1baca235d
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 6 19:34:50 2026 -0700

    iter 159: /api/magazine/gen-queue + 'Up next' rail on today.html — surfaces what watchdog will tackle
---
 public/today.html   | 22 ++++++++++++++++++++++
 src/server/index.ts | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/public/today.html b/public/today.html
index 5be098a..def2c6f 100644
--- a/public/today.html
+++ b/public/today.html
@@ -137,6 +137,11 @@
   <span id="mag-activity-strip" style="font-family:var(--mono);font-size:11px;color:var(--ink-mute)">…</span>
 </section>
 
+<section style="padding:14px 32px;border-bottom:1px solid var(--rule);background:rgba(184,153,104,0.02);overflow-x:auto;white-space:nowrap" id="gen-queue-rail">
+  <span style="font-family:var(--serif);font-style:italic;color:var(--metal);font-size:14px;margin-right:14px">Up next on the gen queue:</span>
+  <span id="gen-queue-strip" style="font-family:var(--mono);font-size:11px;color:var(--ink-mute)">…</span>
+</section>
+
 <section class="day-hero">
   <div class="date" id="today-date">—</div>
   <h2>Three things, <em>in order</em>.</h2>
@@ -754,6 +759,23 @@ async function loadMagActivity() {
 loadMagActivity();
 setInterval(loadMagActivity, 90_000);
 
+async function loadGenQueue() {
+  try {
+    const d = await fetch('/api/magazine/gen-queue?limit=8').then(r => r.json());
+    const items = (d.queue || []).map(q => {
+      const ads = Number(q.paid_ads_count || 0);
+      const adsHtml = ads > 0 ? `<span style="color:var(--accent,#a8763a);margin-left:4px;font-size:9px">$${ads}</span>` : '';
+      const name = String(q.name || '').slice(0, 28);
+      const city = q.city ? `<span style="color:var(--metal);margin-left:4px;font-size:9px">${q.city.slice(0, 12)}</span>` : '';
+      return `<span style="margin-right:24px;color:var(--ink-mute)">${name}${adsHtml}${city}</span>`;
+    });
+    const pill = document.getElementById('gen-queue-strip');
+    if (pill) pill.innerHTML = items.join('') || '<i style="font-style:italic;font-family:var(--serif)">queue empty</i>';
+  } catch {}
+}
+loadGenQueue();
+setInterval(loadGenQueue, 5 * 60_000);
+
 async function updateFeedbackPill() {
   try {
     const d = await fetch('/api/feedback').then(r => r.json());
diff --git a/src/server/index.ts b/src/server/index.ts
index a562eab..1d3f470 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -2037,6 +2037,39 @@ app.get('/api/coverage', async (_req, res) => {
   }
 });
 
+// Gen queue — what the watchdog will likely tackle next. Same ORDER BY logic as
+// generate_features.ts, surfaced for visibility (no random() so the order is stable
+// across calls; the actual gen job re-shuffles ties via random()).
+app.get('/api/magazine/gen-queue', async (req, res) => {
+  try {
+    const limit = Math.min(50, Math.max(1, parseInt(String(req.query.limit || 20), 10) || 20));
+    const r = await query(
+      `SELECT b.id, b.name, b.address, b.city, b.zip,
+              b.raw->>'primary_naics_description' AS naics,
+              p.pitch_type,
+              COALESCE((be.ad_signals->>'paid_ads_count')::int, 0) AS paid_ads_count
+       FROM businesses b
+       LEFT JOIN pitches p ON p.business_id = b.id
+       LEFT JOIN business_enrichment be ON be.business_id = b.id
+       WHERE b.on_corridor
+         AND b.merged_into IS NULL
+         AND b.address IS NOT NULL
+         AND b.name NOT ILIKE '%LLC' AND b.name NOT ILIKE '%INC' AND b.name NOT ILIKE '%CORP'
+         AND length(b.name) BETWEEN 4 AND 60
+         AND NOT EXISTS (SELECT 1 FROM magazine_features mf WHERE mf.business_id = b.id)
+       ORDER BY
+         (COALESCE((be.ad_signals->>'paid_ads_count')::int, 0) > 0) DESC,
+         (p.pitch_type IS NOT NULL) DESC,
+         b.id ASC
+       LIMIT $1`,
+      [limit]
+    );
+    res.json({ count: r.rowCount, queue: r.rows });
+  } catch (e: any) {
+    res.status(500).json({ error: e.message });
+  }
+});
+
 // Coverage history — feeds the "growth over time" line on /coverage.html.
 app.get('/api/coverage/history', async (_req, res) => {
   try {

← 52e164c iter 158: coverage_snapshots table + nightly 11:55 PM job +  ·  back to Ventura Corridor  ·  iter 160: /issue.txt — plain-text dump of full issue (50KB, 4fc18b8 →