// ---
// summary: self-contained HTML dashboard renderer for telemetry aggregates (no external assets or servers).
// read_when:
// - changing dashboard sections, styling, or the inline-data contract.
// ---
import type { TelemetrySummary } from "./aggregate.ts";
export interface DashboardMeta {
generatedAt: number;
windowDays: number;
sourceDir: string;
}
export function renderTelemetryDashboard(summary: TelemetrySummary, meta: DashboardMeta): string {
const generated = new Date(meta.generatedAt).toISOString();
const sections = [
kpis(summary),
section(
"compaction",
"Compaction pressure",
[
`
${barChart(summary.perDay, "events/day (all kinds)")}
`,
`
| reason | count |
${rows(summary.compaction.byReason.map((entry) => [entry.reason, String(entry.n)]))}
`,
`
| stage | failures | top error |
${rows(summary.compactionFailures.map((entry) => [entry.stage, String(entry.n), entry.topError ?? ""]))}
emitted by the owning compaction extension; unresolved-begin counts remain the fallback signal
`,
`
| metric | value |
${rows([
["total compactions", String(summary.compaction.total)],
[
"avg summary chars",
summary.compaction.avgSummaryChars === null
? "n/a"
: String(summary.compaction.avgSummaryChars),
],
[
"max summary chars",
summary.compaction.maxSummaryChars === null
? "n/a"
: String(summary.compaction.maxSummaryChars),
],
["unresolved begins (failed/aborted)", String(summary.compaction.unresolvedBegins)],
[
"stalled after compaction (>10m idle)",
String(summary.compaction.stalledAfterCompaction),
],
])}
`,
].join(""),
),
section(
"compaction-quality",
"Compaction quality",
[
`
| metric | value |
${rows([
["quality events", String(summary.compactionQuality.total)],
[
"validation failures",
`${summary.compactionQuality.validationFailures} (${summary.compactionQuality.validationFailureRatePct}%)`,
],
[
"fallbacks",
`${summary.compactionQuality.fallbacks} (${summary.compactionQuality.fallbackRatePct}%)`,
],
[
"repairs",
`${summary.compactionQuality.repairs} (${summary.compactionQuality.repairRatePct}%)`,
],
[
"split turns",
`${summary.compactionQuality.splitTurns} (${summary.compactionQuality.splitTurnRatePct}%)`,
],
[
"verified worktree",
`${summary.compactionQuality.worktreeVerified} (${summary.compactionQuality.worktreeVerifiedRatePct}%)`,
],
["message omission rate", `${summary.compactionQuality.messageOmissionRatePct}%`],
["avg compacted messages", nullable(summary.compactionQuality.avgCompactedMessages)],
["avg selected messages", nullable(summary.compactionQuality.avgSelectedMessages)],
["avg omitted messages", nullable(summary.compactionQuality.avgOmittedMessages)],
["avg continuity records", nullable(summary.compactionQuality.avgContinuityRecords)],
["avg evidence anchors", nullable(summary.compactionQuality.avgEvidenceAnchors)],
["avg duration ms", nullable(summary.compactionQuality.avgDurationMs)],
])}
`,
`
| boundedness / safety metric | value |
${rows([
[
"omitted managed records",
String(summary.compactionQuality.totalOmittedManagedRecords),
],
["omitted managed blocks", String(summary.compactionQuality.totalOmittedManagedBlocks)],
["redactions", String(summary.compactionQuality.totalRedactions)],
["truncated records", String(summary.compactionQuality.totalTruncatedRecords)],
["avg summary chars", nullable(summary.compactionQuality.avgSummaryChars)],
["avg input token budget", nullable(summary.compactionQuality.avgInputTokenBudget)],
["avg final token budget", nullable(summary.compactionQuality.avgFinalTokenBudget)],
])}
`,
`
| mode | events |
${rows(summary.compactionQuality.byMode.map((entry) => [entry.mode, String(entry.n)]))}
`,
].join(""),
),
section(
"recall",
"Compaction recall",
[
`
| metric | value |
${rows([
["recall calls", String(summary.recall.total)],
["hits returned", String(summary.recall.hits)],
["ranked hits", String(summary.recall.totalRankedHits)],
["zero-hit calls", `${summary.recall.zeroHit} (${summary.recall.zeroHitRatePct}%)`],
[
"scope widened",
`${summary.recall.scopeWidened} (${summary.recall.scopeWidenedRatePct}%)`,
],
["lineage degraded", `${summary.recall.degraded} (${summary.recall.degradedRatePct}%)`],
["source omission rate", `${summary.recall.sourceOmissionRatePct}%`],
["avg source entries", nullable(summary.recall.avgSourceEntries)],
["avg source entries omitted", nullable(summary.recall.avgSourceEntriesOmitted)],
["avg candidates", nullable(summary.recall.avgCandidates)],
["avg ranked hits", nullable(summary.recall.avgTotalHits)],
["avg page hits", nullable(summary.recall.avgHits)],
["avg expanded", nullable(summary.recall.avgExpanded)],
["avg direct refs", nullable(summary.recall.avgDirectRefs)],
["avg duration ms", nullable(summary.recall.avgDurationMs)],
])}
`,
`
| mode | calls |
${rows(summary.recall.byMode.map((entry) => [entry.mode, String(entry.n)]))}
`,
`
| scope | calls |
${rows(summary.recall.byScope.map((entry) => [entry.scope, String(entry.n)]))}
metadata only: no query text, recalled content, file contents, or absolute paths are persisted
`,
].join(""),
),
section(
"tools",
"Tool calls",
`
| tool | failures | top error signature |
${rows(summary.toolCalls.topFailing.map((entry) => [entry.tool, String(entry.n), entry.topError ?? ""]))}
${summary.toolCalls.total} calls, ${summary.toolCalls.failed} failed (${summary.toolCalls.failureRatePct}%)
`,
),
section(
"vault",
"Prompt Vault",
`
| metric | value |
${rows([
["queries", String(summary.vault.total)],
["failed", String(summary.vault.failed)],
])}
`,
),
section(
"skills",
"Skill loads",
`
| skill | loads |
${rows(summary.skills.map((entry) => [entry.skill, String(entry.n)]))}
`,
),
section(
"selfdriving",
"Self-driving follow-ups",
`
| metric | value |
${rows([
["total follow-up attempts", String(summary.followUps.total)],
["sent", String(summary.followUps.sent)],
["blocked", String(summary.followUps.blocked)],
])}
| blocked reason | count |
${rows(summary.followUps.byBlockedReason.map((entry) => [entry.reason, String(entry.n)]))}
`,
),
section(
"subagents",
"Subagents",
`
| profile | dispatches | failed |
${rows(summary.subagents.byProfile.map((entry) => [entry.profile, String(entry.n), String(entry.failed)]))}
${summary.subagents.total} dispatches, ${summary.subagents.failed} failed
`,
),
section(
"raw",
"Event kinds",
`
| kind | events |
${rows(summary.perKind.map((entry) => [entry.kind, String(entry.n)]))}
`,
),
].join("\n");
const navigation = [
"compaction",
"compaction-quality",
"recall",
"tools",
"vault",
"skills",
"selfdriving",
"subagents",
"raw",
];
return `
Pi telemetry — last ${meta.windowDays}d
${sections}
`;
}
function provenance(summary: TelemetrySummary): string {
if (summary.totalEvents === 0) {
return "no data yet — run /telemetry backfill to seed history from session JSONL";
}
const live = summary.sources.find((entry) => entry.source === "live")?.n ?? 0;
const backfilled = summary.sources.find((entry) => entry.source === "backfill")?.n ?? 0;
return `measured live ${live} · derived from history ${backfilled}`;
}
function kpis(summary: TelemetrySummary): string {
const entries: Array<[string, string]> = [
["events", String(summary.totalEvents)],
["compactions", String(summary.compaction.total)],
["quality fallbacks", String(summary.compactionQuality.fallbacks)],
["recall calls", String(summary.recall.total)],
["recall zero-hit", String(summary.recall.zeroHit)],
["stalled >10m", String(summary.compaction.stalledAfterCompaction)],
["tool failures", String(summary.toolCalls.failed)],
["vault queries", String(summary.vault.total)],
["follow-ups sent", String(summary.followUps.sent)],
["follow-ups blocked", String(summary.followUps.blocked)],
["subagents", String(summary.subagents.total)],
];
return `${entries.map(([label, value]) => `
${escapeHtml(value)}${escapeHtml(label)}
`).join("")}
`;
}
function nullable(value: number | null): string {
return value === null ? "n/a" : String(value);
}
function section(id: string, title: string, body: string): string {
return `${escapeHtml(title)}
${body}`;
}
function rows(entries: Array>): string {
if (entries.length === 0) {
return `| no data in window | |
`;
}
return entries
.map((entry) => `${entry.map((value) => `| ${escapeHtml(value)} | `).join("")}
`)
.join("");
}
function barChart(perDay: Array<{ day: string; n: number }>, label: string): string {
if (perDay.length === 0) return `no data in window
`;
const max = Math.max(...perDay.map((entry) => entry.n), 1);
const bars = perDay
.map((entry) => {
const height = Math.max(4, Math.round((entry.n / max) * 100));
return ``;
})
.join("");
return `${bars}
${escapeHtml(label)} · ${perDay.length}d covered
`;
}
function escapeHtml(value: string): string {
return value
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}