← back to Ventura Corridor
feat(coverage): /api/ideas/stats + accept-rate line on /coverage.html
faa570d99d35d2627a7c182df227a34ba691dc73 · 2026-05-07 22:49:49 -0700 · SteveStudio2
Sanity-check the idea-loop debate panel: are we accepting too generously
(>50% rate) or too harshly (<5%)? Current 7/55 = 12.7% sits in the
healthy zone.
Routes:
GET /api/ideas/stats {accepted, rejected, accept_rate, accepted_by_type{},
rejected_by_type{}}. Reads both jsonl files in the
idea-loop data dir; 60s cache.
UI:
/coverage.html idea section monospace stats line above the score-sorted
feed: "N accepted · M rejected · P%
accept rate · by type: …"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M public/coverage.htmlM src/server/index.ts
Diff
commit faa570d99d35d2627a7c182df227a34ba691dc73
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Thu May 7 22:49:49 2026 -0700
feat(coverage): /api/ideas/stats + accept-rate line on /coverage.html
Sanity-check the idea-loop debate panel: are we accepting too generously
(>50% rate) or too harshly (<5%)? Current 7/55 = 12.7% sits in the
healthy zone.
Routes:
GET /api/ideas/stats {accepted, rejected, accept_rate, accepted_by_type{},
rejected_by_type{}}. Reads both jsonl files in the
idea-loop data dir; 60s cache.
UI:
/coverage.html idea section monospace stats line above the score-sorted
feed: "N accepted · M rejected · P%
accept rate · by type: …"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
public/coverage.html | 10 ++++++++++
src/server/index.ts | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/public/coverage.html b/public/coverage.html
index 93e7537..3f29571 100644
--- a/public/coverage.html
+++ b/public/coverage.html
@@ -87,6 +87,7 @@ main{padding:32px;max-width:1200px;margin:0 auto}
<div class="bar-section">
<h3>Idea-loop · accepted ideas (top by score)</h3>
<p style="font-family:'Cormorant Garamond',serif;font-style:italic;font-size:13px;color:var(--ink-mute);margin:0 0 14px">Surviving ideas from the autonomous idea-loop debate panel (qwen3 + gemma3 + deepseek-r1). Full viewer at <a href="http://127.0.0.1:9920/" target="_blank" rel="noopener" style="color:var(--metal)">:9920</a>.</p>
+ <div id="idea-stats" style="font-family:var(--mono);font-size:10px;letter-spacing:.16em;color:var(--ink-mute);margin-bottom:14px;text-transform:uppercase"></div>
<div id="idea-feed"></div>
</div>
@@ -160,6 +161,15 @@ async function load() {
renderBars(d.by_vertical, document.getElementById('vert'));
renderBars(d.by_city, document.getElementById('city'));
+ // Idea-loop stats line
+ try {
+ const stats = await fetch('/api/ideas/stats').then(r => r.json());
+ const pct = (stats.accept_rate * 100).toFixed(1);
+ const types = Object.entries(stats.accepted_by_type || {}).map(([k,v]) => k + ':' + v).join(' · ');
+ document.getElementById('idea-stats').textContent =
+ `${stats.accepted} accepted · ${stats.rejected} rejected · ${pct}% accept rate${types ? ' · by type: ' + types : ''}`;
+ } catch(e) {}
+
// Idea-loop feed
try {
const ideas = await fetch('/api/ideas/recent').then(r => r.json());
diff --git a/src/server/index.ts b/src/server/index.ts
index 9ff6830..c1cd119 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -5143,6 +5143,42 @@ app.get('/api/news/archive', async (_req, res) => {
// Bridge to the idea-loop skill at ~/.claude/skills/idea-loop/data/ideas.jsonl.
// Reads the JSONL on demand (not large, ~kb scale), returns last N parsed
// entries. Pure read; idea-loop owns its own writes.
+// Idea-loop stats: accept/reject totals + breakdown by type. Useful to spot
+// "is the panel too generous" (>50% accept) or "too harsh" (<5% accept).
+app.get('/api/ideas/stats', async (_req, res) => {
+ try {
+ const fs = await import('node:fs/promises');
+ const path = await import('node:path');
+ const home = process.env.HOME || '/Users/stevestudio2';
+ const dir = path.join(home, '.claude', 'skills', 'idea-loop', 'data');
+ const readJsonl = async (file: string) => {
+ try {
+ const txt = await fs.readFile(path.join(dir, file), 'utf8');
+ return txt.split(/\r?\n/).filter(Boolean).map(l => {
+ try { return JSON.parse(l); } catch { return null; }
+ }).filter(Boolean);
+ } catch { return []; }
+ };
+ const accepted = await readJsonl('ideas.jsonl');
+ const rejected = await readJsonl('rejects.jsonl');
+ const by = (arr: any[]) => arr.reduce((m: Record<string, number>, x) => {
+ const t = String(x.type || 'other');
+ m[t] = (m[t] || 0) + 1; return m;
+ }, {});
+ const total = accepted.length + rejected.length;
+ res.set('Cache-Control', 'public, max-age=60');
+ res.json({
+ accepted: accepted.length,
+ rejected: rejected.length,
+ accept_rate: total ? +(accepted.length / total).toFixed(3) : 0,
+ accepted_by_type: by(accepted),
+ rejected_by_type: by(rejected)
+ });
+ } catch (e: any) {
+ res.status(500).json({ error: e.message });
+ }
+});
+
app.get('/api/ideas/recent', async (_req, res) => {
try {
const fs = await import('node:fs/promises');
← c9a00d6 feat(coverage): ideas_accepted count in homepage build-statu
·
back to Ventura Corridor
·
feat(coverage): per-type accept-rate mini-bars in idea-loop df2073d →