From 541f465b23cf3b57c908ea335b5070edac6e7288 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 23 Aug 2026 23:40:03 -0700 Subject: [PATCH 1/2] Add Insights empty-state and latency copy regressions --- apps/web/test/insights-page.test.tsx | 70 +++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/apps/web/test/insights-page.test.tsx b/apps/web/test/insights-page.test.tsx index d4be910e0..02a205194 100644 --- a/apps/web/test/insights-page.test.tsx +++ b/apps/web/test/insights-page.test.tsx @@ -91,17 +91,76 @@ function renderLanding(args: { } describe("InsightsPage usage honesty", () => { - test("ready-empty usage renders zero KPIs, not a load error", () => { + test("ready-empty usage hides usage KPI chrome, keeps Runs, not a load error", () => { const markup = renderLanding({ summary: { kind: "ready", data: EMPTY_OVERALL_USAGE }, activity: { kind: "ready", data: [] }, byTool: { kind: "ready", data: [] }, }); expect(markup).not.toContain("load insights"); - expect(markup).toContain("$0.00"); + expect(markup).not.toContain("$0.00"); + expect(markup).not.toContain("Tokens in / out"); + expect(markup).toContain("Runs"); expect(markup).toContain("Insights"); }); + test("zero-turn landing does not render a padded empty activity chart", () => { + const markup = renderLanding({ + summary: { kind: "ready", data: EMPTY_OVERALL_USAGE }, + activity: { kind: "ready", data: [] }, + byTool: { kind: "ready", data: [] }, + }); + expect(markup).not.toContain("Axis to"); + }); + + test("all-zero activity days do not render a chart even when the raw series is non-empty", () => { + const markup = renderLanding({ + summary: { kind: "ready", data: EMPTY_OVERALL_USAGE }, + activity: { + kind: "ready", + data: [ + { day: "2026-01-09", turns: 0, tokens: 0, byModel: [] }, + { day: "2026-01-10", turns: 0, tokens: 0, byModel: [] }, + { day: "2026-01-11", turns: 0, tokens: 0, byModel: [] }, + { day: "2026-01-12", turns: 0, tokens: 0, byModel: [] }, + { day: "2026-01-13", turns: 0, tokens: 0, byModel: [] }, + { day: "2026-01-14", turns: 0, tokens: 0, byModel: [] }, + { day: "2026-01-15", turns: 0, tokens: 0, byModel: [] }, + ], + }, + byTool: { kind: "ready", data: [] }, + }); + expect(markup).not.toContain("Axis to"); + }); + + test("turns with a true zero cost still show $0.00", () => { + const markup = renderLanding({ + summary: { + kind: "ready", + data: { + turns: 4, + tokens: { + input: 20, + cacheRead: 0, + cacheWrite: 0, + output: 10, + thinking: 0, + total: 30, + }, + costUsd: 0, + byModel: [], + }, + }, + activity: { + kind: "ready", + data: [{ day: "2026-01-15", turns: 4, tokens: 30, byModel: [] }], + }, + byTool: { kind: "ready", data: [] }, + }); + expect(markup).toContain("$0.00"); + expect(markup).toContain("Tokens in / out"); + }); + test("summary API error surfaces load failure instead of zeros", () => { const markup = renderLanding({ summary: { @@ -791,6 +850,10 @@ describe("InsightsPage global landing — turn latency tiles (CL-6257)", () => { expect(el.textContent).toContain("3.4s / 8.0s"); expect(el.textContent).toContain("To first token (p50 / p95)"); expect(el.textContent).toContain("1.2s / 3.4s"); + expect(el.textContent).toContain("wait until first token"); + expect(el.textContent).toContain("first token → reply posted"); + expect(el.textContent).not.toContain("inference start"); + expect(el.textContent).not.toContain("reactor"); // No cold starts this window — that stage tile does not render at all. expect(el.textContent).not.toContain("Cold start"); }); @@ -808,5 +871,8 @@ describe("InsightsPage global landing — turn latency tiles (CL-6257)", () => { }); expect(el.textContent).toContain("Cold start (p50 / p95)"); expect(el.textContent).toContain("30.0s / 45.0s"); + expect(el.textContent).toContain("wait before the model starts"); + expect(el.textContent).not.toContain("reactor"); + expect(el.textContent).not.toContain("inference start"); }); }); From 2d4068fbe4a913e57685cc3cfa67c6eeaef47b45 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 23 Aug 2026 23:40:04 -0700 Subject: [PATCH 2/2] Hide empty Insights KPI chrome and intern latency captions --- apps/web/src/pages/insights-page.tsx | 114 ++++++++++++++++----------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/apps/web/src/pages/insights-page.tsx b/apps/web/src/pages/insights-page.tsx index 62c08fe41..b646ea638 100644 --- a/apps/web/src/pages/insights-page.tsx +++ b/apps/web/src/pages/insights-page.tsx @@ -1,8 +1,10 @@ // Insights over packages/insights: cost KPIs, activity bars, token mosaic, // cost-by-model, calls-by-tool, recent purpose runs, runs history, and -// run-trace detail. Absent usage is zero metrics + zero day series -// (EMPTY_OVERALL_USAGE / activitySeriesForWindow). Null cost/rate still -// means "rate unknown" when turns exist — em-dash, not a fabricated cost. +// run-trace detail. Data may still be EMPTY_OVERALL_USAGE / +// activitySeriesForWindow at the client boundary; zero-turn landings hide +// Cost / Activity / Tokens chrome rather than showing zero KPI tiles. Null +// cost/rate still means "rate unknown" when turns exist — em-dash, not a +// fabricated cost. // Stage layout mirrors the shell mock: KPI row → chart/card grid → recent runs. import { @@ -687,6 +689,9 @@ function InsightsLanding({ const hitRate = cacheHitRate(usage); const missingRates = modelsWithMissingRates(usage); const activityDays = activitySeriesForWindow(activity ?? [], range); + const activityWindowEmpty = recentActivityDays(activityDays).every( + (d) => d.turns === 0, + ); const models = byModel !== null && byModel.length > 0 ? byModel : null; const tools = byTool !== null && byTool.length > 0 ? byTool : null; const recent = purposeRuns.slice(0, 12); @@ -704,35 +709,41 @@ function InsightsLanding({ return (
- - - + {noUsageInWindow ? null : ( + + )} + {noUsageInWindow ? null : ( + + )} + {noUsageInWindow ? null : ( + + )} ) : null} ) : null} - {noUsageInWindow ? ( + {noUsageInWindow && !activityWindowEmpty ? (

No usage recorded yet in this window.

) : null} @@ -824,7 +835,19 @@ function InsightsLanding({
- + {activityWindowEmpty ? ( + } + title="No activity yet" + description={ + noUsageInWindow + ? "No usage recorded yet in this window." + : "Activity shows up here once there are turns in this window." + } + /> + ) : ( + + )}
{mosaicParts.length > 0 ? ( @@ -1325,9 +1348,10 @@ export function InsightsPage({ runs.kind === "loading" || routines.kind === "loading"; - // Usage/activity/tools errors must surface. Loading (and ready-empty / - // no-tenant ready zeros from InsightsRoute) still render zero defaults so - // the dashboard never invents spend. Runs/routines soft-empty on landing. + // Usage/activity/tools errors must surface. Loading and ready-empty / + // no-tenant still use EMPTY_OVERALL_USAGE at the data boundary so the + // dashboard never invents spend; zero-turn chrome hides usage tiles. + // Runs/routines soft-empty on landing. const usageErrorRetry = summary.kind === "error" ? summary.retry @@ -1676,10 +1700,10 @@ export function InsightsRoute({ path }: { readonly path?: string }) { effectiveTenantId === null ? { kind: "ready", data: EMPTY_OVERALL_USAGE } : summary; - const emptyList = (q: APIQuery): APIQuery => - effectiveTenantId === null - ? ({ kind: "ready", data: [] as unknown as T } as APIQuery) - : q; + const activityForPage: APIQuery = + effectiveTenantId === null ? { kind: "ready", data: [] } : activity; + const byToolForPage: APIQuery = + effectiveTenantId === null ? { kind: "ready", data: [] } : byTool; const runsForPage: APIQuery<{ data: readonly InsightsRun[]; nextCursor: string | null; @@ -1710,8 +1734,8 @@ export function InsightsRoute({ path }: { readonly path?: string }) {