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/presets.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
PRESET_PICKER_SCHEMA_VERSION = "loopx_beginner_preset_picker_v0"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class BeginnerPreset:
|
|
13
|
+
preset_id: str
|
|
14
|
+
title: str
|
|
15
|
+
tier: str
|
|
16
|
+
maturity: str
|
|
17
|
+
cadence: str
|
|
18
|
+
default_mode: str
|
|
19
|
+
summary: str
|
|
20
|
+
goal_text: str
|
|
21
|
+
capability_path: tuple[str, ...]
|
|
22
|
+
safety_defaults: tuple[str, ...]
|
|
23
|
+
useful_when: tuple[str, ...]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
BEGINNER_PRESETS: tuple[BeginnerPreset, ...] = (
|
|
27
|
+
BeginnerPreset(
|
|
28
|
+
preset_id="daily-triage",
|
|
29
|
+
title="Daily Triage L1",
|
|
30
|
+
tier="beginner_default",
|
|
31
|
+
maturity="L1 report-only",
|
|
32
|
+
cadence="daily or weekday morning",
|
|
33
|
+
default_mode="report_only",
|
|
34
|
+
summary=(
|
|
35
|
+
"Inspect LoopX status, active todos, open gates, stale signals, and the "
|
|
36
|
+
"single next action so the owner gets a useful project digest."
|
|
37
|
+
),
|
|
38
|
+
goal_text=(
|
|
39
|
+
"Run Daily Triage L1 for this repository: inspect LoopX status, active "
|
|
40
|
+
"todos, open gates, stale signals, and next actions; write a compact "
|
|
41
|
+
"report and ask before code edits or external writes."
|
|
42
|
+
),
|
|
43
|
+
capability_path=(
|
|
44
|
+
"status",
|
|
45
|
+
"todo projection",
|
|
46
|
+
"quota should-run",
|
|
47
|
+
"thin evidence log",
|
|
48
|
+
),
|
|
49
|
+
safety_defaults=(
|
|
50
|
+
"No code edits by default.",
|
|
51
|
+
"No external writes by default.",
|
|
52
|
+
"Human review before changing project files, publishing, or closing gates.",
|
|
53
|
+
),
|
|
54
|
+
useful_when=(
|
|
55
|
+
"You want LoopX to keep a long-running repo goal warm.",
|
|
56
|
+
"You need a low-risk first demo that still uses real LoopX state.",
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
BeginnerPreset(
|
|
60
|
+
preset_id="changelog-draft",
|
|
61
|
+
title="Changelog Draft L1",
|
|
62
|
+
tier="beginner_default",
|
|
63
|
+
maturity="L1 draft-only",
|
|
64
|
+
cadence="per release, or daily during release week",
|
|
65
|
+
default_mode="draft_only",
|
|
66
|
+
summary=(
|
|
67
|
+
"Turn recent merged work and LoopX run history into a release-note draft "
|
|
68
|
+
"that answers the upgrade decision and preserves PR-backed product groups."
|
|
69
|
+
),
|
|
70
|
+
goal_text=(
|
|
71
|
+
"Run Changelog Draft L1 for this repository: summarize recent merged "
|
|
72
|
+
"work and LoopX run history into a human-reviewed release-note draft "
|
|
73
|
+
"that states who should upgrade, what the release solves, whether it is "
|
|
74
|
+
"breaking, how to verify it, and who contributed; preserve PR-backed "
|
|
75
|
+
"product groups and optional-capability activation/rollback guidance; "
|
|
76
|
+
"do not publish."
|
|
77
|
+
),
|
|
78
|
+
capability_path=(
|
|
79
|
+
"history",
|
|
80
|
+
"release notes draft",
|
|
81
|
+
"PR link evidence",
|
|
82
|
+
"human review gate",
|
|
83
|
+
),
|
|
84
|
+
safety_defaults=(
|
|
85
|
+
"Draft only; no release creation or publish action.",
|
|
86
|
+
"Prefer PR links and compact public evidence over raw logs.",
|
|
87
|
+
"Human review before tagging, announcing, or editing canonical docs.",
|
|
88
|
+
),
|
|
89
|
+
useful_when=(
|
|
90
|
+
"You want visible maintainer value without granting write authority.",
|
|
91
|
+
"You need release copy that is grounded in recent work instead of memory.",
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
BeginnerPreset(
|
|
95
|
+
preset_id="pr-watch",
|
|
96
|
+
title="PR Watch L1",
|
|
97
|
+
tier="beginner_default",
|
|
98
|
+
maturity="L1 watch-only",
|
|
99
|
+
cadence="every 10-30 minutes while the PR is active",
|
|
100
|
+
default_mode="watch_only",
|
|
101
|
+
summary=(
|
|
102
|
+
"Watch review, CI, and merge blockers for a PR, then report what changed "
|
|
103
|
+
"and what needs a human decision."
|
|
104
|
+
),
|
|
105
|
+
goal_text=(
|
|
106
|
+
"Run PR Watch L1 for this repository: watch review, CI, and merge "
|
|
107
|
+
"blockers for the target PR; report material changes, blockers, and "
|
|
108
|
+
"next human decisions; do not auto-merge."
|
|
109
|
+
),
|
|
110
|
+
capability_path=(
|
|
111
|
+
"continuous monitor todo",
|
|
112
|
+
"quota monitor-poll",
|
|
113
|
+
"CI/review blocker summary",
|
|
114
|
+
"human merge gate",
|
|
115
|
+
),
|
|
116
|
+
safety_defaults=(
|
|
117
|
+
"No auto-merge.",
|
|
118
|
+
"No code edits unless the owner upgrades the preset to an explicit L2 fix lane.",
|
|
119
|
+
"Quiet unchanged polls should not spend quota.",
|
|
120
|
+
),
|
|
121
|
+
useful_when=(
|
|
122
|
+
"You want fewer manual PR refreshes while keeping merge authority human.",
|
|
123
|
+
"You need an automation-friendly monitor that still produces concrete blockers.",
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
BeginnerPreset(
|
|
127
|
+
preset_id="ci-sweeper",
|
|
128
|
+
title="CI Sweeper L2",
|
|
129
|
+
tier="advanced_opt_in",
|
|
130
|
+
maturity="L2 opt-in patch lane",
|
|
131
|
+
cadence="on failing checks, or every 30-60 minutes while a PR is active",
|
|
132
|
+
default_mode="dry_run_report_first",
|
|
133
|
+
summary=(
|
|
134
|
+
"Classify failing checks, propose the smallest bounded fix, and only draft "
|
|
135
|
+
"a worktree patch after explicit owner opt-in."
|
|
136
|
+
),
|
|
137
|
+
goal_text=(
|
|
138
|
+
"Run CI Sweeper L2 for this repository: inspect failing checks and recent "
|
|
139
|
+
"CI context, classify the likely fix, then produce a dry-run report first. "
|
|
140
|
+
"Only after explicit owner opt-in, draft a bounded patch in an isolated "
|
|
141
|
+
"codex/ worktree, run focused verification, and stop for human review before "
|
|
142
|
+
"push, merge, rerun-cost expansion, or external writes."
|
|
143
|
+
),
|
|
144
|
+
capability_path=(
|
|
145
|
+
"failing-check intake",
|
|
146
|
+
"bounded codex/ worktree patch",
|
|
147
|
+
"focused verifier or smoke",
|
|
148
|
+
"human review gate",
|
|
149
|
+
),
|
|
150
|
+
safety_defaults=(
|
|
151
|
+
"Dry-run report first; no patch until owner opt-in.",
|
|
152
|
+
"Patch only in an isolated codex/ worktree with a narrow allowed scope.",
|
|
153
|
+
"Verifier or focused smoke must pass before marking a patch ready.",
|
|
154
|
+
"Human review before push, merge, rerun-cost expansion, or external writes.",
|
|
155
|
+
"Escalate after repeated failures or unchanged error signatures.",
|
|
156
|
+
),
|
|
157
|
+
useful_when=(
|
|
158
|
+
"A PR or main branch has boring, well-scoped check failures.",
|
|
159
|
+
"You want LoopX to reduce maintainer toil without granting merge authority.",
|
|
160
|
+
),
|
|
161
|
+
),
|
|
162
|
+
BeginnerPreset(
|
|
163
|
+
preset_id="dependency-sweeper",
|
|
164
|
+
title="Dependency Sweeper L2",
|
|
165
|
+
tier="advanced_opt_in",
|
|
166
|
+
maturity="L2 opt-in dependency lane",
|
|
167
|
+
cadence="weekly, per security notice, or during release hardening",
|
|
168
|
+
default_mode="policy_report_first",
|
|
169
|
+
summary=(
|
|
170
|
+
"Review dependency update candidates against a patch/minor policy, denylist, "
|
|
171
|
+
"and verifier plan before drafting any update patch."
|
|
172
|
+
),
|
|
173
|
+
goal_text=(
|
|
174
|
+
"Run Dependency Sweeper L2 for this repository: inspect dependency update "
|
|
175
|
+
"candidates, apply a patch/minor policy and denylist, then produce a policy "
|
|
176
|
+
"report first. Only after explicit owner opt-in, draft a bounded dependency "
|
|
177
|
+
"patch in an isolated codex/ worktree, run focused verification, and stop "
|
|
178
|
+
"for human review before push, merge, publish, or rollout."
|
|
179
|
+
),
|
|
180
|
+
capability_path=(
|
|
181
|
+
"dependency candidate intake",
|
|
182
|
+
"patch/minor policy and denylist",
|
|
183
|
+
"bounded codex/ worktree patch",
|
|
184
|
+
"focused verifier or smoke",
|
|
185
|
+
"human review gate",
|
|
186
|
+
),
|
|
187
|
+
safety_defaults=(
|
|
188
|
+
"Policy report first; no dependency edit until owner opt-in.",
|
|
189
|
+
"Default to patch and minor updates; deny major, runtime, lockfile-wide, "
|
|
190
|
+
"and toolchain jumps unless explicitly allowed.",
|
|
191
|
+
"Verifier or focused smoke must pass before marking an update ready.",
|
|
192
|
+
"Human review before push, merge, publish, rollout, or dependency source changes.",
|
|
193
|
+
"Escalate when updates fail twice, widen scope, or hit a denied package.",
|
|
194
|
+
),
|
|
195
|
+
useful_when=(
|
|
196
|
+
"A repo has recurring safe dependency bumps or security patch noise.",
|
|
197
|
+
"You want update toil reduced while keeping rollout and publish authority human.",
|
|
198
|
+
),
|
|
199
|
+
),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
PRESET_IDS = tuple(preset.preset_id for preset in BEGINNER_PRESETS)
|
|
203
|
+
DEFAULT_PRESET_IDS = tuple(
|
|
204
|
+
preset.preset_id for preset in BEGINNER_PRESETS if preset.tier == "beginner_default"
|
|
205
|
+
)
|
|
206
|
+
ADVANCED_PRESET_IDS = tuple(
|
|
207
|
+
preset.preset_id for preset in BEGINNER_PRESETS if preset.tier == "advanced_opt_in"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _shell(value: str) -> str:
|
|
212
|
+
return shlex.quote(value)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _preset_commands(
|
|
216
|
+
preset: BeginnerPreset,
|
|
217
|
+
*,
|
|
218
|
+
cli_bin: str,
|
|
219
|
+
project: str,
|
|
220
|
+
goal_id: str,
|
|
221
|
+
agent_id: str,
|
|
222
|
+
agent_scope: str,
|
|
223
|
+
) -> dict[str, str]:
|
|
224
|
+
return {
|
|
225
|
+
"slash_start": f"/loopx {preset.goal_text}",
|
|
226
|
+
"cli_start": (
|
|
227
|
+
f"{cli_bin} start-goal --guided --project {_shell(project)} "
|
|
228
|
+
f"--goal-text {_shell(preset.goal_text)}"
|
|
229
|
+
),
|
|
230
|
+
"quota_guard": (
|
|
231
|
+
f"{cli_bin} --format json quota should-run "
|
|
232
|
+
f"--goal-id {_shell(goal_id)} --agent-id {_shell(agent_id)} "
|
|
233
|
+
"--runtime-profile codex_app_heartbeat"
|
|
234
|
+
),
|
|
235
|
+
"heartbeat_prompt": (
|
|
236
|
+
f"{cli_bin} heartbeat-prompt --thin --goal-id {_shell(goal_id)} "
|
|
237
|
+
f"--agent-id {_shell(agent_id)} --agent-scope {_shell(agent_scope)} "
|
|
238
|
+
"--runtime-profile codex_app_heartbeat"
|
|
239
|
+
),
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _preset_to_dict(
|
|
244
|
+
preset: BeginnerPreset,
|
|
245
|
+
*,
|
|
246
|
+
cli_bin: str,
|
|
247
|
+
project: str,
|
|
248
|
+
goal_id: str,
|
|
249
|
+
agent_id: str,
|
|
250
|
+
agent_scope: str,
|
|
251
|
+
) -> dict[str, Any]:
|
|
252
|
+
return {
|
|
253
|
+
"id": preset.preset_id,
|
|
254
|
+
"title": preset.title,
|
|
255
|
+
"tier": preset.tier,
|
|
256
|
+
"maturity": preset.maturity,
|
|
257
|
+
"cadence": preset.cadence,
|
|
258
|
+
"default_mode": preset.default_mode,
|
|
259
|
+
"summary": preset.summary,
|
|
260
|
+
"goal_text": preset.goal_text,
|
|
261
|
+
"capability_path": list(preset.capability_path),
|
|
262
|
+
"safety_defaults": list(preset.safety_defaults),
|
|
263
|
+
"useful_when": list(preset.useful_when),
|
|
264
|
+
"commands": _preset_commands(
|
|
265
|
+
preset,
|
|
266
|
+
cli_bin=cli_bin,
|
|
267
|
+
project=project,
|
|
268
|
+
goal_id=goal_id,
|
|
269
|
+
agent_id=agent_id,
|
|
270
|
+
agent_scope=agent_scope,
|
|
271
|
+
),
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def build_beginner_preset_packet(
|
|
276
|
+
*,
|
|
277
|
+
preset_id: str | None = None,
|
|
278
|
+
cli_bin: str = "loopx",
|
|
279
|
+
project: str = ".",
|
|
280
|
+
goal_id: str = "<goal-id>",
|
|
281
|
+
agent_id: str = "<agent-id>",
|
|
282
|
+
agent_scope: str = "<agent-scope>",
|
|
283
|
+
) -> dict[str, Any]:
|
|
284
|
+
presets = BEGINNER_PRESETS
|
|
285
|
+
if preset_id:
|
|
286
|
+
presets = tuple(preset for preset in BEGINNER_PRESETS if preset.preset_id == preset_id)
|
|
287
|
+
if not presets:
|
|
288
|
+
raise ValueError(f"unknown beginner preset: {preset_id}")
|
|
289
|
+
return {
|
|
290
|
+
"ok": True,
|
|
291
|
+
"schema_version": PRESET_PICKER_SCHEMA_VERSION,
|
|
292
|
+
"summary": (
|
|
293
|
+
"Beginner LoopX presets compile common loop-engineering use cases into "
|
|
294
|
+
"existing LoopX start, quota, heartbeat, and evidence flows."
|
|
295
|
+
),
|
|
296
|
+
"mutation_policy": "read_only; renders commands only; does not write registry, state, automation, PRs, or docs",
|
|
297
|
+
"recommended_order": list(PRESET_IDS),
|
|
298
|
+
"default_preset_ids": list(DEFAULT_PRESET_IDS),
|
|
299
|
+
"advanced_preset_ids": list(ADVANCED_PRESET_IDS),
|
|
300
|
+
"placeholders": {
|
|
301
|
+
"goal_id": goal_id,
|
|
302
|
+
"agent_id": agent_id,
|
|
303
|
+
"agent_scope": agent_scope,
|
|
304
|
+
"project": project,
|
|
305
|
+
},
|
|
306
|
+
"common_setup": [
|
|
307
|
+
f"{cli_bin} doctor",
|
|
308
|
+
f"{cli_bin} slash-commands --install",
|
|
309
|
+
f"{cli_bin} preset list",
|
|
310
|
+
],
|
|
311
|
+
"presets": [
|
|
312
|
+
_preset_to_dict(
|
|
313
|
+
preset,
|
|
314
|
+
cli_bin=cli_bin,
|
|
315
|
+
project=project,
|
|
316
|
+
goal_id=goal_id,
|
|
317
|
+
agent_id=agent_id,
|
|
318
|
+
agent_scope=agent_scope,
|
|
319
|
+
)
|
|
320
|
+
for preset in presets
|
|
321
|
+
],
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def render_beginner_preset_markdown(payload: dict[str, Any]) -> str:
|
|
326
|
+
lines = [
|
|
327
|
+
"# LoopX beginner preset picker",
|
|
328
|
+
"",
|
|
329
|
+
str(payload.get("summary") or ""),
|
|
330
|
+
"",
|
|
331
|
+
f"Mutation policy: {payload.get('mutation_policy')}",
|
|
332
|
+
"",
|
|
333
|
+
"Common setup:",
|
|
334
|
+
]
|
|
335
|
+
for command in payload.get("common_setup", []):
|
|
336
|
+
lines.append(f"- `{command}`")
|
|
337
|
+
lines.append("")
|
|
338
|
+
presets = payload.get("presets") if isinstance(payload.get("presets"), list) else []
|
|
339
|
+
for preset in presets:
|
|
340
|
+
if not isinstance(preset, dict):
|
|
341
|
+
continue
|
|
342
|
+
lines.extend(
|
|
343
|
+
[
|
|
344
|
+
f"## {preset.get('title')}",
|
|
345
|
+
"",
|
|
346
|
+
f"- id: `{preset.get('id')}`",
|
|
347
|
+
f"- tier: `{preset.get('tier')}`",
|
|
348
|
+
f"- maturity: {preset.get('maturity')}",
|
|
349
|
+
f"- cadence: {preset.get('cadence')}",
|
|
350
|
+
f"- default mode: `{preset.get('default_mode')}`",
|
|
351
|
+
f"- summary: {preset.get('summary')}",
|
|
352
|
+
"",
|
|
353
|
+
"Useful when:",
|
|
354
|
+
]
|
|
355
|
+
)
|
|
356
|
+
for item in preset.get("useful_when", []):
|
|
357
|
+
lines.append(f"- {item}")
|
|
358
|
+
lines.extend(["", "Safety defaults:"])
|
|
359
|
+
for item in preset.get("safety_defaults", []):
|
|
360
|
+
lines.append(f"- {item}")
|
|
361
|
+
lines.extend(["", "Commands:", ""])
|
|
362
|
+
commands = preset.get("commands") if isinstance(preset.get("commands"), dict) else {}
|
|
363
|
+
for label in ("slash_start", "cli_start", "quota_guard", "heartbeat_prompt"):
|
|
364
|
+
command = commands.get(label)
|
|
365
|
+
if command:
|
|
366
|
+
lines.extend([f"{label}:", "```bash", str(command), "```", ""])
|
|
367
|
+
lines.append(f"Show only this preset: `loopx preset show {preset.get('id')}`")
|
|
368
|
+
lines.append("")
|
|
369
|
+
return "\n".join(lines).rstrip() + "\n"
|
loopx/project_alias.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .paths import DEFAULT_RUNTIME_ROOT, global_registry_path
|
|
10
|
+
from .registry import registry_goals
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _resolve_path(path: Path) -> Path:
|
|
14
|
+
try:
|
|
15
|
+
return path.expanduser().resolve()
|
|
16
|
+
except OSError:
|
|
17
|
+
return path.expanduser().absolute()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _run_git(project: Path, *args: str) -> subprocess.CompletedProcess[str] | None:
|
|
21
|
+
try:
|
|
22
|
+
return subprocess.run(
|
|
23
|
+
["git", "-C", str(project), *args],
|
|
24
|
+
text=True,
|
|
25
|
+
stdout=subprocess.PIPE,
|
|
26
|
+
stderr=subprocess.PIPE,
|
|
27
|
+
check=False,
|
|
28
|
+
timeout=2,
|
|
29
|
+
)
|
|
30
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _git_root(project: Path) -> Path | None:
|
|
35
|
+
result = _run_git(project, "rev-parse", "--show-toplevel")
|
|
36
|
+
if result is None or result.returncode != 0:
|
|
37
|
+
return None
|
|
38
|
+
value = result.stdout.strip()
|
|
39
|
+
return _resolve_path(Path(value)) if value else None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _git_common_dir(project: Path) -> Path | None:
|
|
43
|
+
root = _git_root(project)
|
|
44
|
+
if root is None:
|
|
45
|
+
return None
|
|
46
|
+
result = _run_git(root, "rev-parse", "--git-common-dir")
|
|
47
|
+
if result is None or result.returncode != 0:
|
|
48
|
+
return None
|
|
49
|
+
value = result.stdout.strip()
|
|
50
|
+
if not value:
|
|
51
|
+
return None
|
|
52
|
+
common = Path(value).expanduser()
|
|
53
|
+
if not common.is_absolute():
|
|
54
|
+
common = root / common
|
|
55
|
+
return _resolve_path(common)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _primary_repo_from_common_dir(common_dir: Path | None) -> Path | None:
|
|
59
|
+
if common_dir is None:
|
|
60
|
+
return None
|
|
61
|
+
if common_dir.name == ".git":
|
|
62
|
+
return _resolve_path(common_dir.parent)
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _default_global_registry_path() -> Path:
|
|
67
|
+
runtime_env = os.environ.get("LOOPX_RUNTIME_ROOT")
|
|
68
|
+
runtime_root = Path(runtime_env).expanduser() if runtime_env else DEFAULT_RUNTIME_ROOT
|
|
69
|
+
return global_registry_path(runtime_root)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _read_global_registry(path: Path) -> dict[str, Any] | None:
|
|
73
|
+
try:
|
|
74
|
+
with path.expanduser().open(encoding="utf-8") as f:
|
|
75
|
+
payload = json.load(f)
|
|
76
|
+
except (FileNotFoundError, OSError, json.JSONDecodeError):
|
|
77
|
+
return None
|
|
78
|
+
return payload if isinstance(payload, dict) else None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _source_repo_for_goal(goal: dict[str, Any]) -> Path | None:
|
|
82
|
+
repo = goal.get("repo")
|
|
83
|
+
if repo:
|
|
84
|
+
return _resolve_path(Path(str(repo)))
|
|
85
|
+
source_registry = goal.get("source_registry")
|
|
86
|
+
if not source_registry:
|
|
87
|
+
return None
|
|
88
|
+
path = _resolve_path(Path(str(source_registry)))
|
|
89
|
+
try:
|
|
90
|
+
return path.parents[1]
|
|
91
|
+
except IndexError:
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _same_path(left: Path | None, right: Path | None) -> bool:
|
|
96
|
+
if left is None or right is None:
|
|
97
|
+
return False
|
|
98
|
+
return _resolve_path(left) == _resolve_path(right)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def resolve_canonical_project_alias(
|
|
102
|
+
project: Path,
|
|
103
|
+
*,
|
|
104
|
+
goal_id: str | None = None,
|
|
105
|
+
global_registry: Path | None = None,
|
|
106
|
+
) -> dict[str, Any]:
|
|
107
|
+
"""Map linked git worktrees back to the canonical LoopX source project."""
|
|
108
|
+
|
|
109
|
+
resolved_project = _resolve_path(project)
|
|
110
|
+
target_common_dir = _git_common_dir(resolved_project)
|
|
111
|
+
global_path = _resolve_path(global_registry or _default_global_registry_path())
|
|
112
|
+
payload = _read_global_registry(global_path)
|
|
113
|
+
if not payload or target_common_dir is None:
|
|
114
|
+
return {
|
|
115
|
+
"applied": False,
|
|
116
|
+
"kind": "none",
|
|
117
|
+
"input_project": str(resolved_project),
|
|
118
|
+
"global_registry": str(global_path),
|
|
119
|
+
"git_common_dir": str(target_common_dir) if target_common_dir else None,
|
|
120
|
+
"reason": "global registry or git common-dir unavailable",
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
primary_repo = _primary_repo_from_common_dir(target_common_dir)
|
|
124
|
+
current_registry = _resolve_path(resolved_project / ".loopx" / "registry.json")
|
|
125
|
+
candidates: list[dict[str, Any]] = []
|
|
126
|
+
for goal in registry_goals(payload):
|
|
127
|
+
candidate_goal_id = str(goal.get("id") or "")
|
|
128
|
+
if goal_id and candidate_goal_id != goal_id:
|
|
129
|
+
continue
|
|
130
|
+
source_repo = _source_repo_for_goal(goal)
|
|
131
|
+
if source_repo is None:
|
|
132
|
+
continue
|
|
133
|
+
source_common_dir = _git_common_dir(source_repo)
|
|
134
|
+
if not _same_path(source_common_dir, target_common_dir):
|
|
135
|
+
continue
|
|
136
|
+
source_registry_value = goal.get("source_registry")
|
|
137
|
+
source_registry = _resolve_path(Path(str(source_registry_value))) if source_registry_value else None
|
|
138
|
+
candidates.append(
|
|
139
|
+
{
|
|
140
|
+
"goal_id": candidate_goal_id,
|
|
141
|
+
"source_repo": str(source_repo),
|
|
142
|
+
"source_registry": str(source_registry) if source_registry else None,
|
|
143
|
+
"source_is_primary_repo": _same_path(source_repo, primary_repo),
|
|
144
|
+
"source_is_current_project": _same_path(source_repo, resolved_project),
|
|
145
|
+
"source_registry_is_current_project": _same_path(source_registry, current_registry),
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if not candidates:
|
|
150
|
+
return {
|
|
151
|
+
"applied": False,
|
|
152
|
+
"kind": "none",
|
|
153
|
+
"input_project": str(resolved_project),
|
|
154
|
+
"global_registry": str(global_path),
|
|
155
|
+
"git_common_dir": str(target_common_dir),
|
|
156
|
+
"primary_repo": str(primary_repo) if primary_repo else None,
|
|
157
|
+
"reason": "no global source_registry shares this git common-dir",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if any(item["source_registry_is_current_project"] for item in candidates):
|
|
161
|
+
current_ids = [
|
|
162
|
+
str(item["goal_id"])
|
|
163
|
+
for item in candidates
|
|
164
|
+
if item["source_registry_is_current_project"] and item.get("goal_id")
|
|
165
|
+
]
|
|
166
|
+
non_current_primary = [
|
|
167
|
+
item
|
|
168
|
+
for item in candidates
|
|
169
|
+
if item["source_is_primary_repo"] and not item["source_registry_is_current_project"]
|
|
170
|
+
]
|
|
171
|
+
if not non_current_primary:
|
|
172
|
+
return {
|
|
173
|
+
"applied": False,
|
|
174
|
+
"kind": "current_project_is_registered_source",
|
|
175
|
+
"input_project": str(resolved_project),
|
|
176
|
+
"global_registry": str(global_path),
|
|
177
|
+
"git_common_dir": str(target_common_dir),
|
|
178
|
+
"primary_repo": str(primary_repo) if primary_repo else None,
|
|
179
|
+
"registered_goal_ids": current_ids,
|
|
180
|
+
"reason": "current project already owns a registered source_registry route",
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
def rank(item: dict[str, Any]) -> tuple[int, int, int, str]:
|
|
184
|
+
return (
|
|
185
|
+
0 if item["source_is_primary_repo"] else 1,
|
|
186
|
+
0 if not item["source_is_current_project"] else 1,
|
|
187
|
+
0 if item.get("source_registry") else 1,
|
|
188
|
+
str(item.get("source_registry") or item.get("source_repo") or ""),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
selected = sorted(candidates, key=rank)[0]
|
|
192
|
+
selected_repo = Path(str(selected["source_repo"]))
|
|
193
|
+
if _same_path(selected_repo, resolved_project):
|
|
194
|
+
return {
|
|
195
|
+
"applied": False,
|
|
196
|
+
"kind": "selected_current_project",
|
|
197
|
+
"input_project": str(resolved_project),
|
|
198
|
+
"global_registry": str(global_path),
|
|
199
|
+
"git_common_dir": str(target_common_dir),
|
|
200
|
+
"primary_repo": str(primary_repo) if primary_repo else None,
|
|
201
|
+
"candidate_goal_ids": sorted({str(item["goal_id"]) for item in candidates if item.get("goal_id")}),
|
|
202
|
+
"reason": "best global route is the current project",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"applied": True,
|
|
207
|
+
"kind": "git_worktree_canonical_source_registry",
|
|
208
|
+
"input_project": str(resolved_project),
|
|
209
|
+
"canonical_project": str(selected_repo),
|
|
210
|
+
"global_registry": str(global_path),
|
|
211
|
+
"git_common_dir": str(target_common_dir),
|
|
212
|
+
"primary_repo": str(primary_repo) if primary_repo else None,
|
|
213
|
+
"source_registry": selected.get("source_registry"),
|
|
214
|
+
"candidate_goal_ids": sorted({str(item["goal_id"]) for item in candidates if item.get("goal_id")}),
|
|
215
|
+
"selected_goal_id": selected.get("goal_id"),
|
|
216
|
+
"reason": "project is a git worktree; reusing canonical source_registry route",
|
|
217
|
+
}
|