teleo-codex/ops/diagnostics/dashboard_epistemic.py
m3taversal 05d74d5e32 sync: import all VPS pipeline + diagnostics code as baseline
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>
2026-04-07 00:00:00 +01:00

239 lines
9.2 KiB
Python

"""Page 4: Epistemic Integrity — "Can we trust what we know?"
Live sections:
- Confidence calibration (from claim-index via vital signs)
- Cascade coverage (from audit_log stage='cascade')
- Review quality (from review_records table)
Placeholder sections:
- Multi-model agreement (needs model_evals table)
- Belief staleness (needs cascade tracking to give it meaning)
- Divergence tracking (needs divergence events)
"""
import json
from datetime import datetime
from shared_ui import render_page
def render_epistemic_page(vital_signs: dict, now: datetime) -> str:
"""Render the Epistemic Integrity page."""
vs_conf = vital_signs.get("confidence_distribution", {})
total_claims = sum(vs_conf.values()) if vs_conf else 0
# Confidence calibration table
conf_rows = ""
for level in ["proven", "likely", "experimental", "speculative"]:
count = vs_conf.get(level, 0)
pct = round(count / total_claims * 100, 1) if total_claims else 0
conf_rows += f'<tr><td>{level}</td><td>{count}</td><td>{pct}%</td></tr>'
body = f"""
<!-- Confidence Calibration (LIVE) -->
<div class="section">
<div class="section-title">Confidence Calibration</div>
<div class="row">
<div class="card">
<table>
<tr><th>Level</th><th>Claims</th><th>Share</th></tr>
{conf_rows}
</table>
<div style="margin-top:12px;font-size:12px;color:#8b949e">
Total claims: {total_claims}
</div>
</div>
<div class="chart-container">
<h2>Confidence Distribution</h2>
<canvas id="confPieChart"></canvas>
</div>
</div>
</div>
<!-- Cascade Coverage (LIVE — from audit_log) -->
<div class="section">
<div class="section-title">Cascade Coverage</div>
<div id="cascade-container">
<div class="card" style="text-align:center;color:#8b949e">Loading cascade data...</div>
</div>
</div>
<!-- Review Quality (LIVE — from review_records table) -->
<div class="section">
<div class="section-title">Review Quality</div>
<div id="review-container">
<div class="card" style="text-align:center;color:#8b949e">Loading review data...</div>
</div>
</div>
<!-- Multi-Model Agreement — Placeholder -->
<div class="section">
<div class="section-title">Multi-Model Agreement</div>
<div class="card" style="text-align:center;padding:40px">
<div style="font-size:40px;margin-bottom:12px;opacity:0.3">&#9881;</div>
<div style="color:#8b949e">
Multi-model agreement rate requires the <code>model_evals</code> table.<br>
<span style="font-size:12px">Blocked on: model_evals table creation (Theseus 2 Phase 3)</span>
</div>
<div style="margin-top:16px;font-size:12px;color:#8b949e">
Current eval models: Haiku (triage), GPT-4o (domain), Sonnet/Opus (Leo).<br>
Agreement tracking needs per-model verdicts stored separately.
</div>
</div>
</div>
<!-- Belief Staleness — Placeholder -->
<div class="section">
<div class="section-title">Belief Staleness</div>
<div class="card" style="text-align:center;padding:40px">
<div style="font-size:40px;margin-bottom:12px;opacity:0.3">&#9202;</div>
<div style="color:#8b949e">
Belief staleness scan will compare belief file <code>depends_on</code> frontmatter<br>
against claim <code>merged_at</code> timestamps.<br>
<span style="font-size:12px">Ready to implement once cascade tracking accumulates data</span>
</div>
</div>
</div>
"""
scripts = f"""<script>
// Confidence pie chart
const confData = {json.dumps(vs_conf)};
const confLabels = Object.keys(confData);
const confValues = Object.values(confData);
if (confLabels.length > 0) {{
const confColors = {{ 'proven': '#3fb950', 'likely': '#58a6ff', 'experimental': '#d29922', 'speculative': '#f85149', 'unknown': '#8b949e' }};
new Chart(document.getElementById('confPieChart'), {{
type: 'doughnut',
data: {{
labels: confLabels,
datasets: [{{
data: confValues,
backgroundColor: confLabels.map(l => confColors[l] || '#8b949e'),
borderColor: '#161b22',
borderWidth: 2,
}}],
}},
options: {{
responsive: true,
plugins: {{
legend: {{ position: 'right', labels: {{ boxWidth: 12 }} }},
}},
}},
}});
}}
// --- Cascade Coverage (live) ---
fetch('/api/cascade-coverage?days=30')
.then(r => r.json())
.then(data => {{
const el = document.getElementById('cascade-container');
if (data.total_triggered === 0) {{
el.innerHTML = `
<div class="card" style="text-align:center;padding:30px">
<div style="font-size:14px;color:#d29922">No cascade events recorded yet</div>
<div style="font-size:12px;color:#8b949e;margin-top:8px">
Cascade instrumentation is deployed. Events will appear as new PRs flow through eval and trigger belief/position reviews.
</div>
</div>`;
return;
}}
const compRate = data.completion_rate != null ? (data.completion_rate * 100).toFixed(1) + '%' : '--';
const compColor = data.completion_rate >= 0.7 ? '#3fb950' : data.completion_rate >= 0.4 ? '#d29922' : '#f85149';
let agentRows = '';
for (const a of (data.by_agent || [])) {{
agentRows += '<tr><td>' + esc(a.agent) + '</td><td>' + a.triggered + '</td><td>' + a.claims_affected + '</td></tr>';
}}
el.innerHTML = `
<div class="grid">
<div class="card"><div class="label">Cascades Triggered</div><div class="hero-value">${{data.total_triggered}}</div></div>
<div class="card"><div class="label">Cascades Reviewed</div><div class="hero-value">${{data.total_reviewed}}</div></div>
<div class="card"><div class="label">Completion Rate</div><div class="hero-value" style="color:${{compColor}}">${{compRate}}</div></div>
<div class="card"><div class="label">Merges w/ Cascade</div><div class="hero-value">${{data.merges_with_cascade}}</div></div>
</div>
<div class="card" style="margin-top:12px">
<table>
<tr><th>Agent</th><th>Cascades Triggered</th><th>Claims Affected</th></tr>
${{agentRows || '<tr><td colspan="3" style="color:#8b949e">No per-agent data</td></tr>'}}
</table>
</div>`;
}}).catch(() => {{
document.getElementById('cascade-container').innerHTML =
'<div class="card" style="color:#f85149">Failed to load cascade data</div>';
}});
// --- Review Quality (live from review_records) ---
fetch('/api/review-summary?days=30')
.then(r => r.json())
.then(data => {{
const el = document.getElementById('review-container');
if (!data.populated) {{
el.innerHTML = `
<div class="card" style="text-align:center;padding:30px">
<div style="font-size:14px;color:#d29922">Review records table is empty</div>
<div style="font-size:12px;color:#8b949e;margin-top:8px">
review_records (migration v12) is deployed. Structured review data will populate as new PRs are evaluated.
</div>
</div>`;
return;
}}
const outcomes = data.outcomes || {{}};
const approved = (outcomes['approved'] || 0) + (outcomes['approved-with-changes'] || 0);
const rejected = outcomes['rejected'] || 0;
const approvalRate = data.total > 0 ? ((approved / data.total) * 100).toFixed(1) : '--';
const approvalColor = approved / data.total >= 0.7 ? '#3fb950' : approved / data.total >= 0.5 ? '#d29922' : '#f85149';
// Rejection reasons
let reasonRows = '';
for (const r of (data.rejection_reasons || [])) {{
reasonRows += '<tr><td><code>' + esc(r.reason) + '</code></td><td>' + r.count + '</td></tr>';
}}
// Disagreement types
let disagreeRows = '';
for (const d of (data.disagreement_types || [])) {{
disagreeRows += '<tr><td>' + esc(d.type) + '</td><td>' + d.count + '</td></tr>';
}}
el.innerHTML = `
<div class="grid">
<div class="card"><div class="label">Total Reviews</div><div class="hero-value">${{data.total}}</div></div>
<div class="card"><div class="label">Approval Rate</div><div class="hero-value" style="color:${{approvalColor}}">${{approvalRate}}%</div></div>
<div class="card"><div class="label">Approved w/ Changes</div><div class="hero-value" style="color:#d29922">${{outcomes['approved-with-changes'] || 0}}</div></div>
<div class="card"><div class="label">Rejected</div><div class="hero-value" style="color:#f85149">${{rejected}}</div></div>
</div>
<div class="row" style="margin-top:12px">
<div class="card">
<div style="font-weight:600;margin-bottom:8px">Rejection Reasons</div>
<table>
<tr><th>Reason</th><th>Count</th></tr>
${{reasonRows || '<tr><td colspan="2" style="color:#8b949e">No rejections</td></tr>'}}
</table>
</div>
<div class="card">
<div style="font-weight:600;margin-bottom:8px">Disagreement Types</div>
<table>
<tr><th>Type</th><th>Count</th></tr>
${{disagreeRows || '<tr><td colspan="2" style="color:#8b949e">No disagreements</td></tr>'}}
</table>
</div>
</div>`;
}}).catch(() => {{
document.getElementById('review-container').innerHTML =
'<div class="card" style="color:#f85149">Failed to load review data</div>';
}});
</script>"""
return render_page(
title="Epistemic Integrity",
subtitle="Can we trust what we know?",
active_path="/epistemic",
body_html=body,
scripts=scripts,
timestamp=now.strftime("%Y-%m-%d %H:%M UTC"),
)