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,1069 @@
1
+ """Run one SkillsBench agent prompt through a real LoopX Turn transaction.
2
+
3
+ The scored workspace and its case-local LoopX registry live behind the existing
4
+ command/file bridge. The agent CLI runs on the host. This module keeps those
5
+ planes separate: it reads a typed Turn plan from the scored workspace, executes
6
+ the host callback, validates a task postcondition through the bridge, and lets
7
+ the Turn executor own writeback and the single quota spend.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import json
14
+ import re
15
+ import shlex
16
+ import subprocess
17
+ import time
18
+ import uuid
19
+ from collections.abc import Callable, Mapping
20
+ from dataclasses import dataclass, replace
21
+ from pathlib import Path
22
+ from typing import Any, Union
23
+
24
+ from ..benchmark_case_state import benchmark_case_loopx_command_prefix
25
+ from ..control_plane.turn_driver import run_loopx_turn_once
26
+ from .skillsbench_acp_failure_policy import (
27
+ RECOVERABLE_CODEX_TURN_FAILURE_PREFIX,
28
+ recoverable_codex_turn_failure_message,
29
+ )
30
+ from .skillsbench_turn_route import LOOPX_TURN_PROOF_CHECKS
31
+
32
+
33
+ SKILLSBENCH_LOOPX_TURN_TRACE_SCHEMA_VERSION = (
34
+ "skillsbench_loopx_turn_agent_cli_trace_v0"
35
+ )
36
+ SKILLSBENCH_BRIDGE_OPERATION_SCHEMA_VERSION = (
37
+ "skillsbench_remote_command_file_bridge_operation_response_v0"
38
+ )
39
+ SKILLSBENCH_TURN_BASELINE_ENV = "LOOPX_TURN_BASELINE_FILE"
40
+ SKILLSBENCH_TURN_SEQUENCE_BASELINE_ENV = "LOOPX_TURN_SEQUENCE_BASELINE_FILE"
41
+ SKILLSBENCH_LOOPX_TURN_TERMINAL_POLICIES = frozenset(
42
+ {"validator", "fixed-n", "stability"}
43
+ )
44
+ SKILLSBENCH_TURN_AGENT_VALIDATION_HANDOFF_RESPONSE = (
45
+ "bounded task-facing progress awaits independent scored-workspace validation"
46
+ )
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class SkillsBenchTurnAgentResult:
51
+ response_text: str
52
+ progress_evidence: Mapping[str, Any]
53
+
54
+
55
+ AgentPromptRunner = Callable[[str], Union[str, SkillsBenchTurnAgentResult]]
56
+ PublicTraceWriter = Callable[[dict[str, Any]], None]
57
+ TurnObserver = Callable[[dict[str, Any], dict[str, Any]], None]
58
+
59
+ SKILLSBENCH_LOOPX_TURN_CONTINUATION_PROMPT = """\
60
+ Continue the same task in this same Codex session. Inspect the current workspace
61
+ state, perform the next bounded task-facing step, and stop. Do not use or request
62
+ official verifier, reward, pass/fail, hidden-test, or gold-answer feedback.
63
+ """
64
+ SKILLSBENCH_LOOPX_TURN_STABILITY_PROMPT = """\
65
+ Review the current solution against the visible task requirements and inspect the
66
+ durable workspace state. Repair concrete defects and run bounded task-derived
67
+ checks when useful. If no repair is needed, stop without manufacturing a change.
68
+ Do not use or request official verifier, reward, pass/fail, hidden-test, or
69
+ gold-answer feedback.
70
+ """
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class SkillsBenchTurnRuntimeConfig:
75
+ bridge_command: str
76
+ validation_command: str
77
+ goal_id: str
78
+ agent_id: str
79
+ runtime_root: Path
80
+ bridge_timeout_seconds: float = 30.0
81
+ agent_timeout_seconds: float = 7200.0
82
+ max_turns: int = 1
83
+ progress_exit_code: int = 10
84
+ terminal_policy: str = "validator"
85
+ sequence_baseline_path: str = ""
86
+ case_cli_path: str = "/app/.local/bin/loopx"
87
+ case_registry_path: str = "/app/.loopx/registry.json"
88
+ case_runtime_root: str = "/app/.loopx/runtime"
89
+
90
+
91
+ class SkillsBenchTurnBridgeError(RuntimeError):
92
+ """A compact failure raised at the host/scored-workspace boundary."""
93
+
94
+ def __init__(
95
+ self,
96
+ message: str,
97
+ *,
98
+ stage: str = "bridge_operation",
99
+ category: str = "bridge_operation_failed",
100
+ exit_code: int | None = None,
101
+ ) -> None:
102
+ super().__init__(message)
103
+ self.stage = stage
104
+ self.category = category
105
+ self.exit_code = exit_code
106
+
107
+
108
+ class SkillsBenchTurnAgentFailure(RuntimeError):
109
+ """A recoverable host Agent CLI failure that must not commit a Turn."""
110
+
111
+
112
+ def _loopx_cli_failure_category(stdout: Any, stderr: Any) -> str:
113
+ try:
114
+ payload = json.loads(str(stdout or ""))
115
+ except json.JSONDecodeError:
116
+ payload = {}
117
+ error = " ".join(
118
+ f"{payload.get('error') if isinstance(payload, dict) else ''} {stderr or ''}".lower().split()
119
+ )
120
+ classifiers = (
121
+ (r"/app/\.local/bin/loopx.*not found", "case_loopx_cli_missing"),
122
+ (r"no module named loopx", "case_loopx_source_missing"),
123
+ (r"loopx cli requires python", "case_python_runtime_missing"),
124
+ (r"python 3\.11\+ is required", "unsupported_python_runtime"),
125
+ (r"permission denied", "scored_workspace_permission_denied"),
126
+ (r"no such file|not found", "scored_workspace_path_missing"),
127
+ (r"unknown agent|agent .*not registered", "case_agent_not_registered"),
128
+ (r"goal .*not found|no matching goal", "case_goal_not_found"),
129
+ (r"public boundary|private material", "case_public_boundary_rejected"),
130
+ (r"operator inbox|lark", "case_operator_inbox_projection_failed"),
131
+ )
132
+ for pattern, category in classifiers:
133
+ if re.search(pattern, error):
134
+ return category
135
+ return "loopx_cli_command_failed"
136
+
137
+
138
+ class SkillsBenchTurnBridge:
139
+ def __init__(self, config: SkillsBenchTurnRuntimeConfig):
140
+ self._config = config
141
+ self.meaningful_operation_count = 0
142
+
143
+ def exec(
144
+ self,
145
+ command: str,
146
+ *,
147
+ meaningful: bool = False,
148
+ allow_nonzero: bool = False,
149
+ ) -> dict[str, Any]:
150
+ request = {
151
+ "operation": "exec",
152
+ "cwd": "/app",
153
+ "command": command,
154
+ "timeout_sec": max(1.0, self._config.bridge_timeout_seconds),
155
+ }
156
+ started = time.monotonic()
157
+ try:
158
+ proc = subprocess.run(
159
+ self._config.bridge_command,
160
+ input=json.dumps(request, separators=(",", ":")),
161
+ text=True,
162
+ stdout=subprocess.PIPE,
163
+ stderr=subprocess.PIPE,
164
+ shell=True,
165
+ timeout=max(2.0, self._config.bridge_timeout_seconds + 5.0),
166
+ check=False,
167
+ )
168
+ except (OSError, subprocess.TimeoutExpired) as exc:
169
+ raise SkillsBenchTurnBridgeError(
170
+ "bridge operation did not complete"
171
+ ) from exc
172
+ if proc.returncode != 0:
173
+ raise SkillsBenchTurnBridgeError(
174
+ "bridge command returned non-zero",
175
+ exit_code=proc.returncode,
176
+ )
177
+ try:
178
+ payload = json.loads(proc.stdout)
179
+ except json.JSONDecodeError as exc:
180
+ raise SkillsBenchTurnBridgeError("bridge response was not JSON") from exc
181
+ if not isinstance(payload, dict):
182
+ raise SkillsBenchTurnBridgeError("bridge response was not an object")
183
+ if payload.get("schema_version") != SKILLSBENCH_BRIDGE_OPERATION_SCHEMA_VERSION:
184
+ raise SkillsBenchTurnBridgeError("bridge operation schema was unsupported")
185
+ exit_code = payload.get("exit_code")
186
+ if payload.get("ok") is not True or exit_code != 0:
187
+ if (
188
+ allow_nonzero
189
+ and isinstance(exit_code, int)
190
+ and not isinstance(exit_code, bool)
191
+ and exit_code != 0
192
+ ):
193
+ if meaningful:
194
+ self.meaningful_operation_count += 1
195
+ return {
196
+ "ok": False,
197
+ "exit_code": exit_code,
198
+ "elapsed_ms": int((time.monotonic() - started) * 1000),
199
+ }
200
+ raise SkillsBenchTurnBridgeError(
201
+ "scored-workspace command failed",
202
+ category=_loopx_cli_failure_category(
203
+ payload.get("stdout"), payload.get("stderr")
204
+ ),
205
+ exit_code=(
206
+ exit_code
207
+ if isinstance(exit_code, int) and not isinstance(exit_code, bool)
208
+ else None
209
+ ),
210
+ )
211
+ if payload.get("stdout_truncated") is True:
212
+ raise SkillsBenchTurnBridgeError("scored-workspace stdout was truncated")
213
+ if meaningful:
214
+ self.meaningful_operation_count += 1
215
+ return {
216
+ "ok": True,
217
+ "exit_code": 0,
218
+ "stdout": str(payload.get("stdout") or ""),
219
+ "elapsed_ms": int((time.monotonic() - started) * 1000),
220
+ }
221
+
222
+ def loopx_json(self, command: str) -> dict[str, Any]:
223
+ result = self.exec(command)
224
+ try:
225
+ payload = json.loads(result["stdout"])
226
+ except json.JSONDecodeError as exc:
227
+ raise SkillsBenchTurnBridgeError("LoopX CLI output was not JSON") from exc
228
+ if not isinstance(payload, dict):
229
+ raise SkillsBenchTurnBridgeError("LoopX CLI output was not an object")
230
+ return payload
231
+
232
+
233
+ def _case_cli_prefix(config: SkillsBenchTurnRuntimeConfig) -> str:
234
+ return benchmark_case_loopx_command_prefix(
235
+ case_cli_path=config.case_cli_path,
236
+ case_registry_path=config.case_registry_path,
237
+ case_runtime_root=config.case_runtime_root,
238
+ )
239
+
240
+
241
+ def _turn_plan(
242
+ bridge: SkillsBenchTurnBridge,
243
+ config: SkillsBenchTurnRuntimeConfig,
244
+ *,
245
+ turn_instance_id: str | None = None,
246
+ ) -> dict[str, Any]:
247
+ prefix = _case_cli_prefix(config)
248
+ command = (
249
+ f"{prefix} turn plan "
250
+ f"--goal-id {shlex.quote(config.goal_id)} "
251
+ f"--agent-id {shlex.quote(config.agent_id)} "
252
+ "--host generic-cli --execution-mode isolated-headless "
253
+ "--include-transaction-detail --scan-root /app --limit 5"
254
+ )
255
+ if turn_instance_id:
256
+ command += f" --turn-instance-id {shlex.quote(turn_instance_id)}"
257
+ try:
258
+ payload = bridge.loopx_json(command)
259
+ except SkillsBenchTurnBridgeError as exc:
260
+ raise SkillsBenchTurnBridgeError(
261
+ str(exc),
262
+ stage="turn_plan",
263
+ category=exc.category,
264
+ exit_code=exc.exit_code,
265
+ ) from exc
266
+ if payload.get("ok") is not True:
267
+ raise SkillsBenchTurnBridgeError(
268
+ "LoopX Turn plan was not executable",
269
+ stage="turn_plan",
270
+ category="loopx_turn_plan_not_executable",
271
+ )
272
+ return payload
273
+
274
+
275
+ def _host_result(request: Mapping[str, Any], response: str) -> dict[str, Any]:
276
+ turn_key = str(request.get("turn_key") or "")
277
+ recoverable_failure = response.startswith(RECOVERABLE_CODEX_TURN_FAILURE_PREFIX)
278
+ if recoverable_failure:
279
+ raise SkillsBenchTurnAgentFailure("agent CLI execution requires repair")
280
+ validation_handoff = (
281
+ response == SKILLSBENCH_TURN_AGENT_VALIDATION_HANDOFF_RESPONSE
282
+ )
283
+ return {
284
+ "schema_version": "loopx_turn_result_v0",
285
+ "turn_key": turn_key,
286
+ "result_kind": "validated_progress",
287
+ "completed_phases": ["host_execute", "typed_result"],
288
+ "classification": (
289
+ "skillsbench_loopx_turn_agent_cli_validation_handoff"
290
+ if validation_handoff
291
+ else "skillsbench_loopx_turn_agent_cli_progress"
292
+ ),
293
+ "recommended_action": "continue from the case-local LoopX frontier if work remains",
294
+ "next_action": "use the next typed Turn rather than an ungoverned prompt poll",
295
+ "delivery_batch_scale": "single_surface",
296
+ "delivery_outcome": "outcome_progress",
297
+ "vision_unchanged_reason": "the benchmark case objective is unchanged",
298
+ "summary": (
299
+ "agent CLI yielded bounded task-facing progress for independent validation"
300
+ if validation_handoff
301
+ else "agent CLI completed one bounded scored-workspace turn"
302
+ ),
303
+ }
304
+
305
+
306
+ def _agent_result(value: str | SkillsBenchTurnAgentResult) -> tuple[str, dict[str, Any]]:
307
+ if isinstance(value, SkillsBenchTurnAgentResult):
308
+ return value.response_text, dict(value.progress_evidence)
309
+ return str(value), {}
310
+
311
+
312
+ def _verified_bridge_content_progress(value: Mapping[str, Any]) -> bool:
313
+ count = value.get("successful_task_file_change_count")
314
+ return bool(
315
+ value.get("schema_version")
316
+ == "skillsbench_bridge_task_progress_receipt_v0"
317
+ and value.get("status") == "verified_task_content_change"
318
+ and isinstance(count, int)
319
+ and not isinstance(count, bool)
320
+ and count > 0
321
+ and value.get("raw_material_recorded") is False
322
+ )
323
+
324
+
325
+ def _turn_baseline_path(request: Mapping[str, Any]) -> str:
326
+ turn_key = str(request.get("turn_key") or "")
327
+ digest = hashlib.sha256(turn_key.encode("utf-8")).hexdigest()[:24]
328
+ return f"/tmp/loopx-turn-agent-baseline-{digest}"
329
+
330
+
331
+ def _callback_payload(payload: Mapping[str, Any]) -> dict[str, Any]:
332
+ return {
333
+ "ok": payload.get("ok") is True,
334
+ "appended": payload.get("appended") is True,
335
+ "classification": payload.get("classification"),
336
+ "generated_at": payload.get("generated_at"),
337
+ "slots": payload.get("slots"),
338
+ "reason": payload.get("reason") or payload.get("error"),
339
+ }
340
+
341
+
342
+ def _validation_baseline(
343
+ bridge: SkillsBenchTurnBridge,
344
+ config: SkillsBenchTurnRuntimeConfig,
345
+ ) -> dict[str, Any]:
346
+ """Observe whether the task postcondition is satisfied before agent work."""
347
+
348
+ try:
349
+ result = bridge.exec(
350
+ _completion_validation_command(config), allow_nonzero=True
351
+ )
352
+ except SkillsBenchTurnBridgeError as exc:
353
+ raise SkillsBenchTurnBridgeError(
354
+ str(exc),
355
+ stage="validation_baseline",
356
+ category=exc.category,
357
+ exit_code=exc.exit_code,
358
+ ) from exc
359
+ exit_code = result.get("exit_code")
360
+ if result.get("ok") is True:
361
+ status = "already_satisfied"
362
+ elif exit_code == config.progress_exit_code:
363
+ status = "progress_validated"
364
+ else:
365
+ status = "unsatisfied"
366
+ return {
367
+ "schema_version": "skillsbench_validation_baseline_v0",
368
+ "status": status,
369
+ "postcondition_kind": "task_declared_independent_command",
370
+ "exit_code": exit_code,
371
+ "raw_command_recorded": False,
372
+ "raw_output_recorded": False,
373
+ }
374
+
375
+
376
+ def _completion_validation_command(config: SkillsBenchTurnRuntimeConfig) -> str:
377
+ if not config.sequence_baseline_path:
378
+ return config.validation_command
379
+ return (
380
+ f"env {SKILLSBENCH_TURN_SEQUENCE_BASELINE_ENV}="
381
+ f"{shlex.quote(config.sequence_baseline_path)} sh -c "
382
+ f"{shlex.quote(config.validation_command)}"
383
+ )
384
+
385
+
386
+ def run_skillsbench_loopx_turn(
387
+ *,
388
+ prompt: str,
389
+ agent_runner: AgentPromptRunner,
390
+ config: SkillsBenchTurnRuntimeConfig,
391
+ turn_instance_id: str | None = None,
392
+ sequence_step_kind: str = "validator",
393
+ ) -> tuple[dict[str, Any], dict[str, Any]]:
394
+ """Execute one real Turn and return its receipt plus compact validation."""
395
+
396
+ if not config.bridge_command:
397
+ raise ValueError("SkillsBench LoopX Turn requires a scored-workspace bridge")
398
+ if not config.validation_command:
399
+ raise ValueError(
400
+ "SkillsBench LoopX Turn requires an independent validation command"
401
+ )
402
+ if not 1 <= config.progress_exit_code <= 255:
403
+ raise ValueError("SkillsBench progress exit code must be between 1 and 255")
404
+ if config.terminal_policy not in SKILLSBENCH_LOOPX_TURN_TERMINAL_POLICIES:
405
+ raise ValueError(
406
+ "SkillsBench terminal policy must be validator, fixed-n, or stability"
407
+ )
408
+ if config.terminal_policy == "stability" and not config.sequence_baseline_path:
409
+ raise ValueError("SkillsBench stability policy requires a sequence baseline")
410
+ if sequence_step_kind not in {"validator", "progress", "terminal"}:
411
+ raise ValueError("SkillsBench sequence step kind was unsupported")
412
+ bridge = SkillsBenchTurnBridge(config)
413
+ if config.terminal_policy == "stability":
414
+ sequence_baseline_path = shlex.quote(config.sequence_baseline_path)
415
+ sequence_baseline_dir = shlex.quote(
416
+ str(Path(config.sequence_baseline_path).parent)
417
+ )
418
+ bridge.exec(
419
+ f"umask 077; mkdir -p {sequence_baseline_dir}; "
420
+ f"test -e {sequence_baseline_path} || : > {sequence_baseline_path}"
421
+ )
422
+ plan = (
423
+ _turn_plan(bridge, config, turn_instance_id=turn_instance_id)
424
+ if turn_instance_id is not None
425
+ else _turn_plan(bridge, config)
426
+ )
427
+ validation_baseline = _validation_baseline(bridge, config)
428
+ prefix = _case_cli_prefix(config)
429
+ baseline_path = ""
430
+ agent_progress_evidence: dict[str, Any] = {}
431
+ stability_validation_evidence: dict[str, bool] = {}
432
+
433
+ def host_runner(request: Mapping[str, Any]) -> dict[str, Any]:
434
+ nonlocal agent_progress_evidence, baseline_path
435
+ baseline_path = _turn_baseline_path(request)
436
+ bridge.exec(f"umask 077; : > {shlex.quote(baseline_path)}")
437
+ response, agent_progress_evidence = _agent_result(agent_runner(prompt))
438
+ return _host_result(request, response)
439
+
440
+ def validator(
441
+ _plan: Mapping[str, Any],
442
+ _result: Mapping[str, Any],
443
+ ) -> Mapping[str, Any]:
444
+ nonlocal stability_validation_evidence
445
+ if not baseline_path:
446
+ return {
447
+ "status": "failed",
448
+ "validator_kind": "skillsbench_scored_workspace_command",
449
+ "summary": "independent scored-workspace baseline was unavailable",
450
+ "recovery_kind": "repair_required",
451
+ "exit_code": 1,
452
+ }
453
+ validation_command = (
454
+ f"env {SKILLSBENCH_TURN_BASELINE_ENV}="
455
+ f"{shlex.quote(baseline_path)} sh -c "
456
+ f"{shlex.quote(config.validation_command)}"
457
+ )
458
+ try:
459
+ validation_result = bridge.exec(
460
+ validation_command,
461
+ meaningful=True,
462
+ allow_nonzero=True,
463
+ )
464
+ except SkillsBenchTurnBridgeError:
465
+ return {
466
+ "status": "failed",
467
+ "validator_kind": "skillsbench_scored_workspace_command",
468
+ "summary": "independent scored-workspace validation returned non-zero",
469
+ "recovery_kind": "repair_required",
470
+ "exit_code": 1,
471
+ }
472
+ exit_code = validation_result.get("exit_code")
473
+ if config.terminal_policy == "stability":
474
+ try:
475
+ completion_result = bridge.exec(
476
+ _completion_validation_command(config),
477
+ meaningful=True,
478
+ allow_nonzero=True,
479
+ )
480
+ except SkillsBenchTurnBridgeError:
481
+ completion_result = {"ok": False, "exit_code": 1}
482
+ completion_satisfied = completion_result.get("ok") is True
483
+ progress_detected = bool(
484
+ exit_code == config.progress_exit_code
485
+ or _verified_bridge_content_progress(agent_progress_evidence)
486
+ )
487
+ stability_validation_evidence = {
488
+ "stability_progress_detected": progress_detected,
489
+ "stability_completion_satisfied": completion_satisfied,
490
+ "stability_completion_checked": True,
491
+ }
492
+ if sequence_step_kind == "terminal" and completion_satisfied:
493
+ return {
494
+ "status": "passed",
495
+ "validator_kind": "skillsbench_stability_postcondition",
496
+ "summary": "independent completion postcondition passed at the Turn limit",
497
+ "exit_code": 0,
498
+ }
499
+ if progress_detected:
500
+ return {
501
+ "status": "progress",
502
+ "validator_kind": "skillsbench_stability_postcondition",
503
+ "summary": "independent task progress requires another review Turn",
504
+ "exit_code": config.progress_exit_code,
505
+ }
506
+ if completion_satisfied:
507
+ return {
508
+ "status": "passed",
509
+ "validator_kind": "skillsbench_stability_postcondition",
510
+ "summary": "no new repair was needed and the completion postcondition passed",
511
+ "exit_code": 0,
512
+ }
513
+ return {
514
+ "status": "failed",
515
+ "validator_kind": "skillsbench_stability_postcondition",
516
+ "summary": "neither independent progress nor completion was validated",
517
+ "recovery_kind": "repair_required",
518
+ "exit_code": completion_result.get("exit_code"),
519
+ }
520
+ validation_succeeded = exit_code in {0, config.progress_exit_code}
521
+ if not validation_succeeded:
522
+ if _verified_bridge_content_progress(agent_progress_evidence):
523
+ effective_step_kind = sequence_step_kind
524
+ if effective_step_kind == "validator":
525
+ effective_step_kind = "progress"
526
+ if effective_step_kind == "terminal":
527
+ return {
528
+ "status": "passed",
529
+ "validator_kind": "skillsbench_bridge_content_progress",
530
+ "summary": "independent task-facing content change validated progress",
531
+ "exit_code": 0,
532
+ }
533
+ return {
534
+ "status": "progress",
535
+ "validator_kind": "skillsbench_bridge_content_progress",
536
+ "summary": "independent task-facing content change validated progress",
537
+ "exit_code": config.progress_exit_code,
538
+ }
539
+ return {
540
+ "status": "failed",
541
+ "validator_kind": "skillsbench_scored_workspace_command",
542
+ "summary": "independent scored-workspace validation returned non-zero",
543
+ "recovery_kind": "repair_required",
544
+ "exit_code": exit_code,
545
+ }
546
+ effective_step_kind = sequence_step_kind
547
+ if effective_step_kind == "validator":
548
+ effective_step_kind = (
549
+ "progress" if exit_code == config.progress_exit_code else "terminal"
550
+ )
551
+ if effective_step_kind == "progress":
552
+ return {
553
+ "status": "progress",
554
+ "validator_kind": "skillsbench_scored_workspace_command",
555
+ "summary": "independent scored-workspace progress validation passed",
556
+ "exit_code": config.progress_exit_code,
557
+ }
558
+ return {
559
+ "status": "passed",
560
+ "validator_kind": "skillsbench_scored_workspace_command",
561
+ "summary": "independent scored-workspace validation passed",
562
+ "exit_code": 0,
563
+ }
564
+
565
+ def writeback(result: dict[str, Any]) -> dict[str, Any]:
566
+ command = (
567
+ f"{prefix} refresh-state "
568
+ f"--goal-id {shlex.quote(config.goal_id)} "
569
+ f"--classification {shlex.quote(str(result['classification']))} "
570
+ f"--recommended-action {shlex.quote(str(result['recommended_action']))} "
571
+ f"--next-action {shlex.quote(str(result['next_action']))} "
572
+ f"--delivery-batch-scale {shlex.quote(str(result['delivery_batch_scale']))} "
573
+ f"--delivery-outcome {shlex.quote(str(result['delivery_outcome']))} "
574
+ f"--agent-id {shlex.quote(config.agent_id)} "
575
+ "--progress-scope goal "
576
+ f"--vision-unchanged-reason {shlex.quote(str(result['vision_unchanged_reason']))} "
577
+ "--no-global-sync"
578
+ )
579
+ return _callback_payload(bridge.loopx_json(command))
580
+
581
+ def spend() -> dict[str, Any]:
582
+ command = (
583
+ f"{prefix} quota spend-slot "
584
+ f"--goal-id {shlex.quote(config.goal_id)} --slots 1 "
585
+ "--source adapter --execute "
586
+ f"--agent-id {shlex.quote(config.agent_id)}"
587
+ )
588
+ return _callback_payload(bridge.loopx_json(command))
589
+
590
+ def scheduler(_spend_payload: dict[str, Any]) -> dict[str, Any]:
591
+ command = (
592
+ f"{prefix} quota should-run "
593
+ f"--goal-id {shlex.quote(config.goal_id)} "
594
+ f"--agent-id {shlex.quote(config.agent_id)} "
595
+ "--runtime-profile outer_controller"
596
+ )
597
+ payload = bridge.loopx_json(command)
598
+ hint = payload.get("scheduler_hint")
599
+ hint = hint if isinstance(hint, dict) else {}
600
+ phase = hint.get("execution_phase")
601
+ if not isinstance(phase, dict):
602
+ return {
603
+ "disposition": "contract_error",
604
+ "completed": False,
605
+ "acknowledged": False,
606
+ "apply_needed": False,
607
+ }
608
+ return dict(phase)
609
+
610
+ try:
611
+ execution = run_loopx_turn_once(
612
+ plan,
613
+ host_runner=host_runner,
614
+ project=Path("/app"),
615
+ runtime_root=config.runtime_root,
616
+ goal_id=config.goal_id,
617
+ timeout_seconds=config.agent_timeout_seconds,
618
+ execute=True,
619
+ retry_failed=True,
620
+ task_validator=validator,
621
+ writeback=writeback,
622
+ spend=spend,
623
+ scheduler=scheduler,
624
+ )
625
+ finally:
626
+ if baseline_path:
627
+ try:
628
+ bridge.exec(f"rm -f {shlex.quote(baseline_path)}")
629
+ except SkillsBenchTurnBridgeError:
630
+ pass
631
+ transaction_validation = execution.get("validation")
632
+ transaction_validation = (
633
+ transaction_validation if isinstance(transaction_validation, dict) else {}
634
+ )
635
+ validation_status = transaction_validation.get("status")
636
+ validation_passed = validation_status in {"passed", "progress"}
637
+ terminal_complete = validation_status == "passed"
638
+ validated_progress = validation_status in {"passed", "progress"}
639
+ bridge_content_progress = _verified_bridge_content_progress(agent_progress_evidence)
640
+ write_count = agent_progress_evidence.get("successful_task_file_write_count")
641
+ if not isinstance(write_count, int) or isinstance(write_count, bool):
642
+ write_count = 0
643
+ change_count = agent_progress_evidence.get("successful_task_file_change_count")
644
+ if not isinstance(change_count, int) or isinstance(change_count, bool):
645
+ change_count = 0
646
+ scored_validation = {
647
+ "schema_version": "skillsbench_scored_workspace_validation_v0",
648
+ "status": ("passed" if validation_passed else "failed"),
649
+ "independent": True,
650
+ "validator_kind": str(
651
+ transaction_validation.get("validator_kind")
652
+ or "skillsbench_scored_workspace_command"
653
+ ),
654
+ "pre_agent_postcondition_checked": True,
655
+ "pre_agent_postcondition_status": validation_baseline["status"],
656
+ "post_agent_postcondition_status": (
657
+ "satisfied"
658
+ if terminal_complete
659
+ else "progress_validated"
660
+ if validated_progress
661
+ else "unsatisfied"
662
+ ),
663
+ "validated_progress": validated_progress,
664
+ "terminal_complete": terminal_complete,
665
+ "terminal_policy": config.terminal_policy,
666
+ "sequence_baseline_configured": bool(config.sequence_baseline_path),
667
+ "stability_progress_detected": bool(
668
+ stability_validation_evidence.get("stability_progress_detected") is True
669
+ ),
670
+ "stability_completion_checked": bool(
671
+ stability_validation_evidence.get("stability_completion_checked") is True
672
+ ),
673
+ "stability_completion_satisfied": bool(
674
+ stability_validation_evidence.get("stability_completion_satisfied") is True
675
+ ),
676
+ "baseline_contract": (
677
+ "task_declared_independent_postcondition_or_verified_content_change"
678
+ ),
679
+ "progress_evidence_kind": (
680
+ "verified_task_content_change"
681
+ if bridge_content_progress
682
+ else "scored_workspace_command"
683
+ ),
684
+ "successful_task_file_write_count": max(0, write_count),
685
+ "successful_task_file_change_count": max(0, change_count),
686
+ "oracle_feedback_used": False,
687
+ "meaningful_operation_count": bridge.meaningful_operation_count,
688
+ "raw_validator_output_recorded": False,
689
+ "raw_task_text_recorded": False,
690
+ "raw_verifier_output_recorded": False,
691
+ }
692
+ return execution, scored_validation
693
+
694
+
695
+ def run_skillsbench_loopx_turn_sequence(
696
+ *,
697
+ prompt: str,
698
+ agent_runner: AgentPromptRunner,
699
+ config: SkillsBenchTurnRuntimeConfig,
700
+ turn_observer: TurnObserver | None = None,
701
+ ) -> tuple[list[tuple[dict[str, Any], dict[str, Any]]], dict[str, Any]]:
702
+ """Run up to N independently validated Turns within one total time budget."""
703
+
704
+ if config.max_turns < 1:
705
+ raise ValueError("SkillsBench LoopX Turn max_turns must be at least 1")
706
+ if config.terminal_policy not in SKILLSBENCH_LOOPX_TURN_TERMINAL_POLICIES:
707
+ raise ValueError(
708
+ "SkillsBench terminal policy must be validator, fixed-n, or stability"
709
+ )
710
+ sequence_id = uuid.uuid4().hex[:16]
711
+ turn_sequence_ref = (
712
+ "sequence:" + hashlib.sha256(sequence_id.encode("utf-8")).hexdigest()[:16]
713
+ )
714
+ sequence_baseline_path = (
715
+ f"{config.case_runtime_root.rstrip('/')}"
716
+ f"/benchmark-turn-sequences/{sequence_id}.baseline"
717
+ )
718
+ sequence_config = replace(
719
+ config,
720
+ sequence_baseline_path=(
721
+ sequence_baseline_path if config.terminal_policy == "stability" else ""
722
+ ),
723
+ )
724
+ deadline = time.monotonic() + max(1.0, config.agent_timeout_seconds)
725
+ records: list[tuple[dict[str, Any], dict[str, Any]]] = []
726
+ stop_reason = "max_turns_reached"
727
+ for turn_index in range(1, config.max_turns + 1):
728
+ remaining_seconds = deadline - time.monotonic()
729
+ if remaining_seconds <= 0:
730
+ stop_reason = "time_budget_exhausted"
731
+ break
732
+ if turn_index == 1:
733
+ turn_prompt = prompt
734
+ elif config.terminal_policy == "stability":
735
+ turn_prompt = SKILLSBENCH_LOOPX_TURN_STABILITY_PROMPT
736
+ else:
737
+ turn_prompt = SKILLSBENCH_LOOPX_TURN_CONTINUATION_PROMPT
738
+ sequence_step_kind = "validator"
739
+ if config.terminal_policy == "fixed-n":
740
+ sequence_step_kind = (
741
+ "terminal" if turn_index == config.max_turns else "progress"
742
+ )
743
+ elif (
744
+ config.terminal_policy == "stability"
745
+ and turn_index == config.max_turns
746
+ ):
747
+ sequence_step_kind = "terminal"
748
+ execution, validation = run_skillsbench_loopx_turn(
749
+ prompt=turn_prompt,
750
+ agent_runner=agent_runner,
751
+ config=replace(
752
+ sequence_config, agent_timeout_seconds=remaining_seconds
753
+ ),
754
+ turn_instance_id=f"{sequence_id}-turn-{turn_index:03d}",
755
+ sequence_step_kind=sequence_step_kind,
756
+ )
757
+ validation["turn_index"] = turn_index
758
+ validation["turn_sequence_ref"] = turn_sequence_ref
759
+ records.append((execution, validation))
760
+ verified_content_progress = bool(
761
+ validation.get("progress_evidence_kind")
762
+ == "verified_task_content_change"
763
+ )
764
+ sequence_terminal_complete = bool(
765
+ validation.get("terminal_complete") is True
766
+ and not (
767
+ config.terminal_policy == "validator"
768
+ and turn_index < config.max_turns
769
+ and verified_content_progress
770
+ )
771
+ )
772
+ validation["sequence_terminal_complete"] = sequence_terminal_complete
773
+ if execution.get("status") != "committed":
774
+ stop_reason = "turn_not_committed"
775
+ elif sequence_terminal_complete:
776
+ stop_reason = "terminal_complete"
777
+ elif validation.get("validated_progress") is not True:
778
+ stop_reason = "no_validated_progress"
779
+ elif turn_index < config.max_turns:
780
+ stop_reason = "continue"
781
+ else:
782
+ stop_reason = "max_turns_reached"
783
+ validation["sequence_stop_reason"] = stop_reason
784
+ if turn_observer is not None:
785
+ turn_observer(execution, validation)
786
+ if stop_reason != "continue":
787
+ break
788
+ return records, {
789
+ "schema_version": "skillsbench_loopx_turn_sequence_v0",
790
+ "status": stop_reason,
791
+ "turn_count": len(records),
792
+ "max_turns": config.max_turns,
793
+ "turn_sequence_ref": turn_sequence_ref,
794
+ "terminal_policy": config.terminal_policy,
795
+ "terminal_complete": stop_reason == "terminal_complete",
796
+ "official_feedback_blinded": True,
797
+ "reward_feedback_forwarded": False,
798
+ }
799
+
800
+
801
+ def build_skillsbench_benchmark_runner_readiness(
802
+ *,
803
+ execution: Mapping[str, Any],
804
+ scored_workspace_validation: Mapping[str, Any],
805
+ ) -> dict[str, Any]:
806
+ """Reduce a Turn outcome to a compact, public-safe runner capability receipt."""
807
+
808
+ pre_agent_unsatisfied = scored_workspace_validation.get(
809
+ "pre_agent_postcondition_status"
810
+ ) in {"unsatisfied", "progress_validated"}
811
+ stability_terminal_review = bool(
812
+ scored_workspace_validation.get("terminal_policy") == "stability"
813
+ and scored_workspace_validation.get("terminal_complete") is True
814
+ and scored_workspace_validation.get("stability_completion_checked") is True
815
+ and scored_workspace_validation.get("stability_completion_satisfied") is True
816
+ )
817
+ checks = {
818
+ "turn_transaction_committed": execution.get("status") == "committed",
819
+ "independent_scored_workspace_validation_passed": bool(
820
+ scored_workspace_validation.get("status") == "passed"
821
+ and scored_workspace_validation.get("independent") is True
822
+ ),
823
+ "pre_agent_postcondition_checked": (
824
+ scored_workspace_validation.get("pre_agent_postcondition_checked") is True
825
+ ),
826
+ "pre_agent_postcondition_eligible": (
827
+ pre_agent_unsatisfied or stability_terminal_review
828
+ ),
829
+ "post_agent_postcondition_satisfied": (
830
+ scored_workspace_validation.get("post_agent_postcondition_status")
831
+ == "satisfied"
832
+ ),
833
+ "post_agent_postcondition_validated": (
834
+ scored_workspace_validation.get("post_agent_postcondition_status")
835
+ in {"progress_validated", "satisfied"}
836
+ ),
837
+ "official_feedback_blinded": (
838
+ scored_workspace_validation.get("oracle_feedback_used") is False
839
+ ),
840
+ }
841
+ blockers = [name for name, passed in checks.items() if not passed]
842
+ turn_proven = all(checks[name] for name in LOOPX_TURN_PROOF_CHECKS)
843
+ receipt = {
844
+ "schema_version": "skillsbench_benchmark_runner_readiness_v0",
845
+ "capability": "benchmark_runner",
846
+ "status": "ready" if not blockers else "blocked",
847
+ "ready": not blockers,
848
+ "turn_proven": turn_proven,
849
+ "checks": checks,
850
+ "blocker_codes": blockers,
851
+ "evidence_kind": "committed_turn_with_independent_postcondition",
852
+ "raw_task_text_recorded": False,
853
+ "raw_validator_output_recorded": False,
854
+ "raw_verifier_output_recorded": False,
855
+ "raw_trajectory_recorded": False,
856
+ "credential_values_recorded": False,
857
+ "local_paths_recorded": False,
858
+ }
859
+ turn_sequence_ref = scored_workspace_validation.get("turn_sequence_ref")
860
+ if isinstance(turn_sequence_ref, str) and re.fullmatch(
861
+ r"sequence:[0-9a-f]{16}", turn_sequence_ref
862
+ ):
863
+ receipt["turn_sequence_ref"] = turn_sequence_ref
864
+ turn_index = scored_workspace_validation.get("turn_index")
865
+ if isinstance(turn_index, int) and not isinstance(turn_index, bool):
866
+ receipt["turn_index"] = max(1, turn_index)
867
+ return receipt
868
+
869
+
870
+ def build_skillsbench_loopx_turn_trace(
871
+ *,
872
+ route: str,
873
+ benchmark_id: str,
874
+ task_id: str,
875
+ execution: Mapping[str, Any],
876
+ scored_workspace_validation: Mapping[str, Any],
877
+ ) -> dict[str, Any]:
878
+ """Build the only public trace emitted by the Turn runtime."""
879
+
880
+ return {
881
+ "schema_version": "skillsbench_host_local_acp_relay_public_trace_v0",
882
+ "ok": execution.get("status") == "committed",
883
+ "route": route,
884
+ "trace_kind": "loopx_turn_execution",
885
+ "benchmark_id": benchmark_id,
886
+ "task_id": task_id,
887
+ "loopx_turn_execution": dict(execution),
888
+ "scored_workspace_validation": dict(scored_workspace_validation),
889
+ "benchmark_runner_readiness": build_skillsbench_benchmark_runner_readiness(
890
+ execution=execution,
891
+ scored_workspace_validation=scored_workspace_validation,
892
+ ),
893
+ "official_feedback_blinded": True,
894
+ "reward_feedback_forwarded": False,
895
+ "boundary": {
896
+ "raw_command_recorded": False,
897
+ "raw_stdout_recorded": False,
898
+ "raw_stderr_recorded": False,
899
+ "raw_task_text_recorded": False,
900
+ "raw_verifier_output_recorded": False,
901
+ "raw_logs_recorded": False,
902
+ "raw_trajectory_recorded": False,
903
+ "credential_values_recorded": False,
904
+ "host_paths_recorded": False,
905
+ "remote_paths_recorded": False,
906
+ "upload_performed": False,
907
+ "submit_performed": False,
908
+ },
909
+ }
910
+
911
+
912
+ def build_skillsbench_loopx_turn_failure_trace(
913
+ *,
914
+ route: str,
915
+ benchmark_id: str,
916
+ task_id: str,
917
+ error: SkillsBenchTurnBridgeError,
918
+ ) -> dict[str, Any]:
919
+ """Build a public-safe receipt when Turn fails before execution exists."""
920
+
921
+ failure: dict[str, Any] = {
922
+ "category": error.category,
923
+ }
924
+ if error.exit_code is not None:
925
+ failure["exit_code"] = error.exit_code
926
+ execution = {
927
+ "schema_version": "loopx_turn_execution_v0",
928
+ "mode": "run_once",
929
+ "status": "failed",
930
+ "execution_mode": "isolated-headless",
931
+ "result_kind": "repair_required",
932
+ "quota_slot_spend_count": 0,
933
+ "failure": failure,
934
+ "receipt": {
935
+ "status": "failed",
936
+ "result_kind": "repair_required",
937
+ "failed_phase": error.stage,
938
+ "next_phase": error.stage,
939
+ "completed_phases": [],
940
+ },
941
+ "effects": {
942
+ "host_invoked": False,
943
+ "state_written": False,
944
+ "quota_spent": False,
945
+ "scheduler_acknowledged": False,
946
+ },
947
+ }
948
+ validation = {
949
+ "schema_version": "skillsbench_scored_workspace_validation_v0",
950
+ "status": "not_attempted",
951
+ "independent": True,
952
+ "validator_kind": "skillsbench_scored_workspace_command",
953
+ "pre_agent_postcondition_checked": error.stage == "validation_baseline",
954
+ "pre_agent_postcondition_status": (
955
+ "check_failed" if error.stage == "validation_baseline" else "not_attempted"
956
+ ),
957
+ "post_agent_postcondition_status": "not_attempted",
958
+ "baseline_contract": "task_declared_independent_postcondition",
959
+ "oracle_feedback_used": False,
960
+ "meaningful_operation_count": 0,
961
+ "raw_validator_output_recorded": False,
962
+ "raw_task_text_recorded": False,
963
+ "raw_verifier_output_recorded": False,
964
+ }
965
+ return build_skillsbench_loopx_turn_trace(
966
+ route=route,
967
+ benchmark_id=benchmark_id,
968
+ task_id=task_id,
969
+ execution=execution,
970
+ scored_workspace_validation=validation,
971
+ )
972
+
973
+
974
+ def run_skillsbench_loopx_turn_relay(
975
+ *,
976
+ prompt: str,
977
+ session_id: str,
978
+ relay_config: Any,
979
+ agent_runner: AgentPromptRunner,
980
+ trace_writer: PublicTraceWriter,
981
+ ) -> str:
982
+ """Adapt one relay prompt to the case-local Turn runtime."""
983
+
984
+ bridge_command = str(relay_config.remote_command_file_bridge_command or "")
985
+ validation_command = str(relay_config.loopx_turn_validation_command or "")
986
+ trace_root = str(relay_config.worker_public_trace_dir or "")
987
+ if not bridge_command:
988
+ raise RuntimeError("LoopX Turn requires the scored-workspace bridge")
989
+ if not validation_command:
990
+ raise RuntimeError("LoopX Turn requires an independent validation command")
991
+ if not trace_root:
992
+ raise RuntimeError("LoopX Turn requires a compact public trace directory")
993
+ max_turns = int(getattr(relay_config, "loopx_turn_max_turns", 1))
994
+ progress_exit_code = int(getattr(relay_config, "loopx_turn_progress_exit_code", 10))
995
+ terminal_policy = str(
996
+ getattr(relay_config, "loopx_turn_terminal_policy", "validator")
997
+ )
998
+ if max_turns < 1:
999
+ raise RuntimeError("LoopX Turn max Turns must be at least one")
1000
+ if not 1 <= progress_exit_code <= 255:
1001
+ raise RuntimeError("LoopX Turn progress exit code must be between 1 and 255")
1002
+ if terminal_policy not in SKILLSBENCH_LOOPX_TURN_TERMINAL_POLICIES:
1003
+ raise RuntimeError(
1004
+ "LoopX Turn terminal policy must be validator, fixed-n, or stability"
1005
+ )
1006
+ runtime_session = (
1007
+ "".join(
1008
+ char if char.isalnum() or char in "_.:-" else "_" for char in session_id
1009
+ )[:120]
1010
+ or "session"
1011
+ )
1012
+ try:
1013
+ turn_records, sequence = run_skillsbench_loopx_turn_sequence(
1014
+ prompt=prompt,
1015
+ agent_runner=agent_runner,
1016
+ config=SkillsBenchTurnRuntimeConfig(
1017
+ bridge_command=bridge_command,
1018
+ validation_command=validation_command,
1019
+ goal_id=relay_config.loopx_case_goal_id,
1020
+ agent_id=relay_config.loopx_case_agent_id,
1021
+ runtime_root=Path(trace_root).parent
1022
+ / ".loopx-turn-runtime"
1023
+ / runtime_session,
1024
+ bridge_timeout_seconds=max(
1025
+ 1.0, relay_config.remote_command_file_bridge_timeout_sec
1026
+ ),
1027
+ agent_timeout_seconds=max(1.0, float(relay_config.timeout_sec)),
1028
+ max_turns=max_turns,
1029
+ progress_exit_code=progress_exit_code,
1030
+ terminal_policy=terminal_policy,
1031
+ case_cli_path=relay_config.loopx_case_cli_path,
1032
+ case_registry_path=relay_config.loopx_case_registry_path,
1033
+ case_runtime_root=relay_config.loopx_case_runtime_root,
1034
+ ),
1035
+ turn_observer=lambda execution, scored_validation: trace_writer(
1036
+ build_skillsbench_loopx_turn_trace(
1037
+ route=relay_config.route,
1038
+ benchmark_id=relay_config.dataset,
1039
+ task_id=relay_config.task_id,
1040
+ execution=execution,
1041
+ scored_workspace_validation=scored_validation,
1042
+ )
1043
+ ),
1044
+ )
1045
+ except SkillsBenchTurnBridgeError as exc:
1046
+ trace_writer(
1047
+ build_skillsbench_loopx_turn_failure_trace(
1048
+ route=relay_config.route,
1049
+ benchmark_id=relay_config.dataset,
1050
+ task_id=relay_config.task_id,
1051
+ error=exc,
1052
+ )
1053
+ )
1054
+ return recoverable_codex_turn_failure_message(
1055
+ f"loopx_turn_{exc.stage}_{exc.category}"
1056
+ )
1057
+ if not turn_records:
1058
+ return recoverable_codex_turn_failure_message(
1059
+ "loopx_turn_" + str(sequence.get("status") or "failed")
1060
+ )
1061
+ execution, _scored_validation = turn_records[-1]
1062
+ if execution.get("status") != "committed":
1063
+ return recoverable_codex_turn_failure_message(
1064
+ "loopx_turn_" + str(execution.get("status") or "failed")
1065
+ )
1066
+ return (
1067
+ "loopx turn sequence stopped after "
1068
+ f"{len(turn_records)} committed turn(s): {sequence['status']}"
1069
+ )