feat: domain breakdown on dashboard — contributions by domain with top contributors

New _domain_breakdown() function cross-references merged PRs with
contributor principals. Dashboard shows per-domain knowledge PR counts
and top 3 contributors for each domain. API: GET /api/domains returns
full breakdown.

Pentagon-Agent: Epimetheus <3D35839A-7722-4740-B93D-51157F7D5E70>
This commit is contained in:
m3taversal 2026-03-26 15:05:48 +00:00
parent ae1cce730c
commit 305445b164

View file

@ -454,6 +454,7 @@ async def handle_dashboard(request):
vital_signs = _compute_vital_signs(conn)
contributors_principal = _contributor_leaderboard(conn, limit=10, view="principal")
contributors_agent = _contributor_leaderboard(conn, limit=10, view="agent")
domain_breakdown = _domain_breakdown(conn)
except sqlite3.Error as e:
return web.Response(
text=_render_error(f"Pipeline database unavailable: {e}"),
@ -461,7 +462,7 @@ async def handle_dashboard(request):
status=503,
)
now = datetime.now(timezone.utc)
html = _render_dashboard(metrics, snapshots, changes, vital_signs, contributors_principal, contributors_agent, now)
html = _render_dashboard(metrics, snapshots, changes, vital_signs, contributors_principal, contributors_agent, domain_breakdown, now)
return web.Response(text=html, content_type="text/html")
@ -502,11 +503,52 @@ async def handle_api_contributors(request):
return web.json_response({"contributors": contributors, "view": view})
def _domain_breakdown(conn) -> dict:
"""Per-domain contribution breakdown: claims, contributors, sources, decisions."""
# Claims per domain from merged knowledge PRs
domain_stats = {}
for r in conn.execute("""
SELECT domain, count(*) as prs,
SUM(CASE WHEN commit_type='knowledge' THEN 1 ELSE 0 END) as knowledge_prs
FROM prs WHERE status='merged' AND domain IS NOT NULL
GROUP BY domain ORDER BY prs DESC
""").fetchall():
domain_stats[r["domain"]] = {
"total_prs": r["prs"],
"knowledge_prs": r["knowledge_prs"] or 0,
"contributors": [],
}
# Top contributors per domain (from PR agent field + principal roll-up)
has_principal = _has_column(conn, "contributors", "principal")
for r in conn.execute("""
SELECT p.domain,
COALESCE(c.principal, p.agent, 'unknown') as contributor,
count(*) as cnt
FROM prs p
LEFT JOIN contributors c ON LOWER(p.agent) = c.handle
WHERE p.status='merged' AND p.commit_type='knowledge' AND p.domain IS NOT NULL
GROUP BY p.domain, contributor
ORDER BY p.domain, cnt DESC
""").fetchall():
domain = r["domain"]
if domain in domain_stats:
domain_stats[domain]["contributors"].append({
"handle": r["contributor"],
"claims": r["cnt"],
})
return domain_stats
async def handle_api_domains(request):
"""GET /api/domains — per-domain health breakdown."""
"""GET /api/domains — per-domain contribution breakdown.
Returns claims, contributors, and knowledge PR counts per domain.
"""
conn = _conn(request)
metrics = _current_metrics(conn)
return web.json_response({"domains": metrics["domains"]})
breakdown = _domain_breakdown(conn)
return web.json_response({"domains": breakdown})
# ─── Dashboard HTML ──────────────────────────────────────────────────────────
@ -521,7 +563,7 @@ def _render_error(message: str) -> str:
</head><body><div class="err"><h1>Argus</h1><p>{message}</p><p>Check if <code>teleo-pipeline.service</code> is running and pipeline.db exists.</p></div></body></html>"""
def _render_dashboard(metrics, snapshots, changes, vital_signs, contributors_principal, contributors_agent, now) -> str:
def _render_dashboard(metrics, snapshots, changes, vital_signs, contributors_principal, contributors_agent, domain_breakdown, now) -> str:
"""Render the full operational dashboard as HTML with Chart.js."""
# Prepare chart data
@ -847,6 +889,21 @@ def _render_dashboard(metrics, snapshots, changes, vital_signs, contributors_pri
</div>
</div>
<!-- Domain Breakdown -->
<div class="section">
<div class="section-title">Contributions by Domain</div>
<div class="card">
<table>
<tr><th>Domain</th><th>Knowledge PRs</th><th>Top Contributors</th></tr>
{"".join(f'''<tr>
<td style="color:#58a6ff">{domain}</td>
<td>{stats["knowledge_prs"]}</td>
<td style="font-size:12px;color:#8b949e">{", ".join(f'{c["handle"]} ({c["claims"]})' for c in stats["contributors"][:3])}</td>
</tr>''' for domain, stats in sorted(domain_breakdown.items(), key=lambda x: x[1]["knowledge_prs"], reverse=True) if stats["knowledge_prs"] > 0)}
</table>
</div>
</div>
<!-- Stagnation Alerts -->
{"" if not vital_signs["domain_activity"]["stagnant"] else f'''
<div class="section">