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
loopx/bootstrap.py
ADDED
|
@@ -0,0 +1,1116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
import shlex
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .control_plane.runtime.time import now_local_iso
|
|
10
|
+
from .control_plane.todos.active_state_editing import (
|
|
11
|
+
TODO_SECTION_HEADINGS,
|
|
12
|
+
insertion_anchor,
|
|
13
|
+
section_bounds,
|
|
14
|
+
)
|
|
15
|
+
from .execution_profile import (
|
|
16
|
+
build_execution_profile,
|
|
17
|
+
compact_execution_profile,
|
|
18
|
+
execution_profile_summary,
|
|
19
|
+
)
|
|
20
|
+
from .global_registry import sync_project_registry_to_global
|
|
21
|
+
from .install_contract import NO_CLONE_INSTALL_URL
|
|
22
|
+
from .onboarding import build_onboarding_scan
|
|
23
|
+
from .orchestration import (
|
|
24
|
+
DEFAULT_ORCHESTRATION_MODE,
|
|
25
|
+
MULTI_SUBAGENT_ORCHESTRATION_MODE,
|
|
26
|
+
)
|
|
27
|
+
from .paths import DEFAULT_RUNTIME_ROOT, rel_or_abs
|
|
28
|
+
from .registry_writability import probe_registry_write_path
|
|
29
|
+
from .todos import add_todo_to_lines
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
DEFAULT_OBJECTIVE = "Improve this project through bounded, verified goal segments."
|
|
33
|
+
DEFAULT_DOMAIN = "project-goal-control-plane"
|
|
34
|
+
GENERIC_ONBOARDING_ADAPTER_KINDS = frozenset(
|
|
35
|
+
{"generic_project_goal_v0", "read_only_project_map_v0"}
|
|
36
|
+
)
|
|
37
|
+
NO_CLONE_INSTALL_REPAIR_COMMAND = (
|
|
38
|
+
f"curl -fsSL {NO_CLONE_INSTALL_URL} | bash\n"
|
|
39
|
+
'export PATH="$HOME/.local/bin:$PATH"\n'
|
|
40
|
+
"loopx doctor"
|
|
41
|
+
)
|
|
42
|
+
HEARTBEAT_OPT_IN_STATUS_REQUIRED = (
|
|
43
|
+
"requires explicit heartbeat=yes/no before a recurring Codex App automation is installed"
|
|
44
|
+
)
|
|
45
|
+
HEARTBEAT_OPT_IN_STATUS_PREAUTHORIZED = (
|
|
46
|
+
"explicitly preauthorized; create or update the host loop before claiming recurring automation is active"
|
|
47
|
+
)
|
|
48
|
+
HEARTBEAT_OPT_IN_STATUS_DECLINED = (
|
|
49
|
+
"explicitly declined; keep the goal manual or on-demand unless the user later opts in"
|
|
50
|
+
)
|
|
51
|
+
HEARTBEAT_OPT_IN_INSTRUCTION = (
|
|
52
|
+
"Ask the user whether to enable the Codex App heartbeat. heartbeat=yes means create or update a recurring "
|
|
53
|
+
"Codex App automation from an identity-scoped `loopx heartbeat-prompt --thin` task body; heartbeat=no means "
|
|
54
|
+
"keep the goal manual or on-demand."
|
|
55
|
+
)
|
|
56
|
+
HEARTBEAT_OPT_IN_PREAUTHORIZED_INSTRUCTION = (
|
|
57
|
+
"The user preauthorized the Codex App heartbeat. Create or update the recurring automation from an "
|
|
58
|
+
"identity-scoped `loopx heartbeat-prompt --thin` task body before claiming recurring automation is active."
|
|
59
|
+
)
|
|
60
|
+
HEARTBEAT_OPT_IN_DECLINED_INSTRUCTION = (
|
|
61
|
+
"The user declined the Codex App heartbeat. Do not install recurring automation; keep the goal manual or "
|
|
62
|
+
"on-demand unless the user later opts in."
|
|
63
|
+
)
|
|
64
|
+
CODEX_APP_HEARTBEAT_CHOICES = {"ask", "yes", "no"}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def slugify_goal_id(value: str) -> str:
|
|
68
|
+
slug = re.sub(r"[^a-zA-Z0-9]+", "-", value.lower()).strip("-")
|
|
69
|
+
return slug or "project-goal"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def default_goal_id(project: Path) -> str:
|
|
73
|
+
return f"{slugify_goal_id(project.name)}-goal"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def now_iso() -> str:
|
|
77
|
+
return now_local_iso()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def read_json_if_exists(path: Path) -> dict[str, Any]:
|
|
81
|
+
if not path.exists():
|
|
82
|
+
return {}
|
|
83
|
+
with path.open(encoding="utf-8") as f:
|
|
84
|
+
payload = json.load(f)
|
|
85
|
+
if not isinstance(payload, dict):
|
|
86
|
+
raise ValueError(f"{path} must contain a JSON object")
|
|
87
|
+
return payload
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def write_json(path: Path, payload: dict[str, Any]) -> None:
|
|
91
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def resolve_project_path(project: Path, path: Path | None) -> Path | None:
|
|
96
|
+
if path is None:
|
|
97
|
+
return None
|
|
98
|
+
path = path.expanduser()
|
|
99
|
+
return path if path.is_absolute() else project / path
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def render_authority_sources(project: Path, goal_doc: Path | None) -> str:
|
|
103
|
+
if not goal_doc:
|
|
104
|
+
return "- No explicit goal document was provided during bootstrap."
|
|
105
|
+
return f"- Primary goal document: `{rel_or_abs(goal_doc, project)}`"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def repair_missing_todo_source_sections(state_text: str) -> tuple[str, list[str]]:
|
|
109
|
+
"""Add missing durable todo sources without replacing existing state content."""
|
|
110
|
+
|
|
111
|
+
lines = state_text.splitlines()
|
|
112
|
+
added_roles: list[str] = []
|
|
113
|
+
for role in ("user", "agent"):
|
|
114
|
+
if section_bounds(lines, role) is not None:
|
|
115
|
+
continue
|
|
116
|
+
anchor = insertion_anchor(lines, role)
|
|
117
|
+
section = [f"## {TODO_SECTION_HEADINGS[role]}", ""]
|
|
118
|
+
if anchor > 0 and lines[anchor - 1].strip():
|
|
119
|
+
section.insert(0, "")
|
|
120
|
+
lines[anchor:anchor] = section
|
|
121
|
+
added_roles.append(role)
|
|
122
|
+
if not added_roles:
|
|
123
|
+
return state_text, []
|
|
124
|
+
trailing_newline = "\n" if state_text.endswith("\n") else ""
|
|
125
|
+
return "\n".join(lines) + trailing_newline, added_roles
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def onboarding_candidates(onboarding_scan: dict[str, Any] | None) -> list[dict[str, Any]]:
|
|
129
|
+
if not isinstance(onboarding_scan, dict):
|
|
130
|
+
return []
|
|
131
|
+
candidates = onboarding_scan.get("agent_todo_candidates")
|
|
132
|
+
if not isinstance(candidates, list):
|
|
133
|
+
return []
|
|
134
|
+
return [candidate for candidate in candidates if isinstance(candidate, dict)]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def normalize_codex_app_heartbeat(value: str | None) -> str:
|
|
138
|
+
choice = (value or "ask").strip().lower()
|
|
139
|
+
if choice not in CODEX_APP_HEARTBEAT_CHOICES:
|
|
140
|
+
raise ValueError("codex_app_heartbeat must be one of: ask, yes, no")
|
|
141
|
+
return choice
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def heartbeat_opt_in_required(
|
|
145
|
+
*,
|
|
146
|
+
onboarding_scan: dict[str, Any] | None,
|
|
147
|
+
codex_app_heartbeat: str,
|
|
148
|
+
) -> bool:
|
|
149
|
+
return bool(onboarding_scan and codex_app_heartbeat == "ask")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def host_loop_activation_required(
|
|
153
|
+
*,
|
|
154
|
+
onboarding_scan: dict[str, Any] | None,
|
|
155
|
+
codex_app_heartbeat: str,
|
|
156
|
+
) -> bool:
|
|
157
|
+
return bool(onboarding_scan and codex_app_heartbeat in {"ask", "yes"})
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def heartbeat_status(codex_app_heartbeat: str) -> str:
|
|
161
|
+
if codex_app_heartbeat == "yes":
|
|
162
|
+
return HEARTBEAT_OPT_IN_STATUS_PREAUTHORIZED
|
|
163
|
+
if codex_app_heartbeat == "no":
|
|
164
|
+
return HEARTBEAT_OPT_IN_STATUS_DECLINED
|
|
165
|
+
return HEARTBEAT_OPT_IN_STATUS_REQUIRED
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def heartbeat_instruction(codex_app_heartbeat: str) -> str | None:
|
|
169
|
+
if codex_app_heartbeat == "yes":
|
|
170
|
+
return HEARTBEAT_OPT_IN_PREAUTHORIZED_INSTRUCTION
|
|
171
|
+
if codex_app_heartbeat == "no":
|
|
172
|
+
return HEARTBEAT_OPT_IN_DECLINED_INSTRUCTION
|
|
173
|
+
return HEARTBEAT_OPT_IN_INSTRUCTION
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def render_onboarding_state_markdown(
|
|
177
|
+
*,
|
|
178
|
+
onboarding_scan: dict[str, Any] | None,
|
|
179
|
+
accept_onboarding_agent_todos: bool,
|
|
180
|
+
begin_autonomous_advance: bool,
|
|
181
|
+
codex_app_heartbeat: str,
|
|
182
|
+
) -> str:
|
|
183
|
+
if not onboarding_scan:
|
|
184
|
+
return ""
|
|
185
|
+
scan_policy = onboarding_scan.get("scan_policy")
|
|
186
|
+
scan_policy = scan_policy if isinstance(scan_policy, dict) else {}
|
|
187
|
+
recent_commits = onboarding_scan.get("recent_commits")
|
|
188
|
+
recent_commits = recent_commits if isinstance(recent_commits, list) else []
|
|
189
|
+
signal_files = onboarding_scan.get("signal_files")
|
|
190
|
+
signal_files = signal_files if isinstance(signal_files, list) else []
|
|
191
|
+
validation_files = onboarding_scan.get("validation_signal_files")
|
|
192
|
+
validation_files = validation_files if isinstance(validation_files, list) else []
|
|
193
|
+
validation_paths = [
|
|
194
|
+
str(item.get("path"))
|
|
195
|
+
for item in validation_files
|
|
196
|
+
if isinstance(item, dict) and item.get("path")
|
|
197
|
+
]
|
|
198
|
+
acceptance_status = (
|
|
199
|
+
"accepted and written into Agent Todo"
|
|
200
|
+
if accept_onboarding_agent_todos
|
|
201
|
+
else "requires user selection before delivery work"
|
|
202
|
+
)
|
|
203
|
+
autonomous_status = (
|
|
204
|
+
"allowed after accepted agent todos and a fresh quota guard"
|
|
205
|
+
if begin_autonomous_advance
|
|
206
|
+
else "requires an explicit user yes/no choice"
|
|
207
|
+
)
|
|
208
|
+
lines = [
|
|
209
|
+
"## Onboarding Control",
|
|
210
|
+
"",
|
|
211
|
+
"- Fast repository scan: `enabled`.",
|
|
212
|
+
f"- Scan read file bodies: `{bool(scan_policy.get('raw_file_bodies_read'))}`.",
|
|
213
|
+
f"- Git repo detected: `{bool(onboarding_scan.get('is_git_repo'))}`.",
|
|
214
|
+
f"- Local change count from `git status --short`: `{int(onboarding_scan.get('status_path_count') or 0)}`.",
|
|
215
|
+
f"- Recent commits sampled: `{len(recent_commits)}`.",
|
|
216
|
+
f"- Project signal files: `{', '.join(str(item) for item in signal_files) or 'none'}`.",
|
|
217
|
+
f"- Validation signal files: `{', '.join(validation_paths) or 'none'}`.",
|
|
218
|
+
f"- Candidate agent todos: `{acceptance_status}`.",
|
|
219
|
+
f"- Autonomous advancement: `{autonomous_status}`.",
|
|
220
|
+
f"- Codex App heartbeat: `{heartbeat_status(codex_app_heartbeat)}`.",
|
|
221
|
+
"",
|
|
222
|
+
"## Proposed Onboarding Candidates",
|
|
223
|
+
"",
|
|
224
|
+
]
|
|
225
|
+
candidates = onboarding_candidates(onboarding_scan)
|
|
226
|
+
for index, candidate in enumerate(candidates, start=1):
|
|
227
|
+
text = str(candidate.get("text") or "").strip()
|
|
228
|
+
reason = str(candidate.get("reason") or "").strip()
|
|
229
|
+
action_kind = str(candidate.get("action_kind") or "analyze").strip()
|
|
230
|
+
task_class = str(candidate.get("task_class") or "advancement_task").strip()
|
|
231
|
+
lines.append(f"{index}. {text}")
|
|
232
|
+
if reason:
|
|
233
|
+
lines.append(f" - reason: {reason}")
|
|
234
|
+
lines.append(f" - metadata: `{task_class}:{action_kind}`")
|
|
235
|
+
if not candidates:
|
|
236
|
+
lines.append("No candidate agent todos were generated.")
|
|
237
|
+
return "\n".join(lines)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def onboarding_user_todo_text(
|
|
241
|
+
*,
|
|
242
|
+
acceptance_required: bool,
|
|
243
|
+
autonomous_choice_required: bool,
|
|
244
|
+
heartbeat_choice_required: bool,
|
|
245
|
+
) -> str | None:
|
|
246
|
+
decisions: list[str] = []
|
|
247
|
+
reply_parts: list[str] = []
|
|
248
|
+
if acceptance_required:
|
|
249
|
+
decisions.append("which proposed onboarding agent todos to accept")
|
|
250
|
+
reply_parts.append("accepted numbers")
|
|
251
|
+
if autonomous_choice_required:
|
|
252
|
+
decisions.append("whether Codex may start autonomous advancement")
|
|
253
|
+
reply_parts.append("autonomous=yes/no")
|
|
254
|
+
if heartbeat_choice_required:
|
|
255
|
+
decisions.append("whether to enable the Codex App heartbeat")
|
|
256
|
+
reply_parts.append("heartbeat=yes/no")
|
|
257
|
+
if not decisions:
|
|
258
|
+
return None
|
|
259
|
+
verb = "Choose" if acceptance_required else "Decide"
|
|
260
|
+
return f"[P1] {verb} {' and '.join(decisions)}; reply with {' plus '.join(reply_parts)}."
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def onboarding_agent_review_todo_text(
|
|
264
|
+
*,
|
|
265
|
+
acceptance_required: bool,
|
|
266
|
+
autonomous_choice_required: bool,
|
|
267
|
+
heartbeat_choice_required: bool,
|
|
268
|
+
) -> str | None:
|
|
269
|
+
if not acceptance_required and not autonomous_choice_required and not heartbeat_choice_required:
|
|
270
|
+
return None
|
|
271
|
+
prompts: list[str] = []
|
|
272
|
+
if acceptance_required:
|
|
273
|
+
prompts.append("which candidate agent todos to accept")
|
|
274
|
+
if autonomous_choice_required:
|
|
275
|
+
prompts.append("whether autonomous advancement may start")
|
|
276
|
+
if heartbeat_choice_required:
|
|
277
|
+
prompts.append("whether to enable the Codex App heartbeat")
|
|
278
|
+
prefix = "Present the onboarding scan and ask " if acceptance_required else "Ask "
|
|
279
|
+
text = f"[P1] {prefix}{', '.join(prompts)} before delivery work."
|
|
280
|
+
if heartbeat_choice_required:
|
|
281
|
+
text += (
|
|
282
|
+
" If heartbeat=yes, create or update the Codex App heartbeat from an identity-scoped "
|
|
283
|
+
"`loopx heartbeat-prompt --thin` task body before claiming recurring automation is active."
|
|
284
|
+
)
|
|
285
|
+
return text
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def onboarding_connection_validation_action(adapter_kind: str) -> dict[str, str] | None:
|
|
289
|
+
if adapter_kind not in GENERIC_ONBOARDING_ADAPTER_KINDS:
|
|
290
|
+
return None
|
|
291
|
+
return {
|
|
292
|
+
"text": (
|
|
293
|
+
"[P1] Run `loopx check` against the project registry and record the first "
|
|
294
|
+
"project-specific adapter signal or an explicit no-follow-up rationale."
|
|
295
|
+
),
|
|
296
|
+
"task_class": "advancement_task",
|
|
297
|
+
"action_kind": "onboarding_connection_validation",
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def onboarding_next_action(
|
|
302
|
+
*,
|
|
303
|
+
adapter_kind: str,
|
|
304
|
+
onboarding_scan: dict[str, Any] | None,
|
|
305
|
+
accept_onboarding_agent_todos: bool,
|
|
306
|
+
begin_autonomous_advance: bool,
|
|
307
|
+
codex_app_heartbeat: str,
|
|
308
|
+
) -> str:
|
|
309
|
+
if not onboarding_scan:
|
|
310
|
+
validation_action = onboarding_connection_validation_action(adapter_kind)
|
|
311
|
+
if validation_action:
|
|
312
|
+
return validation_action["text"]
|
|
313
|
+
return "Initial routing is owned by the connected domain adapter."
|
|
314
|
+
need_heartbeat_choice = codex_app_heartbeat == "ask"
|
|
315
|
+
if not accept_onboarding_agent_todos or not begin_autonomous_advance or need_heartbeat_choice:
|
|
316
|
+
asks: list[str] = []
|
|
317
|
+
if not accept_onboarding_agent_todos:
|
|
318
|
+
asks.append("which proposed onboarding agent todos to accept")
|
|
319
|
+
if not begin_autonomous_advance:
|
|
320
|
+
asks.append("whether Codex may start autonomous advancement")
|
|
321
|
+
if need_heartbeat_choice:
|
|
322
|
+
asks.append("whether to enable the Codex App heartbeat")
|
|
323
|
+
follow_up = "then write accepted choices and refresh state before delivery work."
|
|
324
|
+
heartbeat_follow_up = ""
|
|
325
|
+
if need_heartbeat_choice:
|
|
326
|
+
heartbeat_follow_up = (
|
|
327
|
+
" If heartbeat=yes, create or update the recurring automation from an identity-scoped "
|
|
328
|
+
"`loopx heartbeat-prompt --thin` task body."
|
|
329
|
+
)
|
|
330
|
+
elif codex_app_heartbeat == "yes":
|
|
331
|
+
heartbeat_follow_up = (
|
|
332
|
+
" Create or update the preauthorized recurring automation from an identity-scoped "
|
|
333
|
+
"`loopx heartbeat-prompt --thin` task body."
|
|
334
|
+
)
|
|
335
|
+
autonomous_follow_up = (
|
|
336
|
+
" If autonomous=yes, run the quota guard and execute the first accepted onboarding agent todo."
|
|
337
|
+
if not begin_autonomous_advance
|
|
338
|
+
else " Run the quota guard and execute the first accepted onboarding agent todo once accepted choices permit."
|
|
339
|
+
)
|
|
340
|
+
return f"Ask the user {', '.join(asks)}, {follow_up}{heartbeat_follow_up}{autonomous_follow_up}"
|
|
341
|
+
if codex_app_heartbeat == "yes":
|
|
342
|
+
return (
|
|
343
|
+
"Create or update the Codex App heartbeat from an identity-scoped `loopx heartbeat-prompt --thin` task "
|
|
344
|
+
"body, run the quota guard, execute the first accepted onboarding Agent Todo as a bounded segment, write "
|
|
345
|
+
"evidence, complete or update the todo, and refresh state."
|
|
346
|
+
)
|
|
347
|
+
return (
|
|
348
|
+
"Run the quota guard, execute the first accepted onboarding Agent Todo as a bounded segment, write evidence, "
|
|
349
|
+
"complete or update the todo, and refresh state."
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def apply_onboarding_todos_to_state(
|
|
354
|
+
text: str,
|
|
355
|
+
*,
|
|
356
|
+
adapter_kind: str,
|
|
357
|
+
updated_at: str,
|
|
358
|
+
onboarding_scan: dict[str, Any] | None,
|
|
359
|
+
accept_onboarding_agent_todos: bool,
|
|
360
|
+
begin_autonomous_advance: bool,
|
|
361
|
+
codex_app_heartbeat: str,
|
|
362
|
+
) -> str:
|
|
363
|
+
if not onboarding_scan:
|
|
364
|
+
lines = text.splitlines()
|
|
365
|
+
action = onboarding_connection_validation_action(adapter_kind)
|
|
366
|
+
if action:
|
|
367
|
+
add_todo_to_lines(
|
|
368
|
+
lines,
|
|
369
|
+
role="agent",
|
|
370
|
+
text=action["text"],
|
|
371
|
+
task_class=action["task_class"],
|
|
372
|
+
action_kind=action["action_kind"],
|
|
373
|
+
updated_at=updated_at,
|
|
374
|
+
)
|
|
375
|
+
return "\n".join(lines) + "\n"
|
|
376
|
+
lines = text.splitlines()
|
|
377
|
+
if accept_onboarding_agent_todos:
|
|
378
|
+
for candidate in onboarding_candidates(onboarding_scan):
|
|
379
|
+
todo_text = str(candidate.get("text") or "").strip()
|
|
380
|
+
if not todo_text:
|
|
381
|
+
continue
|
|
382
|
+
add_todo_to_lines(
|
|
383
|
+
lines,
|
|
384
|
+
role="agent",
|
|
385
|
+
text=todo_text,
|
|
386
|
+
task_class=str(candidate.get("task_class") or "advancement_task"),
|
|
387
|
+
action_kind=str(candidate.get("action_kind") or "analyze"),
|
|
388
|
+
updated_at=updated_at,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
acceptance_required = not accept_onboarding_agent_todos
|
|
392
|
+
autonomous_choice_required = not begin_autonomous_advance
|
|
393
|
+
heartbeat_choice_required = codex_app_heartbeat == "ask"
|
|
394
|
+
user_todo = onboarding_user_todo_text(
|
|
395
|
+
acceptance_required=acceptance_required,
|
|
396
|
+
autonomous_choice_required=autonomous_choice_required,
|
|
397
|
+
heartbeat_choice_required=heartbeat_choice_required,
|
|
398
|
+
)
|
|
399
|
+
if user_todo:
|
|
400
|
+
add_todo_to_lines(
|
|
401
|
+
lines,
|
|
402
|
+
role="user",
|
|
403
|
+
text=user_todo,
|
|
404
|
+
task_class="user_gate",
|
|
405
|
+
action_kind="onboarding_decision",
|
|
406
|
+
updated_at=updated_at,
|
|
407
|
+
)
|
|
408
|
+
agent_todo = onboarding_agent_review_todo_text(
|
|
409
|
+
acceptance_required=acceptance_required,
|
|
410
|
+
autonomous_choice_required=autonomous_choice_required,
|
|
411
|
+
heartbeat_choice_required=heartbeat_choice_required,
|
|
412
|
+
)
|
|
413
|
+
if agent_todo:
|
|
414
|
+
add_todo_to_lines(
|
|
415
|
+
lines,
|
|
416
|
+
role="agent",
|
|
417
|
+
text=agent_todo,
|
|
418
|
+
task_class="advancement_task",
|
|
419
|
+
action_kind="onboarding_todo_review",
|
|
420
|
+
updated_at=updated_at,
|
|
421
|
+
)
|
|
422
|
+
return "\n".join(lines) + "\n"
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def render_state_markdown(
|
|
426
|
+
*,
|
|
427
|
+
project: Path,
|
|
428
|
+
goal_id: str,
|
|
429
|
+
adapter_kind: str,
|
|
430
|
+
objective: str,
|
|
431
|
+
updated_at: str,
|
|
432
|
+
goal_doc: Path | None,
|
|
433
|
+
execution_profile: dict[str, Any] | None,
|
|
434
|
+
onboarding_scan: dict[str, Any] | None = None,
|
|
435
|
+
accept_onboarding_agent_todos: bool = False,
|
|
436
|
+
begin_autonomous_advance: bool = False,
|
|
437
|
+
codex_app_heartbeat: str = "ask",
|
|
438
|
+
) -> str:
|
|
439
|
+
safe_objective = objective.replace('"', '\\"')
|
|
440
|
+
profile_summary = execution_profile_summary(execution_profile)
|
|
441
|
+
onboarding_markdown = render_onboarding_state_markdown(
|
|
442
|
+
onboarding_scan=onboarding_scan,
|
|
443
|
+
accept_onboarding_agent_todos=accept_onboarding_agent_todos,
|
|
444
|
+
begin_autonomous_advance=begin_autonomous_advance,
|
|
445
|
+
codex_app_heartbeat=codex_app_heartbeat,
|
|
446
|
+
)
|
|
447
|
+
onboarding_block = f"\n{onboarding_markdown}\n" if onboarding_markdown else ""
|
|
448
|
+
next_action = onboarding_next_action(
|
|
449
|
+
adapter_kind=adapter_kind,
|
|
450
|
+
onboarding_scan=onboarding_scan,
|
|
451
|
+
accept_onboarding_agent_todos=accept_onboarding_agent_todos,
|
|
452
|
+
begin_autonomous_advance=begin_autonomous_advance,
|
|
453
|
+
codex_app_heartbeat=codex_app_heartbeat,
|
|
454
|
+
)
|
|
455
|
+
state_text = f"""---
|
|
456
|
+
status: active
|
|
457
|
+
owner_mode: goal
|
|
458
|
+
objective: "{safe_objective}"
|
|
459
|
+
updated_at: {updated_at}
|
|
460
|
+
adapter_id: {goal_id}
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
# Active Goal State
|
|
464
|
+
|
|
465
|
+
## Objective
|
|
466
|
+
|
|
467
|
+
{objective}
|
|
468
|
+
|
|
469
|
+
## Authority Sources
|
|
470
|
+
|
|
471
|
+
{render_authority_sources(project, goal_doc)}
|
|
472
|
+
|
|
473
|
+
## Operating Contract
|
|
474
|
+
|
|
475
|
+
- Treat this file as the durable goal state for future agent ticks.
|
|
476
|
+
- Treat the authority sources above as the first context to inspect before acting.
|
|
477
|
+
- Read current project evidence before choosing the next action.
|
|
478
|
+
- Run a bounded progress segment when useful; it does not have to be one tiny step.
|
|
479
|
+
- Keep private evidence, credentials, local paths, and raw logs out of public commits.
|
|
480
|
+
- End each tick with changed files, validation, residual risk, and the next action.
|
|
481
|
+
|
|
482
|
+
## Execution Profile
|
|
483
|
+
|
|
484
|
+
- `{profile_summary}`
|
|
485
|
+
- Repeated small-scale follow-through should expand the next delivery batch or report a blocker before spending quota.
|
|
486
|
+
|
|
487
|
+
## Non-Goals
|
|
488
|
+
|
|
489
|
+
- Do not perform irreversible production operations without explicit approval.
|
|
490
|
+
- Do not publish private project evidence.
|
|
491
|
+
- Do not optimize for activity if no useful artifact or decision can be produced.
|
|
492
|
+
{onboarding_block}
|
|
493
|
+
|
|
494
|
+
## User Todo / Owner Review Reading Queue
|
|
495
|
+
|
|
496
|
+
## Agent Todo
|
|
497
|
+
|
|
498
|
+
## Next Action
|
|
499
|
+
|
|
500
|
+
- {next_action}
|
|
501
|
+
|
|
502
|
+
## Recent User Feedback
|
|
503
|
+
|
|
504
|
+
- Initialized by `loopx bootstrap`.
|
|
505
|
+
|
|
506
|
+
## Progress Ledger
|
|
507
|
+
|
|
508
|
+
- Created the initial goal state and registry connection.
|
|
509
|
+
"""
|
|
510
|
+
return apply_onboarding_todos_to_state(
|
|
511
|
+
state_text,
|
|
512
|
+
adapter_kind=adapter_kind,
|
|
513
|
+
updated_at=updated_at,
|
|
514
|
+
onboarding_scan=onboarding_scan,
|
|
515
|
+
accept_onboarding_agent_todos=accept_onboarding_agent_todos,
|
|
516
|
+
begin_autonomous_advance=begin_autonomous_advance,
|
|
517
|
+
codex_app_heartbeat=codex_app_heartbeat,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def relative_state_file(project: Path, state_file: Path) -> str:
|
|
522
|
+
return rel_or_abs(state_file, project)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def todo_add_command(
|
|
526
|
+
*,
|
|
527
|
+
project: Path,
|
|
528
|
+
registry_path: Path,
|
|
529
|
+
goal_id: str,
|
|
530
|
+
candidate: dict[str, Any],
|
|
531
|
+
) -> str:
|
|
532
|
+
return (
|
|
533
|
+
f"loopx --registry {shlex.quote(relative_state_file(project, registry_path))} todo add "
|
|
534
|
+
f"--goal-id {shlex.quote(goal_id)} "
|
|
535
|
+
"--role agent "
|
|
536
|
+
f"--text {shlex.quote(str(candidate.get('text') or ''))} "
|
|
537
|
+
f"--task-class {shlex.quote(str(candidate.get('task_class') or 'advancement_task'))} "
|
|
538
|
+
f"--action-kind {shlex.quote(str(candidate.get('action_kind') or 'analyze'))}"
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def build_goal_entry(
|
|
543
|
+
*,
|
|
544
|
+
project: Path,
|
|
545
|
+
goal_id: str,
|
|
546
|
+
domain: str,
|
|
547
|
+
role: str,
|
|
548
|
+
parent_goal_id: str | None,
|
|
549
|
+
state_file: Path,
|
|
550
|
+
goal_doc: Path | None,
|
|
551
|
+
adapter_kind: str,
|
|
552
|
+
adapter_status: str,
|
|
553
|
+
next_probe: str | None,
|
|
554
|
+
spawn_allowed: bool,
|
|
555
|
+
max_children: int,
|
|
556
|
+
allowed_domains: list[str],
|
|
557
|
+
write_scope: list[str],
|
|
558
|
+
execution_profile: dict[str, Any] | None,
|
|
559
|
+
) -> dict[str, Any]:
|
|
560
|
+
authority_sources = []
|
|
561
|
+
if goal_doc:
|
|
562
|
+
authority_sources.append(
|
|
563
|
+
{
|
|
564
|
+
"kind": "goal_doc",
|
|
565
|
+
"path": rel_or_abs(goal_doc, project),
|
|
566
|
+
"role": "primary_goal_document",
|
|
567
|
+
}
|
|
568
|
+
)
|
|
569
|
+
return {
|
|
570
|
+
"id": goal_id,
|
|
571
|
+
"domain": domain,
|
|
572
|
+
"status": "active",
|
|
573
|
+
"role": role,
|
|
574
|
+
"parent_goal_id": parent_goal_id,
|
|
575
|
+
"repo": str(project),
|
|
576
|
+
"state_file": relative_state_file(project, state_file),
|
|
577
|
+
"authority_sources": authority_sources,
|
|
578
|
+
"adapter": {
|
|
579
|
+
"kind": adapter_kind,
|
|
580
|
+
"status": adapter_status,
|
|
581
|
+
},
|
|
582
|
+
"spawn_policy": {
|
|
583
|
+
"mode": (
|
|
584
|
+
MULTI_SUBAGENT_ORCHESTRATION_MODE
|
|
585
|
+
if spawn_allowed and max(0, max_children) > 0
|
|
586
|
+
else DEFAULT_ORCHESTRATION_MODE
|
|
587
|
+
),
|
|
588
|
+
"allowed": spawn_allowed,
|
|
589
|
+
"max_children": max(0, max_children),
|
|
590
|
+
"allowed_domains": allowed_domains,
|
|
591
|
+
},
|
|
592
|
+
"coordination": {
|
|
593
|
+
"write_scope": write_scope,
|
|
594
|
+
"requires_parent_approval": [
|
|
595
|
+
"write",
|
|
596
|
+
"publish",
|
|
597
|
+
"production-action",
|
|
598
|
+
],
|
|
599
|
+
},
|
|
600
|
+
"execution_profile": compact_execution_profile(execution_profile),
|
|
601
|
+
"next_probe": next_probe
|
|
602
|
+
or f"loopx --registry .loopx/registry.json check --scan-root {project}",
|
|
603
|
+
"guards": [
|
|
604
|
+
"read-only by default",
|
|
605
|
+
"do not mutate production systems without explicit user approval",
|
|
606
|
+
"keep private evidence out of public commits",
|
|
607
|
+
],
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def merge_goal(registry: dict[str, Any], goal_entry: dict[str, Any], *, force: bool) -> tuple[dict[str, Any], str]:
|
|
612
|
+
goals = registry.get("goals")
|
|
613
|
+
if not isinstance(goals, list):
|
|
614
|
+
goals = []
|
|
615
|
+
merged: list[Any] = []
|
|
616
|
+
action = "appended"
|
|
617
|
+
replaced = False
|
|
618
|
+
for item in goals:
|
|
619
|
+
if isinstance(item, dict) and item.get("id") == goal_entry["id"]:
|
|
620
|
+
if force:
|
|
621
|
+
merged.append(goal_entry)
|
|
622
|
+
action = "replaced"
|
|
623
|
+
else:
|
|
624
|
+
merged.append(item)
|
|
625
|
+
action = "kept-existing"
|
|
626
|
+
replaced = True
|
|
627
|
+
else:
|
|
628
|
+
merged.append(item)
|
|
629
|
+
if not replaced:
|
|
630
|
+
merged.append(goal_entry)
|
|
631
|
+
registry["goals"] = merged
|
|
632
|
+
return registry, action
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
def bootstrap_project(
|
|
636
|
+
*,
|
|
637
|
+
project: Path,
|
|
638
|
+
registry_path: Path,
|
|
639
|
+
runtime_root: Path | None,
|
|
640
|
+
goal_id: str | None,
|
|
641
|
+
objective: str,
|
|
642
|
+
domain: str,
|
|
643
|
+
role: str,
|
|
644
|
+
parent_goal_id: str | None,
|
|
645
|
+
state_file: Path | None,
|
|
646
|
+
goal_doc: Path | None,
|
|
647
|
+
adapter_kind: str,
|
|
648
|
+
adapter_status: str,
|
|
649
|
+
next_probe: str | None,
|
|
650
|
+
spawn_allowed: bool,
|
|
651
|
+
max_children: int,
|
|
652
|
+
allowed_domains: list[str] | None,
|
|
653
|
+
write_scope: list[str] | None,
|
|
654
|
+
execution_minimum_scale: str | None = None,
|
|
655
|
+
execution_must_include: list[str] | None = None,
|
|
656
|
+
execution_small_streak_threshold: int | None = None,
|
|
657
|
+
execution_outcome_markers: list[str] | None = None,
|
|
658
|
+
execution_surface_only_hints: list[str] | None = None,
|
|
659
|
+
execution_surface_streak_threshold: int | None = None,
|
|
660
|
+
execution_outcome_must_advance: list[str] | None = None,
|
|
661
|
+
execution_turn_granularity: str | None = None,
|
|
662
|
+
onboarding_scan_enabled: bool = True,
|
|
663
|
+
accept_onboarding_agent_todos: bool = False,
|
|
664
|
+
begin_autonomous_advance: bool = False,
|
|
665
|
+
codex_app_heartbeat: str = "ask",
|
|
666
|
+
onboarding_max_commits: int = 5,
|
|
667
|
+
onboarding_max_status_paths: int = 12,
|
|
668
|
+
onboarding_max_top_level_files: int = 24,
|
|
669
|
+
preserve_todos: bool = False,
|
|
670
|
+
force: bool,
|
|
671
|
+
dry_run: bool,
|
|
672
|
+
sync_global: bool,
|
|
673
|
+
allow_global_route_replacement: bool = False,
|
|
674
|
+
) -> dict[str, Any]:
|
|
675
|
+
project = project.expanduser().resolve()
|
|
676
|
+
codex_app_heartbeat = normalize_codex_app_heartbeat(codex_app_heartbeat)
|
|
677
|
+
registry_path = registry_path.expanduser()
|
|
678
|
+
if not registry_path.is_absolute():
|
|
679
|
+
registry_path = project / registry_path
|
|
680
|
+
goal_id = goal_id or default_goal_id(project)
|
|
681
|
+
state_file = state_file or (project / ".codex" / "goals" / goal_id / "ACTIVE_GOAL_STATE.md")
|
|
682
|
+
state_file = state_file.expanduser()
|
|
683
|
+
if not state_file.is_absolute():
|
|
684
|
+
state_file = project / state_file
|
|
685
|
+
goal_doc = resolve_project_path(project, goal_doc)
|
|
686
|
+
runtime_root = runtime_root.expanduser() if runtime_root else DEFAULT_RUNTIME_ROOT
|
|
687
|
+
updated_at = now_iso()
|
|
688
|
+
execution_profile = build_execution_profile(
|
|
689
|
+
minimum_scale=execution_minimum_scale,
|
|
690
|
+
must_include=execution_must_include,
|
|
691
|
+
small_scale_streak_threshold=execution_small_streak_threshold,
|
|
692
|
+
outcome_markers=execution_outcome_markers,
|
|
693
|
+
surface_only_hints=execution_surface_only_hints,
|
|
694
|
+
surface_streak_threshold=execution_surface_streak_threshold,
|
|
695
|
+
outcome_must_advance=execution_outcome_must_advance,
|
|
696
|
+
turn_granularity=execution_turn_granularity,
|
|
697
|
+
)
|
|
698
|
+
onboarding_scan = (
|
|
699
|
+
build_onboarding_scan(
|
|
700
|
+
project,
|
|
701
|
+
max_commits=onboarding_max_commits,
|
|
702
|
+
max_status_paths=onboarding_max_status_paths,
|
|
703
|
+
max_top_level_files=onboarding_max_top_level_files,
|
|
704
|
+
)
|
|
705
|
+
if onboarding_scan_enabled
|
|
706
|
+
else None
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
registry = read_json_if_exists(registry_path)
|
|
710
|
+
registry.setdefault("schema_version", "0.1")
|
|
711
|
+
registry["updated_at"] = updated_at.split("T")[0]
|
|
712
|
+
registry.setdefault("common_runtime_root", str(runtime_root))
|
|
713
|
+
if runtime_root:
|
|
714
|
+
registry["common_runtime_root"] = str(runtime_root)
|
|
715
|
+
|
|
716
|
+
goal_entry = build_goal_entry(
|
|
717
|
+
project=project,
|
|
718
|
+
goal_id=goal_id,
|
|
719
|
+
domain=domain,
|
|
720
|
+
role=role,
|
|
721
|
+
parent_goal_id=parent_goal_id,
|
|
722
|
+
state_file=state_file,
|
|
723
|
+
goal_doc=goal_doc,
|
|
724
|
+
adapter_kind=adapter_kind,
|
|
725
|
+
adapter_status=adapter_status,
|
|
726
|
+
next_probe=next_probe,
|
|
727
|
+
spawn_allowed=spawn_allowed,
|
|
728
|
+
max_children=max_children,
|
|
729
|
+
allowed_domains=allowed_domains or [],
|
|
730
|
+
write_scope=write_scope or [],
|
|
731
|
+
execution_profile=execution_profile,
|
|
732
|
+
)
|
|
733
|
+
registry, registry_goal_action = merge_goal(registry, goal_entry, force=force)
|
|
734
|
+
|
|
735
|
+
state_exists = state_file.exists()
|
|
736
|
+
state_action = "created"
|
|
737
|
+
if state_exists and force and preserve_todos:
|
|
738
|
+
state_action = "kept-existing-preserve-todos"
|
|
739
|
+
elif state_exists and not force:
|
|
740
|
+
state_action = "kept-existing"
|
|
741
|
+
elif state_exists and force:
|
|
742
|
+
state_action = "replaced"
|
|
743
|
+
|
|
744
|
+
repaired_state_text: str | None = None
|
|
745
|
+
repaired_todo_source_roles: list[str] = []
|
|
746
|
+
if state_exists and state_action in {
|
|
747
|
+
"kept-existing",
|
|
748
|
+
"kept-existing-preserve-todos",
|
|
749
|
+
}:
|
|
750
|
+
repaired_state_text, repaired_todo_source_roles = repair_missing_todo_source_sections(
|
|
751
|
+
state_file.read_text(encoding="utf-8")
|
|
752
|
+
)
|
|
753
|
+
todo_source_migration = (
|
|
754
|
+
{
|
|
755
|
+
"schema_version": "todo_source_section_migration_v0",
|
|
756
|
+
"added_roles": repaired_todo_source_roles,
|
|
757
|
+
"applied": False,
|
|
758
|
+
}
|
|
759
|
+
if repaired_todo_source_roles
|
|
760
|
+
else None
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
dry_state_actions = {
|
|
764
|
+
"created": "would-create",
|
|
765
|
+
"kept-existing": "would-keep-existing",
|
|
766
|
+
"kept-existing-preserve-todos": "would-keep-existing-preserve-todos",
|
|
767
|
+
"replaced": "would-replace",
|
|
768
|
+
}
|
|
769
|
+
force_bootstrap_warning = None
|
|
770
|
+
if state_exists and force:
|
|
771
|
+
force_bootstrap_warning = {
|
|
772
|
+
"kind": "force_reconnect_existing_active_state",
|
|
773
|
+
"state_file": str(state_file),
|
|
774
|
+
"state_action": state_action,
|
|
775
|
+
"will_replace_active_state": state_action == "replaced",
|
|
776
|
+
"preserve_todos_requested": bool(preserve_todos),
|
|
777
|
+
"recommended_scope_migration": (
|
|
778
|
+
"Use configure-goal --write-scope ... --execute to change write scope "
|
|
779
|
+
"without rebuilding the active state."
|
|
780
|
+
),
|
|
781
|
+
"preserve_todos_option": "--preserve-todos",
|
|
782
|
+
}
|
|
783
|
+
actions = [
|
|
784
|
+
{"path": str(registry_path), "action": "would-write" if dry_run else "wrote", "goal": registry_goal_action},
|
|
785
|
+
{
|
|
786
|
+
"path": str(state_file),
|
|
787
|
+
"action": dry_state_actions.get(state_action, "would-write") if dry_run else state_action,
|
|
788
|
+
**({"todo_source_migration": todo_source_migration} if todo_source_migration else {}),
|
|
789
|
+
},
|
|
790
|
+
]
|
|
791
|
+
if sync_global:
|
|
792
|
+
actions.append(
|
|
793
|
+
{
|
|
794
|
+
"path": str(runtime_root / "registry.global.json"),
|
|
795
|
+
"action": "would-sync" if dry_run else "synced",
|
|
796
|
+
"goal": goal_id,
|
|
797
|
+
}
|
|
798
|
+
)
|
|
799
|
+
|
|
800
|
+
candidates = onboarding_candidates(onboarding_scan)
|
|
801
|
+
heartbeat_required = heartbeat_opt_in_required(
|
|
802
|
+
onboarding_scan=onboarding_scan,
|
|
803
|
+
codex_app_heartbeat=codex_app_heartbeat,
|
|
804
|
+
)
|
|
805
|
+
host_loop_required = host_loop_activation_required(
|
|
806
|
+
onboarding_scan=onboarding_scan,
|
|
807
|
+
codex_app_heartbeat=codex_app_heartbeat,
|
|
808
|
+
)
|
|
809
|
+
accept_candidate_commands = [
|
|
810
|
+
todo_add_command(
|
|
811
|
+
project=project,
|
|
812
|
+
registry_path=registry_path,
|
|
813
|
+
goal_id=goal_id,
|
|
814
|
+
candidate=candidate,
|
|
815
|
+
)
|
|
816
|
+
for candidate in candidates
|
|
817
|
+
]
|
|
818
|
+
|
|
819
|
+
global_sync: dict[str, Any] | None = None
|
|
820
|
+
global_writability: dict[str, Any] | None = None
|
|
821
|
+
if sync_global and not dry_run:
|
|
822
|
+
global_writability = probe_registry_write_path(runtime_root / "registry.global.json", create_parent=True)
|
|
823
|
+
if not global_writability.get("ok"):
|
|
824
|
+
for action in actions:
|
|
825
|
+
if action.get("path") == str(runtime_root / "registry.global.json"):
|
|
826
|
+
action["action"] = "blocked-write-denied"
|
|
827
|
+
global_sync = {
|
|
828
|
+
"ok": False,
|
|
829
|
+
"enabled": True,
|
|
830
|
+
"dry_run": dry_run,
|
|
831
|
+
"global_registry": str(runtime_root / "registry.global.json"),
|
|
832
|
+
"synced_goal_ids": [],
|
|
833
|
+
"wrote": False,
|
|
834
|
+
"write_denied": True,
|
|
835
|
+
"error_kind": "global_registry_write_denied",
|
|
836
|
+
"global_registry_writability": global_writability,
|
|
837
|
+
"requires_global_registry_repair": True,
|
|
838
|
+
"requires_host_permission": bool(global_writability.get("requires_host_permission")),
|
|
839
|
+
"recommended_action": global_writability.get("recommended_action"),
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
"ok": False,
|
|
843
|
+
"dry_run": dry_run,
|
|
844
|
+
"project": str(project),
|
|
845
|
+
"goal_id": goal_id,
|
|
846
|
+
"registry": str(registry_path),
|
|
847
|
+
"state_file": str(state_file),
|
|
848
|
+
"goal_doc": str(goal_doc) if goal_doc else None,
|
|
849
|
+
"goal_doc_exists": bool(goal_doc and goal_doc.exists()),
|
|
850
|
+
"runtime_root": str(runtime_root),
|
|
851
|
+
"registry_goal_action": registry_goal_action,
|
|
852
|
+
"state_action": state_action,
|
|
853
|
+
"todo_source_migration": todo_source_migration,
|
|
854
|
+
"force_bootstrap_warning": force_bootstrap_warning,
|
|
855
|
+
"execution_profile": execution_profile,
|
|
856
|
+
"onboarding_scan": onboarding_scan,
|
|
857
|
+
"onboarding_agent_todo_candidates": candidates,
|
|
858
|
+
"accept_onboarding_agent_todos": accept_onboarding_agent_todos,
|
|
859
|
+
"begin_autonomous_advance": begin_autonomous_advance,
|
|
860
|
+
"codex_app_heartbeat": codex_app_heartbeat,
|
|
861
|
+
"onboarding_acceptance_required": bool(onboarding_scan and not accept_onboarding_agent_todos),
|
|
862
|
+
"autonomous_advance_choice_required": bool(onboarding_scan and not begin_autonomous_advance),
|
|
863
|
+
"heartbeat_opt_in_required": heartbeat_required,
|
|
864
|
+
"host_loop_activation_required": host_loop_required,
|
|
865
|
+
"heartbeat_opt_in_instruction": (
|
|
866
|
+
heartbeat_instruction(codex_app_heartbeat) if onboarding_scan else None
|
|
867
|
+
),
|
|
868
|
+
"onboarding_todos_written": False,
|
|
869
|
+
"accept_candidate_commands": accept_candidate_commands,
|
|
870
|
+
"global_sync": global_sync,
|
|
871
|
+
"actions": actions,
|
|
872
|
+
"next_commands": [
|
|
873
|
+
"Fix global registry write access, then rerun this command.",
|
|
874
|
+
"Use --no-global-sync only for an explicit local-only setup.",
|
|
875
|
+
],
|
|
876
|
+
"install_repair_command": NO_CLONE_INSTALL_REPAIR_COMMAND,
|
|
877
|
+
"install_repair_note": (
|
|
878
|
+
"If this local LoopX install is missing or stale, rerun the no-clone installer, "
|
|
879
|
+
"refresh PATH, and confirm with loopx doctor before continuing project delivery."
|
|
880
|
+
),
|
|
881
|
+
"private_boundary_note": "Add .loopx/ and .codex/goals/ to the project .gitignore if the goal state contains private evidence.",
|
|
882
|
+
"error": str(global_writability.get("error") or "global registry is not writable"),
|
|
883
|
+
}
|
|
884
|
+
if not dry_run:
|
|
885
|
+
write_json(registry_path, registry)
|
|
886
|
+
if state_action in {"created", "replaced"}:
|
|
887
|
+
state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
888
|
+
state_file.write_text(
|
|
889
|
+
render_state_markdown(
|
|
890
|
+
project=project,
|
|
891
|
+
goal_id=goal_id,
|
|
892
|
+
adapter_kind=adapter_kind,
|
|
893
|
+
objective=objective,
|
|
894
|
+
updated_at=updated_at,
|
|
895
|
+
goal_doc=goal_doc,
|
|
896
|
+
execution_profile=execution_profile,
|
|
897
|
+
onboarding_scan=onboarding_scan,
|
|
898
|
+
accept_onboarding_agent_todos=accept_onboarding_agent_todos,
|
|
899
|
+
begin_autonomous_advance=begin_autonomous_advance,
|
|
900
|
+
codex_app_heartbeat=codex_app_heartbeat,
|
|
901
|
+
),
|
|
902
|
+
encoding="utf-8",
|
|
903
|
+
)
|
|
904
|
+
elif repaired_state_text is not None and repaired_todo_source_roles:
|
|
905
|
+
state_file.write_text(repaired_state_text, encoding="utf-8")
|
|
906
|
+
if todo_source_migration is not None:
|
|
907
|
+
todo_source_migration["applied"] = True
|
|
908
|
+
if sync_global:
|
|
909
|
+
global_sync = sync_project_registry_to_global(
|
|
910
|
+
registry_path=registry_path,
|
|
911
|
+
runtime_root_override=str(runtime_root),
|
|
912
|
+
goal_id=goal_id,
|
|
913
|
+
dry_run=False,
|
|
914
|
+
allow_route_replacement=allow_global_route_replacement,
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
return {
|
|
918
|
+
"ok": True,
|
|
919
|
+
"dry_run": dry_run,
|
|
920
|
+
"project": str(project),
|
|
921
|
+
"goal_id": goal_id,
|
|
922
|
+
"registry": str(registry_path),
|
|
923
|
+
"state_file": str(state_file),
|
|
924
|
+
"goal_doc": str(goal_doc) if goal_doc else None,
|
|
925
|
+
"goal_doc_exists": bool(goal_doc and goal_doc.exists()),
|
|
926
|
+
"runtime_root": str(runtime_root),
|
|
927
|
+
"registry_goal_action": registry_goal_action,
|
|
928
|
+
"state_action": state_action,
|
|
929
|
+
"todo_source_migration": todo_source_migration,
|
|
930
|
+
"force_bootstrap_warning": force_bootstrap_warning,
|
|
931
|
+
"execution_profile": execution_profile,
|
|
932
|
+
"onboarding_scan": onboarding_scan,
|
|
933
|
+
"onboarding_agent_todo_candidates": candidates,
|
|
934
|
+
"accept_onboarding_agent_todos": accept_onboarding_agent_todos,
|
|
935
|
+
"begin_autonomous_advance": begin_autonomous_advance,
|
|
936
|
+
"codex_app_heartbeat": codex_app_heartbeat,
|
|
937
|
+
"onboarding_acceptance_required": bool(onboarding_scan and not accept_onboarding_agent_todos),
|
|
938
|
+
"autonomous_advance_choice_required": bool(onboarding_scan and not begin_autonomous_advance),
|
|
939
|
+
"heartbeat_opt_in_required": heartbeat_required,
|
|
940
|
+
"host_loop_activation_required": host_loop_required,
|
|
941
|
+
"heartbeat_opt_in_instruction": heartbeat_instruction(codex_app_heartbeat) if onboarding_scan else None,
|
|
942
|
+
"onboarding_todos_written": bool(
|
|
943
|
+
onboarding_scan and state_action in {"created", "replaced"} and not dry_run
|
|
944
|
+
),
|
|
945
|
+
"accept_candidate_commands": accept_candidate_commands,
|
|
946
|
+
"global_sync": global_sync
|
|
947
|
+
or {
|
|
948
|
+
"enabled": sync_global,
|
|
949
|
+
"dry_run": dry_run,
|
|
950
|
+
"global_registry": str(runtime_root / "registry.global.json"),
|
|
951
|
+
"synced_goal_ids": [goal_id] if sync_global else [],
|
|
952
|
+
"wrote": False,
|
|
953
|
+
"route_replacement_allowed": allow_global_route_replacement,
|
|
954
|
+
},
|
|
955
|
+
"actions": actions,
|
|
956
|
+
"next_commands": [
|
|
957
|
+
f"loopx --registry {relative_state_file(project, registry_path)} registry",
|
|
958
|
+
f"loopx --registry {relative_state_file(project, registry_path)} status",
|
|
959
|
+
f"loopx --registry {relative_state_file(project, registry_path)} check --scan-root {project}",
|
|
960
|
+
f"loopx --format json --registry {runtime_root / 'registry.global.json'} quota should-run --goal-id {goal_id} --runtime-profile generic_cli",
|
|
961
|
+
f"loopx --registry {relative_state_file(project, registry_path)} refresh-state --goal-id {goal_id}",
|
|
962
|
+
f"loopx --registry {runtime_root / 'registry.global.json'} status",
|
|
963
|
+
f"loopx --registry {relative_state_file(project, registry_path)} history --goal-id {goal_id}",
|
|
964
|
+
],
|
|
965
|
+
"install_repair_command": NO_CLONE_INSTALL_REPAIR_COMMAND,
|
|
966
|
+
"install_repair_note": (
|
|
967
|
+
"If this local LoopX install is missing or stale, rerun the no-clone installer, "
|
|
968
|
+
"refresh PATH, and confirm with loopx doctor before continuing project delivery."
|
|
969
|
+
),
|
|
970
|
+
"private_boundary_note": "Add .loopx/ and .codex/goals/ to the project .gitignore if the goal state contains private evidence.",
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def render_bootstrap_markdown(payload: dict[str, Any]) -> str:
|
|
975
|
+
execution_profile = (
|
|
976
|
+
payload.get("execution_profile")
|
|
977
|
+
if isinstance(payload.get("execution_profile"), dict)
|
|
978
|
+
else None
|
|
979
|
+
)
|
|
980
|
+
execution_profile_text = execution_profile_summary(execution_profile)
|
|
981
|
+
lines = [
|
|
982
|
+
"# LoopX Bootstrap",
|
|
983
|
+
"",
|
|
984
|
+
f"- ok: `{payload.get('ok')}`",
|
|
985
|
+
f"- dry_run: `{payload.get('dry_run')}`",
|
|
986
|
+
f"- project: `{payload.get('project')}`",
|
|
987
|
+
f"- goal_id: `{payload.get('goal_id')}`",
|
|
988
|
+
f"- registry: `{payload.get('registry')}`",
|
|
989
|
+
f"- state_file: `{payload.get('state_file')}`",
|
|
990
|
+
f"- goal_doc: `{payload.get('goal_doc')}`",
|
|
991
|
+
f"- goal_doc_exists: `{payload.get('goal_doc_exists')}`",
|
|
992
|
+
f"- runtime_root: `{payload.get('runtime_root')}`",
|
|
993
|
+
f"- registry_goal_action: `{payload.get('registry_goal_action')}`",
|
|
994
|
+
f"- state_action: `{payload.get('state_action')}`",
|
|
995
|
+
f"- execution_profile: `{execution_profile_text}`",
|
|
996
|
+
f"- onboarding_acceptance_required: `{payload.get('onboarding_acceptance_required')}`",
|
|
997
|
+
f"- autonomous_advance_choice_required: `{payload.get('autonomous_advance_choice_required')}`",
|
|
998
|
+
f"- codex_app_heartbeat: `{payload.get('codex_app_heartbeat')}`",
|
|
999
|
+
f"- heartbeat_opt_in_required: `{payload.get('heartbeat_opt_in_required')}`",
|
|
1000
|
+
f"- host_loop_activation_required: `{payload.get('host_loop_activation_required')}`",
|
|
1001
|
+
f"- onboarding_todos_written: `{payload.get('onboarding_todos_written')}`",
|
|
1002
|
+
f"- global_sync: `{(payload.get('global_sync') or {}).get('wrote')}`",
|
|
1003
|
+
"",
|
|
1004
|
+
"## Actions",
|
|
1005
|
+
]
|
|
1006
|
+
for action in payload.get("actions") or []:
|
|
1007
|
+
lines.append(f"- `{action.get('path')}`: {action.get('action')} ({action.get('goal', '')})")
|
|
1008
|
+
|
|
1009
|
+
force_warning = payload.get("force_bootstrap_warning")
|
|
1010
|
+
if isinstance(force_warning, dict):
|
|
1011
|
+
lines.extend(
|
|
1012
|
+
[
|
|
1013
|
+
"",
|
|
1014
|
+
"## Force Bootstrap Warning",
|
|
1015
|
+
f"- state_file: `{force_warning.get('state_file')}`",
|
|
1016
|
+
f"- will_replace_active_state: `{force_warning.get('will_replace_active_state')}`",
|
|
1017
|
+
f"- preserve_todos_requested: `{force_warning.get('preserve_todos_requested')}`",
|
|
1018
|
+
f"- recommended_scope_migration: {force_warning.get('recommended_scope_migration')}",
|
|
1019
|
+
f"- preserve_todos_option: `{force_warning.get('preserve_todos_option')}`",
|
|
1020
|
+
]
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
onboarding_scan = payload.get("onboarding_scan")
|
|
1024
|
+
if isinstance(onboarding_scan, dict):
|
|
1025
|
+
recent_commits = onboarding_scan.get("recent_commits")
|
|
1026
|
+
recent_commits = recent_commits if isinstance(recent_commits, list) else []
|
|
1027
|
+
signal_files = onboarding_scan.get("signal_files")
|
|
1028
|
+
signal_files = signal_files if isinstance(signal_files, list) else []
|
|
1029
|
+
status_sample = onboarding_scan.get("status_paths_sample")
|
|
1030
|
+
status_sample = status_sample if isinstance(status_sample, list) else []
|
|
1031
|
+
lines.extend(
|
|
1032
|
+
[
|
|
1033
|
+
"",
|
|
1034
|
+
"## Onboarding Scan",
|
|
1035
|
+
f"- schema: `{onboarding_scan.get('schema_version')}`",
|
|
1036
|
+
f"- git_repo: `{onboarding_scan.get('is_git_repo')}`",
|
|
1037
|
+
f"- status_path_count: `{onboarding_scan.get('status_path_count')}`",
|
|
1038
|
+
f"- recent_commits: `{len(recent_commits)}`",
|
|
1039
|
+
f"- signal_files: `{', '.join(str(item) for item in signal_files) or 'none'}`",
|
|
1040
|
+
"- raw_file_bodies_read: `False`",
|
|
1041
|
+
]
|
|
1042
|
+
)
|
|
1043
|
+
if status_sample:
|
|
1044
|
+
lines.append("- status_sample:")
|
|
1045
|
+
for item in status_sample[:5]:
|
|
1046
|
+
lines.append(f" - `{item}`")
|
|
1047
|
+
if recent_commits:
|
|
1048
|
+
lines.append("- recent_commit_sample:")
|
|
1049
|
+
for commit in recent_commits[:5]:
|
|
1050
|
+
if isinstance(commit, dict):
|
|
1051
|
+
lines.append(f" - `{commit.get('hash')}` {commit.get('subject')}")
|
|
1052
|
+
|
|
1053
|
+
candidates = payload.get("onboarding_agent_todo_candidates")
|
|
1054
|
+
candidates = candidates if isinstance(candidates, list) else []
|
|
1055
|
+
if candidates:
|
|
1056
|
+
lines.extend(["", "## Proposed Onboarding Candidates"])
|
|
1057
|
+
for index, candidate in enumerate(candidates, start=1):
|
|
1058
|
+
if not isinstance(candidate, dict):
|
|
1059
|
+
continue
|
|
1060
|
+
lines.append(f"{index}. {candidate.get('text')}")
|
|
1061
|
+
if candidate.get("reason"):
|
|
1062
|
+
lines.append(f" - reason: {candidate.get('reason')}")
|
|
1063
|
+
lines.append(
|
|
1064
|
+
" - metadata: "
|
|
1065
|
+
f"`{candidate.get('task_class') or 'advancement_task'}:{candidate.get('action_kind') or 'analyze'}`"
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
accept_commands = payload.get("accept_candidate_commands")
|
|
1069
|
+
accept_commands = accept_commands if isinstance(accept_commands, list) else []
|
|
1070
|
+
if accept_commands:
|
|
1071
|
+
lines.extend(
|
|
1072
|
+
[
|
|
1073
|
+
"",
|
|
1074
|
+
"## Accept Candidate Commands",
|
|
1075
|
+
"Run only the commands for candidates the user accepts, then refresh state.",
|
|
1076
|
+
]
|
|
1077
|
+
)
|
|
1078
|
+
for command in accept_commands:
|
|
1079
|
+
lines.append(f"- `{command}`")
|
|
1080
|
+
|
|
1081
|
+
if onboarding_scan:
|
|
1082
|
+
lines.extend(
|
|
1083
|
+
[
|
|
1084
|
+
"",
|
|
1085
|
+
"## Autonomy And Heartbeat Choice",
|
|
1086
|
+
]
|
|
1087
|
+
)
|
|
1088
|
+
instruction = payload.get("heartbeat_opt_in_instruction")
|
|
1089
|
+
if instruction:
|
|
1090
|
+
lines.append(f"- heartbeat_opt_in_instruction: {instruction}")
|
|
1091
|
+
if payload.get("autonomous_advance_choice_required"):
|
|
1092
|
+
lines.append(
|
|
1093
|
+
"- Ask the user whether Codex may start autonomous advancement. If autonomous=yes, run the quota "
|
|
1094
|
+
"guard and execute the first accepted Agent Todo; if autonomous=no, stop after writing accepted todos "
|
|
1095
|
+
"and refresh-state."
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
lines.extend(["", "## Next Commands"])
|
|
1099
|
+
for command in payload.get("next_commands") or []:
|
|
1100
|
+
lines.append(f"- `{command}`")
|
|
1101
|
+
|
|
1102
|
+
if payload.get("install_repair_command"):
|
|
1103
|
+
lines.extend(
|
|
1104
|
+
[
|
|
1105
|
+
"",
|
|
1106
|
+
"## Install / Update Hint",
|
|
1107
|
+
str(payload.get("install_repair_note") or ""),
|
|
1108
|
+
"```bash",
|
|
1109
|
+
str(payload.get("install_repair_command")),
|
|
1110
|
+
"```",
|
|
1111
|
+
]
|
|
1112
|
+
)
|
|
1113
|
+
|
|
1114
|
+
if payload.get("private_boundary_note"):
|
|
1115
|
+
lines.extend(["", "## Boundary Note", str(payload.get("private_boundary_note"))])
|
|
1116
|
+
return "\n".join(lines)
|