Skip to content

Commit df78840

Browse files
authored
perf(showcase-dashboard): order-safe concurrent paged initial status fetch + loading state (CopilotKit#5256)
## Summary The coverage dashboard's initial load was slow and briefly rendered misleading data. Root cause: `useLiveStatus` fetched the entire (growing) PocketBase `status` collection via a single `getFullList`, which PocketBase serial-paginates at perPage≤500 — ~5 sequential blocking round-trips before first paint. During that window the grid rendered authoritative `D0` cells and `✓0 ~0 ✗0` column tallies (not a loading state), and a transient amber-D5/D6 toggle flash appeared pre-hydration. ## Changes - **perf:** concurrent paged fetch — page 1 then `Promise.all` over pages 2..N, merged by array index (deterministic regardless of resolution order), with a stable `sort: "id"` on every page so concurrent requests share one ordering. Initial `setRows` wrapped in `startTransition` to avoid a synchronous full-matrix re-render. Best-effort snapshot; the live SSE subscription reconciles anything created in the brief fetch→subscribe window (PocketBase `id` is random, not monotonic — no growth-completeness claim). - **fix:** `connecting` + empty-map now renders a loading affordance instead of authoritative zeros (the §5.3 "no stale-green/fake-zero lie" guarantee); `loading` made required on the tally type; fail-safe undefined-tally fallback defaults to loading rather than zeros. ## Test plan - [x] Order-safety: pages merge deterministically even when page 1 resolves last (out-of-order resolution) - [x] Stable sort forwarded to every page request (behavioral — mock sorts before slicing) - [x] Loading-state quadrants on both `computeColumnTally` and `computeColumnTallyDetail` (connecting+empty → loading; connecting+rows → authoritative; live+empty → authoritative; error → offline) - [x] `OverlayColumnHeader` renders loading/offline affordance, not zero glyphs - [x] Full dashboard suite: 827 passed / 1 skipped; tsc clean; next build green
2 parents 733146c + 0173ae1 commit df78840

9 files changed

Lines changed: 904 additions & 87 deletions

showcase/shell-dashboard/src/components/__tests__/compute-tally-detail.test.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,79 @@ describe("computeColumnTallyDetail", () => {
9191
amber: [],
9292
red: [],
9393
unknown: true,
94+
loading: false,
9495
});
9596
});
9697

98+
// A4 regression: the connecting+empty initial-load branch in
99+
// `computeColumnTallyDetail` (mirroring `computeColumnTally`). While the
100+
// first PocketBase fetch is in flight the live-status map is empty AND the
101+
// connection is "connecting". Returning authoritative empty buckets in that
102+
// window reads as "every cell is at depth 0" — a lie. The detail must surface
103+
// `loading: true` (a subset of `unknown`) instead, never authoritative
104+
// emptiness, until the first rows land.
105+
it("returns loading: true (unknown, not authoritative empty) while connecting with no rows", () => {
106+
const integration = makeIntegration("test-int", ["feat-1"]);
107+
const features = [makeFeature("feat-1", "Feature 1")];
108+
const liveStatus: LiveStatusMap = new Map();
109+
110+
const result = computeColumnTallyDetail(
111+
integration,
112+
features,
113+
liveStatus,
114+
"connecting",
115+
);
116+
117+
expect(result).toEqual({
118+
green: [],
119+
amber: [],
120+
red: [],
121+
unknown: true,
122+
loading: true,
123+
});
124+
});
125+
126+
it("does NOT treat connecting-with-rows as loading (data already arrived)", () => {
127+
const integration = makeIntegration("test-int", ["feat-a"]);
128+
const features = [makeFeature("feat-a", "Feature A")];
129+
// A delta arrived during a transient reconnect: rows are present, so the
130+
// detail is authoritative even though the connection is mid-reconnect.
131+
const liveStatus: LiveStatusMap = new Map([
132+
["e2e:test-int/feat-a", makeRow("e2e:test-int/feat-a", "e2e", "red")],
133+
]);
134+
135+
const result = computeColumnTallyDetail(
136+
integration,
137+
features,
138+
liveStatus,
139+
"connecting",
140+
);
141+
142+
expect(result.loading).toBe(false);
143+
expect(result.unknown).toBe(false);
144+
expect(result.red).toEqual([
145+
{ label: "Feature A", dimension: "e2e", featureId: "feat-a" },
146+
]);
147+
});
148+
149+
it("live with no rows is NOT loading — authoritative empty result", () => {
150+
const integration = makeIntegration("test-int", ["feat-1"]);
151+
const features = [makeFeature("feat-1", "Feature 1")];
152+
153+
const result = computeColumnTallyDetail(
154+
integration,
155+
features,
156+
new Map(),
157+
"live",
158+
);
159+
160+
expect(result.loading).toBe(false);
161+
expect(result.unknown).toBe(false);
162+
expect(result.green).toEqual([]);
163+
expect(result.amber).toEqual([]);
164+
expect(result.red).toEqual([]);
165+
});
166+
97167
it("green D6 cells land in green bucket", () => {
98168
// Use feature IDs with a D5 mapping (agentic-chat, tool-rendering) so the
99169
// verification ladder is contiguous to D5. Green requires an intact

showcase/shell-dashboard/src/components/__tests__/dashboard-color-matrix.test.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,13 @@ describe("(6) edges + rollup precedence", () => {
803803
// subagents → no rows → gray (excluded)
804804
]);
805805
const tally = computeColumnTally(integration, features, live, "live");
806-
expect(tally).toEqual({ green: 1, amber: 0, red: 1, unknown: false });
806+
expect(tally).toEqual({
807+
green: 1,
808+
amber: 0,
809+
red: 1,
810+
unknown: false,
811+
loading: false,
812+
});
807813
});
808814

809815
it("column tally returns unknown=true when connection is error", () => {
@@ -818,7 +824,13 @@ describe("(6) edges + rollup precedence", () => {
818824
mapOf([]),
819825
"error",
820826
);
821-
expect(tally).toEqual({ green: 0, amber: 0, red: 0, unknown: true });
827+
expect(tally).toEqual({
828+
green: 0,
829+
amber: 0,
830+
red: 0,
831+
unknown: true,
832+
loading: false,
833+
});
822834
});
823835
});
824836

showcase/shell-dashboard/src/components/feature-grid.test.tsx

Lines changed: 159 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { describe, it, expect } from "vitest";
1010
import { render } from "@testing-library/react";
1111
import { LiveIndicator, computeColumnTally, FeatureGrid } from "./feature-grid";
1212
import type { CellContext, CellRenderer } from "./feature-grid";
13+
import { OverlayColumnHeader } from "./overlay-column-header";
14+
import type { Overlay } from "@/lib/overlay-types";
1315
import { urlsFor } from "./cell-pieces";
1416
import { getIntegrations } from "@/lib/registry";
1517
import { starterIsSupported, STARTER_LEVELS } from "@/lib/live-status";
@@ -145,7 +147,13 @@ describe("computeColumnTally", () => {
145147
live.set("e2e:i1/f2", row("e2e:i1/f2", "e2e", "green"));
146148
const t = computeColumnTally(integration, features, live);
147149
// D6-ceiling: D3-only green with no D5/D6 → gray → not counted
148-
expect(t).toEqual({ green: 0, amber: 0, red: 0, unknown: false });
150+
expect(t).toEqual({
151+
green: 0,
152+
amber: 0,
153+
red: 0,
154+
unknown: false,
155+
loading: false,
156+
});
149157
});
150158

151159
it("red D3 → red chip, green D3 without D5/D6 → gray", () => {
@@ -156,15 +164,27 @@ describe("computeColumnTally", () => {
156164
live.set("e2e:i1/f2", row("e2e:i1/f2", "e2e", "green"));
157165
const t = computeColumnTally(integration, features, live);
158166
// f1 red (gate fail), f2 gray (no D5/D6)
159-
expect(t).toEqual({ green: 0, amber: 0, red: 1, unknown: false });
167+
expect(t).toEqual({
168+
green: 0,
169+
amber: 0,
170+
red: 1,
171+
unknown: false,
172+
loading: false,
173+
});
160174
});
161175

162176
it("health row alone does not contribute to tally", () => {
163177
const live: LiveStatusMap = new Map();
164178
live.set("health:i1", row("health:i1", "health", "red"));
165179
const t = computeColumnTally(integration, features, live);
166180
// No D3 rows → all cells gray → nothing counted
167-
expect(t).toEqual({ green: 0, amber: 0, red: 0, unknown: false });
181+
expect(t).toEqual({
182+
green: 0,
183+
amber: 0,
184+
red: 0,
185+
unknown: false,
186+
loading: false,
187+
});
168188
});
169189

170190
it("features without demos are gray (unwired), not counted", () => {
@@ -178,7 +198,13 @@ describe("computeColumnTally", () => {
178198
};
179199
const t = computeColumnTally(partialInt, features, live);
180200
// f1: wired + D3=green but no D5/D6 → gray; f2: unwired → gray
181-
expect(t).toEqual({ green: 0, amber: 0, red: 0, unknown: false });
201+
expect(t).toEqual({
202+
green: 0,
203+
amber: 0,
204+
red: 0,
205+
unknown: false,
206+
loading: false,
207+
});
182208
});
183209

184210
it("not_supported_features are gray, not counted", () => {
@@ -191,7 +217,13 @@ describe("computeColumnTally", () => {
191217
};
192218
const t = computeColumnTally(unsupportedInt, features, live);
193219
// f1: D3=green but no D5/D6 → gray; f2: unsupported → gray
194-
expect(t).toEqual({ green: 0, amber: 0, red: 0, unknown: false });
220+
expect(t).toEqual({
221+
green: 0,
222+
amber: 0,
223+
red: 0,
224+
unknown: false,
225+
loading: false,
226+
});
195227
});
196228

197229
it("returns unknown=true when connection is error", () => {
@@ -205,7 +237,49 @@ describe("computeColumnTally", () => {
205237

206238
it("returns zeros with unknown=false when no rows", () => {
207239
const t = computeColumnTally(integration, features, new Map());
208-
expect(t).toEqual({ green: 0, amber: 0, red: 0, unknown: false });
240+
expect(t).toEqual({
241+
green: 0,
242+
amber: 0,
243+
red: 0,
244+
unknown: false,
245+
loading: false,
246+
});
247+
});
248+
249+
// Regression: while the initial PocketBase fetch is still in flight the
250+
// live-status map is empty AND the connection is "connecting". Returning
251+
// authoritative ✓0 ~0 ✗0 in that window reads as "everything is at depth 0",
252+
// which is a lie — the data simply hasn't arrived yet. The header must show a
253+
// loading/unknown state instead, NEVER zeros, until the first rows land.
254+
it("returns loading=true (unknown, not authoritative zeros) while connecting with no rows", () => {
255+
const t = computeColumnTally(
256+
integration,
257+
features,
258+
new Map(),
259+
"connecting",
260+
);
261+
expect(t.loading).toBe(true);
262+
expect(t.unknown).toBe(true);
263+
expect(t.green).toBe(0);
264+
expect(t.amber).toBe(0);
265+
expect(t.red).toBe(0);
266+
});
267+
268+
it("does NOT treat connecting-with-rows as loading (data already arrived)", () => {
269+
const live: LiveStatusMap = new Map();
270+
live.set("e2e:i1/f1", row("e2e:i1/f1", "e2e", "red"));
271+
// A delta arrived during a transient reconnect: rows are present, so the
272+
// tally is authoritative even though the connection is mid-reconnect.
273+
const t = computeColumnTally(integration, features, live, "connecting");
274+
expect(t.loading).toBe(false);
275+
expect(t.unknown).toBe(false);
276+
expect(t.red).toBe(1);
277+
});
278+
279+
it("live with no rows is NOT loading — it is an authoritative empty result", () => {
280+
const t = computeColumnTally(integration, features, new Map(), "live");
281+
expect(t.loading).toBe(false);
282+
expect(t.unknown).toBe(false);
209283
});
210284

211285
it("amber chip when D5=green but D6 absent", () => {
@@ -236,7 +310,13 @@ describe("computeColumnTally", () => {
236310
);
237311
const t = computeColumnTally(mappedInt, mappedFeatures, mappedLive);
238312
// D3=green, D4=green, D5=green → amber (D6 not yet green)
239-
expect(t).toEqual({ green: 0, amber: 1, red: 0, unknown: false });
313+
expect(t).toEqual({
314+
green: 0,
315+
amber: 1,
316+
red: 0,
317+
unknown: false,
318+
loading: false,
319+
});
240320
});
241321
});
242322

@@ -340,3 +420,75 @@ describe("FeatureGrid — Starter row-group", () => {
340420
expect(cell.textContent).toContain("~");
341421
});
342422
});
423+
424+
/* ------------------------------------------------------------------ */
425+
/* OverlayColumnHeader — loading / offline rendering (spec §5.3, A5) */
426+
/* */
427+
/* The core §5.3 guarantee: during the initial-load window the header */
428+
/* must NOT render authoritative ✓0 ~0 ✗0 (which reads as "every cell */
429+
/* at depth 0" — a lie). It shows a "… loading" affordance while the */
430+
/* first signal is in flight, and "? offline" when the stream is down. */
431+
/* ------------------------------------------------------------------ */
432+
433+
describe("OverlayColumnHeader — loading / offline rendering (§5.3)", () => {
434+
const integration: Integration = {
435+
slug: "i1",
436+
name: "Integration One",
437+
category: "c",
438+
language: "ts",
439+
description: "",
440+
repo: "",
441+
backend_url: "https://x",
442+
deployed: true,
443+
features: ["f1"],
444+
demos: [{ id: "f1", name: "f1", description: "", tags: [] }],
445+
};
446+
447+
const HEALTH: Set<Overlay> = new Set<Overlay>(["health"]);
448+
449+
it("loading tally → '… loading' affordance, NOT authoritative zero counts", () => {
450+
const { getByText, queryByText } = render(
451+
<OverlayColumnHeader
452+
integration={integration}
453+
tally={{ green: 0, amber: 0, red: 0, unknown: true, loading: true }}
454+
overlays={HEALTH}
455+
/>,
456+
);
457+
// The loading affordance renders.
458+
expect(getByText(/loading/)).toBeDefined();
459+
// The authoritative zero glyphs must NOT render during loading.
460+
expect(queryByText(/\s*0/)).toBeNull(); // ✓ 0
461+
expect(queryByText(/\s*0/)).toBeNull(); // ✗ 0
462+
expect(queryByText(/^~\s*0$/)).toBeNull(); // ~ 0
463+
});
464+
465+
it("unknown (offline) tally → '? offline' affordance, NOT authoritative zero counts", () => {
466+
const { getByText, queryByText } = render(
467+
<OverlayColumnHeader
468+
integration={integration}
469+
tally={{ green: 0, amber: 0, red: 0, unknown: true, loading: false }}
470+
overlays={HEALTH}
471+
/>,
472+
);
473+
// The offline affordance renders.
474+
expect(getByText(/offline/)).toBeDefined();
475+
// The authoritative zero glyphs must NOT render while offline.
476+
expect(queryByText(/\s*0/)).toBeNull(); // ✓ 0
477+
expect(queryByText(/\s*0/)).toBeNull(); // ✗ 0
478+
expect(queryByText(/^~\s*0$/)).toBeNull(); // ~ 0
479+
});
480+
481+
it("authoritative (not loading, not unknown) tally → renders the count glyphs", () => {
482+
const { getByText, queryByText } = render(
483+
<OverlayColumnHeader
484+
integration={integration}
485+
tally={{ green: 2, amber: 1, red: 3, unknown: false, loading: false }}
486+
overlays={HEALTH}
487+
/>,
488+
);
489+
// Counts render; neither loading nor offline affordance is shown.
490+
expect(getByText(//)).toBeDefined(); // ✓
491+
expect(queryByText(/loading/)).toBeNull();
492+
expect(queryByText(/offline/)).toBeNull();
493+
});
494+
});

0 commit comments

Comments
 (0)