feat: ship production local assurance viewer in core

This commit is contained in:
thanhnv
2026-07-28 23:09:22 +07:00
parent 3cc897c5bb
commit 9c9efb1043
21 changed files with 2544 additions and 136 deletions
@@ -0,0 +1,383 @@
(() => {
"use strict";
const params = new URLSearchParams(location.search);
const suppliedToken = params.get("token");
if (suppliedToken) sessionStorage.setItem("casan_viewer_token", suppliedToken);
const token = suppliedToken || sessionStorage.getItem("casan_viewer_token") || "";
params.delete("token");
history.replaceState({}, "", `${location.pathname}${params.size ? `?${params}` : ""}${location.hash}`);
const state = {
view: params.get("view") || (params.get("trace") ? "runs" : "overview"),
trace: params.get("trace") || "",
project: null,
registry: null,
h6: null,
run: null,
selectedGate: "H1-context",
};
const $ = (selector) => document.querySelector(selector);
const esc = (value) => String(value ?? "")
.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;")
.replaceAll('"', "&quot;").replaceAll("'", "&#039;");
const slug = (value) => String(value || "unknown").toLowerCase().replaceAll("_", "-").replace(/[^a-z0-9-]/g, "");
const text = (value, fallback = "Unavailable") => value === null || value === undefined || value === "" ? fallback : String(value);
const number = (value) => value === null || value === undefined || !Number.isFinite(Number(value))
? "Unavailable" : new Intl.NumberFormat("en-US").format(Number(value));
const time = (value) => value ? String(value).replace("T", " ").replace("Z", " UTC") : "No timestamp";
const money = (value) => value === null || value === undefined ? "Unavailable" : `$${Number(value).toFixed(6)}`;
const pill = (value) => `<span class="pill ${slug(value)}">${esc(String(value || "unknown").replaceAll("_", " "))}</span>`;
async function api(path) {
const response = await fetch(path, {
headers: { "X-CASAN-Viewer-Token": token },
cache: "no-store",
});
const payload = await response.json().catch(() => ({}));
if (!response.ok) throw new Error(payload.error || `HTTP ${response.status}`);
return payload;
}
function downloadUrl(path, query = {}) {
const next = new URLSearchParams({ ...query, token });
return `${path}?${next}`;
}
function hero({ eyebrow, title, description, verdict, meta = [], actions = "" }) {
return `<article class="hero">
<div class="hero-grid">
<div>
<p class="eyebrow">${esc(eyebrow)}</p>
<h2>${esc(title)}</h2>
<p class="hero-description">${esc(description)}</p>
<div class="hero-meta">${meta.map((item) => `<span>${esc(item)}</span>`).join("")}</div>
</div>
<div class="hero-actions">${verdict ? pill(verdict) : ""}${actions}</div>
</div>
</article>`;
}
function metric(label, value, detail) {
return `<article class="metric-card"><div class="metric-label">${esc(label)}</div>
<div class="metric-value">${esc(text(value))}</div><div class="metric-detail">${esc(detail)}</div></article>`;
}
function panel(eyebrow, title, description, body, right = "") {
return `<article class="panel"><header class="panel-head"><div><p class="eyebrow">${esc(eyebrow)}</p>
<h2>${esc(title)}</h2><p class="panel-description">${esc(description)}</p></div>${right}</header>
<div class="panel-body">${body}</div></article>`;
}
function empty(title, detail) {
return `<div class="empty"><div class="empty-mark">∅</div><strong>${esc(title)}</strong><p>${esc(detail)}</p></div>`;
}
function setView(view, push = true) {
state.view = ["overview", "runs", "h6"].includes(view) ? view : "overview";
document.querySelectorAll(".nav-item").forEach((item) => {
const active = item.dataset.view === state.view;
item.classList.toggle("active", active);
if (active) item.setAttribute("aria-current", "page");
else item.removeAttribute("aria-current");
});
document.querySelectorAll(".view").forEach((item) => item.classList.add("hidden"));
$(`#${state.view}-view`).classList.remove("hidden");
$("#page-title").textContent = {
overview: "Assurance overview", runs: "Run assurance registry", h6: "H6 AgentOps dossier",
}[state.view];
if (push) {
const next = new URL(location.href);
next.searchParams.set("view", state.view);
if (state.trace) next.searchParams.set("trace", state.trace);
else next.searchParams.delete("trace");
history.pushState({}, "", `${next.pathname}?${next.searchParams}`);
}
if (state.view === "runs") renderRuns();
if (state.view === "h6") renderH6();
closeNav();
}
function renderOverview() {
const profile = state.project;
const registry = state.registry;
const latest = registry.latest || {};
const recent = registry.runs || [];
const failures = recent.filter((row) => row.status === "failed").length;
const certified = latest.certified === true;
const latestTrace = latest.trace_id || recent[0]?.trace_id;
const maturity = profile.maturity || {};
const actions = latestTrace
? `<button class="button primary" type="button" data-open-trace="${esc(latestTrace)}">Inspect latest run</button>
<button class="button secondary" type="button" data-open-h6="${esc(latestTrace)}">Open H6</button>`
: `<button class="button secondary" type="button" data-view-target="runs">View registry</button>`;
$("#overview-view").innerHTML =
hero({
eyebrow: "CASAN Core · Local assurance",
title: latestTrace ? "One prompt. One evidence trail." : "Ready for the first governed run.",
description: "A read-only, offline-capable assurance workspace reconstructed from canonical project evidence. No Platform service or HTML generation on the prompt hot path.",
verdict: latestTrace ? (certified ? "certified" : "review required") : "ready",
meta: [
`${profile.project_name} · ${profile.edition}`,
latestTrace ? `Trace ${latestTrace}` : "No finalized trace yet",
`Maturity ${maturity.level == null ? "not assessed" : `L${maturity.level}`} · ${maturity.status}`,
],
actions,
}) +
`<section class="metric-grid">
${metric("Governed records", number(registry.count), `${recent.length} recent records loaded`)}
${metric("Latest H6 quality", latest.telemetry_quality || "Unavailable", "Missing values are disclosed, never coerced to zero")}
${metric("Observed failures", number(failures), `${recent.length ? Math.round((failures / recent.length) * 100) : 0}% of loaded records`)}
${metric("Evidence gates", latestTrace ? "H1 → H7" : "Waiting", latestTrace ? "Control-by-control reconstruction available" : "Evidence appears after a prompt finalizes")}
</section>` +
panel(
"Product boundary",
"Core is visual by default",
"The local viewer covers the complete single-project review workflow; centralized operations remain a Platform capability.",
`<div class="two-col">
<div class="notice success"><strong>Included in Core</strong><br>Latest run, history, interactive H1–H7 evidence, H6 AgentOps, and on-demand JSON/HTML export.</div>
<div class="notice warning"><strong>Platform boundary</strong><br>Multi-project fleet views, centralized RBAC, shared approvals, ingestion and organization-wide retention.</div>
</div>`
) +
recentRunsPanel(recent.slice(0, 6), "Latest governed evidence");
bindActions();
}
function recentRunsPanel(rows, title = "Recent governed runs") {
const body = rows.length ? `<div class="table-scroll"><table>
<thead><tr><th>Observed at</th><th>Lifecycle step</th><th>Verdict</th><th class="right">Latency</th><th class="right">Tokens</th><th class="right">Evidence</th></tr></thead>
<tbody>${rows.map((row) => `<tr>
<td class="mono">${esc(time(row.timestamp))}</td><td><strong>${esc(row.step || row.harness || "Agentic turn")}</strong></td>
<td>${pill(row.status)}</td><td class="right mono">${esc(row.latency_ms == null ? "Unavailable" : `${number(row.latency_ms)} ms`)}</td>
<td class="right mono">${esc(number(row.total_tokens))}</td>
<td class="right">${row.trace_id ? `<button class="button light" type="button" data-open-trace="${esc(row.trace_id)}">Inspect H1–H7</button>` : "—"}</td>
</tr>`).join("")}</tbody></table></div>` : empty("No governed run yet", "Complete a prompt in a configured client, then refresh this viewer.");
return panel("Evidence index", title, "Operational values are shown only when present in canonical telemetry.", body);
}
function renderRuns() {
const rows = state.registry.runs || [];
const selected = state.run;
const body = hero({
eyebrow: "Run assurance registry",
title: "Every governed run, one evidence trail.",
description: "Select a lifecycle record to reconstruct H1–H7 decisions, inspect sanitized evidence and export an independent dossier.",
verdict: rows.some((row) => row.status === "failed") ? "attention" : "operational",
meta: [`${state.registry.count} records`, `${rows.filter((row) => row.status === "failed").length} failures`, "Local canonical evidence"],
actions: `<button class="button primary" type="button" data-view-target="h6">Open H6 dossier</button>`,
}) + (selected ? renderTrace(selected) : "") + recentRunsPanel(rows);
$("#runs-view").innerHTML = body;
bindActions();
bindGates();
}
function renderTrace(report) {
const gates = report.gates || [];
let selected = gates.find((gate) => gate.id === state.selectedGate) || gates[0];
if (!selected) return "";
const rail = gates.map((gate, index) => `<button type="button" aria-pressed="${gate.id === selected.id}" class="gate-button ${gate.id === selected.id ? "selected" : ""}" data-gate="${esc(gate.id)}">
<span class="gate-index">0${index + 1}</span><span class="gate-name">${esc(gate.title)}</span>
<span class="gate-state"><span class="state-dot ${slug(gate.status)}"></span>${esc(gate.status)}</span></button>`).join("");
const events = (selected.events || []).slice(-4).reverse();
const timeline = events.length ? `<div class="timeline">${events.map((event) => `<div class="timeline-item">
<div class="timeline-line"><span class="state-dot ${slug(event.status)}"></span></div>
<div class="timeline-copy"><strong>${esc(event.status)}</strong><time>${esc(time(event.timestamp))}</time><p>${esc(event.reason)}</p></div>
</div>`).join("")}</div>` : `<p class="metric-detail">No individual event was emitted for this control.</p>`;
const entries = Object.entries(selected.evidence || {});
const manifest = entries.length ? `<dl>${entries.map(([key, value]) => `<div class="manifest-row"><dt>${esc(key)}</dt><dd>${esc(typeof value === "string" ? value : JSON.stringify(value))}</dd></div>`).join("")}</dl>`
: empty("No safe evidence fields", "This control has not emitted a sanitized evidence manifest.");
const actions = `<a class="button secondary" href="${downloadUrl(`/api/v1/reports/run/${encodeURIComponent(report.trace_id)}/export`, { format: "json" })}">JSON evidence</a>
<a class="button primary" href="${downloadUrl(`/api/v1/reports/run/${encodeURIComponent(report.trace_id)}/export`, { format: "html" })}">Export dossier</a>`;
return panel(
"Run assurance",
"Governed execution dossier",
"A control-by-control reconstruction backed by sanitized lifecycle evidence.",
`<div class="hero trace-hero">
<div class="hero-grid"><div><p class="eyebrow">Trace provenance</p><h2 class="trace-title">${esc(report.trace_id)}</h2>
<div class="hero-meta trace-meta"><span>${report.summary.gates_observed}/7 controls observed</span><span>${report.project.edition} edition</span></div></div>
<div>${pill(report.verdict)}</div></div></div>
<div class="rail-wrap rail-spaced"><div class="evidence-rail">${rail}</div></div>
<div class="dossier">
<div class="decision-card"><div><p class="eyebrow">Selected control</p><h2>${esc(selected.title)}</h2><p class="panel-description">${esc(selected.description)}</p></div>
<div class="inset"><span class="inset-label">Latest decision · ${esc(time(selected.updated_at))}</span><p>${esc(selected.reason)}</p></div>
<div class="inset"><span class="inset-label">Event history</span>${timeline}</div>
</div>
<div class="manifest"><div class="manifest-head"><div><p class="eyebrow">Safe evidence manifest</p><div class="metric-detail">Sanitized fields persisted by the harness</div></div>${pill(`${entries.length} fields`)}</div>${manifest}</div>
</div>`,
actions,
);
}
function renderH6() {
const data = state.h6;
if (!data) return;
const query = data.scope || {};
const queryObject = Object.fromEntries(Object.entries(query).filter(([, value]) => value));
const actions = `<a class="button secondary" href="${downloadUrl("/api/v1/reports/h6/export", { ...queryObject, format: "json" })}">JSON evidence</a>
<a class="button primary" href="${downloadUrl("/api/v1/reports/h6/export", { ...queryObject, format: "html" })}">Export dossier</a>`;
const findings = data.findings.length ? `<div class="findings">${data.findings.map((item, index) => `<article class="finding ${slug(item.severity)}">
<span class="finding-index">${String(index + 1).padStart(2, "0")}</span><div><code>${esc(item.code)}</code><p>${esc(item.message)}</p></div></article>`).join("")}</div>`
: `<div class="notice success">No threshold breach was detected in the selected evidence boundary.</div>`;
const sources = data.evidence_sources.map((source) => {
const status = !source.present ? "missing" : source.stale ? "stale" : "fresh";
return `<article class="source"><div class="source-top"><div><h3>${esc(source.source)}</h3><div class="source-meta">${source.records} records · ${source.age_s == null ? "no timestamp" : `${number(source.age_s)}s old`}</div></div>${pill(status)}</div>
<div class="source-path">${esc(source.path)}</div></article>`;
}).join("");
const breakdown = (rows, subject) => `<div class="table-scroll"><table><thead><tr><th>${esc(subject)}</th><th class="right">Runs</th><th class="right">Failures</th><th class="right">Avg latency</th><th class="right">Tokens</th><th class="right">Cost</th></tr></thead>
<tbody>${rows.length ? rows.map((row) => `<tr><td><strong>${esc(row.key)}</strong></td><td class="right mono">${row.runs}</td><td class="right mono">${row.failures}</td><td class="right mono">${number(row.latency_avg_ms)} ms</td><td class="right mono">${number(row.tokens)}</td><td class="right mono">${money(row.cost_usd)}</td></tr>`).join("") : `<tr><td colspan="6">${empty("No matching records", `No ${subject.toLowerCase()} evidence is available.`)}</td></tr>`}</tbody></table></div>`;
$("#h6-view").innerHTML =
hero({
eyebrow: "H6 · AgentOps assurance",
title: "Operational evidence, ready for a decision.",
description: "Runtime health, reliability, cost provenance and telemetry integrity—designed to answer what happened, why it matters and where the evidence lives.",
verdict: data.verdict,
meta: [data.report_id, `Generated ${time(data.generated_at)}`, `Freshness ${data.freshness.status}`],
actions,
}) +
panel("Scope", "Evidence boundary", "Filters apply to the screen and both export formats.", h6Filters(data)) +
`<section class="metric-grid">
${metric("Governed runs", number(data.summary.runs), `${data.summary.success} successful · ${data.summary.degraded} degraded`)}
${metric("Failure rate", `${data.summary.failure_rate_pct}%`, `${data.summary.failed} failed · ${data.summary.alerts} alerts`)}
${metric("P95 latency", `${number(data.summary.latency_ms.p95)} ms`, `P50 ${number(data.summary.latency_ms.p50)} ms`)}
${metric("Telemetry integrity", data.data_quality.status.replaceAll("_", " "), `${data.summary.coverage.token_pct}% token · ${data.summary.coverage.cost_pct}% cost`)}
</section>` +
`<div class="two-col">
${panel("Decision brief", "What requires attention", "Threshold findings are review signals, not maturity claims.", findings, pill(data.verdict))}
${panel("Source integrity", "Evidence provenance", "Freshness is evaluated independently for each canonical source.", `<div class="source-grid">${sources}</div>`, pill(data.freshness.status))}
</div>` +
`<div class="breakdown-grid">
${panel("Runtime", "Execution profile by step", "Latency, failure and attribution by governed lifecycle step.", breakdown(data.details.by_step, "Step"))}
${panel("Provider", "Model usage and provenance", "Only provider-reported token and actual cost values are shown.", breakdown(data.details.by_provider, "Provider · model"))}
</div>` +
panel("Evidence quality", "Coverage and disclosure", "Unavailable telemetry remains null. Coverage gaps stay visible.", `<div class="two-col">
<div class="coverage">${coverage("Token attribution", data.summary.coverage.token_pct, `${data.summary.coverage.token_records}/${data.summary.coverage.runtime_records} records`)}
${coverage("Cost attribution", data.summary.coverage.cost_pct, `${data.summary.coverage.cost_records}/${data.summary.coverage.runtime_records} records`)}</div>
<div class="inset"><span class="inset-label">Disclosure notes</span>${data.data_quality.warnings.length ? `<ul>${data.data_quality.warnings.map((warning) => `<li class="metric-detail">${esc(warning)}</li>`).join("")}</ul>` : `<p class="success-copy">All required sources are present with no estimation warning.</p>`}</div>
</div>`, pill(data.data_quality.status));
bindH6Filters();
}
function coverage(label, value, detail) {
const width = Math.round(Math.max(0, Math.min(100, Number(value) || 0)) / 5) * 5;
return `<div><div class="coverage-head"><strong>${esc(label)}</strong><span>${esc(value)}%</span></div>
<div class="coverage-track"><div class="coverage-fill width-${width}"></div></div>
<div class="metric-detail">${esc(detail)}</div></div>`;
}
function h6Filters(data) {
const scope = data.scope || {};
const options = data.available_filters || { projects: [], runs: [] };
const date = (value) => value ? value.slice(0, 10) : "";
return `<form id="h6-filters" class="filters">
<label class="field"><span>Project</span><select name="project"><option value="">All projects</option>${options.projects.map((value) => `<option value="${esc(value)}" ${scope.project === value ? "selected" : ""}>${esc(value)}</option>`).join("")}</select></label>
<label class="field"><span>From</span><input type="date" name="from" value="${esc(date(scope.from))}"></label>
<label class="field"><span>To</span><input type="date" name="to" value="${esc(date(scope.to))}"></label>
<label class="field"><span>Run / trace</span><input name="run" list="run-options" value="${esc(scope.run || "")}" placeholder="All governed runs"><datalist id="run-options">${options.runs.map((value) => `<option value="${esc(value)}"></option>`).join("")}</datalist></label>
<div class="filter-actions"><button class="button primary" type="submit">Apply</button><button class="button light" type="button" id="clear-h6">Clear</button></div>
</form>`;
}
async function loadTrace(traceId) {
if (!traceId) return;
state.trace = traceId;
state.selectedGate = "H1-context";
$("#loading").classList.remove("hidden");
try {
state.run = await api(`/api/v1/reports/run/${encodeURIComponent(traceId)}`);
setView("runs");
} catch (error) {
showError(`Run report could not be reconstructed: ${error.message}`);
} finally {
$("#loading").classList.add("hidden");
}
}
async function loadH6(query = {}) {
const next = new URLSearchParams(Object.fromEntries(Object.entries(query).filter(([, value]) => value)));
$("#loading").classList.remove("hidden");
try {
state.h6 = await api(`/api/v1/reports/h6${next.size ? `?${next}` : ""}`);
renderH6();
} catch (error) {
showError(`H6 report could not be generated: ${error.message}`);
} finally {
$("#loading").classList.add("hidden");
}
}
function bindActions() {
document.querySelectorAll("[data-view-target]").forEach((button) => button.addEventListener("click", () => setView(button.dataset.viewTarget)));
document.querySelectorAll("[data-open-trace]").forEach((button) => button.addEventListener("click", () => loadTrace(button.dataset.openTrace)));
document.querySelectorAll("[data-open-h6]").forEach((button) => button.addEventListener("click", async () => {
await loadH6({ run: button.dataset.openH6 });
setView("h6");
}));
}
function bindGates() {
document.querySelectorAll("[data-gate]").forEach((button) => button.addEventListener("click", () => {
state.selectedGate = button.dataset.gate;
renderRuns();
}));
}
function bindH6Filters() {
$("#h6-filters")?.addEventListener("submit", async (event) => {
event.preventDefault();
const values = Object.fromEntries(new FormData(event.currentTarget));
await loadH6(values);
});
$("#clear-h6")?.addEventListener("click", () => loadH6());
}
function showError(message) {
$("#error").textContent = message;
$("#error").classList.remove("hidden");
}
function closeNav() {
$(".sidebar").classList.remove("open");
$("#nav-scrim").classList.add("hidden");
$("#menu-button").setAttribute("aria-expanded", "false");
}
async function loadAll() {
$("#error").classList.add("hidden");
$("#loading").classList.remove("hidden");
try {
[state.project, state.registry, state.h6] = await Promise.all([
api("/api/v1/project"),
api("/api/v1/runs?limit=100"),
api("/api/v1/reports/h6?limit=50"),
]);
$("#freshness-pill").textContent = `${state.h6.freshness.status} evidence`;
$("#freshness-pill").className = `pill ${slug(state.h6.freshness.status)}`;
renderOverview();
renderH6();
if (state.trace) {
await loadTrace(state.trace);
} else {
setView(state.view, false);
}
} catch (error) {
showError(`Local evidence could not be loaded: ${error.message}`);
} finally {
$("#loading").classList.add("hidden");
}
}
document.querySelectorAll(".nav-item").forEach((button) => button.addEventListener("click", () => setView(button.dataset.view)));
$("#refresh-button").addEventListener("click", loadAll);
$("#menu-button").addEventListener("click", () => {
const open = !$(".sidebar").classList.contains("open");
$(".sidebar").classList.toggle("open", open);
$("#nav-scrim").classList.toggle("hidden", !open);
$("#menu-button").setAttribute("aria-expanded", String(open));
});
$("#nav-scrim").addEventListener("click", closeNav);
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") closeNav();
});
window.addEventListener("popstate", () => setView(new URLSearchParams(location.search).get("view") || "overview", false));
loadAll();
})();