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.
Files changed (370) hide show
  1. phase_loop_runtime/__init__.py +20 -0
  2. phase_loop_runtime/_contract_docs/phase-loop/extraction-readiness.md +90 -0
  3. phase_loop_runtime/_contract_docs/phase-loop/granular-execution-policy.md +137 -0
  4. phase_loop_runtime/_contract_docs/phase-loop/harness-capability-matrix.md +510 -0
  5. phase_loop_runtime/_contract_docs/phase-loop/harness-skill-matrix.md +125 -0
  6. phase_loop_runtime/_contract_docs/phase-loop/harness-substrate-manifest.md +232 -0
  7. phase_loop_runtime/_contract_docs/phase-loop/pi-loop-control.md +247 -0
  8. phase_loop_runtime/_contract_docs/phase-loop/protocol.md +2759 -0
  9. phase_loop_runtime/_contract_docs/phase-loop/runtime-boundary.md +289 -0
  10. phase_loop_runtime/_contract_docs/phase-loop/spec-discovery-roots.md +47 -0
  11. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-governed-pipeline.md +42 -0
  12. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-portal-projection.md +34 -0
  13. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-regenesis.md +29 -0
  14. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-report.md +32 -0
  15. phase_loop_runtime/_contract_docs/runtime/verification-evidence-contract.md +52 -0
  16. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/commands.txt +2 -0
  17. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/manifest.json +16 -0
  18. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/mixed.json +5 -0
  19. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/manifest.json +16 -0
  20. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/timeout-note.txt +2 -0
  21. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/manifest.json +16 -0
  22. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/redacted-summary.json +6 -0
  23. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/manifest.json +16 -0
  24. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/sample.json +7 -0
  25. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/edge.txt +2 -0
  26. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/manifest.json +16 -0
  27. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/manifest.json +16 -0
  28. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-a.txt +1 -0
  29. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-b.txt +1 -0
  30. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-c.txt +1 -0
  31. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/claimed-results.json +5 -0
  32. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/manifest.json +16 -0
  33. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/manifest.json +16 -0
  34. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/metrics.json +9 -0
  35. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/findings.md +10 -0
  36. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/manifest.json +16 -0
  37. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/manifest.json +16 -0
  38. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/scores.json +4 -0
  39. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/summary.txt +1 -0
  40. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/bundle-check.json +8 -0
  41. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/manifest.json +16 -0
  42. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/operator-note.txt +2 -0
  43. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/events-sample.jsonl +3 -0
  44. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/manifest.json +16 -0
  45. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/receipt.txt +1 -0
  46. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/evidence.json +13 -0
  47. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/manifest.json +16 -0
  48. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/summary.txt +2 -0
  49. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/manifest.json +16 -0
  50. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/matrix.json +9 -0
  51. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/notes.md +4 -0
  52. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/console-summary.txt +2 -0
  53. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/manifest.json +16 -0
  54. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/screenshot-standin.ppm +5 -0
  55. phase_loop_runtime/adoption_bundle.py +273 -0
  56. phase_loop_runtime/advisor_board/CONTRACTS.md +257 -0
  57. phase_loop_runtime/advisor_board/__init__.py +288 -0
  58. phase_loop_runtime/advisor_board/backing.py +137 -0
  59. phase_loop_runtime/advisor_board/backing_omnigent.py +498 -0
  60. phase_loop_runtime/advisor_board/config.py +225 -0
  61. phase_loop_runtime/advisor_board/events.py +115 -0
  62. phase_loop_runtime/advisor_board/fixtures/advisor-boards.example.toml +59 -0
  63. phase_loop_runtime/advisor_board/fixtures.py +91 -0
  64. phase_loop_runtime/advisor_board/harness_mapping.py +125 -0
  65. phase_loop_runtime/advisor_board/matrix.py +146 -0
  66. phase_loop_runtime/advisor_board/observability.py +481 -0
  67. phase_loop_runtime/advisor_board/presets.py +188 -0
  68. phase_loop_runtime/advisor_board/registries.py +309 -0
  69. phase_loop_runtime/advisor_board/resolver.py +253 -0
  70. phase_loop_runtime/advisor_board/schema.py +269 -0
  71. phase_loop_runtime/advisor_board/standin.py +166 -0
  72. phase_loop_runtime/advisor_board/validation.py +159 -0
  73. phase_loop_runtime/agent_runtime_provider.py +676 -0
  74. phase_loop_runtime/baml_modular.py +656 -0
  75. phase_loop_runtime/baml_src/dotfiles_adoption_manifest.baml +37 -0
  76. phase_loop_runtime/baml_src/dotfiles_c4_document.baml +14 -0
  77. phase_loop_runtime/baml_src/dotfiles_plan_manifest.baml +50 -0
  78. phase_loop_runtime/baml_src/dotfiles_runtime_projection.baml +16 -0
  79. phase_loop_runtime/baml_src/dotfiles_task_catalog.baml +20 -0
  80. phase_loop_runtime/baml_src/emit_phase_closeout.baml +86 -0
  81. phase_loop_runtime/baml_src/evaluate_suspected_fake_evidence.baml +36 -0
  82. phase_loop_runtime/baml_src/verification_evidence.baml +40 -0
  83. phase_loop_runtime/broker.py +181 -0
  84. phase_loop_runtime/build_bundle.py +294 -0
  85. phase_loop_runtime/capability_registry.py +835 -0
  86. phase_loop_runtime/classifier.py +87 -0
  87. phase_loop_runtime/claude_agent_view.py +625 -0
  88. phase_loop_runtime/claude_channel_sidecar.py +1086 -0
  89. phase_loop_runtime/cli.py +2599 -0
  90. phase_loop_runtime/closeout.py +865 -0
  91. phase_loop_runtime/closeout_classifier.py +120 -0
  92. phase_loop_runtime/closeout_evidence_audit.py +112 -0
  93. phase_loop_runtime/closeout_validation.py +144 -0
  94. phase_loop_runtime/closeout_validators.py +203 -0
  95. phase_loop_runtime/consiliency_gates.py +374 -0
  96. phase_loop_runtime/consiliency_ingest.py +399 -0
  97. phase_loop_runtime/consiliency_layout.py +176 -0
  98. phase_loop_runtime/consiliency_scaffold.py +305 -0
  99. phase_loop_runtime/cross_repo_channel.py +331 -0
  100. phase_loop_runtime/discovery.py +2368 -0
  101. phase_loop_runtime/dispatch_lock.py +167 -0
  102. phase_loop_runtime/doc_delta_validator.py +98 -0
  103. phase_loop_runtime/docs_audit.py +285 -0
  104. phase_loop_runtime/docs_freshness.py +468 -0
  105. phase_loop_runtime/docs_surfaces.py +150 -0
  106. phase_loop_runtime/dotfiles_profile_plugin.py +96 -0
  107. phase_loop_runtime/events.py +154 -0
  108. phase_loop_runtime/events_migration.py +96 -0
  109. phase_loop_runtime/evidence_audit.py +1082 -0
  110. phase_loop_runtime/evidence_audit_config.py +172 -0
  111. phase_loop_runtime/fleet_map.py +426 -0
  112. phase_loop_runtime/fleet_metrics.py +298 -0
  113. phase_loop_runtime/fleet_metrics_export.py +196 -0
  114. phase_loop_runtime/git_discipline.py +479 -0
  115. phase_loop_runtime/git_ops.py +122 -0
  116. phase_loop_runtime/git_topology.py +252 -0
  117. phase_loop_runtime/governed_bundle.py +137 -0
  118. phase_loop_runtime/governed_premerge.py +219 -0
  119. phase_loop_runtime/governed_review.py +264 -0
  120. phase_loop_runtime/handoff.py +932 -0
  121. phase_loop_runtime/handoff_path.py +14 -0
  122. phase_loop_runtime/injection.py +740 -0
  123. phase_loop_runtime/install_status.py +88 -0
  124. phase_loop_runtime/lane_scheduler.py +317 -0
  125. phase_loop_runtime/launcher.py +2760 -0
  126. phase_loop_runtime/lease_store.py +553 -0
  127. phase_loop_runtime/maintenance.py +401 -0
  128. phase_loop_runtime/migrate_handoffs.py +201 -0
  129. phase_loop_runtime/models.py +2144 -0
  130. phase_loop_runtime/observability.py +1290 -0
  131. phase_loop_runtime/panel_invoker.py +1638 -0
  132. phase_loop_runtime/phase_loop_drift_audit.py +388 -0
  133. phase_loop_runtime/phase_worktree_executor.py +433 -0
  134. phase_loop_runtime/pipeline_adapter/__init__.py +12 -0
  135. phase_loop_runtime/pipeline_adapter/branch_ops.py +274 -0
  136. phase_loop_runtime/pipeline_adapter/flag.py +55 -0
  137. phase_loop_runtime/pipeline_adapter/markers.py +13 -0
  138. phase_loop_runtime/pipeline_adapter/merge_policy.py +72 -0
  139. phase_loop_runtime/pipeline_adapter/ratification.py +100 -0
  140. phase_loop_runtime/pipeline_adapter/sibling_matcher.py +123 -0
  141. phase_loop_runtime/plan_ir.py +523 -0
  142. phase_loop_runtime/plan_manifest.py +415 -0
  143. phase_loop_runtime/planner_validation.py +176 -0
  144. phase_loop_runtime/profiles.py +462 -0
  145. phase_loop_runtime/prompts.py +365 -0
  146. phase_loop_runtime/provenance.py +165 -0
  147. phase_loop_runtime/publishing.py +339 -0
  148. phase_loop_runtime/py.typed +1 -0
  149. phase_loop_runtime/reconcile.py +1550 -0
  150. phase_loop_runtime/redaction.py +184 -0
  151. phase_loop_runtime/reflection_sync.py +293 -0
  152. phase_loop_runtime/release_guard.py +186 -0
  153. phase_loop_runtime/render.py +432 -0
  154. phase_loop_runtime/repo_validation.py +427 -0
  155. phase_loop_runtime/review_summary.py +150 -0
  156. phase_loop_runtime/roadmap_lint.py +425 -0
  157. phase_loop_runtime/route_log.py +59 -0
  158. phase_loop_runtime/route_policy/__init__.py +11 -0
  159. phase_loop_runtime/route_policy/fixtures/route_selection.golden.json +263 -0
  160. phase_loop_runtime/runner.py +8400 -0
  161. phase_loop_runtime/runtime_paths.py +225 -0
  162. phase_loop_runtime/runtime_projection.py +113 -0
  163. phase_loop_runtime/runtime_resources.py +36 -0
  164. phase_loop_runtime/schema_export.py +292 -0
  165. phase_loop_runtime/schemas/phase_loop_closeout.schema.json +366 -0
  166. phase_loop_runtime/skill_install.py +176 -0
  167. phase_loop_runtime/skill_inventory.py +662 -0
  168. phase_loop_runtime/skill_paths.py +42 -0
  169. phase_loop_runtime/skill_sources_plugin.py +37 -0
  170. phase_loop_runtime/skills_bundle/claude-advisor-board/README.md +1 -0
  171. phase_loop_runtime/skills_bundle/claude-advisor-board/SKILL.md +38 -0
  172. phase_loop_runtime/skills_bundle/claude-advisor-panel/README.md +1 -0
  173. phase_loop_runtime/skills_bundle/claude-advisor-panel/SKILL.md +38 -0
  174. phase_loop_runtime/skills_bundle/claude-execute-detailed/README.md +1 -0
  175. phase_loop_runtime/skills_bundle/claude-execute-detailed/SKILL.md +152 -0
  176. phase_loop_runtime/skills_bundle/claude-execute-phase/README.md +1 -0
  177. phase_loop_runtime/skills_bundle/claude-execute-phase/SKILL.md +745 -0
  178. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  179. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  180. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  181. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  182. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  183. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  184. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  185. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/state.py +274 -0
  186. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  187. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/team_teardown.sh +32 -0
  188. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/validate_plan_doc.py +642 -0
  189. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/verify_harness.sh +96 -0
  190. phase_loop_runtime/skills_bundle/claude-phase-loop/README.md +1 -0
  191. phase_loop_runtime/skills_bundle/claude-phase-loop/SKILL.md +115 -0
  192. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/README.md +1 -0
  193. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/SKILL.md +439 -0
  194. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/assets/review_prompt.md +19 -0
  195. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  196. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/roadmap-template.md +193 -0
  197. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  198. phase_loop_runtime/skills_bundle/claude-plan-detailed/README.md +1 -0
  199. phase_loop_runtime/skills_bundle/claude-plan-detailed/SKILL.md +334 -0
  200. phase_loop_runtime/skills_bundle/claude-plan-detailed/assets/review_prompt.md +25 -0
  201. phase_loop_runtime/skills_bundle/claude-plan-phase/README.md +1 -0
  202. phase_loop_runtime/skills_bundle/claude-plan-phase/SKILL.md +776 -0
  203. phase_loop_runtime/skills_bundle/claude-plan-phase/assets/review_prompt.md +17 -0
  204. phase_loop_runtime/skills_bundle/claude-plan-phase/scripts/validate_plan_doc.py +1092 -0
  205. phase_loop_runtime/skills_bundle/claude-run-train/README.md +1 -0
  206. phase_loop_runtime/skills_bundle/claude-run-train/SKILL.md +75 -0
  207. phase_loop_runtime/skills_bundle/claude-skill-editor/README.md +1 -0
  208. phase_loop_runtime/skills_bundle/claude-skill-editor/SKILL.md +243 -0
  209. phase_loop_runtime/skills_bundle/claude-skill-editor/assets/editor_prompt.md +72 -0
  210. phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/README.md +1 -0
  211. phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/SKILL.md +249 -0
  212. phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  213. phase_loop_runtime/skills_bundle/claude-task-contextualizer/README.md +1 -0
  214. phase_loop_runtime/skills_bundle/claude-task-contextualizer/SKILL.md +74 -0
  215. phase_loop_runtime/skills_bundle/claude-task-contextualizer/references/task-templates.md +103 -0
  216. phase_loop_runtime/skills_bundle/codex-advisor-board/README.md +1 -0
  217. phase_loop_runtime/skills_bundle/codex-advisor-board/SKILL.md +38 -0
  218. phase_loop_runtime/skills_bundle/codex-advisor-panel/README.md +1 -0
  219. phase_loop_runtime/skills_bundle/codex-advisor-panel/SKILL.md +38 -0
  220. phase_loop_runtime/skills_bundle/codex-execute-detailed/README.md +1 -0
  221. phase_loop_runtime/skills_bundle/codex-execute-detailed/SKILL.md +154 -0
  222. phase_loop_runtime/skills_bundle/codex-execute-phase/README.md +1 -0
  223. phase_loop_runtime/skills_bundle/codex-execute-phase/SKILL.md +231 -0
  224. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  225. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  226. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  227. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  228. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  229. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  230. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  231. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/state.py +274 -0
  232. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  233. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/team_teardown.sh +32 -0
  234. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/validate_plan_doc.py +642 -0
  235. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/verify_harness.sh +96 -0
  236. phase_loop_runtime/skills_bundle/codex-phase-loop/README.md +1 -0
  237. phase_loop_runtime/skills_bundle/codex-phase-loop/SKILL.md +248 -0
  238. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/README.md +1 -0
  239. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/SKILL.md +150 -0
  240. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/assets/review_prompt.md +19 -0
  241. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  242. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/roadmap-template.md +193 -0
  243. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  244. phase_loop_runtime/skills_bundle/codex-plan-detailed/README.md +1 -0
  245. phase_loop_runtime/skills_bundle/codex-plan-detailed/SKILL.md +119 -0
  246. phase_loop_runtime/skills_bundle/codex-plan-detailed/assets/review_prompt.md +25 -0
  247. phase_loop_runtime/skills_bundle/codex-plan-phase/README.md +1 -0
  248. phase_loop_runtime/skills_bundle/codex-plan-phase/SKILL.md +307 -0
  249. phase_loop_runtime/skills_bundle/codex-plan-phase/assets/review_prompt.md +17 -0
  250. phase_loop_runtime/skills_bundle/codex-plan-phase/scripts/validate_plan_doc.py +1092 -0
  251. phase_loop_runtime/skills_bundle/codex-run-train/README.md +1 -0
  252. phase_loop_runtime/skills_bundle/codex-run-train/SKILL.md +75 -0
  253. phase_loop_runtime/skills_bundle/codex-skill-editor/README.md +1 -0
  254. phase_loop_runtime/skills_bundle/codex-skill-editor/SKILL.md +94 -0
  255. phase_loop_runtime/skills_bundle/codex-skill-editor/assets/editor_prompt.md +72 -0
  256. phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/README.md +1 -0
  257. phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/SKILL.md +114 -0
  258. phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  259. phase_loop_runtime/skills_bundle/codex-task-contextualizer/README.md +1 -0
  260. phase_loop_runtime/skills_bundle/codex-task-contextualizer/SKILL.md +102 -0
  261. phase_loop_runtime/skills_bundle/codex-task-contextualizer/references/task-templates.md +103 -0
  262. phase_loop_runtime/skills_bundle/gemini-advisor-board/README.md +1 -0
  263. phase_loop_runtime/skills_bundle/gemini-advisor-board/SKILL.md +38 -0
  264. phase_loop_runtime/skills_bundle/gemini-advisor-panel/README.md +1 -0
  265. phase_loop_runtime/skills_bundle/gemini-advisor-panel/SKILL.md +38 -0
  266. phase_loop_runtime/skills_bundle/gemini-execute-detailed/README.md +1 -0
  267. phase_loop_runtime/skills_bundle/gemini-execute-detailed/SKILL.md +144 -0
  268. phase_loop_runtime/skills_bundle/gemini-execute-phase/README.md +1 -0
  269. phase_loop_runtime/skills_bundle/gemini-execute-phase/SKILL.md +198 -0
  270. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  271. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  272. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  273. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  274. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  275. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  276. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  277. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/state.py +274 -0
  278. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  279. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/team_teardown.sh +32 -0
  280. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/validate_plan_doc.py +642 -0
  281. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/verify_harness.sh +96 -0
  282. phase_loop_runtime/skills_bundle/gemini-phase-loop/README.md +1 -0
  283. phase_loop_runtime/skills_bundle/gemini-phase-loop/SKILL.md +111 -0
  284. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/README.md +1 -0
  285. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/SKILL.md +147 -0
  286. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/assets/review_prompt.md +19 -0
  287. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  288. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/roadmap-template.md +193 -0
  289. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  290. phase_loop_runtime/skills_bundle/gemini-plan-detailed/README.md +1 -0
  291. phase_loop_runtime/skills_bundle/gemini-plan-detailed/SKILL.md +119 -0
  292. phase_loop_runtime/skills_bundle/gemini-plan-detailed/assets/review_prompt.md +25 -0
  293. phase_loop_runtime/skills_bundle/gemini-plan-phase/README.md +1 -0
  294. phase_loop_runtime/skills_bundle/gemini-plan-phase/SKILL.md +217 -0
  295. phase_loop_runtime/skills_bundle/gemini-plan-phase/assets/review_prompt.md +17 -0
  296. phase_loop_runtime/skills_bundle/gemini-plan-phase/scripts/validate_plan_doc.py +1092 -0
  297. phase_loop_runtime/skills_bundle/gemini-run-train/README.md +1 -0
  298. phase_loop_runtime/skills_bundle/gemini-run-train/SKILL.md +75 -0
  299. phase_loop_runtime/skills_bundle/gemini-skill-editor/README.md +1 -0
  300. phase_loop_runtime/skills_bundle/gemini-skill-editor/SKILL.md +87 -0
  301. phase_loop_runtime/skills_bundle/gemini-skill-editor/assets/editor_prompt.md +72 -0
  302. phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/README.md +1 -0
  303. phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/SKILL.md +109 -0
  304. phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  305. phase_loop_runtime/skills_bundle/gemini-task-contextualizer/README.md +1 -0
  306. phase_loop_runtime/skills_bundle/gemini-task-contextualizer/SKILL.md +98 -0
  307. phase_loop_runtime/skills_bundle/gemini-task-contextualizer/references/task-templates.md +103 -0
  308. phase_loop_runtime/skills_bundle/opencode-advisor-board/README.md +1 -0
  309. phase_loop_runtime/skills_bundle/opencode-advisor-board/SKILL.md +38 -0
  310. phase_loop_runtime/skills_bundle/opencode-advisor-panel/README.md +1 -0
  311. phase_loop_runtime/skills_bundle/opencode-advisor-panel/SKILL.md +38 -0
  312. phase_loop_runtime/skills_bundle/opencode-execute-detailed/README.md +1 -0
  313. phase_loop_runtime/skills_bundle/opencode-execute-detailed/SKILL.md +144 -0
  314. phase_loop_runtime/skills_bundle/opencode-execute-phase/README.md +1 -0
  315. phase_loop_runtime/skills_bundle/opencode-execute-phase/SKILL.md +196 -0
  316. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  317. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  318. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  319. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  320. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  321. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  322. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  323. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/state.py +274 -0
  324. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  325. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/team_teardown.sh +32 -0
  326. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/validate_plan_doc.py +642 -0
  327. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/verify_harness.sh +96 -0
  328. phase_loop_runtime/skills_bundle/opencode-phase-loop/README.md +1 -0
  329. phase_loop_runtime/skills_bundle/opencode-phase-loop/SKILL.md +113 -0
  330. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/README.md +1 -0
  331. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/SKILL.md +147 -0
  332. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/assets/review_prompt.md +19 -0
  333. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  334. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/roadmap-template.md +193 -0
  335. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  336. phase_loop_runtime/skills_bundle/opencode-plan-detailed/README.md +1 -0
  337. phase_loop_runtime/skills_bundle/opencode-plan-detailed/SKILL.md +119 -0
  338. phase_loop_runtime/skills_bundle/opencode-plan-detailed/assets/review_prompt.md +25 -0
  339. phase_loop_runtime/skills_bundle/opencode-plan-phase/README.md +1 -0
  340. phase_loop_runtime/skills_bundle/opencode-plan-phase/SKILL.md +211 -0
  341. phase_loop_runtime/skills_bundle/opencode-plan-phase/assets/review_prompt.md +17 -0
  342. phase_loop_runtime/skills_bundle/opencode-plan-phase/scripts/validate_plan_doc.py +1092 -0
  343. phase_loop_runtime/skills_bundle/opencode-run-train/README.md +1 -0
  344. phase_loop_runtime/skills_bundle/opencode-run-train/SKILL.md +75 -0
  345. phase_loop_runtime/skills_bundle/opencode-skill-editor/README.md +1 -0
  346. phase_loop_runtime/skills_bundle/opencode-skill-editor/SKILL.md +87 -0
  347. phase_loop_runtime/skills_bundle/opencode-skill-editor/assets/editor_prompt.md +72 -0
  348. phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/README.md +1 -0
  349. phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/SKILL.md +109 -0
  350. phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  351. phase_loop_runtime/skills_bundle/opencode-task-contextualizer/README.md +1 -0
  352. phase_loop_runtime/skills_bundle/opencode-task-contextualizer/SKILL.md +98 -0
  353. phase_loop_runtime/skills_bundle/opencode-task-contextualizer/references/task-templates.md +103 -0
  354. phase_loop_runtime/state.py +156 -0
  355. phase_loop_runtime/state_degradation.py +136 -0
  356. phase_loop_runtime/state_ops.py +676 -0
  357. phase_loop_runtime/train_ledger.py +215 -0
  358. phase_loop_runtime/train_roadmap.py +339 -0
  359. phase_loop_runtime/train_runner.py +1197 -0
  360. phase_loop_runtime/verification_evidence.py +650 -0
  361. phase_loop_runtime/verification_evidence_validator.py +75 -0
  362. phase_loop_runtime/visual_evidence_validator.py +36 -0
  363. phase_loop_runtime/worker_pool.py +123 -0
  364. phase_loop_runtime/worktree_index.py +270 -0
  365. phase_loop_runtime-0.2.0.data/data/share/phase-loop-runtime/protocol/protocol.md +2475 -0
  366. phase_loop_runtime-0.2.0.dist-info/METADATA +106 -0
  367. phase_loop_runtime-0.2.0.dist-info/RECORD +370 -0
  368. phase_loop_runtime-0.2.0.dist-info/WHEEL +5 -0
  369. phase_loop_runtime-0.2.0.dist-info/entry_points.txt +9 -0
  370. phase_loop_runtime-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ import shutil
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from .baml_modular import BamlValidationError, export_function_schema
10
+ from .skill_install import REQUIRED_SKILLS
11
+ from .skill_paths import HARNESS_DEFAULT_SKILL_ROOTS, resolve_skill_bundle_root
12
+
13
+
14
+ def build_install_status(repo: Path, harnesses: tuple[str, ...] | None = None) -> dict[str, Any]:
15
+ selected = harnesses or tuple(HARNESS_DEFAULT_SKILL_ROOTS)
16
+ records = tuple(_harness_record(harness) for harness in selected)
17
+ payload = {
18
+ "schema": "phase-loop-install-status.v1",
19
+ "summary": summarize_install_status(records),
20
+ "harnesses": records,
21
+ "console_scripts": _console_scripts(),
22
+ "baml_closeout_schema": _baml_schema_status(),
23
+ "dev_skills_ignore": _dev_skills_ignore(repo),
24
+ }
25
+ _assert_redacted(payload)
26
+ return payload
27
+
28
+
29
+ def summarize_install_status(records: tuple[dict[str, Any], ...] | list[dict[str, Any]]) -> str:
30
+ if not records:
31
+ return "unknown"
32
+ if all(record.get("skill_parity") == "complete" for record in records):
33
+ return "installed"
34
+ if any(record.get("root_status") == "missing" for record in records):
35
+ return "partial"
36
+ return "attention"
37
+
38
+
39
+ def _harness_record(harness: str) -> dict[str, Any]:
40
+ root = resolve_skill_bundle_root(harness)
41
+ missing = tuple(
42
+ f"{harness}-{skill}"
43
+ for skill in REQUIRED_SKILLS
44
+ if not (root / f"{harness}-{skill}" / "SKILL.md").is_file()
45
+ )
46
+ return {
47
+ "harness": harness,
48
+ "root_status": "present" if root.exists() else "missing",
49
+ "skill_parity": "complete" if not missing else "missing",
50
+ "required_skill_count": len(REQUIRED_SKILLS),
51
+ "missing_skill_count": len(missing),
52
+ "missing_skills": missing,
53
+ }
54
+
55
+
56
+ def _console_scripts() -> dict[str, str]:
57
+ return {
58
+ "phase-loop": "available" if shutil.which("phase-loop") else "missing",
59
+ "codex-phase-loop": "available" if shutil.which("codex-phase-loop") else "missing",
60
+ }
61
+
62
+
63
+ def _baml_schema_status() -> dict[str, str]:
64
+ try:
65
+ schema = export_function_schema("EmitPhaseCloseout")
66
+ except BamlValidationError:
67
+ return {"status": "missing", "schema": "EmitPhaseCloseout"}
68
+ return {"status": "available", "schema": str(schema.get("title") or "EmitPhaseCloseout")}
69
+
70
+
71
+ def _dev_skills_ignore(repo: Path) -> dict[str, str]:
72
+ gitignore = repo / ".gitignore"
73
+ lines = gitignore.read_text(encoding="utf-8").splitlines() if gitignore.is_file() else []
74
+ ignored = "/.dev-skills/" in lines or ".dev-skills/" in lines
75
+ return {
76
+ "directory": "present" if (repo / ".dev-skills").exists() else "missing",
77
+ "gitignore_entry": "present" if ignored else "missing",
78
+ }
79
+
80
+
81
+ def _assert_redacted(payload: dict[str, Any]) -> None:
82
+ serialized = json.dumps(payload, sort_keys=True)
83
+ forbidden = ("/home/", "/Users/", "/mnt/", "op://", "AKIA", "ghp_")
84
+ leaked = [token for token in forbidden if token in serialized]
85
+ if re.search(r"\bsk-[A-Za-z0-9]{8,}", serialized):
86
+ leaked.append("sk-*")
87
+ if leaked:
88
+ raise ValueError(f"install status contains forbidden metadata token: {leaked[0]}")
@@ -0,0 +1,317 @@
1
+ from __future__ import annotations
2
+
3
+ from fnmatch import fnmatchcase
4
+ from pathlib import Path
5
+
6
+ from .models import (
7
+ LaneIRDiagnostic,
8
+ LaneWave,
9
+ LaneWaveDecision,
10
+ LaneWorktreeAssignment,
11
+ PhasePlanIR,
12
+ PhasePlanLane,
13
+ WorkUnitState,
14
+ )
15
+ from .runtime_paths import lane_worktree_path
16
+
17
+
18
+ COMPLETE_WORK_UNIT_STATUSES = {"complete", "skipped"}
19
+
20
+
21
+ def build_lane_wave_plan(
22
+ ir: PhasePlanIR,
23
+ work_units: dict[str, WorkUnitState] | None = None,
24
+ *,
25
+ mode: str = "serialized",
26
+ assignments: tuple[LaneWorktreeAssignment, ...] = (),
27
+ ) -> tuple[LaneWaveDecision, ...]:
28
+ decisions: list[LaneWaveDecision] = []
29
+ remaining = tuple(ir.lanes)
30
+ completed = set(_completed_lane_ids(ir, work_units or {}))
31
+ while True:
32
+ decision = select_ready_lane_wave(
33
+ ir,
34
+ work_units or {},
35
+ mode=mode,
36
+ completed_lane_ids=tuple(completed),
37
+ assignments=assignments,
38
+ )
39
+ decisions.append(decision)
40
+ if decision.status != "ready" or decision.ready_wave is None:
41
+ break
42
+ completed.update(decision.ready_wave.lane_ids)
43
+ if not [lane for lane in remaining if lane.lane_id not in completed]:
44
+ break
45
+ return tuple(decisions)
46
+
47
+
48
+ def select_ready_lane_wave(
49
+ ir: PhasePlanIR,
50
+ work_units: dict[str, WorkUnitState] | None = None,
51
+ *,
52
+ mode: str = "serialized",
53
+ completed_lane_ids: tuple[str, ...] = (),
54
+ assignments: tuple[LaneWorktreeAssignment, ...] = (),
55
+ expected_base_sha: str | None = None,
56
+ ) -> LaneWaveDecision:
57
+ if mode not in {"serialized", "concurrent"}:
58
+ raise ValueError(f"invalid lane scheduler mode: {mode}")
59
+ if ir.diagnostics:
60
+ return LaneWaveDecision(status="blocked", mode=mode, diagnostics=ir.diagnostics)
61
+ work_units = work_units or {}
62
+ completed = set(completed_lane_ids) | set(_completed_lane_ids(ir, work_units))
63
+ blocked = _blocked_lane_ids(ir, work_units)
64
+ active = _active_lane_ids(ir, work_units)
65
+ ready = [
66
+ lane
67
+ for lane in ir.lanes
68
+ if lane.lane_id not in completed
69
+ and lane.lane_id not in blocked
70
+ and _dependencies_complete(lane, completed)
71
+ and lane.lane_id not in active
72
+ ]
73
+ pending = tuple(lane.lane_id for lane in ir.lanes if lane.lane_id not in completed)
74
+ if not ready:
75
+ status = "empty" if not pending else "blocked"
76
+ return LaneWaveDecision(
77
+ status=status,
78
+ mode=mode,
79
+ pending_lane_ids=pending,
80
+ completed_lane_ids=tuple(_stable_lane_order(ir, completed)),
81
+ blocked_lane_ids=tuple(_stable_lane_order(ir, blocked)),
82
+ diagnostics=_work_unit_blocking_diagnostics(ir, active=active, blocked=blocked),
83
+ )
84
+ selected = ready[:1] if mode == "serialized" else _concurrent_ready_lanes(ready)
85
+ diagnostics = (
86
+ validate_concurrent_lane_ownership(selected, assignments=assignments, expected_base_sha=expected_base_sha)
87
+ if mode == "concurrent"
88
+ else ()
89
+ )
90
+ if diagnostics:
91
+ return LaneWaveDecision(
92
+ status="blocked",
93
+ mode=mode,
94
+ pending_lane_ids=pending,
95
+ completed_lane_ids=tuple(_stable_lane_order(ir, completed)),
96
+ blocked_lane_ids=tuple(_stable_lane_order(ir, blocked)),
97
+ diagnostics=diagnostics,
98
+ )
99
+ wave_id = f"wave-{len(completed) + 1:03d}"
100
+ wave_assignments = _assignments_for_lanes(selected, assignments)
101
+ return LaneWaveDecision(
102
+ status="ready",
103
+ mode=mode,
104
+ ready_wave=LaneWave(
105
+ wave_id=wave_id,
106
+ lane_ids=tuple(lane.lane_id for lane in selected),
107
+ mode=mode,
108
+ assignments=wave_assignments,
109
+ ),
110
+ pending_lane_ids=pending,
111
+ completed_lane_ids=tuple(_stable_lane_order(ir, completed)),
112
+ blocked_lane_ids=tuple(_stable_lane_order(ir, blocked)),
113
+ )
114
+
115
+
116
+ def validate_concurrent_lane_ownership(
117
+ lanes: tuple[PhasePlanLane, ...] | list[PhasePlanLane],
118
+ *,
119
+ assignments: tuple[LaneWorktreeAssignment, ...] = (),
120
+ expected_base_sha: str | None = None,
121
+ ) -> tuple[LaneIRDiagnostic, ...]:
122
+ diagnostics: list[LaneIRDiagnostic] = []
123
+ writers = [lane for lane in lanes if not lane.read_only]
124
+ assignment_by_lane = {assignment.lane_id: assignment for assignment in assignments}
125
+ if len(writers) > 1:
126
+ for lane in writers:
127
+ assignment = assignment_by_lane.get(lane.lane_id)
128
+ if assignment is None or assignment.isolation_mode != "git_worktree":
129
+ diagnostics.append(
130
+ LaneIRDiagnostic(
131
+ kind="unsafe_concurrent_lane",
132
+ lane_id=lane.lane_id,
133
+ message=f"{lane.lane_id} cannot launch concurrently without an isolated git worktree assignment",
134
+ details={"lane_id": lane.lane_id},
135
+ )
136
+ )
137
+ elif expected_base_sha and assignment.base_sha and assignment.base_sha != expected_base_sha:
138
+ diagnostics.append(
139
+ LaneIRDiagnostic(
140
+ kind="stale_worktree_assignment",
141
+ lane_id=lane.lane_id,
142
+ message=f"{lane.lane_id} cannot launch concurrently from stale base SHA {assignment.base_sha}",
143
+ details={"lane_id": lane.lane_id, "base_sha": assignment.base_sha, "expected_base_sha": expected_base_sha},
144
+ )
145
+ )
146
+ for index, left in enumerate(writers):
147
+ for right in writers[index + 1 :]:
148
+ if _patterns_overlap_any(left.owned_files, right.owned_files):
149
+ diagnostics.append(
150
+ LaneIRDiagnostic(
151
+ kind="overlapping_write_ownership",
152
+ lane_id=right.lane_id,
153
+ message=f"{left.lane_id} and {right.lane_id} cannot launch concurrently with overlapping owned files",
154
+ details={"left": left.lane_id, "right": right.lane_id},
155
+ )
156
+ )
157
+ return tuple(diagnostics)
158
+
159
+
160
+ def worktree_assignments_for_wave(
161
+ repo: Path,
162
+ lanes: tuple[PhasePlanLane, ...] | list[PhasePlanLane],
163
+ *,
164
+ branch: str,
165
+ mode: str,
166
+ base_sha: str | None = None,
167
+ ) -> tuple[LaneWorktreeAssignment, ...]:
168
+ if mode == "serialized":
169
+ return tuple(
170
+ LaneWorktreeAssignment(lane_id=lane.lane_id, worktree_path=str(repo), isolation_mode="main_worktree")
171
+ for lane in lanes
172
+ )
173
+ return tuple(
174
+ LaneWorktreeAssignment(
175
+ lane_id=lane.lane_id,
176
+ worktree_path=str(lane_worktree_path(repo, branch=branch, lane_id=lane.lane_id)),
177
+ isolation_mode="git_worktree",
178
+ branch=branch,
179
+ base_sha=base_sha,
180
+ )
181
+ for lane in lanes
182
+ )
183
+
184
+
185
+ def worktree_assignments_for_phase_wave(
186
+ repo: Path,
187
+ phases: tuple[str, ...] | list[str],
188
+ *,
189
+ branch: str,
190
+ mode: str,
191
+ base_sha: str | None = None,
192
+ ) -> tuple[LaneWorktreeAssignment, ...]:
193
+ if mode == "serialized":
194
+ return tuple(
195
+ LaneWorktreeAssignment(lane_id=phase, worktree_path=str(repo), isolation_mode="main_worktree")
196
+ for phase in phases
197
+ )
198
+ return tuple(
199
+ LaneWorktreeAssignment(
200
+ lane_id=phase,
201
+ worktree_path=str(lane_worktree_path(repo, branch=branch, lane_id=phase)),
202
+ isolation_mode="git_worktree",
203
+ branch=branch,
204
+ base_sha=base_sha,
205
+ )
206
+ for phase in phases
207
+ )
208
+
209
+
210
+ def _completed_lane_ids(ir: PhasePlanIR, work_units: dict[str, WorkUnitState]) -> tuple[str, ...]:
211
+ completed = {
212
+ state.identity.lane_id
213
+ for state in work_units.values()
214
+ if state.identity.phase == str(ir.metadata.get("phase", "")).upper() and state.status in COMPLETE_WORK_UNIT_STATUSES
215
+ }
216
+ return tuple(_stable_lane_order(ir, completed))
217
+
218
+
219
+ def _blocked_lane_ids(ir: PhasePlanIR, work_units: dict[str, WorkUnitState]) -> set[str]:
220
+ phase = str(ir.metadata.get("phase", "")).upper()
221
+ return {
222
+ state.identity.lane_id
223
+ for state in work_units.values()
224
+ if state.identity.phase == phase and state.status == "blocked" and state.human_required
225
+ }
226
+
227
+
228
+ def _dependencies_complete(lane: PhasePlanLane, completed: set[str]) -> bool:
229
+ return all(dependency in completed for dependency in lane.depends_on)
230
+
231
+
232
+ def _active_lane_ids(ir: PhasePlanIR, work_units: dict[str, WorkUnitState]) -> set[str]:
233
+ phase = str(ir.metadata.get("phase", "")).upper()
234
+ return {
235
+ state.identity.lane_id
236
+ for state in work_units.values()
237
+ if state.identity.phase == phase and state.status in {"running", "awaiting-closeout"}
238
+ }
239
+
240
+
241
+ def _work_unit_blocking_diagnostics(
242
+ ir: PhasePlanIR,
243
+ *,
244
+ active: set[str],
245
+ blocked: set[str],
246
+ ) -> tuple[LaneIRDiagnostic, ...]:
247
+ diagnostics: list[LaneIRDiagnostic] = []
248
+ for lane_id in _stable_lane_order(ir, active):
249
+ diagnostics.append(
250
+ LaneIRDiagnostic(
251
+ kind="active_work_unit",
252
+ lane_id=lane_id,
253
+ message=f"{lane_id} already has an active work unit",
254
+ details={"lane_id": lane_id},
255
+ )
256
+ )
257
+ for lane_id in _stable_lane_order(ir, blocked):
258
+ diagnostics.append(
259
+ LaneIRDiagnostic(
260
+ kind="human_required_blocked_work_unit",
261
+ lane_id=lane_id,
262
+ message=f"{lane_id} has a human-required blocked work unit",
263
+ details={"lane_id": lane_id},
264
+ )
265
+ )
266
+ return tuple(diagnostics)
267
+
268
+
269
+ def _stable_lane_order(ir: PhasePlanIR, lane_ids: set[str]) -> list[str]:
270
+ return [lane.lane_id for lane in ir.lanes if lane.lane_id in lane_ids]
271
+
272
+
273
+ def _concurrent_ready_lanes(ready: list[PhasePlanLane]) -> tuple[PhasePlanLane, ...]:
274
+ if len(ready) <= 1:
275
+ return tuple(ready)
276
+ prefix = ready[0].lane_id.split("-", 1)[0]
277
+ selected = [lane for lane in ready if lane.parallel_safe and lane.reducer_kind == "none" and lane.lane_id.startswith(prefix)]
278
+ return tuple(selected or ready[:1])
279
+
280
+
281
+ def _assignments_for_lanes(
282
+ lanes: tuple[PhasePlanLane, ...] | list[PhasePlanLane],
283
+ assignments: tuple[LaneWorktreeAssignment, ...],
284
+ ) -> tuple[LaneWorktreeAssignment, ...]:
285
+ by_lane = {assignment.lane_id: assignment for assignment in assignments}
286
+ return tuple(by_lane[lane.lane_id] for lane in lanes if lane.lane_id in by_lane)
287
+
288
+
289
+ def _patterns_overlap_any(left_patterns: tuple[str, ...], right_patterns: tuple[str, ...]) -> bool:
290
+ return any(_patterns_overlap(left, right) for left in left_patterns for right in right_patterns)
291
+
292
+
293
+ def _patterns_overlap(left: str, right: str) -> bool:
294
+ if left == right:
295
+ return True
296
+ left_has_glob = _has_glob(left)
297
+ right_has_glob = _has_glob(right)
298
+ if left_has_glob and fnmatchcase(right, left):
299
+ return True
300
+ if right_has_glob and fnmatchcase(left, right):
301
+ return True
302
+ if left_has_glob and right_has_glob:
303
+ left_prefix = _pattern_prefix(left)
304
+ right_prefix = _pattern_prefix(right)
305
+ return bool(left_prefix and right_prefix and (left_prefix.startswith(right_prefix) or right_prefix.startswith(left_prefix)))
306
+ return left.startswith(right.rstrip("/") + "/") or right.startswith(left.rstrip("/") + "/")
307
+
308
+
309
+ def _has_glob(pattern: str) -> bool:
310
+ return any(token in pattern for token in ("*", "?", "["))
311
+
312
+
313
+ def _pattern_prefix(pattern: str) -> str:
314
+ for index, char in enumerate(pattern):
315
+ if char in "*?[":
316
+ return pattern[:index].rstrip("/")
317
+ return pattern.rstrip("/")