okstra 0.186.7 → 0.187.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 (541) hide show
  1. package/README.md +1 -1
  2. package/dist/cli-registry.d.mts +88 -1
  3. package/dist/cli-registry.mjs +68 -111
  4. package/dist/cli-registry.mjs.map +1 -1
  5. package/dist/commands/execute/render-bundle.mjs +0 -1
  6. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  7. package/dist/commands/execute/run.mjs +8 -3
  8. package/dist/commands/execute/run.mjs.map +1 -1
  9. package/dist/commands/lifecycle/check-project.mjs +1 -14
  10. package/dist/commands/lifecycle/check-project.mjs.map +1 -1
  11. package/dist/commands/lifecycle/config.mjs +38 -40
  12. package/dist/commands/lifecycle/config.mjs.map +1 -1
  13. package/dist/commands/lifecycle/doctor.d.mts +22 -7
  14. package/dist/commands/lifecycle/doctor.mjs +77 -49
  15. package/dist/commands/lifecycle/doctor.mjs.map +1 -1
  16. package/dist/commands/lifecycle/install.d.mts +12 -10
  17. package/dist/commands/lifecycle/install.mjs +104 -39
  18. package/dist/commands/lifecycle/install.mjs.map +1 -1
  19. package/dist/commands/lifecycle/paths.mjs +8 -3
  20. package/dist/commands/lifecycle/paths.mjs.map +1 -1
  21. package/dist/commands/lifecycle/preflight.mjs +2 -1
  22. package/dist/commands/lifecycle/preflight.mjs.map +1 -1
  23. package/dist/commands/lifecycle/setup.mjs +22 -36
  24. package/dist/commands/lifecycle/setup.mjs.map +1 -1
  25. package/dist/commands/lifecycle/uninstall.d.mts +4 -2
  26. package/dist/commands/lifecycle/uninstall.mjs +59 -15
  27. package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
  28. package/dist/commands/memory/memory.mjs +7 -1
  29. package/dist/commands/memory/memory.mjs.map +1 -1
  30. package/dist/lib/helper-scripts.d.mts +1 -1
  31. package/dist/lib/helper-scripts.mjs +10 -18
  32. package/dist/lib/helper-scripts.mjs.map +1 -1
  33. package/dist/lib/host-config.d.mts +72 -0
  34. package/dist/lib/host-config.mjs +404 -0
  35. package/dist/lib/host-config.mjs.map +1 -0
  36. package/dist/lib/host-registry-client.d.mts +2 -2
  37. package/dist/lib/host-registry-client.mjs +0 -3
  38. package/dist/lib/host-registry-client.mjs.map +1 -1
  39. package/dist/lib/install-assets.d.mts +1 -0
  40. package/dist/lib/install-assets.mjs +4 -0
  41. package/dist/lib/install-assets.mjs.map +1 -1
  42. package/dist/lib/proc.d.mts +2 -0
  43. package/dist/lib/proc.mjs +12 -0
  44. package/dist/lib/proc.mjs.map +1 -1
  45. package/dist/lib/python-command.d.mts +2 -0
  46. package/dist/lib/python-command.mjs +52 -0
  47. package/dist/lib/python-command.mjs.map +1 -0
  48. package/dist/lib/python-helper.d.mts +2 -5
  49. package/dist/lib/python-helper.mjs +3 -52
  50. package/dist/lib/python-helper.mjs.map +1 -1
  51. package/dist/lib/runtime-payload.d.mts +23 -0
  52. package/dist/lib/runtime-payload.mjs +57 -0
  53. package/dist/lib/runtime-payload.mjs.map +1 -0
  54. package/dist/lib/types.d.mts +24 -13
  55. package/docs/architecture/storage-model.md +21 -5
  56. package/docs/architecture.md +37 -36
  57. package/docs/cli.md +55 -76
  58. package/docs/coding-rules.md +295 -0
  59. package/docs/container.md +10 -36
  60. package/docs/contributor-change-matrix.md +1 -1
  61. package/docs/for-ai/skills/okstra-code-review.md +0 -1
  62. package/docs/for-ai/skills/okstra-container-build.md +8 -41
  63. package/docs/for-ai/skills/okstra-inspect.md +4 -4
  64. package/docs/for-ai/skills/okstra-manager.md +2 -2
  65. package/docs/for-ai/skills/okstra-rollup.md +0 -1
  66. package/docs/for-ai/skills/okstra-run.md +3 -3
  67. package/docs/for-ai/skills/okstra-user-response.md +0 -1
  68. package/docs/performance-improvement-plan-v2.md +1 -1
  69. package/docs/project-structure-overview.md +62 -62
  70. package/docs/task-process/README.md +3 -3
  71. package/docs/task-process/common-flow.md +1 -1
  72. package/docs/task-process/error-analysis.md +3 -3
  73. package/docs/task-process/implementation-planning.md +3 -1
  74. package/docs/task-process/release-handoff.md +1 -1
  75. package/package.json +3 -2
  76. package/runtime/BUILD.json +2 -2
  77. package/runtime/agents/workers/claude-worker.md +6 -11
  78. package/runtime/agents/workers/report-writer-worker.md +1 -1
  79. package/runtime/bin/lib/okstra/cli.sh +4 -0
  80. package/runtime/bin/lib/okstra/globals.sh +2 -0
  81. package/runtime/bin/lib/okstra/interactive.sh +26 -173
  82. package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
  83. package/runtime/bin/lib/okstra/usage.sh +3 -3
  84. package/runtime/bin/okstra-compact-reminder.sh +1 -5
  85. package/runtime/bin/okstra-error-log.py +19 -2
  86. package/runtime/bin/okstra-import-check.py +26 -0
  87. package/runtime/bin/okstra-inject-report-index.py +5 -4
  88. package/runtime/bin/okstra-provider-exec.py +22 -17
  89. package/runtime/bin/okstra-render-final-report.py +16 -6
  90. package/runtime/bin/okstra-render-report-views.py +25 -18
  91. package/runtime/bin/okstra-report-translate.py +30 -18
  92. package/runtime/bin/okstra-spawn-followups.py +4 -0
  93. package/runtime/bin/okstra-token-usage.py +3 -0
  94. package/runtime/bin/okstra.sh +4 -2
  95. package/runtime/bin/okstra_bootstrap.py +58 -0
  96. package/runtime/prompts/coding-preflight/overview.md +1 -1
  97. package/runtime/prompts/duties/planning-worker.md +1 -1
  98. package/runtime/prompts/launch.template.md +35 -9
  99. package/runtime/prompts/lead/convergence.md +96 -87
  100. package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
  101. package/runtime/prompts/lead/plan-body-verification.md +65 -46
  102. package/runtime/prompts/lead/report-writer.md +34 -4
  103. package/runtime/prompts/lead/team-contract.md +4 -1
  104. package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
  105. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  106. package/runtime/prompts/profiles/_common-contract.md +3 -3
  107. package/runtime/prompts/profiles/_coverage-critic.md +1 -1
  108. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
  109. package/runtime/prompts/profiles/_implementation-executor.md +3 -1
  110. package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
  111. package/runtime/prompts/profiles/error-analysis.md +4 -3
  112. package/runtime/prompts/profiles/final-verification.md +2 -2
  113. package/runtime/prompts/profiles/implementation-planning.md +22 -17
  114. package/runtime/prompts/profiles/implementation.md +3 -2
  115. package/runtime/prompts/profiles/requirements-discovery.md +2 -1
  116. package/runtime/prompts/wizard/prompts.ko.json +29 -8
  117. package/runtime/python/okstra_ctl/__init__.py +6 -27
  118. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
  119. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
  120. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
  121. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +40 -22
  122. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +14 -21
  123. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
  124. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
  125. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
  126. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
  127. package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
  128. package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +12 -1
  129. package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
  130. package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
  131. package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
  132. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +215 -0
  133. package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
  134. package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
  135. package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
  136. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +408 -0
  137. package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
  138. package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
  139. package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
  140. package/runtime/python/okstra_ctl/approval_decisions.py +260 -8
  141. package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
  142. package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
  143. package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
  144. package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
  145. package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
  146. package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
  147. package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
  148. package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
  149. package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
  150. package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
  151. package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
  152. package/runtime/python/okstra_ctl/cmux.py +38 -31
  153. package/runtime/python/okstra_ctl/code_review_target.py +163 -1
  154. package/runtime/python/okstra_ctl/conformance.py +23 -0
  155. package/runtime/python/okstra_ctl/container.py +38 -421
  156. package/runtime/python/okstra_ctl/context_cost.py +16 -2
  157. package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
  158. package/runtime/python/okstra_ctl/convergence.py +101 -15
  159. package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
  160. package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
  161. package/runtime/python/okstra_ctl/convergence_store.py +5 -29
  162. package/runtime/python/okstra_ctl/design_prep.py +108 -12
  163. package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
  164. package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
  165. package/runtime/python/okstra_ctl/dispatch_core.py +102 -25
  166. package/runtime/python/okstra_ctl/dispatch_state.py +268 -39
  167. package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
  168. package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
  169. package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -1
  170. package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
  171. package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
  172. package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
  173. package/runtime/python/okstra_ctl/error_log_core.py +1 -1
  174. package/runtime/python/okstra_ctl/error_report.py +14 -0
  175. package/runtime/python/okstra_ctl/error_zip.py +18 -2
  176. package/runtime/python/okstra_ctl/execution_identity.py +100 -7
  177. package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
  178. package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
  179. package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
  180. package/runtime/python/okstra_ctl/final_report_schema.py +90 -16
  181. package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
  182. package/runtime/python/okstra_ctl/handoff.py +24 -1
  183. package/runtime/python/okstra_ctl/ids.py +6 -16
  184. package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
  185. package/runtime/python/okstra_ctl/implementation_options.py +25 -11
  186. package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
  187. package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
  188. package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
  189. package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
  190. package/runtime/python/okstra_ctl/index.py +2 -2
  191. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
  192. package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
  193. package/runtime/python/okstra_ctl/json_boundary.py +10 -0
  194. package/runtime/python/okstra_ctl/listing.py +0 -91
  195. package/runtime/python/okstra_ctl/locks.py +5 -19
  196. package/runtime/python/okstra_ctl/log_report.py +14 -0
  197. package/runtime/python/okstra_ctl/manager_cli.py +22 -3
  198. package/runtime/python/okstra_ctl/manager_launch.py +4 -8
  199. package/runtime/python/okstra_ctl/material.py +2 -2
  200. package/runtime/python/okstra_ctl/migrate.py +23 -1
  201. package/runtime/python/okstra_ctl/model_cli.py +19 -5
  202. package/runtime/python/okstra_ctl/model_discovery.py +46 -47
  203. package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
  204. package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
  205. package/runtime/python/okstra_ctl/model_io/references.py +370 -0
  206. package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
  207. package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
  208. package/runtime/python/okstra_ctl/model_pool.py +10 -6
  209. package/runtime/python/okstra_ctl/models.py +8 -8
  210. package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
  211. package/runtime/python/okstra_ctl/next_phase.py +121 -69
  212. package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
  213. package/runtime/python/okstra_ctl/pane_title.py +11 -8
  214. package/runtime/python/okstra_ctl/path_hints.py +36 -11
  215. package/runtime/python/okstra_ctl/paths.py +76 -8
  216. package/runtime/python/okstra_ctl/plan_items.py +132 -16
  217. package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
  218. package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
  219. package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
  220. package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
  221. package/runtime/python/okstra_ctl/prepare_error.py +20 -0
  222. package/runtime/python/okstra_ctl/prior_planning.py +157 -0
  223. package/runtime/python/okstra_ctl/profile_show.py +18 -1
  224. package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
  225. package/runtime/python/okstra_ctl/recap.py +22 -1
  226. package/runtime/python/okstra_ctl/reconcile.py +84 -0
  227. package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
  228. package/runtime/python/okstra_ctl/render.py +98 -32
  229. package/runtime/python/okstra_ctl/render_final_report.py +3 -7
  230. package/runtime/python/okstra_ctl/report_assembly.py +297 -7
  231. package/runtime/python/okstra_ctl/report_contract.py +30 -7
  232. package/runtime/python/okstra_ctl/report_finalize.py +206 -15
  233. package/runtime/python/okstra_ctl/report_html/common.py +2 -2
  234. package/runtime/python/okstra_ctl/report_html/render.py +8 -12
  235. package/runtime/python/okstra_ctl/report_language.py +8 -4
  236. package/runtime/python/okstra_ctl/report_narrative.py +90 -3
  237. package/runtime/python/okstra_ctl/report_projections.py +8 -2
  238. package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
  239. package/runtime/python/okstra_ctl/report_translation.py +2 -35
  240. package/runtime/python/okstra_ctl/report_views.py +4 -22
  241. package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
  242. package/runtime/python/okstra_ctl/rollup.py +13 -0
  243. package/runtime/python/okstra_ctl/run.py +639 -138
  244. package/runtime/python/okstra_ctl/run_audit.py +13 -0
  245. package/runtime/python/okstra_ctl/run_index_row.py +0 -12
  246. package/runtime/python/okstra_ctl/seeding.py +21 -11
  247. package/runtime/python/okstra_ctl/sequence.py +1 -1
  248. package/runtime/python/okstra_ctl/session.py +64 -14
  249. package/runtime/python/okstra_ctl/set_work_status.py +18 -0
  250. package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
  251. package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
  252. package/runtime/python/okstra_ctl/stage_map.py +57 -11
  253. package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
  254. package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
  255. package/runtime/python/okstra_ctl/stage_targets.py +3 -12
  256. package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
  257. package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
  258. package/runtime/python/okstra_ctl/task_target.py +4 -16
  259. package/runtime/python/okstra_ctl/team.py +49 -2
  260. package/runtime/python/okstra_ctl/time_report.py +17 -2
  261. package/runtime/python/okstra_ctl/usage_report.py +11 -0
  262. package/runtime/python/okstra_ctl/user_response.py +55 -218
  263. package/runtime/python/okstra_ctl/user_response_values.py +242 -0
  264. package/runtime/python/okstra_ctl/validation_contract.py +3 -0
  265. package/runtime/python/okstra_ctl/verification_target.py +68 -0
  266. package/runtime/python/okstra_ctl/wizard.py +457 -92
  267. package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
  268. package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
  269. package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
  270. package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
  271. package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
  272. package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
  273. package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
  274. package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
  275. package/runtime/python/okstra_ctl/worker_request.py +2 -1
  276. package/runtime/python/okstra_ctl/worker_runner.py +16 -9
  277. package/runtime/python/okstra_ctl/worker_state.py +16 -1
  278. package/runtime/python/okstra_ctl/workflow.py +18 -1
  279. package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
  280. package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
  281. package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
  282. package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
  283. package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
  284. package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
  285. package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
  286. package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
  287. package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
  288. package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
  289. package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
  290. package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
  291. package/runtime/python/okstra_ctl/write_policy.py +90 -213
  292. package/runtime/python/okstra_project/__init__.py +0 -4
  293. package/runtime/python/okstra_project/dirs.py +2 -2
  294. package/runtime/python/okstra_project/phase_pointer.py +84 -0
  295. package/runtime/python/okstra_project/slug.py +24 -0
  296. package/runtime/python/okstra_project/state.py +22 -230
  297. package/runtime/python/okstra_token_usage/claude.py +9 -1
  298. package/runtime/python/okstra_token_usage/cli.py +3 -1
  299. package/runtime/python/okstra_token_usage/collect.py +71 -10
  300. package/runtime/python/okstra_token_usage/cursor.py +2 -0
  301. package/runtime/python/okstra_token_usage/grok.py +1 -5
  302. package/runtime/python/okstra_token_usage/paths.py +26 -0
  303. package/runtime/python/okstra_token_usage/pricing.py +19 -7
  304. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
  305. package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
  306. package/runtime/schemas/final-report-v2.0.schema.json +9 -26
  307. package/runtime/schemas/final-report-v3.0.schema.json +173 -41
  308. package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
  309. package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
  310. package/runtime/skills/okstra-container-build/SKILL.md +16 -47
  311. package/runtime/skills/okstra-inspect/facets/history.md +1 -1
  312. package/runtime/skills/okstra-inspect/facets/status.md +7 -6
  313. package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
  314. package/runtime/skills/okstra-run/SKILL.md +7 -5
  315. package/runtime/templates/report-writer-prompt-preamble.md +1 -1
  316. package/runtime/templates/reports/brief.template.md +6 -2
  317. package/runtime/templates/reports/error-analysis-input.template.md +2 -0
  318. package/runtime/templates/reports/final-verification-input.template.md +2 -0
  319. package/runtime/templates/reports/implementation-input.template.md +7 -1
  320. package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
  321. package/runtime/templates/reports/quick-input.template.md +2 -0
  322. package/runtime/templates/reports/release-handoff-input.template.md +6 -1
  323. package/runtime/templates/reports/report.js +20 -3
  324. package/runtime/templates/reports/schedule.template.md +2 -0
  325. package/runtime/templates/reports/settings.template.json +0 -10
  326. package/runtime/templates/reports/task-brief.template.md +3 -0
  327. package/runtime/templates/reports/user-response.template.md +7 -3
  328. package/runtime/validators/checks/fixtures-01.py +57 -0
  329. package/runtime/validators/checks/fixtures-02.py +565 -0
  330. package/runtime/validators/checks/runners-01.py +108 -0
  331. package/runtime/validators/checks/validate-assets-01.py +60 -0
  332. package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
  333. package/runtime/validators/checks/validate-tasks-01.py +46 -0
  334. package/runtime/validators/checks/validate-tasks-02.py +85 -0
  335. package/runtime/validators/checks/validate-tasks-03.py +61 -0
  336. package/runtime/validators/checks/validate-tasks-04.py +118 -0
  337. package/runtime/validators/forbidden_actions.py +73 -3
  338. package/runtime/validators/lib/common.sh +5 -0
  339. package/runtime/validators/lib/fixtures.sh +7 -591
  340. package/runtime/validators/lib/paths.sh +13 -0
  341. package/runtime/validators/lib/runners.sh +6 -104
  342. package/runtime/validators/lib/summary.sh +1 -1
  343. package/runtime/validators/lib/validate-assets.sh +6 -56
  344. package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
  345. package/runtime/validators/lib/validate-tasks.sh +9 -294
  346. package/runtime/validators/validate-implementation-plan-stages.py +4 -4
  347. package/runtime/validators/validate-run.py +1369 -2654
  348. package/runtime/validators/validate-workflow.sh +56 -16
  349. package/runtime/validators/validate_improvement_report.py +2 -1
  350. package/runtime/validators/validate_session_conformance.py +295 -49
  351. package/dist/commands/execute/agent-prompt.d.mts +0 -1
  352. package/dist/commands/execute/agent-prompt.mjs +0 -24
  353. package/dist/commands/execute/agent-prompt.mjs.map +0 -1
  354. package/dist/commands/execute/codex-dispatch.d.mts +0 -3
  355. package/dist/commands/execute/codex-dispatch.mjs +0 -6
  356. package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
  357. package/dist/commands/execute/codex-run.d.mts +0 -3
  358. package/dist/commands/execute/codex-run.mjs +0 -62
  359. package/dist/commands/execute/codex-run.mjs.map +0 -1
  360. package/dist/commands/execute/convergence.d.mts +0 -1
  361. package/dist/commands/execute/convergence.mjs +0 -38
  362. package/dist/commands/execute/convergence.mjs.map +0 -1
  363. package/dist/commands/execute/error-log.d.mts +0 -1
  364. package/dist/commands/execute/error-log.mjs +0 -18
  365. package/dist/commands/execute/error-log.mjs.map +0 -1
  366. package/dist/commands/execute/git-reconcile.d.mts +0 -1
  367. package/dist/commands/execute/git-reconcile.mjs +0 -30
  368. package/dist/commands/execute/git-reconcile.mjs.map +0 -1
  369. package/dist/commands/execute/handoff.d.mts +0 -1
  370. package/dist/commands/execute/handoff.mjs +0 -31
  371. package/dist/commands/execute/handoff.mjs.map +0 -1
  372. package/dist/commands/execute/incremental-carry.d.mts +0 -1
  373. package/dist/commands/execute/incremental-carry.mjs +0 -20
  374. package/dist/commands/execute/incremental-carry.mjs.map +0 -1
  375. package/dist/commands/execute/incremental-scope.d.mts +0 -1
  376. package/dist/commands/execute/incremental-scope.mjs +0 -29
  377. package/dist/commands/execute/incremental-scope.mjs.map +0 -1
  378. package/dist/commands/execute/integrate-stages.d.mts +0 -1
  379. package/dist/commands/execute/integrate-stages.mjs +0 -24
  380. package/dist/commands/execute/integrate-stages.mjs.map +0 -1
  381. package/dist/commands/execute/pane-title.d.mts +0 -1
  382. package/dist/commands/execute/pane-title.mjs +0 -20
  383. package/dist/commands/execute/pane-title.mjs.map +0 -1
  384. package/dist/commands/execute/plan-items.d.mts +0 -1
  385. package/dist/commands/execute/plan-items.mjs +0 -9
  386. package/dist/commands/execute/plan-items.mjs.map +0 -1
  387. package/dist/commands/execute/plan-validate.d.mts +0 -1
  388. package/dist/commands/execute/plan-validate.mjs +0 -68
  389. package/dist/commands/execute/plan-validate.mjs.map +0 -1
  390. package/dist/commands/execute/plan-verify.d.mts +0 -1
  391. package/dist/commands/execute/plan-verify.mjs +0 -43
  392. package/dist/commands/execute/plan-verify.mjs.map +0 -1
  393. package/dist/commands/execute/spawn-followups.d.mts +0 -1
  394. package/dist/commands/execute/spawn-followups.mjs +0 -22
  395. package/dist/commands/execute/spawn-followups.mjs.map +0 -1
  396. package/dist/commands/execute/team.d.mts +0 -3
  397. package/dist/commands/execute/team.mjs +0 -66
  398. package/dist/commands/execute/team.mjs.map +0 -1
  399. package/dist/commands/execute/token-usage.d.mts +0 -1
  400. package/dist/commands/execute/token-usage.mjs +0 -19
  401. package/dist/commands/execute/token-usage.mjs.map +0 -1
  402. package/dist/commands/execute/worker-audit-check.d.mts +0 -1
  403. package/dist/commands/execute/worker-audit-check.mjs +0 -34
  404. package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
  405. package/dist/commands/execute/worker-dispatch.d.mts +0 -7
  406. package/dist/commands/execute/worker-dispatch.mjs +0 -64
  407. package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
  408. package/dist/commands/execute/worker-state.d.mts +0 -1
  409. package/dist/commands/execute/worker-state.mjs +0 -28
  410. package/dist/commands/execute/worker-state.mjs.map +0 -1
  411. package/dist/commands/execute/worktree-lookup.d.mts +0 -1
  412. package/dist/commands/execute/worktree-lookup.mjs +0 -92
  413. package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
  414. package/dist/commands/execute/worktree-status.d.mts +0 -1
  415. package/dist/commands/execute/worktree-status.mjs +0 -121
  416. package/dist/commands/execute/worktree-status.mjs.map +0 -1
  417. package/dist/commands/inspect/code-review.d.mts +0 -1
  418. package/dist/commands/inspect/code-review.mjs +0 -32
  419. package/dist/commands/inspect/code-review.mjs.map +0 -1
  420. package/dist/commands/inspect/container.d.mts +0 -1
  421. package/dist/commands/inspect/container.mjs +0 -25
  422. package/dist/commands/inspect/container.mjs.map +0 -1
  423. package/dist/commands/inspect/context-cost.d.mts +0 -1
  424. package/dist/commands/inspect/context-cost.mjs +0 -25
  425. package/dist/commands/inspect/context-cost.mjs.map +0 -1
  426. package/dist/commands/inspect/design-prep.d.mts +0 -1
  427. package/dist/commands/inspect/design-prep.mjs +0 -22
  428. package/dist/commands/inspect/design-prep.mjs.map +0 -1
  429. package/dist/commands/inspect/error-report.d.mts +0 -1
  430. package/dist/commands/inspect/error-report.mjs +0 -25
  431. package/dist/commands/inspect/error-report.mjs.map +0 -1
  432. package/dist/commands/inspect/error-zip.d.mts +0 -1
  433. package/dist/commands/inspect/error-zip.mjs +0 -24
  434. package/dist/commands/inspect/error-zip.mjs.map +0 -1
  435. package/dist/commands/inspect/log-report.d.mts +0 -1
  436. package/dist/commands/inspect/log-report.mjs +0 -26
  437. package/dist/commands/inspect/log-report.mjs.map +0 -1
  438. package/dist/commands/inspect/model-io.d.mts +0 -1
  439. package/dist/commands/inspect/model-io.mjs +0 -25
  440. package/dist/commands/inspect/model-io.mjs.map +0 -1
  441. package/dist/commands/inspect/profile-show.d.mts +0 -1
  442. package/dist/commands/inspect/profile-show.mjs +0 -28
  443. package/dist/commands/inspect/profile-show.mjs.map +0 -1
  444. package/dist/commands/inspect/recap.d.mts +0 -1
  445. package/dist/commands/inspect/recap.mjs +0 -30
  446. package/dist/commands/inspect/recap.mjs.map +0 -1
  447. package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
  448. package/dist/commands/inspect/resolve-task-key.mjs +0 -24
  449. package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
  450. package/dist/commands/inspect/rollup.d.mts +0 -1
  451. package/dist/commands/inspect/rollup.mjs +0 -25
  452. package/dist/commands/inspect/rollup.mjs.map +0 -1
  453. package/dist/commands/inspect/run-audit.d.mts +0 -1
  454. package/dist/commands/inspect/run-audit.mjs +0 -25
  455. package/dist/commands/inspect/run-audit.mjs.map +0 -1
  456. package/dist/commands/inspect/set-work-status.d.mts +0 -1
  457. package/dist/commands/inspect/set-work-status.mjs +0 -29
  458. package/dist/commands/inspect/set-work-status.mjs.map +0 -1
  459. package/dist/commands/inspect/stage-map.d.mts +0 -1
  460. package/dist/commands/inspect/stage-map.mjs +0 -131
  461. package/dist/commands/inspect/stage-map.mjs.map +0 -1
  462. package/dist/commands/inspect/task-list.d.mts +0 -1
  463. package/dist/commands/inspect/task-list.mjs +0 -149
  464. package/dist/commands/inspect/task-list.mjs.map +0 -1
  465. package/dist/commands/inspect/task-show.d.mts +0 -1
  466. package/dist/commands/inspect/task-show.mjs +0 -108
  467. package/dist/commands/inspect/task-show.mjs.map +0 -1
  468. package/dist/commands/inspect/time-report.d.mts +0 -1
  469. package/dist/commands/inspect/time-report.mjs +0 -24
  470. package/dist/commands/inspect/time-report.mjs.map +0 -1
  471. package/dist/commands/inspect/usage-report.d.mts +0 -1
  472. package/dist/commands/inspect/usage-report.mjs +0 -23
  473. package/dist/commands/inspect/usage-report.mjs.map +0 -1
  474. package/dist/commands/inspect/user-response.d.mts +0 -1
  475. package/dist/commands/inspect/user-response.mjs +0 -35
  476. package/dist/commands/inspect/user-response.mjs.map +0 -1
  477. package/dist/commands/inspect/worker-liveness.d.mts +0 -1
  478. package/dist/commands/inspect/worker-liveness.mjs +0 -45
  479. package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
  480. package/dist/commands/lifecycle/contract-check.d.mts +0 -1
  481. package/dist/commands/lifecycle/contract-check.mjs +0 -18
  482. package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
  483. package/dist/commands/lifecycle/migrate.d.mts +0 -1
  484. package/dist/commands/lifecycle/migrate.mjs +0 -30
  485. package/dist/commands/lifecycle/migrate.mjs.map +0 -1
  486. package/dist/commands/lifecycle/model.d.mts +0 -1
  487. package/dist/commands/lifecycle/model.mjs +0 -22
  488. package/dist/commands/lifecycle/model.mjs.map +0 -1
  489. package/dist/commands/manager.d.mts +0 -3
  490. package/dist/commands/manager.mjs +0 -50
  491. package/dist/commands/manager.mjs.map +0 -1
  492. package/dist/commands/report/agent-activity.d.mts +0 -1
  493. package/dist/commands/report/agent-activity.mjs +0 -20
  494. package/dist/commands/report/agent-activity.mjs.map +0 -1
  495. package/dist/commands/report/approval-decision.d.mts +0 -1
  496. package/dist/commands/report/approval-decision.mjs +0 -21
  497. package/dist/commands/report/approval-decision.mjs.map +0 -1
  498. package/dist/commands/report/design-snapshot.d.mts +0 -1
  499. package/dist/commands/report/design-snapshot.mjs +0 -19
  500. package/dist/commands/report/design-snapshot.mjs.map +0 -1
  501. package/dist/commands/report/finalize.d.mts +0 -4
  502. package/dist/commands/report/finalize.mjs +0 -64
  503. package/dist/commands/report/finalize.mjs.map +0 -1
  504. package/dist/commands/report/inject-report-index.d.mts +0 -1
  505. package/dist/commands/report/inject-report-index.mjs +0 -21
  506. package/dist/commands/report/inject-report-index.mjs.map +0 -1
  507. package/dist/commands/report/render-final-report.d.mts +0 -1
  508. package/dist/commands/report/render-final-report.mjs +0 -23
  509. package/dist/commands/report/render-final-report.mjs.map +0 -1
  510. package/dist/commands/report/render-views.d.mts +0 -1
  511. package/dist/commands/report/render-views.mjs +0 -27
  512. package/dist/commands/report/render-views.mjs.map +0 -1
  513. package/dist/commands/report/translate.d.mts +0 -1
  514. package/dist/commands/report/translate.mjs +0 -33
  515. package/dist/commands/report/translate.mjs.map +0 -1
  516. package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
  517. package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
  518. package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
  519. package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
  520. package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
  521. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
  522. package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
  523. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
  524. package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
  525. package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
  526. package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
  527. package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
  528. package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
  529. package/runtime/bin/okstra-central.sh +0 -152
  530. package/runtime/bin/okstra-incremental-carry.py +0 -10
  531. package/runtime/bin/okstra-incremental-scope.py +0 -10
  532. package/runtime/bin/okstra-team-reconcile.sh +0 -36
  533. package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
  534. package/runtime/python/okstra_ctl/batch.py +0 -60
  535. package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
  536. package/runtime/python/okstra_ctl/container_registry.py +0 -72
  537. package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
  538. package/runtime/python/okstra_ctl/resolver.py +0 -54
  539. package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
  540. package/runtime/python/okstra_ctl/tmux.py +0 -134
  541. package/runtime/python/okstra_ctl/worktree.py +0 -1099
@@ -8,7 +8,7 @@ from dataclasses import dataclass
8
8
  from pathlib import Path
9
9
  from typing import Any, Callable, Mapping, Sequence
10
10
 
11
- from .agent_activity import agent_activity_rows
11
+ from .agent.activity import agent_activity_rows
12
12
  from .clarification_items import (
13
13
  clarification_disposition,
14
14
  incorporated_clarification_ids,
@@ -22,6 +22,13 @@ from .report_synthesis_packet import (
22
22
  report_synthesis_packet_paths,
23
23
  verify_report_synthesis_packet_sources,
24
24
  )
25
+ from .conformance import strip_stage_declaration_label
26
+ from .design_prep import DesignPrepError, materialize_design_prep_requests
27
+ from .implementation_direction import (
28
+ load_selected_direction_snapshot,
29
+ validate_selected_direction_plan,
30
+ )
31
+ from .paths import task_dir
25
32
  from .report_projections import (
26
33
  ReportProjectionError,
27
34
  project_convergence,
@@ -29,6 +36,8 @@ from .report_projections import (
29
36
  project_execution,
30
37
  project_token_usage,
31
38
  )
39
+ from .scope_provenance import parse_source
40
+ from .verification_target import read_verification_target
32
41
 
33
42
 
34
43
  @dataclass(frozen=True)
@@ -174,7 +183,7 @@ def _carried_clarification_row(source: Mapping[str, Any]) -> dict[str, Any]:
174
183
  key: source[key]
175
184
  for key in (
176
185
  "id", "ticketId", "kind", "statement", "expectedForm", "blocks",
177
- "origin", "userConfirmation", "options",
186
+ "origin", "userConfirmation", "userInput", "options",
178
187
  )
179
188
  if key in source
180
189
  }
@@ -182,8 +191,12 @@ def _carried_clarification_row(source: Mapping[str, Any]) -> dict[str, Any]:
182
191
  row["approvalContext"] = source["approval"]
183
192
  resolution = source.get("resolutionInput")
184
193
  if not isinstance(resolution, Mapping):
185
- row["status"] = "resolved"
186
- return row
194
+ # `resolved` 가 아니라 `answered` 다. 스키마는 `resolved` 에 `resolution`
195
+ # 을 요구하고 그 `checkRefs` 는 **이번 run** 의 활동 행(`A-NNN`)인데,
196
+ # 이월된 답에는 이번 run 의 활동이 없다 — `resolved` 로 쓰면 통과 가능한
197
+ # 값이 없는 행이 된다. 답이 있었다는 사실은 `userInput` 이 나른다.
198
+ row["status"] = "answered"
199
+ return _neutralize_carried_approval_block(row)
187
200
  row.update(
188
201
  status="resolved",
189
202
  userInput=resolution.get("userText", ""),
@@ -193,6 +206,15 @@ def _carried_clarification_row(source: Mapping[str, Any]) -> dict[str, Any]:
193
206
  "checkRefs": list(resolution.get("checkRefs") or []),
194
207
  },
195
208
  )
209
+ return _neutralize_carried_approval_block(row)
210
+
211
+
212
+ def _neutralize_carried_approval_block(row: dict[str, Any]) -> dict[str, Any]:
213
+ """이월 행은 이미 반영된 답이다. Blocks=approval 을 그대로 올리면
214
+ 역추적이 없는 승인 차단이 다시 생긴다."""
215
+ if row.get("blocks") == "approval":
216
+ row["blocks"] = "none"
217
+ row.pop("approvalContext", None)
196
218
  return row
197
219
 
198
220
 
@@ -256,11 +278,77 @@ def _latest_user_response_value(
256
278
  return _project_relative(project_root, responses[-1])
257
279
 
258
280
 
281
+ def _run_inputs_clarification_response(manifest_path: Path) -> str:
282
+ """이 run 을 띄운 carry-in 경로를 per-run `run-inputs-*.json` 에서 읽는다.
283
+
284
+ 같은 사용자 입력을 두 소비자가 서로 다른 파일에서 찾고 있었다.
285
+ `validate-run` 의 `_carry_in_source_for_run` 은 run-inputs 를 보며 그
286
+ 독스트링이 "run-inputs 기록이 유일한 per-run 증거"라고 적어 두는데, 조립은
287
+ 매니페스트와 active-run-context 만 봤다. 두 곳 다 비어 있는 phase 에서는
288
+ 조립이 `clarificationCarryIn` 을 만들지 않고 검증기는 없다고 실패시킨다 —
289
+ 실측(2026-08-26, `fontsninja-nlpvibe` final-verification): 매니페스트에
290
+ 키 없음, active-run-context 의 `instructionSet` 이 `null`, run-inputs 에는
291
+ 값 있음. carry-in 을 붙여 실행한 run 이 구조적으로 실패했다. 그 필드는
292
+ 조립 소유라 작성자가 교정 라운드로도 못 넘는다.
293
+ """
294
+ name = manifest_path.name
295
+ if not name.startswith("run-manifest-") or not name.endswith(".json"):
296
+ return ""
297
+ inputs_path = manifest_path.with_name(
298
+ name.replace("run-manifest-", "run-inputs-", 1)
299
+ )
300
+ if not inputs_path.is_file():
301
+ return ""
302
+ try:
303
+ payload = load_owned_object(inputs_path, artifact="run inputs")
304
+ except (JsonBoundaryError, OSError):
305
+ return ""
306
+ inputs = payload.get("inputs") if isinstance(payload, Mapping) else None
307
+ value = (
308
+ inputs.get("clarificationResponsePath") if isinstance(inputs, Mapping) else None
309
+ )
310
+ return value.strip() if isinstance(value, str) else ""
311
+
312
+
313
+ def _verification_scope(project_root: Path, manifest: Mapping[str, Any]) -> str:
314
+ """이 run 이 검증한 범위 — 준비된 target 스냅샷이 정본이다.
315
+
316
+ 이 필드는 읽는 쪽만 있고 쓰는 쪽이 없었다. `handoff record-verified` 와
317
+ `report_finalize` 의 teardown 분기가 읽고, `validate-run` 은 target 과
318
+ 대조한다. 그런데 서술문 스키마에 속성이 없어 작성자가 저작할 수 없고
319
+ 조립도 만들지 않았다 — 그래서 값은 언제나 비었고, 세 소비자가 전부 조용히
320
+ 엉뚱하게 동작했다: `record-verified` 는 항상 거부하고, teardown 은 항상
321
+ 건너뛰고, target 대조는 `actual` 이 비어 아예 비교되지 않았다. 실측
322
+ (2026-08-26, `fontsninja-nlpvibe` final-verification): 리포트는 수락인데
323
+ `consumers.jsonl` 의 verified 행은 0건인 채로 run 이 `passed` 로 닫혔다.
324
+
325
+ 작성자의 것이 아니라는 것은 프로필이 이미 못박고 있다 — "`verificationScope`
326
+ in particular gates both stage-group eligibility and release-handoff routing,
327
+ so it is not the report's to restate". 그래서 조립이 target 에서 읽어 싣는다.
328
+ 다이제스트가 안 맞는 스냅샷은 `read_verification_target` 이 ``None`` 을
329
+ 돌려주므로 그때는 값을 만들지 않는다 — 변조된 근거로 라우팅을 여는 것보다
330
+ 비어 있는 편이 낫다.
331
+ """
332
+ instruction_set = manifest.get("instructionSet")
333
+ relative = (
334
+ str(instruction_set.get("verificationTargetPath") or "").strip()
335
+ if isinstance(instruction_set, Mapping)
336
+ else ""
337
+ )
338
+ if not relative:
339
+ return ""
340
+ target = read_verification_target(project_root, relative)
341
+ return str((target or {}).get("scope") or "").strip()
342
+
343
+
259
344
  def _clarification_carry_in(
260
345
  project_root: Path,
261
346
  manifest: Mapping[str, Any],
347
+ manifest_path: Path,
262
348
  ) -> dict[str, str] | None:
263
- value = _clarification_response_value(project_root, manifest)
349
+ value = _clarification_response_value(project_root, manifest) or (
350
+ _run_inputs_clarification_response(manifest_path)
351
+ )
264
352
  if not value:
265
353
  return None
266
354
  path = Path(value)
@@ -358,18 +446,118 @@ def _compose(
358
446
  data["tokenUsage"] = usage
359
447
  data["crossVerification"] = convergence_data["crossVerification"]
360
448
  data["clarificationItems"] = _clarifications(ledger, activities, inputs["approval-decisions"].path)
361
- carry_in = _clarification_carry_in(project_root, manifest)
449
+ carry_in = _clarification_carry_in(project_root, manifest, manifest_path)
362
450
  if carry_in is not None:
363
451
  data["clarificationCarryIn"] = carry_in
452
+ scope = _verification_scope(project_root, manifest)
453
+ if scope:
454
+ data["verificationScope"] = scope
364
455
  data["agentActivity"] = activities
365
456
  data["evidence"] = {"primary": _promoted_evidence(convergence_data)}
366
457
  data["missingInformation"] = []
458
+ # 스냅샷 복사가 _attach_metadata 보다 먼저다 — frontmatter 가
459
+ # selectedDirectionRef.snapshotPath 를 읽는다.
460
+ _apply_selected_direction_snapshot(data, project_root, manifest)
367
461
  _attach_metadata(data, manifest)
368
462
  _attach_machine_planning(data, inputs)
369
463
  _attach_plan_backlinks(data, activities)
464
+ _fill_end_state_coverage(data)
370
465
  return data
371
466
 
372
467
 
468
+ def _selected_direction_inputs(
469
+ project_root: Path, manifest: Mapping[str, Any]
470
+ ) -> tuple[Path, Path]:
471
+ """구현 진입(run.py `_validate_selected_implementation_plan`)과 같은 경로."""
472
+ _project_id, task_group, task_id = _identity(manifest)
473
+ instruction_set = task_dir(project_root, task_group, task_id) / "instruction-set"
474
+ return (
475
+ instruction_set / "selected-direction.json",
476
+ instruction_set / "task-brief.md",
477
+ )
478
+
479
+
480
+ def _apply_selected_direction_snapshot(
481
+ data: dict[str, Any], project_root: Path, manifest: Mapping[str, Any]
482
+ ) -> None:
483
+ """selected-direction 계획의 기계 복사 필드를 스냅샷 원문으로 채운다.
484
+
485
+ `directionRealization` 의 4필드는 `!=` 완전 일치가 계약인데
486
+ (implementation_direction `_direction_realization_errors`) writer 가
487
+ 의역하는 실측 실패가 있었다(dev-10341, 4필드 전부). 판단이 없는 복사는
488
+ 조립 소유다 — `_attach_machine_planning` 이 design-prep 투영을 소유하는
489
+ 것과 같은 근거. `conformanceTests` 의 `stage-<N> — ` 라벨 잔류도 같은
490
+ 계급이라 여기서 뗀다.
491
+ """
492
+ planning = data.get("implementationPlanning")
493
+ if (
494
+ not isinstance(planning, dict)
495
+ or planning.get("planningContract") != "selected-direction"
496
+ ):
497
+ return
498
+ snapshot_path, _brief_path = _selected_direction_inputs(project_root, manifest)
499
+ snapshot, load_failures = load_selected_direction_snapshot(snapshot_path)
500
+ if snapshot is None:
501
+ _fail(
502
+ "orchestrator", snapshot_path,
503
+ "implementationPlanning.selectedDirectionRef",
504
+ "; ".join(load_failures),
505
+ )
506
+ # selectedDirectionRef 5필드도 전부 기계 유도값이다 — sourceReport /
507
+ # sourceDataSha256 / optionId 는 스냅샷 본문에, 경로·해시는 스냅샷
508
+ # 실물에 있다. run 001 실측에서 writer 가 snapshotPath 를 틀렸다.
509
+ planning["selectedDirectionRef"] = {
510
+ "sourceReport": snapshot.data.get("sourceReport"),
511
+ "sourceDataSha256": snapshot.data.get("sourceDataSha256"),
512
+ "optionId": snapshot.data.get("optionId"),
513
+ "snapshotPath": snapshot.relative_path,
514
+ "snapshotSha256": snapshot.sha256,
515
+ }
516
+ realization = planning.get("directionRealization")
517
+ direction = snapshot.data.get("direction")
518
+ if isinstance(realization, dict) and isinstance(direction, Mapping):
519
+ realization["coreMechanism"] = direction.get("coreMechanism")
520
+ realization["architectureBoundaries"] = direction.get(
521
+ "architectureBoundaries"
522
+ )
523
+ realization["planningInvariants"] = snapshot.data.get("planningInvariants")
524
+ realization["userConstraints"] = snapshot.data.get("userConstraints")
525
+ for stage in planning.get("stages") or []:
526
+ if not isinstance(stage, dict):
527
+ continue
528
+ declared = stage.get("conformanceTests")
529
+ number = stage.get("stage")
530
+ if (
531
+ isinstance(declared, str)
532
+ and isinstance(number, int)
533
+ and not isinstance(number, bool)
534
+ and number >= 1
535
+ ):
536
+ stage["conformanceTests"] = strip_stage_declaration_label(
537
+ number, declared
538
+ )
539
+
540
+
541
+ def _selected_direction_publication_errors(
542
+ data: Mapping[str, Any], project_root: Path, manifest: Mapping[str, Any]
543
+ ) -> list[str]:
544
+ """게시 직전에 구현 진입 검증을 그대로 돌린다.
545
+
546
+ 같은 검증이 말미 validate-run 에서는 advisory 로만 나와, 통과 발행된
547
+ 계획이 구현 진입(run.py:1445)에서 처음 하드 거부되는 실측 wedge 가
548
+ 있었다(dev-10341, 17건). 게시 시점 실패는 writer/lead 가 run 안에서
549
+ 고칠 수 있다.
550
+ """
551
+ planning = data.get("implementationPlanning")
552
+ if (
553
+ not isinstance(planning, Mapping)
554
+ or planning.get("planningContract") != "selected-direction"
555
+ ):
556
+ return []
557
+ snapshot_path, brief_path = _selected_direction_inputs(project_root, manifest)
558
+ return validate_selected_direction_plan(data, brief_path, snapshot_path)
559
+
560
+
373
561
  def _identity(manifest: Mapping[str, Any]) -> tuple[str, str, str]:
374
562
  task_key = str(manifest.get("taskKey") or "")
375
563
  parts = task_key.split(":")
@@ -481,6 +669,84 @@ def _attach_machine_planning(
481
669
  "planBodyVerification", "must be an object",
482
670
  )
483
671
  planning["planBodyVerification"] = dict(projection)
672
+ _sync_human_summary_with_plan_body_gate(data, projection)
673
+
674
+
675
+ def _sync_human_summary_with_plan_body_gate(
676
+ data: dict[str, Any], projection: Mapping[str, Any],
677
+ ) -> None:
678
+ """게이트가 통과했는데 내러티브가 unseeded 로 남으면 사람용 칸을 맞춘다.
679
+
680
+ report-writer 는 PBV 전에 쓰고, 조립이 게이트를 덮어씌운다. blockers 와
681
+ nextStep 이 그때의 문장을 그대로 두면 통과한 계획이 아직 검증 전으로 보인다.
682
+ """
683
+ gate = str(projection.get("gateResult") or "")
684
+ if gate not in {"passed", "passed-with-dissent"}:
685
+ return
686
+ summary = data.get("humanSummary")
687
+ if isinstance(summary, dict) and isinstance(summary.get("blockers"), list):
688
+ summary["blockers"] = [
689
+ row for row in summary["blockers"]
690
+ if "unseeded" not in str(row).lower()
691
+ ]
692
+ card = data.get("verdictCard")
693
+ if not isinstance(card, dict):
694
+ return
695
+ next_step = str(card.get("nextStep") or "")
696
+ lowered = next_step.lower()
697
+ if "unseeded" not in lowered and "seed" not in lowered:
698
+ return
699
+ next_step = (
700
+ "Plan-body verification passed. Do not start implementation "
701
+ "until `frontmatter.approved` is true."
702
+ )
703
+ card["nextStep"] = next_step
704
+ final = data.get("finalVerdict")
705
+ if isinstance(final, dict):
706
+ final["nextStep"] = next_step
707
+
708
+
709
+ def _fill_end_state_coverage(data: dict[str, Any]) -> None:
710
+ """writer 가 못 쓴 endStateCoverage 를 requirementCoverage brief:EB-001 에서 채운다.
711
+
712
+ 내러티브 스키마가 이 칸을 열어 주면 writer 가 쓸 수 있다. 비어 있으면
713
+ validate-run 이 brief id 마다 한 행을 요구하므로 조립이 채운다.
714
+ """
715
+ existing = data.get("endStateCoverage")
716
+ if isinstance(existing, list) and existing:
717
+ return
718
+ planning = data.get("implementationPlanning")
719
+ if not isinstance(planning, dict):
720
+ return
721
+ derived = _end_state_rows_from_requirement_coverage(planning)
722
+ if derived:
723
+ data["endStateCoverage"] = derived
724
+
725
+
726
+ def _end_state_rows_from_requirement_coverage(
727
+ planning: Mapping[str, Any],
728
+ ) -> list[dict[str, Any]]:
729
+ rows: list[dict[str, Any]] = []
730
+ seen: set[str] = set()
731
+ for item in planning.get("requirementCoverage") or []:
732
+ if not isinstance(item, Mapping):
733
+ continue
734
+ ref = parse_source(str(item.get("source") or ""))
735
+ if not ref.is_end_state_id or ref.value in seen:
736
+ continue
737
+ seen.add(ref.value)
738
+ status = str(item.get("status") or "")
739
+ row: dict[str, Any] = {
740
+ "id": ref.value,
741
+ "disposition": "addressed" if status == "covered" else "deferred",
742
+ "coveredBy": str(item.get("id") or item.get("coveredBy") or ""),
743
+ }
744
+ if row["disposition"] != "addressed":
745
+ row["rationale"] = (
746
+ f"requirementCoverage `{item.get('id')}` status is `{status or 'empty'}`."
747
+ )
748
+ rows.append(row)
749
+ return rows
484
750
 
485
751
 
486
752
  def _schema_owner(error: str) -> str:
@@ -522,6 +788,20 @@ def _publish(
522
788
  os.unlink(name)
523
789
 
524
790
 
791
+ def _materialize_planning_requests(target: Path) -> None:
792
+ """게시한 계획 기록에서 Okstra 소유 design-prep 요청을 만든다.
793
+
794
+ 계약 3.0 은 token-usage 에서 `--substitute-data` 를 빼므로, v2 가
795
+ populate_token_cells 안에서 하던 생성을 조립이 맡는다.
796
+ """
797
+ if not target.name.startswith("final-report-implementation-planning-"):
798
+ return
799
+ try:
800
+ materialize_design_prep_requests(target)
801
+ except DesignPrepError as exc:
802
+ _fail("orchestrator", target, "designPreparation", exc)
803
+
804
+
525
805
  def assemble_report(
526
806
  project_root: Path, manifest_path: Path, *,
527
807
  replace: Callable[[str, str], None] = os.replace,
@@ -557,13 +837,16 @@ def assemble_report(
557
837
  raise ReportAssemblyError(tuple(input_issues))
558
838
  data = _compose(project_root, manifest_path, manifest, inputs, schema)
559
839
  errors = validate(data, schema)
840
+ direction_errors = _selected_direction_publication_errors(
841
+ data, project_root, manifest
842
+ )
560
843
  value = manifest.get("expectedReportRecordPath")
561
844
  if not isinstance(value, str) or not value:
562
845
  _fail("orchestrator", manifest_path, "expectedReportRecordPath", "required")
563
846
  target = Path(value)
564
847
  target = target if target.is_absolute() else project_root / target
565
848
  _publish(target, data, replace)
566
- if errors:
849
+ if errors or direction_errors:
567
850
  raise ReportAssemblyError(
568
851
  tuple(
569
852
  AssemblyIssue(
@@ -574,5 +857,12 @@ def assemble_report(
574
857
  )
575
858
  for error in errors
576
859
  )
860
+ + tuple(
861
+ AssemblyIssue(
862
+ "report-writer", str(target), "implementationPlanning", error
863
+ )
864
+ for error in direction_errors
865
+ )
577
866
  )
867
+ _materialize_planning_requests(target)
578
868
  return data
@@ -149,7 +149,37 @@ IMPLEMENTATION_PLANNING_INVALIDATED_REQUIRED_HUMAN_FIELDS = (
149
149
  )
150
150
 
151
151
 
152
+ def _renders_no_row(data: dict, field: str) -> bool:
153
+ """행 하나마다 찍히는 마커인데 행이 0개인가.
154
+
155
+ `<prop>.<array>.<subfield>` 꼴 필수 필드의 마커는 HTML 템플릿의 행 루프
156
+ 안에 있다. 배열이 비면 마커는 나올 수 없고, 그래도 요구하면 렌더러가
157
+ 만들 수 없는 것을 검증기가 요구한다. 실제로
158
+ `implementationOptionSelection.rankedOptions.requirementCoverage` 가 유효
159
+ 후보 0건인 run 을 contract-violated 로 만들었다 — 스키마는 빈 배열을
160
+ 허용하고, `implementation_options._validate_option_count_and_routing` 는
161
+ 그 상태에서 `routing: blocked` 를 **요구**한다.
162
+
163
+ 부모가 객체인 세 마디 필드(`decisionContext.originalRequirementIds`)는
164
+ 행 단위가 아니므로 그대로 남는다.
165
+ """
166
+ parts = field.split(".")
167
+ if len(parts) < 3:
168
+ return False
169
+ node: object = data
170
+ for part in parts[:-1]:
171
+ if not isinstance(node, dict):
172
+ return False
173
+ node = node.get(part)
174
+ return isinstance(node, list) and not node
175
+
176
+
152
177
  def required_human_fields_for_report(task_type: str, data: dict) -> tuple[str, ...]:
178
+ fields = _required_human_fields_by_branch(task_type, data)
179
+ return tuple(field for field in fields if not _renders_no_row(data, field))
180
+
181
+
182
+ def _required_human_fields_by_branch(task_type: str, data: dict) -> tuple[str, ...]:
153
183
  if task_type != "implementation-planning":
154
184
  return TASK_TYPE_REQUIRED_HUMAN_FIELDS.get(task_type, ())
155
185
  planning = data.get("implementationPlanning", {})
@@ -160,13 +190,6 @@ def required_human_fields_for_report(task_type: str, data: dict) -> tuple[str, .
160
190
  return IMPLEMENTATION_PLANNING_PLAN_READY_REQUIRED_HUMAN_FIELDS
161
191
 
162
192
 
163
- def report_property_for(task_type: str) -> str:
164
- try:
165
- return TASK_TYPE_DATA_PROPERTY[task_type]
166
- except KeyError as exc:
167
- raise ValueError(f"unsupported public report task type: {task_type}") from exc
168
-
169
-
170
193
  def html_template_for(task_type: str) -> str:
171
194
  try:
172
195
  return TASK_TYPE_HTML_TEMPLATE[task_type]