okstra 0.206.0 → 0.207.0

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 (259) hide show
  1. package/README.md +3 -3
  2. package/dist/cli-registry.mjs +7 -1
  3. package/dist/cli-registry.mjs.map +1 -1
  4. package/dist/commands/lifecycle/install.mjs +1 -1
  5. package/dist/commands/lifecycle/install.mjs.map +1 -1
  6. package/docs/architecture/storage-model.md +1 -0
  7. package/docs/architecture.md +40 -16
  8. package/docs/cli.md +17 -15
  9. package/docs/contributor-change-matrix.md +3 -2
  10. package/docs/performance-improvement-plan-v2.md +1 -1
  11. package/docs/project-structure-overview.md +43 -20
  12. package/package.json +1 -1
  13. package/runtime/BUILD.json +2 -2
  14. package/runtime/agents/operations/code-review.json +1 -1
  15. package/runtime/bin/lib/okstra/usage.sh +3 -3
  16. package/runtime/bin/okstra-compact-reminder.sh +1 -1
  17. package/runtime/bin/okstra-spawn-followups.py +2 -2
  18. package/runtime/prompts/duties/direction-selection-worker.json +1 -1
  19. package/runtime/prompts/launch.template.md +2 -2
  20. package/runtime/prompts/lead/adapters/cmux.md +4 -3
  21. package/runtime/prompts/lead/context-loader.md +1 -1
  22. package/runtime/prompts/lead/convergence.md +44 -12
  23. package/runtime/prompts/lead/okstra-lead-contract.md +44 -73
  24. package/runtime/prompts/lead/phase-routing.md +64 -0
  25. package/runtime/prompts/lead/report-writer.md +10 -8
  26. package/runtime/prompts/lead/team-contract.md +1 -1
  27. package/runtime/prompts/profiles/_clarification-recommendation.md +4 -4
  28. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  29. package/runtime/prompts/profiles/_common-contract.md +2 -2
  30. package/runtime/prompts/profiles/_coverage-critic.md +1 -1
  31. package/runtime/prompts/profiles/forbidden-actions.json +0 -94
  32. package/runtime/prompts/wizard/prompts.ko.json +2 -1
  33. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/relay.md +1 -1
  34. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +5 -5
  35. package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -1
  36. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +3 -2
  37. package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +1 -1
  38. package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +1 -1
  39. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +17 -26
  40. package/runtime/python/okstra_ctl/agent/prompt_cli/batch.py +183 -0
  41. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +60 -10
  42. package/runtime/python/okstra_ctl/agent/prompt_cli/corrections.py +1 -1
  43. package/runtime/python/okstra_ctl/agent/prompt_cli/jobs.py +21 -4
  44. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +10 -1
  45. package/runtime/python/okstra_ctl/analysis_inputs.py +0 -39
  46. package/runtime/python/okstra_ctl/analysis_scope.py +31 -0
  47. package/runtime/python/okstra_ctl/approval_decisions.py +32 -2
  48. package/runtime/python/okstra_ctl/asset_roots.py +19 -0
  49. package/runtime/python/okstra_ctl/assignment_resolver.py +8 -0
  50. package/runtime/python/okstra_ctl/blocking_checks.py +7 -0
  51. package/runtime/python/okstra_ctl/code_review_target.py +92 -6
  52. package/runtime/python/okstra_ctl/consumers.py +12 -0
  53. package/runtime/python/okstra_ctl/dispatch_checkpoints.py +121 -0
  54. package/runtime/python/okstra_ctl/dispatch_core.py +54 -32
  55. package/runtime/python/okstra_ctl/dispatch_state.py +34 -5
  56. package/runtime/python/okstra_ctl/doctor.py +2 -1
  57. package/runtime/python/okstra_ctl/domain/provider.py +5 -0
  58. package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -2
  59. package/runtime/python/okstra_ctl/domain/write_policy.py +2 -1
  60. package/runtime/python/okstra_ctl/execution_mutation_audit.py +46 -9
  61. package/runtime/python/okstra_ctl/handoff.py +11 -466
  62. package/runtime/python/okstra_ctl/handoff_error.py +5 -0
  63. package/runtime/python/okstra_ctl/implementation_direction.py +0 -477
  64. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +13 -1
  65. package/runtime/python/okstra_ctl/lead_progress.py +33 -1
  66. package/runtime/python/okstra_ctl/manager_view.py +26 -19
  67. package/runtime/python/okstra_ctl/model_io/lines.py +21 -4
  68. package/runtime/python/okstra_ctl/models.py +4 -1
  69. package/runtime/python/okstra_ctl/operation_invocation.py +11 -2
  70. package/runtime/python/okstra_ctl/option_comparison.py +3 -165
  71. package/runtime/python/okstra_ctl/option_votes.py +3 -191
  72. package/runtime/python/okstra_ctl/paths.py +8 -6
  73. package/runtime/python/okstra_ctl/phases/catalog.py +56 -12
  74. package/runtime/python/okstra_ctl/phases/change_impact_analysis/boundary.json +11 -0
  75. package/runtime/python/okstra_ctl/phases/change_impact_analysis/entry.py +39 -0
  76. package/runtime/python/okstra_ctl/{report_html/view_models/change_impact_analysis.py → phases/change_impact_analysis/report.py} +3 -3
  77. package/runtime/python/okstra_ctl/phases/change_impact_analysis/spec.md +26 -0
  78. package/runtime/python/okstra_ctl/phases/change_impact_analysis/validation.py +23 -0
  79. package/runtime/python/okstra_ctl/phases/error_analysis/__init__.py +1 -0
  80. package/runtime/python/okstra_ctl/phases/error_analysis/boundary.json +9 -0
  81. package/runtime/{prompts/profiles/error-analysis.md → python/okstra_ctl/phases/error_analysis/profile.md} +2 -2
  82. package/runtime/python/okstra_ctl/{report_html/view_models/error_analysis.py → phases/error_analysis/report.py} +9 -8
  83. package/runtime/{templates/reports → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis-input.template.md +1 -1
  84. package/runtime/python/okstra_ctl/phases/error_analysis/spec.md +118 -0
  85. package/runtime/python/okstra_ctl/phases/error_analysis/validation.py +241 -0
  86. package/runtime/python/okstra_ctl/phases/feature_analysis/__init__.py +1 -0
  87. package/runtime/python/okstra_ctl/phases/feature_analysis/boundary.json +8 -0
  88. package/runtime/python/okstra_ctl/phases/feature_analysis/entry.py +63 -0
  89. package/runtime/python/okstra_ctl/{report_html/view_models/feature_analysis.py → phases/feature_analysis/report.py} +12 -5
  90. package/runtime/python/okstra_ctl/phases/feature_analysis/spec.md +22 -0
  91. package/runtime/python/okstra_ctl/phases/feature_analysis/validation.py +27 -0
  92. package/runtime/python/okstra_ctl/phases/feature_analysis/wizard.py +95 -0
  93. package/runtime/python/okstra_ctl/phases/final_verification/boundary.json +8 -0
  94. package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -2
  95. package/runtime/{templates/reports → python/okstra_ctl/phases/final_verification/report_assets}/final-verification-input.template.md +1 -1
  96. package/runtime/python/okstra_ctl/phases/final_verification/spec.md +1 -1
  97. package/runtime/python/okstra_ctl/phases/implementation/__init__.py +1 -0
  98. package/runtime/python/okstra_ctl/phases/implementation/boundary.json +17 -0
  99. package/runtime/python/okstra_ctl/{implementation_stage.py → phases/implementation/entry.py} +22 -10
  100. package/runtime/{prompts/host-orchestration/implementation.md → python/okstra_ctl/phases/implementation/host-rules.md} +1 -1
  101. package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-deliverable.md +1 -1
  102. package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-executor.md +4 -3
  103. package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-verifier.md +18 -7
  104. package/runtime/{prompts/profiles/implementation.md → python/okstra_ctl/phases/implementation/profile.md} +5 -5
  105. package/runtime/python/okstra_ctl/{report_html/view_models/implementation.py → phases/implementation/report.py} +3 -3
  106. package/runtime/{templates/reports → python/okstra_ctl/phases/implementation/report_assets}/implementation-input.template.md +1 -1
  107. package/runtime/python/okstra_ctl/phases/implementation/spec.md +238 -0
  108. package/runtime/python/okstra_ctl/phases/implementation/validation.py +205 -0
  109. package/runtime/python/okstra_ctl/phases/implementation/wizard.py +39 -0
  110. package/runtime/python/okstra_ctl/phases/implementation_option_selection/__init__.py +1 -0
  111. package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +80 -0
  112. package/runtime/python/okstra_ctl/phases/implementation_option_selection/boundary.json +10 -0
  113. package/runtime/python/okstra_ctl/phases/implementation_option_selection/comparison.py +168 -0
  114. package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +27 -0
  115. package/runtime/{prompts/profiles/implementation-option-selection.md → python/okstra_ctl/phases/implementation_option_selection/profile.md} +3 -3
  116. package/runtime/python/okstra_ctl/{report_html/view_models/implementation_option_selection.py → phases/implementation_option_selection/report.py} +2 -2
  117. package/runtime/python/okstra_ctl/phases/implementation_option_selection/spec.md +83 -0
  118. package/runtime/python/okstra_ctl/{implementation_options.py → phases/implementation_option_selection/validation.py} +3 -3
  119. package/runtime/python/okstra_ctl/phases/implementation_option_selection/votes.py +194 -0
  120. package/runtime/python/okstra_ctl/phases/implementation_planning/__init__.py +1 -0
  121. package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +2345 -0
  122. package/runtime/python/okstra_ctl/phases/implementation_planning/boundary.json +12 -0
  123. package/runtime/python/okstra_ctl/phases/implementation_planning/entry.py +161 -0
  124. package/runtime/python/okstra_ctl/phases/implementation_planning/guidance.py +178 -0
  125. package/runtime/{prompts/lead → python/okstra_ctl/phases/implementation_planning/instructions}/plan-body-verification.md +61 -51
  126. package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +3295 -0
  127. package/runtime/{prompts/profiles/implementation-planning.md → python/okstra_ctl/phases/implementation_planning/profile.md} +74 -25
  128. package/runtime/python/okstra_ctl/phases/implementation_planning/report.py +237 -0
  129. package/runtime/{templates/reports → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning-input.template.md +2 -2
  130. package/runtime/python/okstra_ctl/phases/implementation_planning/spec.md +204 -0
  131. package/runtime/python/okstra_ctl/phases/implementation_planning/validation.py +597 -0
  132. package/runtime/python/okstra_ctl/phases/implementation_planning/wizard.py +166 -0
  133. package/runtime/python/okstra_ctl/phases/improvement_discovery/boundary.json +12 -0
  134. package/runtime/python/okstra_ctl/{improvement_lenses.py → phases/improvement_discovery/lenses.py} +1 -6
  135. package/runtime/{prompts/profiles/improvement-discovery.md → python/okstra_ctl/phases/improvement_discovery/profile.md} +5 -5
  136. package/runtime/python/okstra_ctl/{report_html/view_models/improvement_discovery.py → phases/improvement_discovery/report.py} +3 -3
  137. package/runtime/{templates/reports → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery-input.template.md +1 -2
  138. package/runtime/python/okstra_ctl/phases/improvement_discovery/spec.md +29 -0
  139. package/runtime/{validators/validate_improvement_report.py → python/okstra_ctl/phases/improvement_discovery/validation.py} +5 -14
  140. package/runtime/python/okstra_ctl/phases/project_analysis/__init__.py +1 -0
  141. package/runtime/python/okstra_ctl/phases/project_analysis/boundary.json +8 -0
  142. package/runtime/python/okstra_ctl/phases/project_analysis/entry.py +11 -0
  143. package/runtime/python/okstra_ctl/{report_html/view_models/project_analysis.py → phases/project_analysis/report.py} +3 -3
  144. package/runtime/python/okstra_ctl/phases/project_analysis/spec.md +33 -0
  145. package/runtime/python/okstra_ctl/phases/project_analysis/validation.py +55 -0
  146. package/runtime/python/okstra_ctl/phases/release_handoff/__init__.py +1 -0
  147. package/runtime/python/okstra_ctl/phases/release_handoff/boundary.json +17 -0
  148. package/runtime/python/okstra_ctl/phases/release_handoff/entry.py +147 -0
  149. package/runtime/python/okstra_ctl/phases/release_handoff/operations.py +446 -0
  150. package/runtime/{prompts/profiles/release-handoff.md → python/okstra_ctl/phases/release_handoff/profile.md} +3 -3
  151. package/runtime/python/okstra_ctl/{report_html/view_models/release_handoff.py → phases/release_handoff/report.py} +3 -3
  152. package/runtime/{templates/reports → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff-input.template.md +1 -1
  153. package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +233 -0
  154. package/runtime/python/okstra_ctl/phases/release_handoff/wizard.py +84 -0
  155. package/runtime/python/okstra_ctl/phases/requirements_discovery/__init__.py +1 -0
  156. package/runtime/python/okstra_ctl/phases/requirements_discovery/boundary.json +9 -0
  157. package/runtime/{prompts/profiles/requirements-discovery.md → python/okstra_ctl/phases/requirements_discovery/profile.md} +2 -3
  158. package/runtime/python/okstra_ctl/{report_html/view_models/requirements_discovery.py → phases/requirements_discovery/report.py} +3 -3
  159. package/runtime/python/okstra_ctl/phases/requirements_discovery/spec.md +132 -0
  160. package/runtime/{validators/validate_fanout.py → python/okstra_ctl/phases/requirements_discovery/validation.py} +11 -12
  161. package/runtime/python/okstra_ctl/phases/technical_verification/__init__.py +1 -0
  162. package/runtime/python/okstra_ctl/phases/technical_verification/boundary.json +9 -0
  163. package/runtime/python/okstra_ctl/phases/technical_verification/entry.py +100 -0
  164. package/runtime/{prompts/profiles/technical-verification.md → python/okstra_ctl/phases/technical_verification/profile.md} +2 -2
  165. package/runtime/python/okstra_ctl/{report_html/view_models/technical_verification.py → phases/technical_verification/report.py} +2 -2
  166. package/runtime/python/okstra_ctl/phases/technical_verification/spec.md +37 -0
  167. package/runtime/python/okstra_ctl/phases/technical_verification/validation.py +90 -0
  168. package/runtime/python/okstra_ctl/plan_approval.py +70 -0
  169. package/runtime/python/okstra_ctl/plan_items_cli.py +2 -2130
  170. package/runtime/python/okstra_ctl/process_group.py +118 -0
  171. package/runtime/python/okstra_ctl/profile_show.py +3 -3
  172. package/runtime/python/okstra_ctl/render.py +15 -4
  173. package/runtime/python/okstra_ctl/report_assembly.py +28 -92
  174. package/runtime/python/okstra_ctl/report_finalize.py +106 -2
  175. package/runtime/python/okstra_ctl/report_html/context_links.py +1 -1
  176. package/runtime/python/okstra_ctl/report_projections.py +1 -36
  177. package/runtime/python/okstra_ctl/report_routing.py +23 -0
  178. package/runtime/python/okstra_ctl/report_synthesis_packet.py +4 -73
  179. package/runtime/python/okstra_ctl/report_validation_identity.py +38 -0
  180. package/runtime/python/okstra_ctl/report_views.py +1 -1
  181. package/runtime/python/okstra_ctl/run.py +68 -350
  182. package/runtime/python/okstra_ctl/run_artifact_prune.py +200 -0
  183. package/runtime/python/okstra_ctl/stage_map.py +13 -0
  184. package/runtime/python/okstra_ctl/team.py +108 -9
  185. package/runtime/python/okstra_ctl/technical_verification_facts.py +52 -0
  186. package/runtime/python/okstra_ctl/wizard/__init__.py +31 -31
  187. package/runtime/python/okstra_ctl/wizard/api.py +18 -0
  188. package/runtime/python/okstra_ctl/wizard/outcome.py +3 -12
  189. package/runtime/python/okstra_ctl/wizard/registry.py +20 -12
  190. package/runtime/python/okstra_ctl/wizard/steps_analysis.py +0 -97
  191. package/runtime/python/okstra_ctl/wizard/steps_options.py +8 -0
  192. package/runtime/python/okstra_ctl/wizard/steps_plan.py +10 -263
  193. package/runtime/python/okstra_ctl/wizard/steps_roles.py +2 -1
  194. package/runtime/python/okstra_ctl/work_categories.py +1 -1
  195. package/runtime/python/okstra_ctl/worker_dispatch.py +44 -3
  196. package/runtime/python/okstra_ctl/worker_prompt_contract.py +36 -0
  197. package/runtime/python/okstra_ctl/worker_prompt_policy.py +19 -0
  198. package/runtime/python/okstra_ctl/worker_runner.py +21 -3
  199. package/runtime/python/okstra_ctl/workflow.py +26 -143
  200. package/runtime/python/okstra_ctl/write_policy.py +57 -7
  201. package/runtime/python/okstra_project/dirs.py +14 -0
  202. package/runtime/python/okstra_project/resolver.py +2 -1
  203. package/runtime/schemas/execution-manifest-v2.schema.json +2 -1
  204. package/runtime/skills/okstra-brief-gen/SKILL.md +3 -3
  205. package/runtime/skills/okstra-code-review/SKILL.md +70 -32
  206. package/runtime/skills/okstra-code-review/references/review-calibration.md +26 -6
  207. package/runtime/skills/okstra-run/SKILL.md +3 -3
  208. package/runtime/templates/manager/view.template.html +18 -1
  209. package/runtime/templates/reports/quick-input.template.md +1 -1
  210. package/runtime/templates/reports/task-brief.template.md +1 -1
  211. package/runtime/validators/validate-brief.py +2 -2
  212. package/runtime/validators/validate-run.py +299 -3940
  213. package/runtime/validators/validate_analysis_report.py +14 -126
  214. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +0 -147
  215. package/runtime/python/okstra_ctl/technical_verification.py +0 -195
  216. /package/runtime/{prompts/profiles/change-impact-analysis.json → python/okstra_ctl/phases/change_impact_analysis/profile.json} +0 -0
  217. /package/runtime/{prompts/profiles/change-impact-analysis.md → python/okstra_ctl/phases/change_impact_analysis/profile.md} +0 -0
  218. /package/runtime/{templates/reports → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis-input.template.md +0 -0
  219. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis.template.html +0 -0
  220. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/change_impact_analysis/report_assets}/change-impact-analysis.template.md +0 -0
  221. /package/runtime/{prompts/profiles/error-analysis.json → python/okstra_ctl/phases/error_analysis/profile.json} +0 -0
  222. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis.template.html +0 -0
  223. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/error_analysis/report_assets}/error-analysis.template.md +0 -0
  224. /package/runtime/{prompts/profiles/feature-analysis.json → python/okstra_ctl/phases/feature_analysis/profile.json} +0 -0
  225. /package/runtime/{prompts/profiles/feature-analysis.md → python/okstra_ctl/phases/feature_analysis/profile.md} +0 -0
  226. /package/runtime/{templates/reports → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis-input.template.md +0 -0
  227. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis.template.html +0 -0
  228. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/feature_analysis/report_assets}/feature-analysis.template.md +0 -0
  229. /package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-diff-review.md +0 -0
  230. /package/runtime/{prompts/profiles → python/okstra_ctl/phases/implementation/instructions}/_implementation-self-check.md +0 -0
  231. /package/runtime/{prompts/profiles/implementation.json → python/okstra_ctl/phases/implementation/profile.json} +0 -0
  232. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation/report_assets}/implementation.template.html +0 -0
  233. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation/report_assets}/implementation.template.md +0 -0
  234. /package/runtime/{prompts/profiles/implementation-option-selection.json → python/okstra_ctl/phases/implementation_option_selection/profile.json} +0 -0
  235. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation_option_selection/report_assets}/implementation-option-selection.template.html +0 -0
  236. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation_option_selection/report_assets}/implementation-option-selection.template.md +0 -0
  237. /package/runtime/{prompts/host-orchestration/implementation-planning.md → python/okstra_ctl/phases/implementation_planning/host-rules.md} +0 -0
  238. /package/runtime/{prompts/profiles/implementation-planning.json → python/okstra_ctl/phases/implementation_planning/profile.json} +0 -0
  239. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning.template.html +0 -0
  240. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/implementation_planning/report_assets}/implementation-planning.template.md +0 -0
  241. /package/runtime/{prompts/profiles/improvement-discovery.json → python/okstra_ctl/phases/improvement_discovery/profile.json} +0 -0
  242. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery.template.html +0 -0
  243. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/improvement_discovery/report_assets}/improvement-discovery.template.md +0 -0
  244. /package/runtime/{prompts/profiles/project-analysis.json → python/okstra_ctl/phases/project_analysis/profile.json} +0 -0
  245. /package/runtime/{prompts/profiles/project-analysis.md → python/okstra_ctl/phases/project_analysis/profile.md} +0 -0
  246. /package/runtime/{templates/reports → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis-input.template.md +0 -0
  247. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis.template.html +0 -0
  248. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/project_analysis/report_assets}/project-analysis.template.md +0 -0
  249. /package/runtime/{prompts/profiles/release-handoff.json → python/okstra_ctl/phases/release_handoff/profile.json} +0 -0
  250. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff.template.html +0 -0
  251. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/release_handoff/report_assets}/release-handoff.template.md +0 -0
  252. /package/runtime/python/okstra_ctl/{fanout.py → phases/requirements_discovery/fanout.py} +0 -0
  253. /package/runtime/{prompts/profiles/requirements-discovery.json → python/okstra_ctl/phases/requirements_discovery/profile.json} +0 -0
  254. /package/runtime/{templates/reports → python/okstra_ctl/phases/requirements_discovery/report_assets}/fan-out-unit.template.md +0 -0
  255. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/requirements_discovery/report_assets}/requirements-discovery.template.html +0 -0
  256. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/requirements_discovery/report_assets}/requirements-discovery.template.md +0 -0
  257. /package/runtime/{prompts/profiles/technical-verification.json → python/okstra_ctl/phases/technical_verification/profile.json} +0 -0
  258. /package/runtime/{templates/reports/html/tasks → python/okstra_ctl/phases/technical_verification/report_assets}/technical-verification.template.html +0 -0
  259. /package/runtime/{templates/reports/md/tasks → python/okstra_ctl/phases/technical_verification/report_assets}/technical-verification.template.md +0 -0
@@ -0,0 +1,233 @@
1
+ # release-handoff process
2
+
3
+ This directory owns the handoff preparation, stage selection, PR planning, remote reconciliation, and report presentation. Shared verification evidence stays in `okstra_ctl.handoff_verification`; verification recording and CLI assembly stay in `okstra_ctl.handoff`. Generic Git and stage lifecycle operations stay in their common modules.
4
+
5
+ Report shapes remain in `schemas/final-report-v2.0.schema.json` and `schemas/final-report-v3.0.schema.json`, under `releaseHandoff`. Routing remains in `prompts/lead/phase-routing.md`. The tests beside this specification exercise stage PR bases, origin reconciliation, checkout occupancy, conflict probes, and optional report fields. Common `tests/handoff/` tests cover verification recording and the prepare/CLI boundary.
6
+
7
+ ## Guarantees
8
+
9
+ | ID | Guarantee | Enforcement |
10
+ |---|---|---|
11
+ | RH-1 | A PR carries a release-ready stage's recorded commit and dependency base | `scripts/okstra_ctl/phases/release_handoff/operations.py::pr_plan` |
12
+ | RH-2 | Diverged origin branches are rejected without force-pushing | `scripts/okstra_ctl/phases/release_handoff/tests/test_handoff_pr_plan.py::test_pr_plan_refuses_a_branch_it_cannot_deliver` |
13
+ | RH-3 | An actively reserved stage is not handed to the main worktree | `scripts/okstra_ctl/phases/release_handoff/tests/test_local_checkout_occupancy.py::test_an_active_stage_row_is_refused` |
14
+ | RH-4 | Missing optional report fields do not prevent rendering | `scripts/okstra_ctl/phases/release_handoff/tests/test_release_handoff_optional_keys.py::test_the_shape_a_skipped_run_writes_renders_end_to_end` |
15
+
16
+ ## Index
17
+
18
+ - [1. Purpose](#1-purpose)
19
+ - [2. okstra-run wizard flow](#2-okstra-run-wizard-flow)
20
+ - [3. prepare stage](#3-prepare-stage)
21
+ - [4. entry gate](#4-entry-gate)
22
+ - [5. lead-only execution flow](#5-lead-only-execution-flow)
23
+ - [6. PR template resolution](#6-pr-template-resolution)
24
+ - [7. Deliverables](#7-deliverables)
25
+ - [8. Forbidden actions](#8-forbidden-actions)
26
+ - [9. Verified code](#9-verified-code)
27
+
28
+ ## 1. Purpose
29
+
30
+ `release-handoff` is the terminal phase that pushes already-committed implementation stages with a release-ready verdict, or hands them off as pull requests. **One stage is one PR.** The PR head is that stage's stack branch; the base comes from its `depends-on` — the release base for a stage with no live dependency, the predecessor's branch for a stage with one (a stacked PR), and a branch merging the predecessors for a stage with several. The only commits this phase may create are the merge commits `okstra handoff pr-plan` makes on such a merge-base branch.
31
+
32
+ Because the PRs are a stack, they must be merged in ascending stage order with a merge commit or a rebase-merge. A squash replaces the commits the next PR's base points at, so the stack breaks — every PR body states this, and the lead never merges.
33
+
34
+ This phase has no worker dispatch. It does not use a provider or report-writer roster; the host-native Okstra lead performs git/gh inspection, user questions, the PR draft, and the final report inline.
35
+
36
+ ## 2. okstra-run wizard flow
37
+
38
+ ```mermaid
39
+ flowchart TD
40
+ Start[/okstra-run/] --> Common[common task identity flow]
41
+ Common --> Type[task-type = release-handoff]
42
+ Type --> Plan[approved plan auto/pick]
43
+ Plan --> Scope[handoff stage pick<br/>eligible stages, one PR each]
44
+ Scope --> Worktree{active task worktree?}
45
+ Worktree -->|yes| RoleCount[role-count min..max<br/>omit uses recommended; skip if min==max]
46
+ Worktree -->|no| BaseRef[base-ref pick/text]
47
+ BaseRef --> RoleCount
48
+ RoleCount --> RoleModel[role-model provider/model per slot]
49
+ RoleModel --> RoleAdd[min=0 roles via role-add only<br/>default skip]
50
+ RoleAdd --> Extras[directive, related tasks, clarification]
51
+ Extras --> Template[PR template override?]
52
+ Template --> TemplateScope[save template to project/global?]
53
+ TemplateScope --> Confirm[confirmation]
54
+ Confirm --> Render[render-bundle]
55
+ ```
56
+
57
+ `release-handoff` has no analysis-worker dispatch. Launch selection still shows any applicable role-count / role-model steps; current-session lead is this session and is listed on the confirmation summary. There is no provider roster multi-pick and no `Use defaults / Customize` fork. Dynamic verifiers are not chosen at launch. `--workers` is not a launch picker, and the runtime forces the worker list to empty. The wizard outcome's `renderArgs` includes `pr-template-path` only for release-handoff. Scope selection finishes before prepare, and the project/global save runs before `render-bundle` via the `config.set pr-template-path` action of `outcome.persistActions[]`. Only the stages that were marked `verified` by a release-ready verification in the Stage Lifecycle Snapshot and are not yet covered by a `pr` become candidates; leaving `--stages` empty takes all of them, and the wizard picker offers an all-stages option beside the individual ones. Both verification scopes mark a stage: a single-stage report marks its own stage, and a whole-task report marks every stage in its `stageReports` whose completed commit the verified head contains.
58
+
59
+ Note that this phase is also a target of task worktree provisioning. The normal flow reuses the implementation/final-verification result of the same task-key. Starting a new task may create a new branch, and it is likely to be blocked at the entry gate's "implementation commit exists" condition.
60
+
61
+ ## 3. prepare stage
62
+
63
+ ```mermaid
64
+ sequenceDiagram
65
+ participant W as okstra-run
66
+ participant P as prepare_task_bundle
67
+ participant T as PR template resolver
68
+ participant WT as worktree registry
69
+ participant FS as task artifacts
70
+
71
+ W->>P: task-type=release-handoff, approved-plan, stages csv, optional pr-template-path
72
+ P->>P: enforce stage eligibility (Stage Lifecycle Snapshot)
73
+ P->>FS: generate release-handoff-input.md (cited verification reports)
74
+ P->>P: force workers=[]
75
+ P->>T: resolve PR template
76
+ P->>WT: reuse or provision task worktree
77
+ P->>FS: write manifests with empty roster
78
+ P->>FS: expose PR_TEMPLATE_PATH and PR_TEMPLATE_SOURCE
79
+ P-->>W: lead prompt for current session
80
+ ```
81
+
82
+ The profile has no `Required workers:` block, and `run.py` also empties the worker override for `release-handoff`. So not going through the general TeamCreate / convergence / report-writer flow of `prompts/lead/okstra-lead-contract.md` is the intended behavior.
83
+
84
+ ## 4. entry gate
85
+
86
+ ```mermaid
87
+ flowchart TD
88
+ Brief[release-handoff-input.md<br/>generated by prepare] --> Source{Source Verification Report present?}
89
+ Source -->|no| Block[blocked<br/>route final-verification]
90
+ Source -->|yes| Verdict{Verdict Token == accepted?}
91
+ Verdict -->|no| Block
92
+ Verdict -->|yes| Eligible[each selected stage<br/>verified and not in PR]
93
+ Eligible --> Status{git status --short clean?}
94
+ Status -->|no| Dirty[blocked<br/>dirty tree]
95
+ Status -->|yes| Commits{each stage has commits<br/>over its own base?}
96
+ Commits -->|no| ImplBlock[blocked<br/>route implementation]
97
+ Commits -->|yes| Ready[handoff questions may begin]
98
+ ```
99
+
100
+ Before asking the user whether to push/PR, the lead confirms the following.
101
+
102
+ - The `## Source Verification Report` of the input document (`release-handoff-input.md`) generated by prepare lists the selected stages (`HANDOFF_STAGES`) and the cited report table. The brief is the input of the entry phase, so it does not exist in release-handoff — the user's stage selection finishes before prepare via the wizard `handoff_stage_pick` or the CLI `--stages` (empty takes every eligible stage).
103
+ - Each cited report must be the latest validated execution for the run it belongs to — the stage's own run for a single-stage report, the task's run for a whole-task one — and must match the recorded implementation commit. A newer unfinished, broken, or blocked execution prevents fallback to an older success. Prepare and `okstra handoff pr-plan` re-check this evidence. A new implementation start or completion invalidates the earlier approval.
104
+ - The working tree is clean.
105
+ - The current branch is recorded as evidence, not used as a PR head: every head comes from `okstra handoff pr-plan`. A release base branch such as `main`, `master`, `prod`, `preprod`, `staging`, or `dev` is never pushed.
106
+ - Each stage's `<base_commit>..<head_commit>` range is non-empty.
107
+
108
+ `accepted` is release-ready. `conditional-accept` is release-ready only when its non-empty condition list explicitly sets every `blocksReleaseHandoff` to `false`; those conditions remain in the generated input and PR body. `blocked`, missing conditions, and ambiguous verdicts stop delivery. `okstra_ctl.release_gate.release_handoff_allowed` owns this rule.
109
+
110
+ Verification targets are preserved per execution under the run's state directory. Old records without provable task/stage/commit evidence require re-verification; they are not rewritten. The checks are enforced by `okstra_ctl.handoff_verification`, `consumers.verified_accepted_stages`, and the handoff regression tests.
111
+
112
+ ## 5. lead-only execution flow
113
+
114
+ ```mermaid
115
+ stateDiagram-v2
116
+ [*] --> Gate: entry gate
117
+ Gate --> Q1: action selection
118
+ Q1 --> LocalCheckout: local checkout
119
+ Q1 --> Skip: skip
120
+ Q1 --> Q2: push + PR
121
+ state "okstra handoff pr-plan (head/base per stage)" as Plan
122
+ Q2 --> Plan: choose release base
123
+ Plan --> Probe: rows ready
124
+ Probe --> Q3: no conflict
125
+ Probe --> Conflict: conflict detected
126
+ Conflict --> Q2: change base branch
127
+ Conflict --> Q3: proceed anyway
128
+ Conflict --> Cancel: cancel
129
+ Q3 --> Push: use as-is or edit then proceed
130
+ Q3 --> Cancel: cancel
131
+ Push --> ReuseOrCreate: push each branch in stage order
132
+ ReuseOrCreate --> FinalReport: gh pr list / gh pr create per stage
133
+ LocalCheckout --> FinalReport
134
+ Skip --> FinalReport
135
+ Cancel --> FinalReport
136
+ FinalReport --> [*]
137
+ ```
138
+
139
+ User interaction is exactly three steps.
140
+
141
+ 1. Q1 action: `local checkout`, `push + PR`, `skip`
142
+ 2. Q2 release base: a branch from the profile menu such as `staging`, `preprod`, `main`, or Enter directly
143
+ 3. Q3 PR title/body: every stage's draft in one question — `use as-is`, `edit then proceed`, `cancel`
144
+
145
+ The merge-conflict probe happens only for `push + PR`, once per stage against that stage's own base.
146
+
147
+ `local checkout` takes one stage and hands that stage's branch to the main worktree; there is no whole-task target.
148
+
149
+ ```mermaid
150
+ flowchart TD
151
+ PushPR[push + PR selected] --> Fetch[git fetch origin chosen-base]
152
+ Fetch --> MergeTree[git merge-tree --write-tree<br/>stage head vs its own PR base]
153
+ MergeTree --> Conflict{any stage conflicts?}
154
+ Conflict -->|no| Draft[show PR draft]
155
+ Conflict -->|yes| Ask[ask proceed/change base/cancel]
156
+ Ask -->|proceed anyway| Draft
157
+ Ask -->|change base branch| Base[return to Q2]
158
+ Ask -->|cancel| Report[final report without push/PR]
159
+ ```
160
+
161
+ The probe must not change the working tree. `git merge`, `git rebase`, and `git pull` are not part of this probe.
162
+
163
+ ## 6. PR template resolution
164
+
165
+ ```mermaid
166
+ flowchart TD
167
+ Override[--pr-template-path from wizard] --> Chosen{exists?}
168
+ Chosen -->|yes| UseOverride[use override]
169
+ Chosen -->|no| Project[project config template]
170
+ Project -->|exists| UseProject[use project template]
171
+ Project -->|missing| Global[global config template]
172
+ Global -->|exists| UseGlobal[use global template]
173
+ Global -->|missing| Default[okstra skill default template]
174
+ ```
175
+
176
+ When the user picks a template on the customize path, the okstra-run skill performs the project/global scope save before render-bundle. The runtime puts the resolved `PR_TEMPLATE_PATH` and `PR_TEMPLATE_SOURCE` into the run context, and the lead reads this file as-is, removes the HTML comments, and fills the placeholders. The section structure must not be hard-coded.
177
+
178
+ ## 7. Deliverables
179
+
180
+ ```mermaid
181
+ flowchart TD
182
+ Verdict[Source Verification Report<br/>accepted token] --> Report[release-handoff final report]
183
+ State[feature branch + clean status] --> Report
184
+ User[Q1/Q2/Q2b/Q3 user selections] --> Report
185
+ Commands[git/gh commands + exit codes] --> Report
186
+ Commits[git log base..HEAD commit list] --> Report
187
+ Probe[Merge Conflict Probe] --> Report
188
+ PR[one PR row per stage:<br/>created / reused / skipped] --> Report
189
+ Report --> Done[routing recommendation: done]
190
+ ```
191
+
192
+ The final report requires at least the following.
193
+
194
+ - per selected stage, the originating final-verification report path and its quoted verdict row
195
+ - the selected stages and the chosen release base
196
+ - the pr-plan rows: stage, head branch, base kind, base branch — the merge order of the stack
197
+ - the run's current branch and run start `git status --short`
198
+ - record of user selections
199
+ - all executed git/gh commands and exit codes
200
+ - the implementation commit list, attributed per stage
201
+ - merge-conflict probe result
202
+ - one PR outcome row per stage: created, reused, or skipped
203
+ - routing recommendation `done`
204
+
205
+ ## 8. Forbidden actions
206
+
207
+ ```mermaid
208
+ flowchart TD
209
+ RH[release-handoff] --> Allowed[read git/gh, fetch base, merge-tree probe,<br/>push feature branch, create/reuse PR]
210
+ RH -. forbidden .-> Commit[git add / commit / stash]
211
+ RH -. forbidden .-> Force[force push or +refspec]
212
+ RH -. forbidden .-> BasePush[push directly to a release base branch]
213
+ RH -. forbidden .-> NoVerify[--no-verify / -n]
214
+ RH -. forbidden .-> Publish[release publish / deploy]
215
+ RH -. forbidden .-> Edit[source edit]
216
+ RH -. forbidden .-> Team[TeamCreate or Agent dispatch]
217
+ RH -. forbidden .-> Merge[gh pr merge or squash-merge]
218
+ RH -. forbidden .-> Rewrite[rebase / amend / cherry-pick a stage branch]
219
+ ```
220
+
221
+ A failed `git push` must not be retried with weaker safeguards. When a failure such as non-fast-forward occurs, stop and take the user's instruction, and `--force`-family flags are forbidden even if the user requests them.
222
+
223
+ ## 9. Verified code
224
+
225
+ - [`profile.md`](profile.md)
226
+ - [`report_assets/release-handoff-input.template.md`](report_assets/release-handoff-input.template.md)
227
+ - [`skills/okstra-run/SKILL.md`](../../../../skills/okstra-run/SKILL.md)
228
+ - [`scripts/okstra_ctl/wizard/`](../../../../scripts/okstra_ctl/wizard/)
229
+ - [`scripts/okstra_ctl/run.py`](../../../../scripts/okstra_ctl/run.py)
230
+ - [`scripts/okstra_ctl/pr_template.py`](../../../../scripts/okstra_ctl/pr_template.py)
231
+ - [`src/commands/lifecycle/config.mts`](../../../../src/commands/lifecycle/config.mts)
232
+ - [`scripts/okstra_ctl/handoff.py`](../../../../scripts/okstra_ctl/handoff.py)
233
+ - [`scripts/okstra_ctl/worktree/`](../../../../scripts/okstra_ctl/worktree/)
@@ -0,0 +1,84 @@
1
+ """검증된 단계의 전달 선택과 PR 템플릿 저장 요청."""
2
+ from __future__ import annotations
3
+
4
+ from typing import TYPE_CHECKING, Callable, Optional
5
+
6
+ if TYPE_CHECKING:
7
+ from okstra_ctl.wizard.state import Prompt, WizardState
8
+
9
+ ALL_ELIGIBLE_STAGES = "all"
10
+
11
+
12
+ def _handoff_msgs(state: WizardState) -> dict:
13
+ """handoff_stage_pick 의 JSON 텍스트 묶음 (label 미사용 조회용)."""
14
+ from okstra_ctl.wizard.prompts import _p
15
+
16
+ return _p(state.workspace_root, "handoff_stage_pick", blocked="")
17
+
18
+
19
+ def build_handoff_stage_pick(state: WizardState, eligibility: list) -> Prompt:
20
+ from okstra_ctl.wizard.ids import S_HANDOFF_STAGE_PICK
21
+ from okstra_ctl.wizard.state import Option, Prompt, WizardError
22
+ from okstra_ctl.wizard.prompts import _opt, _p
23
+
24
+ elig = eligibility
25
+ eligible = [e for e in elig if e["eligible"]]
26
+ blocked = [e for e in elig if not e["eligible"]]
27
+ msgs = _handoff_msgs(state)
28
+ blocked_summary = ("; ".join(
29
+ f"stage {e['stage']} ({', '.join(e['reasons'])})" for e in blocked)
30
+ or msgs["labels"]["blocked_none"])
31
+ if not eligible:
32
+ raise WizardError(
33
+ msgs["errors"]["nothing_eligible"].format(blocked=blocked_summary))
34
+ t = _p(state.workspace_root, "handoff_stage_pick", blocked=blocked_summary)
35
+ stage_label = t["labels"]["stage"]
36
+ options: list[Option] = []
37
+ if len(eligible) > 1:
38
+ options.append(_opt(ALL_ELIGIBLE_STAGES, t["labels"]["all_stages"].format(
39
+ stages=", ".join(str(e["stage"]) for e in eligible))))
40
+ for e in eligible:
41
+ deps = ", ".join(str(d) for d in e["depends_on"]) or "-"
42
+ options.append(_opt(str(e["stage"]),
43
+ stage_label.format(stage=e["stage"], deps=deps)))
44
+ return Prompt(
45
+ step=S_HANDOFF_STAGE_PICK, kind="pick", multi=True,
46
+ label=t["label"], options=options,
47
+ echo_template=t["echo_template"],
48
+ )
49
+
50
+
51
+ def submit_handoff_stage_pick(state: WizardState, value: str, eligibility: Callable[[], list]) -> Optional[str]:
52
+ from okstra_ctl.wizard.state import WizardError
53
+
54
+ t = _handoff_msgs(state)
55
+ picks = [v.strip() for v in (value or "").split(",") if v.strip()]
56
+ if not picks:
57
+ raise WizardError(t["errors"]["none_selected"])
58
+ eligible = {str(e["stage"]) for e in eligibility()
59
+ if e["eligible"]}
60
+ if ALL_ELIGIBLE_STAGES in picks:
61
+ picks = sorted(eligible, key=int)
62
+ bad = [p for p in picks if p not in eligible]
63
+ if bad:
64
+ raise WizardError(t["errors"]["not_eligible"].format(
65
+ bad=", ".join(bad), eligible=", ".join(sorted(eligible))))
66
+ nums = sorted({int(p) for p in picks})
67
+ state.handoff_stages = ",".join(str(n) for n in nums)
68
+ return t["echo_variants"]["stages"].format(stages=state.handoff_stages)
69
+
70
+
71
+
72
+ def pr_template_persist_actions(state: WizardState) -> list[dict[str, str]]:
73
+ if not state.pr_template_path:
74
+ return []
75
+ if state.pr_template_scope not in ("project", "global"):
76
+ return []
77
+ return [
78
+ {
79
+ "command": "config.set",
80
+ "key": "pr-template-path",
81
+ "scope": state.pr_template_scope,
82
+ "value": state.pr_template_path,
83
+ }
84
+ ]
@@ -0,0 +1 @@
1
+ """요구사항 확인과 작업 분해 단계의 정책."""
@@ -0,0 +1,9 @@
1
+ {
2
+ "allowed": " - work-category classification (bugfix / feature / refactor / ops / improvement)\n - routing decision as the `routing` object `{nextTaskType, readyWhen, rationale}`: `nextTaskType` is `error-analysis` or `implementation-option-selection` and nothing else — this phase never hands off directly to planning or implementation. Prose alone does not route the task — Phase 7 projects `workflow.nextRecommendedPhase` from `nextTaskType`\n - missing-input list and clarification requests\n - approval / confirmation checkpoints recorded for the next phase\n - one endStateCoverage row per brief end-state id (this phase authors no goal of its own)",
3
+ "forbidden": [
4
+ "source code edits of any kind",
5
+ "implementation planning or detailed design beyond what is required to choose the next phase",
6
+ "executing builds, migrations, deployments, or any state-mutating command",
7
+ "starting `error-analysis`, `implementation-planning`, or `implementation` inside this run (each must be a separate run, and `implementation` additionally requires an approved `implementation-planning` deliverable)"
8
+ ]
9
+ }
@@ -24,7 +24,6 @@
24
24
  - state the reporter's rejection criteria, missing routing inputs, and the evidence boundary behind each recommendation
25
25
  - Primary focus areas:
26
26
  - classify the work as bugfix, feature, improvement, refactor, or ops
27
- - 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
28
27
  - capture the reporter's **rejection criteria** — the delivered outcome that would make this work wrong or unacceptable — as a routing input. Consume it from the brief's `Desired Outcome` / `Out of Scope` / `Source Material` when present; when it is absent AND it would change the classification (e.g. bugfix vs feature) or the next-phase choice, raise it as one `decision` clarification row with `Evidence checked: none — reporter intent`. Never infer it — this is a reporter-intent signal, the mirror of improvement-discovery's `Anti-goals`
29
28
  - record the rejection criteria in `requirementsDiscovery.rejectionCriteria` with the
30
29
  `source` that produced it. When it was absent and would not have changed the
@@ -41,11 +40,11 @@
41
40
  - A single-item / single-domain request is not fanned out (preserving the current single-routing behaviour).
42
41
  - When a request spans two or more domains, or has two or more independently-startable work items,
43
42
  publish each item as a `runs/requirements-discovery/fan-out/unit-<NNN>.md` packet.
44
- The packet follows the `templates/reports/fan-out-unit.template.md` format, and its frontmatter
43
+ The packet follows the `scripts/okstra_ctl/phases/requirements_discovery/report_assets/fan-out-unit.template.md` format, and its frontmatter
45
44
  `domain`(work-category 5-enum: bugfix / feature / refactor / ops / improvement),
46
45
  `depends-on`(an inline list of unit-ids within the same fan-out `[unit-001]`, or `[]` if none),
47
46
  `recommended-next-phase`(error-analysis | implementation-option-selection) are filled in.
48
- Each packet MUST carry a non-empty `## Requirement Provenance` section whose every bullet is `brief:EB-001` / `brief:PB-001` / `brief:EO-001` — an end-state id the brief declares — or `contract:<rule>`. When the brief pins ids, citing a heading is rejected: every brief carries the same generic headings, so a heading cannot say WHICH reporter line demanded this unit, and a fan-out unit becomes the brief for a whole downstream task. Only a brief authored before the end-state sections existed still takes the older `brief:<heading>` form, and there the heading must literally exist in it. `derived:` is not admissible in a packet — cross-packet derivation cannot be resolved from a single packet, so each unit anchors directly on the brief. A unit you cannot source that way is not a work item: raise it as a clarification row instead of publishing a packet for it. **Enforced:** `validators/validate_fanout.py` `_check_provenance`.
47
+ Each packet MUST carry a non-empty `## Requirement Provenance` section whose every bullet is `brief:EB-001` / `brief:PB-001` / `brief:EO-001` — an end-state id the brief declares — or `contract:<rule>`. When the brief pins ids, citing a heading is rejected: every brief carries the same generic headings, so a heading cannot say WHICH reporter line demanded this unit, and a fan-out unit becomes the brief for a whole downstream task. Only a brief authored before the end-state sections existed still takes the older `brief:<heading>` form, and there the heading must literally exist in it. `derived:` is not admissible in a packet — cross-packet derivation cannot be resolved from a single packet, so each unit anchors directly on the brief. A unit you cannot source that way is not a work item: raise it as a clarification row instead of publishing a packet for it. **Enforced:** `scripts/okstra_ctl/phases/requirements_discovery/validation.py` `_check_provenance`.
49
48
  - in `runs/requirements-discovery/fan-out/index.md`, list the packets in depends-on topological order
50
49
  as a numbered list (`1. unit-001`) (a generated view; explicitly do not hand-edit). The depends-on graph
51
50
  must be a DAG — `validate_fanout` rejects a cycle as a validation failure, so if a cycle appears,
@@ -1,9 +1,9 @@
1
1
  """Human-first requirements-discovery view model."""
2
2
  from __future__ import annotations
3
3
 
4
- from ..common import evidence_index
5
- from ..models import HumanReportView, VisualEdge, VisualNode
6
- from ..visualizations import decision_flow_figure
4
+ from okstra_ctl.report_html.common import evidence_index
5
+ from okstra_ctl.report_html.models import HumanReportView, VisualEdge, VisualNode
6
+ from okstra_ctl.report_html.visualizations import decision_flow_figure
7
7
 
8
8
  # Record fields this template anchors as `id-<row id>` (see
9
9
  # `HumanReportView.anchored_fields`); ids elsewhere land in the ledger.
@@ -0,0 +1,132 @@
1
+ # requirements-discovery process
2
+
3
+ ## Guarantees
4
+
5
+ | ID | Guarantee | Enforcement |
6
+ |---|---|---|
7
+ | RD-1 | Optional fan-out absence is accepted | `scripts/okstra_ctl/phases/requirements_discovery/validation.py::validate_fanout` |
8
+ | RD-2 | Each packet cites an admissible brief or contract source | `scripts/okstra_ctl/phases/requirements_discovery/validation.py::_check_provenance` |
9
+ | RD-3 | Dependency cycles and unknown units are rejected | `scripts/okstra_ctl/phases/requirements_discovery/fanout.py::topological_order` |
10
+ | RD-4 | The index lists every packet in dependency order | `scripts/okstra_ctl/phases/requirements_discovery/validation.py::validate_fanout` |
11
+ | RD-5 | Packet destinations use the common routing contract | `scripts/okstra_ctl/report_routing.py::fanout_routing_errors` |
12
+ | RD-6 | Human intent and terminology are resolved from inspected evidence | unenforced — worker instructions and lead review; no generic validator proves intent semantics |
13
+
14
+ ## Index
15
+
16
+ - [1. Purpose](#1-purpose)
17
+ - [2. okstra-run wizard flow](#2-okstra-run-wizard-flow)
18
+ - [3. prepare_task_bundle handling](#3-prepare_task_bundle-handling)
19
+ - [4. lead execution flow](#4-lead-execution-flow)
20
+ - [5. Deliverables and routing](#5-deliverables-and-routing)
21
+ - [6. Code reviewed](#6-code-reviewed)
22
+
23
+ ## 1. Purpose
24
+
25
+ `requirements-discovery` classifies the request before implementation. It determines which of bugfix, feature, improvement, refactor, or ops it is, and chooses whether the next safe phase is `error-analysis` or `implementation-option-selection`. Going directly to planning or implementation is not valid for a new direction. Implementation can only start once a selected direction has been expanded into a separately approved `implementation-planning` report.
26
+
27
+ ## 2. okstra-run wizard flow
28
+
29
+ ```mermaid
30
+ flowchart TD
31
+ Start[/okstra-run/] --> Check[ensure-installed / paths / check-project]
32
+ Check --> Pick{new task or existing task?}
33
+ Pick -->|new| Brief[brief path]
34
+ Brief --> Suggest{brief frontmatter suggestions?}
35
+ Suggest -->|yes| GroupPick[task-group pick]
36
+ Suggest -->|no| GroupText[task-group text]
37
+ GroupPick --> Id
38
+ GroupText --> Id
39
+ Id[task-id pick/text] --> Type[task-type = requirements-discovery]
40
+ Pick -->|existing| Type
41
+ Type --> Keep{existing brief?}
42
+ Keep -->|keep| Base
43
+ Keep -->|change/no brief| Brief
44
+ Type --> Base{active task worktree?}
45
+ Base -->|yes| RoleCount[role-count min..max<br/>omit uses recommended; skip if min==max]
46
+ Base -->|no| BaseRef[base-ref pick/text]
47
+ BaseRef --> RoleCount
48
+ RoleCount --> RoleModel[role-model provider/model per slot]
49
+ RoleModel --> RoleAdd[min=0 roles via role-add only<br/>default skip]
50
+ RoleAdd --> Extras[directive, related tasks, clarification]
51
+ Extras --> Confirm
52
+ Confirm --> Render[render-bundle]
53
+ ```
54
+
55
+ Launch selection uses role slots and model refs only: current-session lead is this session (listed on the confirmation summary), then each static role's count in `min..max` (default **recommended**; the count step is skipped when `min == max`), then one `provider/model` per slot. Roles with `min = 0` stay closed unless the user opens them with role-add (default skip). Duplicate model refs in the same role are rejected. There is no provider roster multi-pick and no `Use defaults / Customize` fork. Dynamic verifiers are not chosen at launch. `--workers` is a CLI compatibility input only, not a launch picker.
56
+
57
+ ## 3. prepare_task_bundle handling
58
+
59
+ ```mermaid
60
+ sequenceDiagram
61
+ participant W as wizard/render-bundle
62
+ participant P as prepare_task_bundle
63
+ participant Prof as requirements-discovery.md
64
+ participant Git as worktree registry
65
+ participant FS as task artifacts
66
+
67
+ W->>P: task-type=requirements-discovery, brief, base-ref, workers
68
+ P->>Prof: profile exists, Required workers parsed
69
+ P->>P: verify installation, upsert project.json
70
+ P->>P: resolve workers from profile + override
71
+ P->>Git: create or reuse task worktree
72
+ P->>P: expand _common-contract include
73
+ P->>FS: write instruction-set and manifests
74
+ P->>FS: render workflow currentPhase=requirements-discovery
75
+ P-->>W: prepared lead prompt
76
+ ```
77
+
78
+ There is no additional hard gate in the runtime for this phase alone. The important gates are: the profile file exists, the brief file exists, the worktree gate requiring base-ref to be resolvable at the first phase, and the gate requiring worker overrides to stay within the profile roster range.
79
+
80
+ ## 4. lead execution flow
81
+
82
+ ```mermaid
83
+ flowchart TD
84
+ P1[Phase 1 intake<br/>manifest, brief, profile, run manifest, team-state] --> P2[Phase 2 prompts]
85
+ P2 --> P3[Phase 3 TeamCreate]
86
+ P3 --> P4[Phase 4/5 dispatch analysers<br/>claude/codex + optional antigravity]
87
+ P4 --> C[Phase 5.5 convergence<br/>default maxRounds = 1]
88
+ C --> R[Phase 6 report-writer authors final report]
89
+ R --> P7[Phase 7 token usage, validate, persist]
90
+ ```
91
+
92
+ `requirements-discovery` has a convergence default of 1 round. This exception is stated in both `render._build_convergence_block()` and `prompts/lead/okstra-lead-contract.md`.
93
+
94
+ ## 5. Deliverables and routing
95
+
96
+ ```mermaid
97
+ flowchart LR
98
+ RD[requirements-discovery final report] --> Class[work-category classification]
99
+ RD --> Missing[missing materials / clarification items]
100
+ RD --> Domain[Domain Alignment<br/>terminology resolution]
101
+ RD --> Route{next safe phase}
102
+ Route --> EA[error-analysis]
103
+ Route --> IOS[implementation-option-selection]
104
+ Route -. invalid .-> Impl[implementation<br/>not allowed directly]
105
+ ```
106
+
107
+ The final report emphasizes the following in particular.
108
+
109
+ - evidence-backed routing decision
110
+ - missing input and uncertainty boundary
111
+ - the next phase and safe resume guidance
112
+ - canonical term resolution for `terminology:*` brief items
113
+ - if there is blocking input, `Blocks=next-phase` in the `## 1. Clarification Items` unified table
114
+
115
+ Non-goals are source edit, plan authoring, build, and deployment.
116
+
117
+ ## 6. Code reviewed
118
+
119
+ - [`skills/okstra-run/SKILL.md`](../../../../skills/okstra-run/SKILL.md)
120
+ - [`scripts/okstra_ctl/wizard/`](../../wizard)
121
+ - [`scripts/okstra_ctl/run.py`](../../run.py)
122
+ - [`scripts/okstra_ctl/workflow.py`](../../workflow.py)
123
+ - [`scripts/okstra_ctl/phases/requirements_discovery/profile.md`](profile.md)
124
+ - [`prompts/lead/okstra-lead-contract.md`](../../../../prompts/lead/okstra-lead-contract.md)
125
+
126
+ ## Phase ownership and validation
127
+
128
+ `profile.md` owns request discovery and decomposition instructions; `profile.json` owns roles. `validation.py` owns fan-out packet identity, work category, requirement provenance, dependency and index checks. `fanout.py` orders dependencies and rejects cycles and unknown units. `report.py` and `report_assets/` own the report body and fan-out packet template.
129
+
130
+ Common scope-provenance helpers continue to define brief end-state identifiers and citation grammar. Shared schemas remain in `schemas/final-report-v2.0.schema.json` and `schemas/final-report-v3.0.schema.json` under `requirementsDiscovery`. The common lead's `prompts/lead/phase-routing.md` owns destination selection; `report_routing.py` checks packet destinations.
131
+
132
+ `tests/test_fanout.py` checks real packet files, missing and invalid provenance, dependency cycles, missing dependencies and index order. The common validator calls `validate_requirements_discovery_fanout` only for this task type, preserving the optional fan-out behavior and the `requirements-discovery:` error prefix. Source inspection and human-intent decisions remain worker instructions rather than machine-enforced semantic claims.
@@ -6,25 +6,19 @@ and a `fan-out/` directory exists under the run dir.
6
6
  from __future__ import annotations
7
7
 
8
8
  import re
9
- import sys
10
9
  from dataclasses import dataclass, field
11
10
  from pathlib import Path
12
11
 
13
- _VALIDATORS_DIR = Path(__file__).resolve().parent
14
- for _ssot_dir in (_VALIDATORS_DIR.parent / "scripts", _VALIDATORS_DIR.parent / "python"):
15
- if _ssot_dir.is_dir() and str(_ssot_dir) not in sys.path:
16
- sys.path.insert(0, str(_ssot_dir))
17
-
18
- from okstra_ctl.work_categories import WORK_CATEGORIES # noqa: E402
19
- from okstra_ctl.fanout import topological_order, CycleError # noqa: E402
20
- from okstra_ctl.scope_provenance import ( # noqa: E402
12
+ from okstra_ctl.report_routing import fanout_routing_errors
13
+ from okstra_ctl.work_categories import WORK_CATEGORIES
14
+ from .fanout import topological_order, CycleError
15
+ from okstra_ctl.scope_provenance import (
21
16
  brief_citation_problem,
22
17
  brief_end_state_ids,
23
18
  brief_headings,
24
19
  parse_source,
25
20
  )
26
21
 
27
- _NEXT_PHASES = ("error-analysis", "implementation-planning")
28
22
  _UNIT_RE = re.compile(r"unit-\d{3}")
29
23
  # index.md 번호목록 항목에서만 unit-NNN 을 추출 — 내러티브 문장 중복 방지
30
24
  _INDEX_ENTRY_RE = re.compile(r"^\s*\d+\.\s+(unit-\d{3})\b", re.MULTILINE)
@@ -138,8 +132,7 @@ def validate_fanout(run_dir: Path, brief_path: Path | None = None) -> Validation
138
132
  errors.append(f"{pkt.name}: unit-id {uid!r} != filename stem {pkt.stem!r}")
139
133
  if fm.get("domain") not in WORK_CATEGORIES:
140
134
  errors.append(f"{pkt.name}: domain {fm.get('domain')!r} not in {WORK_CATEGORIES}")
141
- if fm.get("recommended-next-phase") not in _NEXT_PHASES:
142
- errors.append(f"{pkt.name}: recommended-next-phase not in {_NEXT_PHASES}")
135
+ errors.extend(fanout_routing_errors(pkt.name, fm.get("recommended-next-phase")))
143
136
  units[pkt.stem] = _parse_deps(fm.get("depends-on", ""))
144
137
 
145
138
  if not units:
@@ -167,3 +160,9 @@ def validate_fanout(run_dir: Path, brief_path: Path | None = None) -> Validation
167
160
  errors.append(f"index.md order {listed} is not the topological order {order}")
168
161
 
169
162
  return ValidationResult(ok=not errors, errors=errors)
163
+
164
+
165
+ def validate_requirements_discovery_fanout(run_dir, failures, brief_path=None) -> None:
166
+ """단계 산출물 오류를 공통 실행 검증의 오류 목록에 같은 접두로 전달한다."""
167
+ result = validate_fanout(Path(run_dir), brief_path)
168
+ failures.extend(f"requirements-discovery: {error}" for error in result.errors)
@@ -0,0 +1 @@
1
+ """기술 검증 단계의 입력과 관측 증거 정책."""
@@ -0,0 +1,9 @@
1
+ {
2
+ "allowed": " - falsifiable experiment plans for the frozen unresolved facts\n - dependency installation, source experiments, tests and builds only in this run's experiment copies\n - command logs, observed signals and per-fact supported/refuted/inconclusive/not-run results\n - return to implementation-option-selection with evidence, without adoption or plan approval",
3
+ "forbidden": [
4
+ "source edits or installs in the project checkout, task worktree, or another worker's experiment copy",
5
+ "production credentials, remote writes, deployments, migrations, publishing, commits or merging experiments into product branches",
6
+ "marking candidates feasible, selecting a direction, approving a plan, or declaring task acceptance",
7
+ "starting another lifecycle phase inside this run; return the evidence to implementation-option-selection"
8
+ ]
9
+ }