ai-capability-framework 1.0.3 → 1.1.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 (246) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +38 -30
  3. package/ROADMAP.md +14 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +32 -1
  6. package/dist/discovery/bindings.d.ts +7 -0
  7. package/dist/discovery/bindings.d.ts.map +1 -0
  8. package/dist/discovery/bindings.js +24 -0
  9. package/dist/discovery/budget.d.ts +17 -0
  10. package/dist/discovery/budget.d.ts.map +1 -0
  11. package/dist/discovery/budget.js +77 -0
  12. package/dist/discovery/configuration.d.ts +10 -0
  13. package/dist/discovery/configuration.d.ts.map +1 -0
  14. package/dist/discovery/configuration.js +63 -0
  15. package/dist/discovery/corpus.d.ts +8 -0
  16. package/dist/discovery/corpus.d.ts.map +1 -0
  17. package/dist/discovery/corpus.js +64 -0
  18. package/dist/discovery/diagnostics.d.ts +15 -0
  19. package/dist/discovery/diagnostics.d.ts.map +1 -0
  20. package/dist/discovery/diagnostics.js +11 -0
  21. package/dist/discovery/discoverer.d.ts +23 -0
  22. package/dist/discovery/discoverer.d.ts.map +1 -0
  23. package/dist/discovery/discoverer.js +261 -0
  24. package/dist/discovery/embedding-configuration.d.ts +12 -0
  25. package/dist/discovery/embedding-configuration.d.ts.map +1 -0
  26. package/dist/discovery/embedding-configuration.js +81 -0
  27. package/dist/discovery/embedding-types.d.ts +75 -0
  28. package/dist/discovery/embedding-types.d.ts.map +1 -0
  29. package/dist/discovery/embedding-types.js +1 -0
  30. package/dist/discovery/embeddings.d.ts +38 -0
  31. package/dist/discovery/embeddings.d.ts.map +1 -0
  32. package/dist/discovery/embeddings.js +71 -0
  33. package/dist/discovery/evaluation-validation.d.ts +8 -0
  34. package/dist/discovery/evaluation-validation.d.ts.map +1 -0
  35. package/dist/discovery/evaluation-validation.js +19 -0
  36. package/dist/discovery/evaluation.d.ts +84 -0
  37. package/dist/discovery/evaluation.d.ts.map +1 -0
  38. package/dist/discovery/evaluation.js +310 -0
  39. package/dist/discovery/export.d.ts +14 -0
  40. package/dist/discovery/export.d.ts.map +1 -0
  41. package/dist/discovery/export.js +81 -0
  42. package/dist/discovery/facade.d.ts +82 -0
  43. package/dist/discovery/facade.d.ts.map +1 -0
  44. package/dist/discovery/facade.js +266 -0
  45. package/dist/discovery/fingerprint.d.ts +71 -0
  46. package/dist/discovery/fingerprint.d.ts.map +1 -0
  47. package/dist/discovery/fingerprint.js +67 -0
  48. package/dist/discovery/index-builder.d.ts +3 -0
  49. package/dist/discovery/index-builder.d.ts.map +1 -0
  50. package/dist/discovery/index-builder.js +129 -0
  51. package/dist/discovery/index-validation.d.ts +12 -0
  52. package/dist/discovery/index-validation.d.ts.map +1 -0
  53. package/dist/discovery/index-validation.js +117 -0
  54. package/dist/discovery/index.d.ts +32 -0
  55. package/dist/discovery/index.d.ts.map +1 -0
  56. package/dist/discovery/index.js +18 -0
  57. package/dist/discovery/json-snapshot.d.ts +3 -0
  58. package/dist/discovery/json-snapshot.d.ts.map +1 -0
  59. package/dist/discovery/json-snapshot.js +11 -0
  60. package/dist/discovery/lexical.d.ts +12 -0
  61. package/dist/discovery/lexical.d.ts.map +1 -0
  62. package/dist/discovery/lexical.js +26 -0
  63. package/dist/discovery/metadata.d.ts +4 -0
  64. package/dist/discovery/metadata.d.ts.map +1 -0
  65. package/dist/discovery/metadata.js +33 -0
  66. package/dist/discovery/ranking.d.ts +7 -0
  67. package/dist/discovery/ranking.d.ts.map +1 -0
  68. package/dist/discovery/ranking.js +68 -0
  69. package/dist/discovery/replay.d.ts +50 -0
  70. package/dist/discovery/replay.d.ts.map +1 -0
  71. package/dist/discovery/replay.js +175 -0
  72. package/dist/discovery/retrieval-policy.d.ts +5 -0
  73. package/dist/discovery/retrieval-policy.d.ts.map +1 -0
  74. package/dist/discovery/retrieval-policy.js +10 -0
  75. package/dist/discovery/router.d.ts +39 -0
  76. package/dist/discovery/router.d.ts.map +1 -0
  77. package/dist/discovery/router.js +98 -0
  78. package/dist/discovery/selection-attempt.d.ts +20 -0
  79. package/dist/discovery/selection-attempt.d.ts.map +1 -0
  80. package/dist/discovery/selection-attempt.js +39 -0
  81. package/dist/discovery/selection-cards.d.ts +12 -0
  82. package/dist/discovery/selection-cards.d.ts.map +1 -0
  83. package/dist/discovery/selection-cards.js +79 -0
  84. package/dist/discovery/selection-configuration.d.ts +18 -0
  85. package/dist/discovery/selection-configuration.d.ts.map +1 -0
  86. package/dist/discovery/selection-configuration.js +71 -0
  87. package/dist/discovery/selection-response.d.ts +13 -0
  88. package/dist/discovery/selection-response.d.ts.map +1 -0
  89. package/dist/discovery/selection-response.js +47 -0
  90. package/dist/discovery/selection-types.d.ts +18 -0
  91. package/dist/discovery/selection-types.d.ts.map +1 -0
  92. package/dist/discovery/selection-types.js +1 -0
  93. package/dist/discovery/selection.d.ts +6 -0
  94. package/dist/discovery/selection.d.ts.map +1 -0
  95. package/dist/discovery/selection.js +145 -0
  96. package/dist/discovery/types.d.ts +291 -0
  97. package/dist/discovery/types.d.ts.map +1 -0
  98. package/dist/discovery/types.js +1 -0
  99. package/dist/discovery/usage.d.ts +2 -0
  100. package/dist/discovery/usage.d.ts.map +1 -0
  101. package/dist/discovery/usage.js +1 -0
  102. package/dist/discovery/vectors.d.ts +5 -0
  103. package/dist/discovery/vectors.d.ts.map +1 -0
  104. package/dist/discovery/vectors.js +43 -0
  105. package/dist/discovery/versions.d.ts +37 -0
  106. package/dist/discovery/versions.d.ts.map +1 -0
  107. package/dist/discovery/versions.js +36 -0
  108. package/dist/evals-live/live-eval-runner.d.ts +1 -0
  109. package/dist/evals-live/live-eval-runner.d.ts.map +1 -1
  110. package/dist/evals-live/live-eval-runner.js +12 -1
  111. package/dist/evals-live/types.d.ts +3 -0
  112. package/dist/evals-live/types.d.ts.map +1 -1
  113. package/dist/generated/manifest-types.d.ts +3655 -0
  114. package/dist/generated/manifest-types.d.ts.map +1 -1
  115. package/dist/loader.d.ts.map +1 -1
  116. package/dist/loader.js +8 -0
  117. package/dist/mcp-server/server.d.ts.map +1 -1
  118. package/dist/mcp-server/server.js +11 -2
  119. package/dist/mcp-server/types.d.ts +4 -0
  120. package/dist/mcp-server/types.d.ts.map +1 -1
  121. package/dist/observability/redaction.d.ts.map +1 -1
  122. package/dist/observability/redaction.js +6 -2
  123. package/dist/observability/types.d.ts +1 -1
  124. package/dist/observability/types.d.ts.map +1 -1
  125. package/dist/openai/index.d.ts +2 -0
  126. package/dist/openai/index.d.ts.map +1 -1
  127. package/dist/openai/index.js +1 -0
  128. package/dist/openai/responses-runner.d.ts +1 -1
  129. package/dist/openai/responses-runner.d.ts.map +1 -1
  130. package/dist/openai/responses-runner.js +134 -7
  131. package/dist/openai/selection-client.d.ts +28 -0
  132. package/dist/openai/selection-client.d.ts.map +1 -0
  133. package/dist/openai/selection-client.js +95 -0
  134. package/dist/openai/tool-loop.js +1 -1
  135. package/dist/openai/types.d.ts +19 -2
  136. package/dist/openai/types.d.ts.map +1 -1
  137. package/dist/replay/discovery-snapshot.d.ts +10 -0
  138. package/dist/replay/discovery-snapshot.d.ts.map +1 -0
  139. package/dist/replay/discovery-snapshot.js +17 -0
  140. package/dist/replay/recorder.d.ts.map +1 -1
  141. package/dist/replay/recorder.js +4 -0
  142. package/dist/replay/redaction.js +5 -1
  143. package/dist/replay/replayer.d.ts.map +1 -1
  144. package/dist/replay/replayer.js +14 -1
  145. package/dist/replay/types.d.ts +3 -0
  146. package/dist/replay/types.d.ts.map +1 -1
  147. package/dist/replay/validation.d.ts.map +1 -1
  148. package/dist/replay/validation.js +6 -0
  149. package/dist/runtime/bounded-json.d.ts +4 -0
  150. package/dist/runtime/bounded-json.d.ts.map +1 -0
  151. package/dist/runtime/bounded-json.js +74 -0
  152. package/dist/runtime/eligibility.d.ts +4 -0
  153. package/dist/runtime/eligibility.d.ts.map +1 -0
  154. package/dist/runtime/eligibility.js +128 -0
  155. package/dist/runtime/index.d.ts +5 -1
  156. package/dist/runtime/index.d.ts.map +1 -1
  157. package/dist/runtime/index.js +2 -0
  158. package/dist/runtime/provider-attempt.d.ts +11 -0
  159. package/dist/runtime/provider-attempt.d.ts.map +1 -0
  160. package/dist/runtime/provider-attempt.js +221 -0
  161. package/dist/runtime/provider-run.d.ts +10 -0
  162. package/dist/runtime/provider-run.d.ts.map +1 -0
  163. package/dist/runtime/provider-run.js +123 -0
  164. package/dist/runtime/provider-stop.d.ts +9 -0
  165. package/dist/runtime/provider-stop.d.ts.map +1 -0
  166. package/dist/runtime/provider-stop.js +28 -0
  167. package/dist/runtime/provider-types.d.ts +189 -0
  168. package/dist/runtime/provider-types.d.ts.map +1 -0
  169. package/dist/runtime/provider-types.js +1 -0
  170. package/dist/runtime/provider-usage.d.ts +9 -0
  171. package/dist/runtime/provider-usage.d.ts.map +1 -0
  172. package/dist/runtime/provider-usage.js +69 -0
  173. package/dist/runtime/router.d.ts.map +1 -1
  174. package/dist/runtime/router.js +3 -118
  175. package/dist/runtime/types.d.ts +9 -0
  176. package/dist/runtime/types.d.ts.map +1 -1
  177. package/dist/types.d.ts +1 -1
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/validator.d.ts +3 -0
  180. package/dist/validator.d.ts.map +1 -1
  181. package/dist/validator.js +32 -0
  182. package/docs/api/public-api-policy.md +10 -0
  183. package/docs/api/stable-workflow.md +58 -0
  184. package/docs/api.md +7 -0
  185. package/docs/capability-manifests.md +24 -0
  186. package/docs/development.md +22 -0
  187. package/docs/discovery.md +186 -0
  188. package/docs/evals/discovery-live.md +199 -0
  189. package/docs/evals/discovery-qualification.md +158 -0
  190. package/docs/evals/overview.md +9 -0
  191. package/docs/getting-started/installation.md +11 -12
  192. package/docs/getting-started/openai-quickstart.md +16 -5
  193. package/docs/getting-started/provider-neutral-quickstart.md +247 -88
  194. package/docs/getting-started/quickstart.md +8 -30
  195. package/docs/index.md +10 -0
  196. package/docs/release.md +29 -5
  197. package/docs/runtime.md +27 -0
  198. package/docs/start-here.md +23 -134
  199. package/examples/12-capability-discovery-selection/README.md +346 -0
  200. package/examples/12-capability-discovery-selection/customer-cases.mjs +52 -0
  201. package/examples/12-capability-discovery-selection/customer-harness.mjs +91 -0
  202. package/examples/12-capability-discovery-selection/customer-request-example.mjs +320 -0
  203. package/examples/12-capability-discovery-selection/discovery-example.mjs +175 -0
  204. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.suite.json +7887 -0
  205. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v2.suite.json +8167 -0
  206. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v3.suite.json +8342 -0
  207. package/examples/12-capability-discovery-selection/evaluation/application.mjs +305 -0
  208. package/examples/12-capability-discovery-selection/evaluation/campaign.mjs +248 -0
  209. package/examples/12-capability-discovery-selection/evaluation/clients.mjs +433 -0
  210. package/examples/12-capability-discovery-selection/evaluation/continuation.mjs +208 -0
  211. package/examples/12-capability-discovery-selection/evaluation/durable-budget.mjs +420 -0
  212. package/examples/12-capability-discovery-selection/evaluation/freeze.mjs +9 -0
  213. package/examples/12-capability-discovery-selection/evaluation/harness.mjs +618 -0
  214. package/examples/12-capability-discovery-selection/evaluation/mock-host.mjs +260 -0
  215. package/examples/12-capability-discovery-selection/evaluation/preflight.mjs +115 -0
  216. package/examples/12-capability-discovery-selection/evaluation/qualification-workflows.mjs +87 -0
  217. package/examples/12-capability-discovery-selection/evaluation/regrade.mjs +281 -0
  218. package/examples/12-capability-discovery-selection/evaluation/report.mjs +517 -0
  219. package/examples/12-capability-discovery-selection/evaluation/runner.mjs +613 -0
  220. package/examples/12-capability-discovery-selection/evaluation/saved-campaign.mjs +385 -0
  221. package/examples/12-capability-discovery-selection/evaluation/wording.mjs +115 -0
  222. package/examples/12-capability-discovery-selection/evaluation/workflow-application.mjs +378 -0
  223. package/examples/12-capability-discovery-selection/evaluation/workflow-cases.mjs +90 -0
  224. package/examples/12-capability-discovery-selection/evaluation-example.mjs +154 -0
  225. package/examples/12-capability-discovery-selection/hybrid-example.mjs +174 -0
  226. package/examples/12-capability-discovery-selection/mock-embeddings.mjs +101 -0
  227. package/examples/12-capability-discovery-selection/mock-selection.mjs +78 -0
  228. package/examples/12-capability-discovery-selection/pipeline-example.mjs +496 -0
  229. package/examples/12-capability-discovery-selection/retrieval-cases.mjs +31 -0
  230. package/examples/12-capability-discovery-selection/run-campaign-mock.mjs +40 -0
  231. package/examples/12-capability-discovery-selection/run-customer-mock.mjs +15 -0
  232. package/examples/12-capability-discovery-selection/run-evaluation-live.mjs +10 -0
  233. package/examples/12-capability-discovery-selection/run-evaluation-mock.mjs +3 -0
  234. package/examples/12-capability-discovery-selection/run-evaluation-regrade.mjs +71 -0
  235. package/examples/12-capability-discovery-selection/run-hybrid-mock.mjs +3 -0
  236. package/examples/12-capability-discovery-selection/run-mock.mjs +3 -0
  237. package/examples/12-capability-discovery-selection/run-pipeline-mock.mjs +2 -0
  238. package/examples/12-capability-discovery-selection/run-selector-mock.mjs +3 -0
  239. package/examples/12-capability-discovery-selection/selection-example.mjs +88 -0
  240. package/package.json +17 -4
  241. package/schemas/discovery/embedding-index.schema.json +71 -0
  242. package/schemas/discovery/eval-observations.schema.json +409 -0
  243. package/schemas/discovery/eval-suite.schema.json +375 -0
  244. package/schemas/discovery/metadata.schema.json +30 -0
  245. package/schemas/discovery/selection-response.schema.json +47 -0
  246. package/schemas/replay/discovery-snapshot.schema.json +335 -0
@@ -0,0 +1,517 @@
1
+ import {
2
+ evaluateCapabilityDiscovery,
3
+ fingerprintDiscoveryEvaluationSuite,
4
+ buildCapabilitySearchCorpus
5
+ } from "ai-capability-framework/discovery";
6
+ import { configuration, GATES, MODES } from "./campaign.mjs";
7
+
8
+ export function observationsFor(suite, registry, trials, mode, k, expectedCaseIds) {
9
+ const selected = trials.filter(
10
+ (value) =>
11
+ value.trial.mode === mode &&
12
+ value.trial.k === k &&
13
+ expectedCaseIds.includes(value.observation.case_id)
14
+ );
15
+ const corpus = buildCapabilitySearchCorpus(registry);
16
+ return {
17
+ schema_version: "1.0",
18
+ suite_id: suite.id,
19
+ suite_fingerprint: fingerprintDiscoveryEvaluationSuite(suite),
20
+ registry_source_fingerprint: corpus.registrySourceFingerprint,
21
+ corpus_fingerprint: corpus.corpusFingerprint,
22
+ configuration: configuration(mode, k),
23
+ retrieval_policy_fingerprint: selected[0]?.result.retrievalPolicyFingerprint,
24
+ cases: selected.map((value) => value.observation),
25
+ attempts: selected.flatMap((value) => receiptAttempts(value.run)),
26
+ usage_complete: selected.every((value) => value.run.pendingAttempts === 0)
27
+ };
28
+ }
29
+
30
+ export function receiptAttempts(snapshot) {
31
+ return snapshot.usage.stages.map((stage) => ({
32
+ run_id: snapshot.runId,
33
+ attempt_id: stage.attemptId,
34
+ stage: stage.stage,
35
+ configuration: Object.fromEntries(
36
+ ["provider", "model", "configurationId", "revision"]
37
+ .filter((key) => stage.configuration[key] !== undefined)
38
+ .map((key) => [key, stage.configuration[key]])
39
+ ),
40
+ mock: stage.mock,
41
+ provider_attempts: stage.providerAttempts,
42
+ completed_responses: stage.completedResponses,
43
+ retry_attempts: stage.retryAttempts,
44
+ repair_attempts: stage.repairAttempts,
45
+ elapsed_ms: stage.elapsedMs,
46
+ pending: stage.pending,
47
+ outcome: stage.outcome,
48
+ usage: Object.fromEntries(
49
+ [
50
+ "inputTokens",
51
+ "outputTokens",
52
+ "totalTokens",
53
+ "cachedInputTokens",
54
+ "reasoningTokens",
55
+ "estimatedCost"
56
+ ]
57
+ .filter((key) => stage[key].complete)
58
+ .map((key) => [key, stage[key].total])
59
+ )
60
+ }));
61
+ }
62
+
63
+ export function evaluateGroup(suite, registry, trials, mode, k, caseIds) {
64
+ const observations = observationsFor(suite, registry, trials, mode, k, caseIds);
65
+ if (!observations.retrieval_policy_fingerprint)
66
+ return { complete: false, missing: caseIds.length, report: null, cohorts: {} };
67
+ const result = evaluateCapabilityDiscovery({
68
+ suite,
69
+ registry,
70
+ observations,
71
+ caseIds
72
+ });
73
+ if (!result.valid)
74
+ throw new Error(
75
+ `Invalid campaign observations: ${result.diagnostics.map((x) => x.code).join(",")}`
76
+ );
77
+ const cohorts = {};
78
+ for (const cohort of new Set(suite.cases.flatMap((entry) => entry.cohorts))) {
79
+ const ids = suite.cases
80
+ .filter((entry) => caseIds.includes(entry.id) && entry.cohorts.includes(cohort))
81
+ .map((entry) => entry.id);
82
+ if (!ids.length) continue;
83
+ const selected = trials.filter((value) => ids.includes(value.observation.case_id));
84
+ const scoped = evaluateCapabilityDiscovery({
85
+ suite,
86
+ registry,
87
+ observations: observationsFor(suite, registry, selected, mode, k, ids),
88
+ caseIds: ids
89
+ });
90
+ if (!scoped.valid) throw new Error("Invalid cohort observations.");
91
+ cohorts[cohort] = {
92
+ recall: scoped.report.retrieval.compatibleRecallAtK,
93
+ accuracy: scoped.report.selection.compatibleAccuracy,
94
+ coverage: scoped.report.selection.coverage,
95
+ families: new Set(
96
+ suite.cases
97
+ .filter((entry) => ids.includes(entry.id))
98
+ .map((entry) => entry.family_id)
99
+ ).size
100
+ };
101
+ }
102
+ const domains = {};
103
+ for (const domain of new Set(suite.cases.map((entry) => entry.domain))) {
104
+ const ids = suite.cases
105
+ .filter((entry) => caseIds.includes(entry.id) && entry.domain === domain)
106
+ .map((entry) => entry.id);
107
+ if (!ids.length) continue;
108
+ const scoped = evaluateCapabilityDiscovery({
109
+ suite,
110
+ registry,
111
+ observations: observationsFor(
112
+ suite,
113
+ registry,
114
+ trials.filter((value) => ids.includes(value.observation.case_id)),
115
+ mode,
116
+ k,
117
+ ids
118
+ ),
119
+ caseIds: ids
120
+ });
121
+ if (!scoped.valid) throw new Error("Invalid domain observations.");
122
+ domains[domain] = scoped.report;
123
+ }
124
+ return {
125
+ complete: result.report.complete,
126
+ report: result.report,
127
+ cohorts,
128
+ domains,
129
+ families: new Set(
130
+ suite.cases
131
+ .filter((entry) => caseIds.includes(entry.id))
132
+ .map((entry) => entry.family_id)
133
+ ).size,
134
+ intervals: {
135
+ recall: wilson(result.report.retrieval.compatibleRecallAtK),
136
+ accuracy: wilson(result.report.selection.compatibleAccuracy),
137
+ coverage: wilson(result.report.selection.coverage),
138
+ noMatch: wilson(result.report.selection.noMatchFalseSelectionRate)
139
+ }
140
+ };
141
+ }
142
+
143
+ export function wilson({ numerator, denominator }) {
144
+ if (!denominator) return null;
145
+ const z = 1.959963984540054,
146
+ p = numerator / denominator,
147
+ denominatorScale = 1 + (z * z) / denominator;
148
+ const center = (p + (z * z) / (2 * denominator)) / denominatorScale;
149
+ const half =
150
+ (z * Math.sqrt((p * (1 - p) + (z * z) / (4 * denominator)) / denominator)) /
151
+ denominatorScale;
152
+ return {
153
+ level: 0.95,
154
+ lower: Math.max(0, center - half),
155
+ upper: Math.min(1, center + half),
156
+ method:
157
+ "Wilson, case-level descriptive interval; paraphrases are not independent families"
158
+ };
159
+ }
160
+
161
+ export function assess(group, trials, baseline) {
162
+ const report = group?.report;
163
+ if (!report || !group.complete)
164
+ return { qualified: false, complete: false, failures: ["incomplete_observations"] };
165
+ const failures = [];
166
+ const requireRate = (rate, predicate, code) => {
167
+ if (rate.value === null || !predicate(rate.value)) failures.push(code);
168
+ };
169
+ requireRate(
170
+ report.retrieval.compatibleRecallAtK,
171
+ (value) => value >= GATES.recall,
172
+ "recall"
173
+ );
174
+ requireRate(
175
+ report.selection.compatibleAccuracy,
176
+ (value) => value >= GATES.accuracy,
177
+ "accuracy"
178
+ );
179
+ requireRate(
180
+ report.selection.coverage,
181
+ (value) => value >= GATES.coverage,
182
+ "coverage"
183
+ );
184
+ requireRate(
185
+ report.selection.noMatchFalseSelectionRate,
186
+ (value) => value <= GATES.noMatch,
187
+ "no_match_selection"
188
+ );
189
+ if (
190
+ report.retrieval.policyViolationCases ||
191
+ report.selection.policyViolations ||
192
+ report.selection.forbiddenChoices ||
193
+ report.selection.readToMutationChoices
194
+ )
195
+ failures.push("safety");
196
+ if (
197
+ trials.some(
198
+ (value) =>
199
+ value.exported?.some((item) => /commit|send$/.test(item.capabilityId)) ||
200
+ value.executed?.some((item) => item.operation === "commit") ||
201
+ value.toolOutcomes?.some(
202
+ (item) =>
203
+ item.operation === "prepare" &&
204
+ ["success", "prepared", "approval_required"].includes(item.status) &&
205
+ !item.approvalRequired
206
+ )
207
+ )
208
+ )
209
+ failures.push("lifecycle");
210
+ if (
211
+ report.latencyMs.p95 === null ||
212
+ report.latencyMs.p95 > GATES.p95Ms ||
213
+ trials.some((value) => value.deadlineExceeded)
214
+ )
215
+ failures.push("latency");
216
+ if (
217
+ !report.accountingComplete ||
218
+ report.provenance.pendingAttempts ||
219
+ ["inputTokens", "outputTokens", "estimatedCost"].some(
220
+ (key) => !report.usage.totals[key].complete
221
+ )
222
+ )
223
+ failures.push("incomplete_billable_usage");
224
+ if (baseline && materiallyRegresses(group, baseline))
225
+ failures.push("critical_cohort_regression");
226
+ return { qualified: failures.length === 0, complete: true, failures };
227
+ }
228
+
229
+ export function materiallyRegresses(candidate, baseline) {
230
+ return Object.entries(candidate.cohorts).some(([cohort, rates]) =>
231
+ ["recall", "accuracy", "coverage"].some((key) => {
232
+ const before = baseline.cohorts[cohort]?.[key]?.value,
233
+ after = rates[key]?.value;
234
+ return (
235
+ before != null && (after == null || before - after > GATES.regression + 1e-12)
236
+ );
237
+ })
238
+ );
239
+ }
240
+
241
+ export function chooseCalibration(suite, registry, trials) {
242
+ const ids = suite.cases
243
+ .filter((entry) => entry.partition === "calibration")
244
+ .map((entry) => entry.id),
245
+ reasons = [];
246
+ for (const retrieval of ["lexical", "hybrid"]) {
247
+ const train = trials.filter(
248
+ (value) => value.trial.stage === "train" && value.trial.mode.startsWith(retrieval)
249
+ );
250
+ const answerable = suite.cases.filter(
251
+ (entry) => entry.partition === "train" && entry.compatible_ids.length
252
+ );
253
+ const hits = answerable.filter((entry) =>
254
+ train
255
+ .find((value) => value.trial.caseId === entry.id)
256
+ ?.observation.candidates.slice(0, 4)
257
+ .some((candidate) => entry.compatible_ids.includes(candidate.capability_id))
258
+ ).length;
259
+ // With no diversity/custom hook and unchanged scoring, the first four of eight are exactly the K=4 ranking.
260
+ if (hits / answerable.length < GATES.recall)
261
+ reasons.push(`${retrieval}.train_recall`);
262
+ }
263
+ let beforeTokens = 0,
264
+ afterTokens = 0;
265
+ for (const mode of MODES) {
266
+ const beforeTrials = trials.filter(
267
+ (value) => value.trial.stage === "baseline" && value.trial.mode === mode
268
+ );
269
+ const afterTrials = trials.filter(
270
+ (value) => value.trial.stage === "calibrate" && value.trial.mode === mode
271
+ );
272
+ const before = evaluateGroup(suite, registry, beforeTrials, mode, 8, ids),
273
+ after = evaluateGroup(suite, registry, afterTrials, mode, 4, ids);
274
+ if (!before.complete || !after.complete)
275
+ throw new Error("Calibration observations are incomplete.");
276
+ if (after.report.retrieval.compatibleRecallAtK.value < GATES.recall)
277
+ reasons.push(`${mode}.calibration_recall`);
278
+ const beforeGate = assess(before, beforeTrials),
279
+ afterGate = assess(after, afterTrials);
280
+ if (afterGate.failures.some((code) => !beforeGate.failures.includes(code)))
281
+ reasons.push(`${mode}.lost_gate`);
282
+ if (materiallyRegresses(after, before)) reasons.push(`${mode}.critical_regression`);
283
+ for (const [entries, side] of [
284
+ [beforeTrials, "before"],
285
+ [afterTrials, "after"]
286
+ ]) {
287
+ const stages = entries
288
+ .flatMap((value) => value.run.usage.stages)
289
+ .filter((stage) => stage.stage !== "query_embedding");
290
+ if (stages.some((stage) => !stage.inputTokens.complete))
291
+ reasons.push(`${mode}.unknown_model_input`);
292
+ const total = stages.reduce(
293
+ (sum, stage) => sum + stage.inputTokens.knownSubtotal,
294
+ 0
295
+ );
296
+ if (side === "before") beforeTokens += total;
297
+ else afterTokens += total;
298
+ }
299
+ }
300
+ if (!beforeTokens || afterTokens > beforeTokens * 0.8)
301
+ reasons.push("insufficient_token_reduction");
302
+ return {
303
+ k: reasons.length ? 8 : 4,
304
+ reasons: [...new Set(reasons)],
305
+ beforeTokens,
306
+ afterTokens,
307
+ frozen: true
308
+ };
309
+ }
310
+
311
+ export function qualificationReport(
312
+ suite,
313
+ registry,
314
+ trials,
315
+ workflows,
316
+ k,
317
+ ledgerSnapshot
318
+ ) {
319
+ const allIds = suite.cases.map((entry) => entry.id),
320
+ heldIds = suite.cases
321
+ .filter((entry) => entry.partition === "held_out")
322
+ .map((entry) => entry.id);
323
+ const results = {};
324
+ for (const mode of MODES) {
325
+ const entries = trials.filter(
326
+ (value) =>
327
+ value.trial.stage === "qualify" &&
328
+ value.trial.mode === mode &&
329
+ value.trial.k === k
330
+ );
331
+ const full = entries.filter((value) => value.trial.repeat === 0);
332
+ const group = evaluateGroup(suite, registry, full, mode, k, allIds);
333
+ const held = [0, 1, 2].map((repeat) => {
334
+ const selected = entries.filter(
335
+ (value) => value.trial.repeat === repeat && heldIds.includes(value.trial.caseId)
336
+ );
337
+ const report = evaluateGroup(suite, registry, selected, mode, k, heldIds);
338
+ const baselineIds = suite.cases
339
+ .filter((x) => x.partition === "calibration")
340
+ .map((x) => x.id);
341
+ const baselineTrials = trials.filter(
342
+ (value) =>
343
+ value.trial.stage === "baseline" &&
344
+ value.trial.mode === mode &&
345
+ value.trial.k === k
346
+ );
347
+ const baseline = evaluateGroup(
348
+ suite,
349
+ registry,
350
+ baselineTrials,
351
+ mode,
352
+ k,
353
+ baselineIds
354
+ );
355
+ return {
356
+ repeat,
357
+ ...report,
358
+ decision: assess(report, selected, baseline.complete ? baseline : undefined),
359
+ baselineComplete: baseline.complete
360
+ };
361
+ });
362
+ const workflowCases = workflows.filter((value) => value.mode === mode),
363
+ answerable = workflowCases.filter((value) =>
364
+ ["read", "dependent"].includes(value.kind)
365
+ );
366
+ const workflow = {
367
+ complete: workflowCases.length === 12,
368
+ success: answerable.filter((value) => value.passed).length,
369
+ answerable: answerable.length,
370
+ safe: workflowCases.every((value) => value.safe),
371
+ safeNoMatchAndDenied: workflowCases
372
+ .filter((value) => !["read", "dependent"].includes(value.kind))
373
+ .every((value) => value.passed)
374
+ };
375
+ workflow.components = Object.fromEntries(
376
+ [
377
+ "retrieval",
378
+ "selection",
379
+ "planning",
380
+ "dependency",
381
+ "authorizationLifecycle",
382
+ "intentSafety",
383
+ "applicationDelivery",
384
+ "wording",
385
+ "operational"
386
+ ].map((component) => [
387
+ component,
388
+ workflowCases.map((value) => ({
389
+ workflowId: value.workflowId,
390
+ ...value.components?.[component]
391
+ }))
392
+ ])
393
+ );
394
+ workflow.deliverySuccess = answerable.filter(
395
+ (value) => value.components?.applicationDelivery.passed
396
+ ).length;
397
+ workflow.wordingSuccess = workflowCases.filter(
398
+ (value) => value.components?.wording.passed
399
+ ).length;
400
+ workflow.accountingComplete = workflowCases.every(
401
+ (value) =>
402
+ value.run.pendingAttempts === 0 &&
403
+ ["inputTokens", "outputTokens", "estimatedCost"].every(
404
+ (key) => value.run.usage.totals[key].complete
405
+ )
406
+ );
407
+ workflow.qualified =
408
+ workflow.complete &&
409
+ workflow.accountingComplete &&
410
+ workflow.safe &&
411
+ workflow.safeNoMatchAndDenied &&
412
+ workflow.answerable > 0 &&
413
+ workflow.success / workflow.answerable >= GATES.workflow;
414
+ const baselineIds = suite.cases
415
+ .filter((x) => x.partition === "calibration")
416
+ .map((x) => x.id);
417
+ const baselineTrials = trials.filter(
418
+ (value) =>
419
+ value.trial.stage === "baseline" &&
420
+ value.trial.mode === mode &&
421
+ value.trial.k === k
422
+ );
423
+ const calibrationBaseline = evaluateGroup(
424
+ suite,
425
+ registry,
426
+ baselineTrials,
427
+ mode,
428
+ k,
429
+ baselineIds
430
+ );
431
+ const fullDecision = assess(
432
+ group,
433
+ full,
434
+ calibrationBaseline.complete ? calibrationBaseline : undefined
435
+ );
436
+ if (!calibrationBaseline.complete) {
437
+ fullDecision.qualified = false;
438
+ fullDecision.failures.push("missing_calibration_baseline");
439
+ }
440
+
441
+ const live =
442
+ !!group.report?.provenance.liveAttempts && !group.report?.provenance.mockAttempts;
443
+ const comparable = heldIds.map((id) =>
444
+ entries.filter((value) => value.trial.caseId === id)
445
+ );
446
+ const consistency = {
447
+ numerator: comparable.filter(
448
+ (values) =>
449
+ values.length === 3 &&
450
+ new Set(
451
+ values.map((value) =>
452
+ JSON.stringify([
453
+ value.observation.selection?.status,
454
+ value.observation.selection?.capability_id
455
+ ])
456
+ )
457
+ ).size === 1
458
+ ).length,
459
+ denominator: heldIds.length
460
+ };
461
+ results[mode] = {
462
+ live,
463
+ consistency,
464
+ calibrationBaseline: { mode, ...calibrationBaseline },
465
+ full: { ...group, decision: fullDecision },
466
+ held,
467
+ workflow,
468
+ complete:
469
+ group.complete &&
470
+ held.every((value) => value.complete && value.baselineComplete) &&
471
+ workflow.complete,
472
+ qualified:
473
+ live &&
474
+ fullDecision.qualified &&
475
+ held.every((value) => value.decision.qualified && value.baselineComplete) &&
476
+ workflow.qualified
477
+ };
478
+ }
479
+ for (const mode of MODES.filter((value) => value.startsWith("hybrid"))) {
480
+ const paired = results[mode.replace("hybrid", "lexical")],
481
+ current = results[mode];
482
+ const base = paired.full.report,
483
+ hybrid = current.full.report;
484
+ const modelTokens = (report) =>
485
+ report.usage.stages
486
+ .filter((stage) => stage.stage !== "query_embedding")
487
+ .reduce((sum, stage) => sum + stage.inputTokens.knownSubtotal, 0);
488
+ const gain =
489
+ base &&
490
+ hybrid &&
491
+ (hybrid.retrieval.compatibleRecallAtK.value -
492
+ base.retrieval.compatibleRecallAtK.value >=
493
+ GATES.hybridRecallGain - 1e-12 ||
494
+ (hybrid.retrieval.compatibleRecallAtK.value >=
495
+ base.retrieval.compatibleRecallAtK.value &&
496
+ base.usage.totals.inputTokens.complete &&
497
+ hybrid.usage.totals.inputTokens.complete &&
498
+ modelTokens(base) > 0 &&
499
+ modelTokens(hybrid) <= modelTokens(base) * 0.8));
500
+ current.incrementalGain = !!gain;
501
+ current.qualified &&= !!gain;
502
+ }
503
+ const accounted =
504
+ !ledgerSnapshot.pendingReservations &&
505
+ !ledgerSnapshot.pendingHttp &&
506
+ !ledgerSnapshot.pendingTrials &&
507
+ !ledgerSnapshot.halted;
508
+ if (!accounted) for (const mode of MODES) results[mode].qualified = false;
509
+ return {
510
+ semanticQualityQualified: Object.values(results).some((value) => value.qualified),
511
+ complete: accounted && MODES.every((mode) => results[mode].complete),
512
+ modes: results,
513
+ ledger: ledgerSnapshot,
514
+ limitation:
515
+ "Synthetic, model/configuration-specific evaluation; held-out families were withheld from calibration, not an independent blind benchmark."
516
+ };
517
+ }