Imports 67 files from VPS (/opt/teleo-eval/) into repo as the single source of truth. Previously only 8 of 67 files existed in repo — the rest were deployed directly to VPS via SCP, causing massive drift. Includes: - pipeline/lib/: 33 Python modules (daemon core, extraction, evaluation, merge, cascade, cross-domain, costs, attribution, etc.) - pipeline/: main daemon (teleo-pipeline.py), reweave.py, batch-extract-50.sh - diagnostics/: 19 files (4-page dashboard, alerting, daily digest, review queue, tier1 metrics) - agent-state/: bootstrap, lib-state, cascade inbox processor, schema - systemd/: service unit files for reference - deploy.sh: rsync-based deploy with --dry-run, syntax checks, dirty-tree gate - research-session.sh: updated with Step 8.5 digest + cascade inbox processing No new code written — all files are exact copies from VPS as of 2026-04-06. From this point forward: edit in repo, commit, then deploy.sh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1424 lines
69 KiB
HTML
1424 lines
69 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Teleo Codex — Live Terminal</title>
|
||
<style>
|
||
:root {
|
||
--brand: #6E46E5;
|
||
--brand-dim: rgba(110, 70, 229, 0.15);
|
||
--brand-glow: rgba(110, 70, 229, 0.3);
|
||
|
||
--bg: #0D1117;
|
||
--surface: #161B22;
|
||
--border: #21262D;
|
||
--text: #E6EDF3;
|
||
--text-dim: #8B949E;
|
||
--text-muted: #484F58;
|
||
|
||
--status-healthy: #3FB950;
|
||
--status-warning: #D29922;
|
||
--status-critical: #F85149;
|
||
|
||
/* Domain colors (Clay spec — separate from operations) */
|
||
--d-entertainment: #6E46E5;
|
||
--d-technology: #58A6FF;
|
||
--d-finance: #3FB950;
|
||
--d-internet-finance: #39D353;
|
||
--d-collective-agents: #BC8CFF;
|
||
--d-ai-alignment: #58A6FF;
|
||
--d-health: #F97583;
|
||
--d-space-development: #FFA657;
|
||
--d-grand-strategy: #6E46E5;
|
||
--d-general: #8B949E;
|
||
--d-energy: #3FB950;
|
||
--d-living-agents: #79C0FF;
|
||
--d-collective-intelligence: #D29922;
|
||
--d-critical-systems: #F85149;
|
||
|
||
/* Agent colors */
|
||
--a-leo: #D29922;
|
||
--a-rio: #2DD4BF;
|
||
--a-vida: #3FB950;
|
||
--a-theseus: #F97316;
|
||
--a-clay: #58A6FF;
|
||
--a-astra: #BC8CFF;
|
||
--a-epimetheus: #8B949E;
|
||
--a-ganymede: #E5534B;
|
||
--a-argus: #F0883E;
|
||
--a-oberon: #6E46E5;
|
||
|
||
--font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', monospace;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: var(--font-mono);
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
min-height: 100vh;
|
||
font-size: 12px;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* HEADER */
|
||
.header {
|
||
padding: 0 16px;
|
||
border-bottom: 1px solid var(--border);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
height: 40px;
|
||
background: var(--surface);
|
||
}
|
||
.header-title {
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--text-dim);
|
||
white-space: nowrap;
|
||
}
|
||
.header-title span { color: var(--brand); }
|
||
.header-stats {
|
||
margin-left: auto;
|
||
display: flex;
|
||
gap: 20px;
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
align-items: center;
|
||
}
|
||
.header-stats strong {
|
||
color: var(--text);
|
||
font-size: 12px;
|
||
}
|
||
.live-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--status-healthy);
|
||
display: inline-block;
|
||
animation: pulse 2s ease-in-out infinite;
|
||
margin-right: 4px;
|
||
}
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.3; }
|
||
}
|
||
|
||
/* TAB BAR */
|
||
.tab-bar {
|
||
display: flex;
|
||
border-bottom: 1px solid var(--border);
|
||
background: var(--bg);
|
||
}
|
||
.tab {
|
||
padding: 6px 16px;
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: var(--text-muted);
|
||
cursor: pointer;
|
||
border: none;
|
||
border-bottom: 2px solid transparent;
|
||
background: none;
|
||
font-family: var(--font-mono);
|
||
transition: color 0.15s;
|
||
}
|
||
.tab:hover { color: var(--text-dim); }
|
||
.tab.active { color: var(--text); border-bottom-color: var(--brand); }
|
||
|
||
/* MAIN LAYOUT */
|
||
.main {
|
||
height: calc(100vh - 40px - 30px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* === DASHBOARD VIEW === */
|
||
.view { display: none; height: 100%; }
|
||
.view.active { display: flex; flex-direction: column; }
|
||
|
||
.dash-top {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
|
||
border-bottom: 1px solid var(--border);
|
||
flex-shrink: 0;
|
||
}
|
||
.vital {
|
||
padding: 4px 8px;
|
||
border-right: 1px solid var(--border);
|
||
}
|
||
.vital:last-child { border-right: none; }
|
||
.vital-value {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
line-height: 1.1;
|
||
display: inline;
|
||
}
|
||
.vital-label {
|
||
font-size: 9px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: var(--text-muted);
|
||
display: inline;
|
||
margin-left: 4px;
|
||
}
|
||
.vital-sub {
|
||
font-size: 9px;
|
||
color: var(--text-muted);
|
||
display: inline;
|
||
margin-left: 4px;
|
||
}
|
||
.vital-spark { display: block; margin-top: 2px; }
|
||
.v-healthy { color: var(--status-healthy); }
|
||
.v-warning { color: var(--status-warning); }
|
||
.v-critical { color: var(--status-critical); }
|
||
.v-brand { color: var(--brand); }
|
||
.v-text { color: var(--text); }
|
||
|
||
/* Middle row: 3 panels */
|
||
.dash-mid {
|
||
display: grid;
|
||
grid-template-columns: 38% 32% 30%;
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.panel {
|
||
border-right: 1px solid var(--border);
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
.panel:last-child { border-right: none; }
|
||
.panel-hdr {
|
||
padding: 4px 8px 3px;
|
||
font-size: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: var(--text-muted);
|
||
border-bottom: 1px solid var(--border);
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.panel-hdr .badge {
|
||
font-size: 10px;
|
||
padding: 1px 6px;
|
||
border-radius: 2px;
|
||
background: var(--brand-dim);
|
||
color: var(--brand);
|
||
font-weight: 600;
|
||
}
|
||
.panel-body {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* Timeline feed */
|
||
.tl-row {
|
||
display: grid;
|
||
grid-template-columns: 44px 52px 6px 1fr;
|
||
gap: 4px;
|
||
align-items: center;
|
||
padding: 2px 8px;
|
||
border-bottom: 1px solid rgba(33, 38, 45, 0.4);
|
||
font-size: 11px;
|
||
}
|
||
.tl-row:hover { background: rgba(255, 255, 255, 0.015); }
|
||
.tl-ts { color: var(--text-muted); font-size: 10px; white-space: nowrap; }
|
||
.tl-agent { font-size: 10px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.tl-dot { width: 6px; height: 6px; border-radius: 50%; }
|
||
.tl-desc { color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 11px; }
|
||
|
||
/* Domain bars */
|
||
.domain-row {
|
||
display: grid;
|
||
grid-template-columns: 100px 1fr 36px;
|
||
gap: 4px;
|
||
align-items: center;
|
||
padding: 2px 8px;
|
||
border-bottom: 1px solid rgba(33, 38, 45, 0.4);
|
||
}
|
||
.domain-name { font-size: 11px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.domain-bar-wrap {
|
||
height: 12px;
|
||
background: rgba(255, 255, 255, 0.03);
|
||
border-radius: 1px;
|
||
overflow: hidden;
|
||
}
|
||
.domain-bar {
|
||
height: 100%;
|
||
border-radius: 1px;
|
||
transition: width 0.5s ease;
|
||
}
|
||
.domain-count { font-size: 10px; color: var(--text-dim); text-align: right; }
|
||
|
||
/* Agent + Breaker panel */
|
||
.agent-row {
|
||
display: grid;
|
||
grid-template-columns: 56px 1fr 44px 40px;
|
||
gap: 4px;
|
||
align-items: center;
|
||
padding: 2px 8px;
|
||
border-bottom: 1px solid rgba(33, 38, 45, 0.4);
|
||
}
|
||
.agent-name { font-size: 11px; font-weight: 600; }
|
||
.spark-wrap {
|
||
height: 16px;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
gap: 1px;
|
||
}
|
||
.spark-bar {
|
||
flex: 1;
|
||
min-width: 2px;
|
||
border-radius: 1px 1px 0 0;
|
||
transition: height 0.3s;
|
||
}
|
||
.agent-count { font-size: 10px; color: var(--text-dim); text-align: right; }
|
||
|
||
.breaker-section {
|
||
border-top: 1px solid var(--border);
|
||
padding: 4px 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
.breaker-title {
|
||
font-size: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: var(--text-muted);
|
||
margin-bottom: 4px;
|
||
}
|
||
.breaker-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 4px;
|
||
}
|
||
.breaker {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 10px;
|
||
}
|
||
.breaker-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
}
|
||
.breaker-label { color: var(--text-dim); }
|
||
|
||
/* Bottom bar: funnel */
|
||
.dash-bottom {
|
||
border-top: 1px solid var(--border);
|
||
flex-shrink: 0;
|
||
padding: 3px 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 10px;
|
||
color: var(--text-muted);
|
||
background: var(--surface);
|
||
}
|
||
.funnel-step {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.funnel-val { color: var(--text); font-weight: 600; font-size: 11px; }
|
||
.funnel-arrow { color: var(--text-muted); margin: 0 4px; }
|
||
.funnel-label { font-size: 10px; }
|
||
.funnel-rate {
|
||
margin-left: auto;
|
||
color: var(--brand);
|
||
font-weight: 600;
|
||
font-size: 11px;
|
||
}
|
||
|
||
/* === CONTRIBUTORS VIEW === */
|
||
.lb-wrap { flex: 1; overflow-y: auto; }
|
||
.lb-row {
|
||
display: grid;
|
||
grid-template-columns: 36px 130px repeat(3, 60px) 70px 60px;
|
||
gap: 4px;
|
||
padding: 5px 14px;
|
||
font-size: 11px;
|
||
align-items: center;
|
||
border-bottom: 1px solid rgba(33, 38, 45, 0.4);
|
||
}
|
||
.lb-row.lb-hdr {
|
||
color: var(--text-muted);
|
||
font-size: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
position: sticky;
|
||
top: 0;
|
||
background: var(--bg);
|
||
z-index: 1;
|
||
padding: 8px 14px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.lb-rank { color: var(--text-muted); text-align: center; }
|
||
.lb-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.lb-cell { text-align: right; color: var(--text-dim); }
|
||
.lb-score { font-weight: 700; color: var(--brand); text-align: right; }
|
||
.lb-tier {
|
||
font-size: 9px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
padding: 1px 4px;
|
||
border-radius: 2px;
|
||
}
|
||
.tier-veteran { background: rgba(210, 153, 34, 0.15); color: #D29922; }
|
||
.tier-contributor { background: rgba(139, 148, 158, 0.15); color: #8B949E; }
|
||
|
||
/* === DOMAINS VIEW === */
|
||
.domains-view-inner {
|
||
display: flex;
|
||
gap: 0;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
}
|
||
.domains-left {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
border-right: 1px solid var(--border);
|
||
}
|
||
.domains-right {
|
||
width: 320px;
|
||
overflow-y: auto;
|
||
padding: 16px;
|
||
flex-shrink: 0;
|
||
}
|
||
.domain-row-big {
|
||
display: grid;
|
||
grid-template-columns: 180px 1fr 80px 80px 60px;
|
||
align-items: center;
|
||
padding: 10px 16px;
|
||
border-bottom: 1px solid var(--border);
|
||
cursor: default;
|
||
transition: background 0.15s;
|
||
}
|
||
.domain-row-big:hover {
|
||
background: rgba(255,255,255,0.02);
|
||
}
|
||
.domain-row-big.stagnant {
|
||
opacity: 0.45;
|
||
}
|
||
.domain-row-big .name {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
white-space: nowrap;
|
||
}
|
||
.domain-row-big .name .dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
}
|
||
.domain-row-big .bar-cell {
|
||
padding: 0 12px;
|
||
}
|
||
.domain-bar-outer {
|
||
height: 18px;
|
||
background: rgba(255,255,255,0.03);
|
||
border-radius: 2px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.domain-bar-inner {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
transition: width 0.4s ease;
|
||
}
|
||
.domain-bar-knowledge {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
opacity: 0.35;
|
||
}
|
||
.domain-row-big .num {
|
||
font-size: 12px;
|
||
text-align: right;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.domain-row-big .num strong {
|
||
color: var(--text-bright);
|
||
}
|
||
.domain-row-big .num small {
|
||
color: var(--text-muted);
|
||
font-size: 10px;
|
||
}
|
||
.domain-row-big .badge {
|
||
text-align: center;
|
||
}
|
||
.domain-row-big .badge span {
|
||
font-size: 9px;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
font-weight: 600;
|
||
}
|
||
.domains-summary-card {
|
||
background: rgba(255,255,255,0.02);
|
||
border: 1px solid var(--border);
|
||
border-radius: 4px;
|
||
padding: 14px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.domains-summary-card h4 {
|
||
font-size: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--text-muted);
|
||
margin: 0 0 10px 0;
|
||
}
|
||
.domains-summary-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 11px;
|
||
padding: 3px 0;
|
||
color: var(--text-dim);
|
||
}
|
||
.domains-summary-row strong {
|
||
color: var(--text-bright);
|
||
margin-top: 2px;
|
||
}
|
||
.domain-card-stat strong { color: var(--text); }
|
||
.domain-card.stagnant {
|
||
border-left: 3px solid var(--status-warning);
|
||
}
|
||
|
||
/* Rejection reasons mini-bar */
|
||
.reject-section {
|
||
border-top: 1px solid var(--border);
|
||
padding: 4px 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
.reject-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 10px;
|
||
padding: 1px 0;
|
||
}
|
||
.reject-tag { color: var(--text-dim); min-width: 140px; }
|
||
.reject-bar {
|
||
height: 6px;
|
||
background: var(--status-critical);
|
||
border-radius: 1px;
|
||
opacity: 0.6;
|
||
}
|
||
.reject-count { color: var(--text-muted); min-width: 20px; text-align: right; }
|
||
|
||
/* Skeleton */
|
||
.skeleton {
|
||
background: linear-gradient(90deg, var(--surface) 25%, rgba(255,255,255,0.03) 50%, var(--surface) 75%);
|
||
background-size: 200% 100%;
|
||
animation: shimmer 1.5s infinite;
|
||
height: 12px;
|
||
border-radius: 1px;
|
||
}
|
||
@keyframes shimmer {
|
||
0% { background-position: 200% 0; }
|
||
100% { background-position: -200% 0; }
|
||
}
|
||
|
||
/* Scrollbar */
|
||
::-webkit-scrollbar { width: 4px; }
|
||
::-webkit-scrollbar-track { background: transparent; }
|
||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
|
||
|
||
/* Responsive */
|
||
@media (max-width: 900px) {
|
||
.dash-mid { grid-template-columns: 1fr; }
|
||
.dash-top { grid-template-columns: 1fr 1fr 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- HEADER -->
|
||
<div class="header">
|
||
<div class="header-title">TELEO <span>CODEX</span></div>
|
||
<div class="header-stats">
|
||
<span><span class="live-dot" id="live-dot"></span> <span id="live-label">LIVE</span></span>
|
||
<span>MERGED <strong id="h-merged">--</strong></span>
|
||
<span>APPROVAL <strong id="h-approval">--</strong></span>
|
||
<span>TTM <strong id="h-ttm">--</strong></span>
|
||
<span id="h-updated" style="color:var(--text-muted)"></span>
|
||
<a href="/" style="color:var(--text-muted);font-size:10px;text-decoration:none;border:1px solid var(--border);padding:2px 8px;border-radius:3px;margin-left:8px" title="v1 — Pipeline Operations">← v1 Pipeline Ops</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TABS -->
|
||
<div class="tab-bar">
|
||
<button class="tab active" data-view="dash">DASHBOARD</button>
|
||
<button class="tab" data-view="contributors">CONTRIBUTORS</button>
|
||
<button class="tab" data-view="domains">DOMAINS</button>
|
||
</div>
|
||
|
||
<div class="main">
|
||
|
||
<!-- ======== DASHBOARD ======== -->
|
||
<div class="view active" id="v-dash">
|
||
<!-- Vital signs row -->
|
||
<div class="dash-top" id="vitals">
|
||
<div class="vital">
|
||
<div class="vital-value v-text" id="vt-claims">--</div>
|
||
<div class="vital-label">TOTAL CLAIMS</div>
|
||
<div class="vital-sub" id="vt-claims-sub"></div>
|
||
<canvas class="vital-spark" id="spark-claims" width="120" height="28"></canvas>
|
||
</div>
|
||
<div class="vital">
|
||
<div class="vital-value v-brand" id="vt-approval">--</div>
|
||
<div class="vital-label">APPROVAL RATE</div>
|
||
<div class="vital-sub" id="vt-approval-sub"></div>
|
||
<canvas class="vital-spark" id="spark-approval" width="120" height="28"></canvas>
|
||
</div>
|
||
<div class="vital">
|
||
<div class="vital-value" id="vt-orphan">--</div>
|
||
<div class="vital-label">ORPHAN RATIO</div>
|
||
<div class="vital-sub" id="vt-orphan-sub"></div>
|
||
<canvas class="vital-spark" id="spark-orphan" width="120" height="28"></canvas>
|
||
</div>
|
||
<div class="vital">
|
||
<div class="vital-value" id="vt-fresh">--</div>
|
||
<div class="vital-label">EVIDENCE AGE</div>
|
||
<div class="vital-sub" id="vt-fresh-sub"></div>
|
||
<canvas class="vital-spark" id="spark-fresh" width="120" height="28"></canvas>
|
||
</div>
|
||
<div class="vital">
|
||
<div class="vital-value" id="vt-linkage">--</div>
|
||
<div class="vital-label">CROSS-DOMAIN</div>
|
||
<div class="vital-sub" id="vt-linkage-sub"></div>
|
||
<canvas class="vital-spark" id="spark-linkage" width="120" height="28"></canvas>
|
||
</div>
|
||
<div class="vital">
|
||
<div class="vital-value" id="vt-backlog">--</div>
|
||
<div class="vital-label">REVIEW BACKLOG</div>
|
||
<div class="vital-sub" id="vt-backlog-sub"></div>
|
||
<canvas class="vital-spark" id="spark-backlog" width="120" height="28"></canvas>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Three panels -->
|
||
<div class="dash-mid">
|
||
<!-- LEFT: Pipeline timeline -->
|
||
<div class="panel">
|
||
<div class="panel-hdr">ACTIVITY FEED <span class="badge" id="feed-badge">--</span></div>
|
||
<div class="panel-body" id="timeline-feed"></div>
|
||
<div class="reject-section" id="reject-section" style="display:none">
|
||
<div style="font-size:10px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);margin-bottom:3px">TOP REJECTIONS</div>
|
||
<div id="reject-list"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CENTER: Domains -->
|
||
<div class="panel">
|
||
<div class="panel-hdr">DOMAIN ACTIVITY <span class="badge">7D</span></div>
|
||
<div class="panel-body" id="domain-bars"></div>
|
||
</div>
|
||
|
||
<!-- RIGHT: Agents + Breakers -->
|
||
<div class="panel">
|
||
<div class="panel-hdr">AGENTS</div>
|
||
<div class="panel-body" id="agent-list"></div>
|
||
<div class="breaker-section">
|
||
<div class="breaker-title">CIRCUIT BREAKERS</div>
|
||
<div class="breaker-grid" id="breakers"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Funnel bar -->
|
||
<div class="dash-bottom" id="funnel-bar">
|
||
<span style="color:var(--text-muted)">FUNNEL</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== CONTRIBUTORS ======== -->
|
||
<div class="view" id="v-contributors">
|
||
<div class="panel-hdr" style="display:flex;gap:12px">
|
||
CONTRIBUTORS
|
||
<button class="tab active" data-cview="principal" style="padding:2px 8px;font-size:10px">PRINCIPAL</button>
|
||
<button class="tab" data-cview="agent" style="padding:2px 8px;font-size:10px">AGENT</button>
|
||
</div>
|
||
<div class="lb-wrap" id="lb-wrap">
|
||
<div class="lb-row lb-hdr">
|
||
<div>#</div><div>HANDLE</div><div>MERGED</div><div>TIER</div><div>DOMAINS</div><div>CI SCORE</div><div>LAST</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== DOMAINS ======== -->
|
||
<div class="view" id="v-domains">
|
||
<div class="domains-view-inner">
|
||
<div class="domains-left">
|
||
<div class="domain-row-big" style="border-bottom:1px solid var(--border);opacity:0.6">
|
||
<div class="name" style="font-size:10px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);font-weight:400">DOMAIN</div>
|
||
<div class="bar-cell" style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.06em">VOLUME</div>
|
||
<div class="num" style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.06em">TOTAL</div>
|
||
<div class="num" style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.06em">7D</div>
|
||
<div class="badge" style="font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.06em">STATUS</div>
|
||
</div>
|
||
<div id="domains-rows"></div>
|
||
</div>
|
||
<div class="domains-right">
|
||
<div id="domains-summary"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /main -->
|
||
|
||
<script>
|
||
// ============================================
|
||
// CONFIG
|
||
// ============================================
|
||
const API = 'http://77.42.65.182:8081';
|
||
const REFRESH_MS = 60000;
|
||
|
||
const AGENT_COLORS = {
|
||
leo:'#D29922', rio:'#2DD4BF', vida:'#3FB950', theseus:'#F97316',
|
||
clay:'#58A6FF', astra:'#BC8CFF'
|
||
};
|
||
// Infra agents — tracked but excluded from knowledge contribution displays
|
||
const INFRA_AGENTS = {
|
||
epimetheus:'#8B949E', ganymede:'#E5534B', argus:'#F0883E', oberon:'#6E46E5', rhea:'#8B949E'
|
||
};
|
||
const ALL_AGENT_COLORS = {...AGENT_COLORS, ...INFRA_AGENTS, 'auto-extract':'#8B949E'};
|
||
|
||
const DOMAIN_COLORS = {
|
||
'entertainment':'#6E46E5', 'technology':'#58A6FF', 'finance':'#3FB950',
|
||
'internet-finance':'#39D353', 'collective-agents':'#BC8CFF',
|
||
'ai-alignment':'#58A6FF', 'health':'#F97583', 'space-development':'#FFA657',
|
||
'grand-strategy':'#6E46E5', 'general':'#8B949E', 'energy':'#3FB950',
|
||
'living-agents':'#79C0FF', 'collective-intelligence':'#D29922',
|
||
'critical-systems':'#F85149', 'unknown':'#484F58', 'null':'#484F58'
|
||
};
|
||
|
||
const OP_COLORS = {
|
||
extract:'#58A6FF', ingest:'#58A6FF', new:'#3FB950', enrich:'#D29922',
|
||
challenge:'#F85149', decision:'#BC8CFF', infra:'#8B949E',
|
||
validate:'#D29922', evaluate:'#BC8CFF', merge:'#3FB950',
|
||
fix:'#F0883E', snapshot:'#8B949E', watchdog:'#484F58'
|
||
};
|
||
|
||
// ============================================
|
||
// EMBEDDED FALLBACK DATA (snapshot 2026-03-28)
|
||
// Used when CORS blocks API calls (e.g. file:// protocol)
|
||
// ============================================
|
||
const FALLBACK = {
|
||
'/api/metrics': {"throughput_1h":0,"approval_rate":0.683,"evaluated_24h":60,"approved_24h":41,"status_map":{"closed":663,"merged":667,"open":1},"source_map":{"enrichment":70,"extracted":682,"extracting":4,"null_result":315,"unprocessed":250},"rejection_reasons":[{"tag":"near_duplicate","count":10,"unique_prs":10},{"tag":"frontmatter_schema","count":8,"unique_prs":8},{"tag":"factual_discrepancy","count":5,"unique_prs":5},{"tag":"confidence_miscalibration","count":1,"unique_prs":1},{"tag":"broken_wiki_links","count":1,"unique_prs":1}],"fix_rate":0.227,"fix_attempted":586,"fix_succeeded":133,"median_ttm_minutes":1.6,"domains":{"unknown":{"closed":68,"merged":5},"ai-alignment":{"closed":62,"merged":59},"collective-intelligence":{"closed":1,"merged":1},"critical-systems":{"closed":1},"energy":{"merged":2},"entertainment":{"closed":41,"merged":34},"general":{"closed":113,"merged":255},"grand-strategy":{"closed":2,"merged":17,"open":1},"health":{"closed":96,"merged":70},"internet-finance":{"closed":238,"merged":166},"living-agents":{"merged":1},"space-development":{"closed":41,"merged":57}},"breakers":{"ingest":{"state":"closed","failures":1,"age_s":16369},"validate":{"state":"closed","failures":0,"age_s":8424},"evaluate":{"state":"closed","failures":0,"age_s":8418},"merge":{"state":"closed","failures":0,"age_s":8412},"fix":{"state":"closed","failures":0,"age_s":16350},"snapshot":{"state":"closed","failures":0,"age_s":405},"watchdog":{"state":"closed","failures":0,"age_s":45}}},
|
||
'/api/vital-signs': {"claim_index_status":"live","review_throughput":{"backlog":2,"open_prs":2,"approved_waiting":0,"conflict_prs":0,"conflict_permanent_prs":0,"reviewing_prs":0,"oldest_open_hours":43.8,"status":"healthy"},"orphan_ratio":{"ratio":0.319,"count":212,"total":664,"status":"critical"},"linkage_density":{"avg_outgoing_links":7.31,"cross_domain_links":1517,"cross_domain_ratio":0.313},"confidence_distribution":{"likely":333,"proven":98,"experimental":196,"speculative":36,"plausible":1},"evidence_freshness":{"avg_age_days":39,"median_age_days":23,"fresh_30d_count":399,"fresh_30d_pct":60.1},"domain_activity":{"active":[{"domain":null,"prs_7d":10,"latest":"2026-03-26 18:06:37"},{"domain":"ai-alignment","prs_7d":47,"latest":"2026-03-28 00:36:47"},{"domain":"energy","prs_7d":1,"latest":"2026-03-28 06:20:58"},{"domain":"general","prs_7d":190,"latest":"2026-03-28 09:31:03"},{"domain":"grand-strategy","prs_7d":12,"latest":"2026-03-28 08:09:48"},{"domain":"health","prs_7d":28,"latest":"2026-03-28 04:13:49"},{"domain":"internet-finance","prs_7d":136,"latest":"2026-03-28 07:18:21"},{"domain":"space-development","prs_7d":44,"latest":"2026-03-28 08:44:08"}],"stagnant":["collective-intelligence","critical-systems","entertainment","living-agents"],"status":"warning"},"funnel":{"sources_total":1321,"sources_queued":250,"sources_extracted":682,"prs_total":1331,"prs_merged":667,"conversion_rate":0.501}},
|
||
'/api/activity?limit=100': {"events":[{"timestamp":"2026-03-28 09:31:05","agent":null,"operation":"new","target":"2026-03-24-x-research-vibhu-tweet","domain":"general","description":"Merged PR #2062","status":"merged","pr_number":2062},{"timestamp":"2026-03-28 08:44:09","agent":null,"operation":"new","target":"2026-03-28-keeptrack-starship-v3-april-2026","domain":"space-development","description":"Merged PR #2061","status":"merged","pr_number":2061},{"timestamp":"2026-03-28 08:10:18","agent":null,"operation":"new","target":"research-2026-03-28","domain":"grand-strategy","description":"Merged PR #2060","status":"merged","pr_number":2060},{"timestamp":"2026-03-28 07:18:24","agent":null,"operation":"new","target":"2026-03-27-tg-source-m3taversal-jussy-world","domain":"internet-finance","description":"Merged PR #2059","status":"merged","pr_number":2059},{"timestamp":"2026-03-28 07:17:49","agent":null,"operation":"new","target":"2026-03-27-tg-source-m3taversal-01resolved","domain":"general","description":"Merged PR #2058","status":"merged","pr_number":2058},{"timestamp":"2026-03-28 07:15:15","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu-tweet","domain":"general","description":"Closed PR #2057","status":"closed","pr_number":2057},{"timestamp":"2026-03-28 07:04:08","agent":null,"operation":"new","target":"2026-03-28-payloadspace-vast-haven1-delay-2027","domain":"general","description":"Merged PR #2056","status":"merged","pr_number":2056},{"timestamp":"2026-03-28 06:22:12","agent":null,"operation":"new","target":"2026-03-28-spglobal-hyperscaler-power","domain":"general","description":"Merged PR #2055","status":"merged","pr_number":2055},{"timestamp":"2026-03-28 06:21:36","agent":null,"operation":"new","target":"2026-03-28-nasaspaceflight-new-glenn","domain":"space-development","description":"Merged PR #2054","status":"merged","pr_number":2054},{"timestamp":"2026-03-28 06:21:01","agent":null,"operation":"new","target":"2026-03-28-mintz-nuclear-renaissance","domain":"energy","description":"Merged PR #2053","status":"merged","pr_number":2053},{"timestamp":"2026-03-28 06:18:55","agent":null,"operation":"new","target":"2026-03-28-introl-google-intersect-power","domain":"general","description":"Merged PR #2051","status":"merged","pr_number":2051},{"timestamp":"2026-03-28 06:18:23","agent":null,"operation":"infra","target":"2026-03-28-keeptrack-starship-v3","domain":"space-development","description":"Closed PR #2052","status":"closed","pr_number":2052},{"timestamp":"2026-03-28 06:10:50","agent":null,"operation":"new","target":"research-2026-03-28","domain":"space-development","description":"Merged PR #2050","status":"merged","pr_number":2050},{"timestamp":"2026-03-28 05:02:42","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2049","status":"closed","pr_number":2049},{"timestamp":"2026-03-28 05:00:40","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2048","status":"closed","pr_number":2048},{"timestamp":"2026-03-28 04:15:51","agent":null,"operation":"new","target":"research-2026-03-28","domain":"health","description":"Merged PR #2047","status":"merged","pr_number":2047},{"timestamp":"2026-03-28 03:02:10","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2046","status":"closed","pr_number":2046},{"timestamp":"2026-03-28 02:45:27","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2045","status":"closed","pr_number":2045},{"timestamp":"2026-03-28 01:22:47","agent":null,"operation":"new","target":"2026-03-06-oxford-pentagon","domain":"general","description":"Merged PR #2044","status":"merged","pr_number":2044},{"timestamp":"2026-03-28 01:00:20","agent":null,"operation":"new","target":"2026-03-08-intercept-openai","domain":"general","description":"Merged PR #2039","status":"merged","pr_number":2039},{"timestamp":"2026-03-28 00:58:13","agent":null,"operation":"new","target":"2026-02-27-cnn-openai-pentagon","domain":"general","description":"Merged PR #2035","status":"merged","pr_number":2035},{"timestamp":"2026-03-28 00:54:34","agent":null,"operation":"new","target":"2026-03-28-cnbc-anthropic-dod","domain":"general","description":"Merged PR #2043","status":"merged","pr_number":2043},{"timestamp":"2026-03-28 00:52:27","agent":null,"operation":"new","target":"2026-03-25-aljazeera-anthropic","domain":"general","description":"Merged PR #2041","status":"merged","pr_number":2041},{"timestamp":"2026-03-28 00:52:24","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2042","status":"closed","pr_number":2042},{"timestamp":"2026-03-28 00:51:21","agent":null,"operation":"new","target":"2026-03-17-slotkin-ai-guardrails","domain":"general","description":"Merged PR #2040","status":"merged","pr_number":2040},{"timestamp":"2026-03-28 00:50:47","agent":null,"operation":"new","target":"2026-03-06-oxford-pentagon","domain":"general","description":"Merged PR #2038","status":"merged","pr_number":2038},{"timestamp":"2026-03-28 00:48:41","agent":null,"operation":"new","target":"2026-03-02-axios-senate-dems","domain":"general","description":"Merged PR #2037","status":"merged","pr_number":2037},{"timestamp":"2026-03-28 00:48:06","agent":null,"operation":"new","target":"2026-02-28-govai-rsp-v3","domain":"general","description":"Merged PR #2036","status":"merged","pr_number":2036},{"timestamp":"2026-03-28 00:46:28","agent":null,"operation":"new","target":"2026-02-24-cnn-hegseth-anthropic","domain":"general","description":"Merged PR #2034","status":"merged","pr_number":2034},{"timestamp":"2026-03-28 00:36:49","agent":null,"operation":"new","target":"research-2026-03-28","domain":"ai-alignment","description":"Merged PR #2032","status":"merged","pr_number":2032},{"timestamp":"2026-03-28 00:30:41","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2033","status":"closed","pr_number":2033},{"timestamp":"2026-03-27 22:46:12","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2031","status":"closed","pr_number":2031},{"timestamp":"2026-03-27 22:15:27","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2030","status":"closed","pr_number":2030},{"timestamp":"2026-03-27 20:31:35","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2029","status":"closed","pr_number":2029},{"timestamp":"2026-03-27 20:00:15","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2028","status":"closed","pr_number":2028},{"timestamp":"2026-03-27 18:16:22","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2027","status":"closed","pr_number":2027},{"timestamp":"2026-03-27 18:00:14","agent":null,"operation":"new","target":"stigmergic-coordination-claims","domain":"grand-strategy","description":"Merged PR #2025","status":"merged","pr_number":2025},{"timestamp":"2026-03-27 17:45:31","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2026","status":"closed","pr_number":2026},{"timestamp":"2026-03-27 16:10:27","agent":null,"operation":"new","target":"archive-seed-sources","domain":"space-development","description":"Merged PR #2024","status":"merged","pr_number":2024},{"timestamp":"2026-03-27 16:05:46","agent":null,"operation":"infra","target":"energy-beyond-fusion","domain":"space-development","description":"Closed PR #2023","status":"closed","pr_number":2023},{"timestamp":"2026-03-27 16:03:13","agent":null,"operation":"new","target":"2026-03-27-tg-shared-01resolved","domain":"general","description":"Merged PR #2021","status":"merged","pr_number":2021},{"timestamp":"2026-03-27 16:02:37","agent":null,"operation":"new","target":"2026-03-27-tg-shared-01resolved","domain":"internet-finance","description":"Merged PR #2020","status":"merged","pr_number":2020},{"timestamp":"2026-03-27 16:02:03","agent":null,"operation":"infra","target":"2026-03-27-tg-source","domain":"general","description":"Closed PR #2022","status":"closed","pr_number":2022},{"timestamp":"2026-03-27 15:30:16","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2019","status":"closed","pr_number":2019},{"timestamp":"2026-03-27 13:28:36","agent":null,"operation":"new","target":"asteroid-isru-resubmit","domain":"space-development","description":"Merged PR #2018","status":"merged","pr_number":2018},{"timestamp":"2026-03-27 13:26:27","agent":null,"operation":"new","target":"batch9-governance-energy","domain":"space-development","description":"Merged PR #2016","status":"merged","pr_number":2016},{"timestamp":"2026-03-27 13:15:32","agent":null,"operation":"infra","target":"2026-03-24-x-research-vibhu","domain":"general","description":"Closed PR #2017","status":"closed","pr_number":2017},{"timestamp":"2026-03-27 13:14:01","agent":null,"operation":"new","target":"batch6-orbital-compute","domain":"space-development","description":"Merged PR #2013","status":"merged","pr_number":2013},{"timestamp":"2026-03-27 13:12:27","agent":null,"operation":"new","target":"batch8-settlement-power","domain":"space-development","description":"Merged PR #2015","status":"merged","pr_number":2015},{"timestamp":"2026-03-27 13:07:47","agent":null,"operation":"new","target":"batch7-space-manufacturing","domain":"space-development","description":"Merged PR #2014","status":"merged","pr_number":2014}],"limit":100,"cursor":"2026-03-27 13:07:47","has_more":true},
|
||
'/api/contributors?view=agent': {"contributors":[{"handle":"m3taversal","tier":"veteran","claims_merged":493,"ci":73.95,"domains":[],"last_contribution":"2026-03-12","principal":null},{"handle":"rio","tier":"contributor","claims_merged":17,"ci":7.4,"domains":[],"last_contribution":"2026-03-18","principal":"m3taversal"},{"handle":"leo","tier":"contributor","claims_merged":5,"ci":6.9,"domains":[],"last_contribution":"2026-03-19","principal":"m3taversal"},{"handle":"vida","tier":"contributor","claims_merged":9,"ci":3.35,"domains":[],"last_contribution":"2026-03-19","principal":"m3taversal"},{"handle":"theseus","tier":"contributor","claims_merged":13,"ci":2.75,"domains":[],"last_contribution":"2026-03-19","principal":"m3taversal"},{"handle":"astra","tier":"contributor","claims_merged":10,"ci":2.1,"domains":[],"last_contribution":"2026-03-19","principal":"m3taversal"},{"handle":"clay","tier":"contributor","claims_merged":2,"ci":1.7,"domains":[],"last_contribution":"2026-03-18","principal":"m3taversal"}],"view":"agent"},
|
||
'/api/contributors?view=principal': {"contributors":[{"handle":"m3taversal","tier":"veteran","claims_merged":549,"ci":98.15,"domains":[],"last_contribution":"2026-03-19","agents":["rio","theseus","astra","vida","leo","clay"]},{"handle":"heavey","tier":"veteran","claims_merged":10,"ci":1.5,"domains":[],"last_contribution":"2026-02-16","agents":[]},{"handle":"bostrom","tier":"contributor","claims_merged":9,"ci":1.35,"domains":[],"last_contribution":"2026-02-17","agents":[]},{"handle":"doug-shapiro","tier":"contributor","claims_merged":8,"ci":1.2,"domains":[],"last_contribution":"2026-03-01","agents":[]},{"handle":"conitzer","tier":"contributor","claims_merged":7,"ci":1.05,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"hanson","tier":"contributor","claims_merged":6,"ci":0.9,"domains":[],"last_contribution":"2026-02-16","agents":[]},{"handle":"noah-smith","tier":"contributor","claims_merged":5,"ci":0.75,"domains":[],"last_contribution":"2026-03-06","agents":[]},{"handle":"bak","tier":"contributor","claims_merged":5,"ci":0.75,"domains":[],"last_contribution":"2026-02-16","agents":[]},{"handle":"aquino-michaels","tier":"contributor","claims_merged":5,"ci":0.75,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"knuth","tier":"contributor","claims_merged":4,"ci":0.6,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"kaufmann","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"ward-whitt","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"karpathy","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"hidalgo","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-02-16","agents":[]},{"handle":"blackmore","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-02-16","agents":[]},{"handle":"areal","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"friston","tier":"contributor","claims_merged":3,"ci":0.45,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"ramstead","tier":"contributor","claims_merged":2,"ci":0.3,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"dario-amodei","tier":"contributor","claims_merged":2,"ci":0.3,"domains":[],"last_contribution":"2026-03-07","agents":[]},{"handle":"centola","tier":"contributor","claims_merged":2,"ci":0.3,"domains":[],"last_contribution":"2026-02-28","agents":[]},{"handle":"hayek","tier":"contributor","claims_merged":2,"ci":0.3,"domains":[],"last_contribution":"2026-03-08","agents":[]},{"handle":"dhrumil","tier":"contributor","claims_merged":2,"ci":0.3,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"larsson","tier":"contributor","claims_merged":1,"ci":0.15,"domains":[],"last_contribution":"2026-02-17","agents":[]},{"handle":"anthropic","tier":"contributor","claims_merged":1,"ci":0.15,"domains":[],"last_contribution":"2026-02-17","agents":[]},{"handle":"coase","tier":"contributor","claims_merged":1,"ci":0.15,"domains":[],"last_contribution":"2026-03-08","agents":[]},{"handle":"juarrero","tier":"contributor","claims_merged":1,"ci":0.15,"domains":[],"last_contribution":"2026-02-17","agents":[]},{"handle":"ostrom","tier":"contributor","claims_merged":1,"ci":0.15,"domains":[],"last_contribution":"2026-02-17","agents":[]},{"handle":"futard-io","tier":"contributor","claims_merged":1,"ci":0.15,"domains":[],"last_contribution":"2026-03-26","agents":[]},{"handle":"ganymede","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-16","agents":[]},{"handle":"metaproph3t","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-09","agents":[]},{"handle":"areal-dao","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"dan-slimmon","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"metanallok","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-09","agents":[]},{"handle":"mmdhrumil","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"theiaresearch","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-09","agents":[]},{"handle":"corless","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"oxranga","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-09","agents":[]},{"handle":"vlahakis","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"ceterispar1bus","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-05","agents":[]},{"handle":"futard.io","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2024-07-18","agents":[]},{"handle":"numerai","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-02-28","agents":[]},{"handle":"van-leeuwaarden","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"dagster","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"adams","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-07","agents":[]},{"handle":"tubefilter","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-11","agents":[]},{"handle":"swyx","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-09","agents":[]},{"handle":"spizzirri","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-02-17","agents":[]},{"handle":"simonw","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-03-09","agents":[]},{"handle":"tamim-ansary","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-02-21","agents":[]},{"handle":"clayton-christensen","tier":"contributor","claims_merged":0,"ci":0.0,"domains":[],"last_contribution":"2026-02-21","agents":[]}],"view":"principal"},
|
||
'/api/domains': {"domains":{"general":{"total_prs":255,"knowledge_prs":133,"contributors":[{"handle":"unknown","claims":133}]},"internet-finance":{"total_prs":166,"knowledge_prs":155,"contributors":[{"handle":"unknown","claims":155}]},"health":{"total_prs":70,"knowledge_prs":68,"contributors":[{"handle":"unknown","claims":68}]},"ai-alignment":{"total_prs":59,"knowledge_prs":55,"contributors":[{"handle":"unknown","claims":55}]},"space-development":{"total_prs":57,"knowledge_prs":54,"contributors":[{"handle":"unknown","claims":54}]},"entertainment":{"total_prs":34,"knowledge_prs":34,"contributors":[{"handle":"unknown","claims":34}]},"grand-strategy":{"total_prs":17,"knowledge_prs":7,"contributors":[{"handle":"unknown","claims":7}]},"energy":{"total_prs":2,"knowledge_prs":2,"contributors":[{"handle":"unknown","claims":2}]},"living-agents":{"total_prs":1,"knowledge_prs":1,"contributors":[{"handle":"unknown","claims":1}]},"collective-intelligence":{"total_prs":1,"knowledge_prs":1,"contributors":[{"handle":"unknown","claims":1}]}}}
|
||
};
|
||
|
||
// ============================================
|
||
// API CLIENT (with fallback)
|
||
// ============================================
|
||
let usingFallback = false;
|
||
|
||
async function apiGet(path) {
|
||
try {
|
||
const r = await fetch(`${API}${path}`);
|
||
if (!r.ok) throw new Error(r.status);
|
||
return await r.json();
|
||
} catch(e) {
|
||
console.warn(`API ${path}: falling back to snapshot`, e.message || e);
|
||
usingFallback = true;
|
||
// Exact match first, then fuzzy match (without query params) as last resort
|
||
if (FALLBACK[path]) return FALLBACK[path];
|
||
const key = Object.keys(FALLBACK).find(k => k.split('?')[0] === path.split('?')[0]);
|
||
if (key) return FALLBACK[key];
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// ============================================
|
||
// ESCAPE
|
||
// ============================================
|
||
function esc(s) {
|
||
if (!s) return '';
|
||
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: HEADER
|
||
// ============================================
|
||
function renderHeader(m) {
|
||
if (!m) return;
|
||
const merged = m.status_map?.merged || 0;
|
||
const rate = m.approval_rate;
|
||
const ttm = m.median_ttm_minutes;
|
||
|
||
document.getElementById('h-merged').textContent = merged;
|
||
document.getElementById('h-approval').textContent =
|
||
rate != null ? (rate < 1 ? (rate*100).toFixed(1)+'%' : rate.toFixed(1)+'%') : '--';
|
||
document.getElementById('h-ttm').textContent =
|
||
ttm != null ? ttm.toFixed(1)+'m' : '--';
|
||
const timeStr = new Date().toLocaleTimeString('en-US',{hour:'2-digit',minute:'2-digit',hour12:false});
|
||
document.getElementById('h-updated').textContent = usingFallback ? timeStr + ' (SNAPSHOT)' : timeStr;
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: VITALS
|
||
// ============================================
|
||
function renderVitals(m, vs) {
|
||
if (!vs) return;
|
||
|
||
// Claims
|
||
const merged = m?.status_map?.merged || 0;
|
||
const open = m?.status_map?.open || 0;
|
||
const el = (id) => document.getElementById(id);
|
||
|
||
el('vt-claims').textContent = merged;
|
||
el('vt-claims-sub').textContent = `${open} open`;
|
||
|
||
// Approval
|
||
const rate = m?.approval_rate;
|
||
if (rate != null) {
|
||
const pct = rate < 1 ? (rate*100).toFixed(1) : rate.toFixed(1);
|
||
el('vt-approval').textContent = pct + '%';
|
||
el('vt-approval-sub').textContent = `${m.approved_24h||0} approved 24h`;
|
||
}
|
||
|
||
// Orphan
|
||
const orph = vs.orphan_ratio;
|
||
if (orph) {
|
||
const pct = (orph.ratio * 100).toFixed(1);
|
||
el('vt-orphan').textContent = pct + '%';
|
||
el('vt-orphan').className = 'vital-value ' + statusCls(orph.status);
|
||
el('vt-orphan-sub').textContent = `${orph.count}/${orph.total}`;
|
||
}
|
||
|
||
// Evidence freshness
|
||
const fresh = vs.evidence_freshness;
|
||
if (fresh) {
|
||
el('vt-fresh').textContent = fresh.median_age_days + 'd';
|
||
el('vt-fresh').className = 'vital-value ' + (fresh.median_age_days <= 30 ? 'v-healthy' : 'v-warning');
|
||
el('vt-fresh-sub').textContent = `${fresh.fresh_30d_pct}% <30d`;
|
||
}
|
||
|
||
// Linkage
|
||
const link = vs.linkage_density;
|
||
if (link) {
|
||
el('vt-linkage').textContent = link.cross_domain_links;
|
||
el('vt-linkage').className = 'vital-value v-brand';
|
||
el('vt-linkage-sub').textContent = `${(link.cross_domain_ratio*100).toFixed(0)}% cross-domain`;
|
||
}
|
||
|
||
// Backlog
|
||
const rev = vs.review_throughput;
|
||
if (rev) {
|
||
el('vt-backlog').textContent = rev.backlog;
|
||
el('vt-backlog').className = 'vital-value ' + statusCls(rev.status);
|
||
el('vt-backlog-sub').textContent = `${rev.open_prs} open PRs`;
|
||
}
|
||
}
|
||
|
||
function statusCls(s) {
|
||
if (s === 'critical') return 'v-critical';
|
||
if (s === 'warning' || s === 'above_target') return 'v-warning';
|
||
return 'v-healthy';
|
||
}
|
||
|
||
// ============================================
|
||
// SPARKLINES from /api/snapshots
|
||
// ============================================
|
||
|
||
function drawSparkline(canvasId, data, color) {
|
||
const c = document.getElementById(canvasId);
|
||
if (!c || !data || data.length < 2) return;
|
||
const ctx = c.getContext('2d');
|
||
const w = c.width, h = c.height;
|
||
ctx.clearRect(0, 0, w, h);
|
||
|
||
const min = Math.min(...data);
|
||
const max = Math.max(...data);
|
||
const range = max - min || 1;
|
||
|
||
// Area fill
|
||
ctx.beginPath();
|
||
data.forEach((v, i) => {
|
||
const x = (i / (data.length - 1)) * w;
|
||
const y = h - ((v - min) / range) * (h - 4) - 2;
|
||
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
|
||
});
|
||
ctx.lineTo(w, h);
|
||
ctx.lineTo(0, h);
|
||
ctx.closePath();
|
||
ctx.fillStyle = (color || '#8B949E') + '15';
|
||
ctx.fill();
|
||
|
||
// Line
|
||
ctx.beginPath();
|
||
ctx.strokeStyle = color || '#8B949E';
|
||
ctx.lineWidth = 1.5;
|
||
data.forEach((v, i) => {
|
||
const x = (i / (data.length - 1)) * w;
|
||
const y = h - ((v - min) / range) * (h - 4) - 2;
|
||
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
|
||
});
|
||
ctx.stroke();
|
||
|
||
// Endpoint dot
|
||
const lastY = h - ((data[data.length-1] - min) / range) * (h - 4) - 2;
|
||
ctx.beginPath();
|
||
ctx.arc(w - 1, lastY, 2, 0, Math.PI * 2);
|
||
ctx.fillStyle = color || '#8B949E';
|
||
ctx.fill();
|
||
|
||
// Trend arrow (comparing first third avg to last third avg)
|
||
const third = Math.floor(data.length / 3);
|
||
const earlyAvg = data.slice(0, third).reduce((a,b)=>a+b,0) / third;
|
||
const lateAvg = data.slice(-third).reduce((a,b)=>a+b,0) / third;
|
||
const diff = lateAvg - earlyAvg;
|
||
const pctChange = earlyAvg !== 0 ? ((diff / Math.abs(earlyAvg)) * 100).toFixed(0) : '0';
|
||
// Store trend info on the canvas element for the vital sub to pick up
|
||
c._trend = { direction: diff > 0 ? 'up' : diff < 0 ? 'down' : 'flat', pct: pctChange };
|
||
}
|
||
|
||
async function renderSparklines() {
|
||
// 7-day window for meaningful trend visibility
|
||
const snaps = await apiGet('/api/snapshots?hours=168');
|
||
if (!snaps || !Array.isArray(snaps) || snaps.length < 2) return;
|
||
|
||
const extract = (field) => snaps.map(s => s[field]).filter(v => v != null);
|
||
|
||
drawSparkline('spark-claims', extract('total_prs'), '#8B949E');
|
||
drawSparkline('spark-approval', extract('approval_rate'), '#6E46E5');
|
||
drawSparkline('spark-orphan', extract('orphan_ratio'), '#D29922');
|
||
drawSparkline('spark-fresh', extract('evidence_age_median'), '#58A6FF');
|
||
drawSparkline('spark-linkage', extract('linkage_density'), '#BC8CFF');
|
||
drawSparkline('spark-backlog', extract('pending'), '#F85149');
|
||
|
||
// Update vital subs with 7d trend info
|
||
['claims','approval','orphan','fresh','linkage','backlog'].forEach(key => {
|
||
const canvas = document.getElementById('spark-' + key);
|
||
const sub = document.getElementById('vt-' + key + '-sub');
|
||
if (canvas?._trend && sub) {
|
||
const t = canvas._trend;
|
||
const arrow = t.direction === 'up' ? '↑' : t.direction === 'down' ? '↓' : '→';
|
||
const trendColor = t.direction === 'up' ? '#3FB950' : t.direction === 'down' ? '#F85149' : '#8B949E';
|
||
// For orphan ratio, down is good
|
||
const adjustedColor = key === 'orphan'
|
||
? (t.direction === 'down' ? '#3FB950' : t.direction === 'up' ? '#F85149' : '#8B949E')
|
||
: trendColor;
|
||
sub.innerHTML += ` <span style="color:${adjustedColor};font-size:9px" title="7d trend">${arrow}${Math.abs(t.pct)}%</span>`;
|
||
}
|
||
});
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: TIMELINE (real /api/activity data)
|
||
// ============================================
|
||
|
||
// Infer agent from domain when API returns null
|
||
const DOMAIN_AGENT = {
|
||
'internet-finance':'rio', 'entertainment':'clay', 'ai-alignment':'theseus',
|
||
'health':'vida', 'space-development':'astra', 'energy':'astra',
|
||
'manufacturing':'astra', 'robotics':'astra', 'grand-strategy':'leo',
|
||
'collective-intelligence':'theseus', 'critical-systems':'leo',
|
||
'living-agents':'theseus'
|
||
// 'general' intentionally omitted — unclassified domain, not attributable to any agent
|
||
};
|
||
|
||
const STATUS_COLORS = {
|
||
merged: '#3FB950',
|
||
closed: '#F85149',
|
||
open: '#D29922'
|
||
};
|
||
|
||
const DOMAIN_ABBREV = {
|
||
'entertainment':'ENT', 'technology':'TEC', 'finance':'FIN',
|
||
'internet-finance':'IFI', 'collective-agents':'COL', 'collective-intelligence':'COL',
|
||
'ai-alignment':'AIA', 'health':'HLT', 'space-development':'SPC',
|
||
'grand-strategy':'GRS', 'mechanisms':'MEC', 'living-capital':'LCP',
|
||
'teleohumanity':'TEL', 'critical-systems':'CRT', 'general':'GEN', 'unknown':'GEN'
|
||
};
|
||
|
||
async function renderTimeline() {
|
||
const feed = document.getElementById('timeline-feed');
|
||
const data = await apiGet('/api/activity?limit=100');
|
||
|
||
if (!data?.events?.length) {
|
||
feed.innerHTML = '<div style="padding:8px;color:var(--text-muted)">No activity data</div>';
|
||
return;
|
||
}
|
||
|
||
// Track most recent activity per agent for the agent panel
|
||
window._agentLastSeen = {};
|
||
data.events.forEach(e => {
|
||
const agent = e.agent || DOMAIN_AGENT[e.domain];
|
||
if (agent && AGENT_COLORS[agent] && e.timestamp) {
|
||
if (!window._agentLastSeen[agent] || e.timestamp > window._agentLastSeen[agent]) {
|
||
window._agentLastSeen[agent] = e.timestamp;
|
||
}
|
||
}
|
||
});
|
||
|
||
feed.innerHTML = data.events.map(e => {
|
||
const ts = e.timestamp || '';
|
||
const time = ts.slice(11, 16) || '--:--';
|
||
const agent = e.agent || DOMAIN_AGENT[e.domain] || 'auto-extract';
|
||
const agentColor = ALL_AGENT_COLORS[agent] || '#8B949E';
|
||
const dotColor = STATUS_COLORS[e.status] || '#8B949E';
|
||
// Compact format: DOMAIN_ABBREV · OP — description
|
||
const domAbbrev = DOMAIN_ABBREV[e.domain] || 'GEN';
|
||
const domColor = DOMAIN_COLORS[e.domain] || '#8B949E';
|
||
const op = (e.operation || '').toUpperCase();
|
||
const opColor = OP_COLORS[e.operation] || '#8B949E';
|
||
// Enrich bare "Merged PR #XXXX" / "Closed PR #XXXX" with domain + operation context
|
||
let desc = e.description || e.target || `PR #${e.pr_number || '?'}`;
|
||
const prMatch = desc.match(/^(Merged|Closed)\s+PR\s+#(\d+)$/);
|
||
if (prMatch) {
|
||
const action = prMatch[1];
|
||
const prNum = prMatch[2];
|
||
const domLabel = (!e.domain || e.domain === 'general') ? 'unclassified' : e.domain;
|
||
const opLabel = e.operation && e.operation !== 'new' ? e.operation : 'extraction';
|
||
desc = `${action} ${opLabel} in ${domLabel} (#${prNum})`;
|
||
}
|
||
const safe = esc(desc);
|
||
|
||
return `<div class="tl-row">
|
||
<span class="tl-ts">${time}</span>
|
||
<span class="tl-agent" style="color:${agentColor}">${agent}</span>
|
||
<span class="tl-dot" style="background:${dotColor}"></span>
|
||
<span class="tl-desc" title="${safe}"><span style="color:${domColor}">${domAbbrev}</span> · <span style="color:${opColor}">${op||'—'}</span> — ${safe}</span>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
document.getElementById('feed-badge').textContent = data.events.length;
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: REJECTIONS
|
||
// ============================================
|
||
function renderRejections(m) {
|
||
if (!m?.rejection_reasons?.length) return;
|
||
const sec = document.getElementById('reject-section');
|
||
const list = document.getElementById('reject-list');
|
||
sec.style.display = 'block';
|
||
const maxCount = Math.max(...m.rejection_reasons.map(r=>r.count));
|
||
|
||
list.innerHTML = m.rejection_reasons.slice(0, 5).map(r => `
|
||
<div class="reject-row">
|
||
<span class="reject-tag">${esc(r.tag.replace(/_/g,' '))}</span>
|
||
<div class="reject-bar" style="width:${(r.count/maxCount)*80}px"></div>
|
||
<span class="reject-count">${r.count}</span>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: DOMAIN BARS
|
||
// ============================================
|
||
function renderDomainBars(vs) {
|
||
const wrap = document.getElementById('domain-bars');
|
||
if (!vs?.domain_activity) { wrap.innerHTML = ''; return; }
|
||
|
||
const active = vs.domain_activity.active || [];
|
||
const stagnant = vs.domain_activity.stagnant || [];
|
||
|
||
// Sort by prs_7d descending
|
||
const sorted = [...active].sort((a,b) => (b.prs_7d||0) - (a.prs_7d||0));
|
||
const maxPrs = Math.max(...sorted.map(d => d.prs_7d||0), 1);
|
||
|
||
// Known canonical domains — filter out ghosts
|
||
const KNOWN_DOMAINS = new Set(['internet-finance','ai-alignment','health','entertainment',
|
||
'space-development','grand-strategy','energy','living-agents','collective-intelligence',
|
||
'critical-systems','general']);
|
||
|
||
let html = sorted.map(d => {
|
||
const name = d.domain || 'general';
|
||
const isUnclassified = !d.domain || name === 'general';
|
||
const color = DOMAIN_COLORS[name] || '#8B949E';
|
||
const pct = ((d.prs_7d||0)/maxPrs*100).toFixed(0);
|
||
const abbrev = DOMAIN_ABBREV[name] || 'GEN';
|
||
const rowStyle = isUnclassified ? 'opacity:0.5;font-style:italic' : '';
|
||
const barOpacity = isUnclassified ? '0.3' : '0.7';
|
||
return `<div class="domain-row" style="${rowStyle}">
|
||
<span class="domain-name" style="color:${color}">${abbrev} <span style="color:var(--text-muted);font-size:9px">${esc(isUnclassified ? 'unclassified' : name)}</span></span>
|
||
<div class="domain-bar-wrap"><div class="domain-bar" style="width:${pct}%;background:${color};opacity:${barOpacity}"></div></div>
|
||
<span class="domain-count">${d.prs_7d||0}</span>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
// Show stagnant domains — only known canonical ones
|
||
// Distinguish intentionally paused (low-priority / pending agent) from unexpected inactivity
|
||
const PAUSED_DOMAINS = new Set(['collective-intelligence', 'critical-systems', 'living-agents']);
|
||
const validStagnant = stagnant.filter(n => KNOWN_DOMAINS.has(n));
|
||
if (validStagnant.length) {
|
||
html += `<div style="padding:4px 8px 2px;font-size:9px;text-transform:uppercase;color:var(--text-muted);letter-spacing:0.06em;border-top:1px solid var(--border);margin-top:2px">INACTIVE</div>`;
|
||
html += validStagnant.map(name => {
|
||
const abbrev = DOMAIN_ABBREV[name] || name.slice(0,3).toUpperCase();
|
||
const isPaused = PAUSED_DOMAINS.has(name);
|
||
const label = isPaused ? 'paused' : 'stagnant';
|
||
const labelColor = isPaused ? 'var(--text-muted)' : 'var(--status-warning)';
|
||
return `<div class="domain-row" style="opacity:0.4">
|
||
<span class="domain-name" style="color:${DOMAIN_COLORS[name]||'#484F58'}">${abbrev}</span>
|
||
<div class="domain-bar-wrap"><div class="domain-bar" style="width:0%"></div></div>
|
||
<span class="domain-count" style="color:${labelColor};font-size:9px;min-width:50px">${label}</span>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
wrap.innerHTML = html;
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: AGENTS (real data from contributors API)
|
||
// ============================================
|
||
async function renderAgents() {
|
||
const list = document.getElementById('agent-list');
|
||
const data = await apiGet('/api/contributors?view=agent');
|
||
|
||
// Only show knowledge agents, not infra (epimetheus, ganymede, argus, oberon, rhea)
|
||
const agents = Object.entries(AGENT_COLORS);
|
||
|
||
// Build lookup from contributor data
|
||
const agentStats = {};
|
||
if (data?.contributors) {
|
||
data.contributors.forEach(c => {
|
||
const name = (c.handle || '').toLowerCase();
|
||
if (AGENT_COLORS[name]) agentStats[name] = c;
|
||
});
|
||
}
|
||
|
||
// Compute team CI average for context
|
||
const ciValues = agents.map(([name]) => agentStats[name]?.ci || 0);
|
||
const teamAvgCI = ciValues.length ? ciValues.reduce((a,b) => a+b, 0) / ciValues.length : 0;
|
||
const maxCI = Math.max(...ciValues, 1);
|
||
|
||
// CI formula tooltip
|
||
const ciTooltip = 'CI = sourcer×0.15 + extractor×0.05 + challenger×0.35 + synthesizer×0.25 + reviewer×0.20';
|
||
|
||
// Header row with CI context
|
||
let html = `<div class="agent-row" style="border-bottom:1px solid var(--border);padding-bottom:3px;margin-bottom:2px">
|
||
<span style="font-size:9px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted)">AGENT</span>
|
||
<span style="font-size:9px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);text-align:right">MERGED</span>
|
||
<span style="font-size:9px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);text-align:right;cursor:help" title="${ciTooltip}">CI ⓘ</span>
|
||
<span style="font-size:9px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);text-align:right">LAST</span>
|
||
</div>`;
|
||
|
||
html += agents.map(([name, color]) => {
|
||
const stat = agentStats[name];
|
||
const total = stat?.claims_merged || 0;
|
||
const ci = stat?.ci || 0;
|
||
// Prefer activity-derived last-seen over stale contributor API date
|
||
const activityTs = window._agentLastSeen?.[name];
|
||
let lastDate = '--';
|
||
if (activityTs) {
|
||
lastDate = activityTs.slice(5, 10); // MM-DD from activity feed
|
||
} else if (stat?.last_contribution) {
|
||
lastDate = stat.last_contribution.slice(5);
|
||
}
|
||
const isRecent = activityTs && activityTs.slice(0, 10) >= new Date().toISOString().slice(0, 10);
|
||
const dateColor = isRecent ? 'var(--status-healthy)' : 'var(--text-muted)';
|
||
const ciColor = ci > teamAvgCI ? 'var(--status-healthy)' : ci > 0 ? 'var(--text-dim)' : 'var(--text-muted)';
|
||
|
||
return `<div class="agent-row">
|
||
<span class="agent-name" style="color:${color}">${name}</span>
|
||
<span style="color:var(--text-muted);font-size:10px;min-width:50px;text-align:right">${total}</span>
|
||
<span style="color:${ciColor};font-size:10px;min-width:40px;text-align:right" title="${ciTooltip}">${ci.toFixed(1)}</span>
|
||
<span style="color:${dateColor};font-size:10px;min-width:40px;text-align:right">${lastDate}</span>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
// Team average footer
|
||
html += `<div style="padding:3px 8px;font-size:9px;color:var(--text-muted);border-top:1px solid var(--border);display:flex;justify-content:space-between">
|
||
<span>TEAM AVG</span>
|
||
<span style="color:var(--text-dim)">CI ${teamAvgCI.toFixed(1)}</span>
|
||
</div>`;
|
||
|
||
list.innerHTML = html;
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: CIRCUIT BREAKERS
|
||
// ============================================
|
||
function renderBreakers(m) {
|
||
const el = document.getElementById('breakers');
|
||
if (!m?.breakers) return;
|
||
|
||
const legend = `<div style="grid-column:1/-1;font-size:9px;color:var(--text-muted);margin-bottom:2px">
|
||
<span style="color:var(--status-healthy)">●</span> closed/0 fail
|
||
<span style="margin-left:6px;color:var(--status-warning)">●</span> open/tripped
|
||
<span style="margin-left:6px;color:var(--status-critical)">●</span> failures >0
|
||
</div>`;
|
||
|
||
el.innerHTML = legend + Object.entries(m.breakers).map(([name, b]) => {
|
||
const color = b.state === 'closed' && b.failures === 0
|
||
? 'var(--status-healthy)'
|
||
: b.failures > 0
|
||
? 'var(--status-critical)'
|
||
: 'var(--status-warning)';
|
||
return `<div class="breaker">
|
||
<span class="breaker-dot" style="background:${color}"></span>
|
||
<span class="breaker-label">${name}</span>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: FUNNEL
|
||
// ============================================
|
||
function renderFunnel(vs) {
|
||
const bar = document.getElementById('funnel-bar');
|
||
if (!vs?.funnel) return;
|
||
const f = vs.funnel;
|
||
|
||
bar.innerHTML = `
|
||
<span style="color:var(--text-muted)">FUNNEL</span>
|
||
<span class="funnel-val">${f.sources_total}</span><span class="funnel-label">sources</span>
|
||
<span class="funnel-arrow">→</span>
|
||
<span class="funnel-val">${f.sources_extracted}</span><span class="funnel-label">extracted</span>
|
||
<span class="funnel-arrow">→</span>
|
||
<span class="funnel-val">${f.prs_total}</span><span class="funnel-label">PRs</span>
|
||
<span class="funnel-arrow">→</span>
|
||
<span class="funnel-val">${f.prs_merged}</span><span class="funnel-label">merged</span>
|
||
<span class="funnel-rate">${(f.conversion_rate*100).toFixed(1)}% conversion</span>
|
||
`;
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: CONTRIBUTORS (lazy)
|
||
// ============================================
|
||
let contribLoaded = false;
|
||
async function loadContributors(view) {
|
||
const data = await apiGet(`/api/contributors?view=${view||'principal'}`);
|
||
if (!data?.contributors) return;
|
||
contribLoaded = true;
|
||
|
||
const wrap = document.getElementById('lb-wrap');
|
||
const sorted = [...data.contributors].sort((a,b) => (b.ci||0) - (a.ci||0));
|
||
|
||
const hdr = `<div class="lb-row lb-hdr">
|
||
<div>#</div><div>HANDLE</div><div>MERGED</div><div>TIER</div><div>DOMAINS</div><div>CI</div><div>LAST</div>
|
||
</div>`;
|
||
|
||
const rows = sorted.map((c, i) => {
|
||
const tierCls = c.tier === 'veteran' ? 'tier-veteran' : 'tier-contributor';
|
||
const last = c.last_contribution ? c.last_contribution.slice(5) : '--';
|
||
const domCount = c.domains?.length || 0;
|
||
return `<div class="lb-row">
|
||
<div class="lb-rank">${i+1}</div>
|
||
<div class="lb-name">${esc(c.handle)}</div>
|
||
<div class="lb-cell">${c.claims_merged||0}</div>
|
||
<div><span class="lb-tier ${tierCls}">${c.tier}</span></div>
|
||
<div class="lb-cell">${domCount}</div>
|
||
<div class="lb-score">${(c.ci||0).toFixed(2)}</div>
|
||
<div class="lb-cell">${last}</div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
wrap.innerHTML = hdr + rows;
|
||
}
|
||
|
||
// ============================================
|
||
// RENDER: DOMAINS (lazy)
|
||
// ============================================
|
||
let domainsLoaded = false;
|
||
async function loadDomains() {
|
||
const [domData, vsData] = await Promise.all([
|
||
apiGet('/api/domains'),
|
||
apiGet('/api/vital-signs')
|
||
]);
|
||
if (!domData?.domains) return;
|
||
domainsLoaded = true;
|
||
|
||
const stagnant = vsData?.domain_activity?.stagnant || [];
|
||
const activeMap = {};
|
||
(vsData?.domain_activity?.active || []).forEach(d => {
|
||
if (d.domain) activeMap[d.domain] = d;
|
||
});
|
||
|
||
// Sort domains by total_prs descending
|
||
const sorted = Object.entries(domData.domains).sort((a,b) => b[1].total_prs - a[1].total_prs);
|
||
const maxTotal = Math.max(...sorted.map(([,d]) => d.total_prs), 1);
|
||
|
||
// Compute summary stats
|
||
let totalPRs = 0, totalKnowledge = 0, total7d = 0;
|
||
let activeDomains = 0, stagnantDomains = stagnant.length;
|
||
|
||
const rows = document.getElementById('domains-rows');
|
||
rows.innerHTML = sorted.map(([name, d]) => {
|
||
const color = DOMAIN_COLORS[name] || '#8B949E';
|
||
const isStagnant = stagnant.includes(name);
|
||
const activity = activeMap[name];
|
||
const prs7d = activity?.prs_7d || 0;
|
||
const isUnclassified = !name || name === 'general';
|
||
const displayName = isUnclassified ? 'unclassified' : name;
|
||
const pctTotal = (d.total_prs / maxTotal * 100).toFixed(0);
|
||
const pctKnowledge = (d.knowledge_prs / maxTotal * 100).toFixed(0);
|
||
|
||
totalPRs += d.total_prs;
|
||
totalKnowledge += d.knowledge_prs;
|
||
total7d += prs7d;
|
||
if (!isStagnant && prs7d > 0) activeDomains++;
|
||
|
||
// Status badge
|
||
const PAUSED_SET = new Set(['collective-intelligence', 'critical-systems', 'living-agents']);
|
||
let badge = '';
|
||
if (isStagnant && PAUSED_SET.has(name)) {
|
||
badge = `<span style="background:rgba(139,148,158,0.15);color:#8B949E">PAUSED</span>`;
|
||
} else if (isStagnant) {
|
||
badge = `<span style="background:rgba(245,158,11,0.15);color:#F59E0B">STALE</span>`;
|
||
} else if (prs7d >= 20) {
|
||
badge = `<span style="background:rgba(63,185,80,0.15);color:#3FB950">HOT</span>`;
|
||
} else if (prs7d > 0) {
|
||
badge = `<span style="background:rgba(255,255,255,0.05);color:var(--text-dim)">ACTIVE</span>`;
|
||
} else {
|
||
badge = `<span style="background:rgba(255,255,255,0.03);color:var(--text-muted)">QUIET</span>`;
|
||
}
|
||
|
||
const rowClass = isStagnant ? ' stagnant' : '';
|
||
const nameStyle = name === 'general' ? 'font-style:italic;opacity:0.7' : '';
|
||
|
||
return `<div class="domain-row-big${rowClass}">
|
||
<div class="name" style="${nameStyle}">
|
||
<span class="dot" style="background:${color}"></span>
|
||
${esc(displayName)}
|
||
</div>
|
||
<div class="bar-cell">
|
||
<div class="domain-bar-outer">
|
||
<div class="domain-bar-knowledge" style="width:${pctTotal}%;background:${color}"></div>
|
||
<div class="domain-bar-inner" style="width:${pctKnowledge}%;background:${color}"></div>
|
||
</div>
|
||
</div>
|
||
<div class="num"><strong>${d.total_prs}</strong> <small>/ ${d.knowledge_prs}k</small></div>
|
||
<div class="num" style="color:${prs7d > 0 ? color : 'var(--text-muted)'}"><strong>${prs7d}</strong></div>
|
||
<div class="badge">${badge}</div>
|
||
</div>`;
|
||
}).join('');
|
||
|
||
// Render summary panel
|
||
const knowledgeRatio = totalPRs > 0 ? (totalKnowledge / totalPRs * 100).toFixed(1) : 0;
|
||
const summary = document.getElementById('domains-summary');
|
||
summary.innerHTML = `
|
||
<div class="domains-summary-card">
|
||
<h4>Overview</h4>
|
||
<div class="domains-summary-row"><span>Total domains</span><strong>${sorted.length}</strong></div>
|
||
<div class="domains-summary-row"><span>Active (7d)</span><strong style="color:var(--status-healthy)">${activeDomains}</strong></div>
|
||
<div class="domains-summary-row"><span>Stagnant</span><strong style="color:${stagnantDomains > 0 ? 'var(--status-warning)' : 'var(--text-dim)'}">${stagnantDomains}</strong></div>
|
||
<div class="domains-summary-row"><span>Total PRs</span><strong>${totalPRs.toLocaleString()}</strong></div>
|
||
<div class="domains-summary-row"><span>Knowledge PRs</span><strong>${totalKnowledge.toLocaleString()}</strong></div>
|
||
<div class="domains-summary-row"><span>Knowledge ratio</span><strong>${knowledgeRatio}%</strong></div>
|
||
<div class="domains-summary-row"><span>7d volume</span><strong>${total7d}</strong></div>
|
||
</div>
|
||
<div class="domains-summary-card">
|
||
<h4>Bar Legend</h4>
|
||
<div class="domains-summary-row" style="gap:8px">
|
||
<span style="display:flex;align-items:center;gap:4px">
|
||
<span style="display:inline-block;width:12px;height:8px;background:var(--brand);border-radius:1px"></span> Knowledge PRs
|
||
</span>
|
||
</div>
|
||
<div class="domains-summary-row" style="gap:8px">
|
||
<span style="display:flex;align-items:center;gap:4px">
|
||
<span style="display:inline-block;width:12px;height:8px;background:var(--brand);opacity:0.35;border-radius:1px"></span> Total PRs (incl. infra)
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="domains-summary-card">
|
||
<h4>Status Key</h4>
|
||
<div class="domains-summary-row"><span style="color:#3FB950">HOT</span><span>20+ PRs / 7d</span></div>
|
||
<div class="domains-summary-row"><span style="color:var(--text-dim)">ACTIVE</span><span>1-19 PRs / 7d</span></div>
|
||
<div class="domains-summary-row"><span style="color:var(--text-muted)">QUIET</span><span>0 PRs / 7d</span></div>
|
||
<div class="domains-summary-row"><span style="color:#F59E0B">STALE</span><span>No activity >7d (unexpected)</span></div>
|
||
<div class="domains-summary-row"><span style="color:#8B949E">PAUSED</span><span>Intentionally inactive</span></div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// ============================================
|
||
// TABS
|
||
// ============================================
|
||
document.querySelectorAll('.tab-bar .tab').forEach(t => {
|
||
t.addEventListener('click', () => {
|
||
document.querySelectorAll('.tab-bar .tab').forEach(x => x.classList.remove('active'));
|
||
t.classList.add('active');
|
||
const v = t.dataset.view;
|
||
document.querySelectorAll('.view').forEach(x => x.classList.remove('active'));
|
||
document.getElementById('v-' + v).classList.add('active');
|
||
|
||
if (v === 'contributors' && !contribLoaded) loadContributors('principal');
|
||
if (v === 'domains' && !domainsLoaded) loadDomains();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('[data-cview]').forEach(b => {
|
||
b.addEventListener('click', () => {
|
||
document.querySelectorAll('[data-cview]').forEach(x => x.classList.remove('active'));
|
||
b.classList.add('active');
|
||
loadContributors(b.dataset.cview);
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// INIT
|
||
// ============================================
|
||
async function init() {
|
||
const [metrics, vitals] = await Promise.all([
|
||
apiGet('/api/metrics'),
|
||
apiGet('/api/vital-signs')
|
||
]);
|
||
|
||
renderHeader(metrics);
|
||
renderVitals(metrics, vitals);
|
||
renderSparklines();
|
||
await renderTimeline();
|
||
renderRejections(metrics);
|
||
renderDomainBars(vitals);
|
||
await renderAgents();
|
||
renderBreakers(metrics);
|
||
renderFunnel(vitals);
|
||
|
||
// Update live indicator if using fallback
|
||
if (usingFallback) {
|
||
const dot = document.getElementById('live-dot');
|
||
const label = document.getElementById('live-label');
|
||
if (dot) { dot.style.background = 'var(--status-warning)'; dot.style.animation = 'none'; }
|
||
if (label) label.textContent = 'SNAPSHOT';
|
||
}
|
||
|
||
// Auto-refresh
|
||
setInterval(async () => {
|
||
const [m, v] = await Promise.all([
|
||
apiGet('/api/metrics'),
|
||
apiGet('/api/vital-signs')
|
||
]);
|
||
renderHeader(m);
|
||
renderVitals(m, v);
|
||
renderSparklines();
|
||
renderBreakers(m);
|
||
renderFunnel(v);
|
||
renderDomainBars(v);
|
||
renderTimeline();
|
||
}, REFRESH_MS);
|
||
}
|
||
|
||
init();
|
||
</script>
|
||
</body>
|
||
</html>
|