phase-loop-runtime 0.2.0__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.
- phase_loop_runtime/__init__.py +20 -0
- phase_loop_runtime/_contract_docs/phase-loop/extraction-readiness.md +90 -0
- phase_loop_runtime/_contract_docs/phase-loop/granular-execution-policy.md +137 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-capability-matrix.md +510 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-skill-matrix.md +125 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-substrate-manifest.md +232 -0
- phase_loop_runtime/_contract_docs/phase-loop/pi-loop-control.md +247 -0
- phase_loop_runtime/_contract_docs/phase-loop/protocol.md +2759 -0
- phase_loop_runtime/_contract_docs/phase-loop/runtime-boundary.md +289 -0
- phase_loop_runtime/_contract_docs/phase-loop/spec-discovery-roots.md +47 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-governed-pipeline.md +42 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-portal-projection.md +34 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-regenesis.md +29 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-report.md +32 -0
- phase_loop_runtime/_contract_docs/runtime/verification-evidence-contract.md +52 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/commands.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/mixed.json +5 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/timeout-note.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/redacted-summary.json +6 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/sample.json +7 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/edge.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-a.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-b.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-c.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/claimed-results.json +5 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/metrics.json +9 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/findings.md +10 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/scores.json +4 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/summary.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/bundle-check.json +8 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/operator-note.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/events-sample.jsonl +3 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/receipt.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/evidence.json +13 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/summary.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/matrix.json +9 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/notes.md +4 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/console-summary.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/screenshot-standin.ppm +5 -0
- phase_loop_runtime/adoption_bundle.py +273 -0
- phase_loop_runtime/advisor_board/CONTRACTS.md +257 -0
- phase_loop_runtime/advisor_board/__init__.py +288 -0
- phase_loop_runtime/advisor_board/backing.py +137 -0
- phase_loop_runtime/advisor_board/backing_omnigent.py +498 -0
- phase_loop_runtime/advisor_board/config.py +225 -0
- phase_loop_runtime/advisor_board/events.py +115 -0
- phase_loop_runtime/advisor_board/fixtures/advisor-boards.example.toml +59 -0
- phase_loop_runtime/advisor_board/fixtures.py +91 -0
- phase_loop_runtime/advisor_board/harness_mapping.py +125 -0
- phase_loop_runtime/advisor_board/matrix.py +146 -0
- phase_loop_runtime/advisor_board/observability.py +481 -0
- phase_loop_runtime/advisor_board/presets.py +188 -0
- phase_loop_runtime/advisor_board/registries.py +309 -0
- phase_loop_runtime/advisor_board/resolver.py +253 -0
- phase_loop_runtime/advisor_board/schema.py +269 -0
- phase_loop_runtime/advisor_board/standin.py +166 -0
- phase_loop_runtime/advisor_board/validation.py +159 -0
- phase_loop_runtime/agent_runtime_provider.py +676 -0
- phase_loop_runtime/baml_modular.py +656 -0
- phase_loop_runtime/baml_src/dotfiles_adoption_manifest.baml +37 -0
- phase_loop_runtime/baml_src/dotfiles_c4_document.baml +14 -0
- phase_loop_runtime/baml_src/dotfiles_plan_manifest.baml +50 -0
- phase_loop_runtime/baml_src/dotfiles_runtime_projection.baml +16 -0
- phase_loop_runtime/baml_src/dotfiles_task_catalog.baml +20 -0
- phase_loop_runtime/baml_src/emit_phase_closeout.baml +86 -0
- phase_loop_runtime/baml_src/evaluate_suspected_fake_evidence.baml +36 -0
- phase_loop_runtime/baml_src/verification_evidence.baml +40 -0
- phase_loop_runtime/broker.py +181 -0
- phase_loop_runtime/build_bundle.py +294 -0
- phase_loop_runtime/capability_registry.py +835 -0
- phase_loop_runtime/classifier.py +87 -0
- phase_loop_runtime/claude_agent_view.py +625 -0
- phase_loop_runtime/claude_channel_sidecar.py +1086 -0
- phase_loop_runtime/cli.py +2599 -0
- phase_loop_runtime/closeout.py +865 -0
- phase_loop_runtime/closeout_classifier.py +120 -0
- phase_loop_runtime/closeout_evidence_audit.py +112 -0
- phase_loop_runtime/closeout_validation.py +144 -0
- phase_loop_runtime/closeout_validators.py +203 -0
- phase_loop_runtime/consiliency_gates.py +374 -0
- phase_loop_runtime/consiliency_ingest.py +399 -0
- phase_loop_runtime/consiliency_layout.py +176 -0
- phase_loop_runtime/consiliency_scaffold.py +305 -0
- phase_loop_runtime/cross_repo_channel.py +331 -0
- phase_loop_runtime/discovery.py +2368 -0
- phase_loop_runtime/dispatch_lock.py +167 -0
- phase_loop_runtime/doc_delta_validator.py +98 -0
- phase_loop_runtime/docs_audit.py +285 -0
- phase_loop_runtime/docs_freshness.py +468 -0
- phase_loop_runtime/docs_surfaces.py +150 -0
- phase_loop_runtime/dotfiles_profile_plugin.py +96 -0
- phase_loop_runtime/events.py +154 -0
- phase_loop_runtime/events_migration.py +96 -0
- phase_loop_runtime/evidence_audit.py +1082 -0
- phase_loop_runtime/evidence_audit_config.py +172 -0
- phase_loop_runtime/fleet_map.py +426 -0
- phase_loop_runtime/fleet_metrics.py +298 -0
- phase_loop_runtime/fleet_metrics_export.py +196 -0
- phase_loop_runtime/git_discipline.py +479 -0
- phase_loop_runtime/git_ops.py +122 -0
- phase_loop_runtime/git_topology.py +252 -0
- phase_loop_runtime/governed_bundle.py +137 -0
- phase_loop_runtime/governed_premerge.py +219 -0
- phase_loop_runtime/governed_review.py +264 -0
- phase_loop_runtime/handoff.py +932 -0
- phase_loop_runtime/handoff_path.py +14 -0
- phase_loop_runtime/injection.py +740 -0
- phase_loop_runtime/install_status.py +88 -0
- phase_loop_runtime/lane_scheduler.py +317 -0
- phase_loop_runtime/launcher.py +2760 -0
- phase_loop_runtime/lease_store.py +553 -0
- phase_loop_runtime/maintenance.py +401 -0
- phase_loop_runtime/migrate_handoffs.py +201 -0
- phase_loop_runtime/models.py +2144 -0
- phase_loop_runtime/observability.py +1290 -0
- phase_loop_runtime/panel_invoker.py +1638 -0
- phase_loop_runtime/phase_loop_drift_audit.py +388 -0
- phase_loop_runtime/phase_worktree_executor.py +433 -0
- phase_loop_runtime/pipeline_adapter/__init__.py +12 -0
- phase_loop_runtime/pipeline_adapter/branch_ops.py +274 -0
- phase_loop_runtime/pipeline_adapter/flag.py +55 -0
- phase_loop_runtime/pipeline_adapter/markers.py +13 -0
- phase_loop_runtime/pipeline_adapter/merge_policy.py +72 -0
- phase_loop_runtime/pipeline_adapter/ratification.py +100 -0
- phase_loop_runtime/pipeline_adapter/sibling_matcher.py +123 -0
- phase_loop_runtime/plan_ir.py +523 -0
- phase_loop_runtime/plan_manifest.py +415 -0
- phase_loop_runtime/planner_validation.py +176 -0
- phase_loop_runtime/profiles.py +462 -0
- phase_loop_runtime/prompts.py +365 -0
- phase_loop_runtime/provenance.py +165 -0
- phase_loop_runtime/publishing.py +339 -0
- phase_loop_runtime/py.typed +1 -0
- phase_loop_runtime/reconcile.py +1550 -0
- phase_loop_runtime/redaction.py +184 -0
- phase_loop_runtime/reflection_sync.py +293 -0
- phase_loop_runtime/release_guard.py +186 -0
- phase_loop_runtime/render.py +432 -0
- phase_loop_runtime/repo_validation.py +427 -0
- phase_loop_runtime/review_summary.py +150 -0
- phase_loop_runtime/roadmap_lint.py +425 -0
- phase_loop_runtime/route_log.py +59 -0
- phase_loop_runtime/route_policy/__init__.py +11 -0
- phase_loop_runtime/route_policy/fixtures/route_selection.golden.json +263 -0
- phase_loop_runtime/runner.py +8400 -0
- phase_loop_runtime/runtime_paths.py +225 -0
- phase_loop_runtime/runtime_projection.py +113 -0
- phase_loop_runtime/runtime_resources.py +36 -0
- phase_loop_runtime/schema_export.py +292 -0
- phase_loop_runtime/schemas/phase_loop_closeout.schema.json +366 -0
- phase_loop_runtime/skill_install.py +176 -0
- phase_loop_runtime/skill_inventory.py +662 -0
- phase_loop_runtime/skill_paths.py +42 -0
- phase_loop_runtime/skill_sources_plugin.py +37 -0
- phase_loop_runtime/skills_bundle/claude-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/claude-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/claude-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-execute-detailed/SKILL.md +152 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/SKILL.md +745 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/claude-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-phase-loop/SKILL.md +115 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/SKILL.md +439 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/SKILL.md +334 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/SKILL.md +776 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/claude-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/SKILL.md +243 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/SKILL.md +249 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/SKILL.md +74 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/codex-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/codex-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/codex-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-execute-detailed/SKILL.md +154 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/SKILL.md +231 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/codex-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-phase-loop/SKILL.md +248 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/SKILL.md +150 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/SKILL.md +307 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/codex-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/SKILL.md +94 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/SKILL.md +114 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/SKILL.md +102 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/gemini-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-execute-detailed/SKILL.md +144 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/SKILL.md +198 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/gemini-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-phase-loop/SKILL.md +111 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/SKILL.md +147 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/SKILL.md +217 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/gemini-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/SKILL.md +87 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/SKILL.md +109 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/SKILL.md +98 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/opencode-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-execute-detailed/SKILL.md +144 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/SKILL.md +196 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/opencode-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-phase-loop/SKILL.md +113 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/SKILL.md +147 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/SKILL.md +211 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/opencode-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/SKILL.md +87 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/SKILL.md +109 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/SKILL.md +98 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/state.py +156 -0
- phase_loop_runtime/state_degradation.py +136 -0
- phase_loop_runtime/state_ops.py +676 -0
- phase_loop_runtime/train_ledger.py +215 -0
- phase_loop_runtime/train_roadmap.py +339 -0
- phase_loop_runtime/train_runner.py +1197 -0
- phase_loop_runtime/verification_evidence.py +650 -0
- phase_loop_runtime/verification_evidence_validator.py +75 -0
- phase_loop_runtime/visual_evidence_validator.py +36 -0
- phase_loop_runtime/worker_pool.py +123 -0
- phase_loop_runtime/worktree_index.py +270 -0
- phase_loop_runtime-0.2.0.data/data/share/phase-loop-runtime/protocol/protocol.md +2475 -0
- phase_loop_runtime-0.2.0.dist-info/METADATA +106 -0
- phase_loop_runtime-0.2.0.dist-info/RECORD +370 -0
- phase_loop_runtime-0.2.0.dist-info/WHEEL +5 -0
- phase_loop_runtime-0.2.0.dist-info/entry_points.txt +9 -0
- phase_loop_runtime-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Iterable
|
|
6
|
+
|
|
7
|
+
from .discovery import (
|
|
8
|
+
PLAN_RE,
|
|
9
|
+
WORKFLOW_EXECUTE_SKILLS,
|
|
10
|
+
WORKFLOW_PLAN_SKILLS,
|
|
11
|
+
latest_workflow_handoff,
|
|
12
|
+
repo_identity,
|
|
13
|
+
)
|
|
14
|
+
from .events import event_path, read_events
|
|
15
|
+
from .git_topology import attach_git_topology
|
|
16
|
+
from .models import StateSnapshot
|
|
17
|
+
from .observability import (
|
|
18
|
+
phase_loop_metrics_path,
|
|
19
|
+
read_run_heartbeat,
|
|
20
|
+
read_task_snapshot,
|
|
21
|
+
read_work_unit_metrics,
|
|
22
|
+
summarize_work_unit_metrics,
|
|
23
|
+
)
|
|
24
|
+
from .runtime_paths import (
|
|
25
|
+
ensure_phase_loop_excluded,
|
|
26
|
+
phase_loop_runs_dir,
|
|
27
|
+
phase_loop_runs_dirs,
|
|
28
|
+
phase_loop_stop_file,
|
|
29
|
+
phase_loop_tui_handoff_file,
|
|
30
|
+
)
|
|
31
|
+
from .state import state_path
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def tui_handoff_path(repo: Path) -> Path:
|
|
35
|
+
return phase_loop_tui_handoff_file(repo)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def write_tui_handoff(
|
|
39
|
+
repo: Path,
|
|
40
|
+
roadmap: Path,
|
|
41
|
+
snapshot: StateSnapshot,
|
|
42
|
+
*,
|
|
43
|
+
action: str,
|
|
44
|
+
results: Iterable[Any] = (),
|
|
45
|
+
mode: str = "product",
|
|
46
|
+
) -> Path:
|
|
47
|
+
ensure_phase_loop_excluded(repo)
|
|
48
|
+
snapshot = attach_git_topology(repo, snapshot)
|
|
49
|
+
path = tui_handoff_path(repo)
|
|
50
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
path.write_text(render_tui_handoff(repo, roadmap, snapshot, action=action, results=results, mode=mode), encoding="utf-8")
|
|
52
|
+
return path
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def render_tui_handoff(
|
|
56
|
+
repo: Path,
|
|
57
|
+
roadmap: Path,
|
|
58
|
+
snapshot: StateSnapshot,
|
|
59
|
+
*,
|
|
60
|
+
action: str,
|
|
61
|
+
results: Iterable[Any] = (),
|
|
62
|
+
mode: str = "product",
|
|
63
|
+
) -> str:
|
|
64
|
+
latest = _latest_artifacts(repo, results)
|
|
65
|
+
latest_launch = _read_launch_metadata(latest.get("metadata"))
|
|
66
|
+
recent_metrics = read_work_unit_metrics(repo, limit=50)
|
|
67
|
+
latest_metric = recent_metrics[-1] if recent_metrics else None
|
|
68
|
+
current_status = _current_status(snapshot)
|
|
69
|
+
roadmap_complete = current_status == "complete" and snapshot.current_phase is None
|
|
70
|
+
lines = [
|
|
71
|
+
"# Phase Loop TUI Handoff",
|
|
72
|
+
"",
|
|
73
|
+
f"Updated: {snapshot.timestamp}",
|
|
74
|
+
f"Mode: {mode}",
|
|
75
|
+
f"Action: {action}",
|
|
76
|
+
f"Repo: {repo}",
|
|
77
|
+
f"Roadmap: {_display_path(repo, roadmap)}",
|
|
78
|
+
f"Current phase: {snapshot.current_phase or 'none'}",
|
|
79
|
+
f"Current status: {current_status}",
|
|
80
|
+
"",
|
|
81
|
+
"## Machine Sources",
|
|
82
|
+
"",
|
|
83
|
+
f"- State JSON: `{_display_path(repo, state_path(repo))}`",
|
|
84
|
+
f"- Event ledger: `{_display_path(repo, event_path(repo))}`",
|
|
85
|
+
f"- Runs directory: `{_display_path(repo, phase_loop_runs_dir(repo))}`",
|
|
86
|
+
f"- Metrics artifact: `{_display_path(repo, phase_loop_metrics_path(repo))}`",
|
|
87
|
+
f"- Stop file: `{_display_path(repo, phase_loop_stop_file(repo))}`",
|
|
88
|
+
]
|
|
89
|
+
if latest.get("metadata"):
|
|
90
|
+
lines.append(f"- Latest launch metadata: `{_display_path(repo, Path(str(latest['metadata'])))}`")
|
|
91
|
+
if latest.get("log"):
|
|
92
|
+
lines.append(f"- Latest run log: `{_display_path(repo, Path(str(latest['log'])))}`")
|
|
93
|
+
if latest.get("heartbeat"):
|
|
94
|
+
lines.append(f"- Latest heartbeat: `{_display_path(repo, Path(str(latest['heartbeat'])))}`")
|
|
95
|
+
if latest.get("terminal"):
|
|
96
|
+
lines.append(f"- Latest terminal summary: `{_display_path(repo, Path(str(latest['terminal'])))}`")
|
|
97
|
+
|
|
98
|
+
lines.extend(["", "## Current Status", ""])
|
|
99
|
+
for phase, status in snapshot.phases.items():
|
|
100
|
+
marker = "*" if phase == snapshot.current_phase else "-"
|
|
101
|
+
lines.append(f"{marker} {phase}: {status}")
|
|
102
|
+
|
|
103
|
+
lines.extend(["", "## What Happened", "", _what_happened(snapshot, action, current_status)])
|
|
104
|
+
|
|
105
|
+
reentry_lines = [] if roadmap_complete else _reentry_lines(repo, roadmap)
|
|
106
|
+
if reentry_lines:
|
|
107
|
+
lines.extend(["", "## Reentry Context", ""])
|
|
108
|
+
lines.extend(reentry_lines)
|
|
109
|
+
|
|
110
|
+
if snapshot.human_required or current_status in {"blocked", "awaiting_phase_closeout"}:
|
|
111
|
+
lines.extend(["", "## Required Action", "", _required_action(snapshot)])
|
|
112
|
+
if snapshot.blocker_class:
|
|
113
|
+
lines.append("")
|
|
114
|
+
lines.append(f"Blocker class: `{snapshot.blocker_class}`")
|
|
115
|
+
if snapshot.blocker_summary:
|
|
116
|
+
lines.append(f"Blocker summary: {snapshot.blocker_summary}")
|
|
117
|
+
if snapshot.required_human_inputs:
|
|
118
|
+
lines.append("")
|
|
119
|
+
lines.append("Required human inputs:")
|
|
120
|
+
for item in snapshot.required_human_inputs:
|
|
121
|
+
lines.append(f"- {item}")
|
|
122
|
+
if snapshot.dirty_paths:
|
|
123
|
+
lines.extend(["", "## Dirty Path Classification", ""])
|
|
124
|
+
lines.append(f"- phase-owned dirty: `{str(snapshot.phase_owned_dirty).lower()}`")
|
|
125
|
+
lines.append(f"- dirty paths: `{', '.join(snapshot.dirty_paths)}`")
|
|
126
|
+
if snapshot.phase_owned_dirty_paths:
|
|
127
|
+
lines.append(f"- phase-owned paths: `{', '.join(snapshot.phase_owned_dirty_paths)}`")
|
|
128
|
+
if snapshot.previous_phase_owned_paths:
|
|
129
|
+
lines.append(f"- previous phase-owned paths: `{', '.join(snapshot.previous_phase_owned_paths)}`")
|
|
130
|
+
if snapshot.unowned_dirty_paths:
|
|
131
|
+
lines.append(f"- unowned paths: `{', '.join(snapshot.unowned_dirty_paths)}`")
|
|
132
|
+
if snapshot.pre_existing_dirty_paths:
|
|
133
|
+
lines.append(f"- pre-existing paths: `{', '.join(snapshot.pre_existing_dirty_paths)}`")
|
|
134
|
+
|
|
135
|
+
terminal_summary = _current_terminal_summary(snapshot)
|
|
136
|
+
if terminal_summary:
|
|
137
|
+
lines.extend(["", "## Terminal Summary", ""])
|
|
138
|
+
lines.extend(_terminal_summary_lines(terminal_summary))
|
|
139
|
+
|
|
140
|
+
if latest_metric or recent_metrics:
|
|
141
|
+
lines.extend(["", "## Metrics", ""])
|
|
142
|
+
if latest_metric:
|
|
143
|
+
lines.append(f"- latest_metric_id: `{latest_metric.get('metric_id')}`")
|
|
144
|
+
lines.append(f"- metrics_artifact: `{_display_path(repo, phase_loop_metrics_path(repo))}`")
|
|
145
|
+
lines.extend(_metrics_summary_lines(summarize_work_unit_metrics(recent_metrics)))
|
|
146
|
+
|
|
147
|
+
if snapshot.latest_work_unit:
|
|
148
|
+
lines.extend(["", "## Latest Work Unit", ""])
|
|
149
|
+
lines.extend(_latest_work_unit_lines(snapshot.latest_work_unit))
|
|
150
|
+
|
|
151
|
+
if latest_launch and not roadmap_complete:
|
|
152
|
+
injection_lines = _injection_lines(latest_launch)
|
|
153
|
+
if injection_lines:
|
|
154
|
+
lines.extend(["", "## Injected Context", ""])
|
|
155
|
+
lines.extend(injection_lines)
|
|
156
|
+
taskledger_lines = _taskledger_lines(repo, latest_launch)
|
|
157
|
+
if taskledger_lines:
|
|
158
|
+
lines.extend(["", "## Native Team Ledger", ""])
|
|
159
|
+
lines.extend(taskledger_lines)
|
|
160
|
+
delegation_lines = _delegation_lines(latest_launch)
|
|
161
|
+
if delegation_lines:
|
|
162
|
+
lines.extend(["", "## Delegation Lineage", ""])
|
|
163
|
+
lines.extend(delegation_lines)
|
|
164
|
+
|
|
165
|
+
if snapshot.closeout_summary:
|
|
166
|
+
lines.extend(["", "## Latest Closeout Decision", ""])
|
|
167
|
+
lines.extend(_closeout_summary_lines(snapshot.closeout_summary))
|
|
168
|
+
|
|
169
|
+
if snapshot.access_attempts:
|
|
170
|
+
lines.extend(["", "## Access Attempts", ""])
|
|
171
|
+
lines.append("Redacted metadata only:")
|
|
172
|
+
lines.append("```json")
|
|
173
|
+
lines.append(json.dumps(snapshot.access_attempts, indent=2, sort_keys=True))
|
|
174
|
+
lines.append("```")
|
|
175
|
+
|
|
176
|
+
if snapshot.git_topology:
|
|
177
|
+
lines.extend(["", "## Git Topology", ""])
|
|
178
|
+
lines.extend(_git_topology_lines(snapshot.git_topology))
|
|
179
|
+
|
|
180
|
+
heartbeat = None if roadmap_complete else read_run_heartbeat(latest.get("heartbeat"))
|
|
181
|
+
if heartbeat and _should_render_heartbeat(snapshot, heartbeat):
|
|
182
|
+
lines.extend(["", "## Observed Liveness", ""])
|
|
183
|
+
lines.extend(_heartbeat_lines(heartbeat))
|
|
184
|
+
|
|
185
|
+
lines.extend(["", "## Monitor Command", "", "```bash"])
|
|
186
|
+
lines.append(f"phase-loop monitor --repo {repo} --roadmap {_display_path(repo, roadmap)} --once --json")
|
|
187
|
+
lines.append("```")
|
|
188
|
+
|
|
189
|
+
lines.extend(["", "## Verify Before Resume", ""])
|
|
190
|
+
lines.extend(_verify_commands(snapshot, current_status))
|
|
191
|
+
|
|
192
|
+
lines.extend(["", "## Resume Command", ""])
|
|
193
|
+
if current_status in {"blocked", "awaiting_phase_closeout"} or snapshot.human_required:
|
|
194
|
+
lines.append("Resolve the required action first, then run:")
|
|
195
|
+
elif current_status == "complete" and snapshot.current_phase is None:
|
|
196
|
+
lines.append("No resume command is required; this roadmap is complete.")
|
|
197
|
+
lines.append("")
|
|
198
|
+
return _finish_handoff(lines, latest, repo, snapshot)
|
|
199
|
+
lines.append("")
|
|
200
|
+
lines.append("```bash")
|
|
201
|
+
lines.append(f"phase-loop run --repo {repo} --roadmap {_display_path(repo, roadmap)} --max-phases 1 --observe")
|
|
202
|
+
lines.append("```")
|
|
203
|
+
|
|
204
|
+
return _finish_handoff(lines, latest, repo, snapshot)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _finish_handoff(lines: list[str], latest: dict[str, str], repo: Path, snapshot: StateSnapshot) -> str:
|
|
208
|
+
if latest:
|
|
209
|
+
lines.extend(["", "## Latest Run Artifacts", ""])
|
|
210
|
+
for key in ("root", "metadata", "log", "terminal"):
|
|
211
|
+
if latest.get(key):
|
|
212
|
+
lines.append(f"- {key}: `{_display_path(repo, Path(str(latest[key])))}`")
|
|
213
|
+
if latest.get("heartbeat"):
|
|
214
|
+
lines.append(f"- heartbeat: `{_display_path(repo, Path(str(latest['heartbeat'])))}`")
|
|
215
|
+
|
|
216
|
+
if snapshot.ledger_warnings:
|
|
217
|
+
lines.extend(["", "## Ledger Warnings", ""])
|
|
218
|
+
lines.append("```json")
|
|
219
|
+
lines.append(json.dumps(snapshot.ledger_warnings, indent=2, sort_keys=True))
|
|
220
|
+
lines.append("```")
|
|
221
|
+
|
|
222
|
+
lines.append("")
|
|
223
|
+
return "\n".join(lines)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _latest_work_unit_lines(work_unit: dict[str, Any]) -> list[str]:
|
|
227
|
+
identity = work_unit.get("identity") if isinstance(work_unit.get("identity"), dict) else {}
|
|
228
|
+
work_unit_id = work_unit.get("work_unit_id") or identity.get("work_unit_id")
|
|
229
|
+
return [
|
|
230
|
+
f"- id: `{work_unit_id}`",
|
|
231
|
+
f"- phase_status: `{identity.get('phase') or work_unit.get('phase')}`",
|
|
232
|
+
f"- work_unit_status: `{work_unit.get('status')}`",
|
|
233
|
+
f"- kind: `{identity.get('kind') or work_unit.get('kind')}`",
|
|
234
|
+
f"- lane: `{identity.get('lane_id') or work_unit.get('lane_id')}`",
|
|
235
|
+
f"- attempt: `{identity.get('attempt') or work_unit.get('attempt')}`",
|
|
236
|
+
f"- status: `{work_unit.get('status')}`",
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def handoff_metadata(repo: Path, path: Path) -> dict[str, object]:
|
|
241
|
+
return {
|
|
242
|
+
"tui_handoff_path": str(path),
|
|
243
|
+
"tui_handoff_exists": path.exists(),
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _latest_artifacts(repo: Path, results: Iterable[Any]) -> dict[str, str]:
|
|
248
|
+
for result in reversed(list(results)):
|
|
249
|
+
log_path = getattr(result, "log_path", None)
|
|
250
|
+
if log_path:
|
|
251
|
+
log = Path(str(log_path))
|
|
252
|
+
return {
|
|
253
|
+
"root": str(log.parent),
|
|
254
|
+
"metadata": str(log.parent / "launch.json"),
|
|
255
|
+
"log": str(log),
|
|
256
|
+
"heartbeat": str(log.parent / "heartbeat.json"),
|
|
257
|
+
"terminal": str(log.parent / "terminal-summary.json"),
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
for event in reversed(read_events(repo)):
|
|
261
|
+
metadata = event.get("metadata") or {}
|
|
262
|
+
artifacts = metadata.get("artifacts") or {}
|
|
263
|
+
if isinstance(artifacts, dict) and artifacts:
|
|
264
|
+
return {key: str(value) for key, value in artifacts.items() if key in {"root", "metadata", "log", "heartbeat", "terminal"}}
|
|
265
|
+
launch = metadata.get("launch") or {}
|
|
266
|
+
if isinstance(launch, dict) and launch.get("log_path"):
|
|
267
|
+
log = Path(str(launch["log_path"]))
|
|
268
|
+
heartbeat = launch.get("heartbeat_path") or str(log.parent / "heartbeat.json")
|
|
269
|
+
terminal = launch.get("terminal_path") or str(log.parent / "terminal-summary.json")
|
|
270
|
+
return {"root": str(log.parent), "metadata": str(log.parent / "launch.json"), "log": str(log), "heartbeat": str(heartbeat), "terminal": str(terminal)}
|
|
271
|
+
latest_run = _latest_run_dir(repo)
|
|
272
|
+
if latest_run:
|
|
273
|
+
return {
|
|
274
|
+
"root": str(latest_run),
|
|
275
|
+
"metadata": str(latest_run / "launch.json"),
|
|
276
|
+
"log": str(latest_run / "output.log"),
|
|
277
|
+
"heartbeat": str(latest_run / "heartbeat.json"),
|
|
278
|
+
"terminal": str(latest_run / "terminal-summary.json"),
|
|
279
|
+
}
|
|
280
|
+
return {}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _read_launch_metadata(path: str | None) -> dict[str, Any]:
|
|
284
|
+
if not path:
|
|
285
|
+
return {}
|
|
286
|
+
try:
|
|
287
|
+
data = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
288
|
+
except (OSError, json.JSONDecodeError):
|
|
289
|
+
return {}
|
|
290
|
+
return data if isinstance(data, dict) else {}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _latest_run_dir(repo: Path) -> Path | None:
|
|
294
|
+
candidates: list[Path] = []
|
|
295
|
+
for runs in phase_loop_runs_dirs(repo):
|
|
296
|
+
if runs.exists():
|
|
297
|
+
candidates.extend(path for path in runs.iterdir() if path.is_dir())
|
|
298
|
+
if not candidates:
|
|
299
|
+
return None
|
|
300
|
+
return max(candidates, key=lambda path: path.stat().st_mtime)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _what_happened(snapshot: StateSnapshot, action: str, current_status: str) -> str:
|
|
304
|
+
phase = snapshot.current_phase or "the roadmap"
|
|
305
|
+
terminal = _current_terminal_summary(snapshot) or {}
|
|
306
|
+
if current_status == "complete" and snapshot.current_phase is None:
|
|
307
|
+
return "All phases in the roadmap are complete."
|
|
308
|
+
if snapshot.human_required:
|
|
309
|
+
if snapshot.blocker_class:
|
|
310
|
+
return f"The phase loop stopped at `{phase}` because `{snapshot.blocker_class}` requires operator action."
|
|
311
|
+
return f"The phase loop stopped at `{phase}` because operator action is required."
|
|
312
|
+
if current_status == "blocked":
|
|
313
|
+
return f"The phase loop stopped at `{phase}` because a non-human repair is required before it can continue."
|
|
314
|
+
if current_status == "awaiting_phase_closeout":
|
|
315
|
+
return f"`{phase}` finished with verified dirty phase output. `manual` remains the default closeout policy until the operator opts into `commit` or `push`."
|
|
316
|
+
if current_status == "unknown":
|
|
317
|
+
return f"The phase loop could not safely classify `{phase}` after `{action}`. Inspect the linked logs and ledger before resuming."
|
|
318
|
+
if terminal.get("phase") and terminal.get("terminal_status"):
|
|
319
|
+
return (
|
|
320
|
+
f"The latest observed child exit for `{terminal['phase']}` ended with terminal status "
|
|
321
|
+
f"`{terminal['terminal_status']}`."
|
|
322
|
+
)
|
|
323
|
+
if current_status == "complete":
|
|
324
|
+
return f"`{phase}` is complete. The next run will continue from the next non-complete phase."
|
|
325
|
+
if current_status == "planned":
|
|
326
|
+
return f"`{phase}` is the nearest downstream phase with a current plan artifact. The next run should execute that phase."
|
|
327
|
+
if current_status == "unplanned":
|
|
328
|
+
return f"`{phase}` is the nearest downstream phase without a current plan artifact. The next run should plan that phase."
|
|
329
|
+
if current_status == "executed":
|
|
330
|
+
return f"`{phase}` has executed but still needs repair or completion evidence before the loop can advance."
|
|
331
|
+
return f"The latest loop action was `{action}` and the current phase is `{phase}`."
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _required_action(snapshot: StateSnapshot) -> str:
|
|
335
|
+
pr_action = _pr_required_action(snapshot.git_topology or {})
|
|
336
|
+
if pr_action:
|
|
337
|
+
return pr_action
|
|
338
|
+
if snapshot.blocker_class == "repeated_verification_failure":
|
|
339
|
+
terminal = snapshot.terminal_summary or {}
|
|
340
|
+
missing_plan = terminal.get("terminal_blocker") if isinstance(terminal.get("terminal_blocker"), dict) else {}
|
|
341
|
+
if snapshot.blocker_summary:
|
|
342
|
+
return (
|
|
343
|
+
f"{snapshot.blocker_summary} Inspect the linked terminal summary and event ledger, then rerun the "
|
|
344
|
+
"planning command only after confirming the required plan artifact path."
|
|
345
|
+
)
|
|
346
|
+
if missing_plan:
|
|
347
|
+
return "A child turn exited successfully but did not produce the machine-required artifact. Inspect state and rerun the bounded planning command."
|
|
348
|
+
if snapshot.current_phase and snapshot.phases.get(snapshot.current_phase) == "blocked" and not snapshot.human_required:
|
|
349
|
+
return (
|
|
350
|
+
"Start with `.phase-loop/tui-handoff.md` or `phase-loop handoff`, then confirm the current "
|
|
351
|
+
"machine state with `phase-loop status --json` before making a bounded non-human repair. "
|
|
352
|
+
"If this repo has not migrated yet, `.codex/phase-loop/` remains a legacy read fallback."
|
|
353
|
+
)
|
|
354
|
+
if snapshot.blocker_class == "dirty_worktree_conflict":
|
|
355
|
+
return (
|
|
356
|
+
"Review the target repo worktree and the dirty path classification, then preserve phase-owned output "
|
|
357
|
+
"or isolate unowned/pre-existing paths before rerunning the loop."
|
|
358
|
+
)
|
|
359
|
+
if snapshot.current_phase and snapshot.phases.get(snapshot.current_phase) == "awaiting_phase_closeout":
|
|
360
|
+
return (
|
|
361
|
+
"Preserve the verified phase-owned output in the target repo before rerunning the loop. "
|
|
362
|
+
"`manual` is the default closeout policy; `commit` and `push` are explicit operator opt-ins. "
|
|
363
|
+
"This state is not a human-required blocker, but the loop will not relaunch the phase until closeout is resolved."
|
|
364
|
+
)
|
|
365
|
+
if snapshot.blocker_class == "branch_sync_conflict":
|
|
366
|
+
return (
|
|
367
|
+
"Align the target branch with its upstream before rerunning release "
|
|
368
|
+
"dispatch. Do not cut a release from a local-only commit."
|
|
369
|
+
)
|
|
370
|
+
if snapshot.blocker_summary:
|
|
371
|
+
return snapshot.blocker_summary
|
|
372
|
+
if snapshot.human_required:
|
|
373
|
+
return "Resolve the human-required blocker recorded in the machine state before rerunning the loop."
|
|
374
|
+
return (
|
|
375
|
+
"Start with `.phase-loop/tui-handoff.md` or `phase-loop handoff`, then confirm the current "
|
|
376
|
+
"machine state with `phase-loop status --json` before making a bounded non-human repair. "
|
|
377
|
+
"If this repo has not migrated yet, `.codex/phase-loop/` remains a legacy read fallback."
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _pr_required_action(topology: dict[str, Any]) -> str | None:
|
|
382
|
+
pr_url = topology.get("pr_url")
|
|
383
|
+
if not pr_url:
|
|
384
|
+
return None
|
|
385
|
+
review_decision = topology.get("pr_review_decision")
|
|
386
|
+
mergeable = topology.get("pr_mergeable")
|
|
387
|
+
head = topology.get("pr_head_ref") or topology.get("matching_remote_ref") or "the PR branch"
|
|
388
|
+
base = topology.get("pr_base_ref") or topology.get("base_ref") or "the base branch"
|
|
389
|
+
if review_decision == "REVIEW_REQUIRED":
|
|
390
|
+
return (
|
|
391
|
+
f"Approve and merge PR {pr_url} from `{head}` into `{base}`, then fetch the target repo "
|
|
392
|
+
"so local branch state matches the merged upstream before rerunning the loop."
|
|
393
|
+
)
|
|
394
|
+
if mergeable == "MERGEABLE":
|
|
395
|
+
return (
|
|
396
|
+
f"Merge PR {pr_url} from `{head}` into `{base}`, then fetch the target repo so local branch "
|
|
397
|
+
"state matches the merged upstream before rerunning the loop."
|
|
398
|
+
)
|
|
399
|
+
return (
|
|
400
|
+
f"Resolve PR {pr_url} from `{head}` into `{base}`, then fetch the target repo so local branch "
|
|
401
|
+
"state matches the merged upstream before rerunning the loop."
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _verify_commands(snapshot: StateSnapshot, current_status: str) -> list[str]:
|
|
406
|
+
commands = ["```bash"]
|
|
407
|
+
if current_status == "blocked" and not snapshot.human_required:
|
|
408
|
+
commands.extend(
|
|
409
|
+
[
|
|
410
|
+
"phase-loop handoff",
|
|
411
|
+
"phase-loop status --json",
|
|
412
|
+
"phase-loop monitor --once --json",
|
|
413
|
+
]
|
|
414
|
+
)
|
|
415
|
+
elif snapshot.blocker_class == "dirty_worktree_conflict":
|
|
416
|
+
commands.extend(
|
|
417
|
+
[
|
|
418
|
+
"git status --short --branch",
|
|
419
|
+
"git fetch origin main --tags --prune",
|
|
420
|
+
"git rev-parse HEAD origin/main",
|
|
421
|
+
]
|
|
422
|
+
)
|
|
423
|
+
elif current_status == "awaiting_phase_closeout":
|
|
424
|
+
commands.extend(
|
|
425
|
+
[
|
|
426
|
+
"git status --short --branch",
|
|
427
|
+
"phase-loop status --json",
|
|
428
|
+
]
|
|
429
|
+
)
|
|
430
|
+
elif snapshot.blocker_class == "branch_sync_conflict":
|
|
431
|
+
commands.extend(
|
|
432
|
+
[
|
|
433
|
+
"git status --short --branch",
|
|
434
|
+
"git fetch origin main --tags --prune",
|
|
435
|
+
"git rev-parse HEAD origin/main",
|
|
436
|
+
]
|
|
437
|
+
)
|
|
438
|
+
elif current_status == "blocked" or snapshot.human_required:
|
|
439
|
+
if snapshot.human_required:
|
|
440
|
+
commands.append("phase-loop status --json")
|
|
441
|
+
else:
|
|
442
|
+
commands.extend(
|
|
443
|
+
[
|
|
444
|
+
"phase-loop handoff",
|
|
445
|
+
"phase-loop status --json",
|
|
446
|
+
]
|
|
447
|
+
)
|
|
448
|
+
else:
|
|
449
|
+
commands.append("phase-loop status")
|
|
450
|
+
commands.append("```")
|
|
451
|
+
return commands
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _git_topology_lines(topology: dict[str, Any]) -> list[str]:
|
|
455
|
+
if topology.get("available") is False:
|
|
456
|
+
return [f"- unavailable: {topology.get('reason', 'unknown')}"]
|
|
457
|
+
|
|
458
|
+
lines = [
|
|
459
|
+
f"- branch: `{topology.get('branch', 'unknown')}`",
|
|
460
|
+
f"- head: `{topology.get('head', 'unknown')}`",
|
|
461
|
+
]
|
|
462
|
+
if topology.get("base_ref"):
|
|
463
|
+
base = topology["base_ref"]
|
|
464
|
+
ahead = topology.get("ahead_of_base")
|
|
465
|
+
behind = topology.get("behind_base")
|
|
466
|
+
divergence = []
|
|
467
|
+
if ahead is not None:
|
|
468
|
+
divergence.append(f"ahead {ahead}")
|
|
469
|
+
if behind is not None:
|
|
470
|
+
divergence.append(f"behind {behind}")
|
|
471
|
+
suffix = f" ({', '.join(divergence)})" if divergence else ""
|
|
472
|
+
lines.append(f"- base: `{base}`{suffix}")
|
|
473
|
+
if topology.get("upstream_ref"):
|
|
474
|
+
lines.append(f"- upstream: `{topology['upstream_ref']}`")
|
|
475
|
+
if topology.get("target_push_ref"):
|
|
476
|
+
lines.append(f"- target push ref: `{topology['target_push_ref']}`")
|
|
477
|
+
if topology.get("pr_url"):
|
|
478
|
+
lines.append(f"- pull request: {topology['pr_url']}")
|
|
479
|
+
if topology.get("pr_review_decision"):
|
|
480
|
+
lines.append(f"- PR review decision: `{topology['pr_review_decision']}`")
|
|
481
|
+
if topology.get("pr_mergeable"):
|
|
482
|
+
lines.append(f"- PR mergeable: `{topology['pr_mergeable']}`")
|
|
483
|
+
if topology.get("matching_remote_ref"):
|
|
484
|
+
lines.append(f"- matching remote ref: `{topology['matching_remote_ref']}`")
|
|
485
|
+
if topology.get("pr_head_ref") or topology.get("pr_base_ref"):
|
|
486
|
+
lines.append(
|
|
487
|
+
f"- PR refs: head `{topology.get('pr_head_ref', 'unknown')}`, base `{topology.get('pr_base_ref', 'unknown')}`"
|
|
488
|
+
)
|
|
489
|
+
if topology.get("status_short_branch"):
|
|
490
|
+
lines.extend(["", "```text", str(topology["status_short_branch"]), "```"])
|
|
491
|
+
return lines
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _heartbeat_lines(heartbeat: dict[str, Any]) -> list[str]:
|
|
495
|
+
lines = [
|
|
496
|
+
f"- process alive: `{heartbeat.get('process_alive', False)}`",
|
|
497
|
+
f"- quiet level: `{heartbeat.get('quiet_level', 'unknown')}`",
|
|
498
|
+
]
|
|
499
|
+
if heartbeat.get("elapsed_seconds") is not None:
|
|
500
|
+
lines.append(f"- elapsed: `{heartbeat['elapsed_seconds']}s`")
|
|
501
|
+
if heartbeat.get("seconds_since_log_update") is not None:
|
|
502
|
+
lines.append(f"- seconds since log update: `{heartbeat['seconds_since_log_update']}s`")
|
|
503
|
+
if heartbeat.get("recommended_action"):
|
|
504
|
+
lines.append(f"- recommended action: {heartbeat['recommended_action']}")
|
|
505
|
+
if heartbeat.get("nudge_prompt"):
|
|
506
|
+
lines.extend(["", "Paste-ready nudge if operator judgment says the child may be stalled:", "", "```text", str(heartbeat["nudge_prompt"]), "```"])
|
|
507
|
+
return lines
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def _display_path(repo: Path, path: Path) -> str:
|
|
511
|
+
resolved = path.expanduser().resolve()
|
|
512
|
+
try:
|
|
513
|
+
return str(resolved.relative_to(repo.resolve()))
|
|
514
|
+
except ValueError:
|
|
515
|
+
return str(resolved)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _current_status(snapshot: StateSnapshot) -> str:
|
|
519
|
+
if snapshot.current_phase:
|
|
520
|
+
return snapshot.phases.get(snapshot.current_phase, "unknown")
|
|
521
|
+
if snapshot.phases and all(status == "complete" for status in snapshot.phases.values()):
|
|
522
|
+
return "complete"
|
|
523
|
+
return "unknown"
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _should_render_heartbeat(snapshot: StateSnapshot, heartbeat: dict[str, Any]) -> bool:
|
|
527
|
+
terminal = _current_terminal_summary(snapshot) or {}
|
|
528
|
+
if terminal.get("terminal_status") and not heartbeat.get("process_alive", False):
|
|
529
|
+
return False
|
|
530
|
+
return True
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _closeout_summary_lines(summary: dict[str, Any]) -> list[str]:
|
|
534
|
+
lines: list[str] = []
|
|
535
|
+
if summary.get("closeout_mode"):
|
|
536
|
+
lines.append(f"- mode: `{summary['closeout_mode']}`")
|
|
537
|
+
if summary.get("closeout_action"):
|
|
538
|
+
lines.append(f"- action: `{summary['closeout_action']}`")
|
|
539
|
+
if summary.get("closeout_commit"):
|
|
540
|
+
lines.append(f"- commit: `{summary['closeout_commit']}`")
|
|
541
|
+
if summary.get("closeout_push_ref"):
|
|
542
|
+
lines.append(f"- push target: `{summary['closeout_push_ref']}`")
|
|
543
|
+
if summary.get("closeout_refusal_reason"):
|
|
544
|
+
lines.append(f"- refusal reason: `{summary['closeout_refusal_reason']}`")
|
|
545
|
+
if summary.get("verification_status"):
|
|
546
|
+
lines.append(f"- verification: `{summary['verification_status']}`")
|
|
547
|
+
return lines
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _terminal_summary_lines(summary: dict[str, Any]) -> list[str]:
|
|
551
|
+
lines = [
|
|
552
|
+
f"- terminal status: `{summary.get('terminal_status', 'unknown')}`",
|
|
553
|
+
f"- verification: `{summary.get('verification_status', 'unknown')}`",
|
|
554
|
+
f"- phase-owned dirty: `{str(summary.get('phase_owned_dirty', False)).lower()}`",
|
|
555
|
+
]
|
|
556
|
+
if summary.get("metric_id"):
|
|
557
|
+
lines.append(f"- metric_id: `{summary['metric_id']}`")
|
|
558
|
+
closeout = summary.get("phase_loop_closeout")
|
|
559
|
+
if isinstance(closeout, dict):
|
|
560
|
+
outcome = closeout.get("terminal_status") or closeout.get("outcome") or "unknown"
|
|
561
|
+
sb = closeout.get("source_bundle") if isinstance(closeout.get("source_bundle"), dict) else {}
|
|
562
|
+
phase_id = sb.get("phase_id") or closeout.get("pipeline_phase_id") or "unknown"
|
|
563
|
+
bundle_sha = sb.get("sha256") or closeout.get("source_bundle_sha256")
|
|
564
|
+
lines.append(
|
|
565
|
+
"- Pipeline closeout: "
|
|
566
|
+
f"`{outcome}` "
|
|
567
|
+
f"for `{phase_id}`"
|
|
568
|
+
)
|
|
569
|
+
if bundle_sha:
|
|
570
|
+
lines.append(f"- source bundle: `{str(bundle_sha)[:12]}`")
|
|
571
|
+
latest_verify = summary.get("latest_verification_unit")
|
|
572
|
+
if isinstance(latest_verify, dict) and latest_verify.get("work_unit_id"):
|
|
573
|
+
lines.append(f"- latest verification unit: `{latest_verify['work_unit_id']}`")
|
|
574
|
+
verification_commands = summary.get("verification_commands") or ()
|
|
575
|
+
if verification_commands:
|
|
576
|
+
failed = [
|
|
577
|
+
str(command.get("command"))
|
|
578
|
+
for command in verification_commands
|
|
579
|
+
if isinstance(command, dict) and command.get("status") not in {"passed", "complete", "ok"}
|
|
580
|
+
]
|
|
581
|
+
lines.append(f"- verification commands: `{len(verification_commands)}`")
|
|
582
|
+
if failed:
|
|
583
|
+
lines.append(f"- failed verification commands: `{', '.join(failed)}`")
|
|
584
|
+
if summary.get("next_action"):
|
|
585
|
+
lines.append(f"- next action: {summary['next_action']}")
|
|
586
|
+
blocker = summary.get("terminal_blocker")
|
|
587
|
+
if isinstance(blocker, dict) and blocker.get("blocker_class"):
|
|
588
|
+
lines.append(f"- terminal blocker: `{blocker['blocker_class']}`")
|
|
589
|
+
if blocker.get("blocker_summary"):
|
|
590
|
+
lines.append(f"- blocker summary: {blocker['blocker_summary']}")
|
|
591
|
+
dirty_paths = summary.get("dirty_paths") or ()
|
|
592
|
+
if dirty_paths:
|
|
593
|
+
lines.append(f"- dirty paths: `{', '.join(dirty_paths)}`")
|
|
594
|
+
return lines
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def _metrics_summary_lines(summary: dict[str, object]) -> list[str]:
|
|
598
|
+
if not summary.get("total"):
|
|
599
|
+
return []
|
|
600
|
+
lines = [f"- recent_total: `{summary.get('total')}`"]
|
|
601
|
+
if summary.get("not_run_alert"):
|
|
602
|
+
lines.append(
|
|
603
|
+
"- verification not_run ratio warning: "
|
|
604
|
+
f"`{summary.get('not_run_count')}/{summary.get('sample_size')}` "
|
|
605
|
+
f"ratio `{summary.get('not_run_ratio')}` exceeds threshold `{summary.get('threshold')}`"
|
|
606
|
+
)
|
|
607
|
+
for label, key in (
|
|
608
|
+
("by_executor", "by_executor"),
|
|
609
|
+
("by_model", "by_model"),
|
|
610
|
+
("by_effort", "by_effort"),
|
|
611
|
+
("by_terminal_status", "by_terminal_status"),
|
|
612
|
+
("by_verification_status", "by_verification_status"),
|
|
613
|
+
("by_blocker_class", "by_blocker_class"),
|
|
614
|
+
):
|
|
615
|
+
bucket = summary.get(key)
|
|
616
|
+
if isinstance(bucket, dict) and bucket:
|
|
617
|
+
lines.append(f"- {label}: `{_format_counts(bucket)}`")
|
|
618
|
+
return lines
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def _format_counts(bucket: dict[str, object]) -> str:
|
|
622
|
+
return ", ".join(f"{key}={value}" for key, value in sorted(bucket.items()))
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _injection_lines(metadata: dict[str, Any]) -> list[str]:
|
|
626
|
+
bundle_id = metadata.get("skill_bundle_id")
|
|
627
|
+
bundle_sha = metadata.get("skill_bundle_sha256")
|
|
628
|
+
harness_target = metadata.get("harness_target")
|
|
629
|
+
injection_mode = metadata.get("injection_mode")
|
|
630
|
+
fallback_mode = metadata.get("fallback_mode")
|
|
631
|
+
recommended_roots = metadata.get("recommended_installed_roots") or []
|
|
632
|
+
installed_roots = metadata.get("installed_skill_roots") or []
|
|
633
|
+
warnings = metadata.get("installed_skill_warnings") or []
|
|
634
|
+
bridge_inventory = metadata.get("bridge_skill_inventory") or []
|
|
635
|
+
plugin_artifacts = metadata.get("plugin_bundle_artifacts") if isinstance(metadata.get("plugin_bundle_artifacts"), dict) else {}
|
|
636
|
+
expected_skill_pack = metadata.get("expected_skill_pack") or []
|
|
637
|
+
dispatch = metadata.get("dispatch_decision") if isinstance(metadata.get("dispatch_decision"), dict) else {}
|
|
638
|
+
dispatch_summary = metadata.get("dispatch_summary")
|
|
639
|
+
claude_mode = metadata.get("claude_execution_mode")
|
|
640
|
+
team_policy = metadata.get("claude_team_policy") if isinstance(metadata.get("claude_team_policy"), dict) else {}
|
|
641
|
+
eligibility = metadata.get("phase_team_eligibility") if isinstance(metadata.get("phase_team_eligibility"), dict) else {}
|
|
642
|
+
if not any(
|
|
643
|
+
(
|
|
644
|
+
bundle_id,
|
|
645
|
+
bundle_sha,
|
|
646
|
+
harness_target,
|
|
647
|
+
injection_mode,
|
|
648
|
+
recommended_roots,
|
|
649
|
+
warnings,
|
|
650
|
+
bridge_inventory,
|
|
651
|
+
expected_skill_pack,
|
|
652
|
+
plugin_artifacts,
|
|
653
|
+
dispatch,
|
|
654
|
+
dispatch_summary,
|
|
655
|
+
claude_mode,
|
|
656
|
+
team_policy,
|
|
657
|
+
eligibility,
|
|
658
|
+
)
|
|
659
|
+
):
|
|
660
|
+
return []
|
|
661
|
+
lines = []
|
|
662
|
+
if harness_target:
|
|
663
|
+
lines.append(f"- harness target: `{harness_target}`")
|
|
664
|
+
if dispatch.get("selected_executor"):
|
|
665
|
+
lines.append(f"- selected executor: `{dispatch['selected_executor']}`")
|
|
666
|
+
if dispatch.get("source"):
|
|
667
|
+
lines.append(f"- dispatch source: `{dispatch['source']}`")
|
|
668
|
+
if dispatch.get("selected_via"):
|
|
669
|
+
lines.append(f"- dispatch path: `{dispatch['selected_via']}`")
|
|
670
|
+
if dispatch.get("fallback_applied"):
|
|
671
|
+
lines.append("- dispatch fallback applied: `true`")
|
|
672
|
+
if dispatch.get("considered_executors"):
|
|
673
|
+
lines.append(f"- considered executors: `{', '.join(dispatch['considered_executors'])}`")
|
|
674
|
+
if dispatch.get("blocked_reason"):
|
|
675
|
+
lines.append(f"- dispatch blocked reason: `{dispatch['blocked_reason']}`")
|
|
676
|
+
if dispatch.get("required_capabilities"):
|
|
677
|
+
lines.append(f"- required capabilities: `{', '.join(dispatch['required_capabilities'])}`")
|
|
678
|
+
if dispatch_summary:
|
|
679
|
+
lines.append(f"- dispatch summary: {dispatch_summary}")
|
|
680
|
+
if bundle_id:
|
|
681
|
+
lines.append(f"- injected bundle: `{bundle_id}`")
|
|
682
|
+
if bundle_sha:
|
|
683
|
+
lines.append(f"- bundle sha256: `{bundle_sha}`")
|
|
684
|
+
if injection_mode:
|
|
685
|
+
lines.append(f"- delivery mode: `{injection_mode}`")
|
|
686
|
+
if fallback_mode:
|
|
687
|
+
lines.append(f"- fallback mode: `{fallback_mode}`")
|
|
688
|
+
if expected_skill_pack:
|
|
689
|
+
lines.append(f"- expected skill pack: `{', '.join(expected_skill_pack)}`")
|
|
690
|
+
if plugin_artifacts.get("plugin_dir"):
|
|
691
|
+
lines.append(f"- repo-owned plugin dir: `{plugin_artifacts['plugin_dir']}`")
|
|
692
|
+
if plugin_artifacts.get("settings_path"):
|
|
693
|
+
lines.append(f"- generated settings artifact: `{plugin_artifacts['settings_path']}`")
|
|
694
|
+
if plugin_artifacts.get("agents_path"):
|
|
695
|
+
lines.append(f"- generated agents artifact: `{plugin_artifacts['agents_path']}`")
|
|
696
|
+
if plugin_artifacts.get("mcp_config_path"):
|
|
697
|
+
lines.append(f"- generated mcp artifact: `{plugin_artifacts['mcp_config_path']}`")
|
|
698
|
+
if plugin_artifacts.get("artifact_names"):
|
|
699
|
+
lines.append(f"- plugin artifact inventory: `{', '.join(plugin_artifacts['artifact_names'])}`")
|
|
700
|
+
if claude_mode:
|
|
701
|
+
lines.append(f"- Claude execution mode: `{claude_mode}`")
|
|
702
|
+
if team_policy.get("maturity_label"):
|
|
703
|
+
lines.append(f"- TEAMGOV maturity: `{team_policy['maturity_label']}`")
|
|
704
|
+
if team_policy.get("max_teammates") is not None:
|
|
705
|
+
lines.append(f"- TEAMGOV max teammates: `{team_policy['max_teammates']}`")
|
|
706
|
+
if team_policy.get("max_native_tasks") is not None:
|
|
707
|
+
lines.append(f"- TEAMGOV max native tasks: `{team_policy['max_native_tasks']}`")
|
|
708
|
+
if team_policy.get("max_fanout") is not None:
|
|
709
|
+
lines.append(f"- TEAMGOV max fanout: `{team_policy['max_fanout']}`")
|
|
710
|
+
if team_policy.get("worktree_posture"):
|
|
711
|
+
lines.append(f"- TEAMGOV worktree posture: `{team_policy['worktree_posture']}`")
|
|
712
|
+
if team_policy.get("disallowed_tools"):
|
|
713
|
+
lines.append(f"- TEAMGOV denied tools: `{', '.join(team_policy['disallowed_tools'])}`")
|
|
714
|
+
if eligibility.get("allowed_execution_modes"):
|
|
715
|
+
lines.append(f"- phase team-safe modes: `{', '.join(eligibility['allowed_execution_modes'])}`")
|
|
716
|
+
if eligibility.get("reason"):
|
|
717
|
+
lines.append(f"- phase team eligibility: `{eligibility['reason']}`")
|
|
718
|
+
if eligibility.get("invalid_reasons"):
|
|
719
|
+
lines.append(f"- TEAMGOV denial reasons: `{', '.join(eligibility['invalid_reasons'])}`")
|
|
720
|
+
if recommended_roots:
|
|
721
|
+
lines.append(f"- recommended installed roots: `{', '.join(recommended_roots)}`")
|
|
722
|
+
if installed_roots:
|
|
723
|
+
lines.append(f"- discovered installed roots: `{', '.join(installed_roots)}`")
|
|
724
|
+
for record in bridge_inventory:
|
|
725
|
+
if not isinstance(record, dict):
|
|
726
|
+
continue
|
|
727
|
+
skill_name = record.get("skill_name") or "unknown"
|
|
728
|
+
parity_status = record.get("parity_status") or "unknown"
|
|
729
|
+
lines.append(f"- bridge skill `{skill_name}` parity: `{parity_status}`")
|
|
730
|
+
if record.get("repair_target"):
|
|
731
|
+
lines.append(f" - repair target: `{record['repair_target']}`")
|
|
732
|
+
if warnings:
|
|
733
|
+
lines.append("- installed-skill parity warnings:")
|
|
734
|
+
for warning in warnings:
|
|
735
|
+
lines.append(f" - {warning}")
|
|
736
|
+
return lines
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def _reentry_lines(repo: Path, roadmap: Path) -> list[str]:
|
|
740
|
+
lines: list[str] = []
|
|
741
|
+
trusted = _trusted_workflow_handoff(repo, roadmap)
|
|
742
|
+
if trusted:
|
|
743
|
+
if trusted.get("workflow_skill"):
|
|
744
|
+
lines.append(f"- trusted workflow handoff: `{trusted['workflow_skill']}`")
|
|
745
|
+
if trusted.get("originating_harness"):
|
|
746
|
+
lines.append(f"- trusted originating harness: `{trusted['originating_harness']}`")
|
|
747
|
+
if trusted.get("phase"):
|
|
748
|
+
lines.append(f"- trusted handoff phase: `{trusted['phase']}`")
|
|
749
|
+
if trusted.get("status"):
|
|
750
|
+
lines.append(f"- trusted handoff status: `{trusted['status']}`")
|
|
751
|
+
if trusted.get("artifact"):
|
|
752
|
+
lines.append(f"- trusted handoff artifact: `{_display_path(repo, Path(str(trusted['artifact'])))}`")
|
|
753
|
+
manual = _latest_manual_import(repo, roadmap)
|
|
754
|
+
if manual:
|
|
755
|
+
lines.append(f"- latest manual import phase: `{manual.get('phase') or 'unknown'}`")
|
|
756
|
+
lines.append(f"- latest manual import status: `{manual.get('status') or 'unknown'}`")
|
|
757
|
+
if manual.get("originating_harness"):
|
|
758
|
+
lines.append(f"- latest manual import harness: `{manual['originating_harness']}`")
|
|
759
|
+
if manual.get("workflow_skill"):
|
|
760
|
+
lines.append(f"- latest manual import workflow skill: `{manual['workflow_skill']}`")
|
|
761
|
+
if manual.get("artifact"):
|
|
762
|
+
lines.append(f"- latest manual import artifact: `{_display_path(repo, Path(str(manual['artifact'])))}`")
|
|
763
|
+
for record in manual.get("bridge_skill_inventory") or []:
|
|
764
|
+
if not isinstance(record, dict):
|
|
765
|
+
continue
|
|
766
|
+
skill_name = record.get("skill_name") or "unknown"
|
|
767
|
+
parity_status = record.get("parity_status") or "unknown"
|
|
768
|
+
lines.append(f"- latest manual import bridge skill `{skill_name}` parity: `{parity_status}`")
|
|
769
|
+
warnings = manual.get("installed_skill_warnings") or []
|
|
770
|
+
if warnings:
|
|
771
|
+
lines.append("- latest manual import parity warnings:")
|
|
772
|
+
for warning in warnings:
|
|
773
|
+
lines.append(f" - {warning}")
|
|
774
|
+
return lines
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def _taskledger_lines(repo: Path, metadata: dict[str, Any]) -> list[str]:
|
|
778
|
+
task_ledger = metadata.get("task_ledger_artifacts") if isinstance(metadata.get("task_ledger_artifacts"), dict) else {}
|
|
779
|
+
if not task_ledger:
|
|
780
|
+
return []
|
|
781
|
+
snapshot_path = task_ledger.get("snapshot_path")
|
|
782
|
+
hook_manifest_path = task_ledger.get("hook_manifest_path")
|
|
783
|
+
snapshot = read_task_snapshot(snapshot_path)
|
|
784
|
+
lines: list[str] = []
|
|
785
|
+
if snapshot_path:
|
|
786
|
+
lines.append(f"- task snapshot: `{_display_path(repo, Path(str(snapshot_path)))}`")
|
|
787
|
+
if hook_manifest_path:
|
|
788
|
+
lines.append(f"- hook manifest: `{_display_path(repo, Path(str(hook_manifest_path)))}`")
|
|
789
|
+
hook_inventory = task_ledger.get("hook_policy_inventory") or []
|
|
790
|
+
if hook_inventory:
|
|
791
|
+
event_names = [record.get("event_name") for record in hook_inventory if isinstance(record, dict) and record.get("event_name")]
|
|
792
|
+
if event_names:
|
|
793
|
+
lines.append(f"- hook inventory: `{', '.join(event_names)}`")
|
|
794
|
+
freshness = _taskledger_freshness(snapshot, metadata)
|
|
795
|
+
lines.append(f"- task snapshot freshness: `{freshness}`")
|
|
796
|
+
if snapshot and isinstance(snapshot.get("latest_activity"), dict):
|
|
797
|
+
latest_activity = snapshot["latest_activity"]
|
|
798
|
+
if latest_activity.get("classification"):
|
|
799
|
+
lines.append(f"- wait classification: `{latest_activity['classification']}`")
|
|
800
|
+
if latest_activity.get("summary"):
|
|
801
|
+
lines.append(f"- latest team activity: {latest_activity['summary']}")
|
|
802
|
+
execution_mode = metadata.get("claude_execution_mode")
|
|
803
|
+
if execution_mode:
|
|
804
|
+
lines.append(f"- native execution mode: `{execution_mode}`")
|
|
805
|
+
return lines
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _taskledger_freshness(snapshot: dict[str, Any] | None, metadata: dict[str, Any]) -> str:
|
|
809
|
+
if not snapshot:
|
|
810
|
+
return "missing"
|
|
811
|
+
terminal = metadata.get("terminal_summary") if isinstance(metadata.get("terminal_summary"), dict) else {}
|
|
812
|
+
if terminal.get("terminal_status"):
|
|
813
|
+
return "superseded"
|
|
814
|
+
if snapshot.get("freshness_timestamp"):
|
|
815
|
+
return "fresh"
|
|
816
|
+
return "missing"
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def _trusted_workflow_handoff(repo: Path, roadmap: Path) -> dict[str, str] | None:
|
|
820
|
+
identity = repo_identity(repo)
|
|
821
|
+
handoff = latest_workflow_handoff(
|
|
822
|
+
identity,
|
|
823
|
+
repo,
|
|
824
|
+
roadmap,
|
|
825
|
+
(*WORKFLOW_EXECUTE_SKILLS, *WORKFLOW_PLAN_SKILLS),
|
|
826
|
+
)
|
|
827
|
+
if not handoff:
|
|
828
|
+
return None
|
|
829
|
+
artifact = handoff.get("artifact")
|
|
830
|
+
phase = None
|
|
831
|
+
if artifact:
|
|
832
|
+
match = PLAN_RE.search(Path(str(artifact)).name)
|
|
833
|
+
if match:
|
|
834
|
+
phase = match.group(2).upper()
|
|
835
|
+
return {
|
|
836
|
+
"workflow_skill": str(handoff.get("workflow_skill") or ""),
|
|
837
|
+
"originating_harness": str(handoff.get("originating_harness") or ""),
|
|
838
|
+
"phase": str(phase or ""),
|
|
839
|
+
"status": str(handoff.get("automation_status") or ""),
|
|
840
|
+
"artifact": str(artifact or ""),
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def _latest_manual_import(repo: Path, roadmap: Path) -> dict[str, Any] | None:
|
|
845
|
+
roadmap_value = str(roadmap.resolve())
|
|
846
|
+
for event in reversed(read_events(repo)):
|
|
847
|
+
event_roadmap = event.get("roadmap")
|
|
848
|
+
if isinstance(event_roadmap, str) and event_roadmap:
|
|
849
|
+
if str(Path(event_roadmap).expanduser().resolve()) != roadmap_value:
|
|
850
|
+
continue
|
|
851
|
+
metadata = event.get("metadata") if isinstance(event.get("metadata"), dict) else {}
|
|
852
|
+
manual = metadata.get("manual_import") if isinstance(metadata.get("manual_import"), dict) else {}
|
|
853
|
+
if not manual:
|
|
854
|
+
continue
|
|
855
|
+
return {
|
|
856
|
+
"phase": event.get("phase"),
|
|
857
|
+
"status": event.get("status"),
|
|
858
|
+
"originating_harness": manual.get("originating_harness"),
|
|
859
|
+
"workflow_skill": manual.get("workflow_skill"),
|
|
860
|
+
"artifact": manual.get("artifact"),
|
|
861
|
+
"installed_skill_warnings": manual.get("installed_skill_warnings", []),
|
|
862
|
+
"bridge_skill_inventory": manual.get("bridge_skill_inventory", []),
|
|
863
|
+
}
|
|
864
|
+
return None
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def _delegation_lines(metadata: dict[str, Any]) -> list[str]:
|
|
868
|
+
request = metadata.get("delegation_request") if isinstance(metadata.get("delegation_request"), dict) else {}
|
|
869
|
+
decision = metadata.get("delegation_decision") if isinstance(metadata.get("delegation_decision"), dict) else {}
|
|
870
|
+
parent_child = metadata.get("parent_child") if isinstance(metadata.get("parent_child"), dict) else {}
|
|
871
|
+
if not any((request, decision, parent_child)):
|
|
872
|
+
return []
|
|
873
|
+
lines: list[str] = []
|
|
874
|
+
if request.get("request_id"):
|
|
875
|
+
lines.append(f"- request id: `{request['request_id']}`")
|
|
876
|
+
if request.get("product_action"):
|
|
877
|
+
lines.append(f"- requested action: `{request['product_action']}`")
|
|
878
|
+
if request.get("target_executor"):
|
|
879
|
+
lines.append(f"- requested executor: `{request['target_executor']}`")
|
|
880
|
+
if request.get("priority"):
|
|
881
|
+
lines.append(f"- priority: `{request['priority']}`")
|
|
882
|
+
if request.get("owned_files"):
|
|
883
|
+
lines.append(f"- owned files: `{', '.join(request['owned_files'])}`")
|
|
884
|
+
if request.get("expected_output"):
|
|
885
|
+
lines.append(f"- expected output: {request['expected_output']}")
|
|
886
|
+
if request.get("reason"):
|
|
887
|
+
lines.append(f"- request reason: {request['reason']}")
|
|
888
|
+
if decision.get("status"):
|
|
889
|
+
lines.append(f"- decision: `{decision['status']}`")
|
|
890
|
+
if decision.get("reason_code"):
|
|
891
|
+
lines.append(f"- decision code: `{decision['reason_code']}`")
|
|
892
|
+
if decision.get("summary"):
|
|
893
|
+
lines.append(f"- decision summary: {decision['summary']}")
|
|
894
|
+
if decision.get("selected_executor"):
|
|
895
|
+
lines.append(f"- approved executor: `{decision['selected_executor']}`")
|
|
896
|
+
if parent_child.get("parent_phase"):
|
|
897
|
+
lines.append(f"- parent phase: `{parent_child['parent_phase']}`")
|
|
898
|
+
if parent_child.get("parent_executor"):
|
|
899
|
+
lines.append(f"- parent executor: `{parent_child['parent_executor']}`")
|
|
900
|
+
if parent_child.get("parent_run_id"):
|
|
901
|
+
lines.append(f"- parent run id: `{parent_child['parent_run_id']}`")
|
|
902
|
+
if parent_child.get("child_action"):
|
|
903
|
+
lines.append(f"- child action: `{parent_child['child_action']}`")
|
|
904
|
+
if parent_child.get("child_executor"):
|
|
905
|
+
lines.append(f"- child executor: `{parent_child['child_executor']}`")
|
|
906
|
+
if parent_child.get("child_artifact_root"):
|
|
907
|
+
lines.append(f"- child artifacts: `{parent_child['child_artifact_root']}`")
|
|
908
|
+
if parent_child.get("child_worktree_root"):
|
|
909
|
+
lines.append(f"- child worktree: `{parent_child['child_worktree_root']}`")
|
|
910
|
+
if parent_child.get("child_closeout_result"):
|
|
911
|
+
lines.append(f"- child closeout: `{json.dumps(parent_child['child_closeout_result'], sort_keys=True)}`")
|
|
912
|
+
if parent_child.get("observed_launch_path"):
|
|
913
|
+
lines.append(f"- launch metadata: `{parent_child['observed_launch_path']}`")
|
|
914
|
+
return lines
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def _current_terminal_summary(snapshot: StateSnapshot) -> dict[str, Any] | None:
|
|
918
|
+
if not snapshot.terminal_summary:
|
|
919
|
+
return None
|
|
920
|
+
if snapshot.current_phase is None and snapshot.phases and all(status == "complete" for status in snapshot.phases.values()):
|
|
921
|
+
if snapshot.terminal_summary.get("terminal_status") != "complete":
|
|
922
|
+
return None
|
|
923
|
+
phase = snapshot.terminal_summary.get("phase")
|
|
924
|
+
closeout = snapshot.closeout_summary or {}
|
|
925
|
+
if (
|
|
926
|
+
phase
|
|
927
|
+
and phase != snapshot.current_phase
|
|
928
|
+
and closeout.get("phase") == phase
|
|
929
|
+
and closeout.get("closeout_commit")
|
|
930
|
+
):
|
|
931
|
+
return None
|
|
932
|
+
return snapshot.terminal_summary
|