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
@@ -641,3 +641,16 @@ def _resolve_plan_path(task_root: Path, value: str) -> Path:
641
641
  # 예전에는 okstra_project.state 가 이 어댑터를 들고 okstra_ctl 을 함수 본문에서
642
642
  # 지연 import 했다. 하위 계층이 상위를 되짚는 배치였다. 읽는 대상이 okstra_ctl 의
643
643
  # Stage Map 과 consumer 상태이므로 소유자를 여기로 옮긴다.
644
+
645
+
646
+ def parse_stage_map_for_prepare(plan_path: str) -> list:
647
+ """Parse the approved plan into context records for execution consumers."""
648
+ from .prepare_error import PrepareError
649
+
650
+ try:
651
+ return stage_map_records(parse_stage_map_file(Path(plan_path)))
652
+ except StageMapError as exc:
653
+ raise PrepareError(
654
+ "approved-plan 의 Stage Map 을 신뢰할 수 없어 거부합니다 "
655
+ f"({plan_path}): {exc.reason}. plan 의 Stage Map 을 점검하세요."
656
+ ) from exc
@@ -1,7 +1,11 @@
1
1
  """Stage readiness and verification target rules.
2
2
 
3
- This module owns the policy that decides which Stage Map stage can run, which
4
- commit it branches from, and what final-verification should inspect. It keeps
3
+ This module owns the policy that decides which Stage Map stage can run and which
4
+ commit it branches from, plus the Git and ledger facts final-verification builds
5
+ its target from (whole-task integration, the stage that contains every other
6
+ done stage, teardown after the verdict). Which of those facts a
7
+ final-verification run uses is decided in
8
+ ``phases/final_verification/target.py``. It keeps
5
9
  the stage lifecycle rules behind one interface instead of leaking raw
6
10
  ``consumers.jsonl`` rows and git ancestry checks into prepare callers.
7
11
  """
@@ -10,13 +14,11 @@ from __future__ import annotations
10
14
  import heapq
11
15
  import subprocess
12
16
  import sys
13
- from dataclasses import dataclass, replace
17
+ from dataclasses import dataclass
14
18
  from pathlib import Path
15
19
  from typing import Any
16
20
 
17
- from .plan_run_root import plan_run_root_from_approved_plan
18
21
  from .prepare_error import PrepareError # noqa: F401 — 재노출(단일 참조점)
19
- from .stage_integrate import IntegrateResult
20
22
  from okstra_project.slug import slugify
21
23
 
22
24
 
@@ -37,32 +39,6 @@ class FinalVerificationTarget:
37
39
  reports: list[str]
38
40
 
39
41
 
40
- @dataclass(frozen=True)
41
- class FinalVerificationTargetRequest:
42
- """Semantic inputs needed to acquire one final-verification target."""
43
-
44
- project_root: Path
45
- project_id: str
46
- task_group: str
47
- task_id: str
48
- work_category: str
49
- approved_plan_path: Path
50
- stage: int | None
51
- stage_map: tuple[dict[str, Any], ...]
52
-
53
-
54
- @dataclass(frozen=True)
55
- class FinalVerificationTargetAcquisition:
56
- """Resolved target facts returned to the prepare adapter."""
57
-
58
- target: FinalVerificationTarget
59
- worktree_branch: str
60
- integration_result: IntegrateResult | None
61
- # whole-task 진입이 task worktree 안에서 꺼낸 중첩 stage worktree.
62
- # `{"stage": N, "from": <old path>, "to": <new path>}` 행.
63
- relocations: tuple[dict[str, Any], ...] = ()
64
-
65
-
66
42
  @dataclass(frozen=True)
67
43
  class StageLifecycle:
68
44
  stage: int
@@ -809,117 +785,6 @@ def resolve_and_integrate_whole_task(
809
785
  )
810
786
 
811
787
 
812
- def _resolve_single_stage_target(
813
- *,
814
- requested_stage: int,
815
- done_rows: list[dict[str, Any]],
816
- stage_base: str,
817
- stage_worktree_path: str,
818
- stage_head: str,
819
- stage_dirty: bool,
820
- ) -> FinalVerificationTarget:
821
- """Resolve single-stage final-verification target, enforcing all gates."""
822
- from .consumers import latest_done_by_stage
823
-
824
- n = requested_stage
825
- done_by_stage = latest_done_by_stage(done_rows)
826
- if n not in done_by_stage:
827
- raise StageTargetError(
828
- f"final-verification(single-stage): stage {n} not done — "
829
- f"run implementation --stage {n} first"
830
- )
831
- if not stage_worktree_path:
832
- raise StageTargetError(
833
- f"final-verification(single-stage): stage worktree not found for "
834
- f"stage {n} (torn down?) — use whole-task mode (--stage auto)"
835
- )
836
- if stage_dirty:
837
- raise StageTargetError(
838
- "final-verification: worktree has uncommitted source changes "
839
- "(outside .okstra/) — commit or stash before verifying"
840
- )
841
- return FinalVerificationTarget(
842
- scope="single-stage",
843
- base=stage_base,
844
- head=stage_head,
845
- worktree_path=stage_worktree_path,
846
- stages=[n],
847
- reports=[done_by_stage[n].get("report_path", "")],
848
- )
849
-
850
-
851
- def _read_final_verification_done_rows(
852
- request: FinalVerificationTargetRequest,
853
- registry_coordinates: tuple[str, str, str],
854
- ) -> list[dict[str, Any]]:
855
- from .consumers import backfill_done_from_carry, read_stage_consumer_state
856
- from .stage_reconcile import auto_reconcile_best_effort
857
-
858
- plan_run_root = plan_run_root_from_approved_plan(request.approved_plan_path)
859
- backfill_done_from_carry(plan_run_root)
860
- project_id, task_group, task_id = registry_coordinates
861
- auto_reconcile_best_effort(
862
- replace(
863
- request,
864
- project_id=project_id,
865
- task_group=task_group,
866
- task_id=task_id,
867
- ),
868
- plan_run_root,
869
- )
870
- return read_stage_consumer_state(plan_run_root).done_rows
871
-
872
-
873
- def _final_verification_registry_coordinates(
874
- request: FinalVerificationTargetRequest,
875
- ) -> tuple[str, str, str]:
876
-
877
- def segment(value: str) -> str:
878
- return slugify(value) or "_"
879
-
880
- return (
881
- segment(request.project_id),
882
- segment(request.task_group),
883
- segment(request.task_id),
884
- )
885
-
886
-
887
- def _acquire_single_stage_target(
888
- request: FinalVerificationTargetRequest,
889
- done_rows: list[dict[str, Any]],
890
- registry_coordinates: tuple[str, str, str],
891
- ) -> FinalVerificationTargetAcquisition:
892
- from . import worktree_registry
893
- from .worktree import _git, is_dirty_excluding_okstra
894
-
895
- stage = request.stage
896
- assert stage is not None
897
- row = worktree_registry.get_stage_row(*registry_coordinates, stage)
898
- worktree_path = (row or {}).get("worktree_path", "")
899
- head = ""
900
- if worktree_path and Path(worktree_path).is_dir():
901
- head_result = _git(worktree_path, "rev-parse", "HEAD")
902
- if head_result.returncode == 0:
903
- head = head_result.stdout.strip()
904
- if not head:
905
- worktree_path = ""
906
- target = _resolve_single_stage_target(
907
- requested_stage=stage,
908
- done_rows=done_rows,
909
- stage_base=(row or {}).get("base_ref", ""),
910
- stage_worktree_path=worktree_path,
911
- stage_head=head,
912
- stage_dirty=(
913
- is_dirty_excluding_okstra(worktree_path) if worktree_path else False
914
- ),
915
- )
916
- return FinalVerificationTargetAcquisition(
917
- target=target,
918
- worktree_branch=(row or {}).get("branch", ""),
919
- integration_result=None,
920
- )
921
-
922
-
923
788
  def single_containing_head(project_root: Path,
924
789
  heads: dict[int, str]) -> int | None:
925
790
  """주어진 stage 들의 commit 을 혼자서 모두 담고 있는 stage. 없으면 None.
@@ -952,117 +817,12 @@ def containing_stage(project_root: Path,
952
817
  return single_containing_head(project_root, heads)
953
818
 
954
819
 
955
- def _acquire_tip_stage_target(
956
- request: FinalVerificationTargetRequest,
957
- done_rows: list[dict[str, Any]],
958
- registry_coordinates: tuple[str, str, str],
959
- ) -> FinalVerificationTargetAcquisition | None:
960
- """전체를 담은 단일 stage 브랜치가 있으면 그것을 whole-task 검증 대상으로
961
- 삼는다 — 머지 없이. 없거나 그 워크트리가 쓸 수 없으면 None 을 돌려 기존
962
- task 브랜치 통합 경로로 넘긴다."""
963
- from . import worktree_registry
964
- from .consumers import latest_done_by_stage
965
- from .worktree import _git, is_dirty_excluding_okstra
966
-
967
- done = latest_done_by_stage(done_rows)
968
- planned = [int(s["stage_number"]) for s in request.stage_map]
969
- if any(n not in done for n in planned):
970
- return None # 미완 stage 의 거부 메시지는 기존 경로가 낸다
971
- tip = containing_stage(request.project_root,
972
- {n: done[n] for n in planned})
973
- if tip is None:
974
- return None
975
- row = worktree_registry.get_stage_row(*registry_coordinates, tip) or {}
976
- worktree_path = row.get("worktree_path", "")
977
- if not worktree_path or not Path(worktree_path).is_dir():
978
- return None
979
- head_result = _git(worktree_path, "rev-parse", "HEAD")
980
- if head_result.returncode != 0:
981
- return None
982
- head = head_result.stdout.strip()
983
- if head != done[tip].get("head_commit", ""):
984
- # 브랜치 tip 이 done 기록과 갈라져 있다. 검증 대상이 무엇인지 모호하므로
985
- # 판정을 기존 경로로 넘긴다.
986
- return None
987
- if is_dirty_excluding_okstra(worktree_path):
988
- raise StageTargetError(
989
- "final-verification: worktree has uncommitted source changes "
990
- "(outside .okstra/) — commit or stash before verifying"
991
- )
992
- anchor_base = worktree_registry.get_implementation_base(
993
- *registry_coordinates) or ""
994
- stages = sorted(planned)
995
- return FinalVerificationTargetAcquisition(
996
- target=FinalVerificationTarget(
997
- scope="whole-task",
998
- base=anchor_base,
999
- head=head,
1000
- worktree_path=worktree_path,
1001
- stages=stages,
1002
- reports=[done[n].get("report_path", "") for n in stages],
1003
- ),
1004
- relocations=(),
1005
- worktree_branch=row.get("branch", ""),
1006
- integration_result=None,
1007
- )
1008
-
1009
-
1010
- def _acquire_whole_task_target(
1011
- request: FinalVerificationTargetRequest,
1012
- done_rows: list[dict[str, Any]],
1013
- registry_coordinates: tuple[str, str, str],
1014
- ) -> FinalVerificationTargetAcquisition:
1015
- from . import worktree_registry
1016
-
1017
- tip = _acquire_tip_stage_target(request, done_rows, registry_coordinates)
1018
- if tip is not None:
1019
- return tip
1020
-
1021
- project_id, task_group, task_id = registry_coordinates
1022
- entry = worktree_registry.lookup(project_id, task_group, task_id)
1023
- worktree_path = (
1024
- entry.worktree_path if entry is not None else str(request.project_root)
1025
- )
1026
- relocations = (
1027
- relocate_nested_stage_worktrees(
1028
- project_id, task_group, task_id, worktree_path,
1029
- stages=[int(row["stage_number"]) for row in request.stage_map],
1030
- )
1031
- if entry is not None
1032
- else []
1033
- )
1034
- whole = _resolve_and_integrate_whole_task_unlocked(
1035
- project_id=project_id,
1036
- task_group=task_group,
1037
- task_id=task_id,
1038
- task_worktree_path=worktree_path,
1039
- stage_map=list(request.stage_map),
1040
- done_rows=done_rows,
1041
- anchor_base=(
1042
- worktree_registry.get_implementation_base(
1043
- project_id, task_group, task_id
1044
- )
1045
- or ""
1046
- ),
1047
- # 정리는 판정 뒤로 미룬다(Phase 7 `teardown-stages`). 되돌릴 수 없는 정리를
1048
- # 판정 앞에 두면, 재작업이 가장 필요한 blocked 판정에서 stage 작업물이 이미
1049
- # 사라져 있다. 여기서는 통합만 하고 worktree/registry 키는 남긴다.
1050
- teardown=False,
1051
- )
1052
- return FinalVerificationTargetAcquisition(
1053
- target=whole["target"],
1054
- relocations=tuple(relocations),
1055
- worktree_branch=entry.branch if entry is not None else "",
1056
- integration_result=whole["integrate_result"],
1057
- )
1058
-
1059
-
1060
820
  def integrate_and_teardown_whole_task(
1061
821
  *, project_root: Path, task_group: str, task_id: str,
1062
822
  ) -> dict[str, Any]:
1063
823
  """판정이 끝난 whole-task 검증의 stage worktree 와 registry 키를 회수한다.
1064
824
 
1065
- 진입은 통합만 하고 정리를 남겨 두므로(`_resolve_whole_task_acquisition`), 정리는
825
+ 진입은 통합만 하고 정리를 남겨 두므로(`phases/final_verification/target.py`), 정리는
1066
826
  판정 뒤인 Phase 7 에서 여기로 들어온다. 통합은 이미 끝나 있어 Phase A 는 전부
1067
827
  `already_merged` 로 지나가고 Phase B 만 실제 일을 한다. 두 번 불려도 결과는 같다 —
1068
828
  사라진 worktree 는 건너뛰고, 미커밋 변경이 남은 stage 트리는 보존한다.
@@ -1117,7 +877,7 @@ def integrate_and_teardown_whole_task(
1117
877
  done_rows=done_rows,
1118
878
  teardown=True,
1119
879
  # 한 stage 브랜치가 이미 전체를 담고 있으면 검증은 거기서 돌았다
1120
- # (`_acquire_tip_stage_target`). 그 경우 task 브랜치로의 머지는 아무도
880
+ # (`phases/final_verification/target.py`). 그 경우 task 브랜치로의 머지는 아무도
1121
881
  # 읽지 않는 산출물이므로 정리만 한다.
1122
882
  merge=tip is None,
1123
883
  verified_head=(done_by_stage.get(tip) or {}).get("head_commit", ""),
@@ -1130,40 +890,3 @@ def integrate_and_teardown_whole_task(
1130
890
  ],
1131
891
  "warnings": result.warnings,
1132
892
  }
1133
-
1134
-
1135
- def acquire_final_verification_target(
1136
- request: FinalVerificationTargetRequest,
1137
- ) -> FinalVerificationTargetAcquisition:
1138
- """Acquire a stable final-verification target behind one task-key lock."""
1139
- from okstra_project.dirs import okstra_home
1140
-
1141
- from .locks import worktree_provision_mutex
1142
-
1143
- try:
1144
- with worktree_provision_mutex(
1145
- okstra_home(),
1146
- request.project_id,
1147
- slugify(request.task_group),
1148
- slugify(request.task_id),
1149
- ):
1150
- registry_coordinates = _final_verification_registry_coordinates(request)
1151
- done_rows = _read_final_verification_done_rows(
1152
- request,
1153
- registry_coordinates,
1154
- )
1155
- if request.stage is not None:
1156
- return _acquire_single_stage_target(
1157
- request,
1158
- done_rows,
1159
- registry_coordinates,
1160
- )
1161
- return _acquire_whole_task_target(
1162
- request,
1163
- done_rows,
1164
- registry_coordinates,
1165
- )
1166
- except PrepareError:
1167
- raise
1168
- except (OSError, RuntimeError, StageTargetError, ValueError) as exc:
1169
- raise PrepareError(str(exc)) from exc
@@ -0,0 +1,52 @@
1
+ """방향 비교와 기술 검증이 함께 읽는 미확정 사실 계약."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Mapping
5
+ from typing import Any, TypedDict
6
+
7
+ from .clarification_items import USER_INPUT_BLOCKS, progress_blocking_ids
8
+
9
+
10
+ class VerificationFact(TypedDict):
11
+ id: str
12
+ candidateId: str
13
+ factIndex: int
14
+ fact: str
15
+ whyItMatters: str
16
+ evidence: list[str]
17
+
18
+
19
+ class TechnicalVerificationError(ValueError):
20
+ """시험 입력 또는 관측 결과가 원본 사실과 일치하지 않는다."""
21
+
22
+
23
+ def technical_verification_facts(data: Mapping[str, Any]) -> list[VerificationFact]:
24
+ """안전 차단이 없는 후보의 명시적인 기술 검증 사실만 추출한다."""
25
+ blockers = progress_blocking_ids(
26
+ data.get("clarificationItems"), USER_INPUT_BLOCKS, report_data=data
27
+ )
28
+ if blockers:
29
+ raise TechnicalVerificationError(
30
+ "unresolved user decisions: " + ", ".join(blockers)
31
+ )
32
+ selection = data.get("implementationOptionSelection") or {}
33
+ facts: list[VerificationFact] = []
34
+ for candidate in selection.get("candidateAudit") or []:
35
+ if candidate.get("safetyBlockers"):
36
+ continue
37
+ for index, fact in enumerate(candidate.get("unresolvedFeasibilityFacts") or []):
38
+ if fact.get("resolutionKind") != "technical-verification":
39
+ continue
40
+ facts.append(
41
+ {
42
+ "id": f"TV-{len(facts) + 1:03d}",
43
+ "candidateId": candidate["id"],
44
+ "factIndex": index,
45
+ "fact": fact["fact"],
46
+ "whyItMatters": fact["whyItMatters"],
47
+ "evidence": fact["evidence"],
48
+ }
49
+ )
50
+ if not facts:
51
+ raise TechnicalVerificationError("no eligible technical-verification facts")
52
+ return facts