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,444 @@
1
+ """Per-goal handoff mode: which ownership authority governs todo handoffs.
2
+
3
+ The goal's ACTIVE_GOAL_STATE.md YAML front-matter may declare ``handoff_mode``:
4
+
5
+ * absent / ``legacy`` (default): today's dual soft-claim + hard-lease
6
+ behavior, byte-for-byte. The known soft-claim/hard-lease split brain stays
7
+ open in this mode by design; it is surfaced additively, never silently
8
+ repaired.
9
+ * ``soft_claim``: the markdown claim is the only ownership record. Task-lease
10
+ acquire/renew/transfer are typed-rejected; release and inspect stay allowed
11
+ for cleanup and observability of legacy leftovers.
12
+ * ``hard_lease``: ownership changes on an existing todo require the acting
13
+ agent to hold that todo's time-active task lease, and the completion fence
14
+ becomes mandatory for both user-role and agent-role todos. An exact linked
15
+ user-gate decision-scope override authorizes only the exact linked decision
16
+ transition; it does not bypass the completion fence. The delegated
17
+ ``coordination.todo_lifecycle_authority`` override is the one audited door
18
+ through the gate.
19
+
20
+ The mode lives in the front-matter (not the registry) because the markdown
21
+ file is the artifact that travels across endpoints; lease JSON and registry
22
+ are host-local. Pre-NoKV the file syncs last-writer-wins, so two hosts can
23
+ briefly disagree about the mode; that window is documented, not engineered
24
+ around here.
25
+
26
+ The v0 transition scan is materialized-state only: it reads open claims from
27
+ the locked ``ACTIVE_GOAL_STATE.md`` text plus time-active local lease files. It
28
+ does not merge the event projection, so a claim that exists only in the event
29
+ log can be missed. A successful switch is therefore not a proof that every
30
+ projection is quiescent. The selected mode's typed per-write gate remains the
31
+ safety boundary for later governed ownership and completion mutations,
32
+ including event-projected completion.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ from contextlib import ExitStack
38
+ from pathlib import Path
39
+ from typing import Any
40
+
41
+ from ..goals.active_state_metadata import parse_state_frontmatter
42
+ from .contract import normalize_todo_claimed_by
43
+
44
+ HANDOFF_MODE_SCHEMA_VERSION = "goal_handoff_mode_v0"
45
+ HANDOFF_MODE_FRONTMATTER_KEY = "handoff_mode"
46
+ HANDOFF_MODE_LEGACY = "legacy"
47
+ HANDOFF_MODE_SOFT_CLAIM = "soft_claim"
48
+ HANDOFF_MODE_HARD_LEASE = "hard_lease"
49
+ HANDOFF_MODE_VALUES = (
50
+ HANDOFF_MODE_LEGACY,
51
+ HANDOFF_MODE_SOFT_CLAIM,
52
+ HANDOFF_MODE_HARD_LEASE,
53
+ )
54
+ DELEGATED_AUTHORITY_MODE = "delegated_orchestration_override"
55
+
56
+
57
+ class HandoffModeError(ValueError):
58
+ """Typed handoff-mode failure; mirrors TaskLeaseError's code/payload shape."""
59
+
60
+ def __init__(
61
+ self, message: str, *, code: str, payload: dict[str, Any] | None = None
62
+ ) -> None:
63
+ super().__init__(message)
64
+ self.code = code
65
+ self.payload = payload or {}
66
+
67
+
68
+ def normalize_handoff_mode(value: Any) -> str:
69
+ candidate = str(value or "").strip()
70
+ if not candidate:
71
+ return HANDOFF_MODE_LEGACY
72
+ if candidate not in HANDOFF_MODE_VALUES:
73
+ raise HandoffModeError(
74
+ f"unsupported handoff_mode {candidate!r}; expected one of: "
75
+ + ", ".join(HANDOFF_MODE_VALUES),
76
+ code="invalid_handoff_mode",
77
+ payload={"handoff_mode": candidate, "supported": list(HANDOFF_MODE_VALUES)},
78
+ )
79
+ return candidate
80
+
81
+
82
+ def goal_handoff_mode(state_text: str) -> str:
83
+ """Read the goal handoff mode from active-state text; absent means legacy."""
84
+
85
+ front_matter = parse_state_frontmatter(state_text)
86
+ return normalize_handoff_mode(front_matter.get(HANDOFF_MODE_FRONTMATTER_KEY))
87
+
88
+
89
+ def _resolve_state(
90
+ *,
91
+ registry_path: Path,
92
+ goal_id: str,
93
+ project: Path | None = None,
94
+ state_file: Path | None = None,
95
+ ) -> tuple[Path | None, Path]:
96
+ from ...todos import resolve_todo_state_path
97
+
98
+ return resolve_todo_state_path(
99
+ registry_path=registry_path,
100
+ goal_id=goal_id,
101
+ project=project,
102
+ state_file=state_file,
103
+ )
104
+
105
+
106
+ def goal_handoff_mode_for_goal(
107
+ *,
108
+ registry_path: Path,
109
+ goal_id: str,
110
+ project: Path | None = None,
111
+ state_file: Path | None = None,
112
+ ) -> str:
113
+ _project, resolved_state_file = _resolve_state(
114
+ registry_path=registry_path,
115
+ goal_id=goal_id,
116
+ project=project,
117
+ state_file=state_file,
118
+ )
119
+ return goal_handoff_mode(resolved_state_file.read_text(encoding="utf-8"))
120
+
121
+
122
+ def enter_todo_ownership_handoff_gate(
123
+ stack: ExitStack,
124
+ *,
125
+ state_text: str,
126
+ registry_path: Path,
127
+ goal_id: str,
128
+ todo_id: str,
129
+ mutation_authority: dict[str, Any],
130
+ actor_agent_id: str | None,
131
+ ownership_mutation: bool,
132
+ ) -> dict[str, Any]:
133
+ """Gate one claimed_by mutation on an existing todo behind the goal mode.
134
+
135
+ Returns additive payload fields (always ``handoff_mode``; plus the door
136
+ marker or the held holder-gate receipt in hard_lease mode). In hard_lease
137
+ mode the per-goal lease lock is entered on ``stack`` so it stays held
138
+ through the markdown commit, matching the completion fence's
139
+ state-lock-then-lease-lock order.
140
+ """
141
+
142
+ mode = goal_handoff_mode(state_text)
143
+ extras: dict[str, Any] = {"handoff_mode": mode}
144
+ if not ownership_mutation or mode != HANDOFF_MODE_HARD_LEASE:
145
+ return extras
146
+ if mutation_authority.get("mode") == DELEGATED_AUTHORITY_MODE:
147
+ extras["handoff_gate_overridden"] = True
148
+ return extras
149
+ from ..work_items.task_lease import hold_handoff_lease_holder_gate
150
+
151
+ extras["task_lease_holder_gate"] = stack.enter_context(
152
+ hold_handoff_lease_holder_gate(
153
+ registry_path=registry_path,
154
+ goal_id=goal_id,
155
+ todo_id=todo_id,
156
+ actor_agent_id=actor_agent_id,
157
+ )
158
+ )
159
+ return extras
160
+
161
+
162
+ def enter_added_todo_ownership_handoff_gate(
163
+ stack: ExitStack,
164
+ *,
165
+ lines: list[str],
166
+ state_text: str,
167
+ registry_path: Path,
168
+ goal_id: str,
169
+ role: str,
170
+ text: str,
171
+ claimed_by: str | None,
172
+ actor_agent_id: str | None,
173
+ ) -> dict[str, Any]:
174
+ """Gate the ``todo add`` path when it would reassign an existing todo.
175
+
176
+ ``todo add`` matches an open todo by text and upserts its metadata, so a
177
+ ``claimed_by`` argument on that branch changes the claim of a todo that
178
+ may already hold a lease. Creating a todo stays ungated in every mode: a
179
+ todo id that does not exist yet cannot hold one. Re-adding a todo with the
180
+ claim it already carries is not an ownership change and is not gated.
181
+
182
+ The delegated-authority door is not offered here. Reassignment under a
183
+ ``coordination.todo_lifecycle_authority`` grant goes through
184
+ ``todo update --claimed-by``, which is the verb that owns that action.
185
+ """
186
+
187
+ from ...todos import matching_todo_block # loopx.todos, deferred: import cycle
188
+ from .active_state_editing import section_bounds
189
+
190
+ bounds = section_bounds(lines, role)
191
+ existing = (
192
+ matching_todo_block(
193
+ lines, bounds[0], bounds[1], text, role=role, source_section=bounds[2]
194
+ )
195
+ if bounds
196
+ else None
197
+ )
198
+ requested = normalize_todo_claimed_by(claimed_by) if claimed_by else None
199
+ current = normalize_todo_claimed_by(existing.get("claimed_by")) if existing else None
200
+ return enter_todo_ownership_handoff_gate(
201
+ stack,
202
+ state_text=state_text,
203
+ registry_path=registry_path,
204
+ goal_id=goal_id,
205
+ todo_id=str(existing.get("todo_id") or "") if existing else "",
206
+ mutation_authority={},
207
+ actor_agent_id=actor_agent_id,
208
+ ownership_mutation=(
209
+ role == "agent"
210
+ and existing is not None
211
+ and requested is not None
212
+ and requested != current
213
+ ),
214
+ )
215
+
216
+
217
+ def resolve_todo_completion_handoff(
218
+ *,
219
+ state_text: str,
220
+ mutation_authority: dict[str, Any],
221
+ ) -> dict[str, Any]:
222
+ """Resolve mode plus the delegated-authority door for one todo completion."""
223
+
224
+ mode = goal_handoff_mode(state_text)
225
+ extras: dict[str, Any] = {"handoff_mode": mode}
226
+ if (
227
+ mode == HANDOFF_MODE_HARD_LEASE
228
+ and mutation_authority.get("mode") == DELEGATED_AUTHORITY_MODE
229
+ ):
230
+ extras["handoff_gate_overridden"] = True
231
+ return extras
232
+
233
+
234
+ def show_goal_handoff_mode(
235
+ *,
236
+ registry_path: Path,
237
+ goal_id: str,
238
+ project: Path | None = None,
239
+ state_file: Path | None = None,
240
+ ) -> dict[str, Any]:
241
+ _project, resolved_state_file = _resolve_state(
242
+ registry_path=registry_path,
243
+ goal_id=goal_id,
244
+ project=project,
245
+ state_file=state_file,
246
+ )
247
+ front_matter = parse_state_frontmatter(
248
+ resolved_state_file.read_text(encoding="utf-8")
249
+ )
250
+ raw = front_matter.get(HANDOFF_MODE_FRONTMATTER_KEY)
251
+ return {
252
+ "ok": True,
253
+ "schema_version": HANDOFF_MODE_SCHEMA_VERSION,
254
+ "action": "show",
255
+ "goal_id": goal_id,
256
+ "handoff_mode": normalize_handoff_mode(raw),
257
+ "source": "frontmatter" if str(raw or "").strip() else "default",
258
+ "state_file": str(resolved_state_file),
259
+ }
260
+
261
+
262
+ def _frontmatter_bounds(lines: list[str]) -> tuple[int, int]:
263
+ if not lines or lines[0].strip() != "---":
264
+ raise HandoffModeError(
265
+ "active state file has no YAML front-matter; add one before setting "
266
+ "handoff_mode",
267
+ code="state_frontmatter_missing",
268
+ )
269
+ for index in range(1, len(lines)):
270
+ if lines[index].strip() == "---":
271
+ return 1, index
272
+ raise HandoffModeError(
273
+ "active state front-matter is not terminated by ---",
274
+ code="state_frontmatter_missing",
275
+ )
276
+
277
+
278
+ def _quiescence_offenders(
279
+ *,
280
+ registry_path: Path,
281
+ goal_id: str,
282
+ state_text: str,
283
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
284
+ """Return blockers visible to the v0 materialized-state scan.
285
+
286
+ Event-projection overlays are intentionally outside this pre-NoKV scan.
287
+ Callers must not interpret an empty result as an authority-wide safety
288
+ guarantee; later governed writes still cross the selected handoff-mode
289
+ gate.
290
+ """
291
+
292
+ from ..work_items.task_lease import (
293
+ lease_is_active,
294
+ read_lease,
295
+ runtime_root_from_registry,
296
+ task_lease_dir,
297
+ )
298
+ from .active_state_todo_parser import parse_active_state_todos
299
+
300
+ claimed: list[dict[str, Any]] = []
301
+ todos = parse_active_state_todos(state_text, item_limit=None)
302
+ for role in ("user_todos", "agent_todos"):
303
+ summary = todos.get(role)
304
+ items = summary.get("items") if isinstance(summary, dict) else []
305
+ for item in items or []:
306
+ if not isinstance(item, dict) or item.get("done") is True:
307
+ continue
308
+ owner = normalize_todo_claimed_by(item.get("claimed_by"))
309
+ if owner:
310
+ claimed.append(
311
+ {
312
+ "todo_id": item.get("todo_id"),
313
+ "claimed_by": owner,
314
+ "status": item.get("status"),
315
+ }
316
+ )
317
+ leases: list[dict[str, Any]] = []
318
+ runtime_root = runtime_root_from_registry(registry_path, None)
319
+ lease_dir = task_lease_dir(runtime_root=runtime_root, goal_id=goal_id)
320
+ if lease_dir.exists():
321
+ for path in sorted(lease_dir.glob("todo_*.json")):
322
+ lease = read_lease(path)
323
+ if lease_is_active(lease):
324
+ leases.append(
325
+ {
326
+ "todo_id": lease.get("todo_id"),
327
+ "owner": lease.get("owner"),
328
+ "expires_at": lease.get("expires_at"),
329
+ "lease_path": str(path),
330
+ }
331
+ )
332
+ return claimed, leases
333
+
334
+
335
+ def set_goal_handoff_mode(
336
+ *,
337
+ registry_path: Path,
338
+ goal_id: str,
339
+ mode: str,
340
+ project: Path | None = None,
341
+ state_file: Path | None = None,
342
+ ) -> dict[str, Any]:
343
+ """Set the goal handoff mode; requires a quiescent goal for transitions.
344
+
345
+ In v0, quiescence means no open todo materialized in the locked active-state
346
+ Markdown carries a claimed_by owner and no time-active lease file exists
347
+ under the goal. The scan does not overlay event-only todos, so success is a
348
+ pre-NoKV migration check rather than an authority-wide safety guarantee;
349
+ every later governed ownership or completion write still crosses the
350
+ selected mode's typed gate. Visible non-quiescence refuses with a typed
351
+ offender list and there is no force override. Hand-editing the front-matter
352
+ bypasses this check and is out of contract.
353
+ """
354
+
355
+ from ...file_lock import exclusive_file_lock
356
+ from ..work_items.task_lease import (
357
+ runtime_root_from_registry,
358
+ task_lease_lock_path,
359
+ )
360
+
361
+ requested = normalize_handoff_mode(mode)
362
+ if not str(mode or "").strip():
363
+ raise HandoffModeError(
364
+ "handoff-mode set requires an explicit --mode value",
365
+ code="invalid_handoff_mode",
366
+ )
367
+ _project, resolved_state_file = _resolve_state(
368
+ registry_path=registry_path,
369
+ goal_id=goal_id,
370
+ project=project,
371
+ state_file=state_file,
372
+ )
373
+ with exclusive_file_lock(resolved_state_file, operation="handoff_mode_set"):
374
+ original = resolved_state_file.read_text(encoding="utf-8")
375
+ previous_raw = parse_state_frontmatter(original).get(
376
+ HANDOFF_MODE_FRONTMATTER_KEY
377
+ )
378
+ try:
379
+ previous = normalize_handoff_mode(previous_raw)
380
+ except HandoffModeError as exc:
381
+ previous = str(previous_raw or "").strip()
382
+ previous_mode_fields = {
383
+ "previous_mode": previous,
384
+ "previous_mode_valid": False,
385
+ "previous_mode_error_code": exc.code,
386
+ }
387
+ else:
388
+ previous_mode_fields = {
389
+ "previous_mode": previous,
390
+ "previous_mode_valid": True,
391
+ }
392
+ payload = {
393
+ "ok": True,
394
+ "schema_version": HANDOFF_MODE_SCHEMA_VERSION,
395
+ "action": "set",
396
+ "goal_id": goal_id,
397
+ **previous_mode_fields,
398
+ "handoff_mode": requested,
399
+ "state_file": str(resolved_state_file),
400
+ }
401
+ if previous == requested:
402
+ payload["changed"] = False
403
+ return payload
404
+ lease_lock = task_lease_lock_path(
405
+ runtime_root=runtime_root_from_registry(registry_path, None),
406
+ goal_id=goal_id,
407
+ )
408
+ with exclusive_file_lock(lease_lock, operation="handoff_mode_set"):
409
+ claimed, leases = _quiescence_offenders(
410
+ registry_path=registry_path,
411
+ goal_id=goal_id,
412
+ state_text=original,
413
+ )
414
+ if claimed or leases:
415
+ raise HandoffModeError(
416
+ "handoff_mode can only change while the goal is quiescent: "
417
+ f"{len(claimed)} claimed open todo(s), "
418
+ f"{len(leases)} time-active lease(s)",
419
+ code="handoff_mode_not_quiescent",
420
+ payload={
421
+ "goal_id": goal_id,
422
+ "requested_mode": requested,
423
+ **previous_mode_fields,
424
+ "claimed_todos": claimed,
425
+ "active_leases": leases,
426
+ },
427
+ )
428
+ lines = original.splitlines()
429
+ open_index, close_index = _frontmatter_bounds(lines)
430
+ for index in range(open_index, close_index):
431
+ if (
432
+ lines[index].split(":", 1)[0].strip()
433
+ == HANDOFF_MODE_FRONTMATTER_KEY
434
+ ):
435
+ lines[index] = f"{HANDOFF_MODE_FRONTMATTER_KEY}: {requested}"
436
+ break
437
+ else:
438
+ lines.insert(
439
+ close_index, f"{HANDOFF_MODE_FRONTMATTER_KEY}: {requested}"
440
+ )
441
+ new_text = "\n".join(lines) + ("\n" if original.endswith("\n") else "")
442
+ resolved_state_file.write_text(new_text, encoding="utf-8")
443
+ payload["changed"] = True
444
+ return payload
@@ -0,0 +1,202 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import re
5
+ from typing import Any
6
+
7
+ from .contract import (
8
+ normalize_todo_action_kind,
9
+ normalize_todo_blocks_agent,
10
+ normalize_todo_claimed_by,
11
+ normalize_todo_decision_scope,
12
+ normalize_todo_excluded_agents,
13
+ normalize_todo_id,
14
+ normalize_todo_id_list,
15
+ normalize_todo_required_decision_scopes,
16
+ normalize_todo_resume_when,
17
+ )
18
+
19
+ TODO_HANDOFF_NOTE_SCHEMA_VERSION = "handoff_note_v0"
20
+ TODO_CONTINUATION_HINT_MAX_CHARS = 280
21
+ _INLINE_CREDENTIAL_PATTERN = re.compile(
22
+ r"(?i)(?:\bbearer\s+\S+|\bauthorization\s*:|"
23
+ r"\b(?:ak|sk|api[_-]?key|access[_-]?key(?:[_-]?id)?|secret(?:[_-]?key)?|"
24
+ r"token|password)\b\s*[:=]\s*\S+)"
25
+ )
26
+
27
+
28
+ def _compact_text(value: Any, *, limit: int = 220) -> str | None:
29
+ text = " ".join(str(value or "").strip().split())
30
+ if not text or _INLINE_CREDENTIAL_PATTERN.search(text):
31
+ return None
32
+ if len(text) <= limit:
33
+ return text
34
+ return text[: limit - 1].rstrip() + "..."
35
+
36
+
37
+ def _raw_handoff(item: dict[str, Any]) -> dict[str, Any]:
38
+ handoff = item.get("handoff") or item.get("handoff_note")
39
+ return handoff if isinstance(handoff, dict) else {}
40
+
41
+
42
+ def _handoff_id(item: dict[str, Any]) -> str:
43
+ todo_id = normalize_todo_id(item.get("todo_id"))
44
+ if todo_id:
45
+ return f"handoff_{todo_id.removeprefix('todo_')}"
46
+ digest = hashlib.sha1(
47
+ str(
48
+ (
49
+ item.get("goal_id"),
50
+ item.get("role"),
51
+ item.get("index"),
52
+ item.get("text") or item.get("title"),
53
+ )
54
+ ).encode("utf-8")
55
+ ).hexdigest()[:12]
56
+ return f"handoff_{digest}"
57
+
58
+
59
+ def _first_text(item: dict[str, Any], *keys: str, limit: int = 220) -> str | None:
60
+ handoff = _raw_handoff(item)
61
+ for key in keys:
62
+ value = handoff.get(key) if key in handoff else item.get(key)
63
+ text = _compact_text(value, limit=limit)
64
+ if text:
65
+ return text
66
+ return None
67
+
68
+
69
+ def compact_todo_continuation_hint(item: dict[str, Any]) -> str | None:
70
+ """Project the current bounded continuation without copying raw evidence."""
71
+
72
+ return _first_text(
73
+ item,
74
+ "continuation_hint",
75
+ "suggested_next_action",
76
+ "note",
77
+ "reason",
78
+ limit=TODO_CONTINUATION_HINT_MAX_CHARS,
79
+ )
80
+
81
+
82
+ def _evidence_refs(item: dict[str, Any], *, todo_id: str | None) -> list[str]:
83
+ refs: list[str] = []
84
+ handoff = _raw_handoff(item)
85
+ raw_refs = handoff.get("evidence_refs") or item.get("evidence_refs")
86
+ if isinstance(raw_refs, (list, tuple, set)):
87
+ for raw in raw_refs:
88
+ ref = _compact_text(raw, limit=180)
89
+ if ref and ref not in refs:
90
+ refs.append(ref)
91
+
92
+ if item.get("evidence") and todo_id:
93
+ refs.append(f"todo:{todo_id}:evidence")
94
+ if item.get("note") and todo_id:
95
+ refs.append(f"todo:{todo_id}:note")
96
+ latest_event_kind = _compact_text(item.get("latest_event_kind"), limit=80)
97
+ if latest_event_kind and todo_id:
98
+ refs.append(f"rollout_event:{latest_event_kind}:{todo_id}")
99
+ return refs[:6]
100
+
101
+
102
+ def _unresolved_decisions(item: dict[str, Any]) -> list[dict[str, str]]:
103
+ decisions = list(
104
+ normalize_todo_required_decision_scopes(item.get("required_decision_scopes"))
105
+ )
106
+ single = normalize_todo_decision_scope(item.get("decision_scope"))
107
+ if single:
108
+ identity = (single.get("kind"), single.get("granularity"), single.get("scope_key"))
109
+ if not any(
110
+ (item.get("kind"), item.get("granularity"), item.get("scope_key")) == identity
111
+ for item in decisions
112
+ ):
113
+ decisions.append(single)
114
+ return decisions
115
+
116
+
117
+ def build_todo_handoff_note(
118
+ item: dict[str, Any],
119
+ *,
120
+ goal_id: str | None = None,
121
+ source: str | None = None,
122
+ ) -> dict[str, Any] | None:
123
+ """Project an existing todo/history/evidence row into a typed handoff note.
124
+
125
+ The note is a read model: it does not create a dispatcher queue, comment
126
+ stream, approval state, or runtime task separate from the source todo.
127
+ """
128
+
129
+ if not isinstance(item, dict):
130
+ return None
131
+ handoff = _raw_handoff(item)
132
+ todo_id = normalize_todo_id(item.get("todo_id"))
133
+ claimed_by = normalize_todo_claimed_by(item.get("claimed_by"))
134
+ excluded_agents = normalize_todo_excluded_agents(item.get("excluded_agents"))
135
+ from_agent = normalize_todo_claimed_by(
136
+ handoff.get("from_agent") or item.get("agent_id")
137
+ )
138
+ explicit_to_agent = normalize_todo_blocks_agent(handoff.get("to_agent"))
139
+ successor_todo_ids = normalize_todo_id_list(item.get("successor_todo_ids"))
140
+ unblocks_todo_id = normalize_todo_id(item.get("unblocks_todo_id"))
141
+ superseded_by = normalize_todo_id(item.get("superseded_by"))
142
+ has_handoff_signal = bool(
143
+ handoff
144
+ or excluded_agents
145
+ or successor_todo_ids
146
+ or unblocks_todo_id
147
+ or superseded_by
148
+ )
149
+ if not has_handoff_signal:
150
+ return None
151
+ to_agent = explicit_to_agent or claimed_by
152
+
153
+ intent = (
154
+ _first_text(item, "intent", limit=80)
155
+ or normalize_todo_action_kind(item.get("action_kind"))
156
+ or _compact_text(item.get("task_class"), limit=80)
157
+ or "continue"
158
+ )
159
+ blocked_on = (
160
+ _first_text(item, "blocked_on", limit=180)
161
+ or normalize_todo_resume_when(item.get("resume_when"))
162
+ or (f"todo:{unblocks_todo_id}" if unblocks_todo_id else None)
163
+ or (f"todo:{superseded_by}" if superseded_by else None)
164
+ )
165
+ payload: dict[str, Any] = {
166
+ "schema_version": TODO_HANDOFF_NOTE_SCHEMA_VERSION,
167
+ "handoff_id": _handoff_id(item),
168
+ "todo_id": todo_id,
169
+ "goal_id": _compact_text(goal_id or item.get("goal_id"), limit=180),
170
+ "from_agent": from_agent,
171
+ "to_agent": to_agent,
172
+ "intent": intent,
173
+ "summary": _first_text(item, "summary", "note", "reason", "evidence", "title", "text", limit=280),
174
+ "evidence_refs": _evidence_refs(item, todo_id=todo_id),
175
+ "unresolved_decisions": _unresolved_decisions(item),
176
+ "blocked_on": blocked_on,
177
+ "suggested_next_action": _first_text(item, "suggested_next_action", "title", "text", limit=260),
178
+ "source": _compact_text(source or item.get("source"), limit=120),
179
+ }
180
+ if successor_todo_ids:
181
+ payload["successor_todo_ids"] = successor_todo_ids
182
+ if unblocks_todo_id:
183
+ payload["unblocks_todo_id"] = unblocks_todo_id
184
+ if excluded_agents:
185
+ payload["excluded_agents"] = excluded_agents
186
+ return {
187
+ key: value
188
+ for key, value in payload.items()
189
+ if value not in (None, "", [], {})
190
+ }
191
+
192
+
193
+ def attach_todo_handoff_note(
194
+ item: dict[str, Any],
195
+ *,
196
+ goal_id: str | None = None,
197
+ source: str | None = None,
198
+ ) -> dict[str, Any]:
199
+ note = build_todo_handoff_note(item, goal_id=goal_id, source=source)
200
+ if note:
201
+ item["handoff_note"] = note
202
+ return item