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,740 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+ import shutil
7
+
8
+ from .baml_modular import BamlValidationError, build_baml_request
9
+ from .closeout_validation import extract_plan_produces as _extract_closeout_plan_produces
10
+ from .models import HarnessLaneAssignment, PromptBundle
11
+ from .runtime_paths import (
12
+ phase_loop_claude_agents_file,
13
+ phase_loop_claude_mcp_config_file,
14
+ phase_loop_claude_plugin_dir,
15
+ phase_loop_claude_settings_file,
16
+ )
17
+ from .skill_inventory import inspect_skill_parity, resolve_source_skill_dir
18
+
19
+
20
+ class SkillBundleResolutionError(RuntimeError):
21
+ """The workflow-skill bundle was expected but NO skill source resolved.
22
+
23
+ Raised (CR #1a) when a context that EXPECTS skills (a non-empty
24
+ ``expected_skill_pack``) resolves ZERO sources across the whole pack. Emptying
25
+ the generic ``HARNESS_SOURCE_ROOTS`` (EXTRACTSKILLS SL-2) means the dotfiles
26
+ overlay roots now arrive only through the ``phase_loop_runtime.skill_sources``
27
+ seam; if that seam is not live (e.g. the entry point is not yet registered after
28
+ an upgrade), resolution silently yields nothing and the runner would inject an
29
+ EMPTY bundle. This fails loud with an actionable message instead. The legitimate
30
+ clean-standalone case (no skills expected -> empty pack) never reaches here.
31
+ """
32
+
33
+
34
+ # Harnesses whose workflow skills are delivered by COPYING resolved source dirs
35
+ # (the bundle path that resolve_source_skill_dir feeds). `pi` is deliberately
36
+ # EXCLUDED: it delivers skills via a repo-local package-root contract
37
+ # (`phase-loop-pi/**`, see _render_pi_context_body), so resolving zero source dirs
38
+ # for pi is expected, not a fail-open. Single-sourced from build_bundle.ACTIVE_HARNESSES.
39
+ def _bundle_source_harnesses() -> frozenset[str]:
40
+ from .build_bundle import ACTIVE_HARNESSES
41
+
42
+ return frozenset(ACTIVE_HARNESSES)
43
+
44
+
45
+ def _resolve_pack_skill_dirs(
46
+ repo: Path, harness_target: str, expected_skill_pack: tuple[str, ...]
47
+ ) -> dict[str, Path]:
48
+ """Resolve a skill pack to source dirs, failing loud if NONE resolve.
49
+
50
+ Returns ``{skill_name: source_dir}`` for the skills that resolved (a partial miss
51
+ is tolerated -- some skills may legitimately be absent). Raises
52
+ :class:`SkillBundleResolutionError` only when the pack is non-empty AND every skill
53
+ resolved to ``None`` AND the harness is a bundle-source harness -- the fail-open
54
+ window the disentangle opened (CR #1a/#3). An empty pack (standalone, no skills
55
+ expected) returns ``{}`` without raising; a non-bundle harness like ``pi`` (skills
56
+ delivered via package-root, not resolve_source_skill_dir) is exempt from the raise.
57
+ """
58
+ if not expected_skill_pack:
59
+ return {}
60
+ resolved: dict[str, Path] = {}
61
+ for skill_name in expected_skill_pack:
62
+ source_dir = resolve_source_skill_dir(repo, harness_target, skill_name)
63
+ if source_dir is not None:
64
+ resolved[skill_name] = source_dir
65
+ if not resolved and harness_target in _bundle_source_harnesses():
66
+ raise SkillBundleResolutionError(
67
+ f"no skill sources resolved for {harness_target} bundle "
68
+ f"(expected {len(expected_skill_pack)}: {', '.join(expected_skill_pack)}). "
69
+ "A normal pinned `pip install` ships the assembled skill bundle inside the "
70
+ "package and resolves it automatically; if you are seeing this, the packaged "
71
+ "`phase_loop_runtime/skills_bundle/` is missing (regenerate via "
72
+ "`scripts/sync_skills_bundle.py` and reinstall). For a dotfiles dev checkout, "
73
+ "re-run bootstrap so the `phase_loop_runtime.skill_sources` overlay is live. To "
74
+ "point at a custom source, set PHASE_LOOP_SKILL_SOURCE_PLUGINS to a provider "
75
+ "returning **absolute** roots, or set PHASE_LOOP_RUNNER_REPO_ROOT to anchor the "
76
+ "built-in provider's cwd-relative roots (PHASE_LOOP_SKILL_SOURCE_PLUGINS alone is "
77
+ "insufficient when its roots are relative)."
78
+ )
79
+ return resolved
80
+
81
+
82
+ HARNESS_ACTION_SKILLS = {
83
+ "codex": {
84
+ "roadmap": ("codex-phase-roadmap-builder",),
85
+ "plan": ("codex-plan-phase",),
86
+ "execute": ("codex-execute-phase",),
87
+ "repair": ("codex-phase-loop", "codex-execute-phase"),
88
+ "review": ("codex-phase-loop",),
89
+ "maintain-skills": ("codex-skill-improvement-planner",),
90
+ },
91
+ "claude": {
92
+ "roadmap": (
93
+ "claude-phase-roadmap-builder",
94
+ "claude-plan-phase",
95
+ "claude-execute-phase",
96
+ "claude-phase-loop",
97
+ ),
98
+ "plan": (
99
+ "claude-phase-roadmap-builder",
100
+ "claude-plan-phase",
101
+ "claude-execute-phase",
102
+ "claude-phase-loop",
103
+ ),
104
+ "execute": (
105
+ "claude-phase-roadmap-builder",
106
+ "claude-plan-phase",
107
+ "claude-execute-phase",
108
+ "claude-phase-loop",
109
+ ),
110
+ "repair": (
111
+ "claude-phase-roadmap-builder",
112
+ "claude-plan-phase",
113
+ "claude-execute-phase",
114
+ "claude-phase-loop",
115
+ ),
116
+ "review": (
117
+ "claude-phase-roadmap-builder",
118
+ "claude-plan-phase",
119
+ "claude-execute-phase",
120
+ "claude-phase-loop",
121
+ ),
122
+ },
123
+ "gemini": {
124
+ "roadmap": ("gemini-phase-roadmap-builder",),
125
+ "plan": ("gemini-plan-phase",),
126
+ "execute": ("gemini-execute-phase",),
127
+ "repair": ("gemini-execute-phase",),
128
+ "review": ("gemini-plan-phase",),
129
+ },
130
+ "opencode": {
131
+ "roadmap": ("opencode-phase-roadmap-builder",),
132
+ "plan": ("opencode-plan-phase",),
133
+ "execute": ("opencode-execute-phase",),
134
+ "repair": ("opencode-execute-phase",),
135
+ "review": ("opencode-plan-phase",),
136
+ },
137
+ "pi": {
138
+ "roadmap": ("phase-loop-supervisor", "phase-loop-closeout"),
139
+ "plan": ("phase-loop-supervisor", "phase-loop-closeout"),
140
+ "execute": ("phase-loop-supervisor", "phase-loop-closeout"),
141
+ "repair": ("phase-loop-repair", "phase-loop-closeout"),
142
+ "review": ("phase-loop-supervisor", "phase-loop-closeout"),
143
+ },
144
+ "command": {},
145
+ "manual": {},
146
+ }
147
+
148
+ HARNESS_WORKFLOW_COMMANDS = {
149
+ "codex": {
150
+ "roadmap": lambda roadmap, phase, plan: f"codex-phase-roadmap-builder {roadmap}",
151
+ "plan": lambda roadmap, phase, plan: f"codex-plan-phase {roadmap} {phase}",
152
+ "execute": lambda roadmap, phase, plan: f"codex-execute-phase {plan}",
153
+ },
154
+ "claude": {
155
+ "roadmap": lambda roadmap, phase, plan: f"claude-phase-roadmap-builder {roadmap}",
156
+ "plan": lambda roadmap, phase, plan: f"claude-plan-phase {roadmap} {phase}",
157
+ "execute": lambda roadmap, phase, plan: f"claude-execute-phase {plan}",
158
+ },
159
+ "gemini": {
160
+ "roadmap": lambda roadmap, phase, plan: f"gemini-phase-roadmap-builder {roadmap}",
161
+ "plan": lambda roadmap, phase, plan: f"gemini-plan-phase {roadmap} {phase}",
162
+ "execute": lambda roadmap, phase, plan: f"gemini-execute-phase {plan}",
163
+ },
164
+ "opencode": {
165
+ "roadmap": lambda roadmap, phase, plan: f"opencode-phase-roadmap-builder {roadmap}",
166
+ "plan": lambda roadmap, phase, plan: f"opencode-plan-phase {roadmap} {phase}",
167
+ "execute": lambda roadmap, phase, plan: f"opencode-execute-phase {plan}",
168
+ },
169
+ "pi": {
170
+ "roadmap": lambda roadmap, phase, plan: f"pi-agent-watch --roadmap {roadmap} --max-phases 1 --closeout-mode manual",
171
+ "plan": lambda roadmap, phase, plan: f"pi-agent-watch --roadmap {roadmap} --phase {phase} --max-phases 1 --closeout-mode manual",
172
+ "execute": lambda roadmap, phase, plan: f"pi-agent-watch --phase-plan {plan} --max-phases 1 --closeout-mode manual",
173
+ "repair": lambda roadmap, phase, plan: f"pi-agent-watch --roadmap {roadmap} --phase {phase} --max-phases 1 --closeout-mode manual",
174
+ "review": lambda roadmap, phase, plan: f"pi-agent-watch --roadmap {roadmap} --phase {phase or 'STATUS'} --max-phases 1 --closeout-mode manual",
175
+ },
176
+ "command": {
177
+ "roadmap": lambda roadmap, phase, plan: f"phase-loop roadmap {roadmap}",
178
+ "plan": lambda roadmap, phase, plan: f"phase-loop plan {roadmap} {phase}",
179
+ "execute": lambda roadmap, phase, plan: f"phase-loop execute {plan}",
180
+ "repair": lambda roadmap, phase, plan: f"phase-loop repair {roadmap} {phase}",
181
+ "review": lambda roadmap, phase, plan: f"phase-loop review {roadmap} {phase or 'STATUS'}",
182
+ },
183
+ }
184
+
185
+ HARNESS_INJECTION_MODES = {
186
+ "codex": ("prompt_only", None),
187
+ "claude": ("inline", "context_file"),
188
+ "gemini": ("context_file", "inline"),
189
+ "opencode": ("context_file", "inline"),
190
+ "pi": ("context_file", "manual"),
191
+ "command": ("context_file", "manual"),
192
+ "manual": ("manual", "manual"),
193
+ }
194
+
195
+ CLAUDE_PLUGIN_BUNDLE_VERSION = "1.0.0"
196
+ CLAUDE_CONTEXT_MAX_LINES = 250
197
+ CLAUDE_CONTEXT_MAX_CHARS = 16000
198
+ CLAUDE_TASKLEDGER_HOOK_EVENTS = (
199
+ "TaskCreated",
200
+ "TaskCompleted",
201
+ "TeammateIdle",
202
+ "SubagentStop",
203
+ "PostToolBatch",
204
+ "WorktreeCreate",
205
+ )
206
+
207
+
208
+ def _extract_plan_produces(plan: Path | None) -> tuple[str, ...]:
209
+ if plan is None or not plan.exists():
210
+ return ()
211
+ return _extract_closeout_plan_produces(plan)
212
+
213
+
214
+ def build_prompt_bundle(
215
+ *,
216
+ repo: Path,
217
+ harness_target: str,
218
+ action: str,
219
+ roadmap: Path,
220
+ phase: str | None = None,
221
+ plan: Path | None = None,
222
+ body: str = "",
223
+ injection_mode_override: str | None = None,
224
+ fallback_mode_override: str | None = None,
225
+ ) -> PromptBundle:
226
+ expected_skill_pack = HARNESS_ACTION_SKILLS.get(harness_target, {}).get(action, ())
227
+ workflow_command = _workflow_command(harness_target, action, roadmap, phase, plan)
228
+ default_injection_mode, default_fallback_mode = HARNESS_INJECTION_MODES[harness_target]
229
+ injection_mode = injection_mode_override or default_injection_mode
230
+ fallback_mode = fallback_mode_override or default_fallback_mode
231
+ parity = inspect_skill_parity(repo, harness_target, expected_skill_pack)
232
+ skill_bundle_id = _skill_bundle_id(harness_target, action, expected_skill_pack)
233
+ closeout_instruction = _render_baml_closeout_instruction(
234
+ phase_alias=phase or "unknown",
235
+ plan_produces=_extract_plan_produces(plan),
236
+ plan_owned_files=(),
237
+ include_schema_description=action in {"execute", "repair", "review"},
238
+ )
239
+ bundle_body = "\n\n".join(part for part in (body.strip(), closeout_instruction) if part)
240
+ bundle_sha = _bundle_sha256(
241
+ repo=repo,
242
+ harness_target=harness_target,
243
+ action=action,
244
+ workflow_command=workflow_command,
245
+ body=bundle_body,
246
+ expected_skill_pack=expected_skill_pack,
247
+ )
248
+ return PromptBundle(
249
+ workflow_command=workflow_command,
250
+ body=bundle_body,
251
+ context_body=_render_context_body(
252
+ repo=repo,
253
+ harness_target=harness_target,
254
+ action=action,
255
+ body=bundle_body,
256
+ expected_skill_pack=expected_skill_pack,
257
+ injection_mode=injection_mode,
258
+ ),
259
+ injection_mode=injection_mode,
260
+ expected_skill_pack=expected_skill_pack,
261
+ product_action=action,
262
+ skill_bundle_id=skill_bundle_id,
263
+ skill_bundle_sha256=bundle_sha,
264
+ fallback_mode=fallback_mode,
265
+ recommended_installed_roots=parity.recommended_installed_roots,
266
+ installed_skill_roots=parity.installed_skill_roots,
267
+ installed_skill_warnings=parity.installed_skill_warnings,
268
+ bridge_skill_inventory=parity.bridge_skill_inventory,
269
+ )
270
+
271
+
272
+ def build_lane_prompt_bundle(
273
+ *,
274
+ repo: Path,
275
+ harness_target: str,
276
+ action: str,
277
+ roadmap: Path,
278
+ assignment: HarnessLaneAssignment,
279
+ plan: Path | None = None,
280
+ injection_mode_override: str | None = None,
281
+ fallback_mode_override: str | None = None,
282
+ ) -> PromptBundle:
283
+ if assignment.prompt_kind == "review":
284
+ body = render_harness_review_context(assignment)
285
+ elif assignment.prompt_kind == "reducer":
286
+ body = render_harness_reducer_context(assignment)
287
+ else:
288
+ body = render_harness_lane_context(assignment)
289
+ return build_prompt_bundle(
290
+ repo=repo,
291
+ harness_target=harness_target,
292
+ action=action,
293
+ roadmap=roadmap,
294
+ phase=assignment.phase,
295
+ plan=plan,
296
+ body=body,
297
+ injection_mode_override=injection_mode_override,
298
+ fallback_mode_override=fallback_mode_override,
299
+ )
300
+
301
+
302
+ def render_harness_lane_context(assignment: HarnessLaneAssignment) -> str:
303
+ return _render_harness_assignment_context(
304
+ assignment,
305
+ title="Harness Lane Work Unit",
306
+ scope="Execute exactly one runner-selected lane work unit.",
307
+ extra=(
308
+ "Implement only the selected lane. Do not widen into whole-phase implementation authority, "
309
+ "peer lanes, or downstream roadmap planning unless the shared closeout reports a typed blocker."
310
+ ),
311
+ )
312
+
313
+
314
+ def render_harness_review_context(assignment: HarnessLaneAssignment) -> str:
315
+ return _render_harness_assignment_context(
316
+ assignment,
317
+ title="Harness Review Work Unit",
318
+ scope="Review exactly one runner-selected lane work unit.",
319
+ extra=(
320
+ "Review prompts must not make production edits. If repair or execute work should move to another "
321
+ "harness, emit a typed delegation request instead of spawning it directly."
322
+ ),
323
+ )
324
+
325
+
326
+ def render_harness_reducer_context(assignment: HarnessLaneAssignment) -> str:
327
+ return _render_harness_assignment_context(
328
+ assignment,
329
+ title="Harness Reducer Work Unit",
330
+ scope="Reduce the selected lane or phase reducer work unit against its frozen acceptance contract.",
331
+ extra=(
332
+ "Reducer work may summarize producer outputs and closeout state, but it must not claim write "
333
+ "authority for producer-owned files."
334
+ ),
335
+ )
336
+
337
+
338
+ def _render_harness_assignment_context(
339
+ assignment: HarnessLaneAssignment,
340
+ *,
341
+ title: str,
342
+ scope: str,
343
+ extra: str,
344
+ ) -> str:
345
+ policy = json.dumps(assignment.execution_policy, sort_keys=True) if assignment.execution_policy else "{}"
346
+ worktree = assignment.worktree_assignment.to_json() if assignment.worktree_assignment else {}
347
+ worktree_text = json.dumps(worktree, sort_keys=True) if worktree else "{}"
348
+ closeout_instruction = _render_baml_closeout_instruction(
349
+ phase_alias=assignment.phase,
350
+ plan_produces=tuple(
351
+ item for item in assignment.consumed_interfaces if isinstance(item, str) and item.startswith("IF-")
352
+ ),
353
+ plan_owned_files=assignment.owned_files,
354
+ )
355
+ return "\n".join(
356
+ [
357
+ f"## {title}",
358
+ "",
359
+ scope,
360
+ "",
361
+ f"- phase: `{assignment.phase}`",
362
+ f"- lane_id: `{assignment.lane_id}`",
363
+ f"- work_unit_kind: `{assignment.work_unit_kind}`",
364
+ f"- prompt_kind: `{assignment.prompt_kind}`",
365
+ f"- owned_files: `{', '.join(assignment.owned_files) if assignment.owned_files else 'none'}`",
366
+ f"- consumed_interfaces: `{', '.join(assignment.consumed_interfaces) if assignment.consumed_interfaces else 'none'}`",
367
+ f"- depends_on: `{', '.join(assignment.depends_on) if assignment.depends_on else 'none'}`",
368
+ f"- reducer_kind: `{assignment.reducer_kind}`",
369
+ f"- harness_route: `{assignment.harness_route or 'none'}`",
370
+ f"- model: `{assignment.model or 'none'}`",
371
+ f"- effort: `{assignment.effort or 'none'}`",
372
+ f"- fallback_reason: `{assignment.fallback_reason or 'none'}`",
373
+ f"- execution_policy: `{policy}`",
374
+ f"- worktree_assignment: `{worktree_text}`",
375
+ "",
376
+ extra,
377
+ "",
378
+ "BAML closeout schema instruction:",
379
+ closeout_instruction,
380
+ "",
381
+ "Delegation broker contract:",
382
+ "- Do not spawn peer harnesses directly.",
383
+ "- Native Claude team tasks stay internal unless they emit a typed runner delegation request.",
384
+ "- Approved cross-harness child work is limited to `codex` or `claude` for `execute`, `repair`, and `review`.",
385
+ ]
386
+ )
387
+
388
+
389
+ def _render_baml_closeout_instruction(
390
+ *,
391
+ phase_alias: str,
392
+ plan_produces: tuple[str, ...] | list[str],
393
+ plan_owned_files: tuple[str, ...] | list[str],
394
+ closeout_commit_sha: str | None = None,
395
+ include_schema_description: bool = True,
396
+ ) -> str:
397
+ payload = {
398
+ "phase_alias": phase_alias,
399
+ "plan_produces": list(plan_produces),
400
+ "plan_owned_files": list(plan_owned_files),
401
+ "closeout_commit_sha": closeout_commit_sha,
402
+ }
403
+ try:
404
+ prompt = build_baml_request("EmitPhaseCloseout", payload).prompt
405
+ except BamlValidationError as exc:
406
+ prompt = f"Emit one closeout conforming to emit_phase_closeout.baml / EmitPhaseCloseout. BAML prompt render failed: {exc}"
407
+ if not include_schema_description:
408
+ marker = "\n\nPhase-loop closeout JSON schema description:\n"
409
+ if marker in prompt:
410
+ prompt = prompt[: prompt.index(marker)].rstrip()
411
+ return "EmitPhaseCloseout (`vendor/phase-loop-runtime/src/phase_loop_runtime/baml_src/emit_phase_closeout.baml`):\n" + prompt
412
+
413
+
414
+ def materialize_claude_plugin_bundle(*, repo: Path, run_root: Path, prompt_bundle: PromptBundle) -> dict[str, object]:
415
+ if prompt_bundle.product_action is None:
416
+ return {}
417
+ bundle_root = phase_loop_claude_plugin_dir(run_root).parent
418
+ plugin_dir = phase_loop_claude_plugin_dir(run_root)
419
+ plugin_dir.mkdir(parents=True, exist_ok=True)
420
+ (plugin_dir / ".claude-plugin").mkdir(parents=True, exist_ok=True)
421
+ manifest = _claude_plugin_manifest(prompt_bundle.product_action, prompt_bundle.expected_skill_pack)
422
+ plugin_manifest_path = plugin_dir / ".claude-plugin" / "plugin.json"
423
+ plugin_manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
424
+
425
+ skills_root = plugin_dir / "skills"
426
+ skills_root.mkdir(parents=True, exist_ok=True)
427
+ copied_skill_dirs: list[str] = []
428
+ resolved_skill_dirs = _resolve_pack_skill_dirs(repo, "claude", prompt_bundle.expected_skill_pack)
429
+ for skill_name in prompt_bundle.expected_skill_pack:
430
+ source_dir = resolved_skill_dirs.get(skill_name)
431
+ if source_dir is None:
432
+ continue
433
+ target_dir = skills_root / skill_name
434
+ if target_dir.exists():
435
+ shutil.rmtree(target_dir)
436
+ shutil.copytree(source_dir, target_dir)
437
+ copied_skill_dirs.append(str(target_dir))
438
+
439
+ settings_path = phase_loop_claude_settings_file(run_root)
440
+ settings_payload = _claude_settings_payload()
441
+ settings_path.write_text(json.dumps(settings_payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
442
+
443
+ agents_path = phase_loop_claude_agents_file(run_root)
444
+ agents_payload = _claude_agents_payload()
445
+ agents_path.write_text(json.dumps(agents_payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
446
+
447
+ mcp_config_path = phase_loop_claude_mcp_config_file(run_root)
448
+ mcp_payload = _claude_mcp_config_payload()
449
+ mcp_config_path.write_text(json.dumps(mcp_payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
450
+
451
+ return {
452
+ "root": str(bundle_root),
453
+ "plugin_dir": str(plugin_dir),
454
+ "plugin_manifest_path": str(plugin_manifest_path),
455
+ "settings_path": str(settings_path),
456
+ "agents_path": str(agents_path),
457
+ "mcp_config_path": str(mcp_config_path),
458
+ "expected_skill_pack": list(prompt_bundle.expected_skill_pack),
459
+ "artifact_names": ["plugin-dir", "settings", "agents", "mcp-config"],
460
+ "bundle_sha256": prompt_bundle.skill_bundle_sha256,
461
+ "plugin_manifest_sha256": _file_sha256(plugin_manifest_path),
462
+ "settings_sha256": _file_sha256(settings_path),
463
+ "agents_sha256": _file_sha256(agents_path),
464
+ "mcp_config_sha256": _file_sha256(mcp_config_path),
465
+ "skill_dirs": copied_skill_dirs,
466
+ }
467
+
468
+
469
+ def _workflow_command(harness_target: str, action: str, roadmap: Path, phase: str | None, plan: Path | None) -> str:
470
+ commands = HARNESS_WORKFLOW_COMMANDS.get(harness_target, {})
471
+ if action in commands:
472
+ return commands[action](roadmap, phase, plan)
473
+ if action == "maintain-skills" and harness_target == "codex":
474
+ return "codex-skill-improvement-planner"
475
+ if action == "repair":
476
+ return f"Repair the non-human phase-loop blocker for {phase} using {roadmap}."
477
+ return f"Review phase-loop status for {roadmap}."
478
+
479
+
480
+ def _render_context_body(
481
+ *,
482
+ repo: Path,
483
+ harness_target: str,
484
+ action: str,
485
+ body: str,
486
+ expected_skill_pack: tuple[str, ...],
487
+ injection_mode: str,
488
+ ) -> str:
489
+ if harness_target == "claude" and expected_skill_pack:
490
+ return _render_claude_compact_context_body(
491
+ action=action,
492
+ body=body,
493
+ expected_skill_pack=expected_skill_pack,
494
+ )
495
+ if harness_target == "pi" and expected_skill_pack:
496
+ return _render_pi_context_body(
497
+ repo=repo,
498
+ action=action,
499
+ body=body,
500
+ expected_skill_pack=expected_skill_pack,
501
+ )
502
+ skills = _skill_bodies(repo, harness_target, expected_skill_pack)
503
+ sections: list[str] = []
504
+ adapter_preamble = _adapter_context_preamble(harness_target, action)
505
+ if adapter_preamble:
506
+ sections.append(adapter_preamble)
507
+ if body.strip():
508
+ sections.append(body.strip())
509
+ if skills:
510
+ sections.append(skills)
511
+ return "\n\n".join(sections).strip()
512
+
513
+
514
+ def _render_claude_compact_context_body(
515
+ *,
516
+ action: str,
517
+ body: str,
518
+ expected_skill_pack: tuple[str, ...],
519
+ ) -> str:
520
+ sections: list[str] = []
521
+ adapter_preamble = _adapter_context_preamble("claude", action)
522
+ if adapter_preamble:
523
+ sections.append(adapter_preamble)
524
+ if body.strip():
525
+ sections.append(body.strip())
526
+ sections.append(_claude_bundle_manifest(action, expected_skill_pack))
527
+ return "\n\n".join(sections).strip()
528
+
529
+
530
+ def _render_pi_context_body(
531
+ *,
532
+ repo: Path,
533
+ action: str,
534
+ body: str,
535
+ expected_skill_pack: tuple[str, ...],
536
+ ) -> str:
537
+ skills = "\n".join(f"- `{skill_name}`" for skill_name in expected_skill_pack) or "- `none`"
538
+ sections = [
539
+ "## Repo-owned Pi Agent bundle",
540
+ "",
541
+ f"- product_action: `{action}`",
542
+ f"- package_root: `{repo.resolve() / 'phase-loop-pi'}`",
543
+ f"- config_root: `{repo.resolve() / 'pi-config'}`",
544
+ "- installed_root: `~/.pi/agent/skills`",
545
+ "- source_authority: repo-local `phase-loop-pi/**` and `pi-config/**`, not installed skill bodies",
546
+ "- expected skill pack:",
547
+ skills,
548
+ "",
549
+ "Pi Agent lane contract:",
550
+ "- Accept an explicit system prompt from this context file.",
551
+ "- Enforce the runner-provided tool policy before using tools.",
552
+ "- Write only allowed writes from the lane assignment or active phase plan.",
553
+ "- Treat read-only refs, forbidden refs, private paths, raw data, credentials, and evidence sources as protected unless explicitly allowlisted.",
554
+ "- Keep outputs under runner-provided output roots.",
555
+ "- Preserve verification intent and report every command actually run.",
556
+ "- Cite Greenfield authority when the assignment includes authority citations.",
557
+ "- Preserve governed-pipeline assignment fields when present: lane id, wave id, worktree path, base SHA, isolation mode, owned files, read-only refs, harness route, model, effort, and fallback reason.",
558
+ "- Emit one shared `automation:` closeout and compatible `phase_loop_closeout.v1` metadata.",
559
+ "- Do not own global scheduling, runtime ledger updates, worktree allocation, or merge reduction.",
560
+ ]
561
+ if body.strip():
562
+ sections.extend(["", "## Work Unit Context", "", body.strip()])
563
+ return "\n".join(sections).strip()
564
+
565
+
566
+ def _adapter_context_preamble(harness_target: str, action: str) -> str:
567
+ if harness_target == "gemini":
568
+ return (
569
+ "## Phase-loop adapter constraints\n\n"
570
+ f"This is a non-interactive Gemini CLI `{action}` launch controlled by phase-loop. "
571
+ "The command line at the top of this context is authoritative. Use only tools exposed by the active "
572
+ "Gemini CLI session; do not assume a tool named `run_shell_command` exists. If shell execution is "
573
+ "unavailable, use the exposed read/search/edit tools where sufficient. If the phase cannot be completed "
574
+ "without shell execution, stop with one shared `automation:` closeout using "
575
+ "`status: blocked`, `human_required: false`, `blocker_class: sandbox_command_restriction`, and an "
576
+ "actionable blocker summary. Do not loop on unavailable tools. Do not read installed `~/.gemini/**` "
577
+ "handoffs or write Gemini reflection/handoff files. The only permitted writes are repo-local phase "
578
+ "artifacts or phase-owned files plus optional git staging of those repo files. Do not edit `.phase-loop/` "
579
+ "or claim runner-ledger mutation unless a provided command explicitly permits and confirms it; for repair "
580
+ "launches, the parent runner owns durable blocker clearing from your shared closeout. End with one shared "
581
+ "`automation:` closeout on stdout."
582
+ )
583
+ if harness_target != "claude":
584
+ return ""
585
+ hook_events = ", ".join(CLAUDE_TASKLEDGER_HOOK_EVENTS)
586
+ repo_state_constraint = ""
587
+ if action in {"roadmap", "plan", "review", "repair"}:
588
+ repo_state_constraint = (
589
+ " For non-execute phase-loop launches, do not create or edit repo-local `.claude/**` state such as "
590
+ "`.claude/docs-catalog.json`, `.claude/claude-execute-phase-state.json`, or `.claude/worktrees/`; "
591
+ "if an installed workflow skill suggests those writes, skip that step and continue with only the "
592
+ "phase artifact required by this run."
593
+ )
594
+ return (
595
+ "## Phase-loop adapter constraints\n\n"
596
+ f"This is a non-interactive Claude Code `{action}` launch controlled by phase-loop. "
597
+ "The command line at the top of this context is authoritative. Do not read installed "
598
+ "`~/.claude/skills/**` handoffs. Native Claude collaboration tools such as `Agent`, "
599
+ "`TaskCreate`, `TaskUpdate`, `TaskList`, `TeamCreate`, `TeamDelete`, `SendMessage`, "
600
+ "`EnterWorktree`, `ExitWorktree`, and `ToolSearch` are allowed only when the phase-loop launch "
601
+ "selects a non-solo Claude execution mode and the command-line tool allowlist exposes them; otherwise "
602
+ "work solo. Do not call `AskUserQuestion`, `ExitPlanMode`, or `advisor()`. If any workflow "
603
+ "instruction conflicts with this non-interactive adapter posture, this adapter constraint wins and "
604
+ "the launch command plus TEAMGOV policy are authoritative. "
605
+ f"Documented hook-event guardrails are limited to {hook_events}; do not assume hidden task-list "
606
+ "or teammate-management surfaces beyond those exported artifacts. "
607
+ "The only permitted writes are repo-local phase "
608
+ "artifacts or phase-owned files plus optional git staging of those repo files; do not read "
609
+ "or write `~/.claude/**` reflection or handoff files, and do not edit `.phase-loop/` or legacy "
610
+ f"`.codex/phase-loop/` runner artifacts.{repo_state_constraint} End with one shared `automation:` closeout on stdout."
611
+ )
612
+
613
+
614
+ def claude_hook_guardrail_inventory() -> tuple[str, ...]:
615
+ return CLAUDE_TASKLEDGER_HOOK_EVENTS
616
+
617
+
618
+ def _skill_bodies(repo: Path, harness_target: str, expected_skill_pack: tuple[str, ...]) -> str:
619
+ blocks: list[str] = []
620
+ resolved_skill_dirs = _resolve_pack_skill_dirs(repo, harness_target, expected_skill_pack)
621
+ for skill_name in expected_skill_pack:
622
+ skill_dir = resolved_skill_dirs.get(skill_name)
623
+ if skill_dir is None:
624
+ continue
625
+ skill_file = skill_dir / "SKILL.md"
626
+ if not skill_file.exists():
627
+ continue
628
+ blocks.append(f"## Skill: {skill_name}\n\n{skill_file.read_text(encoding='utf-8').strip()}")
629
+ return "\n\n".join(blocks)
630
+
631
+
632
+ def _claude_bundle_manifest(action: str, expected_skill_pack: tuple[str, ...]) -> str:
633
+ skills = "\n".join(f"- `{skill_name}`" for skill_name in expected_skill_pack) or "- `none`"
634
+ return (
635
+ "## Repo-owned Claude bundle\n\n"
636
+ "The repo-owned Claude plugin bundle is the source of truth for workflow skills in this run. "
637
+ "The launch already materializes the bundle beside `launch.json` and passes it through Claude's "
638
+ "plugin, settings, agents, and MCP config flags, so do not re-enumerate or restate raw `SKILL.md` bodies here. "
639
+ "Do not list or read `claude-bundle/plugin/skills/**`, bundled `SKILL.md`, `references/`, `assets/`, "
640
+ "or `reflections/` during the launch; the bundle is already loaded and rereading it wastes the launch budget.\n\n"
641
+ f"- bundle id: `{_claude_plugin_bundle_name(action)}`\n"
642
+ "- bundle provenance: repo-owned `claude-bundle/` artifacts recorded in `launch.json`\n"
643
+ f"- expected skill pack count: `{len(expected_skill_pack)}`\n"
644
+ "- expected skill pack:\n"
645
+ f"{skills}\n"
646
+ f"- context budget: <= `{CLAUDE_CONTEXT_MAX_LINES}` lines and <= `{CLAUDE_CONTEXT_MAX_CHARS}` characters\n"
647
+ "- fallback delivery: run-local `context.md` plus the materialized Claude bundle"
648
+ )
649
+
650
+
651
+ def _bundle_sha256(
652
+ *,
653
+ repo: Path,
654
+ harness_target: str,
655
+ action: str,
656
+ workflow_command: str,
657
+ body: str,
658
+ expected_skill_pack: tuple[str, ...],
659
+ ) -> str:
660
+ manifest = {
661
+ "harness_target": harness_target,
662
+ "action": action,
663
+ "workflow_command": workflow_command,
664
+ "body": body,
665
+ "skills": [],
666
+ }
667
+ if harness_target == "claude":
668
+ manifest["claude_plugin"] = {
669
+ "plugin": _claude_plugin_manifest(action, expected_skill_pack),
670
+ "settings": _claude_settings_payload(),
671
+ "agents": _claude_agents_payload(),
672
+ "mcp_config": _claude_mcp_config_payload(),
673
+ }
674
+ # CR #1a/#3: fail loud if the pack is non-empty but NOTHING resolves, instead of
675
+ # recording {source:None, sha256:None} entries that hash an empty bundle as valid.
676
+ resolved_skill_dirs = _resolve_pack_skill_dirs(repo, harness_target, expected_skill_pack)
677
+ for skill_name in expected_skill_pack:
678
+ skill_dir = resolved_skill_dirs.get(skill_name)
679
+ if skill_dir is None:
680
+ # A partial miss (some skills resolved, this one did not) stays a recorded
681
+ # null entry -- only the all-None case raises (handled above).
682
+ manifest["skills"].append({"name": skill_name, "source": None, "sha256": None})
683
+ continue
684
+ skill_file = skill_dir / "SKILL.md"
685
+ manifest["skills"].append(
686
+ {
687
+ "name": skill_name,
688
+ "source": _skill_source_label(repo, skill_dir),
689
+ "sha256": hashlib.sha256(skill_file.read_bytes()).hexdigest() if skill_file.exists() else None,
690
+ }
691
+ )
692
+ return hashlib.sha256(json.dumps(manifest, sort_keys=True).encode("utf-8")).hexdigest()
693
+
694
+
695
+ def _skill_bundle_id(harness_target: str, action: str, expected_skill_pack: tuple[str, ...]) -> str:
696
+ if harness_target == "claude":
697
+ return _claude_plugin_bundle_name(action)
698
+ return f"{harness_target}:{action}:{','.join(expected_skill_pack) or 'none'}"
699
+
700
+
701
+ def _claude_plugin_bundle_name(action: str) -> str:
702
+ return f"phase-loop-claude-{action}"
703
+
704
+
705
+ def _claude_plugin_manifest(action: str, expected_skill_pack: tuple[str, ...]) -> dict[str, object]:
706
+ return {
707
+ "name": _claude_plugin_bundle_name(action),
708
+ "version": CLAUDE_PLUGIN_BUNDLE_VERSION,
709
+ "description": "Repo-owned Claude phase-loop workflow bundle",
710
+ "skills": "./skills",
711
+ "metadata": {
712
+ "product_action": action,
713
+ "expected_skill_pack": list(expected_skill_pack),
714
+ },
715
+ }
716
+
717
+
718
+ def _claude_settings_payload() -> dict[str, object]:
719
+ return {}
720
+
721
+
722
+ def _claude_agents_payload() -> dict[str, object]:
723
+ return {}
724
+
725
+
726
+ def _claude_mcp_config_payload() -> dict[str, object]:
727
+ return {"mcpServers": {}}
728
+
729
+
730
+ def _file_sha256(path: Path) -> str:
731
+ return hashlib.sha256(path.read_bytes()).hexdigest()
732
+
733
+
734
+ def _skill_source_label(repo: Path, skill_dir: Path) -> str:
735
+ for base in (repo.resolve(), Path(__file__).resolve().parents[4]):
736
+ try:
737
+ return str(skill_dir.relative_to(base))
738
+ except ValueError:
739
+ continue
740
+ return str(skill_dir)