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,1917 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ...agents.agent_scope import (
6
+ agent_scope_blocking_handoff_gates,
7
+ agent_scope_count_advancement_items,
8
+ agent_scope_item_claimed_by,
9
+ agent_scope_item_claimed_by_agent_or_unclaimed,
10
+ )
11
+ from ...agents.profile import agent_profile_requires_vision
12
+ from ...agents.runtime_model import peer_work_key, select_peer_for_work
13
+ from ...runtime.time import parse_timestamp
14
+ from ...todos.projection import todo_item_is_watch_only_monitor
15
+ from ...work_items.autonomous_replan_ack import (
16
+ autonomous_replan_ack_matches_agent,
17
+ autonomous_replan_ack_matches_frontier,
18
+ normalize_projected_autonomous_replan_ack,
19
+ )
20
+ from ...work_items.autonomous_replan_obligation import (
21
+ MONITOR_NO_CHANGE_STREAK_THRESHOLD,
22
+ build_autonomous_replan_obligation_payload,
23
+ ensure_replan_novelty_policy,
24
+ with_replan_novelty_guidance,
25
+ )
26
+ from ...work_items.progress_observation import build_replan_context
27
+ from ..goal_vision_policy import (
28
+ COMPLETED_TODO_CHAIN_REPLAN_THRESHOLD,
29
+ goal_vision_repeats_advancement_until_closed,
30
+ )
31
+ from ..goal_vision_state import (
32
+ goal_vision_state_is_closed,
33
+ goal_vision_state_requires_successor,
34
+ )
35
+ from ..goal_vision_wait import build_goal_vision_wait_state
36
+ from . import outcome_continuity
37
+ from .ack_policy import (
38
+ autonomous_replan_ack_satisfies_obligation,
39
+ replan_successor_transition_ack,
40
+ )
41
+ from .replan_rules import (
42
+ GoalFrontierReplanFacts,
43
+ GoalFrontierReplanRule,
44
+ select_goal_frontier_replan_rule,
45
+ )
46
+ from .semantic_history import (
47
+ latest_agent_vision_from_runs as latest_agent_vision_from_runs,
48
+ )
49
+ from .semantic_history import (
50
+ latest_agent_vision_from_status_payload,
51
+ latest_autonomous_replan_ack_from_status_payload,
52
+ latest_missing_vision_checkpoint_from_status_payload,
53
+ latest_replan_ack_feedback_from_status_payload,
54
+ latest_runs_for_goal,
55
+ )
56
+ from .terminal import (
57
+ GOAL_TERMINAL_SOURCE_COMPLETENESS_SCHEMA_VERSION, # noqa: F401
58
+ GOAL_TERMINAL_STATE_SCHEMA_VERSION, # noqa: F401
59
+ VISION_CHECKPOINT_NO_FOLLOWUP_RESOLUTION, # noqa: F401
60
+ _terminal_no_followup_resolves_vision_checkpoint,
61
+ derive_goal_terminal_state,
62
+ goal_frontier_is_terminal_no_followup, # noqa: F401
63
+ )
64
+
65
+ GOAL_FRONTIER_PROJECTION_SCHEMA_VERSION = "goal_frontier_projection_v0"
66
+ VISION_CONTINUATION_AUDIT_SCHEMA_VERSION = "vision_continuation_audit_v0"
67
+ VISION_GAP_JUDGE_SCHEMA_VERSION = "vision_gap_judge_v0"
68
+ AUTONOMOUS_REPLAN_DECISION_SCHEMA_VERSION = "autonomous_replan_decision_v0"
69
+ AUTONOMOUS_REPLAN_SCOPE_SCHEMA_VERSION = "autonomous_replan_scope_v0"
70
+ AUTONOMOUS_REPLAN_OBLIGATION_SCHEMA_VERSION = "autonomous_replan_obligation_v0"
71
+ AUTONOMOUS_REPLAN_REQUIRED_MODE = "autonomous_replan_required"
72
+ FRONTIER_EXHAUSTED_MONITOR_TRIGGER = "frontier_exhausted_monitor_lane"
73
+ MONITOR_NO_CHANGE_STREAK_TRIGGER = "monitor_no_change_streak"
74
+ LONG_TODO_CHAIN_TRIGGER = "long_todo_chain"
75
+ VISION_ACCEPTANCE_GAP_TRIGGER = "vision_acceptance_gap"
76
+ VISION_SUCCESSOR_GAP_TRIGGER = "vision_successor_required"
77
+ VISION_PROFILE_MISSING_TRIGGER = "required_agent_vision_missing"
78
+ TODO_SUCCESSION_GAP_TRIGGER = "completed_advancement_without_successor"
79
+ TODO_TASK_CLASS_ADVANCEMENT = "advancement_task"
80
+ TODO_TASK_CLASS_MONITOR = "continuous_monitor"
81
+ LONG_TODO_CHAIN_ADVANCEMENT_THRESHOLD = 15
82
+ LONG_TODO_CHAIN_OPEN_THRESHOLD = 20
83
+ VISION_FRONTIER_TODO_DELTA_ACTIONS = {
84
+ "activate",
85
+ "create",
86
+ "reopen",
87
+ "resume",
88
+ "retain",
89
+ }
90
+
91
+
92
+ def safe_non_negative_int(value: Any) -> int:
93
+ try:
94
+ return max(0, int(value or 0))
95
+ except (TypeError, ValueError):
96
+ return 0
97
+
98
+
99
+ def select_autonomous_replan_obligation(
100
+ item: dict[str, Any],
101
+ project_asset: dict[str, Any] | None = None,
102
+ *,
103
+ agent_id: str | None = None,
104
+ ) -> dict[str, Any] | None:
105
+ project_asset = project_asset if isinstance(project_asset, dict) else {}
106
+ safe_agent_id = str(agent_id or "").strip()
107
+ if safe_agent_id:
108
+ for source in (item, project_asset):
109
+ obligations = source.get("autonomous_replan_obligations_by_agent")
110
+ if not isinstance(obligations, dict):
111
+ continue
112
+ value = obligations.get(safe_agent_id)
113
+ if isinstance(value, dict):
114
+ return value
115
+ value = item.get("autonomous_replan_obligation")
116
+ if isinstance(value, dict):
117
+ return value
118
+ value = project_asset.get("autonomous_replan_obligation")
119
+ if isinstance(value, dict):
120
+ return value
121
+ return None
122
+
123
+
124
+ def autonomous_replan_is_required(replan_obligation: dict[str, Any] | None) -> bool:
125
+ return bool(replan_obligation and replan_obligation.get("required"))
126
+
127
+
128
+ def align_autonomous_replan_guidance_with_acceptance_policy(
129
+ replan_obligation: dict[str, Any] | None,
130
+ *,
131
+ acceptance_gaps: list[dict[str, Any]] | None,
132
+ ) -> dict[str, Any] | None:
133
+ """Remove wait-only guidance when the active vision requires advancement."""
134
+
135
+ if not isinstance(replan_obligation, dict):
136
+ return replan_obligation
137
+ trigger_kinds = {
138
+ str(trigger.get("kind") or "").strip()
139
+ for trigger in (replan_obligation.get("triggers") or [])
140
+ if isinstance(trigger, dict)
141
+ }
142
+ if "blocked_successor_no_progress_repeat" not in trigger_kinds or not any(
143
+ goal_vision_repeats_advancement_until_closed(gap.get("advancement_policy"))
144
+ for gap in (acceptance_gaps or [])
145
+ if isinstance(gap, dict)
146
+ ):
147
+ return replan_obligation
148
+
149
+ aligned = dict(replan_obligation)
150
+ aligned["guidance_actions"] = [
151
+ "discover_safe_successor",
152
+ "create_runnable_todo",
153
+ "successor_or_supersede",
154
+ ]
155
+ aligned["todo_actions"] = [
156
+ {
157
+ **action,
158
+ "text": (
159
+ "discover and promote one safe in-scope evidence-backed runnable "
160
+ "todo, or replace the blocked successor through an explicit "
161
+ "successor/supersede transition"
162
+ ),
163
+ }
164
+ if isinstance(action, dict)
165
+ and action.get("action") == "add"
166
+ and action.get("role") == "agent"
167
+ else action
168
+ for action in (replan_obligation.get("todo_actions") or [])
169
+ ]
170
+ aligned["recommended_action"] = with_replan_novelty_guidance(
171
+ "run a bounded autonomous replan for the exact blocked successor: "
172
+ "create or claim one safe in-scope runnable advancement todo, or record "
173
+ "an explicit successor/supersede transition; a maintenance-only "
174
+ "continuation does not satisfy a repeat-until-closed vision"
175
+ )
176
+ aligned["satisfying_semantic_outcomes"] = [
177
+ "fresh_vision_path_outcome",
178
+ "new_runnable_successor",
179
+ "new_concrete_blocker",
180
+ "coverage_backed_exploration_exhausted",
181
+ "coverage_backed_no_followup",
182
+ ]
183
+ return aligned
184
+
185
+
186
+ def autonomous_replan_decision_allowed(
187
+ *,
188
+ replan_obligation: dict[str, Any] | None,
189
+ plan_ok: bool,
190
+ workspace_blocked: bool,
191
+ automation_prompt_upgrade_required: bool,
192
+ agent_id: str | None = None,
193
+ registered_agent_ids: list[str] | None = None,
194
+ ) -> bool:
195
+ return bool(
196
+ autonomous_replan_is_required(replan_obligation)
197
+ and autonomous_replan_scope_decision(
198
+ replan_obligation,
199
+ agent_id=agent_id,
200
+ registered_agent_ids=registered_agent_ids,
201
+ ).get("applies")
202
+ and plan_ok
203
+ and not workspace_blocked
204
+ and not automation_prompt_upgrade_required
205
+ )
206
+
207
+
208
+ def _normalize_replan_agent_id(value: Any) -> str | None:
209
+ text = str(value or "").strip()
210
+ return text or None
211
+
212
+
213
+ def _autonomous_replan_owner_agent_ids(
214
+ replan_obligation: dict[str, Any] | None,
215
+ ) -> list[str]:
216
+ if not isinstance(replan_obligation, dict):
217
+ return []
218
+ owner_keys = (
219
+ "agent_id",
220
+ "claimed_by",
221
+ "bound_agent",
222
+ "owner_agent",
223
+ "target_agent",
224
+ "blocks_agent",
225
+ )
226
+ owners: list[str] = []
227
+
228
+ def append_owner(value: Any) -> None:
229
+ owner = _normalize_replan_agent_id(value)
230
+ if owner and owner not in owners:
231
+ owners.append(owner)
232
+
233
+ for key in owner_keys:
234
+ append_owner(replan_obligation.get(key))
235
+ triggers = (
236
+ replan_obligation.get("triggers")
237
+ if isinstance(replan_obligation.get("triggers"), list)
238
+ else []
239
+ )
240
+ for trigger in triggers:
241
+ if not isinstance(trigger, dict):
242
+ continue
243
+ for key in owner_keys:
244
+ append_owner(trigger.get(key))
245
+ return owners
246
+
247
+
248
+ def autonomous_replan_scope_decision(
249
+ replan_obligation: dict[str, Any] | None,
250
+ *,
251
+ agent_id: str | None,
252
+ registered_agent_ids: list[str] | None = None,
253
+ ) -> dict[str, Any]:
254
+ """Return whether a replan obligation belongs to this agent lane.
255
+
256
+ Explicit agent-owned replans are consumed only by that agent. Unscoped
257
+ goal-level replans are deterministically assigned to one registered peer.
258
+ """
259
+
260
+ normalized_agent_id = _normalize_replan_agent_id(agent_id)
261
+ owners = _autonomous_replan_owner_agent_ids(replan_obligation)
262
+ required = autonomous_replan_is_required(replan_obligation)
263
+ selected_peer_agent = None
264
+ if not required:
265
+ applies = False
266
+ scope = "not_required"
267
+ elif not normalized_agent_id:
268
+ applies = True
269
+ scope = "unscoped_quota_call"
270
+ elif owners:
271
+ applies = normalized_agent_id in owners
272
+ scope = "explicit_agent_owner"
273
+ else:
274
+ selected_peer_agent = select_peer_for_work(
275
+ registered_agent_ids or [],
276
+ work_key=peer_work_key(
277
+ replan_obligation,
278
+ fallback="autonomous_replan",
279
+ ),
280
+ )
281
+ applies = bool(selected_peer_agent and normalized_agent_id == selected_peer_agent)
282
+ scope = "deterministic_peer_assignment"
283
+ payload = {
284
+ "schema_version": AUTONOMOUS_REPLAN_SCOPE_SCHEMA_VERSION,
285
+ "required": required,
286
+ "applies": applies,
287
+ "scope": scope,
288
+ "agent_id": normalized_agent_id,
289
+ "agent_model": "peer_v1",
290
+ "owner_agent_ids": owners,
291
+ "selected_peer_agent": selected_peer_agent,
292
+ }
293
+ return payload
294
+
295
+
296
+ def _compact_projection_text(value: Any, *, limit: int = 360) -> str | None:
297
+ text = " ".join(str(value or "").strip().split())
298
+ if not text:
299
+ return None
300
+ return text[:limit]
301
+
302
+
303
+ def projected_autonomous_replan_ack_for_agent(
304
+ item: dict[str, Any],
305
+ project_asset: dict[str, Any] | None,
306
+ *,
307
+ agent_id: str | None,
308
+ ) -> dict[str, Any] | None:
309
+ """Return the current projected replan ACK when it belongs to this agent."""
310
+
311
+ project_asset = project_asset if isinstance(project_asset, dict) else {}
312
+ for candidate in (
313
+ item.get("autonomous_replan_ack"),
314
+ project_asset.get("autonomous_replan_ack"),
315
+ ):
316
+ normalized = normalize_projected_autonomous_replan_ack(candidate)
317
+ if autonomous_replan_ack_matches_agent(normalized, agent_id=agent_id):
318
+ return normalized
319
+ return None
320
+
321
+
322
+ def acceptance_gaps_from_agent_vision(
323
+ agent_vision: dict[str, Any] | None,
324
+ *,
325
+ goal_status: str | None = None,
326
+ ) -> list[dict[str, Any]]:
327
+ """Convert bounded vision replan triggers into goal-frontier gap records."""
328
+
329
+ if not isinstance(agent_vision, dict):
330
+ return []
331
+ patch = agent_vision.get("vision_patch") if isinstance(agent_vision.get("vision_patch"), dict) else {}
332
+ state = str(agent_vision.get("state") or "").strip()
333
+ if goal_vision_state_is_closed(state):
334
+ normalized_goal_status = str(goal_status or "").strip().lower()
335
+ active_goal = normalized_goal_status == "active" or normalized_goal_status.startswith(
336
+ "active-"
337
+ )
338
+ if goal_vision_state_requires_successor(state) and active_goal:
339
+ return [
340
+ {
341
+ "kind": VISION_SUCCESSOR_GAP_TRIGGER,
342
+ "source": "latest_agent_vision",
343
+ "agent_id": agent_vision.get("agent_id"),
344
+ "state": agent_vision.get("state"),
345
+ "goal_status": normalized_goal_status,
346
+ "replan_trigger_summary": (
347
+ "the current stage vision is closed while the registry goal "
348
+ "remains active; establish a successor vision before continuing"
349
+ ),
350
+ "acceptance_summary": (
351
+ "Write the next bounded agent vision, or explicitly retire, "
352
+ "supersede, or close the lane with no_followup."
353
+ ),
354
+ "advancement_policy": "repeat_until_closed",
355
+ "generated_at": agent_vision.get("generated_at"),
356
+ }
357
+ ]
358
+ return []
359
+ acceptance = _compact_projection_text(patch.get("acceptance_summary"), limit=420)
360
+ explicit_trigger = _compact_projection_text(
361
+ patch.get("replan_trigger_summary"),
362
+ limit=240,
363
+ )
364
+ trigger = explicit_trigger
365
+ if not trigger and acceptance:
366
+ trigger = "active agent vision remains open with acceptance evidence still required"
367
+ if not trigger:
368
+ return []
369
+ gap: dict[str, Any] = {
370
+ "kind": VISION_ACCEPTANCE_GAP_TRIGGER,
371
+ "source": "latest_agent_vision",
372
+ "agent_id": agent_vision.get("agent_id"),
373
+ "state": agent_vision.get("state"),
374
+ "replan_trigger_summary": trigger,
375
+ "replan_trigger_source": (
376
+ "explicit_vision_trigger"
377
+ if explicit_trigger
378
+ else "implicit_open_acceptance"
379
+ ),
380
+ }
381
+ if acceptance:
382
+ gap["acceptance_summary"] = acceptance
383
+ vision_todo_ids = [
384
+ todo_id
385
+ for value in (agent_vision.get("todo_delta") or [])
386
+ if isinstance(value, str)
387
+ and (parts := value.strip().partition(":"))[1]
388
+ and parts[0].strip().lower() in VISION_FRONTIER_TODO_DELTA_ACTIONS
389
+ and (todo_id := _compact_projection_text(parts[2], limit=120))
390
+ ]
391
+ if vision_todo_ids:
392
+ gap["vision_todo_ids"] = list(dict.fromkeys(vision_todo_ids))
393
+ advancement_policy = _compact_projection_text(
394
+ patch.get("advancement_policy"),
395
+ limit=32,
396
+ )
397
+ if advancement_policy:
398
+ gap["advancement_policy"] = advancement_policy
399
+ generated_at = _compact_projection_text(agent_vision.get("generated_at"), limit=80)
400
+ if generated_at:
401
+ gap["generated_at"] = generated_at
402
+ return [gap]
403
+
404
+
405
+ def acceptance_gaps_from_agent_profile_requirement(
406
+ agent_profile: dict[str, Any] | None,
407
+ *,
408
+ agent_id: str | None,
409
+ agent_vision: dict[str, Any] | None,
410
+ missing_checkpoint: dict[str, Any] | None,
411
+ ) -> list[dict[str, Any]]:
412
+ """Require a vision baseline for registered long-lived peer lanes."""
413
+
414
+ if (
415
+ not agent_id
416
+ or not agent_profile_requires_vision(agent_profile)
417
+ or isinstance(agent_vision, dict)
418
+ or isinstance(missing_checkpoint, dict)
419
+ ):
420
+ return []
421
+ return [
422
+ {
423
+ "kind": VISION_PROFILE_MISSING_TRIGGER,
424
+ "source": "agent_profile",
425
+ "agent_id": agent_id,
426
+ "replan_trigger_summary": (
427
+ "the registered long-lived agent lane requires a persisted vision "
428
+ "baseline before ordinary delivery or monitor-only quiet wait"
429
+ ),
430
+ "acceptance_summary": (
431
+ "Write a bounded agent vision with objective, acceptance evidence, "
432
+ "advancement policy, and replan trigger; or explicitly mark the "
433
+ "profile vision requirement optional."
434
+ ),
435
+ "advancement_policy": "repeat_until_closed",
436
+ }
437
+ ]
438
+
439
+
440
+ def build_vision_continuation_audit(
441
+ *,
442
+ goal_id: str | None = None,
443
+ agent_id: str | None,
444
+ acceptance_gaps: list[dict[str, Any]] | None,
445
+ ) -> dict[str, Any] | None:
446
+ """Return the closeout audit contract for an open per-agent vision gap.
447
+
448
+ This is a read-path contract: quota/status can tell an agent that the
449
+ selected todo is only a step toward the active vision. Writeback still goes
450
+ through normal todo, evidence, and refresh-state commands.
451
+ """
452
+
453
+ compact_acceptance_gaps = [
454
+ gap for gap in (acceptance_gaps or []) if isinstance(gap, dict)
455
+ ]
456
+ if not compact_acceptance_gaps:
457
+ return None
458
+ vision_gap_judge = build_vision_gap_judge(
459
+ goal_id=goal_id,
460
+ agent_id=agent_id,
461
+ acceptance_gaps=compact_acceptance_gaps,
462
+ )
463
+ acceptance_requirements = [
464
+ text
465
+ for text in (
466
+ _compact_projection_text(gap.get("acceptance_summary"), limit=180)
467
+ for gap in compact_acceptance_gaps
468
+ )
469
+ if text
470
+ ]
471
+ trigger_kinds = [
472
+ kind
473
+ for kind in (
474
+ _compact_projection_text(gap.get("kind"), limit=80)
475
+ for gap in compact_acceptance_gaps
476
+ )
477
+ if kind
478
+ ]
479
+ audit: dict[str, Any] = {
480
+ "schema_version": VISION_CONTINUATION_AUDIT_SCHEMA_VERSION,
481
+ "required": True,
482
+ "agent_id": agent_id,
483
+ "decision": "acceptance_gap_open",
484
+ "selected_todo_is_goal_completion": False,
485
+ "closeout_allowed_without_evidence": False,
486
+ "trigger_count": len(compact_acceptance_gaps),
487
+ "trigger_kinds": list(dict.fromkeys(trigger_kinds))[:5],
488
+ "acceptance_gaps": compact_acceptance_gaps[:5],
489
+ "vision_gap_judge": vision_gap_judge,
490
+ "authoritative_evidence_kinds": [
491
+ "changed_files",
492
+ "public_safe_evidence_records",
493
+ "public_web_research_findings",
494
+ "evaluation_outputs",
495
+ "successor_state",
496
+ "blocker_state",
497
+ "superseding_agent_vision",
498
+ ],
499
+ "not_satisfied_by": [
500
+ "todo_completion_alone",
501
+ "autonomous_replan_ack_alone",
502
+ "vision_checkpoint_alone",
503
+ "registry_registration_alone",
504
+ "no_followup_without_acceptance_evidence",
505
+ ],
506
+ "required_before_closeout": [
507
+ "derive_requirements_from_active_vision_and_current_todo",
508
+ "name_authoritative_evidence_for_each_requirement",
509
+ "inspect_registry_declared_materials_before_external_research",
510
+ "run_bounded_public_research_when_local_evidence_is_missing",
511
+ "create_successor_or_write_vision_replan_trigger_when_unproven",
512
+ ],
513
+ "recommended_action": (
514
+ "audit active per-agent vision acceptance before todo closeout; "
515
+ "if evidence is weak or missing, inspect registry-declared material "
516
+ "references before bounded public research, then keep the vision "
517
+ "active with a successor todo or --vision-replan-trigger"
518
+ ),
519
+ }
520
+ if acceptance_requirements:
521
+ audit["acceptance_requirements"] = acceptance_requirements[:5]
522
+ return audit
523
+
524
+
525
+ def build_vision_gap_judge(
526
+ *,
527
+ goal_id: str | None = None,
528
+ agent_id: str | None,
529
+ acceptance_gaps: list[dict[str, Any]] | None,
530
+ ) -> dict[str, Any]:
531
+ """Return the strict done/continue judge for active per-agent vision gaps.
532
+
533
+ The read model keeps the prompt essence compact: unless current evidence
534
+ satisfies, blocks, or supersedes the active vision, the agent should judge
535
+ the vision as still CONTINUE.
536
+ """
537
+
538
+ compact_acceptance_gaps = [
539
+ gap for gap in (acceptance_gaps or []) if isinstance(gap, dict)
540
+ ]
541
+ first_gap = compact_acceptance_gaps[0] if compact_acceptance_gaps else {}
542
+ reason = _compact_projection_text(
543
+ first_gap.get("replan_trigger_summary")
544
+ or "active per-agent vision still has an open acceptance gap",
545
+ limit=220,
546
+ )
547
+ evidence_read_instruction = (
548
+ "Use the host-projected compact coverage ledger and uncovered frontier. "
549
+ "The agent-scoped evidence log remains the durable source and an optional "
550
+ "diagnostic readback, not a model-executed closure gate."
551
+ )
552
+ registry_read_instruction = (
553
+ "Before external research, inspect the selected goal's registry-declared "
554
+ "topic_authority and project_materials metadata, preferring any projected "
555
+ "replan_context or agent_material_frontier. Use role, freshness, revision, "
556
+ "boundary, gate_status, and conflict_rule to choose permitted references. "
557
+ "Registration guides discovery; it neither grants access nor proves acceptance."
558
+ )
559
+ public_research_instruction = (
560
+ "If projected evidence, the evidence log, and permitted registry-declared "
561
+ "references remain missing, stale, or too weak and the acceptance question "
562
+ "depends on public facts, run bounded public web research using primary or "
563
+ "authoritative sources; record confirmed/refuted findings as public-safe "
564
+ "evidence or a compact vision replan trigger before judging."
565
+ )
566
+ return {
567
+ "schema_version": VISION_GAP_JUDGE_SCHEMA_VERSION,
568
+ "goal_id": goal_id,
569
+ "agent_id": agent_id,
570
+ "done": False,
571
+ "decision": "continue",
572
+ "reason": (
573
+ reason
574
+ or "active per-agent vision still has an open acceptance gap"
575
+ ),
576
+ "agent_judge_instruction": (
577
+ "Judge vision closure: compare active vision acceptance_summary "
578
+ "with the host-projected agent-scoped coverage ledger. "
579
+ "Inspect permitted registry-declared material references before bounded "
580
+ "public web research. "
581
+ "Mark done only when evidence proves completion, a blocker/user "
582
+ "gate, or superseding/no-follow-up closure; otherwise continue."
583
+ ),
584
+ "evidence_read_instruction": evidence_read_instruction,
585
+ "registry_read_instruction": registry_read_instruction,
586
+ "external_research_instruction": public_research_instruction,
587
+ "research_writeback_required_when_used": [
588
+ "source_url_or_public_reference",
589
+ "confirmed_or_refuted_finding",
590
+ "supports_or_refutes_acceptance_gap",
591
+ "successor_todo_or_vision_replan_trigger",
592
+ ],
593
+ "done_only_when": [
594
+ "authoritative_evidence_satisfies_acceptance",
595
+ "final_deliverable_or_eval_output_satisfies_acceptance",
596
+ "blocker_or_user_gate_is_projected",
597
+ "superseding_vision_or_no_followup_closes_the_frontier",
598
+ ],
599
+ "continue_when": [
600
+ "evidence_is_missing_weak_or_stale",
601
+ "todo_lifecycle_or_protocol_status_is_the_only_proof",
602
+ "acceptance_gap_is_still_projected",
603
+ ],
604
+ "otherwise": "continue",
605
+ }
606
+
607
+
608
+ def _open_todo_count(summary: dict[str, Any] | None) -> int:
609
+ if not isinstance(summary, dict):
610
+ return 0
611
+ return safe_non_negative_int(summary.get("open_count"))
612
+
613
+
614
+ def _blocking_user_open_count(summary: dict[str, Any] | None) -> int:
615
+ if not isinstance(summary, dict):
616
+ return 0
617
+ gate_items = summary.get("gate_open_items")
618
+ if isinstance(gate_items, list):
619
+ return sum(
620
+ 1
621
+ for item in gate_items
622
+ if isinstance(item, dict) and _todo_item_is_actionable_open(item)
623
+ )
624
+ # Legacy or partial summaries without typed gate projection fail closed.
625
+ return _open_todo_count(summary)
626
+
627
+
628
+ def _todo_item_is_actionable_open(item: dict[str, Any]) -> bool:
629
+ if item.get("done") is True:
630
+ return False
631
+ status = str(item.get("status") or "open").strip().lower()
632
+ return status in {"", "open", "todo", "active", "pending"}
633
+
634
+
635
+ def _todo_task_class(item: dict[str, Any]) -> str:
636
+ return str(item.get("task_class") or "").strip()
637
+
638
+
639
+ def _count_advancement_items(items: Any, *, claimed_by: str | None = None) -> int:
640
+ return agent_scope_count_advancement_items(items, claimed_by=claimed_by)
641
+
642
+
643
+ def _summary_task_counts(summary: dict[str, Any] | None) -> dict[str, int]:
644
+ open_count = _open_todo_count(summary)
645
+ if not isinstance(summary, dict):
646
+ return {"open": open_count, "advancement": 0, "monitor": 0, "monitor_due": 0}
647
+ executable = summary.get("executable_backlog_items")
648
+ monitor_open = summary.get("monitor_open_items")
649
+ watch_only_count = (
650
+ len(
651
+ [
652
+ item
653
+ for item in monitor_open
654
+ if isinstance(item, dict)
655
+ and _todo_item_is_actionable_open(item)
656
+ and todo_item_is_watch_only_monitor(item)
657
+ ]
658
+ )
659
+ if isinstance(monitor_open, list)
660
+ else safe_non_negative_int(summary.get("watch_only_monitor_count"))
661
+ )
662
+ open_count = max(0, open_count - watch_only_count)
663
+ advancement_count = (
664
+ _count_advancement_items(executable)
665
+ if isinstance(executable, list)
666
+ else safe_non_negative_int(summary.get("claimed_advancement_open_count"))
667
+ + len(
668
+ [
669
+ item
670
+ for item in (summary.get("unclaimed_priority_open_items") or [])
671
+ if isinstance(item, dict)
672
+ and _todo_task_class(item) == TODO_TASK_CLASS_ADVANCEMENT
673
+ ]
674
+ )
675
+ )
676
+ monitor_count = (
677
+ len(
678
+ [
679
+ item
680
+ for item in monitor_open
681
+ if isinstance(item, dict)
682
+ and _todo_item_is_actionable_open(item)
683
+ and _todo_task_class(item) == TODO_TASK_CLASS_MONITOR
684
+ and not todo_item_is_watch_only_monitor(item)
685
+ ]
686
+ )
687
+ if isinstance(monitor_open, list)
688
+ else safe_non_negative_int(summary.get("claimed_monitor_open_count"))
689
+ )
690
+ return {
691
+ "open": open_count,
692
+ "advancement": advancement_count,
693
+ "monitor": monitor_count,
694
+ "monitor_due": max(
695
+ 0,
696
+ safe_non_negative_int(summary.get("monitor_due_count"))
697
+ - safe_non_negative_int(summary.get("watch_only_monitor_due_count")),
698
+ ),
699
+ }
700
+
701
+
702
+ def _monitor_no_change_streak_trigger(
703
+ agent_todo_summary: dict[str, Any] | None,
704
+ *,
705
+ agent_id: str | None,
706
+ ) -> dict[str, Any] | None:
707
+ if not isinstance(agent_todo_summary, dict):
708
+ return None
709
+ monitor_items = agent_todo_summary.get("monitor_open_items")
710
+ if not isinstance(monitor_items, list):
711
+ return None
712
+
713
+ stalled: list[tuple[int, str, dict[str, Any]]] = []
714
+ for item in monitor_items:
715
+ if not isinstance(item, dict):
716
+ continue
717
+ if not _todo_item_is_actionable_open(item):
718
+ continue
719
+ if _todo_task_class(item) != TODO_TASK_CLASS_MONITOR:
720
+ continue
721
+ if todo_item_is_watch_only_monitor(item):
722
+ continue
723
+ claimed_by = agent_scope_item_claimed_by(item)
724
+ if agent_id and claimed_by != agent_id:
725
+ continue
726
+ no_change_count = safe_non_negative_int(item.get("consecutive_no_change"))
727
+ if no_change_count < MONITOR_NO_CHANGE_STREAK_THRESHOLD:
728
+ continue
729
+ target_key = str(
730
+ item.get("target_key") or item.get("todo_id") or "monitor"
731
+ ).strip()
732
+ stalled.append((no_change_count, target_key, item))
733
+ if not stalled:
734
+ return None
735
+
736
+ no_change_count, target_key, monitor = min(
737
+ stalled,
738
+ key=lambda entry: (-entry[0], entry[1]),
739
+ )
740
+ return {
741
+ "kind": MONITOR_NO_CHANGE_STREAK_TRIGGER,
742
+ "section": "agent_todo_summary.monitor_open_items",
743
+ "text": (
744
+ f"monitor {target_key} recorded {no_change_count} consecutive "
745
+ "unchanged polls without selectable advancement"
746
+ ),
747
+ "todo_id": monitor.get("todo_id"),
748
+ "monitor_target_id": target_key,
749
+ "run_count": no_change_count,
750
+ "threshold": MONITOR_NO_CHANGE_STREAK_THRESHOLD,
751
+ "agent_id": agent_id,
752
+ }
753
+
754
+
755
+ def _frontier_advancement_counts(
756
+ *,
757
+ agent_todo_summary: dict[str, Any] | None,
758
+ agent_id: str | None,
759
+ ) -> dict[str, int]:
760
+ current_agent_advancement_count = (
761
+ safe_non_negative_int(agent_todo_summary.get("current_agent_claimed_advancement_count"))
762
+ if isinstance(agent_todo_summary, dict)
763
+ else 0
764
+ )
765
+ unclaimed_advancement_count = 0
766
+ other_agent_claimed_items: Any = None
767
+ if isinstance(agent_todo_summary, dict):
768
+ executable_items = agent_todo_summary.get("executable_backlog_items")
769
+ if isinstance(executable_items, list):
770
+ if agent_id:
771
+ current_agent_advancement_count = max(
772
+ current_agent_advancement_count,
773
+ _count_advancement_items(executable_items, claimed_by=agent_id),
774
+ )
775
+ unclaimed_advancement_count = _count_advancement_items(
776
+ executable_items,
777
+ claimed_by="__unclaimed__",
778
+ )
779
+ else:
780
+ # The unclaimed visibility lane is diagnostic and may contain work
781
+ # that excludes this agent. Prefer the scoped executable lane when
782
+ # available; retain this fallback for legacy summaries only.
783
+ unclaimed_advancement_count = _count_advancement_items(
784
+ agent_todo_summary.get("unclaimed_priority_open_items"),
785
+ claimed_by="__unclaimed__",
786
+ )
787
+ claim_scope = (
788
+ agent_todo_summary.get("claim_scope")
789
+ if isinstance(agent_todo_summary.get("claim_scope"), dict)
790
+ else {}
791
+ )
792
+ other_agent_claimed_items = claim_scope.get("other_agent_claimed_items")
793
+ return {
794
+ "current_agent_claimed_advancement_count": current_agent_advancement_count,
795
+ "unclaimed_advancement_count": unclaimed_advancement_count,
796
+ "other_agent_claimed_advancement_count": _count_advancement_items(
797
+ other_agent_claimed_items
798
+ ),
799
+ }
800
+
801
+
802
+ def _long_todo_chain_trigger(
803
+ *,
804
+ agent_todo_summary: dict[str, Any] | None,
805
+ agent_counts: dict[str, int],
806
+ frontier_counts: dict[str, int],
807
+ agent_id: str | None,
808
+ ) -> dict[str, Any] | None:
809
+ """Return a trigger when one lane is long enough to need vision replan.
810
+
811
+ The trigger is scoped to the current agent plus unclaimed selectable work.
812
+ Other-agent claimed todos stay out of this count so one role cannot replan
813
+ another role's backlog.
814
+ """
815
+
816
+ current_advancement = frontier_counts.get("current_agent_claimed_advancement_count", 0)
817
+ unclaimed_advancement = frontier_counts.get("unclaimed_advancement_count", 0)
818
+ selectable_advancement = current_advancement + unclaimed_advancement
819
+ if isinstance(agent_todo_summary, dict):
820
+ current_open = safe_non_negative_int(
821
+ agent_todo_summary.get("current_agent_claimed_open_count")
822
+ )
823
+ unclaimed_open = safe_non_negative_int(agent_todo_summary.get("unclaimed_open_count"))
824
+ selectable_open = max(current_open + unclaimed_open, selectable_advancement)
825
+ else:
826
+ selectable_open = max(agent_counts.get("open", 0), selectable_advancement)
827
+ if selectable_advancement >= LONG_TODO_CHAIN_ADVANCEMENT_THRESHOLD:
828
+ return {
829
+ "trigger_count": selectable_advancement,
830
+ "count_kind": "selectable_advancement_todos",
831
+ "selectable_open_count": selectable_open,
832
+ "selectable_advancement_count": selectable_advancement,
833
+ "current_agent_claimed_advancement_count": current_advancement,
834
+ "unclaimed_advancement_count": unclaimed_advancement,
835
+ "threshold": LONG_TODO_CHAIN_ADVANCEMENT_THRESHOLD,
836
+ "agent_id": agent_id,
837
+ }
838
+ if (
839
+ selectable_open >= LONG_TODO_CHAIN_OPEN_THRESHOLD
840
+ and selectable_advancement > 0
841
+ ):
842
+ return {
843
+ "trigger_count": selectable_open,
844
+ "count_kind": "selectable_open_todos",
845
+ "selectable_open_count": selectable_open,
846
+ "selectable_advancement_count": selectable_advancement,
847
+ "current_agent_claimed_advancement_count": current_advancement,
848
+ "unclaimed_advancement_count": unclaimed_advancement,
849
+ "threshold": LONG_TODO_CHAIN_OPEN_THRESHOLD,
850
+ "agent_id": agent_id,
851
+ }
852
+ return None
853
+
854
+
855
+ def _compact_todo_id(item: Any) -> str | None:
856
+ if not isinstance(item, dict):
857
+ return None
858
+ todo_id = str(item.get("todo_id") or "").strip()
859
+ return todo_id or None
860
+
861
+
862
+ def _deferred_successors(
863
+ summary: dict[str, Any] | None,
864
+ *,
865
+ agent_id: str | None,
866
+ ) -> dict[str, Any]:
867
+ if not isinstance(summary, dict):
868
+ return {
869
+ "ready_count": 0,
870
+ "blocked_count": 0,
871
+ "current_agent_ready_count": 0,
872
+ "ready_todo_ids": [],
873
+ }
874
+
875
+ ready_items = [
876
+ item
877
+ for item in (summary.get("deferred_resume_candidates") or [])
878
+ if isinstance(item, dict)
879
+ ]
880
+ deferred_items = [
881
+ item for item in (summary.get("deferred_items") or []) if isinstance(item, dict)
882
+ ]
883
+ deferred_count = max(
884
+ safe_non_negative_int(summary.get("deferred_count")),
885
+ len(deferred_items),
886
+ len(ready_items),
887
+ )
888
+ current_agent_ready_items = [
889
+ item
890
+ for item in ready_items
891
+ if agent_id and agent_scope_item_claimed_by(item) == agent_id
892
+ ]
893
+ ready_todo_ids = [
894
+ todo_id for todo_id in (_compact_todo_id(item) for item in ready_items[:5]) if todo_id
895
+ ]
896
+ projection = {
897
+ "ready_count": len(ready_items),
898
+ "blocked_count": max(0, deferred_count - len(ready_items)),
899
+ "current_agent_ready_count": len(current_agent_ready_items),
900
+ "ready_todo_ids": ready_todo_ids,
901
+ }
902
+ if ready_todo_ids:
903
+ projection["top_ready_todo_id"] = ready_todo_ids[0]
904
+ return projection
905
+
906
+
907
+ def _is_monitor_only_lane(
908
+ work_lane_contract: dict[str, Any] | None,
909
+ ) -> bool:
910
+ return bool(
911
+ _is_continuous_monitor_lane(work_lane_contract)
912
+ and (
913
+ work_lane_contract.get("must_attempt_work") is False
914
+ or work_lane_contract.get("monitor_kind") == "todo_monitor_due"
915
+ )
916
+ )
917
+
918
+
919
+ def _is_continuous_monitor_lane(
920
+ work_lane_contract: dict[str, Any] | None,
921
+ ) -> bool:
922
+ return bool(
923
+ work_lane_contract
924
+ and work_lane_contract.get("lane") == TODO_TASK_CLASS_MONITOR
925
+ )
926
+
927
+
928
+ def _monitor_only_lane_has_future_schedule(
929
+ agent_todo_summary: dict[str, Any] | None,
930
+ ) -> bool:
931
+ if not isinstance(agent_todo_summary, dict):
932
+ return False
933
+ if "monitor_due_count" not in agent_todo_summary:
934
+ return False
935
+ if "monitor_schedule_gap_count" not in agent_todo_summary:
936
+ return False
937
+ if safe_non_negative_int(agent_todo_summary.get("monitor_due_count")) > 0:
938
+ return False
939
+ if safe_non_negative_int(agent_todo_summary.get("monitor_schedule_gap_count")) > 0:
940
+ return False
941
+ monitor_items = agent_todo_summary.get("monitor_open_items")
942
+ return isinstance(monitor_items, list) and len(monitor_items) > 0
943
+
944
+
945
+ def _blocking_handoff_gate_count(
946
+ agent_todo_summary: dict[str, Any] | None,
947
+ *,
948
+ agent_id: str | None,
949
+ ) -> int:
950
+ if not agent_id or not isinstance(agent_todo_summary, dict):
951
+ return 0
952
+ return len(agent_scope_blocking_handoff_gates(agent_todo_summary, agent_id=agent_id))
953
+
954
+
955
+ def _ready_deferred_successor_count(
956
+ agent_todo_summary: dict[str, Any] | None,
957
+ *,
958
+ agent_id: str | None,
959
+ ) -> int:
960
+ if not isinstance(agent_todo_summary, dict):
961
+ return 0
962
+ current_count = safe_non_negative_int(
963
+ agent_todo_summary.get("current_agent_deferred_resume_count")
964
+ )
965
+ unclaimed_count = safe_non_negative_int(
966
+ agent_todo_summary.get("unclaimed_deferred_resume_count")
967
+ )
968
+ if current_count or unclaimed_count:
969
+ return current_count + unclaimed_count
970
+ candidates = (
971
+ agent_todo_summary.get("deferred_resume_candidates")
972
+ if isinstance(agent_todo_summary.get("deferred_resume_candidates"), list)
973
+ else []
974
+ )
975
+ return len(
976
+ [
977
+ item
978
+ for item in candidates
979
+ if isinstance(item, dict)
980
+ and agent_scope_item_claimed_by_agent_or_unclaimed(item, agent_id=agent_id)
981
+ ]
982
+ )
983
+
984
+
985
+ def _succession_gap_items(
986
+ agent_todo_summary: dict[str, Any] | None,
987
+ *,
988
+ agent_id: str | None,
989
+ ) -> list[dict[str, Any]]:
990
+ if not isinstance(agent_todo_summary, dict):
991
+ return []
992
+ warning = (
993
+ agent_todo_summary.get("todo_succession_warning")
994
+ if isinstance(agent_todo_summary.get("todo_succession_warning"), dict)
995
+ else {}
996
+ )
997
+ source_items = (
998
+ warning.get("items")
999
+ if isinstance(warning.get("items"), list)
1000
+ else agent_todo_summary.get("completed_without_successor_items")
1001
+ if isinstance(agent_todo_summary.get("completed_without_successor_items"), list)
1002
+ else []
1003
+ )
1004
+ items = [item for item in source_items if isinstance(item, dict)]
1005
+ if not agent_id:
1006
+ return items
1007
+ return [
1008
+ item
1009
+ for item in items
1010
+ if agent_scope_item_claimed_by_agent_or_unclaimed(item, agent_id=agent_id)
1011
+ ]
1012
+
1013
+
1014
+ def _replan_evidence_acknowledged(
1015
+ items: list[dict[str, Any]],
1016
+ latest_replan_ack: dict[str, Any] | None,
1017
+ *,
1018
+ time_key: str,
1019
+ ) -> bool:
1020
+ """Return true when a valid replan ack postdates the newest evidence item.
1021
+
1022
+ A gap that the agent already acknowledged with a repair delta must not
1023
+ re-derive an endless replan obligation: without this, any stale gap turns
1024
+ quota into a permanent run-now loop that no acknowledgement can settle.
1025
+ """
1026
+
1027
+ if not items or not isinstance(latest_replan_ack, dict):
1028
+ return False
1029
+ ack_time = parse_timestamp(latest_replan_ack.get("generated_at"))
1030
+ if ack_time is None:
1031
+ return False
1032
+ newest = max(
1033
+ (
1034
+ timestamp
1035
+ for item in items
1036
+ if isinstance(item, dict)
1037
+ if (timestamp := parse_timestamp(item.get(time_key))) is not None
1038
+ ),
1039
+ default=None,
1040
+ )
1041
+ if newest is None:
1042
+ return False
1043
+ return ack_time >= newest
1044
+
1045
+
1046
+ def _succession_gap_acknowledged(
1047
+ gap_items: list[dict[str, Any]],
1048
+ latest_replan_ack: dict[str, Any] | None,
1049
+ ) -> bool:
1050
+ """Return true when a valid replan ack postdates the newest succession gap."""
1051
+
1052
+ return _replan_evidence_acknowledged(
1053
+ gap_items,
1054
+ latest_replan_ack,
1055
+ time_key="completed_at",
1056
+ )
1057
+
1058
+
1059
+ def _vision_gap_acknowledged(
1060
+ acceptance_gaps: list[dict[str, Any]],
1061
+ latest_replan_ack: dict[str, Any] | None,
1062
+ ) -> bool:
1063
+ """Return true when a valid replan ack covers the newest vision gap.
1064
+
1065
+ A goal_vision_patch ack settles vision successor/checkpoint gaps even when
1066
+ the ack turn's own vision writebacks carry slightly newer timestamps;
1067
+ without this, an agent's acknowledgement turn regenerates the gap and quota
1068
+ stays in a permanent run-now loop (observed as alternating
1069
+ autonomous_replan_recorded / quota_slot_spent runs with no advancement).
1070
+ """
1071
+
1072
+ if not acceptance_gaps or not isinstance(latest_replan_ack, dict):
1073
+ return False
1074
+ delta_contract = latest_replan_ack.get("delta_contract")
1075
+ delta_kinds = (
1076
+ delta_contract.get("delta_kinds")
1077
+ if isinstance(delta_contract, dict)
1078
+ else None
1079
+ )
1080
+ if isinstance(delta_kinds, list) and any(
1081
+ str(kind) in {"goal_vision_patch", "goal_vision_replan_trigger"}
1082
+ for kind in delta_kinds
1083
+ ):
1084
+ return True
1085
+ return _replan_evidence_acknowledged(
1086
+ acceptance_gaps,
1087
+ latest_replan_ack,
1088
+ time_key="generated_at",
1089
+ )
1090
+
1091
+
1092
+ def derive_goal_frontier_replan_obligation_from_summaries(
1093
+ *,
1094
+ user_todo_summary: dict[str, Any] | None,
1095
+ agent_todo_summary: dict[str, Any] | None,
1096
+ work_lane_contract: dict[str, Any] | None,
1097
+ agent_id: str | None,
1098
+ existing_replan_obligation: dict[str, Any] | None,
1099
+ latest_replan_ack: dict[str, Any] | None = None,
1100
+ acceptance_gaps: list[dict[str, Any]] | None = None,
1101
+ monitor_lane_semantically_valid: bool = True,
1102
+ ) -> dict[str, Any] | None:
1103
+ """Return a compact replan obligation when the goal frontier has no advancement.
1104
+
1105
+ This keeps the per-goal completion/replan rule in the goal-frontier policy
1106
+ seam. Quota should consume the resulting obligation instead of embedding
1107
+ monitor/vision semantics in its scheduler path.
1108
+ """
1109
+
1110
+ agent_counts = _summary_task_counts(agent_todo_summary)
1111
+ frontier_counts = _frontier_advancement_counts(
1112
+ agent_todo_summary=agent_todo_summary,
1113
+ agent_id=agent_id,
1114
+ )
1115
+ total_frontier_advancement = sum(frontier_counts.values())
1116
+ selectable_frontier_advancement = (
1117
+ frontier_counts["current_agent_claimed_advancement_count"]
1118
+ + frontier_counts["unclaimed_advancement_count"]
1119
+ )
1120
+ monitor_no_change_trigger = (
1121
+ _monitor_no_change_streak_trigger(
1122
+ agent_todo_summary,
1123
+ agent_id=agent_id,
1124
+ )
1125
+ if selectable_frontier_advancement == 0
1126
+ else None
1127
+ )
1128
+ compact_acceptance_gaps = [
1129
+ item for item in (acceptance_gaps or []) if isinstance(item, dict)
1130
+ ]
1131
+ successor_vision_required = any(
1132
+ item.get("kind")
1133
+ in {VISION_SUCCESSOR_GAP_TRIGGER, VISION_PROFILE_MISSING_TRIGGER}
1134
+ for item in compact_acceptance_gaps
1135
+ )
1136
+ outcome_checkpoint_replan_required = any(
1137
+ item.get("kind")
1138
+ == outcome_continuity.VISION_OUTCOME_CHECKPOINT_REQUIRED_TRIGGER
1139
+ for item in compact_acceptance_gaps
1140
+ )
1141
+ succession_gap_items = _succession_gap_items(
1142
+ agent_todo_summary,
1143
+ agent_id=agent_id,
1144
+ )
1145
+ long_chain_trigger = _long_todo_chain_trigger(
1146
+ agent_todo_summary=agent_todo_summary,
1147
+ agent_counts=agent_counts,
1148
+ frontier_counts=frontier_counts,
1149
+ agent_id=agent_id,
1150
+ )
1151
+ replan_rule = select_goal_frontier_replan_rule(
1152
+ GoalFrontierReplanFacts(
1153
+ existing_replan_required=autonomous_replan_is_required(
1154
+ existing_replan_obligation
1155
+ ),
1156
+ blocking_handoff_gate_count=_blocking_handoff_gate_count(
1157
+ agent_todo_summary,
1158
+ agent_id=agent_id,
1159
+ ),
1160
+ ready_deferred_successor_count=_ready_deferred_successor_count(
1161
+ agent_todo_summary,
1162
+ agent_id=agent_id,
1163
+ ),
1164
+ successor_vision_required=successor_vision_required,
1165
+ blocking_user_open_count=_blocking_user_open_count(user_todo_summary),
1166
+ user_open_count=_open_todo_count(user_todo_summary),
1167
+ succession_gap_count=len(succession_gap_items),
1168
+ succession_gap_acknowledged=_succession_gap_acknowledged(
1169
+ succession_gap_items,
1170
+ latest_replan_ack,
1171
+ ),
1172
+ vision_gap_acknowledged=_vision_gap_acknowledged(
1173
+ compact_acceptance_gaps,
1174
+ latest_replan_ack,
1175
+ ),
1176
+ agent_advancement_count=agent_counts.get("advancement", 0),
1177
+ total_frontier_advancement=total_frontier_advancement,
1178
+ acceptance_gap_count=len(compact_acceptance_gaps),
1179
+ selectable_frontier_advancement=selectable_frontier_advancement,
1180
+ outcome_checkpoint_replan_required=(
1181
+ outcome_checkpoint_replan_required
1182
+ ),
1183
+ long_todo_chain_triggered=long_chain_trigger is not None,
1184
+ current_agent_blocker_count=safe_non_negative_int(
1185
+ (agent_todo_summary or {}).get("current_agent_blocker_count")
1186
+ ),
1187
+ monitor_no_change_streak_triggered=(
1188
+ monitor_no_change_trigger is not None
1189
+ ),
1190
+ monitor_only_lane=_is_monitor_only_lane(work_lane_contract),
1191
+ monitor_count=agent_counts.get("monitor", 0),
1192
+ monitor_due_count=safe_non_negative_int(
1193
+ (agent_todo_summary or {}).get("monitor_due_count")
1194
+ ),
1195
+ monitor_schedule_gap_count=safe_non_negative_int(
1196
+ (agent_todo_summary or {}).get("monitor_schedule_gap_count")
1197
+ ),
1198
+ future_monitor_schedule_present=(
1199
+ _monitor_only_lane_has_future_schedule(agent_todo_summary)
1200
+ ),
1201
+ monitor_lane_semantically_valid=monitor_lane_semantically_valid,
1202
+ )
1203
+ )
1204
+ if not replan_rule.derives_obligation:
1205
+ return None
1206
+ if replan_rule.rule is GoalFrontierReplanRule.TODO_SUCCESSION_GAP:
1207
+ triggers = [
1208
+ {
1209
+ "kind": TODO_SUCCESSION_GAP_TRIGGER,
1210
+ "section": "agent_todo_summary.todo_succession_warning",
1211
+ "todo_id": item.get("todo_id"),
1212
+ "text": item.get("text")
1213
+ or item.get("title")
1214
+ or "completed advancement needs a successor or no-followup rationale",
1215
+ "agent_id": agent_id,
1216
+ "claimed_by": item.get("claimed_by"),
1217
+ }
1218
+ for item in succession_gap_items[:3]
1219
+ ]
1220
+ return build_autonomous_replan_obligation_payload(
1221
+ schema_version=AUTONOMOUS_REPLAN_OBLIGATION_SCHEMA_VERSION,
1222
+ agent_id=agent_id,
1223
+ include_agent_id=True,
1224
+ stall_threshold=1,
1225
+ trigger_count=len(succession_gap_items),
1226
+ triggers=triggers,
1227
+ guidance_actions=[
1228
+ "create_successor",
1229
+ "link_successor",
1230
+ "record_no_followup",
1231
+ ],
1232
+ todo_actions=[
1233
+ {
1234
+ "action": "add",
1235
+ "role": "agent",
1236
+ "priority": "P0",
1237
+ "text": (
1238
+ "run a bounded successor replan: create or link the next "
1239
+ "runnable advancement todo, or record an explicit "
1240
+ "no-follow-up rationale"
1241
+ ),
1242
+ }
1243
+ ],
1244
+ stop_condition=(
1245
+ "stop if the successor decision requires private material, "
1246
+ "credentials, destructive git, production actions, or owner-only decisions"
1247
+ ),
1248
+ recommended_action=(
1249
+ "run a bounded successor replan before another quiet poll: add/link "
1250
+ "the next advancement todo, or record explicit no-follow-up"
1251
+ ),
1252
+ )
1253
+ if replan_rule.rule is GoalFrontierReplanRule.VISION_ACCEPTANCE_GAP:
1254
+ return build_autonomous_replan_obligation_payload(
1255
+ schema_version=AUTONOMOUS_REPLAN_OBLIGATION_SCHEMA_VERSION,
1256
+ agent_id=agent_id,
1257
+ include_agent_id=True,
1258
+ stall_threshold=1,
1259
+ trigger_count=len(compact_acceptance_gaps),
1260
+ triggers=[
1261
+ {
1262
+ "kind": gap.get("kind") or VISION_ACCEPTANCE_GAP_TRIGGER,
1263
+ "section": "goal_frontier_projection.acceptance_gaps",
1264
+ "text": gap.get("replan_trigger_summary")
1265
+ or "bounded agent vision reports an open acceptance gap",
1266
+ "agent_id": agent_id,
1267
+ "acceptance_summary": gap.get("acceptance_summary"),
1268
+ "advancement_policy": gap.get("advancement_policy"),
1269
+ **{
1270
+ key: gap.get(key)
1271
+ for key in (
1272
+ "completed_todo_id",
1273
+ "completed_todo_count",
1274
+ "completed_todo_threshold",
1275
+ "completed_todo_ids",
1276
+ )
1277
+ if gap.get(key) is not None
1278
+ },
1279
+ }
1280
+ for gap in compact_acceptance_gaps[:3]
1281
+ ],
1282
+ guidance_actions=[
1283
+ "create_successor",
1284
+ "update_agent_vision",
1285
+ "record_evidence_gap",
1286
+ "record_no_followup",
1287
+ ],
1288
+ todo_actions=[
1289
+ {
1290
+ "action": "add",
1291
+ "role": "agent",
1292
+ "priority": "P0",
1293
+ "text": (
1294
+ "run a bounded vision-gap replan: create the next runnable "
1295
+ "advancement todo or record an explicit no-follow-up rationale"
1296
+ ),
1297
+ }
1298
+ ],
1299
+ stop_condition=(
1300
+ "stop if the gap requires private material, credentials, destructive git, "
1301
+ "production actions, or owner-only decisions"
1302
+ ),
1303
+ recommended_action=(
1304
+ "run a bounded vision-gap replan before another quiet poll: create "
1305
+ "successor work, update the agent vision, record evidence gap, or "
1306
+ "record no-follow-up"
1307
+ ),
1308
+ )
1309
+ if replan_rule.rule is GoalFrontierReplanRule.LONG_TODO_CHAIN:
1310
+ assert long_chain_trigger is not None
1311
+ return build_autonomous_replan_obligation_payload(
1312
+ schema_version=AUTONOMOUS_REPLAN_OBLIGATION_SCHEMA_VERSION,
1313
+ agent_id=agent_id,
1314
+ include_agent_id=True,
1315
+ stall_threshold=long_chain_trigger.get("threshold"),
1316
+ trigger_count=long_chain_trigger.get("trigger_count"),
1317
+ triggers=[
1318
+ {
1319
+ "kind": LONG_TODO_CHAIN_TRIGGER,
1320
+ "section": "agent_todo_summary",
1321
+ "text": (
1322
+ "current agent lane has a long selectable todo chain; "
1323
+ "run a vision checkpoint/replan before continuing linearly"
1324
+ ),
1325
+ **long_chain_trigger,
1326
+ }
1327
+ ],
1328
+ guidance_actions=[
1329
+ "read_evidence_log",
1330
+ "run_bounded_public_research_if_local_evidence_is_missing",
1331
+ "group_or_prune_todo_chain",
1332
+ "update_agent_vision",
1333
+ "create_successor",
1334
+ ],
1335
+ todo_actions=[
1336
+ {
1337
+ "action": "add",
1338
+ "role": "agent",
1339
+ "priority": "P1",
1340
+ "text": (
1341
+ "run a bounded long-chain vision replan: compare evidence "
1342
+ "with the active vision, group or prune the todo chain, "
1343
+ "and select the next high-value runnable slice"
1344
+ ),
1345
+ }
1346
+ ],
1347
+ stop_condition=(
1348
+ "stop if pruning or external research requires private material, "
1349
+ "credentials, destructive git, production actions, or owner-only decisions"
1350
+ ),
1351
+ recommended_action=(
1352
+ "run a bounded long-chain vision replan before continuing a 15+ "
1353
+ "todo lane: read evidence, use public research if local evidence "
1354
+ "is weak, group/prune work, and write a concrete todo or vision delta"
1355
+ ),
1356
+ )
1357
+ if replan_rule.rule is GoalFrontierReplanRule.MONITOR_NO_CHANGE_STREAK:
1358
+ assert monitor_no_change_trigger is not None
1359
+ return build_autonomous_replan_obligation_payload(
1360
+ schema_version=AUTONOMOUS_REPLAN_OBLIGATION_SCHEMA_VERSION,
1361
+ agent_id=agent_id,
1362
+ include_agent_id=True,
1363
+ stall_threshold=MONITOR_NO_CHANGE_STREAK_THRESHOLD,
1364
+ trigger_count=1,
1365
+ triggers=[monitor_no_change_trigger],
1366
+ guidance_actions=[
1367
+ "set_watch_expiry",
1368
+ "write_blocker",
1369
+ "supersede_monitor",
1370
+ "create_successor",
1371
+ ],
1372
+ todo_actions=[
1373
+ {
1374
+ "action": "add",
1375
+ "role": "agent",
1376
+ "priority": "P1",
1377
+ "text": (
1378
+ "resolve the stalled monitor lane with an expiry, blocker, "
1379
+ "supersede transition, or runnable successor"
1380
+ ),
1381
+ }
1382
+ ],
1383
+ stop_condition=(
1384
+ "stop if the replan requires private material, credentials, "
1385
+ "destructive git, production actions, or owner-only decisions"
1386
+ ),
1387
+ recommended_action=(
1388
+ "resolve the current agent's stalled monitor lane before another "
1389
+ "quiet poll: set an expiry, write a blocker, supersede the monitor, "
1390
+ "or create runnable successor work"
1391
+ ),
1392
+ )
1393
+ assert replan_rule.rule is GoalFrontierReplanRule.MONITOR_FRONTIER_EXHAUSTED
1394
+ future_schedule_present = _monitor_only_lane_has_future_schedule(agent_todo_summary)
1395
+
1396
+ return build_autonomous_replan_obligation_payload(
1397
+ schema_version=AUTONOMOUS_REPLAN_OBLIGATION_SCHEMA_VERSION,
1398
+ stall_threshold=1,
1399
+ trigger_count=1,
1400
+ triggers=[
1401
+ {
1402
+ "kind": FRONTIER_EXHAUSTED_MONITOR_TRIGGER,
1403
+ "section": "goal_frontier_projection",
1404
+ "text": (
1405
+ "current goal frontier has no current, unclaimed, or other-agent "
1406
+ "advancement todo while only monitor work remains"
1407
+ ),
1408
+ "agent_id": agent_id,
1409
+ "agent_open_count": agent_counts.get("open", 0),
1410
+ "agent_monitor_open_count": agent_counts.get("monitor", 0),
1411
+ "future_monitor_schedule_present": future_schedule_present,
1412
+ }
1413
+ ],
1414
+ guidance_actions=[
1415
+ "create_successor",
1416
+ "supersede_monitor",
1417
+ "set_watch_expiry",
1418
+ "record_no_followup",
1419
+ ],
1420
+ todo_actions=[
1421
+ {
1422
+ "action": "add",
1423
+ "role": "agent",
1424
+ "priority": "P1",
1425
+ "text": (
1426
+ "run a compact goal-frontier replan: create a successor runnable "
1427
+ "todo, supersede stale monitor work, set watch-lane expiry, or "
1428
+ "record an explicit no-follow-up rationale"
1429
+ ),
1430
+ }
1431
+ ],
1432
+ stop_condition=(
1433
+ "stop if the replan requires private material, credentials, destructive git, "
1434
+ "production actions, or owner-only decisions"
1435
+ ),
1436
+ recommended_action=(
1437
+ "run a bounded goal-frontier replan before another monitor-only quiet "
1438
+ "poll: create successor work, supersede the monitor lane, set an expiry, "
1439
+ "or record an evidence-backed no-follow-up outcome"
1440
+ ),
1441
+ )
1442
+
1443
+
1444
+ def build_goal_frontier_projection_from_summaries(
1445
+ *,
1446
+ goal_id: str,
1447
+ agent_id: str | None,
1448
+ user_todo_summary: dict[str, Any] | None,
1449
+ agent_todo_summary: dict[str, Any] | None,
1450
+ work_lane_contract: dict[str, Any] | None,
1451
+ replan_obligation: dict[str, Any] | None,
1452
+ acceptance_gaps: list[dict[str, Any]] | None = None,
1453
+ vision_wait_state: dict[str, Any] | None = None,
1454
+ ) -> dict[str, Any]:
1455
+ user_counts = _summary_task_counts(user_todo_summary)
1456
+ agent_counts = _summary_task_counts(agent_todo_summary)
1457
+ frontier_counts = _frontier_advancement_counts(
1458
+ agent_todo_summary=agent_todo_summary,
1459
+ agent_id=agent_id,
1460
+ )
1461
+ selectable_frontier_advancement = (
1462
+ frontier_counts["current_agent_claimed_advancement_count"]
1463
+ + frontier_counts["unclaimed_advancement_count"]
1464
+ )
1465
+ monitor_only_lane = bool(
1466
+ _is_monitor_only_lane(work_lane_contract)
1467
+ and agent_counts.get("monitor", 0) > 0
1468
+ and agent_counts.get("advancement", 0) == 0
1469
+ and (
1470
+ selectable_frontier_advancement == 0
1471
+ if agent_id
1472
+ else sum(frontier_counts.values()) == 0
1473
+ )
1474
+ )
1475
+ projection = build_goal_frontier_projection(
1476
+ goal_id=goal_id,
1477
+ agent_id=agent_id,
1478
+ user_counts=user_counts,
1479
+ agent_counts=agent_counts,
1480
+ current_agent_claimed_advancement_count=frontier_counts[
1481
+ "current_agent_claimed_advancement_count"
1482
+ ],
1483
+ unclaimed_advancement_count=frontier_counts["unclaimed_advancement_count"],
1484
+ other_agent_claimed_advancement_count=frontier_counts[
1485
+ "other_agent_claimed_advancement_count"
1486
+ ],
1487
+ monitor_only_lane=monitor_only_lane,
1488
+ replan_obligation=replan_obligation,
1489
+ acceptance_gaps=acceptance_gaps,
1490
+ vision_wait_state=vision_wait_state,
1491
+ deferred_successors=_deferred_successors(
1492
+ agent_todo_summary,
1493
+ agent_id=agent_id,
1494
+ ),
1495
+ )
1496
+ source_completeness, terminal_state = derive_goal_terminal_state(
1497
+ user_todo_summary=user_todo_summary,
1498
+ agent_todo_summary=agent_todo_summary,
1499
+ projection=projection,
1500
+ )
1501
+ if terminal_state:
1502
+ projection["source_completeness"] = source_completeness
1503
+ projection["terminal_state"] = terminal_state
1504
+ return projection
1505
+
1506
+
1507
+ def build_goal_frontier_projection_context_from_status(
1508
+ *,
1509
+ goal_id: str,
1510
+ agent_id: str | None,
1511
+ status_payload: dict[str, Any],
1512
+ item: dict[str, Any],
1513
+ project_asset: dict[str, Any] | None,
1514
+ user_todo_summary: dict[str, Any] | None,
1515
+ agent_todo_summary: dict[str, Any] | None,
1516
+ work_lane_contract: dict[str, Any] | None,
1517
+ neutral_replan_ack_classifications: set[str],
1518
+ agent_todo_source_items: list[dict[str, Any]] | None = None,
1519
+ registered_agent_ids: list[str] | None = None,
1520
+ goal_status: str | None = None,
1521
+ agent_profile: dict[str, Any] | None = None,
1522
+ ) -> dict[str, Any]:
1523
+ """Build the quota-facing goal-frontier read model.
1524
+
1525
+ Quota decides delivery permission, but this helper owns the goal-frontier
1526
+ state reduction: existing obligation scope, latest replan ACK, open
1527
+ per-agent vision gaps, derived replan obligation, and final projection.
1528
+ """
1529
+
1530
+ replan_obligation = select_autonomous_replan_obligation(
1531
+ item,
1532
+ project_asset,
1533
+ agent_id=agent_id,
1534
+ )
1535
+ if replan_obligation:
1536
+ replan_obligation = ensure_replan_novelty_policy(replan_obligation)
1537
+ replan_scope = autonomous_replan_scope_decision(
1538
+ replan_obligation,
1539
+ agent_id=agent_id,
1540
+ registered_agent_ids=registered_agent_ids,
1541
+ )
1542
+ if replan_scope.get("required") and not replan_scope.get("applies"):
1543
+ replan_obligation = None
1544
+
1545
+ latest_agent_replan_ack = latest_autonomous_replan_ack_from_status_payload(
1546
+ status_payload,
1547
+ goal_id=goal_id,
1548
+ agent_id=agent_id,
1549
+ neutral_classifications=neutral_replan_ack_classifications,
1550
+ )
1551
+ latest_replan_ack_feedback = latest_replan_ack_feedback_from_status_payload(
1552
+ status_payload,
1553
+ goal_id=goal_id,
1554
+ agent_id=agent_id,
1555
+ )
1556
+ ack_time = parse_timestamp((latest_agent_replan_ack or {}).get("generated_at"))
1557
+ feedback_time = parse_timestamp(
1558
+ (latest_replan_ack_feedback or {}).get("generated_at")
1559
+ )
1560
+ if ack_time is not None and feedback_time is not None and ack_time >= feedback_time:
1561
+ latest_replan_ack_feedback = None
1562
+ latest_agent_vision = latest_agent_vision_from_status_payload(
1563
+ status_payload,
1564
+ goal_id=goal_id,
1565
+ agent_id=agent_id,
1566
+ )
1567
+ latest_missing_vision_checkpoint = latest_missing_vision_checkpoint_from_status_payload(
1568
+ status_payload,
1569
+ goal_id=goal_id,
1570
+ agent_id=agent_id,
1571
+ )
1572
+ latest_vision_checkpoint = (
1573
+ outcome_continuity.latest_outcome_vision_checkpoint_from_status_payload(
1574
+ status_payload,
1575
+ goal_id=goal_id,
1576
+ agent_id=agent_id,
1577
+ )
1578
+ )
1579
+ source_acceptance_gaps = (
1580
+ acceptance_gaps_from_agent_profile_requirement(
1581
+ agent_profile,
1582
+ agent_id=agent_id,
1583
+ agent_vision=latest_agent_vision,
1584
+ missing_checkpoint=latest_missing_vision_checkpoint,
1585
+ )
1586
+ + acceptance_gaps_from_agent_vision(
1587
+ latest_agent_vision,
1588
+ goal_status=goal_status,
1589
+ )
1590
+ + outcome_continuity.acceptance_gaps_from_vision_checkpoint(
1591
+ latest_missing_vision_checkpoint
1592
+ )
1593
+ + outcome_continuity.acceptance_gaps_from_outcome_checkpoint(
1594
+ latest_agent_vision,
1595
+ latest_vision_checkpoint,
1596
+ )
1597
+ + outcome_continuity.acceptance_gaps_from_todo_completion_checkpoint(
1598
+ latest_agent_vision,
1599
+ latest_vision_checkpoint,
1600
+ agent_todo_summary=agent_todo_summary,
1601
+ agent_id=agent_id,
1602
+ completed_todo_threshold=COMPLETED_TODO_CHAIN_REPLAN_THRESHOLD,
1603
+ )
1604
+ )
1605
+ if _terminal_no_followup_resolves_vision_checkpoint(
1606
+ user_todo_summary=user_todo_summary,
1607
+ agent_todo_summary=agent_todo_summary,
1608
+ checkpoint=latest_missing_vision_checkpoint,
1609
+ ):
1610
+ source_acceptance_gaps = [
1611
+ gap
1612
+ for gap in source_acceptance_gaps
1613
+ if gap.get("kind")
1614
+ != outcome_continuity.VISION_CHECKPOINT_MISSING_TRIGGER
1615
+ ]
1616
+ replan_obligation = align_autonomous_replan_guidance_with_acceptance_policy(
1617
+ replan_obligation,
1618
+ acceptance_gaps=source_acceptance_gaps,
1619
+ )
1620
+ frontier_counts = _frontier_advancement_counts(
1621
+ agent_todo_summary=agent_todo_summary,
1622
+ agent_id=agent_id,
1623
+ )
1624
+ vision_wait_state = build_goal_vision_wait_state(
1625
+ agent_todo_summary=agent_todo_summary,
1626
+ agent_id=agent_id,
1627
+ acceptance_gaps=source_acceptance_gaps,
1628
+ selectable_advancement_count=(
1629
+ frontier_counts["current_agent_claimed_advancement_count"]
1630
+ + frontier_counts["unclaimed_advancement_count"]
1631
+ ),
1632
+ )
1633
+ acceptance_gaps = [] if vision_wait_state else source_acceptance_gaps
1634
+ projected_replan_ack = projected_autonomous_replan_ack_for_agent(
1635
+ item,
1636
+ project_asset,
1637
+ agent_id=agent_id,
1638
+ )
1639
+ effective_replan_ack = latest_agent_replan_ack or projected_replan_ack
1640
+ replan_transition_ack = replan_successor_transition_ack(
1641
+ agent_todo_summary,
1642
+ agent_id=agent_id,
1643
+ replan_obligation=replan_obligation,
1644
+ agent_todo_items=agent_todo_source_items,
1645
+ )
1646
+ obligation_ack = replan_transition_ack or effective_replan_ack
1647
+ if (
1648
+ autonomous_replan_is_required(replan_obligation)
1649
+ and autonomous_replan_ack_satisfies_obligation(
1650
+ obligation_ack,
1651
+ replan_obligation=replan_obligation,
1652
+ acceptance_gaps=source_acceptance_gaps,
1653
+ )
1654
+ and autonomous_replan_ack_matches_frontier(
1655
+ obligation_ack,
1656
+ replan_obligation,
1657
+ )
1658
+ ):
1659
+ replan_obligation = None
1660
+ replan_scope = autonomous_replan_scope_decision(
1661
+ replan_obligation,
1662
+ agent_id=agent_id,
1663
+ registered_agent_ids=registered_agent_ids,
1664
+ )
1665
+ frontier_replan_obligation = derive_goal_frontier_replan_obligation_from_summaries(
1666
+ user_todo_summary=user_todo_summary,
1667
+ agent_todo_summary=agent_todo_summary,
1668
+ work_lane_contract=work_lane_contract,
1669
+ agent_id=agent_id,
1670
+ existing_replan_obligation=replan_obligation,
1671
+ latest_replan_ack=effective_replan_ack,
1672
+ acceptance_gaps=acceptance_gaps,
1673
+ monitor_lane_semantically_valid=not goal_vision_state_is_closed(
1674
+ (latest_agent_vision or {}).get("state")
1675
+ ),
1676
+ )
1677
+ frontier_transition_ack = replan_successor_transition_ack(
1678
+ agent_todo_summary,
1679
+ agent_id=agent_id,
1680
+ replan_obligation=frontier_replan_obligation,
1681
+ agent_todo_items=agent_todo_source_items,
1682
+ )
1683
+ frontier_obligation_ack = frontier_transition_ack or effective_replan_ack
1684
+ if (
1685
+ frontier_replan_obligation
1686
+ and autonomous_replan_ack_satisfies_obligation(
1687
+ frontier_obligation_ack,
1688
+ replan_obligation=frontier_replan_obligation,
1689
+ acceptance_gaps=source_acceptance_gaps,
1690
+ )
1691
+ and autonomous_replan_ack_matches_frontier(
1692
+ frontier_obligation_ack,
1693
+ frontier_replan_obligation,
1694
+ )
1695
+ ):
1696
+ frontier_replan_obligation = None
1697
+ replan_transition_ack = frontier_transition_ack
1698
+ if frontier_replan_obligation:
1699
+ replan_obligation = ensure_replan_novelty_policy(
1700
+ frontier_replan_obligation
1701
+ )
1702
+ replan_scope = autonomous_replan_scope_decision(
1703
+ replan_obligation,
1704
+ agent_id=agent_id,
1705
+ registered_agent_ids=registered_agent_ids,
1706
+ )
1707
+
1708
+ if replan_obligation and latest_replan_ack_feedback:
1709
+ replan_obligation = dict(replan_obligation)
1710
+ replan_obligation["replan_ack_feedback"] = latest_replan_ack_feedback
1711
+ claims = latest_replan_ack_feedback.get("rejected_claims") or []
1712
+ first_claim = next(
1713
+ (item for item in claims if isinstance(item, dict)),
1714
+ None,
1715
+ )
1716
+ if first_claim:
1717
+ replan_obligation["recommended_action"] = (
1718
+ "previous replan ACK was rejected: "
1719
+ f"{first_claim.get('kind')}: {first_claim.get('reason')}; "
1720
+ + str(replan_obligation.get("recommended_action") or "replan again")
1721
+ )
1722
+
1723
+ if replan_obligation:
1724
+ replan_obligation = dict(replan_obligation)
1725
+ replan_obligation["replan_context"] = build_replan_context(
1726
+ replan_obligation,
1727
+ goal_id=goal_id,
1728
+ agent_id=agent_id,
1729
+ newest_first_runs=latest_runs_for_goal(
1730
+ status_payload,
1731
+ goal_id=goal_id,
1732
+ ),
1733
+ )
1734
+
1735
+ goal_frontier_projection = build_goal_frontier_projection_from_summaries(
1736
+ goal_id=goal_id,
1737
+ agent_id=agent_id,
1738
+ user_todo_summary=user_todo_summary,
1739
+ agent_todo_summary=agent_todo_summary,
1740
+ work_lane_contract=work_lane_contract,
1741
+ replan_obligation=replan_obligation,
1742
+ acceptance_gaps=acceptance_gaps,
1743
+ vision_wait_state=vision_wait_state,
1744
+ )
1745
+ if latest_replan_ack_feedback:
1746
+ goal_frontier_projection["replan_ack_feedback"] = (
1747
+ latest_replan_ack_feedback
1748
+ )
1749
+ return {
1750
+ "schema_version": "goal_frontier_projection_context_v0",
1751
+ "replan_obligation": replan_obligation,
1752
+ "replan_scope": replan_scope,
1753
+ "goal_frontier_projection": goal_frontier_projection,
1754
+ "acceptance_gaps": acceptance_gaps,
1755
+ "vision_wait_state": vision_wait_state,
1756
+ "latest_replan_ack": latest_agent_replan_ack,
1757
+ "projected_replan_ack": projected_replan_ack,
1758
+ "replan_transition_ack": replan_transition_ack,
1759
+ }
1760
+
1761
+
1762
+ def compact_replan_obligation(replan_obligation: dict[str, Any]) -> dict[str, Any]:
1763
+ compact = {
1764
+ "schema_version": replan_obligation.get("schema_version"),
1765
+ "obligation_id": replan_obligation.get("obligation_id"),
1766
+ "stall_threshold": replan_obligation.get("stall_threshold"),
1767
+ "trigger_count": replan_obligation.get("trigger_count"),
1768
+ "triggers": replan_obligation.get("triggers") or [],
1769
+ "stop_condition": replan_obligation.get("stop_condition"),
1770
+ }
1771
+ if replan_obligation.get("frontier_identity"):
1772
+ compact["frontier_identity"] = replan_obligation.get("frontier_identity")
1773
+ if isinstance(replan_obligation.get("replan_novelty_policy"), dict):
1774
+ # Keep hot quota/status packets on the two authoritative seams. The
1775
+ # detailed selection hint remains available on the full obligation.
1776
+ compact["replan_novelty_policy"] = {
1777
+ "evidence_source": "agent_scoped_evidence_log",
1778
+ "delivery": "host_projected",
1779
+ "writeback": "typed_semantic_delta",
1780
+ }
1781
+ if isinstance(replan_obligation.get("replan_ack_feedback"), dict):
1782
+ compact["replan_ack_feedback"] = replan_obligation["replan_ack_feedback"]
1783
+ compact["recommended_action"] = replan_obligation.get("recommended_action")
1784
+ return compact
1785
+
1786
+
1787
+ def build_autonomous_replan_recommendation(
1788
+ replan_obligation: dict[str, Any],
1789
+ *,
1790
+ reason: str | None = None,
1791
+ ) -> dict[str, Any]:
1792
+ return {
1793
+ "recommended_mode": AUTONOMOUS_REPLAN_REQUIRED_MODE,
1794
+ "notify": "NOTIFY",
1795
+ "replan_obligation": compact_replan_obligation(replan_obligation),
1796
+ "spend_policy": (
1797
+ "append exactly one heartbeat spend only after executing the selected "
1798
+ "replan slice, validating it, and writing back todo split/add/retire state"
1799
+ ),
1800
+ "reason": reason
1801
+ or (
1802
+ "status exposes an autonomous replan obligation; advance the goal-level "
1803
+ "planning-trigger slice before monitor-only or agent-scope wait "
1804
+ "classification; replan turns always notify because DONT_NOTIFY would "
1805
+ "mislead agents into a quiet no-op"
1806
+ ),
1807
+ }
1808
+
1809
+
1810
+ def build_autonomous_replan_decision(replan_obligation: dict[str, Any]) -> dict[str, Any]:
1811
+ triggers = (
1812
+ replan_obligation.get("triggers")
1813
+ if isinstance(replan_obligation.get("triggers"), list)
1814
+ else []
1815
+ )
1816
+ return {
1817
+ "schema_version": AUTONOMOUS_REPLAN_DECISION_SCHEMA_VERSION,
1818
+ "required": True,
1819
+ "decision": AUTONOMOUS_REPLAN_REQUIRED_MODE,
1820
+ "decision_plane": "goal_frontier_before_lane_quiet_or_agent_scope_wait",
1821
+ "not_disturbed_by": [
1822
+ "monitor_quiet_skip",
1823
+ "agent_scope_wait",
1824
+ "agent_scope_exhausted",
1825
+ ],
1826
+ "trigger_count": safe_non_negative_int(replan_obligation.get("trigger_count")),
1827
+ "triggers": [
1828
+ trigger.get("kind")
1829
+ for trigger in triggers
1830
+ if isinstance(trigger, dict) and trigger.get("kind")
1831
+ ],
1832
+ }
1833
+
1834
+
1835
+ def build_goal_frontier_projection(
1836
+ *,
1837
+ goal_id: str,
1838
+ agent_id: str | None,
1839
+ user_counts: dict[str, int],
1840
+ agent_counts: dict[str, int],
1841
+ current_agent_claimed_advancement_count: int,
1842
+ unclaimed_advancement_count: int,
1843
+ other_agent_claimed_advancement_count: int,
1844
+ monitor_only_lane: bool,
1845
+ replan_obligation: dict[str, Any] | None,
1846
+ acceptance_gaps: list[dict[str, Any]] | None = None,
1847
+ deferred_successors: dict[str, Any] | None = None,
1848
+ vision_wait_state: dict[str, Any] | None = None,
1849
+ ) -> dict[str, Any]:
1850
+ replan_required = autonomous_replan_is_required(replan_obligation)
1851
+ blockers: list[str] = []
1852
+ if monitor_only_lane:
1853
+ blockers.append("monitor_only_lane")
1854
+ if (
1855
+ current_agent_claimed_advancement_count == 0
1856
+ and unclaimed_advancement_count == 0
1857
+ and other_agent_claimed_advancement_count > 0
1858
+ ):
1859
+ blockers.append("other_agent_claimed_advancement")
1860
+ if replan_required:
1861
+ blockers.append("autonomous_replan_obligation")
1862
+ if isinstance(vision_wait_state, dict):
1863
+ if vision_wait_state.get("reason_code") == "current_agent_blocker":
1864
+ blockers.append("current_agent_blocker")
1865
+ else:
1866
+ blockers.append("vision_blocked_successor_wait")
1867
+
1868
+ compact_acceptance_gaps = [
1869
+ item for item in (acceptance_gaps or []) if isinstance(item, dict)
1870
+ ]
1871
+ vision_continuation_audit = build_vision_continuation_audit(
1872
+ goal_id=goal_id,
1873
+ agent_id=agent_id,
1874
+ acceptance_gaps=compact_acceptance_gaps,
1875
+ )
1876
+ projection: dict[str, Any] = {
1877
+ "schema_version": GOAL_FRONTIER_PROJECTION_SCHEMA_VERSION,
1878
+ "goal_id": goal_id,
1879
+ "agent_id": agent_id,
1880
+ "source": "quota_should_run",
1881
+ "normalized_progress": {
1882
+ "user_open_count": user_counts.get("open", 0),
1883
+ "agent_open_count": agent_counts.get("open", 0),
1884
+ "agent_advancement_open_count": agent_counts.get("advancement", 0),
1885
+ "agent_monitor_open_count": agent_counts.get("monitor", 0),
1886
+ "agent_monitor_due_count": agent_counts.get("monitor_due", 0),
1887
+ },
1888
+ "remaining_advancement_frontier": {
1889
+ "current_agent_claimed_advancement_count": current_agent_claimed_advancement_count,
1890
+ "unclaimed_advancement_count": unclaimed_advancement_count,
1891
+ "other_agent_claimed_advancement_count": other_agent_claimed_advancement_count,
1892
+ },
1893
+ "monitor_only_lanes": {
1894
+ "present": monitor_only_lane,
1895
+ "quiet_until_material_transition": monitor_only_lane,
1896
+ },
1897
+ "deferred_successors": deferred_successors
1898
+ if isinstance(deferred_successors, dict)
1899
+ else {
1900
+ "ready_count": 0,
1901
+ "blocked_count": 0,
1902
+ "current_agent_ready_count": 0,
1903
+ "ready_todo_ids": [],
1904
+ },
1905
+ "acceptance_gaps": compact_acceptance_gaps[:5],
1906
+ "autonomy_blockers": blockers,
1907
+ "replan_required": replan_required,
1908
+ }
1909
+ if vision_continuation_audit:
1910
+ projection["vision_continuation_audit"] = vision_continuation_audit
1911
+ if isinstance(vision_wait_state, dict):
1912
+ projection["vision_wait_state"] = vision_wait_state
1913
+ if replan_required and isinstance(replan_obligation, dict):
1914
+ projection["autonomous_replan_decision"] = build_autonomous_replan_decision(
1915
+ replan_obligation
1916
+ )
1917
+ return projection