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,669 @@
|
|
|
1
|
+
"""Pure Turn Loop Controller transition contract.
|
|
2
|
+
|
|
3
|
+
This module decides the next disposition of a governed loop from one validated
|
|
4
|
+
Turn receipt plus a fresh quota/scheduler decision. It is a pure function: it
|
|
5
|
+
never invokes a model, sleeps, mutates a host scheduler, writes state, or
|
|
6
|
+
spends quota. `loopx turn run-once` remains the only delivery transaction;
|
|
7
|
+
scheduler process management, host wake APIs, and operator presentation belong
|
|
8
|
+
to later adapters (see the Turn Loop Controller plan in CONTRIBUTOR_TASKS).
|
|
9
|
+
|
|
10
|
+
The transition output space is exactly six dispositions:
|
|
11
|
+
``run_now | wait | user_action_required | repair | replan | terminal``.
|
|
12
|
+
|
|
13
|
+
Input validity is enforced at the typed-input boundary, not encoded as a
|
|
14
|
+
seventh disposition. ``decide_loop_disposition`` raises ``ValueError`` when a
|
|
15
|
+
receipt, envelope, or budget cannot be proven against the shared Turn
|
|
16
|
+
contracts, so the caller is responsible for feeding only validated, fresh
|
|
17
|
+
inputs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from collections.abc import Mapping
|
|
23
|
+
from enum import Enum
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from ..effect_program import SettlementStepKind
|
|
27
|
+
from .driver import LoopXTurnRoute, _typed_route, selected_turn_todo
|
|
28
|
+
from .transaction import (
|
|
29
|
+
LOOPX_TURN_EXECUTION_SCHEMA_VERSION,
|
|
30
|
+
LOOPX_TURN_RECEIPT_VALIDATION_SCHEMA_VERSION,
|
|
31
|
+
LoopXTurnResultKind,
|
|
32
|
+
require_loopx_turn_completion_outcome,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
LOOP_CONTROLLER_DISPOSITION_SCHEMA_VERSION = "loop_turn_loop_disposition_v0"
|
|
36
|
+
BOUNDED_TURN_BUDGET_SCHEMA_VERSION = "loop_bounded_turn_budget_v0"
|
|
37
|
+
VALIDATED_TURN_RECEIPT_SCHEMA_VERSION = "loop_validated_turn_receipt_v0"
|
|
38
|
+
|
|
39
|
+
# Material result kinds that prove a durable delivery effect and may therefore
|
|
40
|
+
# drive a terminal or progress transition. They require a fully committed
|
|
41
|
+
# transaction (all seven phases), not a validation-only intermediate.
|
|
42
|
+
_MATERIAL_PROGRESS_KINDS = {
|
|
43
|
+
LoopXTurnResultKind.VALIDATED_COMPLETION,
|
|
44
|
+
LoopXTurnResultKind.VALIDATED_PROGRESS,
|
|
45
|
+
}
|
|
46
|
+
_MATERIAL_RECEIPT_ORDER = (
|
|
47
|
+
SettlementStepKind.VALIDATION.value,
|
|
48
|
+
SettlementStepKind.DURABLE_WRITEBACK.value,
|
|
49
|
+
SettlementStepKind.QUOTA_SPEND.value,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class LoopDisposition(str, Enum):
|
|
54
|
+
RUN_NOW = "run_now"
|
|
55
|
+
WAIT = "wait"
|
|
56
|
+
USER_ACTION_REQUIRED = "user_action_required"
|
|
57
|
+
REPAIR = "repair"
|
|
58
|
+
REPLAN = "replan"
|
|
59
|
+
TERMINAL = "terminal"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _mapping(value: Any) -> dict[str, Any]:
|
|
63
|
+
return dict(value) if isinstance(value, Mapping) else {}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _disposition(
|
|
67
|
+
disposition: LoopDisposition,
|
|
68
|
+
*,
|
|
69
|
+
reason: str,
|
|
70
|
+
lineage: Mapping[str, str] | None = None,
|
|
71
|
+
extra: Mapping[str, Any] | None = None,
|
|
72
|
+
) -> dict[str, Any]:
|
|
73
|
+
payload: dict[str, Any] = {
|
|
74
|
+
"schema_version": LOOP_CONTROLLER_DISPOSITION_SCHEMA_VERSION,
|
|
75
|
+
"disposition": disposition.value,
|
|
76
|
+
"reason": reason,
|
|
77
|
+
"spends_quota": False,
|
|
78
|
+
"launches_host": False,
|
|
79
|
+
"writes_state": False,
|
|
80
|
+
}
|
|
81
|
+
if lineage:
|
|
82
|
+
payload["lineage"] = dict(lineage)
|
|
83
|
+
if extra:
|
|
84
|
+
payload.update(dict(extra))
|
|
85
|
+
return payload
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _decision_lineage(decision: Mapping[str, Any]) -> dict[str, str]:
|
|
89
|
+
selected_todo = selected_turn_todo(decision)
|
|
90
|
+
return {
|
|
91
|
+
"goal_id": str(decision.get("goal_id") or ""),
|
|
92
|
+
"agent_id": str(decision.get("agent_id") or ""),
|
|
93
|
+
"todo_id": str(selected_todo.get("todo_id") or ""),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _envelope_route(decision: Mapping[str, Any]) -> LoopXTurnRoute:
|
|
98
|
+
"""Return the shared typed route for a fresh quota/scheduler decision.
|
|
99
|
+
|
|
100
|
+
Reuses the Turn plan driver's ``_typed_route`` contract, which requires a
|
|
101
|
+
matching action signature with non-empty equal hashes and an in-budget
|
|
102
|
+
compaction. A projected user action outranks delivery, so it is resolved
|
|
103
|
+
before the typed delivery route. Raises ``ValueError`` when the envelope
|
|
104
|
+
fails the shared contract instead of accepting a forged or truncated
|
|
105
|
+
decision.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
route = _typed_route(decision)
|
|
109
|
+
if route is LoopXTurnRoute.CONTRACT_ERROR:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
"quota decision failed the shared envelope contract "
|
|
112
|
+
"(schema, signature hashes, or compaction budget)"
|
|
113
|
+
)
|
|
114
|
+
user = _mapping(decision.get("user"))
|
|
115
|
+
if user.get("action_required") is True:
|
|
116
|
+
return LoopXTurnRoute.USER_ACTION_REQUIRED
|
|
117
|
+
return route
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _route_to_disposition(route: LoopXTurnRoute) -> LoopDisposition:
|
|
121
|
+
return {
|
|
122
|
+
LoopXTurnRoute.READY_FOR_HOST: LoopDisposition.RUN_NOW,
|
|
123
|
+
LoopXTurnRoute.REPLAN_REQUIRED: LoopDisposition.REPLAN,
|
|
124
|
+
LoopXTurnRoute.REPAIR_REQUIRED: LoopDisposition.REPAIR,
|
|
125
|
+
LoopXTurnRoute.USER_ACTION_REQUIRED: LoopDisposition.USER_ACTION_REQUIRED,
|
|
126
|
+
LoopXTurnRoute.WAIT: LoopDisposition.WAIT,
|
|
127
|
+
LoopXTurnRoute.BLOCKED: LoopDisposition.WAIT,
|
|
128
|
+
}[route]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ValidatedTurnReceipt:
|
|
132
|
+
"""A controller view qualified from one complete Turn execution.
|
|
133
|
+
|
|
134
|
+
Material results are admitted only when the public execution proves the
|
|
135
|
+
M7 typed settlement receipt sequence, one stable effect identity, durable
|
|
136
|
+
writeback and spend, and a completed scheduler handoff. Todo completion
|
|
137
|
+
additionally retains its durable continuation outcome so a local Todo
|
|
138
|
+
completion cannot be confused with Goal terminal closure.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
__slots__ = (
|
|
142
|
+
"lineage",
|
|
143
|
+
"result_kind",
|
|
144
|
+
"settlement_effect_id",
|
|
145
|
+
"status",
|
|
146
|
+
"todo_completion",
|
|
147
|
+
"turn_key",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def __init__(
|
|
151
|
+
self,
|
|
152
|
+
*,
|
|
153
|
+
result_kind: LoopXTurnResultKind,
|
|
154
|
+
status: str,
|
|
155
|
+
lineage: Mapping[str, str],
|
|
156
|
+
turn_key: str | None,
|
|
157
|
+
settlement_effect_id: str | None = None,
|
|
158
|
+
todo_completion: Mapping[str, Any] | None = None,
|
|
159
|
+
) -> None:
|
|
160
|
+
self.result_kind = result_kind
|
|
161
|
+
self.status = status
|
|
162
|
+
self.lineage = {
|
|
163
|
+
"goal_id": str(lineage.get("goal_id") or ""),
|
|
164
|
+
"agent_id": str(lineage.get("agent_id") or ""),
|
|
165
|
+
"todo_id": str(lineage.get("todo_id") or ""),
|
|
166
|
+
}
|
|
167
|
+
self.turn_key = turn_key
|
|
168
|
+
self.settlement_effect_id = settlement_effect_id
|
|
169
|
+
self.todo_completion = dict(todo_completion or {}) or None
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def from_execution(
|
|
173
|
+
cls, execution_payload: Mapping[str, Any]
|
|
174
|
+
) -> ValidatedTurnReceipt:
|
|
175
|
+
execution = _mapping(execution_payload)
|
|
176
|
+
if execution.get("schema_version") != LOOPX_TURN_EXECUTION_SCHEMA_VERSION:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
"validated turn receipt requires schema_version="
|
|
179
|
+
f"{LOOPX_TURN_EXECUTION_SCHEMA_VERSION}"
|
|
180
|
+
)
|
|
181
|
+
receipt = _mapping(execution.get("receipt"))
|
|
182
|
+
if receipt.get("schema_version") != LOOPX_TURN_RECEIPT_VALIDATION_SCHEMA_VERSION:
|
|
183
|
+
raise ValueError(
|
|
184
|
+
"validated turn receipt requires schema_version="
|
|
185
|
+
f"{LOOPX_TURN_RECEIPT_VALIDATION_SCHEMA_VERSION}"
|
|
186
|
+
)
|
|
187
|
+
if receipt.get("ok") is not True:
|
|
188
|
+
raise ValueError("validated turn receipt requires ok=true")
|
|
189
|
+
raw_kind = receipt.get("result_kind")
|
|
190
|
+
try:
|
|
191
|
+
result_kind = LoopXTurnResultKind(str(raw_kind or ""))
|
|
192
|
+
except ValueError:
|
|
193
|
+
raise ValueError(
|
|
194
|
+
f"validated turn receipt has unsupported result_kind {raw_kind!r}"
|
|
195
|
+
) from None
|
|
196
|
+
if execution.get("result_kind") != result_kind.value:
|
|
197
|
+
raise ValueError("execution result_kind does not match its receipt")
|
|
198
|
+
status = str(receipt.get("status") or "")
|
|
199
|
+
if result_kind in _MATERIAL_PROGRESS_KINDS and (
|
|
200
|
+
status != "committed" or execution.get("status") != "committed"
|
|
201
|
+
):
|
|
202
|
+
raise ValueError(
|
|
203
|
+
f"material result {result_kind.value} requires a fully committed "
|
|
204
|
+
"Turn execution and receipt"
|
|
205
|
+
)
|
|
206
|
+
lineage = _mapping(receipt.get("lineage"))
|
|
207
|
+
if not all(lineage.get(k) for k in ("goal_id", "agent_id", "todo_id")):
|
|
208
|
+
raise ValueError(
|
|
209
|
+
"validated turn receipt is missing goal/agent/todo lineage"
|
|
210
|
+
)
|
|
211
|
+
turn_key = str(receipt.get("turn_key") or "") or None
|
|
212
|
+
if not turn_key:
|
|
213
|
+
raise ValueError("validated turn receipt is missing turn_key")
|
|
214
|
+
settlement_effect_id = str(receipt.get("settlement_effect_id") or "") or None
|
|
215
|
+
todo_completion = _mapping(execution.get("todo_completion"))
|
|
216
|
+
if result_kind in _MATERIAL_PROGRESS_KINDS:
|
|
217
|
+
settlement_effect_id = _qualified_material_effect_id(
|
|
218
|
+
execution,
|
|
219
|
+
expected_effect_id=settlement_effect_id,
|
|
220
|
+
)
|
|
221
|
+
if result_kind is LoopXTurnResultKind.VALIDATED_COMPLETION:
|
|
222
|
+
todo_completion = require_loopx_turn_completion_outcome(
|
|
223
|
+
todo_completion,
|
|
224
|
+
expected_todo_id=str(lineage["todo_id"]),
|
|
225
|
+
)
|
|
226
|
+
return cls(
|
|
227
|
+
result_kind=result_kind,
|
|
228
|
+
status=status,
|
|
229
|
+
lineage=lineage,
|
|
230
|
+
turn_key=turn_key,
|
|
231
|
+
settlement_effect_id=settlement_effect_id,
|
|
232
|
+
todo_completion=todo_completion,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def to_mapping(self) -> dict[str, Any]:
|
|
236
|
+
return {
|
|
237
|
+
"schema_version": VALIDATED_TURN_RECEIPT_SCHEMA_VERSION,
|
|
238
|
+
"result_kind": self.result_kind.value,
|
|
239
|
+
"status": self.status,
|
|
240
|
+
"lineage": dict(self.lineage),
|
|
241
|
+
"turn_key": self.turn_key,
|
|
242
|
+
"settlement_effect_id": self.settlement_effect_id,
|
|
243
|
+
**(
|
|
244
|
+
{"todo_completion": dict(self.todo_completion)}
|
|
245
|
+
if self.todo_completion
|
|
246
|
+
else {}
|
|
247
|
+
),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _qualified_material_effect_id(
|
|
252
|
+
execution: Mapping[str, Any],
|
|
253
|
+
*,
|
|
254
|
+
expected_effect_id: str | None,
|
|
255
|
+
) -> str:
|
|
256
|
+
settlement = _mapping(execution.get("settlement_result"))
|
|
257
|
+
if settlement.get("ok") is not True or settlement.get("failure") is not None:
|
|
258
|
+
raise ValueError("material Turn execution requires a successful settlement")
|
|
259
|
+
raw_receipts = settlement.get("receipts")
|
|
260
|
+
if not isinstance(raw_receipts, list):
|
|
261
|
+
raise TypeError("material Turn execution is missing settlement receipts")
|
|
262
|
+
receipts = [_mapping(item) for item in raw_receipts]
|
|
263
|
+
if [str(item.get("step_kind") or "") for item in receipts] != list(
|
|
264
|
+
_MATERIAL_RECEIPT_ORDER
|
|
265
|
+
):
|
|
266
|
+
raise ValueError(
|
|
267
|
+
"material Turn execution requires ordered validation/writeback/spend receipts"
|
|
268
|
+
)
|
|
269
|
+
effect_ids = {str(item.get("effect_id") or "") for item in receipts}
|
|
270
|
+
if "" in effect_ids or len(effect_ids) != 1:
|
|
271
|
+
raise ValueError("material Turn settlement receipts must share one effect identity")
|
|
272
|
+
effect_id = next(iter(effect_ids))
|
|
273
|
+
if not expected_effect_id or expected_effect_id != effect_id:
|
|
274
|
+
raise ValueError(
|
|
275
|
+
"material Turn settlement identity does not match the transaction receipt"
|
|
276
|
+
)
|
|
277
|
+
if any(item.get("status") != "committed" for item in receipts):
|
|
278
|
+
raise ValueError("material Turn settlement receipts must be committed")
|
|
279
|
+
effects = _mapping(execution.get("effects"))
|
|
280
|
+
if effects.get("state_written") is not True or effects.get("quota_spent") is not True:
|
|
281
|
+
raise ValueError("material Turn execution is missing durable effect evidence")
|
|
282
|
+
scheduler = _mapping(execution.get("scheduler"))
|
|
283
|
+
if scheduler.get("completed") is not True:
|
|
284
|
+
raise ValueError("material Turn execution is missing completed scheduler handoff")
|
|
285
|
+
return effect_id
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class BoundedTurnBudget:
|
|
289
|
+
"""A provider-neutral bounded Turn budget tied to one lineage.
|
|
290
|
+
|
|
291
|
+
Construction enforces strict integer domains (booleans are rejected) and a
|
|
292
|
+
sane range, so the transition never guesses an unbounded continuation.
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
__slots__ = ("completed_turns", "lineage", "max_turns")
|
|
296
|
+
|
|
297
|
+
def __init__(
|
|
298
|
+
self,
|
|
299
|
+
*,
|
|
300
|
+
lineage: Mapping[str, str],
|
|
301
|
+
max_turns: int,
|
|
302
|
+
completed_turns: int,
|
|
303
|
+
) -> None:
|
|
304
|
+
if type(max_turns) is not int:
|
|
305
|
+
raise ValueError("bounded turn budget max_turns must be an int")
|
|
306
|
+
if type(completed_turns) is not int:
|
|
307
|
+
raise ValueError("bounded turn budget completed_turns must be an int")
|
|
308
|
+
if max_turns <= 0:
|
|
309
|
+
raise ValueError("bounded turn budget max_turns must be > 0")
|
|
310
|
+
if completed_turns < 0:
|
|
311
|
+
raise ValueError("bounded turn budget completed_turns must be >= 0")
|
|
312
|
+
if completed_turns > max_turns:
|
|
313
|
+
raise ValueError(
|
|
314
|
+
"bounded turn budget completed_turns must be <= max_turns"
|
|
315
|
+
)
|
|
316
|
+
self.lineage = {
|
|
317
|
+
"goal_id": str(lineage.get("goal_id") or ""),
|
|
318
|
+
"agent_id": str(lineage.get("agent_id") or ""),
|
|
319
|
+
"todo_id": str(lineage.get("todo_id") or ""),
|
|
320
|
+
}
|
|
321
|
+
if not all(self.lineage.values()):
|
|
322
|
+
raise ValueError("bounded turn budget is missing goal/agent/todo lineage")
|
|
323
|
+
self.max_turns = max_turns
|
|
324
|
+
self.completed_turns = completed_turns
|
|
325
|
+
|
|
326
|
+
def to_mapping(self) -> dict[str, Any]:
|
|
327
|
+
return {
|
|
328
|
+
"schema_version": BOUNDED_TURN_BUDGET_SCHEMA_VERSION,
|
|
329
|
+
"lineage": dict(self.lineage),
|
|
330
|
+
"max_turns": self.max_turns,
|
|
331
|
+
"completed_turns": self.completed_turns,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def remaining(self) -> int:
|
|
336
|
+
return self.max_turns - self.completed_turns
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _assert_goal_agent_match(
|
|
340
|
+
*,
|
|
341
|
+
receipt_lineage: Mapping[str, str],
|
|
342
|
+
decision_lineage: Mapping[str, str],
|
|
343
|
+
) -> None:
|
|
344
|
+
for key in ("goal_id", "agent_id"):
|
|
345
|
+
if receipt_lineage.get(key) != decision_lineage.get(key):
|
|
346
|
+
raise ValueError(
|
|
347
|
+
"stale_receipt: receipt lineage does not match the fresh decision "
|
|
348
|
+
f"on {key} (receipt={receipt_lineage.get(key)!r}, "
|
|
349
|
+
f"decision={decision_lineage.get(key)!r})"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _assert_same_todo(
|
|
354
|
+
*,
|
|
355
|
+
receipt_lineage: Mapping[str, str],
|
|
356
|
+
decision_lineage: Mapping[str, str],
|
|
357
|
+
) -> None:
|
|
358
|
+
if receipt_lineage.get("todo_id") != decision_lineage.get("todo_id"):
|
|
359
|
+
raise ValueError(
|
|
360
|
+
"stale_receipt: receipt Todo does not match the fresh decision "
|
|
361
|
+
f"(receipt={receipt_lineage.get('todo_id')!r}, "
|
|
362
|
+
f"decision={decision_lineage.get('todo_id')!r})"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _assert_budget_lineage_match(
|
|
367
|
+
*,
|
|
368
|
+
budget_lineage: Mapping[str, str],
|
|
369
|
+
decision_lineage: Mapping[str, str],
|
|
370
|
+
) -> None:
|
|
371
|
+
for key in ("goal_id", "agent_id", "todo_id"):
|
|
372
|
+
if budget_lineage.get(key) != decision_lineage.get(key):
|
|
373
|
+
raise ValueError(
|
|
374
|
+
"stale_budget: budget lineage does not match the fresh decision "
|
|
375
|
+
f"on {key} (budget={budget_lineage.get(key)!r}, "
|
|
376
|
+
f"decision={decision_lineage.get(key)!r})"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def _assert_predecessor_binding(
|
|
381
|
+
*,
|
|
382
|
+
receipt: ValidatedTurnReceipt,
|
|
383
|
+
predecessor_turn_key: str | None,
|
|
384
|
+
) -> None:
|
|
385
|
+
"""Prove the fresh envelope causally succeeds the receipt.
|
|
386
|
+
|
|
387
|
+
The outer adapter must bind the fresh decision to the receipt's
|
|
388
|
+
``turn_key``. The causal assertion stays beside the envelope instead of
|
|
389
|
+
adding an unsigned field to ``loopx_turn_envelope_v0``.
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
predecessor = str(predecessor_turn_key or "")
|
|
393
|
+
if not predecessor:
|
|
394
|
+
raise ValueError(
|
|
395
|
+
"stale_receipt: continuation is missing predecessor_turn_key; "
|
|
396
|
+
"cannot prove causal succession from the receipt"
|
|
397
|
+
)
|
|
398
|
+
if predecessor != receipt.turn_key:
|
|
399
|
+
raise ValueError(
|
|
400
|
+
"stale_receipt: fresh decision predecessor_turn_key "
|
|
401
|
+
f"({predecessor!r}) does not match the receipt turn_key "
|
|
402
|
+
f"({receipt.turn_key!r})"
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _completion_disposition(
|
|
407
|
+
*,
|
|
408
|
+
receipt: ValidatedTurnReceipt,
|
|
409
|
+
decision: Mapping[str, Any],
|
|
410
|
+
decision_lineage: Mapping[str, str],
|
|
411
|
+
route: LoopXTurnRoute,
|
|
412
|
+
) -> dict[str, Any]:
|
|
413
|
+
completion = dict(receipt.todo_completion or {})
|
|
414
|
+
continuation = str(completion.get("continuation") or "")
|
|
415
|
+
if continuation == "no_followup":
|
|
416
|
+
if (
|
|
417
|
+
decision.get("effective_action") != "terminal_no_followup"
|
|
418
|
+
or decision.get("state") != "terminal_no_followup"
|
|
419
|
+
):
|
|
420
|
+
raise ValueError(
|
|
421
|
+
"no_followup completion requires fresh terminal Goal frontier evidence"
|
|
422
|
+
)
|
|
423
|
+
return _disposition(
|
|
424
|
+
LoopDisposition.TERMINAL,
|
|
425
|
+
reason="durable no-follow-up and fresh Goal frontier prove terminal closure",
|
|
426
|
+
lineage=receipt.lineage,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
selected_todo_id = str(decision_lineage.get("todo_id") or "")
|
|
430
|
+
if not selected_todo_id:
|
|
431
|
+
raise ValueError("Todo completion continuation requires a fresh selected Todo")
|
|
432
|
+
if continuation == "successor":
|
|
433
|
+
successor_ids = completion.get("successor_todo_ids")
|
|
434
|
+
if not isinstance(successor_ids, list) or selected_todo_id not in successor_ids:
|
|
435
|
+
raise ValueError(
|
|
436
|
+
"stale_receipt: fresh decision is not a declared completion successor"
|
|
437
|
+
)
|
|
438
|
+
elif continuation == "active_goal" and selected_todo_id == receipt.lineage["todo_id"]:
|
|
439
|
+
raise ValueError(
|
|
440
|
+
"stale_receipt: active Goal continuation reselected the completed Todo"
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
if route is LoopXTurnRoute.USER_ACTION_REQUIRED:
|
|
444
|
+
return _disposition(
|
|
445
|
+
LoopDisposition.USER_ACTION_REQUIRED,
|
|
446
|
+
reason="fresh decision projects a concrete user action after Todo completion",
|
|
447
|
+
lineage=decision_lineage,
|
|
448
|
+
)
|
|
449
|
+
disposition = _route_to_disposition(route)
|
|
450
|
+
if disposition is LoopDisposition.REPLAN:
|
|
451
|
+
return _replan_disposition(
|
|
452
|
+
reason="fresh decision requires replan after Todo completion",
|
|
453
|
+
decision_lineage=decision_lineage,
|
|
454
|
+
)
|
|
455
|
+
return _disposition(
|
|
456
|
+
disposition,
|
|
457
|
+
reason=(
|
|
458
|
+
"durable Todo completion continues through its declared successor"
|
|
459
|
+
if continuation == "successor"
|
|
460
|
+
else "durable Todo completion continues through the fresh Goal frontier"
|
|
461
|
+
),
|
|
462
|
+
lineage=decision_lineage,
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def decide_loop_disposition(
|
|
467
|
+
*,
|
|
468
|
+
turn_receipt: ValidatedTurnReceipt | None,
|
|
469
|
+
quota_decision: Mapping[str, Any],
|
|
470
|
+
predecessor_turn_key: str | None = None,
|
|
471
|
+
bounded_turn_budget: BoundedTurnBudget | None = None,
|
|
472
|
+
) -> dict[str, Any]:
|
|
473
|
+
"""Decide the next loop disposition from one validated receipt and a fresh decision.
|
|
474
|
+
|
|
475
|
+
``turn_receipt`` is a :class:`ValidatedTurnReceipt` (or ``None`` when no
|
|
476
|
+
prior Turn has committed). ``quota_decision`` is a fresh
|
|
477
|
+
``loopx_turn_envelope_v0``. The outer adapter supplies a separate
|
|
478
|
+
``predecessor_turn_key`` matching the receipt when one is present; this
|
|
479
|
+
keeps causal continuation out of the signed quota envelope schema.
|
|
480
|
+
``bounded_turn_budget`` is required for ``validated_progress``.
|
|
481
|
+
|
|
482
|
+
The function is pure: it launches no host, writes no state, and spends no
|
|
483
|
+
quota. Invalid or stale input raises ``ValueError`` at the typed-input
|
|
484
|
+
boundary; the transition output space is always one of the six
|
|
485
|
+
:class:`LoopDisposition` values.
|
|
486
|
+
"""
|
|
487
|
+
|
|
488
|
+
route = _envelope_route(quota_decision)
|
|
489
|
+
decision_lineage = _decision_lineage(quota_decision)
|
|
490
|
+
if not decision_lineage["goal_id"] or not decision_lineage["agent_id"]:
|
|
491
|
+
raise ValueError(
|
|
492
|
+
"fresh quota decision is missing goal/agent lineage"
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
if turn_receipt is None:
|
|
496
|
+
if str(quota_decision.get("effective_action") or "") == "terminal_no_followup":
|
|
497
|
+
if quota_decision.get("state") != "terminal_no_followup":
|
|
498
|
+
raise ValueError(
|
|
499
|
+
"terminal no-follow-up requires fresh Goal frontier state"
|
|
500
|
+
)
|
|
501
|
+
return _disposition(
|
|
502
|
+
LoopDisposition.TERMINAL,
|
|
503
|
+
reason="fresh Goal frontier proves terminal no-follow-up",
|
|
504
|
+
lineage=decision_lineage,
|
|
505
|
+
)
|
|
506
|
+
disposition = _route_to_disposition(route)
|
|
507
|
+
if not decision_lineage["todo_id"] and disposition not in {
|
|
508
|
+
LoopDisposition.WAIT,
|
|
509
|
+
LoopDisposition.USER_ACTION_REQUIRED,
|
|
510
|
+
}:
|
|
511
|
+
raise ValueError("executable quota decision is missing selected Todo lineage")
|
|
512
|
+
if disposition is LoopDisposition.REPLAN:
|
|
513
|
+
return _replan_disposition(
|
|
514
|
+
reason="fresh decision requires replan",
|
|
515
|
+
decision_lineage=decision_lineage,
|
|
516
|
+
)
|
|
517
|
+
return _disposition(
|
|
518
|
+
disposition,
|
|
519
|
+
reason=_no_receipt_reason(disposition),
|
|
520
|
+
lineage=decision_lineage,
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# Prove the envelope causally succeeds this exact receipt before any
|
|
524
|
+
# disposition is considered. This closes the stale-replay gap: an old
|
|
525
|
+
# receipt for the same todo cannot be replayed against a later envelope.
|
|
526
|
+
_assert_predecessor_binding(
|
|
527
|
+
receipt=turn_receipt,
|
|
528
|
+
predecessor_turn_key=predecessor_turn_key,
|
|
529
|
+
)
|
|
530
|
+
_assert_goal_agent_match(
|
|
531
|
+
receipt_lineage=turn_receipt.lineage,
|
|
532
|
+
decision_lineage=decision_lineage,
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
result_kind = turn_receipt.result_kind
|
|
536
|
+
|
|
537
|
+
if result_kind is LoopXTurnResultKind.VALIDATED_COMPLETION:
|
|
538
|
+
return _completion_disposition(
|
|
539
|
+
receipt=turn_receipt,
|
|
540
|
+
decision=quota_decision,
|
|
541
|
+
decision_lineage=decision_lineage,
|
|
542
|
+
route=route,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
effective_lineage = dict(decision_lineage)
|
|
546
|
+
if decision_lineage["todo_id"]:
|
|
547
|
+
_assert_same_todo(
|
|
548
|
+
receipt_lineage=turn_receipt.lineage,
|
|
549
|
+
decision_lineage=decision_lineage,
|
|
550
|
+
)
|
|
551
|
+
elif (
|
|
552
|
+
result_kind is LoopXTurnResultKind.VALIDATED_PROGRESS
|
|
553
|
+
and route
|
|
554
|
+
not in {LoopXTurnRoute.USER_ACTION_REQUIRED, LoopXTurnRoute.WAIT}
|
|
555
|
+
):
|
|
556
|
+
raise ValueError("receipt-backed executable decision is missing selected Todo lineage")
|
|
557
|
+
else:
|
|
558
|
+
# A valid user gate or quiet wait may intentionally have no runnable
|
|
559
|
+
# selected Todo. Preserve the committed predecessor lineage for that
|
|
560
|
+
# no-spend disposition instead of rejecting the envelope.
|
|
561
|
+
effective_lineage["todo_id"] = turn_receipt.lineage["todo_id"]
|
|
562
|
+
|
|
563
|
+
# Decision user action outranks every non-terminal disposition.
|
|
564
|
+
if route is LoopXTurnRoute.USER_ACTION_REQUIRED:
|
|
565
|
+
return _disposition(
|
|
566
|
+
LoopDisposition.USER_ACTION_REQUIRED,
|
|
567
|
+
reason="fresh decision projects a concrete user action",
|
|
568
|
+
lineage=effective_lineage,
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
if result_kind is LoopXTurnResultKind.VALIDATED_PROGRESS:
|
|
572
|
+
if bounded_turn_budget is None:
|
|
573
|
+
raise ValueError(
|
|
574
|
+
"validated progress cannot continue without a proven bounded turn budget"
|
|
575
|
+
)
|
|
576
|
+
_assert_budget_lineage_match(
|
|
577
|
+
budget_lineage=bounded_turn_budget.lineage,
|
|
578
|
+
decision_lineage=effective_lineage,
|
|
579
|
+
)
|
|
580
|
+
if bounded_turn_budget.remaining <= 0:
|
|
581
|
+
return _replan_disposition(
|
|
582
|
+
reason="bounded turn budget exhausted; replan before another Turn",
|
|
583
|
+
decision_lineage=effective_lineage,
|
|
584
|
+
)
|
|
585
|
+
disposition = _route_to_disposition(route)
|
|
586
|
+
if disposition is LoopDisposition.REPLAN:
|
|
587
|
+
return _replan_disposition(
|
|
588
|
+
reason="fresh decision requires replan after progress",
|
|
589
|
+
decision_lineage=effective_lineage,
|
|
590
|
+
)
|
|
591
|
+
return _disposition(
|
|
592
|
+
disposition,
|
|
593
|
+
reason=_progress_reason(disposition),
|
|
594
|
+
lineage=effective_lineage,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
if result_kind is LoopXTurnResultKind.REPLAN_REQUIRED:
|
|
598
|
+
return _replan_disposition(
|
|
599
|
+
reason="turn receipt requires replan",
|
|
600
|
+
decision_lineage=effective_lineage,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
if result_kind is LoopXTurnResultKind.REPAIR_REQUIRED:
|
|
604
|
+
return _disposition(
|
|
605
|
+
LoopDisposition.REPAIR,
|
|
606
|
+
reason="turn receipt requires repair",
|
|
607
|
+
lineage=effective_lineage,
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
if result_kind is LoopXTurnResultKind.USER_ACTION_REQUIRED:
|
|
611
|
+
return _disposition(
|
|
612
|
+
LoopDisposition.USER_ACTION_REQUIRED,
|
|
613
|
+
reason="turn receipt projects a concrete user action",
|
|
614
|
+
lineage=effective_lineage,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
if result_kind is LoopXTurnResultKind.WAIT:
|
|
618
|
+
return _disposition(
|
|
619
|
+
LoopDisposition.WAIT,
|
|
620
|
+
reason="turn receipt is a typed no-spend wait",
|
|
621
|
+
lineage=effective_lineage,
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
# host_failure, validation_failed, writeback_failed, quota_spend_failed:
|
|
625
|
+
# the loop must not guess recovery on its own; hold for repair routing.
|
|
626
|
+
return _disposition(
|
|
627
|
+
LoopDisposition.REPAIR,
|
|
628
|
+
reason=(
|
|
629
|
+
f"turn receipt ended in {result_kind.value}; "
|
|
630
|
+
"route to repair before any successor turn"
|
|
631
|
+
),
|
|
632
|
+
lineage=effective_lineage,
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _no_receipt_reason(disposition: LoopDisposition) -> str:
|
|
637
|
+
return {
|
|
638
|
+
LoopDisposition.RUN_NOW: "no prior receipt and fresh decision allows delivery",
|
|
639
|
+
LoopDisposition.WAIT: "fresh decision is a quiet no-spend wait",
|
|
640
|
+
LoopDisposition.REPAIR: "fresh decision requires repair",
|
|
641
|
+
LoopDisposition.USER_ACTION_REQUIRED: "fresh decision projects a concrete user action",
|
|
642
|
+
}[disposition]
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _progress_reason(disposition: LoopDisposition) -> str:
|
|
646
|
+
return {
|
|
647
|
+
LoopDisposition.RUN_NOW: "validated progress with fresh decision allowing the next turn",
|
|
648
|
+
LoopDisposition.WAIT: "validated progress but fresh decision does not allow the next turn yet",
|
|
649
|
+
LoopDisposition.REPAIR: "fresh decision requires repair after progress",
|
|
650
|
+
LoopDisposition.USER_ACTION_REQUIRED: "fresh decision projects a concrete user action",
|
|
651
|
+
}[disposition]
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def _replan_disposition(
|
|
655
|
+
*, reason: str, decision_lineage: Mapping[str, str]
|
|
656
|
+
) -> dict[str, Any]:
|
|
657
|
+
return _disposition(
|
|
658
|
+
LoopDisposition.REPLAN,
|
|
659
|
+
reason=reason,
|
|
660
|
+
lineage=decision_lineage,
|
|
661
|
+
extra={
|
|
662
|
+
"replan_continuation": {
|
|
663
|
+
"requires_bounded_delta": True,
|
|
664
|
+
"delta_kinds": ["todo_delta", "vision_delta"],
|
|
665
|
+
"stale_todo_rerun_allowed": False,
|
|
666
|
+
"fresh_envelope_required": True,
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
)
|