← 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
M public/today.htmlM src/server/index.ts
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 →