loopx 0.4.8__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 (811) hide show
  1. loopx/__init__.py +5 -0
  2. loopx/agent_onboarding.py +654 -0
  3. loopx/agent_registry.py +112 -0
  4. loopx/ark_managed_agent_host.py +59 -0
  5. loopx/authority.py +805 -0
  6. loopx/benchmark.py +2875 -0
  7. loopx/benchmark_adapters/__init__.py +1 -0
  8. loopx/benchmark_adapters/agentissue.py +2644 -0
  9. loopx/benchmark_adapters/agents_last_exam.py +3998 -0
  10. loopx/benchmark_adapters/edgebench.py +322 -0
  11. loopx/benchmark_adapters/skillsbench.py +5978 -0
  12. loopx/benchmark_adapters/skillsbench_acp_failure_policy.py +143 -0
  13. loopx/benchmark_adapters/skillsbench_acp_process.py +31 -0
  14. loopx/benchmark_adapters/skillsbench_acp_relay.py +4832 -0
  15. loopx/benchmark_adapters/skillsbench_batch.py +124 -0
  16. loopx/benchmark_adapters/skillsbench_bridge_guard.py +209 -0
  17. loopx/benchmark_adapters/skillsbench_bridge_summary.py +203 -0
  18. loopx/benchmark_adapters/skillsbench_codex_goal_recovery.py +271 -0
  19. loopx/benchmark_adapters/skillsbench_codex_goal_trace.py +81 -0
  20. loopx/benchmark_adapters/skillsbench_codex_runtime.py +339 -0
  21. loopx/benchmark_adapters/skillsbench_dockerfile_runtime.py +467 -0
  22. loopx/benchmark_adapters/skillsbench_failure_signals.py +652 -0
  23. loopx/benchmark_adapters/skillsbench_proxy_runtime.py +327 -0
  24. loopx/benchmark_adapters/skillsbench_remote_bridge.py +402 -0
  25. loopx/benchmark_adapters/skillsbench_result_discovery.py +143 -0
  26. loopx/benchmark_adapters/skillsbench_runner_profile.py +436 -0
  27. loopx/benchmark_adapters/skillsbench_runner_source.py +99 -0
  28. loopx/benchmark_adapters/skillsbench_setup_preflight.py +771 -0
  29. loopx/benchmark_adapters/skillsbench_signals.py +15 -0
  30. loopx/benchmark_adapters/skillsbench_task_source.py +141 -0
  31. loopx/benchmark_adapters/skillsbench_turn_route.py +723 -0
  32. loopx/benchmark_adapters/skillsbench_turn_runtime.py +1069 -0
  33. loopx/benchmark_adapters/skillsbench_typed_repair.py +689 -0
  34. loopx/benchmark_adapters/skillsbench_uv_cache.py +111 -0
  35. loopx/benchmark_adapters/skillsbench_verifier_bootstrap.py +227 -0
  36. loopx/benchmark_adapters/skillsbench_verifier_cache.py +138 -0
  37. loopx/benchmark_adapters/terminal_bench.py +10078 -0
  38. loopx/benchmark_case_analysis.py +1276 -0
  39. loopx/benchmark_case_state.py +1079 -0
  40. loopx/benchmark_core/__init__.py +239 -0
  41. loopx/benchmark_core/adapter.py +84 -0
  42. loopx/benchmark_core/artifacts.py +517 -0
  43. loopx/benchmark_core/attempts.py +199 -0
  44. loopx/benchmark_core/container_exec.py +216 -0
  45. loopx/benchmark_core/io.py +68 -0
  46. loopx/benchmark_core/lifecycle.py +211 -0
  47. loopx/benchmark_core/loop_protocol.py +689 -0
  48. loopx/benchmark_core/observable_handles.py +348 -0
  49. loopx/benchmark_core/parity.py +256 -0
  50. loopx/benchmark_core/remote_closeout.py +482 -0
  51. loopx/benchmark_core/rounds.py +215 -0
  52. loopx/benchmark_core/route_profile.py +509 -0
  53. loopx/benchmark_core/run_permissions.py +206 -0
  54. loopx/benchmark_core/split_control.py +925 -0
  55. loopx/benchmark_core/turn_fidelity.py +326 -0
  56. loopx/benchmark_ledger.py +3793 -0
  57. loopx/benchmark_ledger_countability.py +372 -0
  58. loopx/benchmark_ledger_current.py +724 -0
  59. loopx/benchmark_trajectory.py +405 -0
  60. loopx/benchmarks/__init__.py +1 -0
  61. loopx/benchmarks/qualification/__init__.py +1 -0
  62. loopx/benchmarks/qualification/release_outcome_baseline.py +360 -0
  63. loopx/benchmarks/read_models/__init__.py +1 -0
  64. loopx/benchmarks/read_models/benchmark_attempt_accounting.py +53 -0
  65. loopx/benchmarks/read_models/benchmark_comparison.py +414 -0
  66. loopx/benchmarks/read_models/benchmark_event_timeline.py +113 -0
  67. loopx/benchmarks/read_models/benchmark_experiment_report.py +475 -0
  68. loopx/benchmarks/read_models/benchmark_learning_ledger.py +137 -0
  69. loopx/benchmarks/read_models/benchmark_lifecycle_contracts.py +228 -0
  70. loopx/benchmarks/read_models/benchmark_projection.py +723 -0
  71. loopx/benchmarks/read_models/benchmark_result.py +146 -0
  72. loopx/benchmarks/read_models/benchmark_run_execution_contract.py +116 -0
  73. loopx/benchmarks/read_models/benchmark_run_failure.py +157 -0
  74. loopx/benchmarks/read_models/benchmark_run_metrics.py +213 -0
  75. loopx/benchmarks/read_models/benchmark_run_post_execution.py +635 -0
  76. loopx/benchmarks/read_models/benchmark_run_pre_execution.py +541 -0
  77. loopx/benchmarks/read_models/benchmark_status_compaction.py +1255 -0
  78. loopx/benchmarks/read_models/benchmark_status_runner.py +780 -0
  79. loopx/benchmarks/read_models/goal_start_control_score.py +857 -0
  80. loopx/benchmarks/read_models/skillsbench_post_run_debug.py +746 -0
  81. loopx/benchmarks/read_models/skillsbench_verifier_attribution.py +269 -0
  82. loopx/bootstrap.py +1116 -0
  83. loopx/bootstrap_command_pack.py +2167 -0
  84. loopx/boundary_authority.py +199 -0
  85. loopx/canary/__init__.py +1 -0
  86. loopx/canary/maintainability_ratchet.py +800 -0
  87. loopx/canary/planner.py +1984 -0
  88. loopx/canary/premerge.py +1130 -0
  89. loopx/canary/qualification_profiles.py +309 -0
  90. loopx/canary/quality_surface_catalog.py +838 -0
  91. loopx/canary/release_profiles.py +51 -0
  92. loopx/canary/runner.py +1107 -0
  93. loopx/canary/smoke_health.py +581 -0
  94. loopx/canary/smoke_profiles.py +212 -0
  95. loopx/capabilities/__init__.py +0 -0
  96. loopx/capabilities/agent_turn_recall/__init__.py +17 -0
  97. loopx/capabilities/agent_turn_recall/cli.py +369 -0
  98. loopx/capabilities/agent_turn_recall/core.py +296 -0
  99. loopx/capabilities/auto_research/__init__.py +16 -0
  100. loopx/capabilities/auto_research/bootstrap_contract.py +157 -0
  101. loopx/capabilities/auto_research/cli.py +1468 -0
  102. loopx/capabilities/auto_research/core.py +11 -0
  103. loopx/capabilities/auto_research/defaults.py +79 -0
  104. loopx/capabilities/auto_research/demo_e2e.py +1848 -0
  105. loopx/capabilities/auto_research/demo_supervisor.py +186 -0
  106. loopx/capabilities/auto_research/evidence_packet.py +767 -0
  107. loopx/capabilities/auto_research/human_view.py +794 -0
  108. loopx/capabilities/auto_research/kernel.py +191 -0
  109. loopx/capabilities/auto_research/knn_demo_workspace.py +322 -0
  110. loopx/capabilities/auto_research/live_evidence.py +248 -0
  111. loopx/capabilities/auto_research/preset.py +176 -0
  112. loopx/capabilities/auto_research/research_state.py +1085 -0
  113. loopx/capabilities/auto_research/role_profiles.py +394 -0
  114. loopx/capabilities/auto_research/rollout_append.py +97 -0
  115. loopx/capabilities/auto_research/terminal_result_contract.py +422 -0
  116. loopx/capabilities/auto_research/terminal_result_projection.py +171 -0
  117. loopx/capabilities/auto_research/terminal_result_query.py +233 -0
  118. loopx/capabilities/auto_research/terminal_results.py +349 -0
  119. loopx/capabilities/auto_research/user_contract.py +190 -0
  120. loopx/capabilities/auto_research/worker_loop.py +163 -0
  121. loopx/capabilities/auto_research/worker_runtime.py +777 -0
  122. loopx/capabilities/auto_research/worker_skill/SKILL.md +343 -0
  123. loopx/capabilities/benchmark_toolkit/__init__.py +19 -0
  124. loopx/capabilities/benchmark_toolkit/integrity.py +387 -0
  125. loopx/capabilities/catalog.py +1875 -0
  126. loopx/capabilities/change_quality/__init__.py +19 -0
  127. loopx/capabilities/change_quality/cli.py +171 -0
  128. loopx/capabilities/change_quality/context.py +156 -0
  129. loopx/capabilities/change_quality/oracles.py +269 -0
  130. loopx/capabilities/change_quality/policy.py +34 -0
  131. loopx/capabilities/change_quality/receipt.py +482 -0
  132. loopx/capabilities/change_quality/result.py +493 -0
  133. loopx/capabilities/change_quality/scope.py +171 -0
  134. loopx/capabilities/change_quality/shadow.py +680 -0
  135. loopx/capabilities/content_ops/__init__.py +0 -0
  136. loopx/capabilities/content_ops/cli.py +649 -0
  137. loopx/capabilities/content_ops/connector_packets.py +164 -0
  138. loopx/capabilities/content_ops/item_lifecycle.py +1000 -0
  139. loopx/capabilities/content_ops/layout.py +451 -0
  140. loopx/capabilities/content_ops/markdown.py +456 -0
  141. loopx/capabilities/content_ops/schemas.py +51 -0
  142. loopx/capabilities/content_ops/social_browser_x.py +107 -0
  143. loopx/capabilities/content_ops/surface.py +1956 -0
  144. loopx/capabilities/content_ops/templates/layout-catalog-v0.json +72 -0
  145. loopx/capabilities/context_providers/__init__.py +36 -0
  146. loopx/capabilities/context_providers/base.py +189 -0
  147. loopx/capabilities/context_providers/factory.py +32 -0
  148. loopx/capabilities/context_providers/openviking.py +702 -0
  149. loopx/capabilities/context_providers/service_ownership.py +185 -0
  150. loopx/capabilities/decision_context/__init__.py +129 -0
  151. loopx/capabilities/decision_context/architecture.py +83 -0
  152. loopx/capabilities/decision_context/assembler.py +849 -0
  153. loopx/capabilities/decision_context/catalog_entry.py +195 -0
  154. loopx/capabilities/decision_context/cli.py +310 -0
  155. loopx/capabilities/decision_context/cursor_commit.py +535 -0
  156. loopx/capabilities/decision_context/outcome_feedback.py +352 -0
  157. loopx/capabilities/decision_context/packets.py +654 -0
  158. loopx/capabilities/decision_context/private_state.py +189 -0
  159. loopx/capabilities/decision_context/profile.py +453 -0
  160. loopx/capabilities/decision_context/providers.py +228 -0
  161. loopx/capabilities/decision_context/review_settlement.py +136 -0
  162. loopx/capabilities/decision_context/runtime.py +273 -0
  163. loopx/capabilities/decision_context/sources.py +415 -0
  164. loopx/capabilities/explore/__init__.py +1 -0
  165. loopx/capabilities/explore/activation.py +198 -0
  166. loopx/capabilities/explore/adaptive_replay_planner.py +221 -0
  167. loopx/capabilities/explore/child_replay_runtime.py +463 -0
  168. loopx/capabilities/explore/composition_frontier.py +291 -0
  169. loopx/capabilities/explore/counterfactual_runtime.py +578 -0
  170. loopx/capabilities/explore/episode_runtime.py +647 -0
  171. loopx/capabilities/explore/harness_checkpoint.py +171 -0
  172. loopx/capabilities/explore/harness_gate.py +115 -0
  173. loopx/capabilities/explore/harness_runtime.py +1124 -0
  174. loopx/capabilities/explore/replay_metrics.py +206 -0
  175. loopx/capabilities/explore/replay_runtime.py +1271 -0
  176. loopx/capabilities/explore/resource_portfolio.py +173 -0
  177. loopx/capabilities/explore/result_log.py +974 -0
  178. loopx/capabilities/explore/router_state.py +432 -0
  179. loopx/capabilities/explore/source_history_reconcile.py +255 -0
  180. loopx/capabilities/explore/speculative_scheduler.py +498 -0
  181. loopx/capabilities/explore/todo_branch_plan.py +650 -0
  182. loopx/capabilities/explore/todo_evidence.py +141 -0
  183. loopx/capabilities/explore/trace_runtime.py +284 -0
  184. loopx/capabilities/explore/worker_branch_plan.py +1257 -0
  185. loopx/capabilities/integration_branch/__init__.py +13 -0
  186. loopx/capabilities/integration_branch/cli.py +148 -0
  187. loopx/capabilities/integration_branch/core.py +916 -0
  188. loopx/capabilities/issue_fix/__init__.py +19 -0
  189. loopx/capabilities/issue_fix/acceptance_loop.py +1050 -0
  190. loopx/capabilities/issue_fix/candidate_evidence.py +503 -0
  191. loopx/capabilities/issue_fix/candidate_preflight.py +676 -0
  192. loopx/capabilities/issue_fix/cli.py +1822 -0
  193. loopx/capabilities/issue_fix/cli_input.py +87 -0
  194. loopx/capabilities/issue_fix/content_ops_cli.py +148 -0
  195. loopx/capabilities/issue_fix/discovered_issue_promotion.py +947 -0
  196. loopx/capabilities/issue_fix/explore_projection.py +710 -0
  197. loopx/capabilities/issue_fix/feasibility.py +542 -0
  198. loopx/capabilities/issue_fix/github_public.py +661 -0
  199. loopx/capabilities/issue_fix/intake_surface.py +832 -0
  200. loopx/capabilities/issue_fix/metadata_preview.py +218 -0
  201. loopx/capabilities/issue_fix/metrics_projection.py +1340 -0
  202. loopx/capabilities/issue_fix/metrics_supplement.py +634 -0
  203. loopx/capabilities/issue_fix/metrics_supplement_cli.py +127 -0
  204. loopx/capabilities/issue_fix/outcome_projection.py +1235 -0
  205. loopx/capabilities/issue_fix/periodic_report.py +189 -0
  206. loopx/capabilities/issue_fix/pr_description.py +418 -0
  207. loopx/capabilities/issue_fix/pr_gate_reconcile.py +496 -0
  208. loopx/capabilities/issue_fix/pr_gate_reconcile_cli.py +464 -0
  209. loopx/capabilities/issue_fix/pr_lifecycle.py +1327 -0
  210. loopx/capabilities/issue_fix/pr_lifecycle_rollout.py +85 -0
  211. loopx/capabilities/issue_fix/pr_monitor_materialization.py +257 -0
  212. loopx/capabilities/issue_fix/pr_review_ack.py +439 -0
  213. loopx/capabilities/issue_fix/provider_hooks.py +24 -0
  214. loopx/capabilities/issue_fix/repository_commit_evidence.py +186 -0
  215. loopx/capabilities/issue_fix/repository_context.py +457 -0
  216. loopx/capabilities/issue_fix/repository_memory.py +459 -0
  217. loopx/capabilities/issue_fix/repository_memory_provider.py +1454 -0
  218. loopx/capabilities/issue_fix/repository_snapshot.py +454 -0
  219. loopx/capabilities/issue_fix/reviewer_cli.py +917 -0
  220. loopx/capabilities/issue_fix/reviewer_notification.py +882 -0
  221. loopx/capabilities/issue_fix/reviewer_notification_drain.py +942 -0
  222. loopx/capabilities/issue_fix/reviewer_recommendation.py +1057 -0
  223. loopx/capabilities/issue_fix/reviewer_request.py +1282 -0
  224. loopx/capabilities/issue_fix/reward_memory.py +879 -0
  225. loopx/capabilities/issue_fix/workflow_plan.py +1286 -0
  226. loopx/capabilities/material_lifecycle/__init__.py +161 -0
  227. loopx/capabilities/material_lifecycle/_validation.py +183 -0
  228. loopx/capabilities/material_lifecycle/apply.py +672 -0
  229. loopx/capabilities/material_lifecycle/architecture.py +122 -0
  230. loopx/capabilities/material_lifecycle/cli.py +161 -0
  231. loopx/capabilities/material_lifecycle/decision_planning.py +470 -0
  232. loopx/capabilities/material_lifecycle/explore_execution.py +306 -0
  233. loopx/capabilities/material_lifecycle/intake.py +869 -0
  234. loopx/capabilities/material_lifecycle/inventory.py +147 -0
  235. loopx/capabilities/material_lifecycle/lifecycle.py +98 -0
  236. loopx/capabilities/material_lifecycle/preparation.py +147 -0
  237. loopx/capabilities/material_lifecycle/project_skill.py +83 -0
  238. loopx/capabilities/material_lifecycle/ranking.py +267 -0
  239. loopx/capabilities/material_lifecycle/readable_projection.py +500 -0
  240. loopx/capabilities/material_lifecycle/rebuild.py +480 -0
  241. loopx/capabilities/material_lifecycle/settlement.py +238 -0
  242. loopx/capabilities/periodic_report/__init__.py +71 -0
  243. loopx/capabilities/periodic_report/adapters.py +939 -0
  244. loopx/capabilities/periodic_report/archive.py +422 -0
  245. loopx/capabilities/periodic_report/bindings.py +705 -0
  246. loopx/capabilities/periodic_report/cli.py +277 -0
  247. loopx/capabilities/periodic_report/core.py +691 -0
  248. loopx/capabilities/periodic_report/extension_envelope.py +66 -0
  249. loopx/capabilities/periodic_report/presets.py +103 -0
  250. loopx/capabilities/periodic_report/profile.py +235 -0
  251. loopx/capabilities/periodic_report/project_progress.py +179 -0
  252. loopx/capabilities/periodic_report/triggers.py +452 -0
  253. loopx/capabilities/pr_review_queue/__init__.py +17 -0
  254. loopx/capabilities/pr_review_queue/core.py +506 -0
  255. loopx/capabilities/pr_review_queue/review_contract.py +506 -0
  256. loopx/capabilities/registry.py +192 -0
  257. loopx/capabilities/reward_memory/__init__.py +75 -0
  258. loopx/capabilities/reward_memory/application.py +819 -0
  259. loopx/capabilities/reward_memory/architecture.py +572 -0
  260. loopx/capabilities/reward_memory/candidate_review.py +511 -0
  261. loopx/capabilities/reward_memory/cli.py +469 -0
  262. loopx/capabilities/reward_memory/dogfood.py +574 -0
  263. loopx/capabilities/reward_memory/evaluation.py +296 -0
  264. loopx/capabilities/reward_memory/evaluation_fixtures.py +362 -0
  265. loopx/capabilities/reward_memory/experiment.py +567 -0
  266. loopx/capabilities/reward_memory/health.py +222 -0
  267. loopx/capabilities/reward_memory/ingestion.py +519 -0
  268. loopx/capabilities/reward_memory/registry.py +600 -0
  269. loopx/capabilities/reward_memory/runtime_hooks.py +312 -0
  270. loopx/capabilities/reward_memory/scoped_feedback.py +173 -0
  271. loopx/capabilities/semantic_preference/__init__.py +12 -0
  272. loopx/capabilities/semantic_preference/cli.py +189 -0
  273. loopx/capabilities/semantic_preference/contract.py +592 -0
  274. loopx/capabilities/semantic_preference/reward_memory.py +62 -0
  275. loopx/capabilities/value_connectors/__init__.py +1 -0
  276. loopx/capabilities/value_connectors/cli.py +401 -0
  277. loopx/capabilities/value_connectors/finance_extension_migration.py +108 -0
  278. loopx/capabilities/value_connectors/install_check.py +147 -0
  279. loopx/capabilities/value_connectors/planner.py +733 -0
  280. loopx/capabilities/value_connectors/source_map.py +446 -0
  281. loopx/claude_goal_baseline.py +138 -0
  282. loopx/claude_goal_mode/__init__.py +23 -0
  283. loopx/claude_goal_mode/hooks/goal_policy.py +212 -0
  284. loopx/claude_goal_mode/hooks/goal_state.py +139 -0
  285. loopx/claude_goal_mode/mcp/loopx_mcp.py +167 -0
  286. loopx/claude_goal_mode/scripts/connect.py +103 -0
  287. loopx/claude_goal_mode/scripts/goalmode_cmd.py +241 -0
  288. loopx/claude_goal_mode/scripts/install.py +328 -0
  289. loopx/claude_goal_mode/statusline/goal_status.py +97 -0
  290. loopx/cli.py +836 -0
  291. loopx/cli_commands/__init__.py +334 -0
  292. loopx/cli_commands/_host_thread.py +13 -0
  293. loopx/cli_commands/agentissue_runner_flow.py +447 -0
  294. loopx/cli_commands/agents_last_exam.py +160 -0
  295. loopx/cli_commands/agents_last_exam_baked_input.py +302 -0
  296. loopx/cli_commands/agents_last_exam_host_codex.py +374 -0
  297. loopx/cli_commands/agents_last_exam_launch_dry_run.py +372 -0
  298. loopx/cli_commands/agents_last_exam_local_plan.py +322 -0
  299. loopx/cli_commands/agents_last_exam_runner_source.py +352 -0
  300. loopx/cli_commands/agents_last_exam_task_material.py +335 -0
  301. loopx/cli_commands/agents_last_exam_validation_gate.py +236 -0
  302. loopx/cli_commands/benchmark_boundary.py +499 -0
  303. loopx/cli_commands/benchmark_dispatch.py +161 -0
  304. loopx/cli_commands/benchmark_release_outcome.py +123 -0
  305. loopx/cli_commands/benchmark_review_lifecycle.py +1275 -0
  306. loopx/cli_commands/benchmark_run_ledger.py +763 -0
  307. loopx/cli_commands/benchmark_run_ledger_case_analysis.py +249 -0
  308. loopx/cli_commands/benchmark_run_ledger_classification.py +45 -0
  309. loopx/cli_commands/benchmark_run_ledger_maintenance.py +486 -0
  310. loopx/cli_commands/benchmark_run_ledger_maintenance_registration.py +342 -0
  311. loopx/cli_commands/benchmark_run_ledger_maintenance_rendering.py +233 -0
  312. loopx/cli_commands/benchmark_run_ledger_parity.py +92 -0
  313. loopx/cli_commands/bootstrap_connect.py +238 -0
  314. loopx/cli_commands/canary.py +707 -0
  315. loopx/cli_commands/canary_release_qualification.py +79 -0
  316. loopx/cli_commands/capability.py +96 -0
  317. loopx/cli_commands/doctor.py +43 -0
  318. loopx/cli_commands/dreaming.py +143 -0
  319. loopx/cli_commands/edgebench.py +205 -0
  320. loopx/cli_commands/evidence_log.py +275 -0
  321. loopx/cli_commands/explore.py +989 -0
  322. loopx/cli_commands/explore_planning_commands.py +157 -0
  323. loopx/cli_commands/extension.py +271 -0
  324. loopx/cli_commands/first_run_report.py +73 -0
  325. loopx/cli_commands/goal_channel.py +656 -0
  326. loopx/cli_commands/handoff_mode.py +158 -0
  327. loopx/cli_commands/history.py +622 -0
  328. loopx/cli_commands/host_mode_plan.py +113 -0
  329. loopx/cli_commands/lark_inbox.py +431 -0
  330. loopx/cli_commands/lark_kanban.py +629 -0
  331. loopx/cli_commands/ml_experiment.py +321 -0
  332. loopx/cli_commands/multi_agent.py +211 -0
  333. loopx/cli_commands/opencode2_goal_worker.py +217 -0
  334. loopx/cli_commands/pr_review.py +167 -0
  335. loopx/cli_commands/presentation.py +218 -0
  336. loopx/cli_commands/preset.py +96 -0
  337. loopx/cli_commands/project.py +150 -0
  338. loopx/cli_commands/project_lifecycle.py +915 -0
  339. loopx/cli_commands/quota.py +859 -0
  340. loopx/cli_commands/quota_registration.py +241 -0
  341. loopx/cli_commands/quota_request.py +113 -0
  342. loopx/cli_commands/ready_score.py +110 -0
  343. loopx/cli_commands/registry_admin.py +975 -0
  344. loopx/cli_commands/registry_admin_configure.py +344 -0
  345. loopx/cli_commands/registry_admin_peer.py +84 -0
  346. loopx/cli_commands/registry_authority.py +218 -0
  347. loopx/cli_commands/review_batch.py +146 -0
  348. loopx/cli_commands/slash_commands.py +145 -0
  349. loopx/cli_commands/start_goal.py +251 -0
  350. loopx/cli_commands/starter.py +175 -0
  351. loopx/cli_commands/starter_bootstrap.py +179 -0
  352. loopx/cli_commands/starter_bootstrap_registration.py +198 -0
  353. loopx/cli_commands/starter_runtime_idle.py +107 -0
  354. loopx/cli_commands/starter_scheduler.py +207 -0
  355. loopx/cli_commands/starter_session_runtime.py +152 -0
  356. loopx/cli_commands/starter_visible_common.py +54 -0
  357. loopx/cli_commands/starter_visible_driver.py +161 -0
  358. loopx/cli_commands/starter_visible_pilot.py +278 -0
  359. loopx/cli_commands/status.py +867 -0
  360. loopx/cli_commands/status_registration.py +239 -0
  361. loopx/cli_commands/summary_all.py +222 -0
  362. loopx/cli_commands/support_control.py +809 -0
  363. loopx/cli_commands/support_control_registry.py +68 -0
  364. loopx/cli_commands/support_control_supervisor.py +289 -0
  365. loopx/cli_commands/task_lease.py +306 -0
  366. loopx/cli_commands/terminal_bench_adapter.py +717 -0
  367. loopx/cli_commands/terminal_bench_environment_result.py +1246 -0
  368. loopx/cli_commands/todo.py +940 -0
  369. loopx/cli_commands/todo_argument_validation.py +572 -0
  370. loopx/cli_commands/todo_event.py +114 -0
  371. loopx/cli_commands/turn.py +804 -0
  372. loopx/cli_commands/version.py +46 -0
  373. loopx/cli_commands/worker_bridge.py +659 -0
  374. loopx/cli_rollout.py +314 -0
  375. loopx/codex_cli_goal_tui.py +672 -0
  376. loopx/codex_cli_probe.py +1530 -0
  377. loopx/codex_cli_probe_markdown.py +935 -0
  378. loopx/codex_cli_runtime_probe.py +733 -0
  379. loopx/codex_cli_scheduler.py +564 -0
  380. loopx/codex_goal_baseline.py +620 -0
  381. loopx/configuration_catalog.py +617 -0
  382. loopx/configure_goal.py +1375 -0
  383. loopx/contract.py +996 -0
  384. loopx/control_plane/__init__.py +71 -0
  385. loopx/control_plane/agents/__init__.py +1 -0
  386. loopx/control_plane/agents/agent_lane_recommendation.py +516 -0
  387. loopx/control_plane/agents/agent_scope.py +1578 -0
  388. loopx/control_plane/agents/agent_scope_frontier.py +60 -0
  389. loopx/control_plane/agents/capability_gate.py +531 -0
  390. loopx/control_plane/agents/identity.py +140 -0
  391. loopx/control_plane/agents/legacy_migration.py +169 -0
  392. loopx/control_plane/agents/management_projection.py +658 -0
  393. loopx/control_plane/agents/material_frontier.py +608 -0
  394. loopx/control_plane/agents/material_handoff.py +156 -0
  395. loopx/control_plane/agents/multi_agent/__init__.py +1 -0
  396. loopx/control_plane/agents/multi_agent/codex_executable.py +207 -0
  397. loopx/control_plane/agents/multi_agent/collective_round_ledger.py +387 -0
  398. loopx/control_plane/agents/multi_agent/contract.py +474 -0
  399. loopx/control_plane/agents/multi_agent/recipe.py +110 -0
  400. loopx/control_plane/agents/multi_agent/role_successor.py +297 -0
  401. loopx/control_plane/agents/multi_agent/runtime_scripts.py +426 -0
  402. loopx/control_plane/agents/multi_agent/visible_launch_policy.py +149 -0
  403. loopx/control_plane/agents/multi_agent/visible_wake_scheduler.py +392 -0
  404. loopx/control_plane/agents/profile.py +216 -0
  405. loopx/control_plane/agents/runtime_model.py +73 -0
  406. loopx/control_plane/agents/subagent_activity.py +164 -0
  407. loopx/control_plane/agents/supervisor.py +544 -0
  408. loopx/control_plane/agents/supervisor_events.py +462 -0
  409. loopx/control_plane/agents/supervisor_inject.py +204 -0
  410. loopx/control_plane/agents/work_mode.py +56 -0
  411. loopx/control_plane/agents/workspace_guard.py +364 -0
  412. loopx/control_plane/effect_program.py +644 -0
  413. loopx/control_plane/goals/__init__.py +1 -0
  414. loopx/control_plane/goals/active_state_event_projection.py +103 -0
  415. loopx/control_plane/goals/active_state_metadata.py +47 -0
  416. loopx/control_plane/goals/active_state_sections.py +58 -0
  417. loopx/control_plane/goals/configure_goal_service.py +354 -0
  418. loopx/control_plane/goals/contract_health.py +132 -0
  419. loopx/control_plane/goals/dreaming.py +152 -0
  420. loopx/control_plane/goals/global_registry_health.py +199 -0
  421. loopx/control_plane/goals/global_registry_shadow.py +33 -0
  422. loopx/control_plane/goals/goal_channel.py +34 -0
  423. loopx/control_plane/goals/goal_channel_projection.py +560 -0
  424. loopx/control_plane/goals/goal_frontier/__init__.py +1917 -0
  425. loopx/control_plane/goals/goal_frontier/ack_policy.py +149 -0
  426. loopx/control_plane/goals/goal_frontier/outcome_continuity.py +437 -0
  427. loopx/control_plane/goals/goal_frontier/replan_rules.py +210 -0
  428. loopx/control_plane/goals/goal_frontier/semantic_history.py +314 -0
  429. loopx/control_plane/goals/goal_frontier/terminal.py +180 -0
  430. loopx/control_plane/goals/goal_vision.py +443 -0
  431. loopx/control_plane/goals/goal_vision_policy.py +36 -0
  432. loopx/control_plane/goals/goal_vision_state.py +62 -0
  433. loopx/control_plane/goals/goal_vision_wait.py +290 -0
  434. loopx/control_plane/goals/path_resolution.py +20 -0
  435. loopx/control_plane/goals/start_contract.py +206 -0
  436. loopx/control_plane/goals/vision_checkpoint.py +92 -0
  437. loopx/control_plane/handoff/__init__.py +1 -0
  438. loopx/control_plane/handoff/cross_runtime_impl_review.py +311 -0
  439. loopx/control_plane/handoff/delivery_contract.py +161 -0
  440. loopx/control_plane/handoff/handoff_runs.py +71 -0
  441. loopx/control_plane/handoff/project_handoff.py +155 -0
  442. loopx/control_plane/handoff/review_batch.py +463 -0
  443. loopx/control_plane/handoff/review_packet_context.py +216 -0
  444. loopx/control_plane/heartbeat/agent.py +173 -0
  445. loopx/control_plane/heartbeat/budget.py +66 -0
  446. loopx/control_plane/heartbeat/builder.py +501 -0
  447. loopx/control_plane/heartbeat/host.py +64 -0
  448. loopx/control_plane/heartbeat/rules.py +68 -0
  449. loopx/control_plane/heartbeat/task_body.py +759 -0
  450. loopx/control_plane/heartbeat/visible_goal.py +86 -0
  451. loopx/control_plane/projects/__init__.py +1 -0
  452. loopx/control_plane/projects/contract.py +25 -0
  453. loopx/control_plane/projects/registry.py +663 -0
  454. loopx/control_plane/quota/__init__.py +1 -0
  455. loopx/control_plane/quota/cli_projection.py +704 -0
  456. loopx/control_plane/quota/decision_summary.py +431 -0
  457. loopx/control_plane/quota/effect_program.py +152 -0
  458. loopx/control_plane/quota/error_codes.py +19 -0
  459. loopx/control_plane/quota/goal_boundary.py +464 -0
  460. loopx/control_plane/quota/heartbeat_receipt.py +277 -0
  461. loopx/control_plane/quota/heartbeat_recommendation.py +718 -0
  462. loopx/control_plane/quota/host_poll_receipts.py +162 -0
  463. loopx/control_plane/quota/live_decision.py +142 -0
  464. loopx/control_plane/quota/monitor_poll.py +786 -0
  465. loopx/control_plane/quota/policy_constants.py +40 -0
  466. loopx/control_plane/quota/projection_repair.py +262 -0
  467. loopx/control_plane/quota/recent_runs.py +210 -0
  468. loopx/control_plane/quota/scheduler_ack.py +490 -0
  469. loopx/control_plane/quota/selected_todo_projection.py +139 -0
  470. loopx/control_plane/quota/settlement.py +437 -0
  471. loopx/control_plane/quota/settlement_cli.py +246 -0
  472. loopx/control_plane/quota/settlement_validation.py +64 -0
  473. loopx/control_plane/quota/settlement_workspace_causality.py +180 -0
  474. loopx/control_plane/quota/should_run.py +249 -0
  475. loopx/control_plane/quota/should_run_packet.py +1165 -0
  476. loopx/control_plane/quota/should_run_prepare.py +675 -0
  477. loopx/control_plane/quota/slot_accounting.py +1123 -0
  478. loopx/control_plane/quota/spend_sources.py +11 -0
  479. loopx/control_plane/quota/stall_repair.py +397 -0
  480. loopx/control_plane/quota/states.py +29 -0
  481. loopx/control_plane/quota/task_orchestration.py +448 -0
  482. loopx/control_plane/quota/task_orchestration_admission.py +497 -0
  483. loopx/control_plane/quota/turn_envelope.py +889 -0
  484. loopx/control_plane/quota/usage_summary.py +140 -0
  485. loopx/control_plane/reward_memory.py +43 -0
  486. loopx/control_plane/runtime/__init__.py +2 -0
  487. loopx/control_plane/runtime/active_user_assisted_pilot.py +275 -0
  488. loopx/control_plane/runtime/agent_scoped_evidence_log.py +435 -0
  489. loopx/control_plane/runtime/decision_freshness.py +203 -0
  490. loopx/control_plane/runtime/event_ledger.py +197 -0
  491. loopx/control_plane/runtime/event_store_migration_bridge.py +196 -0
  492. loopx/control_plane/runtime/goal_project_route.py +70 -0
  493. loopx/control_plane/runtime/local_state_write_correctness.py +242 -0
  494. loopx/control_plane/runtime/promotion_readiness.py +152 -0
  495. loopx/control_plane/runtime/public_safety.py +120 -0
  496. loopx/control_plane/runtime/run_artifacts.py +78 -0
  497. loopx/control_plane/runtime/run_compaction.py +397 -0
  498. loopx/control_plane/runtime/run_context_retention.py +241 -0
  499. loopx/control_plane/runtime/run_history.py +132 -0
  500. loopx/control_plane/runtime/run_index_duplicates.py +205 -0
  501. loopx/control_plane/runtime/run_index_rebuild.py +263 -0
  502. loopx/control_plane/runtime/run_ingest_health.py +336 -0
  503. loopx/control_plane/runtime/runtime_projection_route.py +624 -0
  504. loopx/control_plane/runtime/runtime_projection_writer.py +98 -0
  505. loopx/control_plane/runtime/session_runtime.py +339 -0
  506. loopx/control_plane/runtime/shared_runtime_material_projection.py +332 -0
  507. loopx/control_plane/runtime/shared_runtime_refresh_projection.py +183 -0
  508. loopx/control_plane/runtime/stale_latest_run.py +90 -0
  509. loopx/control_plane/runtime/status_classifications.py +49 -0
  510. loopx/control_plane/runtime/status_projection_cache.py +235 -0
  511. loopx/control_plane/runtime/stride_observation.py +144 -0
  512. loopx/control_plane/runtime/time.py +39 -0
  513. loopx/control_plane/runtime/trajectory_hygiene.py +149 -0
  514. loopx/control_plane/runtime/validation_command.py +69 -0
  515. loopx/control_plane/scheduler/__init__.py +1 -0
  516. loopx/control_plane/scheduler/ack.py +329 -0
  517. loopx/control_plane/scheduler/arbitration.py +188 -0
  518. loopx/control_plane/scheduler/automation_liveness.py +183 -0
  519. loopx/control_plane/scheduler/execution_context.py +555 -0
  520. loopx/control_plane/scheduler/external_evidence_observation.py +428 -0
  521. loopx/control_plane/scheduler/monitor_display.py +143 -0
  522. loopx/control_plane/scheduler/monitor_poll_policy.py +161 -0
  523. loopx/control_plane/scheduler/monitor_poll_writeback.py +351 -0
  524. loopx/control_plane/scheduler/monitor_target.py +64 -0
  525. loopx/control_plane/scheduler/monitor_todo.py +146 -0
  526. loopx/control_plane/scheduler/monitor_wait.py +237 -0
  527. loopx/control_plane/scheduler/scheduler_hint.py +1284 -0
  528. loopx/control_plane/scheduler/state.py +354 -0
  529. loopx/control_plane/scheduler/state_transition_rules.py +179 -0
  530. loopx/control_plane/scheduler/time.py +10 -0
  531. loopx/control_plane/settlement_driver.py +293 -0
  532. loopx/control_plane/status/__init__.py +6 -0
  533. loopx/control_plane/status/active_state_projection.py +105 -0
  534. loopx/control_plane/status/agent_lane_projection.py +375 -0
  535. loopx/control_plane/status/attention_projection.py +74 -0
  536. loopx/control_plane/status/autonomous_replan_projection.py +103 -0
  537. loopx/control_plane/status/collection.py +140 -0
  538. loopx/control_plane/status/contract_projection.py +31 -0
  539. loopx/control_plane/status/dreaming_projection.py +52 -0
  540. loopx/control_plane/status/goal_attention_projection.py +157 -0
  541. loopx/control_plane/status/lifecycle_projection.py +110 -0
  542. loopx/control_plane/status/monitor_display_projection.py +69 -0
  543. loopx/control_plane/status/registry_health_projection.py +75 -0
  544. loopx/control_plane/status/run_projection.py +70 -0
  545. loopx/control_plane/status/runtime_summaries.py +161 -0
  546. loopx/control_plane/testing/__init__.py +1 -0
  547. loopx/control_plane/testing/actual_default_model_behavior_portfolio.py +1371 -0
  548. loopx/control_plane/testing/canary_harness.py +182 -0
  549. loopx/control_plane/testing/capability_monitor_repair_tool_behavior.py +674 -0
  550. loopx/control_plane/testing/cli_output_budget.py +807 -0
  551. loopx/control_plane/testing/cli_output_differential.py +250 -0
  552. loopx/control_plane/testing/cli_output_semantics.py +87 -0
  553. loopx/control_plane/testing/control_plane_composition_scenarios.py +225 -0
  554. loopx/control_plane/testing/decision_replay.py +268 -0
  555. loopx/control_plane/testing/doubao_model_behavior_actor.py +559 -0
  556. loopx/control_plane/testing/model_behavior_corpus.py +344 -0
  557. loopx/control_plane/testing/model_behavior_qualification.py +769 -0
  558. loopx/control_plane/testing/model_behavior_retained_cases.py +235 -0
  559. loopx/control_plane/testing/model_tool_behavior.py +536 -0
  560. loopx/control_plane/testing/onboarding_model_behavior_qualification.py +642 -0
  561. loopx/control_plane/testing/quota_fixtures.py +208 -0
  562. loopx/control_plane/testing/quota_should_run_parity.py +57 -0
  563. loopx/control_plane/testing/release_commit_qualification.py +671 -0
  564. loopx/control_plane/testing/replan_semantic_action_behavior.py +1302 -0
  565. loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py +527 -0
  566. loopx/control_plane/testing/selected_todo_tool_behavior.py +1002 -0
  567. loopx/control_plane/testing/terminal_settlement_tool_behavior.py +656 -0
  568. loopx/control_plane/todos/__init__.py +1 -0
  569. loopx/control_plane/todos/active_state_editing.py +296 -0
  570. loopx/control_plane/todos/active_state_todo_parser.py +138 -0
  571. loopx/control_plane/todos/active_state_todos.py +175 -0
  572. loopx/control_plane/todos/addition.py +103 -0
  573. loopx/control_plane/todos/claim_visibility.py +253 -0
  574. loopx/control_plane/todos/completed_archive.py +139 -0
  575. loopx/control_plane/todos/completion_fence.py +49 -0
  576. loopx/control_plane/todos/completion_policy.py +153 -0
  577. loopx/control_plane/todos/completion_validation.py +248 -0
  578. loopx/control_plane/todos/completion_validation_accountability.py +27 -0
  579. loopx/control_plane/todos/completion_validation_projection.py +57 -0
  580. loopx/control_plane/todos/contract.py +1476 -0
  581. loopx/control_plane/todos/decision_scope.py +554 -0
  582. loopx/control_plane/todos/deferred_resume.py +546 -0
  583. loopx/control_plane/todos/durable_completion.py +201 -0
  584. loopx/control_plane/todos/event_writeback.py +484 -0
  585. loopx/control_plane/todos/frontier_deadline.py +132 -0
  586. loopx/control_plane/todos/handoff_gate.py +283 -0
  587. loopx/control_plane/todos/handoff_mode.py +444 -0
  588. loopx/control_plane/todos/handoff_note.py +202 -0
  589. loopx/control_plane/todos/line_update.py +361 -0
  590. loopx/control_plane/todos/list_projection.py +205 -0
  591. loopx/control_plane/todos/markdown.py +199 -0
  592. loopx/control_plane/todos/monitor_metadata.py +88 -0
  593. loopx/control_plane/todos/mutation_authority.py +299 -0
  594. loopx/control_plane/todos/projection.py +655 -0
  595. loopx/control_plane/todos/quota_summary.py +1138 -0
  596. loopx/control_plane/todos/route_continuation.py +267 -0
  597. loopx/control_plane/todos/succession_warning.py +174 -0
  598. loopx/control_plane/todos/summary_item.py +223 -0
  599. loopx/control_plane/todos/text.py +30 -0
  600. loopx/control_plane/todos/todo_index.py +226 -0
  601. loopx/control_plane/todos/todo_summary.py +1458 -0
  602. loopx/control_plane/todos/unblock_resume.py +326 -0
  603. loopx/control_plane/todos/user_gate.py +263 -0
  604. loopx/control_plane/todos/write_hint.py +63 -0
  605. loopx/control_plane/todos/write_policy.py +135 -0
  606. loopx/control_plane/turn_driver/__init__.py +85 -0
  607. loopx/control_plane/turn_driver/codex_cli.py +502 -0
  608. loopx/control_plane/turn_driver/driver.py +355 -0
  609. loopx/control_plane/turn_driver/executor.py +1468 -0
  610. loopx/control_plane/turn_driver/loop_controller.py +669 -0
  611. loopx/control_plane/turn_driver/settlement.py +318 -0
  612. loopx/control_plane/turn_driver/transaction.py +375 -0
  613. loopx/control_plane/work_items/__init__.py +1 -0
  614. loopx/control_plane/work_items/attention_fields.py +56 -0
  615. loopx/control_plane/work_items/attention_item.py +77 -0
  616. loopx/control_plane/work_items/attention_queue.py +322 -0
  617. loopx/control_plane/work_items/attention_routing.py +213 -0
  618. loopx/control_plane/work_items/autonomous_candidates.py +135 -0
  619. loopx/control_plane/work_items/autonomous_replan_ack.py +276 -0
  620. loopx/control_plane/work_items/autonomous_replan_obligation.py +786 -0
  621. loopx/control_plane/work_items/backlog_hygiene.py +59 -0
  622. loopx/control_plane/work_items/capability_monitor_fallback.py +221 -0
  623. loopx/control_plane/work_items/delivery_batch_scale.py +66 -0
  624. loopx/control_plane/work_items/delivery_outcome.py +152 -0
  625. loopx/control_plane/work_items/delivery_signals.py +113 -0
  626. loopx/control_plane/work_items/execution_obligation.py +235 -0
  627. loopx/control_plane/work_items/goal_route_hint.py +320 -0
  628. loopx/control_plane/work_items/interaction_contract.py +1540 -0
  629. loopx/control_plane/work_items/issue_meta_surface.py +159 -0
  630. loopx/control_plane/work_items/lifecycle.py +139 -0
  631. loopx/control_plane/work_items/operator_inbox.py +266 -0
  632. loopx/control_plane/work_items/outcome_followthrough.py +69 -0
  633. loopx/control_plane/work_items/primary_action.py +326 -0
  634. loopx/control_plane/work_items/progress_observation.py +630 -0
  635. loopx/control_plane/work_items/project_asset.py +675 -0
  636. loopx/control_plane/work_items/repair_delta.py +693 -0
  637. loopx/control_plane/work_items/runtime_capability_reentry.py +168 -0
  638. loopx/control_plane/work_items/semantic_replan_writeback.py +177 -0
  639. loopx/control_plane/work_items/status_contract.py +49 -0
  640. loopx/control_plane/work_items/task_graph.py +1046 -0
  641. loopx/control_plane/work_items/task_lease.py +1254 -0
  642. loopx/control_plane/work_items/task_lease_settlement.py +422 -0
  643. loopx/control_plane/work_items/work_lane.py +510 -0
  644. loopx/control_plane/work_items/work_lane_context.py +161 -0
  645. loopx/demo.py +247 -0
  646. loopx/diagnose.py +633 -0
  647. loopx/doctor.py +1251 -0
  648. loopx/domain_packs/__init__.py +1 -0
  649. loopx/domain_packs/issue_fix.py +571 -0
  650. loopx/domain_packs/ml_experiment.py +854 -0
  651. loopx/domain_state.py +137 -0
  652. loopx/dreaming.py +706 -0
  653. loopx/entrypoint.py +16 -0
  654. loopx/event_sourced_state.py +981 -0
  655. loopx/execution_profile.py +286 -0
  656. loopx/experiments/__init__.py +1 -0
  657. loopx/experiments/planner_worker/__init__.py +1 -0
  658. loopx/experiments/planner_worker/contract.py +523 -0
  659. loopx/experiments/planner_worker/runtime.py +391 -0
  660. loopx/experiments/planner_worker/traex.py +461 -0
  661. loopx/explore_graph.py +11 -0
  662. loopx/extensions/__init__.py +1 -0
  663. loopx/extensions/bundled.py +28 -0
  664. loopx/extensions/execution_envelope.py +126 -0
  665. loopx/extensions/lark/__init__.py +11 -0
  666. loopx/extensions/lark/event_collector.py +478 -0
  667. loopx/extensions/lark/event_collector_runtime.py +506 -0
  668. loopx/extensions/lark/event_inbox.py +454 -0
  669. loopx/extensions/lark/extension.toml +88 -0
  670. loopx/extensions/lark/goal_channel.py +44 -0
  671. loopx/extensions/lark/goal_channel_contracts.py +388 -0
  672. loopx/extensions/lark/goal_channel_lifecycle.py +218 -0
  673. loopx/extensions/lark/goal_channel_runtime.py +792 -0
  674. loopx/extensions/lark/goal_channel_setup.py +805 -0
  675. loopx/extensions/lark/goal_channel_targets.py +215 -0
  676. loopx/extensions/lark/goal_channel_transport.py +281 -0
  677. loopx/extensions/lark/inbox_reactions.py +650 -0
  678. loopx/extensions/lark/inbox_reply.py +430 -0
  679. loopx/extensions/lark/presentation/__init__.py +11 -0
  680. loopx/extensions/lark/presentation/explore_results.py +2276 -0
  681. loopx/extensions/lark/presentation/explore_singleflight.py +127 -0
  682. loopx/extensions/lark/presentation/explore_source_guard.py +121 -0
  683. loopx/extensions/lark/presentation/explore_stage_document.py +703 -0
  684. loopx/extensions/lark/presentation/explore_visual_integrity.py +122 -0
  685. loopx/extensions/lark/presentation/explore_visual_readback.py +452 -0
  686. loopx/extensions/lark/presentation/explore_visual_styles.py +156 -0
  687. loopx/extensions/lark/presentation/issue_fix_surface.py +612 -0
  688. loopx/extensions/lark/presentation/kanban.py +2791 -0
  689. loopx/extensions/lark/presentation/message_card.py +112 -0
  690. loopx/extensions/lark/presentation/periodic_report.py +261 -0
  691. loopx/extensions/lark/presentation/projection_rows.py +600 -0
  692. loopx/extensions/lark/presentation/record_io.py +95 -0
  693. loopx/extensions/lark/presentation/sync_receipt.py +145 -0
  694. loopx/extensions/lark/private_json.py +40 -0
  695. loopx/extensions/lark/provider.py +86 -0
  696. loopx/extensions/lark/reviewer_notification.py +604 -0
  697. loopx/extensions/manifest.py +385 -0
  698. loopx/extensions/openviking_periodic_report/__init__.py +17 -0
  699. loopx/extensions/openviking_periodic_report/activation.py +173 -0
  700. loopx/extensions/openviking_periodic_report/extension.toml +17 -0
  701. loopx/extensions/openviking_periodic_report/provider.py +355 -0
  702. loopx/extensions/openviking_periodic_report/sink.py +117 -0
  703. loopx/extensions/openviking_semantic_preference/__init__.py +5 -0
  704. loopx/extensions/openviking_semantic_preference/extension.toml +16 -0
  705. loopx/extensions/openviking_semantic_preference/history_export.py +484 -0
  706. loopx/extensions/openviking_semantic_preference/project_peer.py +68 -0
  707. loopx/extensions/openviking_semantic_preference/provider.py +312 -0
  708. loopx/extensions/presentation.py +979 -0
  709. loopx/extensions/process_runtime.py +204 -0
  710. loopx/extensions/readiness.py +168 -0
  711. loopx/extensions/runtime.py +931 -0
  712. loopx/extensions/scaffold.py +335 -0
  713. loopx/feedback.py +581 -0
  714. loopx/file_lock.py +382 -0
  715. loopx/global_registry.py +842 -0
  716. loopx/global_risks.py +970 -0
  717. loopx/global_todos.py +568 -0
  718. loopx/handoff_budget.py +28 -0
  719. loopx/heartbeat_prequota.py +80 -0
  720. loopx/heartbeat_prompt.py +159 -0
  721. loopx/help_surface.py +516 -0
  722. loopx/history.py +1507 -0
  723. loopx/host_loop_activation.py +1311 -0
  724. loopx/host_mode_planner.py +991 -0
  725. loopx/install_contract.py +1 -0
  726. loopx/interface_budget.py +196 -0
  727. loopx/long_task_cadence.py +208 -0
  728. loopx/materials.py +185 -0
  729. loopx/ml_experiment.py +3 -0
  730. loopx/onboarding.py +214 -0
  731. loopx/opencode2_goal_mode/README.md +81 -0
  732. loopx/opencode2_goal_mode/__init__.py +9 -0
  733. loopx/opencode2_goal_mode/opencode2-goal-worker.mjs +1018 -0
  734. loopx/opencode_goal_mode/README.md +99 -0
  735. loopx/opencode_goal_mode/__init__.py +13 -0
  736. loopx/opencode_goal_mode/goal-bridge-runtime.mjs +858 -0
  737. loopx/opencode_goal_mode/loopx-goal.js +8 -0
  738. loopx/operator_gate.py +420 -0
  739. loopx/orchestration.py +127 -0
  740. loopx/paths.py +59 -0
  741. loopx/pi_goal_mode/README.md +67 -0
  742. loopx/pi_goal_mode/__init__.py +13 -0
  743. loopx/pi_goal_mode/loopx-goal.ts +254 -0
  744. loopx/pi_goal_mode/pi-goal-loop-runtime.mjs +574 -0
  745. loopx/pr_review.py +1206 -0
  746. loopx/presentation/__init__.py +1 -0
  747. loopx/presentation/explore_views.py +1334 -0
  748. loopx/presentation/markdown.py +61 -0
  749. loopx/presentation/projection_source_reconcile.py +140 -0
  750. loopx/presentation/public_safety.py +42 -0
  751. loopx/presentation/renderers/__init__.py +17 -0
  752. loopx/presentation/renderers/goal_channel_html.py +269 -0
  753. loopx/presentation/renderers/periodic_report_html.py +786 -0
  754. loopx/presentation/renderers/periodic_report_markdown.py +184 -0
  755. loopx/presentation/renderers/quota_event_markdown.py +116 -0
  756. loopx/presentation/renderers/quota_markdown.py +1112 -0
  757. loopx/presentation/renderers/status_markdown.py +1570 -0
  758. loopx/presentation/renderers/trajectory_hygiene_markdown.py +39 -0
  759. loopx/presentation/renderers/turn_envelope_markdown.py +33 -0
  760. loopx/presentation/sinks/__init__.py +5 -0
  761. loopx/presentation/sinks/openviking_periodic_report.py +7 -0
  762. loopx/presentation/static_site.py +691 -0
  763. loopx/presets.py +369 -0
  764. loopx/project_alias.py +217 -0
  765. loopx/project_map.py +589 -0
  766. loopx/project_prompt.py +1153 -0
  767. loopx/project_skill_cli.py +125 -0
  768. loopx/project_skill_delivery.py +470 -0
  769. loopx/project_uninstall.py +462 -0
  770. loopx/promotion_gate.py +197 -0
  771. loopx/quota.py +1197 -0
  772. loopx/ready_score.py +413 -0
  773. loopx/registry.py +621 -0
  774. loopx/registry_writability.py +64 -0
  775. loopx/release_candidate.py +148 -0
  776. loopx/release_manifest.py +316 -0
  777. loopx/repository_identity.py +100 -0
  778. loopx/review_packet.py +1024 -0
  779. loopx/rollout_event_log.py +505 -0
  780. loopx/runtime.py +112 -0
  781. loopx/self_update.py +750 -0
  782. loopx/session_runtime.py +418 -0
  783. loopx/skill_install_readback.py +500 -0
  784. loopx/slash_command_install.py +1393 -0
  785. loopx/slash_commands.py +264 -0
  786. loopx/state_backup.py +573 -0
  787. loopx/state_migration.py +350 -0
  788. loopx/state_projection.py +809 -0
  789. loopx/state_refresh.py +1416 -0
  790. loopx/status.py +1383 -0
  791. loopx/status_server.py +935 -0
  792. loopx/summary_all.py +725 -0
  793. loopx/terminal_bench_agent.py +2056 -0
  794. loopx/thread_agent_binding.py +408 -0
  795. loopx/todo_followups.py +168 -0
  796. loopx/todo_suggestion_prompt.py +204 -0
  797. loopx/todos.py +2229 -0
  798. loopx/turn_identity.py +17 -0
  799. loopx/upgrade.py +1083 -0
  800. loopx/visible_governance.py +667 -0
  801. loopx/visible_multi_agent_launcher.py +1253 -0
  802. loopx/visible_multi_agent_tmux.py +429 -0
  803. loopx/worker_bridge.py +1574 -0
  804. loopx-0.4.8.dist-info/METADATA +708 -0
  805. loopx-0.4.8.dist-info/RECORD +811 -0
  806. loopx-0.4.8.dist-info/WHEEL +5 -0
  807. loopx-0.4.8.dist-info/entry_points.txt +5 -0
  808. loopx-0.4.8.dist-info/licenses/LICENSE +202 -0
  809. loopx-0.4.8.dist-info/licenses/LICENSE-MIT +21 -0
  810. loopx-0.4.8.dist-info/licenses/NOTICE +6 -0
  811. loopx-0.4.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1124 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import threading
5
+ import time
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from typing import Any, Callable, Mapping, Sequence
9
+
10
+ from .episode_runtime import (
11
+ RECORD_KIND_STANDALONE,
12
+ build_episode_execution_units,
13
+ build_router_probes,
14
+ combine_execution_metrics,
15
+ execute_episode_group,
16
+ item_concurrency_keys,
17
+ recoverable_episode_mode,
18
+ summarize_execution_records,
19
+ )
20
+ from .harness_checkpoint import (
21
+ HARNESS_CHECKPOINT_SCHEMA_VERSION,
22
+ build_arm_checkpoint,
23
+ load_arm_checkpoint,
24
+ write_arm_checkpoint,
25
+ )
26
+ from .router_state import (
27
+ advance_epoch,
28
+ family_routing_terms,
29
+ initial_router_state,
30
+ observe_epoch,
31
+ )
32
+ from .worker_branch_plan import build_explore_worker_branch_plan
33
+
34
+ """Software-agnostic exploration harness runtime.
35
+
36
+ This module is the generic half of a two-layer design:
37
+
38
+ - **This layer (LoopX)** owns scheduling and bookkeeping: independent
39
+ wall-clock budget arms, a shared work queue with concurrency keys, the
40
+ first-seen novelty ledger with adapter-injected flag weights, router-state
41
+ feedback, frontier *scheduling* (when to spend speculative slots and on
42
+ which seed families), anytime checkpoints, and generic metric aggregation.
43
+
44
+ - **The experiment layer** (driven by a Claude/Codex session) owns every
45
+ domain fact: how to execute a work item, what the observation keys mean,
46
+ how failures are classified, and -- crucially -- WHAT the speculative
47
+ variants are. Variant strategy is authored by the agent as a data file
48
+ (the variant catalog), never generated here.
49
+
50
+ Hard rules, in the spirit of the wider LoopX design:
51
+ - this module never calls an LLM and never imports domain vocabulary;
52
+ - variant intelligence travels through files: the harness EMITS
53
+ ``frontier_requests_<arm>.json`` (seed text + seen-observation digest) and
54
+ CONSUMES ``variant_catalog.json`` entries the agent wrote after reading
55
+ those requests -- the same packet-out / decision-in contract the LoopX
56
+ planner uses;
57
+ - novelty is only ever credited from observation keys and weighted flags the
58
+ adapter reports; specs carry intent, not novelty.
59
+
60
+ Adapter protocol (duck-typed; see the experiment layer for a reference
61
+ implementation):
62
+
63
+ - ``list_seed_items() -> list[dict]``: fresh seed work items each epoch,
64
+ each with ``item_id``, ``text``, ``family``; optional ``payload``.
65
+ - ``execute(item, *, run_root, arm, epoch, branch_id) -> dict``: run one work
66
+ item and return an observation record with ``item_id``, ``family``,
67
+ ``observation_keys`` (list[str]), ``weighted_flags`` ({name: weight} for
68
+ true flags), ``accepted`` (bool), ``duration_minutes`` (float),
69
+ ``retryable_infra_error`` (bool), plus any adapter-private fields.
70
+ - ``compile_variant(spec, seed_item) -> dict``: turn an agent-authored
71
+ variant spec into an executable work item (``is_variant`` True,
72
+ ``variant_spec`` attached, own ``concurrency_key``).
73
+ - ``flag_weights`` is implicit: the adapter reports weights per record, so
74
+ the harness never holds a domain weight table.
75
+ - ``item_failure_policy`` may be set to ``"fatal"`` on the adapter to make
76
+ execution exceptions propagate; the default ``"record"`` policy emits a
77
+ zero-value structured observation and keeps independent lanes running.
78
+ - Adapters that can checkpoint an expensive shared execution prefix may
79
+ optionally implement all three recoverable-episode methods:
80
+ ``prepare_episode_group(seed_item, episode_items, **context)`` returns
81
+ ``{handle, prefix_record, checkpoint_ref?}``, ``execute_episode(handle,
82
+ item, **context)`` returns a suffix-only observation, and
83
+ ``release_episode_group(handle, **context)`` releases adapter-owned state.
84
+ Returning ``None`` from prepare is an explicit, side-effect-free fallback
85
+ to legacy ``execute`` for that group (the only case where an episode
86
+ adapter needs ``execute`` at all). The adapter owns all restore mechanics.
87
+ Distinct groups run concurrently on separate workers, so the three methods
88
+ must be thread-safe across groups; concurrently active handles must not alias
89
+ mutable state, while safe sequential or immutable reuse remains valid.
90
+ Malformed grouping inputs (empty or duplicate seed/variant ids, id
91
+ collisions, or variants naming an unknown seed) degrade through the item
92
+ failure policy like any execution error: structured records under ``record``,
93
+ or a pre-dispatch raised error under ``fatal``.
94
+ """
95
+
96
+
97
+ HARNESS_ARM_SCHEMA_VERSION = "loopx_explore_harness_arm_v0"
98
+ VARIANT_CATALOG_SCHEMA_VERSION = "loopx_explore_variant_catalog_v0"
99
+ FRONTIER_REQUESTS_SCHEMA_VERSION = "loopx_explore_frontier_requests_v0"
100
+ HARNESS_RUNTIME_POLICY_SCHEMA_VERSION = "loopx_explore_harness_runtime_policy_v0"
101
+
102
+ class ItemFailurePolicy(str, Enum):
103
+ RECORD = "record"
104
+ FATAL = "fatal"
105
+
106
+
107
+ ITEM_FAILURE_POLICY_RECORD = ItemFailurePolicy.RECORD.value
108
+ ITEM_FAILURE_POLICY_FATAL = ItemFailurePolicy.FATAL.value
109
+ ITEM_FAILURE_POLICIES = {policy.value for policy in ItemFailurePolicy}
110
+
111
+ DEFAULT_OBSERVATION_WEIGHT = 1.0
112
+ _MIN_DURATION_MINUTES = 0.05
113
+
114
+
115
+ def _now_perf_minutes(start: float) -> float:
116
+ return (time.perf_counter() - start) / 60.0
117
+
118
+
119
+ def _write_json(path: Path, payload: Any) -> None:
120
+ path.parent.mkdir(parents=True, exist_ok=True)
121
+ path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
122
+
123
+
124
+ def _read_json(path: Path) -> Any:
125
+ if not path.exists():
126
+ return None
127
+ try:
128
+ return json.loads(path.read_text(encoding="utf-8-sig"))
129
+ except json.JSONDecodeError:
130
+ return None
131
+
132
+
133
+ def _normalize_item_failure_policy(value: Any) -> str:
134
+ if isinstance(value, ItemFailurePolicy):
135
+ return value.value
136
+ policy = str(value or ITEM_FAILURE_POLICY_RECORD).strip().lower().replace("_", "-")
137
+ if policy not in ITEM_FAILURE_POLICIES:
138
+ raise ValueError(
139
+ "item_failure_policy must be one of: "
140
+ + ", ".join(sorted(ITEM_FAILURE_POLICIES))
141
+ )
142
+ return policy
143
+
144
+
145
+ def _adapter_error_record(
146
+ item: Mapping[str, Any],
147
+ error: Exception,
148
+ *,
149
+ duration_minutes: float,
150
+ ) -> dict[str, Any]:
151
+ message = " ".join(str(error).split())
152
+ spec = item.get("variant_spec")
153
+ return {
154
+ "item_id": item.get("item_id"),
155
+ "family": item.get("family"),
156
+ "is_variant": bool(item.get("is_variant")),
157
+ # The error factory must never raise itself: a malformed variant_spec
158
+ # would otherwise escalate one recordable failure into a lost lane.
159
+ "variant_spec_id": (
160
+ spec.get("spec_id")
161
+ if item.get("is_variant") and isinstance(spec, Mapping)
162
+ else None
163
+ ),
164
+ "execution_status": "adapter_error",
165
+ "item_failure_policy": ITEM_FAILURE_POLICY_RECORD,
166
+ "accepted": False,
167
+ "observation_keys": [],
168
+ "weighted_flags": {},
169
+ "duration_minutes": max(_MIN_DURATION_MINUTES, float(duration_minutes)),
170
+ "retryable_infra_error": bool(
171
+ getattr(error, "retryable_infra_error", False)
172
+ ),
173
+ "adapter_error": {
174
+ "schema_version": "loopx_explore_adapter_error_v0",
175
+ "type": type(error).__name__,
176
+ "message": message[:240],
177
+ },
178
+ }
179
+
180
+
181
+ class NoveltyLedger:
182
+ """First-seen ledger: raw observation keys global, flags family-scoped.
183
+
184
+ Raw keys are ledgered globally so relabeling an item under a different
185
+ family can never re-mint an already-seen key; weighted flags are
186
+ family-scoped facts. Weights come from the record itself (adapter-owned).
187
+ """
188
+
189
+ def __init__(self) -> None:
190
+ self.seen: set[str] = set()
191
+
192
+ def raw_value(self, record: Mapping[str, Any]) -> float:
193
+ keys = record.get("observation_keys") or []
194
+ flags = record.get("weighted_flags") or {}
195
+ return DEFAULT_OBSERVATION_WEIGHT * len(keys) + sum(
196
+ float(weight) for weight in flags.values()
197
+ )
198
+
199
+ def credit(self, record: Mapping[str, Any]) -> dict[str, float]:
200
+ family = str(record.get("family") or "general")
201
+ novel_value = 0.0
202
+ novel_keys = 0
203
+ for key in record.get("observation_keys") or []:
204
+ key = str(key)
205
+ if key not in self.seen:
206
+ self.seen.add(key)
207
+ novel_value += DEFAULT_OBSERVATION_WEIGHT
208
+ novel_keys += 1
209
+ for flag, weight in (record.get("weighted_flags") or {}).items():
210
+ pseudo = f"flag:{family}:{flag}"
211
+ if pseudo not in self.seen:
212
+ self.seen.add(pseudo)
213
+ novel_value += float(weight)
214
+ return {"novel_value": novel_value, "novel_observation_keys": novel_keys}
215
+
216
+
217
+ def run_queue_epoch(
218
+ items: Sequence[Mapping[str, Any]],
219
+ *,
220
+ execute: Callable[..., dict[str, Any] | Sequence[dict[str, Any]]],
221
+ worker_count: int,
222
+ run_root: Path,
223
+ arm: str,
224
+ epoch: int,
225
+ stagger_seconds: float = 0.0,
226
+ item_failure_policy: str = ITEM_FAILURE_POLICY_RECORD,
227
+ ) -> list[dict[str, Any]]:
228
+ """Drain one epoch's work items through a shared pull queue.
229
+
230
+ Items may declare one ``concurrency_key`` or several ``concurrency_keys``.
231
+ Any overlap with an in-flight item waits until all keys free up (the generic
232
+ form of "same write scope must serialize"); disjoint items run concurrently
233
+ up to ``worker_count`` pullers. Returns per-worker lane records.
234
+ """
235
+
236
+ failure_policy = _normalize_item_failure_policy(item_failure_policy)
237
+ lock = threading.Lock()
238
+ pending: list[dict[str, Any]] = [dict(item) for item in items]
239
+ in_flight: set[str] = set()
240
+
241
+ def pull_next() -> dict[str, Any] | None:
242
+ with lock:
243
+ for index, item in enumerate(pending):
244
+ keys = item_concurrency_keys(item)
245
+ if any(key in in_flight for key in keys):
246
+ continue
247
+ pending.pop(index)
248
+ in_flight.update(keys)
249
+ return item
250
+ return None
251
+
252
+ def worker(worker_index: int) -> dict[str, Any]:
253
+ branch_id = f"queue_worker_{worker_index + 1:02d}"
254
+ started_at = time.perf_counter()
255
+ if worker_index and stagger_seconds > 0:
256
+ time.sleep(min(30.0, worker_index * stagger_seconds))
257
+ results: list[dict[str, Any]] = []
258
+ while True:
259
+ item = pull_next()
260
+ if item is None:
261
+ with lock:
262
+ if not pending:
263
+ break
264
+ time.sleep(0.5)
265
+ continue
266
+ keys = item_concurrency_keys(item)
267
+ item_started = time.perf_counter()
268
+ try:
269
+ outcome = execute(
270
+ item, run_root=run_root, arm=arm, epoch=epoch, branch_id=branch_id
271
+ )
272
+ if isinstance(outcome, Mapping):
273
+ batch = [dict(outcome)]
274
+ elif isinstance(outcome, Sequence) and not isinstance(
275
+ outcome, (str, bytes)
276
+ ):
277
+ batch = []
278
+ for candidate in outcome:
279
+ if not isinstance(candidate, Mapping):
280
+ raise TypeError(
281
+ "queue execute sequences must contain dict observation records"
282
+ )
283
+ batch.append(dict(candidate))
284
+ else:
285
+ raise TypeError(
286
+ "queue execute must return a dict or sequence of dict observations"
287
+ )
288
+ if not batch:
289
+ raise TypeError("queue execute must return at least one observation")
290
+ for record in batch:
291
+ record.setdefault("item_id", item.get("item_id"))
292
+ record.setdefault("family", item.get("family"))
293
+ record.setdefault("is_variant", bool(item.get("is_variant")))
294
+ if item.get("is_variant"):
295
+ record.setdefault(
296
+ "variant_spec_id",
297
+ (item.get("variant_spec") or {}).get("spec_id"),
298
+ )
299
+ except Exception as error:
300
+ if failure_policy == ITEM_FAILURE_POLICY_FATAL:
301
+ raise
302
+ batch = [
303
+ _adapter_error_record(
304
+ item,
305
+ error,
306
+ duration_minutes=_now_perf_minutes(item_started),
307
+ )
308
+ ]
309
+ finally:
310
+ if keys:
311
+ with lock:
312
+ in_flight.difference_update(keys)
313
+ results.extend(batch)
314
+ return {
315
+ "branch_id": branch_id,
316
+ "launch_index": worker_index,
317
+ "wall_minutes": round(_now_perf_minutes(started_at), 3),
318
+ "item_ids": [r.get("item_id") for r in results],
319
+ "families": [r.get("family") for r in results],
320
+ "results": results,
321
+ }
322
+
323
+ thread_count = max(1, min(int(worker_count), len(pending) or 1))
324
+ import concurrent.futures
325
+
326
+ with concurrent.futures.ThreadPoolExecutor(max_workers=thread_count) as pool:
327
+ futures = [pool.submit(worker, index) for index in range(thread_count)]
328
+ lanes = [future.result() for future in concurrent.futures.as_completed(futures)]
329
+ lanes.sort(key=lambda lane: str(lane.get("branch_id")))
330
+ return lanes
331
+
332
+
333
+ def _frontier_gain(router_state: Mapping[str, Any] | None, family: str, *, epoch: int) -> float:
334
+ """Expected value of spending a speculative slot near this family.
335
+
336
+ Generic signals only: whether the base loop has probed the family yet,
337
+ its residual novelty pressure, and a small age bonus. Field names track
338
+ the router_state family schema; the lookup accepts both the raw family
339
+ id and adapter-prefixed forms (e.g. 'scope:artifacts/<family>').
340
+ """
341
+
342
+ stats: Mapping[str, Any] = {}
343
+ families = (router_state or {}).get("families") if isinstance(router_state, Mapping) else None
344
+ if isinstance(families, Mapping):
345
+ exact = families.get(family)
346
+ if isinstance(exact, Mapping):
347
+ stats = exact
348
+ else:
349
+ suffix = "/" + str(family)
350
+ for key, value in families.items():
351
+ if isinstance(value, Mapping) and str(key).endswith(suffix):
352
+ stats = value
353
+ break
354
+ score = 1.0
355
+ runs = float(stats.get("runs") or 0.0)
356
+ if runs <= 0:
357
+ score += 1.25
358
+ else:
359
+ novelty_rate = stats.get("novelty_rate")
360
+ if novelty_rate is not None:
361
+ score += min(1.25, max(0.0, float(novelty_rate)) * 1.25)
362
+ accept_rate = stats.get("accept_rate_ema")
363
+ if accept_rate is not None and float(accept_rate) <= 0.5:
364
+ score += 0.25
365
+ score += min(0.5, max(0, int(epoch) - 1) * 0.03)
366
+ return round(score, 4)
367
+
368
+
369
+ class VariantCatalog:
370
+ """Agent-authored variant specs + per-arm consumption bookkeeping.
371
+
372
+ Catalog file schema (written by the experiment agent, never by code):
373
+ ``{"schema_version": ..., "specs": [{"spec_id", "seed_family",
374
+ "intent", "ops": [...adapter-defined...], "key_prefix",
375
+ "seed_item_id"?, "priority"?}]}``. The harness treats ``ops`` as opaque.
376
+ """
377
+
378
+ def __init__(self, catalog_path: Path, consumption_path: Path) -> None:
379
+ self.catalog_path = Path(catalog_path)
380
+ self.consumption_path = Path(consumption_path)
381
+ consumed = _read_json(self.consumption_path)
382
+ self.consumed: set[str] = set(consumed or [])
383
+
384
+ def _specs(self) -> list[dict[str, Any]]:
385
+ payload = _read_json(self.catalog_path)
386
+ specs = (payload or {}).get("specs") if isinstance(payload, Mapping) else None
387
+ return [dict(spec) for spec in specs or [] if spec.get("spec_id")]
388
+
389
+ def pending_for(self, family: str) -> list[dict[str, Any]]:
390
+ return [
391
+ spec
392
+ for spec in self._specs()
393
+ if str(spec.get("seed_family") or "") == str(family)
394
+ and str(spec.get("spec_id")) not in self.consumed
395
+ ]
396
+
397
+ def pending_count(self) -> int:
398
+ return len([s for s in self._specs() if str(s.get("spec_id")) not in self.consumed])
399
+
400
+ def consume(self, spec_id: str) -> None:
401
+ self.consumed.add(str(spec_id))
402
+ _write_json(self.consumption_path, sorted(self.consumed))
403
+
404
+ def restore_consumed(self, spec_ids: Sequence[Any]) -> None:
405
+ self.consumed = {str(spec_id) for spec_id in spec_ids if str(spec_id).strip()}
406
+ _write_json(self.consumption_path, sorted(self.consumed))
407
+
408
+
409
+ def select_frontier_work(
410
+ seed_items: Sequence[Mapping[str, Any]],
411
+ *,
412
+ catalog: VariantCatalog,
413
+ adapter: Any,
414
+ router_state: Mapping[str, Any] | None,
415
+ epoch: int,
416
+ max_lanes: int,
417
+ ) -> tuple[list[dict[str, Any]], dict[str, Any]]:
418
+ """Pick up to ``max_lanes`` agent-authored variant specs to execute.
419
+
420
+ Seeds are ranked by the generic frontier gain with an epoch-rotating
421
+ tie-break (multiplicative permutation over a prime modulus) so equal
422
+ gains spread across families over epochs instead of freezing on an
423
+ alphabetical prefix. Spec choice within a family follows the agent's
424
+ ``priority`` then catalog order. The harness only ever schedules specs;
425
+ it cannot invent them.
426
+ """
427
+
428
+ import hashlib
429
+
430
+ def _hash(value: str) -> int:
431
+ return int(hashlib.sha1(value.encode("utf-8")).hexdigest()[:8], 16)
432
+
433
+ by_family: dict[str, Mapping[str, Any]] = {}
434
+ for item in seed_items:
435
+ family = str(item.get("family") or "")
436
+ if family and family not in by_family:
437
+ by_family[family] = item
438
+ ranked = sorted(
439
+ by_family.items(),
440
+ key=lambda pair: (
441
+ -_frontier_gain(router_state, pair[0], epoch=epoch),
442
+ (_hash(pair[0]) * (2 * int(epoch) + 1)) % 1_000_003,
443
+ pair[0],
444
+ ),
445
+ )
446
+ selected: list[dict[str, Any]] = []
447
+ audit_specs: list[dict[str, Any]] = []
448
+ for family, seed in ranked:
449
+ if len(selected) >= max(0, int(max_lanes)):
450
+ break
451
+ pending = sorted(
452
+ catalog.pending_for(family),
453
+ key=lambda spec: (-float(spec.get("priority") or 0.0), str(spec.get("spec_id"))),
454
+ )
455
+ if not pending:
456
+ continue
457
+ spec = pending[0]
458
+ item = adapter.compile_variant(spec, dict(seed))
459
+ item.setdefault("is_variant", True)
460
+ item.setdefault("variant_spec", spec)
461
+ item.setdefault("family", family)
462
+ item.setdefault("seed_item_id", seed.get("item_id"))
463
+ item.setdefault(
464
+ "concurrency_key", f"{family}:variant:{spec.get('spec_id')}"
465
+ )
466
+ catalog.consume(str(spec.get("spec_id")))
467
+ selected.append(item)
468
+ audit_specs.append(
469
+ {
470
+ "spec_id": spec.get("spec_id"),
471
+ "seed_family": family,
472
+ "intent": spec.get("intent"),
473
+ "gain": _frontier_gain(router_state, family, epoch=epoch),
474
+ }
475
+ )
476
+ audit = {
477
+ "requested_lanes": int(max_lanes),
478
+ "admitted": len(selected),
479
+ "catalog_pending_after": catalog.pending_count(),
480
+ "specs": audit_specs,
481
+ }
482
+ return selected, audit
483
+
484
+
485
+ def emit_frontier_requests(
486
+ path: Path,
487
+ *,
488
+ arm_key: str,
489
+ epoch: int,
490
+ seed_items: Sequence[Mapping[str, Any]],
491
+ ledger: NoveltyLedger,
492
+ catalog: VariantCatalog,
493
+ router_state: Mapping[str, Any] | None,
494
+ per_family_key_cap: int = 24,
495
+ ) -> None:
496
+ """Publish what the harness NEEDS from the experiment agent.
497
+
498
+ The agent (a Claude/Codex session driving the experiment) reads this
499
+ packet -- seed text plus a digest of already-seen observation keys and
500
+ exhausted specs -- and appends new specs to the variant catalog. This is
501
+ the LoopX interaction pattern: packet out, decision in, no LLM calls
502
+ from code.
503
+ """
504
+
505
+ families: dict[str, dict[str, Any]] = {}
506
+ for item in seed_items:
507
+ family = str(item.get("family") or "")
508
+ if not family or family in families:
509
+ continue
510
+ seen_for_family = sorted(
511
+ key
512
+ for key in ledger.seen
513
+ if not key.startswith("flag:") or key.startswith(f"flag:{family}:")
514
+ )[: max(1, per_family_key_cap)]
515
+ families[family] = {
516
+ "family": family,
517
+ "seed_item_id": item.get("item_id"),
518
+ "seed_text": item.get("text"),
519
+ "pending_spec_count": len(catalog.pending_for(family)),
520
+ "frontier_gain": _frontier_gain(router_state, family, epoch=epoch),
521
+ "seen_keys_sample": seen_for_family,
522
+ }
523
+ _write_json(
524
+ path,
525
+ {
526
+ "schema_version": FRONTIER_REQUESTS_SCHEMA_VERSION,
527
+ "arm": arm_key,
528
+ "epoch": int(epoch),
529
+ "total_seen_keys": len(ledger.seen),
530
+ "catalog_pending_total": catalog.pending_count(),
531
+ "families": sorted(families.values(), key=lambda f: -float(f["frontier_gain"])),
532
+ "how_to_respond": (
533
+ "Append specs to the variant catalog file: {spec_id, seed_family, "
534
+ "intent, ops (adapter-defined), key_prefix, priority}. The harness "
535
+ "schedules them by frontier gain; it never invents specs."
536
+ ),
537
+ },
538
+ )
539
+
540
+
541
+ def plan_round_robin(seed_items: Sequence[Mapping[str, Any]], **_: Any) -> list[dict[str, Any]]:
542
+ """Baseline planner: every seed, catalog untouched, no ordering signal."""
543
+
544
+ return [dict(item) for item in seed_items]
545
+
546
+
547
+ def plan_router_no_prune(
548
+ seed_items: Sequence[Mapping[str, Any]],
549
+ *,
550
+ goal_id: str,
551
+ agent_id: str,
552
+ worker_count: int,
553
+ router_state: Mapping[str, Any] | None,
554
+ load_profile: Mapping[str, Any] | None,
555
+ harness_profile: str = "moe-router",
556
+ branch_fill_policy: str = "confident-prefix",
557
+ ) -> list[dict[str, Any]]:
558
+ """Router-ordered no-prune planner over generic seed items.
559
+
560
+ Uses the LoopX worker-branch planner for VALUE ORDERING only, then
561
+ appends every unselected seed as a ranked tail -- sorting without
562
+ pruning: an executable seed never loses its slot to low confidence.
563
+ """
564
+
565
+ by_id: dict[str, dict[str, Any]] = {}
566
+ duplicate_items: list[dict[str, Any]] = []
567
+ for item in seed_items:
568
+ item_id = str(item.get("item_id"))
569
+ if item_id in by_id:
570
+ duplicate_items.append(dict(item))
571
+ else:
572
+ by_id[item_id] = dict(item)
573
+ todos = [
574
+ {
575
+ "todo_id": item_id,
576
+ "text": str(item.get("text") or item.get("item_id")),
577
+ "status": "open",
578
+ "task_class": "advancement_task",
579
+ "required_write_scopes": [f"artifacts/{item.get('family')}/**"],
580
+ "index": index,
581
+ }
582
+ for index, (item_id, item) in enumerate(by_id.items())
583
+ ]
584
+ plan = build_explore_worker_branch_plan(
585
+ goal_id=goal_id,
586
+ todos=todos,
587
+ agent_id=agent_id,
588
+ # This standalone benchmark arm opts in explicitly and consumes value
589
+ # ordering only; spawn stays disallowed, so the plan carries no
590
+ # suggested claim/lease commands.
591
+ orchestration={"explore_harness": {"enabled": True}},
592
+ worker_width=worker_count,
593
+ harness_profile=harness_profile,
594
+ branch_fill_policy=branch_fill_policy,
595
+ router_state=router_state,
596
+ load_profile=load_profile,
597
+ )
598
+ ordered: list[dict[str, Any]] = []
599
+ used: set[str] = set()
600
+ for branch in plan.get("selected_worker_branches") or []:
601
+ for todo_id in branch.get("todo_ids") or []:
602
+ todo_id = str(todo_id)
603
+ if todo_id in by_id and todo_id not in used:
604
+ ordered.append(by_id[todo_id])
605
+ used.add(todo_id)
606
+ for branch in plan.get("rejected_worker_branches") or []:
607
+ for todo_id in branch.get("todo_ids") or []:
608
+ todo_id = str(todo_id)
609
+ if todo_id in by_id and todo_id not in used:
610
+ ordered.append(by_id[todo_id])
611
+ used.add(todo_id)
612
+ for item_id, item in by_id.items():
613
+ if item_id not in used:
614
+ ordered.append(item)
615
+ used.add(item_id)
616
+ # Preserve malformed duplicates for the episode grouping boundary. The
617
+ # planner still reasons over unique ids, but no-prune means it must not
618
+ # silently erase an input that record/fatal policy needs to classify.
619
+ ordered.extend(duplicate_items)
620
+ return ordered
621
+
622
+
623
+ def _arm_runtime_signature(
624
+ adapter: Any,
625
+ *,
626
+ arm_key: str,
627
+ worker_count: int,
628
+ use_router: bool,
629
+ frontier_max_lanes: int,
630
+ variant_catalog_enabled: bool,
631
+ goal_id: str,
632
+ agent_id: str,
633
+ item_failure_policy: str,
634
+ episode_mode: str | None,
635
+ ) -> dict[str, Any]:
636
+ adapter_type = f"{type(adapter).__module__}.{type(adapter).__qualname__}"
637
+ signature = {
638
+ "arm_key": str(arm_key),
639
+ "adapter_type": adapter_type,
640
+ "worker_count": max(1, int(worker_count)),
641
+ "use_router": bool(use_router),
642
+ "frontier_max_lanes": max(0, int(frontier_max_lanes)),
643
+ "variant_catalog_enabled": bool(variant_catalog_enabled),
644
+ "goal_id": str(goal_id),
645
+ "agent_id": str(agent_id),
646
+ "item_failure_policy": item_failure_policy,
647
+ }
648
+ # Missing keys in legacy manifests read as None, so old standalone arms
649
+ # remain compatible while a recoverable arm cannot resume as standalone.
650
+ signature["episode_mode"] = episode_mode
651
+ return signature
652
+
653
+
654
+ def run_budget_arm(
655
+ adapter: Any,
656
+ *,
657
+ arm_key: str,
658
+ run_root: Path,
659
+ budget_minutes: float,
660
+ worker_count: int,
661
+ max_epochs: int = 1000,
662
+ use_router: bool = False,
663
+ frontier_max_lanes: int = 0,
664
+ variant_catalog_path: Path | None = None,
665
+ goal_id: str = "exploration",
666
+ agent_id: str = "harness",
667
+ stagger_seconds: float = 0.0,
668
+ duration_guard_factor: float = 1.15,
669
+ duration_guard_recent: int = 3,
670
+ resumable: bool = False,
671
+ resume: bool = False,
672
+ checkpoint_path: Path | None = None,
673
+ item_failure_policy: str | None = None,
674
+ ) -> dict[str, Any]:
675
+ """Run ONE arm alone against its wall-clock budget; return arm payload.
676
+
677
+ ``use_router=False`` is the blind baseline (round-robin sweep, no
678
+ variants). ``use_router=True`` adds router-ordered no-prune planning,
679
+ router-state feedback, and -- when a variant catalog path is given --
680
+ agent-authored frontier variants appended to each epoch's queue. Restart
681
+ is opt-in and only trusts the validated epoch-boundary checkpoint manifest;
682
+ rolling progress remains an observability surface.
683
+ """
684
+
685
+ run_root = Path(run_root)
686
+ failure_policy = _normalize_item_failure_policy(
687
+ item_failure_policy
688
+ if item_failure_policy is not None
689
+ else getattr(adapter, "item_failure_policy", None)
690
+ )
691
+ episode_mode = recoverable_episode_mode(adapter)
692
+ checkpoint_enabled = bool(resumable or resume or checkpoint_path is not None)
693
+ checkpoint_file = (
694
+ Path(checkpoint_path)
695
+ if checkpoint_path is not None
696
+ else run_root / f"arm_checkpoint_{arm_key}.json"
697
+ )
698
+ runtime_signature = _arm_runtime_signature(
699
+ adapter,
700
+ arm_key=arm_key,
701
+ worker_count=worker_count,
702
+ use_router=use_router,
703
+ frontier_max_lanes=frontier_max_lanes,
704
+ variant_catalog_enabled=variant_catalog_path is not None,
705
+ goal_id=goal_id,
706
+ agent_id=agent_id,
707
+ item_failure_policy=failure_policy,
708
+ episode_mode=episode_mode,
709
+ )
710
+ ledger = NoveltyLedger()
711
+ router_state = initial_router_state() if use_router else None
712
+ load_profile: Mapping[str, Any] | None = None
713
+ catalog = (
714
+ VariantCatalog(
715
+ Path(variant_catalog_path),
716
+ run_root / f"variant_consumption_{arm_key}.json",
717
+ )
718
+ if variant_catalog_path
719
+ else None
720
+ )
721
+ epochs: list[dict[str, Any]] = []
722
+ checkpoints: list[dict[str, Any]] = []
723
+ coverage_first_seen: dict[str, float] = {}
724
+ raw_cum = 0.0
725
+ novel_cum = 0.0
726
+ variant_records_cum = 0
727
+ next_epoch = 1
728
+ elapsed_offset = 0.0
729
+ if resume:
730
+ state = load_arm_checkpoint(
731
+ checkpoint_file,
732
+ expected_signature=runtime_signature,
733
+ )
734
+ epochs = [dict(epoch) for epoch in state["epochs"]]
735
+ checkpoints = [dict(checkpoint) for checkpoint in state["checkpoints"]]
736
+ ledger.seen = {str(key) for key in state["novelty_seen"]}
737
+ router_state = (
738
+ dict(state["router_state"])
739
+ if isinstance(state.get("router_state"), Mapping)
740
+ else None
741
+ )
742
+ load_profile = (
743
+ dict(state["load_profile"])
744
+ if isinstance(state.get("load_profile"), Mapping)
745
+ else None
746
+ )
747
+ coverage_first_seen = {
748
+ str(family): float(minutes)
749
+ for family, minutes in state["coverage_first_seen"].items()
750
+ }
751
+ raw_cum = float(state.get("raw_value_total") or 0.0)
752
+ novel_cum = float(state.get("novel_value_total") or 0.0)
753
+ variant_records_cum = int(state.get("variant_records_total") or 0)
754
+ next_epoch = int(state["next_epoch"])
755
+ elapsed_offset = float(state.get("elapsed_minutes") or 0.0)
756
+ if catalog is not None:
757
+ catalog.restore_consumed(state["catalog_consumed"])
758
+ elif checkpoint_enabled and catalog is not None:
759
+ # Reusing a run_root is not an implicit restart contract. A fresh arm
760
+ # starts with fresh consumption; only resume restores prior attempts.
761
+ catalog.restore_consumed([])
762
+ if not resume:
763
+ checkpoint_file.unlink(missing_ok=True)
764
+ execution_metric_fields = set(combine_execution_metrics([]))
765
+ historical_execution_metrics: list[dict[str, Any]] = []
766
+ for completed_epoch in epochs:
767
+ stored_metrics = completed_epoch.get("execution_metrics")
768
+ if (
769
+ isinstance(stored_metrics, Mapping)
770
+ and execution_metric_fields.issubset(stored_metrics)
771
+ ):
772
+ historical_execution_metrics.append(dict(stored_metrics))
773
+ continue
774
+ historical_records = [
775
+ record
776
+ for lane in completed_epoch.get("lanes") or []
777
+ for record in lane.get("results") or []
778
+ ]
779
+ if episode_mode is None:
780
+ historical_records = [
781
+ {
782
+ **record,
783
+ "execution_group_id": None,
784
+ "record_kind": RECORD_KIND_STANDALONE,
785
+ }
786
+ for record in historical_records
787
+ ]
788
+ historical_execution_metrics.append(
789
+ summarize_execution_records(historical_records)
790
+ )
791
+ execution_metrics_cum = combine_execution_metrics(
792
+ historical_execution_metrics
793
+ )
794
+ stop_reason = "max_epochs"
795
+ started = time.perf_counter()
796
+ for epoch in range(next_epoch, int(max_epochs) + 1):
797
+ elapsed = elapsed_offset + _now_perf_minutes(started)
798
+ remaining = float(budget_minutes) - elapsed
799
+ if epochs and remaining <= 0:
800
+ stop_reason = "budget_exhausted"
801
+ break
802
+ if epochs and duration_guard_factor > 0:
803
+ recent = epochs[-max(1, int(duration_guard_recent)) :]
804
+ estimated = max(
805
+ float(entry.get("epoch_wall_minutes") or 0.0) for entry in recent
806
+ ) * float(duration_guard_factor)
807
+ if remaining < estimated:
808
+ stop_reason = "duration_guard"
809
+ break
810
+ seed_items = [dict(item) for item in adapter.list_seed_items()]
811
+ if not seed_items:
812
+ stop_reason = "seed_queue_empty"
813
+ break
814
+ if episode_mode is not None and failure_policy == ITEM_FAILURE_POLICY_FATAL:
815
+ # Router planning keys by item_id and variant compilation calls
816
+ # adapter code, so fatal structural validation must see the raw
817
+ # seeds before either operation can collapse ids or cause effects.
818
+ build_episode_execution_units(
819
+ seed_items,
820
+ arm=arm_key,
821
+ epoch=epoch,
822
+ fatal_preflight=True,
823
+ )
824
+ if use_router:
825
+ queue_items = plan_router_no_prune(
826
+ seed_items,
827
+ goal_id=goal_id,
828
+ agent_id=agent_id,
829
+ worker_count=worker_count,
830
+ router_state=router_state,
831
+ load_profile=load_profile,
832
+ )
833
+ else:
834
+ queue_items = plan_round_robin(seed_items)
835
+ frontier_audit: dict[str, Any] | None = None
836
+ if use_router and catalog is not None and int(frontier_max_lanes) > 0:
837
+ variant_items, frontier_audit = select_frontier_work(
838
+ seed_items,
839
+ catalog=catalog,
840
+ adapter=adapter,
841
+ router_state=router_state,
842
+ epoch=epoch,
843
+ max_lanes=frontier_max_lanes,
844
+ )
845
+ queue_items = queue_items + variant_items
846
+ emit_frontier_requests(
847
+ run_root / f"frontier_requests_{arm_key}.json",
848
+ arm_key=arm_key,
849
+ epoch=epoch,
850
+ seed_items=seed_items,
851
+ ledger=ledger,
852
+ catalog=catalog,
853
+ router_state=router_state,
854
+ )
855
+ epoch_start = time.perf_counter()
856
+ execution_items = queue_items
857
+ if episode_mode is not None:
858
+ execution_items = build_episode_execution_units(
859
+ queue_items,
860
+ arm=arm_key,
861
+ epoch=epoch,
862
+ fatal_preflight=failure_policy == ITEM_FAILURE_POLICY_FATAL,
863
+ )
864
+
865
+ def execute_epoch(item: Mapping[str, Any], **context: Any) -> list[dict[str, Any]]:
866
+ return execute_episode_group(
867
+ item,
868
+ adapter=adapter,
869
+ failure_policy=failure_policy,
870
+ fatal_failure_policy=ITEM_FAILURE_POLICY_FATAL,
871
+ error_record_factory=_adapter_error_record,
872
+ **context,
873
+ )
874
+
875
+ else:
876
+ # Accessed lazily so an episode-only adapter (no legacy execute)
877
+ # is valid as long as prepare_episode_group never returns None.
878
+ execute_epoch: Callable[..., Any] = adapter.execute
879
+
880
+ lanes = run_queue_epoch(
881
+ execution_items,
882
+ execute=execute_epoch,
883
+ worker_count=worker_count,
884
+ run_root=run_root,
885
+ arm=arm_key,
886
+ epoch=epoch,
887
+ stagger_seconds=stagger_seconds,
888
+ item_failure_policy=failure_policy,
889
+ )
890
+ epoch_wall = _now_perf_minutes(epoch_start)
891
+ epoch_raw = 0.0
892
+ epoch_novel = 0.0
893
+ records: list[dict[str, Any]] = []
894
+ for lane in lanes:
895
+ for record in lane.get("results") or []:
896
+ credit = ledger.credit(record)
897
+ record["novel_value"] = credit["novel_value"]
898
+ record["raw_value"] = ledger.raw_value(record)
899
+ epoch_raw += record["raw_value"]
900
+ epoch_novel += credit["novel_value"]
901
+ if record.get("is_variant"):
902
+ variant_records_cum += 1
903
+ records.append(record)
904
+ raw_cum += epoch_raw
905
+ novel_cum += epoch_novel
906
+ metric_records = records
907
+ if episode_mode is None:
908
+ metric_records = [
909
+ {
910
+ **record,
911
+ "execution_group_id": None,
912
+ "record_kind": RECORD_KIND_STANDALONE,
913
+ }
914
+ for record in records
915
+ ]
916
+ epoch_execution_metrics = summarize_execution_records(metric_records)
917
+ execution_metrics_cum = combine_execution_metrics(
918
+ [execution_metrics_cum, epoch_execution_metrics]
919
+ )
920
+ elapsed_after = elapsed_offset + _now_perf_minutes(started)
921
+ for record in records:
922
+ family = str(record.get("family") or "")
923
+ if family:
924
+ coverage_first_seen.setdefault(family, round(elapsed_after, 3))
925
+ if use_router:
926
+ if episode_mode is not None:
927
+ probes = build_router_probes(records)
928
+ else:
929
+ probes = [
930
+ {
931
+ "family": str(record.get("family") or "general"),
932
+ "duration_minutes": record.get("duration_minutes"),
933
+ "observation_keys": record.get("observation_keys") or [],
934
+ "weighted_flags": record.get("weighted_flags") or {},
935
+ "accepted": bool(record.get("accepted")),
936
+ "retryable_infra_error": bool(
937
+ record.get("retryable_infra_error")
938
+ ),
939
+ }
940
+ for record in records
941
+ ]
942
+ router_state = observe_epoch(router_state, epoch=epoch, probes=probes)
943
+ router_state = advance_epoch(
944
+ router_state,
945
+ epoch=epoch,
946
+ eligible_families=sorted(
947
+ {str(item.get("family")) for item in seed_items if item.get("family")}
948
+ ),
949
+ )
950
+ _write_json(run_root / f"router_state_{arm_key}.json", router_state)
951
+ max_lane_wall = max(
952
+ (float(lane.get("wall_minutes") or 0.0) for lane in lanes), default=0.0
953
+ )
954
+ load_profile = {
955
+ "source": f"observed_epoch_{epoch:02d}_{arm_key}",
956
+ "parallel_wall_minutes": epoch_wall,
957
+ "max_branch_minutes": max_lane_wall,
958
+ "branch_count": len(lanes),
959
+ }
960
+ checkpoints.append(
961
+ {
962
+ "epoch": epoch,
963
+ "elapsed_minutes": round(elapsed_after, 3),
964
+ "raw_value_cum": round(raw_cum, 3),
965
+ "novel_value_cum": round(novel_cum, 3),
966
+ "coverage_count_cum": len(coverage_first_seen),
967
+ "variant_records_cum": variant_records_cum,
968
+ "requested_worker_minutes_cum": round(elapsed_after * worker_count, 3),
969
+ "execution_metrics_cum": dict(execution_metrics_cum),
970
+ }
971
+ )
972
+ epochs.append(
973
+ {
974
+ "schema_version": HARNESS_ARM_SCHEMA_VERSION,
975
+ "epoch": epoch,
976
+ "epoch_wall_minutes": round(epoch_wall, 3),
977
+ "queue_size": len(queue_items),
978
+ "execution_unit_count": len(execution_items),
979
+ "execution_metrics": epoch_execution_metrics,
980
+ "frontier": frontier_audit,
981
+ "lanes": lanes,
982
+ }
983
+ )
984
+ if checkpoint_enabled:
985
+ write_arm_checkpoint(
986
+ checkpoint_file,
987
+ build_arm_checkpoint(
988
+ arm_key=arm_key,
989
+ runtime_signature=runtime_signature,
990
+ next_epoch=epoch + 1,
991
+ elapsed_minutes=elapsed_after,
992
+ epochs=epochs,
993
+ checkpoints=checkpoints,
994
+ novelty_seen=sorted(ledger.seen),
995
+ router_state=router_state,
996
+ load_profile=load_profile,
997
+ catalog_consumed=sorted(catalog.consumed) if catalog else [],
998
+ coverage_first_seen=coverage_first_seen,
999
+ raw_value_total=raw_cum,
1000
+ novel_value_total=novel_cum,
1001
+ variant_records_total=variant_records_cum,
1002
+ ),
1003
+ )
1004
+ _write_json(
1005
+ run_root / f"rolling_progress_{arm_key}.json",
1006
+ {"arm": arm_key, "completed_epochs": len(epochs), "last_checkpoint": checkpoints[-1]},
1007
+ )
1008
+ return {
1009
+ "schema_version": HARNESS_ARM_SCHEMA_VERSION,
1010
+ "arm_key": arm_key,
1011
+ "budget_minutes": float(budget_minutes),
1012
+ "elapsed_minutes": round(elapsed_offset + _now_perf_minutes(started), 3),
1013
+ "stop_reason": stop_reason,
1014
+ "epoch_count": len(epochs),
1015
+ "raw_value_total": round(raw_cum, 3),
1016
+ "novel_value_total": round(novel_cum, 3),
1017
+ "variant_records_total": variant_records_cum,
1018
+ "execution_metrics": execution_metrics_cum,
1019
+ "coverage_count": len(coverage_first_seen),
1020
+ "coverage_first_seen_minutes": coverage_first_seen,
1021
+ "checkpoints": checkpoints,
1022
+ "epochs": epochs,
1023
+ "resume": {
1024
+ "schema_version": "loopx_explore_harness_resume_v0",
1025
+ "enabled": checkpoint_enabled,
1026
+ "resumed": bool(resume),
1027
+ "checkpoint_schema_version": HARNESS_CHECKPOINT_SCHEMA_VERSION,
1028
+ "checkpoint_path": str(checkpoint_file) if checkpoint_enabled else None,
1029
+ "restored_epoch_count": next_epoch - 1 if resume else 0,
1030
+ },
1031
+ "runtime_policy": {
1032
+ "schema_version": HARNESS_RUNTIME_POLICY_SCHEMA_VERSION,
1033
+ "item_failure_policy": failure_policy,
1034
+ "item_exception_isolation": failure_policy == ITEM_FAILURE_POLICY_RECORD,
1035
+ "episode_execution": {
1036
+ "mode": episode_mode or "standalone",
1037
+ "adapter_owned_restore": episode_mode is not None,
1038
+ },
1039
+ "planner_guidance": {
1040
+ "retry_backoff": {
1041
+ "enforced": False,
1042
+ "owner": "external_runner",
1043
+ },
1044
+ "infra_cooldown": {
1045
+ "enforced": False,
1046
+ "owner": "external_runner",
1047
+ },
1048
+ },
1049
+ },
1050
+ }
1051
+
1052
+
1053
+ def step_auc(checkpoints: Sequence[Mapping[str, Any]], *, value_key: str, horizon_minutes: float) -> float:
1054
+ """Step-integrate a cumulative anytime curve to the budget horizon."""
1055
+
1056
+ auc = 0.0
1057
+ prev_t = 0.0
1058
+ prev_v = 0.0
1059
+ for checkpoint in checkpoints:
1060
+ t = min(float(checkpoint.get("elapsed_minutes") or 0.0), float(horizon_minutes))
1061
+ if t > prev_t:
1062
+ auc += prev_v * (t - prev_t)
1063
+ prev_t = t
1064
+ prev_v = float(checkpoint.get(value_key) or 0.0)
1065
+ if float(horizon_minutes) > prev_t:
1066
+ auc += prev_v * (float(horizon_minutes) - prev_t)
1067
+ return round(auc, 3)
1068
+
1069
+
1070
+ def aggregate_arms(
1071
+ arms: Mapping[str, Mapping[str, Any]],
1072
+ *,
1073
+ budget_minutes: float,
1074
+ worker_count: int,
1075
+ coverage_denominator: int | None = None,
1076
+ ) -> dict[str, Any]:
1077
+ """Generic two-arm (or N-arm) comparison on novelty-first metrics."""
1078
+
1079
+ summary: dict[str, Any] = {
1080
+ "schema_version": "loopx_explore_harness_score_v0",
1081
+ "budget_minutes_per_arm": float(budget_minutes),
1082
+ "worker_count": int(worker_count),
1083
+ "coverage_denominator": coverage_denominator,
1084
+ "arms": {},
1085
+ }
1086
+ for arm_key, arm in arms.items():
1087
+ checkpoints = arm.get("checkpoints") or []
1088
+ execution_metrics = (
1089
+ dict(arm.get("execution_metrics") or {})
1090
+ if isinstance(arm.get("execution_metrics"), Mapping)
1091
+ else {}
1092
+ )
1093
+ effective_compute = float(
1094
+ execution_metrics.get("effective_compute_minutes") or 0.0
1095
+ )
1096
+ summary["arms"][arm_key] = {
1097
+ "novel_value_endpoint": arm.get("novel_value_total"),
1098
+ "novel_value_auc": step_auc(
1099
+ checkpoints, value_key="novel_value_cum", horizon_minutes=budget_minutes
1100
+ ),
1101
+ "raw_value_total": arm.get("raw_value_total"),
1102
+ "coverage_count": arm.get("coverage_count"),
1103
+ "variant_records_total": arm.get("variant_records_total"),
1104
+ "epoch_count": arm.get("epoch_count"),
1105
+ "elapsed_minutes": arm.get("elapsed_minutes"),
1106
+ "stop_reason": arm.get("stop_reason"),
1107
+ "execution_metrics": execution_metrics,
1108
+ "novel_value_per_requested_worker_minute": round(
1109
+ float(arm.get("novel_value_total") or 0.0)
1110
+ / max(0.001, float(budget_minutes) * worker_count),
1111
+ 4,
1112
+ ),
1113
+ "novel_value_per_effective_compute_minute": (
1114
+ round(
1115
+ float(arm.get("novel_value_total") or 0.0)
1116
+ / effective_compute,
1117
+ 4,
1118
+ )
1119
+ if effective_compute > 0
1120
+ else None
1121
+ ),
1122
+ "anytime_curve": checkpoints,
1123
+ }
1124
+ return summary