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>
239 lines
9.2 KiB
Python
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">⚙</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">⏲</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"),
|
|
)
|