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,462 @@
1
+ from __future__ import annotations
2
+
3
+ from .capability_registry import (
4
+ CLAUDE_HEAVY_MODEL,
5
+ DEFAULT_EXECUTOR,
6
+ default_model_profile_for_executor,
7
+ provider_policy_capabilities,
8
+ )
9
+ from .models import (
10
+ ExecutionPolicyRule,
11
+ ModelSelection,
12
+ ResolvedExecutionPolicy,
13
+ WorkUnitPolicy,
14
+ require_literal,
15
+ )
16
+
17
+
18
+ OPENAI_HEAVY_MODEL = "gpt-5.5"
19
+ OPENCODE_OPENAI_HEAVY_MODEL = "openai/gpt-5.5"
20
+ GEMINI_PRO_ROUTED_MODEL = "pro"
21
+ GEMINI_AUTO_ROUTED_MODEL = "auto"
22
+ GEMINI_FLASH_MODEL = "Gemini 3.5 Flash (High)"
23
+ PI_AUTO_ROUTED_MODEL = "auto"
24
+
25
+ DEFAULT_PROFILES = {
26
+ "roadmap": (OPENAI_HEAVY_MODEL, "high"),
27
+ "plan": (OPENAI_HEAVY_MODEL, "high"),
28
+ "execute": (OPENAI_HEAVY_MODEL, "medium"),
29
+ "repair": (OPENAI_HEAVY_MODEL, "medium"),
30
+ "review": (OPENAI_HEAVY_MODEL, "high"),
31
+ "skill-maintenance": (OPENAI_HEAVY_MODEL, "high"),
32
+ }
33
+
34
+ ACTION_WORK_UNITS = {
35
+ "roadmap": "roadmap_build",
36
+ "plan": "phase_plan",
37
+ "execute": "lane_execute",
38
+ "repair": "repair",
39
+ "review": "lane_review",
40
+ "maintain-skills": "phase_verify",
41
+ }
42
+
43
+ EXECUTOR_MODEL_OVERRIDES = {
44
+ "claude": {
45
+ "roadmap": CLAUDE_HEAVY_MODEL,
46
+ "plan": CLAUDE_HEAVY_MODEL,
47
+ "execute": CLAUDE_HEAVY_MODEL,
48
+ "repair": CLAUDE_HEAVY_MODEL,
49
+ "review": CLAUDE_HEAVY_MODEL,
50
+ },
51
+ "opencode": {
52
+ "roadmap": OPENCODE_OPENAI_HEAVY_MODEL,
53
+ "plan": OPENCODE_OPENAI_HEAVY_MODEL,
54
+ "execute": OPENCODE_OPENAI_HEAVY_MODEL,
55
+ "repair": OPENCODE_OPENAI_HEAVY_MODEL,
56
+ "review": OPENCODE_OPENAI_HEAVY_MODEL,
57
+ },
58
+ "gemini": {
59
+ "roadmap": GEMINI_PRO_ROUTED_MODEL,
60
+ "plan": GEMINI_PRO_ROUTED_MODEL,
61
+ "execute": GEMINI_AUTO_ROUTED_MODEL,
62
+ "repair": GEMINI_AUTO_ROUTED_MODEL,
63
+ "review": GEMINI_PRO_ROUTED_MODEL,
64
+ },
65
+ "pi": {
66
+ "roadmap": PI_AUTO_ROUTED_MODEL,
67
+ "plan": PI_AUTO_ROUTED_MODEL,
68
+ "execute": PI_AUTO_ROUTED_MODEL,
69
+ "repair": PI_AUTO_ROUTED_MODEL,
70
+ "review": PI_AUTO_ROUTED_MODEL,
71
+ },
72
+ }
73
+
74
+ EXECUTOR_EFFORT_OVERRIDES = {
75
+ "claude": {
76
+ "roadmap": "high",
77
+ "plan": "high",
78
+ "execute": "high",
79
+ "repair": "high",
80
+ "review": "high",
81
+ },
82
+ }
83
+
84
+ # --- model-routing-v1: vendor-agnostic model_class -> concrete model ----------
85
+ # Where a provider exposes no separate implementer/worker tier, all classes map
86
+ # to its single model (pi). Non-`phase-loop-` model strings pass through
87
+ # `_resolve_policy_model` unchanged for every executor (claude/codex have no
88
+ # model_aliases; gemini/pi pass through non-alias strings), so these resolve.
89
+ CLAUDE_IMPLEMENTER_MODEL = "claude-sonnet-5"
90
+ CLAUDE_WORKER_MODEL = "claude-haiku-4-5"
91
+ OPENAI_IMPLEMENTER_MODEL = "gpt-5.4"
92
+ OPENAI_WORKER_MODEL = "gpt-5.4-mini"
93
+ OPENCODE_OPENAI_IMPLEMENTER_MODEL = "openai/gpt-5.4"
94
+ OPENCODE_OPENAI_WORKER_MODEL = "openai/gpt-5.4-mini"
95
+ # Gemini planner stays on the CLI `pro` alias; bounded implementer/worker lanes
96
+ # use the validated agy model name directly rather than the broad `auto` alias.
97
+ GEMINI_IMPLEMENTER_MODEL = GEMINI_FLASH_MODEL
98
+ GEMINI_WORKER_MODEL = GEMINI_FLASH_MODEL
99
+
100
+ CLASS_MODEL_OVERRIDES = {
101
+ "claude": {
102
+ "planner": CLAUDE_HEAVY_MODEL,
103
+ "implementer": CLAUDE_IMPLEMENTER_MODEL,
104
+ "worker": CLAUDE_WORKER_MODEL,
105
+ },
106
+ "codex": {
107
+ "planner": OPENAI_HEAVY_MODEL,
108
+ "implementer": OPENAI_IMPLEMENTER_MODEL,
109
+ "worker": OPENAI_WORKER_MODEL,
110
+ },
111
+ "opencode": {
112
+ "planner": OPENCODE_OPENAI_HEAVY_MODEL,
113
+ "implementer": OPENCODE_OPENAI_IMPLEMENTER_MODEL,
114
+ "worker": OPENCODE_OPENAI_WORKER_MODEL,
115
+ },
116
+ "gemini": {
117
+ "planner": GEMINI_PRO_ROUTED_MODEL,
118
+ "implementer": GEMINI_IMPLEMENTER_MODEL,
119
+ "worker": GEMINI_WORKER_MODEL,
120
+ },
121
+ "pi": {
122
+ "planner": PI_AUTO_ROUTED_MODEL,
123
+ "implementer": PI_AUTO_ROUTED_MODEL,
124
+ "worker": PI_AUTO_ROUTED_MODEL,
125
+ },
126
+ }
127
+
128
+
129
+ def resolve_model_class(executor: str, model_class: str) -> str | None:
130
+ """Map (model_class, executor) -> concrete model, or None if unmapped."""
131
+ return CLASS_MODEL_OVERRIDES.get(executor, {}).get(model_class)
132
+
133
+
134
+ # Actions that author a final patch. The `worker` class (bounded, high-volume
135
+ # subtasks) must never own these — enforced as a routing invariant (P5).
136
+ PATCH_AUTHORING_ACTIONS: tuple[str, ...] = ("execute", "repair")
137
+
138
+
139
+ def max_effort_planner_eligible(executor: str) -> bool:
140
+ """True iff `executor`'s planner-class model can actually run at `max` effort.
141
+
142
+ The "planner of record" for a max-effort planning action must deliver max
143
+ reasoning. Gemini ceilings at `high` (its `effort_map` clamps `max -> high`),
144
+ so it is NOT eligible as the max-effort planner of record — it serves as a
145
+ panel member instead, never the authoritative planner. This is the
146
+ dispatch-selection guard the effort clamp alone does not provide: the clamp
147
+ keeps gemini from *running* at max, but only this guard keeps it from being
148
+ *selected* as the max-effort planner.
149
+ """
150
+ capability = provider_policy_capabilities().get(executor)
151
+ return bool(capability and "max" in capability.supported_efforts)
152
+
153
+
154
+ # The repo's SHIPPED model_policy. THIS repo's default: planning at max,
155
+ # implementation at the implementer model. `clamp=True` resolves a sub-max
156
+ # provider's `max` request to its ceiling via the provider effort_map fallback
157
+ # (otherwise normalize_provider_effort RAISES). A downstream repo that ships no
158
+ # policy keeps the registry defaults — that empty-policy path is the back-compat
159
+ # contract (callers pass model_policy_rule=None to get it).
160
+ SHIPPED_MODEL_POLICY = {
161
+ "roadmap": {"model_class": "planner", "effort": "max", "clamp": True},
162
+ "plan": {"model_class": "planner", "effort": "max", "clamp": True},
163
+ "execute": {"model_class": "implementer", "effort": "medium"},
164
+ "repair": {"model_class": "implementer", "effort": "medium"},
165
+ "review": {"model_class": "planner", "effort": "high", "clamp": True},
166
+ }
167
+
168
+
169
+ def shipped_model_policy_rule(action: str) -> ExecutionPolicyRule | None:
170
+ """The shipped model_policy rule for an action, or None if unmapped."""
171
+ spec = SHIPPED_MODEL_POLICY.get(action)
172
+ if spec is None:
173
+ return None
174
+ clamp = bool(spec.get("clamp", False))
175
+ return ExecutionPolicyRule(
176
+ selector=action,
177
+ action=action,
178
+ model_class=spec.get("model_class"),
179
+ effort=spec.get("effort"),
180
+ unsupported_policy_behavior="fallback" if clamp else "block",
181
+ fallback="high" if clamp else None,
182
+ source="model_policy",
183
+ override_reason="shipped model_policy (model-routing-v1)",
184
+ )
185
+
186
+
187
+ def normalize_provider_effort(
188
+ *,
189
+ provider_key: str,
190
+ work_unit_policy: WorkUnitPolicy,
191
+ default_effort: str | None = None,
192
+ ) -> str:
193
+ capabilities = provider_policy_capabilities()
194
+ if provider_key not in capabilities:
195
+ raise ValueError(f"unknown provider policy capability: {provider_key}")
196
+
197
+ capability = capabilities[provider_key]
198
+ require_literal(work_unit_policy.work_unit_kind, capability.supported_work_units, "provider work-unit kind")
199
+ requested_effort = work_unit_policy.effort or default_effort or capability.default_effort
200
+ if requested_effort is None:
201
+ if work_unit_policy.unsupported_policy_behavior == "inherit_default" and work_unit_policy.inherit_default:
202
+ requested_effort = capability.default_effort
203
+ if requested_effort is None:
204
+ raise ValueError(f"no default effort for provider policy capability: {provider_key}")
205
+
206
+ if requested_effort in capability.supported_efforts:
207
+ return requested_effort
208
+
209
+ if work_unit_policy.unsupported_policy_behavior == "inherit_default" and capability.default_effort:
210
+ return capability.default_effort
211
+ if work_unit_policy.unsupported_policy_behavior == "fallback" and work_unit_policy.fallback:
212
+ fallback_effort = capability.effort_map.get(work_unit_policy.fallback, work_unit_policy.fallback)
213
+ if fallback_effort in capability.supported_efforts:
214
+ return fallback_effort
215
+ raise ValueError(f"unsupported effort `{requested_effort}` for provider `{provider_key}`")
216
+
217
+
218
+ def resolve_profile(profile: str, model: str | None = None, effort: str | None = None) -> ModelSelection:
219
+ default_model, default_effort = DEFAULT_PROFILES[profile]
220
+ selected_model = model or default_model
221
+ selected_effort = effort or default_effort
222
+ if model or effort:
223
+ return ModelSelection(
224
+ profile=profile,
225
+ model=selected_model,
226
+ effort=selected_effort,
227
+ source="user_override",
228
+ override_reason="user supplied --model or --effort",
229
+ )
230
+ return ModelSelection(profile=profile, model=selected_model, effort=selected_effort)
231
+
232
+
233
+ def resolve_profile_for_executor(
234
+ *,
235
+ action: str,
236
+ executor: str,
237
+ profile: str | None = None,
238
+ model: str | None = None,
239
+ effort: str | None = None,
240
+ ) -> ModelSelection:
241
+ selected_profile = profile or default_model_profile_for_executor(action, executor)
242
+ selection = resolve_profile(selected_profile, model=model, effort=effort)
243
+ if model is not None:
244
+ return selection
245
+ executor_default = EXECUTOR_MODEL_OVERRIDES.get(executor, {}).get(action)
246
+ effort_default = EXECUTOR_EFFORT_OVERRIDES.get(executor, {}).get(action)
247
+ if not executor_default and not effort_default:
248
+ return selection
249
+ return ModelSelection(
250
+ profile=selection.profile,
251
+ model=executor_default or selection.model,
252
+ effort=effort or effort_default or selection.effort,
253
+ source=f"{executor}_default",
254
+ override_reason=f"{executor} live adapter default model alias",
255
+ )
256
+
257
+
258
+ def resolve_execution_policy(
259
+ *,
260
+ action: str,
261
+ executor: str,
262
+ model_selection: ModelSelection,
263
+ operator_model: str | None = None,
264
+ operator_effort: str | None = None,
265
+ plan_policy: ExecutionPolicyRule | None = None,
266
+ roadmap_policy: ExecutionPolicyRule | None = None,
267
+ model_policy_rule: ExecutionPolicyRule | None = None,
268
+ lane: str | None = None,
269
+ ) -> ResolvedExecutionPolicy:
270
+ require_literal(action, tuple(ACTION_WORK_UNITS.keys()), "execution policy action")
271
+ policy, source = _merge_policies(plan_policy, roadmap_policy, model_policy_rule)
272
+ if _claude_model_needs_claude_executor(executor, model_selection.model, policy):
273
+ executor = "claude"
274
+ work_unit_kind = (
275
+ (policy.work_unit_kind if policy else None)
276
+ or ACTION_WORK_UNITS[action]
277
+ )
278
+ policy_executor = executor
279
+ executor_source = "dispatch decision"
280
+ policy_model = model_selection.model
281
+ model_source = model_selection.source
282
+ policy_effort = model_selection.effort
283
+ effort_source = model_selection.source
284
+ fallback = policy.fallback if policy else None
285
+ fallback_source = source or "registry defaults"
286
+ unsupported_behavior = policy.unsupported_policy_behavior if policy else "block"
287
+ override_reason = policy.override_reason if policy else model_selection.override_reason
288
+
289
+ if policy is not None:
290
+ if policy.executor is not None:
291
+ policy_executor = policy.executor
292
+ executor_source = source or policy.source
293
+ if policy.model is not None:
294
+ policy_model = policy.model
295
+ model_source = source or policy.source
296
+ elif policy.model_class is not None:
297
+ # model_class -> concrete model for the resolved executor. An
298
+ # explicit `model` always wins; a class only fills in when no model
299
+ # is given (model-routing-v1).
300
+ class_model = resolve_model_class(policy_executor, policy.model_class)
301
+ if class_model is not None:
302
+ policy_model = class_model
303
+ model_source = source or policy.source
304
+ if policy.effort is not None:
305
+ policy_effort = policy.effort
306
+ effort_source = source or policy.source
307
+
308
+ if operator_model is not None:
309
+ policy_model = operator_model
310
+ model_source = "CLI/operator override"
311
+ override_reason = "operator supplied --model"
312
+ if operator_effort is not None:
313
+ policy_effort = operator_effort
314
+ effort_source = "CLI/operator override"
315
+ override_reason = "operator supplied --effort"
316
+
317
+ # model-routing-v1 guard (wired, not just asserted): an executor whose
318
+ # planner-class model cannot actually run at `max` (gemini/pi) must never be
319
+ # the max-effort planner of record. Force the clamp so its `max` request
320
+ # resolves to the provider ceiling instead of raising, regardless of whether
321
+ # the policy opted into a fallback. This makes the effort clamp + this guard
322
+ # jointly enforce the invariant at the dispatch-resolution boundary.
323
+ policy_model_class = policy.model_class if policy else None
324
+ if (
325
+ policy_model_class == "planner"
326
+ and policy_effort == "max"
327
+ and not max_effort_planner_eligible(policy_executor)
328
+ ):
329
+ unsupported_behavior = "fallback"
330
+ if not fallback:
331
+ fallback = "high"
332
+
333
+ work_unit_policy = WorkUnitPolicy(
334
+ work_unit_kind=work_unit_kind,
335
+ effort=policy_effort,
336
+ unsupported_policy_behavior=unsupported_behavior,
337
+ fallback=fallback,
338
+ inherit_default=bool(policy.inherit_default) if policy else False,
339
+ )
340
+ normalized_effort = normalize_provider_effort(
341
+ provider_key=policy_executor,
342
+ work_unit_policy=work_unit_policy,
343
+ default_effort=model_selection.effort,
344
+ )
345
+ fallback_applied = normalized_effort != policy_effort
346
+ resolved_model = _resolve_policy_model(policy_executor, work_unit_kind, policy_model, fallback, unsupported_behavior)
347
+ return ResolvedExecutionPolicy(
348
+ action=action,
349
+ lane=lane,
350
+ executor=policy_executor,
351
+ model=resolved_model,
352
+ effort=normalized_effort,
353
+ work_unit_kind=work_unit_kind,
354
+ fallback=fallback,
355
+ unsupported_policy_behavior=unsupported_behavior,
356
+ execution_policy_source=source or "registry defaults",
357
+ execution_policy_override_reason=override_reason,
358
+ executor_source=executor_source,
359
+ model_source=model_source,
360
+ effort_source=effort_source,
361
+ fallback_source=fallback_source,
362
+ fallback_applied=fallback_applied,
363
+ model_class=policy.model_class if policy else None,
364
+ )
365
+
366
+
367
+ def _claude_model_needs_claude_executor(
368
+ executor: str,
369
+ model: str,
370
+ policy: ExecutionPolicyRule | None,
371
+ ) -> bool:
372
+ if executor != "pi":
373
+ return False
374
+ if policy is not None and policy.executor == "pi" and policy.override_reason:
375
+ return False
376
+ return model.lower().startswith(("claude", "anthropic/claude"))
377
+
378
+
379
+ def resolve_model_selection_from_policy(
380
+ *,
381
+ profile: str,
382
+ resolved_policy: ResolvedExecutionPolicy,
383
+ ) -> ModelSelection:
384
+ return ModelSelection(
385
+ profile=profile,
386
+ model=resolved_policy.model,
387
+ effort=resolved_policy.effort,
388
+ source=resolved_policy.execution_policy_source,
389
+ override_reason=resolved_policy.execution_policy_override_reason,
390
+ model_class=resolved_policy.model_class,
391
+ )
392
+
393
+
394
+ def _merge_policies(
395
+ plan_policy: ExecutionPolicyRule | None,
396
+ roadmap_policy: ExecutionPolicyRule | None,
397
+ model_policy_rule: ExecutionPolicyRule | None = None,
398
+ ) -> tuple[ExecutionPolicyRule | None, str | None]:
399
+ # Precedence: plan > roadmap > model_policy > registry defaults — but LAYERED,
400
+ # not winner-take-all. A higher-precedence policy overrides only the fields it
401
+ # specifies; the rest fall through to the lower layer. This is the fix for the
402
+ # tiering-bypass bug: a plan policy that pins only `executor=`/`effort=` (no
403
+ # model/model_class) still inherits the shipped model_policy's `model_class`
404
+ # and its clamp, instead of silently reverting to the registry heavy model.
405
+ layers = [
406
+ (model_policy_rule, "model_policy"),
407
+ (roadmap_policy, "roadmap policy"),
408
+ (plan_policy, "phase-plan policy"),
409
+ ]
410
+ present = [(rule, src) for rule, src in layers if rule is not None]
411
+ if not present:
412
+ return None, None
413
+ top_rule, top_source = present[-1]
414
+ merged: dict[str, object] = {
415
+ "selector": top_rule.selector,
416
+ "action": top_rule.action,
417
+ "lane": top_rule.lane,
418
+ "executor": None,
419
+ "model": None,
420
+ "model_class": None,
421
+ "effort": None,
422
+ "work_unit_kind": None,
423
+ "unsupported_policy_behavior": "block",
424
+ "fallback": None,
425
+ "inherit_default": False,
426
+ "source": top_source,
427
+ "override_reason": None,
428
+ }
429
+ for rule, _src in present: # low → high overlay
430
+ for field_name in ("executor", "model", "model_class", "effort",
431
+ "work_unit_kind", "fallback", "override_reason", "action", "lane"):
432
+ value = getattr(rule, field_name)
433
+ if value is not None:
434
+ merged[field_name] = value
435
+ if rule.unsupported_policy_behavior and rule.unsupported_policy_behavior != "block":
436
+ merged["unsupported_policy_behavior"] = rule.unsupported_policy_behavior
437
+ if rule.inherit_default:
438
+ merged["inherit_default"] = True
439
+ return ExecutionPolicyRule(**merged), top_source
440
+
441
+
442
+ def _resolve_policy_model(
443
+ executor: str,
444
+ work_unit_kind: str,
445
+ model: str,
446
+ fallback: str | None,
447
+ unsupported_behavior: str,
448
+ ) -> str:
449
+ capability = provider_policy_capabilities()[executor]
450
+ if not capability.model_aliases:
451
+ return model
452
+ allowed = set(capability.model_aliases.values())
453
+ default_alias = capability.model_aliases.get(work_unit_kind)
454
+ if model in allowed:
455
+ return model
456
+ if unsupported_behavior == "inherit_default" and default_alias:
457
+ return default_alias
458
+ if unsupported_behavior == "fallback" and fallback in allowed:
459
+ return fallback
460
+ if model.startswith("phase-loop-"):
461
+ raise ValueError(f"unsupported model `{model}` for provider `{executor}`")
462
+ return model