diff --git a/apps/web/src/pages/insights-page-render.test.tsx b/apps/web/src/pages/insights-page-render.test.tsx
index d7e1c994..4be3a9fa 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/src/pages/insights-page.tsx b/apps/web/src/pages/insights-page.tsx
index b04e84be..93a9485a 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 (
+ Runs, sparkline, and outcomes below reflect the 100 most recent runs — + more exist in this window.{" "} + + . +
+ ) : null} +