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,1271 @@
1
+ """Domain-neutral trace and replay-point contracts for Explore Episode V2.
2
+
3
+ The core owns addressable execution lineage and agent-history cursors. An
4
+ adapter owns every environment-specific state object behind an opaque runtime
5
+ binding. Public records deliberately exclude both agent-state keys and
6
+ adapter binding keys.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import threading
12
+ import time
13
+ import uuid
14
+ from dataclasses import dataclass, field, replace
15
+ from enum import Enum
16
+ from typing import Any, Callable, Mapping, Protocol, Sequence
17
+
18
+ from .replay_metrics import ReplayMetricLedger
19
+ from .trace_runtime import (
20
+ AgentCursor,
21
+ TraceCursor,
22
+ TraceEvent,
23
+ TraceEventKind,
24
+ TraceLog,
25
+ freeze_public_mapping,
26
+ require_nonempty_text as _required_text,
27
+ )
28
+
29
+
30
+ class ReplayFidelity(str, Enum):
31
+ EXACT = "exact"
32
+ SEMANTIC_EQUIVALENT = "semantic_equivalent"
33
+ BEST_EFFORT = "best_effort"
34
+ NON_REPLAYABLE = "non_replayable"
35
+
36
+
37
+ class ReplayChildConcurrency(str, Enum):
38
+ """Adapter-declared safety boundary for an isolated child lease."""
39
+
40
+ SERIAL_ONLY = "serial_only"
41
+ THREAD_SAFE = "thread_safe"
42
+ PROCESS_ISOLATED = "process_isolated"
43
+
44
+
45
+ class ReplayRiskDisposition(str, Enum):
46
+ ALLOW = "allow"
47
+ DENY = "deny"
48
+
49
+
50
+ class ReplayChildFailureStage(str, Enum):
51
+ INTENT = "intent"
52
+ SUFFIX = "suffix"
53
+
54
+
55
+ _FIDELITY_RANK = {
56
+ ReplayFidelity.NON_REPLAYABLE: 0,
57
+ ReplayFidelity.BEST_EFFORT: 1,
58
+ ReplayFidelity.SEMANTIC_EQUIVALENT: 2,
59
+ ReplayFidelity.EXACT: 3,
60
+ }
61
+
62
+
63
+ def fidelity_meets(
64
+ actual: ReplayFidelity,
65
+ required: ReplayFidelity,
66
+ ) -> bool:
67
+ """Return whether ``actual`` is at least as trustworthy as ``required``."""
68
+
69
+ return (
70
+ _FIDELITY_RANK[ReplayFidelity(actual)]
71
+ >= _FIDELITY_RANK[ReplayFidelity(required)]
72
+ )
73
+
74
+
75
+ class ReplayPointLifecycle(str, Enum):
76
+ READY = "ready"
77
+ RELEASING = "releasing"
78
+ RELEASED = "released"
79
+ FAILED = "failed"
80
+
81
+
82
+ REPLAYABLE_EPISODE_V2_MODE = "replayable-v2"
83
+ _V2_PROTOCOL_METHODS = (
84
+ "capture_replay_state",
85
+ "restore_replay_state",
86
+ "validate_replay_equivalence",
87
+ "release_replay_state",
88
+ )
89
+ _ISOLATED_CHILD_PROTOCOL_METHODS = (
90
+ "fork_replay_child",
91
+ "validate_replay_child_equivalence",
92
+ "release_replay_child",
93
+ )
94
+ _CHILD_RISK_PROTOCOL_METHODS = (
95
+ "assess_replay_child_intent",
96
+ "assess_replay_child_outcome",
97
+ )
98
+
99
+
100
+ def replayable_episode_v2_mode(adapter: Any) -> str | None:
101
+ """Detect the optional V2 protocol and reject partial implementations."""
102
+
103
+ declared = [hasattr(adapter, name) for name in _V2_PROTOCOL_METHODS]
104
+ implemented = [
105
+ callable(getattr(adapter, name, None)) for name in _V2_PROTOCOL_METHODS
106
+ ]
107
+ if any(declared) and not all(implemented):
108
+ missing = [
109
+ name
110
+ for name, available in zip(_V2_PROTOCOL_METHODS, implemented)
111
+ if not available
112
+ ]
113
+ raise ValueError(
114
+ "replayable Episode V2 adapters must implement all protocol methods; "
115
+ f"missing callable(s): {', '.join(missing)}"
116
+ )
117
+ return REPLAYABLE_EPISODE_V2_MODE if all(implemented) else None
118
+
119
+
120
+ def isolated_replay_child_mode(
121
+ adapter: Any,
122
+ agent_state_store: Any,
123
+ ) -> str | None:
124
+ """Detect the optional two-sided child isolation contract."""
125
+
126
+ declared = [hasattr(adapter, name) for name in _ISOLATED_CHILD_PROTOCOL_METHODS]
127
+ implemented = [
128
+ callable(getattr(adapter, name, None))
129
+ for name in _ISOLATED_CHILD_PROTOCOL_METHODS
130
+ ]
131
+ if any(declared) and not all(implemented):
132
+ missing = [
133
+ name
134
+ for name, available in zip(
135
+ _ISOLATED_CHILD_PROTOCOL_METHODS,
136
+ implemented,
137
+ )
138
+ if not available
139
+ ]
140
+ raise ValueError(
141
+ "isolated replay child adapters must implement all protocol "
142
+ f"methods; missing callable(s): {', '.join(missing)}"
143
+ )
144
+ if not all(implemented):
145
+ return None
146
+ if not callable(getattr(agent_state_store, "fork_agent_state", None)):
147
+ return None
148
+ return "isolated-child-v0"
149
+
150
+
151
+ def replay_child_risk_mode(adapter: Any) -> str | None:
152
+ """Detect the optional two-stage child risk interception contract."""
153
+
154
+ declared = [hasattr(adapter, name) for name in _CHILD_RISK_PROTOCOL_METHODS]
155
+ implemented = [
156
+ callable(getattr(adapter, name, None)) for name in _CHILD_RISK_PROTOCOL_METHODS
157
+ ]
158
+ if any(declared) and not all(implemented):
159
+ missing = [
160
+ name
161
+ for name, available in zip(
162
+ _CHILD_RISK_PROTOCOL_METHODS,
163
+ implemented,
164
+ )
165
+ if not available
166
+ ]
167
+ raise ValueError(
168
+ "replay child risk adapters must implement both protocol "
169
+ f"methods; missing callable(s): {', '.join(missing)}"
170
+ )
171
+ return "child-risk-v0" if all(implemented) else None
172
+
173
+
174
+ @dataclass(frozen=True)
175
+ class CaptureReplayStateRequest:
176
+ replay_point_id: str
177
+ cursor: TraceCursor
178
+
179
+
180
+ @dataclass(frozen=True)
181
+ class RestoreReplayStateRequest:
182
+ replay_point_id: str
183
+ target_branch_id: str
184
+ minimum_fidelity: ReplayFidelity
185
+
186
+
187
+ @dataclass(frozen=True)
188
+ class ForkReplayChildRequest:
189
+ replay_point_id: str
190
+ child_id: str
191
+ target_branch_id: str
192
+ minimum_fidelity: ReplayFidelity
193
+
194
+ def __post_init__(self) -> None:
195
+ for field_name in (
196
+ "replay_point_id",
197
+ "child_id",
198
+ "target_branch_id",
199
+ ):
200
+ object.__setattr__(
201
+ self,
202
+ field_name,
203
+ _required_text(getattr(self, field_name), field_name),
204
+ )
205
+ object.__setattr__(
206
+ self,
207
+ "minimum_fidelity",
208
+ ReplayFidelity(self.minimum_fidelity),
209
+ )
210
+ if self.minimum_fidelity == ReplayFidelity.NON_REPLAYABLE:
211
+ raise ValueError("isolated replay children require replayable fidelity")
212
+
213
+
214
+ @dataclass(frozen=True)
215
+ class AdapterStateLease:
216
+ """Runtime-only Adapter binding; ``binding_key`` is never serialized."""
217
+
218
+ binding_key: object | None
219
+ fidelity: ReplayFidelity
220
+ projection_digest: str | None = None
221
+
222
+ def __post_init__(self) -> None:
223
+ object.__setattr__(self, "fidelity", ReplayFidelity(self.fidelity))
224
+ if self.fidelity == ReplayFidelity.NON_REPLAYABLE:
225
+ if self.binding_key is not None:
226
+ raise ValueError("non_replayable leases must not expose a binding_key")
227
+ elif self.binding_key is None:
228
+ raise ValueError("replayable leases require an opaque binding_key")
229
+ if self.projection_digest is not None:
230
+ object.__setattr__(
231
+ self,
232
+ "projection_digest",
233
+ _required_text(self.projection_digest, "projection_digest"),
234
+ )
235
+
236
+
237
+ @dataclass(frozen=True)
238
+ class RestoreReceipt:
239
+ achieved_fidelity: ReplayFidelity
240
+ restored_projection_digest: str | None = None
241
+ prefix_reused: bool | None = None
242
+
243
+ def __post_init__(self) -> None:
244
+ object.__setattr__(
245
+ self,
246
+ "achieved_fidelity",
247
+ ReplayFidelity(self.achieved_fidelity),
248
+ )
249
+ if self.restored_projection_digest is not None:
250
+ object.__setattr__(
251
+ self,
252
+ "restored_projection_digest",
253
+ _required_text(
254
+ self.restored_projection_digest,
255
+ "restored_projection_digest",
256
+ ),
257
+ )
258
+ if self.prefix_reused is not None and not isinstance(
259
+ self.prefix_reused,
260
+ bool,
261
+ ):
262
+ raise TypeError("prefix_reused must be bool or None")
263
+
264
+
265
+ @dataclass(frozen=True)
266
+ class ReplayChildLease:
267
+ """Runtime-only isolated environment child and its restore receipt."""
268
+
269
+ child_key: object
270
+ receipt: RestoreReceipt
271
+ concurrency: ReplayChildConcurrency = ReplayChildConcurrency.SERIAL_ONLY
272
+ worker_identity: object | None = None
273
+
274
+ def __post_init__(self) -> None:
275
+ if self.child_key is None:
276
+ raise TypeError("ReplayChildLease requires an opaque child_key")
277
+ if not isinstance(self.receipt, RestoreReceipt):
278
+ raise TypeError("ReplayChildLease receipt must be a RestoreReceipt")
279
+ object.__setattr__(
280
+ self,
281
+ "concurrency",
282
+ ReplayChildConcurrency(self.concurrency),
283
+ )
284
+ if (
285
+ self.concurrency == ReplayChildConcurrency.PROCESS_ISOLATED
286
+ and self.worker_identity is None
287
+ ):
288
+ raise TypeError(
289
+ "process-isolated ReplayChildLease requires an opaque worker_identity"
290
+ )
291
+
292
+
293
+ @dataclass(frozen=True)
294
+ class ReplayRiskAssessment:
295
+ disposition: ReplayRiskDisposition
296
+ policy_id: str
297
+ sanitized_reason: str | None = None
298
+
299
+ def __post_init__(self) -> None:
300
+ object.__setattr__(
301
+ self,
302
+ "disposition",
303
+ ReplayRiskDisposition(self.disposition),
304
+ )
305
+ object.__setattr__(
306
+ self,
307
+ "policy_id",
308
+ _required_text(self.policy_id, "policy_id"),
309
+ )
310
+ if self.sanitized_reason is not None:
311
+ frozen = freeze_public_mapping(
312
+ {"sanitized_reason": self.sanitized_reason},
313
+ field_path="replay_risk_assessment",
314
+ )
315
+ object.__setattr__(
316
+ self,
317
+ "sanitized_reason",
318
+ frozen["sanitized_reason"],
319
+ )
320
+
321
+ def to_record(self) -> dict[str, Any]:
322
+ return {
323
+ "disposition": self.disposition.value,
324
+ "policy_id": self.policy_id,
325
+ "sanitized_reason": self.sanitized_reason,
326
+ }
327
+
328
+
329
+ class ReplayRiskRejectedError(RuntimeError):
330
+ def __init__(self, stage: str, assessment: ReplayRiskAssessment) -> None:
331
+ self.stage = _required_text(stage, "stage")
332
+ self.assessment = assessment
333
+ super().__init__(f"replay child {self.stage} denied by {assessment.policy_id}")
334
+
335
+
336
+ @dataclass(frozen=True)
337
+ class EquivalenceCheck:
338
+ check_id: str
339
+ passed: bool
340
+ public_summary: str | None = None
341
+
342
+ def __post_init__(self) -> None:
343
+ object.__setattr__(self, "check_id", _required_text(self.check_id, "check_id"))
344
+ object.__setattr__(self, "passed", bool(self.passed))
345
+ if self.public_summary is not None:
346
+ frozen = freeze_public_mapping(
347
+ {"public_summary": self.public_summary},
348
+ field_path="equivalence_check",
349
+ )
350
+ object.__setattr__(
351
+ self,
352
+ "public_summary",
353
+ frozen["public_summary"],
354
+ )
355
+
356
+
357
+ @dataclass(frozen=True)
358
+ class EquivalenceReport:
359
+ achieved_fidelity: ReplayFidelity
360
+ equivalent: bool
361
+ expected_digest: str | None = None
362
+ observed_digest: str | None = None
363
+ checks: tuple[EquivalenceCheck, ...] = ()
364
+ sanitized_reason: str | None = None
365
+
366
+ def __post_init__(self) -> None:
367
+ object.__setattr__(
368
+ self,
369
+ "achieved_fidelity",
370
+ ReplayFidelity(self.achieved_fidelity),
371
+ )
372
+ object.__setattr__(self, "equivalent", bool(self.equivalent))
373
+ for field_name in ("expected_digest", "observed_digest", "sanitized_reason"):
374
+ value = getattr(self, field_name)
375
+ if value is not None:
376
+ if field_name == "sanitized_reason":
377
+ frozen = freeze_public_mapping(
378
+ {field_name: value},
379
+ field_path="equivalence_report",
380
+ )
381
+ object.__setattr__(self, field_name, frozen[field_name])
382
+ else:
383
+ object.__setattr__(
384
+ self,
385
+ field_name,
386
+ _required_text(value, field_name),
387
+ )
388
+ normalized_checks = tuple(self.checks)
389
+ if not all(isinstance(check, EquivalenceCheck) for check in normalized_checks):
390
+ raise TypeError("checks must contain EquivalenceCheck values")
391
+ object.__setattr__(self, "checks", normalized_checks)
392
+
393
+
394
+ @dataclass(frozen=True)
395
+ class ReplayRestoreResult:
396
+ replay_point_id: str
397
+ target_branch_id: str
398
+ achieved_fidelity: ReplayFidelity
399
+ equivalent: bool
400
+ promotion_eligible: bool
401
+ prefix_reused: bool | None
402
+ report: EquivalenceReport
403
+ restore_minutes: float
404
+ equivalence_minutes: float
405
+ suffix_minutes: float
406
+ replay_wall_minutes: float
407
+
408
+ def to_record(self) -> dict[str, Any]:
409
+ return {
410
+ "replay_point_id": self.replay_point_id,
411
+ "target_branch_id": self.target_branch_id,
412
+ "achieved_fidelity": self.achieved_fidelity.value,
413
+ "equivalent": self.equivalent,
414
+ "promotion_eligible": self.promotion_eligible,
415
+ "prefix_reused": self.prefix_reused,
416
+ "expected_digest": self.report.expected_digest,
417
+ "observed_digest": self.report.observed_digest,
418
+ "checks": [
419
+ {
420
+ "check_id": check.check_id,
421
+ "passed": check.passed,
422
+ "public_summary": check.public_summary,
423
+ }
424
+ for check in self.report.checks
425
+ ],
426
+ "sanitized_reason": self.report.sanitized_reason,
427
+ "restore_minutes": self.restore_minutes,
428
+ "equivalence_minutes": self.equivalence_minutes,
429
+ "suffix_minutes": self.suffix_minutes,
430
+ "replay_wall_minutes": self.replay_wall_minutes,
431
+ }
432
+
433
+
434
+ @dataclass(frozen=True)
435
+ class ReplayChildExecutionContext:
436
+ child_id: str
437
+ restore: ReplayRestoreResult
438
+ isolated: bool
439
+ adapter_child_key: object | None = field(repr=False, compare=False)
440
+
441
+
442
+ @dataclass(frozen=True)
443
+ class ReplayChildTask:
444
+ child_id: str
445
+ target_branch_id: str
446
+ minimum_fidelity: ReplayFidelity
447
+ execute: Callable[[ReplayChildExecutionContext], Any] = field(
448
+ repr=False,
449
+ compare=False,
450
+ )
451
+ intent: Mapping[str, Any] = field(default_factory=dict)
452
+
453
+ def __post_init__(self) -> None:
454
+ object.__setattr__(
455
+ self,
456
+ "child_id",
457
+ _required_text(self.child_id, "child_id"),
458
+ )
459
+ object.__setattr__(
460
+ self,
461
+ "target_branch_id",
462
+ _required_text(self.target_branch_id, "target_branch_id"),
463
+ )
464
+ object.__setattr__(
465
+ self,
466
+ "minimum_fidelity",
467
+ ReplayFidelity(self.minimum_fidelity),
468
+ )
469
+ if self.minimum_fidelity == ReplayFidelity.NON_REPLAYABLE:
470
+ raise ValueError("ReplayChildTask requires replayable fidelity")
471
+ if not callable(self.execute):
472
+ raise TypeError("ReplayChildTask execute must be callable")
473
+ object.__setattr__(
474
+ self,
475
+ "intent",
476
+ freeze_public_mapping(self.intent, field_path="replay_child_intent"),
477
+ )
478
+
479
+
480
+ @dataclass(frozen=True)
481
+ class ReplayChildExecutionResult:
482
+ child_id: str
483
+ restore: ReplayRestoreResult
484
+ outcome: Any
485
+ isolated: bool
486
+ parallel: bool
487
+ intent_assessment: ReplayRiskAssessment | None = None
488
+ outcome_assessment: ReplayRiskAssessment | None = None
489
+
490
+ def to_record(self) -> dict[str, Any]:
491
+ return {
492
+ "child_id": self.child_id,
493
+ "restore": self.restore.to_record(),
494
+ "isolated": self.isolated,
495
+ "parallel": self.parallel,
496
+ "intent_assessment": (
497
+ self.intent_assessment.to_record()
498
+ if self.intent_assessment is not None
499
+ else None
500
+ ),
501
+ "outcome_assessment": (
502
+ self.outcome_assessment.to_record()
503
+ if self.outcome_assessment is not None
504
+ else None
505
+ ),
506
+ }
507
+
508
+
509
+ @dataclass(frozen=True)
510
+ class ReplayChildExecutionFailure:
511
+ child_id: str
512
+ restore: ReplayRestoreResult
513
+ stage: ReplayChildFailureStage
514
+ error_type: str
515
+ isolated: bool
516
+ parallel: bool
517
+ risk_assessment: ReplayRiskAssessment | None = None
518
+
519
+ def __post_init__(self) -> None:
520
+ object.__setattr__(self, "child_id", _required_text(self.child_id, "child_id"))
521
+ object.__setattr__(self, "stage", ReplayChildFailureStage(self.stage))
522
+ object.__setattr__(
523
+ self,
524
+ "error_type",
525
+ _required_text(self.error_type, "error_type"),
526
+ )
527
+
528
+ def to_record(self) -> dict[str, Any]:
529
+ return {
530
+ "child_id": self.child_id,
531
+ "restore": self.restore.to_record(),
532
+ "stage": self.stage.value,
533
+ "error_type": self.error_type,
534
+ "isolated": self.isolated,
535
+ "parallel": self.parallel,
536
+ "risk_assessment": (
537
+ self.risk_assessment.to_record()
538
+ if self.risk_assessment is not None
539
+ else None
540
+ ),
541
+ }
542
+
543
+
544
+ class AgentStateStore(Protocol):
545
+ def capture_agent_state(self, cursor: TraceCursor) -> object: ...
546
+
547
+ def restore_agent_state(self, state_key: object) -> None: ...
548
+
549
+ def release_agent_state(self, state_key: object) -> None: ...
550
+
551
+
552
+ class IsolatedAgentStateStore(AgentStateStore, Protocol):
553
+ def fork_agent_state(self, state_key: object, child_id: str) -> object: ...
554
+
555
+
556
+ class ReplayAdapterV2(Protocol):
557
+ def capture_replay_state(
558
+ self,
559
+ request: CaptureReplayStateRequest,
560
+ ) -> AdapterStateLease: ...
561
+
562
+ def restore_replay_state(
563
+ self,
564
+ binding_key: object,
565
+ request: RestoreReplayStateRequest,
566
+ ) -> RestoreReceipt: ...
567
+
568
+ def validate_replay_equivalence(
569
+ self,
570
+ binding_key: object,
571
+ receipt: RestoreReceipt,
572
+ ) -> EquivalenceReport: ...
573
+
574
+ def release_replay_state(self, binding_key: object) -> None: ...
575
+
576
+
577
+ class IsolatedReplayChildAdapter(ReplayAdapterV2, Protocol):
578
+ def fork_replay_child(
579
+ self,
580
+ binding_key: object,
581
+ request: ForkReplayChildRequest,
582
+ ) -> ReplayChildLease: ...
583
+
584
+ def validate_replay_child_equivalence(
585
+ self,
586
+ child_key: object,
587
+ receipt: RestoreReceipt,
588
+ ) -> EquivalenceReport: ...
589
+
590
+ def release_replay_child(self, child_key: object) -> None: ...
591
+
592
+
593
+ class RiskAwareReplayChildAdapter(IsolatedReplayChildAdapter, Protocol):
594
+ def assess_replay_child_intent(
595
+ self,
596
+ child_key: object,
597
+ intent: Mapping[str, Any],
598
+ ) -> ReplayRiskAssessment: ...
599
+
600
+ def assess_replay_child_outcome(
601
+ self,
602
+ child_key: object,
603
+ intent: Mapping[str, Any],
604
+ outcome: Any,
605
+ ) -> ReplayRiskAssessment: ...
606
+
607
+
608
+ @dataclass(frozen=True)
609
+ class ReplayPoint:
610
+ replay_point_id: str
611
+ cursor: TraceCursor
612
+ fidelity: ReplayFidelity
613
+ captured_projection_digest: str | None
614
+ lifecycle: ReplayPointLifecycle
615
+ created_event_id: str
616
+ replaces_replay_point_id: str | None = None
617
+
618
+ def to_record(self) -> dict[str, Any]:
619
+ """Return the public-safe record; runtime keys intentionally do not exist."""
620
+
621
+ return {
622
+ "replay_point_id": self.replay_point_id,
623
+ "trace_id": self.cursor.trace_id,
624
+ "branch_id": self.cursor.branch_id,
625
+ "after_event_id": self.cursor.after_event_id,
626
+ "next_sequence": self.cursor.next_sequence,
627
+ "agent_history_index": self.cursor.agent_cursor.history_index,
628
+ "agent_history_digest": self.cursor.agent_cursor.history_digest,
629
+ "agent_context_digest": self.cursor.agent_cursor.context_digest,
630
+ "fidelity": self.fidelity.value,
631
+ "captured_projection_digest": self.captured_projection_digest,
632
+ "lifecycle": self.lifecycle.value,
633
+ "created_event_id": self.created_event_id,
634
+ "replaces_replay_point_id": self.replaces_replay_point_id,
635
+ }
636
+
637
+
638
+ def _validate_equivalence_contract(
639
+ point: ReplayPoint,
640
+ receipt: RestoreReceipt,
641
+ report: EquivalenceReport,
642
+ minimum: ReplayFidelity,
643
+ ) -> bool:
644
+ if report.achieved_fidelity != receipt.achieved_fidelity:
645
+ raise ValueError("restore receipt and equivalence report fidelity disagree")
646
+ if (
647
+ receipt.restored_projection_digest is not None
648
+ and report.observed_digest is not None
649
+ and receipt.restored_projection_digest != report.observed_digest
650
+ ):
651
+ raise ValueError(
652
+ "restore receipt and equivalence report observed digests disagree"
653
+ )
654
+ if (
655
+ point.captured_projection_digest is not None
656
+ and report.expected_digest != point.captured_projection_digest
657
+ ):
658
+ raise ValueError(
659
+ "equivalence report expected digest does not match the captured ReplayPoint"
660
+ )
661
+ if report.equivalent and any(not check.passed for check in report.checks):
662
+ raise ValueError("equivalence report cannot pass while a declared check fails")
663
+ if report.achieved_fidelity == ReplayFidelity.EXACT:
664
+ if (
665
+ report.expected_digest is None
666
+ or report.observed_digest is None
667
+ or report.expected_digest != report.observed_digest
668
+ ):
669
+ raise ValueError("exact replay requires equal non-empty state digests")
670
+ trusted_fidelity = fidelity_meets(
671
+ report.achieved_fidelity,
672
+ ReplayFidelity.SEMANTIC_EQUIVALENT,
673
+ )
674
+ if (
675
+ report.achieved_fidelity == ReplayFidelity.SEMANTIC_EQUIVALENT
676
+ and not report.checks
677
+ ):
678
+ raise ValueError(
679
+ "semantic replay requires at least one explicit equivalence check"
680
+ )
681
+ promotion_eligible = bool(
682
+ report.equivalent
683
+ and trusted_fidelity
684
+ and fidelity_meets(report.achieved_fidelity, minimum)
685
+ )
686
+ if trusted_fidelity and not report.equivalent:
687
+ raise ValueError("exact or semantic replay requires verified state equivalence")
688
+ return promotion_eligible
689
+
690
+
691
+ @dataclass
692
+ class _RuntimeReplayBinding:
693
+ point: ReplayPoint
694
+ agent_state_key: object
695
+ adapter_binding_key: object | None
696
+ agent_state_store: AgentStateStore
697
+ adapter: ReplayAdapterV2
698
+ agent_released: bool = False
699
+ adapter_released: bool = False
700
+ replay_lock: threading.Lock = field(default_factory=threading.Lock)
701
+
702
+
703
+ class ReplayPointRegistry:
704
+ """Atomically bind a trace cursor to Core and Adapter runtime state."""
705
+
706
+ def __init__(
707
+ self,
708
+ *,
709
+ id_factory: Callable[[], str] | None = None,
710
+ event_id_factory: Callable[[], str] | None = None,
711
+ metrics: ReplayMetricLedger | None = None,
712
+ ) -> None:
713
+ self._id_factory = id_factory or (lambda: f"replay-{uuid.uuid4().hex}")
714
+ self._event_id_factory = event_id_factory or (
715
+ lambda: f"event-{uuid.uuid4().hex}"
716
+ )
717
+ self._entries: dict[str, _RuntimeReplayBinding] = {}
718
+ self._lock = threading.RLock()
719
+ self._metrics = metrics or ReplayMetricLedger()
720
+
721
+ def capture_at_head(
722
+ self,
723
+ trace: TraceLog,
724
+ agent_cursor: AgentCursor,
725
+ *,
726
+ agent_state_store: AgentStateStore,
727
+ adapter: ReplayAdapterV2,
728
+ replaces_replay_point_id: str | None = None,
729
+ ) -> ReplayPoint:
730
+ if replayable_episode_v2_mode(adapter) is None:
731
+ raise TypeError(
732
+ "adapter does not implement the Replayable Episode V2 protocol"
733
+ )
734
+ capture_started = time.perf_counter()
735
+ self._metrics.increment("capture_attempt_count")
736
+ replay_point_id = _required_text(self._id_factory(), "replay_point_id")
737
+ event_id = _required_text(self._event_id_factory(), "event_id")
738
+ if replaces_replay_point_id is not None:
739
+ replaces_replay_point_id = _required_text(
740
+ replaces_replay_point_id,
741
+ "replaces_replay_point_id",
742
+ )
743
+ self._metrics.increment("replacement_capture_attempt_count")
744
+ agent_state_key: object | None = None
745
+ lease: AdapterStateLease | None = None
746
+ with trace.locked_cursor(agent_cursor) as cursor:
747
+ with self._lock:
748
+ if replay_point_id in self._entries:
749
+ raise ValueError(
750
+ f"replay_point_id is duplicated: {replay_point_id!r}"
751
+ )
752
+ try:
753
+ replaced_point: ReplayPoint | None = None
754
+ if replaces_replay_point_id is not None:
755
+ with self._lock:
756
+ try:
757
+ replaced_point = self._entries[
758
+ replaces_replay_point_id
759
+ ].point
760
+ except KeyError as error:
761
+ raise KeyError(
762
+ "unknown replaces_replay_point_id: "
763
+ f"{replaces_replay_point_id}"
764
+ ) from error
765
+ if replaced_point.lifecycle != ReplayPointLifecycle.RELEASED:
766
+ raise ValueError(
767
+ "a replacement ReplayPoint requires the replaced point "
768
+ "to be released"
769
+ )
770
+ if replaced_point.cursor.trace_id != cursor.trace_id:
771
+ raise ValueError(
772
+ "a replacement ReplayPoint must remain in the same trace"
773
+ )
774
+ if replaced_point.cursor.branch_id == cursor.branch_id:
775
+ raise ValueError(
776
+ "a replacement ReplayPoint must be captured on a new branch"
777
+ )
778
+ agent_state_key = agent_state_store.capture_agent_state(cursor)
779
+ if agent_state_key is None:
780
+ raise TypeError(
781
+ "capture_agent_state must return an opaque state key"
782
+ )
783
+ lease = adapter.capture_replay_state(
784
+ CaptureReplayStateRequest(
785
+ replay_point_id=replay_point_id,
786
+ cursor=cursor,
787
+ )
788
+ )
789
+ if not isinstance(lease, AdapterStateLease):
790
+ raise TypeError(
791
+ "capture_replay_state must return an AdapterStateLease"
792
+ )
793
+ if (
794
+ replaced_point is not None
795
+ and replaced_point.captured_projection_digest is not None
796
+ and lease.projection_digest is not None
797
+ and replaced_point.captured_projection_digest
798
+ != lease.projection_digest
799
+ ):
800
+ self._metrics.increment("replacement_projection_mismatch_count")
801
+ raise ValueError(
802
+ "replacement ReplayPoint projection does not match the "
803
+ "released point"
804
+ )
805
+ event = TraceEvent(
806
+ event_id=event_id,
807
+ trace_id=trace.trace_id,
808
+ branch_id=trace.branch_id,
809
+ sequence=cursor.next_sequence,
810
+ parent_event_id=cursor.after_event_id,
811
+ kind=TraceEventKind.LIFECYCLE,
812
+ replay_point_id=replay_point_id,
813
+ public_payload={
814
+ "operation": "replay_point_captured",
815
+ "fidelity": lease.fidelity.value,
816
+ "cursor_sequence": cursor.next_sequence,
817
+ "replaces_replay_point_id": replaces_replay_point_id,
818
+ },
819
+ )
820
+ point = ReplayPoint(
821
+ replay_point_id=replay_point_id,
822
+ cursor=cursor,
823
+ fidelity=lease.fidelity,
824
+ captured_projection_digest=lease.projection_digest,
825
+ lifecycle=ReplayPointLifecycle.READY,
826
+ created_event_id=event.event_id,
827
+ replaces_replay_point_id=replaces_replay_point_id,
828
+ )
829
+ entry = _RuntimeReplayBinding(
830
+ point=point,
831
+ agent_state_key=agent_state_key,
832
+ adapter_binding_key=lease.binding_key,
833
+ agent_state_store=agent_state_store,
834
+ adapter=adapter,
835
+ adapter_released=lease.binding_key is None,
836
+ )
837
+ with self._lock:
838
+ if replay_point_id in self._entries:
839
+ raise ValueError(
840
+ f"replay_point_id is duplicated: {replay_point_id!r}"
841
+ )
842
+ trace._validate_append_unlocked(event)
843
+ trace._append_unlocked(event)
844
+ self._entries[replay_point_id] = entry
845
+ self._metrics.increment("capture_success_count")
846
+ if replaces_replay_point_id is not None:
847
+ self._metrics.increment("replacement_capture_success_count")
848
+ return point
849
+ except Exception as error:
850
+ self._metrics.increment("capture_failure_count")
851
+ if replaces_replay_point_id is not None:
852
+ self._metrics.increment("replacement_capture_failure_count")
853
+ cleanup_error: Exception | None = None
854
+ compensation_needed = bool(
855
+ (lease is not None and lease.binding_key is not None)
856
+ or agent_state_key is not None
857
+ )
858
+ if compensation_needed:
859
+ self._metrics.increment("capture_compensation_count")
860
+ if lease is not None and lease.binding_key is not None:
861
+ try:
862
+ adapter.release_replay_state(lease.binding_key)
863
+ except Exception as caught:
864
+ cleanup_error = caught
865
+ self._metrics.increment("orphaned_adapter_binding_count")
866
+ if agent_state_key is not None:
867
+ try:
868
+ agent_state_store.release_agent_state(agent_state_key)
869
+ except Exception as caught:
870
+ cleanup_error = cleanup_error or caught
871
+ self._metrics.increment("orphaned_agent_state_count")
872
+ if cleanup_error is not None:
873
+ self._metrics.increment("capture_compensation_failure_count")
874
+ raise error from cleanup_error
875
+ raise
876
+ finally:
877
+ self._metrics.add_seconds(
878
+ "capture_minutes",
879
+ time.perf_counter() - capture_started,
880
+ )
881
+
882
+ def get(self, replay_point_id: str) -> ReplayPoint:
883
+ with self._lock:
884
+ try:
885
+ return self._entries[replay_point_id].point
886
+ except KeyError as error:
887
+ raise KeyError(f"unknown replay_point_id: {replay_point_id}") from error
888
+
889
+ def records(self) -> tuple[dict[str, Any], ...]:
890
+ with self._lock:
891
+ return tuple(entry.point.to_record() for entry in self._entries.values())
892
+
893
+ def metrics(self) -> dict[str, Any]:
894
+ payload = self._metrics.snapshot()
895
+ with self._lock:
896
+ payload.update(
897
+ {
898
+ "active_replay_point_count": sum(
899
+ entry.point.lifecycle != ReplayPointLifecycle.RELEASED
900
+ for entry in self._entries.values()
901
+ ),
902
+ "active_adapter_binding_count": sum(
903
+ not entry.adapter_released for entry in self._entries.values()
904
+ ),
905
+ "failed_replay_point_count": sum(
906
+ entry.point.lifecycle == ReplayPointLifecycle.FAILED
907
+ for entry in self._entries.values()
908
+ ),
909
+ }
910
+ )
911
+ return payload
912
+
913
+ def select_before(
914
+ self,
915
+ *,
916
+ trace_id: str,
917
+ branch_id: str,
918
+ first_affected_sequence: int,
919
+ minimum_fidelity: ReplayFidelity,
920
+ ) -> ReplayPoint:
921
+ """Choose the nearest ready point before the first affected event.
922
+
923
+ A point meeting the requested fidelity wins. If none does, the
924
+ nearest best-effort point may still support an observed-only replay.
925
+ Non-replayable markers are never selected.
926
+ """
927
+
928
+ trace_id = _required_text(trace_id, "trace_id")
929
+ branch_id = _required_text(branch_id, "branch_id")
930
+ affected_sequence = int(first_affected_sequence)
931
+ minimum = ReplayFidelity(minimum_fidelity)
932
+ with self._lock:
933
+ candidates = [
934
+ entry.point
935
+ for entry in self._entries.values()
936
+ if entry.point.lifecycle == ReplayPointLifecycle.READY
937
+ and entry.point.cursor.trace_id == trace_id
938
+ and entry.point.cursor.branch_id == branch_id
939
+ and entry.point.cursor.next_sequence <= affected_sequence
940
+ and entry.point.fidelity != ReplayFidelity.NON_REPLAYABLE
941
+ ]
942
+ if not candidates:
943
+ raise LookupError(
944
+ "no replayable ReplayPoint exists before the affected event"
945
+ )
946
+ trusted = [
947
+ point for point in candidates if fidelity_meets(point.fidelity, minimum)
948
+ ]
949
+ pool = trusted or candidates
950
+ selected = max(pool, key=lambda point: point.cursor.next_sequence)
951
+ self._metrics.increment("replay_point_selection_count")
952
+ if not trusted:
953
+ self._metrics.increment("replay_point_fidelity_fallback_count")
954
+ elif any(
955
+ point.cursor.next_sequence > selected.cursor.next_sequence
956
+ and not fidelity_meets(point.fidelity, minimum)
957
+ for point in candidates
958
+ ):
959
+ self._metrics.increment("replay_point_fidelity_filtered_selection_count")
960
+ return selected
961
+
962
+ def run_from(
963
+ self,
964
+ replay_point_id: str,
965
+ *,
966
+ target_branch_id: str,
967
+ minimum_fidelity: ReplayFidelity,
968
+ execute: Callable[[ReplayRestoreResult], Any],
969
+ ) -> tuple[ReplayRestoreResult, Any]:
970
+ """Restore and execute one suffix while holding the point's serial lock."""
971
+
972
+ target_branch_id = _required_text(target_branch_id, "target_branch_id")
973
+ minimum = ReplayFidelity(minimum_fidelity)
974
+ replay_started = time.perf_counter()
975
+ self._metrics.increment("replay_attempt_count")
976
+ try:
977
+ with self._lock:
978
+ try:
979
+ entry = self._entries[replay_point_id]
980
+ except KeyError as error:
981
+ raise KeyError(
982
+ f"unknown replay_point_id: {replay_point_id}"
983
+ ) from error
984
+ if entry.point.lifecycle != ReplayPointLifecycle.READY:
985
+ raise RuntimeError(
986
+ "ReplayPoint must be ready before restore: "
987
+ f"{entry.point.lifecycle.value}"
988
+ )
989
+ if entry.point.fidelity == ReplayFidelity.NON_REPLAYABLE:
990
+ raise RuntimeError("non_replayable ReplayPoints cannot be restored")
991
+ if entry.adapter_binding_key is None:
992
+ raise RuntimeError("replayable ReplayPoint has no Adapter binding")
993
+
994
+ with entry.replay_lock:
995
+ with self._lock:
996
+ if entry.point.lifecycle != ReplayPointLifecycle.READY:
997
+ raise RuntimeError(
998
+ "ReplayPoint stopped being ready before restore: "
999
+ f"{entry.point.lifecycle.value}"
1000
+ )
1001
+ request = RestoreReplayStateRequest(
1002
+ replay_point_id=replay_point_id,
1003
+ target_branch_id=target_branch_id,
1004
+ minimum_fidelity=minimum,
1005
+ )
1006
+ restore_started = time.perf_counter()
1007
+ self._metrics.increment("restore_attempt_count")
1008
+ try:
1009
+ receipt = entry.adapter.restore_replay_state(
1010
+ entry.adapter_binding_key,
1011
+ request,
1012
+ )
1013
+ if not isinstance(receipt, RestoreReceipt):
1014
+ raise TypeError(
1015
+ "restore_replay_state must return a RestoreReceipt"
1016
+ )
1017
+ if receipt.achieved_fidelity == ReplayFidelity.NON_REPLAYABLE:
1018
+ raise RuntimeError(
1019
+ "Adapter reported non_replayable while restoring a ReplayPoint"
1020
+ )
1021
+ if not fidelity_meets(
1022
+ entry.point.fidelity,
1023
+ receipt.achieved_fidelity,
1024
+ ):
1025
+ raise ValueError(
1026
+ "restore achieved a fidelity stronger than the captured "
1027
+ "ReplayPoint"
1028
+ )
1029
+ except Exception:
1030
+ self._metrics.increment("restore_failure_count")
1031
+ raise
1032
+ else:
1033
+ self._metrics.increment("restore_success_count")
1034
+ if receipt.prefix_reused is True:
1035
+ self._metrics.increment("prefix_reuse_verified_count")
1036
+ elif receipt.prefix_reused is False:
1037
+ self._metrics.increment("prefix_reconstruction_count")
1038
+ else:
1039
+ self._metrics.increment("prefix_reuse_unknown_count")
1040
+ finally:
1041
+ restore_minutes = (time.perf_counter() - restore_started) / 60.0
1042
+ self._metrics.add_seconds(
1043
+ "restore_minutes",
1044
+ time.perf_counter() - restore_started,
1045
+ )
1046
+
1047
+ equivalence_started = time.perf_counter()
1048
+ self._metrics.increment("equivalence_attempt_count")
1049
+ try:
1050
+ report = entry.adapter.validate_replay_equivalence(
1051
+ entry.adapter_binding_key,
1052
+ receipt,
1053
+ )
1054
+ if not isinstance(report, EquivalenceReport):
1055
+ raise TypeError(
1056
+ "validate_replay_equivalence must return an "
1057
+ "EquivalenceReport"
1058
+ )
1059
+ promotion_eligible = _validate_equivalence_contract(
1060
+ entry.point,
1061
+ receipt,
1062
+ report,
1063
+ minimum,
1064
+ )
1065
+ trusted_fidelity = fidelity_meets(
1066
+ report.achieved_fidelity,
1067
+ ReplayFidelity.SEMANTIC_EQUIVALENT,
1068
+ )
1069
+ except Exception:
1070
+ self._metrics.increment("equivalence_failure_count")
1071
+ raise
1072
+ else:
1073
+ if report.equivalent and trusted_fidelity:
1074
+ self._metrics.increment("equivalence_verified_count")
1075
+ else:
1076
+ self._metrics.increment("equivalence_unverified_count")
1077
+ finally:
1078
+ equivalence_minutes = (
1079
+ time.perf_counter() - equivalence_started
1080
+ ) / 60.0
1081
+ self._metrics.add_seconds(
1082
+ "equivalence_minutes",
1083
+ time.perf_counter() - equivalence_started,
1084
+ )
1085
+ self._metrics.observe_fidelity(report.achieved_fidelity.value)
1086
+ entry.agent_state_store.restore_agent_state(entry.agent_state_key)
1087
+ restore = ReplayRestoreResult(
1088
+ replay_point_id=replay_point_id,
1089
+ target_branch_id=target_branch_id,
1090
+ achieved_fidelity=report.achieved_fidelity,
1091
+ equivalent=report.equivalent,
1092
+ promotion_eligible=promotion_eligible,
1093
+ prefix_reused=receipt.prefix_reused,
1094
+ report=report,
1095
+ restore_minutes=restore_minutes,
1096
+ equivalence_minutes=equivalence_minutes,
1097
+ suffix_minutes=0.0,
1098
+ replay_wall_minutes=0.0,
1099
+ )
1100
+ suffix_started = time.perf_counter()
1101
+ self._metrics.increment("suffix_attempt_count")
1102
+ suffix_error: Exception | None = None
1103
+ finalization_error: Exception | None = None
1104
+ try:
1105
+ outcome = execute(restore)
1106
+ except Exception as error:
1107
+ suffix_error = error
1108
+ finally:
1109
+ finalizer = getattr(
1110
+ entry.adapter,
1111
+ "finalize_replay_suffix",
1112
+ None,
1113
+ )
1114
+ if callable(finalizer):
1115
+ self._metrics.increment("suffix_finalization_attempt_count")
1116
+ try:
1117
+ finalizer(
1118
+ entry.adapter_binding_key,
1119
+ request,
1120
+ suffix_succeeded=suffix_error is None,
1121
+ )
1122
+ except Exception as error:
1123
+ finalization_error = error
1124
+ self._metrics.increment("suffix_finalization_failure_count")
1125
+ else:
1126
+ self._metrics.increment("suffix_finalization_success_count")
1127
+ suffix_minutes = (time.perf_counter() - suffix_started) / 60.0
1128
+ self._metrics.add_seconds(
1129
+ "suffix_minutes",
1130
+ time.perf_counter() - suffix_started,
1131
+ )
1132
+ if suffix_error is not None:
1133
+ self._metrics.increment("suffix_failure_count")
1134
+ if finalization_error is not None:
1135
+ with self._lock:
1136
+ entry.point = replace(
1137
+ entry.point,
1138
+ lifecycle=ReplayPointLifecycle.FAILED,
1139
+ )
1140
+ self._metrics.increment("replay_point_quarantine_count")
1141
+ raise suffix_error from finalization_error
1142
+ raise suffix_error
1143
+ if finalization_error is not None:
1144
+ self._metrics.increment("suffix_failure_count")
1145
+ with self._lock:
1146
+ entry.point = replace(
1147
+ entry.point,
1148
+ lifecycle=ReplayPointLifecycle.FAILED,
1149
+ )
1150
+ self._metrics.increment("replay_point_quarantine_count")
1151
+ raise finalization_error
1152
+ self._metrics.increment("suffix_success_count")
1153
+ replay_wall_minutes = (time.perf_counter() - replay_started) / 60.0
1154
+ restore = replace(
1155
+ restore,
1156
+ suffix_minutes=suffix_minutes,
1157
+ replay_wall_minutes=replay_wall_minutes,
1158
+ )
1159
+ self._metrics.increment("replay_success_count")
1160
+ return restore, outcome
1161
+ except Exception:
1162
+ self._metrics.increment("replay_failure_count")
1163
+ raise
1164
+ finally:
1165
+ self._metrics.add_seconds(
1166
+ "replay_wall_minutes",
1167
+ time.perf_counter() - replay_started,
1168
+ )
1169
+
1170
+ def run_child_group_from(
1171
+ self,
1172
+ replay_point_id: str,
1173
+ tasks: Sequence[ReplayChildTask],
1174
+ *,
1175
+ parallel: bool = False,
1176
+ max_workers: int | None = None,
1177
+ ) -> tuple[ReplayChildExecutionResult, ...]:
1178
+ from .child_replay_runtime import run_child_group_from
1179
+
1180
+ return run_child_group_from(
1181
+ self,
1182
+ replay_point_id,
1183
+ tasks,
1184
+ parallel=parallel,
1185
+ max_workers=max_workers,
1186
+ )
1187
+
1188
+ def run_child_group_settled_from(
1189
+ self,
1190
+ replay_point_id: str,
1191
+ tasks: Sequence[ReplayChildTask],
1192
+ *,
1193
+ parallel: bool = False,
1194
+ max_workers: int | None = None,
1195
+ ) -> tuple[ReplayChildExecutionResult | ReplayChildExecutionFailure, ...]:
1196
+ """Preserve safe sibling results when isolated candidates fail safely."""
1197
+
1198
+ from .child_replay_runtime import run_child_group_from
1199
+
1200
+ return run_child_group_from(
1201
+ self,
1202
+ replay_point_id,
1203
+ tasks,
1204
+ parallel=parallel,
1205
+ max_workers=max_workers,
1206
+ settle_failures=True,
1207
+ )
1208
+
1209
+ def release(self, replay_point_id: str) -> ReplayPoint:
1210
+ release_started = time.perf_counter()
1211
+ self._metrics.increment("release_attempt_count")
1212
+ try:
1213
+ with self._lock:
1214
+ try:
1215
+ entry = self._entries[replay_point_id]
1216
+ except KeyError as error:
1217
+ raise KeyError(
1218
+ f"unknown replay_point_id: {replay_point_id}"
1219
+ ) from error
1220
+ if entry.point.lifecycle == ReplayPointLifecycle.RELEASED:
1221
+ self._metrics.increment("release_success_count")
1222
+ return entry.point
1223
+ entry.point = replace(
1224
+ entry.point,
1225
+ lifecycle=ReplayPointLifecycle.RELEASING,
1226
+ )
1227
+
1228
+ adapter_error: Exception | None = None
1229
+ agent_error: Exception | None = None
1230
+ with entry.replay_lock:
1231
+ if not entry.adapter_released and entry.adapter_binding_key is not None:
1232
+ try:
1233
+ entry.adapter.release_replay_state(entry.adapter_binding_key)
1234
+ entry.adapter_released = True
1235
+ except Exception as error:
1236
+ adapter_error = error
1237
+ if not entry.agent_released:
1238
+ try:
1239
+ entry.agent_state_store.release_agent_state(
1240
+ entry.agent_state_key
1241
+ )
1242
+ entry.agent_released = True
1243
+ except Exception as error:
1244
+ agent_error = error
1245
+
1246
+ with self._lock:
1247
+ entry.point = replace(
1248
+ entry.point,
1249
+ lifecycle=(
1250
+ ReplayPointLifecycle.RELEASED
1251
+ if entry.adapter_released and entry.agent_released
1252
+ else ReplayPointLifecycle.FAILED
1253
+ ),
1254
+ )
1255
+ point = entry.point
1256
+ if adapter_error is not None:
1257
+ if agent_error is not None:
1258
+ raise adapter_error from agent_error
1259
+ raise adapter_error
1260
+ if agent_error is not None:
1261
+ raise agent_error
1262
+ self._metrics.increment("release_success_count")
1263
+ return point
1264
+ except Exception:
1265
+ self._metrics.increment("release_failure_count")
1266
+ raise
1267
+ finally:
1268
+ self._metrics.add_seconds(
1269
+ "release_minutes",
1270
+ time.perf_counter() - release_started,
1271
+ )