Skip to content

Commit 616a45b

Browse files
committed
fix(showcase): extend depth ladder to D5/D6 with emerald chips
Depth system was capped at D4 — D5/D6 PocketBase rows existed but the chip never advanced past D4. Extends deriveDepth() through D6 with proper short-circuit gating, adds isD5Green() for multi-key CATALOG_TO_D5_KEY resolution, and colors D5/D6 chips emerald to distinguish from D3/D4 blue.
1 parent b6298ba commit 616a45b

7 files changed

Lines changed: 167 additions & 26 deletions

File tree

showcase/shell-dashboard/src/components/__tests__/depth-chip.test.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
/**
2-
* Unit tests for DepthChip — renders correct text + class for D0-D4,
2+
* Unit tests for DepthChip — renders correct text + class for D0-D6,
33
* unshipped, and regression states.
44
*/
55
import { describe, it, expect } from "vitest";
66
import { render } from "@testing-library/react";
77
import { DepthChip } from "../depth-chip";
88

99
describe("DepthChip", () => {
10-
it.each([0, 1, 2, 3, 4])(
10+
it.each([0, 1, 2, 3, 4, 5, 6])(
1111
"renders D%i for depth=%i with wired status",
1212
(depth) => {
1313
const { getByTestId } = render(
14-
<DepthChip depth={depth as 0 | 1 | 2 | 3 | 4} status="wired" />,
14+
<DepthChip
15+
depth={depth as 0 | 1 | 2 | 3 | 4 | 5 | 6}
16+
status="wired"
17+
/>,
1518
);
1619
const chip = getByTestId("depth-chip");
1720
expect(chip.textContent).toBe(`D${depth}`);
@@ -48,6 +51,18 @@ describe("DepthChip", () => {
4851
expect(chip.className).toContain("accent");
4952
});
5053

54+
it("renders D5 with emerald background class", () => {
55+
const { getByTestId } = render(<DepthChip depth={5} status="wired" />);
56+
const chip = getByTestId("depth-chip");
57+
expect(chip.className).toContain("emerald");
58+
});
59+
60+
it("renders D6 with emerald background class", () => {
61+
const { getByTestId } = render(<DepthChip depth={6} status="wired" />);
62+
const chip = getByTestId("depth-chip");
63+
expect(chip.className).toContain("emerald");
64+
});
65+
5166
it("renders '--' for unshipped status with dashed border", () => {
5267
const { getByTestId } = render(<DepthChip depth={0} status="unshipped" />);
5368
const chip = getByTestId("depth-chip");

showcase/shell-dashboard/src/components/__tests__/depth-utils.test.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Unit tests for depth derivation utility.
3-
* Parameterized: all D0-D4 combos, short-circuit on red, unshipped returns D0.
3+
* Parameterized: all D0-D6 combos, short-circuit on red, unshipped returns D0.
44
*/
55
import { describe, it, expect } from "vitest";
66
import { deriveDepth } from "../depth-utils";
@@ -207,4 +207,96 @@ describe("deriveDepth", () => {
207207
expect(result.isRegression).toBe(true);
208208
});
209209

210+
it("returns D5 when D0-D4 green plus D5 green (via CATALOG_TO_D5_KEY)", () => {
211+
const c = cell("lgp", "agentic-chat");
212+
const live = mapOf([
213+
row("health:lgp", "health", "green"),
214+
row("agent:lgp", "agent", "green"),
215+
row("e2e:lgp/agentic-chat", "e2e", "green"),
216+
row("chat:lgp", "chat", "green"),
217+
row("d5:lgp/agentic-chat", "d5", "green"),
218+
]);
219+
const result = deriveDepth(c, live);
220+
expect(result.achieved).toBe(5);
221+
});
222+
223+
it("returns D4 when D5 row is red", () => {
224+
const c = cell("lgp", "agentic-chat");
225+
const live = mapOf([
226+
row("health:lgp", "health", "green"),
227+
row("agent:lgp", "agent", "green"),
228+
row("e2e:lgp/agentic-chat", "e2e", "green"),
229+
row("chat:lgp", "chat", "green"),
230+
row("d5:lgp/agentic-chat", "d5", "red"),
231+
]);
232+
const result = deriveDepth(c, live);
233+
expect(result.achieved).toBe(4);
234+
});
235+
236+
it("returns D4 when D5 row is missing", () => {
237+
const c = cell("lgp", "agentic-chat");
238+
const live = mapOf([
239+
row("health:lgp", "health", "green"),
240+
row("agent:lgp", "agent", "green"),
241+
row("e2e:lgp/agentic-chat", "e2e", "green"),
242+
row("chat:lgp", "chat", "green"),
243+
]);
244+
const result = deriveDepth(c, live);
245+
expect(result.achieved).toBe(4);
246+
});
247+
248+
it("returns D5 for multi-key D5 mapping (shared-state-read-write)", () => {
249+
const c = cell("lgp", "shared-state-read-write");
250+
const live = mapOf([
251+
row("health:lgp", "health", "green"),
252+
row("agent:lgp", "agent", "green"),
253+
row("e2e:lgp/shared-state-read-write", "e2e", "green"),
254+
row("tools:lgp", "tools", "green"),
255+
row("d5:lgp/shared-state-read", "d5", "green"),
256+
row("d5:lgp/shared-state-write", "d5", "green"),
257+
]);
258+
const result = deriveDepth(c, live);
259+
expect(result.achieved).toBe(5);
260+
});
261+
262+
it("returns D4 when one of multi-key D5 rows is red", () => {
263+
const c = cell("lgp", "shared-state-read-write");
264+
const live = mapOf([
265+
row("health:lgp", "health", "green"),
266+
row("agent:lgp", "agent", "green"),
267+
row("e2e:lgp/shared-state-read-write", "e2e", "green"),
268+
row("tools:lgp", "tools", "green"),
269+
row("d5:lgp/shared-state-read", "d5", "green"),
270+
row("d5:lgp/shared-state-write", "d5", "red"),
271+
]);
272+
const result = deriveDepth(c, live);
273+
expect(result.achieved).toBe(4);
274+
});
275+
276+
it("returns D6 when D0-D5 green plus D6 green", () => {
277+
const c = cell("lgp", "agentic-chat");
278+
const live = mapOf([
279+
row("health:lgp", "health", "green"),
280+
row("agent:lgp", "agent", "green"),
281+
row("e2e:lgp/agentic-chat", "e2e", "green"),
282+
row("chat:lgp", "chat", "green"),
283+
row("d5:lgp/agentic-chat", "d5", "green"),
284+
row("d6:lgp/agentic-chat", "d6", "green"),
285+
]);
286+
const result = deriveDepth(c, live);
287+
expect(result.achieved).toBe(6);
288+
});
289+
290+
it("returns D4 for feature with no D5 mapping", () => {
291+
const c = cell("lgp", "unknown-feature");
292+
const live = mapOf([
293+
row("health:lgp", "health", "green"),
294+
row("agent:lgp", "agent", "green"),
295+
row("e2e:lgp/unknown-feature", "e2e", "green"),
296+
row("chat:lgp", "chat", "green"),
297+
]);
298+
const result = deriveDepth(c, live);
299+
expect(result.achieved).toBe(4);
300+
});
301+
210302
});

showcase/shell-dashboard/src/components/__tests__/overlay-selector-integration.test.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -521,10 +521,8 @@ describe("Overlay selector integration — real UI components", () => {
521521
expect(getByTestId("depth-layer")).toBeInTheDocument();
522522
expect(getByTestId("depth-chip")).toBeInTheDocument();
523523

524-
// No health badges
525-
expect(queryByText("E2E")).not.toBeInTheDocument();
526-
expect(queryByText("D5")).not.toBeInTheDocument();
527-
expect(queryByText("D6")).not.toBeInTheDocument();
524+
// No health badges (health overlay not active)
525+
expect(queryByTestId("health-layer")).not.toBeInTheDocument();
528526

529527
// No docs
530528
expect(queryByText("docs-og")).not.toBeInTheDocument();
@@ -572,6 +570,6 @@ describe("Overlay selector integration — real UI components", () => {
572570
// health+agent+e2e+chat all green, deriveDepth should yield D4
573571
const depthAttr = chip.getAttribute("data-depth");
574572
expect(depthAttr).toBeTruthy();
575-
expect(chip.textContent).toMatch(/^D[0-4]$/);
573+
expect(chip.textContent).toMatch(/^D[0-6]$/);
576574
});
577575
});

showcase/shell-dashboard/src/components/depth-chip.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"use client";
22
/**
3-
* DepthChip — colored chip showing achieved depth D0-D4.
3+
* DepthChip — colored chip showing achieved depth D0-D6.
44
*
55
* Color mapping:
6+
* D5-D6 = emerald — deep multi-turn e2e + parity coverage
67
* D3-D4 = blue (accent) — meaningful e2e + interaction coverage
78
* D1-D2 = amber — basic health and agent checks
89
* D0 = gray — exists but no live probe data
@@ -11,7 +12,7 @@
1112
*/
1213

1314
export interface DepthChipProps {
14-
depth: 0 | 1 | 2 | 3 | 4;
15+
depth: 0 | 1 | 2 | 3 | 4 | 5 | 6;
1516
status: "wired" | "stub" | "unshipped";
1617
/** When true, chip renders in red regardless of depth. */
1718
regression?: boolean;
@@ -23,6 +24,9 @@ function depthColorClass(depth: number, regression?: boolean): string {
2324
return "bg-[var(--danger)] text-white";
2425
}
2526
switch (depth) {
27+
case 5:
28+
case 6:
29+
return "bg-emerald-600 text-white";
2630
case 3:
2731
case 4:
2832
return "bg-[var(--accent)] text-white";

showcase/shell-dashboard/src/components/depth-utils.ts

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
/**
2-
* Pure depth-derivation utility for the D0-D4 depth ladder.
2+
* Pure depth-derivation utility for the D0-D6 depth ladder.
33
*
4-
* Walks D0 through D4 checking PocketBase live-status rows:
4+
* Walks D0 through D6 checking PocketBase live-status rows:
55
* D0 = cell exists with status wired or stub (static, no PB)
66
* D1 = health:<slug> green (integration-scoped)
77
* D2 = agent:<slug> green (integration-scoped)
88
* D3 = e2e:<slug>/<featureId> green (per-cell)
99
* D4 = chat:<slug> OR tools:<slug> green (integration-scoped)
10+
* D5 = d5:<slug>/<d5FeatureType> green (per-cell, mapped via CATALOG_TO_D5_KEY)
11+
* D6 = d6:<slug>/<featureId> green (per-cell)
1012
*
1113
* Achieved depth = highest D where ALL lower depths are also green.
1214
* Short-circuits: if any level is not green, stop there.
1315
*/
14-
import { keyFor, type LiveStatusMap } from "@/lib/live-status";
16+
import { keyFor, CATALOG_TO_D5_KEY, type LiveStatusMap } from "@/lib/live-status";
1517

1618
/** Minimal catalog cell shape consumed by depth derivation. */
1719
export interface CatalogCell {
@@ -27,11 +29,11 @@ export interface CatalogCell {
2729
category_name: string | null;
2830
}
2931

30-
/** Achieved depth on the D0-D4 ladder. */
31-
export type AchievedDepth = 0 | 1 | 2 | 3 | 4;
32+
/** Achieved depth on the D0-D6 ladder. */
33+
export type AchievedDepth = 0 | 1 | 2 | 3 | 4 | 5 | 6;
3234

3335
export interface DepthResult {
34-
/** Highest contiguous depth achieved (0-4). */
36+
/** Highest contiguous depth achieved (0-6). */
3537
achieved: AchievedDepth;
3638
/** Whether achieved depth is below the historical high-water mark (max_depth). */
3739
isRegression: boolean;
@@ -42,11 +44,27 @@ function isGreen(live: LiveStatusMap, key: string): boolean {
4244
return row?.state === "green";
4345
}
4446

47+
/**
48+
* Check whether all D5 PB rows for a given (slug, catalogFeatureId) are green.
49+
* Returns false if the feature has no D5 mapping or any mapped row is missing/non-green.
50+
*/
51+
function isD5Green(
52+
live: LiveStatusMap,
53+
slug: string,
54+
featureId: string,
55+
): boolean {
56+
const d5Keys = CATALOG_TO_D5_KEY[featureId];
57+
if (!d5Keys || d5Keys.length === 0) {
58+
return isGreen(live, keyFor("d5", slug, featureId));
59+
}
60+
return d5Keys.every((d5Key) => isGreen(live, keyFor("d5", slug, d5Key)));
61+
}
62+
4563
/**
4664
* Derive the achieved depth for a single catalog cell.
4765
*
4866
* The walk is contiguous: if D1 is not green, achieved = D0 regardless
49-
* of D2/D3/D4 status (short-circuit).
67+
* of D2/D3/D4/D5/D6 status (short-circuit).
5068
*/
5169
export function deriveDepth(
5270
cell: CatalogCell,
@@ -73,7 +91,7 @@ export function deriveDepth(
7391
achieved = 2;
7492

7593
// D3: e2e:<slug>/<featureId> green (per-cell)
76-
// Guard: skip D3 if feature is null (no per-cell e2e to evaluate).
94+
// Guard: skip D3+ if feature is null (no per-cell e2e to evaluate).
7795
if (cell.feature === null) {
7896
return { achieved, isRegression: achieved < cell.max_depth };
7997
}
@@ -85,8 +103,20 @@ export function deriveDepth(
85103
// D4: chat:<slug> OR tools:<slug> green (integration-scoped)
86104
const chatGreen = isGreen(live, keyFor("chat", cell.integration));
87105
const toolsGreen = isGreen(live, keyFor("tools", cell.integration));
88-
if (chatGreen || toolsGreen) {
89-
achieved = 4;
106+
if (!(chatGreen || toolsGreen)) {
107+
return { achieved, isRegression: achieved < cell.max_depth };
108+
}
109+
achieved = 4;
110+
111+
// D5: d5:<slug>/<d5FeatureType> green (per-cell, mapped via CATALOG_TO_D5_KEY)
112+
if (!isD5Green(live, cell.integration, cell.feature)) {
113+
return { achieved, isRegression: achieved < cell.max_depth };
114+
}
115+
achieved = 5;
116+
117+
// D6: d6:<slug>/<featureId> green (per-cell)
118+
if (isGreen(live, keyFor("d6", cell.integration, cell.feature))) {
119+
achieved = 6;
90120
}
91121

92122
return { achieved, isRegression: achieved < cell.max_depth };

showcase/shell-dashboard/src/components/ref-depth-column.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { DepthChip } from "@/components/depth-chip";
1212
export function RefDepthHeader() {
1313
return (
1414
<th
15-
className="sticky left-[260px] top-0 z-30 px-3 py-3 text-left border-b border-r-2 border-r-[#c4b5fd] border-l border-[var(--border)] font-normal"
15+
className="sticky left-[260px] top-0 z-30 px-1.5 py-1.5 text-left border-b border-r-2 border-r-[#c4b5fd] border-l border-[var(--border)] font-normal"
1616
style={{ backgroundColor: "#f5f0ff" }}
1717
>
1818
<div className="text-[9px] font-semibold uppercase tracking-wider text-[#7c3aed] leading-tight">
@@ -29,7 +29,7 @@ export function RefDepthHeader() {
2929
/* ------------------------------------------------------------------ */
3030

3131
export interface RefDepthCellProps {
32-
depth: 0 | 1 | 2 | 3 | 4;
32+
depth: 0 | 1 | 2 | 3 | 4 | 5 | 6;
3333
status: "wired" | "stub" | "unshipped";
3434
/** When true, chip renders in red regardless of depth. */
3535
regression?: boolean;
@@ -38,7 +38,7 @@ export interface RefDepthCellProps {
3838
export function RefDepthCell({ depth, status, regression }: RefDepthCellProps) {
3939
return (
4040
<td
41-
className="sticky left-[260px] z-10 px-3 py-2 border-r-2 border-r-[#c4b5fd] border-l border-[var(--border)] align-top"
41+
className="sticky left-[260px] z-10 px-1.5 py-1 border-r-2 border-r-[#c4b5fd] border-l border-[var(--border)] align-top"
4242
style={{ backgroundColor: "#f5f0ff" }}
4343
>
4444
<DepthChip depth={depth} status={status} regression={regression} />

showcase/shell-dashboard/src/lib/live-status.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function keyFor(
104104
*
105105
* Mirrors `REGISTRY_TO_D5` in `harness/src/probes/helpers/d5-feature-mapping.ts`.
106106
*/
107-
const CATALOG_TO_D5_KEY: Readonly<Record<string, readonly string[]>> = {
107+
export const CATALOG_TO_D5_KEY: Readonly<Record<string, readonly string[]>> = {
108108
"agentic-chat": ["agentic-chat"],
109109
"tool-rendering": ["tool-rendering"],
110110
"tool-rendering-default-catchall": ["tool-rendering"],
@@ -127,7 +127,9 @@ function resolveD5Row(
127127
featureId: string,
128128
): StatusRow | null {
129129
const d5Keys = CATALOG_TO_D5_KEY[featureId];
130-
if (!d5Keys) return null;
130+
if (!d5Keys) {
131+
return live.get(keyFor("d5", slug, featureId)) ?? null;
132+
}
131133
let worst: StatusRow | null = null;
132134
for (const d5Key of d5Keys) {
133135
const row = live.get(keyFor("d5", slug, d5Key)) ?? null;

0 commit comments

Comments
 (0)