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,2759 @@
1
+ # Phase-Loop Protocol
2
+
3
+ This document is the canonical shared output contract for harness-neutral
4
+ phase-loop artifacts. It freezes the artifact shapes, failure vocabulary, and
5
+ promotion semantics that every executor, adapter, and manual/operator flow must
6
+ produce or consume during the current phase-loop roadmap family.
7
+ Harness-specific launch mechanics, runtime paths, and private skill install
8
+ details stay in harness-local runtime docs.
9
+ Harness-specific path ownership for the dotfiles-hosted substrate is recorded
10
+ in `docs/phase-loop/harness-substrate-manifest.md`; this protocol remains the
11
+ schema and artifact contract.
12
+ IF-0-SUBSTRATE-1 limits downstream substrate citations to runtime code, CLI
13
+ wrappers, bridge skills, shared runner skills, protocol docs, fixtures, tests,
14
+ scripts, and canonical `.phase-loop/**` state; broader dotfiles checkout
15
+ contents are not client dependencies.
16
+ Harness workflow skill naming is recorded in
17
+ `docs/phase-loop/harness-skill-matrix.md`; that matrix freezes the
18
+ `<harness>-<workflow>` contract, Pi Agent role-style exceptions, direct route
19
+ compatibility, and the governed-pipeline `.pipeline/skills/**` namespace guard.
20
+
21
+ ## Plan Frontmatter
22
+
23
+ Execution-ready phase plans remain compatible with the existing
24
+ `phase_loop_plan_version: 1` contract:
25
+
26
+ ```yaml
27
+ ---
28
+ phase_loop_plan_version: 1
29
+ phase: <PHASE_ALIAS>
30
+ roadmap: <repo-relative roadmap path>
31
+ roadmap_sha256: <sha256 of roadmap file bytes at planning time>
32
+ phase_loop_mutation: <optional release_dispatch>
33
+ release_base_ref: <optional base ref for release dispatch, default origin/main>
34
+ source_bundle: <optional repo-relative Pipeline source bundle path>
35
+ source_bundle_sha256: <optional sha256 of Pipeline source bundle bytes>
36
+ pipeline_phase_id: <optional Pipeline phase id>
37
+ pipeline_mode: <optional standalone|pipeline_optional|pipeline_required>
38
+ ---
39
+ ```
40
+
41
+ Required fields:
42
+
43
+ - `phase_loop_plan_version`
44
+ - `phase`
45
+ - `roadmap`
46
+ - `roadmap_sha256`
47
+
48
+ Optional fields:
49
+
50
+ - `phase_loop_mutation`
51
+ - `release_base_ref`
52
+ - `source_bundle`
53
+ - `source_bundle_sha256`
54
+ - `pipeline_phase_id`
55
+ - `pipeline_mode`
56
+
57
+ `PIPELINE_PLAN_FRONTMATTER_CONTRACT` is additive. Pipeline-aware metadata is additive
58
+ metadata, not a replacement for `phase`, `roadmap`, or `roadmap_sha256`.
59
+ Existing standalone plans with only `phase_loop_plan_version: 1`,
60
+ `phase`, `roadmap`, and `roadmap_sha256` remain valid when no Pipeline mode
61
+ requires bundle metadata.
62
+
63
+ The supported `pipeline_mode` literals are:
64
+
65
+ - `standalone`
66
+ - `pipeline_optional`
67
+ - `pipeline_required`
68
+
69
+ Pipeline metadata is required only when `pipeline_mode` is `pipeline_required`.
70
+ A pipeline-required plan missing `source_bundle` or `source_bundle_sha256`, or
71
+ whose `source_bundle_sha256` no longer matches the source bundle bytes, is a
72
+ typed repairable non-human diagnostic rather than partial execution readiness.
73
+ Unknown `pipeline_mode` values fail closed as typed metadata diagnostics.
74
+
75
+ Plans without this metadata, or with metadata that no longer matches the
76
+ selected roadmap, are stale for autonomous execution and must route back
77
+ through planning instead of execution.
78
+
79
+ ## Spec Delta Closeout
80
+
81
+ `spec_delta_closeout.v1` is the metadata-only decision record used when a phase
82
+ may change reusable harness specs, runtime contracts, roadmap/plan templates, or
83
+ workflow skills. Roadmap builders name the expected policy for each phase,
84
+ phase plans copy that policy into a machine-readable `Spec Closeout Plan`,
85
+ execute-phase closeouts choose exactly one decision, and phase-loop handoffs
86
+ preserve the same decision for downstream routing.
87
+
88
+ Required fields:
89
+
90
+ - `schema`: `spec_delta_closeout.v1`
91
+ - `decision`: one of `no_spec_delta`, `roadmap_amendment`,
92
+ `canonical_spec_update`, `governed_pipeline_refresh`,
93
+ `mirror_cutover_required`, `dotfiles_skill_source_update`, or
94
+ `human_source_judgment_required`
95
+ - `target_surfaces`: repo-relative paths or globs describing the spec surfaces
96
+ affected or intentionally deferred
97
+ - `evidence_paths`: repo-relative metadata evidence, such as a phase plan,
98
+ lane closeout, verification log, or runner artifact
99
+ - `redaction_posture`: `metadata_only`
100
+ - `blocker_class`: optional frozen blocker class when the decision cannot be
101
+ completed automatically
102
+
103
+ The frozen target-surface vocabulary for dotfiles-hosted harness spec work is
104
+ `shared/phase-loop/protocol.md`, `vendor/phase-loop-runtime/protocol/protocol.md`,
105
+ `vendor/phase-loop-runtime/src/phase_loop_runtime/baml_src/emit_phase_closeout.baml`,
106
+ `codex-config/skills/**`, `claude-config/claude-skills/**`,
107
+ `gemini-config/skills/**`, `opencode-config/skills/**`,
108
+ `vendor/phase-loop-skills/**`, and `vendor/phase-loop-runtime/tests/**`.
109
+
110
+ `metadata_only` allows repo-relative paths, hashes, phase aliases, IF gates,
111
+ decision literals, artifact names, and redacted diagnostics. It forbids raw
112
+ specs, raw patch bodies, credentials, provider-supplied payloads, local environment values, and
113
+ inferred reads from ignored/private/raw evidence sources. Standalone dotfiles
114
+ phase-loop runs may emit `no_spec_delta` or `dotfiles_skill_source_update`
115
+ without Governed Pipeline, `.pipeline/**`, Portal contracts, ReGenesis files,
116
+ credentials, PMCP, owner shell setup, or sibling repo writes. Decisions that
117
+ require Governed Pipeline ingest, mirror cutover, or human source judgment are
118
+ metadata-only downstream routing signals, not dotfiles write authorization.
119
+
120
+ Missing or malformed spec-delta closeout evidence is a repairable automation
121
+ blocker with `blocker_class=contract_bug` unless the phase explicitly chooses
122
+ `human_source_judgment_required`, in which case the closeout must name the
123
+ non-secret human input required.
124
+
125
+ ## Plan-Doc-Current Heuristic
126
+
127
+ When a phase has been reopened to `planned`, the runner checks the current plan
128
+ document before dispatching another planning turn. The helper
129
+ `is_plan_doc_current(repo, phase, plan, roadmap, recent_commit_window=50)` is
130
+ true only after `find_plan_artifact(repo, phase, roadmap=roadmap)` returns the
131
+ same roadmap-matching plan path and the plan either appears in
132
+ `git log --name-only -n 50 -- <plan>` or its frontmatter includes a non-empty
133
+ `last_generated` field.
134
+
135
+ For `phase-loop run`, `phase-loop resume`, and `phase-loop dry-run`, a reopened
136
+ `planned` phase with a current plan document dispatches execution directly by
137
+ default. Operators can pass `--force-replan` to bypass this heuristic and launch
138
+ the planning turn even when the plan document is current.
139
+
140
+ The skip is auditable but non-terminal. Before launching execution, the runner
141
+ appends an event with `status: plan_skipped` and
142
+ `metadata.plan_doc_skip` containing `reason: plan_doc_current`, the
143
+ repo-relative `plan_artifact`, and `forced_replan: false`.
144
+ `plan_skipped` is valid only through `EVENT_STATUSES`; it must never be added
145
+ to `PHASE_STATUSES`, `automation.status`, closeout `terminal_status`, or
146
+ reducer phase-state outputs.
147
+
148
+ ## Run Loop Mode
149
+
150
+ `phase-loop run` and `phase-loop resume` count `--max-phases N` as dispatched actions by default.
151
+ This preserves the legacy behavior: `--max-phases 1`
152
+ launches exactly one child action, so an unplanned phase launches planning and
153
+ stops before the later execute action.
154
+
155
+ `--full-phase` changes the counting unit from dispatched actions to complete phase cycles.
156
+ In full-phase mode, `--max-phases 1 --full-phase` keeps dispatching
157
+ within the selected phase until one plan-plus-execute cycle reaches `complete`,
158
+ `blocked`, `awaiting_phase_closeout`, or another no-launch terminal state. A
159
+ phase that already has a current plan can skip planning through the
160
+ `plan_skipped` path and count the following execute launch as one complete phase
161
+ cycle. `--max-phases 2 --full-phase` completes two such phase cycles when the
162
+ roadmap has two ready phases.
163
+
164
+ Existing operators who rely on action-count behavior should keep using
165
+ `--max-phases` without `--full-phase`. Operators who intend complete phase
166
+ cycles should add `--full-phase`. When `--max-phases` is explicitly supplied
167
+ without `--full-phase`, the runner may emit a non-blocking legacy-unit hint once
168
+ per run session. `--no-deprecation-hints` suppresses that hint for `run` and
169
+ `resume`.
170
+
171
+ `PIPELINE_PROTECTED_SOURCE_CATEGORIES` freezes the protected source vocabulary
172
+ for Pipeline bridge work:
173
+
174
+ - `specs` - Pipeline specs.
175
+ - `diagrams` - Pipeline diagrams.
176
+ - `adapter_config` - Pipeline adapter config.
177
+ - `definition_files` - Pipeline definition files, including
178
+ `pipeline.definition.json`.
179
+ - `portal_contracts` - Portal contracts.
180
+ - `phase_artifacts` - Pipeline phase artifacts.
181
+
182
+ Protected-source categories remain high-level. Canonical spec adoption semantics
183
+ are expressed through optional protected-source role metadata, not by adding
184
+ new categories or making dotfiles the adoption authority.
185
+ `PIPELINE_PROTECTED_SOURCE_ROLES` freezes the adoption-sensitive role
186
+ vocabulary that source bundles may attach to protected-source entries:
187
+
188
+ - `seed_spec`
189
+ - `predecessor_spec`
190
+ - `active_canonical_spec`
191
+ - `archived_spec`
192
+ - `managed_mirror_file`
193
+ - `unmanaged_spec_input`
194
+ - `legacy_specs_bundle`
195
+ - `root_specs_intake`
196
+ - `pipeline_specs_canonical`
197
+ - `adapter_configured_intake_root`
198
+ - `mirror_manifest`
199
+ - `archive_manifest`
200
+
201
+ The upstream canonical adoption config keys are metadata-only bridge terms:
202
+ `canonical_sources.adoption_mode`,
203
+ `canonical_sources.active_canonical_root`, `canonical_sources.mirror_root`,
204
+ `canonical_sources.archive_manifest_path`,
205
+ `canonical_sources.spec_intake_roots`, and
206
+ `canonical_sources.legacy_seed_roots`. The adoption mode literals are
207
+ `track_existing`, `greenfield_single_spec`, `greenfield_spec_bundle`, and
208
+ `brownfield_existing_specs`.
209
+
210
+ Standalone `phase_loop_plan_version: 1` plans remain valid without Pipeline or
211
+ canonical adoption metadata. A pipeline-required source bundle that declares
212
+ role metadata as required must fail closed with a repairable non-human
213
+ `contract_bug` diagnostic when an adoption-sensitive role is missing, stale,
214
+ malformed, unknown, or cannot be represented in closeout metadata.
215
+
216
+ Phase-loop planners and executors may read protected sources when the selected
217
+ source bundle authorizes that context, but they must not mutate Pipeline-owned
218
+ protected sources by inference. Any Pipeline-owned write must be explicit in the
219
+ bundle policy and in the phase-plan owned-file contract.
220
+
221
+ ## Handoff Storage
222
+
223
+ Workflow skill handoffs are stored in repo-local generated state under
224
+ `.dev-skills/handoffs/<skill-name>/`. The canonical latest pointer for a skill
225
+ is resolved by `shared/phase-loop/handoff_path.py` through
226
+ `resolve_handoff_path(repo, skill_name)` and points to
227
+ `.dev-skills/handoffs/<skill-name>/latest.md`. The resolver returns a `Path`
228
+ only; callers create directories and write content.
229
+
230
+ `phase-loop migrate-handoffs [--dry-run | --apply] [--json]` is the hard-cut
231
+ migration command. It scans legacy Claude, Codex, Gemini, and OpenCode skill
232
+ handoff roots, selects only handoffs whose frontmatter matches the current
233
+ repo, and emits metadata-only records with `skill_name`, `source`, `target`,
234
+ `action`, and `status`. Dry-run mode reports planned moves without mutation.
235
+ Apply mode moves matching `latest.md` files and timestamped siblings into the
236
+ repo-local directory and is idempotent when the target already has matching
237
+ content.
238
+
239
+ Apply mode is guarded by `_quiesced(repo)`. The guard succeeds only when
240
+ `.phase-loop/state.json` has no in-flight phase states, `.phase-loop/events.jsonl`
241
+ has no recent action except `closeout` or `manual_repair`, and no
242
+ `.phase-loop/*.lock` file exists. A failed quiescence check reports the first
243
+ blocker condition and performs no moves.
244
+
245
+ Legacy harness handoff roots are read only for migration after this cutover.
246
+ Reflections remain in harness-specific `reflections/` trees and are not moved
247
+ by HANDOFFS. The root `.gitignore` excludes `/.dev-skills/` so migrated or newly
248
+ written handoffs do not become source artifacts.
249
+
250
+ ## Ledger Debug
251
+
252
+ `phase-loop status --ledger-debug` is a read-only operator diagnostic surface
253
+ for reducer-rejected ledger events. The flag belongs only to the `status`
254
+ subcommand. Default text status output remains unchanged without the flag:
255
+ operators still see only the existing `Ledger warnings: N` summary when warning
256
+ records exist.
257
+
258
+ With `--ledger-debug`, text status appends a `Rejected events` section after
259
+ the ledger warning count. Each row reports the rejected event phase, timestamp,
260
+ action, status, canonical reason, and a redacted `raw_event_summary`.
261
+
262
+ The same debug mode then appends `Duplicates skipped`. This section reports
263
+ events that matched an earlier event in the same reconcile call and were not
264
+ processed again. The zero-duplicate case is explicit as `none`.
265
+
266
+ `phase-loop status --json --ledger-debug` adds a top-level
267
+ `rejected_events` array:
268
+
269
+ ```json
270
+ [
271
+ {
272
+ "phase": "RUNNER",
273
+ "timestamp": "2026-05-23T00:00:00Z",
274
+ "action": "execute",
275
+ "status": "complete",
276
+ "reason": "provenance_mismatch",
277
+ "raw_event_summary": {
278
+ "schema_version": 2,
279
+ "source": "fixture",
280
+ "phase": "RUNNER",
281
+ "action": "execute",
282
+ "status": "complete",
283
+ "timestamp": "2026-05-23T00:00:00Z",
284
+ "roadmap_sha256_present": true,
285
+ "phase_sha256_present": true
286
+ }
287
+ }
288
+ ]
289
+ ```
290
+
291
+ The JSON field is present as `[]` in debug mode when there are no rejected
292
+ events, and absent from default JSON status output.
293
+
294
+ Debug JSON also adds `duplicates_skipped` as `[]` or as records with phase,
295
+ timestamp, action, status, automation_status, blocker_class, duplicate_key, and
296
+ redacted raw_event_summary. The field is absent from default JSON output.
297
+
298
+ The frozen ledger-debug rejection reasons are:
299
+
300
+ - `provenance_mismatch`
301
+ - `phase_missing`
302
+ - `not_in_allowed_status_set`
303
+ - `legacy_pre_schema_v2`
304
+ - `planned_without_plan_artifact`
305
+ - `blocker_supersession`
306
+
307
+ The `raw_event_summary` is an event identity summary only. It may include
308
+ schema version, source, phase, action, status, timestamp, and whether roadmap
309
+ or phase hashes were present. It must not serialize raw event payloads,
310
+ provider output, prompts, local environment contents, credentials, or arbitrary
311
+ nested metadata.
312
+
313
+ Ledger Debug is diagnostic-only. It does not repair, rewrite, or reclassify
314
+ historical events. After reading the debug output, operators continue to use
315
+ the existing reconcile, reopen, manual repair, or roadmap amendment workflows.
316
+ Persistent rejection logs and automatic repair are out of scope for this
317
+ surface.
318
+
319
+ ## Ledger Dedup
320
+
321
+ Each `reconcile(repo, roadmap)` call performs an in-memory duplicate pass over
322
+ normalized automation events after roadmap filtering and before validation or
323
+ phase-state mutation. The duplicate key is:
324
+
325
+ ```text
326
+ (timestamp, phase, action, status, automation_status, blocker_class)
327
+ ```
328
+
329
+ `phase` is uppercased before comparison. `automation_status` and
330
+ `blocker_class` are read from top-level event fields when present, otherwise
331
+ from nested automation, child_automation, terminal summary, or blocker metadata.
332
+
333
+ Dedup is first-event-wins. The first event for a key is processed normally.
334
+ Subsequent events with the same key do not append ledger warnings, do not update
335
+ phase state, do not replace closeout summaries, and are recorded on
336
+ `StateSnapshot.ledger_duplicates_skipped` in encounter order. The ledger itself
337
+ is never rewritten or truncated.
338
+
339
+ This de-noises repeated identical events only. Rejected non-duplicate events
340
+ remain visible under `Rejected events`, and genuine provenance, status, schema,
341
+ planned-artifact, or blocker-supersession warnings still contribute to
342
+ `Ledger warnings: N`. Ledger Dedup extends the v25 LEDGERDEBUG operator surface;
343
+ it does not repair historical event content.
344
+
345
+ ## Skills Bundle
346
+
347
+ The harness-neutral workflow skill source bundle lives at
348
+ `vendor/phase-loop-skills/`. Base source directories are unprefixed:
349
+ `execute-phase`, `plan-phase`, `plan-detailed`, `phase-roadmap-builder`,
350
+ `phase-loop`, `skill-editor`, `skill-improvement-planner`, and
351
+ `task-contextualizer`. Installed names retain the harness prefix, for example
352
+ `codex-execute-phase` and `claude-plan-phase`.
353
+
354
+ Harness-specific differences are escape hatches, not a second source tree.
355
+ Per-harness overlays live under
356
+ `vendor/phase-loop-skills/<skill-name>/_overrides/<harness>/` and are layered
357
+ over the unprefixed source during installation. Operators author those overlay
358
+ files explicitly; the installer does not auto-generate operator-authored
359
+ exceptions.
360
+
361
+ `phase_loop_runtime.skill_paths` exposes the resolver API used by workflow
362
+ skills and installers:
363
+
364
+ - `current_harness(harness: str | None = None) -> str`
365
+ - `resolve_skill_bundle_root(harness: str | None = None) -> Path`
366
+ - `resolve_skill_helper_root(harness: str | None = None) -> Path`
367
+ - `resolve_handoff_root(repo: Path) -> Path`
368
+ - `resolve_reflection_root(skill_name: str, harness: str | None = None) -> Path`
369
+
370
+ Resolver precedence is explicit function argument, `PHASE_LOOP_HARNESS`,
371
+ `PHASE_LOOP_SKILL_BUNDLE`, then the documented harness defaults:
372
+ `~/.claude/skills`, `~/.codex/skills`, `~/.gemini/skills`, and
373
+ `~/.config/opencode/skills`. Handoffs resolve to repo-local
374
+ `.dev-skills/handoffs/`; reflections remain under the selected harness skill
375
+ root.
376
+
377
+ `phase-loop install --harness {claude,codex,gemini,opencode}
378
+ [--source <bundle-path>] [--destination <install-root>] [--symlink|--copy]
379
+ [--dry-run|--apply]` is the metadata-only install surface. Dry-run mode reports
380
+ planned source, destination, harness, skill name, install mode, and action
381
+ without mutating destinations. Apply mode is idempotent and installs
382
+ harness-prefixed workflow skills from `vendor/phase-loop-skills/`.
383
+
384
+ `phase-loop install --status --json` is a read-only runtime visibility surface.
385
+ It never installs, replaces, copies, symlinks, deletes, or rewrites skill files.
386
+ The JSON reports per-harness skill parity, console-script availability, BAML
387
+ closeout schema availability, and `.dev-skills/` ignore readiness using
388
+ symbolic metadata only.
389
+
390
+ ## Operating Modes
391
+
392
+ The phase-loop runner supports three operating modes:
393
+
394
+ - `standalone`: local dotfiles execution. A `phase_loop_plan_version: 1` plan
395
+ with only the required `phase`, `roadmap`, and `roadmap_sha256` fields
396
+ remains valid without Pipeline metadata. Standalone runs do not require
397
+ governed-pipeline, `.pipeline/**`, Portal, Greenfield, or a source bundle.
398
+ - `pipeline_optional`: dotfiles may consume supplied Pipeline metadata and
399
+ source-bundle context when present, but missing Pipeline metadata does not
400
+ invalidate the standalone plan contract.
401
+ - `pipeline_required`: execution must fail closed before child launch when
402
+ validated Pipeline context is missing, stale, malformed, or mismatched.
403
+
404
+ Pipeline-required execution requires a validated `source_bundle`,
405
+ `source_bundle_sha256`, `pipeline_phase_id`, bundle freshness, protected-source
406
+ entries, and protected-source hash checks before execution. When
407
+ `freshness.source_bundle_hash` is a SHA-256 digest, it must match the source
408
+ bundle bytes. Protected-source entries must cite one of
409
+ `PIPELINE_PROTECTED_SOURCE_CATEGORIES`, and protected-source files must exist
410
+ with matching SHA-256 hashes.
411
+
412
+ Governed Pipeline owns adoption, source-bundle emission, canonical refresh,
413
+ replan, closeout ingest, Greenfield reduction, and Portal projection.
414
+ Governed-pipeline owns canonical source-truth refresh, source-bundle emission,
415
+ protected-source freshness, scheduling, closeout ingest, Greenfield reduction,
416
+ and Portal projection. Dotfiles consumes those inputs and emits redacted
417
+ metadata; it does not infer authority over governed-pipeline, Portal,
418
+ Greenfield, `.pipeline/**`, private evidence, raw data, credentials, or
419
+ provider-supplied payloads. Host bootstrap, Shell config, SSH setup, MCP gateway setup,
420
+ generic 1Password setup, raw-evidence, credential-bearing payloads, and local
421
+ environment values are outside the protocol substrate.
422
+ Governed-pipeline also owns canonical spec adoption, archive creation, managed
423
+ mirror refresh, source-truth reconciliation, canonical refresh, replan, and
424
+ preflight block decisions. Dotfiles may echo validated adoption role metadata
425
+ and advisory hints in closeout, but it must not make archive, mirror,
426
+ canonical refresh, replan, or block decisions.
427
+
428
+ ## Dotfiles Source And Visibility Contracts
429
+
430
+ Dotfiles source authority for governed-pipeline adoption is frozen in
431
+ `docs/dotfiles-source-authority-contract.md`. That document classifies every
432
+ top-level path with `path_glob`, `classification`, `owner`,
433
+ `ingestion_policy`, and rationale. Governed-pipeline may pull paths classified
434
+ as `authority`; it must reject `derived`, `runtime_state`, `private`, and
435
+ `out_of_scope` paths as adoption source material.
436
+
437
+ Dotfiles cross-repo visibility is frozen in
438
+ `docs/dotfiles-visibility-contract.md`. That contract exposes only adoption
439
+ inputs, redacted runtime metadata, and the operating-mode declaration. The
440
+ surface is pull-only: dotfiles does not push into governed-pipeline or Portal,
441
+ governed-pipeline must not write into dotfiles, consumers must not depend on
442
+ runtime state paths, and BAML schema contracts must be imported rather than
443
+ paraphrased.
444
+
445
+ ## Dotfiles Schema Pack
446
+
447
+ The dotfiles BAML schema pack lives under
448
+ `vendor/phase-loop-runtime/src/phase_loop_runtime/baml_src/` and freezes the direct import contracts
449
+ for downstream consumers. The authority class names are
450
+ `DotfilesAdoptionManifest`, `DotfilesRuntimeProjection`,
451
+ `DotfilesC4Document`, and `DotfilesTaskCatalog`.
452
+
453
+ Consumers import the `.baml` contracts directly or use
454
+ `phase_loop_runtime.baml_modular.export_function_schema(<class name>)` for a
455
+ dialect-clean JSON Schema projection. Protocol prose may describe the pack and
456
+ its consumers, but it must not paraphrase full field definitions as an
457
+ alternate schema source. `DotfilesAdoptionManifest` is the governed-pipeline
458
+ pull manifest shape, `DotfilesRuntimeProjection` is the redacted runtime
459
+ visibility shape, `DotfilesC4Document` carries Markdown/Mermaid source for
460
+ deterministic rendering, and `DotfilesTaskCatalog` carries audience-tagged task
461
+ entries.
462
+
463
+ After DOTADOPT, `docs/adoption/dotfiles-adoption-bundle.json` is the canonical
464
+ governed-pipeline pull-only ingestion target for dotfiles. The fixture is
465
+ generated by `generate_adoption_bundle`, validates as
466
+ `DotfilesAdoptionManifest`, and carries `sha256` digests for imported `.baml`
467
+ schema sources. This handoff does not produce canonical HTML, rendered Mermaid,
468
+ Portal views, governed-pipeline files, archives, mirrors, or any alternate
469
+ schema authority; the `.baml` files remain the schema source of truth.
470
+
471
+ ### Adoption Bundle Lifecycle
472
+
473
+ The runtime exposes `phase-loop adoption-bundle status --repo <path>` to compare
474
+ current `vendor/phase-loop-runtime/src/phase_loop_runtime/baml_src/*.baml` digests with
475
+ `docs/adoption/dotfiles-adoption-bundle.json`. It exits `0` when fresh, `1`
476
+ when stale, and `2` when the bundle or schema contract cannot be loaded.
477
+ `phase-loop adoption-bundle refresh --repo <path>` regenerates that JSON bundle
478
+ through `generate_adoption_bundle`, preserving the committed fixture metadata
479
+ for `generated_at` and `operating_mode`; it exits `0` after a successful
480
+ refresh or idempotent no-op and `1` when regeneration fails.
481
+
482
+ `.githooks/pre-commit-adoption-bundle` is the optional local automation path.
483
+ When staged BAML files are present it runs the status command, runs refresh only
484
+ for stale bundles, and stages `docs/adoption/dotfiles-adoption-bundle.json`
485
+ after a successful refresh. The hook is never installed by default; operators
486
+ opt in with `phase-loop init --install-hooks`.
487
+
488
+ In standalone mode, root `specs/**` is the default human-visible future-spec
489
+ discovery root when no phase plan, source bundle, or repo-local config
490
+ overrides it. Legacy or project-specific seed roots such as `Specs/**` are
491
+ explicit input roots only; accepted future specs should normalize toward root
492
+ `specs/**` for later governed-pipeline intake.
493
+
494
+ ## Dispatch Hints
495
+
496
+ Roadmaps and phase plans may include an optional markdown section named
497
+ `Dispatch Hints` without changing the required frontmatter contract. The parser
498
+ must treat this section as optional and fail closed when it is absent.
499
+
500
+ Supported keys:
501
+
502
+ - `preferred executors`
503
+ - `allowed executors`
504
+ - `fallback executors`
505
+ - `disabled executors`
506
+ - `required capabilities`
507
+
508
+ Supported action selectors:
509
+
510
+ - `roadmap`
511
+ - `plan`
512
+ - `execute`
513
+ - `repair`
514
+ - `review`
515
+ - `maintain-skills`
516
+
517
+ Accepted forms:
518
+
519
+ ```markdown
520
+ ## Dispatch Hints
521
+ - preferred executors: `codex`
522
+ - allowed executors: `codex`, `claude`
523
+ - fallback executors: `codex`
524
+ - disabled executors: `manual`
525
+ - required capabilities: `live_launch`, `structured_output`
526
+ - execute preferred executors: `codex`
527
+ - review allowed executors: `codex`, `claude`
528
+ ```
529
+
530
+ or action-grouped subsections such as:
531
+
532
+ ```markdown
533
+ ## Dispatch Hints
534
+ ### Default
535
+ - preferred executors: `codex`
536
+
537
+ ### Review
538
+ - allowed executors: `codex`, `claude`
539
+ ```
540
+
541
+ Dispatch hint precedence is:
542
+
543
+ 1. operator / CLI override
544
+ 2. phase-plan hints
545
+ 3. roadmap hints
546
+ 4. registry defaults
547
+
548
+ Disabled executors and required capabilities remain conservative filters: they
549
+ must not be silently ignored when a preferred executor conflicts with them.
550
+
551
+ ## Planner Validation
552
+
553
+ Planner skills validate emitted plan-document literals before writing a plan
554
+ artifact. The shared entrypoint is
555
+ `phase_loop_runtime.planner_validation.validate_plan_dispatch_hints(plan_text,
556
+ *, dispatch_capabilities=None, executors=None, product_loop_actions=None)`.
557
+ It returns metadata-only `ValidationFinding` records with `field_path`,
558
+ `literal`, `allowed_values`, and `suggested_fix`; it never writes files, mutates
559
+ runner state, prints output, or raises on invalid plan text.
560
+
561
+ The validator defaults to `DISPATCH_CAPABILITIES`, `EXECUTORS`, and
562
+ `PRODUCT_LOOP_ACTIONS` from `phase_loop_runtime.models`. It checks
563
+ `Dispatch Hints` executor and required-capability literals, `Execution Policy`
564
+ selectors and executor assignments, and closeout example literals for
565
+ `terminal_status`, `verification_status`, and `blocker_class`. Markdown body
566
+ content outside those planner-emitted protocol surfaces is not validated.
567
+
568
+ Planner skills must call the validator after the complete draft exists and
569
+ before the project-path plan document is written. A finding blocks the write and
570
+ surfaces a validation_failed closeout with `terminal_status=blocked`,
571
+ `verification_status=blocked`, `blocker_class=contract_bug`,
572
+ `human_required=false`, and a non-secret summary of the findings. This is a
573
+ source-side guard: v24 CLOSEOUTHARDEN remains the runtime soft-fail layer for
574
+ child executor closeout drift, and neither layer coerces unknown literals into
575
+ nearby valid statuses.
576
+
577
+ ## Execution Policy
578
+
579
+ Roadmaps and phase plans may include an optional `## Execution Policy` section
580
+ when executor selection also needs model, effort, work-unit defaults, fallback,
581
+ or policy source provenance. Existing artifacts do not need this section.
582
+ `Dispatch Hints` remain valid and are the executor-only fallback surface when no
583
+ execution policy is present.
584
+
585
+ The accepted syntax is line-oriented markdown:
586
+
587
+ ```markdown
588
+ ## Execution Policy
589
+ - work-unit defaults: work-unit=`lane_execute`, effort=`medium`, unsupported=`inherit_default`, inherit-default=`true`
590
+ - execute: executor=`pi`, model=`auto`, effort=`medium`, work-unit=`lane_execute`, reason=`simple bounded lane default`
591
+ - repair: executor=`claude`, model=`claude-opus-4-8`, effort=`high`, work-unit=`repair`
592
+ - SL-2: executor=`gemini`, model=`phase-loop-execute-medium`, effort=`medium`, work-unit=`lane_execute`, unsupported=`fallback`, fallback=`phase-loop-execute-medium`
593
+ ```
594
+
595
+ Supported selectors are `work-unit defaults`, `roadmap`, `plan`, `execute`,
596
+ `repair`, `review`, `maintain-skills`, and lane-specific selectors such as
597
+ `SL-2`. Lane-specific policy only resolves metadata for that lane; POLICYDSL
598
+ does not schedule lane work units.
599
+ Reducer and verification lanes use lane selectors with
600
+ `work-unit=phase_reducer` or `work-unit=phase_verify`; invented action
601
+ selectors such as `reduce` and `verify` are invalid.
602
+
603
+ Execution policy precedence is frozen as:
604
+
605
+ 1. CLI/operator override
606
+ 2. phase-plan policy
607
+ 3. roadmap policy
608
+ 4. `Dispatch Hints`
609
+ 5. registry defaults
610
+
611
+ The resolver must record policy source and override reason for model, effort,
612
+ executor, and fallback decisions. Invalid model, effort, work-unit, fallback, or
613
+ silent downgrade cases fail closed unless explicit fallback or default
614
+ inheritance is recorded.
615
+
616
+ A plan or roadmap whose `## Execution Policy` block fails to parse is recorded
617
+ as a `contract_bug` blocker carrying `path:line_number` and the offending raw
618
+ line, surfaced through `phase-loop status` and `phase-loop handoff`. The runner
619
+ stays alive and exits with code `5` (blocked), distinct from `1` (child launch
620
+ failure) and `0` (success), so operator rotation wrappers can branch on the
621
+ exit code instead of grepping stdout for `Terminal status: blocked`.
622
+
623
+ `phase-loop status --json` exposes `pipeline_mode` at the top level and the
624
+ resolved per-phase execution policy under an `execution_policy` block keyed by
625
+ phase alias, with sub-keys for `plan`, `execute`, `repair`, and `review`. Each
626
+ sub-key reports the resolved `executor`, `model`, `effort`, and `source`
627
+ (`phase-plan policy` or `roadmap policy`). Rotation wrappers consult this block
628
+ to pre-resolve executor pins before dispatching, avoiding the per-phase
629
+ `sandbox_command_restriction` failures that arise when an external rotation
630
+ pushes an executor that the plan policy pins to a different harness.
631
+
632
+ `phase-loop status --runtime-projection --json` emits the
633
+ `DotfilesRuntimeProjection` shape. The projection maps the selected
634
+ `pipeline_mode` to `operating_mode`, validates the emitted JSON through the BAML
635
+ schema parser, and must not include absolute host paths, secret references,
636
+ environment values, provider tokens, or local account identifiers. The surface
637
+ is standalone-first: it works without governed-pipeline, a source bundle,
638
+ Portal, Greenfield, or any acknowledged Pipeline contract.
639
+
640
+ ## Start Gate
641
+
642
+ Before `run`, `resume`, or `dry-run` dispatches planning, execution, lane
643
+ scheduling, work-unit mode, repair, closeout, or `launch_with_spec`, the runner
644
+ performs a cross-phase dirty start gate. The gate scans at most the last 50
645
+ ledger events, ignores malformed events and same-phase evidence, keeps the
646
+ latest dirty evidence per prior phase, and intersects `phase_owned_dirty_paths`
647
+ plus `previous_phase_owned_paths` with current git dirty paths. A prior phase
648
+ holds its dirty-path lien only while it remains in the active plan: an
649
+ `unplanned` phase (dropped by a roadmap edit) or one absent from the roadmap
650
+ entirely is skipped, so its stale ownership cannot perpetually block dispatch.
651
+
652
+ When an overlap remains dirty, the runner refuses dispatch and appends
653
+ `start_gate_refused` with `blocker_class=dirty_worktree_conflict` and
654
+ `metadata.start_gate.status=refused`. The metadata names the current phase, the
655
+ offending phase, its `offending_status`, `last_event_timestamp`,
656
+ `overlapping_dirty_paths`, `scanned_events`, and operator `next_actions`,
657
+ ordered most-reliable first: rerun with `phase-loop run
658
+ --allow-cross-phase-dirty "<reason>"` to bypass the gate; commit the overlapping
659
+ path(s) or set them aside with `git stash -u` (untracked output counts as
660
+ dirty); and, only when the offending phase is `blocked`, run `phase-loop
661
+ reconcile --phase <ALIAS> --to-status planned --allow-dirty --reason <text>` to
662
+ record `manual_recovery` for that dirty-state blocker. The terminal summary is
663
+ blocked and the refusal does not launch a child process.
664
+
665
+ Operators may pass `--allow-cross-phase-dirty <reason>` to `phase-loop run`,
666
+ `phase-loop resume`, or `phase-loop dry-run`. The reason is required and the
667
+ runner appends audited `metadata.start_gate.status=bypassed` before continuing.
668
+ The bypass is limited to this STARTGATE check: it does not mark verification
669
+ passed, perform recovery, auto-commit output, or bypass dirty-worktree,
670
+ Pipeline, access, repair, executor-policy, or launch blockers.
671
+
672
+ ## Model Routing & Run Mode
673
+
674
+ Model selection has two orthogonal axes (model-routing-v1).
675
+
676
+ `model_policy` (*what model*): a `model_class` role — `planner`, `implementer`,
677
+ or `worker` — resolves to a concrete model per executor. An execution-policy
678
+ rule may carry `model_class`; resolution composes registry defaults → shipped
679
+ `model_policy` → plan `## Execution Policy` → CLI override (lowest to highest
680
+ precedence). A checkout with no `model_policy` and no `model_class` resolves
681
+ model and effort exactly as the registry defaults (the empty-policy back-compat
682
+ contract). Requesting an effort a provider does not support raises unless the
683
+ rule opts into the provider `effort_map` fallback; the shipped policy does so for
684
+ its `max`-effort planning actions. `worker` never authors a final patch
685
+ (`execute`/`repair`), and an executor whose ceiling is below `max` (e.g. gemini)
686
+ is never selected as the max-effort planner of record.
687
+
688
+ `run_mode` (*how governed*): `autonomous` (default) or `governed` (opt-in, via
689
+ `--governed` / `PHASE_LOOP_RUN_MODE=governed`). Autonomous invokes no advisor
690
+ panel and adds no `human_required` — the panel is never spawned (the run-level
691
+ guard short-circuits before any leg). Governed adds a plan-stage and pre-merge
692
+ panel gate that reuses the `review_finding` `block`/`nit` severity vocabulary on
693
+ a seam separate from the closeout validator registry; the reviewer pool is
694
+ vendor-disjoint from the author, the review loop is bounded, and every escalation
695
+ terminal is a non-human `review_gate_block` surfaced in the run-end summary —
696
+ never a synchronous human wait.
697
+
698
+ Governed mode is **live on the serial dispatch path** (model-routing-v2). The
699
+ plan-stage gate runs on first-attempt plans (not repair re-plans). The pre-merge
700
+ gate runs **inside the closeout, after `git add` stages the owned paths and
701
+ before the commit is finalized**, and reviews the **exact staged index**
702
+ (`git diff --cached`) over the paths being committed — so the artifact the panel
703
+ reviews is, by construction, exactly what gets committed (no separately-derived
704
+ path set, no untracked-file omission). Reviewer≠author is the **union** of the
705
+ phase's dispatch `selected_executor` vendors (rotation/repair may have several);
706
+ all are excluded. The gate is **fail-closed** (governed is the opt-in enforcement
707
+ mode): a `block`, a non-conforming/unparseable verdict, or no usable reviewer
708
+ disjoint from the author HOLDS the merge as a non-human `review_gate_block` — it
709
+ never silently advisory-passes. Verdicts use a strict terminal-line contract (the
710
+ last line begins with `AGREE` / `PARTIALLY AGREE` / `DISAGREE`). The panel spawns
711
+ the codex + gemini subscription CLI legs (the claude leg is `unavailable` pending
712
+ a native-Agent path). The autonomous default is byte-identical — the gate is a
713
+ no-op off the governed path. Governed mode is **EXPERIMENTAL and fail-safe** — it
714
+ may over-block, but never silently passes unreviewed or self-reviewed code. With
715
+ only the codex + gemini legs live, a **multi-vendor** phase (authored by one vendor
716
+ and repaired by the other) has no disjoint reviewer and is HELD with an explicit
717
+ reason until the deferred claude leg lands. Out of scope / remaining threads:
718
+ concurrent-wave dispatch is **not** governed yet; a held phase is not auto-repaired
719
+ (the findings-driven executor re-dispatch is the remaining thread); and the
720
+ `model_class` escalation decision is recorded on the dispatch metadata but not
721
+ yet re-routed into live model selection.
722
+
723
+ ## Reconcile Command
724
+
725
+ `phase-loop reconcile` has two distinct modes.
726
+
727
+ Completion reconcile:
728
+
729
+ `phase-loop reconcile --phase <ALIAS> [--closeout-commit <SHA>]
730
+ [--repair-summary <text>] [--verification-status passed]
731
+ [--verification-log <path>] [--recovery-mode]`
732
+
733
+ This synthesizes a v28-shape `manual_repair` event for the named phase,
734
+ recording the current `HEAD` (or the supplied SHA) as the closeout commit and
735
+ marking `clears_blocker=true`. It then re-reconciles so `phase-loop status`
736
+ reflects the cleared blocker.
737
+
738
+ The command refuses if the working tree is dirty (override with
739
+ `--allow-dirty`) so the synthesized event always references a clean closeout
740
+ commit. This compresses the post-`dirty_worktree_conflict` recovery ritual —
741
+ manually authoring the v28 P3/P4 event shape and appending to events.jsonl —
742
+ to one CLI call. Use only as a recovery tool when the executor's work is
743
+ correct but ownership classification or lane-evidence gaps left the runner
744
+ blocked; do not use to bypass legitimate verification failures.
745
+
746
+ When completion reconcile uses `--verification-status passed`, it must also
747
+ provide `--verification-log <path>` pointing at the runner-owned
748
+ `verification.json` artifact for the run. The runner validates the artifact
749
+ schema, the sibling `verification.log` SHA-256 against `log_sha256`, and every
750
+ command, env-refresh, and suite exit code before appending a completion repair
751
+ event. Remediation output and event metadata include only the artifact path,
752
+ validation code, and exit summary; raw verification log output is never copied
753
+ into reconcile metadata.
754
+
755
+ Blocked-state recovery:
756
+
757
+ `phase-loop reconcile --phase <ALIAS> --to-status planned --reason <text>`
758
+
759
+ This is an explicit blocked-state recovery transition. It is for stale
760
+ non-human dirty-state blockers only, and it records an auditable
761
+ `manual_recovery` event with `from=blocked`, `to=planned` or `to=unplanned`,
762
+ `trigger=cli`, `clears_blocker=true`, and `verification_status=not_run`.
763
+ It does not mark verification passed and is mutually exclusive with
764
+ `--verification-status passed`.
765
+
766
+ Recovery is allowed only when the selected phase currently reconciles as
767
+ `blocked` and the blocker is dirty-state-derived, such as
768
+ `dirty_worktree_conflict`, phase-owned dirty output, previous-phase-owned dirty
769
+ output, or stale dirty terminal metadata that no longer blocks current git
770
+ state. If a current plan artifact exists, replay reports the phase as
771
+ `planned`; if no current plan artifact exists, replay reports it as
772
+ `unplanned` so the operator can run the planning workflow again.
773
+
774
+ Sticky or human-required blockers are refused. This includes
775
+ `missing_secret`, `account_or_billing_setup`, `admin_approval`,
776
+ `product_decision_missing`, and `destructive_operation`. Access, product,
777
+ destructive, contract, repeated-verification, and unknown blockers require
778
+ explicit dirty-state evidence before recovery can clear stale blocker fields.
779
+
780
+ ### Recovery Mode
781
+
782
+ `phase-loop reconcile --recovery-mode` is for recovery states where the operator
783
+ intentionally needs to reconcile while the worktree is dirty. It implies the
784
+ dirty-tree override, records `manual_repair.recovery_mode=true`, and requires
785
+ explicit `--closeout-commit`, `--repair-summary`, and `--verification-status`
786
+ arguments so the audit trail does not silently inherit defaults.
787
+
788
+ Use recovery mode for parallel-race recovery when a plan doc or other recovery
789
+ artifact is dirty because another runner already produced the phase-owned
790
+ output; the repair summary should state which runner or closeout created the
791
+ dirty path. Use it for stale-event recovery when a clean closeout event exists
792
+ but the active status still reflects an older blocked event. Use it for a
793
+ manual-repair flow only after the operator has verified the repair and can name
794
+ the commit and verification result. Recovery mode does not replace legitimate
795
+ verification repair; failed verification still requires repair or a blocked
796
+ closeout, not a reconciled completion.
797
+
798
+ ## Reopen Command
799
+
800
+ `phase-loop reopen --phase <ALIAS> --reason <text> [--allow-dirty]` is the
801
+ symmetric counterpart to `reconcile`: where `reconcile` advances a `blocked`
802
+ phase to `complete`, `reopen` reverts a spurious `complete` phase back to
803
+ `planned`. Use when an executor reported a phase as complete +
804
+ `verification_status=passed` but the underlying IF gates were not actually
805
+ satisfied (e.g., a repair iteration that reported done with zero diff and no
806
+ real work).
807
+
808
+ The command appends a typed `phase_reopen` event with `status: planned` and
809
+ metadata `{reason, prior_status, prior_closeout_commit, reopen_commit}`. The
810
+ reducer recognizes `action: phase_reopen` and flips the phase back to
811
+ `planned` regardless of plan-artifact existence. Subsequent
812
+ `phase-loop run` invocations re-execute the phase.
813
+
814
+ Refuses if the phase is not currently `complete` (cannot reopen what isn't
815
+ closed). Refuses if the working tree is dirty (override with `--allow-dirty`)
816
+ so `reopen_commit` references a clean state. The `--reason` field is
817
+ required and recorded on the event for audit.
818
+
819
+ This command exists because the runner trusts the executor's reported
820
+ terminal status by default; if an executor hallucinates completion, the
821
+ runner has no way to independently verify the IF gates were satisfied. A
822
+ future "closeout-payload IF-gate cross-check" would prevent the bug at
823
+ emission time; `reopen` is the recovery path until that ships.
824
+
825
+ ## Evidence Audit Command
826
+
827
+ `phase-loop evidence-audit --repo . [--dirty-only|--full-tree|--full-tree-loose]
828
+ [--tier-2]` is an operator-callable spot-check for fake-evidence patterns in
829
+ dirty-tree artifacts. Run it before `phase-loop reconcile` on phases producing
830
+ comparison/verdict evidence (visual-fidelity diffs, audit reports, schema
831
+ validation traces).
832
+
833
+ Surfaced after the regen 2026-05-22 VISUALMATCH incident, where the
834
+ executor committed 21 artifact files that satisfied the v20 closeout
835
+ schema (terminal_status: complete, produced_if_gates populated, real
836
+ dirty_paths) but contained faked evidence: 19 "distinct" prototype
837
+ PNGs all shared one md5 hash; 19/19 similarity scores at exactly
838
+ 0.999999; boilerplate verdict markdown. v20's IF-gate Tier 1
839
+ validator (`closeout_validation.validate_produced_gates`) only checks
840
+ that produced gate names match plan `## Produces` names — it cannot
841
+ see evidence content. Operators must spot-check; `phase-loop
842
+ evidence-audit` codifies the spot-check.
843
+
844
+ Tier 1 detectors run by default:
845
+
846
+ - **`duplicate-content`** — N or more files share the same sha256.
847
+ Default threshold N=3 (`--min-duplicates`). Catches the placeholder-
848
+ duplicated pattern (e.g., the 19 identical PNGs).
849
+ - **`uniform-numeric`** — numeric arrays >= 4 elements (or object
850
+ arrays where every object has an identical numeric field) where all
851
+ values are within `--uniform-epsilon` (default 1e-6). Catches the
852
+ template-fill pattern (e.g., 19/19 entries at similarity=0.999999).
853
+ - **`missing-references`** — JSON artifacts cite path-shaped string
854
+ values that don't exist on disk. Default `--dirty-only` and `--full-tree`
855
+ scans use strict missing-reference mode: the string must have a known
856
+ data/artifact extension and appear under object key/value context that
857
+ names evidence, source, screenshot, fixture, path, reference, or artifact
858
+ material. `--full-tree-loose` is the loose forensic compatibility path; it
859
+ scans the full tree with the older liberal path-shaped string behavior.
860
+ The strict missing-reference default is calibrated to avoid code-string and
861
+ command false positives while still catching cited-but-never-created
862
+ evidence paths.
863
+
864
+ Tier 2 detectors are operator-invoked with `--tier-2`; default invocation
865
+ remains Tier 1 only. Runner closeout integration may also run Tier 2 as the
866
+ first fuzzy stage before a default-OFF Tier 3 check when closeout-time Tier 3
867
+ is explicitly enabled:
868
+
869
+ - **`loose-uniform`** — numeric arrays >= 4 elements (or object arrays
870
+ where every object has a common numeric field) whose coefficient of
871
+ variation is below `--loose-uniform-stdev-threshold` (default 1e-3).
872
+ Exact uniformity is left to the Tier 1 `uniform-numeric` detector and
873
+ is not double-reported as loose-uniform.
874
+ - **`boilerplate-text`** — text files with high non-path token overlap.
875
+ The detector normalizes whitespace and punctuation, lower-cases tokens,
876
+ strips path-shaped tokens, skips binary files, and reports groups with
877
+ overlap above `--boilerplate-token-overlap-threshold` (default 0.80)
878
+ and size at least `--boilerplate-min-group-size` (default 3). The default
879
+ threshold is calibrated against `tests/fixtures/evidence-audit-calibration/`:
880
+ known-fake boilerplate and templated prose fixtures must flag, known-real
881
+ fixtures must not flag, and borderline fixtures remain low-confidence Tier 2
882
+ uncertainty rather than Tier 1-style hard failures.
883
+ - **`size-distribution`** — sibling-directory file groups whose byte-size
884
+ coefficient of variation is below
885
+ `--size-distribution-variance-threshold` (default 0.05), with at least
886
+ `--size-distribution-min-group-size` files (default 3).
887
+
888
+ Text output renders Tier 2 findings with `tier2:` prefixes. JSON output
889
+ includes the `tier2_findings` object only when `--tier-2` is enabled.
890
+
891
+ Tier 3 is default OFF. Operator invocations use `phase-loop evidence-audit
892
+ --enable-tier-3`; runner closeout invocations use `phase-loop run
893
+ --enable-tier-3` or a per-phase config opt-in. The runner also accepts
894
+ `--tier-3-budget` and records `tier3_budget` and `tier3_calls_made` per
895
+ closeout invocation. The default budget is 3. When budget is exhausted,
896
+ remaining Tier 2 uncertain findings are recorded as
897
+ `UNCERTAIN-OPERATOR-REVIEW` without another Tier 3 call.
898
+
899
+ Tier 3 enables the `EvaluateSuspectedFakeEvidence` BAML call only for Tier 2
900
+ uncertain loose-uniform findings, after Tier 1 duplicate-content,
901
+ uniform-numeric, and missing-reference detectors do not already produce
902
+ suspect findings. Clean audits, Tier 1 suspect audits, and Tier 2
903
+ certain-suspect findings bypass Tier 3. For runner closeouts, a `fake`
904
+ verdict or a non-uncertain judgment below the effective confidence threshold
905
+ produces a non-human `contract_bug` blocker with `metadata.tier3_judgment`.
906
+ `real` at or above threshold proceeds. `uncertain`, timeout, parse failure,
907
+ or wrapper error emits warning metadata but does not automatically block.
908
+
909
+ The Tier 3 input contract is bounded: the wrapper sends a Tier 2 signal
910
+ summary, the sample artifact content truncated to 8192 bytes by default,
911
+ and expected artifact characteristics. The structured output is
912
+ `EvidenceJudgment` with `verdict`, `confidence`, `reasoning`, and
913
+ `specific_concerns`. On timeout or parse failure, the wrapper returns an
914
+ uncertain judgment with `confidence: 0.0` and redacted `tier3_call_error`
915
+ reasoning.
916
+
917
+ Every runner-triggered Tier 3 call appends an `evidence_audit_tier3` event to
918
+ `.phase-loop/events.jsonl`. Event metadata includes `prompt_sha256`,
919
+ `response_sha256`, `verdict`, `confidence`, `token_counts`, `latency_ms`, and
920
+ `estimated_cost_usd`, plus the active budget counters. Token counts and
921
+ estimated cost may be null when the current provider wrapper does not expose
922
+ usage details. The ledger shape is prepared for the downstream
923
+ `phase-loop status --tier-3-history` query surface.
924
+
925
+ Per-phase config is optional at `.phase-loop/evidence-audit.yaml`:
926
+
927
+ ```yaml
928
+ tier2_enabled: true
929
+ tier3_enabled: false
930
+ tier3_confidence_threshold: 0.85
931
+ phase_aliases_exclude_tier3:
932
+ - T2DETECTORS
933
+ - T3SCHEMA
934
+ - T3RUNNER
935
+ - T3VALIDATE
936
+ phases:
937
+ FUTUREPHASE:
938
+ tier2_enabled: true
939
+ tier3_enabled: true
940
+ tier3_confidence_threshold: 0.85
941
+ disable_detectors: []
942
+ ```
943
+
944
+ The shipped default excludes `T2DETECTORS`, `T3SCHEMA`, `T3RUNNER`, and
945
+ `T3VALIDATE` so v23 cannot trigger Tier 3 against itself even if an operator
946
+ sets the global flag. Malformed config is a repairable non-human
947
+ `contract_bug`.
948
+
949
+ Operator invocation example:
950
+
951
+ ```bash
952
+ phase-loop evidence-audit --repo . --enable-tier-3 --json
953
+ ```
954
+
955
+ Runner invocation example:
956
+
957
+ ```bash
958
+ phase-loop run --repo . --enable-tier-3 --tier-3-budget 3
959
+ ```
960
+
961
+ ### Enabling Tier 3
962
+
963
+ Operators must treat Tier 3 as an opt-in rollout path, not a default runner
964
+ mode. Before enabling it for a phase, run the non-secret calibration corpus:
965
+
966
+ ```bash
967
+ python3 tests/calibrate_tier3.py --dry-run
968
+ python3 tests/calibrate_tier3.py --confidence-threshold 0.85 --fail-on-accuracy-threshold 0.80
969
+ ```
970
+
971
+ The dry run validates fixture shape and expected outcomes without live LLM
972
+ calls. A live calibration run reports verdict, confidence, estimated token and
973
+ cost metadata, accuracy, borderline confidence distribution, recommended
974
+ confidence threshold, and total estimated cost. Use the same provider, model,
975
+ and temperature for reproducibility; confidence should normally remain within
976
+ +/-0.05 on the same fixtures.
977
+
978
+ Roll out one phase at a time through `.phase-loop/evidence-audit.yaml`, monitor
979
+ recent invocation summaries with `phase-loop status --tier-3-history`, and
980
+ rollback by disabling the phase entry if false positives, latency, or cost
981
+ exceed operator tolerance. The status-history surface only reports timestamp,
982
+ phase, verdict, confidence, cost, and latency; it must not expose raw prompts,
983
+ raw responses, artifact contents, or provider-supplied payloads.
984
+
985
+ The v23 `phase_aliases_exclude_tier3` entries for `T2DETECTORS`, `T3SCHEMA`,
986
+ `T3RUNNER`, and `T3VALIDATE` remain in place until v23 completes. Removing
987
+ those exclusions is a separate explicit operator opt-in.
988
+
989
+ Exit codes: 0 if clean (no findings), 5 if suspect findings present.
990
+ Use the exit code as a pre-reconcile gate:
991
+
992
+ ```bash
993
+ phase-loop evidence-audit --repo . && \
994
+ phase-loop reconcile --repo . --roadmap specs/phase-plans-vN.md \
995
+ --phase <ALIAS> ...
996
+ ```
997
+
998
+ ## Drift Audit Subcommand
999
+
1000
+ `phase-loop closeout-drift-audit --repo . [--repo ../other] [--days 7]
1001
+ [--scope closeout|all-events] [--json]` is an operator-callable pre-flight
1002
+ for closeout literal drift. It scans `.phase-loop/runs/**/terminal-summary.json`
1003
+ and closeout-class entries in `.phase-loop/events.jsonl`, compares
1004
+ `terminal_status`, `verification_status`, `blocker_class`, executor literals,
1005
+ and dispatch capabilities against the live allowlists imported from
1006
+ `phase_loop_runtime.models`, and reports any literal not currently allowed.
1007
+
1008
+ Default scope is `closeout`. This is the normal closeout reconciliation gate
1009
+ and restricts event-ledger scanning to closeout-class surfaces such as
1010
+ terminal summaries, closeout metadata, automation metadata, manual repairs,
1011
+ and reopen/reconcile events. `--scope all-events` is an explicit forensic mode
1012
+ for wider event-ledger scans; it is not a replacement for the closeout-class
1013
+ pre-flight gate.
1014
+
1015
+ Text output is stable for operator review: it includes the scope, days window,
1016
+ cutoff timestamp, per-repo files/events scanned, malformed event counts, setup
1017
+ diagnostics, and per-field literal drift sections. JSON output includes
1018
+ `allowlists`, `repos`, `counts`, `drift`, and `setup_diagnostics`, so
1019
+ `phase-loop closeout-drift-audit --repo . --json | python3 -m json.tool` is a
1020
+ valid machine-readable smoke check.
1021
+
1022
+ Exit codes are:
1023
+
1024
+ - `0` when no drift or setup errors are found.
1025
+ - `1` when drift is found, for example the recurring reproduction payload
1026
+ `terminal_status: "dry_run"` without adding `dry_run` to `PHASE_STATUSES`.
1027
+ - `2` when setup or input errors prevent a trustworthy scan, such as a missing
1028
+ repo path, missing `.phase-loop` directory, invalid `--days`, or invalid
1029
+ scope.
1030
+
1031
+ Example closeout gate:
1032
+
1033
+ ```bash
1034
+ phase-loop closeout-drift-audit --repo . --days 7 --scope closeout && \
1035
+ phase-loop reconcile --repo . --roadmap specs/phase-plans-vN.md \
1036
+ --phase <ALIAS> ...
1037
+ ```
1038
+
1039
+ Example cross-repo forensic scan:
1040
+
1041
+ ```bash
1042
+ phase-loop closeout-drift-audit --repo . --repo ../governed-pipeline \
1043
+ --days 14 --scope all-events --json
1044
+ ```
1045
+
1046
+ ### Verified Dirty Closeout Auto-Recovery
1047
+
1048
+ When the runner has already performed a verified dirty closeout recovery, a
1049
+ later `phase-loop status` reconciliation may supersede the stale non-human
1050
+ blocker without requiring an operator-authored `manual_repair` event. The
1051
+ current phase's latest trusted event must carry
1052
+ `metadata.completion_dirty_worktree.reason:
1053
+ verified_dirty_closeout_recovery`, a `metadata.closeout.closeout_commit`,
1054
+ `metadata.terminal_summary.verification_status: passed`, and no
1055
+ `metadata.completion_dirty_worktree.unowned_dirty_paths`.
1056
+
1057
+ The repository worktree must be clean before this reducer fires. A successful
1058
+ reduction marks the phase complete, clears blocker and dirty-path fields,
1059
+ preserves closeout summary metadata, and records the ledger warning reason
1060
+ `clean_verified_dirty_closeout_recovery_superseded_nonhuman_blocker`. Human
1061
+ blockers and unrelated blocker classes remain authoritative; this reducer only
1062
+ repairs stale non-human dirty-closeout blockers.
1063
+
1064
+ ### Executor Degradation Cache
1065
+
1066
+ Session-scoped executor degradation is stored at the canonical
1067
+ `.phase-loop/executor-degradation.json` path. There is no legacy
1068
+ `.codex/phase-loop/**` fallback for this sidecar. The JSON object is keyed by
1069
+ executor name, and each record has these fields: `since`, `ttl_seconds`,
1070
+ `demoted_to`, `reason`, `source_phase`, and `blocker_summary`. `demoted_to`
1071
+ is limited to `proof_gated` or `manual_only`.
1072
+
1073
+ `state_degradation.load_degradation(repo)` returns valid records and tolerates
1074
+ missing or corrupted files by returning `{}`. `record_degradation(repo,
1075
+ executor, reason, source_phase, blocker_summary, ttl_seconds,
1076
+ demoted_to="proof_gated")` validates `demoted_to` and writes with a temporary
1077
+ file plus `os.replace`. `active_degraded_executors(repo, *, now=None)` returns
1078
+ the TTL-filtered active executor set, and `clear(repo)` removes the sidecar
1079
+ idempotently. `phase-loop archive-state` does not move this cache, so session
1080
+ demotion can survive runtime ledger archival.
1081
+
1082
+ FOUND publishes this cache contract only. DISPATCH wires launcher emissions,
1083
+ dispatch filtering, and any future `--reset-capability` control.
1084
+
1085
+ ### Blocker Classification Heuristics
1086
+
1087
+ Metadata-only launcher preflight must report only redacted probe metadata:
1088
+ command availability, return code, byte counts, and boolean surface presence.
1089
+ It must not persist stdout or stderr excerpts. Missing login, token, or
1090
+ subscription signals classify as `account_or_billing_setup` with
1091
+ `suggested_ttl_seconds: 300` and `demoted_to: proof_gated`.
1092
+
1093
+ Capacity-like provider signals classify as `unretryable_external_outage` with
1094
+ `suggested_ttl_seconds: 1800` and `demoted_to: manual_only`. The frozen
1095
+ capacity patterns are `capacity`, `exhausted`, `rate.limit`, `503`, and
1096
+ `temporarily.unavailable`; `claude auth status` quota-like JSON is reduced
1097
+ through the same capacity path without storing credential or provider-supplied payload
1098
+ values.
1099
+
1100
+ ### Session Capability Degradation During Dispatch
1101
+
1102
+ `resolve_dispatch_decision(..., repo=...)` consults
1103
+ `active_degraded_executors(repo)` after live availability has been confirmed
1104
+ for a candidate and before selecting it. A session-degraded executor is skipped
1105
+ silently so a live fallback can run. If every otherwise viable live candidate
1106
+ is session-degraded, dispatch returns `blocked_reason:
1107
+ all_candidates_session_degraded` with a summary naming the action and no
1108
+ credential or provider-supplied payload values.
1109
+
1110
+ `phase-loop run --reset-capability`, `phase-loop resume --reset-capability`,
1111
+ and `phase-loop dry-run --reset-capability` clear only
1112
+ `.phase-loop/executor-degradation.json` before dispatch setup. They do not
1113
+ archive, rewrite, or reconcile `.phase-loop/state.json`,
1114
+ `.phase-loop/events.jsonl`, or legacy `.codex/phase-loop/**`.
1115
+
1116
+ ### Rotation
1117
+
1118
+ `phase-loop run`, `phase-loop resume`, and `phase-loop dry-run` accept
1119
+ `--rotate-executors <csv>`, `--rotation-mode <phase|work_unit>`, and
1120
+ `--rotation-on-policy-pin <skip|fallback-next>`. The rotation list uses executor
1121
+ names from the frozen executor vocabulary, trims whitespace, deduplicates in
1122
+ order, and fails closed with a non-human `contract_bug` blocker when the list is
1123
+ empty or contains an invalid executor.
1124
+
1125
+ Rotation injects the current cursor executor as an operator-layer preferred executor
1126
+ before `resolve_dispatch_decision(..., repo=...)`. It rotates only
1127
+ executors; model and effort still come from the profile and Execution Policy
1128
+ chain. Plan and roadmap Execution Policy pins remain higher precedence than
1129
+ rotation. A policy pin launches with its pinned executor regardless of cursor
1130
+ position.
1131
+
1132
+ `--rotation-mode phase` consumes rotation at phase launch boundaries.
1133
+ `--rotation-mode work_unit` consumes rotation at work-unit launch starts. A
1134
+ running phase or work unit keeps the executor selected at launch; rotation never
1135
+ switches an in-flight unit. `--rotation-on-policy-pin=skip` is the default and
1136
+ counts a policy pin as a consumed rotation turn. `fallback-next` preserves the
1137
+ cursor until the next non-pinned phase or work unit consumes it.
1138
+
1139
+ The accepted mode literals are `phase` and `work_unit`. The accepted
1140
+ policy-pin literals are `skip` and `fallback-next`.
1141
+
1142
+ Executor degradation remains the DISPATCH authority: candidates listed in
1143
+ `.phase-loop/executor-degradation.json` are excluded by the existing
1144
+ `active_degraded_executors(repo)` filter during final dispatch. Rotation does
1145
+ not read provider-supplied payloads or reimplement degradation. New launch events with a
1146
+ resolved dispatch decision stamp top-level `selected_executor`; old events
1147
+ without that field reduce identically.
1148
+
1149
+ Default harness policy is explicit. Simple bounded scheduler-assigned lane
1150
+ execution defaults to `executor=pi`; Claude or Anthropic model lanes default to
1151
+ Claude Code CLI unless a policy explicitly selects a Pi-wrapped Claude route
1152
+ and records the override reason. Codex and Gemini fallback routes are
1153
+ CLI-based, reason-coded, and must not silently switch to API-key command
1154
+ adapters.
1155
+
1156
+ ## Skill Namespace Contract
1157
+
1158
+ Harness-local workflow skills follow the `<harness>-<workflow>` pattern frozen
1159
+ in `docs/phase-loop/harness-skill-matrix.md`. The active harness families are
1160
+ Codex, Claude Code, Gemini CLI, and OpenCode. Direct Codex, direct Gemini, and
1161
+ direct OpenCode launcher routes remain compatibility-supported during this
1162
+ roadmap, while Claude Code continues to use the `claude -p` path.
1163
+
1164
+ Pi Agent role-style skills are explicit exceptions: `phase-loop-supervisor`,
1165
+ `phase-loop-repair`, and `phase-loop-closeout`. They are adapter roles, not a
1166
+ fifth unnormalized harness workflow family.
1167
+
1168
+ Governed-pipeline `.pipeline/skills/**` is a downstream product/runtime
1169
+ namespace outside dotfiles skill normalization. Dotfiles artifacts may mention
1170
+ canonical workflow skill names as bridge vocabulary, but dotfiles must not
1171
+ rewrite, rename, install, or validate governed-pipeline `.pipeline/skills/**`
1172
+ as `<harness>-<workflow>` skills.
1173
+
1174
+ ## Work-Unit Policy
1175
+
1176
+ WORKPOLICY freezes provider-neutral policy metadata for future model and effort
1177
+ selection. It is a contract surface only; v8 does not schedule individual lanes
1178
+ as runner-owned work units.
1179
+
1180
+ The work-unit kind vocabulary is:
1181
+
1182
+ - `roadmap_build`
1183
+ - `phase_plan`
1184
+ - `lane_execute`
1185
+ - `lane_review`
1186
+ - `phase_reducer`
1187
+ - `phase_verify`
1188
+ - `repair`
1189
+ - `closeout`
1190
+
1191
+ The normalized effort vocabulary is:
1192
+
1193
+ - `minimal`
1194
+ - `low`
1195
+ - `medium`
1196
+ - `high`
1197
+ - `xhigh`
1198
+ - `max`
1199
+
1200
+ Unsupported provider policy must resolve to exactly one explicit behavior:
1201
+
1202
+ - `block`: fail closed when the selected provider cannot honor the requested
1203
+ work-unit, model, effort, or thinking policy.
1204
+ - `fallback`: use a named fallback policy, executor, model alias, or effort
1205
+ mapping and record that fallback in launch metadata.
1206
+ - `inherit_default`: use the provider or profile default only when that default
1207
+ inheritance is explicitly recorded.
1208
+
1209
+ Silent downgrade is forbidden. A requested effort such as `xhigh` must not
1210
+ become `high` unless `fallback` or `inherit_default` was selected and recorded.
1211
+
1212
+ Provider capability normalization covers:
1213
+
1214
+ - Codex/OpenAI: accepts the normalized work-unit and effort metadata directly
1215
+ for `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`.
1216
+ - Claude Code: maps normalized policy onto Claude effort controls; unsupported
1217
+ high-end distinctions such as `xhigh` require explicit fallback to `max`.
1218
+ - Gemini CLI: defaults to built-in routing aliases, using `pro` for
1219
+ planning/review and `auto` for execution/repair so Gemini CLI can apply its
1220
+ own fallback behavior. Explicit phase-loop proof runs may still use
1221
+ run-local user-scope `modelConfigs.customAliases` for
1222
+ `phase-loop-plan-high`, `phase-loop-execute-medium`, and
1223
+ `phase-loop-review-high`; those aliases carry
1224
+ `thinkingConfig.thinkingLevel` and non-secret auth selector metadata. A
1225
+ project-local `.gemini/settings.json` beside the prompt workspace is not
1226
+ sufficient evidence for custom policy injection.
1227
+ - Gemini API/OpenAI-compatible: remains metadata-only unless a future adapter
1228
+ phase explicitly owns and verifies an API path.
1229
+ - OpenCode: records normalized work-unit and effort metadata for adapter
1230
+ selection without changing current dispatch behavior.
1231
+ - Pi Agent: consumes repo-local `phase-loop-pi/**` prompts, skills,
1232
+ extensions, and `pi-config/**` installation metadata through a context-file
1233
+ launch. `executor=pi` is bounded to simple scheduler-assigned lane work and
1234
+ never owns global scheduling, runtime ledger mutation, worktree allocation,
1235
+ or merge reduction.
1236
+ - Manual handoff: non-default and selected only by operator, roadmap, or
1237
+ phase-plan policy.
1238
+ - Generic command adapters: require explicit adapter inputs and fail closed
1239
+ when policy cannot be mapped.
1240
+
1241
+ ## Automation Handoffs
1242
+
1243
+ This protocol is harness-neutral, not a blanket support claim. Operator-facing
1244
+ docs must pair it with the current maturity matrix in
1245
+ `docs/phase-loop/harness-capability-matrix.md` so shared artifact shapes are
1246
+ not mistaken for proof that every executor is live-supported.
1247
+
1248
+ ## Phase-Loop Closeout Schema (v1)
1249
+
1250
+ The canonical closeout schema (`phase_loop_closeout.v1`) is a nested object that describes the terminal state of a phase execution. It is emitted as a JSON object with these top-level fields:
1251
+
1252
+ - `schema`: Always `phase_loop_closeout.v1`.
1253
+ - `phase`: The phase alias (e.g., `RUNNER`).
1254
+ - `terminal_status`: The high-level phase outcome (`complete`, `blocked`, `failed_verification`, `human_required`, `stale_input`).
1255
+ - `automation`: An object describing the next machine steps.
1256
+ - `artifacts`: An object listing produced artifacts and plan metadata.
1257
+ - `verification`: An object describing verification status and evidence.
1258
+ - `blocker`: An object describing why a phase is blocked, if applicable.
1259
+ - `source_bundle`: An object describing the pipeline source context. Standalone
1260
+ closeout keeps this object present with `pipeline_mode: standalone` and may
1261
+ omit Pipeline-only identity fields.
1262
+ - `source_truth_impact`: An advisory object describing metadata-only
1263
+ source-truth impact hints for changed paths.
1264
+ - `lane`: Optional scheduler-owned lane closeout metadata for work-unit
1265
+ launches, containing lane identity, wave identity, worktree identity,
1266
+ verification status, changed paths, and redacted evidence refs.
1267
+
1268
+ Native v1 JSON closeout must not include deprecated root-level v5 automation aliases
1269
+ such as `status`, `next_skill`, `next_command`,
1270
+ `verification_status`, `artifact`, or `artifact_state`. Those values belong
1271
+ inside the nested `automation` object.
1272
+ Terminal-summary extraction remains a legacy compatibility path for rendered
1273
+ `automation:` blocks; it is not the native JSON fixture contract.
1274
+
1275
+ ## Native Output Schema Enforcement
1276
+
1277
+ NATIVE added `CLOSEOUT_SCHEMA` in `phase_loop_runtime.models` as the temporary
1278
+ structured-output contract before BAML became the single source of truth. The
1279
+ schema requires
1280
+ `terminal_status`, `verification_status`, `dirty_paths`, and
1281
+ `produced_if_gates`. A closeout that claims `terminal_status=complete` must
1282
+ report at least one produced IF gate at the schema layer.
1283
+
1284
+ Codex live launches that require a closeout write `CLOSEOUT_SCHEMA` to a
1285
+ temporary JSON file, append `--output-schema <path>`, record the path on
1286
+ `LaunchSpec.cleanup_paths`, and remove it after subprocess completion. Claude
1287
+ live launches that require a closeout append `--json-schema <compact-json>`
1288
+ with the same schema. Gemini, OpenCode, PI, command adapters, and manual paths
1289
+ do not receive native CLI schema flags during NATIVE.
1290
+
1291
+ The NATIVE runner still accepts legacy rendered `automation:` blocks during the
1292
+ compatibility window. Native JSON closeouts are normalized back into the shared
1293
+ automation fields before reducer logic runs.
1294
+
1295
+ ## BAML Closeout Schema
1296
+
1297
+ BAMLBASE moves the closeout-emission boundary to the declarative
1298
+ `EmitPhaseCloseout` function in
1299
+ `vendor/phase-loop-runtime/src/phase_loop_runtime/baml_src/emit_phase_closeout.baml`. The BAML source
1300
+ defines `PhaseLoopCloseoutV1` with the same root fields as the NATIVE closeout
1301
+ schema: `terminal_status`, `verification_status`, `dirty_paths`,
1302
+ `produced_if_gates`, `next_action`, blocker metadata, and
1303
+ `required_human_inputs`.
1304
+
1305
+ `phase_loop_runtime.baml_modular.build_baml_request("EmitPhaseCloseout",
1306
+ payload)` loads the vendored BAML source and returns the model-facing prompt
1307
+ plus request metadata. Harness prompt injection consumes that rendered prompt
1308
+ instead of duplicating the closeout field ceremony in each skill.
1309
+
1310
+ `phase_loop_runtime.baml_modular.parse_baml_response("EmitPhaseCloseout",
1311
+ raw_text)` parses child closeout output through the BAML runtime and then
1312
+ normalizes the typed value back into the runner's shared automation fields.
1313
+ BAML validation errors are reported as repairable non-human `contract_bug`
1314
+ blockers with non-secret summaries.
1315
+
1316
+ ### BAML Prompt Template
1317
+
1318
+ `phase_loop_runtime.baml_modular.render_baml_prompt(prompt_template,
1319
+ context_constants)` performs the repo-local prompt-template pass before BAML
1320
+ loads source text. It supports `{{ name }}` and `{{ name | join(', ') }}` for
1321
+ explicitly provided constants, without adding Jinja2 or changing BAML runtime
1322
+ syntax.
1323
+
1324
+ Closeout prompts currently expose `allowed_terminal_statuses`,
1325
+ `allowed_verification_statuses`, and `allowed_blocker_classes`. These values
1326
+ render from `phase_loop_runtime.models.PHASE_STATUSES`,
1327
+ `phase_loop_runtime.models.BLOCKER_CLASSES`, and the verification-status
1328
+ allowlist so model-facing taxonomy text stays aligned with `models.py`. The
1329
+ blocker-class rendering includes the compatibility `none` marker; closeout
1330
+ payloads should still use `null` when no blocker is present.
1331
+
1332
+ BAML runtime placeholders such as `{{ phase_alias }}`, `{{ ctx.output_format
1333
+ }}`, and control blocks remain BAML-owned and must pass through unchanged.
1334
+ Unknown taxonomy constants are repairable `BamlValidationError` failures, not
1335
+ silent downgrades to stale hard-coded prompt text.
1336
+
1337
+ ### Closeout Evidence Audit
1338
+
1339
+ Roadmaps may opt in to the post-commit closeout evidence audit with
1340
+ `closeout_evidence_audit: true` in roadmap frontmatter or in the first H2
1341
+ metadata block. When enabled, commit closeout runs the audit after the closeout
1342
+ commit lands and never amends that commit.
1343
+
1344
+ The audit compares parseable closeout claims such as `Added`, `Created`,
1345
+ `Wrote`, and `Updated` backticked symbols to the closeout commit diff. Filename
1346
+ basename matches, path-suffix matches, or identifier matches in the diff content
1347
+ count as evidence. If evidence is missing, the closeout event is downgraded to
1348
+ `blocked` with `blocker_class: closeout_evidence_drift` and the redacted
1349
+ summary shape `<N> of <M> closeout claims have no matching files in the
1350
+ closeout diff`.
1351
+
1352
+ The frozen blocker literal for this downgrade is `closeout_evidence_drift`.
1353
+ Audit metadata may record only status and counts. Blocker summaries, metadata,
1354
+ logs, and terminal summaries must not include raw commit bodies, raw diff bodies,
1355
+ secret-bearing payloads, or unmatched symbol text.
1356
+
1357
+ ### Closeout Hardening
1358
+
1359
+ `EmitPhaseCloseout` uses field-anchored enum lists for `terminal_status` and
1360
+ `verification_status`, an explicit `dry_run` negative example, a
1361
+ terminal-status decision tree, and field-pair invariants for
1362
+ `terminal_status` plus `verification_status`. `dry_run` remains an event-level
1363
+ execution mode and must not appear in a phase closeout payload.
1364
+
1365
+ Native closeout parsing first runs
1366
+ `phase_loop_runtime.discovery.parse_closeout_payload_doc(text, kind)` over the
1367
+ extracted JSON payload. Unknown `terminal_status`, `verification_status`, or
1368
+ `blocker_class` literals soft-fail into `CloseoutParseError` diagnostics before
1369
+ BAML schema parsing. The runner converts those diagnostics into non-human
1370
+ `contract_bug` blockers whose summary names the invalid literal and field.
1371
+
1372
+ Operator remediation is deliberate: patch the executor prompt when the literal
1373
+ is prompt drift, or amend the runner allowlist and reinstall the runtime when a
1374
+ new literal is intentionally added. The runtime must not coerce unknown
1375
+ closeout literals into nearby statuses.
1376
+
1377
+ ## Schema-Flow Architecture
1378
+
1379
+ `vendor/phase-loop-runtime/src/phase_loop_runtime/baml_src/emit_phase_closeout.baml` is the canonical
1380
+ closeout contract. `phase_loop_runtime.baml_modular.export_function_schema(
1381
+ "EmitPhaseCloseout")` reads that BAML function, exports the `PhaseLoopCloseoutV1`
1382
+ object shape, applies documented Codex/Claude JSON Schema dialect
1383
+ normalization, and fails with `BamlValidationError` when the BAML function or
1384
+ return class cannot be exported. Missing BAML schema export is a repairable
1385
+ non-human `contract_bug`; the runtime must not silently downgrade to a duplicate
1386
+ hand-written schema.
1387
+ The canonical helper call is `export_function_schema("EmitPhaseCloseout")`.
1388
+
1389
+ `phase_loop_runtime.models.CLOSEOUT_SCHEMA` is a compatibility import path for
1390
+ existing callers and is computed from
1391
+ `export_function_schema("EmitPhaseCloseout")` at module import time. Codex
1392
+ launches that require closeout write that schema to `--output-schema <path>`;
1393
+ Claude launches pass the same canonical schema through
1394
+ `--json-schema <compact-json>`. Gemini, OpenCode, and PI do not expose matching
1395
+ native flags, so their closeout prompts embed deterministic schema-description
1396
+ text from `inject_schema_description(prompt, schema)` with the canonical schema
1397
+ hash and ordered fields.
1398
+
1399
+ All executor closeouts still pass through `parse_closeout_payload_doc(...)`,
1400
+ `parse_baml_response("EmitPhaseCloseout", raw_text)`, and then through IF-Gate
1401
+ Tier 1 validation.
1402
+ The runner compares `produced_if_gates` with the active plan's declared
1403
+ `Produces` / `Interfaces provided` gates, so native flags, prompt embedding,
1404
+ BAML parse, and IF-gate cross-check all consume the same BAML-authored schema
1405
+ flow.
1406
+
1407
+ ### Strict Mode Transition
1408
+
1409
+ BAMLBASE ends the NATIVE compatibility window for native JSON closeouts.
1410
+ Completed closeouts that omit `produced_if_gates`, report an empty gate list,
1411
+ or otherwise violate `PhaseLoopCloseoutV1` are rejected before runner state can
1412
+ advance. Legacy rendered `automation:` blocks remain a compatibility path only
1413
+ for actions that do not emit native JSON closeouts. IF-Gate Tier 1 validation
1414
+ continues to compare the typed `produced_if_gates` list with the active phase
1415
+ plan's declared gates.
1416
+
1417
+ ## IF-Gate Tier 1 Validation
1418
+
1419
+ NATIVE adds `validate_produced_gates(plan_path, closeout_payload)` in
1420
+ `phase_loop_runtime.closeout_validation`. It extracts the active phase plan's
1421
+ declared IF gates from `Produces` and lane `Interfaces provided` declarations,
1422
+ then compares them with closeout `produced_if_gates`.
1423
+
1424
+ During the NATIVE compatibility window, a completed legacy closeout with no
1425
+ `produced_if_gates` field records a warning and remains compatible. A completed
1426
+ closeout with `produced_if_gates: []`, missing expected gates, or unexpected
1427
+ gates is blocked as repairable non-human `contract_bug`. This is the Tier 1
1428
+ scope check only; filesystem evidence verification remains out of scope.
1429
+
1430
+ #### Automation Object
1431
+
1432
+ Phase-loop aware skills and manual TUI runs must emit a machine-readable `automation` object with this exact field set:
1433
+
1434
+ ```yaml
1435
+ automation:
1436
+ status: <phase status literal>
1437
+ next_skill: <skill name or none>
1438
+ next_command: <command string or none>
1439
+ next_model_hint: <model profile key or none>
1440
+ next_effort_hint: <reasoning effort hint or none>
1441
+ human_required: <true|false>
1442
+ blocker_class: <frozen blocker class or none>
1443
+ blocker_summary: <short actionable summary or none>
1444
+ required_human_inputs: []
1445
+ verification_status: <not_run|passed|failed|blocked>
1446
+ artifact: <absolute artifact path or none>
1447
+ artifact_state: <staged|tracked|modified|unstaged|untracked|blocked|none>
1448
+ ```
1449
+
1450
+ #### Artifacts Object
1451
+
1452
+ - `plan_path`: Path to the phase plan file.
1453
+ - `plan_sha256`: SHA-256 of the phase plan file.
1454
+ - `artifact_paths`: A map of logical names to absolute paths for produced artifacts.
1455
+ - `changed_paths`: A list of repository paths modified during the phase.
1456
+ - `evidence_refs`: Metadata-only evidence references. Entries may include paths,
1457
+ labels, and hashes, but not raw transcripts, provider-supplied payloads, local
1458
+ environment values, credentials, or private evidence bytes.
1459
+
1460
+ #### Source Truth Impact Object
1461
+
1462
+ `source_truth_impact` is advisory metadata only. Impact hints are advisory:
1463
+ governed-pipeline owns canonical refresh, replan, and block decisions.
1464
+ Dotfiles must not update governed-pipeline canonical docs/specs, `.pipeline/**`,
1465
+ Portal contracts, Greenfield authority files, raw-evidence, or legacy
1466
+ `.codex/phase-loop/` state in response to these hints.
1467
+
1468
+ - `changed_path_boundaries`: A list of objects containing `path` and `category`.
1469
+ Category is one of `code`, `tests`, `docs`, `specs`,
1470
+ `active_canonical_spec`, `managed_root_mirror_spec`, `mirror_manifest`,
1471
+ `archive_manifest`, `archived_spec`, `unmanaged_spec`,
1472
+ `pipeline_sources`, `portal_contract_refs`, `greenfield_authority_refs`,
1473
+ or `unknown`. The broad `specs` category remains a compatibility literal,
1474
+ but standalone root `specs/**` paths classify as `unmanaged_spec` unless a
1475
+ validated source bundle marks them as managed mirror or archived material.
1476
+ - `canonical_refresh_recommended`: Boolean advisory signal for source-truth
1477
+ sensitive changes.
1478
+ - `canonical_refresh_reason_codes`: A list using `docs_source_truth_touched`,
1479
+ `specs_source_truth_touched`, `active_specs_touched`,
1480
+ `managed_mirror_specs_touched`, `mirror_manifests_touched`,
1481
+ `archive_manifests_touched`, `archived_specs_touched`,
1482
+ `unmanaged_specs_touched`, `adoption_contracts_touched`,
1483
+ `contract_refs_touched`, `pipeline_sources_touched`,
1484
+ `portal_contract_refs_touched`, or `greenfield_authority_refs_touched`.
1485
+ - `redaction_posture`: One of `metadata_only` or
1486
+ `rejected_forbidden_metadata`.
1487
+
1488
+ Impact and evidence metadata exclude raw patch bodies, raw transcripts, secret-like values,
1489
+ absolute private paths, provider-supplied payloads, credential-bearing payloads, local environment contents,
1490
+ and private evidence bytes. Redaction violations make the closeout
1491
+ malformed instead of preserving the forbidden content.
1492
+
1493
+ #### Verification Object
1494
+
1495
+ - `status`: One of the local verification status literals such as `passed`,
1496
+ `failed`, `blocked`, `not_run`, or `unknown`.
1497
+ - `commands`: Optional command strings used as metadata-only proof of what was
1498
+ checked.
1499
+ - `agent_reported_verification_status`: Additive metadata preserving the
1500
+ executor's self-reported verification value before runner evidence
1501
+ validation.
1502
+ - `results`: Optional metadata-only validation records. Closeout acceptance of
1503
+ `passed` is authoritative only when backed by a valid IF-0-VC-1
1504
+ `verification.json` artifact whose sibling `verification.log` hash matches
1505
+ `log_sha256` and whose command, env-refresh, and suite exit codes are all
1506
+ zero.
1507
+
1508
+ By default, `PHASE_LOOP_VERIFY_ENFORCE=hard` behavior is active: missing,
1509
+ malformed, nonzero, or tampered verification evidence blocks a passed closeout
1510
+ with blocker class `verification_evidence_missing`. Operators may set
1511
+ `PHASE_LOOP_VERIFY_ENFORCE=warn` to preserve the reported closeout while
1512
+ emitting structured warning metadata during staged adoption. Operational
1513
+ evidence amendments must use
1514
+ `verification_evidence.append_evidence_entry(doc_path, entry)`, which appends a
1515
+ fresh entry with a runner timestamp and preserves prior document bytes. A
1516
+ blocked operational gate may be re-verdicted only from a fresh entry of the
1517
+ originally specified evidence kind.
1518
+
1519
+ #### DFPARSOAK Receipt Boundary
1520
+
1521
+ DFPARSOAK is the dotfiles substrate soak for the parallel bridge. A valid
1522
+ DFPARSOAK receipt cites Greenfield `GFPARSOAK` and governed-pipeline
1523
+ `GPPARSOAK` as metadata-only upstream receipts with phase alias, repo-relative
1524
+ path, sha256 digest, produced interfaces, verification status, and redacted
1525
+ evidence refs.
1526
+
1527
+ The local wave proof uses scheduler-owned git-worktree assignment metadata and
1528
+ records Pi Agent default coverage, Claude Code CLI exception coverage, and
1529
+ Codex/Gemini fallback coverage. Fallback or default inheritance must be
1530
+ explicitly recorded; silent downgrade is invalid.
1531
+
1532
+ DFPARSOAK closeout evidence must remain redacted evidence handles or hashes. It
1533
+ must preserve no sibling-repo mutation and must not contain raw logs, raw
1534
+ transcripts, raw prompts, provider-supplied payloads, credentials, local environment contents, raw
1535
+ diffs, ignored private paths, or host-only evidence paths.
1536
+
1537
+ #### Source Bundle Object
1538
+
1539
+ - `pipeline_mode`: One of `standalone`, `pipeline_optional`, or
1540
+ `pipeline_required`.
1541
+ - `path`: Path to the source bundle JSON. Required for `pipeline_required`;
1542
+ optional or absent for standalone closeout.
1543
+ - `sha256`: SHA-256 of the source bundle. Required for `pipeline_required`;
1544
+ optional or absent for standalone closeout.
1545
+ - `phase_id`: The canonical pipeline phase ID. Required for
1546
+ `pipeline_required`; optional or absent for standalone closeout.
1547
+ - `protected_sources`: Optional metadata-only protected-source echo. Entries
1548
+ may include `path`, `category`, `sha256`, and adoption-sensitive `role`, but
1549
+ must not include raw specification bodies, raw patch bodies, provider-supplied payloads, credentials,
1550
+ local environment contents, private evidence, or absolute private paths.
1551
+
1552
+ Pipeline-required execution must fail closed before child launch when the plan
1553
+ or deterministic bridge output cannot supply matching `source_bundle.path`,
1554
+ `source_bundle.sha256`, `source_bundle.phase_id`, `source_bundle.pipeline_mode`,
1555
+ `phase`, `phase_alias`, `plan_path`, and `plan_sha256`. The resulting direct
1556
+ closeout is still a typed `phase_loop_closeout.v1` blocker with metadata-only
1557
+ diagnostics. Dotfiles reports the consumed identity; governed-pipeline remains
1558
+ the canonical source authority.
1559
+ - `pipeline_mode`: One of `standalone`, `pipeline_optional`, `pipeline_required`.
1560
+
1561
+ #### Lane Object
1562
+
1563
+ The optional `lane` object is present only when a closeout summarizes a
1564
+ runner-assigned work unit. It records scheduler-owned metadata without making
1565
+ dotfiles the scheduler, runtime ledger, worktree allocator, merge reducer, or
1566
+ authority-digest owner:
1567
+
1568
+ - `lane_id`: Lane identity selected by the phase plan.
1569
+ - `wave_id`: Wave identity assigned by the scheduler.
1570
+ - `worktree_path`: Worktree identity assigned by the scheduler.
1571
+ - `verification_status`: Work-unit verification result.
1572
+ - `changed_paths`: Repository paths changed by the work unit.
1573
+ - `evidence_refs`: Redacted evidence references such as artifact paths and
1574
+ digests; raw transcripts, credentials, private file contents, and provider
1575
+ tokens are not closeout fields.
1576
+
1577
+ When human-readable top-level handoff fields such as `artifact`,
1578
+ `artifact_state`, `next_skill`, or `next_command` are also present, they must
1579
+ agree with `automation.*` or the handoff is malformed.
1580
+
1581
+ The phase status vocabulary used by `automation.status` and reconciled state is
1582
+ frozen to `PHASE_STATUSES`:
1583
+
1584
+ - `unplanned`
1585
+ - `planned`
1586
+ - `executing`
1587
+ - `executed`
1588
+ - `awaiting_phase_closeout`
1589
+ - `complete`
1590
+ - `blocked`
1591
+ - `unknown`
1592
+
1593
+ `EVENT_STATUSES` is the event-ledger validation vocabulary. It includes all
1594
+ `PHASE_STATUSES` plus the event-only status:
1595
+
1596
+ - `plan_skipped`
1597
+
1598
+ ## Roadmap Validation
1599
+
1600
+ Roadmap-aware command-start paths run warning-only phase heading validation
1601
+ after roadmap selection and before normal execution. The validator only
1602
+ examines `### Phase ...` heading lines; it does not validate roadmap body
1603
+ content, enforce a numbering convention, rewrite roadmap files, or change event
1604
+ provenance semantics.
1605
+
1606
+ Each validation finding carries:
1607
+
1608
+ - `line_number`
1609
+ - `raw_text`
1610
+ - `reason`
1611
+ - `suggested_fix`
1612
+
1613
+ Warnings are emitted to stderr using this operator-facing shape:
1614
+
1615
+ ```text
1616
+ phase-loop roadmap warning: line <line>: <reason>; raw heading: '<heading>'; suggested fix: <fix>
1617
+ ```
1618
+
1619
+ The validator reports loose phase-heading candidates that strict
1620
+ `PHASE_HEADING_RE` would not include in phase hash lookup, duplicate phase
1621
+ aliases, and aliases outside `[A-Z][A-Z0-9._-]*`. Commands must continue with
1622
+ their existing return-code policy and stdout payloads. Common fixes are to use
1623
+ `### Phase <number> - Title (ALIAS)`, keep aliases uppercase, and give each
1624
+ phase a unique final parenthesized alias.
1625
+
1626
+ ## Event Ledger Records
1627
+
1628
+ Durable loop events are append-only records stored in the active runtime ledger.
1629
+ Shared event semantics include:
1630
+
1631
+ - `schema_version: 2`
1632
+ - `roadmap_sha256`
1633
+ - `phase_sha256`
1634
+ - event status literals from `EVENT_STATUSES`
1635
+ - blocker metadata when a blocker exists
1636
+ - optional top-level `selected_executor` when dispatch or work-unit launch
1637
+ selected an executor
1638
+ - model provenance and executor metadata when available
1639
+ - dispatch metadata when selection occurs: `selected_executor`, `source`,
1640
+ `selected_via`, `considered_executors`, `fallback_applied`,
1641
+ `blocked_reason`, and `blocked_summary`
1642
+ - optional delegation metadata when a run proposes or launches nested work:
1643
+ request contract, approval or denial decision, budget metadata, and
1644
+ parent-child lineage pointing to child artifacts without copying prompt bodies
1645
+ or collapsing native Claude team activity into runner-visible child work
1646
+
1647
+ Manual/operator events must use the same contract surface as autonomous runs.
1648
+ Legacy hashless records may remain visible for audit but must not drive future
1649
+ autonomous execution.
1650
+
1651
+ ## Hotfix Work Unit
1652
+
1653
+ `phase-loop hotfix --init-stub <path>` writes a minimal stub with `objective`
1654
+ and `verification_command` fields and exits without creating a run directory.
1655
+
1656
+ `phase-loop hotfix --reason <text> --plan <stub-path>` is the sanctioned
1657
+ emergency path for a single bounded change with no interface freeze. Anything
1658
+ that changes interfaces, roadmap scope, or downstream work uses a roadmap
1659
+ phase instead.
1660
+
1661
+ A hotfix execution creates `.phase-loop/runs/<ts>-hotfix-<slug>/`, records
1662
+ launch metadata, runs dependency-manifest env refresh plus the stub command and
1663
+ the effective suite command through IF-0-VC-1 verification evidence, validates
1664
+ the artifact with the same RG gate semantics used for execute closeout, and
1665
+ appends a closeout event containing `work_unit: hotfix`, the redacted reason,
1666
+ the plan stub, `verification_artifact_path`, `verification_log_path`, and the
1667
+ artifact validation summary. A hotfix closeout cannot report
1668
+ `verification_status: passed` unless the validation accepts the evidence
1669
+ artifact and sibling log.
1670
+
1671
+ ## Closeout Event Emission
1672
+
1673
+ After a live child executor emits a valid native closeout, the runner appends an
1674
+ executor-terminal `LoopEvent` before applying later runner-owned dirty-path or
1675
+ closeout classification. The event is append-only evidence with:
1676
+
1677
+ - `action: executor.closeout`
1678
+ - `status` equal to the executor closeout `terminal_status`
1679
+ - current `roadmap_sha256` and `phase_sha256` from
1680
+ `event_provenance(roadmap, phase)`
1681
+ - `metadata.executor_closeout_event.source_status`
1682
+ - `metadata.executor_closeout_event.verification_status`
1683
+ - `metadata.executor_closeout_event.produced_if_gates`
1684
+ - `metadata.executor_closeout_event.dirty_paths`
1685
+ - normalized child automation metadata under `metadata.child_automation`
1686
+
1687
+ Writers emit only the `executor.closeout` action for this executor-terminal
1688
+ record. Legacy ledgers that contain `action: run` with a dict-valued
1689
+ `metadata.executor_closeout_event` remain valid audit evidence; reducers
1690
+ normalize that legacy shape to `executor.closeout` at read time for identity and
1691
+ status processing without rewriting the on-disk event.
1692
+
1693
+ The executor-terminal event does not replace the later runner-classified event.
1694
+ Reducer authority remains ledger-order based: the most recent terminal event
1695
+ with valid provenance wins. A later runner-classified `blocked` event therefore
1696
+ supersedes an earlier executor-terminal `complete`, while the earlier executor
1697
+ event remains preserved for audit and recovery.
1698
+
1699
+ ## Live Adapter Contract
1700
+
1701
+ The live-adapter readiness contract is frozen around these shared code surfaces:
1702
+
1703
+ - `LaunchRequest` carries adapter input selection: repo, roadmap, phase, plan,
1704
+ model, permission policy, injection metadata, dispatch policy, and optional
1705
+ delegation lineage. Mixed-run delegation keeps the resolved
1706
+ `DispatchDecision`, typed `DelegationRequest`, and `ParentChildRunMetadata`
1707
+ on the shared request object instead of reconstructing them later. TEAMGOV
1708
+ extends this request contract with `claude_execution_mode` literals
1709
+ `solo`, `subagent`, and `agent_team`, plus optional typed
1710
+ `claude_team_policy` and `phase_team_eligibility` metadata for governed
1711
+ Claude-native collaboration.
1712
+ - `LaunchSpec` carries the reduced launch contract written to `launch.json`:
1713
+ the redacted command, availability, proof gate, promotion status, auth
1714
+ preflight mode, timeout posture, output-capture format, delivery literals
1715
+ `prompt_only`, `inline`, `stdin`, `context_file`, and `manual`, plus the
1716
+ `terminal-summary.json` artifact path contract. When an adapter needs
1717
+ executor-specific launch state such as permission posture, selected agent,
1718
+ provider-qualified model, or reasoning variant, that metadata must also stay
1719
+ in `LaunchSpec` and `launch.json` instead of being inferred later from raw
1720
+ output. The generic `command` adapter is frozen as an explicit
1721
+ `command_template` contract: it records `command_adapter_name`,
1722
+ `command_template`, `wrapped_cwd`, delivery mode, `context_path`, and
1723
+ `context_sha256`; it must fail closed when required placeholders or adapter
1724
+ inputs are missing. Governed Claude launch specs must also preserve the
1725
+ resolved `claude_execution_mode`, the typed `claude_team_policy`, and the
1726
+ evaluated `phase_team_eligibility` so unsafe native-team requests are
1727
+ rejected before launch and safe governed launches record their policy even
1728
+ when public task-list CLI flags remain unavailable.
1729
+ - `LaunchResult` carries the observed child result and the same reduced
1730
+ availability metadata back into events and monitors.
1731
+ - `ExecutorCapabilityRecord` is the frozen registry surface for per-executor
1732
+ live readiness, proof-gate status, promotion requirements, permission
1733
+ posture, auth-preflight probe mode, timeout policy, output capture, and
1734
+ terminal-summary reduction expectations. The registry vocabulary is frozen to
1735
+ `promotion_status` literals `live`, `proof_gated`, and `manual_only`; shared
1736
+ failure literals `adapter_failure` and `phase_failure`; and blocker posture
1737
+ literals `human_required` and `repairable_non_human`.
1738
+
1739
+ Later adapter phases may fill in executor-specific command vectors and
1740
+ disposable proof artifacts, but they must not move these shared fields into ad
1741
+ hoc dicts or harness-specific markdown.
1742
+
1743
+ Shared failure reduction rules are also frozen here:
1744
+
1745
+ - `adapter_failure` covers launch, output-capture, automation-closeout,
1746
+ terminal-summary, stale-handoff, and other executor-path failures where the
1747
+ selected harness could not satisfy the shared contract for the requested
1748
+ phase.
1749
+ - `phase_failure` covers requested work that ran inside the shared contract but
1750
+ still failed because the product change, test result, or verification outcome
1751
+ was genuinely unsuccessful.
1752
+ - `human_required` blockers require a true operator action such as account,
1753
+ billing, secret, admin, destructive-operation, or product-decision input.
1754
+ - `repairable_non_human` blockers are runner or adapter failures that should
1755
+ route back through repair or planning instead of being described as success.
1756
+
1757
+ ## Launch Artifacts
1758
+
1759
+ Observed launches reduce into these shared artifacts:
1760
+
1761
+ - `launch.json` records the launch request/spec metadata, redacted command,
1762
+ availability, proof gate, promotion requirements, auth-preflight posture,
1763
+ output capture, delivery metadata, `context_path`, `context_sha256`,
1764
+ `expected_skill_pack`, `skill_bundle_sha256`, `fallback_mode`, and artifact
1765
+ paths without persisting raw prompt or skill-body content. Mixed-run launch
1766
+ records must also preserve `dispatch_decision`, `delegation_request`,
1767
+ `delegation_decision`, and `parent_child` lineage metadata when present.
1768
+ Governed Claude launch metadata must additionally preserve
1769
+ `claude_execution_mode`, `claude_team_policy`, and
1770
+ `phase_team_eligibility`.
1771
+ - `phase-loop` is the neutral operator alias for the same runner implementation
1772
+ as `codex-phase-loop`; documentation and bootstrap may expose both names, but
1773
+ they must point to the same runtime contract.
1774
+ - Live Claude launches must also reduce the non-interactive `claude -p`
1775
+ response back into the shared `automation:` contract. Missing or malformed
1776
+ closeout is a repairable non-human blocker, not silent success. TEAMGOV keeps
1777
+ team creation, task creation, task list, and direct teammate messaging
1778
+ denied by default outside governed native-team mode, and native-team policy
1779
+ metadata must stay distinct from runner-brokered delegation lineage.
1780
+ - Live Gemini launches may use a minimal `--prompt` that points at the
1781
+ run-local `context.md` artifact, must set `--skip-trust` for disposable
1782
+ headless repos, and must reduce `text`, `json`, or `stream-json` output back
1783
+ into the shared `automation:` contract. Installed-skill conflicts stay
1784
+ warning-only when repo-sourced injected context succeeded.
1785
+ - Live OpenCode launches must use `opencode run` with explicit `--dir`,
1786
+ `--agent`, `--model`, `--format json`, and the shared `context.md` artifact.
1787
+ If the selected OpenCode agent posture would otherwise remain permissive, the
1788
+ runner must record that posture explicitly and refuse the launch unless an
1789
+ operator or runner policy intentionally opts in. OpenCode `json` output and
1790
+ closeout-bearing default output must both reduce back into the shared
1791
+ `automation:` contract, and installed-skill conflicts remain warning-only
1792
+ when repo-sourced injected context succeeded.
1793
+ - `.phase-loop/runs/<run-id>/context.md` is the frozen run-local context
1794
+ artifact path when the selected delivery mode requires a file. It must live
1795
+ beside `launch.json` and contain the same repo-sourced workflow bundle the
1796
+ adapter receives: workflow command, action-specific instructions, injected
1797
+ skill bodies, delegation guidance when present, and closeout requirements.
1798
+ - `terminal-summary.json` records the shared child-exit reduction used by
1799
+ closeout, repair, and monitor flows.
1800
+
1801
+ Adapters may add transport-specific metadata under additional keys, but the
1802
+ artifact filenames and their shared contract roles stay frozen.
1803
+
1804
+ ## Operator Maturity Boundaries
1805
+
1806
+ The shared protocol freezes artifact shapes, reentry semantics, delegation
1807
+ metadata, and closeout reduction across supported harnesses. It does not by
1808
+ itself promote an executor to live-supported status. Live support, experimental
1809
+ status, or manual-only posture must come from the current roadmap closeout and
1810
+ capability matrix, while this document stays limited to the common artifact and
1811
+ monitoring contract.
1812
+
1813
+ Operator-facing maturity labels remain distinct from registry promotion
1814
+ statuses:
1815
+
1816
+ - `live-supported` means the current roadmap closeout and disposable proof
1817
+ support autonomous use of that executor.
1818
+ - `proof-blocked` means the executor can still expose launch metadata or manual
1819
+ reentry surfaces, but the latest shared disposable proof did not satisfy the
1820
+ autonomous contract.
1821
+ - `experimental` means the executor contract is intentionally narrower than the
1822
+ first-class live adapters.
1823
+ - `manual-only` means manual import or operator reentry is supported without a
1824
+ current autonomous live-support claim.
1825
+
1826
+ ## Terminal Summary
1827
+
1828
+ `build_terminal_summary(...)` must emit exactly these fields:
1829
+
1830
+ - `terminal_status`
1831
+ - `terminal_blocker`
1832
+ - `verification_status`
1833
+ - `next_action`
1834
+ - `dirty_paths`
1835
+ - `phase_owned_dirty`
1836
+ - `phase_owned_dirty_paths`
1837
+ - `previous_phase_owned_paths`
1838
+ - `unowned_dirty_paths`
1839
+ - `pre_existing_dirty_paths`
1840
+ - `artifact_paths`
1841
+
1842
+ These fields are the shared child-exit reduction contract. Adapters may add
1843
+ transport-specific metadata elsewhere, but they must not rename or omit these
1844
+ summary fields.
1845
+
1846
+ ## Monitor Payloads
1847
+
1848
+ `build_notification_payload(...)` must emit exactly these top-level fields:
1849
+
1850
+ - `timestamp`
1851
+ - `repo`
1852
+ - `roadmap`
1853
+ - `event_kind`
1854
+ - `monitor_status`
1855
+ - `current_phase`
1856
+ - `current_status`
1857
+ - `human_required`
1858
+ - `blocker_class`
1859
+ - `blocker_summary`
1860
+ - `required_human_inputs`
1861
+ - `latest_heartbeat`
1862
+ - `terminal_summary`
1863
+ - `state_path`
1864
+ - `event_path`
1865
+ - `tui_handoff_path`
1866
+ - `run_log_path`
1867
+ - `recommended_action`
1868
+ - `not_run_ratio`
1869
+ - `not_run_count`
1870
+ - `sample_size`
1871
+ - `threshold`
1872
+
1873
+ This payload is the normalized machine callback surface for monitors,
1874
+ notification commands, and external supervisors.
1875
+
1876
+ ## Human-Required Blockers
1877
+
1878
+ The blocker taxonomy is frozen to these literals:
1879
+
1880
+ - `missing_secret`
1881
+ - `account_or_billing_setup`
1882
+ - `admin_approval`
1883
+ - `destructive_operation`
1884
+ - `ambiguous_roadmap_selection`
1885
+ - `product_decision_missing`
1886
+ - `dirty_worktree_conflict`
1887
+ - `branch_sync_conflict`
1888
+ - `sandbox_command_restriction`
1889
+ - `upstream_phase_unmet`
1890
+ - `contract_bug`
1891
+ - `gold_record_amendment`
1892
+ - `stalled_child_observation`
1893
+ - `repeated_verification_failure`
1894
+ - `unretryable_external_outage`
1895
+ - `stuck_loop`
1896
+ - `merge_conflict`
1897
+ - `operator_override_missing_reason`
1898
+ - `concurrent_dispatch`
1899
+ - `review_gate_block`
1900
+ - `docs_freshness_stale`
1901
+ - `consiliency_gate_blocked`
1902
+
1903
+ `consiliency_gate_blocked` is the non-human blocker raised by the CS-0.6
1904
+ `.consiliency/` L0 gates (presence, local-integrity, layout-validity,
1905
+ version-skew) when the opt-in `PHASE_LOOP_CONSILIENCY_GATES=hard` control is
1906
+ set and at least one gate has a finding. The default is `warn`
1907
+ (`PHASE_LOOP_CONSILIENCY_GATES` unset or `warn`): findings are recorded in the
1908
+ closeout's `consiliency_gates`/`consiliency_gates_detail` fields but never
1909
+ block. A repo with no `.consiliency/manifest` is a pure no-op (`skipped`) --
1910
+ the gates only ever act on repos that have opted in. The version-skew gate
1911
+ never escalates past `warn` regardless of this control (the version-skew
1912
+ protocol pins Phase 0 severity to `warn`). It never sets `human_required` —
1913
+ the fix is to resolve the named finding (author the missing doc, repair the
1914
+ manifest, etc.) or drop back to `warn`/`off`.
1915
+
1916
+ `docs_freshness_stale` is the non-human blocker raised by the docs-freshness
1917
+ closeout gate (issue #18) when a **release/package phase** closes `complete`
1918
+ while a public-doc surface (top-level/package `README.md`, `CHANGELOG`,
1919
+ release-notes) still carries an unambiguous stale placeholder
1920
+ (`recovery commit pending`, `TBD`, …). Unlike the `PHASE_LOOP_REVIEW` review
1921
+ gates, it is a hard gate governed by its own `PHASE_LOOP_DOCS_FRESHNESS` control
1922
+ (`hard` default | `warn` | `off`) and so blocks independent of
1923
+ `PHASE_LOOP_REVIEW`; it is **inert for non-release phases** (status `skipped`).
1924
+ The closeout payload always carries `docs_freshness: passed|skipped|blocked`
1925
+ plus a `docs_freshness_detail` evidence record, so a clean worktree alone cannot
1926
+ imply public docs are current. It never sets `human_required` — the fix is to
1927
+ refresh the named doc surface (or add a `<!-- freshness-ok -->` marker for a
1928
+ legitimate use of an otherwise-suspicious token).
1929
+
1930
+ `review_gate_block` is the non-human blocker raised by a pluggable closeout
1931
+ review validator (doc-delta, verification-evidence, visual-evidence, …) when
1932
+ the `PHASE_LOOP_REVIEW` control is dialed to `block`. Review gates default to
1933
+ `PHASE_LOOP_REVIEW=warn` (record a finding to the closeout and continue — the
1934
+ autonomous loop is never stalled), so this blocker appears only under explicit
1935
+ opt-in. It never sets `human_required`; the finding's `code`/`reason` name an
1936
+ agent-recoverable fix (update docs, attach a verification log or screenshot, or
1937
+ record a justified opt-out).
1938
+
1939
+ `stuck_loop` fires when a phase has been ping-ponging in
1940
+ `(action=run, status=executing)` past the runner's iteration cap
1941
+ (default 5, via `--stuck-loop-iterations`) or time ceiling (default
1942
+ 30 minutes, via `--stuck-loop-minutes`) without converging to
1943
+ `complete` or `blocked`. The blocker's `metadata.stuck_loop` payload
1944
+ includes the `trigger` (iteration_cap or time_ceiling), iteration
1945
+ count, elapsed minutes, and first/latest executing-event timestamps.
1946
+ Resolve by either `phase-loop reopen --phase <ALIAS> --reason "..."`
1947
+ (if work isn't actually done and a re-plan is needed) or
1948
+ `phase-loop reconcile --phase <ALIAS> ...` (if the work is complete
1949
+ but the executor failed to report it).
1950
+
1951
+ Before setting `human_required=true`, the runner or skill must record safe,
1952
+ redacted access or environment probes when access is part of the blocker.
1953
+
1954
+ When live launch remains blocked behind metadata-only auth or subscription
1955
+ checks, the default blocker reduction is `account_or_billing_setup` unless a
1956
+ different frozen blocker literal is explicitly justified by the observed probe
1957
+ result.
1958
+
1959
+ ## Lane IR Plan Parser Contract
1960
+
1961
+ Execution-ready phase plans may be reduced into `PhasePlanIR` without launching
1962
+ lane work. The IR is parser-only and records:
1963
+
1964
+ - `PhasePlanIR` metadata, attached `ExecutionPolicyDocument`, dispatch hints,
1965
+ lane records, dependency edges, and typed diagnostics
1966
+ - `PhasePlanLane` identity, display name, owned files, read-only status,
1967
+ `depends_on`, `blocks`, provided interfaces, consumed interfaces, task
1968
+ buckets, verification commands, `parallel_safe`, reducer kind, and
1969
+ lane-specific execution policy
1970
+ - `LaneDependency` edges from producer or prerequisite lanes to dependent lanes
1971
+ - `LaneTaskSet` buckets for `test`, `impl`, `verify`, and `other`
1972
+ - `LaneIRDiagnostic` with `kind`, `message`, optional `lane_id`,
1973
+ `human_required=false`, and `blocker_class=contract_bug`
1974
+
1975
+ The frozen diagnostic kinds are `cycle`, `overlapping_write_ownership`,
1976
+ `unsafe_concurrent_lane`, `missing_producer_dependency`, `missing_owned_files`,
1977
+ `malformed_owned_files`, `malformed_dependencies`,
1978
+ `unsupported_lane_policy`, and `missing_lane_sections`.
1979
+
1980
+ The frozen reducer kinds are `none`, `acceptance_reducer`,
1981
+ `compatibility_reducer`, `verification_reducer`, and `summary_reducer`.
1982
+
1983
+ Malformed lane IR is a repairable non-human blocker. The parser fails closed
1984
+ for dependency cycles, overlapping write ownership, missing producer
1985
+ dependencies, malformed or missing owned-file lines, malformed dependencies,
1986
+ and unsupported lane policy literals. Existing `phase_loop_plan_version: 1`
1987
+ frontmatter remains unchanged. The LANEIR contract does not add scheduler mode,
1988
+ worktree fanout, runner-owned lane launches, reducer launches, or any change to
1989
+ coarse phase execution defaults.
1990
+
1991
+ ## Work-Unit Lifecycle
1992
+
1993
+ UNITRUN adds runner-owned work-unit records behind explicit work-unit mode while
1994
+ leaving coarse phase execution intact. A work-unit identity is frozen as
1995
+ `<phase>.<kind>.<lane-or-reducer-id>.<attempt>` and is represented by typed
1996
+ fields on `WorkUnitIdentity`: `phase`, `kind`, `lane_id`, `attempt`, and
1997
+ `work_unit_id`.
1998
+
1999
+ The frozen work-unit statuses are `pending`, `running`, `complete`, `blocked`,
2000
+ `skipped`, `superseded`, and `awaiting-closeout`. These are intentionally
2001
+ separate from phase statuses such as `planned`, `executing`, and `complete`.
2002
+
2003
+ The state snapshot may include `work_units` and `latest_work_unit`. Each
2004
+ `WorkUnitState` records identity, status, parent phase event, policy, artifact
2005
+ paths, `heartbeat_path`, `terminal_summary_path`, retry lineage, blocker
2006
+ metadata, and timestamps. Malformed work-unit state records are ignored by the
2007
+ work-unit loader rather than breaking legacy phase-level state reads.
2008
+
2009
+ The event ledger may include `event_kind: work_unit` entries. A
2010
+ `WorkUnitEventMetadata` record stores launch metadata, heartbeat path, terminal
2011
+ summary path, closeout summary, retry lineage, blocker metadata, and the current
2012
+ work-unit status. These records coexist with phase-level `LoopEvent` entries and
2013
+ must not cause older phase-level reconciliation to treat work-unit statuses as
2014
+ phase statuses.
2015
+
2016
+ The shared `automation:` closeout contract remains valid at phase level.
2017
+ Work-unit closeout adds optional work-unit fields through `WorkUnitCloseout` and
2018
+ optional `terminal-summary.work_unit` data without changing the required frozen
2019
+ terminal summary fields. A lane work-unit closeout may record `lane_id`,
2020
+ `wave_id`, `worktree_path`, `verification_status`, `changed_paths`, and
2021
+ redacted `evidence_refs`; it must not store raw transcripts, credential
2022
+ payloads, private source bytes, or provider tokens. Work-unit metrics may
2023
+ include `work_unit_metric.v1.work_unit_id`, `work_unit_metric.v1.lane_id`, and
2024
+ `work_unit_metric.v1.wave_id`.
2025
+
2026
+ Resume semantics are work-unit granular: completed units are skipped,
2027
+ non-human blocked or stale running units may be retried with a new attempt,
2028
+ stale attempts are marked `superseded`, and human-required blocked units remain
2029
+ blocked until operator action resolves them. UNITRUN does not schedule
2030
+ dependency waves or isolated worktrees; WAVESCHED owns that later behavior.
2031
+
2032
+ ## Lane Scheduler
2033
+
2034
+ WAVESCHED adds runner-owned lane scheduling behind an explicit scheduler gate.
2035
+ Lane scheduler modes are `off`, `serialized`, and `concurrent`. `off` is the
2036
+ default and preserves coarse phase execution. `serialized` may select one ready
2037
+ lane on the main worktree. `concurrent` may select multiple ready lanes only
2038
+ when writer ownership is disjoint and each writer has a `git_worktree`
2039
+ assignment.
2040
+
2041
+ `LaneWave` records `wave_id`, ordered `lane_ids`, scheduler `mode`, and
2042
+ optional `LaneWorktreeAssignment` records. `LaneWaveDecision` records status
2043
+ `ready`, `blocked`, or `empty`, plus pending, completed, blocked lane IDs, and
2044
+ typed diagnostics. `LaneWorktreeAssignment` records `lane_id`,
2045
+ `worktree_path`, `isolation_mode`, optional branch, and optional `base_sha`.
2046
+ Worktree isolation modes are `main_worktree` and `git_worktree`.
2047
+
2048
+ The scheduler-owned assignment field list accepted by dotfiles is: lane id
2049
+ (`lane_id`), wave id (`wave_id`), worktree path (`worktree_path`), base SHA
2050
+ (`base_sha`), isolation mode (`isolation_mode`), owned files (`owned_files`),
2051
+ read-only refs (`read_only_refs`), harness route (`harness_route`), model
2052
+ (`model`), effort (`effort`), and fallback reason (`fallback_reason`).
2053
+ Governed Pipeline owns scheduling, runtime ledger, worktree assignment, and
2054
+ merge reduction. Greenfield owns deterministic authority contracts, authority
2055
+ refs, and authority digests. Dotfiles consumes governed-pipeline assignments
2056
+ and Greenfield authority refs as inputs and must not promote those inputs into
2057
+ dotfiles-owned runtime authority.
2058
+
2059
+ Worktree placement uses `<WORKTREE-PATH-REDACTED>
2060
+ when `/mnt/workspace` exists; otherwise it uses default sibling placement next
2061
+ to the repo. The runner must halt between lane launches when a stop file is
2062
+ present and must not rewrite in-flight heartbeat, terminal-summary, metric, or
2063
+ closeout artifacts.
2064
+
2065
+ Concurrent scheduling is available only for scheduler-approved
2066
+ `parallel_safe` non-reducer writer waves with disjoint writable ownership and
2067
+ isolated `git_worktree` assignments. Serialized scheduling and coarse phase
2068
+ execution remain compatibility paths and do not require scheduler-owned
2069
+ worktree assignments. Unsafe concurrent selection must fail closed with typed
2070
+ diagnostics for overlapping owned files, missing worktree assignment, stale
2071
+ base SHA, active work unit, human-required blocked work unit,
2072
+ reducer-in-wave, stop-file interruption, and malformed closeout.
2073
+
2074
+ Dirty-path reduction uses `DirtyPathClassification` with classifications
2075
+ `pre_existing`, `lane_owned`, `peer_owned`, `reducer_owned`, and `unowned`.
2076
+
2077
+ Ignored, private, raw-data, credential, and evidence-source files are
2078
+ read-protected by default. Child executors may read those paths only when the
2079
+ run-local context, phase plan, or Pipeline source bundle explicitly allowlists
2080
+ the exact path or glob as a read input. Owned output paths do not imply
2081
+ permission to read ignored or private source inputs, and old memory or prior
2082
+ phase behavior is not an allowlist.
2083
+
2084
+ Before any phase or work-unit closeout claims success, the child executor must
2085
+ audit `git status --short` against the active owned-file contract. Dirty paths
2086
+ must be classified as phase/lane-owned, planning/control, pre-existing
2087
+ unrelated, or unowned. Ignored phase-owned outputs may be preserved only when
2088
+ the plan/source bundle includes an explicit allowlist or staging policy;
2089
+ otherwise the executor must report a repairable `dirty_worktree_conflict`.
2090
+
2091
+ File-rename pairs in the dirty tree — both the `R src -> dst` form that git
2092
+ emits when both sides are staged, and the unpaired `D src` + `?? dst` form
2093
+ that arises when an executor performs a filesystem-only `mv` — are detected by
2094
+ exact blob-hash equality between the deleted HEAD blob and the untracked
2095
+ working-tree file. When the destination is phase-owned, the source path is
2096
+ promoted to `phase_owned_dirty` automatically so a "sensible refactor
2097
+ instinct" move does not fire as `unowned_dirty_paths`. Detection is exact,
2098
+ not similarity-based: a move that also rewrites content is not paired, and
2099
+ must be declared in the plan's owned-file contract explicitly.
2100
+
2101
+ ## Parallel Plan Safety
2102
+
2103
+ Planning turns that run concurrently for the same roadmap may leave sibling
2104
+ phase plan documents in the worktree. A dirty path is expected sibling planning
2105
+ dirt only when it is a repo-relative `plans/phase-plan-<roadmap-version>-<alias>.md`
2106
+ artifact, `<alias>` exists in the selected roadmap, `<alias>` is not the current
2107
+ phase, and `<roadmap-version>` matches the selected roadmap filename.
2108
+
2109
+ Runner dirty-path classification must report those paths as
2110
+ `expected_sibling_dirty_paths` with `expected_sibling_dirty: true`, and must not
2111
+ also include them in `unowned_dirty_paths` or let them cause a
2112
+ `dirty_worktree_conflict`. Current-phase plan docs, foreign roadmap versions,
2113
+ alternate directories, absolute paths, path traversal, and non-plan dirt remain
2114
+ governed by the ordinary owned-file and control-path rules.
2115
+
2116
+ ## Harness Lane Workflows
2117
+
2118
+ HARNESSLANE adds a runner-owned lane assignment payload for harness-specific
2119
+ work units while preserving the coarse phase execution path. The frozen payload
2120
+ is `HarnessLaneAssignment` with `schema=harness_lane_assignment.v1` semantics:
2121
+
2122
+ - `phase`
2123
+ - `lane_id`
2124
+ - `work_unit_kind`
2125
+ - `prompt_kind`
2126
+ - `wave_id`
2127
+ - `owned_files`
2128
+ - `read_only_refs`
2129
+ - `consumed_interfaces`
2130
+ - `depends_on`
2131
+ - `execution_policy`
2132
+ - `worktree_assignment`
2133
+ - `harness_route`
2134
+ - `model`
2135
+ - `effort`
2136
+ - `fallback_reason`
2137
+ - `closeout_schema_required`
2138
+ - `reducer_kind`
2139
+ - `metadata`
2140
+
2141
+ The frozen `HarnessWorkUnitPromptKind` literals are `implementation`, `review`,
2142
+ `reducer`, `verify`, and `closeout`. Implementation prompts can edit only the
2143
+ assigned `owned_files`. Review prompts must not make production edits. Reducer
2144
+ prompts may summarize producer results and closeout state, but must not claim
2145
+ producer write ownership.
2146
+
2147
+ `build_lane_prompt_bundle`, `render_harness_lane_context`,
2148
+ `render_harness_review_context`, and `render_harness_reducer_context` render a
2149
+ single selected work unit for Codex, Claude, Gemini, OpenCode, or command
2150
+ adapters. Each bundle carries the selected lane id, owned files, consumed
2151
+ interfaces, work-unit kind, execution-policy summary, worktree assignment
2152
+ metadata, and the required shared `automation:` closeout fields. These bundles
2153
+ do not grant whole-phase implementation authority.
2154
+
2155
+ `LaunchSpec.harness_lane_assignment` and `launch.json.harness_lane_assignment`
2156
+ freeze lane metadata for live launches. `launch.json.lane_id` and
2157
+ `launch.json.work_unit_kind` are convenience copies for metrics and terminal
2158
+ summaries. Claude `subagent` and `agent_team` launches remain gated by
2159
+ team-safe lane policy, runner work-unit state, delegation broker depth/fanout
2160
+ limits, and owned-file boundaries. Gemini lane launches use built-in routing
2161
+ aliases such as `pro` and `auto` by default; explicit run-local aliases such as
2162
+ `phase-loop-execute-medium` and `phase-loop-review-high` remain opt-in proof
2163
+ surfaces and fail closed through the unsupported-policy diagnostic path when
2164
+ misspelled. OpenCode lane launches record explicit `opencode run`,
2165
+ `--dir`, `--agent`, provider-qualified `--model`, optional `--variant`,
2166
+ `--format json`, and permission posture metadata. Command adapter lane launches
2167
+ require an explicit template that includes `{context_file}`.
2168
+
2169
+ ## Closeout Modes
2170
+
2171
+ Closeout policy is frozen to:
2172
+
2173
+ - `manual`
2174
+ - `commit`
2175
+ - `push`
2176
+
2177
+ `manual` preserves verified phase-owned dirty output without mutating git
2178
+ state. `commit` may preserve only trusted phase-owned dirty paths in a local
2179
+ commit. `push` may build on that local commit only when branch/base topology is
2180
+ safe and explicit.
2181
+
2182
+ ## Roadmap Amendments
2183
+
2184
+ Execution discoveries that change downstream scope must amend the nearest
2185
+ downstream roadmap phase that is not already executing. After the amendment:
2186
+
2187
+ - reconcile against the amended roadmap before selecting the next phase
2188
+ - treat older downstream phase plans or handoffs as stale when their roadmap
2189
+ metadata no longer matches
2190
+ - regenerate any affected downstream phase plan through the planner before
2191
+ execution resumes
2192
+ - preserve prior ledger records for audit, but let later trusted events on the
2193
+ amended roadmap suppress stale mismatch noise
2194
+
2195
+ ## Delegation Broker
2196
+
2197
+ Cross-harness delegation remains runner-owned. Child runs may recommend nested
2198
+ work only through a typed delegation request contract. The frozen request shape
2199
+ includes:
2200
+
2201
+ - `request_id`
2202
+ - `product_action`
2203
+ - `target_executor`
2204
+ - `reason`
2205
+ - `owned_files`
2206
+ - `expected_output`
2207
+ - `priority`
2208
+ - optional `review_context`
2209
+ - optional `repair_context`
2210
+ - optional metadata-only `budget`
2211
+
2212
+ Delegation approval is validated in this order:
2213
+
2214
+ 1. active-loop mode
2215
+ 2. ownership boundaries
2216
+ 3. depth limit
2217
+ 4. fanout limit
2218
+ 5. budget metadata
2219
+ 6. dispatch policy
2220
+
2221
+ Denied requests must produce a typed non-human blocked outcome with a clear
2222
+ reason code and summary. Approved requests must reuse the normal runner launch
2223
+ path so launch artifacts record request metadata, resolved executor, observed
2224
+ launch path, and parent-child linkage without changing the frozen top-level
2225
+ monitor payload fields.
2226
+
2227
+ Claude-native subagents and agent teams do not implicitly create runner child
2228
+ runs. They become runner-visible child work only after a typed delegation
2229
+ request is emitted and approved through this broker contract.
2230
+
2231
+ Delegated cross-harness child work is frozen to `execute`, `repair`, and
2232
+ `review`, and approved child executors are limited to `codex` and `claude`.
2233
+ Native Claude team tasks stay internal unless they externalize through the same
2234
+ typed `DelegationRequest` broker path.
2235
+ Top-level executor switching remains an operator or runner decision, not a
2236
+ native-team side effect: choose the top-level harness through normal dispatch
2237
+ selection or `--executor`, then require typed broker approval before any child
2238
+ work crosses harness boundaries.
2239
+
2240
+ The shared mixed-run lineage surface includes:
2241
+
2242
+ - `DispatchDecision.selected_via`
2243
+ - `DispatchDecision.considered_executors`
2244
+ - typed delegation denial `reason_code`
2245
+ - `ParentChildRunMetadata.parent_executor`
2246
+ - `ParentChildRunMetadata.child_executor`
2247
+ - `ParentChildRunMetadata.child_artifact_root`
2248
+ - `ParentChildRunMetadata.child_worktree_root`
2249
+ - `ParentChildRunMetadata.child_closeout_result`
2250
+
2251
+ ## Manual Advancement and Import
2252
+
2253
+ Manual TUI runs remain valid when they emit the same shared artifact contract
2254
+ as automated runs. Shared manual rules:
2255
+
2256
+ - append a `manual` source event when phase-loop state already exists
2257
+ - keep `automation.*` aligned with the human-readable handoff
2258
+ - use `manual_repair` with `clears_blocker=true` only when a repair actually
2259
+ clears the recorded blocker
2260
+ - autonomous child executors should not edit `.phase-loop/` or claim durable
2261
+ ledger mutation unless the injected launch explicitly permits and confirms
2262
+ it; the parent runner owns blocker clearing and closeout reconciliation from
2263
+ valid shared automation closeouts
2264
+ - when a manual run amended the roadmap downstream, compute
2265
+ `roadmap_sha256` and `phase_sha256` from the amended roadmap for the current
2266
+ phase being closed instead of reusing stale plan hashes
2267
+ - installed bridge skills are recommended for manual takeover and TUI reentry,
2268
+ but repo-injected workflow bundles remain the autonomous source of truth for
2269
+ child launches
2270
+
2271
+ ## Runtime Path
2272
+
2273
+ For this roadmap family, `.phase-loop/` is the canonical durable runtime path
2274
+ for state, ledger, handoff, and observed-run artifacts. Existing
2275
+ `.codex/phase-loop/` artifacts remain a legacy read fallback so active repos are
2276
+ not stranded during migration. New writes use `.phase-loop/`.
2277
+ Legacy `.codex/phase-loop/` is never a new write target for closeout,
2278
+ delegation, source-bundle, or runner evidence artifacts.
2279
+
2280
+ `phase-loop init [--repo <path>] [--dry-run]` initializes the repo-local
2281
+ handoff storage expected by workflow skills. It idempotently adds
2282
+ `/.dev-skills/` to the target repo `.gitignore` without duplication and creates
2283
+ `.dev-skills/handoffs/` unless `--dry-run` is set.
2284
+
2285
+ ## Runtime Boundary
2286
+
2287
+ The phase-loop runtime is isolated as a coherent internal package within the
2288
+ dotfiles repository. It provides a stable CLI and Python API boundary for
2289
+ external tools and repositories.
2290
+ The authoritative boundary document is `docs/phase-loop/runtime-boundary.md`.
2291
+ The dotfiles-specific substrate path inventory is
2292
+ `docs/phase-loop/harness-substrate-manifest.md`; it is intentionally separate
2293
+ from this schema contract.
2294
+
2295
+ Key boundary properties:
2296
+ - The primary CLI entrypoint is `phase-loop` (aliased as `codex-phase-loop`).
2297
+ - The CLI supports a `version` command and `--version` flag for contract reporting.
2298
+ - Public Python modules are exposed under the `phase_loop_runtime.*` namespace.
2299
+ - Internal modules not explicitly listed in `__all__` or the boundary document
2300
+ are considered private and subject to change.
2301
+ - Durable state and events are stored under `.phase-loop/` (legacy fallback
2302
+ `.codex/phase-loop/`).
2303
+ - New writes use `.phase-loop/`.
2304
+
2305
+ ## Extraction Readiness
2306
+
2307
+ The protocol and runtime boundary are considered "Extraction Ready" when:
2308
+ 1. All public CLI commands are documented and stable.
2309
+ 2. All machine-readable artifacts follow frozen schemas.
2310
+ 3. Cross-repo compatibility fixtures exist for downstream validation.
2311
+ 4. The migration path does not break existing `dotfiles` installation.
2312
+
2313
+ This roadmap family confirms that phase-loop has met these criteria.
2314
+
2315
+ ## Cross-Repo Compatibility Fixtures
2316
+
2317
+ To ensure stable integration with downstream repositories such as `governed-pipeline`, this repository provides a set of canonical fixtures that demonstrate various phase-loop outcomes.
2318
+
2319
+ ### Fixture Location
2320
+
2321
+ Stable fixtures are located under:
2322
+ `vendor/phase-loop-runtime/tests/fixtures/phase_loop_pipeline_bridge/`
2323
+
2324
+ These fixtures are dotfiles-owned substrate. The governed-pipeline mirror
2325
+ location is `packages/pipeline-runtime/test/fixtures/phase-loop-bridge/`, and
2326
+ mirror updates, closeout ingest, canonical refresh, replan, and preflight block
2327
+ decisions are governed-pipeline-owned work. Dotfiles must not edit the
2328
+ governed-pipeline mirror path from this repository.
2329
+ Fixture and closeout examples remain metadata-only: they do not authorize raw
2330
+ evidence reads, credential-bearing payloads, provider-supplied payloads, local environment
2331
+ values, `.pipeline/**` writes, Portal writes, Greenfield writes, or broad
2332
+ dotfiles root dependencies.
2333
+
2334
+ ### Available Scenarios
2335
+
2336
+ - `complete.json`: A standard successful phase execution.
2337
+ - `blocked.json`: A phase blocked by a non-human restriction (e.g., sandbox).
2338
+ - `stale_input.json`: A phase with mismatched plan or roadmap hashes.
2339
+ - `failed_verification.json`: A phase where verification failed.
2340
+ - `human_required.json`: A phase requiring operator intervention (e.g., billing).
2341
+ - `dfbundlecloseout_standalone.json`: A standalone closeout without
2342
+ Pipeline-only source bundle path or hash identity.
2343
+ - `dfdriftsignal_canonical_refresh_recommended.json`: Advisory changed-path
2344
+ metadata recommending governed-pipeline canonical refresh.
2345
+ - `dfbundlecloseout_malformed_*.json`: Negative fixtures for malformed
2346
+ closeout, missing bundle hash, deprecated root aliases, invalid nested
2347
+ objects, invalid terminal status, and forbidden metadata.
2348
+ - `dfadoptbridge_adoption_complete.json`: Adoption bridge fixture with active
2349
+ canonical spec, managed root mirror, mirror manifest, archive manifest,
2350
+ source-bundle identity, protected-source roles, and SHA-256 evidence refs.
2351
+ - `dfadoptbridge_stale_mirror_manifest.json`: Adoption bridge fixture for
2352
+ stale mirror manifest metadata and governed-pipeline-owned refresh handling.
2353
+ - `dfadoptbridge_unmanaged_spec_input.json`: Adoption bridge fixture for
2354
+ unmanaged root `specs/**` intake as metadata-only evidence.
2355
+
2356
+ Downstream consumers should use these fixtures to verify their ingestion logic against the `phase_loop_closeout.v1` schema.
2357
+
2358
+ DFADOPTBRIDGE fixtures extend the adoption bridge matrix for governed-pipeline
2359
+ v11. They cover adoption complete, blocked adoption metadata, stale source
2360
+ bundle, stale mirror manifest, unmanaged spec input, archive manifest touched,
2361
+ standalone non-adoption, deprecated root aliases, and redaction rejection.
2362
+ Pipeline-required fixtures use `source_bundle.path`, `source_bundle.sha256`,
2363
+ `source_bundle.phase_id`, protected-source `path`, protected-source `sha256`,
2364
+ protected-source `role`, plan `sha256`, changed-path categories, advisory
2365
+ reason codes, and evidence-ref `sha256` metadata. They do not grant dotfiles
2366
+ permission to write governed-pipeline mirror paths, `.pipeline/**`, Portal
2367
+ contracts, Greenfield authority files, raw-evidence, raw data, credentials, or
2368
+ legacy `.codex/phase-loop/**` state.
2369
+
2370
+ DFADOPTSOAK is the dotfiles adoption integration release gate. It proves root
2371
+ `specs/**` standalone advisory hints, explicit non-default spec-root intake
2372
+ metadata, unconfigured non-default root non-advisory behavior,
2373
+ pipeline-required source-bundle echo, stale-input blockers, v14 bridge fixture
2374
+ retention, DFADOPTBRIDGE fixture parity, and unittest-first verification. It
2375
+ consumes governed-pipeline `CADOPTSOAK` only as read-only metadata evidence;
2376
+ governed-pipeline retains ownership of mirror copies, adoption decisions,
2377
+ archive creation, managed mirror refresh, source-truth reconciliation,
2378
+ source-bundle emission, canonical refresh, replan, and preflight block
2379
+ decisions.
2380
+
2381
+ DFFAKESMOKE adds a metadata-only local substrate receipt at
2382
+ `docs/phase-loop/dffakesmoke-substrate-receipt.md` and a fake-smoke matrix at
2383
+ `vendor/phase-loop-runtime/tests/fixtures/phase_loop_fake_smoke/matrix.json`. The receipt fields are
2384
+ `phase`, `roadmap_sha256`, `plan_path`, `fake_fixture_matrix`,
2385
+ `smoke_commands`, `work_unit_evidence_refs`, `verification_status`,
2386
+ `changed_path_boundaries`, and `redaction_posture`. These fields are
2387
+ downstream evidence refs only; governed-pipeline remains responsible for
2388
+ scheduling, runtime ledger policy, worktree assignment, closeout ingest, and
2389
+ merge reduction.
2390
+
2391
+ DFPROMPTSYNC adds a prompt-safe contract map at
2392
+ `docs/phase-loop/dfpromptsync-contract-map.md`, a readiness receipt at
2393
+ `docs/phase-loop/dfpromptsync-readiness.md`, and a fixture matrix at
2394
+ `vendor/phase-loop-runtime/tests/fixtures/phase_loop_prompt_sync/matrix.json`. Prompt bundles and harness
2395
+ lane workflows may cite schema names, field names, fixture paths, artifact
2396
+ refs, and digests from those receipts, but must not copy raw secrets, raw
2397
+ transcripts, raw patch bodies, raw provider-supplied payloads, credential file contents, local
2398
+ env values, or prompt-only containment claims.
2399
+
2400
+ ## Direct Invocation Policy
2401
+
2402
+ INVOKEOPT adds support for optional direct invocation by external systems (such as Governed Pipeline) while preserving the shared artifact contract as the primary interoperability surface.
2403
+
2404
+ ### Supported CLI Shape
2405
+
2406
+ When direct invocation is enabled, the `phase-loop` CLI exposes a deterministic shape:
2407
+
2408
+ ```bash
2409
+ phase-loop execute <phase> --bundle <path> --output <path> --mode <execute|repair|review>
2410
+ ```
2411
+
2412
+ - `<phase>`: The phase alias to execute.
2413
+ - `--bundle <path>`: Path to a `phase-source-bundle.v1` artifact.
2414
+ - `--output <path>`: Path where exactly one closeout JSON file must be written.
2415
+ - `--mode`: One of `execute`, `repair`, or `review`.
2416
+ Invalid mode literals fail at the direct CLI boundary before child launch.
2417
+
2418
+ ### Rejection Policy
2419
+
2420
+ Unsupported direct invocation must fail closed with a typed diagnostic. The following scenarios result in a `sandbox_command_restriction` or `contract_bug` blocker:
2421
+
2422
+ - Missing or invalid `<phase>`.
2423
+ - Missing `--bundle` when `pipeline_required` mode or the phase plan requires it.
2424
+ - Missing `--output` when direct invocation is attempted.
2425
+ - Invalid or unsupported `--mode`.
2426
+ - Stale `freshness.source_bundle_hash` when it is a SHA-256 digest.
2427
+ - Unknown phase IDs or aliases in the source bundle.
2428
+ - Missing or stale protected source entries.
2429
+ - Malformed `phase_loop_closeout.v1` payloads.
2430
+ - Missing or incompatible runner for the requested phase.
2431
+
2432
+ ### Output Semantics
2433
+
2434
+ Output semantics are deterministic: exactly one closeout JSON file is written
2435
+ to the path specified by `--output` for supported `execute`, `repair`, and
2436
+ `review` invocations, including typed bridge blockers detected before child
2437
+ launch. Standard output may contain human-readable logs, but machine
2438
+ integration must depend only on the output file.
2439
+
2440
+ ## DFPARSOAK Integrated Soak
2441
+
2442
+ DFPARSOAK adds the integrated parallel substrate soak source map at
2443
+ `docs/phase-loop/dfparsoak-source-map.md`, the governed-pipeline-consumable
2444
+ receipt at `docs/phase-loop/dfparsoak-receipt.md`, the operator runbook at
2445
+ `docs/phase-loop/dfparsoak-runbook.md`, and the fixture matrix at
2446
+ `vendor/phase-loop-runtime/tests/fixtures/phase_loop_dfparsoak/matrix.json`.
2447
+
2448
+ The soak closeout records `lane_id`, `wave_id`, `worktree_path`,
2449
+ `isolation_mode`, `base_sha`, `harness_route`, `work_unit_kind`, `model`,
2450
+ `effort`, `policy_source`, `fallback_reason`, verification status, changed
2451
+ paths, and redacted evidence refs. Governed Pipeline still owns scheduling and
2452
+ closeout ingest; Greenfield still owns authority schemas and contract-pack
2453
+ expectations.
2454
+
2455
+ ## Closeout Exceptions (Graduated Ownership Gate)
2456
+
2457
+ Roadmap v40 introduces a **graduated** closeout gate for the case where a phase
2458
+ verifies green (`verification_status == "passed"`) but the executor modified
2459
+ files outside the plan's declared owned-files globs. Instead of an unconditional
2460
+ hard block, beyond-ownership paths are classified and either auto-committed as a
2461
+ recorded soft exception or held for an explicit operator break-glass. This
2462
+ section freezes the shared vocabulary (PROTO); GATE and BREAKGLASS implement the
2463
+ behavior. The frozen constants live in
2464
+ `vendor/phase-loop-runtime/src/phase_loop_runtime/models.py` and are the single
2465
+ source of truth — this prose and the four harness `runtime-state.md` mirrors
2466
+ restate them and must agree.
2467
+
2468
+ ### Sensitivity taxonomy (`SENSITIVITY_CLASSES`)
2469
+
2470
+ Every unowned dirty path is mapped to one sensitivity class:
2471
+
2472
+ - **SAFE** (`SAFE_SENSITIVITY_CLASSES`): `docs`, `plans`, `handoffs`,
2473
+ `config_nonsource`. Low blast radius; may auto-pass as a soft exception when
2474
+ verification passed.
2475
+ - **UNSAFE** (`UNSAFE_SENSITIVITY_CLASSES`): `source`, `ci`, `secrets`,
2476
+ `lockfile`. Always blocks unless the operator supplies an explicit break-glass
2477
+ reason.
2478
+
2479
+ **Deny-by-default rule:** any path that matches **no** SAFE class is treated as
2480
+ UNSAFE. An unmatched/unknown path is never auto-passed. This is the safety
2481
+ substitute for separation of duties in a single-operator fleet: the classifier,
2482
+ not a human second-signature, decides what may auto-commit.
2483
+
2484
+ ### Closeout-exception record (`CloseoutException`)
2485
+
2486
+ A frozen dataclass with exactly these fields:
2487
+
2488
+ | Field | Meaning |
2489
+ |---|---|
2490
+ | `paths` | the beyond-ownership paths this exception covers |
2491
+ | `exception_kind` | a `CLOSEOUT_EXCEPTION_KINDS` member: `soft` or `break_glass` |
2492
+ | `sensitivity_class` | the `SENSITIVITY_CLASSES` member that classified the paths |
2493
+ | `reason` | operator-supplied break-glass reason (`None` for `soft`) |
2494
+ | `verification_status` | always `passed` — unverified work is never excepted |
2495
+
2496
+ - `soft` — all unowned paths were SAFE; the gate auto-commits and records the
2497
+ exception. No blocker.
2498
+ - `break_glass` — unowned paths included an UNSAFE class; commit only under an
2499
+ explicit operator reason. An empty/missing reason is the
2500
+ `operator_override_missing_reason` blocker. A verified-but-unowned UNSAFE path
2501
+ with no override is the `closeout_scope_violation` blocker.
2502
+
2503
+ ### Visibility
2504
+
2505
+ Every exception is recorded in closeout metadata under the
2506
+ `closeout.closeout_exceptions` key (`CLOSEOUT_EXCEPTIONS_METADATA_KEY`) and in
2507
+ the event ledger, with its own tally — never silently folded into a clean-pass
2508
+ count. `soft` and `break_glass` exceptions share the tally, distinguished by
2509
+ `exception_kind`.
2510
+
2511
+ ### Graduated gate (GATE)
2512
+
2513
+ `_perform_phase_closeout` classifies the beyond-ownership dirty remainder via
2514
+ `closeout_classifier.classify_unowned_path(repo_relpath) -> SensitivityVerdict`
2515
+ (`{sensitivity_class, safe}`) and splits it:
2516
+
2517
+ - **all-SAFE remainder** → the SAFE paths join the commit and are recorded as
2518
+ `soft` `CloseoutException`s (one per sensitivity class). No blocker;
2519
+ `verification_status` stays `passed`.
2520
+ - **any-UNSAFE remainder** → the UNSAFE subset blocks with `closeout_scope_violation`
2521
+ (human-required); SAFE paths in a mixed remainder still soft-commit.
2522
+
2523
+ Classifier precedence is load-bearing: UNSAFE-specific patterns (`secrets`,
2524
+ `lockfile`, `ci`) are matched before any broad SAFE rule, then `tests` → `source`
2525
+ (UNSAFE), then the narrow SAFE rules, then deny-by-default → `source`.
2526
+ `config_nonsource` is a tight filename allowlist (not a `.toml`/`.yaml`/`.json`
2527
+ suffix rule), and a bare `.txt` is docs only under `docs/`.
2528
+
2529
+ **`tests` are UNSAFE.** The taxonomy has no `tests` class; test paths only earn
2530
+ owned status via structural sibling matching upstream (`_runtime_relaxation_evidence`,
2531
+ `expanded_dirty_ownership_matches`). A test path reaching the classifier failed that
2532
+ matching, so it maps to `source` (UNSAFE) and is never auto-committed — operator
2533
+ break-glass (BREAKGLASS), not auto-commit, is the escape.
2534
+
2535
+ ### Operator break-glass (BREAKGLASS)
2536
+
2537
+ The escape for the `closeout_scope_violation` block is the
2538
+ `--closeout-allow-unowned <reason>` CLI flag (valid on `run`/`resume`/`dry-run`
2539
+ only; requires a non-empty reason, rejected pre-`run_loop`; requires `--phase`,
2540
+ which bounds the blast radius to a single phase). It threads
2541
+ `allow_unowned_reason` through `run_loop` into `_perform_phase_closeout`, where
2542
+ the UNSAFE remainder is folded into the same closeout commit as `break_glass`
2543
+ `CloseoutException`s carrying the reason — the remainder is emptied, so the
2544
+ post-commit scope block does not fire. No amend, no second commit.
2545
+
2546
+ - **`secrets` are NEVER break-glassable.** A path whose `classify_unowned_path`
2547
+ class is `secrets` (`.env*`, `*.pem`, `secrets/**`) is held back regardless of
2548
+ the reason and still blocks with `closeout_scope_violation` — a one-line reason
2549
+ must not commit a secret into history (irreversible). Break-glass folds only
2550
+ `source`/`ci`/`lockfile`.
2551
+ - **Empty reason.** The CLI rejects a blank reason before `run_loop`. For
2552
+ programmatic callers, an explicitly-empty reason reaching closeout emits the
2553
+ `operator_override_missing_reason` blocker and does NOT commit the unsafe paths.
2554
+ - **Audit / bounded blast radius.** Every break-glass commit records a
2555
+ `break_glass` `CloseoutException` (exact paths + reason, in the shared tally)
2556
+ AND a phase-scoped `closeout_allow_unowned` attestation event in the ledger.
2557
+ The audit always names what was punched through and why.
2558
+
2559
+ ### Reconcile parity (BREAKGLASS SL-2)
2560
+
2561
+ `reconcile` reads the `closeout_allow_unowned` attestation event to lift the
2562
+ `unowned_dirty_paths` bail in `_clean_verified_dirty_closeout_recovery_supersedes_blocker`
2563
+ (the **non-human** verified-dirty-closeout-recovery path) — so a phase whose
2564
+ verified dirty closeout already committed but recorded an unowned remainder
2565
+ recovers under recorded operator attestation. This is *parity*, not the primary
2566
+ escape: a live human-required `closeout_scope_violation` is broken through by the
2567
+ `--closeout-allow-unowned` rerun (which commits), not by reconcile. Attestation
2568
+ matching mirrors `_lane_ir_override`: `roadmap_sha256` + `phase_sha256` (content
2569
+ freshness) + phase + non-empty reason. A **stale** attestation (content drifted
2570
+ since it was written) no longer matches and does not authorize a later closeout;
2571
+ a recorded `secrets`-class remainder never recovers (the clean-worktree guard is
2572
+ the real backstop — SL-1 never commits a secret, so the worktree stays dirty).
2573
+
2574
+ ## Cross-Repo Release Train (#29)
2575
+
2576
+ The cross-repo release-train coordinator (`train_runner.run_train`) orchestrates
2577
+ multi-repo changes in a single atomic train: draft PRs open across all nodes in
2578
+ topo order, a train-level governed review gates the full diff, then nodes merge
2579
+ sequentially with downstream re-verification against the upstream MERGED SHA
2580
+ before each downstream merge.
2581
+
2582
+ This section documents the coordinator's ledger shape, the merge-SHA-pinned
2583
+ cross-repo gate, and the merge/re-verify invariants that are enforced
2584
+ structurally (not just by convention).
2585
+
2586
+ ### Train Ledger Shape
2587
+
2588
+ The train coordinator writes a side-car JSONL ledger at a caller-supplied path
2589
+ (never inside any repo's `.phase-loop/`). Each line is a `LedgerRecord`:
2590
+
2591
+ ```json
2592
+ {
2593
+ "node_id": "<repo>/<plan-path>",
2594
+ "status": "pending|running|pr_open|approved|merged|blocked",
2595
+ "branch": "<feature-branch-name>",
2596
+ "pr_url": "<draft-PR-url>",
2597
+ "head_sha": "<draft-PR-head-commit-SHA>",
2598
+ "upstream_merge_sha": "<merge-commit-SHA-from-upstream-merge>",
2599
+ "merge_order": 0
2600
+ }
2601
+ ```
2602
+
2603
+ Fields:
2604
+
2605
+ - `node_id`: `"<repo>/<plan-path>"` — identifies the train node.
2606
+ - `status`: one of `pending`, `running`, `pr_open`, `approved`, `merged`,
2607
+ `blocked`.
2608
+ - `branch`: the feature branch opened by P3 for this node.
2609
+ - `pr_url`: the GitHub PR URL opened by P3.
2610
+ - `head_sha`: the commit SHA at the HEAD of the draft PR when it was opened
2611
+ (the *draft pin*).
2612
+ - `upstream_merge_sha`: the merge-commit SHA of this node's PR (populated
2613
+ after the P4 merge step). For root nodes (no upstream dependency) this is
2614
+ the node's own merge SHA. For downstream nodes this field is also written
2615
+ when a dependent upstream's merged SHA is required.
2616
+ - `merge_order`: zero-based integer indicating the sequential merge position
2617
+ in the topo-sorted order.
2618
+
2619
+ A synthetic `_train_review_` record (`node_id="_train_review_"`) is appended
2620
+ when the train-level review panel approves. Its `status` is `"approved"`.
2621
+
2622
+ Idempotent resume: re-running `run_train` reads the ledger to skip nodes that
2623
+ are already `pr_open` (confirmed via a live `_pr_is_open` check) or already
2624
+ `merged`. A `blocked` node in the ledger is retried on resume.
2625
+
2626
+ ### Merge-SHA-Pinned Cross-Repo Gate (False-Green Killer)
2627
+
2628
+ Before merging each downstream node, the coordinator:
2629
+
2630
+ 1. Calls `set_upstream_ref(workspace, channel, upstream_merged_sha)` to
2631
+ re-resolve the downstream workspace's channel to the upstream **MERGED SHA**
2632
+ (not the draft SHA used during P3).
2633
+ 2. Calls `reverify_fn(workspace, roadmap_path, run_mode)` to re-run the
2634
+ downstream's per-repo `run_loop` in verification mode.
2635
+ 3. **Only** if reverify passes does `merge_pr_fn(workspace, branch)` execute
2636
+ for the downstream.
2637
+
2638
+ Step 1 **must precede** step 2. This ordering is structural (not advisory):
2639
+ the `set_upstream_ref` call is in the call log before the `reverify` call, as
2640
+ asserted by `tests/test_train_invariants.py::TestInvariant2FalseGreenKiller`.
2641
+
2642
+ A downstream that was green only against the draft ref (step 1 skipped) would
2643
+ produce a false-green verdict — the entire reason for the merged-SHA-pinned
2644
+ gate.
2645
+
2646
+ ### Merge and Re-Verify Invariants
2647
+
2648
+ The following invariants are enforced by the coordinator and asserted in the
2649
+ test suite (`tests/test_train_invariants.py`):
2650
+
2651
+ **INV-1 — No merge before approval.** `merge_pr_fn` is never called until the
2652
+ train-level review panel approves. A rejection returns
2653
+ `status="review_halted"` with `terminal_blocker.human_required=False`; no node
2654
+ is merged.
2655
+
2656
+ **INV-2 — Merged SHA injected before re-verify (false-green killer).** For
2657
+ every downstream node, `set_upstream_ref` is called with the upstream MERGED
2658
+ SHA before `reverify_fn`. The ref passed to `set_upstream_ref` must equal the
2659
+ upstream merge-commit SHA, not the draft SHA. The ordering is asserted via a
2660
+ shared call log.
2661
+
2662
+ **INV-3 — Preflight failure opens zero PRs.** `run_train` runs preflight on
2663
+ all nodes before entering the per-node loop. Any preflight error causes an
2664
+ immediate return with `status="preflight_failed"` and zero `publish_from_worktree`
2665
+ calls.
2666
+
2667
+ **INV-4 — Train state off `.phase-loop/`.** The ledger path must not be inside
2668
+ any repo's `.phase-loop/`. `append_record` raises `ValueError` on violation.
2669
+
2670
+ **INV-5 — Autonomous mode stops at `drafts_open`.** Cross-repo merges are
2671
+ never auto-merged. With `run_mode="autonomous"` and `_merge_phase_enabled=True`
2672
+ the coordinator returns `status="drafts_open"` without calling `merge_pr_fn` or
2673
+ the review panel.
2674
+
2675
+ **INV-6 — `_live_reverify` reads real `StateSnapshot` failure signals.** The
2676
+ live-default `_live_reverify` returns False on `closeout_terminal_status` in
2677
+ `{"blocked","stale_input","failed_verification","human_required"}`, on
2678
+ `human_required=True`, and on `blocker_class is not None`.
2679
+ `closeout_terminal_status=None` is NOT a failure (verify mode may leave this
2680
+ field absent on a clean run).
2681
+
2682
+ **INV-6a — `run_loop` failure-signal contract (load-bearing for the false-green
2683
+ killer).** Because INV-6 treats `closeout_terminal_status=None` +
2684
+ `human_required=False` + `blocker_class=None` as a *pass*, the false-green killer
2685
+ is only sound if `run_loop` **always** surfaces a real verification failure
2686
+ through at least one of those three signals — i.e. it must NEVER return a
2687
+ "silent-failure" snapshot (all three clear) for a genuine failure. The runtime
2688
+ upholds this today: failure paths set `blocker_class` (defaulting to
2689
+ `contract_bug`), a non-`complete` `closeout_terminal_status`
2690
+ (`failed_verification`/`blocked`/`stale_input`), or `human_required=True`. Any
2691
+ future change to `run_loop` that can return a clean-looking snapshot on a real
2692
+ failure would silently defeat the downstream re-verify gate and MUST instead
2693
+ raise or set a failure signal. (INV-6's consumer-side tests pin `_live_reverify`;
2694
+ this producer-side contract is the assumption they rest on.)
2695
+
2696
+ **Forward-only guard.** A downstream re-verify failure halts the merge train
2697
+ at that node (`status="merge_halted"`); already-merged upstream nodes stay
2698
+ merged and are never reverted. The remaining nodes are blocked. Resuming with
2699
+ `--governed` after the integration issue is fixed picks up from the first
2700
+ unmerged node.
2701
+
2702
+ ### Documented Limitation: Draft-Time Pin in Downstream PR
2703
+
2704
+ The downstream draft PR (opened during P3) retains the P3-time upstream pin,
2705
+ not the P4 merged-SHA pin. The re-resolution in step 1 of the gate changes
2706
+ only the **local workspace file** used for the re-verify pass; it does not
2707
+ amend the draft PR's content or rebase the feature branch.
2708
+
2709
+ As a result, the merged downstream PR carries the **draft-time pin** (the
2710
+ version of the upstream artifact that was current when the draft PR was opened),
2711
+ not the merge-commit SHA.
2712
+
2713
+ This is safe under **expand/contract** upstream contracts: the upstream artifact
2714
+ must be backward-compatible at the draft-time version, so the downstream
2715
+ integrates correctly even if the upstream has since moved to the merge SHA.
2716
+ Teams should author upstream changes as expand/contract (additive first,
2717
+ backward-compatible) so sequential merges are safe regardless of this pin
2718
+ limitation.
2719
+
2720
+ A future release may address this by amending the downstream draft PR after the
2721
+ upstream merges (requiring an update-existing-PR primitive); that work is
2722
+ deferred.
2723
+
2724
+ ### Cross-Repo Train Roadmap Format
2725
+
2726
+ A train roadmap is a Markdown file consumed by `parse_train_roadmap()`:
2727
+
2728
+ ```markdown
2729
+ # Release Train: <name>
2730
+
2731
+ ## Nodes
2732
+
2733
+ ### Node: <repo> / <plan-path>
2734
+
2735
+ **Depends on:** (none)
2736
+ **Channel:** (none)
2737
+
2738
+ ### Node: <downstream-repo> / <downstream-plan>
2739
+
2740
+ **Depends on:** <repo> / <plan-path>
2741
+ **Channel:** submodule path=<submodule-path>
2742
+ ```
2743
+
2744
+ **Channel descriptors:**
2745
+
2746
+ - `submodule path=<path>`: the downstream uses a git submodule at `<path>`
2747
+ pointing to the upstream repo. The coordinator calls `set_upstream_ref` with
2748
+ the upstream merged SHA to update the submodule pointer before re-verify.
2749
+ - `pin file=<file> key=<yaml-key>`: the downstream reads the upstream SHA from
2750
+ a pinfile at `<file>` under the YAML key `<key>`.
2751
+
2752
+ **Authoring recommendations:**
2753
+
2754
+ - Declare `**Depends on:** (none)` and `**Channel:** (none)` explicitly for
2755
+ root nodes.
2756
+ - Use expand/contract upstream changes so sequential merges are safe even if
2757
+ the downstream PR carries the draft-time pin.
2758
+ - Keep node `<repo>/<plan-path>` identifiers unique within a train.
2759
+ - Topo order is computed automatically; declare dependencies, not order.