oh-my-knowledge 1.0.0-beta.1 → 1.0.0-beta.3

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 (120) hide show
  1. package/README.md +1 -0
  2. package/README.zh.md +2 -1
  3. package/dist/assets/agent-skills/omk/references/commands.md +17 -17
  4. package/dist/cli/commands/eval/index.js +17 -17
  5. package/dist/dsh-plugin/core-adapter.js +1 -1
  6. package/dist/eval-core/analysis/runtime.js +30 -3
  7. package/dist/eval-core/analysis/types.d.ts +4 -0
  8. package/dist/eval-core/analysis/types.js +2 -0
  9. package/dist/eval-core/contracts/analysis-bundle.js +25 -1
  10. package/dist/eval-core/contracts/artifacts.d.ts +65 -4
  11. package/dist/eval-core/contracts/artifacts.js +6 -4
  12. package/dist/eval-core/contracts/comparability.d.ts +10 -10
  13. package/dist/eval-core/contracts/comparability.js +42 -42
  14. package/dist/eval-core/contracts/json-schema.d.ts +2 -0
  15. package/dist/eval-core/contracts/json-schema.js +9 -2
  16. package/dist/eval-core/contracts/schemas/v2/analysis-bundle.schema.json +1022 -0
  17. package/dist/eval-core/contracts/schemas/v2/comparability-assessment.schema.json +452 -0
  18. package/dist/eval-core/contracts/schemas/v2/evaluation-report.schema.json +1041 -0
  19. package/dist/eval-core/contracts/schemas/v2/series-analysis-bundle.schema.json +1299 -0
  20. package/dist/eval-core/contracts/series.d.ts +8 -8
  21. package/dist/eval-core/contracts/series.js +2 -2
  22. package/dist/eval-core/index.d.ts +1 -1
  23. package/dist/eval-core/schema-url.d.ts +2 -2
  24. package/dist/eval-core/schema-url.js +3 -3
  25. package/dist/eval-core/schemas.d.ts +1 -0
  26. package/dist/eval-core/schemas.js +8 -0
  27. package/dist/eval-runtime/adapters/executor-fn.d.ts +21 -0
  28. package/dist/eval-runtime/adapters/executor-fn.js +74 -0
  29. package/dist/eval-runtime/adapters/invoke-contract.d.ts +57 -0
  30. package/dist/eval-runtime/adapters/invoke-contract.js +58 -0
  31. package/dist/eval-runtime/adapters/json-executor.d.ts +43 -0
  32. package/dist/eval-runtime/adapters/json-executor.js +142 -0
  33. package/dist/{eval-workflows/runtime-adapter/adapters/shared → eval-runtime/adapters}/same-process.d.ts +27 -24
  34. package/dist/{eval-workflows/runtime-adapter/adapters/shared → eval-runtime/adapters}/same-process.js +1 -1
  35. package/dist/eval-runtime/advanced.d.ts +32 -0
  36. package/dist/eval-runtime/advanced.js +17 -0
  37. package/dist/eval-runtime/builders/exact-match.d.ts +31 -0
  38. package/dist/eval-runtime/builders/exact-match.js +48 -0
  39. package/dist/eval-runtime/builders/paired-comparison.d.ts +42 -0
  40. package/dist/eval-runtime/builders/paired-comparison.js +123 -0
  41. package/dist/eval-runtime/builders/policy.d.ts +11 -0
  42. package/dist/eval-runtime/builders/policy.js +57 -0
  43. package/dist/eval-runtime/clock.d.ts +3 -0
  44. package/dist/eval-runtime/clock.js +25 -0
  45. package/dist/eval-runtime/conformance/executor.d.ts +46 -0
  46. package/dist/eval-runtime/conformance/executor.js +231 -0
  47. package/dist/eval-runtime/contracts.d.ts +4 -0
  48. package/dist/eval-runtime/contracts.js +2 -0
  49. package/dist/eval-runtime/engine.d.ts +3 -0
  50. package/dist/eval-runtime/engine.js +3 -0
  51. package/dist/eval-runtime/evaluate.d.ts +219 -0
  52. package/dist/eval-runtime/evaluate.js +649 -0
  53. package/dist/eval-runtime/evaluators/exact-match.d.ts +16 -0
  54. package/dist/eval-runtime/evaluators/exact-match.js +70 -0
  55. package/dist/eval-runtime/identity.d.ts +56 -0
  56. package/dist/eval-runtime/identity.js +88 -0
  57. package/dist/eval-runtime/index.d.ts +3 -0
  58. package/dist/eval-runtime/index.js +2 -0
  59. package/dist/eval-runtime/judges/invocation.d.ts +43 -0
  60. package/dist/eval-runtime/judges/invocation.js +72 -0
  61. package/dist/eval-runtime/judges/rubric-contracts.d.ts +57 -0
  62. package/dist/eval-runtime/judges/rubric-contracts.js +74 -0
  63. package/dist/eval-runtime/judges/rubric-judge.d.ts +83 -0
  64. package/dist/eval-runtime/judges/rubric-judge.js +472 -0
  65. package/dist/eval-runtime/judges/rubric-kit.d.ts +51 -0
  66. package/dist/eval-runtime/judges/rubric-kit.js +139 -0
  67. package/dist/eval-runtime/judges/rubric-prompt.d.ts +5 -0
  68. package/dist/eval-runtime/judges/rubric-prompt.js +59 -0
  69. package/dist/eval-runtime/judges/trace-summary.d.ts +8 -0
  70. package/dist/eval-runtime/judges/trace-summary.js +98 -0
  71. package/dist/eval-runtime/runner.d.ts +34 -0
  72. package/dist/eval-runtime/runner.js +93 -0
  73. package/dist/eval-runtime/runtime.d.ts +35 -0
  74. package/dist/eval-runtime/runtime.js +111 -0
  75. package/dist/eval-runtime/traces/source-neutral.d.ts +57 -0
  76. package/dist/eval-runtime/traces/source-neutral.js +216 -0
  77. package/dist/eval-workflows/artifact-store/contracts.d.ts +4 -4
  78. package/dist/eval-workflows/instruments/judge-trace.d.ts +1 -8
  79. package/dist/eval-workflows/instruments/judge-trace.js +1 -94
  80. package/dist/eval-workflows/instruments/judge.d.ts +2 -2
  81. package/dist/eval-workflows/instruments/judge.js +2 -2
  82. package/dist/eval-workflows/instruments/prompts/debias-instructions.d.ts +1 -2
  83. package/dist/eval-workflows/instruments/prompts/debias-instructions.js +1 -16
  84. package/dist/eval-workflows/instruments/prompts/judge-prompts.d.ts +11 -8
  85. package/dist/eval-workflows/instruments/prompts/judge-prompts.js +4 -54
  86. package/dist/eval-workflows/production-host/node-cli-evaluation-resolver.js +41 -3
  87. package/dist/eval-workflows/production-host/runtime-registry.d.ts +1 -2
  88. package/dist/eval-workflows/production-host/runtime-registry.js +2 -24
  89. package/dist/eval-workflows/runtime-adapter/adapters/anthropic/api.js +1 -1
  90. package/dist/eval-workflows/runtime-adapter/adapters/claude/cli.js +1 -1
  91. package/dist/eval-workflows/runtime-adapter/adapters/claude/resources.d.ts +1 -1
  92. package/dist/eval-workflows/runtime-adapter/adapters/claude/sdk.js +1 -1
  93. package/dist/eval-workflows/runtime-adapter/adapters/codex/cli-protocol.d.ts +1 -1
  94. package/dist/eval-workflows/runtime-adapter/adapters/codex/cli.js +1 -1
  95. package/dist/eval-workflows/runtime-adapter/adapters/codex/resources.d.ts +1 -1
  96. package/dist/eval-workflows/runtime-adapter/adapters/codex/sdk-protocol.d.ts +1 -1
  97. package/dist/eval-workflows/runtime-adapter/adapters/codex/sdk.js +1 -1
  98. package/dist/eval-workflows/runtime-adapter/adapters/custom/command.js +1 -1
  99. package/dist/eval-workflows/runtime-adapter/adapters/index.d.ts +1 -1
  100. package/dist/eval-workflows/runtime-adapter/adapters/index.js +1 -1
  101. package/dist/eval-workflows/runtime-adapter/adapters/openai/api.js +1 -1
  102. package/dist/eval-workflows/runtime-adapter/adapters/shared/omk-resource-same-process.d.ts +15 -0
  103. package/dist/eval-workflows/runtime-adapter/adapters/shared/omk-resource-same-process.js +7 -0
  104. package/dist/eval-workflows/runtime-adapter/adapters/shared/stateless-api-resources.d.ts +1 -1
  105. package/dist/eval-workflows/runtime-adapter/analysis/assertion-layer-node.js +6 -1
  106. package/dist/eval-workflows/runtime-adapter/builtins.js +1 -1
  107. package/dist/eval-workflows/runtime-adapter/evaluators/execution-assertions.d.ts +1 -1
  108. package/dist/eval-workflows/runtime-adapter/evaluators/llm-assertions.d.ts +2 -2
  109. package/dist/eval-workflows/runtime-adapter/evaluators/llm-assertions.js +1 -1
  110. package/dist/eval-workflows/runtime-adapter/evaluators/llm-judge-invocation.d.ts +2 -43
  111. package/dist/eval-workflows/runtime-adapter/evaluators/llm-judge-invocation.js +1 -72
  112. package/dist/eval-workflows/runtime-adapter/evaluators/output-assertions.d.ts +1 -1
  113. package/dist/eval-workflows/runtime-adapter/evaluators/rubric-judge.d.ts +4 -71
  114. package/dist/eval-workflows/runtime-adapter/evaluators/rubric-judge.js +19 -404
  115. package/dist/eval-workflows/runtime-adapter/source-neutral-trace.d.ts +1 -57
  116. package/dist/eval-workflows/runtime-adapter/source-neutral-trace.js +1 -66
  117. package/dist/index.d.ts +2 -4
  118. package/dist/index.js +2 -4
  119. package/dist/studio/core-runs/renderer.js +1 -1
  120. package/package.json +14 -1
package/README.md CHANGED
@@ -228,6 +228,7 @@ The full docs are published at **[oh-my-knowledge.pages.dev](https://oh-my-knowl
228
228
  - **[CLI reference](docs/reference/cli.md)** — all top-level commands with bash examples and flag tables
229
229
  - **[Migrate to the 1.0 preview](docs/guides/v1-preview-migration.md)** — install channel, storage reset, sample protocol, CLI automation, and embedded API changes since 0.54
230
230
  - **[Evaluation Core cutover](docs/guides/eval-core-cutover.md)** — `BREAKING-SCHEMA` storage, resume, Studio, Gold, managed-evidence, and evolve migration
231
+ - **[Embed OMK in a service](docs/guides/eval-runtime.md)** — one `evaluate()` API for Node.js and FaaS hosts
231
232
  - **[Storage layout v2](docs/specs/storage-layout-spec.md)** — project/global domains, compatibility boundary, and Git policy
232
233
  - **[Executors](docs/reference/executors.md)** & **[artifact layout](docs/reference/artifact-layout.md)** — built-in / custom executors; how `variant` resolves to an artifact + runtime context
233
234
  - **[How-to guides](docs/guides/agent-eval.md)** — [evaluate an agent](docs/guides/agent-eval.md) (project runtime context) and [use non-Claude models](docs/guides/non-claude-models.md) (GLM / Qwen / DeepSeek / Moonshot / Ollama)
package/README.zh.md CHANGED
@@ -157,7 +157,7 @@ RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比
157
157
 
158
158
  | 特性 | 说明 |
159
159
  |------|------|
160
- | **Core 发布决定** | 六种结论 + 稳定 reason code + exit code 路由;Studio 投影同一份经过认证的 Decision |
160
+ | **Core 发布决定** | 六种判定 + 稳定 reason code + exit code 路由;Studio 投影同一份经过认证的 Decision |
161
161
  | **五层 evidence graph** | Assertion / LLM / Judge / Dimension / Composite 保持独立,coverage、成本、状态与 lineage 与分数正交 |
162
162
  | **多执行器** | 支持 Claude CLI / Claude SDK / Codex CLI / Codex SDK / DeepSeek Harness / OpenAI / Anthropic API 及自定义命令 |
163
163
  | **30+ 种断言** | 包含子串、正则、JSON Schema、ROUGE/BLEU/Levenshtein 相似度、Agent 工具调用、语义相似度、自定义函数等 |
@@ -228,6 +228,7 @@ omk-mcp
228
228
  - **[CLI 参考](docs/zh/reference/cli.md)** —— 顶层命令的 bash 示例和 flag 表
229
229
  - **[迁移到 1.0 预览版](docs/zh/guides/v1-preview-migration.md)** —— 从 `0.54` 升级时的安装渠道、存储重建、用例协议、CLI 自动化与嵌入式 API 变化
230
230
  - **[Evaluation Core 生产切换](docs/zh/guides/eval-core-cutover.md)** —— `BREAKING-SCHEMA` 存储、resume、Studio、Gold、受管证据与 evolve 迁移
231
+ - **[在服务中嵌入 OMK](docs/zh/guides/eval-runtime.md)** —— 面向 Node.js 与 FaaS 宿主的单一 `evaluate()` API
231
232
  - **[存储布局 v2](docs/zh/specs/storage-layout-spec.md)** —— 项目/全局领域、迁移兼容与 Git 策略
232
233
  - **[执行器](docs/zh/reference/executors.md)** & **[知识载体布局](docs/zh/reference/artifact-layout.md)** —— 内置 / 自定义执行器;variant 如何解析为 artifact + runtime context
233
234
  - **[操作指南](docs/zh/guides/agent-eval.md)** —— [评测 agent](docs/zh/guides/agent-eval.md)(项目 runtime context)与[使用非 Claude 模型](docs/zh/guides/non-claude-models.md)(GLM / 通义 / DeepSeek / Moonshot / Ollama)
@@ -63,7 +63,7 @@ omk doctor --json --gate
63
63
 
64
64
  ## omk eval
65
65
 
66
- 跑评测:对一个 control vs 多个 treatment skill 做对照试验,产 verdict 报告。
66
+ 跑评测:用一个对照组与多个实验组 skill 做对照试验,生成判定报告。
67
67
 
68
68
  **用法:**
69
69
 
@@ -73,16 +73,16 @@ omk eval [flags]
73
73
 
74
74
  **Flags:**
75
75
 
76
- - `--batch` `boolean`:batch 模式:baseline vs 每个 skill
76
+ - `--batch` `boolean`:batch 模式:baseline 作为对照组,逐个 skill 作为实验组
77
77
  - `--bootstrap` `boolean`:加 bootstrap CI
78
78
  - `--bootstrap-samples` `option`:bootstrap 重采样次数,默认 1000
79
- - `--budget-per-sample-ms` `option`:单 sample 时长上限 ms(必须 > 0,不传则无上限)
80
- - `--budget-per-sample-usd` `option`:单 sample 预算上限 USD(必须 > 0,不传则无上限)
79
+ - `--budget-per-sample-ms` `option`:单用例时长上限 ms(必须 > 0,不传则无上限)
80
+ - `--budget-per-sample-usd` `option`:单用例预算上限 USD(必须 > 0,不传则无上限)
81
81
  - `--budget-usd` `option`:总预算上限 USD(必须 > 0,不传则无上限)
82
82
  - `--concurrency` `option`:并发数,默认 1
83
83
  - `--config` `option`:eval.yaml 路径
84
- - `--control` `option`:control variant 表达式(仅 artifact 身份)
85
- - `--control-cwd` `option`:control runtime context 目录
84
+ - `--control` `option`:对照组(control)的 variant 表达式(仅 artifact 身份)
85
+ - `--control-cwd` `option`:对照组(control)的 runtime context 目录
86
86
  - `--dry-run` `boolean`:只 plan 不实跑
87
87
  - `--effort` `option`:被测 LLM 扩展思考预算 low/medium/high/xhigh/max(默认 low;跨 effort 报告不严格可比)。
88
88
  - `--executor` `option`:执行器:claude / claude-sdk / codex / codex-sdk / anthropic-api / openai-api / 自定义命令。Codex 任务内自动用 codex;也可用 OMK_EXECUTOR 设置环境偏好。
@@ -90,7 +90,7 @@ omk eval [flags]
90
90
  - `--gold-dir` `option`:gold dataset 目录
91
91
  - `--holdout-ratio` `option`:留出比例 0-1(如 0.3);切出 holdout 子集,对比 train/holdout 综合分检测过拟合
92
92
  - `--judge-models` `option`:评委配置,格式 executor:model[,...],例 claude:haiku 或 codex:<model>(≥ 2 个 = ensemble)。默认跟随所选执行器;Codex 沿用被测模型。
93
- - `--judge-repeat` `option`:每个 dim 评 N 次
93
+ - `--judge-repeat` `option`:每个维度由评委评价 N 次
94
94
  - `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
95
95
  - `--layered-stats` `boolean`:输出分层统计
96
96
  - `--mcp-config` `option`:MCP 配置文件路径
@@ -99,30 +99,30 @@ omk eval [flags]
99
99
  - `--no-debias-length` `boolean`:关 length-debias(默认开)
100
100
  - `--no-diagnostic` `boolean`:关闭基于 Core 失败、缺失、排除与稳定 reason code 的诊断投影。
101
101
  - `--no-evidence` `boolean`:不把本次评测写成证据追加进受管记录(默认会为已 install 的 skill 自动写)。
102
- - `--no-gate` `boolean`:关 verdict gate
103
- - `--no-judge` `boolean`:跳过 LLM judge
102
+ - `--no-gate` `boolean`:关闭判定门禁
103
+ - `--no-judge` `boolean`:跳过 LLM 评委
104
104
  - `--no-serve` `boolean`:不启 report server
105
105
  - `--no-strict-baseline` `boolean`:关闭 baseline 隔离
106
106
  - `--output-dir` `option`:报告输出目录(默认项目级 .omk/eval)
107
- - `--repeat` `option`:每个 sample 重复跑 N 次
108
- - `--report-only` `boolean`:生成报告并打印 verdict,但始终 exit 0(不参与 CI gate)。
107
+ - `--repeat` `option`:每个用例重复运行 N 次
108
+ - `--report-only` `boolean`:生成报告并打印判定,但始终 exit 0(不参与 CI gate)。
109
109
  - `--resume` `option`:复用经过完整契约校验的 Core runId;拒绝时失败关闭
110
- - `--retry` `option`:失败 sample 重试次数
111
- - `--samples` `option`:用例路径。自动发现项目级或单 treatment 目录 skill 下的 eval-samples.json / eval-samples.yaml;显式路径可为 JSON / YAML 文件或分片目录。
110
+ - `--retry` `option`:单用例失败重试次数
111
+ - `--samples` `option`:用例路径。自动发现项目级或单个实验组(treatment)目录 skill 下的 eval-samples.json / eval-samples.yaml;显式路径可为 JSON / YAML 文件或分片目录。
112
112
  - `--skill-dir` `option`:skill 目录,默认 skills
113
113
  - `--skip-connectivity` `boolean`:跳 LLM 连通性预检
114
114
  - `--skip-doctor` `boolean`:escape hatch:跳 doctor 健康检查门禁(默认强制启用)。沙箱 mock 提供依赖时绕开 doctor 物理路径误报;garbage-in 风险自负。
115
115
  - `--strict-baseline` `boolean`:强制 baseline 隔离(default true)
116
- - `--threshold` `option`:verdict 阈值,默认 3.5
116
+ - `--threshold` `option`:判定阈值,默认 3.5
117
117
  - `--timeout` `option`:单用例超时秒,默认 120
118
- - `--treatment` `option`:treatment variant 列表,逗号分隔(仅 artifact 身份)
119
- - `--treatment-cwd` `option`:treatment runtime context 目录列表,逗号分隔、与 --treatment 按序对齐(空位 = 无 cwd)
118
+ - `--treatment` `option`:实验组(treatment)的 variant 列表,逗号分隔(仅 artifact 身份)
119
+ - `--treatment-cwd` `option`:实验组(treatment)的 runtime context 目录列表,逗号分隔、与 --treatment 按序对齐(空位 = 无 cwd)
120
120
  - `--trivial-diff` `option`:可忽略 diff 容差,0 表示不启用容差
121
121
  - `--verbose` `boolean`:详细日志
122
122
 
123
123
  **示例:**
124
124
 
125
- > 最简对照:baseline vs my-skill
125
+ > 最简对照:baseline 作为对照组,my-skill 作为实验组
126
126
 
127
127
  ```bash
128
128
  omk eval --control baseline --treatment my-skill
@@ -174,13 +174,13 @@ async function runEval(_args, flags, lang) {
174
174
  }
175
175
  export default class Eval extends BaseCommand {
176
176
  static description = bilingual({
177
- zh: '跑评测:对一个 control vs 多个 treatment skill 做对照试验,产 verdict 报告。',
177
+ zh: '跑评测:用一个对照组与多个实验组 skill 做对照试验,生成判定报告。',
178
178
  en: 'Run evaluation: control vs treatment(s) comparison, produce verdict report.',
179
179
  });
180
180
  static examples = [
181
181
  {
182
182
  description: bilingual({
183
- zh: '最简对照:baseline vs my-skill',
183
+ zh: '最简对照:baseline 作为对照组,my-skill 作为实验组',
184
184
  en: 'Minimal A/B: baseline vs my-skill',
185
185
  }),
186
186
  command: '<%= config.bin %> eval --control baseline --treatment my-skill',
@@ -197,23 +197,23 @@ export default class Eval extends BaseCommand {
197
197
  lang: LANG_FLAG,
198
198
  // ── 实验角色 ──
199
199
  control: Flags.string({
200
- description: bilingual({ zh: 'control variant 表达式(仅 artifact 身份)', en: 'Control variant expr (artifact identity only)' }),
200
+ description: bilingual({ zh: '对照组(control)的 variant 表达式(仅 artifact 身份)', en: 'Control variant expr (artifact identity only)' }),
201
201
  }),
202
202
  treatment: Flags.string({
203
203
  description: bilingual({
204
- zh: 'treatment variant 列表,逗号分隔(仅 artifact 身份)',
204
+ zh: '实验组(treatment)的 variant 列表,逗号分隔(仅 artifact 身份)',
205
205
  en: 'Treatment variants, comma-separated (artifact identity only)',
206
206
  }),
207
207
  }),
208
208
  'control-cwd': Flags.string({
209
209
  description: bilingual({
210
- zh: 'control runtime context 目录',
210
+ zh: '对照组(control)的 runtime context 目录',
211
211
  en: 'Runtime context dir for control',
212
212
  }),
213
213
  }),
214
214
  'treatment-cwd': Flags.string({
215
215
  description: bilingual({
216
- zh: 'treatment runtime context 目录列表,逗号分隔、与 --treatment 按序对齐(空位 = 无 cwd)',
216
+ zh: '实验组(treatment)的 runtime context 目录列表,逗号分隔、与 --treatment 按序对齐(空位 = 无 cwd)',
217
217
  en: 'Runtime context dirs for treatments, comma-separated, index-aligned with --treatment (blank = none)',
218
218
  }),
219
219
  }),
@@ -222,7 +222,7 @@ export default class Eval extends BaseCommand {
222
222
  }),
223
223
  samples: Flags.string({
224
224
  description: bilingual({
225
- zh: '用例路径。自动发现项目级或单 treatment 目录 skill 下的 eval-samples.json / eval-samples.yaml;显式路径可为 JSON / YAML 文件或分片目录。',
225
+ zh: '用例路径。自动发现项目级或单个实验组(treatment)目录 skill 下的 eval-samples.json / eval-samples.yaml;显式路径可为 JSON / YAML 文件或分片目录。',
226
226
  en: 'Samples path. Auto-discovers eval-samples.json / eval-samples.yaml at project scope or for a single directory-skill treatment; an explicit path may be a JSON / YAML file or split directory.',
227
227
  }),
228
228
  }),
@@ -256,7 +256,7 @@ export default class Eval extends BaseCommand {
256
256
  }),
257
257
  // ── 评测 toggle ──
258
258
  'no-judge': Flags.boolean({
259
- description: bilingual({ zh: '跳过 LLM judge', en: 'Skip LLM judge' }),
259
+ description: bilingual({ zh: '跳过 LLM 评委', en: 'Skip LLM judge' }),
260
260
  }),
261
261
  'no-cache': Flags.boolean({
262
262
  description: bilingual({ zh: '跳过 executor cache', en: 'Skip executor cache' }),
@@ -277,7 +277,7 @@ export default class Eval extends BaseCommand {
277
277
  }),
278
278
  batch: Flags.boolean({
279
279
  description: bilingual({
280
- zh: 'batch 模式:baseline vs 每个 skill',
280
+ zh: 'batch 模式:baseline 作为对照组,逐个 skill 作为实验组',
281
281
  en: 'Batch mode: baseline vs each skill',
282
282
  }),
283
283
  }),
@@ -300,7 +300,7 @@ export default class Eval extends BaseCommand {
300
300
  description: bilingual({ zh: '详细日志', en: 'Verbose logging' }),
301
301
  }),
302
302
  retry: Flags.string({
303
- description: bilingual({ zh: '失败 sample 重试次数', en: 'Per-sample retry count' }),
303
+ description: bilingual({ zh: '单用例失败重试次数', en: 'Per-sample retry count' }),
304
304
  parse: integerStringParser('--retry', { min: 0 }),
305
305
  }),
306
306
  resume: Flags.string({
@@ -333,7 +333,7 @@ export default class Eval extends BaseCommand {
333
333
  }),
334
334
  // ── eval-runner extra ──
335
335
  repeat: Flags.string({
336
- description: bilingual({ zh: '每个 sample 重复跑 N 次', en: 'Repeat each sample N times' }),
336
+ description: bilingual({ zh: '每个用例重复运行 N 次', en: 'Repeat each sample N times' }),
337
337
  parse: integerStringParser('--repeat', { min: 1 }),
338
338
  }),
339
339
  'holdout-ratio': Flags.string({
@@ -344,7 +344,7 @@ export default class Eval extends BaseCommand {
344
344
  parse: numberStringParser('--holdout-ratio', { min: 0, max: 1 }),
345
345
  }),
346
346
  'judge-repeat': Flags.string({
347
- description: bilingual({ zh: '每个 dim 评 N 次', en: 'Judge each dim N times' }),
347
+ description: bilingual({ zh: '每个维度由评委评价 N 次', en: 'Judge each dim N times' }),
348
348
  parse: integerStringParser('--judge-repeat', { min: 1 }),
349
349
  }),
350
350
  bootstrap: Flags.boolean({
@@ -365,16 +365,16 @@ export default class Eval extends BaseCommand {
365
365
  parse: numberStringParser('--budget-usd', { minExclusive: 0 }),
366
366
  }),
367
367
  'budget-per-sample-usd': Flags.string({
368
- description: bilingual({ zh: ' sample 预算上限 USD(必须 > 0,不传则无上限)', en: 'Per-sample budget cap USD (must be > 0; omit for no cap)' }),
368
+ description: bilingual({ zh: '单用例预算上限 USD(必须 > 0,不传则无上限)', en: 'Per-sample budget cap USD (must be > 0; omit for no cap)' }),
369
369
  parse: numberStringParser('--budget-per-sample-usd', { minExclusive: 0 }),
370
370
  }),
371
371
  'budget-per-sample-ms': Flags.string({
372
- description: bilingual({ zh: ' sample 时长上限 ms(必须 > 0,不传则无上限)', en: 'Per-sample time cap ms (must be > 0; omit for no cap)' }),
372
+ description: bilingual({ zh: '单用例时长上限 ms(必须 > 0,不传则无上限)', en: 'Per-sample time cap ms (must be > 0; omit for no cap)' }),
373
373
  parse: integerStringParser('--budget-per-sample-ms', { min: 1 }),
374
374
  }),
375
375
  threshold: Flags.string({
376
376
  description: bilingual({
377
- zh: `verdict 阈值,默认 ${DEFAULT_GATE_THRESHOLD}`,
377
+ zh: `判定阈值,默认 ${DEFAULT_GATE_THRESHOLD}`,
378
378
  en: `Verdict threshold, default ${DEFAULT_GATE_THRESHOLD}`,
379
379
  }),
380
380
  parse: numberStringParser('--threshold'),
@@ -385,12 +385,12 @@ export default class Eval extends BaseCommand {
385
385
  }),
386
386
  'report-only': Flags.boolean({
387
387
  description: bilingual({
388
- zh: '生成报告并打印 verdict,但始终 exit 0(不参与 CI gate)。',
388
+ zh: '生成报告并打印判定,但始终 exit 0(不参与 CI gate)。',
389
389
  en: 'Produce the report and print verdict, but always exit 0 (no CI gate).',
390
390
  }),
391
391
  }),
392
392
  'no-gate': Flags.boolean({
393
- description: bilingual({ zh: '关 verdict gate', en: 'Disable verdict gate' }),
393
+ description: bilingual({ zh: '关闭判定门禁', en: 'Disable verdict gate' }),
394
394
  }),
395
395
  'no-evidence': Flags.boolean({
396
396
  description: bilingual({
@@ -3,7 +3,7 @@ import { JsonValueSchema, RuntimeIdentitySchema, canonicalizeJson, deepFreezeCan
3
3
  import { ExecutionPortFailure, } from '../eval-core/execution/index.js';
4
4
  import { createDshHostRuntimeFingerprint } from '../executors/core/runtime-fingerprint.js';
5
5
  import { captureClaudeCliRunState, captureClaudeCliTarget, disposeClaudeCliTrial, openClaudeCliTrial, } from '../eval-workflows/runtime-adapter/adapters/claude/resources.js';
6
- import { createSameProcessExecutorAdapter } from '../eval-workflows/runtime-adapter/adapters/shared/same-process.js';
6
+ import { createSameProcessExecutorAdapter } from '../eval-runtime/adapters/same-process.js';
7
7
  import { DSH_HOST_CORE_ADAPTER_IMPLEMENTATION_VERSION, dshHostCoreExecutorCapabilities, parseDshHostCoreResult, } from './core-protocol.js';
8
8
  import { supportsDshTraceEventType } from './trace-adapter.js';
9
9
  export { DSH_HOST_CORE_ADAPTER_IMPLEMENTATION_VERSION, createDshHostCoreSchemaValidators, } from './core-protocol.js';
@@ -260,20 +260,28 @@ function rowsForInputs(inputs) {
260
260
  }
261
261
  return [...byId.values()].sort((left, right) => compareStrings(left.rowId, right.rowId));
262
262
  }
263
- function observationCoverage(rows, includedRowIds, comparableRowIds) {
263
+ function observationCoverage(rows, includedRowIds, comparableRowIds, notApplicableRowIds = []) {
264
264
  const included = new Set(includedRowIds);
265
265
  const comparable = new Set(comparableRowIds);
266
+ const notApplicable = new Set(notApplicableRowIds);
266
267
  const known = new Set(rows.map((row) => row.rowId));
267
268
  if (included.size !== includedRowIds.length
268
269
  || comparable.size !== comparableRowIds.length
270
+ || notApplicable.size !== notApplicableRowIds.length
269
271
  || [...included].some((rowId) => !known.has(rowId))
270
- || [...comparable].some((rowId) => !included.has(rowId))) {
272
+ || [...comparable].some((rowId) => !included.has(rowId))
273
+ || [...notApplicable].some((rowId) => !known.has(rowId) || included.has(rowId))) {
271
274
  throw new TypeError('Analysis implementation returned invalid row membership.');
272
275
  }
276
+ if (rows.some((row) => (notApplicable.has(row.rowId)
277
+ && (row.rowStatus !== 'missing' || row.censored)))) {
278
+ throw new TypeError('Analysis implementation marked an ineligible row as not applicable.');
279
+ }
273
280
  const coverage = {
274
281
  planned: rows.length,
275
282
  observed: 0,
276
283
  missing: 0,
284
+ notApplicable: notApplicable.size,
277
285
  invalid: 0,
278
286
  evaluationFailed: 0,
279
287
  sourceUnavailable: 0,
@@ -284,6 +292,8 @@ function observationCoverage(rows, includedRowIds, comparableRowIds) {
284
292
  comparable: comparable.size,
285
293
  };
286
294
  for (const row of rows) {
295
+ if (notApplicable.has(row.rowId))
296
+ continue;
287
297
  if (row.rowStatus === 'observed')
288
298
  coverage.observed += 1;
289
299
  else if (row.rowStatus === 'missing')
@@ -319,6 +329,18 @@ function exclusionFacts(rows, includedRowIds) {
319
329
  }))
320
330
  .sort((left, right) => compareStrings(left.rowId, right.rowId));
321
331
  }
332
+ function notApplicableFacts(rows, notApplicableRowIds) {
333
+ const notApplicable = new Set(notApplicableRowIds);
334
+ return rows
335
+ .filter((row) => notApplicable.has(row.rowId))
336
+ .map((row) => {
337
+ if (row.rowStatus === 'observed') {
338
+ throw new TypeError('Observed Analysis rows cannot be structurally not applicable.');
339
+ }
340
+ return { rowId: row.rowId, reasonCode: row.reasonCode };
341
+ })
342
+ .sort((left, right) => compareStrings(left.rowId, right.rowId));
343
+ }
322
344
  function safeError(error) {
323
345
  if (error instanceof AnalysisPortFailure) {
324
346
  const parsed = EvaluationErrorSchema.safeParse(error.evaluationError);
@@ -583,6 +605,7 @@ async function runAnalysis(plan, ports, options, prepared, stream) {
583
605
  inputReferences: binding.node.inputs,
584
606
  coverage: emptyCoverage(rows),
585
607
  exclusions: exclusionFacts(rows, []),
608
+ notApplicableRows: [],
586
609
  assumptionChecks: [],
587
610
  analysisMode: plan.analysis.analysisGraph.analysisMode,
588
611
  derivedAt: ports.clock.timestamp(),
@@ -735,9 +758,10 @@ async function runAnalysis(plan, ports, options, prepared, stream) {
735
758
  .map((row) => row.rowId);
736
759
  const includedRowIds = output.includedRowIds ?? observedRowIds;
737
760
  const comparableRowIds = output.comparableRowIds ?? includedRowIds;
761
+ const notApplicableRowIds = output.notApplicableRowIds ?? [];
738
762
  let coverage;
739
763
  try {
740
- coverage = observationCoverage(rows, includedRowIds, comparableRowIds);
764
+ coverage = observationCoverage(rows, includedRowIds, comparableRowIds, notApplicableRowIds);
741
765
  }
742
766
  catch (error) {
743
767
  const evaluationError = safeError(error);
@@ -784,6 +808,7 @@ async function runAnalysis(plan, ports, options, prepared, stream) {
784
808
  ...base,
785
809
  coverage,
786
810
  exclusions: exclusionFacts(rows, includedRowIds),
811
+ notApplicableRows: notApplicableFacts(rows, notApplicableRowIds),
787
812
  assumptionChecks: checks,
788
813
  }, runtimeDependencies, {
789
814
  analysisStatus: 'inconclusive',
@@ -832,6 +857,7 @@ async function runAnalysis(plan, ports, options, prepared, stream) {
832
857
  ...base,
833
858
  coverage,
834
859
  exclusions: exclusionFacts(rows, includedRowIds),
860
+ notApplicableRows: notApplicableFacts(rows, notApplicableRowIds),
835
861
  assumptionChecks: checks,
836
862
  }, runtimeDependencies, {
837
863
  analysisStatus: 'completed',
@@ -852,6 +878,7 @@ async function runAnalysis(plan, ports, options, prepared, stream) {
852
878
  ...base,
853
879
  coverage,
854
880
  exclusions: exclusionFacts(rows, includedRowIds),
881
+ notApplicableRows: notApplicableFacts(rows, notApplicableRowIds),
855
882
  assumptionChecks: checks,
856
883
  }, runtimeDependencies, {
857
884
  analysisStatus: 'inconclusive',
@@ -80,12 +80,14 @@ export type AnalysisNodeExecutionResult = {
80
80
  value: JsonValue;
81
81
  includedRowIds?: readonly Sha256Digest[];
82
82
  comparableRowIds?: readonly Sha256Digest[];
83
+ notApplicableRowIds?: readonly Sha256Digest[];
83
84
  assumptionChecks?: readonly Omit<AssumptionCheck, 'nodeId'>[];
84
85
  } | {
85
86
  analysisStatus: 'inconclusive';
86
87
  reasonCodes: readonly string[];
87
88
  includedRowIds?: readonly Sha256Digest[];
88
89
  comparableRowIds?: readonly Sha256Digest[];
90
+ notApplicableRowIds?: readonly Sha256Digest[];
89
91
  assumptionChecks?: readonly Omit<AssumptionCheck, 'nodeId'>[];
90
92
  };
91
93
  export declare const AnalysisNodeExecutionResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -101,6 +103,7 @@ export declare const AnalysisNodeExecutionResultSchema: z.ZodDiscriminatedUnion<
101
103
  value: z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>;
102
104
  includedRowIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
105
  comparableRowIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
106
+ notApplicableRowIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
104
107
  assumptionChecks: z.ZodOptional<z.ZodArray<z.ZodObject<{
105
108
  details: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
106
109
  reasonCode: z.ZodOptional<z.ZodString>;
@@ -116,6 +119,7 @@ export declare const AnalysisNodeExecutionResultSchema: z.ZodDiscriminatedUnion<
116
119
  reasonCodes: z.ZodArray<z.ZodString>;
117
120
  includedRowIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
118
121
  comparableRowIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
122
+ notApplicableRowIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
119
123
  assumptionChecks: z.ZodOptional<z.ZodArray<z.ZodObject<{
120
124
  details: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
121
125
  reasonCode: z.ZodOptional<z.ZodString>;
@@ -26,6 +26,7 @@ export const AnalysisNodeExecutionResultSchema = z.discriminatedUnion('analysisS
26
26
  value: JsonValueSchema,
27
27
  includedRowIds: z.array(Sha256DigestSchema).optional(),
28
28
  comparableRowIds: z.array(Sha256DigestSchema).optional(),
29
+ notApplicableRowIds: z.array(Sha256DigestSchema).optional(),
29
30
  assumptionChecks: z.array(PortAssumptionCheckSchema).optional(),
30
31
  }).strict(),
31
32
  z.object({
@@ -33,6 +34,7 @@ export const AnalysisNodeExecutionResultSchema = z.discriminatedUnion('analysisS
33
34
  reasonCodes: z.array(IdentifierSchema).min(1),
34
35
  includedRowIds: z.array(Sha256DigestSchema).optional(),
35
36
  comparableRowIds: z.array(Sha256DigestSchema).optional(),
37
+ notApplicableRowIds: z.array(Sha256DigestSchema).optional(),
36
38
  assumptionChecks: z.array(PortAssumptionCheckSchema).optional(),
37
39
  }).strict(),
38
40
  ]);
@@ -24,6 +24,7 @@ function compareStrings(left, right) {
24
24
  function assertObservationCoverage(coverage) {
25
25
  if (coverage.planned !== coverage.observed
26
26
  + coverage.missing
27
+ + coverage.notApplicable
27
28
  + coverage.invalid
28
29
  + coverage.evaluationFailed
29
30
  + coverage.sourceUnavailable
@@ -58,6 +59,20 @@ function assertRecords(bundle) {
58
59
  if (record.exclusions.length !== record.coverage.excluded) {
59
60
  throw new AnalysisBundleValidationError('ANALYSIS_BUNDLE_COVERAGE_INVALID', 'Analysis exclusions must account for every excluded observation row.');
60
61
  }
62
+ if (record.notApplicableRows.length !== record.coverage.notApplicable) {
63
+ throw new AnalysisBundleValidationError('ANALYSIS_BUNDLE_COVERAGE_INVALID', 'Analysis not-applicable rows must account for structural coverage.');
64
+ }
65
+ const exclusionById = new Map(record.exclusions.map((entry) => [entry.rowId, entry]));
66
+ for (let index = 0; index < record.notApplicableRows.length; index += 1) {
67
+ const fact = record.notApplicableRows[index];
68
+ const previousFact = record.notApplicableRows[index - 1];
69
+ if (previousFact !== undefined && compareStrings(previousFact.rowId, fact.rowId) >= 0) {
70
+ throw new AnalysisBundleValidationError('ANALYSIS_BUNDLE_COVERAGE_INVALID', 'Analysis not-applicable rows must use unique canonical row order.');
71
+ }
72
+ if (exclusionById.get(fact.rowId)?.reasonCode !== fact.reasonCode) {
73
+ throw new AnalysisBundleValidationError('ANALYSIS_BUNDLE_COVERAGE_INVALID', 'Every not-applicable row must be retained as a matching exclusion fact.');
74
+ }
75
+ }
61
76
  for (let exclusionIndex = 0; exclusionIndex < record.exclusions.length; exclusionIndex += 1) {
62
77
  const exclusion = record.exclusions[exclusionIndex];
63
78
  const previousExclusion = record.exclusions[exclusionIndex - 1];
@@ -282,10 +297,12 @@ function expectedRows(plan, execution, evaluation, node) {
282
297
  return rows.sort((left, right) => compareStrings(left.rowId, right.rowId));
283
298
  }
284
299
  function assertSourceCoverage(record, rows) {
300
+ const notApplicableIds = new Set(record.notApplicableRows.map((entry) => entry.rowId));
285
301
  const expected = {
286
302
  planned: rows.length,
287
303
  observed: rows.filter((row) => row.rowStatus === 'observed').length,
288
- missing: rows.filter((row) => row.rowStatus === 'missing').length,
304
+ missing: rows.filter((row) => (row.rowStatus === 'missing' && !notApplicableIds.has(row.rowId))).length,
305
+ notApplicable: record.notApplicableRows.length,
289
306
  invalid: rows.filter((row) => row.rowStatus === 'invalid').length,
290
307
  evaluationFailed: rows.filter((row) => row.rowStatus === 'evaluation-failed').length,
291
308
  sourceUnavailable: rows.filter((row) => row.rowStatus === 'source-unavailable').length,
@@ -296,6 +313,7 @@ function assertSourceCoverage(record, rows) {
296
313
  planned: record.coverage.planned,
297
314
  observed: record.coverage.observed,
298
315
  missing: record.coverage.missing,
316
+ notApplicable: record.coverage.notApplicable,
299
317
  invalid: record.coverage.invalid,
300
318
  evaluationFailed: record.coverage.evaluationFailed,
301
319
  sourceUnavailable: record.coverage.sourceUnavailable,
@@ -308,6 +326,12 @@ function assertSourceCoverage(record, rows) {
308
326
  const byId = new Map(rows.map((row) => [row.rowId, row]));
309
327
  const exclusionById = new Map(record.exclusions.map((entry) => [entry.rowId, entry]));
310
328
  if (record.exclusions.some((entry) => !byId.has(entry.rowId))
329
+ || record.notApplicableRows.some((entry) => {
330
+ const row = byId.get(entry.rowId);
331
+ return row?.rowStatus !== 'missing'
332
+ || row.censored
333
+ || row.reasonCode !== entry.reasonCode;
334
+ })
311
335
  || rows.some((row) => row.rowStatus !== 'observed'
312
336
  && (exclusionById.get(row.rowId)?.reasonCode !== row.reasonCode))) {
313
337
  throw new AnalysisBundleValidationError('ANALYSIS_BUNDLE_SOURCE_MISMATCH', 'Analysis exclusions do not match source observation identities and reasons.');