โ back to Ventura Corridor
iter 74: bulk-generate 50 features now + nightly magazine launchd โ kicked off background nohup npm run magazine:gen 50 (PID 58664, ~25min for 50 features at ~25-31s/each on Mac1 Ollama qwen3:14b); com.steve.ventura-corridor-magazine-gen launchd loaded fires daily 1:32am for 20 fresh features (so the magazine grows organically overnight); /api/magazine/stats endpoint returns total/draft/reviewed/published/spiked/gen_24h + corridor_total for coverage %; /today.html grows a ๐ 'The Boulevard ยท corridor magazine' progress card with stat strip + horizontal coverage bar (currently 6/17,462 = 0.03%) auto-refreshing every 30s; ๐ magazine link added to /today.html nav with metal-glow color
78b1bea840db0231044f18a01c1606fcce70e250 ยท 2026-05-06 16:05:49 -0700 ยท SteveStudio2
Files touched
M public/today.htmlM src/server/index.ts
Diff
commit 78b1bea840db0231044f18a01c1606fcce70e250
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Wed May 6 16:05:49 2026 -0700
iter 74: bulk-generate 50 features now + nightly magazine launchd โ kicked off background nohup npm run magazine:gen 50 (PID 58664, ~25min for 50 features at ~25-31s/each on Mac1 Ollama qwen3:14b); com.steve.ventura-corridor-magazine-gen launchd loaded fires daily 1:32am for 20 fresh features (so the magazine grows organically overnight); /api/magazine/stats endpoint returns total/draft/reviewed/published/spiked/gen_24h + corridor_total for coverage %; /today.html grows a ๐ 'The Boulevard ยท corridor magazine' progress card with stat strip + horizontal coverage bar (currently 6/17,462 = 0.03%) auto-refreshing every 30s; ๐ magazine link added to /today.html nav with metal-glow color
---
public/today.html | 31 +++++++++++++++++++++++++++++++
src/server/index.ts | 20 ++++++++++++++++++++
2 files changed, 51 insertions(+)
diff --git a/public/today.html b/public/today.html
index 2722e59..8ce1a26 100644
--- a/public/today.html
+++ b/public/today.html
@@ -117,6 +117,7 @@
<a href="/responses.html">responses</a>
<a href="/postcards.html">postcards</a>
<a href="/buildings.html">buildings</a>
+ <a href="/magazine.html" style="color:var(--metal-glow)">๐ magazine</a>
<a href="/crawl-derby.html">๐ derby</a>
<a href="/today.html" class="active">today</a>
<span class="theme-toggle-host" style="display:inline-flex;align-items:center;margin-left:6px"></span>
@@ -143,6 +144,17 @@
<div id="prio-strip" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:8px"></div>
</section>
+<section id="magazine-section" style="padding:20px 32px;border-bottom:1px solid var(--rule);background:rgba(184,153,104,0.04)">
+ <div style="display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:10px">
+ <h3 style="font-family:var(--serif);font-style:italic;font-weight:400;font-size:22px;color:var(--metal-glow);margin:0">๐ The Boulevard <span style="font-size:11px;letter-spacing:.18em;color:var(--ink-mute);margin-left:8px">corridor magazine</span></h3>
+ <a href="/magazine.html" style="color:var(--metal);text-decoration:none;font-size:10px;letter-spacing:.18em;text-transform:uppercase;border:1px solid var(--rule);padding:4px 10px">Open issue โ</a>
+ </div>
+ <div id="magazine-stats" style="margin-top:10px;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-mute)">โ</div>
+ <div id="magazine-progress" style="margin-top:8px;height:6px;background:var(--noir);border:1px solid var(--rule);overflow:hidden">
+ <div id="magazine-bar" style="height:100%;background:var(--metal-glow);width:0%;transition:width 0.6s"></div>
+ </div>
+</section>
+
<section id="stale-section" style="display:none;padding:20px 32px;border-bottom:1px solid var(--rule);background:rgba(182,101,101,0.05)">
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:12px;flex-wrap:wrap;gap:10px">
<h3 style="font-family:var(--serif);font-style:italic;font-weight:400;font-size:22px;color:#c47b7b;margin:0">Cold leads ยท re-touch or close</h3>
@@ -568,6 +580,25 @@ async function loadStale() {
}
loadStale();
setInterval(loadStale, 5 * 60_000);
+
+// โโโ Magazine progress (iter 74) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+async function loadMagazineStats() {
+ try {
+ const d = await fetch('/api/magazine/stats').then(r => r.json());
+ const total = Number(d.total || 0);
+ const corr = Number(d.corridor_total || 1);
+ const pct = Math.min(100, (100 * total / corr));
+ document.getElementById('magazine-stats').innerHTML =
+ `<b style="color:var(--metal-glow);font-family:var(--mono);font-size:13px">${total.toLocaleString()}</b> features written ยท ` +
+ `<b style="color:var(--metal);font-family:var(--mono)">${d.draft || 0}</b> drafts ยท ` +
+ `<b style="color:var(--green,#6a9b73);font-family:var(--mono)">${d.published || 0}</b> published ยท ` +
+ `<b style="color:var(--metal);font-family:var(--mono)">${d.gen_24h || 0}</b> generated last 24h ยท ` +
+ `coverage <b style="color:var(--metal-glow);font-family:var(--mono)">${pct.toFixed(1)}%</b> of ${corr.toLocaleString()} businesses`;
+ document.getElementById('magazine-bar').style.width = pct + '%';
+ } catch {}
+}
+loadMagazineStats();
+setInterval(loadMagazineStats, 30_000);
</script>
</body>
</html>
diff --git a/src/server/index.ts b/src/server/index.ts
index 851ad0c..5dc8f61 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1742,6 +1742,26 @@ app.get('/api/data-freshness', async (_req, res) => {
}
});
+app.get('/api/magazine/stats', async (_req, res) => {
+ try {
+ const r = await query(`
+ SELECT
+ count(*) AS total,
+ count(*) FILTER (WHERE status = 'draft') AS draft,
+ count(*) FILTER (WHERE status = 'reviewed') AS reviewed,
+ count(*) FILTER (WHERE status = 'published') AS published,
+ count(*) FILTER (WHERE status = 'spiked') AS spiked,
+ count(*) FILTER (WHERE generated_at >= NOW() - INTERVAL '24 hours') AS gen_24h,
+ max(generated_at) AS last_generated_at
+ FROM magazine_features
+ `);
+ const corr = await query(`SELECT count(*) AS n FROM businesses WHERE on_corridor AND merged_into IS NULL`);
+ res.json({ ...r.rows[0], corridor_total: Number(corr.rows[0].n) });
+ } catch (e: any) {
+ res.status(500).json({ error: e.message });
+ }
+});
+
// โโโ Magazine: AI-generated features for corridor businesses โโโโโโโโ
app.get('/api/magazine', async (req, res) => {
try {
โ 1daefa6 iter 73: PIVOT to corridor magazine MVP โ migration 016 adds
ยท
back to Ventura Corridor
ยท
iter 75: feature regen-via-API endpoint โ POST /api/magazine e31d8bb โ