agentinputlab 0.1.0a1__py3-none-any.whl

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 (469) hide show
  1. agentinputlab/__init__.py +28 -0
  2. agentinputlab/algorithms/__init__.py +6 -0
  3. agentinputlab/algorithms/acquire/__init__.py +145 -0
  4. agentinputlab/algorithms/acquire/adaptive_rag/__init__.py +43 -0
  5. agentinputlab/algorithms/acquire/adaptive_rag/replay.py +367 -0
  6. agentinputlab/algorithms/acquire/adaptive_rag/runtime.py +1202 -0
  7. agentinputlab/algorithms/acquire/adaptive_rag_common_support.py +542 -0
  8. agentinputlab/algorithms/acquire/hyde.py +296 -0
  9. agentinputlab/algorithms/acquire/hyde_controlled.py +200 -0
  10. agentinputlab/algorithms/acquire/hyde_external.py +148 -0
  11. agentinputlab/algorithms/acquire/ircot.py +28 -0
  12. agentinputlab/algorithms/acquire/ircot_common_support.py +478 -0
  13. agentinputlab/algorithms/acquire/ircot_native.py +355 -0
  14. agentinputlab/algorithms/acquire/ircot_replay.py +327 -0
  15. agentinputlab/algorithms/acquire/qrag/__init__.py +79 -0
  16. agentinputlab/algorithms/acquire/qrag/bridge.py +828 -0
  17. agentinputlab/algorithms/acquire/qrag/common_support.py +891 -0
  18. agentinputlab/algorithms/acquire/qrag/native_axis.py +71 -0
  19. agentinputlab/algorithms/acquire/qrag/reader_adapter.py +139 -0
  20. agentinputlab/algorithms/acquire/r2ag/__init__.py +57 -0
  21. agentinputlab/algorithms/acquire/r2ag/bridge.py +1312 -0
  22. agentinputlab/algorithms/acquire/r2ag/common_support.py +1029 -0
  23. agentinputlab/algorithms/acquire/r2ag/socket_runtime.py +255 -0
  24. agentinputlab/algorithms/clarify/__init__.py +61 -0
  25. agentinputlab/algorithms/clarify/_structured.py +64 -0
  26. agentinputlab/algorithms/clarify/active_task_disambiguation.py +371 -0
  27. agentinputlab/algorithms/clarify/ask_or_assume/__init__.py +17 -0
  28. agentinputlab/algorithms/clarify/ask_or_assume/monitor.py +410 -0
  29. agentinputlab/algorithms/clarify/future_turns.py +379 -0
  30. agentinputlab/algorithms/clarify/udcs.py +255 -0
  31. agentinputlab/algorithms/clarify/udcs_deterministic.py +196 -0
  32. agentinputlab/algorithms/curate/__init__.py +65 -0
  33. agentinputlab/algorithms/curate/acon/__init__.py +27 -0
  34. agentinputlab/algorithms/curate/acon/common.py +284 -0
  35. agentinputlab/algorithms/curate/acon/common_support.py +136 -0
  36. agentinputlab/algorithms/curate/acon/history_bridge.py +309 -0
  37. agentinputlab/algorithms/curate/acon/observation_bridge.py +290 -0
  38. agentinputlab/algorithms/curate/llmlingua/__init__.py +30 -0
  39. agentinputlab/algorithms/curate/llmlingua/adapters.py +537 -0
  40. agentinputlab/algorithms/curate/llmlingua/budget.py +48 -0
  41. agentinputlab/algorithms/curate/llmlingua/controlled.py +175 -0
  42. agentinputlab/algorithms/curate/llmlingua/multilingual.py +105 -0
  43. agentinputlab/algorithms/curate/llmlingua/span_map.py +167 -0
  44. agentinputlab/algorithms/curate/recomp/__init__.py +47 -0
  45. agentinputlab/algorithms/curate/recomp/external.py +1129 -0
  46. agentinputlab/algorithms/curate/recomp/selective.py +304 -0
  47. agentinputlab/algorithms/decompose/__init__.py +107 -0
  48. agentinputlab/algorithms/decompose/decomp/__init__.py +53 -0
  49. agentinputlab/algorithms/decompose/decomp/common_support.py +478 -0
  50. agentinputlab/algorithms/decompose/decomp/planner_component.py +199 -0
  51. agentinputlab/algorithms/decompose/decomp/worker_bridge.py +416 -0
  52. agentinputlab/algorithms/decompose/llm_compiler/__init__.py +35 -0
  53. agentinputlab/algorithms/decompose/llm_compiler/bridge.py +520 -0
  54. agentinputlab/algorithms/decompose/llm_compiler/common_support.py +504 -0
  55. agentinputlab/algorithms/decompose/plan_and_budget/__init__.py +43 -0
  56. agentinputlab/algorithms/decompose/plan_and_budget/cached_soft_budget.py +362 -0
  57. agentinputlab/algorithms/decompose/plan_and_budget/decomposer_component.py +107 -0
  58. agentinputlab/algorithms/decompose/plan_and_budget/full.py +205 -0
  59. agentinputlab/algorithms/decompose/plan_and_budget/hard_cap.py +246 -0
  60. agentinputlab/algorithms/decompose/plan_and_budget/regenerated_soft_budget.py +448 -0
  61. agentinputlab/algorithms/decompose/plan_and_solve/__init__.py +33 -0
  62. agentinputlab/algorithms/decompose/plan_and_solve/plan_component.py +192 -0
  63. agentinputlab/algorithms/decompose/plan_and_solve/ps_prompt.py +127 -0
  64. agentinputlab/algorithms/decompose/self_ask/__init__.py +27 -0
  65. agentinputlab/algorithms/decompose/self_ask/loop.py +362 -0
  66. agentinputlab/algorithms/decompose/self_ask/planner_component.py +117 -0
  67. agentinputlab/algorithms/reformulate/__init__.py +85 -0
  68. agentinputlab/algorithms/reformulate/infocqr/__init__.py +40 -0
  69. agentinputlab/algorithms/reformulate/infocqr/cache_replay.py +362 -0
  70. agentinputlab/algorithms/reformulate/infocqr/external.py +675 -0
  71. agentinputlab/algorithms/reformulate/infocqr/student.py +189 -0
  72. agentinputlab/algorithms/reformulate/query2doc/__init__.py +42 -0
  73. agentinputlab/algorithms/reformulate/query2doc/controlled_live.py +190 -0
  74. agentinputlab/algorithms/reformulate/query2doc/models.py +95 -0
  75. agentinputlab/algorithms/reformulate/query2doc/official_cached.py +335 -0
  76. agentinputlab/algorithms/reformulate/query2doc/querygym_bridge.py +223 -0
  77. agentinputlab/algorithms/reformulate/query2doc/querygym_runtime.py +326 -0
  78. agentinputlab/algorithms/reformulate/reformer/__init__.py +23 -0
  79. agentinputlab/algorithms/reformulate/reformer/external.py +417 -0
  80. agentinputlab/algorithms/reformulate/rrr.py +696 -0
  81. agentinputlab/annotation/__init__.py +5 -0
  82. agentinputlab/annotation/independent.py +428 -0
  83. agentinputlab/annotation/server.py +233 -0
  84. agentinputlab/annotation/static/app.js +121 -0
  85. agentinputlab/annotation/static/index.html +60 -0
  86. agentinputlab/annotation/static/review.css +2 -0
  87. agentinputlab/annotation/static/review.html +10 -0
  88. agentinputlab/annotation/static/review.js +11 -0
  89. agentinputlab/annotation/static/styles.css +55 -0
  90. agentinputlab/annotation/store.py +444 -0
  91. agentinputlab/api.py +368 -0
  92. agentinputlab/artifacts/__init__.py +15 -0
  93. agentinputlab/artifacts/json_file.py +104 -0
  94. agentinputlab/artifacts/manifest.py +98 -0
  95. agentinputlab/artifacts/store.py +157 -0
  96. agentinputlab/backends/__init__.py +98 -0
  97. agentinputlab/backends/embedding/__init__.py +25 -0
  98. agentinputlab/backends/embedding/base.py +145 -0
  99. agentinputlab/backends/embedding/callable.py +75 -0
  100. agentinputlab/backends/embedding/replay.py +129 -0
  101. agentinputlab/backends/llm/__init__.py +31 -0
  102. agentinputlab/backends/llm/base.py +149 -0
  103. agentinputlab/backends/llm/callable.py +108 -0
  104. agentinputlab/backends/llm/decomp_completion.py +88 -0
  105. agentinputlab/backends/llm/final_content_stop.py +72 -0
  106. agentinputlab/backends/llm/gpt_oss_final_channel.py +84 -0
  107. agentinputlab/backends/llm/openai_compatible.py +332 -0
  108. agentinputlab/backends/llm/transformers.py +171 -0
  109. agentinputlab/backends/retrieval/__init__.py +35 -0
  110. agentinputlab/backends/retrieval/base.py +50 -0
  111. agentinputlab/backends/retrieval/dense.py +777 -0
  112. agentinputlab/backends/retrieval/memory.py +119 -0
  113. agentinputlab/backends/retrieval/sqlite_fts.py +155 -0
  114. agentinputlab/backends/user/__init__.py +15 -0
  115. agentinputlab/backends/user/base.py +66 -0
  116. agentinputlab/backends/user/callable.py +54 -0
  117. agentinputlab/backends/user/replay.py +89 -0
  118. agentinputlab/backends/vector.py +38 -0
  119. agentinputlab/benchmarks/__init__.py +246 -0
  120. agentinputlab/benchmarks/acquisition.py +106 -0
  121. agentinputlab/benchmarks/ale.py +540 -0
  122. agentinputlab/benchmarks/appworld_ul.py +669 -0
  123. agentinputlab/benchmarks/assets.py +226 -0
  124. agentinputlab/benchmarks/base.py +68 -0
  125. agentinputlab/benchmarks/beir_archive.py +235 -0
  126. agentinputlab/benchmarks/beir_sample.py +269 -0
  127. agentinputlab/benchmarks/bfcl_native.py +245 -0
  128. agentinputlab/benchmarks/bfcl_v3.py +607 -0
  129. agentinputlab/benchmarks/cache.py +218 -0
  130. agentinputlab/benchmarks/clariq.py +203 -0
  131. agentinputlab/benchmarks/data/registry.json +321 -0
  132. agentinputlab/benchmarks/data/smoke_assets.json +511 -0
  133. agentinputlab/benchmarks/data/statebench_v0_8_1_task_ids.json +475 -0
  134. agentinputlab/benchmarks/data/toolsandbox_165848b_scenario_names.json +1 -0
  135. agentinputlab/benchmarks/execution.py +1111 -0
  136. agentinputlab/benchmarks/interactive.py +257 -0
  137. agentinputlab/benchmarks/ir_query.py +204 -0
  138. agentinputlab/benchmarks/longbench.py +203 -0
  139. agentinputlab/benchmarks/paired_replay.py +479 -0
  140. agentinputlab/benchmarks/preprocess.py +2313 -0
  141. agentinputlab/benchmarks/production.py +236 -0
  142. agentinputlab/benchmarks/qa.py +963 -0
  143. agentinputlab/benchmarks/registry.py +110 -0
  144. agentinputlab/benchmarks/retrieval.py +459 -0
  145. agentinputlab/benchmarks/runtime.py +509 -0
  146. agentinputlab/benchmarks/statebench.py +938 -0
  147. agentinputlab/benchmarks/tau3.py +725 -0
  148. agentinputlab/benchmarks/toolsandbox.py +626 -0
  149. agentinputlab/benchmarks/trec_dl_sample.py +263 -0
  150. agentinputlab/campaign.py +654 -0
  151. agentinputlab/campaign_runner.py +953 -0
  152. agentinputlab/cli/__init__.py +6 -0
  153. agentinputlab/cli/main.py +933 -0
  154. agentinputlab/connectors/__init__.py +46 -0
  155. agentinputlab/connectors/knowledge/__init__.py +4 -0
  156. agentinputlab/connectors/tools/__init__.py +30 -0
  157. agentinputlab/connectors/tools/base.py +57 -0
  158. agentinputlab/connectors/tools/mcp.py +222 -0
  159. agentinputlab/connectors/tools/memory.py +41 -0
  160. agentinputlab/connectors/tools/openapi.py +695 -0
  161. agentinputlab/connectors/tools/schema.py +102 -0
  162. agentinputlab/contracts/__init__.py +91 -0
  163. agentinputlab/contracts/availability.py +24 -0
  164. agentinputlab/contracts/dependency.py +58 -0
  165. agentinputlab/contracts/enums.py +82 -0
  166. agentinputlab/contracts/errors.py +63 -0
  167. agentinputlab/contracts/evidence.py +43 -0
  168. agentinputlab/contracts/intent.py +110 -0
  169. agentinputlab/contracts/lifecycle.py +164 -0
  170. agentinputlab/contracts/patch.py +167 -0
  171. agentinputlab/contracts/serialization.py +93 -0
  172. agentinputlab/contracts/state.py +127 -0
  173. agentinputlab/contracts/trace.py +59 -0
  174. agentinputlab/contracts/worker.py +42 -0
  175. agentinputlab/core/__init__.py +7 -0
  176. agentinputlab/data/__init__.py +32 -0
  177. agentinputlab/data/checksums.py +9 -0
  178. agentinputlab/data/downloader.py +235 -0
  179. agentinputlab/data/http.py +171 -0
  180. agentinputlab/data/index.py +21 -0
  181. agentinputlab/data/overlap.py +28 -0
  182. agentinputlab/data/preprocess.py +32 -0
  183. agentinputlab/data/storage.py +57 -0
  184. agentinputlab/data/trec_sample.py +427 -0
  185. agentinputlab/evaluation/__init__.py +5 -0
  186. agentinputlab/evaluation/outcomes.py +32 -0
  187. agentinputlab/experiments/__init__.py +278 -0
  188. agentinputlab/experiments/analysis.py +981 -0
  189. agentinputlab/experiments/artifacts.py +687 -0
  190. agentinputlab/experiments/automatic_measurement.py +678 -0
  191. agentinputlab/experiments/auxiliary_matrix.py +203 -0
  192. agentinputlab/experiments/bindings.py +430 -0
  193. agentinputlab/experiments/blueprints.py +147 -0
  194. agentinputlab/experiments/builders/__init__.py +45 -0
  195. agentinputlab/experiments/builders/executable_external_payload.py +423 -0
  196. agentinputlab/experiments/builders/external_process_arena.py +1018 -0
  197. agentinputlab/experiments/builders/registry_intervention.py +463 -0
  198. agentinputlab/experiments/cli.py +158 -0
  199. agentinputlab/experiments/controls.py +340 -0
  200. agentinputlab/experiments/driver_guard.py +205 -0
  201. agentinputlab/experiments/drivers/__init__.py +89 -0
  202. agentinputlab/experiments/drivers/external_arena.py +2908 -0
  203. agentinputlab/experiments/drivers/method_process.py +662 -0
  204. agentinputlab/experiments/drivers/parity_process.py +1484 -0
  205. agentinputlab/experiments/drivers/static_production.py +1574 -0
  206. agentinputlab/experiments/executor.py +448 -0
  207. agentinputlab/experiments/loader.py +605 -0
  208. agentinputlab/experiments/measurement.py +675 -0
  209. agentinputlab/experiments/paper_analysis.py +1818 -0
  210. agentinputlab/experiments/parity_identity.py +605 -0
  211. agentinputlab/experiments/plan.py +277 -0
  212. agentinputlab/experiments/production_builders.py +3739 -0
  213. agentinputlab/experiments/readiness.py +432 -0
  214. agentinputlab/experiments/schema.py +514 -0
  215. agentinputlab/experiments/secrets.py +177 -0
  216. agentinputlab/experiments/stateful_analysis.py +1738 -0
  217. agentinputlab/experiments/transfer_analysis.py +1316 -0
  218. agentinputlab/experiments/unseen_analysis.py +1025 -0
  219. agentinputlab/integrations/__init__.py +1 -0
  220. agentinputlab/integrations/action_space/__init__.py +1 -0
  221. agentinputlab/integrations/external_workers/__init__.py +76 -0
  222. agentinputlab/integrations/external_workers/ace.py +431 -0
  223. agentinputlab/integrations/external_workers/base.py +181 -0
  224. agentinputlab/integrations/external_workers/decomp_live.py +249 -0
  225. agentinputlab/integrations/external_workers/decomp_planner.py +30 -0
  226. agentinputlab/integrations/external_workers/infocqr_student.py +280 -0
  227. agentinputlab/integrations/external_workers/ircot.py +576 -0
  228. agentinputlab/integrations/external_workers/llmcompiler_live.py +360 -0
  229. agentinputlab/integrations/external_workers/longllmlingua.py +162 -0
  230. agentinputlab/integrations/external_workers/method.py +314 -0
  231. agentinputlab/integrations/external_workers/process.py +678 -0
  232. agentinputlab/integrations/external_workers/supervisor.py +1071 -0
  233. agentinputlab/integrations/full_architecture/__init__.py +1 -0
  234. agentinputlab/integrations/full_architecture/hyde.py +212 -0
  235. agentinputlab/integrations/full_architecture/plan_and_budget.py +188 -0
  236. agentinputlab/integrations/live_history.py +222 -0
  237. agentinputlab/integrations/prompt_optimization/__init__.py +1 -0
  238. agentinputlab/integrations/training/__init__.py +1 -0
  239. agentinputlab/metrics/__init__.py +50 -0
  240. agentinputlab/metrics/catalog.py +313 -0
  241. agentinputlab/metrics/harm.py +95 -0
  242. agentinputlab/metrics/paired.py +40 -0
  243. agentinputlab/metrics/qa.py +76 -0
  244. agentinputlab/metrics/text.py +33 -0
  245. agentinputlab/metrics/third_party.py +161 -0
  246. agentinputlab/models/__init__.py +4 -0
  247. agentinputlab/observability/__init__.py +56 -0
  248. agentinputlab/observability/budget.py +316 -0
  249. agentinputlab/observability/instrumentation.py +754 -0
  250. agentinputlab/observability/models.py +403 -0
  251. agentinputlab/observability/monitor.py +380 -0
  252. agentinputlab/optimizers/__init__.py +1 -0
  253. agentinputlab/optimizers/clarify/__init__.py +12 -0
  254. agentinputlab/optimizers/context/__init__.py +22 -0
  255. agentinputlab/optimizers/decompose/__init__.py +64 -0
  256. agentinputlab/optimizers/evidence/__init__.py +22 -0
  257. agentinputlab/optimizers/rewrite/__init__.py +30 -0
  258. agentinputlab/parity/__init__.py +5 -0
  259. agentinputlab/parity/base.py +206 -0
  260. agentinputlab/production_campaign.py +763 -0
  261. agentinputlab/py.typed +1 -0
  262. agentinputlab/recipes/__init__.py +333 -0
  263. agentinputlab/recipes/ace.py +418 -0
  264. agentinputlab/recipes/ace_bfcl.py +273 -0
  265. agentinputlab/recipes/ace_common_support.py +169 -0
  266. agentinputlab/recipes/ace_native_plan.py +322 -0
  267. agentinputlab/recipes/ace_online.py +711 -0
  268. agentinputlab/recipes/ace_retrieval.py +330 -0
  269. agentinputlab/recipes/ace_statebench.py +382 -0
  270. agentinputlab/recipes/ace_tau.py +463 -0
  271. agentinputlab/recipes/ace_toolsandbox.py +432 -0
  272. agentinputlab/recipes/acon_guideline.py +22 -0
  273. agentinputlab/recipes/adaptive_rag.py +822 -0
  274. agentinputlab/recipes/llmlingua2_inference.py +146 -0
  275. agentinputlab/recipes/llmlingua2_training.py +796 -0
  276. agentinputlab/recipes/mce.py +1486 -0
  277. agentinputlab/recipes/mce_final.py +335 -0
  278. agentinputlab/recipes/mce_text_fit.py +499 -0
  279. agentinputlab/recipes/mce_text_only.py +298 -0
  280. agentinputlab/recipes/qrag.py +729 -0
  281. agentinputlab/recipes/qrag_inference.py +364 -0
  282. agentinputlab/recipes/r2ag_features.py +236 -0
  283. agentinputlab/recipes/r2ag_inference.py +503 -0
  284. agentinputlab/recipes/r2ag_training.py +948 -0
  285. agentinputlab/recipes/recomp_abstractive.py +592 -0
  286. agentinputlab/recipes/recomp_extractive.py +525 -0
  287. agentinputlab/recipes/reformer.py +17 -0
  288. agentinputlab/recipes/rqrag.py +1516 -0
  289. agentinputlab/recipes/rqrag_common_support.py +193 -0
  290. agentinputlab/recipes/rqrag_completion.py +147 -0
  291. agentinputlab/recipes/skillopt.py +462 -0
  292. agentinputlab/recipes/skillopt_common_support.py +154 -0
  293. agentinputlab/recipes/skillopt_environment.py +165 -0
  294. agentinputlab/recipes/skillopt_environment_cli.py +5 -0
  295. agentinputlab/recipes/skillopt_retrieval.py +188 -0
  296. agentinputlab/recipes/smartsearch.py +897 -0
  297. agentinputlab/recipes/smartsearch_curriculum.py +283 -0
  298. agentinputlab/recipes/smartsearch_grpo_contracts.py +58 -0
  299. agentinputlab/recipes/smartsearch_grpo_data.py +314 -0
  300. agentinputlab/recipes/smartsearch_grpo_meter.py +324 -0
  301. agentinputlab/recipes/smartsearch_grpo_prefix.py +144 -0
  302. agentinputlab/recipes/smartsearch_grpo_rollout.py +119 -0
  303. agentinputlab/recipes/smartsearch_grpo_tools.py +364 -0
  304. agentinputlab/recipes/smartsearch_grpo_training.py +305 -0
  305. agentinputlab/recipes/smartsearch_grpo_transport.py +248 -0
  306. agentinputlab/recipes/smartsearch_grpo_worker.py +364 -0
  307. agentinputlab/recipes/smartsearch_inference.py +524 -0
  308. agentinputlab/recipes/smartsearch_native_data.py +172 -0
  309. agentinputlab/recipes/smartsearch_policy.py +616 -0
  310. agentinputlab/recipes/smartsearch_policy_worker.py +9 -0
  311. agentinputlab/recipes/smartsearch_sampling.py +232 -0
  312. agentinputlab/recipes/smartsearch_teacher.py +203 -0
  313. agentinputlab/recipes/smartsearch_training.py +265 -0
  314. agentinputlab/registry/__init__.py +46 -0
  315. agentinputlab/registry/beta.py +727 -0
  316. agentinputlab/registry/data/algorithms.json +1959 -0
  317. agentinputlab/registry/data/auxiliary_method_profiles.json +34 -0
  318. agentinputlab/registry/loader.py +100 -0
  319. agentinputlab/registry/result_manifest.py +184 -0
  320. agentinputlab/registry/runtime.py +195 -0
  321. agentinputlab/registry/schema.py +449 -0
  322. agentinputlab/runner/__init__.py +30 -0
  323. agentinputlab/runner/balance.py +248 -0
  324. agentinputlab/runner/cache.py +135 -0
  325. agentinputlab/runner/composite.py +130 -0
  326. agentinputlab/runner/engine.py +285 -0
  327. agentinputlab/runner/runtime_guard.py +268 -0
  328. agentinputlab/runner/worker.py +80 -0
  329. agentinputlab/runtime_bootstrap.py +288 -0
  330. agentinputlab/selectors/__init__.py +196 -0
  331. agentinputlab/selectors/advanced.py +1514 -0
  332. agentinputlab/selectors/artifacts.py +1087 -0
  333. agentinputlab/selectors/base.py +112 -0
  334. agentinputlab/selectors/campaigns.py +1893 -0
  335. agentinputlab/selectors/catalog.py +86 -0
  336. agentinputlab/selectors/discovery.py +1860 -0
  337. agentinputlab/selectors/evaluation.py +1348 -0
  338. agentinputlab/selectors/features.py +383 -0
  339. agentinputlab/selectors/learned.py +502 -0
  340. agentinputlab/selectors/no_op.py +28 -0
  341. agentinputlab/selectors/outcome_table.py +475 -0
  342. agentinputlab/selectors/producer.py +853 -0
  343. agentinputlab/selectors/rules.py +109 -0
  344. agentinputlab/selectors/runtime.py +420 -0
  345. agentinputlab/studies/__init__.py +16 -0
  346. agentinputlab/studies/design.py +165 -0
  347. agentinputlab/studies/paired.py +322 -0
  348. agentinputlab/studio/__init__.py +6 -0
  349. agentinputlab/studio/campaigns.py +819 -0
  350. agentinputlab/studio/engine.py +1408 -0
  351. agentinputlab/studio/model_backends.py +425 -0
  352. agentinputlab/studio/prepared.py +410 -0
  353. agentinputlab/studio/server.py +613 -0
  354. agentinputlab/studio/static/app.js +397 -0
  355. agentinputlab/studio/static/campaigns.html +175 -0
  356. agentinputlab/studio/static/campaigns.js +426 -0
  357. agentinputlab/studio/static/index.html +192 -0
  358. agentinputlab/studio/static/result.html +103 -0
  359. agentinputlab/studio/static/result.js +273 -0
  360. agentinputlab/studio/static/styles.css +467 -0
  361. agentinputlab-0.1.0a1.data/data/share/agentinputlab/CITATION.cff +22 -0
  362. agentinputlab-0.1.0a1.dist-info/METADATA +392 -0
  363. agentinputlab-0.1.0a1.dist-info/RECORD +469 -0
  364. agentinputlab-0.1.0a1.dist-info/WHEEL +5 -0
  365. agentinputlab-0.1.0a1.dist-info/entry_points.txt +6 -0
  366. agentinputlab-0.1.0a1.dist-info/licenses/LICENSE +21 -0
  367. agentinputlab-0.1.0a1.dist-info/licenses/NOTICE +31 -0
  368. agentinputlab-0.1.0a1.dist-info/licenses/THIRD_PARTY_NOTICES.md +94 -0
  369. agentinputlab-0.1.0a1.dist-info/top_level.txt +2 -0
  370. workers/README.md +94 -0
  371. workers/ace/README.md +261 -0
  372. workers/ace/__init__.py +1 -0
  373. workers/ace/compatibility_manifest.json +39 -0
  374. workers/ace/driver.py +1682 -0
  375. workers/acon/README.md +215 -0
  376. workers/acon/__init__.py +5 -0
  377. workers/acon/compatibility_manifest.json +93 -0
  378. workers/acon/driver.py +1859 -0
  379. workers/acon/fitted.py +395 -0
  380. workers/acon/guideline.py +470 -0
  381. workers/acon/guideline_source_manifest.json +20 -0
  382. workers/adaptive_rag/README.md +96 -0
  383. workers/adaptive_rag/__init__.py +15 -0
  384. workers/adaptive_rag/compatibility_manifest.json +61 -0
  385. workers/adaptive_rag/driver.py +1470 -0
  386. workers/adaptive_rag/fixture_runtime.py +163 -0
  387. workers/adaptive_rag/runtime.py +252 -0
  388. workers/ask_or_assume/README.md +124 -0
  389. workers/ask_or_assume/__init__.py +1 -0
  390. workers/ask_or_assume/compatibility_manifest.json +36 -0
  391. workers/ask_or_assume/driver.py +1227 -0
  392. workers/decomp/README.md +133 -0
  393. workers/decomp/UPSTREAM_LICENSE.Apache-2.0.txt +201 -0
  394. workers/decomp/__init__.py +5 -0
  395. workers/decomp/compatibility_manifest.json +205 -0
  396. workers/decomp/driver.py +1015 -0
  397. workers/decomp/live.py +137 -0
  398. workers/decomp/runtime.py +231 -0
  399. workers/infocqr/README.md +106 -0
  400. workers/infocqr/__init__.py +1 -0
  401. workers/infocqr/compatibility_manifest.json +84 -0
  402. workers/infocqr/driver.py +1766 -0
  403. workers/ircot/README.md +167 -0
  404. workers/ircot/__init__.py +7 -0
  405. workers/ircot/compatibility_manifest.json +121 -0
  406. workers/ircot/controlled.py +457 -0
  407. workers/ircot/driver.py +1373 -0
  408. workers/ircot/fixture_bindings.json +39 -0
  409. workers/ircot/fixture_config.json +48 -0
  410. workers/ircot/fixture_prompt.txt +2 -0
  411. workers/ircot/native_qa.py +398 -0
  412. workers/ircot/qa_compatibility_manifest.json +57 -0
  413. workers/ircot/runtime.py +97 -0
  414. workers/llmcompiler/README.md +160 -0
  415. workers/llmcompiler/__init__.py +1 -0
  416. workers/llmcompiler/compatibility_manifest.json +45 -0
  417. workers/llmcompiler/driver.py +1514 -0
  418. workers/llmcompiler/export_prompts.py +74 -0
  419. workers/llmcompiler/live.py +174 -0
  420. workers/llmcompiler/patch_manifest.json +147 -0
  421. workers/mce/README.md +198 -0
  422. workers/mce/__init__.py +29 -0
  423. workers/mce/compatibility_manifest.json +115 -0
  424. workers/mce/driver.py +2585 -0
  425. workers/mce/final_eval.py +173 -0
  426. workers/official_arenas/README.md +25 -0
  427. workers/official_arenas/__init__.py +1 -0
  428. workers/official_arenas/capture_records.py +110 -0
  429. workers/official_arenas/driver.py +619 -0
  430. workers/official_arenas/protocol.py +334 -0
  431. workers/official_arenas/providers/__init__.py +1 -0
  432. workers/official_arenas/providers/_live_history.py +228 -0
  433. workers/official_arenas/providers/_message_compat.py +77 -0
  434. workers/official_arenas/providers/appworld_ul.py +196 -0
  435. workers/official_arenas/providers/fixture.py +112 -0
  436. workers/official_arenas/providers/statebench.py +960 -0
  437. workers/official_arenas/providers/tau3.py +1672 -0
  438. workers/official_arenas/providers/tau_retail.py +586 -0
  439. workers/official_arenas/providers/toolsandbox.py +1321 -0
  440. workers/official_arenas/providers/tooltalk.py +699 -0
  441. workers/official_arenas/retail_capture.py +188 -0
  442. workers/official_arenas/retail_driver.py +275 -0
  443. workers/qrag/README.md +172 -0
  444. workers/qrag/__init__.py +1 -0
  445. workers/qrag/compatibility_manifest.json +64 -0
  446. workers/qrag/driver.py +673 -0
  447. workers/r2ag/README.md +154 -0
  448. workers/r2ag/__init__.py +5 -0
  449. workers/r2ag/compatibility_manifest.json +55 -0
  450. workers/r2ag/driver.py +1287 -0
  451. workers/r2ag/feature_schema.json +8 -0
  452. workers/r2ag/runtime.py +589 -0
  453. workers/recomp/README.md +120 -0
  454. workers/recomp/__init__.py +1 -0
  455. workers/recomp/compatibility_manifest.json +43 -0
  456. workers/recomp/driver.py +1653 -0
  457. workers/reformer/README.md +125 -0
  458. workers/reformer/__init__.py +1 -0
  459. workers/reformer/compatibility_manifest.json +68 -0
  460. workers/reformer/driver.py +1459 -0
  461. workers/reformer/induction.py +457 -0
  462. workers/rrr/README.md +126 -0
  463. workers/rrr/__init__.py +1 -0
  464. workers/rrr/compatibility_manifest.json +57 -0
  465. workers/rrr/driver.py +1133 -0
  466. workers/smartsearch/README.md +331 -0
  467. workers/smartsearch/__init__.py +1 -0
  468. workers/smartsearch/compatibility_manifest.json +41 -0
  469. workers/smartsearch/driver.py +1643 -0
@@ -0,0 +1,1738 @@
1
+ """Fail-closed analysis for gated stateful-arena replications.
2
+
3
+ Stateful arenas do not share an official raw outcome scale. This module
4
+ therefore validates the complete atomic-replay evidence chain and estimates
5
+ effects inside each arena relative to the same-origin ``NO_OP`` result. It
6
+ never pools raw metrics across arenas. The only cross-arena summaries are
7
+ scale-free direction and win-minus-harm diagnostics over arena-local paired
8
+ effects.
9
+
10
+ The public record validator is deliberately reusable by transfer analyses.
11
+ It validates record-local atomic, scorer, source, dependency, and balance
12
+ evidence, but leaves the decision to require an approved external gate to the
13
+ calling protocol. The stateful-replication entry point always requires that
14
+ gate and additionally cross-checks it against the immutable driver
15
+ fingerprint stored in the verified run directory.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ import math
22
+ import random
23
+ from collections import defaultdict
24
+ from collections.abc import Mapping, Sequence
25
+ from dataclasses import dataclass
26
+ from pathlib import Path
27
+ from statistics import fmean, median
28
+ from typing import Any
29
+
30
+ from agentinputlab.benchmarks import (
31
+ ATOMIC_EXTERNAL_PAIR_CONTRACT,
32
+ AtomicReplayArmReceipt,
33
+ AtomicReplayReceipt,
34
+ )
35
+ from agentinputlab.contracts.enums import SemanticFamily
36
+ from agentinputlab.contracts.errors import ArtifactError, ValidationError
37
+ from agentinputlab.contracts.serialization import content_hash
38
+
39
+ from .analysis import _load_manifest, load_verified_cell_results
40
+ from .artifacts import verify_experiment_run_directory
41
+ from .schema import ExperimentManifest, ExperimentStage
42
+
43
+ STATEFUL_ARENA_REPLICATION_PROTOCOL = "stateful-arena-replication-v1"
44
+ STATEFUL_ARENA_REPLICATION_ARTIFACT = "agentinputlab.stateful_arena_replication_analysis"
45
+
46
+ _EXTERNAL_ARENA_DRIVER = "agentinputlab.experiments.drivers.external_arena:factory"
47
+ _EXTERNAL_ARENA_IMPLEMENTATION = "agentinputlab.external_arena.atomic_multi_arm"
48
+ _VALID_SUCCESS_FORMULA = "agentinputlab.valid_success.v1"
49
+ _VALID_SUCCESS_METRIC = "valid_success"
50
+ _SUPPORTED_STATEFUL_SUITES = frozenset(
51
+ {
52
+ "tau3.banking_knowledge",
53
+ "state_bench.common_support",
54
+ "toolsandbox.common_support",
55
+ "appworld_ul.common_support",
56
+ }
57
+ )
58
+ _DEPENDENCY_SLOTS = frozenset(
59
+ {
60
+ "agent_model",
61
+ "official_runtime",
62
+ "corpus",
63
+ "preprocessing",
64
+ "index",
65
+ "embedder",
66
+ "retriever",
67
+ "reranker",
68
+ "reader",
69
+ "scorer",
70
+ }
71
+ )
72
+ _NON_MODEL_DEPENDENCY_SLOTS = _DEPENDENCY_SLOTS - {"agent_model"}
73
+ _ALLOWED_STAGES = frozenset(
74
+ {
75
+ ExperimentStage.CONFIRMATORY,
76
+ ExperimentStage.SELECTOR_CONFIRMATION,
77
+ ExperimentStage.REPLICATION,
78
+ }
79
+ )
80
+
81
+
82
+ @dataclass(frozen=True, slots=True)
83
+ class _VerifiedStatefulRun:
84
+ root: Path
85
+ manifest: ExperimentManifest
86
+ records: tuple[Mapping[str, Any], ...]
87
+ verification: Mapping[str, Any]
88
+ driver_fingerprint: Mapping[str, Any]
89
+
90
+
91
+ def validate_external_arena_record_receipts(
92
+ manifest: ExperimentManifest,
93
+ record: Mapping[str, Any],
94
+ *,
95
+ metric: str,
96
+ ) -> Mapping[str, Any]:
97
+ """Validate one external-arena result and return a normalized paired row.
98
+
99
+ The returned mapping contains no unpaired raw outcome summary. It carries
100
+ the requested value for each arm only so a caller can form arena-local,
101
+ same-checkpoint effects. An ``external_gate_receipt`` is validated when
102
+ present and returned as ``gate_receipt``; protocols such as unseen-suite
103
+ transfer may apply a different gate rule, whereas
104
+ :func:`analyze_stateful_arena_replication` requires it unconditionally.
105
+ """
106
+
107
+ if not isinstance(manifest, ExperimentManifest):
108
+ raise TypeError("external arena record validation requires ExperimentManifest")
109
+ metric_name = _metric_name(metric)
110
+ if not isinstance(record, Mapping):
111
+ raise ArtifactError("external arena result record must be an object")
112
+
113
+ cell = _object(record.get("cell"), label="external arena result cell")
114
+ result = _object(record.get("result"), label="external arena result")
115
+ if result.get("status") != "succeeded":
116
+ raise ArtifactError("external arena receipt analysis requires a succeeded cell")
117
+ payload = _object(
118
+ result.get("payload"),
119
+ label="external arena result payload",
120
+ )
121
+
122
+ suite_id = _text(cell.get("benchmark_id"), label="external arena cell benchmark_id")
123
+ split = _text(cell.get("split"), label="external arena cell split")
124
+ unit_id = _text(cell.get("unit_id"), label="external arena cell unit_id")
125
+ model_slot = _text(cell.get("model_slot"), label="external arena cell model_slot")
126
+ seed = _seed(cell.get("seed"), label="external arena cell seed")
127
+ target = _manifest_target(manifest, suite_id=suite_id, split=split)
128
+ if target.external is not True or target.source_revision is None:
129
+ raise ArtifactError("stateful arena analysis requires an external source-locked target")
130
+ if manifest.metadata.get("requires_atomic_external_replay") is not True:
131
+ raise ArtifactError("external arena manifest lacks requires_atomic_external_replay=true")
132
+ if model_slot not in manifest.model_slots or seed not in manifest.seeds:
133
+ raise ArtifactError("external arena cell model slot or seed is outside the manifest")
134
+
135
+ planned_arm_ids = _text_sequence(
136
+ cell.get("arm_ids"),
137
+ label="external arena cell arm_ids",
138
+ )
139
+ expected_arm_ids = target.arm_ids or tuple(arm.arm_id for arm in manifest.arms)
140
+ if planned_arm_ids != expected_arm_ids:
141
+ raise ArtifactError("external arena cell arms differ from the manifest target")
142
+ _require_payload_identity(
143
+ payload,
144
+ suite_id=suite_id,
145
+ split=split,
146
+ unit_id=unit_id,
147
+ model_slot=model_slot,
148
+ seed=seed,
149
+ planned_arm_ids=planned_arm_ids,
150
+ )
151
+
152
+ baseline_arm_id = _text(
153
+ payload.get("baseline_arm_id"),
154
+ label="external arena baseline_arm_id",
155
+ )
156
+ arm_specs = {arm.arm_id: arm for arm in manifest.arms}
157
+ if baseline_arm_id not in planned_arm_ids:
158
+ raise ArtifactError("external arena baseline is outside the planned arm inventory")
159
+ baseline_spec = arm_specs.get(baseline_arm_id)
160
+ if baseline_spec is None or baseline_spec.family is not SemanticFamily.NO_OP:
161
+ raise ArtifactError("external arena baseline must be the manifest NO_OP arm")
162
+
163
+ source_revision = target.source_revision
164
+ checkpoint = _checkpoint_identity(
165
+ payload.get("checkpoint_identity"),
166
+ source_revision=source_revision,
167
+ )
168
+ if payload.get("target_source_revision") != source_revision:
169
+ raise ArtifactError("external arena target source receipt differs from the manifest")
170
+ if payload.get("atomic_replay_contract") != ATOMIC_EXTERNAL_PAIR_CONTRACT:
171
+ raise ArtifactError("external arena result lacks the atomic replay contract")
172
+
173
+ dependencies = _dependency_hashes(
174
+ payload.get("observed_dependency_hashes"),
175
+ label="external arena observed dependency receipt",
176
+ exact_slots=True,
177
+ )
178
+ _validate_not_applicable_slots(payload, dependencies=dependencies)
179
+
180
+ baseline_consistency = _sha256(
181
+ payload.get("baseline_consistency_sha256"),
182
+ label="external arena baseline consistency receipt",
183
+ )
184
+ raw_outcomes = _sequence(
185
+ payload.get("arms"),
186
+ label="external arena arm outcomes",
187
+ )
188
+ if len(raw_outcomes) != len(planned_arm_ids):
189
+ raise ArtifactError("external arena arm outcome inventory is partial")
190
+ outcomes: dict[str, Mapping[str, Any]] = {}
191
+ metric_schema: tuple[str, ...] | None = None
192
+ scorer_identity: tuple[str, str] | None = None
193
+ official_success_metric: str | None = None
194
+ outcome_contract_sha256: str | None = None
195
+ normalized_arms: dict[str, Mapping[str, Any]] = {}
196
+ for index, raw_outcome in enumerate(raw_outcomes):
197
+ outcome = _object(raw_outcome, label=f"external arena arm outcome {index}")
198
+ arm_id = _text(
199
+ outcome.get("arm_id"),
200
+ label=f"external arena arm outcome {index} arm_id",
201
+ )
202
+ if arm_id in outcomes:
203
+ raise ArtifactError("external arena result contains duplicate arm outcomes")
204
+ if arm_id != planned_arm_ids[index]:
205
+ raise ArtifactError("external arena arm outcomes differ from planned order")
206
+ spec = arm_specs.get(arm_id)
207
+ if spec is None:
208
+ raise ArtifactError("external arena result references an unknown manifest arm")
209
+ if outcome.get("status") != "succeeded":
210
+ raise ArtifactError("stateful arena analysis refuses failed or imputed arm outcomes")
211
+ if (
212
+ outcome.get("release_id") != spec.release_id
213
+ or outcome.get("family") != spec.family.value
214
+ or outcome.get("profile_kind") != spec.profile_kind.value
215
+ ):
216
+ raise ArtifactError(f"external arena arm {arm_id!r} identity differs from the manifest")
217
+ metrics = _metrics(
218
+ outcome.get("metrics"),
219
+ label=f"external arena arm {arm_id!r} metrics",
220
+ )
221
+ current_schema = tuple(sorted(metrics))
222
+ if metric_schema is None:
223
+ metric_schema = current_schema
224
+ elif current_schema != metric_schema:
225
+ raise ArtifactError("external arena arms expose heterogeneous metric schemas")
226
+ if metric_name not in metrics:
227
+ raise ArtifactError(
228
+ f"external arena arm {arm_id!r} lacks requested metric {metric_name!r}"
229
+ )
230
+ _sha256(
231
+ outcome.get("payload_sha256"),
232
+ label=f"external arena arm {arm_id!r} payload receipt",
233
+ )
234
+
235
+ composition = _validate_valid_success_composition(
236
+ outcome,
237
+ metrics=metrics,
238
+ suite_id=suite_id,
239
+ arm_id=arm_id,
240
+ )
241
+ current_scorer = (
242
+ str(composition["scorer_id"]),
243
+ str(composition["scorer_revision"]),
244
+ )
245
+ current_official_metric = str(composition["official_success_metric"])
246
+ current_outcome_contract = str(composition["outcome_contract_sha256"])
247
+ if scorer_identity is None:
248
+ scorer_identity = current_scorer
249
+ official_success_metric = current_official_metric
250
+ outcome_contract_sha256 = current_outcome_contract
251
+ elif (
252
+ current_scorer != scorer_identity
253
+ or current_official_metric != official_success_metric
254
+ or current_outcome_contract != outcome_contract_sha256
255
+ ):
256
+ raise ArtifactError(
257
+ "external arena official scorer, metric, or outcome contract "
258
+ "identity differs across arms"
259
+ )
260
+ outcomes[arm_id] = outcome
261
+ normalized_arms[arm_id] = {
262
+ "arm_id": arm_id,
263
+ "release_id": spec.release_id,
264
+ "family": spec.family.value,
265
+ "profile_kind": spec.profile_kind.value,
266
+ "arm_kind": spec.arm_kind.value,
267
+ "value": metrics[metric_name],
268
+ }
269
+
270
+ assert metric_schema is not None
271
+ assert scorer_identity is not None
272
+ assert official_success_metric is not None
273
+ assert outcome_contract_sha256 is not None
274
+ atomic_pairs = _validate_atomic_pairs(
275
+ payload,
276
+ outcomes=outcomes,
277
+ planned_arm_ids=planned_arm_ids,
278
+ baseline_arm_id=baseline_arm_id,
279
+ suite_id=suite_id,
280
+ unit_id=unit_id,
281
+ seed=seed,
282
+ source_revision=source_revision,
283
+ checkpoint=checkpoint,
284
+ dependencies=dependencies,
285
+ scorer_identity=scorer_identity,
286
+ baseline_consistency=baseline_consistency,
287
+ )
288
+ gate_receipt = _optional_gate_receipt(
289
+ payload.get("external_gate_receipt"),
290
+ manifest=manifest,
291
+ suite_id=suite_id,
292
+ split=split,
293
+ source_revision=source_revision,
294
+ scorer_identity=scorer_identity,
295
+ atomic_replay_contract=ATOMIC_EXTERNAL_PAIR_CONTRACT,
296
+ )
297
+
298
+ source_receipt = {
299
+ "suite_id": suite_id,
300
+ "split": split,
301
+ "source_revision": source_revision,
302
+ "checkpoint_identity_sha256": checkpoint["identity_sha256"],
303
+ "origin_state_sha256": checkpoint["origin_state_sha256"],
304
+ "replay_identity_sha256": checkpoint["replay_identity_sha256"],
305
+ "atomic_pair_receipt_sha256s": tuple(pair["receipt_sha256"] for pair in atomic_pairs),
306
+ }
307
+ dependency_receipt = {
308
+ "model_slot": model_slot,
309
+ "locked_dependency_hashes": dependencies,
310
+ "atomic_pair_count": len(atomic_pairs),
311
+ }
312
+ balance_receipt = {
313
+ "baseline_arm_id": baseline_arm_id,
314
+ "planned_arm_ids": planned_arm_ids,
315
+ "complete_atomic_pair_inventory": True,
316
+ "baseline_consistency_sha256": baseline_consistency,
317
+ "metric_schema": metric_schema,
318
+ "dependency_receipt_sha256": content_hash(dependency_receipt),
319
+ }
320
+ observation_id = content_hash(
321
+ {
322
+ "suite_id": suite_id,
323
+ "split": split,
324
+ "unit_id": unit_id,
325
+ "model_slot": model_slot,
326
+ "seed": seed,
327
+ }
328
+ )
329
+ return {
330
+ "suite_id": suite_id,
331
+ "split": split,
332
+ "unit_id": unit_id,
333
+ "group_id": checkpoint["group_sha256"],
334
+ "observation_id": observation_id,
335
+ "model_slot": model_slot,
336
+ "seed": seed,
337
+ "metric": metric_name,
338
+ "metric_schema": metric_schema,
339
+ "official_success_metric": official_success_metric,
340
+ "outcome_contract_sha256": outcome_contract_sha256,
341
+ "source_revision": source_revision,
342
+ "scorer_identity": {
343
+ "scorer_id": scorer_identity[0],
344
+ "scorer_revision": scorer_identity[1],
345
+ },
346
+ "bridge_identity": {
347
+ "bridge_id": atomic_pairs[0]["bridge_id"],
348
+ "bridge_revision": atomic_pairs[0]["bridge_revision"],
349
+ },
350
+ "dependency_identity": dependencies,
351
+ "baseline_arm_id": baseline_arm_id,
352
+ "baseline_identity": {
353
+ key: normalized_arms[baseline_arm_id][key]
354
+ for key in ("release_id", "family", "profile_kind", "arm_kind")
355
+ },
356
+ "arms": normalized_arms,
357
+ "gate_receipt": gate_receipt,
358
+ "source_receipt": {
359
+ **source_receipt,
360
+ "receipt_sha256": content_hash(source_receipt),
361
+ },
362
+ "dependency_receipt": {
363
+ **dependency_receipt,
364
+ "receipt_sha256": content_hash(dependency_receipt),
365
+ },
366
+ "balance_receipt": {
367
+ **balance_receipt,
368
+ "receipt_sha256": content_hash(balance_receipt),
369
+ },
370
+ }
371
+
372
+
373
+ def analyze_stateful_arena_replication(
374
+ run_dirs: Sequence[str | Path],
375
+ *,
376
+ metric: str = _VALID_SUCCESS_METRIC,
377
+ bootstrap_samples: int = 2_000,
378
+ random_seed: int = 2027,
379
+ pool_raw_metrics: bool = False,
380
+ ) -> Mapping[str, Any]:
381
+ """Analyze one or more verified, gate-approved external arena runs.
382
+
383
+ Every arena retains its own estimand. ``pool_raw_metrics`` exists only as
384
+ an explicit fail-closed API boundary: any value other than ``False`` is
385
+ rejected because official reward, task completion, similarity, and task
386
+ success are not a common raw scale.
387
+ """
388
+
389
+ metric_name = _metric_name(metric)
390
+ if pool_raw_metrics is not False:
391
+ raise ValidationError("stateful arena analysis forbids pooling heterogeneous raw metrics")
392
+ _resample_arguments(
393
+ bootstrap_samples=bootstrap_samples,
394
+ random_seed=random_seed,
395
+ )
396
+ roots = _run_roots(run_dirs)
397
+ runs = tuple(_load_verified_stateful_run(root) for root in roots)
398
+
399
+ rows_by_suite: dict[str, list[Mapping[str, Any]]] = defaultdict(list)
400
+ run_receipts_by_suite: dict[str, list[Mapping[str, Any]]] = defaultdict(list)
401
+ observed_ids: set[str] = set()
402
+ for run in runs:
403
+ _validate_stateful_manifest(run.manifest)
404
+ normalized = tuple(
405
+ validate_external_arena_record_receipts(
406
+ run.manifest,
407
+ record,
408
+ metric=metric_name,
409
+ )
410
+ for record in run.records
411
+ )
412
+ if not normalized:
413
+ raise ArtifactError("verified stateful arena run contains no result records")
414
+ _validate_run_fingerprint(run, normalized)
415
+ for row in normalized:
416
+ if row["gate_receipt"] is None:
417
+ raise ArtifactError(
418
+ "stateful arena replication requires an approved external gate receipt"
419
+ )
420
+ observation_id = str(row["observation_id"])
421
+ if observation_id in observed_ids:
422
+ raise ArtifactError(
423
+ "stateful arena runs contain a duplicate checkpoint/model/seed observation"
424
+ )
425
+ observed_ids.add(observation_id)
426
+ suite_id = str(row["suite_id"])
427
+ rows_by_suite[suite_id].append(row)
428
+ for suite_id in sorted({str(row["suite_id"]) for row in normalized}):
429
+ run_receipts_by_suite[suite_id].append(
430
+ {
431
+ "run_dir": str(run.root),
432
+ "study_id": run.manifest.study_id,
433
+ "manifest_hash": run.verification["manifest_hash"],
434
+ "plan_hash": run.verification["plan_hash"],
435
+ "driver_hash": run.verification["driver_hash"],
436
+ "comparison_profile_hash": run.verification["comparison_profile_hash"],
437
+ }
438
+ )
439
+
440
+ rng = random.Random(random_seed)
441
+ suites: dict[str, Mapping[str, Any]] = {}
442
+ for suite_id, rows in sorted(rows_by_suite.items()):
443
+ suites[suite_id] = _summarize_suite(
444
+ suite_id,
445
+ rows,
446
+ run_receipts=run_receipts_by_suite[suite_id],
447
+ bootstrap_samples=bootstrap_samples,
448
+ rng=rng,
449
+ )
450
+
451
+ return {
452
+ "schema_version": 1,
453
+ "artifact": STATEFUL_ARENA_REPLICATION_ARTIFACT,
454
+ "analysis": "stateful_arena_replication",
455
+ "analysis_protocol": STATEFUL_ARENA_REPLICATION_PROTOCOL,
456
+ "metric": metric_name,
457
+ "run_count": len(runs),
458
+ "suite_count": len(suites),
459
+ "estimand": (
460
+ "arena-local protected-checkpoint-group mean paired difference "
461
+ "relative to same-origin NO_OP"
462
+ ),
463
+ "analysis_unit": (
464
+ "protected checkpoint group; unit variants, model slots, and seeds "
465
+ "are repeated observations collapsed within group"
466
+ ),
467
+ "receipt_policy": {
468
+ "complete_verified_runs_required": True,
469
+ "approved_gate_required": True,
470
+ "atomic_replay_required": True,
471
+ "official_scorer_receipt_required": True,
472
+ "source_receipt_required": True,
473
+ "dependency_balance_required": True,
474
+ },
475
+ "suites": suites,
476
+ "cross_suite_robustness": _cross_suite_robustness(suites),
477
+ "raw_metric_pooling_performed": False,
478
+ "bootstrap_samples": bootstrap_samples,
479
+ "random_seed": random_seed,
480
+ "experiments_executed_by_analysis": False,
481
+ "paper_claim_authorized": False,
482
+ }
483
+
484
+
485
+ def _load_verified_stateful_run(root: Path) -> _VerifiedStatefulRun:
486
+ verification = verify_experiment_run_directory(root)
487
+ if verification["is_shard"]:
488
+ raise ArtifactError(
489
+ "stateful arena analysis refuses a shard without an exact verified merge"
490
+ )
491
+ if not verification["complete"] or not verification["all_succeeded"]:
492
+ raise ArtifactError(
493
+ "stateful arena analysis requires one complete all-succeeded verified run"
494
+ )
495
+ manifest = _load_manifest(root)
496
+ records = load_verified_cell_results(root)
497
+ identity = _read_json_object(
498
+ root / "run_identity.json",
499
+ label="stateful arena run identity",
500
+ )
501
+ fingerprint = identity.get("driver_fingerprint")
502
+ if not isinstance(fingerprint, Mapping):
503
+ raise ArtifactError("stateful arena run lacks a driver fingerprint")
504
+ return _VerifiedStatefulRun(
505
+ root=root,
506
+ manifest=manifest,
507
+ records=records,
508
+ verification=verification,
509
+ driver_fingerprint=fingerprint,
510
+ )
511
+
512
+
513
+ def _validate_stateful_manifest(manifest: ExperimentManifest) -> None:
514
+ if manifest.stage not in _ALLOWED_STAGES:
515
+ raise ArtifactError(
516
+ "stateful arena replication requires a confirmatory, selector-confirmation, "
517
+ "or replication manifest"
518
+ )
519
+ if manifest.driver_entrypoint != _EXTERNAL_ARENA_DRIVER:
520
+ raise ArtifactError("stateful arena replication requires the external atomic-arena driver")
521
+ if manifest.metadata.get("requires_atomic_external_replay") is not True:
522
+ raise ArtifactError(
523
+ "stateful arena replication manifest lacks the atomic replay requirement"
524
+ )
525
+ unsupported = sorted(
526
+ {
527
+ target.cell_id
528
+ for target in manifest.benchmarks
529
+ if target.cell_id not in _SUPPORTED_STATEFUL_SUITES
530
+ }
531
+ )
532
+ if unsupported:
533
+ raise ArtifactError(
534
+ "stateful arena replication contains unsupported suites: " + ", ".join(unsupported)
535
+ )
536
+ if any(not target.external or target.source_revision is None for target in manifest.benchmarks):
537
+ raise ArtifactError("stateful arena replication targets must be external and source locked")
538
+
539
+
540
+ def _validate_run_fingerprint(
541
+ run: _VerifiedStatefulRun,
542
+ rows: Sequence[Mapping[str, Any]],
543
+ ) -> None:
544
+ fingerprint = run.driver_fingerprint
545
+ if (
546
+ fingerprint.get("implementation_id") != _EXTERNAL_ARENA_IMPLEMENTATION
547
+ or fingerprint.get("schema_version") != 3
548
+ ):
549
+ raise ArtifactError(
550
+ "stateful arena run was not produced by the locked atomic multi-arm driver"
551
+ )
552
+ targets = _object(
553
+ fingerprint.get("benchmark_targets"),
554
+ label="stateful arena driver benchmark targets",
555
+ )
556
+ arms = _object(
557
+ fingerprint.get("arms"),
558
+ label="stateful arena driver arm bindings",
559
+ )
560
+ gates = _object(
561
+ fingerprint.get("resolved_gate_bindings"),
562
+ label="stateful arena driver gate bindings",
563
+ )
564
+ baseline_arm_id = _text(
565
+ fingerprint.get("baseline_arm_id"),
566
+ label="stateful arena driver baseline_arm_id",
567
+ )
568
+ manifest_arms = {arm.arm_id: arm for arm in run.manifest.arms}
569
+ for arm_id, spec in manifest_arms.items():
570
+ binding = _object(
571
+ arms.get(arm_id),
572
+ label=f"stateful arena driver arm binding {arm_id!r}",
573
+ )
574
+ if (
575
+ binding.get("release_id") != spec.release_id
576
+ or binding.get("family") != spec.family.value
577
+ or binding.get("profile_kind") != spec.profile_kind.value
578
+ or binding.get("arm_kind") != spec.arm_kind.value
579
+ ):
580
+ raise ArtifactError(
581
+ f"stateful arena driver binding for arm {arm_id!r} has mixed identity"
582
+ )
583
+
584
+ for row in rows:
585
+ if row["baseline_arm_id"] != baseline_arm_id:
586
+ raise ArtifactError(
587
+ "stateful arena result baseline differs from the driver fingerprint"
588
+ )
589
+ key = f"{row['suite_id']}:{row['split']}"
590
+ target = _object(
591
+ targets.get(key),
592
+ label=f"stateful arena driver target {key!r}",
593
+ )
594
+ bridge_lock = _object(
595
+ target.get("bridge_lock"),
596
+ label=f"stateful arena driver bridge lock {key!r}",
597
+ )
598
+ outcome_contract = _object(
599
+ target.get("outcome_contract"),
600
+ label=f"stateful arena driver outcome contract {key!r}",
601
+ )
602
+ expected_scorer = {
603
+ "scorer_id": bridge_lock.get("scorer_id"),
604
+ "scorer_revision": bridge_lock.get("scorer_revision"),
605
+ }
606
+ expected_bridge = {
607
+ "bridge_id": bridge_lock.get("bridge_id"),
608
+ "bridge_revision": bridge_lock.get("bridge_revision"),
609
+ }
610
+ dependency_by_model = _object(
611
+ bridge_lock.get("dependency_hashes_by_model_slot"),
612
+ label=f"stateful arena driver dependency locks {key!r}",
613
+ )
614
+ expected_dependencies = _dependency_hashes(
615
+ dependency_by_model.get(row["model_slot"]),
616
+ label=(f"stateful arena driver dependencies for model {row['model_slot']!r}"),
617
+ exact_slots=True,
618
+ )
619
+ if (
620
+ target.get("benchmark_id") != row["suite_id"]
621
+ or target.get("split") != row["split"]
622
+ or target.get("source_revision") != row["source_revision"]
623
+ or expected_scorer != row["scorer_identity"]
624
+ or expected_bridge != row["bridge_identity"]
625
+ or expected_dependencies != row["dependency_identity"]
626
+ or outcome_contract.get("formula_id") != _VALID_SUCCESS_FORMULA
627
+ or outcome_contract.get("official_success_metric") != row["official_success_metric"]
628
+ or content_hash(outcome_contract) != row["outcome_contract_sha256"]
629
+ ):
630
+ raise ArtifactError(
631
+ "stateful arena result identity differs from its driver fingerprint"
632
+ )
633
+ gate = row["gate_receipt"]
634
+ resolved_gate = gates.get(key)
635
+ if (
636
+ gate is None
637
+ or not isinstance(resolved_gate, Mapping)
638
+ or dict(gate) != dict(resolved_gate)
639
+ ):
640
+ raise ArtifactError(
641
+ "stateful arena gate receipt differs from the resolved driver binding"
642
+ )
643
+
644
+
645
+ def _summarize_suite(
646
+ suite_id: str,
647
+ rows: Sequence[Mapping[str, Any]],
648
+ *,
649
+ run_receipts: Sequence[Mapping[str, Any]],
650
+ bootstrap_samples: int,
651
+ rng: random.Random,
652
+ ) -> Mapping[str, Any]:
653
+ reference = rows[0]
654
+ reference_fields = (
655
+ "split",
656
+ "source_revision",
657
+ "metric",
658
+ "metric_schema",
659
+ "official_success_metric",
660
+ "outcome_contract_sha256",
661
+ "scorer_identity",
662
+ "bridge_identity",
663
+ "baseline_identity",
664
+ )
665
+ for row in rows[1:]:
666
+ for field in reference_fields:
667
+ if row[field] != reference[field]:
668
+ raise ArtifactError(f"stateful suite {suite_id!r} mixes {field} identities")
669
+
670
+ dependencies_by_model: dict[str, Mapping[str, str]] = {}
671
+ arm_identity_by_release: dict[str, Mapping[str, Any]] = {}
672
+ arm_ids_by_release: dict[str, set[str]] = defaultdict(set)
673
+ effects_by_release_group: dict[str, dict[str, list[float]]] = defaultdict(
674
+ lambda: defaultdict(list)
675
+ )
676
+ effects_by_release_model_group: dict[str, dict[str, dict[str, list[float]]]] = defaultdict(
677
+ lambda: defaultdict(lambda: defaultdict(list))
678
+ )
679
+ replicate_design_by_release_group: dict[str, dict[str, set[tuple[str, int]]]] = defaultdict(
680
+ lambda: defaultdict(set)
681
+ )
682
+ source_receipt_hashes: set[str] = set()
683
+ dependency_receipt_hashes: set[str] = set()
684
+ balance_receipt_hashes: set[str] = set()
685
+ gate_receipt_hashes: set[str] = set()
686
+
687
+ for row in rows:
688
+ model_slot = str(row["model_slot"])
689
+ dependencies = row["dependency_identity"]
690
+ prior_dependencies = dependencies_by_model.get(model_slot)
691
+ if prior_dependencies is None:
692
+ dependencies_by_model[model_slot] = dependencies
693
+ elif prior_dependencies != dependencies:
694
+ raise ArtifactError(
695
+ f"stateful suite {suite_id!r} dependency identity drifts within "
696
+ f"model slot {model_slot!r}"
697
+ )
698
+ baseline = float(row["arms"][row["baseline_arm_id"]]["value"])
699
+ for arm_id, arm in row["arms"].items():
700
+ if arm_id == row["baseline_arm_id"]:
701
+ continue
702
+ release_id = str(arm["release_id"])
703
+ identity = {
704
+ key: arm[key] for key in ("release_id", "family", "profile_kind", "arm_kind")
705
+ }
706
+ prior_identity = arm_identity_by_release.get(release_id)
707
+ if prior_identity is None:
708
+ arm_identity_by_release[release_id] = identity
709
+ elif prior_identity != identity:
710
+ raise ArtifactError(
711
+ f"stateful suite {suite_id!r} mixes treatment identity "
712
+ f"for release {release_id!r}"
713
+ )
714
+ arm_ids_by_release[release_id].add(str(arm_id))
715
+ effect = float(arm["value"]) - baseline
716
+ group_id = str(row["group_id"])
717
+ effects_by_release_group[release_id][group_id].append(effect)
718
+ effects_by_release_model_group[release_id][model_slot][group_id].append(effect)
719
+ replicate_design_by_release_group[release_id][group_id].add(
720
+ (model_slot, int(row["seed"]))
721
+ )
722
+ source_receipt_hashes.add(str(row["source_receipt"]["receipt_sha256"]))
723
+ dependency_receipt_hashes.add(str(row["dependency_receipt"]["receipt_sha256"]))
724
+ balance_receipt_hashes.add(str(row["balance_receipt"]["receipt_sha256"]))
725
+ gate_receipt_hashes.add(str(row["gate_receipt"]["receipt_sha256"]))
726
+
727
+ _validate_cross_model_dependency_balance(
728
+ suite_id,
729
+ dependencies_by_model=dependencies_by_model,
730
+ )
731
+ treatments: dict[str, Mapping[str, Any]] = {}
732
+ for release_id in sorted(effects_by_release_group):
733
+ by_group = effects_by_release_group[release_id]
734
+ replicate_designs = {
735
+ tuple(sorted(design))
736
+ for design in replicate_design_by_release_group[release_id].values()
737
+ }
738
+ if len(replicate_designs) != 1:
739
+ raise ArtifactError(
740
+ f"stateful suite {suite_id!r} treatment {release_id!r} has "
741
+ "unbalanced model/seed coverage across checkpoint groups"
742
+ )
743
+ cluster_effects = tuple(fmean(values) for _group_id, values in sorted(by_group.items()))
744
+ if len(cluster_effects) < 2:
745
+ raise ArtifactError(
746
+ f"stateful suite {suite_id!r} treatment {release_id!r} "
747
+ "requires at least two independent checkpoint groups"
748
+ )
749
+ win_rate = sum(value > 0 for value in cluster_effects) / len(cluster_effects)
750
+ tie_rate = sum(value == 0 for value in cluster_effects) / len(cluster_effects)
751
+ harm_rate = sum(value < 0 for value in cluster_effects) / len(cluster_effects)
752
+ model_strata: dict[str, Mapping[str, Any]] = {}
753
+ for model_slot, group_values in sorted(effects_by_release_model_group[release_id].items()):
754
+ collapsed = tuple(fmean(values) for _group_id, values in sorted(group_values.items()))
755
+ model_strata[model_slot] = {
756
+ "checkpoint_group_count": len(collapsed),
757
+ "mean_effect_vs_no_op": fmean(collapsed),
758
+ "win_rate_vs_no_op": (sum(value > 0 for value in collapsed) / len(collapsed)),
759
+ "harm_rate_vs_no_op": (sum(value < 0 for value in collapsed) / len(collapsed)),
760
+ }
761
+ treatments[release_id] = {
762
+ **arm_identity_by_release[release_id],
763
+ "arm_ids": sorted(arm_ids_by_release[release_id]),
764
+ "checkpoint_group_count": len(cluster_effects),
765
+ "paired_record_count": sum(len(values) for values in by_group.values()),
766
+ "model_seed_replicate_design": [
767
+ {"model_slot": model_slot, "seed": seed}
768
+ for model_slot, seed in next(iter(replicate_designs))
769
+ ],
770
+ "mean_effect_vs_no_op": fmean(cluster_effects),
771
+ "median_effect_vs_no_op": median(cluster_effects),
772
+ "cluster_bootstrap_95ci": list(
773
+ _bootstrap_mean_interval(
774
+ cluster_effects,
775
+ samples=bootstrap_samples,
776
+ rng=rng,
777
+ )
778
+ ),
779
+ "win_rate_vs_no_op": win_rate,
780
+ "tie_rate_vs_no_op": tie_rate,
781
+ "harm_rate_vs_no_op": harm_rate,
782
+ "normalized_win_minus_harm": win_rate - harm_rate,
783
+ "model_strata": model_strata,
784
+ }
785
+
786
+ group_ids = {str(row["group_id"]) for row in rows}
787
+ return {
788
+ "suite_id": suite_id,
789
+ "split": reference["split"],
790
+ "source_revision": reference["source_revision"],
791
+ "metric": reference["metric"],
792
+ "metric_schema": list(reference["metric_schema"]),
793
+ "official_success_metric": reference["official_success_metric"],
794
+ "outcome_contract_sha256": reference["outcome_contract_sha256"],
795
+ "scorer_identity": reference["scorer_identity"],
796
+ "bridge_identity": reference["bridge_identity"],
797
+ "baseline_identity": reference["baseline_identity"],
798
+ "run_count": len(run_receipts),
799
+ "record_count": len(rows),
800
+ "checkpoint_group_count": len(group_ids),
801
+ "run_receipts": list(run_receipts),
802
+ "gate_receipts": {
803
+ "count": len(gate_receipt_hashes),
804
+ "receipt_sha256s": sorted(gate_receipt_hashes),
805
+ "approved": True,
806
+ },
807
+ "source_receipts": {
808
+ "count": len(source_receipt_hashes),
809
+ "receipt_sha256s": sorted(source_receipt_hashes),
810
+ "verified": True,
811
+ },
812
+ "dependency_receipts": {
813
+ "count": len(dependency_receipt_hashes),
814
+ "receipt_sha256s": sorted(dependency_receipt_hashes),
815
+ "by_model_slot": {
816
+ model_slot: dict(dependencies)
817
+ for model_slot, dependencies in sorted(dependencies_by_model.items())
818
+ },
819
+ "balanced": True,
820
+ },
821
+ "balance_receipts": {
822
+ "count": len(balance_receipt_hashes),
823
+ "receipt_sha256s": sorted(balance_receipt_hashes),
824
+ "atomic_pair_inventory_complete": True,
825
+ "same_origin_no_op_verified": True,
826
+ "model_seed_replicate_balance_verified": True,
827
+ },
828
+ "treatments": treatments,
829
+ "raw_metric_pooled_across_suites": False,
830
+ "estimand_scope": "this arena and split only",
831
+ }
832
+
833
+
834
+ def _cross_suite_robustness(
835
+ suites: Mapping[str, Mapping[str, Any]],
836
+ ) -> Mapping[str, Any]:
837
+ scorer_identities = {
838
+ (
839
+ str(suite["scorer_identity"]["scorer_id"]),
840
+ str(suite["scorer_identity"]["scorer_revision"]),
841
+ )
842
+ for suite in suites.values()
843
+ }
844
+ schemas = {tuple(str(item) for item in suite["metric_schema"]) for suite in suites.values()}
845
+ identities: dict[str, Mapping[str, Any]] = {}
846
+ normalized: dict[str, dict[str, float]] = defaultdict(dict)
847
+ directions: dict[str, dict[str, str]] = defaultdict(dict)
848
+ for suite_id, suite in suites.items():
849
+ for release_id, treatment in suite["treatments"].items():
850
+ identity = {
851
+ key: treatment[key] for key in ("release_id", "family", "profile_kind", "arm_kind")
852
+ }
853
+ prior = identities.get(release_id)
854
+ if prior is None:
855
+ identities[release_id] = identity
856
+ elif prior != identity:
857
+ raise ArtifactError(
858
+ f"cross-suite release {release_id!r} has mixed treatment identity"
859
+ )
860
+ normalized[release_id][suite_id] = float(treatment["normalized_win_minus_harm"])
861
+ effect = float(treatment["mean_effect_vs_no_op"])
862
+ directions[release_id][suite_id] = (
863
+ "positive" if effect > 0 else "negative" if effect < 0 else "tie"
864
+ )
865
+
866
+ by_treatment: dict[str, Mapping[str, Any]] = {}
867
+ for release_id in sorted(normalized):
868
+ values = tuple(normalized[release_id].values())
869
+ direction_values = tuple(directions[release_id].values())
870
+ by_treatment[release_id] = {
871
+ **identities[release_id],
872
+ "suite_count": len(values),
873
+ "direction_counts": {
874
+ direction: sum(value == direction for value in direction_values)
875
+ for direction in ("positive", "tie", "negative")
876
+ },
877
+ "direction_by_suite": dict(sorted(directions[release_id].items())),
878
+ "normalized_win_minus_harm_by_suite": dict(sorted(normalized[release_id].items())),
879
+ "normalized_heterogeneity_range": (
880
+ max(values) - min(values) if len(values) > 1 else None
881
+ ),
882
+ "direction_consistent_across_suites": (
883
+ len({value for value in direction_values if value != "tie"}) <= 1
884
+ ),
885
+ }
886
+ return {
887
+ "summary_scope": (
888
+ "arena-level direction and win-minus-harm diagnostics only; "
889
+ "no official raw metric is pooled"
890
+ ),
891
+ "suite_estimands_retained": True,
892
+ "raw_metric_pooling_performed": False,
893
+ "metric_schemas_identical": len(schemas) == 1,
894
+ "official_scorer_identities_identical": len(scorer_identities) == 1,
895
+ "incompatible_scorers_or_schemas_are_stratified": True,
896
+ "by_treatment": by_treatment,
897
+ }
898
+
899
+
900
+ def _validate_atomic_pairs(
901
+ payload: Mapping[str, Any],
902
+ *,
903
+ outcomes: Mapping[str, Mapping[str, Any]],
904
+ planned_arm_ids: tuple[str, ...],
905
+ baseline_arm_id: str,
906
+ suite_id: str,
907
+ unit_id: str,
908
+ seed: int,
909
+ source_revision: str,
910
+ checkpoint: Mapping[str, str],
911
+ dependencies: Mapping[str, str],
912
+ scorer_identity: tuple[str, str],
913
+ baseline_consistency: str,
914
+ ) -> tuple[Mapping[str, Any], ...]:
915
+ raw_pairs = _sequence(
916
+ payload.get("atomic_pairs"),
917
+ label="external arena atomic pair receipts",
918
+ )
919
+ expected_treatments = tuple(arm_id for arm_id in planned_arm_ids if arm_id != baseline_arm_id)
920
+ if len(raw_pairs) != len(expected_treatments):
921
+ raise ArtifactError("external arena atomic pair receipt inventory is partial")
922
+
923
+ parsed: list[Mapping[str, Any]] = []
924
+ runtime_instances: set[str] = set()
925
+ receipt_hashes: list[str] = []
926
+ baseline_outcome = outcomes[baseline_arm_id]
927
+ baseline_metrics = _metrics(
928
+ baseline_outcome.get("metrics"),
929
+ label="external arena NO_OP metrics",
930
+ )
931
+ raw_metric_schema = tuple(
932
+ key for key in sorted(baseline_metrics) if key != _VALID_SUCCESS_METRIC
933
+ )
934
+ for index, raw_pair in enumerate(raw_pairs):
935
+ pair = _strict_object(
936
+ raw_pair,
937
+ label=f"external arena atomic pair {index}",
938
+ keys={
939
+ "treatment_arm_id",
940
+ "request_sha256",
941
+ "receipt",
942
+ "receipt_sha256",
943
+ "baseline_consistency_sha256",
944
+ "metric_deltas",
945
+ "resource_receipt_sha256s",
946
+ },
947
+ )
948
+ treatment_id = _text(
949
+ pair["treatment_arm_id"],
950
+ label=f"external arena atomic pair {index} treatment_arm_id",
951
+ )
952
+ if treatment_id != expected_treatments[index]:
953
+ raise ArtifactError(
954
+ "external arena atomic pairs differ from the planned treatment order"
955
+ )
956
+ _sha256(
957
+ pair["request_sha256"],
958
+ label=f"external arena atomic pair {index} request receipt",
959
+ )
960
+ receipt = _atomic_replay_receipt(
961
+ pair["receipt"],
962
+ label=f"external arena atomic pair {index} receipt",
963
+ )
964
+ receipt_sha256 = _sha256(
965
+ pair["receipt_sha256"],
966
+ label=f"external arena atomic pair {index} receipt_sha256",
967
+ )
968
+ if content_hash(pair["receipt"]) != receipt_sha256:
969
+ raise ArtifactError(f"external arena atomic pair {index} receipt checksum mismatch")
970
+ if pair["baseline_consistency_sha256"] != baseline_consistency:
971
+ raise ArtifactError("external arena atomic pair baseline consistency receipt changed")
972
+ if (
973
+ receipt.benchmark_id != suite_id
974
+ or receipt.checkpoint_id != unit_id
975
+ or receipt.source_revision != source_revision
976
+ or receipt.origin_state_hash != checkpoint["origin_state_sha256"]
977
+ or receipt.replay_identity != checkpoint["replay_identity_sha256"]
978
+ or receipt.seed != seed
979
+ or dict(receipt.locked_dependency_hashes) != dict(dependencies)
980
+ or (receipt.scorer_id, receipt.scorer_revision) != scorer_identity
981
+ ):
982
+ raise ArtifactError(
983
+ "external arena atomic receipt source, replay, dependency, or scorer "
984
+ "identity differs from the result"
985
+ )
986
+ by_role = {arm.role: arm for arm in receipt.arms}
987
+ if (
988
+ by_role["baseline"].arm_id != baseline_arm_id
989
+ or by_role["treatment"].arm_id != treatment_id
990
+ ):
991
+ raise ArtifactError("external arena atomic receipt arms differ from the plan")
992
+ for arm_receipt in receipt.arms:
993
+ if arm_receipt.runtime_instance_id in runtime_instances:
994
+ raise ArtifactError("external arena atomic pairs reuse a runtime instance identity")
995
+ runtime_instances.add(arm_receipt.runtime_instance_id)
996
+
997
+ deltas = _metrics(
998
+ pair["metric_deltas"],
999
+ label=f"external arena atomic pair {index} metric deltas",
1000
+ )
1001
+ if tuple(sorted(deltas)) != raw_metric_schema:
1002
+ raise ArtifactError(
1003
+ "external arena atomic delta schema differs from official arm metrics"
1004
+ )
1005
+ treatment_metrics = _metrics(
1006
+ outcomes[treatment_id].get("metrics"),
1007
+ label=f"external arena treatment {treatment_id!r} metrics",
1008
+ )
1009
+ for name, delta in deltas.items():
1010
+ if not math.isclose(
1011
+ delta,
1012
+ treatment_metrics[name] - baseline_metrics[name],
1013
+ rel_tol=0.0,
1014
+ abs_tol=1e-12,
1015
+ ):
1016
+ raise ArtifactError(f"external arena atomic metric delta {name!r} is inconsistent")
1017
+ resource_hashes = _text_sequence(
1018
+ pair["resource_receipt_sha256s"],
1019
+ label=f"external arena atomic pair {index} resource receipt hashes",
1020
+ allow_empty=True,
1021
+ )
1022
+ for digest in resource_hashes:
1023
+ _sha256(
1024
+ digest,
1025
+ label=f"external arena atomic pair {index} resource receipt",
1026
+ )
1027
+ expected_consistency = _baseline_consistency_hash(
1028
+ baseline_outcome,
1029
+ receipt=receipt,
1030
+ )
1031
+ if expected_consistency != baseline_consistency:
1032
+ raise ArtifactError(
1033
+ "external arena NO_OP metrics, trace, usage, or logical receipt "
1034
+ "differs from its baseline consistency hash"
1035
+ )
1036
+ receipt_hashes.append(receipt_sha256)
1037
+ parsed.append(
1038
+ {
1039
+ "treatment_arm_id": treatment_id,
1040
+ "receipt_sha256": receipt_sha256,
1041
+ "bridge_id": receipt.bridge_id,
1042
+ "bridge_revision": receipt.bridge_revision,
1043
+ }
1044
+ )
1045
+
1046
+ baseline_hashes = _text_sequence(
1047
+ outcomes[baseline_arm_id].get("atomic_pair_receipt_sha256s"),
1048
+ label="external arena NO_OP atomic receipt inventory",
1049
+ )
1050
+ if baseline_hashes != tuple(receipt_hashes):
1051
+ raise ArtifactError("external arena NO_OP receipt inventory differs from atomic pairs")
1052
+ for treatment_id, receipt_sha256 in zip(
1053
+ expected_treatments,
1054
+ receipt_hashes,
1055
+ strict=True,
1056
+ ):
1057
+ if outcomes[treatment_id].get("atomic_pair_receipt_sha256") != receipt_sha256:
1058
+ raise ArtifactError(
1059
+ f"external arena treatment {treatment_id!r} receipt binding differs "
1060
+ "from its atomic pair"
1061
+ )
1062
+ bridge_identities = {(str(item["bridge_id"]), str(item["bridge_revision"])) for item in parsed}
1063
+ if len(bridge_identities) != 1:
1064
+ raise ArtifactError("external arena bridge identity changes across atomic pairs")
1065
+ return tuple(parsed)
1066
+
1067
+
1068
+ def _validate_valid_success_composition(
1069
+ outcome: Mapping[str, Any],
1070
+ *,
1071
+ metrics: Mapping[str, float],
1072
+ suite_id: str,
1073
+ arm_id: str,
1074
+ ) -> Mapping[str, Any]:
1075
+ composition = _strict_object(
1076
+ outcome.get("valid_success_composition"),
1077
+ label=f"external arena arm {arm_id!r} valid-success composition",
1078
+ keys={
1079
+ "schema_version",
1080
+ "formula_id",
1081
+ "benchmark_id",
1082
+ "arm_id",
1083
+ "official_success_metric",
1084
+ "components",
1085
+ "valid_success",
1086
+ "outcome_contract_sha256",
1087
+ "scorer_id",
1088
+ "scorer_revision",
1089
+ "trace_hash",
1090
+ "composition_receipt_sha256",
1091
+ },
1092
+ )
1093
+ if (
1094
+ composition["schema_version"] != 1
1095
+ or composition["formula_id"] != _VALID_SUCCESS_FORMULA
1096
+ or composition["benchmark_id"] != suite_id
1097
+ or composition["arm_id"] != arm_id
1098
+ ):
1099
+ raise ArtifactError(
1100
+ f"external arena arm {arm_id!r} valid-success receipt identity is invalid"
1101
+ )
1102
+ official_metric = _text(
1103
+ composition["official_success_metric"],
1104
+ label=f"external arena arm {arm_id!r} official metric",
1105
+ )
1106
+ components = _strict_object(
1107
+ composition["components"],
1108
+ label=f"external arena arm {arm_id!r} valid-success components",
1109
+ keys={
1110
+ "official_success",
1111
+ "contract_preserved",
1112
+ "privileged_leakage",
1113
+ },
1114
+ )
1115
+ official = _unit_interval(
1116
+ components["official_success"],
1117
+ label=f"external arena arm {arm_id!r} official success",
1118
+ )
1119
+ preserved = _unit_interval(
1120
+ components["contract_preserved"],
1121
+ label=f"external arena arm {arm_id!r} contract preservation",
1122
+ )
1123
+ leakage = _unit_interval(
1124
+ components["privileged_leakage"],
1125
+ label=f"external arena arm {arm_id!r} privileged leakage",
1126
+ )
1127
+ valid_success = _unit_interval(
1128
+ composition["valid_success"],
1129
+ label=f"external arena arm {arm_id!r} valid success",
1130
+ )
1131
+ if (
1132
+ official_metric not in metrics
1133
+ or not math.isclose(metrics[official_metric], official, rel_tol=0.0, abs_tol=1e-12)
1134
+ or not math.isclose(
1135
+ metrics.get("contract_preserved", math.nan),
1136
+ preserved,
1137
+ rel_tol=0.0,
1138
+ abs_tol=1e-12,
1139
+ )
1140
+ or not math.isclose(
1141
+ metrics.get("privileged_leakage", math.nan),
1142
+ leakage,
1143
+ rel_tol=0.0,
1144
+ abs_tol=1e-12,
1145
+ )
1146
+ or not math.isclose(
1147
+ metrics.get(_VALID_SUCCESS_METRIC, math.nan),
1148
+ valid_success,
1149
+ rel_tol=0.0,
1150
+ abs_tol=1e-12,
1151
+ )
1152
+ or not math.isclose(
1153
+ official * preserved * (1.0 - leakage),
1154
+ valid_success,
1155
+ rel_tol=0.0,
1156
+ abs_tol=1e-12,
1157
+ )
1158
+ ):
1159
+ raise ArtifactError(
1160
+ f"external arena arm {arm_id!r} valid-success composition is inconsistent"
1161
+ )
1162
+ _sha256(
1163
+ composition["outcome_contract_sha256"],
1164
+ label=f"external arena arm {arm_id!r} outcome contract receipt",
1165
+ )
1166
+ _text(
1167
+ composition["scorer_id"],
1168
+ label=f"external arena arm {arm_id!r} scorer_id",
1169
+ )
1170
+ _sha256(
1171
+ composition["scorer_revision"],
1172
+ label=f"external arena arm {arm_id!r} scorer_revision",
1173
+ )
1174
+ trace_hash = _sha256(
1175
+ composition["trace_hash"],
1176
+ label=f"external arena arm {arm_id!r} trace hash",
1177
+ )
1178
+ if outcome.get("trace_hash") != trace_hash:
1179
+ raise ArtifactError(f"external arena arm {arm_id!r} trace differs from its scorer receipt")
1180
+ usage = _object(
1181
+ outcome.get("usage"),
1182
+ label=f"external arena arm {arm_id!r} usage",
1183
+ )
1184
+ for name, value in usage.items():
1185
+ if (
1186
+ not isinstance(name, str)
1187
+ or not name
1188
+ or isinstance(value, bool)
1189
+ or not isinstance(value, int | float)
1190
+ or not math.isfinite(float(value))
1191
+ or float(value) < 0
1192
+ ):
1193
+ raise ArtifactError(f"external arena arm {arm_id!r} usage receipt is invalid")
1194
+ expected_hash = _sha256(
1195
+ composition["composition_receipt_sha256"],
1196
+ label=f"external arena arm {arm_id!r} composition receipt",
1197
+ )
1198
+ body = {key: value for key, value in composition.items() if key != "composition_receipt_sha256"}
1199
+ if content_hash(body) != expected_hash:
1200
+ raise ArtifactError(f"external arena arm {arm_id!r} composition receipt checksum mismatch")
1201
+ return composition
1202
+
1203
+
1204
+ def _optional_gate_receipt(
1205
+ value: Any,
1206
+ *,
1207
+ manifest: ExperimentManifest,
1208
+ suite_id: str,
1209
+ split: str,
1210
+ source_revision: str,
1211
+ scorer_identity: tuple[str, str],
1212
+ atomic_replay_contract: str,
1213
+ ) -> Mapping[str, Any] | None:
1214
+ if value is None:
1215
+ return None
1216
+ receipt = _strict_object(
1217
+ value,
1218
+ label="external arena gate receipt",
1219
+ keys={
1220
+ "file_sha256",
1221
+ "receipt_sha256",
1222
+ "scope_sha256",
1223
+ "benchmark_id",
1224
+ "split",
1225
+ "source_revision",
1226
+ "manifest_hash",
1227
+ "driver_config_sha256",
1228
+ "bridge",
1229
+ "scorer",
1230
+ "approval",
1231
+ "verification",
1232
+ "unit_inventory_sha256",
1233
+ },
1234
+ )
1235
+ for name in (
1236
+ "file_sha256",
1237
+ "receipt_sha256",
1238
+ "scope_sha256",
1239
+ "manifest_hash",
1240
+ "driver_config_sha256",
1241
+ "unit_inventory_sha256",
1242
+ ):
1243
+ _sha256(receipt[name], label=f"external arena gate {name}")
1244
+ if (
1245
+ receipt["benchmark_id"] != suite_id
1246
+ or receipt["split"] != split
1247
+ or receipt["source_revision"] != source_revision
1248
+ or receipt["manifest_hash"] != manifest.manifest_hash
1249
+ ):
1250
+ raise ArtifactError("external arena gate receipt is bound to another source or manifest")
1251
+ bridge = _strict_object(
1252
+ receipt["bridge"],
1253
+ label="external arena gate bridge",
1254
+ keys={"bridge_id", "bridge_revision"},
1255
+ )
1256
+ _text(bridge["bridge_id"], label="external arena gate bridge_id")
1257
+ _sha256(bridge["bridge_revision"], label="external arena gate bridge_revision")
1258
+ scorer = _strict_object(
1259
+ receipt["scorer"],
1260
+ label="external arena gate scorer",
1261
+ keys={"scorer_id", "scorer_revision"},
1262
+ )
1263
+ if (
1264
+ scorer.get("scorer_id"),
1265
+ scorer.get("scorer_revision"),
1266
+ ) != scorer_identity:
1267
+ raise ArtifactError(
1268
+ "external arena gate official scorer differs from arm composition receipts"
1269
+ )
1270
+ _text(scorer["scorer_id"], label="external arena gate scorer_id")
1271
+ _sha256(scorer["scorer_revision"], label="external arena gate scorer_revision")
1272
+ verification = _strict_object(
1273
+ receipt["verification"],
1274
+ label="external arena gate verification",
1275
+ keys={
1276
+ "atomic_replay_contract",
1277
+ "reset_mode",
1278
+ "reset_verified",
1279
+ "reset_receipt_sha256",
1280
+ "no_op_parity_verified",
1281
+ "no_op_parity_receipt_sha256",
1282
+ },
1283
+ )
1284
+ if (
1285
+ verification["atomic_replay_contract"] != atomic_replay_contract
1286
+ or verification["reset_verified"] is not True
1287
+ or verification["no_op_parity_verified"] is not True
1288
+ ):
1289
+ raise ArtifactError("external arena gate does not approve atomic reset and NO_OP parity")
1290
+ _text(verification["reset_mode"], label="external arena gate reset_mode")
1291
+ _sha256(
1292
+ verification["reset_receipt_sha256"],
1293
+ label="external arena gate reset receipt",
1294
+ )
1295
+ _sha256(
1296
+ verification["no_op_parity_receipt_sha256"],
1297
+ label="external arena gate NO_OP parity receipt",
1298
+ )
1299
+ approval = _strict_object(
1300
+ receipt["approval"],
1301
+ label="external arena gate approval",
1302
+ keys={
1303
+ "approved",
1304
+ "approver_id",
1305
+ "approval_revision",
1306
+ "approved_at",
1307
+ "scope_sha256",
1308
+ },
1309
+ )
1310
+ if approval["approved"] is not True or approval["scope_sha256"] != receipt["scope_sha256"]:
1311
+ raise ArtifactError("external arena gate receipt is not approved for this scope")
1312
+ _text(approval["approver_id"], label="external arena gate approver_id")
1313
+ _sha256(
1314
+ approval["approval_revision"],
1315
+ label="external arena gate approval revision",
1316
+ )
1317
+ _text(approval["approved_at"], label="external arena gate approval timestamp")
1318
+ return receipt
1319
+
1320
+
1321
+ def _checkpoint_identity(value: Any, *, source_revision: str) -> Mapping[str, str]:
1322
+ checkpoint = _strict_object(
1323
+ value,
1324
+ label="external arena checkpoint identity",
1325
+ keys={
1326
+ "identity_sha256",
1327
+ "group_sha256",
1328
+ "visible_state_sha256",
1329
+ "origin_state_sha256",
1330
+ "replay_identity_sha256",
1331
+ "source_revision",
1332
+ },
1333
+ )
1334
+ if checkpoint["source_revision"] != source_revision:
1335
+ raise ArtifactError("external arena checkpoint source differs from the manifest")
1336
+ for name in (
1337
+ "identity_sha256",
1338
+ "visible_state_sha256",
1339
+ "origin_state_sha256",
1340
+ "replay_identity_sha256",
1341
+ ):
1342
+ _sha256(checkpoint[name], label=f"external arena checkpoint {name}")
1343
+ group_id = checkpoint["group_sha256"]
1344
+ if (
1345
+ not isinstance(group_id, str)
1346
+ or not group_id.startswith("group_")
1347
+ or len(group_id) != 70
1348
+ or not _is_sha256(group_id[6:])
1349
+ ):
1350
+ raise ArtifactError("external arena checkpoint group receipt must be group_<sha256>")
1351
+ return checkpoint
1352
+
1353
+
1354
+ def _atomic_replay_receipt(value: Any, *, label: str) -> AtomicReplayReceipt:
1355
+ raw = _strict_object(
1356
+ value,
1357
+ label=label,
1358
+ keys={
1359
+ "benchmark_id",
1360
+ "checkpoint_id",
1361
+ "source_revision",
1362
+ "bridge_id",
1363
+ "bridge_revision",
1364
+ "scorer_id",
1365
+ "scorer_revision",
1366
+ "origin_state_hash",
1367
+ "replay_identity",
1368
+ "seed",
1369
+ "locked_dependency_hashes",
1370
+ "arms",
1371
+ },
1372
+ )
1373
+ raw_arms = _sequence(raw["arms"], label=f"{label} arms")
1374
+ if len(raw_arms) != 2:
1375
+ raise ArtifactError(f"{label} requires exactly two arm receipts")
1376
+ arms: list[AtomicReplayArmReceipt] = []
1377
+ for index, value_arm in enumerate(raw_arms):
1378
+ arm = _strict_object(
1379
+ value_arm,
1380
+ label=f"{label} arm {index}",
1381
+ keys={
1382
+ "role",
1383
+ "arm_id",
1384
+ "origin_state_hash",
1385
+ "replay_identity",
1386
+ "seed",
1387
+ "locked_dependency_hashes",
1388
+ "runtime_instance_id",
1389
+ "completed",
1390
+ },
1391
+ )
1392
+ try:
1393
+ arms.append(
1394
+ AtomicReplayArmReceipt(
1395
+ role=arm["role"],
1396
+ arm_id=arm["arm_id"],
1397
+ origin_state_hash=arm["origin_state_hash"],
1398
+ replay_identity=arm["replay_identity"],
1399
+ seed=arm["seed"],
1400
+ locked_dependency_hashes=arm["locked_dependency_hashes"],
1401
+ runtime_instance_id=arm["runtime_instance_id"],
1402
+ completed=arm["completed"],
1403
+ )
1404
+ )
1405
+ except (TypeError, ValueError) as exc:
1406
+ raise ArtifactError(f"{label} arm {index} is invalid: {exc}") from exc
1407
+ try:
1408
+ return AtomicReplayReceipt(
1409
+ benchmark_id=raw["benchmark_id"],
1410
+ checkpoint_id=raw["checkpoint_id"],
1411
+ source_revision=raw["source_revision"],
1412
+ bridge_id=raw["bridge_id"],
1413
+ bridge_revision=raw["bridge_revision"],
1414
+ scorer_id=raw["scorer_id"],
1415
+ scorer_revision=raw["scorer_revision"],
1416
+ origin_state_hash=raw["origin_state_hash"],
1417
+ replay_identity=raw["replay_identity"],
1418
+ seed=raw["seed"],
1419
+ locked_dependency_hashes=raw["locked_dependency_hashes"],
1420
+ arms=tuple(arms),
1421
+ )
1422
+ except (TypeError, ValueError) as exc:
1423
+ raise ArtifactError(f"{label} is invalid: {exc}") from exc
1424
+
1425
+
1426
+ def _baseline_consistency_hash(
1427
+ baseline_outcome: Mapping[str, Any],
1428
+ *,
1429
+ receipt: AtomicReplayReceipt,
1430
+ ) -> str:
1431
+ metrics = {
1432
+ key: value
1433
+ for key, value in _metrics(
1434
+ baseline_outcome.get("metrics"),
1435
+ label="external arena NO_OP metrics",
1436
+ ).items()
1437
+ if key != _VALID_SUCCESS_METRIC
1438
+ }
1439
+ baseline_receipts = tuple(arm for arm in receipt.arms if arm.role == "baseline")
1440
+ if len(baseline_receipts) != 1:
1441
+ raise ArtifactError("external arena atomic pair lacks one NO_OP receipt")
1442
+ arm = baseline_receipts[0]
1443
+ # ``ResourceUsage.as_dict()`` adds the derived ``total_cost_usd`` property,
1444
+ # while the driver's logical baseline hash is computed from the dataclass
1445
+ # fields. Remove only that deterministic projection before recomputing the
1446
+ # receipt; every primitive usage field remains covered.
1447
+ usage = dict(
1448
+ _object(
1449
+ baseline_outcome.get("usage"),
1450
+ label="external arena NO_OP usage",
1451
+ )
1452
+ )
1453
+ usage.pop("total_cost_usd", None)
1454
+ return content_hash(
1455
+ {
1456
+ "metrics": metrics,
1457
+ "trace_hash": baseline_outcome.get("trace_hash"),
1458
+ "usage": usage,
1459
+ "receipt": {
1460
+ "benchmark_id": receipt.benchmark_id,
1461
+ "checkpoint_id": receipt.checkpoint_id,
1462
+ "source_revision": receipt.source_revision,
1463
+ "bridge_id": receipt.bridge_id,
1464
+ "bridge_revision": receipt.bridge_revision,
1465
+ "scorer_id": receipt.scorer_id,
1466
+ "scorer_revision": receipt.scorer_revision,
1467
+ "origin_state_hash": receipt.origin_state_hash,
1468
+ "replay_identity": receipt.replay_identity,
1469
+ "seed": receipt.seed,
1470
+ "locked_dependency_hashes": receipt.locked_dependency_hashes,
1471
+ "baseline_arm": {
1472
+ "role": arm.role,
1473
+ "arm_id": arm.arm_id,
1474
+ "origin_state_hash": arm.origin_state_hash,
1475
+ "replay_identity": arm.replay_identity,
1476
+ "seed": arm.seed,
1477
+ "locked_dependency_hashes": arm.locked_dependency_hashes,
1478
+ "completed": arm.completed,
1479
+ },
1480
+ },
1481
+ }
1482
+ )
1483
+
1484
+
1485
+ def _validate_cross_model_dependency_balance(
1486
+ suite_id: str,
1487
+ *,
1488
+ dependencies_by_model: Mapping[str, Mapping[str, str]],
1489
+ ) -> None:
1490
+ if not dependencies_by_model:
1491
+ raise ArtifactError(f"stateful suite {suite_id!r} has no dependency receipts")
1492
+ for slot in sorted(_NON_MODEL_DEPENDENCY_SLOTS):
1493
+ values = {str(dependencies[slot]) for dependencies in dependencies_by_model.values()}
1494
+ if len(values) != 1:
1495
+ raise ArtifactError(f"stateful suite {suite_id!r} mixes non-model dependency {slot!r}")
1496
+
1497
+
1498
+ def _require_payload_identity(
1499
+ payload: Mapping[str, Any],
1500
+ *,
1501
+ suite_id: str,
1502
+ split: str,
1503
+ unit_id: str,
1504
+ model_slot: str,
1505
+ seed: int,
1506
+ planned_arm_ids: tuple[str, ...],
1507
+ ) -> None:
1508
+ expected = {
1509
+ "benchmark_id": suite_id,
1510
+ "split": split,
1511
+ "unit_id": unit_id,
1512
+ "model_slot": model_slot,
1513
+ "seed": seed,
1514
+ }
1515
+ for name, value in expected.items():
1516
+ if payload.get(name) != value:
1517
+ raise ArtifactError(f"external arena payload {name} differs from its frozen cell")
1518
+ if (
1519
+ _text_sequence(
1520
+ payload.get("planned_arm_ids"),
1521
+ label="external arena payload planned_arm_ids",
1522
+ )
1523
+ != planned_arm_ids
1524
+ ):
1525
+ raise ArtifactError("external arena payload arm inventory differs from its cell")
1526
+
1527
+
1528
+ def _validate_not_applicable_slots(
1529
+ payload: Mapping[str, Any],
1530
+ *,
1531
+ dependencies: Mapping[str, str],
1532
+ ) -> None:
1533
+ slots = _text_sequence(
1534
+ payload.get("not_applicable_dependency_slots"),
1535
+ label="external arena not-applicable dependency slots",
1536
+ allow_empty=True,
1537
+ )
1538
+ if tuple(sorted(slots)) != slots or len(slots) != len(set(slots)):
1539
+ raise ArtifactError(
1540
+ "external arena not-applicable dependency slots must be sorted and unique"
1541
+ )
1542
+ for slot in slots:
1543
+ if slot not in _DEPENDENCY_SLOTS:
1544
+ raise ArtifactError(
1545
+ "external arena not-applicable dependency receipt contains an unknown slot"
1546
+ )
1547
+ expected = content_hash(
1548
+ {
1549
+ "agentinputlab_dependency": "not_applicable",
1550
+ "schema_version": 1,
1551
+ "slot": slot,
1552
+ }
1553
+ )
1554
+ if dependencies[slot] != expected:
1555
+ raise ArtifactError(
1556
+ f"external arena not-applicable dependency {slot!r} has another hash"
1557
+ )
1558
+
1559
+
1560
+ def _manifest_target(
1561
+ manifest: ExperimentManifest,
1562
+ *,
1563
+ suite_id: str,
1564
+ split: str,
1565
+ ) -> Any:
1566
+ targets = tuple(
1567
+ target
1568
+ for target in manifest.benchmarks
1569
+ if target.cell_id == suite_id and target.split == split
1570
+ )
1571
+ if len(targets) != 1:
1572
+ raise ArtifactError("external arena result has no unique source-locked manifest target")
1573
+ return targets[0]
1574
+
1575
+
1576
+ def _dependency_hashes(
1577
+ value: Any,
1578
+ *,
1579
+ label: str,
1580
+ exact_slots: bool,
1581
+ ) -> Mapping[str, str]:
1582
+ dependencies = _object(value, label=label)
1583
+ if exact_slots and set(dependencies) != _DEPENDENCY_SLOTS:
1584
+ raise ArtifactError(f"{label} must contain the exact shared dependency slots")
1585
+ parsed: dict[str, str] = {}
1586
+ for name, digest in dependencies.items():
1587
+ if not isinstance(name, str) or not name:
1588
+ raise ArtifactError(f"{label} contains an invalid slot")
1589
+ parsed[name] = _sha256(digest, label=f"{label} slot {name!r}")
1590
+ if not parsed:
1591
+ raise ArtifactError(f"{label} must not be empty")
1592
+ return parsed
1593
+
1594
+
1595
+ def _metrics(value: Any, *, label: str) -> Mapping[str, float]:
1596
+ raw = _object(value, label=label)
1597
+ if not raw:
1598
+ raise ArtifactError(f"{label} must not be empty")
1599
+ metrics: dict[str, float] = {}
1600
+ for name, value_item in raw.items():
1601
+ if not isinstance(name, str) or not name:
1602
+ raise ArtifactError(f"{label} contains an invalid metric name")
1603
+ if (
1604
+ isinstance(value_item, bool)
1605
+ or not isinstance(value_item, int | float)
1606
+ or not math.isfinite(float(value_item))
1607
+ ):
1608
+ raise ArtifactError(f"{label} metric {name!r} must be finite numeric")
1609
+ metrics[name] = float(value_item)
1610
+ return metrics
1611
+
1612
+
1613
+ def _bootstrap_mean_interval(
1614
+ values: Sequence[float],
1615
+ *,
1616
+ samples: int,
1617
+ rng: random.Random,
1618
+ ) -> tuple[float, float]:
1619
+ estimates = [fmean(values[rng.randrange(len(values))] for _ in values) for _ in range(samples)]
1620
+ ordered = sorted(estimates)
1621
+ lower = ordered[max(0, math.floor(0.025 * (len(ordered) - 1)))]
1622
+ upper = ordered[min(len(ordered) - 1, math.ceil(0.975 * (len(ordered) - 1)))]
1623
+ return lower, upper
1624
+
1625
+
1626
+ def _run_roots(run_dirs: Sequence[str | Path]) -> tuple[Path, ...]:
1627
+ if isinstance(run_dirs, str | bytes) or not isinstance(run_dirs, Sequence):
1628
+ raise ValidationError("run_dirs must be a sequence of paths")
1629
+ roots = tuple(Path(path).expanduser().resolve() for path in run_dirs)
1630
+ if not roots:
1631
+ raise ValidationError("stateful arena analysis requires at least one run directory")
1632
+ if len(roots) != len(set(roots)):
1633
+ raise ValidationError("stateful arena run directories must be unique")
1634
+ return roots
1635
+
1636
+
1637
+ def _resample_arguments(*, bootstrap_samples: int, random_seed: int) -> None:
1638
+ if (
1639
+ isinstance(bootstrap_samples, bool)
1640
+ or not isinstance(bootstrap_samples, int)
1641
+ or bootstrap_samples < 100
1642
+ ):
1643
+ raise ValidationError("bootstrap_samples must be an integer >= 100")
1644
+ if isinstance(random_seed, bool) or not isinstance(random_seed, int) or random_seed < 0:
1645
+ raise ValidationError("random_seed must be a non-negative integer")
1646
+
1647
+
1648
+ def _metric_name(value: Any) -> str:
1649
+ return _text(value, label="stateful arena analysis metric")
1650
+
1651
+
1652
+ def _read_json_object(path: Path, *, label: str) -> Mapping[str, Any]:
1653
+ try:
1654
+ value = json.loads(path.read_text(encoding="utf-8"))
1655
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
1656
+ raise ArtifactError(f"{label} is missing or invalid") from exc
1657
+ return _object(value, label=label)
1658
+
1659
+
1660
+ def _strict_object(value: Any, *, label: str, keys: set[str]) -> Mapping[str, Any]:
1661
+ result = _object(value, label=label)
1662
+ if set(result) != keys:
1663
+ missing = sorted(keys - set(result))
1664
+ unknown = sorted(set(result) - keys)
1665
+ raise ArtifactError(f"{label} fields are invalid; missing={missing}, unknown={unknown}")
1666
+ return result
1667
+
1668
+
1669
+ def _object(value: Any, *, label: str) -> Mapping[str, Any]:
1670
+ if not isinstance(value, Mapping):
1671
+ raise ArtifactError(f"{label} must be an object")
1672
+ if any(not isinstance(key, str) for key in value):
1673
+ raise ArtifactError(f"{label} keys must be strings")
1674
+ return value
1675
+
1676
+
1677
+ def _sequence(value: Any, *, label: str) -> tuple[Any, ...]:
1678
+ if isinstance(value, str | bytes) or not isinstance(value, Sequence):
1679
+ raise ArtifactError(f"{label} must be an array")
1680
+ return tuple(value)
1681
+
1682
+
1683
+ def _text_sequence(
1684
+ value: Any,
1685
+ *,
1686
+ label: str,
1687
+ allow_empty: bool = False,
1688
+ ) -> tuple[str, ...]:
1689
+ items = _sequence(value, label=label)
1690
+ if not items and not allow_empty:
1691
+ raise ArtifactError(f"{label} must not be empty")
1692
+ return tuple(_text(item, label=f"{label}[{index}]") for index, item in enumerate(items))
1693
+
1694
+
1695
+ def _text(value: Any, *, label: str) -> str:
1696
+ if (
1697
+ not isinstance(value, str)
1698
+ or not value.strip()
1699
+ or value != value.strip()
1700
+ or any(not character.isprintable() for character in value)
1701
+ ):
1702
+ raise ValidationError(f"{label} must be trimmed non-empty text")
1703
+ return value
1704
+
1705
+
1706
+ def _seed(value: Any, *, label: str) -> int:
1707
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
1708
+ raise ArtifactError(f"{label} must be a non-negative integer")
1709
+ return value
1710
+
1711
+
1712
+ def _unit_interval(value: Any, *, label: str) -> float:
1713
+ if (
1714
+ isinstance(value, bool)
1715
+ or not isinstance(value, int | float)
1716
+ or not math.isfinite(float(value))
1717
+ or not 0.0 <= float(value) <= 1.0
1718
+ ):
1719
+ raise ArtifactError(f"{label} must be finite and in [0, 1]")
1720
+ return float(value)
1721
+
1722
+
1723
+ def _sha256(value: Any, *, label: str) -> str:
1724
+ if not isinstance(value, str) or not _is_sha256(value):
1725
+ raise ArtifactError(f"{label} must be a lowercase SHA-256")
1726
+ return value
1727
+
1728
+
1729
+ def _is_sha256(value: str) -> bool:
1730
+ return len(value) == 64 and all(character in "0123456789abcdef" for character in value)
1731
+
1732
+
1733
+ __all__ = [
1734
+ "STATEFUL_ARENA_REPLICATION_ARTIFACT",
1735
+ "STATEFUL_ARENA_REPLICATION_PROTOCOL",
1736
+ "analyze_stateful_arena_replication",
1737
+ "validate_external_arena_record_receipts",
1738
+ ]