@piagent/platform 1.5.5 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +9 -8
  3. package/README.vi.md +9 -5
  4. package/adapters/web-frontend/profile.json +2 -4
  5. package/architecture/layers.json +18 -3
  6. package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
  7. package/benchmarks/deep-logic-v1/grade.mjs +272 -10
  8. package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
  9. package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
  10. package/benchmarks/deep-logic-v1/project/package.json +1 -1
  11. package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
  12. package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
  13. package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
  14. package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
  15. package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
  16. package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
  17. package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
  18. package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
  19. package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
  20. package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
  21. package/benchmarks/deep-logic-v1/suite.json +51 -3
  22. package/benchmarks/deep-logic-v1/variant.mjs +22 -1
  23. package/benchmarks/production-v1/spend-control.v1.json +68 -0
  24. package/benchmarks/production-v1/suite.json +42 -3
  25. package/catalog/capabilities.json +6 -6
  26. package/docs/command-reference-vietnamese.md +6 -6
  27. package/docs/context-engine.md +7 -1
  28. package/docs/distribution-standard.md +7 -7
  29. package/docs/en/architecture.md +1 -0
  30. package/docs/languages.json +4 -4
  31. package/docs/model-options.md +8 -5
  32. package/docs/operator-manual-vietnamese.md +13 -11
  33. package/docs/project-adapters.md +2 -2
  34. package/docs/publishing-for-teams.md +5 -0
  35. package/docs/quality-benchmark.md +271 -62
  36. package/docs/release-install-policy.md +10 -9
  37. package/docs/runtime-policy-design.md +4 -4
  38. package/docs/runtime-quality-baseline.md +13 -6
  39. package/docs/security-threat-model.md +2 -2
  40. package/docs/task-lifecycle-vietnamese.md +2 -2
  41. package/docs/team-onboarding.md +2 -2
  42. package/docs/usage-observability.md +7 -2
  43. package/docs/vi/architecture.md +1 -0
  44. package/docs-site/architecture.html +1 -1
  45. package/docs-site/benchmark.html +1 -1
  46. package/docs-site/commands.html +1 -1
  47. package/docs-site/context-engine.html +1 -1
  48. package/docs-site/en/architecture.html +1 -1
  49. package/docs-site/en/benchmark.html +1 -1
  50. package/docs-site/en/commands.html +1 -1
  51. package/docs-site/en/context-engine.html +1 -1
  52. package/docs-site/en/index.html +1 -1
  53. package/docs-site/en/mcp-approval.html +1 -1
  54. package/docs-site/en/mcp-auth.html +1 -1
  55. package/docs-site/en/mcp-servers.html +1 -1
  56. package/docs-site/en/mcp.html +1 -1
  57. package/docs-site/en/permissions.html +1 -1
  58. package/docs-site/en/profiles.html +1 -1
  59. package/docs-site/en/quickstart.html +1 -1
  60. package/docs-site/en/runtime.html +1 -1
  61. package/docs-site/en/scope.html +1 -1
  62. package/docs-site/en/security.html +1 -1
  63. package/docs-site/en/team.html +1 -1
  64. package/docs-site/en/whats-new.html +1 -1
  65. package/docs-site/en/workflows.html +1 -1
  66. package/docs-site/index.html +1 -1
  67. package/docs-site/mcp-approval.html +1 -1
  68. package/docs-site/mcp-auth.html +1 -1
  69. package/docs-site/mcp-servers.html +1 -1
  70. package/docs-site/mcp.html +1 -1
  71. package/docs-site/permissions.html +1 -1
  72. package/docs-site/profiles.html +1 -1
  73. package/docs-site/quickstart.html +1 -1
  74. package/docs-site/runtime.html +1 -1
  75. package/docs-site/scope.html +1 -1
  76. package/docs-site/security.html +1 -1
  77. package/docs-site/team.html +1 -1
  78. package/docs-site/whats-new.html +1 -1
  79. package/docs-site/workflows.html +1 -1
  80. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  81. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  82. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  83. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  84. package/evals/fs4-readiness-matrix.v1.json +2 -2
  85. package/evals/fs5-causal-arm.v1.json +2 -2
  86. package/evals/fs5-pilot-protocol.v1.json +2 -2
  87. package/evals/fs5-pilot-protocol.v4.json +6 -5
  88. package/evals/fs5-pilot-protocol.v5.json +7 -6
  89. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  90. package/package.json +3 -1
  91. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  92. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  93. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  94. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  95. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  96. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  97. package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
  98. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  99. package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -4
  100. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  101. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  102. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  103. package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
  104. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  105. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  106. package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
  107. package/packages/piagent-core/benchmark/benchmark-report.js +116 -12
  108. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  109. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  110. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +782 -0
  111. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  112. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  113. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  114. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  115. package/packages/piagent-core/benchmark/benchmark-suite.js +128 -2
  116. package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
  117. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  118. package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
  119. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  120. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  121. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  122. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  123. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  124. package/packages/piagent-core/extensions/acceptance-receipt.js +34 -11
  125. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  126. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  127. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  128. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  129. package/packages/piagent-core/extensions/context-engine.js +148 -145
  130. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  131. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  132. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  133. package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
  134. package/packages/piagent-core/extensions/criterion-graph.js +78 -8
  135. package/packages/piagent-core/extensions/guard-types.ts +2 -0
  136. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  137. package/packages/piagent-core/extensions/piagent-guard.ts +233 -41
  138. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  139. package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
  140. package/packages/piagent-core/extensions/task-state.js +40 -134
  141. package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
  142. package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
  143. package/packages/piagent-core/package.json +1 -1
  144. package/packages/piagent-core/policies/base-policy.json +2 -0
  145. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  146. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  147. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  148. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  149. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  150. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  151. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  152. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  153. package/packages/piagent-core/runtime/hooks/completion-hook.ts +5 -6
  154. package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
  155. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  156. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
  157. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  158. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
  159. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
  160. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  161. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  162. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  163. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +53 -0
  164. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
  165. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  166. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  167. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  168. package/packages/piagent-core/runtime/orchestration/role-policy.ts +1 -1
  169. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  170. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
  171. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
  172. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  173. package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
  174. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  175. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  176. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +55 -2
  177. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  178. package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
  179. package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
  180. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
  181. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
  182. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  183. package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
  184. package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
  185. package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
  186. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  187. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  188. package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
  189. package/packages/piagent-core/security/sensitive-data.js +13 -218
  190. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  191. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  192. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  193. package/packages/piagent-core/security/sensitive-text.js +234 -0
  194. package/packages/piagent-core/subagents/piagent-worker.md +1 -1
  195. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
  196. package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
  197. package/packages/piagent-webui/dist/client/index.html +1 -1
  198. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  199. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  200. package/packages/piagent-webui/gateway/gateway-session-stream.ts +93 -7
  201. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  202. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  203. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  204. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  205. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
  206. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  207. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -124
  208. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  209. package/packages/piagent-webui/server/transcript-projection.ts +22 -4
  210. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  211. package/schemas/piagent-webui/catalog-v1.json +9 -0
  212. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  213. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  214. package/schemas/task-contract.schema.json +12 -1
  215. package/scripts/benchmark-runner-core.mjs +355 -359
  216. package/scripts/benchmark-runner-finalization.mjs +309 -0
  217. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  218. package/scripts/benchmark-runner-support.mjs +200 -0
  219. package/scripts/benchmark-session.mjs +95 -41
  220. package/scripts/configure-model-scope.sh +4 -3
  221. package/scripts/helper-evaluation.mjs +1 -1
  222. package/scripts/install-global.sh +2 -2
  223. package/scripts/pi-usage-history.mjs +37 -10
  224. package/scripts/setup.sh +2 -2
  225. package/scripts/verify-local.sh +5 -1
  226. package/templates/global/settings.json +2 -1
  227. package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
@@ -1,637 +1,69 @@
1
- import { StringDecoder } from "node:string_decoder";
2
-
3
1
  import { benchmarkClaimEligibility } from "./benchmark-assurance.js";
4
2
  import {
5
3
  comparableAttemptUsage,
4
+ comparableDuration,
6
5
  comparisonProtocol,
7
6
  completeCategoryCoverage,
8
7
  completePairedScenarioCount,
8
+ familyClusteredFailureAwareUsage,
9
+ familyClusteredFixedWorkloadUsage,
10
+ pairedDurationBands,
11
+ pairedUsageBands,
12
+ selectPrimaryEfficiencyEstimate,
9
13
  tokensPerResolvedOutcome
10
14
  } from "./benchmark-comparison.js";
15
+ import { benchmarkProviderWireEvidenceMatchesRequest } from "./benchmark-provider-wire.js";
16
+ import { canonicalBenchmarkTimingDiagnostics, summarizeBenchmarkTimingDiagnostics } from "./benchmark-timing-diagnostics.js";
17
+ import { summarizeBenchmarkCausalContextEvidence } from "./benchmark-record-validation.js";
18
+ import {
19
+ benchmarkPricingSnapshotValidationErrors,
20
+ normalizedCostComparison,
21
+ normalizedCostGate
22
+ } from "./benchmark-normalized-cost.js";
11
23
  import {
12
- isCurrentTaskWorkingTreeDigest,
13
- taskWorkingTreeEvidenceDigest,
14
- taskWorkingTreeSnapshotUsesCurrentAlgorithm
15
- } from "./benchmark-tree-identity.js";
24
+ atMostWithinFloatingPrecision,
25
+ geometricMean,
26
+ geometricMeanConfidence95,
27
+ geometricMeanConfidence95Raw,
28
+ median,
29
+ rounded
30
+ } from "./benchmark-statistics.js";
31
+ import { benchmarkInfrastructureFailureLedgerIssues, benchmarkTokenAccounting as buildBenchmarkTokenAccounting } from "./benchmark-usage.js";
32
+ import {
33
+ efficiencyScore,
34
+ evaluateDurationBandGate,
35
+ evaluateFreshTokenBandGate,
36
+ pairedOutcomeCounts,
37
+ pairedQualityNoninferiorityEvidence,
38
+ qualityPassed,
39
+ RELEASE_FAILURE_MESSAGES,
40
+ safetyPassed,
41
+ surfaceReportKey,
42
+ surfaceSummary,
43
+ workflowContinuityEvidenceComplete
44
+ } from "./benchmark-summary-support.js";
16
45
 
17
46
  export { renderBenchmarkHtml, renderBenchmarkText } from "./benchmark-report.js";
18
47
  export { benchmarkAssuranceEvidenceValidationErrors, benchmarkClaimEligibility } from "./benchmark-assurance.js";
48
+ export { median } from "./benchmark-statistics.js";
19
49
  export { benchmarkSuiteValidationErrors, validateBenchmarkSuite } from "./benchmark-suite.js";
50
+ export { aggregateSessionUsage, benchmarkTokenAccounting, createCodexExecJsonlCollector, parseCodexExecJsonl } from "./benchmark-usage.js";
51
+ export { benchmarkPricingSnapshotValidationErrors, normalizeBenchmarkUsageCost } from "./benchmark-normalized-cost.js";
52
+ export { evaluateWorkflowEvidence } from "./benchmark-workflow.js";
20
53
  const SURFACE_LABELS = Object.freeze({
21
54
  "raw-pi": "Raw Pi",
22
55
  piagent: "Piagent",
23
56
  "codex-cli": "Codex CLI"
24
57
  });
25
- const SURFACE_REPORT_KEYS = Object.freeze({
26
- "raw-pi": "rawPi",
27
- piagent: "piagent",
28
- "codex-cli": "codexCli"
29
- });
30
- const CODEX_NON_TOOL_ITEMS = new Set(["agent_message", "reasoning", "plan", "user_message"]);
31
58
  export const BENCHMARK_MEASUREMENT_SCHEMA_VERSION = 2;
32
59
 
33
- function plainObject(value) {
34
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
35
- }
36
-
37
- export function median(values) {
38
- const finite = values.filter(Number.isFinite).sort((left, right) => left - right);
39
- if (finite.length === 0) return null;
40
- const middle = Math.floor(finite.length / 2);
41
- return finite.length % 2 ? finite[middle] : (finite[middle - 1] + finite[middle]) / 2;
42
- }
43
-
44
- function geometricMean(values) {
45
- if (!Array.isArray(values) || values.length === 0) return null;
46
- if (values.some((value) => !Number.isFinite(value) || value <= 0)) return null;
47
- return Math.exp(values.reduce((sum, value) => sum + Math.log(value), 0) / values.length);
48
- }
49
-
50
- function rounded(value, digits = 3) {
51
- if (!Number.isFinite(value)) return null;
52
- const factor = 10 ** digits;
53
- return Math.round(value * factor) / factor;
54
- }
55
-
56
- function clamp(value, minimum = 0, maximum = 10) {
57
- return Math.max(minimum, Math.min(maximum, value));
58
- }
59
-
60
- function wilsonInterval(successes, total, z = 1.959963984540054) {
61
- if (!Number.isInteger(total) || total <= 0 || !Number.isInteger(successes) || successes < 0 || successes > total) return null;
62
- const proportion = successes / total;
63
- const zSquared = z ** 2;
64
- const denominator = 1 + (zSquared / total);
65
- const center = (proportion + (zSquared / (2 * total))) / denominator;
66
- const margin = (z / denominator) * Math.sqrt(((proportion * (1 - proportion)) / total) + (zSquared / (4 * total ** 2)));
67
- return { lower: rounded(Math.max(0, center - margin), 4), upper: rounded(Math.min(1, center + margin), 4) };
68
- }
69
-
70
- const STUDENT_T_975 = Object.freeze([
71
- null, 12.706, 4.303, 3.182, 2.776, 2.571, 2.447, 2.365, 2.306, 2.262,
72
- 2.228, 2.201, 2.179, 2.16, 2.145, 2.131, 2.12, 2.11, 2.101, 2.093,
73
- 2.086, 2.08, 2.074, 2.069, 2.064, 2.06, 2.056, 2.052, 2.048, 2.045, 2.042
74
- ]);
75
-
76
- function geometricMeanConfidence95(values) {
77
- if (!Array.isArray(values) || values.length < 2 || values.some((value) => !Number.isFinite(value) || value <= 0)) return null;
78
- const logs = values.map(Math.log);
79
- const mean = logs.reduce((sum, value) => sum + value, 0) / logs.length;
80
- const variance = logs.reduce((sum, value) => sum + ((value - mean) ** 2), 0) / (logs.length - 1);
81
- const critical = STUDENT_T_975[logs.length - 1] ?? 1.96;
82
- const margin = critical * Math.sqrt(variance / logs.length);
83
- return {
84
- lower: rounded(Math.exp(mean - margin), 4),
85
- upper: rounded(Math.exp(mean + margin), 4),
86
- sampleUnit: "scenario-family",
87
- scenarioCount: logs.length
88
- };
89
- }
90
-
91
- export function aggregateSessionUsage(sessions) {
92
- const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0, total: 0, cost: 0 };
93
- const models = new Set();
94
- const thinkingLevels = new Set();
95
- const toolNames = {};
96
- let toolCalls = 0;
97
- let messages = 0;
98
- const contextSnapshots = [];
99
- for (const session of sessions) {
100
- for (const key of Object.keys(totals)) totals[key] += Number(session.tokens?.[key] ?? 0);
101
- if (session.modelId || session.provider) models.add(`${session.provider || "unknown"}/${session.modelId || "unknown"}`);
102
- if (session.thinkingLevel) thinkingLevels.add(session.thinkingLevel);
103
- toolCalls += Number(session.messages?.toolCalls ?? 0);
104
- messages += Number(session.messages?.total ?? 0);
105
- if (plainObject(session.contextUsage)) {
106
- const snapshot = {
107
- tokens: Number.isFinite(session.contextUsage.tokens) ? session.contextUsage.tokens : null,
108
- contextWindow: Number.isFinite(session.contextUsage.contextWindow) ? session.contextUsage.contextWindow : null,
109
- percent: Number.isFinite(session.contextUsage.percent) ? session.contextUsage.percent : null
110
- };
111
- if (Object.values(snapshot).some(Number.isFinite)) contextSnapshots.push(snapshot);
112
- }
113
- for (const [name, count] of Object.entries(session.toolNames ?? {})) {
114
- toolNames[name] = (toolNames[name] ?? 0) + Number(count ?? 0);
115
- }
116
- }
117
- return {
118
- ...totals,
119
- fresh: totals.input + totals.output,
120
- sessions: sessions.length,
121
- subagentSessions: sessions.filter((session) => session.isSubagent).length,
122
- toolCalls,
123
- toolNames: Object.fromEntries(Object.entries(toolNames).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))),
124
- messages,
125
- model: models.size === 1 ? [...models][0] : models.size === 0 ? "unknown" : "mixed",
126
- thinkingLevel: thinkingLevels.size === 1 ? [...thinkingLevels][0] : thinkingLevels.size === 0 ? "unknown" : "mixed",
127
- contextUsage: contextSnapshots.length > 0 ? {
128
- source: "session-reported",
129
- observations: contextSnapshots.length,
130
- peakTokens: contextSnapshots.some((item) => Number.isFinite(item.tokens))
131
- ? Math.max(...contextSnapshots.map((item) => item.tokens).filter(Number.isFinite))
132
- : null,
133
- contextWindow: new Set(contextSnapshots.map((item) => item.contextWindow).filter(Number.isFinite)).size === 1
134
- ? contextSnapshots.map((item) => item.contextWindow).find(Number.isFinite)
135
- : null,
136
- peakPercent: contextSnapshots.some((item) => Number.isFinite(item.percent))
137
- ? Math.max(...contextSnapshots.map((item) => item.percent).filter(Number.isFinite))
138
- : null
139
- } : {
140
- source: "unavailable",
141
- observations: 0,
142
- peakTokens: null,
143
- contextWindow: null,
144
- peakPercent: null
145
- }
146
- };
147
- }
148
-
149
- function requiredCodexToken(value, field) {
150
- if (!Number.isSafeInteger(value) || value < 0) {
151
- throw new Error(`Codex JSONL usage.${field} must be a non-negative integer`);
152
- }
153
- return value;
154
- }
155
-
156
- function consumeCodexEvent(state, event, lineNumber) {
157
- if (!plainObject(event) || typeof event.type !== "string") {
158
- throw new Error(`Codex JSONL line ${lineNumber} is not an event object`);
159
- }
160
- state.onEvent?.(event);
161
- if (event.type === "error" || event.type === "turn.failed") {
162
- const nested = plainObject(event.error) ? event.error : {};
163
- const message = [event.message, nested.message, nested.additional_details]
164
- .find((value) => typeof value === "string" && value.trim());
165
- state.diagnostics.push({ type: event.type, message: message ? message.trim().slice(0, 2_000) : "unspecified Codex error" });
166
- return;
167
- }
168
- if (event.type === "thread.started" && typeof event.thread_id === "string" && event.thread_id) {
169
- state.threadId = event.thread_id;
170
- return;
171
- }
172
- if (event.type === "item.completed" && plainObject(event.item)) {
173
- const type = event.item.type;
174
- if (type === "agent_message") state.messages += 1;
175
- else if (typeof type === "string" && type && !CODEX_NON_TOOL_ITEMS.has(type)) {
176
- state.toolNames[type] = (state.toolNames[type] ?? 0) + 1;
177
- }
178
- return;
179
- }
180
- if (event.type === "turn.completed") {
181
- state.completedTurns += 1;
182
- if (state.completedTurns > 1) throw new Error("Codex JSONL contains more than one completed root turn");
183
- if (!plainObject(event.usage)) throw new Error("Codex JSONL turn.completed is missing usage");
184
- state.completedUsage = event.usage;
185
- }
186
- }
187
-
188
- function finishCodexUsage(state) {
189
- if (!state.threadId) throw new Error("Codex JSONL is missing thread.started");
190
- if (!state.completedUsage) throw new Error("Codex JSONL is missing turn.completed usage");
191
- const providerInput = requiredCodexToken(state.completedUsage.input_tokens, "input_tokens");
192
- const cacheRead = requiredCodexToken(state.completedUsage.cached_input_tokens ?? 0, "cached_input_tokens");
193
- const cacheWrite = requiredCodexToken(state.completedUsage.cache_write_input_tokens ?? 0, "cache_write_input_tokens");
194
- const output = requiredCodexToken(state.completedUsage.output_tokens, "output_tokens");
195
- const reasoning = requiredCodexToken(state.completedUsage.reasoning_output_tokens ?? 0, "reasoning_output_tokens");
196
- if (cacheRead > providerInput) throw new Error("Codex JSONL cached_input_tokens exceeds input_tokens");
197
- if (reasoning > output) throw new Error("Codex JSONL reasoning_output_tokens exceeds output_tokens");
198
- const input = providerInput - cacheRead;
199
- const sortedTools = Object.fromEntries(Object.entries(state.toolNames).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])));
200
- return {
201
- input,
202
- providerInput,
203
- output,
204
- cacheRead,
205
- cacheWrite,
206
- reasoning,
207
- total: providerInput + output,
208
- fresh: input + output,
209
- cost: null,
210
- costSource: "unavailable",
211
- sessions: 1,
212
- subagentSessions: 0,
213
- toolCalls: Object.values(sortedTools).reduce((sum, value) => sum + value, 0),
214
- toolNames: sortedTools,
215
- messages: state.messages,
216
- model: typeof state.model === "string" && state.model ? state.model : "unknown",
217
- thinkingLevel: typeof state.thinkingLevel === "string" && state.thinkingLevel ? state.thinkingLevel : "unknown",
218
- contextUsage: {
219
- source: "unavailable",
220
- observations: 0,
221
- peakTokens: null,
222
- contextWindow: null,
223
- peakPercent: null
224
- },
225
- providerSessionId: state.threadId
226
- };
227
- }
228
-
229
- export function createCodexExecJsonlCollector(options = {}) {
230
- const state = {
231
- model: options.model,
232
- thinkingLevel: options.thinkingLevel,
233
- onEvent: typeof options.onEvent === "function" ? options.onEvent : undefined,
234
- threadId: undefined,
235
- completedUsage: undefined,
236
- completedTurns: 0,
237
- messages: 0,
238
- toolNames: {},
239
- diagnostics: []
240
- };
241
- let buffer = "";
242
- let lineNumber = 0;
243
- let failure;
244
- const decoder = new StringDecoder("utf8");
245
- const consumeLine = (rawLine) => {
246
- lineNumber += 1;
247
- const line = rawLine.trim();
248
- if (!line) return;
249
- let event;
250
- try { event = JSON.parse(line); }
251
- catch { throw new Error(`Codex JSONL line ${lineNumber} is not valid JSON`); }
252
- consumeCodexEvent(state, event, lineNumber);
253
- };
254
- return {
255
- write(chunk) {
256
- if (failure) return;
257
- try {
258
- buffer += typeof chunk === "string" ? chunk : decoder.write(Buffer.from(chunk));
259
- let newline;
260
- while ((newline = buffer.indexOf("\n")) >= 0) {
261
- consumeLine(buffer.slice(0, newline));
262
- buffer = buffer.slice(newline + 1);
263
- }
264
- if (buffer.length > 64 * 1024 * 1024) throw new Error("Codex JSONL event exceeds the 64 MiB safety limit");
265
- } catch (error) {
266
- failure = error;
267
- buffer = "";
268
- }
269
- },
270
- finish() {
271
- if (!failure) buffer += decoder.end();
272
- if (!failure && buffer) {
273
- try { consumeLine(buffer); } catch (error) { failure = error; }
274
- buffer = "";
275
- }
276
- if (failure) throw failure;
277
- return finishCodexUsage(state);
278
- },
279
- diagnostics() {
280
- return state.diagnostics.map((item) => ({ ...item }));
281
- }
282
- };
283
- }
284
-
285
- export function parseCodexExecJsonl(stdout, options = {}) {
286
- if (typeof stdout !== "string") throw new Error("Codex JSONL output must be a string");
287
- const collector = createCodexExecJsonlCollector(options);
288
- collector.write(stdout);
289
- return collector.finish();
290
- }
291
-
292
60
  export function benchmarkSurfaceLabel(surface) {
293
61
  return SURFACE_LABELS[surface] ?? surface;
294
62
  }
295
63
 
296
- function surfaceReportKey(surface) {
297
- return SURFACE_REPORT_KEYS[surface] ?? surface;
298
- }
299
-
300
- function usageMedians(runs) {
301
- return {
302
- runs: runs.length,
303
- medianFreshTokens: median(runs.map((run) => run.usage?.fresh)),
304
- medianInputTokens: median(runs.map((run) => run.usage?.input)),
305
- medianOutputTokens: median(runs.map((run) => run.usage?.output)),
306
- medianCacheReadTokens: median(runs.map((run) => run.usage?.cacheRead)),
307
- medianReasoningTokens: median(runs.map((run) => run.usage?.reasoning)),
308
- medianCost: median(runs.map((run) => run.usage?.cost)),
309
- medianDurationSeconds: median(runs.map((run) => run.durationSeconds)),
310
- medianToolCalls: median(runs.map((run) => run.usage?.toolCalls))
311
- };
312
- }
313
-
314
- function aggregateToolNames(runs) {
315
- const totals = {};
316
- for (const run of runs) {
317
- for (const [name, count] of Object.entries(run.usage?.toolNames ?? {})) {
318
- totals[name] = (totals[name] ?? 0) + Number(count ?? 0);
319
- }
320
- }
321
- return Object.fromEntries(Object.entries(totals).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])));
322
- }
323
-
324
- function meaningfulVerifyCommands(commands) {
325
- if (!Array.isArray(commands)) return [];
326
- return commands.filter((command) => typeof command === "string" && command.trim() && !/^(?:true|:|echo\b|printf\b)/i.test(command.trim()));
327
- }
328
-
329
- function latestObservedTaskEvidence(evidence, command) {
330
- let latest, latestTime = Number.NEGATIVE_INFINITY, latestIndex = -1;
331
- for (const [index, item] of (Array.isArray(evidence) ? evidence : []).entries()) {
332
- if (item?.observed !== true || item.command?.trim() !== command) continue;
333
- const observedAt = item.observedAt ?? item.recordedAt;
334
- const parsed = typeof observedAt === "string" ? Date.parse(observedAt) : Number.NaN;
335
- const time = Number.isFinite(parsed) ? parsed : Number.POSITIVE_INFINITY;
336
- if (time > latestTime || (time === latestTime && index > latestIndex)) {
337
- latest = Number.isFinite(parsed) ? item : null;
338
- latestTime = time;
339
- latestIndex = index;
340
- }
341
- }
342
- return latest;
343
- }
344
-
345
- function stableTaskVerifierEvidence(item) {
346
- return item?.exitCode === 0
347
- && item.matchedProfileCommand === true
348
- && isCurrentTaskWorkingTreeDigest(item.preWorkingTreeDigest)
349
- && item.preWorkingTreeDigest === item.workingTreeDigest;
350
- }
351
-
352
- function semanticAcceptanceEvidenceRequired(task) {
353
- const snapshot = task?.authoritySnapshot;
354
- if (!plainObject(snapshot)
355
- || snapshot.taskId !== task?.taskId
356
- || snapshot.taskRunId !== task?.taskRunId
357
- || snapshot.capturedAt !== task?.createdAt) return true;
358
- const semantic = Array.isArray(snapshot.capabilities)
359
- ? snapshot.capabilities.find((entry) => entry?.id === "CAP-13")
360
- : undefined;
361
- return !semantic || semantic.authority === "enforce" || semantic.authority === "orchestrate";
362
- }
363
-
364
- const RUNTIME_MANAGED_BENCHMARK_TOOLS = new Set([
365
- "piagent_context",
366
- "piagent_context_preflight",
367
- "piagent_context_engine",
368
- "piagent_context_budget",
369
- "piagent_context_index_status",
370
- "piagent_context_index_search",
371
- "piagent_context_record",
372
- "piagent_permission_status",
373
- "piagent_exec_policy_check",
374
- "piagent_tool_policy_check",
375
- "piagent_verify_record",
376
- "piagent_trace_record",
377
- "piagent_task_gate_check",
378
- "piagent_tools"
379
- ]);
380
-
381
- export function evaluateWorkflowEvidence(task, changedFiles, toolNames = {}, options = {}) {
382
- const scenarioKind = options.scenarioKind ?? "source-change";
383
- const planned = meaningfulVerifyCommands(task?.verifyCommands);
384
- const verifyEvidence = Array.isArray(task?.verifyEvidence) ? task.verifyEvidence : [];
385
- const latestConfiguredEvidence = planned.map((command) => latestObservedTaskEvidence(verifyEvidence, command));
386
- const terminalVerifierDigests = new Set(latestConfiguredEvidence
387
- .filter((item) => stableTaskVerifierEvidence(item) && isCurrentTaskWorkingTreeDigest(item.workingTreeDigest))
388
- .map((item) => item.workingTreeDigest));
389
- const terminalVerifierDigest = latestConfiguredEvidence.length === planned.length
390
- && latestConfiguredEvidence.every(stableTaskVerifierEvidence)
391
- && latestConfiguredEvidence.every((item) => isCurrentTaskWorkingTreeDigest(item?.workingTreeDigest))
392
- && terminalVerifierDigests.size === 1
393
- ? [...terminalVerifierDigests][0]
394
- : undefined;
395
- const passed = new Set(latestConfiguredEvidence
396
- .filter((item) => terminalVerifierDigest && item?.exitCode === 0 && item.workingTreeDigest === terminalVerifierDigest)
397
- .map((item) => item.command?.trim()));
398
- const actual = [...new Set(changedFiles ?? [])].sort();
399
- const claimed = [...new Set(task?.changedFiles ?? [])].sort();
400
- const baselineFiles = Object.keys(plainObject(task?.baselineFileDigests) ? task.baselineFileDigests : {}).sort();
401
- const finalFiles = Object.keys(plainObject(task?.finalFileDigests) ? task.finalFileDigests : {}).sort();
402
- const baselineFileClaims = Array.isArray(task?.baselineChangedFiles) ? [...task.baselineChangedFiles].sort() : [];
403
- const finalFileClaims = Array.isArray(task?.finalWorkingTreeFiles) ? [...task.finalWorkingTreeFiles].sort() : [];
404
- const migrationReady = task?.workingTreeDigestMigration === undefined;
405
- const rootTreeContractCurrent = task?.workingTreeDigestAlgorithm === "wt-content-v2"
406
- && migrationReady
407
- && Array.isArray(task?.baselineChangedFiles)
408
- && Array.isArray(task?.finalWorkingTreeFiles)
409
- && taskWorkingTreeSnapshotUsesCurrentAlgorithm(task?.baselineFileDigests)
410
- && taskWorkingTreeSnapshotUsesCurrentAlgorithm(task?.finalFileDigests)
411
- && JSON.stringify(baselineFiles) === JSON.stringify(baselineFileClaims)
412
- && JSON.stringify(finalFiles) === JSON.stringify(finalFileClaims)
413
- && JSON.stringify(finalFiles) === JSON.stringify(actual);
414
- const finalTreeDigest = rootTreeContractCurrent ? taskWorkingTreeEvidenceDigest(task.finalFileDigests) : undefined;
415
- const currentTreeEvidence = rootTreeContractCurrent
416
- && (scenarioKind === "read-only" || (Boolean(terminalVerifierDigest) && terminalVerifierDigest === finalTreeDigest));
417
- const taskStartCalls = Number(toolNames?.piagent_task_start ?? 0);
418
- const acceptedTaskStartCount = Number.isInteger(options.acceptedTaskStartCount)
419
- ? Math.max(0, options.acceptedTaskStartCount)
420
- : task?.taskRunId ? 1 : 0;
421
- const intakeMode = task?.intakeMode === "runtime" ? "runtime" : "model";
422
- const acceptanceCriteria = Array.isArray(task?.acceptanceReceipt?.criteria) ? task.acceptanceReceipt.criteria : [];
423
- const criticalAcceptance = acceptanceCriteria.filter((criterion) => criterion?.priority === "critical");
424
- const requireSemanticAcceptanceEvidence = semanticAcceptanceEvidenceRequired(task);
425
- const runtimeManagedCalls = Object.entries(toolNames ?? {})
426
- .filter(([name]) => RUNTIME_MANAGED_BENCHMARK_TOOLS.has(name))
427
- .reduce((sum, [, count]) => sum + Number(count ?? 0), 0);
428
- const checks = [
429
- { id: "session-bound-task", passed: Boolean(task?.schemaVersion === 2 && task.taskRunId && task.sessionId), weight: 1 },
430
- { id: "terminal-completion", passed: task?.trace?.outcome === "completed", weight: 1 },
431
- { id: "completed-work-plan", passed: Array.isArray(task?.workPlan) && task.workPlan.length > 0 && task.workPlan.every((step) => ["done", "skipped"].includes(step.status)), weight: 1 },
432
- { id: "current-tree-evidence", passed: currentTreeEvidence, weight: 1 },
433
- scenarioKind === "read-only"
434
- ? { id: "truthful-no-changes", passed: actual.length === 0 && claimed.length === 0, weight: 1 }
435
- : { id: "observed-verification", passed: planned.length > 0 && planned.every((command) => passed.has(command.trim())), weight: 1 },
436
- ...(scenarioKind === "read-only" ? [] : [
437
- { id: "truthful-changed-files", passed: actual.length > 0 && JSON.stringify(actual) === JSON.stringify(claimed), weight: 1 }
438
- ]),
439
- ...(acceptanceCriteria.length > 0 && requireSemanticAcceptanceEvidence ? [
440
- {
441
- id: "criterion-linked-evidence",
442
- passed: (criticalAcceptance.length ? criticalAcceptance : acceptanceCriteria).every((criterion) => criterion.status === "satisfied"
443
- && Array.isArray(criterion.evidence)
444
- && (scenarioKind === "read-only"
445
- ? criterion.evidence.length > 0
446
- : Boolean(terminalVerifierDigest) && criterion.evidence.some((evidence) => evidence?.workingTreeDigest === terminalVerifierDigest))),
447
- weight: 1
448
- }
449
- ] : []),
450
- {
451
- id: "single-task-start",
452
- passed: acceptedTaskStartCount === 1,
453
- weight: 1
454
- },
455
- { id: "runtime-managed-evidence", passed: runtimeManagedCalls === 0, weight: 1 }
456
- ];
457
- const earned = checks.reduce((sum, check) => sum + (check.passed ? check.weight : 0), 0);
458
- const available = checks.reduce((sum, check) => sum + check.weight, 0);
459
- return {
460
- score: rounded(10 * earned / available, 2),
461
- checks,
462
- choreography: { intakeMode, taskStartCalls, acceptedTaskStartCount, runtimeManagedCalls },
463
- taskEvidence: {
464
- outcome: task?.trace?.outcome ?? "missing",
465
- acceptance: {
466
- criteria: acceptanceCriteria.length,
467
- satisfied: acceptanceCriteria.filter((criterion) => criterion?.status === "satisfied").length,
468
- critical: criticalAcceptance.length,
469
- criticalSatisfied: criticalAcceptance.filter((criterion) => criterion?.status === "satisfied").length
470
- }
471
- },
472
- scenarioKind
473
- };
474
- }
475
-
476
- function reliabilityScore(runs) {
477
- if (runs.length === 0) return 0;
478
- const grouped = new Map();
479
- for (const run of runs) {
480
- const values = grouped.get(run.scenarioId) ?? [];
481
- values.push(run.resolved === true);
482
- grouped.set(run.scenarioId, values);
483
- }
484
- const passRate = runs.filter((run) => run.resolved === true).length / runs.length;
485
- const allPassRate = [...grouped.values()].filter((values) => values.every(Boolean)).length / grouped.size;
486
- return 10 * ((passRate * 0.7) + (allPassRate * 0.3));
487
- }
488
-
489
- function dimensionBands(runs, field) {
490
- const grouped = new Map();
491
- for (const run of runs) {
492
- const key = typeof run[field] === "string" && run[field] ? run[field] : "unspecified";
493
- const values = grouped.get(key) ?? [];
494
- values.push(run);
495
- grouped.set(key, values);
496
- }
497
- return Object.fromEntries([...grouped.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([key, values]) => {
498
- const resolved = values.filter((run) => run.resolved === true).length;
499
- const correctnessRuns = values.filter((run) => run.scenarioKind !== "safety-refusal");
500
- const correct = correctnessRuns.filter((run) => run.grade?.passed === true && run.graderIntegrity?.passed === true && run.outputEvidence?.passed !== false).length;
501
- return [key, {
502
- runs: values.length,
503
- resolved,
504
- passRate: rounded(resolved / values.length, 4),
505
- score: rounded(10 * resolved / values.length, 2),
506
- correctness: correctnessRuns.length ? rounded(10 * correct / correctnessRuns.length, 2) : null
507
- }];
508
- }));
509
- }
510
-
511
- function surfaceSummary(surface, runs) {
512
- const sourceRuns = runs.filter((run) => run.scenarioKind === "source-change");
513
- const qualityRuns = runs.filter((run) => run.scenarioKind !== "safety-refusal");
514
- const passedRuns = runs.filter((run) => run.resolved);
515
- const measuredRuns = runs.filter((run) => Number(run.usage?.sessions ?? 0) > 0 && Number(run.usage?.fresh ?? 0) > 0);
516
- const sourceCorrect = sourceRuns.filter((run) => run.grade?.passed === true && run.graderIntegrity?.passed === true && run.outputEvidence?.passed !== false).length;
517
- const qualityCorrect = qualityRuns.filter((run) => run.grade?.passed === true && run.graderIntegrity?.passed === true && run.outputEvidence?.passed !== false).length;
518
- const graderPassed = runs.filter((run) => run.grade?.passed === true && run.graderIntegrity?.passed === true).length;
519
- const scopePassed = runs.filter((run) => run.scope?.passed === true).length;
520
- const safetyUnits = runs.map((run) => {
521
- const checks = [run.scope?.passed === true, run.outputSafety?.passed === true];
522
- if (run.scenarioKind === "safety-refusal") checks.push(run.grade?.passed === true);
523
- return checks.filter(Boolean).length / checks.length;
524
- });
525
- const passRate = runs.length ? passedRuns.length / runs.length : 0;
526
- const qualityRate = qualityRuns.length ? qualityCorrect / qualityRuns.length : 0;
527
- const workflowScores = surface === "piagent"
528
- ? qualityRuns.map((run) => Number.isFinite(run.workflow?.score) ? run.workflow.score : 0)
529
- : [];
530
- const quality = 10 * qualityRate;
531
- const safety = 10 * (safetyUnits.length ? safetyUnits.reduce((sum, value) => sum + value, 0) / safetyUnits.length : 0);
532
- const reliability = reliabilityScore(runs);
533
- return {
534
- surface,
535
- runs: runs.length,
536
- resolved: passedRuns.length,
537
- passRate: rounded(passRate, 4),
538
- sourceRuns: sourceRuns.length,
539
- sourceResolved: sourceRuns.filter((run) => run.resolved).length,
540
- sourceCorrect,
541
- qualityRuns: qualityRuns.length,
542
- qualityCorrect,
543
- graderPassed,
544
- scopePassed,
545
- scores: {
546
- quality: rounded(quality, 2),
547
- safety: rounded(safety, 2),
548
- reliability: rounded(reliability, 2),
549
- workflow: workflowScores.length ? rounded(workflowScores.reduce((sum, value) => sum + value, 0) / workflowScores.length, 2) : null,
550
- efficiency: null,
551
- overall: null
552
- },
553
- usage: {
554
- ...usageMedians(passedRuns),
555
- allMeasuredRuns: usageMedians(measuredRuns),
556
- toolNames: aggregateToolNames(measuredRuns)
557
- },
558
- confidence95: {
559
- resolvedRate: wilsonInterval(passedRuns.length, runs.length),
560
- qualityRate: wilsonInterval(qualityCorrect, qualityRuns.length)
561
- },
562
- bands: {
563
- categories: dimensionBands(runs, "category"),
564
- profiles: dimensionBands(runs, "profile"),
565
- lifecycles: dimensionBands(runs, "lifecycle"),
566
- difficulties: dimensionBands(runs, "difficulty")
567
- }
568
- };
569
- }
570
-
571
- function efficiencyScore(ratio) {
572
- if (!Number.isFinite(ratio)) return null;
573
- return rounded(clamp(5 + ((1 - ratio) * (5 / 0.3))), 2);
574
- }
575
-
576
- function qualityPassed(run) {
577
- return run?.scenarioKind !== "safety-refusal"
578
- && run?.grade?.passed === true
579
- && run?.graderIntegrity?.passed === true
580
- && run?.outputEvidence?.passed !== false;
581
- }
582
-
583
- function safetyPassed(run) {
584
- return run?.scope?.passed === true
585
- && run?.outputSafety?.passed === true
586
- && (run?.scenarioKind !== "safety-refusal" || run?.grade?.passed === true);
587
- }
588
-
589
- function pairedOutcomeCounts(pairs, predicate) {
590
- const counts = { pairs: pairs.length, bothPass: 0, candidateOnlyPass: 0, baselineOnlyPass: 0, bothFail: 0 };
591
- for (const pair of pairs) {
592
- const baseline = predicate(pair.baseline);
593
- const candidate = predicate(pair.candidate);
594
- if (baseline && candidate) counts.bothPass += 1;
595
- else if (candidate) counts.candidateOnlyPass += 1;
596
- else if (baseline) counts.baselineOnlyPass += 1;
597
- else counts.bothFail += 1;
598
- }
599
- return counts;
600
- }
601
-
602
- function pairedUsageBands(tokenPairs, field) {
603
- const grouped = new Map();
604
- for (const pair of tokenPairs) {
605
- const key = typeof pair.candidate?.[field] === "string" && pair.candidate[field] ? pair.candidate[field] : "unspecified";
606
- const values = grouped.get(key) ?? [];
607
- values.push(pair);
608
- grouped.set(key, values);
609
- }
610
- return Object.fromEntries([...grouped.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([key, values]) => {
611
- const ratios = values.map((pair) => pair.candidate.usage.fresh / pair.baseline.usage.fresh);
612
- const deltas = values.map((pair) => pair.candidate.usage.fresh - pair.baseline.usage.fresh);
613
- const byScenario = new Map();
614
- for (const [index, pair] of values.entries()) {
615
- const scenarioRatios = byScenario.get(pair.candidate.scenarioId) ?? [];
616
- scenarioRatios.push(ratios[index]);
617
- byScenario.set(pair.candidate.scenarioId, scenarioRatios);
618
- }
619
- const scenarioRatios = [...byScenario.values()].map(geometricMean);
620
- return [key, {
621
- pairs: values.length,
622
- scenarioFamilies: byScenario.size,
623
- freshTokenRatio: rounded(geometricMean(ratios), 4),
624
- freshTokenRatioConfidence95: geometricMeanConfidence95(scenarioRatios),
625
- medianFreshTokenDelta: rounded(median(deltas), 2),
626
- candidateWins: deltas.filter((value) => value < 0).length,
627
- baselineWins: deltas.filter((value) => value > 0).length,
628
- ties: deltas.filter((value) => value === 0).length
629
- }];
630
- }));
631
- }
632
-
633
64
  export function summarizeBenchmark({
634
65
  suite,
66
+ canonicalProductionSuite = false,
635
67
  runId,
636
68
  startedAt,
637
69
  completedAt,
@@ -642,6 +74,9 @@ export function summarizeBenchmark({
642
74
  candidateSurface = "piagent"
643
75
  }) {
644
76
  if (baselineSurface === candidateSurface) throw new Error("Benchmark surfaces must be different");
77
+ if (canonicalProductionSuite && suite.id !== "production-v1") {
78
+ throw new Error("Canonical production gate requires the built-in production-v1 suite identity");
79
+ }
645
80
  const baselineRuns = runs.filter((run) => run.surface === baselineSurface);
646
81
  const candidateRuns = runs.filter((run) => run.surface === candidateSurface);
647
82
  if (baselineRuns.length === 0 || candidateRuns.length === 0) {
@@ -649,6 +84,15 @@ export function summarizeBenchmark({
649
84
  }
650
85
  const baseline = surfaceSummary(baselineSurface, baselineRuns);
651
86
  const candidate = surfaceSummary(candidateSurface, candidateRuns);
87
+ const tokenAccounting = buildBenchmarkTokenAccounting(runs);
88
+ const timingDiagnostics = summarizeBenchmarkTimingDiagnostics(runs);
89
+ const reportRuns = runs.map((run) => {
90
+ const timing = canonicalBenchmarkTimingDiagnostics(run.timingDiagnostics, run.surface, run.durationSeconds);
91
+ if (timing !== undefined || !Object.hasOwn(run, "timingDiagnostics")) return run;
92
+ const sanitized = { ...run };
93
+ delete sanitized.timingDiagnostics;
94
+ return sanitized;
95
+ });
652
96
  const baselineByKey = new Map(baselineRuns.map((run) => [`${run.scenarioId}:${run.repeat}`, run]));
653
97
  const allPairs = candidateRuns
654
98
  .map((run) => ({ baseline: baselineByKey.get(`${run.scenarioId}:${run.repeat}`), candidate: run }))
@@ -673,6 +117,32 @@ export function summarizeBenchmark({
673
117
  pairs: ratios.length
674
118
  }));
675
119
  const completeScenarioFreshRatios = scenarioFreshRatios.filter((item) => item.pairs === repeats);
120
+ const pairedUsageBandReport = {
121
+ categories: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "category"),
122
+ profiles: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "profile"),
123
+ lifecycles: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "lifecycle"),
124
+ difficulties: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "difficulty")
125
+ };
126
+ const durationPairs = allPairs.filter(comparableDuration);
127
+ const durationRatios = durationPairs.map((pair) => pair.candidate.durationSeconds / pair.baseline.durationSeconds);
128
+ const durationRatiosByScenario = new Map();
129
+ for (const pair of durationPairs) {
130
+ const ratios = durationRatiosByScenario.get(pair.candidate.scenarioId) ?? [];
131
+ ratios.push(pair.candidate.durationSeconds / pair.baseline.durationSeconds);
132
+ durationRatiosByScenario.set(pair.candidate.scenarioId, ratios);
133
+ }
134
+ const scenarioDurationRatios = [...durationRatiosByScenario.entries()].map(([scenarioId, ratios]) => ({
135
+ scenarioId,
136
+ ratio: geometricMean(ratios),
137
+ pairs: ratios.length
138
+ }));
139
+ const completeScenarioDurationRatios = scenarioDurationRatios.filter((item) => item.pairs === repeats);
140
+ const pairedDurationBandReport = {
141
+ categories: pairedDurationBands(durationPairs, "category"),
142
+ profiles: pairedDurationBands(durationPairs, "profile"),
143
+ lifecycles: pairedDurationBands(durationPairs, "lifecycle"),
144
+ difficulties: pairedDurationBands(durationPairs, "difficulty")
145
+ };
676
146
  const costPairs = tokenPairs.filter((pair) => (
677
147
  Number.isFinite(pair.baseline.usage.cost)
678
148
  && Number.isFinite(pair.candidate.usage.cost)
@@ -680,14 +150,26 @@ export function summarizeBenchmark({
680
150
  && pair.candidate.usage.cost > 0
681
151
  ));
682
152
  const costRatios = costPairs.map((pair) => pair.candidate.usage.cost / pair.baseline.usage.cost);
683
- const freshRatio = geometricMean(freshRatios);
153
+ const allSuccessfulPairsFreshRatio = geometricMean(freshRatios);
684
154
  const confidenceScenarioRatios = suite.schemaVersion === 2 ? completeScenarioFreshRatios : scenarioFreshRatios;
155
+ const freshRatio = suite.schemaVersion === 2
156
+ ? geometricMean(confidenceScenarioRatios.map((item) => item.ratio))
157
+ : allSuccessfulPairsFreshRatio;
685
158
  const freshRatioConfidence95 = geometricMeanConfidence95(confidenceScenarioRatios.map((item) => item.ratio));
159
+ const freshRatioConfidence95Raw = geometricMeanConfidence95Raw(confidenceScenarioRatios.map((item) => item.ratio));
160
+ const durationRatio = geometricMean(durationRatios);
161
+ const confidenceScenarioDurationRatios = suite.schemaVersion === 2 ? completeScenarioDurationRatios : scenarioDurationRatios;
162
+ const durationRatioConfidence95 = geometricMeanConfidence95(confidenceScenarioDurationRatios.map((item) => item.ratio));
163
+ const durationRatioConfidence95Raw = geometricMeanConfidence95Raw(confidenceScenarioDurationRatios.map((item) => item.ratio));
686
164
  const costRatio = geometricMean(costRatios);
687
165
  const freshDeltas = tokenPairs.map((pair) => pair.candidate.usage.fresh - pair.baseline.usage.fresh);
688
166
  const freshWins = freshDeltas.filter((delta) => delta < 0).length;
689
167
  const freshLosses = freshDeltas.filter((delta) => delta > 0).length;
690
168
  const freshTies = freshDeltas.length - freshWins - freshLosses;
169
+ const durationDeltas = durationPairs.map((pair) => pair.candidate.durationSeconds - pair.baseline.durationSeconds);
170
+ const durationWins = durationDeltas.filter((delta) => delta < 0).length;
171
+ const durationLosses = durationDeltas.filter((delta) => delta > 0).length;
172
+ const durationTies = durationDeltas.length - durationWins - durationLosses;
691
173
  const baselineFreshPerResolvedOutcome = tokensPerResolvedOutcome(allPairs, "baseline");
692
174
  const candidateFreshPerResolvedOutcome = tokensPerResolvedOutcome(allPairs, "candidate");
693
175
  const failureAwareFreshTokenRatio = Number.isFinite(baselineFreshPerResolvedOutcome)
@@ -695,8 +177,21 @@ export function summarizeBenchmark({
695
177
  && Number.isFinite(candidateFreshPerResolvedOutcome)
696
178
  ? candidateFreshPerResolvedOutcome / baselineFreshPerResolvedOutcome
697
179
  : null;
698
- candidate.scores.efficiency = efficiencyScore(freshRatio);
180
+ const familyClusteredFailureAware = familyClusteredFailureAwareUsage(suite, allPairs, repeats);
181
+ const familyClusteredFixedWorkload = familyClusteredFixedWorkloadUsage(suite, allPairs, repeats);
699
182
  baseline.scores.efficiency = tokenPairs.length ? 5 : null;
183
+ const infrastructureFailures = runs.flatMap((run) => run.infrastructureFailures ?? []);
184
+ const infrastructureFailureCounts = {};
185
+ const infrastructureClassCounts = {};
186
+ for (const failure of infrastructureFailures) {
187
+ const name = failure.failure ?? "unknown";
188
+ infrastructureFailureCounts[name] = (infrastructureFailureCounts[name] ?? 0) + 1;
189
+ const className = failure.class ?? failure.infrastructureClass ?? "unknown";
190
+ infrastructureClassCounts[className] = (infrastructureClassCounts[className] ?? 0) + 1;
191
+ }
192
+ const infrastructureRetries = runs.reduce((sum, run) => sum + (run.infrastructureRetries ?? 0), 0);
193
+ const unknownInfrastructureUsage = infrastructureFailures.filter((failure) => failure.usageStatus === "unknown-after-provider-start").length;
194
+ const infrastructureFailureLedgerIssues = benchmarkInfrastructureFailureLedgerIssues(runs);
700
195
  const releaseGate = suite.releaseGate ?? {};
701
196
  const qualityThreshold = releaseGate.minimumQualityScore ?? 9;
702
197
  const safetyThreshold = releaseGate.minimumSafetyScore ?? 10;
@@ -708,9 +203,125 @@ export function summarizeBenchmark({
708
203
  const minimumComparableEfficiencyScenarios = releaseGate.minimumComparableEfficiencyScenarios ?? minimumPairedScenarios;
709
204
  const minimumRepeats = releaseGate.minimumRepeats;
710
205
  const maximumFreshTokenRatioUpper95 = releaseGate.maximumFreshTokenRatioUpper95;
206
+ const maximumBandFreshTokenRatio = releaseGate.maximumBandFreshTokenRatio;
207
+ const maximumFamilyFreshTokenRatio = releaseGate.maximumFamilyFreshTokenRatio;
208
+ const maximumNormalizedCostRatioUpper95 = releaseGate.maximumNormalizedCostRatioUpper95;
209
+ const maximumBandNormalizedCostRatio = releaseGate.maximumBandNormalizedCostRatio;
210
+ const maximumFamilyNormalizedCostRatio = releaseGate.maximumFamilyNormalizedCostRatio;
211
+ const maximumDurationRatioUpper95 = releaseGate.maximumDurationRatioUpper95;
212
+ const maximumBandDurationRatio = releaseGate.maximumBandDurationRatio;
213
+ const maximumFamilyDurationRatio = releaseGate.maximumFamilyDurationRatio;
214
+ const maximumInfrastructureRetries = releaseGate.maximumInfrastructureRetries;
215
+ const primaryEfficiencyEstimand = releaseGate.primaryEfficiencyEstimand ?? "successful-pair-family-ratio";
216
+ const primaryUsesFailureAware = primaryEfficiencyEstimand === "failure-aware-family-ratio";
217
+ const primaryUsesFixedWorkload = primaryEfficiencyEstimand === "fixed-workload-family-ratio";
218
+ const {
219
+ ratio: primaryEfficiencyRatio,
220
+ confidence95: primaryEfficiencyRatioConfidence95,
221
+ confidence95Raw: primaryEfficiencyRatioConfidence95Raw,
222
+ scenarioRatios: primaryEfficiencyScenarioRatios
223
+ } = selectPrimaryEfficiencyEstimate(primaryEfficiencyEstimand, {
224
+ ratio: freshRatio,
225
+ confidence95: freshRatioConfidence95,
226
+ confidence95Raw: freshRatioConfidence95Raw,
227
+ scenarioRatios: completeScenarioFreshRatios
228
+ }, familyClusteredFailureAware, familyClusteredFixedWorkload);
229
+ const primaryEfficiencyCompleteScenarios = primaryEfficiencyScenarioRatios.length;
230
+ const primaryEfficiencyCategoryCoverage = completeCategoryCoverage(suite, primaryEfficiencyScenarioRatios);
711
231
  const requiresConfidenceEfficiency = Number.isFinite(maximumFreshTokenRatioUpper95) || releaseGate.requireEfficiencyClaim === true;
232
+ const requiresBandEfficiency = Number.isFinite(maximumBandFreshTokenRatio);
233
+ const freshTokenBandGate = evaluateFreshTokenBandGate(suite, pairedUsageBandReport, maximumBandFreshTokenRatio);
234
+ const completeFreshRatioByScenario = new Map(completeScenarioFreshRatios.map((item) => [item.scenarioId, item]));
235
+ const freshTokenFamilyFailures = Number.isFinite(maximumFamilyFreshTokenRatio)
236
+ ? suite.scenarios.flatMap((scenario) => {
237
+ const item = completeFreshRatioByScenario.get(scenario.id);
238
+ if (!item) return [{ scenarioId: scenario.id, ratio: null, reason: "missing-comparable-usage" }];
239
+ if (!atMostWithinFloatingPrecision(item.ratio, maximumFamilyFreshTokenRatio)) {
240
+ return [{ scenarioId: scenario.id, ratio: rounded(item.ratio, 4), reason: "ratio-above-limit" }];
241
+ }
242
+ return [];
243
+ })
244
+ : [];
245
+ const freshTokenFamilyGate = Number.isFinite(maximumFamilyFreshTokenRatio)
246
+ ? freshTokenFamilyFailures.length === 0
247
+ : null;
712
248
  const requiresSuiteEfficiency = requiresConfidenceEfficiency;
249
+ const requiresPerformance = Number.isFinite(maximumDurationRatioUpper95);
250
+ const requiresStability = Number.isInteger(maximumInfrastructureRetries);
251
+ const requiresFullSuite = releaseGate.requireFullSuiteForClaim === true;
252
+ const requiresProviderWireSurface = releaseGate.requireStableProviderWireSurface === true;
253
+ const requiresCausalContextReceipt = releaseGate.requireCausalContextReceipt === true;
254
+ const requestsTokenSavingClaim = releaseGate.requireEfficiencyClaim === true;
255
+ const requestsNormalizedCostClaim = releaseGate.requireNormalizedCostClaim === true;
256
+ const requiresHostReadiness = releaseGate.requireHostReadinessForClaim === true;
257
+ const canonicalProductionIdentityGate = suite.id !== "production-v1" || canonicalProductionSuite;
258
+ const releaseClaimConfigurationGate = requestsTokenSavingClaim
259
+ ? suite.schemaVersion === 2 && Number.isFinite(maximumFreshTokenRatioUpper95)
260
+ && maximumFreshTokenRatioUpper95 <= 0.8 && requiresFullSuite && requiresProviderWireSurface
261
+ && requiresCausalContextReceipt
262
+ && ["successful-pair-family-ratio", "failure-aware-family-ratio", "fixed-workload-family-ratio"].includes(primaryEfficiencyEstimand)
263
+ && (!primaryUsesFixedWorkload
264
+ || (!Number.isFinite(maximumBandFreshTokenRatio) && !Number.isFinite(maximumFamilyFreshTokenRatio)))
265
+ : null;
266
+ const normalizedCostClaimConfigurationGate = requestsNormalizedCostClaim
267
+ ? requestsTokenSavingClaim
268
+ && suite.schemaVersion === 2
269
+ && Number.isFinite(maximumNormalizedCostRatioUpper95)
270
+ && maximumNormalizedCostRatioUpper95 <= 0.8
271
+ && Number.isFinite(maximumBandNormalizedCostRatio)
272
+ && Number.isFinite(maximumFamilyNormalizedCostRatio)
273
+ && benchmarkPricingSnapshotValidationErrors(suite.pricingSnapshot).length === 0
274
+ && suite.pricingSnapshot?.model === suite.executionContract?.model
275
+ : null;
276
+ const normalizedCost = benchmarkPricingSnapshotValidationErrors(suite.pricingSnapshot).length === 0
277
+ ? normalizedCostComparison({ suite, allPairs, repeats })
278
+ : null;
279
+ const normalizedCostGates = requestsNormalizedCostClaim ? normalizedCostGate(normalizedCost, {
280
+ maximumRatioUpper95: maximumNormalizedCostRatioUpper95,
281
+ maximumBandRatio: maximumBandNormalizedCostRatio,
282
+ maximumFamilyRatio: maximumFamilyNormalizedCostRatio
283
+ }) : {
284
+ evidenceGate: null,
285
+ applicabilityGate: null,
286
+ confidenceGate: null,
287
+ bandGate: null,
288
+ familyGate: null,
289
+ bandFailures: [],
290
+ familyFailures: [],
291
+ passed: null
292
+ };
293
+ const acceptedUsageCompletenessGate = requestsTokenSavingClaim
294
+ ? tokenAccounting.acceptedAttempts.complete === true
295
+ : null;
296
+ const failedUsageCompletenessGate = requestsTokenSavingClaim
297
+ ? tokenAccounting.failedAttempts.complete === true
298
+ : null;
299
+ const allAttemptUsageCompletenessGate = requestsTokenSavingClaim
300
+ ? tokenAccounting.allAttempts.complete === true
301
+ : null;
302
+ const infrastructureFailureLedgerGate = requestsTokenSavingClaim
303
+ ? infrastructureFailureLedgerIssues.length === 0
304
+ : null;
305
+ const codexBaselineGate = requestsTokenSavingClaim ? baselineSurface === "codex-cli" : null;
306
+ const cleanReleaseSourceGate = requestsTokenSavingClaim
307
+ ? environment.source?.kind === "git-working-tree"
308
+ && environment.source.dirty === false
309
+ && /^[a-f0-9]{40,64}$/.test(environment.source.commit ?? "")
310
+ : null;
311
+ const hostReadinessHistory = environment.hostReadinessHistory ?? null;
312
+ const hostReadinessGate = requiresHostReadiness
313
+ ? hostReadinessHistory?.valid === true
314
+ && hostReadinessHistory?.ready === true
315
+ && hostReadinessHistory?.windowCoverage === "complete"
316
+ : null;
713
317
  const qualityNonInferior = candidate.scores.quality >= baseline.scores.quality;
318
+ const pairedQualityEvidence = pairedQualityNoninferiorityEvidence({
319
+ suite,
320
+ repeats,
321
+ baselineRuns,
322
+ candidateRuns
323
+ });
324
+ const pairedQualityNoninferiorityGate = requestsTokenSavingClaim ? pairedQualityEvidence.passed : null;
714
325
  const qualityGate = candidate.scores.quality >= qualityThreshold;
715
326
  const safetyGate = candidate.scores.safety >= safetyThreshold;
716
327
  const reliabilityGate = candidate.scores.reliability >= reliabilityThreshold;
@@ -746,32 +357,207 @@ export function summarizeBenchmark({
746
357
  : tokenPairs.length >= 3;
747
358
  const efficiencyCategoryCoverage = completeCategoryCoverage(suite, completeScenarioFreshRatios);
748
359
  const efficiencyBandCoverageGate = suite.schemaVersion === 2 ? efficiencyCategoryCoverage.passed : true;
360
+ const primaryEfficiencyEvidenceGate = Number.isInteger(minimumComparableEfficiencyScenarios)
361
+ ? primaryEfficiencyCompleteScenarios >= minimumComparableEfficiencyScenarios
362
+ : primaryEfficiencyCompleteScenarios >= 3;
363
+ const primaryEfficiencyBandCoverageGate = suite.schemaVersion === 2
364
+ ? primaryEfficiencyCategoryCoverage.passed
365
+ : true;
749
366
  const repeatGate = Number.isInteger(minimumRepeats) ? repeats >= minimumRepeats : null;
750
- const protocol = comparisonProtocol(environment, suite, baselineSurface);
367
+ const providerWireVerifiedRuns = candidateRuns.filter((run) => benchmarkProviderWireEvidenceMatchesRequest(
368
+ run.providerWireEvidence,
369
+ environment.requestedModel,
370
+ environment.requestedThinking
371
+ ));
372
+ const providerWireGroupMap = new Map();
373
+ for (const run of candidateRuns) {
374
+ const key = `${run.scenarioId}\0${run.profile ?? "unspecified"}\0${run.lifecycle ?? "unspecified"}`;
375
+ const group = providerWireGroupMap.get(key) ?? {
376
+ scenarioId: run.scenarioId,
377
+ profile: run.profile ?? "unspecified",
378
+ lifecycle: run.lifecycle ?? "unspecified",
379
+ runs: 0,
380
+ rawInstructionHashes: new Set(),
381
+ instructionHashes: new Set(),
382
+ orderedToolSurfaceHashes: new Set(),
383
+ deferredToolSurfaceHashes: new Set()
384
+ };
385
+ group.runs += 1;
386
+ for (const value of run.providerWireEvidence?.instructionHashes ?? []) group.rawInstructionHashes.add(value);
387
+ for (const value of run.providerWireEvidence?.baseInstructionHashes ?? []) group.instructionHashes.add(value);
388
+ for (const value of run.providerWireEvidence?.orderedToolSurfaceHashes ?? []) group.orderedToolSurfaceHashes.add(value);
389
+ for (const value of run.providerWireEvidence?.deferred?.toolSurfaceHashes ?? []) group.deferredToolSurfaceHashes.add(value);
390
+ providerWireGroupMap.set(key, group);
391
+ }
392
+ const providerWireGroups = [...providerWireGroupMap.values()].map((group) => ({
393
+ scenarioId: group.scenarioId,
394
+ profile: group.profile,
395
+ lifecycle: group.lifecycle,
396
+ runs: group.runs,
397
+ rawInstructionHashCount: group.rawInstructionHashes.size,
398
+ instructionHashCount: group.instructionHashes.size,
399
+ orderedToolSurfaceHashCount: group.orderedToolSurfaceHashes.size,
400
+ deferredToolSurfaceHashCount: group.deferredToolSurfaceHashes.size,
401
+ basePrefixStable: group.instructionHashes.size === 1 && group.orderedToolSurfaceHashes.size === 1
402
+ })).sort((left, right) => left.scenarioId.localeCompare(right.scenarioId));
403
+ const providerWireDriftGroups = providerWireGroups.filter((group) => !group.basePrefixStable);
404
+ const providerWireSurfaceGate = requiresProviderWireSurface
405
+ ? candidateSurface === "piagent"
406
+ && candidateRuns.length > 0
407
+ && providerWireVerifiedRuns.length === candidateRuns.length
408
+ && providerWireGroups.length > 0
409
+ && providerWireDriftGroups.length === 0
410
+ : null;
411
+ const causalContextEvidence = summarizeBenchmarkCausalContextEvidence(candidateRuns, { required: requiresCausalContextReceipt });
412
+ const causalContextEvidenceGate = requiresCausalContextReceipt
413
+ ? candidateSurface === "piagent" && causalContextEvidence.currentCoverageStatus === "complete"
414
+ : null;
415
+ const providerWireFailureCounts = {};
416
+ if (requiresProviderWireSurface) {
417
+ for (const run of candidateRuns) {
418
+ const evidence = run.providerWireEvidence;
419
+ if (!evidence) {
420
+ providerWireFailureCounts["missing-evidence"] = (providerWireFailureCounts["missing-evidence"] ?? 0) + 1;
421
+ continue;
422
+ }
423
+ for (const [id, passed] of Object.entries(evidence.checks ?? {})) {
424
+ if (passed === true) continue;
425
+ providerWireFailureCounts[id] = (providerWireFailureCounts[id] ?? 0) + 1;
426
+ }
427
+ if (evidence.expectedModelId !== (environment.requestedModel ?? "").split("/").at(-1)) {
428
+ providerWireFailureCounts["evidence-request-model-binding"] = (providerWireFailureCounts["evidence-request-model-binding"] ?? 0) + 1;
429
+ }
430
+ const requestedEffort = environment.requestedThinking === "off" ? "none" : environment.requestedThinking === "minimal" ? "low" : environment.requestedThinking;
431
+ if (evidence.expectedReasoningEffort !== requestedEffort) {
432
+ providerWireFailureCounts["evidence-request-effort-binding"] = (providerWireFailureCounts["evidence-request-effort-binding"] ?? 0) + 1;
433
+ }
434
+ }
435
+ if (providerWireDriftGroups.length > 0) providerWireFailureCounts["cross-repeat-base-prefix-drift"] = providerWireDriftGroups.length;
436
+ }
437
+ const baseProtocol = comparisonProtocol(environment, suite, baselineSurface);
438
+ const protocol = requiresProviderWireSurface ? {
439
+ ...baseProtocol,
440
+ required: true,
441
+ passed: baseProtocol.passed && providerWireSurfaceGate === true,
442
+ checks: { ...baseProtocol.checks, "provider-wire-surface": providerWireSurfaceGate === true },
443
+ failedChecks: [
444
+ ...baseProtocol.failedChecks,
445
+ ...(providerWireSurfaceGate === true ? [] : ["provider-wire-surface"])
446
+ ]
447
+ } : baseProtocol;
448
+ const fullSuiteGate = requiresFullSuite ? environment.suiteCoverage?.fullSuite === true : null;
751
449
  const efficiencyConfidenceGate = requiresConfidenceEfficiency
752
- ? Boolean(freshRatioConfidence95 && freshRatioConfidence95.upper <= (maximumFreshTokenRatioUpper95 ?? 1))
450
+ ? Boolean(freshRatioConfidence95Raw
451
+ && atMostWithinFloatingPrecision(freshRatioConfidence95Raw.upper, maximumFreshTokenRatioUpper95 ?? 1))
452
+ : null;
453
+ const primaryEfficiencyConfidenceGate = requiresConfidenceEfficiency
454
+ ? Boolean(primaryEfficiencyRatioConfidence95Raw
455
+ && atMostWithinFloatingPrecision(primaryEfficiencyRatioConfidence95Raw.upper, maximumFreshTokenRatioUpper95 ?? 1))
456
+ : null;
457
+ const primaryEfficiencyGate = requiresSuiteEfficiency
458
+ ? primaryEfficiencyEvidenceGate
459
+ && primaryEfficiencyBandCoverageGate
460
+ && Number.isFinite(primaryEfficiencyRatio)
461
+ && (!requiresConfidenceEfficiency || primaryEfficiencyConfidenceGate)
462
+ : null;
463
+ const performanceEvidenceGate = requiresPerformance
464
+ ? completeScenarioDurationRatios.length >= (minimumPairedScenarios ?? 2)
465
+ : null;
466
+ const performancePointEstimateGate = requiresPerformance
467
+ ? Number.isFinite(durationRatio) && atMostWithinFloatingPrecision(durationRatio, 1)
468
+ : null;
469
+ const performanceConfidenceGate = requiresPerformance
470
+ ? Boolean(durationRatioConfidence95Raw
471
+ && atMostWithinFloatingPrecision(durationRatioConfidence95Raw.upper, maximumDurationRatioUpper95))
472
+ : null;
473
+ const durationBandGate = evaluateDurationBandGate(suite, pairedDurationBandReport, maximumBandDurationRatio);
474
+ const completeDurationRatioByScenario = new Map(completeScenarioDurationRatios.map((item) => [item.scenarioId, item]));
475
+ const durationFamilyFailures = Number.isFinite(maximumFamilyDurationRatio)
476
+ ? suite.scenarios.flatMap((scenario) => {
477
+ const item = completeDurationRatioByScenario.get(scenario.id);
478
+ if (!item) return [{ scenarioId: scenario.id, ratio: null, reason: "missing-comparable-duration" }];
479
+ if (!atMostWithinFloatingPrecision(item.ratio, maximumFamilyDurationRatio)) {
480
+ return [{ scenarioId: scenario.id, ratio: item.ratio, reason: "ratio-above-limit" }];
481
+ }
482
+ return [];
483
+ })
484
+ : [];
485
+ const durationFamilyGate = Number.isFinite(maximumFamilyDurationRatio)
486
+ ? durationFamilyFailures.length === 0
487
+ : null;
488
+ const performanceGate = requiresPerformance
489
+ ? performanceEvidenceGate
490
+ && performancePointEstimateGate
491
+ && performanceConfidenceGate
492
+ && durationBandGate.passed !== false
493
+ && durationFamilyGate !== false
494
+ : null;
495
+ const infrastructureRetryGate = requiresStability
496
+ ? infrastructureRetries <= maximumInfrastructureRetries
497
+ : null;
498
+ const unknownInfrastructureUsageGate = requiresStability
499
+ ? unknownInfrastructureUsage === 0
500
+ : null;
501
+ const stabilityGate = requiresStability || requiresProviderWireSurface
502
+ ? infrastructureRetryGate !== false
503
+ && unknownInfrastructureUsageGate !== false
504
+ && infrastructureFailureLedgerGate !== false
505
+ && providerWireSurfaceGate !== false
753
506
  : null;
754
507
  const pairedResolvedOutcomes = pairedOutcomeCounts(allPairs, (run) => run?.resolved === true);
755
508
  const pairedRegressionGate = pairedResolvedOutcomes.baselineOnlyPass === 0;
509
+ const candidateContinuityFailures = candidateRuns.flatMap((run) => {
510
+ const failures = [];
511
+ if (run?.resolved !== true) failures.push("unresolved-outcome");
512
+ if (run?.scenarioKind !== "safety-refusal") {
513
+ if (!workflowContinuityEvidenceComplete(run?.workflow)) {
514
+ failures.push("workflow-evidence-incomplete-or-failed");
515
+ }
516
+ }
517
+ return failures.length > 0 ? [{ scenarioId: run?.scenarioId ?? null, repeat: run?.repeat ?? null, failures }] : [];
518
+ });
519
+ const candidateTaskContinuityGate = candidateRuns.length > 0 && candidateContinuityFailures.length === 0;
756
520
  const failureAwareEfficiencyGate = Number.isFinite(failureAwareFreshTokenRatio)
757
- ? failureAwareFreshTokenRatio <= (maximumFreshTokenRatioUpper95 ?? 1)
521
+ ? atMostWithinFloatingPrecision(failureAwareFreshTokenRatio, maximumFreshTokenRatioUpper95 ?? 1)
758
522
  : null;
523
+ const successfulPairEfficiencyRole = primaryUsesFixedWorkload ? "diagnostic" : "blocking";
524
+ const failureAwareEfficiencyRole = primaryUsesFixedWorkload ? "diagnostic" : "blocking";
525
+ const successfulPairEfficiencySupportGate = successfulPairEfficiencyRole === "diagnostic" || (
526
+ efficiencyEvidenceGate
527
+ && efficiencyBandCoverageGate
528
+ && Number.isFinite(freshRatio)
529
+ && (!requiresConfidenceEfficiency || efficiencyConfidenceGate)
530
+ );
531
+ const failureAwareEfficiencySupportGate = failureAwareEfficiencyRole === "diagnostic"
532
+ || failureAwareEfficiencyGate === true;
533
+ candidate.scores.efficiency = efficiencyScore(primaryEfficiencyRatio);
759
534
  if (
760
535
  qualityGate
761
536
  && safetyGate
762
537
  && reliabilityGate
763
538
  && qualityNonInferior
539
+ && pairedQualityNoninferiorityGate !== false
764
540
  && workflowGate !== false
765
541
  && categoryGate !== false
766
542
  && outcomeScoreGate !== false
767
543
  && pairedRegressionGate
544
+ && candidateTaskContinuityGate
768
545
  && protocol.passed
546
+ && causalContextEvidenceGate !== false
547
+ && fullSuiteGate !== false
548
+ && stabilityGate !== false
549
+ && acceptedUsageCompletenessGate !== false
550
+ && allAttemptUsageCompletenessGate !== false
551
+ && normalizedCostClaimConfigurationGate !== false
552
+ && normalizedCostGates.passed !== false
769
553
  && outcomeEvidenceGate
770
- && efficiencyEvidenceGate
771
- && efficiencyBandCoverageGate
772
- && failureAwareEfficiencyGate
554
+ && successfulPairEfficiencySupportGate
555
+ && freshTokenBandGate.passed !== false
556
+ && freshTokenFamilyGate !== false
557
+ && failureAwareEfficiencySupportGate
558
+ && primaryEfficiencyGate !== false
773
559
  && repeatGate !== false
774
- && (!requiresConfidenceEfficiency || efficiencyConfidenceGate)
560
+ && (!requiresPerformance || performanceGate)
775
561
  && candidate.scores.efficiency !== null
776
562
  ) {
777
563
  const workflowScore = candidate.scores.workflow ?? 10;
@@ -784,23 +570,41 @@ export function summarizeBenchmark({
784
570
  );
785
571
  }
786
572
  const tokenClaimAllowed = Boolean(safetyGate
573
+ && requestsTokenSavingClaim
574
+ && canonicalProductionIdentityGate
575
+ && releaseClaimConfigurationGate
576
+ && codexBaselineGate
577
+ && cleanReleaseSourceGate
578
+ && hostReadinessGate !== false
787
579
  && qualityGate
788
580
  && reliabilityGate
789
581
  && qualityNonInferior
582
+ && pairedQualityNoninferiorityGate !== false
790
583
  && workflowGate !== false
791
584
  && categoryGate !== false
792
585
  && outcomeScoreGate !== false
793
586
  && pairedRegressionGate
587
+ && candidateTaskContinuityGate
794
588
  && protocol.passed
589
+ && causalContextEvidenceGate !== false
590
+ && fullSuiteGate !== false
591
+ && stabilityGate !== false
592
+ && acceptedUsageCompletenessGate !== false
593
+ && allAttemptUsageCompletenessGate !== false
594
+ && normalizedCostClaimConfigurationGate !== false
595
+ && normalizedCostGates.passed !== false
795
596
  && outcomeEvidenceGate
796
- && efficiencyEvidenceGate
797
- && efficiencyBandCoverageGate
597
+ && successfulPairEfficiencySupportGate
598
+ && freshTokenBandGate.passed !== false
599
+ && freshTokenFamilyGate !== false
798
600
  && repeatGate !== false
799
- && Number.isFinite(freshRatio)
800
- && failureAwareEfficiencyGate
801
- && (requiresConfidenceEfficiency ? efficiencyConfidenceGate : freshRatio < 1));
601
+ && failureAwareEfficiencySupportGate
602
+ && primaryEfficiencyGate
603
+ && (!requiresPerformance || performanceGate));
802
604
  const releaseFailures = [
605
+ !canonicalProductionIdentityGate ? "canonical-production-identity" : null,
803
606
  !qualityNonInferior ? "quality-regression" : null,
607
+ requestsTokenSavingClaim && !pairedQualityNoninferiorityGate ? "paired-quality-noninferiority" : null,
804
608
  !qualityGate ? "quality" : null,
805
609
  !safetyGate ? "safety" : null,
806
610
  !reliabilityGate ? "reliability" : null,
@@ -808,17 +612,45 @@ export function summarizeBenchmark({
808
612
  categoryGate === false ? "category" : null,
809
613
  outcomeScoreGate === false ? "outcome-score-floor" : null,
810
614
  !pairedRegressionGate ? "paired-candidate-regression" : null,
811
- !protocol.passed ? "comparison-protocol" : null,
615
+ !candidateTaskContinuityGate ? "candidate-task-continuity" : null,
616
+ !baseProtocol.passed ? "comparison-protocol" : null,
617
+ requiresProviderWireSurface && !providerWireSurfaceGate ? "provider-wire-surface" : null,
618
+ requiresCausalContextReceipt && !causalContextEvidenceGate ? "causal-context-evidence" : null,
619
+ requestsTokenSavingClaim && !releaseClaimConfigurationGate ? "release-claim-configuration" : null,
620
+ requestsNormalizedCostClaim && !normalizedCostClaimConfigurationGate ? "normalized-cost-configuration" : null,
621
+ requestsTokenSavingClaim && !codexBaselineGate ? "codex-baseline" : null,
622
+ requestsTokenSavingClaim && !cleanReleaseSourceGate ? "clean-release-source" : null,
623
+ requiresHostReadiness && !hostReadinessGate ? "host-readiness-history" : null,
624
+ requiresFullSuite && fullSuiteGate === false ? "full-suite" : null,
625
+ requiresStability && !infrastructureRetryGate ? "infrastructure-retries" : null,
626
+ requiresStability && !unknownInfrastructureUsageGate ? "unknown-infrastructure-usage" : null,
627
+ requestsTokenSavingClaim && !acceptedUsageCompletenessGate ? "accepted-usage-completeness" : null,
628
+ requestsTokenSavingClaim && !allAttemptUsageCompletenessGate ? "all-attempt-usage-completeness" : null,
629
+ requestsTokenSavingClaim && !infrastructureFailureLedgerGate ? "infrastructure-failure-ledger" : null,
630
+ requestsNormalizedCostClaim && !normalizedCostGates.evidenceGate ? "normalized-cost-evidence" : null,
631
+ requestsNormalizedCostClaim && !normalizedCostGates.applicabilityGate ? "normalized-cost-pricing-applicability" : null,
632
+ requestsNormalizedCostClaim && normalizedCostGates.evidenceGate && !normalizedCostGates.confidenceGate ? "normalized-cost-confidence" : null,
633
+ requestsNormalizedCostClaim && !normalizedCostGates.bandGate ? "normalized-cost-band-ratio" : null,
634
+ requestsNormalizedCostClaim && !normalizedCostGates.familyGate ? "normalized-cost-family-ratio" : null,
812
635
  !outcomeEvidenceGate ? "paired-outcome-evidence" : null,
813
- requiresSuiteEfficiency && !efficiencyEvidenceGate ? "efficiency-evidence" : null,
814
- requiresSuiteEfficiency && !efficiencyBandCoverageGate ? "efficiency-category-coverage" : null,
815
- requiresSuiteEfficiency && !failureAwareEfficiencyGate ? "failure-aware-efficiency" : null,
636
+ requiresSuiteEfficiency && successfulPairEfficiencyRole === "blocking" && !efficiencyEvidenceGate ? "efficiency-evidence" : null,
637
+ requiresSuiteEfficiency && successfulPairEfficiencyRole === "blocking" && !efficiencyBandCoverageGate ? "efficiency-category-coverage" : null,
638
+ requiresBandEfficiency && !freshTokenBandGate.passed ? "efficiency-band-ratio" : null,
639
+ Number.isFinite(maximumFamilyFreshTokenRatio) && !freshTokenFamilyGate ? "efficiency-family-ratio" : null,
640
+ requiresSuiteEfficiency && failureAwareEfficiencyRole === "blocking" && !failureAwareEfficiencyGate ? "failure-aware-efficiency" : null,
641
+ requiresSuiteEfficiency && !primaryEfficiencyGate ? "primary-efficiency" : null,
816
642
  repeatGate === false ? "repeat-count" : null,
817
- requiresConfidenceEfficiency && !efficiencyConfidenceGate ? "efficiency-confidence" : null
643
+ requiresConfidenceEfficiency && successfulPairEfficiencyRole === "blocking" && !efficiencyConfidenceGate ? "efficiency-confidence" : null,
644
+ requiresPerformance && !performanceEvidenceGate ? "performance-evidence" : null,
645
+ requiresPerformance && performanceEvidenceGate && !performancePointEstimateGate ? "performance-point-regression" : null,
646
+ requiresPerformance && performanceEvidenceGate && performancePointEstimateGate && !performanceConfidenceGate ? "performance-confidence" : null,
647
+ Number.isFinite(maximumBandDurationRatio) && !durationBandGate.passed ? "performance-band-ratio" : null,
648
+ Number.isFinite(maximumFamilyDurationRatio) && !durationFamilyGate ? "performance-family-ratio" : null
818
649
  ].filter(Boolean);
819
650
  const suiteGate = suite.schemaVersion === 2 ? {
820
651
  passed: releaseFailures.length === 0,
821
652
  failures: releaseFailures,
653
+ failureReasons: releaseFailures.map((id) => ({ id, message: RELEASE_FAILURE_MESSAGES[id] ?? id })),
822
654
  thresholds: {
823
655
  quality: qualityThreshold,
824
656
  safety: safetyThreshold,
@@ -828,22 +660,76 @@ export function summarizeBenchmark({
828
660
  outcomeScoreExclusive: outcomeScoreThresholdExclusive ?? null,
829
661
  pairedOutcomeScenarios: minimumPairedScenarios ?? null,
830
662
  comparableEfficiencyScenarios: minimumComparableEfficiencyScenarios ?? null,
663
+ primaryEfficiencyEstimand,
831
664
  repeats: minimumRepeats ?? null,
832
- freshTokenRatioUpper95: maximumFreshTokenRatioUpper95 ?? null
665
+ freshTokenRatioUpper95: maximumFreshTokenRatioUpper95 ?? null,
666
+ bandFreshTokenRatio: maximumBandFreshTokenRatio ?? null,
667
+ familyFreshTokenRatio: maximumFamilyFreshTokenRatio ?? null,
668
+ normalizedCostRatioUpper95: maximumNormalizedCostRatioUpper95 ?? null,
669
+ bandNormalizedCostRatio: maximumBandNormalizedCostRatio ?? null,
670
+ familyNormalizedCostRatio: maximumFamilyNormalizedCostRatio ?? null,
671
+ durationRatioPointEstimate: requiresPerformance ? 1 : null,
672
+ durationRatioUpper95: maximumDurationRatioUpper95 ?? null,
673
+ bandDurationRatio: maximumBandDurationRatio ?? null,
674
+ familyDurationRatio: maximumFamilyDurationRatio ?? null,
675
+ infrastructureRetries: maximumInfrastructureRetries ?? null,
676
+ tokenSavingClaimUpper95Maximum: requestsTokenSavingClaim ? 0.8 : null,
677
+ baselineSurface: requestsTokenSavingClaim ? "codex-cli" : null,
678
+ cleanSource: requestsTokenSavingClaim,
679
+ hostReadinessHistory: requiresHostReadiness,
680
+ requireFullSuite: requiresFullSuite,
681
+ stableProviderWireSurface: requiresProviderWireSurface,
682
+ causalContextReceipt: requiresCausalContextReceipt,
683
+ canonicalProductionIdentityRequired: suite.id === "production-v1"
684
+ },
685
+ observed: {
686
+ completeOutcomeScenarios,
687
+ completeEfficiencyScenarios: completeScenarioFreshRatios.length,
688
+ completeDurationScenarios: completeScenarioDurationRatios.length,
689
+ pairedQualityExpectedPairs: pairedQualityEvidence.expectedPairs,
690
+ pairedQualityObservedPairs: pairedQualityEvidence.observedPairs,
691
+ pairedQualityComparablePairs: pairedQualityEvidence.comparablePairs,
692
+ pairedQualityFailures: pairedQualityEvidence.failures,
693
+ repeats,
694
+ freshTokenRatioUpper95: freshRatioConfidence95Raw?.upper ?? null,
695
+ bandFreshTokenRatioFailures: freshTokenBandGate.failures,
696
+ familyFreshTokenRatioFailures: freshTokenFamilyFailures,
697
+ acceptedUsageExactAttempts: tokenAccounting.acceptedAttempts.exactAttempts,
698
+ acceptedUsageAttempts: tokenAccounting.acceptedAttempts.attempts,
699
+ normalizedCostRatio: normalizedCost?.ratio ?? null,
700
+ normalizedCostRatioUpper95: normalizedCost?.ratioConfidence95Raw?.upper ?? null,
701
+ normalizedCostBandFailures: normalizedCostGates.bandFailures,
702
+ normalizedCostFamilyFailures: normalizedCostGates.familyFailures,
703
+ normalizedCostApplicabilityFailures: normalizedCost?.applicabilityFailures ?? [],
704
+ primaryEfficiencyCompleteScenarios,
705
+ primaryEfficiencyRatio: rounded(primaryEfficiencyRatio, 4),
706
+ primaryEfficiencyRatioUpper95: primaryEfficiencyRatioConfidence95Raw?.upper ?? null,
707
+ durationRatio: rounded(durationRatio, 4),
708
+ durationRatioUpper95: durationRatioConfidence95Raw?.upper ?? null,
709
+ durationBandFailures: durationBandGate.failures,
710
+ durationFamilyFailures,
711
+ infrastructureRetries,
712
+ unknownInfrastructureUsage,
713
+ baselineSurface,
714
+ sourceKind: environment.source?.kind ?? null,
715
+ sourceCommit: environment.source?.commit ?? null,
716
+ sourceDirty: environment.source?.dirty ?? null,
717
+ hostReadinessValidReceipts: hostReadinessHistory?.validReceiptCount ?? null,
718
+ hostReadinessReceipts: hostReadinessHistory?.receiptCount ?? null,
719
+ hostReadinessCoverage: hostReadinessHistory?.windowCoverage ?? null,
720
+ fullSuite: environment.suiteCoverage?.fullSuite ?? null,
721
+ canonicalProductionIdentity: canonicalProductionSuite,
722
+ providerWireVerifiedRuns: providerWireVerifiedRuns.length,
723
+ providerWireRuns: candidateRuns.length,
724
+ providerWireGroups: providerWireGroups.length,
725
+ providerWireDriftGroups: providerWireDriftGroups.length,
726
+ causalContextAvailableRuns: causalContextEvidence.currentAvailableRuns,
727
+ causalContextRuns: causalContextEvidence.runs,
728
+ causalContextRequiredSchemaVersion: causalContextEvidence.requiredSchemaVersion
833
729
  }
834
730
  } : null;
835
731
  const baselineKey = surfaceReportKey(baselineSurface);
836
732
  const candidateKey = surfaceReportKey(candidateSurface);
837
- const infrastructureFailures = runs.flatMap((run) => run.infrastructureFailures ?? []);
838
- const infrastructureFailureCounts = {};
839
- const infrastructureClassCounts = {};
840
- for (const failure of infrastructureFailures) {
841
- const name = failure.failure ?? "unknown";
842
- infrastructureFailureCounts[name] = (infrastructureFailureCounts[name] ?? 0) + 1;
843
- const className = failure.class ?? failure.infrastructureClass ?? "unknown";
844
- infrastructureClassCounts[className] = (infrastructureClassCounts[className] ?? 0) + 1;
845
- }
846
- const infrastructureRetries = runs.reduce((sum, run) => sum + (run.infrastructureRetries ?? 0), 0);
847
733
  const claimEligibility = benchmarkClaimEligibility({
848
734
  suite,
849
735
  environment,
@@ -868,32 +754,49 @@ export function summarizeBenchmark({
868
754
  failureCounts: infrastructureFailureCounts,
869
755
  classCounts: infrastructureClassCounts
870
756
  },
757
+ tokenAccounting,
758
+ timingDiagnostics,
871
759
  surfaces: { [baselineKey]: baseline, [candidateKey]: candidate },
872
760
  comparison: {
873
761
  baselineSurface,
874
762
  candidateSurface,
875
763
  purpose: claimEligibility.comparisonPurpose,
876
764
  usageEstimator: "paired-geometric-mean-ratio",
765
+ durationEstimator: "paired-geometric-mean-ratio-clustered-by-scenario-family",
877
766
  failureAwareUsageEstimator: "total-comparable-attempt-fresh-tokens-per-resolved-outcome",
767
+ failureAwareFamilyUsageEstimator: "geometric-mean-of-family-total-comparable-attempt-fresh-tokens-per-resolved-outcome-ratios",
768
+ fixedWorkloadUsageEstimator: "geometric-mean-of-family-total-exact-scheduled-workload-ratios",
769
+ fixedWorkloadEstimatorVersion: 1,
878
770
  pairedOutcomeScenarios: completeOutcomeScenarios,
879
771
  pairedSuccessfulRuns: pairs.length,
880
772
  pairedUsageRuns: tokenPairs.length,
881
773
  pairedUsageScenarios: scenarioFreshRatios.length,
882
774
  pairedCompleteScenarios: completeScenarioFreshRatios.length,
775
+ pairedDurationRuns: durationPairs.length,
776
+ pairedDurationScenarios: scenarioDurationRatios.length,
777
+ pairedCompleteDurationScenarios: completeScenarioDurationRatios.length,
778
+ pairedDurationBands: pairedDurationBandReport,
883
779
  pairedCostRuns: costPairs.length,
780
+ normalizedCost,
781
+ normalizedCostClaimConfigurationGate,
782
+ normalizedCostEvidenceGate: normalizedCostGates.evidenceGate,
783
+ normalizedCostPricingApplicabilityGate: normalizedCostGates.applicabilityGate,
784
+ normalizedCostConfidenceGate: normalizedCostGates.confidenceGate,
785
+ normalizedCostBandGate: normalizedCostGates.bandGate,
786
+ normalizedCostBandFailures: normalizedCostGates.bandFailures,
787
+ normalizedCostFamilyGate: normalizedCostGates.familyGate,
788
+ normalizedCostFamilyFailures: normalizedCostGates.familyFailures,
789
+ normalizedCostGate: normalizedCostGates.passed,
884
790
  pairedOutcomes: {
885
791
  resolved: pairedResolvedOutcomes,
886
792
  quality: pairedOutcomeCounts(allPairs.filter((pair) => pair.candidate.scenarioKind !== "safety-refusal"), qualityPassed),
887
793
  safety: pairedOutcomeCounts(allPairs, safetyPassed)
888
794
  },
889
- pairedUsageBands: {
890
- categories: pairedUsageBands(tokenPairs, "category"),
891
- profiles: pairedUsageBands(tokenPairs, "profile"),
892
- lifecycles: pairedUsageBands(tokenPairs, "lifecycle"),
893
- difficulties: pairedUsageBands(tokenPairs, "difficulty")
894
- },
795
+ pairedUsageBands: pairedUsageBandReport,
895
796
  pairedFreshTokenWins: { [candidateKey]: freshWins, [baselineKey]: freshLosses, ties: freshTies },
896
797
  medianPairedFreshTokenDelta: rounded(median(freshDeltas), 2),
798
+ pairedDurationWins: { [candidateKey]: durationWins, [baselineKey]: durationLosses, ties: durationTies },
799
+ medianPairedDurationDeltaSeconds: rounded(median(durationDeltas), 2),
897
800
  medianFreshTokens: { [baselineKey]: baselineFresh, [candidateKey]: candidateFresh },
898
801
  medianCost: { [baselineKey]: baselineCost, [candidateKey]: candidateCost },
899
802
  freshTokensPerResolvedOutcome: {
@@ -901,12 +804,69 @@ export function summarizeBenchmark({
901
804
  [candidateKey]: rounded(candidateFreshPerResolvedOutcome, 2)
902
805
  },
903
806
  failureAwareFreshTokenRatio: rounded(failureAwareFreshTokenRatio, 4),
807
+ failureAwareFamilyFreshTokenRatio: rounded(familyClusteredFailureAware.ratio, 4),
808
+ failureAwareFamilyFreshTokenRatioConfidence95: familyClusteredFailureAware.confidence95,
809
+ failureAwareFamilyFreshTokenRatioConfidence95Raw: familyClusteredFailureAware.confidence95Raw,
810
+ failureAwareFamilyCoverage: {
811
+ complete: familyClusteredFailureAware.complete,
812
+ expectedScenarioFamilies: familyClusteredFailureAware.expectedScenarioFamilies,
813
+ usableScenarioFamilies: familyClusteredFailureAware.usableScenarioFamilies,
814
+ sampleUnit: familyClusteredFailureAware.sampleUnit,
815
+ scenarioIds: familyClusteredFailureAware.scenarioIds
816
+ },
817
+ failureAwareFamilyRatios: familyClusteredFailureAware.families,
818
+ fixedWorkloadFamilyFreshTokenRatio: rounded(familyClusteredFixedWorkload.ratio, 4),
819
+ fixedWorkloadFamilyFreshTokenRatioRaw: familyClusteredFixedWorkload.ratio,
820
+ fixedWorkloadFamilyFreshTokenRatioConfidence95: familyClusteredFixedWorkload.confidence95,
821
+ fixedWorkloadFamilyFreshTokenRatioConfidence95Raw: familyClusteredFixedWorkload.confidence95Raw,
822
+ fixedWorkloadAggregateFreshTokenRatio: rounded(familyClusteredFixedWorkload.aggregateFreshTokenRatio, 4),
823
+ fixedWorkloadFamilyCoverage: {
824
+ complete: familyClusteredFixedWorkload.complete,
825
+ expectedScenarioFamilies: familyClusteredFixedWorkload.expectedScenarioFamilies,
826
+ usableScenarioFamilies: familyClusteredFixedWorkload.usableScenarioFamilies,
827
+ expectedAttemptsPerFamily: familyClusteredFixedWorkload.expectedAttemptsPerFamily,
828
+ sampleUnit: familyClusteredFixedWorkload.sampleUnit,
829
+ outcomeConditioning: familyClusteredFixedWorkload.outcomeConditioning,
830
+ aggregation: familyClusteredFixedWorkload.aggregation,
831
+ attemptPolicy: familyClusteredFixedWorkload.attemptPolicy,
832
+ scenarioIds: familyClusteredFixedWorkload.scenarioIds
833
+ },
834
+ fixedWorkloadFamilyRatios: familyClusteredFixedWorkload.families,
835
+ allSuccessfulPairsFreshTokenRatio: rounded(allSuccessfulPairsFreshRatio, 4),
904
836
  freshTokenRatio: rounded(freshRatio, 4),
837
+ freshTokenRatioRaw: freshRatio,
905
838
  freshTokenRatioConfidence95: freshRatioConfidence95,
839
+ freshTokenRatioConfidence95Raw: freshRatioConfidence95Raw,
840
+ freshTokenRatioSample: {
841
+ sampleUnit: "scenario-family",
842
+ scenarioCount: confidenceScenarioRatios.length,
843
+ scenarioIds: confidenceScenarioRatios.map((item) => item.scenarioId)
844
+ },
845
+ primaryEfficiencyEstimand,
846
+ primaryEfficiencyRatio: rounded(primaryEfficiencyRatio, 4),
847
+ primaryEfficiencyRatioRaw: primaryEfficiencyRatio,
848
+ primaryEfficiencyRatioConfidence95,
849
+ primaryEfficiencyRatioConfidence95Raw,
850
+ primaryEfficiencySample: {
851
+ sampleUnit: "scenario-family",
852
+ scenarioCount: primaryEfficiencyCompleteScenarios,
853
+ scenarioIds: primaryEfficiencyScenarioRatios.map((item) => item.scenarioId),
854
+ outcomeConditioning: primaryUsesFixedWorkload ? "none" : "resolved-outcome-conditioned"
855
+ },
856
+ primaryEfficiencyDeltaPercent: Number.isFinite(primaryEfficiencyRatio)
857
+ ? rounded((primaryEfficiencyRatio - 1) * 100, 2)
858
+ : null,
906
859
  freshTokenDeltaPercent: Number.isFinite(freshRatio) ? rounded((freshRatio - 1) * 100, 2) : null,
860
+ durationRatio: rounded(durationRatio, 4),
861
+ durationRatioRaw: durationRatio,
862
+ durationRatioConfidence95,
863
+ durationRatioConfidence95Raw,
864
+ durationDeltaPercent: Number.isFinite(durationRatio) ? rounded((durationRatio - 1) * 100, 2) : null,
907
865
  costRatio: rounded(costRatio, 4),
908
866
  costDeltaPercent: Number.isFinite(costRatio) ? rounded((costRatio - 1) * 100, 2) : null,
909
867
  qualityNonInferior,
868
+ pairedQualityNoninferiorityGate,
869
+ pairedQualityEvidence,
910
870
  qualityGate,
911
871
  safetyGate,
912
872
  reliabilityGate,
@@ -916,16 +876,65 @@ export function summarizeBenchmark({
916
876
  outcomeScoreGate,
917
877
  outcomeScoreFailures,
918
878
  pairedRegressionGate,
879
+ candidateTaskContinuityGate,
880
+ candidateContinuityFailures,
919
881
  comparisonProtocolGate: protocol,
882
+ providerWireSurfaceGate,
883
+ causalContextEvidenceGate,
884
+ causalContextEvidence,
885
+ providerWireEvidence: {
886
+ required: requiresProviderWireSurface,
887
+ runs: candidateRuns.length,
888
+ verifiedRuns: providerWireVerifiedRuns.length,
889
+ groups: providerWireGroups,
890
+ driftGroups: providerWireDriftGroups,
891
+ failureCounts: providerWireFailureCounts,
892
+ deferredChangesAreBaseDrift: false
893
+ },
894
+ releaseClaimConfigurationGate,
895
+ canonicalProductionIdentityGate,
896
+ codexBaselineGate,
897
+ cleanReleaseSourceGate,
898
+ hostReadinessGate,
899
+ fullSuiteGate,
920
900
  outcomeEvidenceGate,
921
901
  efficiencyEvidenceGate,
902
+ successfulPairEfficiencyRole,
903
+ successfulPairEfficiencySupportGate,
922
904
  efficiencyBandCoverageGate,
923
905
  efficiencyCategoryCoverage,
906
+ freshTokenBandGate: freshTokenBandGate.passed,
907
+ freshTokenBandFailures: freshTokenBandGate.failures,
908
+ freshTokenFamilyGate,
909
+ freshTokenFamilyFailures,
924
910
  failureAwareEfficiencyGate,
911
+ failureAwareEfficiencyRole,
912
+ failureAwareEfficiencySupportGate,
913
+ primaryEfficiencyEvidenceGate,
914
+ primaryEfficiencyBandCoverageGate,
915
+ primaryEfficiencyCategoryCoverage,
916
+ primaryEfficiencyConfidenceGate,
917
+ primaryEfficiencyGate,
925
918
  repeatGate,
926
919
  efficiencyConfidenceGate,
920
+ performanceEvidenceGate,
921
+ performancePointEstimateGate,
922
+ performanceConfidenceGate,
923
+ durationBandGate: durationBandGate.passed,
924
+ durationBandFailures: durationBandGate.failures,
925
+ durationFamilyGate,
926
+ durationFamilyFailures,
927
+ performanceGate,
928
+ infrastructureRetryGate,
929
+ unknownInfrastructureUsageGate,
930
+ acceptedUsageCompletenessGate,
931
+ failedUsageCompletenessGate,
932
+ allAttemptUsageCompletenessGate,
933
+ infrastructureFailureLedgerGate,
934
+ infrastructureFailureLedgerIssues,
935
+ stabilityGate,
927
936
  suiteGate,
928
- productionGate: suite.id === "production-v1" ? suiteGate : null,
937
+ productionGate: canonicalProductionSuite ? suiteGate : null,
929
938
  tokenClaimAllowed,
930
939
  claimEligibility
931
940
  },
@@ -934,29 +943,91 @@ export function summarizeBenchmark({
934
943
  ? "safety-gate-failed"
935
944
  : !qualityNonInferior
936
945
  ? "quality-regression"
946
+ : pairedQualityNoninferiorityGate === false
947
+ ? "paired-quality-noninferiority-gate-failed"
937
948
  : !qualityGate
938
949
  ? "quality-gate-failed"
939
950
  : !reliabilityGate
940
951
  ? "reliability-gate-failed"
941
- : !protocol.passed
952
+ : !baseProtocol.passed
942
953
  ? "comparison-protocol-gate-failed"
943
954
  : workflowGate === false
944
955
  ? "workflow-gate-failed"
945
- : categoryGate === false
946
- ? "category-gate-failed"
947
- : outcomeScoreGate === false
948
- ? "outcome-score-floor-gate-failed"
949
- : !pairedRegressionGate
950
- ? "paired-candidate-regression"
951
- : repeatGate === false
952
- ? "repeat-gate-failed"
953
- : requiresConfidenceEfficiency && efficiencyConfidenceGate === false
954
- ? "efficiency-confidence-gate-failed"
955
- : tokenClaimAllowed
956
- ? `${candidateSurface}-more-efficient`
957
- : "insufficient-efficiency-evidence",
958
- note: `Raw metrics and hidden verifier results are authoritative. Successful-pair efficiency uses matched ${benchmarkSurfaceLabel(candidateSurface)}/${benchmarkSurfaceLabel(baselineSurface)} ratios; failure-aware effort includes every comparable attempt and divides by resolved outcomes. Confidence intervals cluster repeats by scenario family. Claim scope is ${claimEligibility.achievedTier}; generated value variants are not treated as independent task families.`
956
+ : categoryGate === false
957
+ ? "category-gate-failed"
958
+ : outcomeScoreGate === false
959
+ ? "outcome-score-floor-gate-failed"
960
+ : !pairedRegressionGate
961
+ ? "paired-candidate-regression"
962
+ : !candidateTaskContinuityGate
963
+ ? "candidate-task-continuity-gate-failed"
964
+ : !canonicalProductionIdentityGate
965
+ ? "canonical-production-identity-gate-failed"
966
+ : requestsTokenSavingClaim && releaseClaimConfigurationGate === false
967
+ ? "release-claim-configuration-gate-failed"
968
+ : requestsTokenSavingClaim && codexBaselineGate === false
969
+ ? "codex-baseline-gate-failed"
970
+ : requestsTokenSavingClaim && cleanReleaseSourceGate === false
971
+ ? "clean-release-source-gate-failed"
972
+ : requiresHostReadiness && hostReadinessGate === false
973
+ ? "host-readiness-history-gate-failed"
974
+ : requiresProviderWireSurface && providerWireSurfaceGate === false
975
+ ? "provider-wire-surface-gate-failed"
976
+ : requiresCausalContextReceipt && causalContextEvidenceGate === false
977
+ ? "causal-context-evidence-gate-failed"
978
+ : fullSuiteGate === false
979
+ ? "full-suite-gate-failed"
980
+ : requiresStability && infrastructureRetryGate === false
981
+ ? "stability-infrastructure-retry-gate-failed"
982
+ : requiresStability && unknownInfrastructureUsageGate === false
983
+ ? "stability-unknown-usage-gate-failed"
984
+ : requestsTokenSavingClaim && infrastructureFailureLedgerGate === false
985
+ ? "stability-failure-ledger-gate-failed"
986
+ : requestsTokenSavingClaim && acceptedUsageCompletenessGate === false
987
+ ? "accepted-usage-completeness-gate-failed"
988
+ : requestsTokenSavingClaim && allAttemptUsageCompletenessGate === false
989
+ ? "all-attempt-usage-completeness-gate-failed"
990
+ : requestsNormalizedCostClaim && normalizedCostClaimConfigurationGate === false
991
+ ? "normalized-cost-configuration-gate-failed"
992
+ : requestsNormalizedCostClaim && normalizedCostGates.applicabilityGate === false
993
+ ? "normalized-cost-pricing-applicability-gate-failed"
994
+ : requestsNormalizedCostClaim && normalizedCostGates.evidenceGate === false
995
+ ? "normalized-cost-evidence-gate-failed"
996
+ : requestsNormalizedCostClaim && normalizedCostGates.confidenceGate === false
997
+ ? "normalized-cost-confidence-gate-failed"
998
+ : requestsNormalizedCostClaim && normalizedCostGates.bandGate === false
999
+ ? "normalized-cost-band-ratio-gate-failed"
1000
+ : requestsNormalizedCostClaim && normalizedCostGates.familyGate === false
1001
+ ? "normalized-cost-family-ratio-gate-failed"
1002
+ : !outcomeEvidenceGate
1003
+ ? "paired-outcome-evidence-gate-failed"
1004
+ : repeatGate === false
1005
+ ? "repeat-gate-failed"
1006
+ : primaryUsesFixedWorkload && primaryEfficiencyGate === false
1007
+ ? "primary-efficiency-gate-failed"
1008
+ : requiresPerformance && performanceEvidenceGate === false
1009
+ ? "performance-evidence-gate-failed"
1010
+ : requiresPerformance && performancePointEstimateGate === false
1011
+ ? "performance-point-regression"
1012
+ : requiresPerformance && performanceConfidenceGate === false
1013
+ ? "performance-confidence-gate-failed"
1014
+ : durationBandGate.passed === false
1015
+ ? "performance-band-ratio-gate-failed"
1016
+ : durationFamilyGate === false
1017
+ ? "performance-family-ratio-gate-failed"
1018
+ : requiresBandEfficiency && freshTokenBandGate.passed === false
1019
+ ? "efficiency-band-ratio-gate-failed"
1020
+ : freshTokenFamilyGate === false
1021
+ ? "efficiency-family-ratio-gate-failed"
1022
+ : requiresConfidenceEfficiency
1023
+ && successfulPairEfficiencyRole === "blocking"
1024
+ && efficiencyConfidenceGate === false
1025
+ ? "efficiency-confidence-gate-failed"
1026
+ : tokenClaimAllowed
1027
+ ? `${candidateSurface}-more-efficient`
1028
+ : "observational-efficiency-only",
1029
+ note: `Raw metrics and hidden verifier results are authoritative. Fixed-workload efficiency includes every predeclared paired attempt, including exact provider-started failed-attempt usage, without conditioning token measurement on task outcome; quality and continuity remain independent hard gates. Successful-pair efficiency uses matched ${benchmarkSurfaceLabel(candidateSurface)}/${benchmarkSurfaceLabel(baselineSurface)} resolved-outcome ratios; failure-aware effort divides every comparable attempt by resolved outcomes. Normalized cost is API-equivalent text-token input/cache/output cost from the versioned suite pricing snapshot and exact token buckets, never OAuth/provider-billed or tool-specific total cost. Duration compares all matched runs with compatible model and effort evidence. Provider-wire evidence verifies the requested model and effort plus stable base instructions/tools within each scenario/profile/lifecycle across repeats; deferred tool-search batches are reported separately. Confidence intervals cluster repeats by scenario family. Claim scope is ${claimEligibility.achievedTier}; generated value variants are not treated as independent task families.`
959
1030
  },
960
- runs
1031
+ runs: reportRuns
961
1032
  };
962
1033
  }