oh-my-knowledge 0.32.0 → 0.33.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 (119) hide show
  1. package/README.md +11 -9
  2. package/README.zh.md +11 -9
  3. package/dist/analysis/saturation.d.ts +8 -0
  4. package/dist/analysis/saturation.d.ts.map +1 -1
  5. package/dist/analysis/saturation.js +12 -4
  6. package/dist/analysis/saturation.js.map +1 -1
  7. package/dist/authoring/evolver.d.ts +43 -2
  8. package/dist/authoring/evolver.d.ts.map +1 -1
  9. package/dist/authoring/evolver.js +91 -8
  10. package/dist/authoring/evolver.js.map +1 -1
  11. package/dist/authoring/generator.d.ts +29 -1
  12. package/dist/authoring/generator.d.ts.map +1 -1
  13. package/dist/authoring/generator.js +113 -0
  14. package/dist/authoring/generator.js.map +1 -1
  15. package/dist/cli/commands/doctor.d.ts +2 -0
  16. package/dist/cli/commands/doctor.d.ts.map +1 -1
  17. package/dist/cli/commands/doctor.js +22 -3
  18. package/dist/cli/commands/doctor.js.map +1 -1
  19. package/dist/cli/commands/eval/index.d.ts.map +1 -1
  20. package/dist/cli/commands/eval/index.js +3 -2
  21. package/dist/cli/commands/eval/index.js.map +1 -1
  22. package/dist/cli/commands/evolve.d.ts +1 -0
  23. package/dist/cli/commands/evolve.d.ts.map +1 -1
  24. package/dist/cli/commands/evolve.js +16 -0
  25. package/dist/cli/commands/evolve.js.map +1 -1
  26. package/dist/cli/commands/observe/index.d.ts.map +1 -1
  27. package/dist/cli/commands/observe/index.js +6 -2
  28. package/dist/cli/commands/observe/index.js.map +1 -1
  29. package/dist/cli/commands/sample.d.ts +2 -0
  30. package/dist/cli/commands/sample.d.ts.map +1 -1
  31. package/dist/cli/commands/sample.js +98 -6
  32. package/dist/cli/commands/sample.js.map +1 -1
  33. package/dist/cli/lib/cmd-flags.d.ts +5 -0
  34. package/dist/cli/lib/cmd-flags.d.ts.map +1 -1
  35. package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
  36. package/dist/cli/lib/i18n-dict/common.d.ts.map +1 -1
  37. package/dist/cli/lib/i18n-dict/common.js +18 -2
  38. package/dist/cli/lib/i18n-dict/common.js.map +1 -1
  39. package/dist/cli/lib/i18n-dict/evolve.d.ts +1 -1
  40. package/dist/cli/lib/i18n-dict/evolve.d.ts.map +1 -1
  41. package/dist/cli/lib/i18n-dict/evolve.js +8 -0
  42. package/dist/cli/lib/i18n-dict/evolve.js.map +1 -1
  43. package/dist/cli/lib/parse-run-config/variant-resolution.d.ts +8 -3
  44. package/dist/cli/lib/parse-run-config/variant-resolution.d.ts.map +1 -1
  45. package/dist/cli/lib/parse-run-config/variant-resolution.js +16 -10
  46. package/dist/cli/lib/parse-run-config/variant-resolution.js.map +1 -1
  47. package/dist/cli/lib/resolve-skill-input.d.ts.map +1 -1
  48. package/dist/cli/lib/resolve-skill-input.js +7 -5
  49. package/dist/cli/lib/resolve-skill-input.js.map +1 -1
  50. package/dist/cli/lib/update-check.d.ts +65 -0
  51. package/dist/cli/lib/update-check.d.ts.map +1 -1
  52. package/dist/cli/lib/update-check.js +220 -31
  53. package/dist/cli/lib/update-check.js.map +1 -1
  54. package/dist/cli/lib/update-fetch-worker.d.ts +2 -0
  55. package/dist/cli/lib/update-fetch-worker.d.ts.map +1 -0
  56. package/dist/cli/lib/update-fetch-worker.js +35 -0
  57. package/dist/cli/lib/update-fetch-worker.js.map +1 -0
  58. package/dist/doctor/health/load-custom-dimensions.d.ts +2 -0
  59. package/dist/doctor/health/load-custom-dimensions.d.ts.map +1 -0
  60. package/dist/doctor/health/load-custom-dimensions.js +31 -0
  61. package/dist/doctor/health/load-custom-dimensions.js.map +1 -0
  62. package/dist/eval-core/bootstrap.d.ts +8 -0
  63. package/dist/eval-core/bootstrap.d.ts.map +1 -1
  64. package/dist/eval-core/bootstrap.js +11 -3
  65. package/dist/eval-core/bootstrap.js.map +1 -1
  66. package/dist/eval-core/evaluation-reporting.d.ts.map +1 -1
  67. package/dist/eval-core/evaluation-reporting.js +4 -4
  68. package/dist/eval-core/evaluation-reporting.js.map +1 -1
  69. package/dist/eval-core/verdict.d.ts +21 -3
  70. package/dist/eval-core/verdict.d.ts.map +1 -1
  71. package/dist/eval-core/verdict.js +54 -4
  72. package/dist/eval-core/verdict.js.map +1 -1
  73. package/dist/eval-workflows/evaluation-preparation.d.ts.map +1 -1
  74. package/dist/eval-workflows/evaluation-preparation.js +39 -11
  75. package/dist/eval-workflows/evaluation-preparation.js.map +1 -1
  76. package/dist/eval-workflows/run-evaluation.d.ts +1 -1
  77. package/dist/eval-workflows/run-evaluation.d.ts.map +1 -1
  78. package/dist/eval-workflows/run-evaluation.js +7 -7
  79. package/dist/eval-workflows/run-evaluation.js.map +1 -1
  80. package/dist/inputs/eval-config.d.ts.map +1 -1
  81. package/dist/inputs/eval-config.js +1 -0
  82. package/dist/inputs/eval-config.js.map +1 -1
  83. package/dist/inputs/skill-loader.d.ts +22 -0
  84. package/dist/inputs/skill-loader.d.ts.map +1 -1
  85. package/dist/inputs/skill-loader.js +171 -12
  86. package/dist/inputs/skill-loader.js.map +1 -1
  87. package/dist/observability/skill-health-analyzer.d.ts +13 -0
  88. package/dist/observability/skill-health-analyzer.d.ts.map +1 -1
  89. package/dist/observability/skill-health-analyzer.js +17 -2
  90. package/dist/observability/skill-health-analyzer.js.map +1 -1
  91. package/dist/renderer/layout.d.ts.map +1 -1
  92. package/dist/renderer/layout.js +2 -0
  93. package/dist/renderer/layout.js.map +1 -1
  94. package/dist/renderer/skill-detail-renderer.d.ts.map +1 -1
  95. package/dist/renderer/skill-detail-renderer.js +98 -19
  96. package/dist/renderer/skill-detail-renderer.js.map +1 -1
  97. package/dist/renderer/skill-health-renderer.d.ts.map +1 -1
  98. package/dist/renderer/skill-health-renderer.js +33 -4
  99. package/dist/renderer/skill-health-renderer.js.map +1 -1
  100. package/dist/renderer/skill-list-renderer.d.ts.map +1 -1
  101. package/dist/renderer/skill-list-renderer.js +18 -6
  102. package/dist/renderer/skill-list-renderer.js.map +1 -1
  103. package/dist/renderer/summary.d.ts.map +1 -1
  104. package/dist/renderer/summary.js +20 -8
  105. package/dist/renderer/summary.js.map +1 -1
  106. package/dist/server/report-server.d.ts.map +1 -1
  107. package/dist/server/report-server.js +9 -2
  108. package/dist/server/report-server.js.map +1 -1
  109. package/dist/server/skill-index.d.ts.map +1 -1
  110. package/dist/server/skill-index.js +3 -0
  111. package/dist/server/skill-index.js.map +1 -1
  112. package/dist/server/skill-insights.d.ts.map +1 -1
  113. package/dist/server/skill-insights.js +33 -5
  114. package/dist/server/skill-insights.js.map +1 -1
  115. package/dist/types/eval.d.ts +1 -0
  116. package/dist/types/eval.d.ts.map +1 -1
  117. package/dist/types/skill-index.d.ts +3 -0
  118. package/dist/types/skill-index.d.ts.map +1 -1
  119. package/package.json +4 -4
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  **English** | [简体中文](./README.zh.md)
10
10
 
11
11
  **Did your prompt actually get better?**
12
- A/B test your prompts and skills with statistical rigor — bootstrap CI, Krippendorff α, length-debias — all on by default.
12
+ A/B test your prompts and skills with statistical rigor — bootstrap CI and length-debias on by default, Krippendorff α the moment you add a gold set.
13
13
 
14
14
  ![omk report — verdict pill "v2 is clearly better than v1 — ready to ship"](./assets/screenshots/report-overview.png)
15
15
 
@@ -23,6 +23,8 @@ omk eval --control code-review-v1 --treatment code-review-v2
23
23
 
24
24
  That's it — no editing required. `omk init` scaffolds two skill variants and three sample cases; `omk eval` runs the controlled A/B and opens an HTML report with a one-line verdict in about five minutes.
25
25
 
26
+ > The CLI notifies you when a newer version is available (at most once per 20h); set `OMK_SKIP_UPDATE_CHECK=1` to silence it permanently.
27
+
26
28
  Walkthrough: [5-minute quickstart guide](docs/quickstart-skill-eval.md) (recommended for first-time users).
27
29
 
28
30
  Deeper: [CLI reference](docs/reference/cli.md) · [how it works](docs/explanation/architecture.md) · [eval sample format](docs/reference/eval-sample-format.md) · [executors & artifact layout](docs/reference/executors.md)
@@ -62,7 +64,7 @@ Teams doing knowledge engineering produce lots of knowledge artifacts (skills to
62
64
  | | omk | promptfoo | DeepEval | LangSmith |
63
65
  |--|--|--|--|--|
64
66
  | Bootstrap CI | ✓ default | ✗ | ✗ | ✗ |
65
- | Krippendorff α (judge ↔ human) | ✓ default | ✗ | ✗ | ✗ |
67
+ | Krippendorff α (judge ↔ human) | ✓ with gold set | ✗ | ✗ | ✗ |
66
68
  | Length-debias judge prompt | ✓ default | ✗ | ✗ | ✗ |
67
69
  | Saturation curve | ✓ | ✗ | ✗ | ✗ |
68
70
  | Three-layer scoring isolation | ✓ | ✗ | partial | ✗ |
@@ -70,7 +72,7 @@ Teams doing knowledge engineering produce lots of knowledge artifacts (skills to
70
72
  | Native Claude Code skill | ✓ | ✗ | ✗ | ✗ |
71
73
  | Hosted SaaS dashboard | ✗ | ✗ | ✓ | ✓ |
72
74
 
73
- omk's moat is **default-on safety net** — Bootstrap CI, judge ↔ human α, and length-debias aren't advanced flags; they're the default. Other tools let you opt into confidence intervals; omk makes them unavoidable. Need a hosted SaaS dashboard? Choose LangSmith. Want quick local prompt iteration without statistics? Choose promptfoo. **Shipping to production and someone will ask "why should I trust this number?" Choose omk.**
75
+ omk's moat is **default-on safety net** — Bootstrap CI and length-debias aren't advanced flags; they're the default, and judge ↔ human α comes free the moment you add a gold set. Other tools let you opt into confidence intervals; omk makes them unavoidable. Need a hosted SaaS dashboard? Choose LangSmith. Want quick local prompt iteration without statistics? Choose promptfoo. **Shipping to production and someone will ask "why should I trust this number?" Choose omk.**
74
76
 
75
77
  RAG-specific evals: see RAGAS (separate niche, complementary to omk). Full comparison with 7 tools across 25+ dimensions: [docs/reference/comparison.md](docs/reference/comparison.md).
76
78
 
@@ -80,9 +82,9 @@ RAG-specific evals: see RAGAS (separate niche, complementary to omk). Full compa
80
82
  |---|---|
81
83
  | **One-line verdict** | `omk eval` six-tier verdict + ship recommendation + exit-code routing; HTML pill shares the same rules |
82
84
  | **Six-dim evaluation** | Fact / Behavior / LLM-judge / Cost / Efficiency / Stability shown independently |
83
- | **Multi-executor** | Claude CLI / Claude SDK / Codex CLI / Codex SDK / OpenAI / Gemini / any custom command |
84
- | **21+ assertion types** | substring, regex, JSON Schema, ROUGE/BLEU/Levenshtein similarity, agent tool-call assertions, semantic similarity, custom JS |
85
- | **Statistical rigor** | Bootstrap CI / Krippendorff α / length-debias / saturation curve — all on by default. [Details →](docs/explanation/statistical-rigor.md) |
85
+ | **Multi-executor** | Claude CLI / Claude SDK / Codex CLI / Codex SDK / OpenAI / Gemini / Anthropic API / any custom command |
86
+ | **30+ assertion types** | substring, regex, JSON Schema, ROUGE/BLEU/Levenshtein similarity, agent tool-call assertions, semantic similarity, custom JS |
87
+ | **Statistical rigor** | Bootstrap CI / length-debias / saturation curve on by default; Krippendorff α auto-computed with a gold set. [Details →](docs/explanation/statistical-rigor.md) |
86
88
  | **RAG metrics** | `faithfulness` / `answer_relevancy` / `context_recall` — anti-hallucination + answer relevance + context coverage |
87
89
  | **LLM health audit** | `omk doctor` grades 7 builtin dimensions; `--static-only` runs offline without an LLM |
88
90
  | **Production observability** | parse Claude Code session JSONL traces; measure per-skill failure rate / latency / cost / knowledge-gap signals |
@@ -100,7 +102,7 @@ RAG-specific evals: see RAGAS (separate niche, complementary to omk). Full compa
100
102
  ## Documentation
101
103
 
102
104
  - **[How it works](docs/explanation/architecture.md)** — interleaved scheduling, variant resolution, dual-channel scoring, six-dim report
103
- - **[Eval sample format](docs/reference/eval-sample-format.md)** — sample schema, scoring formulas, 21+ assertion types, custom JS assertions
105
+ - **[Eval sample format](docs/reference/eval-sample-format.md)** — sample schema, scoring formulas, 30+ assertion types, custom JS assertions
104
106
  - **[CLI reference](docs/reference/cli.md)** — all seven commands with bash examples and flag tables
105
107
  - **[Executors & artifact layout](docs/reference/executors.md)** — built-in / custom executors, agent evaluation, common model configs (Claude / OpenAI / GLM / Qwen / DeepSeek / Moonshot / Ollama)
106
108
  - **[Quickstart](docs/quickstart-skill-eval.md)** — first-time five-minute walkthrough
@@ -117,9 +119,9 @@ RAG-specific evals: see RAGAS (separate niche, complementary to omk). Full compa
117
119
 
118
120
  ## Requirements
119
121
 
120
- - Node.js >= 20
122
+ - Node.js >= 22
121
123
  - `claude` CLI (for the default executor and LLM judge; see [Claude Code](https://claude.ai/code))
122
- - not needed if you use other executors (openai / gemini) with `--no-judge`
124
+ - not needed if you use other executors (openai-api / anthropic-api / gemini) with `--no-judge`
123
125
 
124
126
  ## Security notice
125
127
 
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [English](./README.md) | **简体中文**
10
10
 
11
11
  **你改完 prompt,真的变好了吗?**
12
- 用统计严谨性 A/B 测试你的 prompt 和 skill —— Bootstrap 置信区间、Krippendorff α、长度去偏,全部默认开。
12
+ 用统计严谨性 A/B 测试你的 prompt 和 skill —— Bootstrap 置信区间、长度去偏默认开,配一份人工 gold 就自动算 Krippendorff α。
13
13
 
14
14
  ![omk 报告 — verdict pill「v2 明显优于 v1,可以发布」](./assets/screenshots/report-overview-zh.png)
15
15
 
@@ -23,6 +23,8 @@ omk eval --control code-review-v1 --treatment code-review-v2
23
23
 
24
24
  不用改任何文件 —— `omk init` 帮你脚手架两版 skill 和三条评测用例;`omk eval` 跑控制变量 A/B,5 分钟内出 HTML 报告 + 一行 verdict。
25
25
 
26
+ > 命令行有新版本时会自动提示(每 20 小时最多一次);想永久关闭该提醒,设环境变量 `OMK_SKIP_UPDATE_CHECK=1` 即可。
27
+
26
28
  手把手教程:[5 分钟快速上手](docs/zh/quickstart-skill-eval.md)(推荐第一次跑评测的用户)。
27
29
 
28
30
  深入:[CLI 参考](docs/zh/reference/cli.md) · [工作原理](docs/zh/explanation/architecture.md) · [评测样本格式](docs/zh/reference/eval-sample-format.md) · [执行器与 artifact 布局](docs/zh/reference/executors.md)
@@ -62,7 +64,7 @@ omk sample skills/my-skill.md
62
64
  | | omk | promptfoo | DeepEval | LangSmith |
63
65
  |--|--|--|--|--|
64
66
  | Bootstrap 置信区间 | ✓ 默认 | ✗ | ✗ | ✗ |
65
- | Krippendorff α(评委 ↔ 人工) | ✓ 默认 | ✗ | ✗ | ✗ |
67
+ | Krippendorff α(评委 ↔ 人工) | ✓ gold 即开 | ✗ | ✗ | ✗ |
66
68
  | 长度去偏的评委 prompt | ✓ 默认 | ✗ | ✗ | ✗ |
67
69
  | 饱和曲线 | ✓ | ✗ | ✗ | ✗ |
68
70
  | 三层独立评分 | ✓ | ✗ | 部分 | ✗ |
@@ -70,7 +72,7 @@ omk sample skills/my-skill.md
70
72
  | 原生 Claude Code skill | ✓ | ✗ | ✗ | ✗ |
71
73
  | 托管 SaaS 看板 | ✗ | ✗ | ✓ | ✓ |
72
74
 
73
- omk 的护城河是 **default-on 安全网** —— Bootstrap CI / 评委 ↔ 人工 α / 长度去偏不是 advanced flag,是默认行为。其他工具让你**手动**接置信区间;omk 让你**默认无法忽略**它。需要 SaaS 看板?选 LangSmith。要快速 prompt 迭代不要统计层?选 promptfoo。**要发到生产且会被问「为什么应该相信这个数字」?选 omk。**
75
+ omk 的护城河是 **default-on 安全网** —— Bootstrap CI / 长度去偏不是 advanced flag,是默认行为;评委 ↔ 人工 α 只要给一份 gold 集就自动算。其他工具让你**手动**接置信区间;omk 让你**默认无法忽略**它。需要 SaaS 看板?选 LangSmith。要快速 prompt 迭代不要统计层?选 promptfoo。**要发到生产且会被问「为什么应该相信这个数字」?选 omk。**
74
76
 
75
77
  RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比(7 个工具 × 25+ 维度): [docs/zh/reference/comparison.md](docs/zh/reference/comparison.md)
76
78
 
@@ -80,9 +82,9 @@ RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比
80
82
  |------|------|
81
83
  | **Verdict 一行结论** | `omk eval` 六档判定 + ship 建议 + exit code 路由,与 HTML 报告 verdict pill 共享规则 |
82
84
  | **六维评估** | 事实 / 行为 / LLM 评价 / 成本 / 效率 / 稳定性独立展示 |
83
- | **多执行器** | 支持 Claude CLI / Claude SDK / Codex CLI / Codex SDK / OpenAI / Gemini 及自定义命令 |
84
- | **21+ 种断言** | 包含子串、正则、JSON Schema、ROUGE/BLEU/Levenshtein 相似度、Agent 工具调用、语义相似度、自定义函数等 |
85
- | **统计严谨性** | Bootstrap CI / Krippendorff α / 长度去偏 / 饱和曲线 —— 全部默认开。[详情 →](docs/zh/explanation/statistical-rigor.md) |
85
+ | **多执行器** | 支持 Claude CLI / Claude SDK / Codex CLI / Codex SDK / OpenAI / Gemini / Anthropic API 及自定义命令 |
86
+ | **30+ 种断言** | 包含子串、正则、JSON Schema、ROUGE/BLEU/Levenshtein 相似度、Agent 工具调用、语义相似度、自定义函数等 |
87
+ | **统计严谨性** | Bootstrap CI / 长度去偏 / 饱和曲线默认开,Krippendorff α 提供 gold 集即自动计算。[详情 →](docs/zh/explanation/statistical-rigor.md) |
86
88
  | **RAG metrics** | `faithfulness` / `answer_relevancy` / `context_recall` 三 metric — 反幻觉 + 切题度 + context 覆盖 |
87
89
  | **LLM 健康度审计** | `omk doctor` 给 7 个内置维度独立打分;`--static-only` 可离线无 LLM 调用 |
88
90
  | **线上 session 观测** | 解析 Claude Code session JSONL,测量各 skill 的失败率、耗时、token 成本、知识缺口信号 |
@@ -100,7 +102,7 @@ RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比
100
102
  ## 文档
101
103
 
102
104
  - **[工作原理](docs/zh/explanation/architecture.md)** —— 交错调度、variant 解析、双通道评分、六维报告
103
- - **[评测样本格式](docs/zh/reference/eval-sample-format.md)** —— sample schema、评分公式、21+ 断言类型、自定义 JS 断言
105
+ - **[评测样本格式](docs/zh/reference/eval-sample-format.md)** —— sample schema、评分公式、30+ 断言类型、自定义 JS 断言
104
106
  - **[CLI 参考](docs/zh/reference/cli.md)** —— 7 个命令的 bash 示例和 flag 表
105
107
  - **[执行器与 artifact 布局](docs/zh/reference/executors.md)** —— 内置 / 自定义执行器、Agent 评测、常见模型配置(Claude / OpenAI / GLM / 通义 / DeepSeek / Moonshot / Ollama)
106
108
  - **[快速上手](docs/zh/quickstart-skill-eval.md)** —— 第一次跑评测的 5 分钟教程
@@ -117,9 +119,9 @@ RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比
117
119
 
118
120
  ## 系统要求
119
121
 
120
- - Node.js >= 20
122
+ - Node.js >= 22
121
123
  - `claude` CLI(默认执行器和 LLM 评委需要,参考 [Claude Code](https://claude.ai/code))
122
- - 如果使用其它执行器(openai / gemini)+ `--no-judge` 则可不需要
124
+ - 如果使用其它执行器(openai-api / anthropic-api / gemini)+ `--no-judge` 则可不需要
123
125
 
124
126
  ## 安全说明
125
127
 
@@ -28,6 +28,14 @@
28
28
  * that look like convergence but aren't.
29
29
  */
30
30
  export type SaturationMethod = 'slope' | 'bootstrap-ci-width' | 'plateau-height';
31
+ /** Default consecutive-window run length required before declaring saturation. */
32
+ export declare const DEFAULT_SATURATION_WINDOW_SIZE = 3;
33
+ /**
34
+ * Default `bootstrap-ci-width` cutoff: declare saturation when the relative
35
+ * CI-width shrink per checkpoint stays under 5%. Single source of truth for
36
+ * the documented threshold; guarded by `doc-constants-drift.test.ts`.
37
+ */
38
+ export declare const DEFAULT_CI_WIDTH_SHRINK_THRESHOLD = 0.05;
31
39
  /**
32
40
  * One observation in a saturation curve. `n` is the cumulative sample
33
41
  * count at this checkpoint; `mean` and (optional) `ciWidth` come from
@@ -1 +1 @@
1
- {"version":3,"file":"saturation.d.ts","sourceRoot":"","sources":["../../src/analysis/saturation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;AAEjF;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,SAAS,EAAE,OAAO,CAAC;IACnB,kFAAkF;IAClF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,iFAAiF;IACjF,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,wCAAwC;IACxC,MAAM,EAAE,gBAAgB,CAAC;IACzB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,KAAK,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAC5B,MAAM,GAAE,gBAAuC,EAC/C,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,SAAI,EACd,gBAAgB,SAAO,EACvB,IAAI,CAAC,EAAE,MAAM,GACZ,gBAAgB,CA4GlB;AAUD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAQlE"}
1
+ {"version":3,"file":"saturation.d.ts","sourceRoot":"","sources":["../../src/analysis/saturation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;AAEjF,kFAAkF;AAClF,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,OAAO,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,SAAS,EAAE,OAAO,CAAC;IACnB,kFAAkF;IAClF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,iFAAiF;IACjF,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,wCAAwC;IACxC,MAAM,EAAE,gBAAgB,CAAC;IACzB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,KAAK,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAC5B,MAAM,GAAE,gBAAuC,EAC/C,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,SAAiC,EAC3C,gBAAgB,SAA4B,EAC5C,IAAI,CAAC,EAAE,MAAM,GACZ,gBAAgB,CA4GlB;AAUD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAQlE"}
@@ -27,7 +27,15 @@
27
27
  * window run-of-success is required. This guards against random dips
28
28
  * that look like convergence but aren't.
29
29
  */
30
- import { bootstrapMeanCI } from '../eval-core/bootstrap.js';
30
+ import { bootstrapMeanCI, DEFAULT_BOOTSTRAP_ALPHA, DEFAULT_BOOTSTRAP_SAMPLES } from '../eval-core/bootstrap.js';
31
+ /** Default consecutive-window run length required before declaring saturation. */
32
+ export const DEFAULT_SATURATION_WINDOW_SIZE = 3;
33
+ /**
34
+ * Default `bootstrap-ci-width` cutoff: declare saturation when the relative
35
+ * CI-width shrink per checkpoint stays under 5%. Single source of truth for
36
+ * the documented threshold; guarded by `doc-constants-drift.test.ts`.
37
+ */
38
+ export const DEFAULT_CI_WIDTH_SHRINK_THRESHOLD = 0.05;
31
39
  /**
32
40
  * Compute saturation from a sequence of cumulative score arrays.
33
41
  *
@@ -44,13 +52,13 @@ import { bootstrapMeanCI } from '../eval-core/bootstrap.js';
44
52
  * @param bootstrapSamples Used only by 'bootstrap-ci-width' (default 1000).
45
53
  * @param seed Optional seed for reproducible CIs.
46
54
  */
47
- export function findSaturationPoint(cumulativeScores, method = 'bootstrap-ci-width', threshold, windowSize = 3, bootstrapSamples = 1000, seed) {
55
+ export function findSaturationPoint(cumulativeScores, method = 'bootstrap-ci-width', threshold, windowSize = DEFAULT_SATURATION_WINDOW_SIZE, bootstrapSamples = DEFAULT_BOOTSTRAP_SAMPLES, seed) {
48
56
  const trace = [];
49
57
  const checkpoints = [];
50
58
  for (const scores of cumulativeScores) {
51
59
  if (scores.length === 0)
52
60
  continue;
53
- const ci = bootstrapMeanCI(scores, 0.05, bootstrapSamples, seed);
61
+ const ci = bootstrapMeanCI(scores, DEFAULT_BOOTSTRAP_ALPHA, bootstrapSamples, seed);
54
62
  checkpoints.push({
55
63
  n: scores.length,
56
64
  mean: ci.estimate,
@@ -151,7 +159,7 @@ export function findSaturationPoint(cumulativeScores, method = 'bootstrap-ci-wid
151
159
  function defaultThreshold(method) {
152
160
  switch (method) {
153
161
  case 'slope': return 0.005;
154
- case 'bootstrap-ci-width': return 0.05;
162
+ case 'bootstrap-ci-width': return DEFAULT_CI_WIDTH_SHRINK_THRESHOLD;
155
163
  case 'plateau-height': return 0.1;
156
164
  }
157
165
  }
@@ -1 +1 @@
1
- {"version":3,"file":"saturation.js","sourceRoot":"","sources":["../../src/analysis/saturation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"saturation.js","sourceRoot":"","sources":["../../src/analysis/saturation.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,28 @@ interface WeakSample {
15
15
  none?: string;
16
16
  };
17
17
  }
18
- export declare function extractWeakSamples(report: Report, variantKey: string, count?: number): WeakSample[];
18
+ export declare function extractWeakSamples(report: Report, variantKey: string, count?: number, sampleIdFilter?: Set<string>): WeakSample[];
19
+ /** A train / holdout partition of a sample set. */
20
+ interface HoldoutSplit {
21
+ trainIds: Set<string>;
22
+ holdoutIds: Set<string>;
23
+ }
24
+ /**
25
+ * Deterministically split sample ids into train / holdout by `ratio` (fraction
26
+ * held out). Holdout members are picked at an even stride so the partition is
27
+ * representative of the ordering, and the split is stable across rounds and runs
28
+ * (no RNG). Returns null when ratio ≤ 0 or either side would drop below
29
+ * MIN_HOLDOUT_SUBSET — the caller then scores on the full set.
30
+ */
31
+ export declare function splitHoldout(sampleIds: string[], ratio: number): HoldoutSplit | null;
32
+ /**
33
+ * A view of `report` whose results are restricted to `sampleIds`. Used to keep the
34
+ * holdout split out of the sample-fixer: under an active holdout, only training-split
35
+ * samples may enter the --auto-fix-samples prompt or be rewritten — otherwise the
36
+ * skill's samples get tuned to the very samples that decide acceptance, reintroducing
37
+ * the leak holdout exists to prevent.
38
+ */
39
+ export declare function restrictReportToSamples(report: Report, sampleIds: Set<string>): Report;
19
40
  export declare function allNonTripwireAssertionsPass(report: Report, variantKey: string): boolean;
20
41
  export declare function buildImprovementPrompt(skillContent: string, score: number, weakSamples: WeakSample[]): string;
21
42
  /** @deprecated Use ProgressCallback from evaluation-core.ts */
@@ -61,15 +82,26 @@ interface EvolveOptions {
61
82
  noDiagnostic?: boolean;
62
83
  /** 跳过 doctor 健康检查门禁。默认 false。 */
63
84
  skipDoctor?: boolean;
85
+ /** Fraction of samples held out for the accept decision (0..1). Default 0 = off.
86
+ * When > 0, a candidate is accepted on its **holdout** composite rather than the
87
+ * training composite, and weak-sample extraction only sees the training split —
88
+ * so the skill is never tuned to the samples that judge it. Too small a split
89
+ * (either side < MIN_HOLDOUT_SUBSET) falls back to full-set scoring + a warning. */
90
+ holdoutRatio?: number;
64
91
  onProgress?: ProgressCallback | null;
65
92
  onRoundProgress?: ((progress: EvolveRoundProgressInfo) => void) | null;
66
93
  }
67
94
  interface TrajectoryEntry {
68
95
  round: number;
96
+ /** Accept-decision score: holdout composite when holdout is active, else full-set. */
69
97
  score: number;
70
98
  delta: number;
71
99
  accepted: boolean;
72
100
  costUSD: number;
101
+ /** Present when holdout is active: the training-split composite (improvement signal). */
102
+ trainScore?: number;
103
+ /** Present when holdout is active: the holdout-split composite (== score). */
104
+ holdoutScore?: number;
73
105
  }
74
106
  export interface EvolveResult {
75
107
  startScore: number;
@@ -86,6 +118,15 @@ export interface EvolveResult {
86
118
  reusedBaselineReportId?: string;
87
119
  /** False = 任一轮的 exec / judge 不报 cost → totalCostUSD 是 lower-bound 而非真值。 */
88
120
  costReported?: boolean;
121
+ /** Holdout split summary when `--holdout-ratio` > 0. `disabled` is true when the
122
+ * split was too small and evolve fell back to full-set scoring (CLI formats the
123
+ * user-facing message bilingually). */
124
+ holdout?: {
125
+ ratio: number;
126
+ trainCount: number;
127
+ holdoutCount: number;
128
+ disabled?: boolean;
129
+ };
89
130
  trajectory: TrajectoryEntry[];
90
131
  bestSkillPath: string;
91
132
  allVersions: string[];
@@ -97,6 +138,6 @@ export interface RoundReport {
97
138
  report: Report;
98
139
  }
99
140
  export declare function mergeEvolveReports(roundReports: RoundReport[], skillName: string, totalCostUSD: number, samples?: Sample[], skillPath?: string): Report;
100
- export declare function evolveSkill({ skillPath, samplesPath, rounds, target, stopOnAssertionsPass, autoFixSamples, sampleFixMaxAttempts, reuseLatestEval, model, judgeModels, improveModel, improveMode, executorName, concurrency, timeoutMs, skipConnectivity, effort, noDiagnostic, skipDoctor, onProgress, onRoundProgress, }: EvolveOptions): Promise<EvolveResult>;
141
+ export declare function evolveSkill({ skillPath, samplesPath, rounds, target, stopOnAssertionsPass, autoFixSamples, sampleFixMaxAttempts, reuseLatestEval, model, judgeModels, improveModel, improveMode, executorName, concurrency, timeoutMs, skipConnectivity, effort, noDiagnostic, skipDoctor, holdoutRatio, onProgress, onRoundProgress, }: EvolveOptions): Promise<EvolveResult>;
101
142
  export {};
102
143
  //# sourceMappingURL=evolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"evolver.d.ts","sourceRoot":"","sources":["../../src/authoring/evolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"evolver.d.ts","sourceRoot":"","sources":["../../src/authoring/evolver.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import { persistReport, DEFAULT_OUTPUT_DIR, generateRunId, hashString } from '..
6
6
  import { createFileStore } from '../server/report-store.js';
7
7
  import { analyzeResults } from '../analysis/report-diagnostics.js';
8
8
  import { loadSamples } from '../inputs/load-samples.js';
9
+ import { buildVariantSummary } from '../eval-core/schema.js';
9
10
  import { fixSamples } from './sample-fixer.js';
10
11
  const IMPROVE_SYSTEM_PROMPT = `你是一个 AI 提示词改进专家。你的任务是分析评测结果中的薄弱环节,针对性地改进 skill(系统提示词),使其在评测中获得更高的分数。
11
12
 
@@ -121,9 +122,11 @@ function readSkillName(skillPath) {
121
122
  return null;
122
123
  }
123
124
  }
124
- export function extractWeakSamples(report, variantKey, count = 5) {
125
+ export function extractWeakSamples(report, variantKey, count = 5, sampleIdFilter) {
125
126
  const weakSamples = [];
126
127
  for (const r of report.results) {
128
+ if (sampleIdFilter && !sampleIdFilter.has(r.sample_id))
129
+ continue;
127
130
  const v = r.variants[variantKey];
128
131
  if (!v || typeof v.compositeScore !== 'number')
129
132
  continue;
@@ -153,6 +156,60 @@ export function extractWeakSamples(report, variantKey, count = 5) {
153
156
  .sort((a, b) => a.compositeScore - b.compositeScore)
154
157
  .slice(0, count);
155
158
  }
159
+ /** Below this many samples on either side, a holdout split is too small to be
160
+ * meaningful — evolve falls back to full-set scoring and warns. */
161
+ const MIN_HOLDOUT_SUBSET = 3;
162
+ /**
163
+ * Deterministically split sample ids into train / holdout by `ratio` (fraction
164
+ * held out). Holdout members are picked at an even stride so the partition is
165
+ * representative of the ordering, and the split is stable across rounds and runs
166
+ * (no RNG). Returns null when ratio ≤ 0 or either side would drop below
167
+ * MIN_HOLDOUT_SUBSET — the caller then scores on the full set.
168
+ */
169
+ export function splitHoldout(sampleIds, ratio) {
170
+ if (!(ratio > 0) || sampleIds.length === 0)
171
+ return null;
172
+ const holdoutCount = Math.round(sampleIds.length * ratio);
173
+ const trainCount = sampleIds.length - holdoutCount;
174
+ if (holdoutCount < MIN_HOLDOUT_SUBSET || trainCount < MIN_HOLDOUT_SUBSET)
175
+ return null;
176
+ const stride = sampleIds.length / holdoutCount;
177
+ const holdoutIds = new Set();
178
+ for (let k = 0; k < holdoutCount; k++) {
179
+ holdoutIds.add(sampleIds[Math.floor(k * stride)]);
180
+ }
181
+ const trainIds = new Set(sampleIds.filter((id) => !holdoutIds.has(id)));
182
+ return { trainIds, holdoutIds };
183
+ }
184
+ /**
185
+ * Mean composite over the subset of a report's results whose sample_id is in
186
+ * `ids`, using the same aggregation as the full-run summary
187
+ * (`buildVariantSummary`) so train / holdout scores stay comparable to the
188
+ * headline composite. Returns 0 when the subset has no scorable entries.
189
+ */
190
+ function subsetCompositeScore(report, variantKey, ids) {
191
+ const entries = [];
192
+ for (const r of report.results) {
193
+ if (!ids.has(r.sample_id))
194
+ continue;
195
+ const v = r.variants[variantKey];
196
+ if (v)
197
+ entries.push(v);
198
+ }
199
+ if (entries.length === 0)
200
+ return 0;
201
+ return buildVariantSummary(entries).avgCompositeScore ?? 0;
202
+ }
203
+ /**
204
+ * A view of `report` whose results are restricted to `sampleIds`. Used to keep the
205
+ * holdout split out of the sample-fixer: under an active holdout, only training-split
206
+ * samples may enter the --auto-fix-samples prompt or be rewritten — otherwise the
207
+ * skill's samples get tuned to the very samples that decide acceptance, reintroducing
208
+ * the leak holdout exists to prevent.
209
+ */
210
+ export function restrictReportToSamples(report, sampleIds) {
211
+ return { ...report, results: report.results.filter((r) => sampleIds.has(r.sample_id)) };
212
+ }
156
213
  export function allNonTripwireAssertionsPass(report, variantKey) {
157
214
  for (const entry of report.results) {
158
215
  const variant = entry.variants[variantKey];
@@ -401,7 +458,7 @@ export function mergeEvolveReports(roundReports, skillName, totalCostUSD, sample
401
458
  report.analysis = analyzeResults(report, { samples });
402
459
  return report;
403
460
  }
404
- export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target = null, stopOnAssertionsPass = false, autoFixSamples = false, sampleFixMaxAttempts = 2, reuseLatestEval = false, model = DEFAULT_MODEL, judgeModels, improveModel = DEFAULT_MODEL, improveMode = 'agent', executorName = 'claude', concurrency = 1, timeoutMs, skipConnectivity = false, effort, noDiagnostic, skipDoctor, onProgress = null, onRoundProgress = null, }) {
461
+ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target = null, stopOnAssertionsPass = false, autoFixSamples = false, sampleFixMaxAttempts = 2, reuseLatestEval = false, model = DEFAULT_MODEL, judgeModels, improveModel = DEFAULT_MODEL, improveMode = 'agent', executorName = 'claude', concurrency = 1, timeoutMs, skipConnectivity = false, effort, noDiagnostic, skipDoctor, holdoutRatio = 0, onProgress = null, onRoundProgress = null, }) {
405
462
  if (judgeModels && judgeModels.length > 1) {
406
463
  throw new Error('evolveSkill does not support multi-judge ensemble (received '
407
464
  + `${judgeModels.length} judges). Pass a single-judge array, e.g. `
@@ -421,6 +478,28 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
421
478
  if (!existsSync(absSamplesPath))
422
479
  throw new Error(`samples file not found: ${absSamplesPath}`);
423
480
  mkdirSync(evolveDir, { recursive: true });
481
+ // Holdout split (opt-in). Computed once over the canonical sample order so it's
482
+ // stable across rounds. When active, accept decisions use the holdout composite
483
+ // and weak-sample extraction only sees the training split — the skill is never
484
+ // tuned to the samples that decide whether it's accepted.
485
+ const allSampleIds = loadSamples(absSamplesPath).samples.map((s) => s.sample_id);
486
+ const holdoutSplit = splitHoldout(allSampleIds, holdoutRatio);
487
+ const holdoutInfo = holdoutRatio > 0
488
+ ? {
489
+ ratio: holdoutRatio,
490
+ trainCount: holdoutSplit?.trainIds.size ?? allSampleIds.length,
491
+ holdoutCount: holdoutSplit?.holdoutIds.size ?? 0,
492
+ ...(holdoutSplit ? {} : { disabled: true }),
493
+ }
494
+ : undefined;
495
+ // Accept-decision score for a report's variant: holdout composite when the split
496
+ // is active, otherwise the full-set composite (legacy behavior).
497
+ const decisionScore = (report, key) => holdoutSplit
498
+ ? subsetCompositeScore(report, key, holdoutSplit.holdoutIds)
499
+ : (report.summary[key]?.avgCompositeScore ?? 0);
500
+ const trainScoreOf = (report, key) => holdoutSplit ? subsetCompositeScore(report, key, holdoutSplit.trainIds) : undefined;
501
+ // Per-round trajectory tail: train / holdout breakdown, only when split active.
502
+ const splitScores = (report, key, decision) => holdoutSplit ? { trainScore: trainScoreOf(report, key), holdoutScore: decision } : {};
424
503
  // Save original as r0
425
504
  let currentBest = readFileSync(absSkillPath, 'utf-8').trim();
426
505
  const r0Path = join(evolveDir, `${skillName}.r0.md`);
@@ -461,13 +540,13 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
461
540
  });
462
541
  }
463
542
  const baselineVariantKey = Object.keys(baselineReport.summary)[0];
464
- bestScore = baselineReport.summary[baselineVariantKey]?.avgCompositeScore ?? 0;
543
+ bestScore = decisionScore(baselineReport, baselineVariantKey);
465
544
  const baselineCost = baselineReused ? 0 : baselineReport.meta.totalCostUSD;
466
545
  totalCostUSD += baselineCost;
467
546
  const baselineCostReported = baselineReused || !reportHasUnreportedCost(baselineReport);
468
547
  if (!baselineCostReported)
469
548
  totalCostReported = false;
470
- trajectory.push({ round: 0, score: bestScore, delta: 0, accepted: true, costUSD: baselineCost });
549
+ trajectory.push({ round: 0, score: bestScore, delta: 0, accepted: true, costUSD: baselineCost, ...splitScores(baselineReport, baselineVariantKey, bestScore) });
471
550
  roundReports.push({ round: 0, accepted: true, report: baselineReport });
472
551
  if (onRoundProgress)
473
552
  onRoundProgress({ round: 0, totalRounds: rounds, phase: 'baseline', score: bestScore, costUSD: baselineCost, costReported: baselineCostReported, reused: baselineReused });
@@ -487,6 +566,7 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
487
566
  ...(sampleFixes.length > 0 && { sampleFixes }),
488
567
  ...(reusedBaselineReportId && { reusedBaselineReportId }),
489
568
  ...(totalCostReported ? {} : { costReported: false }),
569
+ ...(holdoutInfo ? { holdout: holdoutInfo } : {}),
490
570
  trajectory,
491
571
  bestSkillPath: allVersions[bestRound],
492
572
  allVersions,
@@ -513,7 +593,7 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
513
593
  totalCostReported = false;
514
594
  }
515
595
  const lastVariantKey = Object.keys(lastReport.summary)[0];
516
- const weakSamples = extractWeakSamples(lastReport, lastVariantKey);
596
+ const weakSamples = extractWeakSamples(lastReport, lastVariantKey, 5, holdoutSplit?.trainIds);
517
597
  // Generate improvement
518
598
  const candidatePath = join(evolveDir, `${skillName}.r${round}.md`);
519
599
  const basePrompt = buildImprovementPrompt(currentBest, bestScore, weakSamples);
@@ -575,7 +655,9 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
575
655
  const sampleFix = await autoFixSamplesAfterSkillRound({
576
656
  samplesPath: absSamplesPath,
577
657
  skillContent: candidateContent,
578
- report: lastReport,
658
+ // Under an active holdout, the sample-fixer may only see training-split samples —
659
+ // never the holdout samples that drive the accept decision (leak guard).
660
+ report: holdoutSplit ? restrictReportToSamples(lastReport, holdoutSplit.trainIds) : lastReport,
579
661
  treatmentKey: lastVariantKey,
580
662
  executorName,
581
663
  model: improveModel,
@@ -593,7 +675,7 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
593
675
  samplesPath: absSamplesPath, skillDir, model, judgeModels: effectiveJudgeModels, executorName, concurrency, timeoutMs, skipConnectivity, effort, noDiagnostic, skipDoctor, onProgress,
594
676
  });
595
677
  const candidateVariantKey = Object.keys(candidateReport.summary)[0];
596
- const candidateScore = candidateReport.summary[candidateVariantKey]?.avgCompositeScore ?? 0;
678
+ const candidateScore = decisionScore(candidateReport, candidateVariantKey);
597
679
  const roundCost = improveCostUSD + preEvalSampleFixCost + candidateReport.meta.totalCostUSD;
598
680
  const roundCostReported = improveCostReported && !reportHasUnreportedCost(candidateReport);
599
681
  if (!roundCostReported)
@@ -612,7 +694,7 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
612
694
  if (accepted)
613
695
  roundReports.push({ round, accepted, report: candidateReport });
614
696
  const roundDelta = candidateScore - trajectory[trajectory.length - 1].score;
615
- trajectory.push({ round, score: candidateScore, delta: roundDelta, accepted, costUSD: roundCost });
697
+ trajectory.push({ round, score: candidateScore, delta: roundDelta, accepted, costUSD: roundCost, ...splitScores(candidateReport, candidateVariantKey, candidateScore) });
616
698
  if (onRoundProgress)
617
699
  onRoundProgress({ round, totalRounds: rounds, phase: 'done', score: candidateScore, delta: roundDelta, accepted, costUSD: roundCost, costReported: roundCostReported });
618
700
  // Early stop
@@ -652,6 +734,7 @@ export async function evolveSkill({ skillPath, samplesPath, rounds = 5, target =
652
734
  ...(sampleFixes.length > 0 && { sampleFixes }),
653
735
  ...(reusedBaselineReportId && { reusedBaselineReportId }),
654
736
  ...(totalCostReported ? {} : { costReported: false }),
737
+ ...(holdoutInfo ? { holdout: holdoutInfo } : {}),
655
738
  trajectory,
656
739
  bestSkillPath: allVersions[bestRound],
657
740
  allVersions,