niceeval 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/INDEX.md +55 -0
  2. package/dist/agents/types.d.ts +317 -0
  3. package/dist/context/types.d.ts +335 -0
  4. package/dist/i18n/core.d.ts +11 -0
  5. package/dist/i18n/en.d.ts +181 -0
  6. package/dist/i18n/index.d.ts +5 -0
  7. package/dist/i18n/zh-CN.d.ts +183 -0
  8. package/dist/o11y/execution-tree.d.ts +103 -0
  9. package/dist/o11y/otlp/parse.d.ts +3 -0
  10. package/dist/o11y/otlp/receiver.d.ts +21 -0
  11. package/dist/o11y/otlp/select.d.ts +22 -0
  12. package/dist/o11y/otlp/turn-otel.d.ts +43 -0
  13. package/dist/o11y/types.d.ts +190 -0
  14. package/dist/report/aggregate.d.ts +57 -0
  15. package/dist/report/aggregate.js +212 -0
  16. package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +1 -0
  17. package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -0
  18. package/dist/report/built-ins/index.d.ts +1 -0
  19. package/dist/report/built-ins/index.js +2 -0
  20. package/dist/report/components.d.ts +178 -0
  21. package/dist/report/components.js +132 -0
  22. package/dist/report/compute.d.ts +106 -0
  23. package/dist/report/compute.js +618 -0
  24. package/dist/report/flag.d.ts +6 -0
  25. package/dist/report/flag.js +8 -0
  26. package/dist/report/format.d.ts +32 -0
  27. package/dist/report/format.js +126 -0
  28. package/dist/report/index.d.ts +19 -0
  29. package/dist/report/index.js +30 -0
  30. package/dist/report/load.d.ts +12 -0
  31. package/dist/report/load.js +56 -0
  32. package/dist/report/locale.d.ts +83 -0
  33. package/dist/report/locale.js +160 -0
  34. package/dist/report/metrics.d.ts +21 -0
  35. package/dist/report/metrics.js +115 -0
  36. package/dist/report/primitives.d.ts +64 -0
  37. package/dist/report/primitives.js +113 -0
  38. package/dist/report/react/AttemptList.d.ts +23 -0
  39. package/dist/report/react/AttemptList.js +20 -0
  40. package/dist/report/react/DeltaTable.d.ts +8 -0
  41. package/dist/report/react/DeltaTable.js +24 -0
  42. package/dist/report/react/EvalList.d.ts +10 -0
  43. package/dist/report/react/EvalList.js +0 -0
  44. package/dist/report/react/ExperimentList.d.ts +10 -0
  45. package/dist/report/react/ExperimentList.js +15 -0
  46. package/dist/report/react/GroupSummary.d.ts +8 -0
  47. package/dist/report/react/GroupSummary.js +8 -0
  48. package/dist/report/react/MetricBars.d.ts +10 -0
  49. package/dist/report/react/MetricBars.js +48 -0
  50. package/dist/report/react/MetricLine.d.ts +9 -0
  51. package/dist/report/react/MetricLine.js +68 -0
  52. package/dist/report/react/MetricMatrix.d.ts +10 -0
  53. package/dist/report/react/MetricMatrix.js +23 -0
  54. package/dist/report/react/MetricScatter.d.ts +9 -0
  55. package/dist/report/react/MetricScatter.js +100 -0
  56. package/dist/report/react/MetricTable.d.ts +11 -0
  57. package/dist/report/react/MetricTable.js +26 -0
  58. package/dist/report/react/RunOverview.d.ts +8 -0
  59. package/dist/report/react/RunOverview.js +12 -0
  60. package/dist/report/react/Scoreboard.d.ts +8 -0
  61. package/dist/report/react/Scoreboard.js +26 -0
  62. package/dist/report/react/cell.d.ts +9 -0
  63. package/dist/report/react/cell.js +9 -0
  64. package/dist/report/react/chart-math.d.ts +14 -0
  65. package/dist/report/react/chart-math.js +68 -0
  66. package/dist/report/react/colors.d.ts +14 -0
  67. package/dist/report/react/colors.js +45 -0
  68. package/dist/report/react/fixtures.d.ts +19 -0
  69. package/dist/report/react/fixtures.js +393 -0
  70. package/dist/report/react/format.d.ts +3 -0
  71. package/dist/report/react/format.js +7 -0
  72. package/dist/report/react/index.d.ts +6 -0
  73. package/dist/report/react/index.js +14 -0
  74. package/dist/report/report.d.ts +24 -0
  75. package/dist/report/report.js +49 -0
  76. package/dist/report/text/faces.d.ts +29 -0
  77. package/dist/report/text/faces.js +503 -0
  78. package/dist/report/text/layout.d.ts +75 -0
  79. package/dist/report/text/layout.js +173 -0
  80. package/dist/report/text/plot.d.ts +37 -0
  81. package/dist/report/text/plot.js +81 -0
  82. package/dist/report/text/table.d.ts +8 -0
  83. package/dist/report/text/table.js +86 -0
  84. package/dist/report/tree.d.ts +99 -0
  85. package/dist/report/tree.js +228 -0
  86. package/dist/report/types.d.ts +420 -0
  87. package/dist/report/types.js +4 -0
  88. package/dist/report/web.d.ts +15 -0
  89. package/dist/report/web.js +37 -0
  90. package/dist/results/annotated-source.d.ts +61 -0
  91. package/dist/results/attempt-evidence.d.ts +69 -0
  92. package/dist/results/attempt-source.d.ts +15 -0
  93. package/dist/results/locator.d.ts +75 -0
  94. package/dist/results/locator.js +0 -0
  95. package/dist/results/select.d.ts +45 -0
  96. package/dist/results/select.js +230 -0
  97. package/dist/results/source-hash.d.ts +9 -0
  98. package/dist/results/types.d.ts +204 -0
  99. package/dist/runner/eval-source.d.ts +22 -0
  100. package/dist/runner/fingerprint.d.ts +23 -0
  101. package/dist/runner/types.d.ts +332 -0
  102. package/dist/sandbox/docker-stream.d.ts +24 -0
  103. package/dist/sandbox/docker.d.ts +84 -0
  104. package/dist/sandbox/e2b.d.ts +31 -0
  105. package/dist/sandbox/errors.d.ts +4 -0
  106. package/dist/sandbox/local-files.d.ts +6 -0
  107. package/dist/sandbox/paths.d.ts +4 -0
  108. package/dist/sandbox/registry.d.ts +13 -0
  109. package/dist/sandbox/resolve.d.ts +40 -0
  110. package/dist/sandbox/retry.d.ts +10 -0
  111. package/dist/sandbox/shell.d.ts +11 -0
  112. package/dist/sandbox/source-files.d.ts +13 -0
  113. package/dist/sandbox/types.d.ts +203 -0
  114. package/dist/sandbox/vercel.d.ts +37 -0
  115. package/dist/scoring/types.d.ts +70 -0
  116. package/dist/shared/aggregate.d.ts +29 -0
  117. package/dist/shared/aggregate.js +55 -0
  118. package/dist/shared/types.d.ts +27 -0
  119. package/dist/shared/verdict.d.ts +27 -0
  120. package/dist/shared/verdict.js +34 -0
  121. package/dist/tty-line.d.ts +6 -0
  122. package/dist/types.d.ts +7 -0
  123. package/dist/util.d.ts +22 -0
  124. package/docs-site/zh/concepts/adapter.mdx +9 -4
  125. package/docs-site/zh/concepts/assert.mdx +8 -8
  126. package/docs-site/zh/concepts/evals.mdx +5 -5
  127. package/docs-site/zh/concepts/experiment.mdx +3 -1
  128. package/docs-site/zh/concepts/judge.mdx +9 -9
  129. package/docs-site/zh/concepts/overview.mdx +2 -2
  130. package/docs-site/zh/concepts/tier.mdx +6 -6
  131. package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
  132. package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
  133. package/docs-site/zh/guides/agent-feedback-loop.mdx +235 -78
  134. package/docs-site/zh/guides/connect-otel.mdx +3 -3
  135. package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
  136. package/docs-site/zh/guides/custom-reports.mdx +196 -36
  137. package/docs-site/zh/guides/experiments.mdx +7 -0
  138. package/docs-site/zh/guides/fixtures.mdx +1 -1
  139. package/docs-site/zh/guides/official-adapters.mdx +31 -9
  140. package/docs-site/zh/guides/publish-report.mdx +4 -4
  141. package/docs-site/zh/guides/report-components.mdx +194 -55
  142. package/docs-site/zh/guides/reporters.mdx +2 -2
  143. package/docs-site/zh/guides/results-data.mdx +35 -34
  144. package/docs-site/zh/guides/runner.mdx +2 -2
  145. package/docs-site/zh/guides/sandbox-agent.mdx +10 -5
  146. package/docs-site/zh/guides/sandbox-providers.mdx +102 -0
  147. package/docs-site/zh/guides/viewing-results.mdx +199 -79
  148. package/docs-site/zh/guides/write-experiment.mdx +23 -1
  149. package/docs-site/zh/guides/write-send.mdx +2 -2
  150. package/docs-site/zh/index.mdx +4 -3
  151. package/docs-site/zh/introduction.mdx +2 -1
  152. package/docs-site/zh/quickstart.mdx +5 -3
  153. package/docs-site/zh/reference/builtin-agents.mdx +38 -13
  154. package/docs-site/zh/reference/capabilities.mdx +1 -1
  155. package/docs-site/zh/reference/cli.mdx +36 -13
  156. package/docs-site/zh/reference/define-agent.mdx +24 -12
  157. package/docs-site/zh/reference/define-config.mdx +1 -1
  158. package/docs-site/zh/reference/define-eval.mdx +3 -3
  159. package/docs-site/zh/reference/events.mdx +7 -1
  160. package/docs-site/zh/reference/expect.mdx +3 -3
  161. package/package.json +10 -6
  162. package/src/agents/bub.ts +134 -43
  163. package/src/agents/claude-code.test.ts +152 -0
  164. package/src/agents/claude-code.ts +127 -13
  165. package/src/agents/codex.test.ts +192 -0
  166. package/src/agents/codex.ts +151 -12
  167. package/src/agents/index.ts +11 -3
  168. package/src/agents/manifest.ts +41 -0
  169. package/src/agents/plugin-config.test.ts +94 -0
  170. package/src/agents/shared.ts +33 -2
  171. package/src/agents/skills.test.ts +214 -0
  172. package/src/agents/skills.ts +304 -0
  173. package/src/agents/streaming.ts +5 -5
  174. package/src/agents/types.ts +62 -2
  175. package/src/cli.ts +74 -47
  176. package/src/context/context.test.ts +20 -2
  177. package/src/context/context.ts +3 -0
  178. package/src/context/control-flow.ts +1 -1
  179. package/src/context/session.test.ts +92 -1
  180. package/src/context/session.ts +26 -1
  181. package/src/context/types.ts +4 -4
  182. package/src/define.test.ts +82 -0
  183. package/src/define.ts +72 -11
  184. package/src/expect/index.ts +5 -5
  185. package/src/i18n/en.ts +36 -15
  186. package/src/i18n/zh-CN.ts +39 -21
  187. package/src/index.ts +1 -1
  188. package/src/o11y/execution-tree.test.ts +452 -0
  189. package/src/o11y/execution-tree.ts +367 -0
  190. package/src/o11y/otlp/receiver.ts +1 -1
  191. package/src/o11y/otlp/select.ts +7 -3
  192. package/src/o11y/parsers/claude-code.test.ts +175 -0
  193. package/src/o11y/parsers/claude-code.ts +32 -0
  194. package/src/o11y/types.ts +6 -1
  195. package/src/report/aggregate.ts +26 -20
  196. package/src/report/built-in-user-parity.test.tsx +643 -0
  197. package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
  198. package/src/report/built-ins/index.ts +2 -0
  199. package/src/report/components.tsx +223 -50
  200. package/src/report/compute.ts +358 -53
  201. package/src/report/dual-render.test.tsx +1125 -0
  202. package/src/report/flag.ts +1 -1
  203. package/src/report/format.ts +50 -2
  204. package/src/report/index.ts +48 -14
  205. package/src/report/load.ts +1 -1
  206. package/src/report/locale.ts +206 -0
  207. package/src/report/metrics.ts +39 -15
  208. package/src/report/primitives.tsx +100 -2
  209. package/src/report/react/AttemptList.tsx +115 -0
  210. package/src/report/react/DeltaTable.tsx +9 -6
  211. package/src/report/react/EvalList.tsx +0 -0
  212. package/src/report/react/ExperimentList.tsx +108 -0
  213. package/src/report/react/GroupSummary.tsx +66 -0
  214. package/src/report/react/MetricBars.tsx +24 -13
  215. package/src/report/react/MetricLine.tsx +21 -21
  216. package/src/report/react/MetricMatrix.tsx +8 -4
  217. package/src/report/react/MetricScatter.tsx +136 -96
  218. package/src/report/react/MetricTable.tsx +115 -15
  219. package/src/report/react/RunOverview.tsx +48 -26
  220. package/src/report/react/Scoreboard.tsx +15 -7
  221. package/src/report/react/cell.tsx +18 -9
  222. package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
  223. package/src/report/react/colors.ts +24 -15
  224. package/src/report/react/enhance.js +129 -0
  225. package/src/report/react/fixtures.ts +163 -34
  226. package/src/report/react/format.ts +1 -1
  227. package/src/report/react/index.tsx +22 -6
  228. package/src/report/react/render.test.tsx +164 -31
  229. package/src/report/react/styles.css +405 -213
  230. package/src/report/report.test.ts +403 -94
  231. package/src/report/report.ts +29 -11
  232. package/src/report/text/faces.ts +303 -95
  233. package/src/report/text/layout.ts +76 -11
  234. package/src/report/text/table.ts +98 -0
  235. package/src/report/tree.ts +103 -20
  236. package/src/report/types.ts +196 -25
  237. package/src/report/web.ts +46 -19
  238. package/src/results/annotated-source.test.ts +146 -0
  239. package/src/results/annotated-source.ts +128 -0
  240. package/src/results/attempt-evidence.test.ts +158 -0
  241. package/src/results/attempt-evidence.ts +158 -0
  242. package/src/results/attempt-source.ts +48 -0
  243. package/src/results/copy.ts +154 -139
  244. package/src/results/format.ts +54 -47
  245. package/src/results/host-equivalence.test.ts +527 -0
  246. package/src/results/index.ts +56 -14
  247. package/src/results/locator.test.ts +198 -0
  248. package/src/results/locator.ts +0 -0
  249. package/src/results/open.ts +392 -175
  250. package/src/results/results.test.ts +943 -494
  251. package/src/results/select.ts +129 -23
  252. package/src/results/skipped-notice.ts +0 -0
  253. package/src/results/source-hash.ts +28 -0
  254. package/src/results/types.ts +98 -62
  255. package/src/results/writer.ts +304 -176
  256. package/src/runner/attempt.test.ts +170 -0
  257. package/src/runner/attempt.ts +97 -15
  258. package/src/runner/discover.test.ts +65 -0
  259. package/src/runner/discover.ts +6 -2
  260. package/src/runner/eval-source.test.ts +121 -0
  261. package/src/runner/eval-source.ts +45 -0
  262. package/src/runner/fingerprint.ts +54 -1
  263. package/src/runner/remote-sandbox.ts +1 -1
  264. package/src/runner/report.test.ts +6 -8
  265. package/src/runner/report.ts +5 -4
  266. package/src/runner/reporters/artifacts.ts +27 -41
  267. package/src/runner/reporters/braintrust.test.ts +3 -3
  268. package/src/runner/reporters/braintrust.ts +1 -1
  269. package/src/runner/reporters/console.ts +6 -6
  270. package/src/runner/reporters/json.ts +4 -4
  271. package/src/runner/reporters/live.test.ts +56 -0
  272. package/src/runner/reporters/live.ts +36 -10
  273. package/src/runner/reporters/quiet.test.ts +66 -0
  274. package/src/runner/reporters/quiet.ts +49 -0
  275. package/src/runner/reporters/shared.ts +5 -5
  276. package/src/runner/reporters/table.ts +17 -17
  277. package/src/runner/run.ts +61 -102
  278. package/src/runner/types.ts +43 -32
  279. package/src/sandbox/checkpoint.ts +1 -1
  280. package/src/sandbox/docker.ts +15 -2
  281. package/src/sandbox/e2b.ts +11 -5
  282. package/src/sandbox/errors.ts +13 -0
  283. package/src/sandbox/index.ts +3 -3
  284. package/src/sandbox/local-files.ts +1 -1
  285. package/src/sandbox/registry.ts +6 -5
  286. package/src/sandbox/resolve.ts +38 -23
  287. package/src/sandbox/retry.test.ts +68 -0
  288. package/src/sandbox/retry.ts +44 -0
  289. package/src/sandbox/types.ts +69 -28
  290. package/src/sandbox/vercel.ts +17 -3
  291. package/src/scoring/collector.ts +3 -3
  292. package/src/scoring/judge.ts +2 -2
  293. package/src/scoring/scoped.ts +13 -1
  294. package/src/scoring/types.ts +7 -7
  295. package/src/scoring/verdict.ts +4 -4
  296. package/src/shared/aggregate.ts +23 -3
  297. package/src/shared/types.ts +2 -2
  298. package/src/shared/{outcome.ts → verdict.ts} +15 -15
  299. package/src/show/compose.ts +12 -132
  300. package/src/show/index.ts +125 -94
  301. package/src/show/render.ts +660 -160
  302. package/src/show/show.test.ts +472 -253
  303. package/src/source-loc.ts +1 -1
  304. package/src/tty-line.ts +32 -0
  305. package/src/types.ts +2 -2
  306. package/src/util.test.ts +21 -1
  307. package/src/util.ts +5 -1
  308. package/src/view/app/App.tsx +33 -198
  309. package/src/view/app/components/AttemptModal.tsx +6 -6
  310. package/src/view/app/components/CodeView.tsx +2 -2
  311. package/src/view/app/components/CopyControls.tsx +32 -36
  312. package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
  313. package/src/view/app/components/ui/badge.tsx +1 -1
  314. package/src/view/app/components/ui/dialog.tsx +2 -2
  315. package/src/view/app/i18n.ts +40 -31
  316. package/src/view/app/lib/artifact-url.ts +1 -1
  317. package/src/view/app/lib/attempt-route.test.ts +37 -47
  318. package/src/view/app/lib/attempt-route.ts +26 -26
  319. package/src/view/app/lib/rows.ts +9 -112
  320. package/src/view/app/lib/verdict.ts +25 -0
  321. package/src/view/app/main.tsx +9 -12
  322. package/src/view/app/pages/RunsPage.tsx +4 -4
  323. package/src/view/app/pages/TracesPage.tsx +3 -3
  324. package/src/view/app/shared.ts +0 -1
  325. package/src/view/app/types.ts +6 -51
  326. package/src/view/artifact-serving.test.ts +136 -0
  327. package/src/view/client-dist/app.css +1 -1
  328. package/src/view/client-dist/app.js +21 -21
  329. package/src/view/data.test.ts +214 -143
  330. package/src/view/data.ts +166 -120
  331. package/src/view/index.ts +23 -11
  332. package/src/view/server.ts +52 -17
  333. package/src/view/shared/types.ts +22 -31
  334. package/src/view/styles.css +117 -327
  335. package/src/view/view-report.test.ts +146 -98
  336. package/docs-site/zh/guides/sandbox-backends.mdx +0 -75
  337. package/src/report/default-report.tsx +0 -222
  338. package/src/report/dual-face.test.tsx +0 -527
  339. package/src/report/react/CaseList.tsx +0 -70
  340. package/src/view/app/components/CostScoreChart.tsx +0 -147
  341. package/src/view/app/components/ExperimentTable.tsx +0 -284
  342. package/src/view/app/components/GroupSelector.tsx +0 -61
  343. package/src/view/app/components/primitives.tsx +0 -43
  344. package/src/view/app/lib/outcome.ts +0 -82
@@ -8,29 +8,35 @@ import { dirname, join } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { renderToStaticMarkup } from "react-dom/server";
10
10
  import { describe, expect, it } from "vitest";
11
+ import type { AttemptLocator } from "../../results/locator.ts";
11
12
 
12
13
  import {
13
- CaseList,
14
+ AttemptList,
14
15
  DeltaTable,
16
+ EvalList,
17
+ ExperimentList,
15
18
  MetricMatrix,
16
19
  MetricScatter,
17
20
  MetricTable,
18
21
  RunOverview,
19
22
  Scoreboard,
20
23
  } from "./index.tsx";
21
- import { NRE_PALETTE, colorClassForKey, colorIndexForKey } from "./colors.ts";
24
+ import { colorClassForKey, seriesClassForKey } from "./colors.ts";
22
25
  import {
23
- caseListData,
26
+ attemptListItems,
24
27
  deltaData,
28
+ evalListItems,
29
+ experimentListItems,
25
30
  matrixData,
26
31
  overviewData,
27
32
  overviewWithCost,
28
33
  scatterData,
29
34
  scoreboardData,
30
35
  tableData,
36
+ tableDataWithMeta,
31
37
  } from "./fixtures.ts";
32
38
 
33
- const attemptHref = (ref: { run: string; result: number }) => `/attempts/${ref.run}/${ref.result}`;
39
+ const attemptHref = (locator: AttemptLocator) => `/attempts/${locator}`;
34
40
 
35
41
  describe("RunOverview", () => {
36
42
  const html = renderToStaticMarkup(<RunOverview data={overviewData} />);
@@ -39,7 +45,11 @@ describe("RunOverview", () => {
39
45
  expect(html).toContain("nre-overview");
40
46
  expect(html).toContain("<dd>12</dd>"); // 题目
41
47
  expect(html).toContain("<dd>48</dd>"); // attempts
42
- expect(html).toContain("78%"); // 36 / (36+8+2),skipped 不进分母
48
+ // totals.passRate.display 原样渲染(两级聚合 70%),不是从 passed/failed/errored
49
+ // 现场重算的 78%(36/(36+8+2))—— 两个数刻意不同,证明组件没有偷懒重算
50
+ expect(html).toContain("70%");
51
+ expect(html).not.toContain("78%");
52
+ expect(html).toContain("46/48"); // samples < total:覆盖率角标(2 个 skipped attempt 不进桶)
43
53
  expect(html).toContain("4m 21s"); // 261000ms
44
54
  });
45
55
 
@@ -88,7 +98,54 @@ describe("MetricTable", () => {
88
98
  });
89
99
 
90
100
  it("refs + attemptHref 出普通 <a>", () => {
91
- expect(html).toContain('href="/attempts/run-a/0"');
101
+ expect(html).toContain('href="/attempts/@1a0a0a0"');
102
+ });
103
+
104
+ it("渐进增强的 data 属性:所有表头 data-nre-sort、格子 data-sort-value(无 JS 时纯属性,内容完整)", () => {
105
+ expect(html.match(/data-nre-sort/g)).toHaveLength(3); // 维度列 + 2 指标列
106
+ expect(html).toContain('data-sort-value="codex"');
107
+ expect(html).toContain('data-sort-value="0.87"');
108
+ // 缺数据格子的排序值为空串:enhance.js 排序时恒沉底
109
+ expect(html).toContain('data-sort-value=""');
110
+ });
111
+ });
112
+
113
+ describe("MetricTable:meta 榜单 parity 与 filter", () => {
114
+ const html = renderToStaticMarkup(<MetricTable data={tableDataWithMeta} />);
115
+
116
+ it("meta 在场补 Model / Agent 列,列序 experiment、model、agent、指标、verdicts", () => {
117
+ expect(html).toContain(">Model</th>");
118
+ expect(html).toContain(">Agent</th>");
119
+ expect(html).toContain(">Verdicts</th>");
120
+ expect(html.indexOf(">Model</th>")).toBeLessThan(html.indexOf(">Agent</th>"));
121
+ expect(html.indexOf(">Agent</th>")).toBeLessThan(html.indexOf("pass rate"));
122
+ expect(html).toContain("gpt-5.4");
123
+ });
124
+
125
+ it("verdict 计票列:eval 级折叠口径的 pill,零计数不出 pill", () => {
126
+ expect(html).toContain("nre-verdict-pill");
127
+ expect(html).toContain("1 passed");
128
+ expect(html).toContain("1 failed");
129
+ expect(html).toContain("2 passed");
130
+ expect(html).not.toContain("0 errored");
131
+ });
132
+
133
+ it("filter 开:表格前渲染 data-nre-filter 输入框(无 JS 静默无功能),仍无 <script>", () => {
134
+ const filtered = renderToStaticMarkup(<MetricTable data={tableDataWithMeta} filter />);
135
+ expect(filtered).toContain("data-nre-filter");
136
+ expect(filtered).toContain('class="nre-filter"');
137
+ expect(filtered.indexOf("nre-filter")).toBeLessThan(filtered.indexOf("<table"));
138
+ expect(filtered).toContain('placeholder="Filter rows…"');
139
+ expect(filtered).not.toContain("<script");
140
+ });
141
+
142
+ it("locale=zh-CN:chrome 文案与 meta 列头走字典;display 不本地化", () => {
143
+ const zh = renderToStaticMarkup(<MetricTable data={tableDataWithMeta} filter locale="zh-CN" />);
144
+ expect(zh).toContain(">模型</th>");
145
+ expect(zh).toContain(">结果</th>");
146
+ expect(zh).toContain("1 通过");
147
+ expect(zh).toContain('placeholder="筛选行…"');
148
+ expect(zh).toContain("50%"); // display 是 format 产物,不本地化
92
149
  });
93
150
  });
94
151
 
@@ -107,8 +164,8 @@ describe("MetricMatrix", () => {
107
164
  it("格子数字与 refs 下钻链接", () => {
108
165
  expect(html).toContain("100%");
109
166
  expect(html).toContain("0%");
110
- expect(html).toContain('href="/attempts/run-b/3"');
111
- expect(html).toContain('href="/attempts/run-b/7"');
167
+ expect(html).toContain('href="/attempts/@1b3b3b3"');
168
+ expect(html).toContain('href="/attempts/@1b7b7b7"');
112
169
  });
113
170
 
114
171
  it("列头(维度键)带稳定散列配色 class", () => {
@@ -145,13 +202,14 @@ describe("Scoreboard", () => {
145
202
  describe("MetricScatter", () => {
146
203
  const html = renderToStaticMarkup(<MetricScatter data={scatterData} pointHref={(row) => `/exp/${row.key}`} />);
147
204
 
205
+ // data-key 在点的 <g> 上,坐标在其内第一个 circle 上(非贪婪跨标签匹配)
148
206
  const cxOf = (key: string): number => {
149
- const m = html.match(new RegExp(`data-key="${key}"[^>]*\\bcx="([\\d.]+)"`));
207
+ const m = html.match(new RegExp(`data-key="${key}"[\\s\\S]*?\\bcx="([\\d.]+)"`));
150
208
  expect(m, `circle for ${key}`).toBeTruthy();
151
209
  return Number(m![1]);
152
210
  };
153
211
  const cyOf = (key: string): number => {
154
- const m = html.match(new RegExp(`data-key="${key}"[^>]*\\bcy="([\\d.]+)"`));
212
+ const m = html.match(new RegExp(`data-key="${key}"[\\s\\S]*?\\bcy="([\\d.]+)"`));
155
213
  expect(m, `circle for ${key}`).toBeTruthy();
156
214
  return Number(m![1]);
157
215
  };
@@ -168,12 +226,21 @@ describe("MetricScatter", () => {
168
226
  expect(cyOf("compare/bub-high")).toBeLessThan(cyOf("compare/bub-low"));
169
227
  });
170
228
 
171
- it("同系列点连线并标系列名,线色来自稳定散列调色板", () => {
229
+ it("同系列点连线,系列色走类名(nre-series-cN,CSS 上色跟随深浅主题),图例列系列名", () => {
172
230
  expect(html).toContain("<polyline");
173
- expect(html).toContain(">bub</text>");
174
- expect(html).toContain(NRE_PALETTE[colorIndexForKey("bub")]);
175
- // codex 只有 1 个可画点:不出线,但系列名仍标出
176
- expect(html).toContain(">codex</text>");
231
+ expect(html).toContain(`nre-scatter-line ${seriesClassForKey("bub")}`);
232
+ // 渲染面零内联 hex:深色主题下由 CSS 变量切换
233
+ expect(html).not.toMatch(/#[0-9a-f]{6}/i);
234
+ // 图例:codex 只有 1 个可画点、不出线,但系列名仍在图例里
235
+ expect(html).toContain(">bub</span>");
236
+ expect(html).toContain(">codex</span>");
237
+ });
238
+
239
+ it("niceTicks 网格与每点直接标签(id 末段)", () => {
240
+ expect(html).toContain("nre-scatter-grid");
241
+ expect(html.match(/nre-scatter-tick/g)!.length).toBeGreaterThanOrEqual(6);
242
+ expect(html).toContain(">bub-low</text>");
243
+ expect(html).toContain(">codex-mid</text>");
177
244
  });
178
245
 
179
246
  it("null 点不画,底部注脚如实报数", () => {
@@ -190,7 +257,7 @@ describe("MetricScatter", () => {
190
257
  expect(html).toContain('href="/exp/compare/bub-low"');
191
258
  });
192
259
 
193
- it("全部点都缺数据时不画空坐标系", () => {
260
+ it("全部点都缺数据时不画空坐标系:显式说明缺哪两个指标", () => {
194
261
  const empty = renderToStaticMarkup(
195
262
  <MetricScatter
196
263
  data={{
@@ -200,9 +267,23 @@ describe("MetricScatter", () => {
200
267
  />,
201
268
  );
202
269
  expect(empty).not.toContain("<svg");
203
- expect(empty).toContain("no data");
270
+ expect(empty).toContain("No data to plot"); // 0 可画点:命名 x/y 指标,不画空图
204
271
  expect(empty).toContain("4 points missing data");
205
272
  });
273
+
274
+ it("恰好 1 个可画点:比较至少要两个实验,不画孤点图", () => {
275
+ const single = renderToStaticMarkup(
276
+ <MetricScatter
277
+ data={{
278
+ ...scatterData,
279
+ // 只留第一个点可画,其余 x 置空
280
+ rows: scatterData.rows.map((r, i) => (i === 0 ? r : { ...r, x: { ...r.x, value: null } })),
281
+ }}
282
+ />,
283
+ );
284
+ expect(single).not.toContain("<svg");
285
+ expect(single).toContain("At least 2 experiments needed to compare");
286
+ });
206
287
  });
207
288
 
208
289
  describe("DeltaTable", () => {
@@ -233,12 +314,16 @@ describe("DeltaTable", () => {
233
314
  });
234
315
  });
235
316
 
236
- describe("CaseList", () => {
237
- const html = renderToStaticMarkup(<CaseList data={caseListData} attemptHref={attemptHref} />);
317
+ // AttemptList / EvalList / ExperimentList 的公开(报告组件)Props 没有 attemptHref 覆盖口子——
318
+ // 证据室深链恒经 ctx.attemptHref,宿主外直接嵌进 React 应用时退化为默认 `#/attempt/<locator>`
319
+ // (tree.ts 的 DEFAULT_WEB_CONTEXT),不是纯展示、也不发明另一套断链(与 MetricTable 等
320
+ // 可选 attemptHref 的组件不同,这三个组件的下钻不是可选行为)。
238
321
 
239
- it("逐条失败断言:name、score、detail、evidence", () => {
322
+ describe("AttemptList", () => {
323
+ const html = renderToStaticMarkup(<AttemptList items={attemptListItems} />);
324
+
325
+ it("逐条断言:name、score、detail、evidence", () => {
240
326
  expect(html).toContain("roots-correct");
241
- expect(html).toContain("score 0");
242
327
  expect(html).toContain("expected x=2, got x=3");
243
328
  expect(html).toContain("judge: sign flipped when substituting into the quadratic formula");
244
329
  });
@@ -247,21 +332,67 @@ describe("CaseList", () => {
247
332
  expect(html).toContain("TypeError: cannot read properties of undefined");
248
333
  });
249
334
 
250
- it("truncated 如实报「还有 n 条没列」", () => {
251
- expect(html).toContain("and 2 more not shown");
335
+ it("total > items.length 时如实报「还有 n 条没列」", () => {
336
+ const html2 = renderToStaticMarkup(<AttemptList items={attemptListItems} total={attemptListItems.length + 2} />);
337
+ expect(html2).toContain("and 2 more not shown");
338
+ // 不传 total(或 total === items.length)不产出截断文案
339
+ expect(html).not.toContain("more not shown");
252
340
  });
253
341
 
254
- it("每条案例带 attemptHref 下钻链接", () => {
255
- expect(html).toContain('href="/attempts/run-a/4"');
256
- expect(html).toContain('href="/attempts/run-c/1"');
342
+ it("每条 attempt 带 locator + 默认证据室深链 + 证据能力标记", () => {
343
+ expect(html).toContain('href="#/attempt/@1a4a4a4"');
344
+ expect(html).toContain('href="#/attempt/@1c1c1c1"');
345
+ expect(html).toContain("[E,X,⏱]"); // failedAttempt: eval + execution + timing
257
346
  });
258
347
 
259
- it("长文本收进 <details>,零 JS 可展开", () => {
348
+ it("长文本(evidence)收进 <details>,零 JS 可展开", () => {
260
349
  expect(html).toContain("<details");
261
350
  expect(html).toContain("<summary>");
262
351
  });
263
352
  });
264
353
 
354
+ describe("EvalList", () => {
355
+ const html = renderToStaticMarkup(<EvalList items={evalListItems} />);
356
+
357
+ it("每项一个 experimentId + evalId,判定与分数在场", () => {
358
+ expect(html).toContain("algebra/quadratic");
359
+ expect(html).toContain("geometry/angles");
360
+ expect(html).toContain("compare/bub");
361
+ expect(html).toContain("compare/codex");
362
+ });
363
+
364
+ it("展开到这道题的 Attempt(与 AttemptList 同一套 AttemptRow 渲染)", () => {
365
+ expect(html).toContain('href="#/attempt/@1a4a4a4"');
366
+ expect(html).toContain("roots-correct");
367
+ });
368
+
369
+ it("零 JS 靠原生 <details>,静态 HTML 内容已完整", () => {
370
+ expect(html).toContain("<details");
371
+ expect(html).not.toContain("<script");
372
+ });
373
+ });
374
+
375
+ describe("ExperimentList", () => {
376
+ const html = renderToStaticMarkup(<ExperimentList items={experimentListItems} />);
377
+
378
+ it("主行:身份、agent/model、官方两级聚合指标", () => {
379
+ expect(html).toContain("compare/bub");
380
+ expect(html).toContain("compare/codex");
381
+ expect(html).toContain("gpt-5.4");
382
+ expect(html).toContain("50%"); // passRate.display
383
+ });
384
+
385
+ it("展开到 Eval:判定符 + 该题 Attempt 的 locator 徽标内联", () => {
386
+ expect(html).toContain("algebra/quadratic");
387
+ expect(html).toContain('href="#/attempt/@1a4a4a4"');
388
+ });
389
+
390
+ it("零 JS 靠原生 <details>,无 <script>", () => {
391
+ expect(html).toContain("<details");
392
+ expect(html).not.toContain("<script");
393
+ });
394
+ });
395
+
265
396
  describe("跨组件契约", () => {
266
397
  it("同一维度键在所有块里同色(稳定散列,与渲染顺序无关)", () => {
267
398
  const cls = colorClassForKey("bub");
@@ -269,12 +400,12 @@ describe("跨组件契约", () => {
269
400
  const matrix = renderToStaticMarkup(<MetricMatrix data={matrixData} />);
270
401
  const board = renderToStaticMarkup(<Scoreboard data={scoreboardData} />);
271
402
  const delta = renderToStaticMarkup(<DeltaTable data={deltaData} />);
272
- const cases = renderToStaticMarkup(<CaseList data={caseListData} />);
273
- for (const html of [table, matrix, board, delta, cases]) {
403
+ const attempts = renderToStaticMarkup(<AttemptList items={attemptListItems} />);
404
+ for (const html of [table, matrix, board, delta, attempts]) {
274
405
  expect(html).toContain(cls);
275
406
  }
276
407
  const scatter = renderToStaticMarkup(<MetricScatter data={scatterData} />);
277
- expect(scatter).toContain(NRE_PALETTE[colorIndexForKey("bub")]);
408
+ expect(scatter).toContain(seriesClassForKey("bub"));
278
409
  });
279
410
 
280
411
  it("静态输出不含 <script>:交互只靠 <a>/<details>/CSS", () => {
@@ -285,7 +416,9 @@ describe("跨组件契约", () => {
285
416
  renderToStaticMarkup(<Scoreboard data={scoreboardData} />),
286
417
  renderToStaticMarkup(<MetricScatter data={scatterData} />),
287
418
  renderToStaticMarkup(<DeltaTable data={deltaData} />),
288
- renderToStaticMarkup(<CaseList data={caseListData} />),
419
+ renderToStaticMarkup(<AttemptList items={attemptListItems} />),
420
+ renderToStaticMarkup(<EvalList items={evalListItems} />),
421
+ renderToStaticMarkup(<ExperimentList items={experimentListItems} />),
289
422
  ].join("");
290
423
  expect(all).not.toContain("<script");
291
424
  });