@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,4 +1,255 @@
1
1
  const benchmarkSurfaces = new Set(["raw-pi", "piagent", "codex-cli"]);
2
+ const causalCoverageLanesV1 = new Set([
3
+ "telemetry-window", "session-lifecycle", "criterion-initial-pack", "pack-lifecycle", "direct-fallback-rereads", "managed-prefix"
4
+ ]);
5
+ const causalCoverageLanesV2 = new Set([...causalCoverageLanesV1, "edit-recovery-context"]);
6
+ const causalCoverageLanesV3 = causalCoverageLanesV2;
7
+ const causalMaximumAggregate = 1_000_000_000;
8
+
9
+ function exactKeys(value, keys) {
10
+ return value && typeof value === "object" && !Array.isArray(value)
11
+ && Object.keys(value).length === keys.length
12
+ && keys.every((key) => Object.hasOwn(value, key));
13
+ }
14
+
15
+ function boundedInteger(value) {
16
+ return Number.isSafeInteger(value) && value >= 0 && value <= causalMaximumAggregate;
17
+ }
18
+
19
+ function validCausalTriplet(value) {
20
+ return exactKeys(value, ["offered", "delivered", "injected"])
21
+ && [value.offered, value.delivered, value.injected].every(boundedInteger)
22
+ && value.delivered <= value.offered && value.injected === value.delivered;
23
+ }
24
+
25
+ function validCriterionAggregate(value, aggregate) {
26
+ if (!exactKeys(value, ["attempts", "selectedAttempts", "candidates", "selectedItems", "estimatedTokens", "zeroSelectionReasonCounts", "offered", "delivered", "injected"])
27
+ || ![value.attempts, value.selectedAttempts, value.candidates, value.selectedItems, value.estimatedTokens,
28
+ value.offered, value.delivered, value.injected].every(boundedInteger)
29
+ || !exactKeys(value.zeroSelectionReasonCounts, ["autoContextDisabled", "criterionGraphUnavailable", "noCandidates", "noReadableSelection"])
30
+ || !Object.values(value.zeroSelectionReasonCounts).every(boundedInteger)) return false;
31
+ const zeroAttempts = Object.values(value.zeroSelectionReasonCounts).reduce((sum, count) => sum + count, 0);
32
+ return value.selectedAttempts <= value.attempts
33
+ && value.selectedAttempts === value.offered
34
+ && value.offered === value.delivered && value.delivered === value.injected
35
+ && value.selectedItems >= value.selectedAttempts && value.candidates >= value.selectedItems
36
+ && zeroAttempts === value.attempts - value.selectedAttempts
37
+ && value.offered <= aggregate.packCounts.offered
38
+ && value.selectedItems <= aggregate.selectedItemCounts.offered
39
+ && value.estimatedTokens <= aggregate.estimatedTokens.offered;
40
+ }
41
+
42
+ function validEditRecoveryAggregate(value) {
43
+ if (!exactKeys(value, ["count", "failuresObserved", "suppressedFailures", "injectedChars", "injectedEstimatedTokens", "evidenceCoverage", "definition"])
44
+ || ![value.count, value.failuresObserved, value.suppressedFailures, value.injectedChars, value.injectedEstimatedTokens].every(boundedInteger)
45
+ || value.failuresObserved !== value.count + value.suppressedFailures
46
+ || !exactKeys(value.evidenceCoverage, ["status", "observed", "comparable", "rate"])
47
+ || value.evidenceCoverage.status !== "complete"
48
+ || ![value.evidenceCoverage.observed, value.evidenceCoverage.comparable].every(boundedInteger)
49
+ || value.evidenceCoverage.observed !== value.count || value.evidenceCoverage.comparable !== value.count
50
+ || value.evidenceCoverage.rate !== 1
51
+ || value.definition !== "matched-edit-recovery-context-receipt-v1") return false;
52
+ return value.count === 0
53
+ ? value.injectedChars === 0 && value.injectedEstimatedTokens === 0
54
+ : value.injectedChars > 0 && value.injectedEstimatedTokens > 0;
55
+ }
56
+
57
+ function validRuntimeSection(value, evidenceSource) {
58
+ return exactKeys(value, ["coverageStatus", "observedEvents", "evidenceSource", "aggregate"])
59
+ && ["not-observed", "partial", "complete"].includes(value.coverageStatus)
60
+ && boundedInteger(value.observedEvents)
61
+ && value.evidenceSource === evidenceSource
62
+ && (value.coverageStatus === "not-observed"
63
+ ? value.observedEvents === 0 && value.aggregate === null
64
+ : value.observedEvents > 0 && (value.coverageStatus === "partial") === (value.aggregate === null));
65
+ }
66
+
67
+ function validAdaptiveContextRuntime(value) {
68
+ if (!validRuntimeSection(value, "context-telemetry")) return false;
69
+ if (value.coverageStatus !== "complete") return true;
70
+ const aggregate = value.aggregate;
71
+ const fields = ["projected", "noOp", "cancelled", "deterministicCompactions", "hostSummaryFallbacks",
72
+ "estimatedSavingsTokens", "minimumSavingsTokens", "minimumSavingsMet", "minimumSavingsNotMet",
73
+ "governorProviderCalls", "deterministicCompactionProviderCalls", "protocolIntegrity", "definition"];
74
+ if (!exactKeys(aggregate, fields)
75
+ || !fields.slice(0, -2).every((field) => boundedInteger(aggregate[field]))
76
+ || aggregate.definition !== "adaptive-context-runtime-receipt-v1"
77
+ || !exactKeys(aggregate.protocolIntegrity, ["checks", "intact", "failed", "orphanCalls", "orphanResults"])
78
+ || !Object.values(aggregate.protocolIntegrity).every(boundedInteger)) return false;
79
+ const eventCount = aggregate.projected + aggregate.noOp + aggregate.cancelled
80
+ + aggregate.deterministicCompactions + aggregate.hostSummaryFallbacks;
81
+ const savingsDecisions = aggregate.minimumSavingsMet + aggregate.minimumSavingsNotMet;
82
+ const protocol = aggregate.protocolIntegrity;
83
+ return eventCount === value.observedEvents
84
+ && savingsDecisions <= eventCount
85
+ && aggregate.deterministicCompactionProviderCalls === 0
86
+ && protocol.intact + protocol.failed === protocol.checks
87
+ && (protocol.failed > 0 || protocol.orphanCalls + protocol.orphanResults === 0);
88
+ }
89
+
90
+ function validEditFreshnessRuntime(value) {
91
+ if (!validRuntimeSection(value, "context-telemetry")) return false;
92
+ if (value.coverageStatus !== "complete") return true;
93
+ const aggregate = value.aggregate;
94
+ const fields = ["snapshotObservations", "readSnapshots", "mutationSnapshots", "staleDetections",
95
+ "enforcedStaleDetections", "staleBlocks", "rereadRecoveries", "mutationRecoveries",
96
+ "incompleteRecoveries", "definition"];
97
+ if (!exactKeys(aggregate, fields)
98
+ || !fields.slice(0, -1).every((field) => boundedInteger(aggregate[field]))
99
+ || aggregate.definition !== "edit-freshness-runtime-receipt-v1") return false;
100
+ return aggregate.snapshotObservations + aggregate.staleDetections === value.observedEvents
101
+ && aggregate.readSnapshots + aggregate.mutationSnapshots === aggregate.snapshotObservations
102
+ && aggregate.enforcedStaleDetections <= aggregate.staleDetections
103
+ && aggregate.staleBlocks === aggregate.enforcedStaleDetections
104
+ && aggregate.rereadRecoveries === aggregate.enforcedStaleDetections
105
+ && aggregate.mutationRecoveries <= aggregate.rereadRecoveries
106
+ && aggregate.incompleteRecoveries === 0;
107
+ }
108
+
109
+ function validDelegatedRuntimeSection(value) {
110
+ return validRuntimeSection(value, "provider-free-runtime-conformance")
111
+ && value.coverageStatus === "not-observed";
112
+ }
113
+
114
+ function validRuntimeCausalAggregate(value) {
115
+ return exactKeys(value, ["adaptiveContext", "operationLifecycle", "boundedEmission", "editFreshness"])
116
+ && validAdaptiveContextRuntime(value.adaptiveContext)
117
+ && validDelegatedRuntimeSection(value.operationLifecycle)
118
+ && validDelegatedRuntimeSection(value.boundedEmission)
119
+ && validEditFreshnessRuntime(value.editFreshness);
120
+ }
121
+
122
+ function validCausalAggregates(value, criterionExpected, schemaVersion) {
123
+ const expectedKeys = ["packCounts", "estimatedTokens", "selectedItemEstimatedTokens", "selectedItemCounts", "criterionInitialPack", "directFallbackRereads", "compaction", "managedPrefix"];
124
+ if (schemaVersion >= 2) expectedKeys.splice(6, 0, "editRecoveryContext");
125
+ if (schemaVersion >= 3) expectedKeys.splice(7, 0, "runtimeCausal");
126
+ if (!exactKeys(value, expectedKeys)
127
+ || !validCausalTriplet(value.packCounts)
128
+ || !validCausalTriplet(value.estimatedTokens)
129
+ || !validCausalTriplet(value.selectedItemEstimatedTokens)
130
+ || !validCausalTriplet(value.selectedItemCounts)
131
+ || !exactKeys(value.directFallbackRereads, ["successfulCalls", "shellToolCallsObserved", "definition"])
132
+ || !boundedInteger(value.directFallbackRereads.successfulCalls)
133
+ || !boundedInteger(value.directFallbackRereads.shellToolCallsObserved)
134
+ || value.directFallbackRereads.definition !== "successful-direct-path-tool-call-v1"
135
+ || !exactKeys(value.compaction, ["eventsObserved", "state"])
136
+ || !boundedInteger(value.compaction.eventsObserved)
137
+ || !["observed", "not-observed"].includes(value.compaction.state)
138
+ || (value.compaction.eventsObserved > 0) !== (value.compaction.state === "observed")
139
+ || !exactKeys(value.managedPrefix, ["promptsObserved", "compactedPrompts", "state"])
140
+ || !boundedInteger(value.managedPrefix.promptsObserved) || value.managedPrefix.promptsObserved === 0
141
+ || !boundedInteger(value.managedPrefix.compactedPrompts)
142
+ || value.managedPrefix.compactedPrompts > value.managedPrefix.promptsObserved
143
+ || !["compacted", "uncompacted", "mixed"].includes(value.managedPrefix.state)
144
+ || (schemaVersion >= 2 && !validEditRecoveryAggregate(value.editRecoveryContext))
145
+ || (schemaVersion >= 3 && !validRuntimeCausalAggregate(value.runtimeCausal))) return false;
146
+ const compacted = value.managedPrefix.compactedPrompts;
147
+ const prompts = value.managedPrefix.promptsObserved;
148
+ const expectedState = compacted === 0 ? "uncompacted" : compacted === prompts ? "compacted" : "mixed";
149
+ return value.managedPrefix.state === expectedState
150
+ && validCriterionAggregate(value.criterionInitialPack, value)
151
+ && (criterionExpected !== true || value.criterionInitialPack.attempts > 0);
152
+ }
153
+
154
+ export function validBenchmarkCausalContextReceipt(receipt, surface) {
155
+ if (!exactKeys(receipt, ["schemaVersion", "evidenceSource", "applicability", "available", "coverage", "aggregates"])
156
+ || ![1, 2, 3].includes(receipt.schemaVersion) || typeof receipt.available !== "boolean"
157
+ || !exactKeys(receipt.coverage, ["status", "telemetryTruncated", "telemetryIntegrityFailures", "recoverableTailBytes", "criterionExpected", "sessionEventsObserved", "observedLanes", "requiredLanes", "missingLanes"])) return false;
158
+ const coverage = receipt.coverage;
159
+ if (surface !== "piagent") return receipt.schemaVersion === 1 && receipt.evidenceSource === "not-applicable"
160
+ && receipt.applicability === "not-applicable" && receipt.available === false && receipt.aggregates === null
161
+ && coverage.status === "not-applicable" && coverage.telemetryTruncated === false
162
+ && coverage.telemetryIntegrityFailures === 0 && coverage.recoverableTailBytes === 0
163
+ && coverage.criterionExpected === false && coverage.sessionEventsObserved === 0
164
+ && coverage.observedLanes === 0 && coverage.requiredLanes === 0
165
+ && Array.isArray(coverage.missingLanes) && coverage.missingLanes.length === 0;
166
+ const coverageLanes = receipt.schemaVersion === 3 ? causalCoverageLanesV3
167
+ : receipt.schemaVersion === 2 ? causalCoverageLanesV2 : causalCoverageLanesV1;
168
+ const evidenceSource = `context-telemetry-closed-aggregate-v${receipt.schemaVersion}`;
169
+ if (receipt.evidenceSource !== evidenceSource || receipt.applicability !== "piagent"
170
+ || typeof coverage.telemetryTruncated !== "boolean" || typeof coverage.criterionExpected !== "boolean"
171
+ || ![coverage.telemetryIntegrityFailures, coverage.recoverableTailBytes, coverage.sessionEventsObserved,
172
+ coverage.observedLanes, coverage.requiredLanes].every(boundedInteger)
173
+ || coverage.requiredLanes !== coverageLanes.size || !Array.isArray(coverage.missingLanes)
174
+ || new Set(coverage.missingLanes).size !== coverage.missingLanes.length
175
+ || coverage.missingLanes.some((lane) => !coverageLanes.has(lane))
176
+ || coverage.observedLanes + coverage.missingLanes.length !== coverage.requiredLanes) return false;
177
+ if (receipt.available) return coverage.status === "complete" && coverage.missingLanes.length === 0
178
+ && coverage.telemetryTruncated === false && coverage.telemetryIntegrityFailures === 0
179
+ && coverage.recoverableTailBytes === 0 && coverage.sessionEventsObserved > 0
180
+ && validCausalAggregates(receipt.aggregates, coverage.criterionExpected, receipt.schemaVersion);
181
+ return ["partial", "unavailable"].includes(coverage.status)
182
+ && coverage.missingLanes.length > 0 && receipt.aggregates === null;
183
+ }
184
+
185
+ export function summarizeBenchmarkCausalContextEvidence(runs, { required = false } = {}) {
186
+ const piagentRuns = Array.isArray(runs) ? runs.filter((run) => run?.surface === "piagent") : [];
187
+ const complete = piagentRuns.filter((run) => validBenchmarkCausalContextReceipt(run.causalContextReceipt, run.surface)
188
+ && run.causalContextReceipt.available === true
189
+ && run.causalContextReceipt.coverage.status === "complete"
190
+ && run.causalContextReceipt.aggregates);
191
+ const fullyCovered = piagentRuns.length > 0 && complete.length === piagentRuns.length;
192
+ const recoveryComplete = complete.filter((run) => run.causalContextReceipt.schemaVersion >= 2);
193
+ const recoveryFullyCovered = fullyCovered && recoveryComplete.length === piagentRuns.length;
194
+ const sum = (select) => complete.reduce((total, run) => total + select(run.causalContextReceipt.aggregates), 0);
195
+ const sumRecovery = (select) => recoveryComplete.reduce((total, run) => total + select(run.causalContextReceipt.aggregates.editRecoveryContext), 0);
196
+ const triplet = (field) => Object.fromEntries(["offered", "delivered", "injected"]
197
+ .map((lane) => [lane, sum((value) => value[field][lane])]));
198
+ const criterion = fullyCovered ? complete.reduce((total, run) => {
199
+ const value = run.causalContextReceipt.aggregates.criterionInitialPack;
200
+ for (const field of ["attempts", "selectedAttempts", "candidates", "selectedItems", "estimatedTokens", "offered", "delivered", "injected"]) {
201
+ total[field] += value[field];
202
+ }
203
+ for (const field of Object.keys(total.zeroSelectionReasonCounts)) {
204
+ total.zeroSelectionReasonCounts[field] += value.zeroSelectionReasonCounts[field];
205
+ }
206
+ return total;
207
+ }, {
208
+ attempts: 0, selectedAttempts: 0, candidates: 0, selectedItems: 0, estimatedTokens: 0,
209
+ zeroSelectionReasonCounts: { autoContextDisabled: 0, criterionGraphUnavailable: 0, noCandidates: 0, noReadableSelection: 0 },
210
+ offered: 0, delivered: 0, injected: 0
211
+ }) : null;
212
+ return {
213
+ required,
214
+ runs: piagentRuns.length,
215
+ availableRuns: complete.length,
216
+ unavailableRuns: piagentRuns.length - complete.length,
217
+ coverageStatus: fullyCovered ? "complete" : complete.length > 0 ? "partial" : "unavailable",
218
+ requiredSchemaVersion: 2,
219
+ currentAvailableRuns: recoveryComplete.length,
220
+ currentUnavailableRuns: piagentRuns.length - recoveryComplete.length,
221
+ currentCoverageStatus: recoveryFullyCovered ? "complete" : recoveryComplete.length > 0 ? "partial" : "unavailable",
222
+ aggregates: fullyCovered ? {
223
+ packCounts: triplet("packCounts"),
224
+ estimatedTokens: triplet("estimatedTokens"),
225
+ selectedItemEstimatedTokens: triplet("selectedItemEstimatedTokens"),
226
+ selectedItemCounts: triplet("selectedItemCounts"),
227
+ criterionInitialPack: criterion,
228
+ directFallbackRereads: {
229
+ successfulCalls: sum((value) => value.directFallbackRereads.successfulCalls),
230
+ shellToolCallsObserved: sum((value) => value.directFallbackRereads.shellToolCallsObserved),
231
+ definition: "successful-direct-path-tool-call-v1"
232
+ },
233
+ editRecoveryContext: {
234
+ count: recoveryFullyCovered ? sumRecovery((value) => value.count) : null,
235
+ failuresObserved: recoveryFullyCovered ? sumRecovery((value) => value.failuresObserved) : null,
236
+ suppressedFailures: recoveryFullyCovered ? sumRecovery((value) => value.suppressedFailures) : null,
237
+ injectedChars: recoveryFullyCovered ? sumRecovery((value) => value.injectedChars) : null,
238
+ injectedEstimatedTokens: recoveryFullyCovered ? sumRecovery((value) => value.injectedEstimatedTokens) : null,
239
+ evidenceCoverage: {
240
+ status: recoveryFullyCovered ? "complete" : recoveryComplete.length > 0 ? "partial" : "unavailable",
241
+ runs: piagentRuns.length,
242
+ comparableRuns: recoveryComplete.length,
243
+ rate: piagentRuns.length > 0 ? recoveryComplete.length / piagentRuns.length : 0
244
+ },
245
+ definition: "matched-edit-recovery-context-receipt-v1"
246
+ },
247
+ compactionEventsObserved: sum((value) => value.compaction.eventsObserved),
248
+ managedPrefixPromptsObserved: sum((value) => value.managedPrefix.promptsObserved),
249
+ managedPrefixCompactedPrompts: sum((value) => value.managedPrefix.compactedPrompts)
250
+ } : null
251
+ };
252
+ }
2
253
 
3
254
  export function completedBenchmarkRecord(record) {
4
255
  const nonnegative = (value) => Number.isFinite(value) && value >= 0;
@@ -17,6 +268,7 @@ export function completedBenchmarkRecord(record) {
17
268
  && typeof record.profile === "string"
18
269
  && typeof record.lifecycle === "string"
19
270
  && benchmarkSurfaces.has(record.surface)
271
+ && validBenchmarkCausalContextReceipt(record.causalContextReceipt, record.surface)
20
272
  && Number.isInteger(record.repeat) && record.repeat > 0
21
273
  && Number.isInteger(record.infrastructureAttempt) && record.infrastructureAttempt > 0
22
274
  && Number.isInteger(record.infrastructureAttempts) && record.infrastructureAttempts > 0
@@ -17,6 +17,10 @@ function displayPercent(value) {
17
17
  return Number.isFinite(value) ? `${display(value)}%` : "n/a";
18
18
  }
19
19
 
20
+ function displayRatioPercent(value) {
21
+ return Number.isFinite(value) ? displayPercent(value * 100) : "n/a";
22
+ }
23
+
20
24
  function topToolSummary(toolNames, limit = 8) {
21
25
  const entries = Object.entries(toolNames ?? {}).slice(0, limit);
22
26
  return entries.length ? entries.map(([name, count]) => `${name}:${count}`).join(", ") : "none";
@@ -38,6 +42,16 @@ function pairedOutcomeSummary(value, candidateLabel, baselineLabel) {
38
42
  return `both ${value.bothPass} | ${candidateLabel}-only ${value.candidateOnlyPass} | ${baselineLabel}-only ${value.baselineOnlyPass} | neither ${value.bothFail}`;
39
43
  }
40
44
 
45
+ function timingSummary(value, surface) {
46
+ const summary = value?.surfaces?.[surface];
47
+ if (!summary) return "unavailable";
48
+ const phase = (name) => {
49
+ const item = summary.phases?.[name];
50
+ return `${display(item?.medianSeconds, 3)}s (${item?.availableRuns ?? 0}/${summary.runs})`;
51
+ };
52
+ return `startup ${phase("processStartup")} | model-turn wait ${phase("modelTurnWait")} | tool ${phase("toolExecution")} | other ${phase("other")} | complete ${summary.completeDiagnostics}/${summary.runs}`;
53
+ }
54
+
41
55
  function pairedUsageBandLines(bands) {
42
56
  return Object.entries(bands ?? {}).map(([name, band]) => (
43
57
  ` ${name}`.padEnd(30)
@@ -79,8 +93,10 @@ export function renderBenchmarkText(report) {
79
93
  + `${display(usage.medianInputTokens, 0)}`.padEnd(11)
80
94
  + `${display(usage.medianOutputTokens, 0)}`.padEnd(11)
81
95
  + `${display(usage.medianCacheReadTokens, 0)}`.padEnd(12)
96
+ + `${display(usage.medianCacheWriteTokens, 0)}`.padEnd(13)
82
97
  + `${display(usage.medianReasoningTokens, 0)}`.padEnd(11)
83
98
  + `${display(usage.medianFreshTokens, 0)}`.padEnd(11)
99
+ + `${display(usage.medianTotalTokens, 0)}`.padEnd(11)
84
100
  + `${display(usage.medianToolCalls, 0)}`.padEnd(7)
85
101
  + display(usage.medianCost, 6);
86
102
  };
@@ -89,6 +105,21 @@ export function renderBenchmarkText(report) {
89
105
  ));
90
106
  const usageBandLines = pairedUsageBandLines(report.comparison.pairedUsageBands?.categories);
91
107
  const confidence = report.comparison.freshTokenRatioConfidence95;
108
+ const primaryConfidence = report.comparison.primaryEfficiencyRatioConfidence95;
109
+ const fixedWorkloadConfidence = report.comparison.fixedWorkloadFamilyFreshTokenRatioConfidence95;
110
+ const failureAwareFamilyConfidence = report.comparison.failureAwareFamilyFreshTokenRatioConfidence95;
111
+ const durationConfidence = report.comparison.durationRatioConfidence95;
112
+ const normalizedCost = report.comparison.normalizedCost;
113
+ const normalizedCostConfidence = normalizedCost?.ratioConfidence95;
114
+ const codexRelative = report.comparison.codexRelativeEfficiency;
115
+ const codexRelativeTrafficConfidence = codexRelative?.totalTokenTraffic?.ratioConfidence95;
116
+ const codexRelativeCostConfidence = codexRelative?.apiEquivalentCost?.ratioConfidence95;
117
+ const causalContext = report.comparison.causalContextEvidence;
118
+ const causalAggregates = causalContext?.aggregates;
119
+ const hostReadiness = report.environment.hostReadinessHistory;
120
+ const suiteFailureReasons = (report.comparison.suiteGate?.failureReasons ?? [])
121
+ .map((failure) => `${failure.id}: ${failure.message}`)
122
+ .join("; ");
92
123
  const lines = [
93
124
  `Piagent Benchmark — ${report.suite.title}`,
94
125
  `Run: ${report.runId} | ${report.runCount} sessions | ${report.repeats} repeat(s)`,
@@ -98,6 +129,10 @@ export function renderBenchmarkText(report) {
98
129
  `Comparison: ${candidateLabel} vs ${baselineLabel}`,
99
130
  `Comparison purpose: ${report.comparison.purpose ?? "unspecified"}`,
100
131
  `Claim tier: ${report.comparison.claimEligibility?.achievedTier ?? "unavailable"} (declared ${report.comparison.claimEligibility?.declaredTier ?? "unavailable"})`,
132
+ `Token claim scope: ${report.comparison.claimEligibility?.tokenClaimScope ?? "unavailable"}`,
133
+ ...(report.comparison.tokenClaimUnavailableReason
134
+ ? [`Token claim restriction: ${report.comparison.tokenClaimUnavailableReason}`]
135
+ : []),
101
136
  `Suite digest: ${report.environment.suiteDigest ?? "unknown"} | Source: ${report.environment.source?.kind ?? "unknown"}${report.environment.source?.dirty === true ? " (dirty)" : ""}`,
102
137
  `Infrastructure: ${report.infrastructure?.attempts ?? report.runCount} attempts | ${report.infrastructure?.retries ?? 0} retries across ${report.infrastructure?.retriedRuns ?? 0} measured runs`,
103
138
  "",
@@ -106,41 +141,103 @@ export function renderBenchmarkText(report) {
106
141
  scoreLine(candidateLabel, candidate),
107
142
  "",
108
143
  "Median usage across all measured runs",
109
- "Surface Input Output Cache read Reasoning Fresh Tools Cost",
144
+ "Surface Input Output Cache read Cache write Reasoning Fresh Total Tools Reported cost",
110
145
  usageLine(baselineLabel, baseline),
111
146
  usageLine(candidateLabel, candidate),
112
147
  `${baselineLabel} tools: ${topToolSummary(baseline.usage.toolNames)}`,
113
148
  `${candidateLabel} tools: ${topToolSummary(candidate.usage.toolNames)}`,
149
+ `Observational timing (${baselineLabel}): ${timingSummary(report.timingDiagnostics, baselineSurface)}`,
150
+ `Observational timing (${candidateLabel}): ${timingSummary(report.timingDiagnostics, candidateSurface)}`,
151
+ "Timing authority: privacy-safe monotonic JSONL receipt boundaries only; unavailable boundaries are not estimated and timing never affects duration or a release/spend gate.",
114
152
  `Piagent workflow gaps: ${workflowGapSummary(report.runs.filter((run) => run.surface === "piagent"))}`,
153
+ `Token accounting: input=fresh input; reasoning is included in output; fresh=input+output; total=input+cache read+cache write+output`,
154
+ `Attempt completeness: accepted ${report.tokenAccounting?.acceptedAttempts.exactAttempts ?? 0}/${report.tokenAccounting?.acceptedAttempts.attempts ?? 0} exact | failed ${report.tokenAccounting?.failedAttempts.exactAttempts ?? 0}/${report.tokenAccounting?.failedAttempts.attempts ?? 0} exact | failed fresh ${display(report.tokenAccounting?.failedAttempts.tokens.fresh, 0)}`,
115
155
  "",
116
156
  `Paired successful runs: ${report.comparison.pairedSuccessfulRuns}`,
117
157
  `Paired runs with comparable usage: ${report.comparison.pairedUsageRuns}`,
118
158
  `Independent paired scenario families: ${report.comparison.pairedUsageScenarios ?? 0}`,
119
159
  `Complete paired scenario families: ${report.comparison.pairedCompleteScenarios ?? 0}`,
160
+ `Paired runs with comparable duration: ${report.comparison.pairedDurationRuns ?? 0}`,
161
+ `Complete paired duration families: ${report.comparison.pairedCompleteDurationScenarios ?? 0}`,
120
162
  `Complete paired outcome families: ${report.comparison.pairedOutcomeScenarios ?? 0}`,
121
163
  `Usage estimator: ${report.comparison.usageEstimator}`,
164
+ `Primary efficiency estimand: ${report.comparison.primaryEfficiencyEstimand ?? "unavailable"}`,
165
+ `Primary fresh-token ratio: ${display(report.comparison.primaryEfficiencyRatio, 4)} | 95% CI ${primaryConfidence ? `${display(primaryConfidence.lower, 4)}..${display(primaryConfidence.upper, 4)}` : "n/a"}`,
166
+ `Fixed-workload family ratio: ${display(report.comparison.fixedWorkloadFamilyFreshTokenRatio, 4)} | 95% CI ${fixedWorkloadConfidence ? `${display(fixedWorkloadConfidence.lower, 4)}..${display(fixedWorkloadConfidence.upper, 4)}` : "n/a"} | complete ${report.comparison.fixedWorkloadFamilyCoverage?.usableScenarioFamilies ?? 0}/${report.comparison.fixedWorkloadFamilyCoverage?.expectedScenarioFamilies ?? 0}`,
167
+ `Fixed-workload aggregate ratio (descriptive): ${display(report.comparison.fixedWorkloadAggregateFreshTokenRatio, 4)}`,
122
168
  `Fresh-token pair wins: ${candidateLabel} ${report.comparison.pairedFreshTokenWins[candidateKey]} | ${baselineLabel} ${report.comparison.pairedFreshTokenWins[baselineKey]} | ties ${report.comparison.pairedFreshTokenWins.ties}`,
123
169
  `Median paired fresh-token delta: ${display(report.comparison.medianPairedFreshTokenDelta, 0)} tok (negative favors ${candidateLabel})`,
124
- `Fresh-token ratio 95% CI: ${confidence ? `${display(confidence.lower, 4)}..${display(confidence.upper, 4)}` : "n/a"}`,
170
+ `Successful-pair complete-family ratio: ${display(report.comparison.freshTokenRatio, 4)} | 95% CI ${confidence ? `${display(confidence.lower, 4)}..${display(confidence.upper, 4)}` : "n/a"}`,
171
+ `All-successful-pairs descriptive ratio: ${display(report.comparison.allSuccessfulPairsFreshTokenRatio, 4)}`,
172
+ `Normalized API-equivalent text-token cost: ${display(normalizedCost?.ratio, 4)} | 95% CI ${normalizedCostConfidence ? `${display(normalizedCostConfidence.lower, 4)}..${display(normalizedCostConfidence.upper, 4)}` : "n/a"} | delta ${displayPercent(normalizedCost?.deltaPercent)}`,
173
+ `Normalized-cost source: ${normalizedCost?.pricingSnapshot?.id ?? "n/a"} | billed/provider-reported cost: no | exact/applicable pairs ${normalizedCost?.comparablePairs ?? 0}/${normalizedCost?.expectedPairs ?? 0}`,
174
+ `Codex-relative total token traffic: ${display(codexRelative?.totalTokenTraffic?.ratio, 4)} | 95% CI ${codexRelativeTrafficConfidence ? `${display(codexRelativeTrafficConfidence.lower, 4)}..${display(codexRelativeTrafficConfidence.upper, 4)}` : "n/a"} | required upper <= ${display(codexRelative?.policy?.maximumTotalTokenTrafficRatioUpper95, 2)}`,
175
+ `Codex-relative API-equivalent cost: ${display(codexRelative?.apiEquivalentCost?.ratio, 4)} | 95% CI ${codexRelativeCostConfidence ? `${display(codexRelativeCostConfidence.lower, 4)}..${display(codexRelativeCostConfidence.upper, 4)}` : "n/a"} | required upper <= ${display(codexRelative?.policy?.maximumApiEquivalentCostRatioUpper95, 2)}`,
176
+ `Codex-relative subagent budget: ${codexRelative?.subagents?.sessions ?? "n/a"} sessions | ${displayRatioPercent(codexRelative?.subagents?.trafficShare)} traffic share | max ${codexRelative?.policy?.maximumSubagentSessionsPerAttempt ?? "n/a"}/attempt and ${displayRatioPercent(codexRelative?.policy?.maximumSubagentTrafficShare)}`,
177
+ `Codex-relative efficiency gate: ${report.comparison.codexRelativeEfficiencyGate === null ? "n/a" : report.comparison.codexRelativeEfficiencyGate ? "pass" : `fail (${(codexRelative?.failures ?? []).join(", ") || "missing evidence"})`} | subscription-billed claim: no`,
178
+ `Duration pair wins: ${candidateLabel} ${report.comparison.pairedDurationWins?.[candidateKey] ?? 0} | ${baselineLabel} ${report.comparison.pairedDurationWins?.[baselineKey] ?? 0} | ties ${report.comparison.pairedDurationWins?.ties ?? 0}`,
179
+ `Median paired duration delta: ${display(report.comparison.medianPairedDurationDeltaSeconds, 2)}s (negative favors ${candidateLabel})`,
180
+ `Duration ratio 95% CI: ${durationConfidence ? `${display(durationConfidence.lower, 4)}..${display(durationConfidence.upper, 4)}` : "n/a"}`,
125
181
  `Failure-aware fresh tokens/resolved outcome: ${candidateLabel} ${display(report.comparison.freshTokensPerResolvedOutcome?.[candidateKey], 2)} | ${baselineLabel} ${display(report.comparison.freshTokensPerResolvedOutcome?.[baselineKey], 2)} | ratio ${display(report.comparison.failureAwareFreshTokenRatio, 4)}`,
182
+ `Failure-aware family ratio: ${display(report.comparison.failureAwareFamilyFreshTokenRatio, 4)} | 95% CI ${failureAwareFamilyConfidence ? `${display(failureAwareFamilyConfidence.lower, 4)}..${display(failureAwareFamilyConfidence.upper, 4)}` : "n/a"} | complete ${report.comparison.failureAwareFamilyCoverage?.usableScenarioFamilies ?? 0}/${report.comparison.failureAwareFamilyCoverage?.expectedScenarioFamilies ?? 0}`,
126
183
  `Paired resolved outcomes: ${pairedOutcomeSummary(report.comparison.pairedOutcomes?.resolved, candidateLabel, baselineLabel)}`,
127
184
  `Paired quality outcomes: ${pairedOutcomeSummary(report.comparison.pairedOutcomes?.quality, candidateLabel, baselineLabel)}`,
128
185
  `Paired safety outcomes: ${pairedOutcomeSummary(report.comparison.pairedOutcomes?.safety, candidateLabel, baselineLabel)}`,
129
186
  `Comparison protocol gate: ${report.comparison.comparisonProtocolGate?.passed ? "pass" : `fail (${(report.comparison.comparisonProtocolGate?.failedChecks ?? []).join(", ") || "missing evidence"})`}`,
187
+ `Provider-wire surface gate: ${report.comparison.providerWireSurfaceGate === null ? "n/a" : report.comparison.providerWireSurfaceGate ? "pass" : "fail"}`,
188
+ `Provider-wire evidence: ${report.comparison.providerWireEvidence?.verifiedRuns ?? 0}/${report.comparison.providerWireEvidence?.runs ?? 0} runs | ${report.comparison.providerWireEvidence?.groups?.length ?? 0} repeat groups | ${(report.comparison.providerWireEvidence?.driftGroups ?? []).length} base-prefix drift groups | failures ${topToolSummary(report.comparison.providerWireEvidence?.failureCounts)}`,
189
+ `Deferred tool surfaces: ${(report.comparison.providerWireEvidence?.groups ?? []).filter((group) => group.deferredToolSurfaceHashCount > 1).length} varying groups (reported separately; not base-prefix drift)`,
190
+ `Causal context receipt gate: ${report.comparison.causalContextEvidenceGate === null ? "n/a" : report.comparison.causalContextEvidenceGate ? "pass" : "fail"} | current v${causalContext?.requiredSchemaVersion ?? "?"} complete ${causalContext?.currentAvailableRuns ?? 0}/${causalContext?.runs ?? 0} Piagent runs`,
191
+ `Causal pack totals: offered ${causalAggregates?.packCounts.offered ?? "n/a"} | delivered ${causalAggregates?.packCounts.delivered ?? "n/a"} | injected ${causalAggregates?.packCounts.injected ?? "n/a"} | injected estimated tokens ${causalAggregates?.estimatedTokens.injected ?? "n/a"} | selected items ${causalAggregates?.selectedItemCounts.injected ?? "n/a"}`,
192
+ `Causal criterion/continuity: attempts ${causalAggregates?.criterionInitialPack.attempts ?? "n/a"} | selected ${causalAggregates?.criterionInitialPack.selectedAttempts ?? "n/a"} | successful direct fallback rereads ${causalAggregates?.directFallbackRereads.successfulCalls ?? "n/a"} | shell calls after injection ${causalAggregates?.directFallbackRereads.shellToolCallsObserved ?? "n/a"} | compactions ${causalAggregates?.compactionEventsObserved ?? "n/a"} | managed-prefix compacted ${causalAggregates?.managedPrefixCompactedPrompts ?? "n/a"}/${causalAggregates?.managedPrefixPromptsObserved ?? "n/a"}`,
193
+ `Causal edit recovery: contexts ${causalAggregates?.editRecoveryContext?.count ?? "n/a"}/${causalAggregates?.editRecoveryContext?.failuresObserved ?? "n/a"} failures | suppressed ${causalAggregates?.editRecoveryContext?.suppressedFailures ?? "n/a"} | injected chars ${causalAggregates?.editRecoveryContext?.injectedChars ?? "n/a"} | injected estimated tokens ${causalAggregates?.editRecoveryContext?.injectedEstimatedTokens ?? "n/a"} | evidence ${causalAggregates?.editRecoveryContext?.evidenceCoverage?.status ?? "unavailable"} (${causalAggregates?.editRecoveryContext?.evidenceCoverage?.comparableRuns ?? 0}/${causalAggregates?.editRecoveryContext?.evidenceCoverage?.runs ?? 0} runs)`,
194
+ `Release-claim configuration gate: ${report.comparison.releaseClaimConfigurationGate === false ? "fail" : report.comparison.releaseClaimConfigurationGate === true ? "pass" : "n/a"}`,
195
+ `Codex baseline gate: ${report.comparison.codexBaselineGate === null ? "n/a" : report.comparison.codexBaselineGate ? "pass" : "fail"}`,
196
+ `Clean release source gate: ${report.comparison.cleanReleaseSourceGate === null ? "n/a" : report.comparison.cleanReleaseSourceGate ? "pass" : "fail"}`,
197
+ `Host-readiness history gate: ${report.comparison.hostReadinessGate === null ? "n/a" : report.comparison.hostReadinessGate ? "pass" : "fail"} | valid receipts ${hostReadiness?.validReceiptCount ?? 0}/${hostReadiness?.receiptCount ?? 0} | coverage ${hostReadiness?.windowCoverage ?? "n/a"}`,
198
+ `Full-suite gate: ${report.comparison.fullSuiteGate === null ? "n/a" : report.comparison.fullSuiteGate ? "pass" : "fail"}`,
130
199
  `Paired outcome evidence gate: ${report.comparison.outcomeEvidenceGate ? "pass" : "fail"}`,
131
- `Efficiency evidence gate: ${report.comparison.efficiencyEvidenceGate ? "pass" : "fail"}`,
132
- `Efficiency category coverage: ${report.comparison.efficiencyBandCoverageGate ? "pass" : `fail (${(report.comparison.efficiencyCategoryCoverage?.missing ?? []).join(", ") || "missing evidence"})`}`,
133
- `Failure-aware efficiency gate: ${report.comparison.failureAwareEfficiencyGate ? "pass" : "fail"}`,
200
+ `Successful-pair evidence (${report.comparison.successfulPairEfficiencyRole ?? "blocking"}): ${report.comparison.efficiencyEvidenceGate ? "complete" : "incomplete"}`,
201
+ `Successful-pair category coverage (${report.comparison.successfulPairEfficiencyRole ?? "blocking"}): ${report.comparison.efficiencyBandCoverageGate ? "complete" : `incomplete (${(report.comparison.efficiencyCategoryCoverage?.missing ?? []).join(", ") || "missing evidence"})`}`,
202
+ `Failure-aware efficiency (${report.comparison.failureAwareEfficiencyRole ?? "blocking"}): ${report.comparison.failureAwareEfficiencyGate ? "within threshold" : "unavailable or above threshold"}`,
203
+ `Primary efficiency evidence gate: ${report.comparison.primaryEfficiencyEvidenceGate ? "pass" : "fail"}`,
204
+ `Primary efficiency category coverage: ${report.comparison.primaryEfficiencyBandCoverageGate ? "pass" : `fail (${(report.comparison.primaryEfficiencyCategoryCoverage?.missing ?? []).join(", ") || "missing evidence"})`}`,
205
+ `Primary efficiency confidence gate: ${report.comparison.primaryEfficiencyConfidenceGate === null ? "n/a" : report.comparison.primaryEfficiencyConfidenceGate ? "pass" : "fail"}`,
206
+ `Primary efficiency gate: ${report.comparison.primaryEfficiencyGate === null ? "n/a" : report.comparison.primaryEfficiencyGate ? "pass" : "fail"}`,
134
207
  `Paired candidate-regression gate: ${report.comparison.pairedRegressionGate ? "pass" : "fail"}`,
135
208
  `Repeat-count gate: ${report.comparison.repeatGate === null ? "n/a" : report.comparison.repeatGate ? "pass" : "fail"}`,
136
209
  `Efficiency confidence gate: ${report.comparison.efficiencyConfidenceGate === null ? "n/a" : report.comparison.efficiencyConfidenceGate ? "pass" : "fail"}`,
210
+ `Fresh-token band gate: ${report.comparison.freshTokenBandGate === null ? "n/a" : report.comparison.freshTokenBandGate ? "pass" : `fail (${(report.comparison.freshTokenBandFailures ?? []).map((item) => `${item.dimension}:${item.name}=${item.observedBound ?? "n/a"}`).join(", ")})`}`,
211
+ `Fresh-token family guardrail: ${report.comparison.freshTokenFamilyGate === null ? "n/a" : report.comparison.freshTokenFamilyGate ? "pass" : `fail (${(report.comparison.freshTokenFamilyFailures ?? []).map((item) => `${item.scenarioId}=${item.ratio}`).join(", ")})`}`,
212
+ `Performance evidence gate: ${report.comparison.performanceEvidenceGate === null ? "n/a" : report.comparison.performanceEvidenceGate ? "pass" : "fail"}`,
213
+ `Performance point-estimate gate (ratio <= 1.0): ${report.comparison.performancePointEstimateGate === null ? "n/a" : report.comparison.performancePointEstimateGate ? "pass" : "fail"}`,
214
+ `Performance confidence gate: ${report.comparison.performanceConfidenceGate === null ? "n/a" : report.comparison.performanceConfidenceGate ? "pass" : "fail"}`,
215
+ `Duration band gate: ${report.comparison.durationBandGate === null ? "n/a" : report.comparison.durationBandGate ? "pass" : `fail (${(report.comparison.durationBandFailures ?? []).map((item) => `${item.dimension}:${item.name}=${item.ratio ?? "n/a"}`).join(", ")})`}`,
216
+ `Duration family guardrail: ${report.comparison.durationFamilyGate === null ? "n/a" : report.comparison.durationFamilyGate ? "pass" : `fail (${(report.comparison.durationFamilyFailures ?? []).map((item) => `${item.scenarioId}=${item.ratio ?? "n/a"}`).join(", ")})`}`,
217
+ `Infrastructure retry gate: ${report.comparison.infrastructureRetryGate === null ? "n/a" : report.comparison.infrastructureRetryGate ? "pass" : "fail"}`,
218
+ `Infrastructure failure-ledger gate: ${report.comparison.infrastructureFailureLedgerGate === null ? "n/a" : report.comparison.infrastructureFailureLedgerGate ? "pass" : `fail (${report.comparison.infrastructureFailureLedgerIssues?.length ?? 0} runs)`}`,
219
+ `Unknown infrastructure usage gate: ${report.comparison.unknownInfrastructureUsageGate === null ? "n/a" : report.comparison.unknownInfrastructureUsageGate ? "pass" : "fail"}`,
220
+ `Accepted usage completeness gate: ${report.comparison.acceptedUsageCompletenessGate === null ? "n/a" : report.comparison.acceptedUsageCompletenessGate ? "pass" : "fail"}`,
221
+ `Failed-attempt usage completeness gate: ${report.comparison.failedUsageCompletenessGate === null ? "n/a" : report.comparison.failedUsageCompletenessGate ? "pass" : "fail"}`,
222
+ `All-attempt usage completeness gate: ${report.comparison.allAttemptUsageCompletenessGate === null ? "n/a" : report.comparison.allAttemptUsageCompletenessGate ? "pass" : "fail"}`,
223
+ `Normalized-cost configuration gate: ${report.comparison.normalizedCostClaimConfigurationGate === null ? "n/a" : report.comparison.normalizedCostClaimConfigurationGate ? "pass" : "fail"}`,
224
+ `Normalized-cost pricing applicability gate: ${report.comparison.normalizedCostPricingApplicabilityGate === null ? "n/a" : report.comparison.normalizedCostPricingApplicabilityGate ? "pass" : "fail"}`,
225
+ `Normalized-cost confidence gate: ${report.comparison.normalizedCostConfidenceGate === null ? "n/a" : report.comparison.normalizedCostConfidenceGate ? "pass" : "fail"}`,
226
+ `Normalized-cost band gate: ${report.comparison.normalizedCostBandGate === null ? "n/a" : report.comparison.normalizedCostBandGate ? "pass" : "fail"}`,
227
+ `Normalized-cost family gate: ${report.comparison.normalizedCostFamilyGate === null ? "n/a" : report.comparison.normalizedCostFamilyGate ? "pass" : "fail"}`,
137
228
  `Quality gate: ${report.comparison.qualityGate ? "pass" : "fail"}`,
138
229
  `Reliability gate: ${report.comparison.reliabilityGate ? "pass" : "fail"}`,
139
- `Fresh-token delta: ${displayPercent(report.comparison.freshTokenDeltaPercent)} (negative favors ${candidateLabel})`,
140
- `Cost delta: ${displayPercent(report.comparison.costDeltaPercent)} (negative favors ${candidateLabel})`,
230
+ `Successful-pair complete-family token delta: ${displayPercent(report.comparison.freshTokenDeltaPercent)} (negative favors ${candidateLabel})`,
231
+ `Primary efficiency token delta: ${displayPercent(report.comparison.primaryEfficiencyDeltaPercent)} (negative favors ${candidateLabel})`,
232
+ `Duration delta: ${displayPercent(report.comparison.durationDeltaPercent)} (negative favors ${candidateLabel})`,
233
+ `Provider-reported cost delta: ${displayPercent(report.comparison.costDeltaPercent)} (negative favors ${candidateLabel}; not normalized cost)`,
141
234
  `Workflow gate: ${report.comparison.workflowGate === null ? "n/a" : report.comparison.workflowGate ? "pass" : "fail"}`,
142
235
  `Category gate: ${report.comparison.categoryGate === null ? "n/a" : report.comparison.categoryGate ? "pass" : "fail"}`,
143
236
  `Every outcome score > floor: ${report.comparison.outcomeScoreGate === null ? "n/a" : report.comparison.outcomeScoreGate ? "pass" : `fail (${(report.comparison.outcomeScoreFailures ?? []).map((item) => `${item.id}=${item.score ?? "n/a"}`).join(", ")})`}`,
237
+ ...(report.comparison.suiteGate ? [
238
+ `Suite gate: ${report.comparison.suiteGate.passed ? "pass" : "fail"}`,
239
+ `Suite gate failures: ${suiteFailureReasons || "none"}`
240
+ ] : []),
144
241
  ...(usageBandLines.length ? ["", "Paired fresh-token ratio by category", "Category Pairs Families Ratio Median delta", ...usageBandLines] : []),
145
242
  ...(categoryLines.length ? ["", `${candidateLabel} category bands`, "Category Resolved Score", ...categoryLines] : []),
146
243
  `Verdict: ${report.verdict.status}`,
@@ -163,21 +260,42 @@ export function renderBenchmarkHtml(report) {
163
260
  const surfaceEntries = [[baselineSurface, report.surfaces[baselineKey]], [candidateSurface, report.surfaces[candidateKey]]];
164
261
  const baselineLabel = surfaceLabel(baselineSurface);
165
262
  const candidateLabel = surfaceLabel(candidateSurface);
166
- const rows = report.runs.map((run) => `<tr><td>${htmlEscape(run.scenarioId)}</td><td>${htmlEscape(run.category ?? "unspecified")}</td><td>${htmlEscape(run.difficulty ?? "unspecified")}</td><td>${htmlEscape(run.profile ?? "unspecified")}</td><td>${htmlEscape(run.lifecycle ?? "unspecified")}</td><td>${htmlEscape(run.surface)}</td><td>${run.repeat}</td><td>${run.infrastructureRetries ?? 0}</td><td>${run.resolved ? "PASS" : "FAIL"}</td><td>${run.grade?.passed ? "PASS" : "FAIL"}</td><td>${run.scope?.passed ? "PASS" : "FAIL"}</td><td>${display(run.workflow?.score)}</td><td>${htmlEscape((run.workflow?.checks ?? []).filter((check) => !check.passed).map((check) => check.id).join(", ") || "none")}</td><td>${htmlEscape(run.usage?.model ?? "unknown")}</td><td>${htmlEscape(run.usage?.thinkingLevel ?? "unknown")}</td><td>${display(run.usage?.input, 0)}</td><td>${display(run.usage?.output, 0)}</td><td>${display(run.usage?.cacheRead, 0)}</td><td>${display(run.usage?.reasoning, 0)}</td><td>${display(run.usage?.fresh, 0)}</td><td>${display(run.usage?.toolCalls, 0)}</td><td>${htmlEscape(topToolSummary(run.usage?.toolNames, 5))}</td><td>${display(run.usage?.cost, 6)}</td><td>${display(run.durationSeconds, 1)}</td><td>${htmlEscape(run.failure ?? "")}</td></tr>`).join("");
263
+ const rows = report.runs.map((run) => `<tr><td>${htmlEscape(run.scenarioId)}</td><td>${htmlEscape(run.category ?? "unspecified")}</td><td>${htmlEscape(run.difficulty ?? "unspecified")}</td><td>${htmlEscape(run.profile ?? "unspecified")}</td><td>${htmlEscape(run.lifecycle ?? "unspecified")}</td><td>${htmlEscape(run.surface)}</td><td>${run.repeat}</td><td>${run.infrastructureRetries ?? 0}</td><td>${run.resolved ? "PASS" : "FAIL"}</td><td>${run.grade?.passed ? "PASS" : "FAIL"}</td><td>${run.scope?.passed ? "PASS" : "FAIL"}</td><td>${display(run.workflow?.score)}</td><td>${htmlEscape((run.workflow?.checks ?? []).filter((check) => !check.passed).map((check) => check.id).join(", ") || "none")}</td><td>${htmlEscape(run.usage?.model ?? "unknown")}</td><td>${htmlEscape(run.usage?.thinkingLevel ?? "unknown")}</td><td>${htmlEscape(run.usage?.usageSource ?? "unknown")}</td><td>${display(run.usage?.input, 0)}</td><td>${display(run.usage?.output, 0)}</td><td>${display(run.usage?.cacheRead, 0)}</td><td>${display(run.usage?.cacheWrite, 0)}</td><td>${display(run.usage?.reasoning, 0)}</td><td>${display(run.usage?.fresh, 0)}</td><td>${display(run.usage?.total, 0)}</td><td>${display(run.usage?.toolCalls, 0)}</td><td>${htmlEscape(topToolSummary(run.usage?.toolNames, 5))}</td><td>${display(run.usage?.cost, 6)}</td><td>${display(run.durationSeconds, 1)}</td><td>${htmlEscape(run.failure ?? "")}</td></tr>`).join("");
167
264
  const scoreRows = surfaceEntries.map(([id, surface]) => `<tr><th>${htmlEscape(surfaceLabel(id))}</th><td>${surface.resolved}/${surface.runs}</td><td>${surface.qualityCorrect}/${surface.qualityRuns}</td><td>${surface.scopePassed}/${surface.runs}</td><td>${display(surface.scores.quality)}</td><td>${display(surface.scores.safety)}</td><td>${display(surface.scores.reliability)}</td><td>${display(surface.scores.workflow)}</td><td>${display(surface.scores.efficiency)}</td><td>${display(surface.scores.overall)}</td></tr>`).join("");
168
- const usageRows = surfaceEntries.map(([id, surface]) => { const usage = surface.usage.allMeasuredRuns; return `<tr><th>${htmlEscape(surfaceLabel(id))}</th><td>${display(usage.medianInputTokens, 0)}</td><td>${display(usage.medianOutputTokens, 0)}</td><td>${display(usage.medianCacheReadTokens, 0)}</td><td>${display(usage.medianReasoningTokens, 0)}</td><td>${display(usage.medianFreshTokens, 0)}</td><td>${display(usage.medianToolCalls, 0)}</td><td>${display(usage.medianCost, 6)}</td><td>${display(usage.medianDurationSeconds, 1)}</td><td>${htmlEscape(topToolSummary(surface.usage.toolNames))}</td></tr>`; }).join("");
265
+ const usageRows = surfaceEntries.map(([id, surface]) => { const usage = surface.usage.allMeasuredRuns; return `<tr><th>${htmlEscape(surfaceLabel(id))}</th><td>${display(usage.medianInputTokens, 0)}</td><td>${display(usage.medianOutputTokens, 0)}</td><td>${display(usage.medianCacheReadTokens, 0)}</td><td>${display(usage.medianCacheWriteTokens, 0)}</td><td>${display(usage.medianReasoningTokens, 0)}</td><td>${display(usage.medianFreshTokens, 0)}</td><td>${display(usage.medianTotalTokens, 0)}</td><td>${display(usage.medianToolCalls, 0)}</td><td>${display(usage.medianCost, 6)}</td><td>${display(usage.medianDurationSeconds, 1)}</td><td>${htmlEscape(topToolSummary(surface.usage.toolNames))}</td></tr>`; }).join("");
169
266
  const categoryRows = Object.entries(report.surfaces[candidateKey].bands?.categories ?? {}).map(([name, band]) => `<tr><th>${htmlEscape(name)}</th><td>${band.resolved}/${band.runs}</td><td>${display(band.score)}</td><td>${display(band.correctness)}</td></tr>`).join("");
170
267
  const tokenBandRows = Object.entries(report.comparison.pairedUsageBands?.categories ?? {}).map(([name, band]) => `<tr><th>${htmlEscape(name)}</th><td>${band.pairs}</td><td>${band.scenarioFamilies}</td><td>${display(band.freshTokenRatio, 4)}</td><td>${display(band.medianFreshTokenDelta, 0)}</td><td>${band.candidateWins}/${band.baselineWins}/${band.ties}</td></tr>`).join("");
171
268
  const confidence = report.comparison.freshTokenRatioConfidence95;
269
+ const primaryConfidence = report.comparison.primaryEfficiencyRatioConfidence95;
270
+ const fixedWorkloadConfidence = report.comparison.fixedWorkloadFamilyFreshTokenRatioConfidence95;
271
+ const failureAwareFamilyConfidence = report.comparison.failureAwareFamilyFreshTokenRatioConfidence95;
272
+ const durationConfidence = report.comparison.durationRatioConfidence95;
273
+ const normalizedCost = report.comparison.normalizedCost;
274
+ const normalizedCostConfidence = normalizedCost?.ratioConfidence95;
275
+ const codexRelative = report.comparison.codexRelativeEfficiency;
276
+ const codexRelativeTrafficConfidence = codexRelative?.totalTokenTraffic?.ratioConfidence95;
277
+ const codexRelativeCostConfidence = codexRelative?.apiEquivalentCost?.ratioConfidence95;
278
+ const causalContext = report.comparison.causalContextEvidence;
279
+ const causalAggregates = causalContext?.aggregates;
280
+ const hostReadiness = report.environment.hostReadinessHistory;
281
+ const suiteFailureReasons = (report.comparison.suiteGate?.failureReasons ?? [])
282
+ .map((failure) => `${failure.id}: ${failure.message}`)
283
+ .join("; ");
172
284
  return `<!doctype html>
173
285
  <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Piagent Benchmark ${htmlEscape(report.runId)}</title>
174
286
  <style>body{font:14px system-ui,sans-serif;color:#202124;max-width:1180px;margin:32px auto;padding:0 20px}h1{font-size:24px}h2{font-size:17px;margin-top:28px}.table-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;white-space:nowrap}th,td{border:1px solid #d8dadd;padding:8px;text-align:left}thead th{background:#f4f5f6}.metric{display:inline-block;margin:0 24px 8px 0}.note{color:#5f6368}</style></head><body>
175
287
  <h1>Piagent Benchmark</h1><p>${htmlEscape(report.suite.title)} · ${htmlEscape(report.runId)}</p><p class="note">Platform v${htmlEscape(report.environment.platformVersion ?? "unknown")} · Pi ${htmlEscape(report.environment.piVersion ?? "unknown")}${report.environment.codexVersion ? ` · Codex ${htmlEscape(report.environment.codexVersion)}` : ""} · Node ${htmlEscape(report.environment.nodeVersion ?? "unknown")} · Baseline ${htmlEscape(report.environment.treatmentBaseline ?? "unknown")} · Piagent treatment ${htmlEscape(report.environment.piagentTreatment?.id ?? "unrecorded")} · Suite ${htmlEscape(report.environment.suiteDigest ?? "unknown")}</p>
176
- <p class="metric"><strong>Comparison:</strong> ${htmlEscape(candidateLabel)} vs ${htmlEscape(baselineLabel)}</p><p class="metric"><strong>Purpose:</strong> ${htmlEscape(report.comparison.purpose ?? "unspecified")}</p><p class="metric"><strong>Claim tier:</strong> ${htmlEscape(report.comparison.claimEligibility?.achievedTier ?? "unavailable")}</p><p class="metric"><strong>Verdict:</strong> ${htmlEscape(report.verdict.status)}</p><p class="metric"><strong>Protocol:</strong> ${report.comparison.comparisonProtocolGate?.passed ? "PASS" : `FAIL (${htmlEscape((report.comparison.comparisonProtocolGate?.failedChecks ?? []).join(", ") || "missing evidence")})`}</p><p class="metric"><strong>Infrastructure retries:</strong> ${report.infrastructure?.retries ?? 0}</p><p class="metric"><strong>Paired token-ratio delta:</strong> ${displayPercent(report.comparison.freshTokenDeltaPercent)}</p><p class="metric"><strong>Token ratio 95% CI:</strong> ${confidence ? `${display(confidence.lower, 4)}–${display(confidence.upper, 4)}` : "n/a"}</p><p class="metric"><strong>Failure-aware ratio:</strong> ${display(report.comparison.failureAwareFreshTokenRatio, 4)}</p><p class="metric"><strong>Paired cost-ratio delta:</strong> ${displayPercent(report.comparison.costDeltaPercent)}</p><p class="metric"><strong>Comparable pairs:</strong> ${report.comparison.pairedUsageRuns}</p><p class="metric"><strong>Scenario families:</strong> ${report.comparison.pairedUsageScenarios ?? 0}</p><p class="metric"><strong>Complete families:</strong> ${report.comparison.pairedCompleteScenarios ?? 0}</p><p class="metric"><strong>Outcome families:</strong> ${report.comparison.pairedOutcomeScenarios ?? 0}</p><p class="metric"><strong>Pair wins:</strong> ${htmlEscape(candidateLabel)} ${report.comparison.pairedFreshTokenWins[candidateKey]} · ${htmlEscape(baselineLabel)} ${report.comparison.pairedFreshTokenWins[baselineKey]} · ties ${report.comparison.pairedFreshTokenWins.ties}</p>
288
+ <p class="metric"><strong>Comparison:</strong> ${htmlEscape(candidateLabel)} vs ${htmlEscape(baselineLabel)}</p><p class="metric"><strong>Purpose:</strong> ${htmlEscape(report.comparison.purpose ?? "unspecified")}</p><p class="metric"><strong>Claim tier:</strong> ${htmlEscape(report.comparison.claimEligibility?.achievedTier ?? "unavailable")}</p><p class="metric"><strong>Token claim scope:</strong> ${htmlEscape(report.comparison.claimEligibility?.tokenClaimScope ?? "unavailable")}</p>${report.comparison.tokenClaimUnavailableReason ? `<p class="metric"><strong>Token claim restriction:</strong> ${htmlEscape(report.comparison.tokenClaimUnavailableReason)}</p>` : ""}<p class="metric"><strong>Verdict:</strong> ${htmlEscape(report.verdict.status)}</p><p class="metric"><strong>Protocol:</strong> ${report.comparison.comparisonProtocolGate?.passed ? "PASS" : `FAIL (${htmlEscape((report.comparison.comparisonProtocolGate?.failedChecks ?? []).join(", ") || "missing evidence")})`}</p><p class="metric"><strong>Codex-relative gate:</strong> ${report.comparison.codexRelativeEfficiencyGate === null ? "n/a" : report.comparison.codexRelativeEfficiencyGate ? "PASS" : "FAIL"}</p><p class="metric"><strong>Total token traffic:</strong> ${display(codexRelative?.totalTokenTraffic?.ratio, 4)}${codexRelativeTrafficConfidence ? ` (${display(codexRelativeTrafficConfidence.lower, 4)}–${display(codexRelativeTrafficConfidence.upper, 4)})` : ""} · upper ≤ ${display(codexRelative?.policy?.maximumTotalTokenTrafficRatioUpper95, 2)}</p><p class="metric"><strong>API-equivalent cost:</strong> ${display(codexRelative?.apiEquivalentCost?.ratio, 4)}${codexRelativeCostConfidence ? ` (${display(codexRelativeCostConfidence.lower, 4)}–${display(codexRelativeCostConfidence.upper, 4)})` : ""} · upper ≤ ${display(codexRelative?.policy?.maximumApiEquivalentCostRatioUpper95, 2)}</p><p class="metric"><strong>Subagent traffic:</strong> ${displayRatioPercent(codexRelative?.subagents?.trafficShare)} · ${codexRelative?.subagents?.sessions ?? "n/a"} sessions</p><p class="metric"><strong>Provider wire:</strong> ${report.comparison.providerWireSurfaceGate === null ? "n/a" : report.comparison.providerWireSurfaceGate ? "PASS" : "FAIL"} (${report.comparison.providerWireEvidence?.verifiedRuns ?? 0}/${report.comparison.providerWireEvidence?.runs ?? 0} runs, ${(report.comparison.providerWireEvidence?.driftGroups ?? []).length} drift groups)</p><p class="metric"><strong>Causal receipt:</strong> ${report.comparison.causalContextEvidenceGate === null ? "n/a" : report.comparison.causalContextEvidenceGate ? "PASS" : "FAIL"} (current v${causalContext?.requiredSchemaVersion ?? "?"} ${causalContext?.currentAvailableRuns ?? 0}/${causalContext?.runs ?? 0} runs)</p><p class="metric"><strong>Host readiness:</strong> ${report.comparison.hostReadinessGate === null ? "n/a" : report.comparison.hostReadinessGate ? "PASS" : "FAIL"} (${hostReadiness?.validReceiptCount ?? 0}/${hostReadiness?.receiptCount ?? 0} valid receipts · ${htmlEscape(hostReadiness?.windowCoverage ?? "n/a")} coverage)</p><p class="metric"><strong>Causal packs:</strong> ${causalAggregates ? `${causalAggregates.packCounts.offered}/${causalAggregates.packCounts.delivered}/${causalAggregates.packCounts.injected} offered/delivered/injected · ${causalAggregates.estimatedTokens.injected} injected estimated tokens · ${causalAggregates.selectedItemCounts.injected} selected items` : "unavailable"}</p><p class="metric"><strong>Causal direct rereads:</strong> ${causalAggregates ? `${causalAggregates.directFallbackRereads.successfulCalls} successful direct-path calls · ${causalAggregates.directFallbackRereads.shellToolCallsObserved} shell calls after injection` : "unavailable"}</p><p class="metric"><strong>Causal edit recovery:</strong> ${causalAggregates?.editRecoveryContext ? `${causalAggregates.editRecoveryContext.count ?? "n/a"} contexts · ${causalAggregates.editRecoveryContext.injectedChars ?? "n/a"} chars · ${causalAggregates.editRecoveryContext.injectedEstimatedTokens ?? "n/a"} estimated tokens · ${htmlEscape(causalAggregates.editRecoveryContext.evidenceCoverage?.status ?? "unavailable")} evidence` : "unavailable"}</p><p class="metric"><strong>Suite gate:</strong> ${report.comparison.suiteGate ? report.comparison.suiteGate.passed ? "PASS" : "FAIL" : "n/a"}</p><p class="metric"><strong>Infrastructure retries:</strong> ${report.infrastructure?.retries ?? 0}</p><p class="metric"><strong>Stability gate:</strong> ${report.comparison.stabilityGate === null ? "n/a" : report.comparison.stabilityGate ? "PASS" : "FAIL"}</p><p class="metric"><strong>Primary estimand:</strong> ${htmlEscape(report.comparison.primaryEfficiencyEstimand ?? "unavailable")}</p><p class="metric"><strong>Primary token delta:</strong> ${displayPercent(report.comparison.primaryEfficiencyDeltaPercent)}</p><p class="metric"><strong>Primary ratio 95% CI:</strong> ${primaryConfidence ? `${display(primaryConfidence.lower, 4)}–${display(primaryConfidence.upper, 4)}` : "n/a"}</p><p class="metric"><strong>Fixed-workload ratio:</strong> ${display(report.comparison.fixedWorkloadFamilyFreshTokenRatio, 4)}${fixedWorkloadConfidence ? ` (${display(fixedWorkloadConfidence.lower, 4)}–${display(fixedWorkloadConfidence.upper, 4)})` : ""}</p><p class="metric"><strong>Fixed-workload coverage:</strong> ${report.comparison.fixedWorkloadFamilyCoverage?.usableScenarioFamilies ?? 0}/${report.comparison.fixedWorkloadFamilyCoverage?.expectedScenarioFamilies ?? 0} families</p><p class="metric"><strong>Successful-pair diagnostic role:</strong> ${htmlEscape(report.comparison.successfulPairEfficiencyRole ?? "blocking")}</p><p class="metric"><strong>Successful-pair token delta:</strong> ${displayPercent(report.comparison.freshTokenDeltaPercent)}</p><p class="metric"><strong>Successful-pair ratio 95% CI:</strong> ${confidence ? `${display(confidence.lower, 4)}–${display(confidence.upper, 4)}` : "n/a"}</p><p class="metric"><strong>Normalized API-equivalent text-token cost ratio:</strong> ${display(normalizedCost?.ratio, 4)}${normalizedCostConfidence ? ` (${display(normalizedCostConfidence.lower, 4)}–${display(normalizedCostConfidence.upper, 4)})` : ""}</p><p class="metric"><strong>Normalized-cost gate:</strong> ${report.comparison.normalizedCostGate === null ? "n/a" : report.comparison.normalizedCostGate ? "PASS" : "FAIL"}</p><p class="metric"><strong>Failure-aware family ratio:</strong> ${display(report.comparison.failureAwareFamilyFreshTokenRatio, 4)}${failureAwareFamilyConfidence ? ` (${display(failureAwareFamilyConfidence.lower, 4)}–${display(failureAwareFamilyConfidence.upper, 4)})` : ""}</p><p class="metric"><strong>Failure-aware pooled ratio:</strong> ${display(report.comparison.failureAwareFreshTokenRatio, 4)}</p><p class="metric"><strong>Duration-ratio delta:</strong> ${displayPercent(report.comparison.durationDeltaPercent)}</p><p class="metric"><strong>Duration ratio 95% CI:</strong> ${durationConfidence ? `${display(durationConfidence.lower, 4)}–${display(durationConfidence.upper, 4)}` : "n/a"}</p><p class="metric"><strong>Performance gate:</strong> ${report.comparison.performanceGate === null ? "n/a" : report.comparison.performanceGate ? "PASS" : "FAIL"}</p><p class="metric"><strong>Provider-reported cost-ratio delta:</strong> ${displayPercent(report.comparison.costDeltaPercent)}</p><p class="metric"><strong>Comparable pairs:</strong> ${report.comparison.pairedUsageRuns}</p><p class="metric"><strong>Scenario families:</strong> ${report.comparison.pairedUsageScenarios ?? 0}</p><p class="metric"><strong>Complete families:</strong> ${report.comparison.pairedCompleteScenarios ?? 0}</p><p class="metric"><strong>Duration families:</strong> ${report.comparison.pairedCompleteDurationScenarios ?? 0}</p><p class="metric"><strong>Outcome families:</strong> ${report.comparison.pairedOutcomeScenarios ?? 0}</p><p class="metric"><strong>Pair wins:</strong> ${htmlEscape(candidateLabel)} ${report.comparison.pairedFreshTokenWins[candidateKey]} · ${htmlEscape(baselineLabel)} ${report.comparison.pairedFreshTokenWins[baselineKey]} · ties ${report.comparison.pairedFreshTokenWins.ties}</p>
289
+ <p class="metric"><strong>Observational timing (${htmlEscape(baselineLabel)}):</strong> ${htmlEscape(timingSummary(report.timingDiagnostics, baselineSurface))}</p><p class="metric"><strong>Observational timing (${htmlEscape(candidateLabel)}):</strong> ${htmlEscape(timingSummary(report.timingDiagnostics, candidateSurface))}</p>
290
+ <p class="note">Timing uses privacy-safe monotonic JSONL receipt boundaries only. Missing boundaries remain unavailable; these diagnostics do not alter authoritative duration or any release/spend gate.</p>
291
+ <p class="metric"><strong>Edit-recovery failures:</strong> ${causalAggregates?.editRecoveryContext?.failuresObserved ?? "n/a"} observed · ${causalAggregates?.editRecoveryContext?.suppressedFailures ?? "n/a"} safely suppressed</p>
292
+ <p class="metric"><strong>Exact attempts:</strong> accepted ${report.tokenAccounting?.acceptedAttempts.exactAttempts ?? 0}/${report.tokenAccounting?.acceptedAttempts.attempts ?? 0} · failed ${report.tokenAccounting?.failedAttempts.exactAttempts ?? 0}/${report.tokenAccounting?.failedAttempts.attempts ?? 0}</p><p class="metric"><strong>Failed-attempt fresh:</strong> ${display(report.tokenAccounting?.failedAttempts.tokens.fresh, 0)}</p>
293
+ <p class="note"><strong>Token definitions:</strong> Input is fresh input only. Reasoning is already included in output. Fresh = input + output. Total = input + cache read + cache write + output. Normalized API-equivalent cost uses the versioned suite pricing snapshot and is not OAuth/provider-billed cost.</p>
294
+ ${suiteFailureReasons ? `<p><strong>Suite gate failures:</strong> ${htmlEscape(suiteFailureReasons)}</p>` : ""}
177
295
  <h2>Score bands</h2><div class="table-wrap"><table><thead><tr><th>Surface</th><th>Resolved</th><th>Task grader</th><th>Scope</th><th>Quality</th><th>Safety</th><th>Reliability</th><th>Workflow</th><th>Efficiency</th><th>Overall</th></tr></thead><tbody>${scoreRows}</tbody></table></div>
178
296
  ${categoryRows ? `<h2>${htmlEscape(candidateLabel)} category bands</h2><div class="table-wrap"><table><thead><tr><th>Category</th><th>Resolved</th><th>Score</th><th>Correctness</th></tr></thead><tbody>${categoryRows}</tbody></table></div>` : ""}
179
297
  ${tokenBandRows ? `<h2>Paired fresh-token ratio by category</h2><div class="table-wrap"><table><thead><tr><th>Category</th><th>Pairs</th><th>Families</th><th>Ratio</th><th>Median delta</th><th>${htmlEscape(candidateLabel)}/${htmlEscape(baselineLabel)}/ties</th></tr></thead><tbody>${tokenBandRows}</tbody></table></div>` : ""}
180
- <h2>Median usage across all measured runs</h2><div class="table-wrap"><table><thead><tr><th>Surface</th><th>Input</th><th>Output</th><th>Cache read</th><th>Reasoning</th><th>Fresh</th><th>Tools</th><th>Cost</th><th>Seconds</th><th>Top tools</th></tr></thead><tbody>${usageRows}</tbody></table></div>
181
- <h2>Runs</h2><div class="table-wrap"><table><thead><tr><th>Scenario</th><th>Category</th><th>Difficulty</th><th>Profile</th><th>Lifecycle</th><th>Surface</th><th>Repeat</th><th>Infra retries</th><th>Resolved</th><th>Grader</th><th>Scope</th><th>Workflow</th><th>Workflow gaps</th><th>Model</th><th>Thinking</th><th>Input</th><th>Output</th><th>Cache read</th><th>Reasoning</th><th>Fresh</th><th>Tools</th><th>Top tools</th><th>Cost</th><th>Seconds</th><th>Failure</th></tr></thead><tbody>${rows}</tbody></table></div>
298
+ <h2>Median usage across all measured runs</h2><div class="table-wrap"><table><thead><tr><th>Surface</th><th>Fresh input</th><th>Output</th><th>Cache read</th><th>Cache write</th><th>Reasoning ⊂ output</th><th>Fresh</th><th>Total</th><th>Tools</th><th>Provider-reported cost</th><th>Seconds</th><th>Top tools</th></tr></thead><tbody>${usageRows}</tbody></table></div>
299
+ <h2>Runs</h2><div class="table-wrap"><table><thead><tr><th>Scenario</th><th>Category</th><th>Difficulty</th><th>Profile</th><th>Lifecycle</th><th>Surface</th><th>Repeat</th><th>Infra retries</th><th>Resolved</th><th>Grader</th><th>Scope</th><th>Workflow</th><th>Workflow gaps</th><th>Model</th><th>Thinking</th><th>Usage source</th><th>Fresh input</th><th>Output</th><th>Cache read</th><th>Cache write</th><th>Reasoning ⊂ output</th><th>Fresh</th><th>Total</th><th>Tools</th><th>Top tools</th><th>Provider-reported cost</th><th>Seconds</th><th>Failure</th></tr></thead><tbody>${rows}</tbody></table></div>
182
300
  <p class="note">${htmlEscape(report.verdict.note)}</p></body></html>\n`;
183
301
  }