okstra 0.205.1 → 0.206.1

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 (233) hide show
  1. package/README.md +2 -2
  2. package/dist/commands/lifecycle/install.mjs +2 -1
  3. package/dist/commands/lifecycle/install.mjs.map +1 -1
  4. package/docs/architecture.md +16 -16
  5. package/docs/cli.md +4 -4
  6. package/docs/contributor-change-matrix.md +3 -2
  7. package/docs/performance-improvement-plan-v2.md +1 -1
  8. package/docs/project-structure-overview.md +45 -21
  9. package/package.json +2 -3
  10. package/runtime/BUILD.json +2 -2
  11. package/runtime/bin/okstra-spawn-followups.py +2 -2
  12. package/runtime/prompts/launch.template.md +1 -1
  13. package/runtime/prompts/lead/context-loader.md +1 -1
  14. package/runtime/prompts/lead/convergence.md +3 -3
  15. package/runtime/prompts/lead/okstra-lead-contract.md +14 -55
  16. package/runtime/prompts/lead/phase-routing.md +82 -0
  17. package/runtime/prompts/lead/report-writer.md +2 -2
  18. package/runtime/prompts/lead/team-contract.md +1 -1
  19. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  20. package/runtime/prompts/profiles/_common-contract.md +1 -1
  21. package/runtime/prompts/profiles/_coverage-critic.md +1 -1
  22. package/runtime/prompts/profiles/forbidden-actions.json +0 -94
  23. package/runtime/python/okstra_ctl/agent/prompt_cli/corrections.py +1 -1
  24. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +10 -1
  25. package/runtime/python/okstra_ctl/analysis_inputs.py +0 -39
  26. package/runtime/python/okstra_ctl/analysis_scope.py +31 -0
  27. package/runtime/python/okstra_ctl/asset_roots.py +19 -0
  28. package/runtime/python/okstra_ctl/consumers.py +12 -0
  29. package/runtime/python/okstra_ctl/contract_graph.py +75 -10
  30. package/runtime/python/okstra_ctl/dispatch_state.py +22 -0
  31. package/runtime/python/okstra_ctl/doctor.py +15 -4
  32. package/runtime/python/okstra_ctl/execution_mutation_audit.py +27 -1
  33. package/runtime/python/okstra_ctl/handoff.py +11 -466
  34. package/runtime/python/okstra_ctl/handoff_error.py +5 -0
  35. package/runtime/python/okstra_ctl/implementation_direction.py +9 -485
  36. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +8 -1
  37. package/runtime/python/okstra_ctl/next_phase.py +2 -2
  38. package/runtime/python/okstra_ctl/option_comparison.py +3 -165
  39. package/runtime/python/okstra_ctl/option_votes.py +3 -191
  40. package/runtime/python/okstra_ctl/paths.py +22 -6
  41. package/runtime/python/okstra_ctl/phases/__init__.py +4 -0
  42. package/runtime/python/okstra_ctl/phases/catalog.py +260 -0
  43. package/runtime/python/okstra_ctl/phases/change_impact_analysis/boundary.json +11 -0
  44. package/runtime/python/okstra_ctl/phases/change_impact_analysis/entry.py +39 -0
  45. package/runtime/python/okstra_ctl/{report_html/view_models/change_impact_analysis.py → phases/change_impact_analysis/report.py} +3 -3
  46. package/runtime/python/okstra_ctl/phases/change_impact_analysis/spec.md +26 -0
  47. package/runtime/python/okstra_ctl/phases/change_impact_analysis/validation.py +23 -0
  48. package/runtime/python/okstra_ctl/phases/error_analysis/__init__.py +1 -0
  49. package/runtime/python/okstra_ctl/phases/error_analysis/boundary.json +9 -0
  50. package/runtime/{prompts/profiles/error-analysis.md → python/okstra_ctl/phases/error_analysis/profile.md} +2 -2
  51. package/runtime/python/okstra_ctl/{report_html/view_models/error_analysis.py → phases/error_analysis/report.py} +9 -8
  52. package/runtime/{templates/reports → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis-input.template.md +1 -1
  53. package/{docs/task-process/error-analysis.md → runtime/python/okstra_ctl/phases/error_analysis/spec.md} +22 -7
  54. package/runtime/python/okstra_ctl/phases/error_analysis/validation.py +241 -0
  55. package/runtime/python/okstra_ctl/phases/feature_analysis/__init__.py +1 -0
  56. package/runtime/python/okstra_ctl/phases/feature_analysis/boundary.json +8 -0
  57. package/runtime/python/okstra_ctl/phases/feature_analysis/entry.py +63 -0
  58. package/runtime/python/okstra_ctl/{report_html/view_models/feature_analysis.py → phases/feature_analysis/report.py} +12 -5
  59. package/runtime/python/okstra_ctl/phases/feature_analysis/spec.md +22 -0
  60. package/runtime/python/okstra_ctl/phases/feature_analysis/validation.py +27 -0
  61. package/runtime/python/okstra_ctl/phases/feature_analysis/wizard.py +95 -0
  62. package/runtime/python/okstra_ctl/phases/final_verification/__init__.py +4 -0
  63. package/runtime/python/okstra_ctl/phases/final_verification/boundary.json +8 -0
  64. package/runtime/python/okstra_ctl/phases/final_verification/entry.py +166 -0
  65. package/runtime/{prompts/profiles/final-verification.md → python/okstra_ctl/phases/final_verification/profile.md} +5 -5
  66. package/runtime/python/okstra_ctl/{report_html/view_models/final_verification.py → phases/final_verification/report.py} +12 -3
  67. package/runtime/{templates/reports → python/okstra_ctl/phases/final_verification/report_assets}/final-verification-input.template.md +1 -1
  68. package/{docs/task-process/final-verification.md → runtime/python/okstra_ctl/phases/final_verification/spec.md} +42 -25
  69. package/runtime/python/okstra_ctl/phases/final_verification/target.py +296 -0
  70. package/runtime/python/okstra_ctl/phases/final_verification/validation.py +190 -0
  71. package/runtime/python/okstra_ctl/phases/final_verification/wizard.py +38 -0
  72. package/runtime/python/okstra_ctl/phases/implementation/__init__.py +1 -0
  73. package/runtime/python/okstra_ctl/phases/implementation/boundary.json +17 -0
  74. package/runtime/python/okstra_ctl/{implementation_stage.py → phases/implementation/entry.py} +22 -10
  75. package/runtime/{prompts/host-orchestration/implementation.md → python/okstra_ctl/phases/implementation/host-rules.md} +1 -1
  76. package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-deliverable.md +1 -1
  77. package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-executor.md +4 -3
  78. package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-verifier.md +4 -4
  79. package/runtime/{prompts/profiles/implementation.md → python/okstra_ctl/phases/implementation/profile.md} +5 -5
  80. package/runtime/python/okstra_ctl/{report_html/view_models/implementation.py → phases/implementation/report.py} +3 -3
  81. package/runtime/{templates/reports → python/okstra_ctl/phases/implementation/report_assets}/implementation-input.template.md +1 -1
  82. package/{docs/task-process/implementation.md → runtime/python/okstra_ctl/phases/implementation/spec.md} +20 -8
  83. package/runtime/python/okstra_ctl/phases/implementation/validation.py +205 -0
  84. package/runtime/python/okstra_ctl/phases/implementation/wizard.py +39 -0
  85. package/runtime/python/okstra_ctl/phases/implementation_option_selection/__init__.py +1 -0
  86. package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +80 -0
  87. package/runtime/python/okstra_ctl/phases/implementation_option_selection/boundary.json +10 -0
  88. package/runtime/python/okstra_ctl/phases/implementation_option_selection/comparison.py +168 -0
  89. package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +27 -0
  90. package/runtime/{prompts/profiles/implementation-option-selection.md → python/okstra_ctl/phases/implementation_option_selection/profile.md} +2 -2
  91. package/runtime/python/okstra_ctl/{report_html/view_models/implementation_option_selection.py → phases/implementation_option_selection/report.py} +2 -2
  92. package/{docs/task-process/implementation-option-selection.md → runtime/python/okstra_ctl/phases/implementation_option_selection/spec.md} +20 -7
  93. package/runtime/python/okstra_ctl/{implementation_options.py → phases/implementation_option_selection/validation.py} +3 -3
  94. package/runtime/python/okstra_ctl/phases/implementation_option_selection/votes.py +194 -0
  95. package/runtime/python/okstra_ctl/phases/implementation_planning/__init__.py +1 -0
  96. package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +2345 -0
  97. package/runtime/python/okstra_ctl/phases/implementation_planning/boundary.json +12 -0
  98. package/runtime/python/okstra_ctl/phases/implementation_planning/entry.py +161 -0
  99. package/runtime/python/okstra_ctl/phases/implementation_planning/guidance.py +178 -0
  100. package/runtime/{prompts/lead → python/okstra_ctl/phases/implementation_planning/instructions}/plan-body-verification.md +61 -51
  101. package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +3295 -0
  102. package/runtime/{prompts/profiles/implementation-planning.md → python/okstra_ctl/phases/implementation_planning/profile.md} +74 -25
  103. package/runtime/python/okstra_ctl/phases/implementation_planning/report.py +237 -0
  104. package/runtime/{templates/reports → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning-input.template.md +2 -2
  105. package/{docs/task-process/implementation-planning.md → runtime/python/okstra_ctl/phases/implementation_planning/spec.md} +30 -6
  106. package/runtime/python/okstra_ctl/phases/implementation_planning/validation.py +597 -0
  107. package/runtime/python/okstra_ctl/phases/implementation_planning/wizard.py +166 -0
  108. package/runtime/python/okstra_ctl/phases/improvement_discovery/boundary.json +12 -0
  109. package/runtime/python/okstra_ctl/{improvement_lenses.py → phases/improvement_discovery/lenses.py} +1 -6
  110. package/runtime/{prompts/profiles/improvement-discovery.md → python/okstra_ctl/phases/improvement_discovery/profile.md} +5 -5
  111. package/runtime/python/okstra_ctl/{report_html/view_models/improvement_discovery.py → phases/improvement_discovery/report.py} +3 -3
  112. package/runtime/{templates/reports → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery-input.template.md +1 -2
  113. package/runtime/python/okstra_ctl/phases/improvement_discovery/spec.md +29 -0
  114. package/runtime/{validators/validate_improvement_report.py → python/okstra_ctl/phases/improvement_discovery/validation.py} +5 -14
  115. package/runtime/python/okstra_ctl/phases/project_analysis/__init__.py +1 -0
  116. package/runtime/python/okstra_ctl/phases/project_analysis/boundary.json +8 -0
  117. package/runtime/python/okstra_ctl/phases/project_analysis/entry.py +11 -0
  118. package/runtime/python/okstra_ctl/{report_html/view_models/project_analysis.py → phases/project_analysis/report.py} +3 -3
  119. package/runtime/python/okstra_ctl/phases/project_analysis/spec.md +33 -0
  120. package/runtime/python/okstra_ctl/phases/project_analysis/validation.py +55 -0
  121. package/runtime/python/okstra_ctl/phases/release_handoff/__init__.py +1 -0
  122. package/runtime/python/okstra_ctl/phases/release_handoff/boundary.json +17 -0
  123. package/runtime/python/okstra_ctl/phases/release_handoff/entry.py +147 -0
  124. package/runtime/python/okstra_ctl/phases/release_handoff/operations.py +446 -0
  125. package/runtime/{prompts/profiles/release-handoff.md → python/okstra_ctl/phases/release_handoff/profile.md} +3 -3
  126. package/runtime/python/okstra_ctl/{report_html/view_models/release_handoff.py → phases/release_handoff/report.py} +3 -3
  127. package/runtime/{templates/reports → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff-input.template.md +1 -1
  128. package/{docs/task-process/release-handoff.md → runtime/python/okstra_ctl/phases/release_handoff/spec.md} +22 -9
  129. package/runtime/python/okstra_ctl/phases/release_handoff/wizard.py +84 -0
  130. package/runtime/python/okstra_ctl/phases/requirements_discovery/__init__.py +1 -0
  131. package/runtime/python/okstra_ctl/phases/requirements_discovery/boundary.json +9 -0
  132. package/runtime/{prompts/profiles/requirements-discovery.md → python/okstra_ctl/phases/requirements_discovery/profile.md} +2 -3
  133. package/runtime/python/okstra_ctl/{report_html/view_models/requirements_discovery.py → phases/requirements_discovery/report.py} +3 -3
  134. package/{docs/task-process/requirements-discovery.md → runtime/python/okstra_ctl/phases/requirements_discovery/spec.md} +25 -6
  135. package/runtime/{validators/validate_fanout.py → python/okstra_ctl/phases/requirements_discovery/validation.py} +11 -12
  136. package/runtime/python/okstra_ctl/phases/technical_verification/__init__.py +1 -0
  137. package/runtime/python/okstra_ctl/phases/technical_verification/boundary.json +9 -0
  138. package/runtime/python/okstra_ctl/phases/technical_verification/entry.py +100 -0
  139. package/runtime/{prompts/profiles/technical-verification.md → python/okstra_ctl/phases/technical_verification/profile.md} +1 -1
  140. package/runtime/python/okstra_ctl/{report_html/view_models/technical_verification.py → phases/technical_verification/report.py} +2 -2
  141. package/runtime/python/okstra_ctl/phases/technical_verification/spec.md +37 -0
  142. package/runtime/python/okstra_ctl/phases/technical_verification/validation.py +90 -0
  143. package/runtime/python/okstra_ctl/plan_approval.py +70 -0
  144. package/runtime/python/okstra_ctl/plan_items_cli.py +2 -2130
  145. package/runtime/python/okstra_ctl/profile_show.py +9 -3
  146. package/runtime/python/okstra_ctl/render.py +9 -2
  147. package/runtime/python/okstra_ctl/render_final_report.py +3 -2
  148. package/runtime/python/okstra_ctl/report_assembly.py +14 -93
  149. package/runtime/python/okstra_ctl/report_html/context_links.py +1 -1
  150. package/runtime/python/okstra_ctl/report_html/render.py +3 -2
  151. package/runtime/python/okstra_ctl/report_html/router.py +9 -33
  152. package/runtime/python/okstra_ctl/report_projections.py +1 -36
  153. package/runtime/python/okstra_ctl/report_routing.py +23 -0
  154. package/runtime/python/okstra_ctl/report_synthesis_packet.py +4 -73
  155. package/runtime/python/okstra_ctl/report_template_loader.py +35 -0
  156. package/runtime/python/okstra_ctl/report_validation_identity.py +38 -0
  157. package/runtime/python/okstra_ctl/report_views.py +18 -2
  158. package/runtime/python/okstra_ctl/run.py +113 -503
  159. package/runtime/python/okstra_ctl/stage_map.py +13 -0
  160. package/runtime/python/okstra_ctl/stage_targets.py +9 -286
  161. package/runtime/python/okstra_ctl/technical_verification_facts.py +52 -0
  162. package/runtime/python/okstra_ctl/user_response.py +199 -4
  163. package/runtime/python/okstra_ctl/verification_target.py +1 -1
  164. package/runtime/python/okstra_ctl/wizard/__init__.py +31 -31
  165. package/runtime/python/okstra_ctl/wizard/api.py +18 -0
  166. package/runtime/python/okstra_ctl/wizard/outcome.py +3 -12
  167. package/runtime/python/okstra_ctl/wizard/registry.py +20 -12
  168. package/runtime/python/okstra_ctl/wizard/state.py +6 -2
  169. package/runtime/python/okstra_ctl/wizard/steps_analysis.py +0 -97
  170. package/runtime/python/okstra_ctl/wizard/steps_plan.py +27 -278
  171. package/runtime/python/okstra_ctl/wizard/steps_roles.py +2 -1
  172. package/runtime/python/okstra_ctl/work_categories.py +1 -1
  173. package/runtime/python/okstra_ctl/worker_prompt_contract.py +36 -0
  174. package/runtime/python/okstra_ctl/workflow.py +26 -143
  175. package/runtime/skills/okstra-brief-gen/SKILL.md +3 -3
  176. package/runtime/skills/okstra-run/SKILL.md +1 -1
  177. package/runtime/skills/okstra-user-response/SKILL.md +23 -4
  178. package/runtime/templates/reports/quick-input.template.md +1 -1
  179. package/runtime/templates/reports/task-brief.template.md +1 -1
  180. package/runtime/validators/validate-brief.py +2 -2
  181. package/runtime/validators/validate-run.py +287 -4091
  182. package/runtime/validators/validate_analysis_report.py +14 -126
  183. package/docs/task-process/README.md +0 -82
  184. package/docs/task-process/common-flow.md +0 -173
  185. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +0 -147
  186. package/runtime/python/okstra_ctl/technical_verification.py +0 -195
  187. /package/runtime/{prompts/profiles/change-impact-analysis.json → python/okstra_ctl/phases/change_impact_analysis/profile.json} +0 -0
  188. /package/runtime/{prompts/profiles/change-impact-analysis.md → python/okstra_ctl/phases/change_impact_analysis/profile.md} +0 -0
  189. /package/runtime/{templates/reports → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis-input.template.md +0 -0
  190. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis.template.html +0 -0
  191. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis.template.md +0 -0
  192. /package/runtime/{prompts/profiles/error-analysis.json → python/okstra_ctl/phases/error_analysis/profile.json} +0 -0
  193. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis.template.html +0 -0
  194. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis.template.md +0 -0
  195. /package/runtime/{prompts/profiles/feature-analysis.json → python/okstra_ctl/phases/feature_analysis/profile.json} +0 -0
  196. /package/runtime/{prompts/profiles/feature-analysis.md → python/okstra_ctl/phases/feature_analysis/profile.md} +0 -0
  197. /package/runtime/{templates/reports → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis-input.template.md +0 -0
  198. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis.template.html +0 -0
  199. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis.template.md +0 -0
  200. /package/runtime/{prompts/profiles/final-verification.json → python/okstra_ctl/phases/final_verification/profile.json} +0 -0
  201. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/final_verification/report_assets}/final-verification.template.html +0 -0
  202. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/final_verification/report_assets}/final-verification.template.md +0 -0
  203. /package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-diff-review.md +0 -0
  204. /package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-self-check.md +0 -0
  205. /package/runtime/{prompts/profiles/implementation.json → python/okstra_ctl/phases/implementation/profile.json} +0 -0
  206. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation/report_assets}/implementation.template.html +0 -0
  207. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation/report_assets}/implementation.template.md +0 -0
  208. /package/runtime/{prompts/profiles/implementation-option-selection.json → python/okstra_ctl/phases/implementation_option_selection/profile.json} +0 -0
  209. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation_option_selection/report_assets}/implementation-option-selection.template.html +0 -0
  210. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation_option_selection/report_assets}/implementation-option-selection.template.md +0 -0
  211. /package/runtime/{prompts/host-orchestration/implementation-planning.md → python/okstra_ctl/phases/implementation_planning/host-rules.md} +0 -0
  212. /package/runtime/{prompts/profiles/implementation-planning.json → python/okstra_ctl/phases/implementation_planning/profile.json} +0 -0
  213. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning.template.html +0 -0
  214. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning.template.md +0 -0
  215. /package/runtime/{prompts/profiles/improvement-discovery.json → python/okstra_ctl/phases/improvement_discovery/profile.json} +0 -0
  216. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery.template.html +0 -0
  217. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery.template.md +0 -0
  218. /package/runtime/{prompts/profiles/project-analysis.json → python/okstra_ctl/phases/project_analysis/profile.json} +0 -0
  219. /package/runtime/{prompts/profiles/project-analysis.md → python/okstra_ctl/phases/project_analysis/profile.md} +0 -0
  220. /package/runtime/{templates/reports → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis-input.template.md +0 -0
  221. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis.template.html +0 -0
  222. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis.template.md +0 -0
  223. /package/runtime/{prompts/profiles/release-handoff.json → python/okstra_ctl/phases/release_handoff/profile.json} +0 -0
  224. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff.template.html +0 -0
  225. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff.template.md +0 -0
  226. /package/runtime/python/okstra_ctl/{fanout.py → phases/requirements_discovery/fanout.py} +0 -0
  227. /package/runtime/{prompts/profiles/requirements-discovery.json → python/okstra_ctl/phases/requirements_discovery/profile.json} +0 -0
  228. /package/runtime/{templates/reports → python/okstra_ctl/phases/requirements_discovery/report_assets}/fan-out-unit.template.md +0 -0
  229. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/requirements_discovery/report_assets}/requirements-discovery.template.html +0 -0
  230. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/requirements_discovery/report_assets}/requirements-discovery.template.md +0 -0
  231. /package/runtime/{prompts/profiles/technical-verification.json → python/okstra_ctl/phases/technical_verification/profile.json} +0 -0
  232. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/technical_verification/report_assets}/technical-verification.template.html +0 -0
  233. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/technical_verification/report_assets}/technical-verification.template.md +0 -0
@@ -95,7 +95,7 @@ okstra/
95
95
  - `docs/cli.md`
96
96
  - `docs/container.md`
97
97
  - `docs/performance-improvement-plan-v2.md`
98
- - selected contributor, follow-up, AI, and task-process documentation paths
98
+ - selected contributor and follow-up documentation paths
99
99
  - `README.md`
100
100
 
101
101
  So `runtime/` is not the only npm-published content. It is the install payload copied into `~/.okstra`.
@@ -174,7 +174,7 @@ The Module column below is where the command's behaviour lives — a `src/` modu
174
174
  | `git-reconcile` | `scripts/okstra_ctl/git_reconcile.py` | Reconcile stale stage SHAs after external git history changes |
175
175
  | `stage-close` | `scripts/okstra_ctl/stage_close.py` | Close an already-landed implementation stage as done (commit + conformance evidence required) |
176
176
  | `option-votes` | `scripts/okstra_ctl/option_votes.py` | List the implementation candidates that only lack feasibility votes, and the analyser owing each one |
177
- | `handoff` | `scripts/okstra_ctl/handoff.py` | Stage-group release-handoff eligibility / assemble / record helpers |
177
+ | `handoff` | `scripts/okstra_ctl/handoff.py` | CLI assembly and common verification recording; release policy is in `phases/release_handoff/operations.py` |
178
178
  | `integrate-stages` | `scripts/okstra_ctl/stage_integrate.py` | Merge verified stages into the task worktree and clean stage worktrees |
179
179
  | `task-list`, `task-show` | `scripts/okstra_ctl/task_list_cli.py`, `scripts/okstra_ctl/task_show_cli.py` | Task/run introspection for skills; `task-show` consumes the Python task read-side snapshot |
180
180
  | `resolve-task-key` | `scripts/okstra_ctl/resolve_task_key.py` | Resolve a bare task-id to candidate task-keys from the project catalog |
@@ -240,15 +240,15 @@ Important modules:
240
240
 
241
241
  | Module | Role |
242
242
  |---|---|
243
- | `run.py` | `prepare_task_bundle()` single authority and CLI parser; for final-verification it adapts CLI stage input into `FinalVerificationTargetRequest`, maps the acquired target into render context, and owns `verification-target.md` snapshot/digest materialization before manifests and prompts are rendered |
243
+ | `run.py` | `prepare_task_bundle()` single authority and CLI parser; for final-verification it passes CLI values to `phases/final_verification/`, whose `entry.py` builds the target request, applies the acquired target to render context, and writes the `verification-target.md` snapshot and digest before manifests and prompts are rendered |
244
244
  | `agent/activity.py` | Records activity rows against run-manifest identity, imports validated command evidence from worker audit sidecars, and deterministically projects the current run's `lead-events-*.jsonl` activity rows into `agentActivity[]`. Manifests without `activityContractVersion: 1` are left unchanged. |
245
245
  | `exact_coverage.py` | Shared pure calculator for requirement coverage and scope precision in option selection and selected-direction planning |
246
- | `implementation_options.py` | Option-selection criteria, weighting, candidate fingerprint convergence, ranking, and semantic validation |
246
+ | `phases/implementation_option_selection/validation.py` | Option-selection criteria, weighting, candidate fingerprint convergence, ranking, and semantic validation |
247
247
  | `implementation_direction.py` | Selected report/response validation, direction snapshot materialization, and selected-direction reference validation |
248
- | `technical_verification.py` | Optional `technical-verification` phase backend — resolves and freezes the explicitly classified unresolved facts from the same task's implementation-option-selection report into `state/technical-verification-input-<seq>.json` (`write_technical_verification_input` / `resolve_technical_verification_input`, driven from `run.py`). No selected direction is required and unresolved user decisions still block entry; it links test inputs to observed results and never produces adoption approval or changes candidate feasibility |
249
- | `verification_target.py` | Shared reader for the prepared final-verification target snapshot (`verification-target.md`) written by `run.write_verification_target_snapshot`. One implementation of the digest/scope rule serves both consumers — report assembly (records `verificationScope`) and `validators/validate-run.py` (re-checks the published report against the target) — so the two cannot drift |
250
- | `implementation_stage.py` | `implementation` single-stage run orchestration — read the Stage Lifecycle Snapshot → pick an available Stage Map entry → provision an isolated stage worktree → publish the selected stage as run context (extracted from `run.py`) |
251
- | `stage_targets.py` | Stage readiness/verification policy SSOT — from the Stage Lifecycle Snapshot (`consumers.jsonl` ledger + carry sidecar backfill + active registry reservation) it decides which stage is runnable, which commit it branches from, and what final-verification checks. `acquire_final_verification_target()` acquires the ledger, registry, worktree, Git, and optional whole-task integration facts behind one task-key mutex and returns a typed target without render-context coupling. `order_stage_closure` topologically sorts (Kahn) the dependency closure of the wizard's multi-selected stage set to produce the unattended `chain-stages` chaining order |
248
+ | `phases/technical_verification/entry.py` | Optional `technical-verification` phase backend — resolves and freezes the explicitly classified unresolved facts from the same task's implementation-option-selection report into `state/technical-verification-input-<seq>.json` (`write_technical_verification_input` / `resolve_technical_verification_input`, driven from `run.py`). No selected direction is required and unresolved user decisions still block entry; it links test inputs to observed results and never produces adoption approval or changes candidate feasibility |
249
+ | `verification_target.py` | Shared reader for the prepared final-verification target snapshot (`verification-target.md`) written by `phases/final_verification/entry.py::write_verification_target_snapshot`. One implementation of the digest/scope rule serves both consumers — report assembly (records `verificationScope`) and `validators/validate-run.py` (re-checks the published report against the target) — so the two cannot drift |
250
+ | `phases/implementation/entry.py` | `implementation` single-stage run orchestration — read the Stage Lifecycle Snapshot → pick an available Stage Map entry → provision an isolated stage worktree → publish the selected stage as run context (extracted from `run.py`) |
251
+ | `stage_targets.py` | Stage readiness/verification policy SSOT — from the Stage Lifecycle Snapshot (`consumers.jsonl` ledger + carry sidecar backfill + active registry reservation) it decides which stage is runnable, which commit it branches from, and the Git and ledger facts final-verification builds its target from: whole-task integration, nested stage worktree relocation, the stage that contains every other done stage, and teardown after the verdict. `order_stage_closure` topologically sorts (Kahn) the dependency closure of the wizard's multi-selected stage set to produce the unattended `chain-stages` chaining order |
252
252
  | `stage_fix_carry.py` | fix-run carry derivation for a re-run on an `implementation` stage whose latest final-report data.json carries verifier `FAIL` verdicts — collects the previous report path, previous run HEAD, failed verifiers, carried blocking findings, and a routing recommendation, which `run.py` renders into the analysis profile through the `{{FIX_RUN_CONTEXT}}` token. A first run, or a re-run after `PASS`, yields no carry and renders the token empty |
253
253
  | `stage_reconcile.py` | best-effort git reconciliation shared by the stage prepare flow (delegates to `git_reconcile.auto_reconcile`; advisory — failures are only reported to stderr, the dependency gate stays authoritative) |
254
254
  | `stage_ledger.py` | assembles the Stage Ledger handed to plan authoring — "what is already built" from the carry sidecar's plan, "which stage numbers are used" from the latest plan (ADR-0015 append-only, judged on the latest plan's `max`); it only joins `stage_targets` (status/lifecycle) and `stage_map` (source-of-stage) and serialises, owning no verdict. Carries `sourcePlan`/`latestPlan` and surfaces `planDivergence`; when the ledger cannot be read it emits the reason in plain text under the same heading instead of omitting the block |
@@ -270,7 +270,7 @@ Important modules:
270
270
  | `timeline_runs.py` | read-side overlay of a `history/timeline.json` entry with its run-manifest's current facts (`current_run_facts`) — the entry is a prepare-time snapshot (`status`, `workflowSnapshot`, reserved `reportRecordPath`) and `validate-run` writes the end state to the run-manifest only; a run whose `validation.status` is `not-run` projects no report path. Shared by `recap.py` and the `history-input` / overview projections in `model_io/renderers.py` |
271
271
  | `render.py` | task manifest, run manifest, timeline, task index, discovery, team-state, prompt/template render |
272
272
  | `group_context.py` | task-group context document (`.okstra/briefs/<task-group>/group-context.md`): the skeleton writer behind `okstra group-context init` (template `templates/reports/group-context.template.md`), `validate_group_context` (four required sections, no template placeholder left, directory slug matches the frontmatter `task-group`) that `validators/validate-brief.py` dispatches to on frontmatter `type: group-context`, and the path helpers `run.py` uses to validate the file at preflight and copy it to `instruction-set/task-group-context.md` for the analysis packet's `## Task-Group Context` section |
273
- | `workflow.py` | Phase sequence (`PHASE_SEQUENCE`), per-phase allowed outputs, forbidden actions. It does not decide the next phase — that is `next_phase.py` |
273
+ | `workflow.py` | Common phase sequence (`PHASE_SEQUENCE`) and boundary rendering. Allowed outputs and forbidden actions are read from the selected phase's `boundary.json`; next-phase decisions remain in the lead routing instructions. |
274
274
  | `next_phase.py` | `workflow.nextRecommendedPhase` SSOT — the pointer's shape (`make` / `is_pointer` over `{phase, status, rationale}`, `status` ∈ `ready`/`pending`/`blocked`/`terminal`), the promotion of a legacy string pointer (`promote`), the projection of one report's routing field into a pointer (`project`), and the `ready`-only read the shell and wizard autofill share (`autofill_task_type`). There is no static phase table and no sequence walk: the next phase comes from what the report authored, and nothing else may compute one |
275
275
  | `workers.py`, `models.py` | Worker roster; `models.py` is the model catalog SSOT (`ModelSpec` per alias + `ROLE_DEFAULTS`) — add-a-model single reference point from which picker options, codex pricing, and role defaults all derive |
276
276
  | `worktree/`, `worktree_registry.py` | One worktree per task-key, branch registry, sync dirs/files/snapshots. The package layers the job: `naming` (path/branch strings, no disk), `sync_config` (which paths follow the checkout across), `git_ops` (the git calls), then `cleanliness` (dirty by okstra's definition), `linking` (installs the symlinks), `decisions` (answers what provisioning would do, without doing it), and `provision` — the only layer with side effects |
@@ -322,7 +322,7 @@ Important modules:
322
322
  | `code_review_target.py` | `okstra code-review target` backend — argument validation and JSON shaping only. Stage mode delegates whole to `okstra_project.state.code_review_target_snapshot`; branch mode is resolved here, defaulting the diff base to the merge-base with the default branch (`refs/remotes/origin/HEAD`, else `main`/`master`). Read-only: it never creates the review directory |
323
323
  | `session.py`, `seeding.py`, `locks.py`, `invocation.py`, `sequence.py`, `ids.py`, `material.py` | Supporting lifecycle helpers |
324
324
  | `pane_reclaim.py` | resolves which runs of the current project still hold a non-terminal dispatch, so the `SessionStart(compact)` hook can re-inject the pane-cleanup obligation for them. The signal is the newest `team-state` per run directory, not the central run index — an in-session run never appears there (ADR-0011). Imports the status split from the `dispatch_state.NON_TERMINAL_WORKER_STATUSES` SSOT |
325
- | `improvement_lenses.py` | lens enum SSOT + cap constants for the improvement-discovery phase (DEFAULT 8, ABSOLUTE 12, MIN/MAX PRIORITY 1/4, SOURCE_WORKERS) |
325
+ | `phases/improvement_discovery/lenses.py` | lens enum SSOT + cap constants for the improvement-discovery phase (DEFAULT 8, ABSOLUTE 12, MIN/MAX PRIORITY 1/4, SOURCE_WORKERS) |
326
326
  | `container.py` | the `okstra container` convergence entrypoint of the okstra-container-build public skill — `provision_container_group` + `up`/`status`/`down` dispatch, env-override synthesis, compose argv assembly, and healthcheck polling |
327
327
  | `plan_run_root.py` | shared helper deriving `approved_plan_path` → `plan_run_root` and back-tracing the task-key |
328
328
  | `manager_cli.py` | `okstra manager` Python entrypoint — purpose-specific fixed text by default, machine JSON with `--json` |
@@ -341,7 +341,10 @@ Important modules:
341
341
  | `cmux.py` | cmux-pane worker backend — a worker that gets a pane frees the lead process, and anything that stops a pane opening degrades quietly to the blocking wrapper. Detects a usable cmux session before selecting the backend (CLI resolves + ping answers PONG + the lead's workspace is resolvable), derives placement from the workspace geometry each dispatch, relays lead/worker events to the cmux sidebar, and records the run's terminal backend in the manifest so both phases of a run land on one backend. A sandbox that hides cmux (`PermissionError` on the socket) stops dispatch with the remedy instead of degrading into the same broken fallback; a quit app (`FileNotFoundError`) still degrades |
342
342
  | `codex_dispatch.py` | Compatibility adapter delegating `okstra codex-dispatch` to the provider-neutral `worker_dispatch` path |
343
343
  | `analysis_packet.py` | assembles the compact analysis-worker input packet for a task run from worker-owned profile sections; report/lead procedure stays outside the packet |
344
- | `analysis_inputs.py` | shared input boundary for `project-analysis`, `feature-analysis`, and `change-impact-analysis` — validates evidence-report identity and review status, enforces the type-to-type relation allowlist, computes `exact`/`stale` freshness, and resolves free-text or `PF-NNN` feature targets for both wizard and prepare paths |
344
+ | `analysis_scope.py` | Shared normalized project-relative path and scope-inclusion predicates for analysis evidence and project-map validation |
345
+ | `technical_verification_facts.py` | Shared unresolved technical-fact identities, candidate safety checks and user-decision gate consumed by option selection and technical verification |
346
+ | `handoff.py`, `handoff_verification.py`, `handoff_error.py` | Common verification recording, accepted-evidence reading and handoff error type; release-specific preparation and remote-operation policy live in `phases/release_handoff/` |
347
+ | `analysis_inputs.py` | shared input boundary for `project-analysis`, `feature-analysis`, and `change-impact-analysis` — validates evidence-report identity and review status, enforces the type-to-type relation allowlist, computes `exact`/`stale` freshness, and exposes report candidates to the phase-owned feature-target resolver |
345
348
  | `user_response.py` | parses clarification/approval responses and the analysis-review sidecar; `parse_analysis_review` validates accepted, revision-requested, and rejected decisions plus their affected IDs and reason; `format_show_view` prints why-asked, linked plan items, and cited artifacts for the in-session picker |
346
349
  | `context_cost.py` | read-side context-cost estimator for a prepared okstra task bundle (the `okstra context-cost` backend) |
347
350
  | `schema_excerpt.py` | generates a task-type-scoped excerpt of the final-report schema — a schema reduction to inject into the worker/lead prompt |
@@ -366,7 +369,7 @@ Important modules:
366
369
  | `plan_items.py`, `plan_items_cli.py` | deterministic extraction of the report-writer narrative `P-*` plan-item queue plus the `okstra plan-items extract` / `validate` / `seed` / `collect-verdicts` / `apply-verdicts` / `derivations` adapter; v2 data.json remains a read input |
367
370
  | `claim_reproduction.py` | reproduces a plan-body single-vote `fact` claim before it can block on one vote — runs the declared probe (`path-exists` / `path-absent` / `literal-present` / `literal-absent` / `citations-differ`) inside the resolved project root and returns `reproduced` / `not-reproduced` / `not-runnable`, which `plan-items apply-verdicts --run-manifest` writes into `reproductionResult` (always overwriting the worker-sent value so a verifier cannot score its own claim). A `judgement` claim, or a `fact` that does not reproduce, takes the quorum route |
368
371
  | `plan_derivations.py` | the supersession sweep `_common-contract.md` requires an author to do by hand — extracts the symbols, paths, and ids an answered clarification names and reports every plan string that mentions one. Advisory: it locates candidates and never judges which are now false |
369
- | `scope_provenance.py` | single source of truth for the scope-provenance grammar every phase-emitted requirement must declare, shared by `validators/validate-run.py` and `validators/validate_fanout.py` so the planning report and fan-out packets cannot drift |
372
+ | `scope_provenance.py` | single source of truth for the scope-provenance grammar every phase-emitted requirement must declare, shared by `validators/validate-run.py` and `scripts/okstra_ctl/phases/requirements_discovery/validation.py` so the planning report and fan-out packets cannot drift |
370
373
  | `worker_artifact_paths.py` | canonical worker artifact path derivation (e.g. `audit_sidecar_rel` inserts `-audit-` after the first `-worker-` token), so dispatch and validation agree on non-canonical-path rejection |
371
374
  | `report_translation_dispatch.py` | Phase 7 `translate` step — for a non-English `reportLanguage` and no `*.i18n.<lang>.json` sidecar, reuses or materializes this run's translator reservation (`agent-prompt materialize --audience translator` in-process, instruction file under `state/`), runs the CLI-wrapper dispatch, and succeeds only when the sidecar exists afterwards. Replaces the manual lead sequence that was skipped in practice |
372
375
  | `report_finalize.py` | Phase 7 post-report sequence **SSOT** — runs `translate` → `token-usage` → `render-views` → `spawn-followups` → `validate-run` → `record-group-memory` → `teardown-stages` in that load-bearing order. A non-zero exit still runs every later check through `validate-run` and names the earliest failure; `record-group-memory` (this run's conclusion into the task-group's `group-context.md`, plus `nextInGroup` for the closeout) and `teardown-stages` are skipped when any earlier step failed. Both lead paths converge here: the Codex adapter calls it in-process (`codex_dispatch`), a Claude-led run reaches it through `okstra report-finalize`. Neither reimplements the sequence |
@@ -381,6 +384,9 @@ Important modules:
381
384
  | `task_target.py` | shared helper resolving `task-key → (task_root, project_root)` (`resolve_task_root`) |
382
385
  | `contract_refreeze.py` | re-freezes a running run's frozen contracts in the installed format. A run freezes its duty/role/common contracts at start and pins their digest, so installing a release that changed the contract format leaves that run unable to produce another prompt (`run duty snapshot catalog digest does not match`). Backs `okstra agent-prompt refreeze-contracts`, rewrites the frozen copies and the manifest digest only, and never touches a prompt, result or ledger |
383
386
  | `operation_invocation.py` | prepares an Okstra-owned LLM operation that runs outside `okstra-run` — the operation contract (`agents/operations/<id>.json`) owns the duty and the worker count, and the role is derived from that duty's `roleId`, so a skill passes the operation name instead of assembling role/provider/model itself (ADR-0017). Backs `okstra agent-prompt resolve-operation` |
387
+ | `phases/catalog.py` | Fixed map from the 12 public task types to their phase package names, whether each phase has moved into `phases/<package>/`, and its HTML view builder. Resolves a task type's profile Markdown/JSON and phase-owned report templates inside one asset root (repo checkout, `runtime/python`, or `~/.okstra/lib/python`) and resolves `{{INCLUDE:...}}` targets for a phase `profile.md`. Importing it loads no phase module |
388
+ | `phases/final_verification/` | Final-verification policy and assets: `profile.md`/`profile.json` (recorded under the logical paths `prompts/profiles/final-verification.*`), `spec.md` (process note and guarantees), `entry.py` (verification-target request, snapshot writer, prepare-flag checks), `validation.py` (added-surface, verdict, and scope checks that `validate-run` calls before its routing check), `wizard.py` (whole-task pick condition), `target.py` (`acquire_final_verification_target()`: chooses single-stage, containing-stage, or integrated whole-task target behind one task-key mutex), `report.py` (HTML view builder and `verificationScope` reader), and `report_assets/` (report body templates). Its `tests/` directory is collected by pytest and left out of the runtime copy |
389
+ | `report_template_loader.py` | Jinja loader for report templates: a logical name owned by a migrated phase opens from that phase's `report_assets/`, and every other name opens from `templates/reports` |
384
390
  | `contract_graph.py`, `contract_graph_cli.py` | runtime-contract graph loader + cross-reference/dependency-closure validator and its `okstra contract-check --root <dir> (--profile\|--operation)` CLI boundary. Loads the agent contract schemas (`common`/`role`/`duty`/`profile`/`operation`), validates known role capabilities, and reports the dependency closure with per-file `path`/`schemaVersion`/`sha256`; an invalid contract raises `ContractGraphError` |
385
391
  | `json_boundary.py` | strict JSON persistence boundaries for okstra-owned artifacts — a sealed `ExternalJsonSource` (validated producer + path) is the only way owned JSON is read, and `JsonBoundaryError` names artifact / reason / path when a write cannot satisfy its contract; the SSOT that keeps the model out of internal JSON key/path authorship |
386
392
  | `fixed_text.py` | shared scalar-line format for the model-facing fixed-text projections — `scalar` neutralises complex values and control characters (backticks escaped for the code span `line` wraps it in), `block` projects a prose body outside any code span with its backticks intact, `line` renders one static-labelled Markdown list row, and `value_lines` losslessly flattens a JSON-shaped value into fixed name/order/value rows |
@@ -413,6 +419,27 @@ Important modules:
413
419
 
414
420
  > `i18n.py` (the final-report i18n dictionary loader + Jinja2 lookup) is an intentionally undocumented internal helper — it is a render helper that users and contributors do not need to know about in the canonical docs, so it is excluded from the module map.
415
421
 
422
+ #### Phase packages
423
+
424
+ All 12 task types have a phase package. Each phase owns `profile.md`, `profile.json`, `boundary.json`, `spec.md`, `report.py`, dedicated `report_assets/` and its policy tests. The catalog resolves the existing logical profile and template names to these files. Pytest collects phase-local tests; the runtime copy excludes them. Common report shells, schemas, worker dispatch and evidence readers remain shared.
425
+
426
+ | Task type and specification | Phase-owned policy |
427
+ |---|---|
428
+ | [requirements-discovery](../scripts/okstra_ctl/phases/requirements_discovery/spec.md) | `fanout.py` orders decomposed units; `validation.py` checks their provenance, dependencies and index. |
429
+ | [improvement-discovery](../scripts/okstra_ctl/phases/improvement_discovery/spec.md) | `lenses.py` defines scan lenses and bounds; `validation.py` checks candidate scope, sources and report branches. [Input template](../scripts/okstra_ctl/phases/improvement_discovery/report_assets/improvement-discovery-input.template.md). |
430
+ | [project-analysis](../scripts/okstra_ctl/phases/project_analysis/spec.md) | `entry.py` rejects upstream inputs; `validation.py` checks entry-point scope and component references. [Input template](../scripts/okstra_ctl/phases/project_analysis/report_assets/project-analysis-input.template.md). |
431
+ | [feature-analysis](../scripts/okstra_ctl/phases/feature_analysis/spec.md) | `entry.py` resolves the feature target; `wizard.py` owns target questions; `validation.py` checks the report target against the resolved input. [Input template](../scripts/okstra_ctl/phases/feature_analysis/report_assets/feature-analysis-input.template.md). |
432
+ | [change-impact-analysis](../scripts/okstra_ctl/phases/change_impact_analysis/spec.md) | `entry.py` collects feature evidence; `validation.py` limits planning inputs to constraints and unknowns. [Input template](../scripts/okstra_ctl/phases/change_impact_analysis/report_assets/change-impact-analysis-input.template.md). |
433
+ | [error-analysis](../scripts/okstra_ctl/phases/error_analysis/spec.md) | `validation.py` checks reproduction evidence and cause relationships. [Input template](../scripts/okstra_ctl/phases/error_analysis/report_assets/error-analysis-input.template.md). |
434
+ | [technical-verification](../scripts/okstra_ctl/phases/technical_verification/spec.md) | `entry.py` freezes experiment inputs; `validation.py` checks observed evidence against those inputs. |
435
+ | [implementation-option-selection](../scripts/okstra_ctl/phases/implementation_option_selection/spec.md) | `entry.py`, `validation.py`, `votes.py` and `comparison.py` own direction comparison, feasibility and ranking; `authoring.py` supplies report instructions. |
436
+ | [implementation-planning](../scripts/okstra_ctl/phases/implementation_planning/spec.md) | `entry.py`, `wizard.py`, `validation.py`, `plan_body.py` and `authoring.py` own selected-direction preparation and plan verification; `instructions/` contains plan-body guidance. [Input template](../scripts/okstra_ctl/phases/implementation_planning/report_assets/implementation-planning-input.template.md). |
437
+ | [implementation](../scripts/okstra_ctl/phases/implementation/spec.md) | `entry.py` claims and publishes a stage run; `wizard.py` owns stage selection; `validation.py` checks verifier independence; `instructions/` contains executor and verifier guidance. [Input template](../scripts/okstra_ctl/phases/implementation/report_assets/implementation-input.template.md). |
438
+ | [final-verification](../scripts/okstra_ctl/phases/final_verification/spec.md) | `entry.py`, `target.py`, `wizard.py` and `validation.py` own the prepared verification target, scope and acceptance checks. [Input template](../scripts/okstra_ctl/phases/final_verification/report_assets/final-verification-input.template.md). |
439
+ | [release-handoff](../scripts/okstra_ctl/phases/release_handoff/spec.md) | `entry.py`, `operations.py` and `wizard.py` own handoff input, eligibility, selected remote operations and delivery choices. [Input template](../scripts/okstra_ctl/phases/release_handoff/report_assets/release-handoff-input.template.md). |
440
+
441
+ `prompts/lead/phase-routing.md` owns the lead's continuation decisions. Common `next_phase.py`, release gates, verification evidence, stage state and selected-direction readers consume recorded facts without importing another phase's execution policy. CLI modules such as `option_votes.py`, `option_comparison.py` and `handoff.py` remain assembly entrypoints.
442
+
416
443
  ### 4.4 `scripts/okstra_project/`
417
444
 
418
445
  Project resolver and read-only state helpers:
@@ -439,9 +466,7 @@ Token/cost accounting:
439
466
  | `launch.template.md` | Lead prompt template rendered for each run |
440
467
  | `duties/<duty>.json` | Canonical functional duty contracts composed into every Okstra-owned LLM invocation, alongside the common contract at `agents/common.json` and the role contract at `agents/roles/<role>.json`; `direction-selection-worker` owns direction comparison/validation while `planning-worker` realizes the selected direction; provider/model identity does not select the duty |
441
468
  | `profiles/_common-contract.md` | Shared phase contract |
442
- | `profiles/<task-type>.md` | Phase profiles (single language — runtime always loads from `profiles/`, never a translated mirror) |
443
- | `implementation-option-selection.md` | Read-only lifecycle profile for candidate comparison or preselected-direction validation before detailed planning |
444
- | `project-analysis.md`, `feature-analysis.md`, `change-impact-analysis.md` | Read-only sidetrack profiles for project mapping, one-feature behavior tracing, and proposed-change impact mapping |
469
+ | `profiles/_*.md` | Shared include fragments; task profiles are canonical under `scripts/okstra_ctl/phases/<package>/profile.md`, never a translated mirror |
445
470
  | `wizard/prompts.ko.json` | Korean wizard prompt single source of truth |
446
471
 
447
472
  ### 4.7 `templates/`
@@ -450,11 +475,11 @@ Token/cost accounting:
450
475
  |---|---|
451
476
  | `templates/reports/final-report-v2.template.md` | Full reading copy Markdown spine |
452
477
  | `templates/reports/final-report-v2.template.md` | Schema v2 full reading copy Markdown spine |
453
- | `templates/reports/md/tasks/*.template.md`, `md/macros/sections.md` | Eleven dedicated task bodies for the full reading copy Markdown, sibling of `html/tasks/`; shared section macro |
454
- | `templates/reports/html/base.template.html`, `html/tasks/*.template.html` | Shared HTML shell plus eleven dedicated task templates for human reports; task bodies are not shared |
478
+ | `templates/reports/md/macros/sections.md` | Shared Markdown sections; dedicated task bodies live in each phase's `report_assets/` |
479
+ | `templates/reports/html/base.template.html`, `html/macros/` | Shared HTML shell and macros; dedicated task templates live in each phase's `report_assets/` |
455
480
  | `templates/reports/report.css`, `report.js` | Inline assets for self-contained HTML report views |
456
481
  | `templates/reports/*.template.md` | Inputs, schedule, user-response, settings templates |
457
- | `project-analysis-input.template.md`, `feature-analysis-input.template.md`, `change-impact-analysis-input.template.md` | Brief input templates for the three analysis sidetracks |
482
+ | Phase `report_assets/*-input.template.md` | Dedicated brief input templates for task types that provide one; common inputs remain under `templates/reports/` |
458
483
  | `user-response.template.md`, `report.js` | Analysis Review sidecar block and the browser control that exports accept/revision/reject without changing the source report |
459
484
  | `templates/project-docs/task-index.template.md` | Project task index template |
460
485
  | `templates/worker-prompt-preamble.md` | Initial analysis audience procedure and output contract |
@@ -485,7 +510,6 @@ Optional (v1.0 backward-compatible) top-level keys:
485
510
  | `validate_analysis_report.py` | Cross-field validation for the three read-only analysis reports: frozen target/evidence snapshots, current-code evidence, review-source identity, and exact affected-ID resolution coverage on revision reruns |
486
511
  | `validate-schedule.py` | Schedule section/order/code validation |
487
512
  | `validate-implementation-plan-stages.py` | enforces the Stage Map structure — checks the S1–S8 rules (`## 5.5 Stage Map` + `## 5.5.<i> Stage <i>` sections, ≤ 8 steps per stage, etc.) |
488
- | `validate_improvement_report.py` | enforces the 11-item contract of the improvement-discovery final-report. Automatically invoked by `validate-run.py` when `task_type == "improvement-discovery"` |
489
513
  | `detect_self_mock.py` | self-mock detector — runs BOTH gates and writes the run's sidecar. Gate A (static) scans the changed TEST files for SUT-stub signals (patterns imported from the SSOT `scripts/okstra_ctl/self_mock_signals.py`, never redefined here), matching each file as one whole-file string so multi-line signals are caught. Python strings and comments are token-masked without changing line positions before those regexes run, so examples in docstrings and comments do not become findings while executable `patch.object(self, ...)` and `sut._private` accesses remain detectable. Writes a `qa/self-mock[-stage-<N>].json` sidecar and prints `QA-RESULT: PASS|FAIL` as its last line (exit 0 = no hits, exit 1 = at least one hit). The sidecar records `scannedFiles`/`skippedFiles` so the gate can prove every changed test file was actually scanned (a run that skips them cannot pass on empty input). An optional `--waivers <path>` moves hits matching `(file,line,signal)` from `staticDetect.hits` to `staticDetect.waived` (each carrying the user's `reason`/`acknowledgedBy`) and records the file as `waiverSource`. Gate B (mutation) runs in the same call: `--changed-file` takes the stage's WHOLE changed set (each adapter selects its own production sources out of it), `--diff` and `--worktree` scope it, and `scripts/okstra_ctl/mutation_probe.py` writes the result into the sidecar's `mutation` block; the received set is recorded as `changedFiles` so the gate can prove gate B was not handed an empty input. `overall` and the exit code follow BOTH gates — a mutation FAIL with a clean static scan still exits 1. The same `--waivers` file feeds both (gate A reads its `signal` entries, gate B its `mutant` ones). Its verdict feeds the fail-closed `_validate_selfmock` gate in `validate-run.py` (implementation / final-verification): a diff that touches test files with no readable PASS sidecar blocks the run; a `waived` entry missing `reason`/`acknowledgedBy`, or a `waiverSource` that is not the task's own `qa/self-mock-waivers.json`, also blocks |
490
514
  | `validate-workflow.sh` | End-to-end fixture workflow validation |
491
515
  | `lib/*.sh` | Shared shell validator helpers and fixtures |
@@ -607,7 +631,7 @@ Current report pipeline:
607
631
  3. Report-writer worker writes `worker-results/report-writer-narrative-<task-type>-<seq>.md`, including `humanSummary` and one task-type deliverable; Phase 7 later assembles the schema v3 report record.
608
632
  4. For implementation-planning, `okstra plan-items extract` creates the complete `P-*` queue, `validate` proves it still matches data.json, and the analyser instances run the separate plan-body verification round.
609
633
  5. Token usage substitution fills usage/cost cells in the report record. The full reading copy is rendered on demand with `okstra render-final-report` from `templates/reports/final-report-v2.template.md`.
610
- 6. `scripts/okstra-render-report-views.py` independently selects one of eleven dedicated task templates and emits human-facing HTML directly from the same data.json; run validation checks the record and the human HTML. A quick Markdown input retains its legacy conditional path.
634
+ 6. `scripts/okstra-render-report-views.py` independently selects one of twelve dedicated task templates and emits human-facing HTML directly from the same data.json; run validation checks the record and the human HTML. A quick Markdown input retains its legacy conditional path.
611
635
 
612
636
  For the three analysis sidetracks, the HTML view also exports an immutable-source `## ANALYSIS REVIEW` sidecar. A revision rerun carries that sidecar, reanalyzes the whole confirmed scope, and records one `analysisReviewResolution` row for every affected ID before `validate_analysis_report.py` accepts the result.
613
637
 
@@ -718,7 +742,7 @@ When changing code, keep these docs in sync:
718
742
  - New runtime source copied to users: update `tools/build.mjs`, install/uninstall manifests if applicable, and this file.
719
743
  - New skill/agent: update `README.md`, this file, install/uninstall fallback lists, and `CHANGES.md`.
720
744
  - New report field/section: update schema, template, report-writer worker, validator tests, this file's report model if user-visible.
721
- - New phase/profile behavior: update `prompts/profiles/*`, `docs/architecture.md`, `docs/cli.md`, and `README.md` if user-facing.
745
+ - New phase/profile behavior: update the phase directory under `scripts/okstra_ctl/phases/` and its `spec.md`, plus `docs/architecture.md`, `docs/cli.md`, and `README.md` if user-facing.
722
746
 
723
747
  Edit English canonical Markdown sources directly; nothing asks you to touch the Korean mirror in the same change. A maintainer session reconciles the mirrors on its own schedule with `$sync-korean-sources` or `/sync-korean-sources`, which begins by reading `node tools/korean-sources/cli.mjs status`. `.project-docs/ko-sources/**` is maintainer-local only: it is neither published nor committed.
724
748
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.205.1",
3
+ "version": "0.206.1",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -26,7 +26,6 @@
26
26
  "docs/performance-improvement-plan-v2.md",
27
27
  "docs/pr-template-usage.md",
28
28
  "docs/project-structure-overview.md",
29
- "docs/task-process/",
30
29
  "README.md"
31
30
  ],
32
31
  "engines": {
@@ -38,7 +37,7 @@
38
37
  "build": "npm run build:ts && node tools/build.mjs",
39
38
  "prepack": "npm run build",
40
39
  "test:js": "node --test tests-js/*.test.mjs",
41
- "test:py": "python3 tools/run-pytest-shards.py tests",
40
+ "test:py": "python3 tools/run-pytest-shards.py",
42
41
  "test:workflow": "bash validators/validate-workflow.sh",
43
42
  "test:e2e": "bash tests-e2e/run-all.sh",
44
43
  "check": "npm run build && npm run test:js && npm run test:py && npm run test:workflow && npm run test:e2e"
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.205.1",
3
- "builtAt": "2026-09-24T12:56:22.833Z",
2
+ "package": "0.206.1",
3
+ "builtAt": "2026-09-25T07:37:13.757Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -56,13 +56,13 @@ prefer_colocated_modules(__file__, "okstra_ctl/next_phase.py")
56
56
  from okstra_ctl import next_phase # noqa: E402
57
57
  from okstra_ctl.final_report_paths import final_report_markdown_path # noqa: E402
58
58
  from okstra_ctl.paths import task_manifest_file # noqa: E402
59
- from okstra_ctl.workflow import PHASE_RULES # noqa: E402
59
+ from okstra_ctl.phases.catalog import task_types as phase_task_types # noqa: E402
60
60
  from okstra_project.dirs import tasks_root # noqa: E402
61
61
 
62
62
 
63
63
  SLUG_RE = re.compile(r"[^a-zA-Z0-9-]+")
64
64
 
65
- ALLOWED_TASK_TYPES = set(PHASE_RULES)
65
+ ALLOWED_TASK_TYPES = set(phase_task_types())
66
66
  ALLOWED_ORIGINS = {
67
67
  "phase-continuation",
68
68
  "out-of-plan",
@@ -189,7 +189,7 @@ The **default is full re-verification**. Narrow this re-run to the impacted stag
189
189
  ```
190
190
  The CLI reads the plan's dependency graph from the prior `implementationPlanning.stageMap`, which is authoritative for the impacted stage numbers. The CLI prints JSON `{mode, reverify_stages, carry_stages, reason}` **and writes the same decision** to the record this run's manifest names in `incrementalDecisionPath`. You do not transcribe it: the report writer receives it through its authoring contract, and `incremental-carry` reads the same record. An `unresolved` result is a question back to you and is deliberately not recorded.
191
191
  4. **`mode == "full"`** → run the existing full re-verification path unchanged; ignore `reverify_stages` / `carry_stages`.
192
- 5. **`mode == "incremental"`** → scope every worker dispatch prompt to `reverify_stages` only (the downstream closure of the impacted stages). Do NOT re-analyze `carry_stages` — their prior plan-item verdicts are carried forward verbatim (see `prompts/profiles/implementation-planning.md` "Cross-verification mode" and `prompts/lead/convergence.md` "Convergence scope"). When the pin in step 0 was `auto`, this is the decision — do not upgrade it to full.
192
+ 5. **`mode == "incremental"`** → scope every worker dispatch prompt to `reverify_stages` only (the downstream closure of the impacted stages). Do NOT re-analyze `carry_stages` — their prior plan-item verdicts are carried forward verbatim (see `scripts/okstra_ctl/phases/implementation_planning/profile.md` "Cross-verification mode" and `prompts/lead/convergence.md` "Convergence scope"). When the pin in step 0 was `auto`, this is the decision — do not upgrade it to full.
193
193
  6. **`mode == "unresolved"`** → ask the user for stage numbers; re-enter step 3 with those numbers in `--impacted`. Do not fall back to full. Do not record `unresolved` as `incrementalDecision`. Then continue from the new `mode`.
194
194
  7. **Merge carried-forward verdicts.** In `incremental` mode the report writer receives the carried stage rows as a packet source and is told, in its own authoring contract, to copy them unchanged — you do not repeat that instruction to it. After `okstra plan-items seed --narrative ... --state ...`, the lead runs:
195
195
  ```
@@ -9,7 +9,7 @@
9
9
 
10
10
  Do not open, parse, or infer Okstra-owned task, run, discovery, or active-context JSON. This contract uses the fixed text views below before the lead contract is loaded, so it cannot bypass that boundary.
11
11
 
12
- - `okstra model-io project-context --project-root <project-root> --task-ref <task-ref>` provides project identity and the pointer for one explicit task reference. `task-ref` accepts a bare task ID, full task key, or that task's manifest path.
12
+ - `okstra model-io project-context --project-root <project-root> --task-ref <task-ref>` provides project identity and the pointer for one explicit task reference. `task-ref` accepts a bare task ID, full task key, or that task's manifest path. A bare task ID matches the catalog `taskId` exactly, which is the whole task-id segment (`task-42-fix-login`), not a ticket-number prefix of it and not a `<group>/<task-id>` path.
13
13
  - `okstra model-io run-input --run-manifest <run-manifest-path>` provides the current run identity, worker roster, model assignments, and permitted artifact paths.
14
14
 
15
15
  ## Step 1: Resolve the Task and Run Paths
@@ -52,7 +52,7 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
52
52
  | `verificationMode` | `"lightweight"` | `"lightweight"` or `"full-reanalysis"` |
53
53
  | `adversarial` | phase-aware: `true` for `requirements-discovery` / `error-analysis` / `implementation-option-selection` / `implementation-planning` / `project-analysis` / `feature-analysis` / `change-impact-analysis`, `false` otherwise | When `true`, Phase 5.5 runs in **adversarial mode** (see §"Adversarial Verification Mode"): verifiers actively try to refute each finding, the burden of proof sits on the claim, and `verificationMode` is forced to `"full-reanalysis"` scoped to the finding's cited evidence. Resolved by `scripts/okstra_ctl/render.py` `_build_convergence_block` and recorded in `config.adversarial` of the convergence state artifact. |
54
54
 
55
- **Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the active profile's `Required workers:` block (see `prompts/profiles/*.md`) resolves to fewer than 2 analyser workers — e.g. `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phases 5.5 and the plan-body verification round ([plan-body-verification](./plan-body-verification.md)), and record `finalState: "converged"` with `totalRounds: 0`, `round2SkippedReason: "auto-disabled"`, an empty `roundHistory`, and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). The plan-body round inherits the same rule via its `gating=false` advisory path.
55
+ **Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the active profile's `Required workers:` block (see `prompts/profiles/*.md`) resolves to fewer than 2 analyser workers — e.g. `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phases 5.5 and the plan-body verification round (`plan-body-verification` (the absolute path in **Okstra Runtime Resources**)), and record `finalState: "converged"` with `totalRounds: 0`, `round2SkippedReason: "auto-disabled"`, an empty `roundHistory`, and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). The plan-body round inherits the same rule via its `gating=false` advisory path.
56
56
 
57
57
  ## Finding Category
58
58
 
@@ -66,7 +66,7 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
66
66
 
67
67
  ## Convergence Algorithm
68
68
 
69
- **Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. In adversarial mode a tie whose DISAGREE carries `counter-evidence` does not carry forward — it is classified `contested` in that round (§"Adversarial Verification Mode"). This rule applies identically to the plan-body verification round ([plan-body-verification](./plan-body-verification.md)) where the same verdict tokens are reused.
69
+ **Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. In adversarial mode a tie whose DISAGREE carries `counter-evidence` does not carry forward — it is classified `contested` in that round (§"Adversarial Verification Mode"). This rule applies identically to the plan-body verification round (`plan-body-verification` (the absolute path in **Okstra Runtime Resources**)) where the same verdict tokens are reused.
70
70
 
71
71
  **Enforced:** the engine owns the classifier and replays it — `scripts/okstra_ctl/convergence_engine.py` `validate_final_state` recomputes each finding's expected final classification from its recorded votes and rejects the state when the persisted value differs, and `finalize` runs that same check before it writes, so a tie scored as a consensus never reaches the artifact. `okstra convergence validate` is the same call on demand. Nothing re-derives the majority rule outside the engine; a second implementation would only drift from it.
72
72
 
@@ -761,4 +761,4 @@ If `convergence.enabled: false`, this contract is skipped. Phase 6 operates usin
761
761
 
762
762
  ## Plan-body verification mode (implementation-planning only)
763
763
 
764
- Moved to its own contract: [plan-body-verification](./plan-body-verification.md). It fires only for `task-type = implementation-planning`, as a Phase 6 sub-step after the report-writer draft — read that file at that sub-step, NOT during the Phase 5.5 finding convergence this contract governs. The finding queue (`F-*`, this contract) and the plan-item queue (`P-*`, that contract) are disjoint — see its "MUTUAL EXCLUSION" section.
764
+ Moved to its own contract: `plan-body-verification` (the absolute path in **Okstra Runtime Resources**). It fires only for `task-type = implementation-planning`, as a Phase 6 sub-step after the report-writer draft — read that file at that sub-step, NOT during the Phase 5.5 finding convergence this contract governs. The finding queue (`F-*`, this contract) and the plan-item queue (`P-*`, that contract) are disjoint — see its "MUTUAL EXCLUSION" section.
@@ -23,7 +23,7 @@ This document is the operating contract and phase index. Detailed procedures liv
23
23
  | [context-loader](./context-loader.md) | Phase 1 task-bundle discovery, manifest fields, run-directory layout |
24
24
  | [team-contract](./team-contract.md) | Phase 2–5 worker roster, model assignment rules, prompt composition (anchor headers, `[Required reading]`, `[Error reporting]`), worker output contract, terminal statuses, usage tracking |
25
25
  | [convergence](./convergence.md) | Phase 5.5 finding convergence loop, finding categories, reverify dispatch, convergence state schema. Use the bounded common read under Doctrine lazy reads |
26
- | [plan-body-verification](./plan-body-verification.md) | Phase 6 plan-body verification sub-step (implementation-planning only) — plan-item extraction, verdict semantics, gate resolution, state schema. Read only the common procedure at that sub-step, using the bounded read below |
26
+ | `plan-body-verification` (the absolute path in **Okstra Runtime Resources**) | Phase 6 plan-body verification sub-step (implementation-planning only) — plan-item extraction, verdict semantics, gate resolution, state schema. Read only the common procedure at that sub-step, using the bounded read below |
27
27
  | [report-writer](./report-writer.md) | Phase 6 final-report authorship, dispatch template, resume-safe dispatch, shared-graph integrity check, Phase 7 token-usage collector |
28
28
 
29
29
  Read-side inspection (`/okstra-inspect`) and scheduling (`/okstra-schedule-gen`) are user-invoked skills, not lead support contracts — the lead does not consult them during a run.
@@ -196,7 +196,7 @@ Phase-transition checklist (lead, end of run):
196
196
 
197
197
  1. Confirm the current phase's required outputs are complete and recorded in the final report.
198
198
  2. Set `workflow.phaseStates.<currentPhase>.state = "completed"` in `task-manifest.json` (validator does this when the run passes; verify the value).
199
- 3. Record **Phase Routing** in the final report — the routing field your task type owns is the only input the Next-Phase Pointer is built from, including the `rationale` the next run's closeout quotes. Update `workflow.lastCompletedPhase`. The pointer (`workflow.nextRecommendedPhase`) is not a field anyone writes: Phase 7 projects it from that routing field, and [report-writer](./report-writer.md) owns which field feeds it and what the projection does with it.
199
+ 3. Record **Phase Routing** in the final report — the routing field your task type owns is the only input the Next-Phase Pointer is built from, including the `rationale` the next run's closeout quotes. When [phase-routing](./phase-routing.md) has a section for your task type, read that section before writing the field: it lists the destinations that phase allows, and the phase profile does not repeat them. A task type with no section there keeps its routing rules in its own phase profile. Update `workflow.lastCompletedPhase`. The pointer (`workflow.nextRecommendedPhase`) is not a field anyone writes: Phase 7 projects it from that routing field, and [report-writer](./report-writer.md) owns which field feeds it and what the projection does with it.
200
200
  4. **Do NOT start the next phase inside the current run.** A new okstra invocation with the new `--task-type` is the only legal way to advance.
201
201
 
202
202
  User-utterance interpretation rule:
@@ -205,7 +205,7 @@ User-utterance interpretation rule:
205
205
  - If the current phase's outputs are already complete and the user clearly wants to advance, reply with the phase-transition checklist above and the exact next-run command. Wait for explicit user confirmation before any action that belongs to the next phase.
206
206
  - If the Next-Phase Pointer target (`nextRecommendedPhase.phase`) is `implementation-planning`, the next run produces a **plan**, not code. The next run after that is `implementation`.
207
207
 
208
- **Enforced:** the Forbidden actions column is declared in `prompts/profiles/forbidden-actions.json` and scanned post-hoc by `validators/forbidden_actions.py` `scan_forbidden_actions`, which matches this run's task type through `forbidden_patterns_for` against the commands the run actually executed.
208
+ **Enforced:** the Forbidden actions column is declared in each phase module’s `boundary.json` (`scripts/okstra_ctl/phases/<phase>/boundary.json`) and scanned post-hoc by `validators/forbidden_actions.py` `scan_forbidden_actions`, which matches this run's task type through `forbidden_patterns_for` against the commands the run actually executed.
209
209
 
210
210
  ## Progress reporting (BLOCKING)
211
211
 
@@ -247,7 +247,7 @@ Required checkpoints:
247
247
  - `PROGRESS: phase-5.6-critic provider=<provider> gaps=<n>` — after the critic result is collected (Phase 5.6, opt-in; the critic dispatch itself fires concurrently with the first 5.5 reverify round). Omitted when `convergence.critic.enabled == false`.
248
248
  - `PROGRESS: phase-batch-cleanup panes=<n>` — immediately after cleaning up the previous batch's panes, at each batch boundary (① just before the first `phase-5.5-convergence` round ② just before the `phase-6-synthesis` report-writer dispatch). `<n>` is the number of panes closed at that boundary — the panes of dispatches this run recorded and that have since finished — read from the `okstra team reclaim --project-root <dir> --run-manifest <path> --dry-run` pass taken immediately before the closing pass, never estimated. A pane the harness opened for its own teammate carries no recorded id, so it is not counted and not closed. Expose only the counts and NEVER expose a raw `paneId` or worker handle. Just before the first batch (analysis-worker dispatch) there is nothing to clean up, so it is a no-op and the marker is omitted.
249
249
  - `PROGRESS: phase-6-synthesis dispatching report-writer-worker` — at the start of Phase 6.
250
- - `PROGRESS: phase-5.5.9-plan-verify round=<N> items=<count>` — immediately before dispatching each plan-body verification round (`implementation-planning` only; see [plan-body-verification](./plan-body-verification.md) §"Round protocol"). Each round is a worker batch like any other, so round 2 and later MUST be preceded by a `phase-batch-cleanup` line reclaiming the previous round's verifiers. The numbering keeps this line sorted where the work happens — after Phase 6, because the round verifies the drafted plan body.
250
+ - `PROGRESS: phase-5.5.9-plan-verify round=<N> items=<count>` — immediately before dispatching each plan-body verification round (`implementation-planning` only; see `plan-body-verification` (the absolute path in **Okstra Runtime Resources**) §"Round protocol"). Each round is a worker batch like any other, so round 2 and later MUST be preceded by a `phase-batch-cleanup` line reclaiming the previous round's verifiers. The numbering keeps this line sorted where the work happens — after Phase 6, because the round verifies the drafted plan body.
251
251
  - `PROGRESS: user-confirm <C-NNN> <the question, one line>` — immediately before asking the user about anything that would otherwise become an open `Blocks=approval` row (see "User confirmation before an approval blocker" below). Not tied to a phase: it fires wherever the blocker surfaces. `<C-NNN>` is the id the row will carry, so the answer and the row can be matched afterwards.
252
252
  - `PROGRESS: phase-7-persist updating manifests` — at the start of Phase 7.
253
253
  - `PROGRESS: phase-7-teardown shutting-down-workers` — only after usage collection and user approval, immediately before `shutdown_workers`; omitted when no cleanup resource exists or the user keeps it.
@@ -291,7 +291,7 @@ The sequence is fixed:
291
291
  4. On an answer — record the raw text in the row's `userInput`, set `status: answered` and `userConfirmation: asked-and-answered`, and apply the selected disposition in this run.
292
292
  5. Only when asking fails does the row stay open: `asked-awaiting` when the user has not answered, `deferred-no-interactive-session` when this run has no user to ask.
293
293
 
294
- For report contract v3 `implementation-planning`, record active approval decisions only through `okstra approval-decision`; report assembly derives each report row's status, resolution, and backtraces from that lead-owned ledger plus the activity ledger. Classify a user-owned selection as `user-decision`, a surviving non-correctness majority disagreement as `noncritical-dissent`, and a cited path/symbol mismatch, `P-Req-*` coverage mismatch, or independent Requirement Coverage blocker as `correctness-critical`. `select` is limited to `user-decision`. `accept-risk` is available to all three classifications: it ends the gate, keeps the DISAGREE votes and the clarification row as evidence, and later stages read that record. `request-revision` / `reject` are available to all three and still withhold the next phase. Contract v2 remains read-only compatible; do not create a new v2 report. **Enforced:** `scripts/okstra_ctl/approval_decisions.py` rejects an invalid option/disposition combination at the moment the decision is opened or resolved, so a classification that does not admit the disposition never reaches the ledger. The backtraces are not re-derived afterwards — they are produced by assembly (next paragraph), which is why the lead has no hand-written path to them. `validators/validate-run.py` `_validate_v3_approval_context` checks the one claim assembly cannot see: an `approved` frontmatter published against a row that still blocks progress.
294
+ For report contract v3 `implementation-planning`, record active approval decisions only through `okstra approval-decision`; report assembly derives each report row's status, resolution, and backtraces from that lead-owned ledger plus the activity ledger. Classify a user-owned selection as `user-decision`, a surviving non-correctness majority disagreement as `noncritical-dissent`, and a cited path/symbol mismatch, `P-Req-*` coverage mismatch, or independent Requirement Coverage blocker as `correctness-critical`. `select` is limited to `user-decision`. `accept-risk` is available to all three classifications: it ends the gate, keeps the DISAGREE votes and the clarification row as evidence, and later stages read that record. `request-revision` / `reject` are available to all three and still withhold the next phase. Contract v2 remains read-only compatible; do not create a new v2 report. **Enforced:** `scripts/okstra_ctl/approval_decisions.py` rejects an invalid option/disposition combination at the moment the decision is opened or resolved, so a classification that does not admit the disposition never reaches the ledger. The backtraces are not re-derived afterwards — they are produced by assembly (next paragraph), which is why the lead has no hand-written path to them. `scripts/okstra_ctl/phases/implementation_planning/plan_body.py` `_validate_v3_approval_context` checks the one claim assembly cannot see: an `approved` frontmatter published against a row that still blocks progress.
295
295
 
296
296
  The approval state transitions are fixed:
297
297
 
@@ -301,7 +301,7 @@ The approval state transitions are fixed:
301
301
 
302
302
  Do not move `answered` back to `open` because a check failed. The user's choice stands. Record the failed check on the row; later stages still see the DISAGREE votes.
303
303
 
304
- `open` blocks until the user judges. `answered` with `select` / `accept-risk` / `answer`, `resolved`, and `obsolete` do not block approval or the next phase. `request-revision` and `reject` still withhold the next phase until this report's `supersessionLedger` records that the answer was incorporated (`superseded` or `no-dependent-statement`). `accept-risk` does not require re-verification `AGREE`. The worker votes stay on the plan item so a later stage can still see the dissent. **Enforced:** `scripts/okstra_ctl/clarification_items.py` `row_blocks_progress`, `validators/validate-run.py` `_user_accepted_plan_item_ids`.
304
+ `open` blocks until the user judges. `answered` with `select` / `accept-risk` / `answer`, `resolved`, and `obsolete` do not block approval or the next phase. `request-revision` and `reject` still withhold the next phase until this report's `supersessionLedger` records that the answer was incorporated (`superseded` or `no-dependent-statement`). `accept-risk` does not require re-verification `AGREE`. The worker votes stay on the plan item so a later stage can still see the dissent. **Enforced:** `scripts/okstra_ctl/clarification_items.py` `row_blocks_progress`, `scripts/okstra_ctl/phases/implementation_planning/plan_body.py` `_user_accepted_plan_item_ids`.
305
305
 
306
306
  When a terminal row preserves a pre-correction dissent classification, keep superseded votes in `state/plan-body-verification-implementation-planning-<seq>.json`. Activities that implement or check the decision record the exact `C-NNN` in `clarificationRefs` and the affected `P-*` identifiers in `planItemIds`. Report assembly verifies that every resolution `checkRefs` value names an existing activity and derives each plan item's `clarificationRefs`; the lead never copies those references into `approvalContext`. A corrected coverage-only blocker keeps its `C-NNN` in the non-blocking Requirement Coverage row's `decisionRefs`. An `obsolete` row is invalid while its disagreement or coverage blocker remains active in the current plan. **Enforced at assembly, not after it:** `scripts/okstra_ctl/report_assembly.py` `_clarification_row` refuses a resolution whose `checkRefs` is empty, names an activity that does not exist, or names one whose `clarificationRefs` omits this `C-NNN`; `_attach_plan_backlinks` derives every plan item's `clarificationRefs` from the activity ledger, so a hand-copied reference has nowhere to enter. Nothing recomputes these links afterwards — assembly failing is the whole check.
307
307
 
@@ -378,7 +378,9 @@ After context-loader completes, read **only the compact intake files below** in
378
378
  **Doctrine lazy reads (BLOCKING — read at the round, not at Phase 1):**
379
379
 
380
380
  - [convergence](./convergence.md) — read the common procedure with the bounded command below before the first `PROGRESS: phase-5.5-convergence` line of any run that runs a convergence round.
381
- - [plan-body-verification](./plan-body-verification.md) — read the common procedure with the bounded command in the Phase 6 sub-step before the first `PROGRESS: phase-5.5.9-plan-verify` line.
381
+ - `plan-body-verification` (the absolute path in **Okstra Runtime Resources**) — read the common procedure with the bounded command in the Phase 6 sub-step before the first `PROGRESS: phase-5.5.9-plan-verify` line.
382
+
383
+ For a run frozen before the phase-package migration, a missing `prompts/lead/plan-body-verification.md` pointer resolves within the asset root containing this lead contract. Identify its unique `okstra_ctl` package at `scripts/okstra_ctl`, `python/okstra_ctl`, or `lib/python/okstra_ctl` by `__init__.py`, then read `phases/implementation_planning/instructions/plan-body-verification.md` inside that package at the original Phase 6 sub-step. Preserve the bounded read and the `phase-5.5.9-plan-verify` checkpoint order below. If the package is ambiguous or the resource is missing, stop and report it; do not search another installation or rewrite frozen profiles, manifests, prompts or run context. The mapping is checked by `scripts/okstra_ctl/phases/implementation_planning/tests/test_instruction_assets.py`; the existing entry guard checks read timing by the unchanged basename.
382
384
 
383
385
  Both stay out of the Phase 1 baseline for the token reason above, and neither is optional at its round: together they carry more than half of this contract family's MUST clauses, so a round dispatched without the read is a round run from memory. **Enforced:** `validators/validate_session_conformance.py` `_ENTRY_GUARD_READS` requires each read — a `Read` call or a shell command naming the file — inside this run's window and before that checkpoint. The requirement is conditioned on the checkpoint actually appearing, so a run that holds no such round is never asked for it.
384
386
 
@@ -406,7 +408,9 @@ checks the emitted common procedure. The same entry-guard read trace applies.
406
408
 
407
409
  **Implementation profile lazy reading discipline (BLOCKING — applies only when `task_type == "implementation"`):**
408
410
 
409
- The `implementation` profile's thin core (`prompts/profiles/implementation.md`) is intentionally minimal so the Phase 1 baseline stays small. Three sidecar files carry the bulk of the rules and MUST be read at the listed phase — do NOT pre-load them at Phase 1. The sidecar list and each one's `Read at` phase live in that profile's "Lazy section pointers" table, which arrives in the Phase 1 intake via `analysis-profile.md`, so it is already in context whenever this discipline applies.
411
+ The `implementation` profile's thin core (`scripts/okstra_ctl/phases/implementation/profile.md`) is intentionally minimal so the Phase 1 baseline stays small. Three sidecar files carry the bulk of the rules and MUST be read at the listed phase — do NOT pre-load them at Phase 1. The sidecar list and each one's `Read at` phase live in that profile's "Lazy section pointers" table, which arrives in the Phase 1 intake via `analysis-profile.md`, so it is already in context whenever this discipline applies.
412
+
413
+ For a run frozen before the phase-package migration, resolve a missing `prompts/profiles/_implementation-*.md` pointer within the asset root containing this lead contract. That root has one `okstra_ctl` package at `scripts/okstra_ctl`, `python/okstra_ctl`, or `lib/python/okstra_ctl`, identified by its `__init__.py`. Read `phases/implementation/instructions/<same-basename>` inside that package at the original `Read at` phase. If no unique package or requested file exists, stop and report the missing resource. Keep the frozen `analysis-profile.md` unchanged and do not search another installation. New runs carry resolved instruction paths. The path mapping is checked by `scripts/okstra_ctl/phases/implementation/tests/test_assets.py`; the entry guard below checks the recorded read timing.
410
414
 
411
415
  **Entry guard (BLOCKING).** Before transitioning into Phase 5 or Phase 6 for an `implementation` run, lead MUST load the sidecar(s) whose `Read at` (per that table) matches the entering phase — either a single `Read` tool call, or a shell command naming that file (`cat`, `sed -n`), since some hosts steer file reads to the shell. If lead enters the phase without that load recorded in the selected adapter's conformance evidence/event source, phase entry is refused — lead writes a `contract-violation` to the run-level errors log with `--message "implementation-sidecar-not-loaded"` and stops. Re-entry requires the sidecar Read first. **Enforcement:** the Phase 7 validator (`validate_session_conformance.py`) verifies post-hoc that all three sidecar loads exist in the selected adapter's declared source within this run's window, and that they precede the `phase-6-synthesis` / `phase-7-persist` checkpoints respectively.
412
416
 
@@ -552,54 +556,9 @@ All categories must appear in the final synthesis. Do not omit contested or work
552
556
 
553
557
  If only one worker result is usable: reduced-confidence synthesis. If evidence is missing: say `I don't know`. If no meaningful worker differences: say so explicitly.
554
558
 
555
- ### Phase 6 sub-step: Plan-body verification (implementation-planning only, BLOCKING)
556
-
557
- After the Report writer worker narrative is reviewed, **if** `task_type == "implementation-planning"` **and** `task-manifest.json` `convergence.planBodyVerification.enabled == true` (default), the lead MUST run the plan-body verification sequence on the consolidated plan body before declaring Phase 6 complete and entering Phase 7.
558
-
559
- This is a Phase 6 sub-step — it does NOT introduce a new top-level lifecycle phase; the lead operating-phase model (Phase 1 Intake → Phase 7 Persist, labels in the "Quick Reference" table above as the single source of truth) is preserved. The round's outcome is read from the final report's `### 5.5.9 Plan Body Verification` section and `implementationPlanning.planBodyVerification` in its data.json — it is not a separate lifecycle phase identifier.
560
-
561
- **REQUIRED RESOURCE:** Read the common procedure of [plan-body-verification](./plan-body-verification.md) for the round protocol, plan-item ID scheme (`P-Dir-1` for selected-direction; `P-Opt-*` for legacy candidate comparison; then `P-Step-*` / `P-Dep-*` / `P-Val-*` / `P-Rb-*` / `P-Req-*` / `P-Prep-*`), verdict semantics (`AGREE` / `DISAGREE(a-f)` / `SUPPLEMENT`), classification rules, gate-result resolution, and state-path authority. Read the state-file schema only when diagnosing state or projection validation. For `P-Dir-1`, compare `directionRealization` with `selectedDirectionRef` and its snapshot: verify the core mechanism, architecture boundaries, planning invariants, and any hidden direction change.
562
-
563
- Read from the resolved runtime resource path, replacing `<plan-body-contract-path>`
564
- with that resource's absolute path. This prints the complete common procedure and
565
- its conditional reading table, stopping before reference examples:
566
-
567
- ```sh
568
- awk '/^## Reference material$/ {exit} {print}' '<plan-body-contract-path>'
569
- ```
570
-
571
- Follow the conditional reading table for later rounds and validation failures.
572
- The command keeps the filename in the read trace used by
573
- `validators/validate_session_conformance.py` `_ENTRY_GUARD_READS`.
574
- The bounded output is checked by
575
- `tests/contract/test_contract_examples_execute.py::test_plan_body_common_read_keeps_gate_rules`.
576
-
577
- Distinct from Phase 5.5 finding convergence:
578
-
579
- - Phase 5.5 reconciles worker **findings** (F-*) from independent analysis.
580
- - This sub-step reconciles the **consolidated plan body** (P-*) authored by the Report writer worker.
581
- - The two rounds use disjoint queues and separate state files — see [plan-body-verification](./plan-body-verification.md) "MUTUAL EXCLUSION (BLOCKING)".
582
-
583
- Lead's responsibilities in this sub-step (in order):
584
-
585
- For a new `implementation-planning` run, the fixed order is initial verification → one planner self-fix → targeted re-verification → lead decision or immediate user confirmation. The initial verification is round 1 and the targeted re-verification is round 2. A second automatic self-fix is a contract violation. When `okstra plan-items prepare` reports `"gating": false` (one-stage `no-design-inputs` plan), skip the self-fix loop and the sweep batch: extraction and round 1 still run, then go to the user gate. Two-or-more stages, a PREP item, or non-empty `designPreparation.items` keep `gating: true` and the full order.
586
-
587
- 1. Build the queue with `okstra plan-items prepare --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`, place the output of `okstra plan-items prompt --run-manifest <run-manifest>` verbatim in every verifier prompt, then run `okstra plan-items validate-prepared --narrative <report-writer-narrative.md> --run-manifest <run-manifest>`. Python resolves the one convergence-owned state path from that run identity. The lead MUST NOT summarise, select, omit, reorder, or renumber the queue. Each prompt uses the compact subject plus the lossless payload, and asks every item:
588
-
589
- ```text
590
- What concrete false-positive input, failure ordering, or omitted dependency
591
- would make this plan item incorrect even if its happy path succeeds?
592
- ```
593
-
594
- An `AGREE` response records the considered counterexample and exclusion reason in its note; unverified external material is `verification-error`, not `DISAGREE`.
595
- 2. Dispatch a single plan-body reverify round to every analyser worker in the roster (`claude`, `codex`, and `antigravity` when opted in). `Report writer worker` is NOT a participant in this round.
596
- 3. Record each verifier Markdown result through `okstra plan-items apply-verdicts --state <plan-body-verification.json> --result <worker-id>=<result.md> --round <N>`. Python validates every submitted `P-*` identifier against the current convergence state and overwrites only that round's verdicts. Then resolve the gate result to one of `passed` / `passed-with-dissent` / `blocked-by-disagreement` / `aborted-non-result`.
597
- 4. After `okstra plan-verify` succeeds, run `okstra plan-items complete-round --state <plan-body-verification.json> --run-manifest <current-run-manifest.json> --round <N>`. Python reads the current worker assignments, atomically appends the convergence-owned history, and updates its nested final projection. This state is the only plan-verification input report assembly reads.
598
- 5. Record every *in-scope execution* `majority-disagree` decision through `okstra approval-decision`; record its plan and clarification links only on activities. Do not promote `observed` / `deferred` / `record` items, and do not append `clarificationItems[]` directly.
599
- 6. Run report assembly after the final plan-body state, approval ledger, design snapshot, activity ledger, and team state are complete. Assembly writes `implementationPlanning.planBodyVerification` and derived clarification rows while publishing `data.json` once.
600
- 7. Publish the report record `frontmatter.approved` field as `false`. There is no in-body `- [ ] Approved` marker line — approval lives only in the record (see [plan-body-verification](./plan-body-verification.md) §"Round protocol" step 9). The user may set it to `true` (via `--approve` or the in-session wizard) when the gate is `passed` / `passed-with-dissent`, or under `blocked-by-disagreement` once every remaining `Blocks=approval` row is user-proceeded (`accept-risk` / `select` / `answer`); `aborted-non-result` still withholds approval. **Enforced:** run-prep (`scripts/okstra_ctl/run.py` `_validate_approved_plan` / `_blocking_gate_survives_user_decision`) fail-closes an `approved: true` plan whose blocking gate carries no user disposition, and `validators/validate-run.py` `_validate_plan_body_gate_recompute` fails a gate value upgraded past what the recorded votes support. Manually flipping a blocked gate to passing is a contract violation.
559
+ ### Phase 6 sub-step: Plan-body verification (implementation-planning only)
601
560
 
602
- If `convergence.planBodyVerification.enabled == false` (set by `--no-plan-verification` or by `okstra config set plan-verification off`), the entire sub-step is skipped and the top-of-report Approval marker is rendered unconditionally (legacy behaviour). This opt-out is intended for fast iteration only and is not recommended for handoff-ready plans.
561
+ For `implementation-planning`, follow the Phase 6 plan-body verification sub-step in the selected phase profile before entering Phase 7. The phase profile owns the enablement gate, round protocol, and approval publication rules.
603
562
 
604
563
  ## Phase 7: Artifact persistence and validator handoff
605
564
 
@@ -0,0 +1,82 @@
1
+ # Phase routing
2
+
3
+ The lead chooses the next phase. Code projects the chosen `target` into `workflow.nextRecommendedPhase`. It does not infer a destination.
4
+
5
+ ## final-verification
6
+
7
+ Allowed targets:
8
+
9
+ - `release-handoff`
10
+ - `release-handoff(stage-group)`
11
+ - `final-verification`
12
+ - `error-analysis`
13
+ - `implementation-option-selection`
14
+ - `implementation-planning`
15
+ - `implementation`
16
+ - `done`
17
+
18
+ `finalVerification.routingRecommendation` is an **object** with exactly two fields — `target`, one value from the Allowed targets list, and `rationale`, the sentence tying that choice to the verdict and the blocker list. Free routing prose is not the field; a target named only in the prose does not route the task, because Phase 7 projects `workflow.nextRecommendedPhase` from `target` alone. `final-verification` re-runs this phase on the same head and is for exactly one situation: every remaining blocker is an environment or configuration fault whose cause this report already names — a `qaCommands` entry pointing at a path that no longer exists, a missing credential, a stale fixture — so nothing in the code, the plan, or the selected direction is being re-decided. Name the repair in the `rationale`. When any blocker needs a code, plan, or direction change, route to the phase that owns that change instead; routing a defect you have not diagnosed back into this phase re-runs the verification that already failed. Both `release-handoff` forms are allowed ONLY when the verdict is release-ready — `accepted`, or `conditional-accept` with every condition declaring `blocksReleaseHandoff: false`. Either verification scope may route there: release-handoff opens one PR per stage, so a release-ready `single-stage` run is the evidence for that stage's PR, and `release-handoff(stage-group)` is only a scope qualifier that projects onto the same phase. `done` ends the lifecycle here. Enforcement: `schemas/final-report-v2.0.schema.json` rejects a `target` outside the enum, a missing `rationale`, and a string in place of the object; `validators/validate-run.py` rejects a missing `target` and a verdict that is not release-ready routed to either `release-handoff` form (naming the condition ids that block it).
19
+
20
+ ## release-handoff
21
+
22
+ - if any cited verdict is `blocked`, a `conditional-accept` carrying a condition that blocks release, or any other token (including ambiguous phrasing like "looks good"), the run MUST end immediately with status `blocked` and a routing recommendation back to `error-analysis` or `implementation-planning`. Do NOT prompt the user; Do NOT run any git command.
23
+
24
+ - **Routing recommendation**: explicit `done` token, since release-handoff is the terminal lifecycle phase. If the run ended in `skip` or `cancel`, or delivered only some of the selected stages, the recommendation MUST state which stages still need a PR and that re-entry into release-handoff is appropriate.
25
+
26
+ 1. **Entry-gate audit** — the report cites, for every stage it delivered, that stage's final-verification report path and the literal `Verdict Token` row with a release-ready value. If any is missing, or a `conditional-accept` stage's PR body omits its conditions, the run is invalid and MUST be re-routed to `final-verification`.
27
+
28
+ Enforcement boundary: `scripts/okstra_ctl/release_gate.py` `release_handoff_allowed` rejects non-release-ready verification evidence, and `scripts/okstra_ctl/phases/release_handoff/operations.py` `_require_eligible` blocks ineligible stages. The lead performs the routing and report-completeness judgement through the phase-transition checklist in [the lead contract](./okstra-lead-contract.md); these code checks do not choose its next destination.
29
+
30
+ ## technical-verification
31
+
32
+ Route only to `implementation-option-selection`, with one matching phase-continuation follow-up. The next comparison consumes this report through `--clarification-response` and authors fresh feasibility votes.
33
+
34
+ Enforcement: `technicalVerification.routing.nextTaskType` and phase-continuation constraints in the report schema; `scripts/okstra_ctl/report_routing.py` checks the routing value during publication and run validation.
35
+
36
+ ## implementation-option-selection
37
+
38
+ When no candidate is valid and explicit eligible technical facts remain, route to `technical-verification` to collect experimental evidence. Keep `rankedOptions` empty and `recommendedOptionId` null. `validate_blocked_answer_channel` rejects this route while user decisions remain unresolved or no safe, explicitly classified technical fact is available. Historical blocked reports can be supplied explicitly without rewriting their verdict.
39
+
40
+ With valid options set routing to `pending-direction-selection`; without a valid option use `blocked`.
41
+
42
+ In `candidate-comparison`, keep `preselectedDirection` null and do not route directly to `implementation-planning`. In `preselected-validation`, emit exactly one validated option, an empty `candidateAudit`, a cited `preselectedDirection`, and routing `implementation-planning`.
43
+
44
+ ## requirements-discovery
45
+
46
+ determine whether `error-analysis` or `implementation-option-selection` is the next safe step. Direct `implementation-planning` or `implementation` handoff is never a valid routing target — implementation requires direction selection followed by an approved `implementation-planning` report
47
+
48
+ The lead owns the final routing decision and safe resume guidance. The phase supplies request classification, rejection criteria, missing evidence and dependency facts. The report schemas constrain `requirementsDiscovery.routing.nextTaskType`; common `report_routing.fanout_routing_errors` checks each packet destination.
49
+
50
+ ## error-analysis
51
+
52
+ Allowed targets:
53
+
54
+ - `implementation-option-selection`
55
+ - `error-analysis`
56
+
57
+ If the cause is credible, recommend `implementation-option-selection` with the verified evidence; if the cause is still unclear, recommend another `error-analysis` run with the next diagnostic. The lead's Phase Routing settles the next phase.
58
+
59
+ A route to `implementation-option-selection` requires a credible leading cause referenced by `routing.leadingCauseId` and `begin-option-selection` as the direction. A route back to `error-analysis` requires the sharp next diagnostic and `continue-investigation` as the direction.
60
+
61
+ The selected target is recorded in `errorAnalysis.routing.nextTaskType`. `schemas/final-report-v2.0.schema.json` and `schemas/final-report-v3.0.schema.json` constrain the target values. `okstra_ctl.phases.error_analysis.validation` checks the cause reference, matching verdict directions, and the single phase-continuation row. Common `okstra_ctl.next_phase` projects that selected target without choosing a destination.
62
+
63
+ ## improvement-discovery
64
+
65
+ Both branches: Direction `routing`; Next Step "ask the user to select K candidates (see the ## 5.9 table)".
66
+
67
+ - `## 3. Recommended Next Steps` first entry summarises per-candidate routing and proposes new task-key names of the form `<task-group>/imp-<Cand-ID>`
68
+
69
+ ### Candidate conversion
70
+
71
+ - Each candidate the user picks becomes a new okstra task. Suggested task-key: `<task-group>/imp-<Cand-ID>`.
72
+ - The candidate row's Recommended next-phase determines which `--task-type` to launch with.
73
+
74
+ ## implementation
75
+
76
+ Pick `final-verification` when this stage's plan items landed and validation passed; `error-analysis` when a failure's cause is not understood; `implementation-planning` when the approved plan itself no longer fits the evidence; `implementation` when work remains inside this stage and the next run is a fix run.
77
+
78
+ For the `selected-direction` plan branch: A direction change routes to `implementation-option-selection`; a detail-only plan correction routes to `implementation-planning`.
79
+
80
+ For the legacy candidate-comparison branch: Any deviation MUST be justified in the final report AND routed to a new `implementation-planning` run; never silently expand scope.
81
+
82
+ Enforcement boundary: the lead applies these semantic distinctions through the phase-transition checklist in [the lead contract](./okstra-lead-contract.md). `tests/contract/test_prompt_fragment_ownership.py` preserves the decision rules at this canonical location; it checks instruction ownership, not the correctness of an individual model judgement.