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.
- loopx/__init__.py +5 -0
- loopx/agent_onboarding.py +654 -0
- loopx/agent_registry.py +112 -0
- loopx/ark_managed_agent_host.py +59 -0
- loopx/authority.py +805 -0
- loopx/benchmark.py +2875 -0
- loopx/benchmark_adapters/__init__.py +1 -0
- loopx/benchmark_adapters/agentissue.py +2644 -0
- loopx/benchmark_adapters/agents_last_exam.py +3998 -0
- loopx/benchmark_adapters/edgebench.py +322 -0
- loopx/benchmark_adapters/skillsbench.py +5978 -0
- loopx/benchmark_adapters/skillsbench_acp_failure_policy.py +143 -0
- loopx/benchmark_adapters/skillsbench_acp_process.py +31 -0
- loopx/benchmark_adapters/skillsbench_acp_relay.py +4832 -0
- loopx/benchmark_adapters/skillsbench_batch.py +124 -0
- loopx/benchmark_adapters/skillsbench_bridge_guard.py +209 -0
- loopx/benchmark_adapters/skillsbench_bridge_summary.py +203 -0
- loopx/benchmark_adapters/skillsbench_codex_goal_recovery.py +271 -0
- loopx/benchmark_adapters/skillsbench_codex_goal_trace.py +81 -0
- loopx/benchmark_adapters/skillsbench_codex_runtime.py +339 -0
- loopx/benchmark_adapters/skillsbench_dockerfile_runtime.py +467 -0
- loopx/benchmark_adapters/skillsbench_failure_signals.py +652 -0
- loopx/benchmark_adapters/skillsbench_proxy_runtime.py +327 -0
- loopx/benchmark_adapters/skillsbench_remote_bridge.py +402 -0
- loopx/benchmark_adapters/skillsbench_result_discovery.py +143 -0
- loopx/benchmark_adapters/skillsbench_runner_profile.py +436 -0
- loopx/benchmark_adapters/skillsbench_runner_source.py +99 -0
- loopx/benchmark_adapters/skillsbench_setup_preflight.py +771 -0
- loopx/benchmark_adapters/skillsbench_signals.py +15 -0
- loopx/benchmark_adapters/skillsbench_task_source.py +141 -0
- loopx/benchmark_adapters/skillsbench_turn_route.py +723 -0
- loopx/benchmark_adapters/skillsbench_turn_runtime.py +1069 -0
- loopx/benchmark_adapters/skillsbench_typed_repair.py +689 -0
- loopx/benchmark_adapters/skillsbench_uv_cache.py +111 -0
- loopx/benchmark_adapters/skillsbench_verifier_bootstrap.py +227 -0
- loopx/benchmark_adapters/skillsbench_verifier_cache.py +138 -0
- loopx/benchmark_adapters/terminal_bench.py +10078 -0
- loopx/benchmark_case_analysis.py +1276 -0
- loopx/benchmark_case_state.py +1079 -0
- loopx/benchmark_core/__init__.py +239 -0
- loopx/benchmark_core/adapter.py +84 -0
- loopx/benchmark_core/artifacts.py +517 -0
- loopx/benchmark_core/attempts.py +199 -0
- loopx/benchmark_core/container_exec.py +216 -0
- loopx/benchmark_core/io.py +68 -0
- loopx/benchmark_core/lifecycle.py +211 -0
- loopx/benchmark_core/loop_protocol.py +689 -0
- loopx/benchmark_core/observable_handles.py +348 -0
- loopx/benchmark_core/parity.py +256 -0
- loopx/benchmark_core/remote_closeout.py +482 -0
- loopx/benchmark_core/rounds.py +215 -0
- loopx/benchmark_core/route_profile.py +509 -0
- loopx/benchmark_core/run_permissions.py +206 -0
- loopx/benchmark_core/split_control.py +925 -0
- loopx/benchmark_core/turn_fidelity.py +326 -0
- loopx/benchmark_ledger.py +3793 -0
- loopx/benchmark_ledger_countability.py +372 -0
- loopx/benchmark_ledger_current.py +724 -0
- loopx/benchmark_trajectory.py +405 -0
- loopx/benchmarks/__init__.py +1 -0
- loopx/benchmarks/qualification/__init__.py +1 -0
- loopx/benchmarks/qualification/release_outcome_baseline.py +360 -0
- loopx/benchmarks/read_models/__init__.py +1 -0
- loopx/benchmarks/read_models/benchmark_attempt_accounting.py +53 -0
- loopx/benchmarks/read_models/benchmark_comparison.py +414 -0
- loopx/benchmarks/read_models/benchmark_event_timeline.py +113 -0
- loopx/benchmarks/read_models/benchmark_experiment_report.py +475 -0
- loopx/benchmarks/read_models/benchmark_learning_ledger.py +137 -0
- loopx/benchmarks/read_models/benchmark_lifecycle_contracts.py +228 -0
- loopx/benchmarks/read_models/benchmark_projection.py +723 -0
- loopx/benchmarks/read_models/benchmark_result.py +146 -0
- loopx/benchmarks/read_models/benchmark_run_execution_contract.py +116 -0
- loopx/benchmarks/read_models/benchmark_run_failure.py +157 -0
- loopx/benchmarks/read_models/benchmark_run_metrics.py +213 -0
- loopx/benchmarks/read_models/benchmark_run_post_execution.py +635 -0
- loopx/benchmarks/read_models/benchmark_run_pre_execution.py +541 -0
- loopx/benchmarks/read_models/benchmark_status_compaction.py +1255 -0
- loopx/benchmarks/read_models/benchmark_status_runner.py +780 -0
- loopx/benchmarks/read_models/goal_start_control_score.py +857 -0
- loopx/benchmarks/read_models/skillsbench_post_run_debug.py +746 -0
- loopx/benchmarks/read_models/skillsbench_verifier_attribution.py +269 -0
- loopx/bootstrap.py +1116 -0
- loopx/bootstrap_command_pack.py +2167 -0
- loopx/boundary_authority.py +199 -0
- loopx/canary/__init__.py +1 -0
- loopx/canary/maintainability_ratchet.py +800 -0
- loopx/canary/planner.py +1984 -0
- loopx/canary/premerge.py +1130 -0
- loopx/canary/qualification_profiles.py +309 -0
- loopx/canary/quality_surface_catalog.py +838 -0
- loopx/canary/release_profiles.py +51 -0
- loopx/canary/runner.py +1107 -0
- loopx/canary/smoke_health.py +581 -0
- loopx/canary/smoke_profiles.py +212 -0
- loopx/capabilities/__init__.py +0 -0
- loopx/capabilities/agent_turn_recall/__init__.py +17 -0
- loopx/capabilities/agent_turn_recall/cli.py +369 -0
- loopx/capabilities/agent_turn_recall/core.py +296 -0
- loopx/capabilities/auto_research/__init__.py +16 -0
- loopx/capabilities/auto_research/bootstrap_contract.py +157 -0
- loopx/capabilities/auto_research/cli.py +1468 -0
- loopx/capabilities/auto_research/core.py +11 -0
- loopx/capabilities/auto_research/defaults.py +79 -0
- loopx/capabilities/auto_research/demo_e2e.py +1848 -0
- loopx/capabilities/auto_research/demo_supervisor.py +186 -0
- loopx/capabilities/auto_research/evidence_packet.py +767 -0
- loopx/capabilities/auto_research/human_view.py +794 -0
- loopx/capabilities/auto_research/kernel.py +191 -0
- loopx/capabilities/auto_research/knn_demo_workspace.py +322 -0
- loopx/capabilities/auto_research/live_evidence.py +248 -0
- loopx/capabilities/auto_research/preset.py +176 -0
- loopx/capabilities/auto_research/research_state.py +1085 -0
- loopx/capabilities/auto_research/role_profiles.py +394 -0
- loopx/capabilities/auto_research/rollout_append.py +97 -0
- loopx/capabilities/auto_research/terminal_result_contract.py +422 -0
- loopx/capabilities/auto_research/terminal_result_projection.py +171 -0
- loopx/capabilities/auto_research/terminal_result_query.py +233 -0
- loopx/capabilities/auto_research/terminal_results.py +349 -0
- loopx/capabilities/auto_research/user_contract.py +190 -0
- loopx/capabilities/auto_research/worker_loop.py +163 -0
- loopx/capabilities/auto_research/worker_runtime.py +777 -0
- loopx/capabilities/auto_research/worker_skill/SKILL.md +343 -0
- loopx/capabilities/benchmark_toolkit/__init__.py +19 -0
- loopx/capabilities/benchmark_toolkit/integrity.py +387 -0
- loopx/capabilities/catalog.py +1875 -0
- loopx/capabilities/change_quality/__init__.py +19 -0
- loopx/capabilities/change_quality/cli.py +171 -0
- loopx/capabilities/change_quality/context.py +156 -0
- loopx/capabilities/change_quality/oracles.py +269 -0
- loopx/capabilities/change_quality/policy.py +34 -0
- loopx/capabilities/change_quality/receipt.py +482 -0
- loopx/capabilities/change_quality/result.py +493 -0
- loopx/capabilities/change_quality/scope.py +171 -0
- loopx/capabilities/change_quality/shadow.py +680 -0
- loopx/capabilities/content_ops/__init__.py +0 -0
- loopx/capabilities/content_ops/cli.py +649 -0
- loopx/capabilities/content_ops/connector_packets.py +164 -0
- loopx/capabilities/content_ops/item_lifecycle.py +1000 -0
- loopx/capabilities/content_ops/layout.py +451 -0
- loopx/capabilities/content_ops/markdown.py +456 -0
- loopx/capabilities/content_ops/schemas.py +51 -0
- loopx/capabilities/content_ops/social_browser_x.py +107 -0
- loopx/capabilities/content_ops/surface.py +1956 -0
- loopx/capabilities/content_ops/templates/layout-catalog-v0.json +72 -0
- loopx/capabilities/context_providers/__init__.py +36 -0
- loopx/capabilities/context_providers/base.py +189 -0
- loopx/capabilities/context_providers/factory.py +32 -0
- loopx/capabilities/context_providers/openviking.py +702 -0
- loopx/capabilities/context_providers/service_ownership.py +185 -0
- loopx/capabilities/decision_context/__init__.py +129 -0
- loopx/capabilities/decision_context/architecture.py +83 -0
- loopx/capabilities/decision_context/assembler.py +849 -0
- loopx/capabilities/decision_context/catalog_entry.py +195 -0
- loopx/capabilities/decision_context/cli.py +310 -0
- loopx/capabilities/decision_context/cursor_commit.py +535 -0
- loopx/capabilities/decision_context/outcome_feedback.py +352 -0
- loopx/capabilities/decision_context/packets.py +654 -0
- loopx/capabilities/decision_context/private_state.py +189 -0
- loopx/capabilities/decision_context/profile.py +453 -0
- loopx/capabilities/decision_context/providers.py +228 -0
- loopx/capabilities/decision_context/review_settlement.py +136 -0
- loopx/capabilities/decision_context/runtime.py +273 -0
- loopx/capabilities/decision_context/sources.py +415 -0
- loopx/capabilities/explore/__init__.py +1 -0
- loopx/capabilities/explore/activation.py +198 -0
- loopx/capabilities/explore/adaptive_replay_planner.py +221 -0
- loopx/capabilities/explore/child_replay_runtime.py +463 -0
- loopx/capabilities/explore/composition_frontier.py +291 -0
- loopx/capabilities/explore/counterfactual_runtime.py +578 -0
- loopx/capabilities/explore/episode_runtime.py +647 -0
- loopx/capabilities/explore/harness_checkpoint.py +171 -0
- loopx/capabilities/explore/harness_gate.py +115 -0
- loopx/capabilities/explore/harness_runtime.py +1124 -0
- loopx/capabilities/explore/replay_metrics.py +206 -0
- loopx/capabilities/explore/replay_runtime.py +1271 -0
- loopx/capabilities/explore/resource_portfolio.py +173 -0
- loopx/capabilities/explore/result_log.py +974 -0
- loopx/capabilities/explore/router_state.py +432 -0
- loopx/capabilities/explore/source_history_reconcile.py +255 -0
- loopx/capabilities/explore/speculative_scheduler.py +498 -0
- loopx/capabilities/explore/todo_branch_plan.py +650 -0
- loopx/capabilities/explore/todo_evidence.py +141 -0
- loopx/capabilities/explore/trace_runtime.py +284 -0
- loopx/capabilities/explore/worker_branch_plan.py +1257 -0
- loopx/capabilities/integration_branch/__init__.py +13 -0
- loopx/capabilities/integration_branch/cli.py +148 -0
- loopx/capabilities/integration_branch/core.py +916 -0
- loopx/capabilities/issue_fix/__init__.py +19 -0
- loopx/capabilities/issue_fix/acceptance_loop.py +1050 -0
- loopx/capabilities/issue_fix/candidate_evidence.py +503 -0
- loopx/capabilities/issue_fix/candidate_preflight.py +676 -0
- loopx/capabilities/issue_fix/cli.py +1822 -0
- loopx/capabilities/issue_fix/cli_input.py +87 -0
- loopx/capabilities/issue_fix/content_ops_cli.py +148 -0
- loopx/capabilities/issue_fix/discovered_issue_promotion.py +947 -0
- loopx/capabilities/issue_fix/explore_projection.py +710 -0
- loopx/capabilities/issue_fix/feasibility.py +542 -0
- loopx/capabilities/issue_fix/github_public.py +661 -0
- loopx/capabilities/issue_fix/intake_surface.py +832 -0
- loopx/capabilities/issue_fix/metadata_preview.py +218 -0
- loopx/capabilities/issue_fix/metrics_projection.py +1340 -0
- loopx/capabilities/issue_fix/metrics_supplement.py +634 -0
- loopx/capabilities/issue_fix/metrics_supplement_cli.py +127 -0
- loopx/capabilities/issue_fix/outcome_projection.py +1235 -0
- loopx/capabilities/issue_fix/periodic_report.py +189 -0
- loopx/capabilities/issue_fix/pr_description.py +418 -0
- loopx/capabilities/issue_fix/pr_gate_reconcile.py +496 -0
- loopx/capabilities/issue_fix/pr_gate_reconcile_cli.py +464 -0
- loopx/capabilities/issue_fix/pr_lifecycle.py +1327 -0
- loopx/capabilities/issue_fix/pr_lifecycle_rollout.py +85 -0
- loopx/capabilities/issue_fix/pr_monitor_materialization.py +257 -0
- loopx/capabilities/issue_fix/pr_review_ack.py +439 -0
- loopx/capabilities/issue_fix/provider_hooks.py +24 -0
- loopx/capabilities/issue_fix/repository_commit_evidence.py +186 -0
- loopx/capabilities/issue_fix/repository_context.py +457 -0
- loopx/capabilities/issue_fix/repository_memory.py +459 -0
- loopx/capabilities/issue_fix/repository_memory_provider.py +1454 -0
- loopx/capabilities/issue_fix/repository_snapshot.py +454 -0
- loopx/capabilities/issue_fix/reviewer_cli.py +917 -0
- loopx/capabilities/issue_fix/reviewer_notification.py +882 -0
- loopx/capabilities/issue_fix/reviewer_notification_drain.py +942 -0
- loopx/capabilities/issue_fix/reviewer_recommendation.py +1057 -0
- loopx/capabilities/issue_fix/reviewer_request.py +1282 -0
- loopx/capabilities/issue_fix/reward_memory.py +879 -0
- loopx/capabilities/issue_fix/workflow_plan.py +1286 -0
- loopx/capabilities/material_lifecycle/__init__.py +161 -0
- loopx/capabilities/material_lifecycle/_validation.py +183 -0
- loopx/capabilities/material_lifecycle/apply.py +672 -0
- loopx/capabilities/material_lifecycle/architecture.py +122 -0
- loopx/capabilities/material_lifecycle/cli.py +161 -0
- loopx/capabilities/material_lifecycle/decision_planning.py +470 -0
- loopx/capabilities/material_lifecycle/explore_execution.py +306 -0
- loopx/capabilities/material_lifecycle/intake.py +869 -0
- loopx/capabilities/material_lifecycle/inventory.py +147 -0
- loopx/capabilities/material_lifecycle/lifecycle.py +98 -0
- loopx/capabilities/material_lifecycle/preparation.py +147 -0
- loopx/capabilities/material_lifecycle/project_skill.py +83 -0
- loopx/capabilities/material_lifecycle/ranking.py +267 -0
- loopx/capabilities/material_lifecycle/readable_projection.py +500 -0
- loopx/capabilities/material_lifecycle/rebuild.py +480 -0
- loopx/capabilities/material_lifecycle/settlement.py +238 -0
- loopx/capabilities/periodic_report/__init__.py +71 -0
- loopx/capabilities/periodic_report/adapters.py +939 -0
- loopx/capabilities/periodic_report/archive.py +422 -0
- loopx/capabilities/periodic_report/bindings.py +705 -0
- loopx/capabilities/periodic_report/cli.py +277 -0
- loopx/capabilities/periodic_report/core.py +691 -0
- loopx/capabilities/periodic_report/extension_envelope.py +66 -0
- loopx/capabilities/periodic_report/presets.py +103 -0
- loopx/capabilities/periodic_report/profile.py +235 -0
- loopx/capabilities/periodic_report/project_progress.py +179 -0
- loopx/capabilities/periodic_report/triggers.py +452 -0
- loopx/capabilities/pr_review_queue/__init__.py +17 -0
- loopx/capabilities/pr_review_queue/core.py +506 -0
- loopx/capabilities/pr_review_queue/review_contract.py +506 -0
- loopx/capabilities/registry.py +192 -0
- loopx/capabilities/reward_memory/__init__.py +75 -0
- loopx/capabilities/reward_memory/application.py +819 -0
- loopx/capabilities/reward_memory/architecture.py +572 -0
- loopx/capabilities/reward_memory/candidate_review.py +511 -0
- loopx/capabilities/reward_memory/cli.py +469 -0
- loopx/capabilities/reward_memory/dogfood.py +574 -0
- loopx/capabilities/reward_memory/evaluation.py +296 -0
- loopx/capabilities/reward_memory/evaluation_fixtures.py +362 -0
- loopx/capabilities/reward_memory/experiment.py +567 -0
- loopx/capabilities/reward_memory/health.py +222 -0
- loopx/capabilities/reward_memory/ingestion.py +519 -0
- loopx/capabilities/reward_memory/registry.py +600 -0
- loopx/capabilities/reward_memory/runtime_hooks.py +312 -0
- loopx/capabilities/reward_memory/scoped_feedback.py +173 -0
- loopx/capabilities/semantic_preference/__init__.py +12 -0
- loopx/capabilities/semantic_preference/cli.py +189 -0
- loopx/capabilities/semantic_preference/contract.py +592 -0
- loopx/capabilities/semantic_preference/reward_memory.py +62 -0
- loopx/capabilities/value_connectors/__init__.py +1 -0
- loopx/capabilities/value_connectors/cli.py +401 -0
- loopx/capabilities/value_connectors/finance_extension_migration.py +108 -0
- loopx/capabilities/value_connectors/install_check.py +147 -0
- loopx/capabilities/value_connectors/planner.py +733 -0
- loopx/capabilities/value_connectors/source_map.py +446 -0
- loopx/claude_goal_baseline.py +138 -0
- loopx/claude_goal_mode/__init__.py +23 -0
- loopx/claude_goal_mode/hooks/goal_policy.py +212 -0
- loopx/claude_goal_mode/hooks/goal_state.py +139 -0
- loopx/claude_goal_mode/mcp/loopx_mcp.py +167 -0
- loopx/claude_goal_mode/scripts/connect.py +103 -0
- loopx/claude_goal_mode/scripts/goalmode_cmd.py +241 -0
- loopx/claude_goal_mode/scripts/install.py +328 -0
- loopx/claude_goal_mode/statusline/goal_status.py +97 -0
- loopx/cli.py +836 -0
- loopx/cli_commands/__init__.py +334 -0
- loopx/cli_commands/_host_thread.py +13 -0
- loopx/cli_commands/agentissue_runner_flow.py +447 -0
- loopx/cli_commands/agents_last_exam.py +160 -0
- loopx/cli_commands/agents_last_exam_baked_input.py +302 -0
- loopx/cli_commands/agents_last_exam_host_codex.py +374 -0
- loopx/cli_commands/agents_last_exam_launch_dry_run.py +372 -0
- loopx/cli_commands/agents_last_exam_local_plan.py +322 -0
- loopx/cli_commands/agents_last_exam_runner_source.py +352 -0
- loopx/cli_commands/agents_last_exam_task_material.py +335 -0
- loopx/cli_commands/agents_last_exam_validation_gate.py +236 -0
- loopx/cli_commands/benchmark_boundary.py +499 -0
- loopx/cli_commands/benchmark_dispatch.py +161 -0
- loopx/cli_commands/benchmark_release_outcome.py +123 -0
- loopx/cli_commands/benchmark_review_lifecycle.py +1275 -0
- loopx/cli_commands/benchmark_run_ledger.py +763 -0
- loopx/cli_commands/benchmark_run_ledger_case_analysis.py +249 -0
- loopx/cli_commands/benchmark_run_ledger_classification.py +45 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance.py +486 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance_registration.py +342 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance_rendering.py +233 -0
- loopx/cli_commands/benchmark_run_ledger_parity.py +92 -0
- loopx/cli_commands/bootstrap_connect.py +238 -0
- loopx/cli_commands/canary.py +707 -0
- loopx/cli_commands/canary_release_qualification.py +79 -0
- loopx/cli_commands/capability.py +96 -0
- loopx/cli_commands/doctor.py +43 -0
- loopx/cli_commands/dreaming.py +143 -0
- loopx/cli_commands/edgebench.py +205 -0
- loopx/cli_commands/evidence_log.py +275 -0
- loopx/cli_commands/explore.py +989 -0
- loopx/cli_commands/explore_planning_commands.py +157 -0
- loopx/cli_commands/extension.py +271 -0
- loopx/cli_commands/first_run_report.py +73 -0
- loopx/cli_commands/goal_channel.py +656 -0
- loopx/cli_commands/handoff_mode.py +158 -0
- loopx/cli_commands/history.py +622 -0
- loopx/cli_commands/host_mode_plan.py +113 -0
- loopx/cli_commands/lark_inbox.py +431 -0
- loopx/cli_commands/lark_kanban.py +629 -0
- loopx/cli_commands/ml_experiment.py +321 -0
- loopx/cli_commands/multi_agent.py +211 -0
- loopx/cli_commands/opencode2_goal_worker.py +217 -0
- loopx/cli_commands/pr_review.py +167 -0
- loopx/cli_commands/presentation.py +218 -0
- loopx/cli_commands/preset.py +96 -0
- loopx/cli_commands/project.py +150 -0
- loopx/cli_commands/project_lifecycle.py +915 -0
- loopx/cli_commands/quota.py +859 -0
- loopx/cli_commands/quota_registration.py +241 -0
- loopx/cli_commands/quota_request.py +113 -0
- loopx/cli_commands/ready_score.py +110 -0
- loopx/cli_commands/registry_admin.py +975 -0
- loopx/cli_commands/registry_admin_configure.py +344 -0
- loopx/cli_commands/registry_admin_peer.py +84 -0
- loopx/cli_commands/registry_authority.py +218 -0
- loopx/cli_commands/review_batch.py +146 -0
- loopx/cli_commands/slash_commands.py +145 -0
- loopx/cli_commands/start_goal.py +251 -0
- loopx/cli_commands/starter.py +175 -0
- loopx/cli_commands/starter_bootstrap.py +179 -0
- loopx/cli_commands/starter_bootstrap_registration.py +198 -0
- loopx/cli_commands/starter_runtime_idle.py +107 -0
- loopx/cli_commands/starter_scheduler.py +207 -0
- loopx/cli_commands/starter_session_runtime.py +152 -0
- loopx/cli_commands/starter_visible_common.py +54 -0
- loopx/cli_commands/starter_visible_driver.py +161 -0
- loopx/cli_commands/starter_visible_pilot.py +278 -0
- loopx/cli_commands/status.py +867 -0
- loopx/cli_commands/status_registration.py +239 -0
- loopx/cli_commands/summary_all.py +222 -0
- loopx/cli_commands/support_control.py +809 -0
- loopx/cli_commands/support_control_registry.py +68 -0
- loopx/cli_commands/support_control_supervisor.py +289 -0
- loopx/cli_commands/task_lease.py +306 -0
- loopx/cli_commands/terminal_bench_adapter.py +717 -0
- loopx/cli_commands/terminal_bench_environment_result.py +1246 -0
- loopx/cli_commands/todo.py +940 -0
- loopx/cli_commands/todo_argument_validation.py +572 -0
- loopx/cli_commands/todo_event.py +114 -0
- loopx/cli_commands/turn.py +804 -0
- loopx/cli_commands/version.py +46 -0
- loopx/cli_commands/worker_bridge.py +659 -0
- loopx/cli_rollout.py +314 -0
- loopx/codex_cli_goal_tui.py +672 -0
- loopx/codex_cli_probe.py +1530 -0
- loopx/codex_cli_probe_markdown.py +935 -0
- loopx/codex_cli_runtime_probe.py +733 -0
- loopx/codex_cli_scheduler.py +564 -0
- loopx/codex_goal_baseline.py +620 -0
- loopx/configuration_catalog.py +617 -0
- loopx/configure_goal.py +1375 -0
- loopx/contract.py +996 -0
- loopx/control_plane/__init__.py +71 -0
- loopx/control_plane/agents/__init__.py +1 -0
- loopx/control_plane/agents/agent_lane_recommendation.py +516 -0
- loopx/control_plane/agents/agent_scope.py +1578 -0
- loopx/control_plane/agents/agent_scope_frontier.py +60 -0
- loopx/control_plane/agents/capability_gate.py +531 -0
- loopx/control_plane/agents/identity.py +140 -0
- loopx/control_plane/agents/legacy_migration.py +169 -0
- loopx/control_plane/agents/management_projection.py +658 -0
- loopx/control_plane/agents/material_frontier.py +608 -0
- loopx/control_plane/agents/material_handoff.py +156 -0
- loopx/control_plane/agents/multi_agent/__init__.py +1 -0
- loopx/control_plane/agents/multi_agent/codex_executable.py +207 -0
- loopx/control_plane/agents/multi_agent/collective_round_ledger.py +387 -0
- loopx/control_plane/agents/multi_agent/contract.py +474 -0
- loopx/control_plane/agents/multi_agent/recipe.py +110 -0
- loopx/control_plane/agents/multi_agent/role_successor.py +297 -0
- loopx/control_plane/agents/multi_agent/runtime_scripts.py +426 -0
- loopx/control_plane/agents/multi_agent/visible_launch_policy.py +149 -0
- loopx/control_plane/agents/multi_agent/visible_wake_scheduler.py +392 -0
- loopx/control_plane/agents/profile.py +216 -0
- loopx/control_plane/agents/runtime_model.py +73 -0
- loopx/control_plane/agents/subagent_activity.py +164 -0
- loopx/control_plane/agents/supervisor.py +544 -0
- loopx/control_plane/agents/supervisor_events.py +462 -0
- loopx/control_plane/agents/supervisor_inject.py +204 -0
- loopx/control_plane/agents/work_mode.py +56 -0
- loopx/control_plane/agents/workspace_guard.py +364 -0
- loopx/control_plane/effect_program.py +644 -0
- loopx/control_plane/goals/__init__.py +1 -0
- loopx/control_plane/goals/active_state_event_projection.py +103 -0
- loopx/control_plane/goals/active_state_metadata.py +47 -0
- loopx/control_plane/goals/active_state_sections.py +58 -0
- loopx/control_plane/goals/configure_goal_service.py +354 -0
- loopx/control_plane/goals/contract_health.py +132 -0
- loopx/control_plane/goals/dreaming.py +152 -0
- loopx/control_plane/goals/global_registry_health.py +199 -0
- loopx/control_plane/goals/global_registry_shadow.py +33 -0
- loopx/control_plane/goals/goal_channel.py +34 -0
- loopx/control_plane/goals/goal_channel_projection.py +560 -0
- loopx/control_plane/goals/goal_frontier/__init__.py +1917 -0
- loopx/control_plane/goals/goal_frontier/ack_policy.py +149 -0
- loopx/control_plane/goals/goal_frontier/outcome_continuity.py +437 -0
- loopx/control_plane/goals/goal_frontier/replan_rules.py +210 -0
- loopx/control_plane/goals/goal_frontier/semantic_history.py +314 -0
- loopx/control_plane/goals/goal_frontier/terminal.py +180 -0
- loopx/control_plane/goals/goal_vision.py +443 -0
- loopx/control_plane/goals/goal_vision_policy.py +36 -0
- loopx/control_plane/goals/goal_vision_state.py +62 -0
- loopx/control_plane/goals/goal_vision_wait.py +290 -0
- loopx/control_plane/goals/path_resolution.py +20 -0
- loopx/control_plane/goals/start_contract.py +206 -0
- loopx/control_plane/goals/vision_checkpoint.py +92 -0
- loopx/control_plane/handoff/__init__.py +1 -0
- loopx/control_plane/handoff/cross_runtime_impl_review.py +311 -0
- loopx/control_plane/handoff/delivery_contract.py +161 -0
- loopx/control_plane/handoff/handoff_runs.py +71 -0
- loopx/control_plane/handoff/project_handoff.py +155 -0
- loopx/control_plane/handoff/review_batch.py +463 -0
- loopx/control_plane/handoff/review_packet_context.py +216 -0
- loopx/control_plane/heartbeat/agent.py +173 -0
- loopx/control_plane/heartbeat/budget.py +66 -0
- loopx/control_plane/heartbeat/builder.py +501 -0
- loopx/control_plane/heartbeat/host.py +64 -0
- loopx/control_plane/heartbeat/rules.py +68 -0
- loopx/control_plane/heartbeat/task_body.py +759 -0
- loopx/control_plane/heartbeat/visible_goal.py +86 -0
- loopx/control_plane/projects/__init__.py +1 -0
- loopx/control_plane/projects/contract.py +25 -0
- loopx/control_plane/projects/registry.py +663 -0
- loopx/control_plane/quota/__init__.py +1 -0
- loopx/control_plane/quota/cli_projection.py +704 -0
- loopx/control_plane/quota/decision_summary.py +431 -0
- loopx/control_plane/quota/effect_program.py +152 -0
- loopx/control_plane/quota/error_codes.py +19 -0
- loopx/control_plane/quota/goal_boundary.py +464 -0
- loopx/control_plane/quota/heartbeat_receipt.py +277 -0
- loopx/control_plane/quota/heartbeat_recommendation.py +718 -0
- loopx/control_plane/quota/host_poll_receipts.py +162 -0
- loopx/control_plane/quota/live_decision.py +142 -0
- loopx/control_plane/quota/monitor_poll.py +786 -0
- loopx/control_plane/quota/policy_constants.py +40 -0
- loopx/control_plane/quota/projection_repair.py +262 -0
- loopx/control_plane/quota/recent_runs.py +210 -0
- loopx/control_plane/quota/scheduler_ack.py +490 -0
- loopx/control_plane/quota/selected_todo_projection.py +139 -0
- loopx/control_plane/quota/settlement.py +437 -0
- loopx/control_plane/quota/settlement_cli.py +246 -0
- loopx/control_plane/quota/settlement_validation.py +64 -0
- loopx/control_plane/quota/settlement_workspace_causality.py +180 -0
- loopx/control_plane/quota/should_run.py +249 -0
- loopx/control_plane/quota/should_run_packet.py +1165 -0
- loopx/control_plane/quota/should_run_prepare.py +675 -0
- loopx/control_plane/quota/slot_accounting.py +1123 -0
- loopx/control_plane/quota/spend_sources.py +11 -0
- loopx/control_plane/quota/stall_repair.py +397 -0
- loopx/control_plane/quota/states.py +29 -0
- loopx/control_plane/quota/task_orchestration.py +448 -0
- loopx/control_plane/quota/task_orchestration_admission.py +497 -0
- loopx/control_plane/quota/turn_envelope.py +889 -0
- loopx/control_plane/quota/usage_summary.py +140 -0
- loopx/control_plane/reward_memory.py +43 -0
- loopx/control_plane/runtime/__init__.py +2 -0
- loopx/control_plane/runtime/active_user_assisted_pilot.py +275 -0
- loopx/control_plane/runtime/agent_scoped_evidence_log.py +435 -0
- loopx/control_plane/runtime/decision_freshness.py +203 -0
- loopx/control_plane/runtime/event_ledger.py +197 -0
- loopx/control_plane/runtime/event_store_migration_bridge.py +196 -0
- loopx/control_plane/runtime/goal_project_route.py +70 -0
- loopx/control_plane/runtime/local_state_write_correctness.py +242 -0
- loopx/control_plane/runtime/promotion_readiness.py +152 -0
- loopx/control_plane/runtime/public_safety.py +120 -0
- loopx/control_plane/runtime/run_artifacts.py +78 -0
- loopx/control_plane/runtime/run_compaction.py +397 -0
- loopx/control_plane/runtime/run_context_retention.py +241 -0
- loopx/control_plane/runtime/run_history.py +132 -0
- loopx/control_plane/runtime/run_index_duplicates.py +205 -0
- loopx/control_plane/runtime/run_index_rebuild.py +263 -0
- loopx/control_plane/runtime/run_ingest_health.py +336 -0
- loopx/control_plane/runtime/runtime_projection_route.py +624 -0
- loopx/control_plane/runtime/runtime_projection_writer.py +98 -0
- loopx/control_plane/runtime/session_runtime.py +339 -0
- loopx/control_plane/runtime/shared_runtime_material_projection.py +332 -0
- loopx/control_plane/runtime/shared_runtime_refresh_projection.py +183 -0
- loopx/control_plane/runtime/stale_latest_run.py +90 -0
- loopx/control_plane/runtime/status_classifications.py +49 -0
- loopx/control_plane/runtime/status_projection_cache.py +235 -0
- loopx/control_plane/runtime/stride_observation.py +144 -0
- loopx/control_plane/runtime/time.py +39 -0
- loopx/control_plane/runtime/trajectory_hygiene.py +149 -0
- loopx/control_plane/runtime/validation_command.py +69 -0
- loopx/control_plane/scheduler/__init__.py +1 -0
- loopx/control_plane/scheduler/ack.py +329 -0
- loopx/control_plane/scheduler/arbitration.py +188 -0
- loopx/control_plane/scheduler/automation_liveness.py +183 -0
- loopx/control_plane/scheduler/execution_context.py +555 -0
- loopx/control_plane/scheduler/external_evidence_observation.py +428 -0
- loopx/control_plane/scheduler/monitor_display.py +143 -0
- loopx/control_plane/scheduler/monitor_poll_policy.py +161 -0
- loopx/control_plane/scheduler/monitor_poll_writeback.py +351 -0
- loopx/control_plane/scheduler/monitor_target.py +64 -0
- loopx/control_plane/scheduler/monitor_todo.py +146 -0
- loopx/control_plane/scheduler/monitor_wait.py +237 -0
- loopx/control_plane/scheduler/scheduler_hint.py +1284 -0
- loopx/control_plane/scheduler/state.py +354 -0
- loopx/control_plane/scheduler/state_transition_rules.py +179 -0
- loopx/control_plane/scheduler/time.py +10 -0
- loopx/control_plane/settlement_driver.py +293 -0
- loopx/control_plane/status/__init__.py +6 -0
- loopx/control_plane/status/active_state_projection.py +105 -0
- loopx/control_plane/status/agent_lane_projection.py +375 -0
- loopx/control_plane/status/attention_projection.py +74 -0
- loopx/control_plane/status/autonomous_replan_projection.py +103 -0
- loopx/control_plane/status/collection.py +140 -0
- loopx/control_plane/status/contract_projection.py +31 -0
- loopx/control_plane/status/dreaming_projection.py +52 -0
- loopx/control_plane/status/goal_attention_projection.py +157 -0
- loopx/control_plane/status/lifecycle_projection.py +110 -0
- loopx/control_plane/status/monitor_display_projection.py +69 -0
- loopx/control_plane/status/registry_health_projection.py +75 -0
- loopx/control_plane/status/run_projection.py +70 -0
- loopx/control_plane/status/runtime_summaries.py +161 -0
- loopx/control_plane/testing/__init__.py +1 -0
- loopx/control_plane/testing/actual_default_model_behavior_portfolio.py +1371 -0
- loopx/control_plane/testing/canary_harness.py +182 -0
- loopx/control_plane/testing/capability_monitor_repair_tool_behavior.py +674 -0
- loopx/control_plane/testing/cli_output_budget.py +807 -0
- loopx/control_plane/testing/cli_output_differential.py +250 -0
- loopx/control_plane/testing/cli_output_semantics.py +87 -0
- loopx/control_plane/testing/control_plane_composition_scenarios.py +225 -0
- loopx/control_plane/testing/decision_replay.py +268 -0
- loopx/control_plane/testing/doubao_model_behavior_actor.py +559 -0
- loopx/control_plane/testing/model_behavior_corpus.py +344 -0
- loopx/control_plane/testing/model_behavior_qualification.py +769 -0
- loopx/control_plane/testing/model_behavior_retained_cases.py +235 -0
- loopx/control_plane/testing/model_tool_behavior.py +536 -0
- loopx/control_plane/testing/onboarding_model_behavior_qualification.py +642 -0
- loopx/control_plane/testing/quota_fixtures.py +208 -0
- loopx/control_plane/testing/quota_should_run_parity.py +57 -0
- loopx/control_plane/testing/release_commit_qualification.py +671 -0
- loopx/control_plane/testing/replan_semantic_action_behavior.py +1302 -0
- loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py +527 -0
- loopx/control_plane/testing/selected_todo_tool_behavior.py +1002 -0
- loopx/control_plane/testing/terminal_settlement_tool_behavior.py +656 -0
- loopx/control_plane/todos/__init__.py +1 -0
- loopx/control_plane/todos/active_state_editing.py +296 -0
- loopx/control_plane/todos/active_state_todo_parser.py +138 -0
- loopx/control_plane/todos/active_state_todos.py +175 -0
- loopx/control_plane/todos/addition.py +103 -0
- loopx/control_plane/todos/claim_visibility.py +253 -0
- loopx/control_plane/todos/completed_archive.py +139 -0
- loopx/control_plane/todos/completion_fence.py +49 -0
- loopx/control_plane/todos/completion_policy.py +153 -0
- loopx/control_plane/todos/completion_validation.py +248 -0
- loopx/control_plane/todos/completion_validation_accountability.py +27 -0
- loopx/control_plane/todos/completion_validation_projection.py +57 -0
- loopx/control_plane/todos/contract.py +1476 -0
- loopx/control_plane/todos/decision_scope.py +554 -0
- loopx/control_plane/todos/deferred_resume.py +546 -0
- loopx/control_plane/todos/durable_completion.py +201 -0
- loopx/control_plane/todos/event_writeback.py +484 -0
- loopx/control_plane/todos/frontier_deadline.py +132 -0
- loopx/control_plane/todos/handoff_gate.py +283 -0
- loopx/control_plane/todos/handoff_mode.py +444 -0
- loopx/control_plane/todos/handoff_note.py +202 -0
- loopx/control_plane/todos/line_update.py +361 -0
- loopx/control_plane/todos/list_projection.py +205 -0
- loopx/control_plane/todos/markdown.py +199 -0
- loopx/control_plane/todos/monitor_metadata.py +88 -0
- loopx/control_plane/todos/mutation_authority.py +299 -0
- loopx/control_plane/todos/projection.py +655 -0
- loopx/control_plane/todos/quota_summary.py +1138 -0
- loopx/control_plane/todos/route_continuation.py +267 -0
- loopx/control_plane/todos/succession_warning.py +174 -0
- loopx/control_plane/todos/summary_item.py +223 -0
- loopx/control_plane/todos/text.py +30 -0
- loopx/control_plane/todos/todo_index.py +226 -0
- loopx/control_plane/todos/todo_summary.py +1458 -0
- loopx/control_plane/todos/unblock_resume.py +326 -0
- loopx/control_plane/todos/user_gate.py +263 -0
- loopx/control_plane/todos/write_hint.py +63 -0
- loopx/control_plane/todos/write_policy.py +135 -0
- loopx/control_plane/turn_driver/__init__.py +85 -0
- loopx/control_plane/turn_driver/codex_cli.py +502 -0
- loopx/control_plane/turn_driver/driver.py +355 -0
- loopx/control_plane/turn_driver/executor.py +1468 -0
- loopx/control_plane/turn_driver/loop_controller.py +669 -0
- loopx/control_plane/turn_driver/settlement.py +318 -0
- loopx/control_plane/turn_driver/transaction.py +375 -0
- loopx/control_plane/work_items/__init__.py +1 -0
- loopx/control_plane/work_items/attention_fields.py +56 -0
- loopx/control_plane/work_items/attention_item.py +77 -0
- loopx/control_plane/work_items/attention_queue.py +322 -0
- loopx/control_plane/work_items/attention_routing.py +213 -0
- loopx/control_plane/work_items/autonomous_candidates.py +135 -0
- loopx/control_plane/work_items/autonomous_replan_ack.py +276 -0
- loopx/control_plane/work_items/autonomous_replan_obligation.py +786 -0
- loopx/control_plane/work_items/backlog_hygiene.py +59 -0
- loopx/control_plane/work_items/capability_monitor_fallback.py +221 -0
- loopx/control_plane/work_items/delivery_batch_scale.py +66 -0
- loopx/control_plane/work_items/delivery_outcome.py +152 -0
- loopx/control_plane/work_items/delivery_signals.py +113 -0
- loopx/control_plane/work_items/execution_obligation.py +235 -0
- loopx/control_plane/work_items/goal_route_hint.py +320 -0
- loopx/control_plane/work_items/interaction_contract.py +1540 -0
- loopx/control_plane/work_items/issue_meta_surface.py +159 -0
- loopx/control_plane/work_items/lifecycle.py +139 -0
- loopx/control_plane/work_items/operator_inbox.py +266 -0
- loopx/control_plane/work_items/outcome_followthrough.py +69 -0
- loopx/control_plane/work_items/primary_action.py +326 -0
- loopx/control_plane/work_items/progress_observation.py +630 -0
- loopx/control_plane/work_items/project_asset.py +675 -0
- loopx/control_plane/work_items/repair_delta.py +693 -0
- loopx/control_plane/work_items/runtime_capability_reentry.py +168 -0
- loopx/control_plane/work_items/semantic_replan_writeback.py +177 -0
- loopx/control_plane/work_items/status_contract.py +49 -0
- loopx/control_plane/work_items/task_graph.py +1046 -0
- loopx/control_plane/work_items/task_lease.py +1254 -0
- loopx/control_plane/work_items/task_lease_settlement.py +422 -0
- loopx/control_plane/work_items/work_lane.py +510 -0
- loopx/control_plane/work_items/work_lane_context.py +161 -0
- loopx/demo.py +247 -0
- loopx/diagnose.py +633 -0
- loopx/doctor.py +1251 -0
- loopx/domain_packs/__init__.py +1 -0
- loopx/domain_packs/issue_fix.py +571 -0
- loopx/domain_packs/ml_experiment.py +854 -0
- loopx/domain_state.py +137 -0
- loopx/dreaming.py +706 -0
- loopx/entrypoint.py +16 -0
- loopx/event_sourced_state.py +981 -0
- loopx/execution_profile.py +286 -0
- loopx/experiments/__init__.py +1 -0
- loopx/experiments/planner_worker/__init__.py +1 -0
- loopx/experiments/planner_worker/contract.py +523 -0
- loopx/experiments/planner_worker/runtime.py +391 -0
- loopx/experiments/planner_worker/traex.py +461 -0
- loopx/explore_graph.py +11 -0
- loopx/extensions/__init__.py +1 -0
- loopx/extensions/bundled.py +28 -0
- loopx/extensions/execution_envelope.py +126 -0
- loopx/extensions/lark/__init__.py +11 -0
- loopx/extensions/lark/event_collector.py +478 -0
- loopx/extensions/lark/event_collector_runtime.py +506 -0
- loopx/extensions/lark/event_inbox.py +454 -0
- loopx/extensions/lark/extension.toml +88 -0
- loopx/extensions/lark/goal_channel.py +44 -0
- loopx/extensions/lark/goal_channel_contracts.py +388 -0
- loopx/extensions/lark/goal_channel_lifecycle.py +218 -0
- loopx/extensions/lark/goal_channel_runtime.py +792 -0
- loopx/extensions/lark/goal_channel_setup.py +805 -0
- loopx/extensions/lark/goal_channel_targets.py +215 -0
- loopx/extensions/lark/goal_channel_transport.py +281 -0
- loopx/extensions/lark/inbox_reactions.py +650 -0
- loopx/extensions/lark/inbox_reply.py +430 -0
- loopx/extensions/lark/presentation/__init__.py +11 -0
- loopx/extensions/lark/presentation/explore_results.py +2276 -0
- loopx/extensions/lark/presentation/explore_singleflight.py +127 -0
- loopx/extensions/lark/presentation/explore_source_guard.py +121 -0
- loopx/extensions/lark/presentation/explore_stage_document.py +703 -0
- loopx/extensions/lark/presentation/explore_visual_integrity.py +122 -0
- loopx/extensions/lark/presentation/explore_visual_readback.py +452 -0
- loopx/extensions/lark/presentation/explore_visual_styles.py +156 -0
- loopx/extensions/lark/presentation/issue_fix_surface.py +612 -0
- loopx/extensions/lark/presentation/kanban.py +2791 -0
- loopx/extensions/lark/presentation/message_card.py +112 -0
- loopx/extensions/lark/presentation/periodic_report.py +261 -0
- loopx/extensions/lark/presentation/projection_rows.py +600 -0
- loopx/extensions/lark/presentation/record_io.py +95 -0
- loopx/extensions/lark/presentation/sync_receipt.py +145 -0
- loopx/extensions/lark/private_json.py +40 -0
- loopx/extensions/lark/provider.py +86 -0
- loopx/extensions/lark/reviewer_notification.py +604 -0
- loopx/extensions/manifest.py +385 -0
- loopx/extensions/openviking_periodic_report/__init__.py +17 -0
- loopx/extensions/openviking_periodic_report/activation.py +173 -0
- loopx/extensions/openviking_periodic_report/extension.toml +17 -0
- loopx/extensions/openviking_periodic_report/provider.py +355 -0
- loopx/extensions/openviking_periodic_report/sink.py +117 -0
- loopx/extensions/openviking_semantic_preference/__init__.py +5 -0
- loopx/extensions/openviking_semantic_preference/extension.toml +16 -0
- loopx/extensions/openviking_semantic_preference/history_export.py +484 -0
- loopx/extensions/openviking_semantic_preference/project_peer.py +68 -0
- loopx/extensions/openviking_semantic_preference/provider.py +312 -0
- loopx/extensions/presentation.py +979 -0
- loopx/extensions/process_runtime.py +204 -0
- loopx/extensions/readiness.py +168 -0
- loopx/extensions/runtime.py +931 -0
- loopx/extensions/scaffold.py +335 -0
- loopx/feedback.py +581 -0
- loopx/file_lock.py +382 -0
- loopx/global_registry.py +842 -0
- loopx/global_risks.py +970 -0
- loopx/global_todos.py +568 -0
- loopx/handoff_budget.py +28 -0
- loopx/heartbeat_prequota.py +80 -0
- loopx/heartbeat_prompt.py +159 -0
- loopx/help_surface.py +516 -0
- loopx/history.py +1507 -0
- loopx/host_loop_activation.py +1311 -0
- loopx/host_mode_planner.py +991 -0
- loopx/install_contract.py +1 -0
- loopx/interface_budget.py +196 -0
- loopx/long_task_cadence.py +208 -0
- loopx/materials.py +185 -0
- loopx/ml_experiment.py +3 -0
- loopx/onboarding.py +214 -0
- loopx/opencode2_goal_mode/README.md +81 -0
- loopx/opencode2_goal_mode/__init__.py +9 -0
- loopx/opencode2_goal_mode/opencode2-goal-worker.mjs +1018 -0
- loopx/opencode_goal_mode/README.md +99 -0
- loopx/opencode_goal_mode/__init__.py +13 -0
- loopx/opencode_goal_mode/goal-bridge-runtime.mjs +858 -0
- loopx/opencode_goal_mode/loopx-goal.js +8 -0
- loopx/operator_gate.py +420 -0
- loopx/orchestration.py +127 -0
- loopx/paths.py +59 -0
- loopx/pi_goal_mode/README.md +67 -0
- loopx/pi_goal_mode/__init__.py +13 -0
- loopx/pi_goal_mode/loopx-goal.ts +254 -0
- loopx/pi_goal_mode/pi-goal-loop-runtime.mjs +574 -0
- loopx/pr_review.py +1206 -0
- loopx/presentation/__init__.py +1 -0
- loopx/presentation/explore_views.py +1334 -0
- loopx/presentation/markdown.py +61 -0
- loopx/presentation/projection_source_reconcile.py +140 -0
- loopx/presentation/public_safety.py +42 -0
- loopx/presentation/renderers/__init__.py +17 -0
- loopx/presentation/renderers/goal_channel_html.py +269 -0
- loopx/presentation/renderers/periodic_report_html.py +786 -0
- loopx/presentation/renderers/periodic_report_markdown.py +184 -0
- loopx/presentation/renderers/quota_event_markdown.py +116 -0
- loopx/presentation/renderers/quota_markdown.py +1112 -0
- loopx/presentation/renderers/status_markdown.py +1570 -0
- loopx/presentation/renderers/trajectory_hygiene_markdown.py +39 -0
- loopx/presentation/renderers/turn_envelope_markdown.py +33 -0
- loopx/presentation/sinks/__init__.py +5 -0
- loopx/presentation/sinks/openviking_periodic_report.py +7 -0
- loopx/presentation/static_site.py +691 -0
- loopx/presets.py +369 -0
- loopx/project_alias.py +217 -0
- loopx/project_map.py +589 -0
- loopx/project_prompt.py +1153 -0
- loopx/project_skill_cli.py +125 -0
- loopx/project_skill_delivery.py +470 -0
- loopx/project_uninstall.py +462 -0
- loopx/promotion_gate.py +197 -0
- loopx/quota.py +1197 -0
- loopx/ready_score.py +413 -0
- loopx/registry.py +621 -0
- loopx/registry_writability.py +64 -0
- loopx/release_candidate.py +148 -0
- loopx/release_manifest.py +316 -0
- loopx/repository_identity.py +100 -0
- loopx/review_packet.py +1024 -0
- loopx/rollout_event_log.py +505 -0
- loopx/runtime.py +112 -0
- loopx/self_update.py +750 -0
- loopx/session_runtime.py +418 -0
- loopx/skill_install_readback.py +500 -0
- loopx/slash_command_install.py +1393 -0
- loopx/slash_commands.py +264 -0
- loopx/state_backup.py +573 -0
- loopx/state_migration.py +350 -0
- loopx/state_projection.py +809 -0
- loopx/state_refresh.py +1416 -0
- loopx/status.py +1383 -0
- loopx/status_server.py +935 -0
- loopx/summary_all.py +725 -0
- loopx/terminal_bench_agent.py +2056 -0
- loopx/thread_agent_binding.py +408 -0
- loopx/todo_followups.py +168 -0
- loopx/todo_suggestion_prompt.py +204 -0
- loopx/todos.py +2229 -0
- loopx/turn_identity.py +17 -0
- loopx/upgrade.py +1083 -0
- loopx/visible_governance.py +667 -0
- loopx/visible_multi_agent_launcher.py +1253 -0
- loopx/visible_multi_agent_tmux.py +429 -0
- loopx/worker_bridge.py +1574 -0
- loopx-0.4.8.dist-info/METADATA +708 -0
- loopx-0.4.8.dist-info/RECORD +811 -0
- loopx-0.4.8.dist-info/WHEEL +5 -0
- loopx-0.4.8.dist-info/entry_points.txt +5 -0
- loopx-0.4.8.dist-info/licenses/LICENSE +202 -0
- loopx-0.4.8.dist-info/licenses/LICENSE-MIT +21 -0
- loopx-0.4.8.dist-info/licenses/NOTICE +6 -0
- 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
|