@piagent/platform 1.5.5 → 1.6.1

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 (323) hide show
  1. package/CHANGELOG.md +166 -0
  2. package/README.md +25 -21
  3. package/README.vi.md +34 -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 +92 -0
  24. package/benchmarks/production-v1/suite.json +46 -3
  25. package/catalog/capabilities.json +18 -18
  26. package/docs/authority-manifest.md +1 -1
  27. package/docs/auto-delegation-policy.md +38 -73
  28. package/docs/command-reference-vietnamese.md +27 -35
  29. package/docs/context-engine.md +12 -4
  30. package/docs/context-window-policy.md +37 -1
  31. package/docs/distribution-standard.md +7 -7
  32. package/docs/en/architecture.md +27 -13
  33. package/docs/languages.json +4 -4
  34. package/docs/model-options.md +8 -5
  35. package/docs/operator-manual-vietnamese.md +27 -45
  36. package/docs/project-adapters.md +2 -2
  37. package/docs/publishing-for-teams.md +10 -0
  38. package/docs/quality-benchmark.md +427 -62
  39. package/docs/release-install-policy.md +10 -9
  40. package/docs/runtime-harness-standard.md +3 -3
  41. package/docs/runtime-policy-design.md +4 -4
  42. package/docs/runtime-quality-baseline.md +13 -6
  43. package/docs/security-threat-model.md +2 -2
  44. package/docs/subagent-orchestration-capabilities.md +19 -51
  45. package/docs/subagents-and-multiagent.md +21 -47
  46. package/docs/task-implementation-contract.md +3 -3
  47. package/docs/task-lifecycle-vietnamese.md +3 -3
  48. package/docs/team-onboarding.md +2 -2
  49. package/docs/usage-observability.md +7 -2
  50. package/docs/vi/architecture.md +27 -13
  51. package/docs-site/architecture.html +1 -1
  52. package/docs-site/benchmark.html +89 -95
  53. package/docs-site/commands.html +1 -1
  54. package/docs-site/content/benchmark.html +88 -94
  55. package/docs-site/content/en/benchmark.html +134 -90
  56. package/docs-site/content/en/index.html +19 -19
  57. package/docs-site/content/en/runtime.html +4 -4
  58. package/docs-site/content/en/whats-new.html +12 -1
  59. package/docs-site/content/en/workflows.html +2 -2
  60. package/docs-site/content/index.html +19 -19
  61. package/docs-site/content/runtime.html +4 -4
  62. package/docs-site/content/whats-new.html +12 -1
  63. package/docs-site/content/workflows.html +2 -2
  64. package/docs-site/context-engine.html +1 -1
  65. package/docs-site/en/architecture.html +1 -1
  66. package/docs-site/en/benchmark.html +135 -91
  67. package/docs-site/en/commands.html +1 -1
  68. package/docs-site/en/context-engine.html +1 -1
  69. package/docs-site/en/index.html +20 -20
  70. package/docs-site/en/mcp-approval.html +1 -1
  71. package/docs-site/en/mcp-auth.html +1 -1
  72. package/docs-site/en/mcp-servers.html +1 -1
  73. package/docs-site/en/mcp.html +1 -1
  74. package/docs-site/en/permissions.html +1 -1
  75. package/docs-site/en/profiles.html +1 -1
  76. package/docs-site/en/quickstart.html +1 -1
  77. package/docs-site/en/runtime.html +5 -5
  78. package/docs-site/en/scope.html +1 -1
  79. package/docs-site/en/security.html +1 -1
  80. package/docs-site/en/team.html +1 -1
  81. package/docs-site/en/whats-new.html +13 -2
  82. package/docs-site/en/workflows.html +3 -3
  83. package/docs-site/index.html +20 -20
  84. package/docs-site/mcp-approval.html +1 -1
  85. package/docs-site/mcp-auth.html +1 -1
  86. package/docs-site/mcp-servers.html +1 -1
  87. package/docs-site/mcp.html +1 -1
  88. package/docs-site/permissions.html +1 -1
  89. package/docs-site/profiles.html +1 -1
  90. package/docs-site/quickstart.html +1 -1
  91. package/docs-site/runtime.html +5 -5
  92. package/docs-site/scope.html +1 -1
  93. package/docs-site/security.html +1 -1
  94. package/docs-site/team.html +1 -1
  95. package/docs-site/whats-new.html +13 -2
  96. package/docs-site/workflows.html +3 -3
  97. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  98. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  99. package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
  100. package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
  101. package/evals/fixtures/helper-request.invalid.json +1 -1
  102. package/evals/fixtures/helper-request.valid.json +1 -1
  103. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  104. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  105. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
  106. package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
  107. package/evals/fs4-readiness-matrix.v1.json +2 -2
  108. package/evals/fs5-causal-arm.v1.json +3 -3
  109. package/evals/fs5-pilot-protocol.v1.json +3 -3
  110. package/evals/fs5-pilot-protocol.v2.json +8 -8
  111. package/evals/fs5-pilot-protocol.v3.json +8 -8
  112. package/evals/fs5-pilot-protocol.v4.json +7 -6
  113. package/evals/fs5-pilot-protocol.v5.json +9 -7
  114. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  115. package/evals/runtime-conformance-v1/README.md +43 -0
  116. package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
  117. package/evals/runtime-conformance-v1/lane.json +57 -0
  118. package/evals/runtime-conformance-v1/paid-canary.json +31 -0
  119. package/evals/runtime-conformance-v1/runner.mjs +456 -0
  120. package/package.json +3 -1
  121. package/packages/piagent-core/README.md +13 -4
  122. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  123. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  124. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  125. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  126. package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
  127. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  128. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  129. package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
  130. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  131. package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
  132. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  133. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  134. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  135. package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
  136. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  137. package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
  138. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  139. package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
  140. package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
  141. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  142. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  143. package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
  144. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
  145. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  146. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  147. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  148. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  149. package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
  150. package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
  151. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  152. package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
  153. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  154. package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
  155. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  156. package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
  157. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  158. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  159. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  160. package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
  161. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  162. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  163. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  164. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  165. package/packages/piagent-core/extensions/context-engine.js +149 -156
  166. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  167. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  168. package/packages/piagent-core/extensions/context-index-policy.js +4 -0
  169. package/packages/piagent-core/extensions/context-query-paths.js +57 -0
  170. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  171. package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
  172. package/packages/piagent-core/extensions/criterion-graph.js +82 -9
  173. package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
  174. package/packages/piagent-core/extensions/guard-types.ts +6 -0
  175. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  176. package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
  177. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  178. package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
  179. package/packages/piagent-core/extensions/task-state.js +109 -142
  180. package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
  181. package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
  182. package/packages/piagent-core/package.json +1 -1
  183. package/packages/piagent-core/policies/base-policy.json +44 -2
  184. package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
  185. package/packages/piagent-core/prompts/be-to-fe.md +2 -8
  186. package/packages/piagent-core/prompts/plan.md +1 -1
  187. package/packages/piagent-core/prompts/platform-improve.md +2 -6
  188. package/packages/piagent-core/prompts/review.md +2 -7
  189. package/packages/piagent-core/prompts/scout.md +1 -1
  190. package/packages/piagent-core/prompts/task.md +4 -3
  191. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  192. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  193. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  194. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  195. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  196. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  197. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  198. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  199. package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
  200. package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
  201. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  202. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
  203. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  204. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
  205. package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
  206. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
  207. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  208. package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
  209. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
  210. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  211. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  212. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
  213. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
  214. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
  215. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  216. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  217. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  218. package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
  219. package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
  220. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
  221. package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
  222. package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
  223. package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
  224. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  225. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
  226. package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
  227. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
  228. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  229. package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
  230. package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
  231. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  232. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  233. package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
  234. package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
  235. package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
  236. package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
  237. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
  238. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  239. package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
  240. package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
  241. package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
  242. package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
  243. package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
  244. package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
  245. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
  246. package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
  247. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
  248. package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
  249. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  250. package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
  251. package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
  252. package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
  253. package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
  254. package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
  255. package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
  256. package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
  257. package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
  258. package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
  259. package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
  260. package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
  261. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  262. package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
  263. package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
  264. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  265. package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
  266. package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
  267. package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
  268. package/packages/piagent-core/security/sensitive-data.js +13 -218
  269. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  270. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  271. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  272. package/packages/piagent-core/security/sensitive-text.js +234 -0
  273. package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
  274. package/packages/piagent-core/subagents/piagent-planner.md +6 -4
  275. package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
  276. package/packages/piagent-core/subagents/piagent-scout.md +4 -2
  277. package/packages/piagent-core/subagents/piagent-worker.md +7 -5
  278. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
  279. package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
  280. package/packages/piagent-webui/dist/client/index.html +1 -1
  281. package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
  282. package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
  283. package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
  284. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  285. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  286. package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
  287. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  288. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  289. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  290. package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
  291. package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
  292. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  293. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
  294. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  295. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
  296. package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
  297. package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
  298. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  299. package/packages/piagent-webui/server/transcript-projection.ts +113 -9
  300. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  301. package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
  302. package/schemas/piagent-webui/catalog-v1.json +9 -0
  303. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  304. package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
  305. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  306. package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
  307. package/schemas/project-profile.schema.json +1 -1
  308. package/schemas/task-contract.schema.json +27 -1
  309. package/scripts/benchmark-runner-core.mjs +367 -363
  310. package/scripts/benchmark-runner-finalization.mjs +313 -0
  311. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  312. package/scripts/benchmark-runner-support.mjs +200 -0
  313. package/scripts/benchmark-session.mjs +95 -41
  314. package/scripts/configure-model-scope.sh +4 -3
  315. package/scripts/configure-subagents.sh +42 -34
  316. package/scripts/helper-evaluation.mjs +2 -2
  317. package/scripts/install-global.sh +2 -2
  318. package/scripts/pi-usage-history.mjs +37 -10
  319. package/scripts/setup.sh +2 -2
  320. package/scripts/team-doctor.sh +1 -1
  321. package/scripts/verify-local.sh +10 -1
  322. package/templates/global/settings.json +2 -1
  323. package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
@@ -1,637 +1,70 @@
1
- import { StringDecoder } from "node:string_decoder";
2
-
3
1
  import { benchmarkClaimEligibility } from "./benchmark-assurance.js";
2
+ import { integrateCodexRelativeEfficiencyReport } from "./benchmark-codex-relative-efficiency.js";
4
3
  import {
5
4
  comparableAttemptUsage,
5
+ comparableDuration,
6
6
  comparisonProtocol,
7
7
  completeCategoryCoverage,
8
8
  completePairedScenarioCount,
9
+ familyClusteredFailureAwareUsage,
10
+ familyClusteredFixedWorkloadUsage,
11
+ pairedDurationBands,
12
+ pairedUsageBands,
13
+ selectPrimaryEfficiencyEstimate,
9
14
  tokensPerResolvedOutcome
10
15
  } from "./benchmark-comparison.js";
16
+ import { benchmarkProviderWireEvidenceMatchesRequest } from "./benchmark-provider-wire.js";
17
+ import { canonicalBenchmarkTimingDiagnostics, summarizeBenchmarkTimingDiagnostics } from "./benchmark-timing-diagnostics.js";
18
+ import { summarizeBenchmarkCausalContextEvidence } from "./benchmark-record-validation.js";
11
19
  import {
12
- isCurrentTaskWorkingTreeDigest,
13
- taskWorkingTreeEvidenceDigest,
14
- taskWorkingTreeSnapshotUsesCurrentAlgorithm
15
- } from "./benchmark-tree-identity.js";
16
-
20
+ benchmarkPricingSnapshotValidationErrors,
21
+ normalizedCostComparison,
22
+ normalizedCostGate
23
+ } from "./benchmark-normalized-cost.js";
24
+ import {
25
+ atMostWithinFloatingPrecision,
26
+ geometricMean,
27
+ geometricMeanConfidence95,
28
+ geometricMeanConfidence95Raw,
29
+ median,
30
+ rounded
31
+ } from "./benchmark-statistics.js";
32
+ import { benchmarkInfrastructureFailureLedgerIssues, benchmarkTokenAccounting as buildBenchmarkTokenAccounting } from "./benchmark-usage.js";
33
+ import {
34
+ efficiencyScore,
35
+ evaluateDurationBandGate,
36
+ evaluateFreshTokenBandGate,
37
+ pairedOutcomeCounts,
38
+ pairedQualityNoninferiorityEvidence,
39
+ qualityPassed,
40
+ RELEASE_FAILURE_MESSAGES,
41
+ safetyPassed,
42
+ surfaceReportKey,
43
+ surfaceSummary,
44
+ workflowContinuityEvidenceComplete
45
+ } from "./benchmark-summary-support.js";
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 { CODEX_RELATIVE_EFFICIENCY_POLICY, PRODUCTION_SUBAGENT_BUDGET_POLICY, evaluateCodexRelativeEfficiency, summarizeBenchmarkSubagentBudget } from "./benchmark-codex-relative-efficiency.js";
53
+ export { evaluateWorkflowEvidence } from "./benchmark-workflow.js";
20
54
  const SURFACE_LABELS = Object.freeze({
21
55
  "raw-pi": "Raw Pi",
22
56
  piagent: "Piagent",
23
57
  "codex-cli": "Codex CLI"
24
58
  });
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
59
  export const BENCHMARK_MEASUREMENT_SCHEMA_VERSION = 2;
32
60
 
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
61
  export function benchmarkSurfaceLabel(surface) {
293
62
  return SURFACE_LABELS[surface] ?? surface;
294
63
  }
295
64
 
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
65
  export function summarizeBenchmark({
634
66
  suite,
67
+ canonicalProductionSuite = false,
635
68
  runId,
636
69
  startedAt,
637
70
  completedAt,
@@ -642,6 +75,9 @@ export function summarizeBenchmark({
642
75
  candidateSurface = "piagent"
643
76
  }) {
644
77
  if (baselineSurface === candidateSurface) throw new Error("Benchmark surfaces must be different");
78
+ if (canonicalProductionSuite && suite.id !== "production-v1") {
79
+ throw new Error("Canonical production gate requires the built-in production-v1 suite identity");
80
+ }
645
81
  const baselineRuns = runs.filter((run) => run.surface === baselineSurface);
646
82
  const candidateRuns = runs.filter((run) => run.surface === candidateSurface);
647
83
  if (baselineRuns.length === 0 || candidateRuns.length === 0) {
@@ -649,6 +85,15 @@ export function summarizeBenchmark({
649
85
  }
650
86
  const baseline = surfaceSummary(baselineSurface, baselineRuns);
651
87
  const candidate = surfaceSummary(candidateSurface, candidateRuns);
88
+ const tokenAccounting = buildBenchmarkTokenAccounting(runs);
89
+ const timingDiagnostics = summarizeBenchmarkTimingDiagnostics(runs);
90
+ const reportRuns = runs.map((run) => {
91
+ const timing = canonicalBenchmarkTimingDiagnostics(run.timingDiagnostics, run.surface, run.durationSeconds);
92
+ if (timing !== undefined || !Object.hasOwn(run, "timingDiagnostics")) return run;
93
+ const sanitized = { ...run };
94
+ delete sanitized.timingDiagnostics;
95
+ return sanitized;
96
+ });
652
97
  const baselineByKey = new Map(baselineRuns.map((run) => [`${run.scenarioId}:${run.repeat}`, run]));
653
98
  const allPairs = candidateRuns
654
99
  .map((run) => ({ baseline: baselineByKey.get(`${run.scenarioId}:${run.repeat}`), candidate: run }))
@@ -673,6 +118,32 @@ export function summarizeBenchmark({
673
118
  pairs: ratios.length
674
119
  }));
675
120
  const completeScenarioFreshRatios = scenarioFreshRatios.filter((item) => item.pairs === repeats);
121
+ const pairedUsageBandReport = {
122
+ categories: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "category"),
123
+ profiles: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "profile"),
124
+ lifecycles: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "lifecycle"),
125
+ difficulties: pairedUsageBands(allPairs.filter(comparableAttemptUsage), "difficulty")
126
+ };
127
+ const durationPairs = allPairs.filter(comparableDuration);
128
+ const durationRatios = durationPairs.map((pair) => pair.candidate.durationSeconds / pair.baseline.durationSeconds);
129
+ const durationRatiosByScenario = new Map();
130
+ for (const pair of durationPairs) {
131
+ const ratios = durationRatiosByScenario.get(pair.candidate.scenarioId) ?? [];
132
+ ratios.push(pair.candidate.durationSeconds / pair.baseline.durationSeconds);
133
+ durationRatiosByScenario.set(pair.candidate.scenarioId, ratios);
134
+ }
135
+ const scenarioDurationRatios = [...durationRatiosByScenario.entries()].map(([scenarioId, ratios]) => ({
136
+ scenarioId,
137
+ ratio: geometricMean(ratios),
138
+ pairs: ratios.length
139
+ }));
140
+ const completeScenarioDurationRatios = scenarioDurationRatios.filter((item) => item.pairs === repeats);
141
+ const pairedDurationBandReport = {
142
+ categories: pairedDurationBands(durationPairs, "category"),
143
+ profiles: pairedDurationBands(durationPairs, "profile"),
144
+ lifecycles: pairedDurationBands(durationPairs, "lifecycle"),
145
+ difficulties: pairedDurationBands(durationPairs, "difficulty")
146
+ };
676
147
  const costPairs = tokenPairs.filter((pair) => (
677
148
  Number.isFinite(pair.baseline.usage.cost)
678
149
  && Number.isFinite(pair.candidate.usage.cost)
@@ -680,14 +151,26 @@ export function summarizeBenchmark({
680
151
  && pair.candidate.usage.cost > 0
681
152
  ));
682
153
  const costRatios = costPairs.map((pair) => pair.candidate.usage.cost / pair.baseline.usage.cost);
683
- const freshRatio = geometricMean(freshRatios);
154
+ const allSuccessfulPairsFreshRatio = geometricMean(freshRatios);
684
155
  const confidenceScenarioRatios = suite.schemaVersion === 2 ? completeScenarioFreshRatios : scenarioFreshRatios;
156
+ const freshRatio = suite.schemaVersion === 2
157
+ ? geometricMean(confidenceScenarioRatios.map((item) => item.ratio))
158
+ : allSuccessfulPairsFreshRatio;
685
159
  const freshRatioConfidence95 = geometricMeanConfidence95(confidenceScenarioRatios.map((item) => item.ratio));
160
+ const freshRatioConfidence95Raw = geometricMeanConfidence95Raw(confidenceScenarioRatios.map((item) => item.ratio));
161
+ const durationRatio = geometricMean(durationRatios);
162
+ const confidenceScenarioDurationRatios = suite.schemaVersion === 2 ? completeScenarioDurationRatios : scenarioDurationRatios;
163
+ const durationRatioConfidence95 = geometricMeanConfidence95(confidenceScenarioDurationRatios.map((item) => item.ratio));
164
+ const durationRatioConfidence95Raw = geometricMeanConfidence95Raw(confidenceScenarioDurationRatios.map((item) => item.ratio));
686
165
  const costRatio = geometricMean(costRatios);
687
166
  const freshDeltas = tokenPairs.map((pair) => pair.candidate.usage.fresh - pair.baseline.usage.fresh);
688
167
  const freshWins = freshDeltas.filter((delta) => delta < 0).length;
689
168
  const freshLosses = freshDeltas.filter((delta) => delta > 0).length;
690
169
  const freshTies = freshDeltas.length - freshWins - freshLosses;
170
+ const durationDeltas = durationPairs.map((pair) => pair.candidate.durationSeconds - pair.baseline.durationSeconds);
171
+ const durationWins = durationDeltas.filter((delta) => delta < 0).length;
172
+ const durationLosses = durationDeltas.filter((delta) => delta > 0).length;
173
+ const durationTies = durationDeltas.length - durationWins - durationLosses;
691
174
  const baselineFreshPerResolvedOutcome = tokensPerResolvedOutcome(allPairs, "baseline");
692
175
  const candidateFreshPerResolvedOutcome = tokensPerResolvedOutcome(allPairs, "candidate");
693
176
  const failureAwareFreshTokenRatio = Number.isFinite(baselineFreshPerResolvedOutcome)
@@ -695,8 +178,21 @@ export function summarizeBenchmark({
695
178
  && Number.isFinite(candidateFreshPerResolvedOutcome)
696
179
  ? candidateFreshPerResolvedOutcome / baselineFreshPerResolvedOutcome
697
180
  : null;
698
- candidate.scores.efficiency = efficiencyScore(freshRatio);
181
+ const familyClusteredFailureAware = familyClusteredFailureAwareUsage(suite, allPairs, repeats);
182
+ const familyClusteredFixedWorkload = familyClusteredFixedWorkloadUsage(suite, allPairs, repeats);
699
183
  baseline.scores.efficiency = tokenPairs.length ? 5 : null;
184
+ const infrastructureFailures = runs.flatMap((run) => run.infrastructureFailures ?? []);
185
+ const infrastructureFailureCounts = {};
186
+ const infrastructureClassCounts = {};
187
+ for (const failure of infrastructureFailures) {
188
+ const name = failure.failure ?? "unknown";
189
+ infrastructureFailureCounts[name] = (infrastructureFailureCounts[name] ?? 0) + 1;
190
+ const className = failure.class ?? failure.infrastructureClass ?? "unknown";
191
+ infrastructureClassCounts[className] = (infrastructureClassCounts[className] ?? 0) + 1;
192
+ }
193
+ const infrastructureRetries = runs.reduce((sum, run) => sum + (run.infrastructureRetries ?? 0), 0);
194
+ const unknownInfrastructureUsage = infrastructureFailures.filter((failure) => failure.usageStatus === "unknown-after-provider-start").length;
195
+ const infrastructureFailureLedgerIssues = benchmarkInfrastructureFailureLedgerIssues(runs);
700
196
  const releaseGate = suite.releaseGate ?? {};
701
197
  const qualityThreshold = releaseGate.minimumQualityScore ?? 9;
702
198
  const safetyThreshold = releaseGate.minimumSafetyScore ?? 10;
@@ -708,9 +204,125 @@ export function summarizeBenchmark({
708
204
  const minimumComparableEfficiencyScenarios = releaseGate.minimumComparableEfficiencyScenarios ?? minimumPairedScenarios;
709
205
  const minimumRepeats = releaseGate.minimumRepeats;
710
206
  const maximumFreshTokenRatioUpper95 = releaseGate.maximumFreshTokenRatioUpper95;
207
+ const maximumBandFreshTokenRatio = releaseGate.maximumBandFreshTokenRatio;
208
+ const maximumFamilyFreshTokenRatio = releaseGate.maximumFamilyFreshTokenRatio;
209
+ const maximumNormalizedCostRatioUpper95 = releaseGate.maximumNormalizedCostRatioUpper95;
210
+ const maximumBandNormalizedCostRatio = releaseGate.maximumBandNormalizedCostRatio;
211
+ const maximumFamilyNormalizedCostRatio = releaseGate.maximumFamilyNormalizedCostRatio;
212
+ const maximumDurationRatioUpper95 = releaseGate.maximumDurationRatioUpper95;
213
+ const maximumBandDurationRatio = releaseGate.maximumBandDurationRatio;
214
+ const maximumFamilyDurationRatio = releaseGate.maximumFamilyDurationRatio;
215
+ const maximumInfrastructureRetries = releaseGate.maximumInfrastructureRetries;
216
+ const primaryEfficiencyEstimand = releaseGate.primaryEfficiencyEstimand ?? "successful-pair-family-ratio";
217
+ const primaryUsesFailureAware = primaryEfficiencyEstimand === "failure-aware-family-ratio";
218
+ const primaryUsesFixedWorkload = primaryEfficiencyEstimand === "fixed-workload-family-ratio";
219
+ const {
220
+ ratio: primaryEfficiencyRatio,
221
+ confidence95: primaryEfficiencyRatioConfidence95,
222
+ confidence95Raw: primaryEfficiencyRatioConfidence95Raw,
223
+ scenarioRatios: primaryEfficiencyScenarioRatios
224
+ } = selectPrimaryEfficiencyEstimate(primaryEfficiencyEstimand, {
225
+ ratio: freshRatio,
226
+ confidence95: freshRatioConfidence95,
227
+ confidence95Raw: freshRatioConfidence95Raw,
228
+ scenarioRatios: completeScenarioFreshRatios
229
+ }, familyClusteredFailureAware, familyClusteredFixedWorkload);
230
+ const primaryEfficiencyCompleteScenarios = primaryEfficiencyScenarioRatios.length;
231
+ const primaryEfficiencyCategoryCoverage = completeCategoryCoverage(suite, primaryEfficiencyScenarioRatios);
711
232
  const requiresConfidenceEfficiency = Number.isFinite(maximumFreshTokenRatioUpper95) || releaseGate.requireEfficiencyClaim === true;
233
+ const requiresBandEfficiency = Number.isFinite(maximumBandFreshTokenRatio);
234
+ const freshTokenBandGate = evaluateFreshTokenBandGate(suite, pairedUsageBandReport, maximumBandFreshTokenRatio);
235
+ const completeFreshRatioByScenario = new Map(completeScenarioFreshRatios.map((item) => [item.scenarioId, item]));
236
+ const freshTokenFamilyFailures = Number.isFinite(maximumFamilyFreshTokenRatio)
237
+ ? suite.scenarios.flatMap((scenario) => {
238
+ const item = completeFreshRatioByScenario.get(scenario.id);
239
+ if (!item) return [{ scenarioId: scenario.id, ratio: null, reason: "missing-comparable-usage" }];
240
+ if (!atMostWithinFloatingPrecision(item.ratio, maximumFamilyFreshTokenRatio)) {
241
+ return [{ scenarioId: scenario.id, ratio: rounded(item.ratio, 4), reason: "ratio-above-limit" }];
242
+ }
243
+ return [];
244
+ })
245
+ : [];
246
+ const freshTokenFamilyGate = Number.isFinite(maximumFamilyFreshTokenRatio)
247
+ ? freshTokenFamilyFailures.length === 0
248
+ : null;
712
249
  const requiresSuiteEfficiency = requiresConfidenceEfficiency;
250
+ const requiresPerformance = Number.isFinite(maximumDurationRatioUpper95);
251
+ const requiresStability = Number.isInteger(maximumInfrastructureRetries);
252
+ const requiresFullSuite = releaseGate.requireFullSuiteForClaim === true;
253
+ const requiresProviderWireSurface = releaseGate.requireStableProviderWireSurface === true;
254
+ const requiresCausalContextReceipt = releaseGate.requireCausalContextReceipt === true;
255
+ const requestsTokenSavingClaim = releaseGate.requireEfficiencyClaim === true;
256
+ const requestsNormalizedCostClaim = releaseGate.requireNormalizedCostClaim === true;
257
+ const requiresHostReadiness = releaseGate.requireHostReadinessForClaim === true;
258
+ const canonicalProductionIdentityGate = suite.id !== "production-v1" || canonicalProductionSuite;
259
+ const releaseClaimConfigurationGate = requestsTokenSavingClaim
260
+ ? suite.schemaVersion === 2 && Number.isFinite(maximumFreshTokenRatioUpper95)
261
+ && maximumFreshTokenRatioUpper95 <= 0.8 && requiresFullSuite && requiresProviderWireSurface
262
+ && requiresCausalContextReceipt
263
+ && ["successful-pair-family-ratio", "failure-aware-family-ratio", "fixed-workload-family-ratio"].includes(primaryEfficiencyEstimand)
264
+ && (!primaryUsesFixedWorkload
265
+ || (!Number.isFinite(maximumBandFreshTokenRatio) && !Number.isFinite(maximumFamilyFreshTokenRatio)))
266
+ : null;
267
+ const normalizedCostClaimConfigurationGate = requestsNormalizedCostClaim
268
+ ? requestsTokenSavingClaim
269
+ && suite.schemaVersion === 2
270
+ && Number.isFinite(maximumNormalizedCostRatioUpper95)
271
+ && maximumNormalizedCostRatioUpper95 <= 0.8
272
+ && Number.isFinite(maximumBandNormalizedCostRatio)
273
+ && Number.isFinite(maximumFamilyNormalizedCostRatio)
274
+ && benchmarkPricingSnapshotValidationErrors(suite.pricingSnapshot).length === 0
275
+ && suite.pricingSnapshot?.model === suite.executionContract?.model
276
+ : null;
277
+ const normalizedCost = benchmarkPricingSnapshotValidationErrors(suite.pricingSnapshot).length === 0
278
+ ? normalizedCostComparison({ suite, allPairs, repeats })
279
+ : null;
280
+ const normalizedCostGates = requestsNormalizedCostClaim ? normalizedCostGate(normalizedCost, {
281
+ maximumRatioUpper95: maximumNormalizedCostRatioUpper95,
282
+ maximumBandRatio: maximumBandNormalizedCostRatio,
283
+ maximumFamilyRatio: maximumFamilyNormalizedCostRatio
284
+ }) : {
285
+ evidenceGate: null,
286
+ applicabilityGate: null,
287
+ confidenceGate: null,
288
+ bandGate: null,
289
+ familyGate: null,
290
+ bandFailures: [],
291
+ familyFailures: [],
292
+ passed: null
293
+ };
294
+ const acceptedUsageCompletenessGate = requestsTokenSavingClaim
295
+ ? tokenAccounting.acceptedAttempts.complete === true
296
+ : null;
297
+ const failedUsageCompletenessGate = requestsTokenSavingClaim
298
+ ? tokenAccounting.failedAttempts.complete === true
299
+ : null;
300
+ const allAttemptUsageCompletenessGate = requestsTokenSavingClaim
301
+ ? tokenAccounting.allAttempts.complete === true
302
+ : null;
303
+ const infrastructureFailureLedgerGate = requestsTokenSavingClaim
304
+ ? infrastructureFailureLedgerIssues.length === 0
305
+ : null;
306
+ const codexBaselineGate = requestsTokenSavingClaim ? baselineSurface === "codex-cli" : null;
307
+ const cleanReleaseSourceGate = requestsTokenSavingClaim
308
+ ? environment.source?.kind === "git-working-tree"
309
+ && environment.source.dirty === false
310
+ && /^[a-f0-9]{40,64}$/.test(environment.source.commit ?? "")
311
+ : null;
312
+ const hostReadinessHistory = environment.hostReadinessHistory ?? null;
313
+ const hostReadinessGate = requiresHostReadiness
314
+ ? hostReadinessHistory?.valid === true
315
+ && hostReadinessHistory?.ready === true
316
+ && hostReadinessHistory?.windowCoverage === "complete"
317
+ : null;
713
318
  const qualityNonInferior = candidate.scores.quality >= baseline.scores.quality;
319
+ const pairedQualityEvidence = pairedQualityNoninferiorityEvidence({
320
+ suite,
321
+ repeats,
322
+ baselineRuns,
323
+ candidateRuns
324
+ });
325
+ const pairedQualityNoninferiorityGate = requestsTokenSavingClaim ? pairedQualityEvidence.passed : null;
714
326
  const qualityGate = candidate.scores.quality >= qualityThreshold;
715
327
  const safetyGate = candidate.scores.safety >= safetyThreshold;
716
328
  const reliabilityGate = candidate.scores.reliability >= reliabilityThreshold;
@@ -746,32 +358,207 @@ export function summarizeBenchmark({
746
358
  : tokenPairs.length >= 3;
747
359
  const efficiencyCategoryCoverage = completeCategoryCoverage(suite, completeScenarioFreshRatios);
748
360
  const efficiencyBandCoverageGate = suite.schemaVersion === 2 ? efficiencyCategoryCoverage.passed : true;
361
+ const primaryEfficiencyEvidenceGate = Number.isInteger(minimumComparableEfficiencyScenarios)
362
+ ? primaryEfficiencyCompleteScenarios >= minimumComparableEfficiencyScenarios
363
+ : primaryEfficiencyCompleteScenarios >= 3;
364
+ const primaryEfficiencyBandCoverageGate = suite.schemaVersion === 2
365
+ ? primaryEfficiencyCategoryCoverage.passed
366
+ : true;
749
367
  const repeatGate = Number.isInteger(minimumRepeats) ? repeats >= minimumRepeats : null;
750
- const protocol = comparisonProtocol(environment, suite, baselineSurface);
368
+ const providerWireVerifiedRuns = candidateRuns.filter((run) => benchmarkProviderWireEvidenceMatchesRequest(
369
+ run.providerWireEvidence,
370
+ environment.requestedModel,
371
+ environment.requestedThinking
372
+ ));
373
+ const providerWireGroupMap = new Map();
374
+ for (const run of candidateRuns) {
375
+ const key = `${run.scenarioId}\0${run.profile ?? "unspecified"}\0${run.lifecycle ?? "unspecified"}`;
376
+ const group = providerWireGroupMap.get(key) ?? {
377
+ scenarioId: run.scenarioId,
378
+ profile: run.profile ?? "unspecified",
379
+ lifecycle: run.lifecycle ?? "unspecified",
380
+ runs: 0,
381
+ rawInstructionHashes: new Set(),
382
+ instructionHashes: new Set(),
383
+ orderedToolSurfaceHashes: new Set(),
384
+ deferredToolSurfaceHashes: new Set()
385
+ };
386
+ group.runs += 1;
387
+ for (const value of run.providerWireEvidence?.instructionHashes ?? []) group.rawInstructionHashes.add(value);
388
+ for (const value of run.providerWireEvidence?.baseInstructionHashes ?? []) group.instructionHashes.add(value);
389
+ for (const value of run.providerWireEvidence?.orderedToolSurfaceHashes ?? []) group.orderedToolSurfaceHashes.add(value);
390
+ for (const value of run.providerWireEvidence?.deferred?.toolSurfaceHashes ?? []) group.deferredToolSurfaceHashes.add(value);
391
+ providerWireGroupMap.set(key, group);
392
+ }
393
+ const providerWireGroups = [...providerWireGroupMap.values()].map((group) => ({
394
+ scenarioId: group.scenarioId,
395
+ profile: group.profile,
396
+ lifecycle: group.lifecycle,
397
+ runs: group.runs,
398
+ rawInstructionHashCount: group.rawInstructionHashes.size,
399
+ instructionHashCount: group.instructionHashes.size,
400
+ orderedToolSurfaceHashCount: group.orderedToolSurfaceHashes.size,
401
+ deferredToolSurfaceHashCount: group.deferredToolSurfaceHashes.size,
402
+ basePrefixStable: group.instructionHashes.size === 1 && group.orderedToolSurfaceHashes.size === 1
403
+ })).sort((left, right) => left.scenarioId.localeCompare(right.scenarioId));
404
+ const providerWireDriftGroups = providerWireGroups.filter((group) => !group.basePrefixStable);
405
+ const providerWireSurfaceGate = requiresProviderWireSurface
406
+ ? candidateSurface === "piagent"
407
+ && candidateRuns.length > 0
408
+ && providerWireVerifiedRuns.length === candidateRuns.length
409
+ && providerWireGroups.length > 0
410
+ && providerWireDriftGroups.length === 0
411
+ : null;
412
+ const causalContextEvidence = summarizeBenchmarkCausalContextEvidence(candidateRuns, { required: requiresCausalContextReceipt });
413
+ const causalContextEvidenceGate = requiresCausalContextReceipt
414
+ ? candidateSurface === "piagent" && causalContextEvidence.currentCoverageStatus === "complete"
415
+ : null;
416
+ const providerWireFailureCounts = {};
417
+ if (requiresProviderWireSurface) {
418
+ for (const run of candidateRuns) {
419
+ const evidence = run.providerWireEvidence;
420
+ if (!evidence) {
421
+ providerWireFailureCounts["missing-evidence"] = (providerWireFailureCounts["missing-evidence"] ?? 0) + 1;
422
+ continue;
423
+ }
424
+ for (const [id, passed] of Object.entries(evidence.checks ?? {})) {
425
+ if (passed === true) continue;
426
+ providerWireFailureCounts[id] = (providerWireFailureCounts[id] ?? 0) + 1;
427
+ }
428
+ if (evidence.expectedModelId !== (environment.requestedModel ?? "").split("/").at(-1)) {
429
+ providerWireFailureCounts["evidence-request-model-binding"] = (providerWireFailureCounts["evidence-request-model-binding"] ?? 0) + 1;
430
+ }
431
+ const requestedEffort = environment.requestedThinking === "off" ? "none" : environment.requestedThinking === "minimal" ? "low" : environment.requestedThinking;
432
+ if (evidence.expectedReasoningEffort !== requestedEffort) {
433
+ providerWireFailureCounts["evidence-request-effort-binding"] = (providerWireFailureCounts["evidence-request-effort-binding"] ?? 0) + 1;
434
+ }
435
+ }
436
+ if (providerWireDriftGroups.length > 0) providerWireFailureCounts["cross-repeat-base-prefix-drift"] = providerWireDriftGroups.length;
437
+ }
438
+ const baseProtocol = comparisonProtocol(environment, suite, baselineSurface);
439
+ const protocol = requiresProviderWireSurface ? {
440
+ ...baseProtocol,
441
+ required: true,
442
+ passed: baseProtocol.passed && providerWireSurfaceGate === true,
443
+ checks: { ...baseProtocol.checks, "provider-wire-surface": providerWireSurfaceGate === true },
444
+ failedChecks: [
445
+ ...baseProtocol.failedChecks,
446
+ ...(providerWireSurfaceGate === true ? [] : ["provider-wire-surface"])
447
+ ]
448
+ } : baseProtocol;
449
+ const fullSuiteGate = requiresFullSuite ? environment.suiteCoverage?.fullSuite === true : null;
751
450
  const efficiencyConfidenceGate = requiresConfidenceEfficiency
752
- ? Boolean(freshRatioConfidence95 && freshRatioConfidence95.upper <= (maximumFreshTokenRatioUpper95 ?? 1))
451
+ ? Boolean(freshRatioConfidence95Raw
452
+ && atMostWithinFloatingPrecision(freshRatioConfidence95Raw.upper, maximumFreshTokenRatioUpper95 ?? 1))
453
+ : null;
454
+ const primaryEfficiencyConfidenceGate = requiresConfidenceEfficiency
455
+ ? Boolean(primaryEfficiencyRatioConfidence95Raw
456
+ && atMostWithinFloatingPrecision(primaryEfficiencyRatioConfidence95Raw.upper, maximumFreshTokenRatioUpper95 ?? 1))
457
+ : null;
458
+ const primaryEfficiencyGate = requiresSuiteEfficiency
459
+ ? primaryEfficiencyEvidenceGate
460
+ && primaryEfficiencyBandCoverageGate
461
+ && Number.isFinite(primaryEfficiencyRatio)
462
+ && (!requiresConfidenceEfficiency || primaryEfficiencyConfidenceGate)
463
+ : null;
464
+ const performanceEvidenceGate = requiresPerformance
465
+ ? completeScenarioDurationRatios.length >= (minimumPairedScenarios ?? 2)
466
+ : null;
467
+ const performancePointEstimateGate = requiresPerformance
468
+ ? Number.isFinite(durationRatio) && atMostWithinFloatingPrecision(durationRatio, 1)
469
+ : null;
470
+ const performanceConfidenceGate = requiresPerformance
471
+ ? Boolean(durationRatioConfidence95Raw
472
+ && atMostWithinFloatingPrecision(durationRatioConfidence95Raw.upper, maximumDurationRatioUpper95))
473
+ : null;
474
+ const durationBandGate = evaluateDurationBandGate(suite, pairedDurationBandReport, maximumBandDurationRatio);
475
+ const completeDurationRatioByScenario = new Map(completeScenarioDurationRatios.map((item) => [item.scenarioId, item]));
476
+ const durationFamilyFailures = Number.isFinite(maximumFamilyDurationRatio)
477
+ ? suite.scenarios.flatMap((scenario) => {
478
+ const item = completeDurationRatioByScenario.get(scenario.id);
479
+ if (!item) return [{ scenarioId: scenario.id, ratio: null, reason: "missing-comparable-duration" }];
480
+ if (!atMostWithinFloatingPrecision(item.ratio, maximumFamilyDurationRatio)) {
481
+ return [{ scenarioId: scenario.id, ratio: item.ratio, reason: "ratio-above-limit" }];
482
+ }
483
+ return [];
484
+ })
485
+ : [];
486
+ const durationFamilyGate = Number.isFinite(maximumFamilyDurationRatio)
487
+ ? durationFamilyFailures.length === 0
488
+ : null;
489
+ const performanceGate = requiresPerformance
490
+ ? performanceEvidenceGate
491
+ && performancePointEstimateGate
492
+ && performanceConfidenceGate
493
+ && durationBandGate.passed !== false
494
+ && durationFamilyGate !== false
495
+ : null;
496
+ const infrastructureRetryGate = requiresStability
497
+ ? infrastructureRetries <= maximumInfrastructureRetries
498
+ : null;
499
+ const unknownInfrastructureUsageGate = requiresStability
500
+ ? unknownInfrastructureUsage === 0
501
+ : null;
502
+ const stabilityGate = requiresStability || requiresProviderWireSurface
503
+ ? infrastructureRetryGate !== false
504
+ && unknownInfrastructureUsageGate !== false
505
+ && infrastructureFailureLedgerGate !== false
506
+ && providerWireSurfaceGate !== false
753
507
  : null;
754
508
  const pairedResolvedOutcomes = pairedOutcomeCounts(allPairs, (run) => run?.resolved === true);
755
509
  const pairedRegressionGate = pairedResolvedOutcomes.baselineOnlyPass === 0;
510
+ const candidateContinuityFailures = candidateRuns.flatMap((run) => {
511
+ const failures = [];
512
+ if (run?.resolved !== true) failures.push("unresolved-outcome");
513
+ if (run?.scenarioKind !== "safety-refusal") {
514
+ if (!workflowContinuityEvidenceComplete(run?.workflow)) {
515
+ failures.push("workflow-evidence-incomplete-or-failed");
516
+ }
517
+ }
518
+ return failures.length > 0 ? [{ scenarioId: run?.scenarioId ?? null, repeat: run?.repeat ?? null, failures }] : [];
519
+ });
520
+ const candidateTaskContinuityGate = candidateRuns.length > 0 && candidateContinuityFailures.length === 0;
756
521
  const failureAwareEfficiencyGate = Number.isFinite(failureAwareFreshTokenRatio)
757
- ? failureAwareFreshTokenRatio <= (maximumFreshTokenRatioUpper95 ?? 1)
522
+ ? atMostWithinFloatingPrecision(failureAwareFreshTokenRatio, maximumFreshTokenRatioUpper95 ?? 1)
758
523
  : null;
524
+ const successfulPairEfficiencyRole = primaryUsesFixedWorkload ? "diagnostic" : "blocking";
525
+ const failureAwareEfficiencyRole = primaryUsesFixedWorkload ? "diagnostic" : "blocking";
526
+ const successfulPairEfficiencySupportGate = successfulPairEfficiencyRole === "diagnostic" || (
527
+ efficiencyEvidenceGate
528
+ && efficiencyBandCoverageGate
529
+ && Number.isFinite(freshRatio)
530
+ && (!requiresConfidenceEfficiency || efficiencyConfidenceGate)
531
+ );
532
+ const failureAwareEfficiencySupportGate = failureAwareEfficiencyRole === "diagnostic"
533
+ || failureAwareEfficiencyGate === true;
534
+ candidate.scores.efficiency = efficiencyScore(primaryEfficiencyRatio);
759
535
  if (
760
536
  qualityGate
761
537
  && safetyGate
762
538
  && reliabilityGate
763
539
  && qualityNonInferior
540
+ && pairedQualityNoninferiorityGate !== false
764
541
  && workflowGate !== false
765
542
  && categoryGate !== false
766
543
  && outcomeScoreGate !== false
767
544
  && pairedRegressionGate
545
+ && candidateTaskContinuityGate
768
546
  && protocol.passed
547
+ && causalContextEvidenceGate !== false
548
+ && fullSuiteGate !== false
549
+ && stabilityGate !== false
550
+ && acceptedUsageCompletenessGate !== false
551
+ && allAttemptUsageCompletenessGate !== false
552
+ && normalizedCostClaimConfigurationGate !== false
553
+ && normalizedCostGates.passed !== false
769
554
  && outcomeEvidenceGate
770
- && efficiencyEvidenceGate
771
- && efficiencyBandCoverageGate
772
- && failureAwareEfficiencyGate
555
+ && successfulPairEfficiencySupportGate
556
+ && freshTokenBandGate.passed !== false
557
+ && freshTokenFamilyGate !== false
558
+ && failureAwareEfficiencySupportGate
559
+ && primaryEfficiencyGate !== false
773
560
  && repeatGate !== false
774
- && (!requiresConfidenceEfficiency || efficiencyConfidenceGate)
561
+ && (!requiresPerformance || performanceGate)
775
562
  && candidate.scores.efficiency !== null
776
563
  ) {
777
564
  const workflowScore = candidate.scores.workflow ?? 10;
@@ -784,23 +571,41 @@ export function summarizeBenchmark({
784
571
  );
785
572
  }
786
573
  const tokenClaimAllowed = Boolean(safetyGate
574
+ && requestsTokenSavingClaim
575
+ && canonicalProductionIdentityGate
576
+ && releaseClaimConfigurationGate
577
+ && codexBaselineGate
578
+ && cleanReleaseSourceGate
579
+ && hostReadinessGate !== false
787
580
  && qualityGate
788
581
  && reliabilityGate
789
582
  && qualityNonInferior
583
+ && pairedQualityNoninferiorityGate !== false
790
584
  && workflowGate !== false
791
585
  && categoryGate !== false
792
586
  && outcomeScoreGate !== false
793
587
  && pairedRegressionGate
588
+ && candidateTaskContinuityGate
794
589
  && protocol.passed
590
+ && causalContextEvidenceGate !== false
591
+ && fullSuiteGate !== false
592
+ && stabilityGate !== false
593
+ && acceptedUsageCompletenessGate !== false
594
+ && allAttemptUsageCompletenessGate !== false
595
+ && normalizedCostClaimConfigurationGate !== false
596
+ && normalizedCostGates.passed !== false
795
597
  && outcomeEvidenceGate
796
- && efficiencyEvidenceGate
797
- && efficiencyBandCoverageGate
598
+ && successfulPairEfficiencySupportGate
599
+ && freshTokenBandGate.passed !== false
600
+ && freshTokenFamilyGate !== false
798
601
  && repeatGate !== false
799
- && Number.isFinite(freshRatio)
800
- && failureAwareEfficiencyGate
801
- && (requiresConfidenceEfficiency ? efficiencyConfidenceGate : freshRatio < 1));
602
+ && failureAwareEfficiencySupportGate
603
+ && primaryEfficiencyGate
604
+ && (!requiresPerformance || performanceGate));
802
605
  const releaseFailures = [
606
+ !canonicalProductionIdentityGate ? "canonical-production-identity" : null,
803
607
  !qualityNonInferior ? "quality-regression" : null,
608
+ requestsTokenSavingClaim && !pairedQualityNoninferiorityGate ? "paired-quality-noninferiority" : null,
804
609
  !qualityGate ? "quality" : null,
805
610
  !safetyGate ? "safety" : null,
806
611
  !reliabilityGate ? "reliability" : null,
@@ -808,17 +613,45 @@ export function summarizeBenchmark({
808
613
  categoryGate === false ? "category" : null,
809
614
  outcomeScoreGate === false ? "outcome-score-floor" : null,
810
615
  !pairedRegressionGate ? "paired-candidate-regression" : null,
811
- !protocol.passed ? "comparison-protocol" : null,
616
+ !candidateTaskContinuityGate ? "candidate-task-continuity" : null,
617
+ !baseProtocol.passed ? "comparison-protocol" : null,
618
+ requiresProviderWireSurface && !providerWireSurfaceGate ? "provider-wire-surface" : null,
619
+ requiresCausalContextReceipt && !causalContextEvidenceGate ? "causal-context-evidence" : null,
620
+ requestsTokenSavingClaim && !releaseClaimConfigurationGate ? "release-claim-configuration" : null,
621
+ requestsNormalizedCostClaim && !normalizedCostClaimConfigurationGate ? "normalized-cost-configuration" : null,
622
+ requestsTokenSavingClaim && !codexBaselineGate ? "codex-baseline" : null,
623
+ requestsTokenSavingClaim && !cleanReleaseSourceGate ? "clean-release-source" : null,
624
+ requiresHostReadiness && !hostReadinessGate ? "host-readiness-history" : null,
625
+ requiresFullSuite && fullSuiteGate === false ? "full-suite" : null,
626
+ requiresStability && !infrastructureRetryGate ? "infrastructure-retries" : null,
627
+ requiresStability && !unknownInfrastructureUsageGate ? "unknown-infrastructure-usage" : null,
628
+ requestsTokenSavingClaim && !acceptedUsageCompletenessGate ? "accepted-usage-completeness" : null,
629
+ requestsTokenSavingClaim && !allAttemptUsageCompletenessGate ? "all-attempt-usage-completeness" : null,
630
+ requestsTokenSavingClaim && !infrastructureFailureLedgerGate ? "infrastructure-failure-ledger" : null,
631
+ requestsNormalizedCostClaim && !normalizedCostGates.evidenceGate ? "normalized-cost-evidence" : null,
632
+ requestsNormalizedCostClaim && !normalizedCostGates.applicabilityGate ? "normalized-cost-pricing-applicability" : null,
633
+ requestsNormalizedCostClaim && normalizedCostGates.evidenceGate && !normalizedCostGates.confidenceGate ? "normalized-cost-confidence" : null,
634
+ requestsNormalizedCostClaim && !normalizedCostGates.bandGate ? "normalized-cost-band-ratio" : null,
635
+ requestsNormalizedCostClaim && !normalizedCostGates.familyGate ? "normalized-cost-family-ratio" : null,
812
636
  !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,
637
+ requiresSuiteEfficiency && successfulPairEfficiencyRole === "blocking" && !efficiencyEvidenceGate ? "efficiency-evidence" : null,
638
+ requiresSuiteEfficiency && successfulPairEfficiencyRole === "blocking" && !efficiencyBandCoverageGate ? "efficiency-category-coverage" : null,
639
+ requiresBandEfficiency && !freshTokenBandGate.passed ? "efficiency-band-ratio" : null,
640
+ Number.isFinite(maximumFamilyFreshTokenRatio) && !freshTokenFamilyGate ? "efficiency-family-ratio" : null,
641
+ requiresSuiteEfficiency && failureAwareEfficiencyRole === "blocking" && !failureAwareEfficiencyGate ? "failure-aware-efficiency" : null,
642
+ requiresSuiteEfficiency && !primaryEfficiencyGate ? "primary-efficiency" : null,
816
643
  repeatGate === false ? "repeat-count" : null,
817
- requiresConfidenceEfficiency && !efficiencyConfidenceGate ? "efficiency-confidence" : null
644
+ requiresConfidenceEfficiency && successfulPairEfficiencyRole === "blocking" && !efficiencyConfidenceGate ? "efficiency-confidence" : null,
645
+ requiresPerformance && !performanceEvidenceGate ? "performance-evidence" : null,
646
+ requiresPerformance && performanceEvidenceGate && !performancePointEstimateGate ? "performance-point-regression" : null,
647
+ requiresPerformance && performanceEvidenceGate && performancePointEstimateGate && !performanceConfidenceGate ? "performance-confidence" : null,
648
+ Number.isFinite(maximumBandDurationRatio) && !durationBandGate.passed ? "performance-band-ratio" : null,
649
+ Number.isFinite(maximumFamilyDurationRatio) && !durationFamilyGate ? "performance-family-ratio" : null
818
650
  ].filter(Boolean);
819
651
  const suiteGate = suite.schemaVersion === 2 ? {
820
652
  passed: releaseFailures.length === 0,
821
653
  failures: releaseFailures,
654
+ failureReasons: releaseFailures.map((id) => ({ id, message: RELEASE_FAILURE_MESSAGES[id] ?? id })),
822
655
  thresholds: {
823
656
  quality: qualityThreshold,
824
657
  safety: safetyThreshold,
@@ -828,22 +661,76 @@ export function summarizeBenchmark({
828
661
  outcomeScoreExclusive: outcomeScoreThresholdExclusive ?? null,
829
662
  pairedOutcomeScenarios: minimumPairedScenarios ?? null,
830
663
  comparableEfficiencyScenarios: minimumComparableEfficiencyScenarios ?? null,
664
+ primaryEfficiencyEstimand,
831
665
  repeats: minimumRepeats ?? null,
832
- freshTokenRatioUpper95: maximumFreshTokenRatioUpper95 ?? null
666
+ freshTokenRatioUpper95: maximumFreshTokenRatioUpper95 ?? null,
667
+ bandFreshTokenRatio: maximumBandFreshTokenRatio ?? null,
668
+ familyFreshTokenRatio: maximumFamilyFreshTokenRatio ?? null,
669
+ normalizedCostRatioUpper95: maximumNormalizedCostRatioUpper95 ?? null,
670
+ bandNormalizedCostRatio: maximumBandNormalizedCostRatio ?? null,
671
+ familyNormalizedCostRatio: maximumFamilyNormalizedCostRatio ?? null,
672
+ durationRatioPointEstimate: requiresPerformance ? 1 : null,
673
+ durationRatioUpper95: maximumDurationRatioUpper95 ?? null,
674
+ bandDurationRatio: maximumBandDurationRatio ?? null,
675
+ familyDurationRatio: maximumFamilyDurationRatio ?? null,
676
+ infrastructureRetries: maximumInfrastructureRetries ?? null,
677
+ tokenSavingClaimUpper95Maximum: requestsTokenSavingClaim ? 0.8 : null,
678
+ baselineSurface: requestsTokenSavingClaim ? "codex-cli" : null,
679
+ cleanSource: requestsTokenSavingClaim,
680
+ hostReadinessHistory: requiresHostReadiness,
681
+ requireFullSuite: requiresFullSuite,
682
+ stableProviderWireSurface: requiresProviderWireSurface,
683
+ causalContextReceipt: requiresCausalContextReceipt,
684
+ canonicalProductionIdentityRequired: suite.id === "production-v1"
685
+ },
686
+ observed: {
687
+ completeOutcomeScenarios,
688
+ completeEfficiencyScenarios: completeScenarioFreshRatios.length,
689
+ completeDurationScenarios: completeScenarioDurationRatios.length,
690
+ pairedQualityExpectedPairs: pairedQualityEvidence.expectedPairs,
691
+ pairedQualityObservedPairs: pairedQualityEvidence.observedPairs,
692
+ pairedQualityComparablePairs: pairedQualityEvidence.comparablePairs,
693
+ pairedQualityFailures: pairedQualityEvidence.failures,
694
+ repeats,
695
+ freshTokenRatioUpper95: freshRatioConfidence95Raw?.upper ?? null,
696
+ bandFreshTokenRatioFailures: freshTokenBandGate.failures,
697
+ familyFreshTokenRatioFailures: freshTokenFamilyFailures,
698
+ acceptedUsageExactAttempts: tokenAccounting.acceptedAttempts.exactAttempts,
699
+ acceptedUsageAttempts: tokenAccounting.acceptedAttempts.attempts,
700
+ normalizedCostRatio: normalizedCost?.ratio ?? null,
701
+ normalizedCostRatioUpper95: normalizedCost?.ratioConfidence95Raw?.upper ?? null,
702
+ normalizedCostBandFailures: normalizedCostGates.bandFailures,
703
+ normalizedCostFamilyFailures: normalizedCostGates.familyFailures,
704
+ normalizedCostApplicabilityFailures: normalizedCost?.applicabilityFailures ?? [],
705
+ primaryEfficiencyCompleteScenarios,
706
+ primaryEfficiencyRatio: rounded(primaryEfficiencyRatio, 4),
707
+ primaryEfficiencyRatioUpper95: primaryEfficiencyRatioConfidence95Raw?.upper ?? null,
708
+ durationRatio: rounded(durationRatio, 4),
709
+ durationRatioUpper95: durationRatioConfidence95Raw?.upper ?? null,
710
+ durationBandFailures: durationBandGate.failures,
711
+ durationFamilyFailures,
712
+ infrastructureRetries,
713
+ unknownInfrastructureUsage,
714
+ baselineSurface,
715
+ sourceKind: environment.source?.kind ?? null,
716
+ sourceCommit: environment.source?.commit ?? null,
717
+ sourceDirty: environment.source?.dirty ?? null,
718
+ hostReadinessValidReceipts: hostReadinessHistory?.validReceiptCount ?? null,
719
+ hostReadinessReceipts: hostReadinessHistory?.receiptCount ?? null,
720
+ hostReadinessCoverage: hostReadinessHistory?.windowCoverage ?? null,
721
+ fullSuite: environment.suiteCoverage?.fullSuite ?? null,
722
+ canonicalProductionIdentity: canonicalProductionSuite,
723
+ providerWireVerifiedRuns: providerWireVerifiedRuns.length,
724
+ providerWireRuns: candidateRuns.length,
725
+ providerWireGroups: providerWireGroups.length,
726
+ providerWireDriftGroups: providerWireDriftGroups.length,
727
+ causalContextAvailableRuns: causalContextEvidence.currentAvailableRuns,
728
+ causalContextRuns: causalContextEvidence.runs,
729
+ causalContextRequiredSchemaVersion: causalContextEvidence.requiredSchemaVersion
833
730
  }
834
731
  } : null;
835
732
  const baselineKey = surfaceReportKey(baselineSurface);
836
733
  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
734
  const claimEligibility = benchmarkClaimEligibility({
848
735
  suite,
849
736
  environment,
@@ -851,7 +738,7 @@ export function summarizeBenchmark({
851
738
  protocolPassed: protocol.passed,
852
739
  tokenClaimAllowed
853
740
  });
854
- return {
741
+ return integrateCodexRelativeEfficiencyReport({
855
742
  schemaVersion: 2,
856
743
  measurementSchemaVersion: BENCHMARK_MEASUREMENT_SCHEMA_VERSION,
857
744
  runId,
@@ -868,32 +755,49 @@ export function summarizeBenchmark({
868
755
  failureCounts: infrastructureFailureCounts,
869
756
  classCounts: infrastructureClassCounts
870
757
  },
758
+ tokenAccounting,
759
+ timingDiagnostics,
871
760
  surfaces: { [baselineKey]: baseline, [candidateKey]: candidate },
872
761
  comparison: {
873
762
  baselineSurface,
874
763
  candidateSurface,
875
764
  purpose: claimEligibility.comparisonPurpose,
876
765
  usageEstimator: "paired-geometric-mean-ratio",
766
+ durationEstimator: "paired-geometric-mean-ratio-clustered-by-scenario-family",
877
767
  failureAwareUsageEstimator: "total-comparable-attempt-fresh-tokens-per-resolved-outcome",
768
+ failureAwareFamilyUsageEstimator: "geometric-mean-of-family-total-comparable-attempt-fresh-tokens-per-resolved-outcome-ratios",
769
+ fixedWorkloadUsageEstimator: "geometric-mean-of-family-total-exact-scheduled-workload-ratios",
770
+ fixedWorkloadEstimatorVersion: 1,
878
771
  pairedOutcomeScenarios: completeOutcomeScenarios,
879
772
  pairedSuccessfulRuns: pairs.length,
880
773
  pairedUsageRuns: tokenPairs.length,
881
774
  pairedUsageScenarios: scenarioFreshRatios.length,
882
775
  pairedCompleteScenarios: completeScenarioFreshRatios.length,
776
+ pairedDurationRuns: durationPairs.length,
777
+ pairedDurationScenarios: scenarioDurationRatios.length,
778
+ pairedCompleteDurationScenarios: completeScenarioDurationRatios.length,
779
+ pairedDurationBands: pairedDurationBandReport,
883
780
  pairedCostRuns: costPairs.length,
781
+ normalizedCost,
782
+ normalizedCostClaimConfigurationGate,
783
+ normalizedCostEvidenceGate: normalizedCostGates.evidenceGate,
784
+ normalizedCostPricingApplicabilityGate: normalizedCostGates.applicabilityGate,
785
+ normalizedCostConfidenceGate: normalizedCostGates.confidenceGate,
786
+ normalizedCostBandGate: normalizedCostGates.bandGate,
787
+ normalizedCostBandFailures: normalizedCostGates.bandFailures,
788
+ normalizedCostFamilyGate: normalizedCostGates.familyGate,
789
+ normalizedCostFamilyFailures: normalizedCostGates.familyFailures,
790
+ normalizedCostGate: normalizedCostGates.passed,
884
791
  pairedOutcomes: {
885
792
  resolved: pairedResolvedOutcomes,
886
793
  quality: pairedOutcomeCounts(allPairs.filter((pair) => pair.candidate.scenarioKind !== "safety-refusal"), qualityPassed),
887
794
  safety: pairedOutcomeCounts(allPairs, safetyPassed)
888
795
  },
889
- pairedUsageBands: {
890
- categories: pairedUsageBands(tokenPairs, "category"),
891
- profiles: pairedUsageBands(tokenPairs, "profile"),
892
- lifecycles: pairedUsageBands(tokenPairs, "lifecycle"),
893
- difficulties: pairedUsageBands(tokenPairs, "difficulty")
894
- },
796
+ pairedUsageBands: pairedUsageBandReport,
895
797
  pairedFreshTokenWins: { [candidateKey]: freshWins, [baselineKey]: freshLosses, ties: freshTies },
896
798
  medianPairedFreshTokenDelta: rounded(median(freshDeltas), 2),
799
+ pairedDurationWins: { [candidateKey]: durationWins, [baselineKey]: durationLosses, ties: durationTies },
800
+ medianPairedDurationDeltaSeconds: rounded(median(durationDeltas), 2),
897
801
  medianFreshTokens: { [baselineKey]: baselineFresh, [candidateKey]: candidateFresh },
898
802
  medianCost: { [baselineKey]: baselineCost, [candidateKey]: candidateCost },
899
803
  freshTokensPerResolvedOutcome: {
@@ -901,12 +805,69 @@ export function summarizeBenchmark({
901
805
  [candidateKey]: rounded(candidateFreshPerResolvedOutcome, 2)
902
806
  },
903
807
  failureAwareFreshTokenRatio: rounded(failureAwareFreshTokenRatio, 4),
808
+ failureAwareFamilyFreshTokenRatio: rounded(familyClusteredFailureAware.ratio, 4),
809
+ failureAwareFamilyFreshTokenRatioConfidence95: familyClusteredFailureAware.confidence95,
810
+ failureAwareFamilyFreshTokenRatioConfidence95Raw: familyClusteredFailureAware.confidence95Raw,
811
+ failureAwareFamilyCoverage: {
812
+ complete: familyClusteredFailureAware.complete,
813
+ expectedScenarioFamilies: familyClusteredFailureAware.expectedScenarioFamilies,
814
+ usableScenarioFamilies: familyClusteredFailureAware.usableScenarioFamilies,
815
+ sampleUnit: familyClusteredFailureAware.sampleUnit,
816
+ scenarioIds: familyClusteredFailureAware.scenarioIds
817
+ },
818
+ failureAwareFamilyRatios: familyClusteredFailureAware.families,
819
+ fixedWorkloadFamilyFreshTokenRatio: rounded(familyClusteredFixedWorkload.ratio, 4),
820
+ fixedWorkloadFamilyFreshTokenRatioRaw: familyClusteredFixedWorkload.ratio,
821
+ fixedWorkloadFamilyFreshTokenRatioConfidence95: familyClusteredFixedWorkload.confidence95,
822
+ fixedWorkloadFamilyFreshTokenRatioConfidence95Raw: familyClusteredFixedWorkload.confidence95Raw,
823
+ fixedWorkloadAggregateFreshTokenRatio: rounded(familyClusteredFixedWorkload.aggregateFreshTokenRatio, 4),
824
+ fixedWorkloadFamilyCoverage: {
825
+ complete: familyClusteredFixedWorkload.complete,
826
+ expectedScenarioFamilies: familyClusteredFixedWorkload.expectedScenarioFamilies,
827
+ usableScenarioFamilies: familyClusteredFixedWorkload.usableScenarioFamilies,
828
+ expectedAttemptsPerFamily: familyClusteredFixedWorkload.expectedAttemptsPerFamily,
829
+ sampleUnit: familyClusteredFixedWorkload.sampleUnit,
830
+ outcomeConditioning: familyClusteredFixedWorkload.outcomeConditioning,
831
+ aggregation: familyClusteredFixedWorkload.aggregation,
832
+ attemptPolicy: familyClusteredFixedWorkload.attemptPolicy,
833
+ scenarioIds: familyClusteredFixedWorkload.scenarioIds
834
+ },
835
+ fixedWorkloadFamilyRatios: familyClusteredFixedWorkload.families,
836
+ allSuccessfulPairsFreshTokenRatio: rounded(allSuccessfulPairsFreshRatio, 4),
904
837
  freshTokenRatio: rounded(freshRatio, 4),
838
+ freshTokenRatioRaw: freshRatio,
905
839
  freshTokenRatioConfidence95: freshRatioConfidence95,
840
+ freshTokenRatioConfidence95Raw: freshRatioConfidence95Raw,
841
+ freshTokenRatioSample: {
842
+ sampleUnit: "scenario-family",
843
+ scenarioCount: confidenceScenarioRatios.length,
844
+ scenarioIds: confidenceScenarioRatios.map((item) => item.scenarioId)
845
+ },
846
+ primaryEfficiencyEstimand,
847
+ primaryEfficiencyRatio: rounded(primaryEfficiencyRatio, 4),
848
+ primaryEfficiencyRatioRaw: primaryEfficiencyRatio,
849
+ primaryEfficiencyRatioConfidence95,
850
+ primaryEfficiencyRatioConfidence95Raw,
851
+ primaryEfficiencySample: {
852
+ sampleUnit: "scenario-family",
853
+ scenarioCount: primaryEfficiencyCompleteScenarios,
854
+ scenarioIds: primaryEfficiencyScenarioRatios.map((item) => item.scenarioId),
855
+ outcomeConditioning: primaryUsesFixedWorkload ? "none" : "resolved-outcome-conditioned"
856
+ },
857
+ primaryEfficiencyDeltaPercent: Number.isFinite(primaryEfficiencyRatio)
858
+ ? rounded((primaryEfficiencyRatio - 1) * 100, 2)
859
+ : null,
906
860
  freshTokenDeltaPercent: Number.isFinite(freshRatio) ? rounded((freshRatio - 1) * 100, 2) : null,
861
+ durationRatio: rounded(durationRatio, 4),
862
+ durationRatioRaw: durationRatio,
863
+ durationRatioConfidence95,
864
+ durationRatioConfidence95Raw,
865
+ durationDeltaPercent: Number.isFinite(durationRatio) ? rounded((durationRatio - 1) * 100, 2) : null,
907
866
  costRatio: rounded(costRatio, 4),
908
867
  costDeltaPercent: Number.isFinite(costRatio) ? rounded((costRatio - 1) * 100, 2) : null,
909
868
  qualityNonInferior,
869
+ pairedQualityNoninferiorityGate,
870
+ pairedQualityEvidence,
910
871
  qualityGate,
911
872
  safetyGate,
912
873
  reliabilityGate,
@@ -916,16 +877,65 @@ export function summarizeBenchmark({
916
877
  outcomeScoreGate,
917
878
  outcomeScoreFailures,
918
879
  pairedRegressionGate,
880
+ candidateTaskContinuityGate,
881
+ candidateContinuityFailures,
919
882
  comparisonProtocolGate: protocol,
883
+ providerWireSurfaceGate,
884
+ causalContextEvidenceGate,
885
+ causalContextEvidence,
886
+ providerWireEvidence: {
887
+ required: requiresProviderWireSurface,
888
+ runs: candidateRuns.length,
889
+ verifiedRuns: providerWireVerifiedRuns.length,
890
+ groups: providerWireGroups,
891
+ driftGroups: providerWireDriftGroups,
892
+ failureCounts: providerWireFailureCounts,
893
+ deferredChangesAreBaseDrift: false
894
+ },
895
+ releaseClaimConfigurationGate,
896
+ canonicalProductionIdentityGate,
897
+ codexBaselineGate,
898
+ cleanReleaseSourceGate,
899
+ hostReadinessGate,
900
+ fullSuiteGate,
920
901
  outcomeEvidenceGate,
921
902
  efficiencyEvidenceGate,
903
+ successfulPairEfficiencyRole,
904
+ successfulPairEfficiencySupportGate,
922
905
  efficiencyBandCoverageGate,
923
906
  efficiencyCategoryCoverage,
907
+ freshTokenBandGate: freshTokenBandGate.passed,
908
+ freshTokenBandFailures: freshTokenBandGate.failures,
909
+ freshTokenFamilyGate,
910
+ freshTokenFamilyFailures,
924
911
  failureAwareEfficiencyGate,
912
+ failureAwareEfficiencyRole,
913
+ failureAwareEfficiencySupportGate,
914
+ primaryEfficiencyEvidenceGate,
915
+ primaryEfficiencyBandCoverageGate,
916
+ primaryEfficiencyCategoryCoverage,
917
+ primaryEfficiencyConfidenceGate,
918
+ primaryEfficiencyGate,
925
919
  repeatGate,
926
920
  efficiencyConfidenceGate,
921
+ performanceEvidenceGate,
922
+ performancePointEstimateGate,
923
+ performanceConfidenceGate,
924
+ durationBandGate: durationBandGate.passed,
925
+ durationBandFailures: durationBandGate.failures,
926
+ durationFamilyGate,
927
+ durationFamilyFailures,
928
+ performanceGate,
929
+ infrastructureRetryGate,
930
+ unknownInfrastructureUsageGate,
931
+ acceptedUsageCompletenessGate,
932
+ failedUsageCompletenessGate,
933
+ allAttemptUsageCompletenessGate,
934
+ infrastructureFailureLedgerGate,
935
+ infrastructureFailureLedgerIssues,
936
+ stabilityGate,
927
937
  suiteGate,
928
- productionGate: suite.id === "production-v1" ? suiteGate : null,
938
+ productionGate: canonicalProductionSuite ? suiteGate : null,
929
939
  tokenClaimAllowed,
930
940
  claimEligibility
931
941
  },
@@ -934,29 +944,91 @@ export function summarizeBenchmark({
934
944
  ? "safety-gate-failed"
935
945
  : !qualityNonInferior
936
946
  ? "quality-regression"
947
+ : pairedQualityNoninferiorityGate === false
948
+ ? "paired-quality-noninferiority-gate-failed"
937
949
  : !qualityGate
938
950
  ? "quality-gate-failed"
939
951
  : !reliabilityGate
940
952
  ? "reliability-gate-failed"
941
- : !protocol.passed
953
+ : !baseProtocol.passed
942
954
  ? "comparison-protocol-gate-failed"
943
955
  : workflowGate === false
944
956
  ? "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.`
957
+ : categoryGate === false
958
+ ? "category-gate-failed"
959
+ : outcomeScoreGate === false
960
+ ? "outcome-score-floor-gate-failed"
961
+ : !pairedRegressionGate
962
+ ? "paired-candidate-regression"
963
+ : !candidateTaskContinuityGate
964
+ ? "candidate-task-continuity-gate-failed"
965
+ : !canonicalProductionIdentityGate
966
+ ? "canonical-production-identity-gate-failed"
967
+ : requestsTokenSavingClaim && releaseClaimConfigurationGate === false
968
+ ? "release-claim-configuration-gate-failed"
969
+ : requestsTokenSavingClaim && codexBaselineGate === false
970
+ ? "codex-baseline-gate-failed"
971
+ : requestsTokenSavingClaim && cleanReleaseSourceGate === false
972
+ ? "clean-release-source-gate-failed"
973
+ : requiresHostReadiness && hostReadinessGate === false
974
+ ? "host-readiness-history-gate-failed"
975
+ : requiresProviderWireSurface && providerWireSurfaceGate === false
976
+ ? "provider-wire-surface-gate-failed"
977
+ : requiresCausalContextReceipt && causalContextEvidenceGate === false
978
+ ? "causal-context-evidence-gate-failed"
979
+ : fullSuiteGate === false
980
+ ? "full-suite-gate-failed"
981
+ : requiresStability && infrastructureRetryGate === false
982
+ ? "stability-infrastructure-retry-gate-failed"
983
+ : requiresStability && unknownInfrastructureUsageGate === false
984
+ ? "stability-unknown-usage-gate-failed"
985
+ : requestsTokenSavingClaim && infrastructureFailureLedgerGate === false
986
+ ? "stability-failure-ledger-gate-failed"
987
+ : requestsTokenSavingClaim && acceptedUsageCompletenessGate === false
988
+ ? "accepted-usage-completeness-gate-failed"
989
+ : requestsTokenSavingClaim && allAttemptUsageCompletenessGate === false
990
+ ? "all-attempt-usage-completeness-gate-failed"
991
+ : requestsNormalizedCostClaim && normalizedCostClaimConfigurationGate === false
992
+ ? "normalized-cost-configuration-gate-failed"
993
+ : requestsNormalizedCostClaim && normalizedCostGates.applicabilityGate === false
994
+ ? "normalized-cost-pricing-applicability-gate-failed"
995
+ : requestsNormalizedCostClaim && normalizedCostGates.evidenceGate === false
996
+ ? "normalized-cost-evidence-gate-failed"
997
+ : requestsNormalizedCostClaim && normalizedCostGates.confidenceGate === false
998
+ ? "normalized-cost-confidence-gate-failed"
999
+ : requestsNormalizedCostClaim && normalizedCostGates.bandGate === false
1000
+ ? "normalized-cost-band-ratio-gate-failed"
1001
+ : requestsNormalizedCostClaim && normalizedCostGates.familyGate === false
1002
+ ? "normalized-cost-family-ratio-gate-failed"
1003
+ : !outcomeEvidenceGate
1004
+ ? "paired-outcome-evidence-gate-failed"
1005
+ : repeatGate === false
1006
+ ? "repeat-gate-failed"
1007
+ : primaryUsesFixedWorkload && primaryEfficiencyGate === false
1008
+ ? "primary-efficiency-gate-failed"
1009
+ : requiresPerformance && performanceEvidenceGate === false
1010
+ ? "performance-evidence-gate-failed"
1011
+ : requiresPerformance && performancePointEstimateGate === false
1012
+ ? "performance-point-regression"
1013
+ : requiresPerformance && performanceConfidenceGate === false
1014
+ ? "performance-confidence-gate-failed"
1015
+ : durationBandGate.passed === false
1016
+ ? "performance-band-ratio-gate-failed"
1017
+ : durationFamilyGate === false
1018
+ ? "performance-family-ratio-gate-failed"
1019
+ : requiresBandEfficiency && freshTokenBandGate.passed === false
1020
+ ? "efficiency-band-ratio-gate-failed"
1021
+ : freshTokenFamilyGate === false
1022
+ ? "efficiency-family-ratio-gate-failed"
1023
+ : requiresConfidenceEfficiency
1024
+ && successfulPairEfficiencyRole === "blocking"
1025
+ && efficiencyConfidenceGate === false
1026
+ ? "efficiency-confidence-gate-failed"
1027
+ : tokenClaimAllowed
1028
+ ? `${candidateSurface}-more-efficient`
1029
+ : "observational-efficiency-only",
1030
+ 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
1031
  },
960
- runs
961
- };
1032
+ runs: reportRuns
1033
+ }, suite);
962
1034
  }