[object Object]

← back to Ventura Corridor

iter 165: /api/magazine/duplicates + nav pill — surfaces qwen3 headline-templating (5 groups affecting 14 features today)

680bcb7fb215540650fa47f3ca6b5cddeafd1b5b · 2026-05-06 19:51:07 -0700 · SteveStudio2

Files touched

Diff

commit 680bcb7fb215540650fa47f3ca6b5cddeafd1b5b
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 6 19:51:07 2026 -0700

    iter 165: /api/magazine/duplicates + nav pill — surfaces qwen3 headline-templating (5 groups affecting 14 features today)
---
 public/today.html   | 20 ++++++++++++++++++++
 src/server/index.ts | 30 ++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/public/today.html b/public/today.html
index e733c4d..16cc0f7 100644
--- a/public/today.html
+++ b/public/today.html
@@ -127,6 +127,7 @@
     <a href="/coverage.html">▰ coverage</a>
     <a href="/magazine.html?quality=1" id="nav-quality" style="color:var(--ink-mute)">🔧 quality</a>
     <a href="/health.html">♥ health</a>
+    <a href="/api/magazine/duplicates" id="nav-dupes" target="_blank" style="color:var(--ink-mute)">⊕ dupes</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>
@@ -783,6 +784,25 @@ async function loadGenQueue() {
 loadGenQueue();
 setInterval(loadGenQueue, 5 * 60_000);
 
+async function loadDupesPill() {
+  try {
+    const d = await fetch('/api/magazine/duplicates').then(r => r.json());
+    const a = document.getElementById('nav-dupes');
+    if (!a) return;
+    if (d.groups > 0) {
+      a.style.color = 'var(--metal-glow)';
+      a.innerHTML = `⊕ dupes <span style="background:var(--metal);color:#0a0a0c;padding:0 6px;margin-left:4px;font-size:8px;letter-spacing:.1em;font-weight:600">${d.groups}</span>`;
+      a.title = `${d.groups} duplicate-headline groups affecting ${d.total_affected} features — qwen3 templating signal`;
+    } else {
+      a.style.color = 'var(--green)';
+      a.textContent = '⊕ dupes ✓';
+      a.title = 'every headline is unique';
+    }
+  } catch {}
+}
+loadDupesPill();
+setInterval(loadDupesPill, 5 * 60_000);
+
 async function loadDraftPick() {
   try {
     const d = await fetch('/api/magazine/draft-pick-of-the-night').then(r => r.json());
diff --git a/src/server/index.ts b/src/server/index.ts
index f4ee494..f458566 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1932,6 +1932,36 @@ app.get('/api/magazine/random', async (req, res) => {
   }
 });
 
+// Duplicate-headline detector — when qwen3 converges to the same headline pattern
+// (e.g. "Precision Care in Encino's Heart" 3×), the editorial assistant is
+// templating. Surfacing duplicates lets Steve regen the runner-ups for variety.
+app.get('/api/magazine/duplicates', async (_req, res) => {
+  try {
+    const r = await query(`
+      SELECT mf.headline,
+             count(*) AS dup_count,
+             array_agg(json_build_object(
+               'id', mf.id,
+               'biz_name', b.name,
+               'category_tag', mf.category_tag,
+               'status', mf.status,
+               'editorial_len', length(mf.editorial),
+               'generated_at', mf.generated_at
+             ) ORDER BY mf.generated_at DESC) AS features
+      FROM magazine_features mf
+      JOIN businesses b ON b.id = mf.business_id
+      WHERE mf.headline IS NOT NULL
+      GROUP BY mf.headline
+      HAVING count(*) > 1
+      ORDER BY count(*) DESC, mf.headline ASC
+    `);
+    const totalAffected = r.rows.reduce((acc: number, row: any) => acc + Number(row.dup_count), 0);
+    res.json({ groups: r.rowCount, total_affected: totalAffected, rows: r.rows });
+  } catch (e: any) {
+    res.status(500).json({ error: e.message });
+  }
+});
+
 // Draft-pick-of-the-night — strongest unpublished draft from the last 24h,
 // scored by editorial length × pull-quote-presence × subhead × ad-signal × random tiebreak.
 // Used to seed the "Publish next" pill on /today.html and the morning email.

← 48b3342 iter 164: morning_feature_email now includes 'Suggested next  ·  back to Ventura Corridor  ·  iter 166: /duplicates.html admin viewer — keeps oldest, clic 1efb250 →