commit c92b3e2e5df83dfa6dd829a10e3b038766228b68
parent 111fd13e8ec810c3ae1455b4a632bde9de285994
Author: Brian Graham <brian@buildingbetterteams.de>
Date: Sat, 4 Apr 2026 09:53:18 +0200
Fix process is not defined error, split types for client safety
Split data.ts into types.ts (client-safe, no Node imports) and
data.ts (server-only, uses fs/path/process). All React components
now import from types.ts, preventing the "process is not defined"
hydration error on the client.
Also fixed Efficiency Frontier tooltip to show readable config keys
instead of raw cell_id string.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
16 files changed, 135 insertions(+), 123 deletions(-)
diff --git a/dashboard/src/components/BumpChart.tsx b/dashboard/src/components/BumpChart.tsx
@@ -9,8 +9,8 @@ import {
ResponsiveContainer,
ReferenceDot,
} from "recharts";
-import type { Run } from "../lib/data";
-import { AXIS_NAMES, type AxisName } from "../lib/data";
+import type { Run } from "../lib/types";
+import { AXIS_NAMES, type AxisName } from "../lib/types";
interface BumpChartProps {
runs: Run[];
diff --git a/dashboard/src/components/Charts.tsx b/dashboard/src/components/Charts.tsx
@@ -8,7 +8,7 @@ import {
ResponsiveContainer,
Legend,
} from "recharts";
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
interface ChartsProps {
runs: Run[];
diff --git a/dashboard/src/components/ConfigTreemap.tsx b/dashboard/src/components/ConfigTreemap.tsx
@@ -1,7 +1,7 @@
import React, { useState, useCallback } from "react";
import { Treemap, ResponsiveContainer, Tooltip } from "recharts";
import type { TreemapNode } from "recharts/types/chart/Treemap";
-import type { Run, AxisName } from "../lib/data";
+import type { Run, AxisName } from "../lib/types";
interface ConfigTreemapProps {
runs: Run[];
diff --git a/dashboard/src/components/CorrelationMatrix.tsx b/dashboard/src/components/CorrelationMatrix.tsx
@@ -1,4 +1,4 @@
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
interface CorrelationMatrixProps {
runs: Run[];
diff --git a/dashboard/src/components/EfficiencyFrontier.tsx b/dashboard/src/components/EfficiencyFrontier.tsx
@@ -8,7 +8,7 @@ import {
Tooltip,
ResponsiveContainer,
} from "recharts";
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
interface EfficiencyFrontierProps {
runs: Run[];
@@ -190,7 +190,7 @@ function CustomTooltip({
color: getModelColor(point.model),
}}
>
- {point.cell_id}
+ {point.cell_id.split("_").filter(s => s.includes("=")).map(s => s.replace("=", ": ")).join(" ")}
</div>
<div style={{ marginBottom: "6px" }}>
<span style={{ color: "var(--text-muted)" }}>score: </span>
diff --git a/dashboard/src/components/Filters.tsx b/dashboard/src/components/Filters.tsx
@@ -1,5 +1,5 @@
import { useState } from "react";
-import type { AxisName } from "../lib/data";
+import type { AxisName } from "../lib/types";
interface FiltersProps {
axisValues: Record<AxisName, string[]>;
diff --git a/dashboard/src/components/Grid.tsx b/dashboard/src/components/Grid.tsx
@@ -1,5 +1,5 @@
import { useState, useMemo } from "react";
-import type { Run, AxisName } from "../lib/data";
+import type { Run, AxisName } from "../lib/types";
import Filters from "./Filters";
interface GridProps {
diff --git a/dashboard/src/components/HeatmapMatrix.tsx b/dashboard/src/components/HeatmapMatrix.tsx
@@ -1,6 +1,6 @@
import { useState, useMemo } from "react";
-import type { Run, AxisName } from "../lib/data";
-import { AXIS_NAMES } from "../lib/data";
+import type { Run, AxisName } from "../lib/types";
+import { AXIS_NAMES } from "../lib/types";
interface HeatmapMatrixProps {
runs: Run[];
diff --git a/dashboard/src/components/Insights.tsx b/dashboard/src/components/Insights.tsx
@@ -1,5 +1,5 @@
import { useState, useMemo } from "react";
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
import { computeMainEffects, computeInteraction } from "../lib/analysis";
import TornadoChart from "./TornadoChart";
import Heatmap from "./Heatmap";
diff --git a/dashboard/src/components/RadarComparison.tsx b/dashboard/src/components/RadarComparison.tsx
@@ -8,7 +8,7 @@ import {
ResponsiveContainer,
Tooltip,
} from "recharts";
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
interface RadarComparisonProps {
runs: Run[];
diff --git a/dashboard/src/components/RunDetail.tsx b/dashboard/src/components/RunDetail.tsx
@@ -1,4 +1,4 @@
-import type { Run, AxisName } from "../lib/data";
+import type { Run, AxisName } from "../lib/types";
import TranscriptViewer from "./TranscriptViewer";
const REPO_URL = "https://git.statagroup.com/research/loop-benchmarking";
diff --git a/dashboard/src/components/ScatterPlot.tsx b/dashboard/src/components/ScatterPlot.tsx
@@ -8,7 +8,7 @@ import {
ResponsiveContainer,
Legend,
} from "recharts";
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
interface ScatterPlotProps {
runs: Run[];
diff --git a/dashboard/src/components/Surprises.tsx b/dashboard/src/components/Surprises.tsx
@@ -1,4 +1,4 @@
-import type { Run } from "../lib/data";
+import type { Run } from "../lib/types";
interface SurprisesProps {
runs: Run[];
diff --git a/dashboard/src/lib/analysis.ts b/dashboard/src/lib/analysis.ts
@@ -1,4 +1,5 @@
-import type { Run, AxisName, AXIS_NAMES } from "./data";
+import type { Run, AxisName } from "./types";
+import { AXIS_NAMES } from "./types";
export interface EffectEntry {
value: string;
diff --git a/dashboard/src/lib/data.ts b/dashboard/src/lib/data.ts
@@ -1,77 +1,19 @@
+/**
+ * Data loading module. Server-side only (uses Node.js fs).
+ * Types are re-exported from ./types.ts which is client-safe.
+ */
import fs from "node:fs";
import path from "node:path";
-import { fileURLToPath } from "node:url";
// Use process.cwd() which in Astro build is the dashboard/ directory.
-// Go up one level to the project root, then into results/.
const RESULTS_DIR = path.resolve(process.cwd(), "../results");
-export interface RunMeta {
- run_id: string;
- cell_id: string;
- task: string;
- model: string;
- effort: string;
- prompt_style: string;
- language: string;
- human_language: string;
- tool_read: string;
- tool_write: string;
- tool_edit: string;
- tool_glob: string;
- tool_grep: string;
- linter: string;
- playwright: string;
- context_file: string;
- sub_agents: string;
- web_search: string;
- max_budget: string;
- max_budget_usd: number;
- run_number: number;
- wall_time_seconds?: number;
- exit_code?: number;
- started_at?: string;
- completed_at?: string;
-}
+// Re-export types so existing imports from "./data" still work
+export type { RunMeta, EvalResults, ClaudeOutput, Run, AxisName } from "./types";
+export { AXIS_NAMES } from "./types";
-export interface EvalResults {
- structural?: {
- pass: boolean;
- score: number;
- checks: Array<{ name: string; pass: boolean; detail: string }>;
- };
- functional?: {
- pass: boolean;
- score: number;
- total?: number;
- passed?: number;
- failed?: number;
- };
- quality?: {
- pass?: boolean;
- score: number;
- [key: string]: unknown;
- };
- score: number | null;
-}
-
-export interface ClaudeOutput {
- result?: string;
- total_cost_usd?: number;
- num_turns?: number;
- duration_ms?: number;
- usage?: {
- input_tokens?: number;
- output_tokens?: number;
- };
-}
-
-export interface Run {
- meta: RunMeta;
- eval_results: EvalResults | null;
- claude_output: ClaudeOutput | null;
- has_transcript: boolean;
-}
+import type { RunMeta, EvalResults, ClaudeOutput, Run, AxisName } from "./types";
+import { AXIS_NAMES } from "./types";
export function loadAllRuns(): Run[] {
const runsDir = path.join(RESULTS_DIR, "runs");
@@ -129,45 +71,6 @@ export function loadTranscript(runId: string): string[] {
.filter((line) => line.trim());
}
-export type AxisName = keyof Pick<
- RunMeta,
- | "model"
- | "effort"
- | "prompt_style"
- | "language"
- | "human_language"
- | "tool_read"
- | "tool_write"
- | "tool_edit"
- | "tool_glob"
- | "tool_grep"
- | "linter"
- | "playwright"
- | "context_file"
- | "sub_agents"
- | "web_search"
- | "max_budget"
->;
-
-export const AXIS_NAMES: AxisName[] = [
- "model",
- "effort",
- "prompt_style",
- "language",
- "human_language",
- "tool_read",
- "tool_write",
- "tool_edit",
- "tool_glob",
- "tool_grep",
- "linter",
- "playwright",
- "context_file",
- "sub_agents",
- "web_search",
- "max_budget",
-];
-
export function getAxisValues(runs: Run[]): Record<AxisName, string[]> {
const values: Record<string, Set<string>> = {};
for (const axis of AXIS_NAMES) {
diff --git a/dashboard/src/lib/types.ts b/dashboard/src/lib/types.ts
@@ -0,0 +1,108 @@
+/** Shared types for the dashboard. Client-safe -- no Node.js imports. */
+
+export interface RunMeta {
+ run_id: string;
+ cell_id: string;
+ task: string;
+ model: string;
+ effort: string;
+ prompt_style: string;
+ language: string;
+ human_language: string;
+ tool_read: string;
+ tool_write: string;
+ tool_edit: string;
+ tool_glob: string;
+ tool_grep: string;
+ linter: string;
+ playwright: string;
+ context_file: string;
+ sub_agents: string;
+ web_search: string;
+ max_budget: string;
+ max_budget_usd: number;
+ run_number: number;
+ wall_time_seconds?: number;
+ exit_code?: number;
+ started_at?: string;
+ completed_at?: string;
+ claude_version?: string;
+}
+
+export interface EvalResults {
+ structural?: {
+ pass: boolean;
+ score: number;
+ checks: Array<{ name: string; pass: boolean; detail: string }>;
+ };
+ functional?: {
+ pass: boolean;
+ score: number;
+ total?: number;
+ passed?: number;
+ failed?: number;
+ };
+ quality?: {
+ pass?: boolean;
+ score: number;
+ [key: string]: unknown;
+ };
+ score: number | null;
+}
+
+export interface ClaudeOutput {
+ result?: string;
+ total_cost_usd?: number;
+ num_turns?: number;
+ duration_ms?: number;
+ usage?: {
+ input_tokens?: number;
+ output_tokens?: number;
+ };
+}
+
+export interface Run {
+ meta: RunMeta;
+ eval_results: EvalResults | null;
+ claude_output: ClaudeOutput | null;
+ has_transcript: boolean;
+}
+
+export type AxisName = keyof Pick<
+ RunMeta,
+ | "model"
+ | "effort"
+ | "prompt_style"
+ | "language"
+ | "human_language"
+ | "tool_read"
+ | "tool_write"
+ | "tool_edit"
+ | "tool_glob"
+ | "tool_grep"
+ | "linter"
+ | "playwright"
+ | "context_file"
+ | "sub_agents"
+ | "web_search"
+ | "max_budget"
+>;
+
+export const AXIS_NAMES: AxisName[] = [
+ "model",
+ "effort",
+ "prompt_style",
+ "language",
+ "human_language",
+ "tool_read",
+ "tool_write",
+ "tool_edit",
+ "tool_glob",
+ "tool_grep",
+ "linter",
+ "playwright",
+ "context_file",
+ "sub_agents",
+ "web_search",
+ "max_budget",
+];