From 6d4eae01f53df1a950d644c37ca5d39c015a5793 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Fri, 21 Aug 2026 15:45:51 -0700 Subject: [PATCH 1/2] Add tests for Insights running-now window decoupling, elapsed ticking, and run-cap disclosure Covers three Insights defects: a run started before the 7-day window but still running must stay in the "Running now" strip and KPI; the elapsed label next to the live status dot must tick instead of freezing at first render; and the landing view must disclose when the 100-run fetch cap truncates the runs backing its KPIs, sparkline, and outcome chart. --- .../src/pages/insights-page-render.test.tsx | 57 +++++++++++++++++++ apps/web/test/insights-page.test.tsx | 45 +++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/apps/web/src/pages/insights-page-render.test.tsx b/apps/web/src/pages/insights-page-render.test.tsx index d7e1c994f..4be3a9fa3 100644 --- a/apps/web/src/pages/insights-page-render.test.tsx +++ b/apps/web/src/pages/insights-page-render.test.tsx @@ -134,4 +134,61 @@ describe("InsightsPage 'Running now' strip", () => { expect(el.textContent).toContain("1 in progress"); expect(el.textContent).toContain("Weekly digest"); }); + + // Liveness is not a windowed property: a run that started long before the + // 7-day window and is still running must not disappear from the strip or + // read 0 in the "Running now" KPI just because its start time falls + // outside `range`. + test("a run started 8 days ago that is still running stays in the strip and the KPI", () => { + const eightDaysAgo = new Date( + Date.now() - 8 * 24 * 60 * 60 * 1000, + ).toISOString(); + const el = render("/insights", { + data: [ + { + id: "run_long_haul", + tenantId: "tnt_bench_a", + definitionId: "wfd_a", + definitionName: "Long haul", + address: "addr", + status: "running", + createdAt: eightDaysAgo, + updatedAt: eightDaysAgo, + routineId: null, + routineName: null, + }, + ], + nextCursor: null, + }); + expect(el.textContent).toContain("Running now"); + expect(el.textContent).toContain("1 in progress"); + expect(el.textContent).toContain("Long haul"); + expect(el.textContent).toContain("in flight"); + }); + + test("the elapsed label ticks forward while a run is live, not frozen at first render", async () => { + const startedAt = new Date(Date.now() - 2_000).toISOString(); + const el = render("/insights", { + data: [ + { + id: "run_ticking", + tenantId: "tnt_bench_a", + definitionId: "wfd_a", + definitionName: "Weekly digest", + address: "addr", + status: "running", + createdAt: startedAt, + updatedAt: startedAt, + routineId: null, + routineName: null, + }, + ], + nextCursor: null, + }); + const before = el.textContent; + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1_200)); + }); + expect(el.textContent).not.toBe(before); + }); }); diff --git a/apps/web/test/insights-page.test.tsx b/apps/web/test/insights-page.test.tsx index fd8200b4b..6edbe1f06 100644 --- a/apps/web/test/insights-page.test.tsx +++ b/apps/web/test/insights-page.test.tsx @@ -278,6 +278,51 @@ describe("InsightsPage scope switcher", () => { }); }); +function renderAtPathWithRuns(path: string, nextCursor: string | null): string { + return renderToStaticMarkup( + + undefined}> + + null} + scopeLabel="All workbenches" + /> + + + , + ); +} + +describe("InsightsPage landing run-cap disclosure", () => { + // The feed is fetched at a fixed limit=100 (see insightsTopLevelRunsPath). + // A non-null nextCursor means the window truly holds more runs than were + // fetched, so the KPIs/sparkline/outcome chart built from that truncated + // set must say so rather than presenting it as the complete series — + // InsightsRunsHistory already discloses its own cap; the landing must too. + test("a non-null nextCursor discloses the 100-run cap on the landing view", () => { + const markup = renderAtPathWithRuns("/insights", "cursor_2"); + expect(markup).toContain("100 most recent runs"); + }); + + test("a null nextCursor (fewer than 100 runs) shows no cap disclosure", () => { + const markup = renderAtPathWithRuns("/insights", null); + expect(markup).not.toContain("100 most recent runs"); + }); +}); + describe("InsightsPage breadcrumbs", () => { test("runs history puts an Insights / Run history trail in the top bar", () => { const markup = renderAtPath("/insights/runs"); From 797522e70857461984246762829f642b64ed5536 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Fri, 21 Aug 2026 15:45:58 -0700 Subject: [PATCH 2/2] Insights: decouple running-now from the time window, tick the elapsed label, disclose the run-fetch cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes to the landing dashboard: - "Running now" (both the KPI tile and the strip) now reads off every fetched run rather than the range-filtered subset, since being in flight is not a windowed property — a run that started before the 7-day window and is still running no longer disappears or reads 0. - The elapsed label beside the pulsing live status dot now ticks once a second while a run is running, instead of freezing at whatever instant the strip last rendered for an unrelated reason. - The landing now discloses when the feed's fixed limit=100 fetch caps the runs backing its KPIs, sparkline, and outcome chart, matching the disclosure InsightsRunsHistory already gives on its own view. --- apps/web/src/pages/insights-page.tsx | 64 +++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/apps/web/src/pages/insights-page.tsx b/apps/web/src/pages/insights-page.tsx index b04e84be1..93a9485ae 100644 --- a/apps/web/src/pages/insights-page.tsx +++ b/apps/web/src/pages/insights-page.tsx @@ -442,6 +442,29 @@ export function elapsedLabel(createdAt: string, now: number): string { return durationLabel(Math.max(0, now - startMs)); } +const ELAPSED_TICK_MS = 1_000; + +/** Ticks once a second while `enabled` — the clock the elapsed label next to + * the pulsing `StatusDot` reads from, so it counts up like the live indicator + * beside it instead of freezing at whatever instant this component mounted + * or last re-rendered for an unrelated reason. */ +function useTickingNow(enabled: boolean): number { + const [now, setNow] = useState(() => Date.now()); + useEffect(() => { + if (!enabled) return undefined; + const timer = setInterval(() => setNow(Date.now()), ELAPSED_TICK_MS); + return () => clearInterval(timer); + }, [enabled]); + return now; +} + +/** A run actually in flight right now (`status: running | updating`) — + * liveness is not a windowed property, so this filters the full run set, + * never the range-filtered one. */ +function isRunningNow(run: InsightsRun): boolean { + return run.status === "running" || run.status === "updating"; +} + /** * "Running now" — a horizontally scrolling strip of the runs actually in * flight this instant (`status: running | updating`), not a fabricated @@ -457,11 +480,9 @@ function RunningNowStrip({ readonly runs: readonly InsightsRun[]; readonly onOpenRun: (id: string) => void; }) { - const running = runs.filter( - (r) => r.status === "running" || r.status === "updating", - ); + const running = runs.filter(isRunningNow); + const now = useTickingNow(running.length > 0); if (running.length === 0) return null; - const now = Date.now(); return (
@@ -619,6 +640,7 @@ function InsightsLanding({ byModel, byTool, runs, + runsNextCursor, routines, workbenches, latency, @@ -633,6 +655,12 @@ function InsightsLanding({ readonly byModel: readonly ModelUsage[] | null; readonly byTool: readonly ToolCall[] | null; readonly runs: readonly InsightsRun[]; + /** The feed's own `nextCursor` (`limit=100` fetch, see + * `insightsTopLevelRunsPath`) — non-null means more runs exist in this + * window than the 100 fetched, so the KPIs/sparkline/outcome chart below + * disclose the cap instead of silently presenting a truncated series as + * complete. */ + readonly runsNextCursor: string | null; readonly routines: readonly Routine[]; /** Null while `/workbenches` hasn't resolved (or this landing is already * scoped to one workbench, where a breakdown of one has nothing to @@ -653,6 +681,12 @@ function InsightsLanding({ const stats = computeInsightsStats(windowedRuns, routines); const purposeRuns = purposeRunsForInsights(windowedRuns); + // Liveness is not a windowed property: a run that started before + // `range.from` and is still going is running right now regardless of when + // it started, so "Running now" reads off every fetched run, never the + // range-filtered subset above. + const runningNow = purposeRunsForInsights(runs).filter(isRunningNow); + // Absent usage → zeros at the client boundary (never demo peaks / em-dash // for "no spend"). Real fetched summary is preserved when present. const usage = summary ?? EMPTY_OVERALL_USAGE; @@ -726,17 +760,17 @@ function InsightsLanding({ loading={loading} /> ) : null} - {stats.running > 0 || loading ? ( + {runningNow.length > 0 || loading ? ( ) : null} - + {latency !== null && latency.total.samples > 0 ? ( @@ -780,6 +814,21 @@ function InsightsLanding({

) : null} + {runsNextCursor !== null ? ( +

+ Runs, sparkline, and outcomes below reflect the 100 most recent runs — + more exist in this window.{" "} + + . +

+ ) : null} +
@@ -1476,6 +1525,7 @@ export function InsightsPage({ byModel={byModelData} byTool={byToolData} runs={runsData} + runsNextCursor={runsNextCursor} routines={routinesData} workbenches={workbenchesData} latency={latencyData}