okstra 0.186.6 → 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 (548) 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 +487 -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 +32 -7
  232. package/runtime/python/okstra_ctl/report_finalize.py +206 -15
  233. package/runtime/python/okstra_ctl/report_html/common.py +4 -9
  234. package/runtime/python/okstra_ctl/report_html/render.py +11 -12
  235. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
  236. package/runtime/python/okstra_ctl/report_language.py +8 -4
  237. package/runtime/python/okstra_ctl/report_narrative.py +90 -3
  238. package/runtime/python/okstra_ctl/report_projections.py +8 -2
  239. package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
  240. package/runtime/python/okstra_ctl/report_translation.py +2 -35
  241. package/runtime/python/okstra_ctl/report_views.py +4 -22
  242. package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
  243. package/runtime/python/okstra_ctl/rollup.py +13 -0
  244. package/runtime/python/okstra_ctl/run.py +639 -138
  245. package/runtime/python/okstra_ctl/run_audit.py +13 -0
  246. package/runtime/python/okstra_ctl/run_index_row.py +0 -12
  247. package/runtime/python/okstra_ctl/seeding.py +21 -11
  248. package/runtime/python/okstra_ctl/sequence.py +1 -1
  249. package/runtime/python/okstra_ctl/session.py +64 -14
  250. package/runtime/python/okstra_ctl/set_work_status.py +18 -0
  251. package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
  252. package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
  253. package/runtime/python/okstra_ctl/stage_map.py +57 -11
  254. package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
  255. package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
  256. package/runtime/python/okstra_ctl/stage_targets.py +3 -12
  257. package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
  258. package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
  259. package/runtime/python/okstra_ctl/task_target.py +4 -16
  260. package/runtime/python/okstra_ctl/team.py +49 -2
  261. package/runtime/python/okstra_ctl/time_report.py +17 -2
  262. package/runtime/python/okstra_ctl/usage_report.py +11 -0
  263. package/runtime/python/okstra_ctl/user_response.py +55 -218
  264. package/runtime/python/okstra_ctl/user_response_values.py +242 -0
  265. package/runtime/python/okstra_ctl/validation_contract.py +3 -0
  266. package/runtime/python/okstra_ctl/verification_target.py +68 -0
  267. package/runtime/python/okstra_ctl/wizard.py +457 -92
  268. package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
  269. package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
  270. package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
  271. package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
  272. package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
  273. package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
  274. package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
  275. package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
  276. package/runtime/python/okstra_ctl/worker_request.py +2 -1
  277. package/runtime/python/okstra_ctl/worker_runner.py +16 -9
  278. package/runtime/python/okstra_ctl/worker_state.py +16 -1
  279. package/runtime/python/okstra_ctl/workflow.py +18 -1
  280. package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
  281. package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
  282. package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
  283. package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
  284. package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
  285. package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
  286. package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
  287. package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
  288. package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
  289. package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
  290. package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
  291. package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
  292. package/runtime/python/okstra_ctl/write_policy.py +90 -213
  293. package/runtime/python/okstra_project/__init__.py +0 -4
  294. package/runtime/python/okstra_project/dirs.py +2 -2
  295. package/runtime/python/okstra_project/phase_pointer.py +84 -0
  296. package/runtime/python/okstra_project/slug.py +24 -0
  297. package/runtime/python/okstra_project/state.py +22 -230
  298. package/runtime/python/okstra_token_usage/claude.py +9 -1
  299. package/runtime/python/okstra_token_usage/cli.py +3 -1
  300. package/runtime/python/okstra_token_usage/collect.py +71 -10
  301. package/runtime/python/okstra_token_usage/cursor.py +2 -0
  302. package/runtime/python/okstra_token_usage/grok.py +1 -5
  303. package/runtime/python/okstra_token_usage/paths.py +26 -0
  304. package/runtime/python/okstra_token_usage/pricing.py +19 -7
  305. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
  306. package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
  307. package/runtime/schemas/final-report-v2.0.schema.json +13 -26
  308. package/runtime/schemas/final-report-v3.0.schema.json +177 -41
  309. package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
  310. package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
  311. package/runtime/skills/okstra-container-build/SKILL.md +16 -47
  312. package/runtime/skills/okstra-inspect/facets/history.md +1 -1
  313. package/runtime/skills/okstra-inspect/facets/status.md +7 -6
  314. package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
  315. package/runtime/skills/okstra-run/SKILL.md +7 -5
  316. package/runtime/templates/report-writer-prompt-preamble.md +1 -1
  317. package/runtime/templates/reports/brief.template.md +6 -2
  318. package/runtime/templates/reports/error-analysis-input.template.md +2 -0
  319. package/runtime/templates/reports/final-verification-input.template.md +2 -0
  320. package/runtime/templates/reports/html/base.template.html +13 -0
  321. package/runtime/templates/reports/html/i18n/en.json +19 -0
  322. package/runtime/templates/reports/html/i18n/ko.json +19 -0
  323. package/runtime/templates/reports/html/tasks/final-verification.template.html +2 -2
  324. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +19 -9
  325. package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
  326. package/runtime/templates/reports/implementation-input.template.md +7 -1
  327. package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
  328. package/runtime/templates/reports/quick-input.template.md +2 -0
  329. package/runtime/templates/reports/release-handoff-input.template.md +6 -1
  330. package/runtime/templates/reports/report.js +20 -3
  331. package/runtime/templates/reports/schedule.template.md +2 -0
  332. package/runtime/templates/reports/settings.template.json +0 -10
  333. package/runtime/templates/reports/task-brief.template.md +3 -0
  334. package/runtime/templates/reports/user-response.template.md +7 -3
  335. package/runtime/validators/checks/fixtures-01.py +57 -0
  336. package/runtime/validators/checks/fixtures-02.py +565 -0
  337. package/runtime/validators/checks/runners-01.py +108 -0
  338. package/runtime/validators/checks/validate-assets-01.py +60 -0
  339. package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
  340. package/runtime/validators/checks/validate-tasks-01.py +46 -0
  341. package/runtime/validators/checks/validate-tasks-02.py +85 -0
  342. package/runtime/validators/checks/validate-tasks-03.py +61 -0
  343. package/runtime/validators/checks/validate-tasks-04.py +118 -0
  344. package/runtime/validators/forbidden_actions.py +73 -3
  345. package/runtime/validators/lib/common.sh +5 -0
  346. package/runtime/validators/lib/fixtures.sh +7 -591
  347. package/runtime/validators/lib/paths.sh +13 -0
  348. package/runtime/validators/lib/runners.sh +6 -104
  349. package/runtime/validators/lib/summary.sh +1 -1
  350. package/runtime/validators/lib/validate-assets.sh +6 -56
  351. package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
  352. package/runtime/validators/lib/validate-tasks.sh +9 -294
  353. package/runtime/validators/validate-implementation-plan-stages.py +4 -4
  354. package/runtime/validators/validate-run.py +1369 -2654
  355. package/runtime/validators/validate-workflow.sh +56 -16
  356. package/runtime/validators/validate_improvement_report.py +2 -1
  357. package/runtime/validators/validate_session_conformance.py +295 -49
  358. package/dist/commands/execute/agent-prompt.d.mts +0 -1
  359. package/dist/commands/execute/agent-prompt.mjs +0 -24
  360. package/dist/commands/execute/agent-prompt.mjs.map +0 -1
  361. package/dist/commands/execute/codex-dispatch.d.mts +0 -3
  362. package/dist/commands/execute/codex-dispatch.mjs +0 -6
  363. package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
  364. package/dist/commands/execute/codex-run.d.mts +0 -3
  365. package/dist/commands/execute/codex-run.mjs +0 -62
  366. package/dist/commands/execute/codex-run.mjs.map +0 -1
  367. package/dist/commands/execute/convergence.d.mts +0 -1
  368. package/dist/commands/execute/convergence.mjs +0 -38
  369. package/dist/commands/execute/convergence.mjs.map +0 -1
  370. package/dist/commands/execute/error-log.d.mts +0 -1
  371. package/dist/commands/execute/error-log.mjs +0 -18
  372. package/dist/commands/execute/error-log.mjs.map +0 -1
  373. package/dist/commands/execute/git-reconcile.d.mts +0 -1
  374. package/dist/commands/execute/git-reconcile.mjs +0 -30
  375. package/dist/commands/execute/git-reconcile.mjs.map +0 -1
  376. package/dist/commands/execute/handoff.d.mts +0 -1
  377. package/dist/commands/execute/handoff.mjs +0 -31
  378. package/dist/commands/execute/handoff.mjs.map +0 -1
  379. package/dist/commands/execute/incremental-carry.d.mts +0 -1
  380. package/dist/commands/execute/incremental-carry.mjs +0 -20
  381. package/dist/commands/execute/incremental-carry.mjs.map +0 -1
  382. package/dist/commands/execute/incremental-scope.d.mts +0 -1
  383. package/dist/commands/execute/incremental-scope.mjs +0 -29
  384. package/dist/commands/execute/incremental-scope.mjs.map +0 -1
  385. package/dist/commands/execute/integrate-stages.d.mts +0 -1
  386. package/dist/commands/execute/integrate-stages.mjs +0 -24
  387. package/dist/commands/execute/integrate-stages.mjs.map +0 -1
  388. package/dist/commands/execute/pane-title.d.mts +0 -1
  389. package/dist/commands/execute/pane-title.mjs +0 -20
  390. package/dist/commands/execute/pane-title.mjs.map +0 -1
  391. package/dist/commands/execute/plan-items.d.mts +0 -1
  392. package/dist/commands/execute/plan-items.mjs +0 -9
  393. package/dist/commands/execute/plan-items.mjs.map +0 -1
  394. package/dist/commands/execute/plan-validate.d.mts +0 -1
  395. package/dist/commands/execute/plan-validate.mjs +0 -68
  396. package/dist/commands/execute/plan-validate.mjs.map +0 -1
  397. package/dist/commands/execute/plan-verify.d.mts +0 -1
  398. package/dist/commands/execute/plan-verify.mjs +0 -43
  399. package/dist/commands/execute/plan-verify.mjs.map +0 -1
  400. package/dist/commands/execute/spawn-followups.d.mts +0 -1
  401. package/dist/commands/execute/spawn-followups.mjs +0 -22
  402. package/dist/commands/execute/spawn-followups.mjs.map +0 -1
  403. package/dist/commands/execute/team.d.mts +0 -3
  404. package/dist/commands/execute/team.mjs +0 -66
  405. package/dist/commands/execute/team.mjs.map +0 -1
  406. package/dist/commands/execute/token-usage.d.mts +0 -1
  407. package/dist/commands/execute/token-usage.mjs +0 -19
  408. package/dist/commands/execute/token-usage.mjs.map +0 -1
  409. package/dist/commands/execute/worker-audit-check.d.mts +0 -1
  410. package/dist/commands/execute/worker-audit-check.mjs +0 -34
  411. package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
  412. package/dist/commands/execute/worker-dispatch.d.mts +0 -7
  413. package/dist/commands/execute/worker-dispatch.mjs +0 -64
  414. package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
  415. package/dist/commands/execute/worker-state.d.mts +0 -1
  416. package/dist/commands/execute/worker-state.mjs +0 -28
  417. package/dist/commands/execute/worker-state.mjs.map +0 -1
  418. package/dist/commands/execute/worktree-lookup.d.mts +0 -1
  419. package/dist/commands/execute/worktree-lookup.mjs +0 -92
  420. package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
  421. package/dist/commands/execute/worktree-status.d.mts +0 -1
  422. package/dist/commands/execute/worktree-status.mjs +0 -121
  423. package/dist/commands/execute/worktree-status.mjs.map +0 -1
  424. package/dist/commands/inspect/code-review.d.mts +0 -1
  425. package/dist/commands/inspect/code-review.mjs +0 -32
  426. package/dist/commands/inspect/code-review.mjs.map +0 -1
  427. package/dist/commands/inspect/container.d.mts +0 -1
  428. package/dist/commands/inspect/container.mjs +0 -25
  429. package/dist/commands/inspect/container.mjs.map +0 -1
  430. package/dist/commands/inspect/context-cost.d.mts +0 -1
  431. package/dist/commands/inspect/context-cost.mjs +0 -25
  432. package/dist/commands/inspect/context-cost.mjs.map +0 -1
  433. package/dist/commands/inspect/design-prep.d.mts +0 -1
  434. package/dist/commands/inspect/design-prep.mjs +0 -22
  435. package/dist/commands/inspect/design-prep.mjs.map +0 -1
  436. package/dist/commands/inspect/error-report.d.mts +0 -1
  437. package/dist/commands/inspect/error-report.mjs +0 -25
  438. package/dist/commands/inspect/error-report.mjs.map +0 -1
  439. package/dist/commands/inspect/error-zip.d.mts +0 -1
  440. package/dist/commands/inspect/error-zip.mjs +0 -24
  441. package/dist/commands/inspect/error-zip.mjs.map +0 -1
  442. package/dist/commands/inspect/log-report.d.mts +0 -1
  443. package/dist/commands/inspect/log-report.mjs +0 -26
  444. package/dist/commands/inspect/log-report.mjs.map +0 -1
  445. package/dist/commands/inspect/model-io.d.mts +0 -1
  446. package/dist/commands/inspect/model-io.mjs +0 -25
  447. package/dist/commands/inspect/model-io.mjs.map +0 -1
  448. package/dist/commands/inspect/profile-show.d.mts +0 -1
  449. package/dist/commands/inspect/profile-show.mjs +0 -28
  450. package/dist/commands/inspect/profile-show.mjs.map +0 -1
  451. package/dist/commands/inspect/recap.d.mts +0 -1
  452. package/dist/commands/inspect/recap.mjs +0 -30
  453. package/dist/commands/inspect/recap.mjs.map +0 -1
  454. package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
  455. package/dist/commands/inspect/resolve-task-key.mjs +0 -24
  456. package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
  457. package/dist/commands/inspect/rollup.d.mts +0 -1
  458. package/dist/commands/inspect/rollup.mjs +0 -25
  459. package/dist/commands/inspect/rollup.mjs.map +0 -1
  460. package/dist/commands/inspect/run-audit.d.mts +0 -1
  461. package/dist/commands/inspect/run-audit.mjs +0 -25
  462. package/dist/commands/inspect/run-audit.mjs.map +0 -1
  463. package/dist/commands/inspect/set-work-status.d.mts +0 -1
  464. package/dist/commands/inspect/set-work-status.mjs +0 -29
  465. package/dist/commands/inspect/set-work-status.mjs.map +0 -1
  466. package/dist/commands/inspect/stage-map.d.mts +0 -1
  467. package/dist/commands/inspect/stage-map.mjs +0 -131
  468. package/dist/commands/inspect/stage-map.mjs.map +0 -1
  469. package/dist/commands/inspect/task-list.d.mts +0 -1
  470. package/dist/commands/inspect/task-list.mjs +0 -149
  471. package/dist/commands/inspect/task-list.mjs.map +0 -1
  472. package/dist/commands/inspect/task-show.d.mts +0 -1
  473. package/dist/commands/inspect/task-show.mjs +0 -108
  474. package/dist/commands/inspect/task-show.mjs.map +0 -1
  475. package/dist/commands/inspect/time-report.d.mts +0 -1
  476. package/dist/commands/inspect/time-report.mjs +0 -24
  477. package/dist/commands/inspect/time-report.mjs.map +0 -1
  478. package/dist/commands/inspect/usage-report.d.mts +0 -1
  479. package/dist/commands/inspect/usage-report.mjs +0 -23
  480. package/dist/commands/inspect/usage-report.mjs.map +0 -1
  481. package/dist/commands/inspect/user-response.d.mts +0 -1
  482. package/dist/commands/inspect/user-response.mjs +0 -35
  483. package/dist/commands/inspect/user-response.mjs.map +0 -1
  484. package/dist/commands/inspect/worker-liveness.d.mts +0 -1
  485. package/dist/commands/inspect/worker-liveness.mjs +0 -45
  486. package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
  487. package/dist/commands/lifecycle/contract-check.d.mts +0 -1
  488. package/dist/commands/lifecycle/contract-check.mjs +0 -18
  489. package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
  490. package/dist/commands/lifecycle/migrate.d.mts +0 -1
  491. package/dist/commands/lifecycle/migrate.mjs +0 -30
  492. package/dist/commands/lifecycle/migrate.mjs.map +0 -1
  493. package/dist/commands/lifecycle/model.d.mts +0 -1
  494. package/dist/commands/lifecycle/model.mjs +0 -22
  495. package/dist/commands/lifecycle/model.mjs.map +0 -1
  496. package/dist/commands/manager.d.mts +0 -3
  497. package/dist/commands/manager.mjs +0 -50
  498. package/dist/commands/manager.mjs.map +0 -1
  499. package/dist/commands/report/agent-activity.d.mts +0 -1
  500. package/dist/commands/report/agent-activity.mjs +0 -20
  501. package/dist/commands/report/agent-activity.mjs.map +0 -1
  502. package/dist/commands/report/approval-decision.d.mts +0 -1
  503. package/dist/commands/report/approval-decision.mjs +0 -21
  504. package/dist/commands/report/approval-decision.mjs.map +0 -1
  505. package/dist/commands/report/design-snapshot.d.mts +0 -1
  506. package/dist/commands/report/design-snapshot.mjs +0 -19
  507. package/dist/commands/report/design-snapshot.mjs.map +0 -1
  508. package/dist/commands/report/finalize.d.mts +0 -4
  509. package/dist/commands/report/finalize.mjs +0 -64
  510. package/dist/commands/report/finalize.mjs.map +0 -1
  511. package/dist/commands/report/inject-report-index.d.mts +0 -1
  512. package/dist/commands/report/inject-report-index.mjs +0 -21
  513. package/dist/commands/report/inject-report-index.mjs.map +0 -1
  514. package/dist/commands/report/render-final-report.d.mts +0 -1
  515. package/dist/commands/report/render-final-report.mjs +0 -23
  516. package/dist/commands/report/render-final-report.mjs.map +0 -1
  517. package/dist/commands/report/render-views.d.mts +0 -1
  518. package/dist/commands/report/render-views.mjs +0 -27
  519. package/dist/commands/report/render-views.mjs.map +0 -1
  520. package/dist/commands/report/translate.d.mts +0 -1
  521. package/dist/commands/report/translate.mjs +0 -33
  522. package/dist/commands/report/translate.mjs.map +0 -1
  523. package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
  524. package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
  525. package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
  526. package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
  527. package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
  528. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
  529. package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
  530. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
  531. package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
  532. package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
  533. package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
  534. package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
  535. package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
  536. package/runtime/bin/okstra-central.sh +0 -152
  537. package/runtime/bin/okstra-incremental-carry.py +0 -10
  538. package/runtime/bin/okstra-incremental-scope.py +0 -10
  539. package/runtime/bin/okstra-team-reconcile.sh +0 -36
  540. package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
  541. package/runtime/python/okstra_ctl/batch.py +0 -60
  542. package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
  543. package/runtime/python/okstra_ctl/container_registry.py +0 -72
  544. package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
  545. package/runtime/python/okstra_ctl/resolver.py +0 -54
  546. package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
  547. package/runtime/python/okstra_ctl/tmux.py +0 -134
  548. package/runtime/python/okstra_ctl/worktree.py +0 -1099
@@ -15,7 +15,7 @@ import re
15
15
  from pathlib import Path
16
16
  from typing import NamedTuple
17
17
 
18
- from .stage_targets import PrepareError
18
+ from .prepare_error import PrepareError
19
19
 
20
20
  _FINAL_REPORT_RE = re.compile(
21
21
  r"final-report-implementation-planning-(\d+)\.data\.json$"
@@ -0,0 +1,51 @@
1
+ """`okstra plan-validate` 의 프로젝션 — 승인 계획 문서의 구조 검증.
2
+
3
+ 원래 `src/commands/execute/plan-validate.mts` 의 문자열 리터럴 안에 있던 코드다
4
+ (docs/coding-rules.md R1 위반). 로직은 그대로 옮겼다.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import json
10
+
11
+
12
+ _CLI_EPILOG = r"""Usage:
13
+ okstra plan-validate <plan-path>
14
+
15
+ Output: JSON { ok: true, planPath } on success.
16
+ On failure: { ok: false, reason } with non-zero exit code.
17
+
18
+ Use this in flows that need to confirm a plan was approved without invoking
19
+ the full prepare_task_bundle pipeline.
20
+ """
21
+ _CLI_DESCRIPTION = "Verify an approved-plan file has a recognised approval marker."
22
+
23
+
24
+ def main(argv: list[str] | None = None) -> int:
25
+ parser = argparse.ArgumentParser(
26
+ description=_CLI_DESCRIPTION,
27
+ epilog=_CLI_EPILOG,
28
+ formatter_class=argparse.RawDescriptionHelpFormatter,
29
+ prog="okstra plan-validate")
30
+ parser.add_argument("plan_path", metavar="plan-path")
31
+ args = parser.parse_args(argv)
32
+
33
+ # run.py 는 무거운 모듈이라 import 시점을 이 함수 안으로 미룬다 —
34
+ # `--help` 만 부르는 호출에 prepare 전체를 적재하지 않기 위해서다.
35
+ from .run import PrepareError, _validate_approved_plan
36
+
37
+ try:
38
+ _validate_approved_plan(args.plan_path)
39
+ except PrepareError as exc:
40
+ print(json.dumps({
41
+ "ok": False, "stage": "validation",
42
+ "reason": str(exc), "planPath": args.plan_path,
43
+ }))
44
+ return 1
45
+
46
+ print(json.dumps({"ok": True, "planPath": args.plan_path}))
47
+ return 0
48
+
49
+
50
+ if __name__ == "__main__":
51
+ raise SystemExit(main())
@@ -0,0 +1,80 @@
1
+ """`okstra plan-verify` 의 진입점.
2
+
3
+ 판정 로직은 `validators/validate-run.py --section plan-body` 가 전부 갖고 있다.
4
+ 이 모듈이 하는 일은 둘뿐이다 — 그 `--section` 값을 이 명령이 정하는 것으로
5
+ 못박고, 자기 이름의 도움말을 갖는 것. 검증기의 파서를 그대로 보여 주면 prog 가
6
+ `validate-run.py` 로 나오고, 여기서 금지된 `--section` 이 선택 가능한 플래그로
7
+ 보인다.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import subprocess
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ from .report_finalize import resolve_workspace_validator
17
+
18
+ _CLI_EPILOG = r"""Usage:
19
+ okstra plan-verify --narrative <report-writer-narrative.md> --state <plan-body-verification.json>
20
+ okstra plan-verify --report <final-report-implementation-planning-<seq>.data.json>
21
+
22
+ Recomputes the gate from the convergence-owned state's `planItems[].verdicts`
23
+ before publication. The `--report` form reads historical v2 reports. It runs
24
+ every plan-body contract check that a round can be judged on its own — verdict
25
+ provenance, fixability, subject substance, self-fix grouping, round recording,
26
+ clarification matching, and the state-file round history.
27
+
28
+ Emits one JSON object:
29
+
30
+ gate.recomputed the gate value this round's votes actually support
31
+ gate.blockedBy the `gateBlockedBy` causes, recomputed
32
+ gate.blockingItems the plan items classified `majority-disagree`
33
+ gate.items[] per-item classification
34
+ failures[] blocking contract violations — exit code 2 when non-empty
35
+ advisories[] contract findings that are recorded, not round-blocking
36
+ warnings[] advisory signals (self-fix recurrence, uniform verifier)
37
+
38
+ Exit code 0 means no blocking failure, not "no finding". What blocks is the
39
+ allowlist in `scripts/okstra_ctl/blocking_checks.py` — the same set the finished
40
+ run's validation blocks on, so a finding the completed run would let pass cannot
41
+ hold a round open mid-loop. Everything else lands in `advisories[]` and on
42
+ stderr as `validate-run: advisory — ...`; record it, then continue the round.
43
+
44
+ Run this at every round boundary and record what it returns. The single-vote
45
+ blocking kinds, the advisory-only kinds and the P-Var / P-Rb exemptions live in
46
+ this one implementation — a lead that tallies the verdicts by hand instead is
47
+ re-deriving them per round, and a round scored wrong spends its self-fix budget
48
+ on the wrong items.
49
+ """
50
+
51
+
52
+ def main(argv: list[str] | None = None) -> int:
53
+ parser = argparse.ArgumentParser(
54
+ prog="okstra plan-verify",
55
+ description="Score the §5.5.9 plan-body gate for one self-fix round.",
56
+ epilog=_CLI_EPILOG,
57
+ formatter_class=argparse.RawDescriptionHelpFormatter,
58
+ )
59
+ parser.add_argument("--workspace-root", required=True,
60
+ help=argparse.SUPPRESS)
61
+ parser.add_argument("--narrative", default="",
62
+ help="the report-writer narrative this round scored")
63
+ parser.add_argument("--state", default="",
64
+ help="the convergence-owned plan-body-verification.json")
65
+ parser.add_argument("--report", default="",
66
+ help="a historical v2 final-report .data.json")
67
+ args = parser.parse_args(argv)
68
+
69
+ forwarded: list[str] = ["--section", "plan-body"]
70
+ for flag in ("narrative", "state", "report"):
71
+ value = getattr(args, flag)
72
+ if value:
73
+ forwarded += [f"--{flag}", value]
74
+
75
+ validator = resolve_workspace_validator(Path(args.workspace_root), "validate-run.py")
76
+ return subprocess.run([sys.executable, str(validator), *forwarded], check=False).returncode
77
+
78
+
79
+ if __name__ == "__main__":
80
+ raise SystemExit(main())
@@ -0,0 +1,20 @@
1
+ """task bundle prepare 실패 타입.
2
+
3
+ `stage_targets` 안에 있던 것을 leaf 로 뽑았다. 그 모듈의 도크스트링은 이 예외가
4
+ "저수준 모듈에 정의"돼 있다고 적었지만, 실제로는 944줄에 `stage_integrate` 를
5
+ 끌어오는 모듈이었다. 그래서 예외 타입 하나가 필요한 `plan_run_root` 가
6
+ `stage_targets` 전체를 끌어오게 되고, 반대로 `stage_targets` 는
7
+ `plan_run_root` 를 함수 본문에서 지연 import 해야 했다.
8
+
9
+ `stage_targets` 와 `run` 이 이 이름을 재노출하므로 기존 호출부는 바뀌지 않는다.
10
+ 이 모듈은 아무것도 import 하지 않는다.
11
+ """
12
+ from __future__ import annotations
13
+
14
+
15
+ class PrepareError(Exception):
16
+ """surface to caller — task bundle prepare failed.
17
+
18
+ run.py(final-verification) 와 container.py 가 whole-task 통합 헬퍼의 실패를
19
+ 같은 타입으로 받는다.
20
+ """
@@ -0,0 +1,157 @@
1
+ """직전 implementation-planning run 이 남긴 계획 맥락을 packet 용으로 좁힌다.
2
+
3
+ 계획 run 이 실패한 뒤 `--selected-direction` 으로 다시 도는 경우, 새 run 은 직전
4
+ run 의 산출물을 하나도 받지 못했다. analysis packet 에 그것을 실을 자리가 없었고
5
+ (`analysis_packet.build_analysis_packet` 에 직전 finding·판정·리포트를 받는 인자가
6
+ 없었다), 판정 이월은 답변된 clarification 이 있는 경로에만 있다
7
+ (`incremental_scope.decide_scope` 는 답변된 `C-NNN` 이 없으면 `full` 을 되돌린다).
8
+ 그래서 planner 들이 brief 에서 전부 다시 유도했고, 같은 이견이 새 `P-*` 번호로
9
+ 다시 열렸다.
10
+
11
+ 이 모듈은 그 재유도를 줄일 만큼만 모은다 — clarification 행, Stage Map 한 줄씩,
12
+ 직전 수렴이 못 가른 finding, 그리고 나머지를 위한 리포트 경로. 리포트 전문은
13
+ 싣지 않는다: 그 중복이 실측 283K 리포트로 892K 중복 읽기를 만들어 report-writer 를
14
+ timeout 시킨 적이 있다(`clarification_items.carry` 참고).
15
+
16
+ 판정은 소유하지 않는다. 이 블록은 사실 기록이고, 승인되지 않은 계획을 그대로
17
+ 복사하라는 지시가 아니다 — 그 문장은 packet 블록 머리글이 직접 말한다
18
+ (`analysis_packet._prior_planning_block`).
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import re
23
+ from pathlib import Path
24
+ from typing import Any, Mapping
25
+
26
+ from .clarification_items import carried_clarification_rows
27
+ from .final_report_paths import final_report_data_path
28
+ from .json_boundary import JsonBoundaryError, load_owned_object
29
+ from .paths import RunRef, infer_project_root, project_rel
30
+
31
+
32
+ PLANNING_TASK_TYPE = "implementation-planning"
33
+ # 직전 run 이 끝내지 못한 두 상태. `convergence_engine._CLASSIFICATION_COUNT_KEYS`
34
+ # 의 어휘를 그대로 쓴다 — 새 어휘를 만들면 같은 finding 이 run 마다 다르게 읽힌다.
35
+ UNSETTLED_CLASSIFICATIONS = ("contested", "worker-unique")
36
+ _SUMMARY_LIMIT = 200
37
+ _WHITESPACE_RE = re.compile(r"\s+")
38
+
39
+
40
+ def build_prior_planning_summary(task_root: Path) -> str:
41
+ """이 task 의 직전 계획 run 요약. 직전 run 이 없으면 빈 문자열.
42
+
43
+ 직전 리포트 해소는 wizard / interactive 진입점이 쓰는 것과 같다
44
+ (`RunRef.latest_under`: mtime 최신, 동률이면 basename, `.data.json` 우선).
45
+ 같은 task-type 으로 좁히므로 다른 phase 의 리포트를 계획 맥락으로 집지 않는다.
46
+ """
47
+ task_root = Path(task_root)
48
+ ref = RunRef.latest_under(task_root, (PLANNING_TASK_TYPE,))
49
+ if ref is None:
50
+ return ""
51
+ report = ref.report
52
+ project_root = infer_project_root(task_root)
53
+ sections = [f"- Prior planning report: `{project_rel(report, project_root)}`"]
54
+ rows = carried_clarification_rows(report)
55
+ if rows:
56
+ sections.append(f"### Prior Clarification Items\n\n{rows}")
57
+ stage_map = _stage_map_lines(report)
58
+ if stage_map:
59
+ sections.append(
60
+ "### Prior Stage Map\n\n"
61
+ "`stage | title | depends-on`, as the prior plan wrote it.\n\n"
62
+ + "\n".join(stage_map)
63
+ )
64
+ unsettled = _unsettled_finding_lines(ref)
65
+ if unsettled:
66
+ sections.append(
67
+ "### Findings the prior run left unsettled\n\n"
68
+ + "\n".join(unsettled)
69
+ )
70
+ return "\n\n".join(sections)
71
+
72
+
73
+ def _stage_map_lines(report: Path) -> list[str]:
74
+ """직전 계획의 Stage Map 을 스테이지당 한 줄로.
75
+
76
+ `stageMap` 이 정본이고 `stages` 는 그것이 없는 리포트를 위한 폴백이다 —
77
+ `plan_items._planning_stage_rows` 가 쓰는 것과 같은 우선순위다.
78
+ """
79
+ planning = _planning(report)
80
+ rows = planning.get("stageMap")
81
+ if not isinstance(rows, list) or not rows:
82
+ rows = planning.get("stages")
83
+ if not isinstance(rows, list):
84
+ return []
85
+ lines = []
86
+ for row in rows:
87
+ if not isinstance(row, Mapping):
88
+ continue
89
+ stage = row.get("stage")
90
+ if not isinstance(stage, int) or isinstance(stage, bool):
91
+ continue
92
+ title = _one_line(row.get("title")) or "(untitled)"
93
+ depends = _one_line(row.get("dependsOn")) or "(unrecorded)"
94
+ lines.append(f"- {stage} | {title} | depends-on: {depends}")
95
+ return lines
96
+
97
+
98
+ def _unsettled_finding_lines(ref: RunRef) -> list[str]:
99
+ """직전 수렴이 `contested` / `worker-unique` 로 닫은 finding 한 줄씩.
100
+
101
+ 상태 파일이 없거나 못 읽으면 빈 목록이다. 없는 것이 정상 분기다 — 수렴을
102
+ 돌기 전에 죽은 run, 그리고 디렉터리별 seq 가 갈린 run 이 둘 다 여기로 온다
103
+ (`RunRef.convergence_state` 참고).
104
+
105
+ seq 를 못 읽은 ref 도 같다. 옛 타임스탬프 이름의 리포트가 그 경우이고, 그
106
+ 이름에서는 상태 파일 이름을 재구성할 수 없다.
107
+ """
108
+ if ref.seq is None:
109
+ return []
110
+ state = _load_object(ref.convergence_state)
111
+ findings = state.get("findings")
112
+ if not isinstance(findings, list):
113
+ return []
114
+ lines = []
115
+ for finding in findings:
116
+ if not isinstance(finding, Mapping):
117
+ continue
118
+ classification = finding.get("classification")
119
+ if classification not in UNSETTLED_CLASSIFICATIONS:
120
+ continue
121
+ finding_id = _one_line(finding.get("findingId"))
122
+ if not finding_id:
123
+ continue
124
+ summary = _one_line(finding.get("summary")) or "(no summary recorded)"
125
+ lines.append(f"- `{finding_id}` ({classification}) — {summary}")
126
+ return lines
127
+
128
+
129
+ def _planning(report: Path) -> Mapping[str, Any]:
130
+ data = _load_object(final_report_data_path(report))
131
+ planning = data.get("implementationPlanning")
132
+ return planning if isinstance(planning, Mapping) else {}
133
+
134
+
135
+ def _load_object(path: Path) -> Mapping[str, Any]:
136
+ """소유한 JSON 객체, 못 읽으면 빈 매핑.
137
+
138
+ 이 블록은 전부 최선 노력이다. 직전 run 의 산출물이 깨졌다고 새 run 의 준비를
139
+ 막으면, 다시 도는 이유였던 그 실패가 재실행 자체를 못 하게 만든다.
140
+ """
141
+ try:
142
+ data = load_owned_object(path, artifact="prior planning artifact")
143
+ except (JsonBoundaryError, OSError):
144
+ # expected-miss: 직전 run 이 남기지 못했거나 깨뜨린 파일. 이 블록은
145
+ # 맥락일 뿐이라 없는 상태가 정상 분기고, 소비처는 해당 하위 블록을
146
+ # 생략한다.
147
+ return {}
148
+ return data if isinstance(data, Mapping) else {}
149
+
150
+
151
+ def _one_line(value: object) -> str:
152
+ if not isinstance(value, str):
153
+ return ""
154
+ collapsed = _WHITESPACE_RE.sub(" ", value).strip()
155
+ if len(collapsed) <= _SUMMARY_LIMIT:
156
+ return collapsed
157
+ return collapsed[: _SUMMARY_LIMIT - 1].rstrip() + "…"
@@ -107,9 +107,26 @@ def _write_stdout(text: str) -> None:
107
107
  os.close(devnull)
108
108
 
109
109
 
110
+ _CLI_EPILOG = r"""Usage:
111
+ okstra profile show <task-type> [--resolved]
112
+
113
+ --resolved expand {{INCLUDE:}} targets and append the lazy-read sidecars
114
+
115
+ A profile is assembled from the top-level body, its {{INCLUDE:}} targets, and
116
+ the sidecars its own table names — so grepping the top-level file and finding
117
+ nothing does not mean the rule is absent. This prints all of it, so one grep
118
+ answers "does this task-type cover X".
119
+
120
+ Read-only: writes no manifest and registers no run. That is what separates it
121
+ from 'okstra render-bundle', which answers the same question with side effects.
122
+ """
123
+
124
+
110
125
  def main(argv: list[str] | None = None) -> int:
111
126
  parser = argparse.ArgumentParser(
112
- prog="okstra profile show",
127
+ epilog=_CLI_EPILOG,
128
+ formatter_class=argparse.RawDescriptionHelpFormatter,
129
+ prog="okstra profile",
113
130
  description="Print a phase profile, optionally fully resolved (read-only).",
114
131
  )
115
132
  parser.add_argument("command", choices=("show",))
@@ -0,0 +1,140 @@
1
+ """프로젝트 루트 해석·project.json upsert·PR 템플릿 해석의 CLI 경계.
2
+
3
+ 이 네 갈래는 원래 `config.mts` · `setup.mts` · `check-project.mts` 안에
4
+ 파이썬 소스 배열로 각각 복사돼 있었다(docs/coding-rules.md R1 위반, 그리고
5
+ 같은 해석을 네 벌 유지하는 중복). 로직은 그대로 옮겼고 출력 형식도 그대로다 —
6
+ 호출부의 파서를 건드리지 않기 위해서다.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from okstra_project import (
16
+ ResolverError,
17
+ project_json_path,
18
+ resolve_project_root,
19
+ upsert_project_json,
20
+ )
21
+
22
+ from .json_boundary import JsonBoundaryError, load_owned_object
23
+
24
+
25
+ def _resolve(args: argparse.Namespace) -> int:
26
+ """`PROJECT_ROOT` / `PROJECT_JSON` 또는 `RESOLVER_ERROR` 태그 줄."""
27
+ try:
28
+ pr = resolve_project_root(explicit_root=args.explicit_root, cwd=args.cwd)
29
+ except ResolverError as exc:
30
+ print("RESOLVER_ERROR", exc)
31
+ return 0
32
+ print("PROJECT_ROOT", pr)
33
+ print("PROJECT_JSON", project_json_path(pr))
34
+ return 0
35
+
36
+
37
+ def _resolve_root(args: argparse.Namespace) -> int:
38
+ """루트 경로만 stdout 으로.
39
+
40
+ `--on-error` 로 실패 취급이 갈린다. `fail` 은 stderr + 비0(엄격한 호출부),
41
+ `empty` 는 아무것도 내지 않고 0(autofill 처럼 실패해도 진행하는 호출부).
42
+ 두 갈래를 한 구현 안에 두는 이유는, 예전에 셸과 TS 가 같은 해석을 각자
43
+ 베껴 두어 갈라질 수 있었기 때문이다.
44
+ """
45
+ try:
46
+ root = resolve_project_root(explicit_root=args.explicit_root, cwd=args.cwd)
47
+ except ResolverError as exc:
48
+ if args.on_error == "fail":
49
+ sys.stderr.write(f"{args.error_prefix}{exc}\n")
50
+ return args.error_code
51
+ return 0
52
+ print(root)
53
+ return 0
54
+
55
+
56
+ def _upsert(args: argparse.Namespace) -> int:
57
+ """`--format tagged` 는 `OK <json>` / `ERROR <msg>`, `strict` 는 stderr + 비0."""
58
+ try:
59
+ result = upsert_project_json(Path(args.project_root), args.project_id)
60
+ except ResolverError as exc:
61
+ if args.format == "strict":
62
+ sys.stderr.write(f"project.json upsert failed: {exc}\n")
63
+ return 1
64
+ print("ERROR", exc)
65
+ return 0
66
+ if args.format == "tagged":
67
+ print("OK", json.dumps(result))
68
+ return 0
69
+
70
+
71
+ def _project_json(args: argparse.Namespace) -> int:
72
+ """`<projectId>\t<projectRoot>` 한 줄. 해석·파일 어느 쪽이든 없으면 아무것도 내지 않는다."""
73
+ try:
74
+ root = resolve_project_root(explicit_root=args.explicit_root or "")
75
+ except ResolverError:
76
+ return 0
77
+ path = project_json_path(Path(root))
78
+ if not path.is_file():
79
+ return 0
80
+ try:
81
+ data = load_owned_object(path, artifact="project.json")
82
+ except JsonBoundaryError:
83
+ # 깨진 project.json 은 autofill 을 끄는 신호다 — 이 경로의 호출부는
84
+ # 값을 못 얻으면 사용자에게 직접 묻는다. 여기서 죽이면 물어볼 기회도 없다.
85
+ return 0
86
+ print(f"{data.get('projectId', '')}\t{root}")
87
+ return 0
88
+
89
+
90
+ def _pr_template(args: argparse.Namespace) -> int:
91
+ from .pr_template import PrTemplateError, resolve_pr_template_path
92
+
93
+ try:
94
+ resolved = resolve_pr_template_path(Path(args.project_root))
95
+ except PrTemplateError as exc:
96
+ print(json.dumps({"ok": False, "reason": str(exc)}))
97
+ return 0
98
+ print(json.dumps({
99
+ "ok": True, "path": str(resolved.path), "source": resolved.source,
100
+ }))
101
+ return 0
102
+
103
+
104
+ def main(argv: list[str] | None = None) -> int:
105
+ parser = argparse.ArgumentParser(prog="okstra project-setup")
106
+ sub = parser.add_subparsers(dest="op", required=True)
107
+
108
+ resolve = sub.add_parser("resolve")
109
+ resolve.add_argument("--explicit-root", default="")
110
+ resolve.add_argument("--cwd", required=True)
111
+ resolve.set_defaults(handler=_resolve)
112
+
113
+ resolve_root = sub.add_parser("resolve-root")
114
+ resolve_root.add_argument("--explicit-root", default="")
115
+ resolve_root.add_argument("--cwd", default="")
116
+ resolve_root.add_argument("--on-error", choices=("fail", "empty"), default="fail")
117
+ resolve_root.add_argument("--error-prefix", default="")
118
+ resolve_root.add_argument("--error-code", type=int, default=2)
119
+ resolve_root.set_defaults(handler=_resolve_root)
120
+
121
+ upsert = sub.add_parser("upsert")
122
+ upsert.add_argument("--project-root", required=True)
123
+ upsert.add_argument("--project-id", required=True)
124
+ upsert.add_argument("--format", choices=("tagged", "strict"), default="tagged")
125
+ upsert.set_defaults(handler=_upsert)
126
+
127
+ project_json = sub.add_parser("project-json")
128
+ project_json.add_argument("--explicit-root", default="")
129
+ project_json.set_defaults(handler=_project_json)
130
+
131
+ pr_template = sub.add_parser("pr-template")
132
+ pr_template.add_argument("--project-root", required=True)
133
+ pr_template.set_defaults(handler=_pr_template)
134
+
135
+ args = parser.parse_args(argv)
136
+ return args.handler(args)
137
+
138
+
139
+ if __name__ == "__main__":
140
+ raise SystemExit(main())
@@ -213,8 +213,29 @@ def _add_common(sp) -> None:
213
213
  sp.add_argument("--cwd", default=".")
214
214
 
215
215
 
216
+ _CLI_EPILOG = r"""Usage:
217
+ okstra recap assemble <task-root|task-key> [--project-root <dir>] [--cwd <dir>]
218
+ okstra recap record <task-root|task-key> --kind <summary|qa> --mode <artifact|code> \
219
+ [--question <text>] --answer <text> [--citation <path:line> ...]
220
+ okstra recap note <task-root|task-key> \
221
+ --kind <verification-evidence|decision-draft|analysis-note> --slug <topic> \
222
+ --purpose <text> --scope-note <text> (--body <markdown> | --body-file <path>)
223
+
224
+ `assemble` is read-only and prints a JSON summary of phase transitions across runs.
225
+ `record` appends one line to <task-root>/recap/recap-log.jsonl; it never mutates
226
+ other task artifacts.
227
+ `note` writes an agent-authored markdown note to <task-root>/notes/ and prints its
228
+ path plus the `--clarification-response` argument for feeding it into a later run.
229
+ """
230
+ _CLI_DESCRIPTION = "Assemble a task's run-to-run phase recap, append a recap log entry, or write an agent note."
231
+
232
+
216
233
  def main(argv: list[str] | None = None) -> int:
217
- parser = argparse.ArgumentParser(prog="okstra recap")
234
+ parser = argparse.ArgumentParser(
235
+ description=_CLI_DESCRIPTION,
236
+ epilog=_CLI_EPILOG,
237
+ formatter_class=argparse.RawDescriptionHelpFormatter,
238
+ prog="okstra recap")
218
239
  sub = parser.add_subparsers(dest="command", required=True)
219
240
 
220
241
  p_asm = sub.add_parser("assemble", help="assemble run-to-run phase transitions")
@@ -227,3 +227,87 @@ def _reconcile_recent_locked(home: Path, *, project: Optional[str] = None) -> di
227
227
  for pid, urows in updated_by_project.items():
228
228
  _sync_project_index(home, pid, urows)
229
229
  return summary
230
+
231
+
232
+ def reconcile_home(home: Path, *, project: Optional[str] = None) -> dict:
233
+ """active 승격과 recent in-place 갱신을 한 번에 수행하는 단일 호출점.
234
+
235
+ 두 함수는 서로 다른 파일을 보지만 같은 사실(디스크 run-manifest 의
236
+ validation)을 근거로 삼는 한 쌍이다. active 만 돌리면 `--render-only` 로
237
+ recent 에 prepared 로 박힌 행이 영영 갱신되지 않고, recent 만 돌리면 끝난
238
+ run 이 active 에 running 으로 남는다. 호출자가 한쪽만 부를 수 있는 표면을
239
+ 남기지 않기 위해 여기서 묶는다.
240
+
241
+ project 를 주면 해당 projectId 행만 대상으로 삼는다. 두 함수 각각이 중앙
242
+ 락을 잡았다 놓으므로, 이 함수를 central_lock 안에서 부르면 self-deadlock
243
+ 이다(flock 은 재진입 불가).
244
+ """
245
+ return {
246
+ "active": reconcile_active(home, project=project),
247
+ "recent": reconcile_recent(home, project=project),
248
+ }
249
+
250
+
251
+ def settle_run_row(home: Path, *, project_id: str, execution_manifest_rel: str,
252
+ validation_status: str) -> bool:
253
+ """검증기가 방금 확정한 판정을 그 run 의 인덱스 행에 그대로 기록한다.
254
+
255
+ `reconcile_*` 는 디스크에서 상태를 **추론**한다 — 어느 run 이 끝났는지 모르는
256
+ 호출자를 위한 경로다. 검증기는 자기가 방금 무엇을 썼는지 알고 있으므로 추론할
257
+ 것이 없고, 추론에 맡기면 실제로 틀린다: `reconcile_recent` 는 이미 종결 상태인
258
+ 행을 갱신 대상에서 빼므로, 한 번 `failed` 로 굳은 뒤 고쳐서 다시 통과시킨 run
259
+ 이 인덱스에는 영영 `failed` 로 남는다(검증 재실행은 흔한 흐름이다).
260
+
261
+ 행은 `executionManifestPath` 로 찾는다 — `runDirRel` 은 같은 task-type 의 여러
262
+ run-seq 가 공유해 유일하지 않다. 그 필드가 없는 옛 행은 못 찾고 False 를
263
+ 돌려주며, 그런 행은 `reconcile_home` 의 일반 스윕이 맡는다.
264
+
265
+ 반환값: 행을 찾아 기록했으면 True.
266
+ """
267
+ with central_lock(home):
268
+ return _settle_run_row_locked(
269
+ home, project_id=project_id,
270
+ execution_manifest_rel=execution_manifest_rel,
271
+ validation_status=validation_status)
272
+
273
+
274
+ def _matches_run(row: dict, project_id: str, execution_manifest_rel: str) -> bool:
275
+ return (row.get("projectId") == project_id
276
+ and str(row.get("executionManifestPath") or "") == execution_manifest_rel)
277
+
278
+
279
+ def _settle_run_row_locked(home: Path, *, project_id: str,
280
+ execution_manifest_rel: str,
281
+ validation_status: str) -> bool:
282
+ # 빈 키는 따로 막지 않는다 — `_matches_run` 의 문자열 동일성 비교가 어떤 행도
283
+ # 고르지 않으므로 결과가 같다(`test_settle_run_row_needs_both_...` 이 고정).
284
+ status = normalize_reconciled_report_status(validation_status)
285
+ finished_at = _now_iso()
286
+
287
+ active = home / "active.jsonl"
288
+ rows = read_run_index(active)
289
+ survivors = [r for r in rows
290
+ if not _matches_run(r, project_id, execution_manifest_rel)]
291
+ promoted = [r for r in rows
292
+ if _matches_run(r, project_id, execution_manifest_rel)]
293
+ if promoted:
294
+ for row in promoted:
295
+ row["status"] = status
296
+ row["finishedAt"] = finished_at
297
+ row["validation"] = validation_status
298
+ _publish_promoted(home, active, survivors, promoted)
299
+ return True
300
+
301
+ recent = home / "recent.jsonl"
302
+ rows = read_run_index(recent)
303
+ updated = [r for r in rows
304
+ if _matches_run(r, project_id, execution_manifest_rel)]
305
+ if not updated:
306
+ return False
307
+ for row in updated:
308
+ row["status"] = status
309
+ row["finishedAt"] = finished_at
310
+ row["validation"] = validation_status
311
+ rewrite_jsonl(recent, [slim_run_row(row) for row in rows])
312
+ _sync_project_index(home, project_id, updated)
313
+ return True
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  import inspect
5
5
  from collections.abc import Iterable, Mapping
6
+ from functools import lru_cache
6
7
  from pathlib import Path
7
8
 
8
9
  from okstra_project.dirs import okstra_home
@@ -158,7 +159,23 @@ def default_host_registry(
158
159
  bundled_root = Path(__file__).resolve().parents[1] / "adapters" / "hosts"
159
160
  user_root = okstra_home() / "adapters" / "hosts"
160
161
  providers = provider_registry or default_provider_registry()
161
- return HostAdapterRegistry.from_roots((bundled_root, user_root), providers)
162
+ return _host_registry_for_roots(bundled_root, user_root, providers)
163
+
164
+
165
+ @lru_cache(maxsize=None)
166
+ def _host_registry_for_roots(
167
+ bundled_root: Path,
168
+ user_root: Path,
169
+ provider_registry: ProviderRegistry,
170
+ ) -> HostAdapterRegistry:
171
+ """discovery root 조합마다 레지스트리를 한 번만 조립한다.
172
+
173
+ `from_roots` 는 매니페스트를 디스크에서 훑고 어댑터 팩토리 모듈을 importlib
174
+ 로 적재하므로 호출당 비용이 크다. 호출부(위저드의 역할/모델 프롬프트 등)는
175
+ 같은 root 로 이걸 루프 안에서 반복 호출한다. `default_provider_registry` 의
176
+ `_registry_for_roots` 와 같은 캐시 형태를 쓴다.
177
+ """
178
+ return HostAdapterRegistry.from_roots((bundled_root, user_root), provider_registry)
162
179
 
163
180
 
164
181
  def _load_entries(