evijit HF Staff Claude Opus 4.7 (1M context) commited on
Commit
5279156
·
1 Parent(s): 051fa16

Merge corpus dashboard into home as paper-aligned landing

Browse files

The home page now renders the four interpretive signals (paper §4.2.1)
as a compact 4-tile strip alongside corpus stats from the rollout
hierarchy (paper §3.2). The standalone /corpus page redirects to home
and the Corpus tab is removed from navigation. Reproducibility headline
is inverted (% documented rather than % with gaps) for impact while the
paper-aligned gap rate stays in the detail line.

About page is rewritten to mirror the paper's structure: six-level
rollout hierarchy, four interpretive signals, two reader modes, and
composition with EEE + Auto-BenchmarkCards.

CorpusDashboard now accepts an `embedded` prop so callers can host the
full drill-down without its self-contained masthead. Manifest type is
extended with the count fields the upstream pipeline already emits so
the home page reads counts directly instead of pulling model-cards-lite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

app/about/page.tsx CHANGED
@@ -1,5 +1,15 @@
1
  import Link from "next/link"
2
- import { ArrowRight, BookOpenText, Database, Scale, Search } from "lucide-react"
 
 
 
 
 
 
 
 
 
 
3
 
4
  import { Navigation } from "@/components/navigation"
5
  import { PageHeader } from "@/components/page-header"
@@ -13,148 +23,212 @@ export default function AboutPage() {
13
  <PageHeader
14
  eyebrow="About"
15
  title="About Eval Cards"
16
- description="Why public AI evaluation reporting needs shared infrastructure, and what this project builds toward it."
17
  size="wide"
18
  metaItems={[
19
- { label: "Modes", value: "Research + Policy" },
20
- { label: "Core", value: "Reporting + Schema + Platform" },
21
  ]}
22
  />
23
 
24
- <main className="mx-auto w-full max-w-[92rem] px-4 py-10 sm:px-6 lg:px-8">
25
- <div className="space-y-16">
 
26
  <section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
27
  <div className="space-y-3">
28
- <SectionLabel label="Why this exists" />
29
- <h2 className="max-w-sm text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-[2.2rem]">
30
- Evaluation scores travel faster than the context needed to interpret them.
31
  </h2>
32
  </div>
33
 
34
- <div className="grid gap-6 xl:grid-cols-[minmax(0,1.2fr)_minmax(260px,0.8fr)]">
35
- <div className="space-y-4 text-base leading-8 text-muted-foreground">
36
- <p>
37
- AI evaluations shape discussions about capability, safety, and deployment. Public reporting arrives through papers, leaderboards, blog posts, and framework outputs that rarely line up.
38
- </p>
39
- <p>
40
- Eval Cards treats reporting as infrastructure. Beyond collecting scores, it surfaces who reported a result, when it was run, what slice it covers, and whether the setup makes comparison reasonable.
41
- </p>
42
- </div>
43
-
44
- <div className="rounded-[1.75rem] border border-border/70 bg-muted/20 p-5">
45
- <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground">
46
- Reading stance
47
- </div>
48
- <div className="mt-4 space-y-4">
49
- <SignalLine icon={<Search className="h-4 w-4" />} label="Inspect provenance alongside rankings." tone="sky" />
50
- <SignalLine icon={<Scale className="h-4 w-4" />} label="Surface comparability caveats early." tone="amber" />
51
- <SignalLine icon={<BookOpenText className="h-4 w-4" />} label="Support research and policy use without duplicating the record." tone="emerald" />
52
- </div>
53
- </div>
54
  </div>
55
  </section>
56
 
57
- <section className="space-y-5 border-b border-border/60 pb-12">
58
- <div className="grid gap-4 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:items-start">
59
- <div className="space-y-3">
60
- <SectionLabel label="Core contribution" />
61
- <h2 className="text-3xl font-semibold tracking-[-0.03em] text-foreground">
62
- Three layers, one public interface.
63
- </h2>
64
- </div>
65
- <p className="max-w-3xl text-base leading-8 text-muted-foreground">
66
- A reporting framework, a standardized data layer, and an interface for exploring the evidence across models, evaluations, and benchmarks.
67
  </p>
68
  </div>
69
 
70
- <div className="grid gap-4 xl:grid-cols-3">
71
- <AboutPanel
72
- icon={<BookOpenText className="h-4 w-4" />}
73
- tone="amber"
74
- title="Reporting framework"
75
- body="A structured view of which evaluation details matter for interpretation, reproducibility, and public accountability."
76
  />
77
- <AboutPanel
78
- icon={<Database className="h-4 w-4" />}
79
- tone="sky"
80
- title="Standardized records"
81
- body="Evaluation outputs and benchmark metadata are shaped into a consistent layer that can be queried and compared across sources."
82
  />
83
- <AboutPanel
84
- icon={<Scale className="h-4 w-4" />}
85
- tone="emerald"
86
- title="Public reading interface"
87
- body="The interface keeps setup differences, provenance, and evidence gaps visible so interpretation does not detach from reporting conditions."
88
  />
89
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  </section>
91
 
92
- <section className="grid gap-10 border-b border-border/60 pb-12 lg:grid-cols-2">
93
- <div className="space-y-5">
94
- <SectionLabel label="What the platform supports" />
95
- <div className="space-y-4 text-base leading-8 text-muted-foreground">
96
- <p>
97
- Move from a model to its reported benchmarks, from an evaluation to the models that ran on it, and from a benchmark family down to its slices and metrics.
98
- </p>
99
- <p>
100
- Scores are useful only when paired with benchmark scope, evaluator identity, generation setup, and the places where reporting is incomplete.
101
- </p>
102
- </div>
103
  </div>
104
 
105
- <div className="space-y-5">
106
- <SectionLabel label="Who it is for" />
107
- <div className="grid gap-4 sm:grid-cols-2">
108
- <AudienceCard
109
- title="Researchers"
110
- body="Need reproducibility cues, benchmark decomposition, and apples-to-apples comparison support before drawing substantive conclusions."
111
- />
112
- <AudienceCard
113
- title="Policy readers"
114
- body="Need evaluator independence, benchmark purpose, source accountability, and clearly stated limitations before using reported results."
115
- />
116
- </div>
117
- <p className="text-sm leading-7 text-muted-foreground">
118
- Audience modes change emphasis, not the underlying record.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  </p>
120
  </div>
 
 
 
 
 
 
 
 
 
 
 
121
  </section>
122
 
123
- <section className="grid gap-10 lg:grid-cols-[minmax(0,1.12fr)_minmax(280px,0.72fr)]">
124
- <div className="space-y-5">
125
- <SectionLabel label="Current scope" />
126
- <div className="space-y-4 text-base leading-8 text-muted-foreground">
127
- <p>
128
- Read this version as a public beta. It tries to make structured evaluation reporting legible, while testing which fields, comparison flows, and abstractions hold up in practice.
129
- </p>
130
- <p>
131
- The larger goal: make AI evaluation evidence easier to inspect and harder to over-interpret.
132
- </p>
133
- </div>
134
  </div>
135
 
136
- <div className="space-y-4 rounded-[1.75rem] border border-border/70 bg-[linear-gradient(180deg,rgba(71,129,177,0.08),rgba(71,129,177,0.02))] p-5">
137
- <SectionLabel label="Next steps" />
138
- <div className="space-y-3 text-sm leading-7 text-muted-foreground">
139
- <p>The homepage is the shortest path into the data.</p>
140
- <p>The survey collects missing fields, confusing labels, and misleading comparisons.</p>
141
- </div>
142
- <div className="flex flex-wrap gap-3 pt-2">
143
- <Link href="/">
144
- <Button className="gap-2 rounded-full px-5">
145
- Back to homepage
146
- <ArrowRight className="h-4 w-4" />
147
- </Button>
148
- </Link>
149
- <Link href="/survey">
150
- <Button variant="outline" className="gap-2 rounded-full px-5">
151
- Leave feedback
152
- <ArrowRight className="h-4 w-4" />
153
- </Button>
154
- </Link>
155
- </div>
156
  </div>
157
  </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  </div>
159
  </main>
160
  </div>
@@ -169,83 +243,60 @@ function SectionLabel({ label }: { label: string }) {
169
  )
170
  }
171
 
172
- function AboutRow({
173
- title,
174
- body,
175
- }: {
176
- title: string
177
- body: string
178
- }) {
179
  return (
180
- <div className="border-t border-border/50 pt-5 first:border-t-0 first:pt-0">
181
- <div className="text-sm font-semibold text-foreground">{title}</div>
182
- <p className="mt-2 text-sm leading-7 text-muted-foreground">{body}</p>
183
- </div>
 
 
 
 
 
184
  )
185
  }
186
 
187
- function AboutPanel({
188
  icon,
189
- title,
 
190
  body,
191
- tone,
192
  }: {
193
  icon: React.ReactNode
194
- title: string
 
195
  body: string
196
- tone: "amber" | "emerald" | "sky"
197
  }) {
198
- const toneClasses = {
199
- amber: "bg-amber-50/70 dark:bg-amber-950/20",
200
- emerald: "bg-emerald-50/70 dark:bg-emerald-950/20",
201
- sky: "bg-sky-50/70 dark:bg-sky-950/20",
202
- }[tone]
203
-
204
  return (
205
- <div className={`rounded-[1.5rem] border border-border/70 p-5 ${toneClasses}`}>
206
- <div className="flex items-center gap-2 text-sm font-semibold text-foreground">
207
- <span className="rounded-full bg-background/80 p-2 text-muted-foreground">{icon}</span>
208
- {title}
209
  </div>
210
- <p className="mt-3 text-sm leading-7 text-muted-foreground">{body}</p>
 
211
  </div>
212
  )
213
  }
214
 
215
- function AudienceCard({
216
- title,
217
- body,
218
- }: {
219
- title: string
220
- body: string
221
- }) {
222
  return (
223
- <div className="rounded-[1.35rem] border border-border/70 bg-background/80 p-4">
224
  <div className="text-sm font-semibold text-foreground">{title}</div>
225
- <p className="mt-2 text-sm leading-7 text-muted-foreground">{body}</p>
226
  </div>
227
  )
228
  }
229
 
230
- function SignalLine({
231
- icon,
232
- label,
233
- tone,
234
- }: {
235
- icon: React.ReactNode
236
- label: string
237
- tone: "amber" | "emerald" | "sky"
238
- }) {
239
- const toneClasses = {
240
- amber: "bg-amber-50 text-amber-800 dark:bg-amber-950/30 dark:text-amber-300",
241
- emerald: "bg-emerald-50 text-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-300",
242
- sky: "bg-sky-50 text-sky-800 dark:bg-sky-950/30 dark:text-sky-300",
243
- }[tone]
244
-
245
  return (
246
- <div className="flex items-start gap-3">
247
- <div className={`rounded-full p-2 ${toneClasses}`}>{icon}</div>
248
- <div className="text-sm leading-6 text-muted-foreground">{label}</div>
 
 
 
249
  </div>
250
  )
251
  }
 
1
  import Link from "next/link"
2
+ import {
3
+ ArrowRight,
4
+ BookOpenText,
5
+ ClipboardCheck,
6
+ Database,
7
+ GitCompareArrows,
8
+ Layers,
9
+ Scale,
10
+ ShieldCheck,
11
+ Users,
12
+ } from "lucide-react"
13
 
14
  import { Navigation } from "@/components/navigation"
15
  import { PageHeader } from "@/components/page-header"
 
23
  <PageHeader
24
  eyebrow="About"
25
  title="About Eval Cards"
26
+ description="An interpretative integration layer for AI evaluation reporting. Eval Cards composes existing evaluation infrastructure into a single reading surface, organizes reported scores through a six-level rollout hierarchy, and surfaces four interpretive signals that help readers decide whether to trust a reported result."
27
  size="wide"
28
  metaItems={[
29
+ { label: "Reader modes", value: "Research + Policy" },
30
+ { label: "Signal layers", value: "Reproducibility · Completeness · Provenance · Comparability" },
31
  ]}
32
  />
33
 
34
+ <main className="mx-auto w-full max-w-[88rem] px-4 py-12 sm:px-6 lg:px-8">
35
+ <div className="space-y-14">
36
+ {/* The framing problem */}
37
  <section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
38
  <div className="space-y-3">
39
+ <SectionLabel label="The problem" />
40
+ <h2 className="max-w-sm text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
41
+ Evaluation results travel faster than the context needed to interpret them.
42
  </h2>
43
  </div>
44
 
45
+ <div className="space-y-4 text-base leading-7 text-muted-foreground">
46
+ <p>
47
+ AI evaluations are produced at scale and reported through papers, leaderboards, model cards, and
48
+ framework outputs that rarely share conventions. The cost of fragmentation is paid at the point
49
+ of interpretation: provenance gets lost, configurations differ silently, and incomplete reporting
50
+ is treated indistinguishably from complete reporting.
51
+ </p>
52
+ <p>
53
+ Eval Cards composes three existing sources into a single record — Auto-BenchmarkCards for
54
+ benchmark metadata, the EEE schema for evaluation run data, and voluntary developer disclosure
55
+ for fields neither captures then exposes interpretive signals over the result.
56
+ </p>
 
 
 
 
 
 
 
 
57
  </div>
58
  </section>
59
 
60
+ {/* Six-level rollout hierarchy */}
61
+ <section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
62
+ <div className="space-y-3">
63
+ <SectionLabel label="Rollout hierarchy" />
64
+ <h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
65
+ Six levels from a benchmark family down to a single metric.
66
+ </h2>
67
+ <p className="max-w-md text-sm leading-7 text-muted-foreground">
68
+ Reports are not flat (model, benchmark, score) triples. Every score resolves to an explicit
69
+ path through this hierarchy, which makes drill-down and apples-to-apples comparison possible.
70
  </p>
71
  </div>
72
 
73
+ <ol className="space-y-3">
74
+ <RollupRow
75
+ index="1"
76
+ title="Family"
77
+ body="A related collection sharing a common object of measurement or methodological lineage (e.g., the SWE-bench family, the MMLU family)."
 
78
  />
79
+ <RollupRow
80
+ index="2"
81
+ title="Suite"
82
+ body="A named composite reporting unit that aggregates multiple benchmarks under a unified presentation (e.g., Open LLM Leaderboard v2, HELM Instruct)."
 
83
  />
84
+ <RollupRow
85
+ index="3"
86
+ title="Single benchmark"
87
+ body="An individual evaluation with a defined dataset and scoring method (e.g., GSM8K, IFEval, MMLU-Pro)."
 
88
  />
89
+ <RollupRow
90
+ index="4"
91
+ title="Split"
92
+ body="A named partition of a benchmark's item set (e.g., test, validation, or a language-specific subset)."
93
+ />
94
+ <RollupRow
95
+ index="5"
96
+ title="Subtask"
97
+ body="A capability- or construct-level decomposition within a benchmark (e.g., algebra within MATH)."
98
+ />
99
+ <RollupRow
100
+ index="6"
101
+ title="Metric"
102
+ body="The specific scoring rule attached to a result (e.g., pass@1, accuracy, F1)."
103
+ />
104
+ </ol>
105
  </section>
106
 
107
+ {/* Four interpretive signals */}
108
+ <section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
109
+ <div className="space-y-3">
110
+ <SectionLabel label="Interpretive signals" />
111
+ <h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
112
+ Four signals that help readers decide whether to trust a reported score.
113
+ </h2>
114
+ <p className="max-w-md text-sm leading-7 text-muted-foreground">
115
+ Each signal is computed at the record level and rolled up at the corpus level. Per-record
116
+ instances appear on every model and benchmark page; rollups appear on the home page.
117
+ </p>
118
  </div>
119
 
120
+ <div className="grid gap-3 sm:grid-cols-2">
121
+ <SignalCard
122
+ icon={<ShieldCheck className="h-4 w-4" />}
123
+ label="Reproducibility"
124
+ question="Can this be reproduced?"
125
+ body="Flags reported scores whose generation and prompting setup is too underspecified to re-run independently."
126
+ />
127
+ <SignalCard
128
+ icon={<ClipboardCheck className="h-4 w-4" />}
129
+ label="Reporting completeness"
130
+ question="Is the documentation complete?"
131
+ body="Measures the fraction of operationalized framework fields that are populated for a benchmark."
132
+ />
133
+ <SignalCard
134
+ icon={<Users className="h-4 w-4" />}
135
+ label="Provenance"
136
+ question="Who reported this, and what risks does the benchmark carry?"
137
+ body="Distinguishes first-party, third-party, and collaborative reporting and surfaces multi-source coverage."
138
+ />
139
+ <SignalCard
140
+ icon={<GitCompareArrows className="h-4 w-4" />}
141
+ label="Comparability"
142
+ question="Are these scores really comparable?"
143
+ body="Flags score divergence across setup variants and across reporting parties for the same (model, benchmark, metric)."
144
+ />
145
+ </div>
146
+ </section>
147
+
148
+ {/* Two reader modes */}
149
+ <section className="grid gap-8 border-b border-border/60 pb-12 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
150
+ <div className="space-y-3">
151
+ <SectionLabel label="Reader modes" />
152
+ <h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
153
+ Two renderings of the same record.
154
+ </h2>
155
+ <p className="max-w-md text-sm leading-7 text-muted-foreground">
156
+ The two modes operate on the same underlying data. The difference is which fields are
157
+ surfaced, which are compressed, and which framing is used.
158
  </p>
159
  </div>
160
+
161
+ <div className="grid gap-3 sm:grid-cols-2">
162
+ <ModeCard
163
+ title="Research mode"
164
+ body="Foregrounds methodology and configuration. Reproducibility gaps list specific missing fields. Comparability surfaces the underlying setup differences. Default for technical evaluators, benchmark developers, and meta-analysis."
165
+ />
166
+ <ModeCard
167
+ title="Policy mode"
168
+ body="Foregrounds accountability and plain-language interpretation. The same signals render with narrative caveats and compressed metric configuration. Default for regulators, standards bodies, and non-technical readers."
169
+ />
170
+ </div>
171
  </section>
172
 
173
+ {/* Composition + sources */}
174
+ <section className="grid gap-8 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
175
+ <div className="space-y-3">
176
+ <SectionLabel label="Composition" />
177
+ <h2 className="text-2xl font-semibold tracking-[-0.03em] text-foreground sm:text-[1.75rem]">
178
+ Eval Cards does not collect evaluation data directly.
179
+ </h2>
180
+ <p className="max-w-md text-sm leading-7 text-muted-foreground">
181
+ It composes three existing sources and applies a canonicalization layer that maps surface
182
+ identifiers to nodes in the rollout hierarchy.
183
+ </p>
184
  </div>
185
 
186
+ <div className="space-y-3">
187
+ <SourceRow
188
+ icon={<BookOpenText className="h-4 w-4" />}
189
+ title="Auto-BenchmarkCards"
190
+ body="Benchmark metadata: design intent, scoring methodology, data licensing, risk annotations."
191
+ />
192
+ <SourceRow
193
+ icon={<Database className="h-4 w-4" />}
194
+ title="EEE (evaluation run data)"
195
+ body="Generation configuration, evaluator relationships, and per-instance results from major frameworks."
196
+ />
197
+ <SourceRow
198
+ icon={<Scale className="h-4 w-4" />}
199
+ title="Voluntary disclosure"
200
+ body="Two reserved fields accept developer-supplied disclosure: preregistration links and lifecycle status."
201
+ />
 
 
 
 
202
  </div>
203
  </section>
204
+
205
+ {/* CTAs */}
206
+ <section className="flex flex-wrap gap-3 border-t border-border/60 pt-10">
207
+ <Link href="/">
208
+ <Button className="gap-2 rounded-full px-5">
209
+ Back to home
210
+ <ArrowRight className="h-4 w-4" />
211
+ </Button>
212
+ </Link>
213
+ <Link href="/models">
214
+ <Button variant="outline" className="gap-2 rounded-full px-5">
215
+ Browse models
216
+ <ArrowRight className="h-4 w-4" />
217
+ </Button>
218
+ </Link>
219
+ <Link href="/evals">
220
+ <Button variant="outline" className="gap-2 rounded-full px-5">
221
+ Browse evaluations
222
+ <ArrowRight className="h-4 w-4" />
223
+ </Button>
224
+ </Link>
225
+ <Link href="/survey">
226
+ <Button variant="ghost" className="gap-2 rounded-full px-4 text-foreground">
227
+ Leave feedback
228
+ <ArrowRight className="h-4 w-4" />
229
+ </Button>
230
+ </Link>
231
+ </section>
232
  </div>
233
  </main>
234
  </div>
 
243
  )
244
  }
245
 
246
+ function RollupRow({ index, title, body }: { index: string; title: string; body: string }) {
 
 
 
 
 
 
247
  return (
248
+ <li className="flex gap-4 rounded-2xl border border-border/70 bg-card p-4">
249
+ <div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted/70 text-xs font-semibold text-muted-foreground">
250
+ {index}
251
+ </div>
252
+ <div className="min-w-0">
253
+ <div className="text-sm font-semibold text-foreground">{title}</div>
254
+ <p className="mt-1 text-sm leading-6 text-muted-foreground">{body}</p>
255
+ </div>
256
+ </li>
257
  )
258
  }
259
 
260
+ function SignalCard({
261
  icon,
262
+ label,
263
+ question,
264
  body,
 
265
  }: {
266
  icon: React.ReactNode
267
+ label: string
268
+ question: string
269
  body: string
 
270
  }) {
 
 
 
 
 
 
271
  return (
272
+ <div className="flex flex-col gap-2 rounded-2xl border border-border/70 bg-card p-4">
273
+ <div className="flex items-center gap-2">
274
+ <span className="rounded-full bg-muted/60 p-1.5 text-muted-foreground">{icon}</span>
275
+ <div className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">{label}</div>
276
  </div>
277
+ <div className="text-sm font-semibold text-foreground">{question}</div>
278
+ <p className="text-sm leading-6 text-muted-foreground">{body}</p>
279
  </div>
280
  )
281
  }
282
 
283
+ function ModeCard({ title, body }: { title: string; body: string }) {
 
 
 
 
 
 
284
  return (
285
+ <div className="rounded-2xl border border-border/70 bg-card p-4">
286
  <div className="text-sm font-semibold text-foreground">{title}</div>
287
+ <p className="mt-2 text-sm leading-6 text-muted-foreground">{body}</p>
288
  </div>
289
  )
290
  }
291
 
292
+ function SourceRow({ icon, title, body }: { icon: React.ReactNode; title: string; body: string }) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  return (
294
+ <div className="flex items-start gap-3 rounded-2xl border border-border/70 bg-card p-4">
295
+ <span className="rounded-full bg-muted/60 p-1.5 text-muted-foreground">{icon}</span>
296
+ <div className="min-w-0">
297
+ <div className="text-sm font-semibold text-foreground">{title}</div>
298
+ <p className="mt-1 text-sm leading-6 text-muted-foreground">{body}</p>
299
+ </div>
300
  </div>
301
  )
302
  }
app/corpus/page.tsx CHANGED
@@ -1,36 +1,7 @@
1
- import { CorpusDashboard } from "@/components/signals/corpus-dashboard"
2
- import { Navigation } from "@/components/navigation"
3
- import { fetchCorpusAggregates, fetchEvalListLite } from "@/lib/hf-data"
4
 
5
- export default async function CorpusPage() {
6
- const [aggregates, evalList] = await Promise.all([
7
- fetchCorpusAggregates(),
8
- fetchEvalListLite().catch(() => ({ evals: [] })),
9
- ])
10
-
11
- const completenessScores = evalList.evals
12
- .map((entry) => entry.evalcards?.annotations?.reporting_completeness?.completeness_score)
13
- .filter((score): score is number => typeof score === "number" && Number.isFinite(score))
14
-
15
- return (
16
- <div className="min-h-screen bg-background">
17
- <Navigation />
18
- <main className="container mx-auto px-4 py-8">
19
- {aggregates ? (
20
- <CorpusDashboard aggregates={aggregates} completenessScores={completenessScores} />
21
- ) : (
22
- <section className="rounded-2xl border border-dashed border-border/70 bg-card p-8 text-center">
23
- <div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
24
- Interpretive signals
25
- </div>
26
- <h1 className="mt-2 text-2xl font-semibold tracking-tight">Corpus aggregates are not available yet</h1>
27
- <p className="mx-auto mt-3 max-w-2xl text-sm leading-6 text-muted-foreground">
28
- The frontend is ready for `corpus-aggregates.json`, but this cached backend snapshot does not include it yet.
29
- Once the dataset ships the file, this page will render reproducibility, completeness, provenance, and comparability rollups.
30
- </p>
31
- </section>
32
- )}
33
- </main>
34
- </div>
35
- )
36
  }
 
1
+ import { redirect } from "next/navigation"
 
 
2
 
3
+ // Corpus signals were folded into the home page. Preserve any external links
4
+ // to /corpus by sending readers to the same content at the root.
5
+ export default function CorpusRedirect() {
6
+ redirect("/")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  }
app/page.tsx CHANGED
@@ -1,256 +1,213 @@
1
  import Link from "next/link"
 
 
2
  import { Button } from "@/components/ui/button"
3
- import { ArrowRight, BookOpenText, Database, MessageSquare, Scale } from "lucide-react"
4
  import { HomeModeLabel } from "@/components/home-mode-label"
5
  import { Navigation } from "@/components/navigation"
6
- import { getBackendManifestData, getEvalListLiteData, getModelCardsLite } from "@/lib/data-backend"
 
 
 
 
 
 
7
 
8
  function formatGeneratedAt(value: string | null | undefined) {
9
- if (!value) return "Unknown"
10
-
11
  try {
12
- return new Date(value).toLocaleString("en-US", {
13
- month: "short",
 
 
 
14
  day: "numeric",
15
- hour: "numeric",
16
- minute: "2-digit",
17
  })
18
  } catch {
19
  return value
20
  }
21
  }
22
 
 
 
 
 
 
23
  export default async function HomePage() {
24
- const [models, evalList, manifest] = await Promise.all([
25
- getModelCardsLite(),
26
- getEvalListLiteData(),
27
- getBackendManifestData(),
 
28
  ])
29
 
30
- const evalSummaries = evalList.evals
31
- const developerCount = new Set(models.map((entry) => entry.developer).filter(Boolean)).size
32
- const avgBenchmarksPerModel =
33
- models.length > 0
34
- ? models.reduce((sum, entry) => sum + entry.benchmarks_count, 0) / models.length
35
- : 0
36
- const totalReportedResults = models.reduce((sum, entry) => sum + entry.evaluations_count, 0)
 
 
 
 
 
37
 
38
  return (
39
  <div className="min-h-screen bg-background">
40
  <Navigation />
41
- <main className="relative overflow-hidden">
42
- <div className="absolute inset-x-0 top-0 -z-10 h-[24rem] bg-[radial-gradient(circle_at_top,rgba(196,167,96,0.12),transparent_60%)]" />
43
- <div className="absolute inset-y-0 right-0 -z-10 hidden w-[36rem] bg-[radial-gradient(circle_at_center,rgba(71,129,177,0.08),transparent_66%)] lg:block" />
44
 
45
- <section className="mx-auto flex min-h-[calc(100vh-4.25rem)] w-full max-w-[92rem] flex-col gap-10 px-4 pb-10 pt-12 sm:px-6 sm:pt-16 lg:px-8 lg:pb-12">
46
- <div className="flex flex-col gap-8 xl:flex-row xl:items-stretch">
47
- <div className="flex flex-1 flex-col gap-6">
48
- <div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-[11px] font-semibold uppercase tracking-[0.24em] text-muted-foreground">
49
- <span className="rounded-full border border-border/70 bg-background px-3 py-1">Beta preview</span>
50
- <HomeModeLabel />
51
- {manifest ? <span>Updated {formatGeneratedAt(manifest.generated_at)}</span> : null}
52
- </div>
 
 
 
 
53
 
54
- <div className="space-y-4">
55
- <h1 className="max-w-4xl text-balance text-4xl font-semibold tracking-[-0.05em] text-foreground sm:text-5xl lg:text-[4.25rem] lg:leading-[1.02]">
56
- Public reporting for AI evaluations
57
- </h1>
58
- <p className="max-w-2xl text-base leading-7 text-muted-foreground">
59
- Explore which models and benchmarks are reported, where coverage is sparse, and what details are missing.
60
- </p>
61
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- <div className="flex flex-wrap gap-3 pt-1">
64
- <Link href="/models">
65
- <Button size="lg" className="gap-2 rounded-full px-6">
66
- Browse models
67
- <ArrowRight className="h-4 w-4" />
68
- </Button>
69
- </Link>
70
- <Link href="/evals">
71
- <Button size="lg" variant="outline" className="gap-2 rounded-full px-6">
72
- Browse evaluations
73
- <ArrowRight className="h-4 w-4" />
74
- </Button>
75
- </Link>
76
- <Link href="/survey">
77
- <Button size="lg" variant="ghost" className="gap-2 rounded-full px-4 text-foreground">
78
- Leave feedback
79
- <ArrowRight className="h-4 w-4" />
80
- </Button>
81
- </Link>
82
  </div>
 
 
 
 
 
 
 
 
83
  </div>
84
 
85
- <aside className="flex min-w-[300px] flex-col rounded-[2rem] border border-border/70 bg-card/80 p-5 shadow-[0_18px_50px_rgba(15,23,42,0.06)] xl:max-w-[22rem]">
86
- <div className="grid grid-cols-2 gap-3">
87
- <QuietStat label="Models" value={models.length.toString()} detail="Tracked" tone="amber" />
88
- <QuietStat label="Evaluations" value={evalSummaries.length.toString()} detail="Benchmarks" tone="sky" />
89
- <QuietStat label="Developers" value={developerCount.toString()} detail="Organizations" tone="emerald" />
90
- <QuietStat
91
- label="Results"
92
- value={totalReportedResults.toLocaleString()}
93
- detail={`${avgBenchmarksPerModel.toFixed(1)} avg per model`}
94
- tone="slate"
95
- />
96
- </div>
97
- </aside>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  </div>
 
99
 
100
- <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
101
- <SignalCard
102
- icon={<Database className="h-4 w-4" />}
103
- title="Reported benchmarks"
104
- body="Which benchmarks, settings, and sources are documented."
105
- tone="sky"
106
- />
107
- <SignalCard
108
- icon={<Scale className="h-4 w-4" />}
109
- title="Comparison context"
110
- body="Evaluator relationships and config gaps attached to each record."
111
- tone="amber"
112
- />
113
- <SignalCard
114
- icon={<BookOpenText className="h-4 w-4" />}
115
- title="Reader modes"
116
- body="Research and policy views highlight different fields."
117
- tone="emerald"
118
- />
119
  </div>
120
 
121
- <div className="grid grid-cols-1 gap-4 border-t border-border/60 pt-8 sm:grid-cols-2 lg:grid-cols-4">
122
- <RoutePanel
123
- href="/models"
124
- icon={<Database className="h-4 w-4" />}
125
- title="Model records"
126
- body="Reported benchmarks per model, plus what's missing."
127
- />
128
- <RoutePanel
129
- href="/evals"
130
- icon={<BookOpenText className="h-4 w-4" />}
131
- title="Benchmark records"
132
- body="How a benchmark is reported across models."
133
- />
134
- <RoutePanel
135
- href="/about"
136
- icon={<Scale className="h-4 w-4" />}
137
- title="Project notes"
138
- body="Why this reporting format exists."
139
- />
140
- <Link
141
- href="/survey"
142
- className="group flex h-full flex-col rounded-[1.5rem] border border-border/70 bg-muted/20 p-5 transition-colors hover:bg-muted/30"
143
- >
144
- <div className="flex items-center gap-2 text-sm font-semibold text-foreground">
145
- <span className="rounded-full bg-rose-50 p-2 text-rose-600 dark:bg-rose-950/30">
146
- <MessageSquare className="h-4 w-4" />
147
- </span>
148
- Feedback
149
- </div>
150
- <p className="mt-3 text-sm leading-7 text-muted-foreground">
151
- Flag missing fields or unclear labels.
152
  </p>
153
- <div className="mt-auto pt-4 inline-flex items-center gap-2 text-sm font-semibold text-foreground">
154
- Open survey
155
- <ArrowRight className="h-4 w-4" />
156
- </div>
157
- </Link>
158
- </div>
159
  </section>
160
  </main>
161
  </div>
162
  )
163
  }
164
 
165
- function QuietStat({
166
  label,
167
  value,
168
  detail,
169
- tone,
170
  }: {
171
  label: string
172
  value: string
173
  detail: string
174
- tone: "amber" | "emerald" | "rose" | "sky" | "slate"
175
  }) {
176
- const toneClasses = {
177
- amber: "border-amber-200/80 bg-amber-50/70 dark:border-amber-900/50 dark:bg-amber-950/20",
178
- emerald: "border-emerald-200/80 bg-emerald-50/70 dark:border-emerald-900/50 dark:bg-emerald-950/20",
179
- rose: "border-rose-200/80 bg-rose-50/70 dark:border-rose-900/50 dark:bg-rose-950/20",
180
- sky: "border-sky-200/80 bg-sky-50/70 dark:border-sky-900/50 dark:bg-sky-950/20",
181
- slate: "border-border/70 bg-muted/25",
182
- }[tone]
183
-
184
  return (
185
- <div className={`flex h-full flex-col rounded-[1.35rem] border p-4 ${toneClasses}`}>
186
- <div className="text-[10px] font-semibold uppercase tracking-[0.18em] text-muted-foreground">{label}</div>
187
- <div className="mt-2 text-2xl font-semibold tracking-tight text-foreground tabular-nums">{value}</div>
188
- <p className="text-sm leading-6 text-muted-foreground">{detail}</p>
189
- </div>
190
- )
191
- }
192
-
193
- function SignalCard({
194
- icon,
195
- title,
196
- body,
197
- tone,
198
- }: {
199
- icon: React.ReactNode
200
- title: string
201
- body: string
202
- tone: "amber" | "emerald" | "sky"
203
- }) {
204
- const toneClasses = {
205
- amber: "bg-amber-50 text-amber-800 dark:bg-amber-950/30 dark:text-amber-300",
206
- emerald: "bg-emerald-50 text-emerald-800 dark:bg-emerald-950/30 dark:text-emerald-300",
207
- sky: "bg-sky-50 text-sky-800 dark:bg-sky-950/30 dark:text-sky-300",
208
- }[tone]
209
-
210
- const surfaceClasses = {
211
- amber: "border-amber-200/70 bg-amber-50/40 dark:border-amber-900/40 dark:bg-amber-950/15",
212
- emerald: "border-emerald-200/70 bg-emerald-50/40 dark:border-emerald-900/40 dark:bg-emerald-950/15",
213
- sky: "border-sky-200/70 bg-sky-50/40 dark:border-sky-900/40 dark:bg-sky-950/15",
214
- }[tone]
215
-
216
- return (
217
- <div className={`flex h-full flex-col gap-3 rounded-[1.5rem] border p-5 ${surfaceClasses}`}>
218
- <div className="flex items-center gap-2 text-sm font-semibold text-foreground">
219
- <span className={`rounded-full p-2 ${toneClasses}`}>{icon}</span>
220
- {title}
221
  </div>
222
- <p className="text-sm leading-6 text-muted-foreground">{body}</p>
223
- </div>
224
- )
225
- }
226
-
227
- function RoutePanel({
228
- href,
229
- icon,
230
- title,
231
- body,
232
- }: {
233
- href: string
234
- icon: React.ReactNode
235
- title: string
236
- body: string
237
- }) {
238
- return (
239
- <Link
240
- href={href}
241
- className="group flex h-full flex-col rounded-[1.5rem] border border-border/70 bg-background/80 p-5 transition-colors hover:bg-muted/20"
242
- >
243
- <div className="flex items-center gap-2 text-sm font-semibold text-foreground">
244
- <span className="rounded-full bg-muted/60 p-2 text-muted-foreground transition-colors group-hover:text-foreground">
245
- {icon}
246
- </span>
247
- {title}
248
  </div>
249
- <p className="mt-3 text-sm leading-7 text-muted-foreground">{body}</p>
250
- <div className="mt-auto pt-4 inline-flex items-center gap-2 text-sm font-semibold text-foreground">
251
- Open
252
- <ArrowRight className="h-4 w-4" />
253
- </div>
254
- </Link>
255
  )
256
  }
 
1
  import Link from "next/link"
2
+ import { ArrowRight } from "lucide-react"
3
+
4
  import { Button } from "@/components/ui/button"
5
+ import { Badge } from "@/components/ui/badge"
6
  import { HomeModeLabel } from "@/components/home-mode-label"
7
  import { Navigation } from "@/components/navigation"
8
+ import { CorpusSignalsStrip } from "@/components/signals/corpus-signals-strip"
9
+ import { getDeveloperList } from "@/lib/data-backend"
10
+ import {
11
+ fetchBackendManifest,
12
+ fetchCorpusAggregates,
13
+ fetchEvalHierarchy,
14
+ } from "@/lib/hf-data"
15
 
16
  function formatGeneratedAt(value: string | null | undefined) {
17
+ if (!value) return null
 
18
  try {
19
+ const date = new Date(value)
20
+ if (Number.isNaN(date.getTime())) return value
21
+ return date.toLocaleDateString("en-US", {
22
+ year: "numeric",
23
+ month: "long",
24
  day: "numeric",
 
 
25
  })
26
  } catch {
27
  return value
28
  }
29
  }
30
 
31
+ function formatNumber(value: number | undefined | null): string {
32
+ if (value == null || !Number.isFinite(value)) return "—"
33
+ return value.toLocaleString("en-US")
34
+ }
35
+
36
  export default async function HomePage() {
37
+ const [aggregates, manifest, hierarchy, developers] = await Promise.all([
38
+ fetchCorpusAggregates(),
39
+ fetchBackendManifest(),
40
+ fetchEvalHierarchy(),
41
+ getDeveloperList().catch(() => []),
42
  ])
43
 
44
+ const stats = hierarchy.stats
45
+ const familyCount = stats?.family_count ?? hierarchy.families.length
46
+ const compositeCount = stats?.composite_count ?? 0
47
+ const singleBenchmarkCount = stats?.single_benchmark_count ?? 0
48
+ const standaloneBenchmarkCount = stats?.standalone_benchmark_count ?? 0
49
+ const benchmarkLeafCount = singleBenchmarkCount + standaloneBenchmarkCount
50
+ const sliceCount = stats?.slice_count ?? 0
51
+ const metricCount = stats?.metric_count ?? 0
52
+ const tripleCount = stats?.metric_rows_scanned ?? 0
53
+ const modelCount = manifest?.model_count ?? 0
54
+ const developerCount = developers.length
55
+ const generatedAt = formatGeneratedAt(manifest?.generated_at)
56
 
57
  return (
58
  <div className="min-h-screen bg-background">
59
  <Navigation />
 
 
 
60
 
61
+ <main className="mx-auto w-full max-w-[88rem] px-4 py-12 sm:px-6 lg:px-8 lg:py-16">
62
+ {/* Masthead */}
63
+ <section className="border-b border-border/60 pb-10">
64
+ <div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-[11px] font-semibold uppercase tracking-[0.24em] text-muted-foreground">
65
+ <HomeModeLabel />
66
+ {generatedAt && <span>Snapshot · {generatedAt}</span>}
67
+ {aggregates && (
68
+ <Badge variant="outline" className="font-normal tracking-normal">
69
+ Signals v{aggregates.signal_version}
70
+ </Badge>
71
+ )}
72
+ </div>
73
 
74
+ <h1 className="mt-5 max-w-4xl text-balance text-4xl font-semibold tracking-[-0.04em] text-foreground sm:text-5xl lg:text-[3.75rem] lg:leading-[1.05]">
75
+ Eval Cards
76
+ </h1>
77
+ <p className="mt-3 max-w-3xl text-base leading-7 text-muted-foreground sm:text-lg sm:leading-8">
78
+ An interpretative integration layer for AI evaluation reporting. Eval Cards composes
79
+ benchmark metadata, evaluation run data, and provenance into a single reading surface,
80
+ and surfaces four interpretive signals — reproducibility, reporting completeness,
81
+ provenance, and comparability — over a public corpus of reported scores.
82
+ </p>
83
+
84
+ <div className="mt-6 flex flex-wrap gap-3">
85
+ <Link href="/models">
86
+ <Button size="lg" className="gap-2 rounded-full px-6">
87
+ Browse models
88
+ <ArrowRight className="h-4 w-4" />
89
+ </Button>
90
+ </Link>
91
+ <Link href="/evals">
92
+ <Button size="lg" variant="outline" className="gap-2 rounded-full px-6">
93
+ Browse evaluations
94
+ <ArrowRight className="h-4 w-4" />
95
+ </Button>
96
+ </Link>
97
+ <Link href="/about">
98
+ <Button size="lg" variant="ghost" className="gap-2 rounded-full px-4 text-foreground">
99
+ About this project
100
+ <ArrowRight className="h-4 w-4" />
101
+ </Button>
102
+ </Link>
103
+ </div>
104
+ </section>
105
 
106
+ {/* Corpus stats paper §5.1 */}
107
+ <section className="border-b border-border/60 py-10">
108
+ <div className="grid gap-6 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
109
+ <div>
110
+ <div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
111
+ Corpus
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  </div>
113
+ <h2 className="mt-2 text-2xl font-semibold tracking-tight text-foreground sm:text-[1.75rem]">
114
+ What this snapshot covers
115
+ </h2>
116
+ <p className="mt-3 max-w-md text-sm leading-7 text-muted-foreground">
117
+ Reports are organized through a six-level rollout hierarchy
118
+ (family → suite → single benchmark → split → subtask → metric) so that
119
+ aggregate claims can be drilled down to the evidence supporting them.
120
+ </p>
121
  </div>
122
 
123
+ <div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
124
+ <CorpusStat
125
+ label="Models"
126
+ value={formatNumber(modelCount)}
127
+ detail="Tracked across reporting sources"
128
+ />
129
+ <CorpusStat
130
+ label="Reported results"
131
+ value={formatNumber(tripleCount)}
132
+ detail="(model, benchmark, metric) triples"
133
+ />
134
+ <CorpusStat
135
+ label="Reporting organizations"
136
+ value={formatNumber(developerCount)}
137
+ detail="Developers and third-party evaluators"
138
+ />
139
+ <CorpusStat
140
+ label="Benchmark families"
141
+ value={formatNumber(familyCount)}
142
+ detail="Top of the rollout hierarchy"
143
+ />
144
+ <CorpusStat
145
+ label="Suites"
146
+ value={formatNumber(compositeCount)}
147
+ detail="Composite reporting units"
148
+ />
149
+ <CorpusStat
150
+ label="Single benchmarks"
151
+ value={formatNumber(benchmarkLeafCount)}
152
+ detail={`${formatNumber(sliceCount)} slices · ${formatNumber(metricCount)} metrics`}
153
+ />
154
+ </div>
155
  </div>
156
+ </section>
157
 
158
+ {/* Four interpretive signals */}
159
+ <section className="py-10">
160
+ <div className="mb-6 grid gap-4 lg:grid-cols-[minmax(220px,0.34fr)_minmax(0,1fr)] lg:gap-12">
161
+ <div>
162
+ <div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
163
+ Interpretive signals
164
+ </div>
165
+ <h2 className="mt-2 text-2xl font-semibold tracking-tight text-foreground sm:text-[1.75rem]">
166
+ Reproducibility, completeness, provenance, comparability
167
+ </h2>
168
+ </div>
169
+ <p className="max-w-3xl text-sm leading-7 text-muted-foreground">
170
+ Each signal answers a question a reader brings to a reported score. Per-record
171
+ instances appear on every model and benchmark page. Corpus-level rollups below
172
+ show how reporting practice looks across the public record as a whole.
173
+ </p>
 
 
 
174
  </div>
175
 
176
+ {aggregates ? (
177
+ <CorpusSignalsStrip aggregates={aggregates} />
178
+ ) : (
179
+ <div className="rounded-2xl border border-dashed border-border/70 bg-muted/10 p-8 text-center">
180
+ <h3 className="text-lg font-semibold tracking-tight">Corpus aggregates unavailable</h3>
181
+ <p className="mx-auto mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
182
+ The current backend snapshot does not include <code>corpus-aggregates.json</code>.
183
+ When it does, this section will render the four corpus-level rollups.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </p>
185
+ </div>
186
+ )}
 
 
 
 
187
  </section>
188
  </main>
189
  </div>
190
  )
191
  }
192
 
193
+ function CorpusStat({
194
  label,
195
  value,
196
  detail,
 
197
  }: {
198
  label: string
199
  value: string
200
  detail: string
 
201
  }) {
 
 
 
 
 
 
 
 
202
  return (
203
+ <div className="rounded-2xl border border-border/70 bg-card p-4">
204
+ <div className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
205
+ {label}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  </div>
207
+ <div className="mt-2 text-2xl font-semibold tracking-tight text-foreground tabular-nums sm:text-[1.625rem]">
208
+ {value}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  </div>
210
+ <p className="mt-1 text-xs leading-5 text-muted-foreground">{detail}</p>
211
+ </div>
 
 
 
 
212
  )
213
  }
components/navigation.tsx CHANGED
@@ -38,12 +38,6 @@ export function Navigation() {
38
  icon: BarChart3,
39
  isActive: pathname === "/evals" || pathname?.startsWith("/evals/")
40
  },
41
- {
42
- href: "/corpus",
43
- label: "Corpus",
44
- icon: FlaskConical,
45
- isActive: pathname === "/corpus" || pathname?.startsWith("/corpus/")
46
- },
47
  {
48
  href: "/survey",
49
  label: "Survey",
 
38
  icon: BarChart3,
39
  isActive: pathname === "/evals" || pathname?.startsWith("/evals/")
40
  },
 
 
 
 
 
 
41
  {
42
  href: "/survey",
43
  label: "Survey",
components/signals/corpus-dashboard.tsx CHANGED
@@ -32,9 +32,16 @@ const SOURCE_COLORS: Record<string, string> = {
32
  export function CorpusDashboard({
33
  aggregates,
34
  completenessScores,
 
35
  }: {
36
  aggregates: CorpusAggregates
37
  completenessScores: number[]
 
 
 
 
 
 
38
  }) {
39
  const { mode } = useAudienceMode()
40
  const [view, setView] = useState<"overall" | "category">("overall")
@@ -56,44 +63,72 @@ export function CorpusDashboard({
56
 
57
  return (
58
  <div className="space-y-6">
59
- <section className="rounded-2xl border border-border/70 bg-card p-5 shadow-sm">
60
- <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
61
- <div>
62
- <div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
63
- Interpretive signals
64
- </div>
65
- <h1 className="mt-2 text-3xl font-semibold tracking-tight">Corpus Dashboard</h1>
66
- <p className="mt-2 max-w-3xl text-sm leading-6 text-muted-foreground">
67
- Corpus-level rollups for reproducibility, documentation completeness, source provenance, and comparability.
68
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
- <div className="flex flex-wrap items-center gap-2">
72
- <Badge variant="outline">Signals v{aggregates.signal_version}</Badge>
73
- <Badge variant="outline">Generated {formatGeneratedDate(aggregates.generated_at)}</Badge>
74
- <div className="inline-flex rounded-full border bg-muted/20 p-1">
75
- <Button
76
- type="button"
77
- size="sm"
78
- variant={view === "overall" ? "default" : "ghost"}
79
- className="h-8 rounded-full"
80
- onClick={() => setView("overall")}
81
- >
82
- Overall
83
- </Button>
84
- <Button
85
- type="button"
86
- size="sm"
87
- variant={view === "category" ? "default" : "ghost"}
88
- className="h-8 rounded-full"
89
- onClick={() => setView("category")}
90
- >
91
- By category
92
- </Button>
 
93
  </div>
94
  </div>
95
- </div>
96
- </section>
97
 
98
  {view === "overall" ? (
99
  <div className="grid gap-6">
 
32
  export function CorpusDashboard({
33
  aggregates,
34
  completenessScores,
35
+ embedded = false,
36
  }: {
37
  aggregates: CorpusAggregates
38
  completenessScores: number[]
39
+ /**
40
+ * When true, hides the dashboard's own masthead card. The host page is
41
+ * expected to provide its own title and generated-date badge. The view
42
+ * toggle is preserved inline above the signal sections.
43
+ */
44
+ embedded?: boolean
45
  }) {
46
  const { mode } = useAudienceMode()
47
  const [view, setView] = useState<"overall" | "category">("overall")
 
63
 
64
  return (
65
  <div className="space-y-6">
66
+ {embedded ? (
67
+ <div className="flex flex-wrap items-center justify-between gap-3">
68
+ <div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
69
+ Interpretive signals
70
+ </div>
71
+ <div className="inline-flex rounded-full border bg-muted/20 p-1">
72
+ <Button
73
+ type="button"
74
+ size="sm"
75
+ variant={view === "overall" ? "default" : "ghost"}
76
+ className="h-8 rounded-full"
77
+ onClick={() => setView("overall")}
78
+ >
79
+ Overall
80
+ </Button>
81
+ <Button
82
+ type="button"
83
+ size="sm"
84
+ variant={view === "category" ? "default" : "ghost"}
85
+ className="h-8 rounded-full"
86
+ onClick={() => setView("category")}
87
+ >
88
+ By category
89
+ </Button>
90
  </div>
91
+ </div>
92
+ ) : (
93
+ <section className="rounded-2xl border border-border/70 bg-card p-5 shadow-sm">
94
+ <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
95
+ <div>
96
+ <div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
97
+ Interpretive signals
98
+ </div>
99
+ <h1 className="mt-2 text-3xl font-semibold tracking-tight">Corpus Dashboard</h1>
100
+ <p className="mt-2 max-w-3xl text-sm leading-6 text-muted-foreground">
101
+ Corpus-level rollups for reproducibility, reporting completeness, provenance, and comparability.
102
+ </p>
103
+ </div>
104
 
105
+ <div className="flex flex-wrap items-center gap-2">
106
+ <Badge variant="outline">Signals v{aggregates.signal_version}</Badge>
107
+ <Badge variant="outline">Generated {formatGeneratedDate(aggregates.generated_at)}</Badge>
108
+ <div className="inline-flex rounded-full border bg-muted/20 p-1">
109
+ <Button
110
+ type="button"
111
+ size="sm"
112
+ variant={view === "overall" ? "default" : "ghost"}
113
+ className="h-8 rounded-full"
114
+ onClick={() => setView("overall")}
115
+ >
116
+ Overall
117
+ </Button>
118
+ <Button
119
+ type="button"
120
+ size="sm"
121
+ variant={view === "category" ? "default" : "ghost"}
122
+ className="h-8 rounded-full"
123
+ onClick={() => setView("category")}
124
+ >
125
+ By category
126
+ </Button>
127
+ </div>
128
  </div>
129
  </div>
130
+ </section>
131
+ )}
132
 
133
  {view === "overall" ? (
134
  <div className="grid gap-6">
components/signals/corpus-signals-strip.tsx ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { CorpusAggregates } from "@/lib/backend-artifacts"
2
+
3
+ /**
4
+ * Compact 4-tile rollup of the four interpretive signals (paper §4.2.1).
5
+ * Designed for the home page: one headline per signal, one caption, one
6
+ * sub-detail line. Drill-down lives on per-record pages.
7
+ */
8
+ export function CorpusSignalsStrip({
9
+ aggregates,
10
+ }: {
11
+ aggregates: CorpusAggregates
12
+ }) {
13
+ const repro = aggregates.reproducibility.overall
14
+ const comp = aggregates.completeness.overall
15
+ const prov = aggregates.provenance.overall
16
+ const cmp = aggregates.comparability.overall
17
+
18
+ // Invert the gap rate so the headline reads as "how many are documented",
19
+ // not "how many have gaps". The inverse is more impactful and lower-is-worse,
20
+ // which matches the user's intuition. The gap rate stays in the detail line.
21
+ const reproDocumentedRate =
22
+ repro.reproducibility_gap_rate == null
23
+ ? null
24
+ : Math.max(0, 1 - repro.reproducibility_gap_rate)
25
+ const reproDocumentedRateText = formatPct(reproDocumentedRate)
26
+ const reproGapRateText = formatPct(repro.reproducibility_gap_rate)
27
+ const reproDetail = topMissingFields(repro.per_field_missingness, 2)
28
+
29
+ const compMean = formatPct(comp.completeness_score_mean)
30
+ const compMedian = formatPct(comp.completeness_score_median)
31
+
32
+ const provMulti = formatPct(prov.multi_source_rate)
33
+ const totalReports = prov.total_triples
34
+ const tpShare = totalReports > 0 ? prov.source_type_distribution.third_party / totalReports : 0
35
+ const fpShare = totalReports > 0 ? prov.source_type_distribution.first_party / totalReports : 0
36
+
37
+ const cmpRate = formatPct(cmp.variant_divergence_rate)
38
+ const crossPartyAvailable = cmp.cross_party_eligible_groups > 0
39
+
40
+ return (
41
+ <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
42
+ <SignalTile
43
+ label="Reproducibility"
44
+ value={reproDocumentedRateText}
45
+ caption="of reported scores have a complete setup recorded — the rest cannot be independently re-run"
46
+ detail={
47
+ reproDetail
48
+ ? `${reproGapRateText} have at least one undocumented field. Most often missing: ${reproDetail}`
49
+ : `${reproGapRateText} have at least one undocumented field`
50
+ }
51
+ />
52
+
53
+ <SignalTile
54
+ label="Reporting completeness"
55
+ value={compMean}
56
+ caption={`mean across ${comp.total_benchmarks.toLocaleString()} benchmarks (median ${compMedian})`}
57
+ detail="Source-provenance fields populate fully; preregistration fields are unmet"
58
+ />
59
+
60
+ <SignalTile
61
+ label="Provenance"
62
+ value={provMulti}
63
+ caption="of (model, benchmark) groups have reports from more than one party"
64
+ detail={`${formatPct(tpShare)} third-party, ${formatPct(fpShare)} first-party of ${totalReports.toLocaleString()} results`}
65
+ />
66
+
67
+ <SignalTile
68
+ label="Comparability"
69
+ value={cmpRate}
70
+ caption={`of setup-eligible groups diverge across variants (${cmp.variant_divergent_groups.toLocaleString()} of ${cmp.variant_eligible_groups.toLocaleString()})`}
71
+ detail={
72
+ crossPartyAvailable
73
+ ? `Cross-party divergence: ${formatPct(cmp.cross_party_divergence_rate)}`
74
+ : "Cross-party divergence not yet computable — too few multi-org reports"
75
+ }
76
+ />
77
+ </div>
78
+ )
79
+ }
80
+
81
+ function SignalTile({
82
+ label,
83
+ value,
84
+ caption,
85
+ detail,
86
+ }: {
87
+ label: string
88
+ value: string
89
+ caption: string
90
+ detail: string
91
+ }) {
92
+ return (
93
+ <div className="flex flex-col gap-2 rounded-2xl border border-border/70 bg-card p-4">
94
+ <div className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
95
+ {label}
96
+ </div>
97
+ <div className="flex items-baseline gap-2">
98
+ <span className="text-3xl font-semibold tabular-nums tracking-tight text-foreground">
99
+ {value}
100
+ </span>
101
+ </div>
102
+ <p className="text-sm leading-snug text-muted-foreground">{caption}.</p>
103
+ <p className="mt-auto text-xs leading-snug text-muted-foreground/80">{detail}.</p>
104
+ </div>
105
+ )
106
+ }
107
+
108
+ function formatPct(value: number | null | undefined): string {
109
+ if (value == null || !Number.isFinite(value)) return "—"
110
+ if (value === 0) return "0%"
111
+ if (value > 0 && value < 0.01) return "<1%"
112
+ return `${Math.round(value * 100)}%`
113
+ }
114
+
115
+ const FIELD_LABELS: Record<string, string> = {
116
+ temperature: "temperature",
117
+ max_tokens: "max tokens",
118
+ top_p: "top-p",
119
+ prompt_template: "prompt template",
120
+ eval_plan: "eval plan",
121
+ eval_limits: "eval limits",
122
+ }
123
+
124
+ /**
125
+ * Returns a short comma-separated list of the top-N missing fields, ranked by
126
+ * missing count. We rank by count rather than rate so cross-denominator fields
127
+ * (agentic-only vs all-triples) don't get artificially boosted.
128
+ */
129
+ function topMissingFields(
130
+ perField: Record<
131
+ string,
132
+ { missing_count: number; missing_rate: number | null; denominator: string }
133
+ >,
134
+ n: number,
135
+ ): string {
136
+ const entries = Object.entries(perField)
137
+ .filter(([, v]) => v.missing_count > 0)
138
+ .sort((a, b) => b[1].missing_count - a[1].missing_count)
139
+ .slice(0, n)
140
+
141
+ if (entries.length === 0) return ""
142
+
143
+ return entries
144
+ .map(([key, v]) => `${FIELD_LABELS[key] ?? key.replace(/_/g, " ")} (${formatPct(v.missing_rate)})`)
145
+ .join(", ")
146
+ }
lib/backend-artifacts.ts CHANGED
@@ -2,6 +2,14 @@ export interface BackendManifest {
2
  generated_at: string
3
  config_version: number
4
  skipped_configs: string[]
 
 
 
 
 
 
 
 
5
  summary_artifacts?: {
6
  corpus_aggregates?: string
7
  [key: string]: string | undefined
 
2
  generated_at: string
3
  config_version: number
4
  skipped_configs: string[]
5
+ // Counts the upstream pipeline already records in manifest.json. The home
6
+ // page reads these directly so it does not have to load model-cards-lite
7
+ // (~20MB) just to display headline numbers.
8
+ model_count?: number
9
+ eval_count?: number
10
+ metric_eval_count?: number
11
+ source_config_count?: number
12
+ skipped_config_count?: number
13
  summary_artifacts?: {
14
  corpus_aggregates?: string
15
  [key: string]: string | undefined