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
@@ -456,8 +456,21 @@ def audit_runs(home: Path, *, now: dt.datetime) -> list[dict]:
456
456
  return violations
457
457
 
458
458
 
459
+ _CLI_EPILOG = r"""Usage:
460
+ okstra run-audit
461
+
462
+ Walks the global run-index and checks each run's artifacts (run-manifest,
463
+ final-report data, worker results) against invariants that no error log
464
+ would catch: verification rounds that never ran, reports that never
465
+ appeared, rosters that came up short, approvals waiting past the window.
466
+ Read-only. Prints a JSON summary to stdout.
467
+ """
468
+
469
+
459
470
  def main(argv: list[str] | None = None) -> int:
460
471
  parser = argparse.ArgumentParser(
472
+ epilog=_CLI_EPILOG,
473
+ formatter_class=argparse.RawDescriptionHelpFormatter,
461
474
  prog="okstra run-audit",
462
475
  description="런 아티팩트에서 불변식 위반을 찾는다 (읽기 전용).",
463
476
  )
@@ -104,15 +104,3 @@ def _rewrite_slim(path: Path) -> int:
104
104
  rewrite_jsonl(path, [slim_run_row(row) for row in rows])
105
105
  return len(rows)
106
106
 
107
-
108
- def reindex_slim(home: Path) -> int:
109
- """기존 run-index 파일들을 slim 으로 일괄 재작성. 멱등. 변환 row 총수 반환."""
110
- total = 0
111
- targets = [home / "recent.jsonl", home / "active.jsonl"]
112
- projects = home / "projects"
113
- if projects.is_dir():
114
- targets += sorted(projects.glob("*/index.jsonl"))
115
- for path in targets:
116
- if path.is_file():
117
- total += _rewrite_slim(path)
118
- return total
@@ -10,6 +10,7 @@ okstra worker wrapper 호출이 자동 허용되도록 한다.
10
10
  from __future__ import annotations
11
11
 
12
12
  import os
13
+ import sys
13
14
  import time
14
15
  from pathlib import Path
15
16
  from typing import Optional
@@ -69,9 +70,8 @@ def verify_installation(workspace_root: Path) -> None:
69
70
  이 검사는 ~/.okstra 자산만을 본다; workspace_root 의 존재는 별도 검증.
70
71
 
71
72
  `OKSTRA_SKIP_INSTALL_CHECK=1` 이면 설치 자산 검사를 건너뛴다 — 격리
72
- OKSTRA_HOME 으로 subprocess 를 띄우는 테스트용 게이트(conftest 가 설정,
73
- OKSTRA_CTL_SKIP_RECONCILE/BACKFILL 과 같은 패턴). workspace_root 검증은
74
- 게이트와 무관하게 항상 수행한다.
73
+ OKSTRA_HOME 으로 subprocess 를 띄우는 테스트용 게이트(conftest 가 설정).
74
+ workspace_root 검증은 게이트와 무관하게 항상 수행한다.
75
75
  """
76
76
  if os.environ.get("OKSTRA_SKIP_INSTALL_CHECK", "").strip():
77
77
  missing = []
@@ -105,24 +105,34 @@ def cleanup_obsolete_generated_docs(
105
105
  ):
106
106
  target = legacy_root / rel
107
107
  if target.exists():
108
- try:
109
- target.unlink()
110
- except OSError:
111
- pass
108
+ _unlink_deprecated(target)
112
109
  obsolete = Path(instruction_set_dir) / "okstra-skill.md"
113
110
  if obsolete.exists():
114
- try:
115
- obsolete.unlink()
116
- except OSError:
117
- pass
111
+ _unlink_deprecated(obsolete)
118
112
  okstra_dir = legacy_root / "okstra"
119
113
  if okstra_dir.is_dir():
120
114
  try:
121
115
  okstra_dir.rmdir()
122
116
  except OSError:
117
+ # expected-miss: 사용자가 이 디렉터리에 자기 파일을 두었으면 비어 있지
118
+ # 않아 rmdir 이 실패한다. 남의 파일을 지우지 않는 것이 맞는 동작이다.
123
119
  pass
124
120
 
125
121
 
122
+ def _unlink_deprecated(target: Path) -> None:
123
+ """이미 존재를 확인한 deprecated 파일을 지운다. 실패는 stderr 로 보고한다.
124
+
125
+ 존재를 확인하고 부르므로 여기서 나는 OSError 는 권한·I/O 오류다. 조용히 넘기면
126
+ 다음 run 이 같은 파일을 또 만나 같은 자리에서 또 실패하는데, 왜 안 지워지는지는
127
+ 아무 데도 남지 않는다.
128
+ """
129
+ try:
130
+ target.unlink()
131
+ except OSError as exc:
132
+ print(f"seeding: cannot remove the deprecated file {target} ({exc})",
133
+ file=sys.stderr)
134
+
135
+
126
136
  def installed_settings_template_path() -> Path:
127
137
  """okstra install 이 만들어 둔 settings.local.json template 의 절대경로."""
128
138
  return okstra_home() / "templates" / "settings.local.json"
@@ -20,7 +20,7 @@ def predict_next_run_seq(project_root: Path, task_group: str, task_id: str,
20
20
  1. reports/ 의 final-report-<task_type>-<seq:03d>.md (완료된 run)
21
21
  2. manifests/ 의 run-manifest-<task_type>-<seq:03d>.json (시작했지만 종료 전)
22
22
  3. home/active.jsonl 의 같은 (project_id, group, task_id, task_type) row
23
- (다른 okstra-ctl 프로세스가 이미 예약한 in-flight run) — home/project_id 가 주어진 경우만
23
+ (다른 prepare 프로세스가 이미 예약한 in-flight run) — home/project_id 가 주어진 경우만
24
24
 
25
25
  이 셋의 max + 1 이 안전한 다음 seq.
26
26
  """
@@ -27,20 +27,26 @@ def _claude_projects_dir_for(cwd: Path) -> Path:
27
27
  return Path.home() / ".claude" / "projects" / encoded
28
28
 
29
29
 
30
- def resolve_inproc_lead_session_id(project_root: Path) -> str:
30
+ def resolve_inproc_lead_session_id(session_cwd: Path) -> str:
31
31
  """Best-effort detection of the running Claude session's id when okstra is
32
32
  invoked render-only from inside a live session (the `okstra-run` skill
33
33
  path). The current session's jsonl is being actively written to under
34
34
  `~/.claude/projects/<encoded-cwd>/`, so the most recently modified file
35
35
  in that directory is, with very high probability, the calling session.
36
36
 
37
+ 인자는 **부르는 세션의 cwd** 다. 대상 프로젝트 루트를 넘기면 안 된다 —
38
+ `okstra preflight --cwd <다른 프로젝트>` 로 프로젝트 B 를 A 의 세션에서 몰 때
39
+ B 의 디렉터리에서 가장 최근 파일을 집으면 남의 세션 id 가 run manifest 에
40
+ 박힌다(실측 2026-08-18). 그 id 는 실재하는 파일을 가리키므로 조용히 통과하고,
41
+ Phase 7 에서 리드 토큰과 PROGRESS 증거를 통째로 잃는다.
42
+
37
43
  Returns the UUID stem on success, empty string on failure (directory
38
44
  missing, no jsonl files, permission error). Callers must treat this as
39
45
  best-effort — a failure does not invalidate the session, it just means
40
46
  auto-detection could not confirm one.
41
47
  """
42
48
  try:
43
- d = _claude_projects_dir_for(project_root)
49
+ d = _claude_projects_dir_for(session_cwd)
44
50
  if not d.exists():
45
51
  return ""
46
52
  candidates = [p for p in d.iterdir() if p.suffix == ".jsonl"]
@@ -95,24 +101,68 @@ def resolve_lead_session_id_for_run(
95
101
  경로를 언급하므로 needle 에도 걸린다. 워커가 리드보다 늦게 끝나면 mtime
96
102
  정렬에서 먼저 잡혀 워커 세션이 리드로 기록된다 — dispatch 가 발급한 id 를
97
103
  호출자가 넘겨 그 세션들을 후보에서 뺀다.
104
+
105
+ 프로젝트 디렉터리에서 아무도 안 잡히면 나머지 인코딩 디렉터리까지 넓힌다.
106
+ 리드 세션의 cwd 가 대상 프로젝트 루트와 다를 수 있기 때문이다 —
107
+ `okstra preflight --cwd <다른 프로젝트>` 로 프로젝트 B 의 태스크를 A 의
108
+ 세션에서 몰 때, transcript 는 A 로 인코딩된 디렉터리에 놓인다. 넓힌 패스는
109
+ run 디렉터리가 생기기 전에 마지막으로 쓰인 파일을 건너뛴다 — 그런 세션은
110
+ 아직 존재하지 않던 경로를 언급할 수 없다.
98
111
  """
99
- proj_dir = _claude_projects_dir_for(project_root)
100
- try:
101
- candidates = [p for p in proj_dir.iterdir() if p.suffix == ".jsonl"]
102
- except OSError:
103
- return ""
104
112
  needle = str(run_dir)
105
113
  excluded = set(exclude_session_ids)
106
- for path in sorted(candidates, key=lambda p: p.stat().st_mtime, reverse=True):
107
- if path.stem in excluded:
108
- continue
109
- if _session_has_agent_name(path):
110
- continue
111
- if _session_mentions(path, needle):
112
- return path.stem
114
+ for candidates in _lead_session_candidate_passes(project_root, run_dir):
115
+ for path in sorted(candidates, key=lambda p: p.stat().st_mtime, reverse=True):
116
+ if path.stem in excluded:
117
+ continue
118
+ if _session_has_agent_name(path):
119
+ continue
120
+ if _session_mentions(path, needle):
121
+ return path.stem
113
122
  return ""
114
123
 
115
124
 
125
+ def _run_dir_created_at(run_dir: Path) -> float:
126
+ """run 디렉터리가 생긴 시각. 넓힌 스캔의 하한이다."""
127
+ try:
128
+ st = run_dir.stat()
129
+ except OSError:
130
+ return 0.0
131
+ return float(getattr(st, "st_birthtime", st.st_ctime))
132
+
133
+
134
+ def _lead_session_candidate_passes(project_root: Path, run_dir: Path):
135
+ """후보 파일 묶음을 순서대로 내준다 — 프로젝트 디렉터리, 그다음 나머지.
136
+
137
+ 첫 묶음에서 리드가 잡히면 두 번째는 열리지 않는다. 같은 cwd 로 도는 보통의
138
+ 런은 예전과 같은 비용으로 끝나고, 넓은 스캔은 지금 통째로 실패하는
139
+ cross-project 런에서만 값을 치른다.
140
+ """
141
+ proj_dir = _claude_projects_dir_for(project_root)
142
+ try:
143
+ yield [p for p in proj_dir.iterdir() if p.suffix == ".jsonl"]
144
+ except OSError:
145
+ yield []
146
+ floor = _run_dir_created_at(run_dir)
147
+ others: list[Path] = []
148
+ try:
149
+ for entry in proj_dir.parent.iterdir():
150
+ if not entry.is_dir() or entry == proj_dir:
151
+ continue
152
+ for path in entry.iterdir():
153
+ if path.suffix != ".jsonl":
154
+ continue
155
+ try:
156
+ if path.stat().st_mtime < floor:
157
+ continue
158
+ except OSError:
159
+ continue
160
+ others.append(path)
161
+ except OSError:
162
+ return
163
+ yield others
164
+
165
+
116
166
  def record_observed_lead_session(project_root: Path, team_state_path: Path) -> str:
117
167
  """이 run 의 live lead 세션을 관측해 team-state 에 append(멱등). 이 run 을
118
168
  다룬 lead 세션을 못 찾거나 중복이면 '' 반환. 재발급으로 갈린 세대를 축1 이
@@ -61,8 +61,26 @@ def _manifest_path(project_root: Path, entry: dict) -> Path:
61
61
  return task_manifest_file(task_dir(project_root, group_seg, id_seg))
62
62
 
63
63
 
64
+ _CLI_EPILOG = r"""Usage:
65
+ okstra set-work-status <token> <status> [--note <text>] [--task-group <g>]
66
+ [--project-root <dir>] [--cwd <dir>] [--json]
67
+
68
+ <token> is a full task-key (<project-id>:<task-group>:<task-id>) or a bare
69
+ task-id. <status> is one of: todo | in-progress | blocked | done.
70
+
71
+ Updates workStatus + workStatusUpdatedAt in task-manifest.json (workStatusNote
72
+ only when --note is passed) using the same JSON serialization as the manifest
73
+ renderer. Output: JSON { ok, taskKey, previousWorkStatus, workStatus, ... };
74
+ ok:false stages: resolve | catalog | not-found | ambiguous (pick from
75
+ matches[]) | manifest-missing | manifest-invalid.
76
+ """
77
+
78
+
64
79
  def _parse_args(argv: list[str] | None):
65
80
  parser = argparse.ArgumentParser(
81
+ prog="okstra set-work-status",
82
+ epilog=_CLI_EPILOG,
83
+ formatter_class=argparse.RawDescriptionHelpFormatter,
66
84
  description="Set a task's user-managed workStatus in task-manifest.json."
67
85
  )
68
86
  parser.add_argument(
@@ -187,6 +187,16 @@ def _teardown_stage(project_id, task_group, task_id, task_wt, stage_n, row):
187
187
  return ("torn_down", "")
188
188
 
189
189
 
190
+ _CLI_EPILOG = r"""Usage:
191
+ okstra integrate-stages --project-id <id> \
192
+ --task-group <g> --task-id <t> --plan-run-root <dir> \
193
+ --task-worktree-path <dir> [--stage-map-json '<json>']
194
+
195
+ Exit codes: 0 ok / 2 stage 간 merge 충돌 / 1 python 프로세스 비정상 종료(폴백)
196
+ """
197
+ _CLI_DESCRIPTION = "Stage \ub4e4\uc744 task \ube0c\ub79c\uce58\uc5d0 \ud1b5\ud569 \uba38\uc9c0 + worktree \uc815\ub9ac."
198
+
199
+
190
200
  def main(argv: list[str] | None = None) -> int:
191
201
  import argparse
192
202
  import json
@@ -194,7 +204,11 @@ def main(argv: list[str] | None = None) -> int:
194
204
 
195
205
  from .consumers import read_consumers
196
206
 
197
- p = argparse.ArgumentParser(prog="okstra integrate-stages")
207
+ p = argparse.ArgumentParser(
208
+ description=_CLI_DESCRIPTION,
209
+ epilog=_CLI_EPILOG,
210
+ formatter_class=argparse.RawDescriptionHelpFormatter,
211
+ prog="okstra integrate-stages")
198
212
  p.add_argument("--project-id", required=True)
199
213
  p.add_argument("--task-group", required=True)
200
214
  p.add_argument("--task-id", required=True)
@@ -30,7 +30,7 @@ from .stage_map import (
30
30
  load_task_stage_map,
31
31
  )
32
32
  from .stage_targets import stage_lifecycle_snapshot_from_state
33
- from .task_target import infer_project_root
33
+ from .paths import infer_project_root
34
34
 
35
35
 
36
36
  def build_stage_ledger(task_root: Path) -> dict[str, Any] | None:
@@ -13,7 +13,7 @@ from .fixed_text import line, value_lines
13
13
  from .md_table import is_separator_row, split_pipe_row
14
14
  from .paths import RunRef
15
15
  from .plan_run_root import list_implementation_planning_reports
16
- from .task_target import infer_project_root
16
+ from .paths import infer_project_root
17
17
 
18
18
 
19
19
  STAGE_MAP_HEADING = re.compile(r"^##\s+5\.5\s+Stage\s+Map\b", re.MULTILINE)
@@ -506,22 +506,63 @@ def load_task_stage_map(
506
506
  ) -> StageMapSnapshot:
507
507
  """완료된 stage 를 무엇으로 지었는지 답하는 Stage Map.
508
508
 
509
- carry 사이드카가 가리키는 계획을 우선한다 — 그게 실행이 실제로 따른
510
- 문서이기 때문이다. "어떤 stage 번호가 이미 쓰였나" 는 다른 질문이며
511
- `load_latest_plan_stage_map` 이 답한다.
509
+ carry 사이드카가 가리키는 계획을 읽는다 — 그게 실행이 실제로 따른
510
+ 문서이기 때문이다. 오직 그 질문에만 답한다. "어떤 stage 가 존재하나" 와
511
+ "어떤 번호가 이미 쓰였나" 는 최신 계획이 답하며, 그쪽을 물어야 하는
512
+ 소비자는 `load_latest_plan_stage_map` 을 쓴다 — 계획을 고친 task 에서
513
+ carry 소스는 새로 붙은 stage 를 아직 모른다.
512
514
  """
513
515
  carried = _unique_carry_source_paths(task_root)
514
516
  if len(carried) > 1:
515
- paths = tuple(str(path) for path in carried)
516
- raise StageMapError(
517
- "plan-source-conflict",
518
- "implementation carries reference different source plans",
519
- conflicting_paths=paths,
520
- )
521
- source = carried[0] if carried else _latest_plan_report(task_root)
517
+ source: Path | None = _reconcile_carried_sources(carried)
518
+ else:
519
+ source = carried[0] if carried else _latest_plan_report(task_root)
522
520
  return _snapshot_for(source)
523
521
 
524
522
 
523
+ def _reconcile_carried_sources(carried: list[Path]) -> Path:
524
+ """서로 다른 계획을 가리키는 carry 들을 하나로 접는다.
525
+
526
+ ADR-0016 은 계획 수정의 산출물을 새 planning 리포트로 정한다. 그래서 수정을
527
+ 한 번이라도 거친 task 에서는 carry 들이 서로 다른 계획을 가리키는 것이
528
+ 정상이다 — 먼저 끝난 stage 는 그때의 계획을, 그 뒤 stage 는 새 계획을
529
+ 적는다. 이 모양을 무조건 충돌로 올리면 계획을 수정한 모든 task 가 Stage Map
530
+ 을 영구히 잃는다.
531
+
532
+ ADR-0015 의 append-only 가 지켜졌다면 두 계획은 공유하는 번호에서 같은
533
+ 작업을 가리킨다. 그 조건을 실제로 확인한 뒤에만 가장 긴 계획을 답으로 쓴다
534
+ — 확인 없이 하나를 고르면 번호 재배치가 조용히 통과하고 완료 커밋이 엉뚱한
535
+ stage 에 귀속된다.
536
+ """
537
+ titles = {
538
+ path: {
539
+ record["stage_number"]: record["title"]
540
+ for record in stage_map_records(parse_stage_map_file(path))
541
+ }
542
+ for path in carried
543
+ }
544
+ conflicts = tuple(str(path) for path in carried)
545
+ for index, left in enumerate(carried):
546
+ for right in carried[index + 1:]:
547
+ renamed = [
548
+ number
549
+ for number in sorted(set(titles[left]) & set(titles[right]))
550
+ if titles[left][number] != titles[right][number]
551
+ ]
552
+ if renamed:
553
+ raise StageMapError(
554
+ "plan-source-conflict",
555
+ "implementation carries reference plans that name different "
556
+ "work under the same stage number: "
557
+ + ", ".join(f"stage {number}" for number in renamed),
558
+ conflicting_paths=conflicts,
559
+ )
560
+ # `_validate_stage_numbers` 가 stage 번호를 `1..N` 단조로 강제하므로 계획의
561
+ # 번호 집합은 언제나 앞이 막힌 구간이다. 그래서 가장 긴 계획이 나머지의
562
+ # superset 인 것은 따로 검사할 명제가 아니라 파서가 이미 보장하는 사실이다.
563
+ return max(carried, key=lambda path: len(titles[path]))
564
+
565
+
525
566
  def load_latest_plan_stage_map(task_root: Path) -> StageMapSnapshot:
526
567
  """이 task 의 최신 계획 리포트가 선언한 Stage Map.
527
568
 
@@ -579,3 +620,8 @@ def _resolve_plan_path(task_root: Path, value: str) -> Path:
579
620
  if task_relative.exists():
580
621
  return task_relative
581
622
  return project_relative
623
+
624
+ # ---- read-side view -------------------------------------------------------
625
+ # 예전에는 okstra_project.state 가 이 어댑터를 들고 okstra_ctl 을 함수 본문에서
626
+ # 지연 import 했다. 하위 계층이 상위를 되짚는 배치였다. 읽는 대상이 okstra_ctl 의
627
+ # Stage Map 과 consumer 상태이므로 소유자를 여기로 옮긴다.
@@ -0,0 +1,83 @@
1
+ """`okstra stage-map` 의 프로젝션 — 한 task 의 구현 계획 Stage Map 과 완료 stage.
2
+
3
+ 원래 `src/commands/inspect/stage-map.mts` 의 문자열 리터럴 안에 있던 코드다
4
+ (docs/coding-rules.md R1 위반). 로직은 그대로 옮겼다.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import json
10
+ from pathlib import Path
11
+
12
+ from okstra_project import ResolverError, StateError, resolve_project_root
13
+
14
+ from .stage_map import render_stage_map_text
15
+ from .stage_map_view import stage_map_read_side_snapshot
16
+
17
+
18
+ def emit(payload: dict, output_mode: str) -> None:
19
+ if output_mode == "text":
20
+ print(render_stage_map_text(payload))
21
+ else:
22
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
23
+
24
+
25
+ _CLI_EPILOG = r"""Usage:
26
+ okstra stage-map <task-key> task-key is project-id:task-group:task-id
27
+ okstra stage-map <task-key> --cwd <dir> Resolve PROJECT_ROOT from <dir>
28
+ okstra stage-map <task-key> --project <dir> Use <dir> directly as PROJECT_ROOT
29
+ okstra stage-map <task-key> --text Emit fixed text fields for model use
30
+
31
+ Output: default and --json emit JSON { ok, taskKey, taskRoot, state, sourcePlanPath,
32
+ stages:[…], doneStages:[int], planning:{…} }; --text emits fixed count/name/value fields.
33
+ Each stage row carries stage_number, title, depends_on, step_count and exit_contract_summary; a
34
+ schema-v2 report adds that stage's sliceValue, acceptance and exitContract.
35
+ planning carries the report's task-level rows — rollbackStrategy, validationChecklist,
36
+ crossProjectDependencies, dependencyMigrationRisk, recommendedOption — so a consumer renders the
37
+ plan instead of re-summarising the report body. Both are absent for a schema-v1 report.
38
+ stages is [] when no implementation-planning Stage Map exists.
39
+ """
40
+ _CLI_DESCRIPTION = "Dump a task's implementation-planning Stage Map + done stages."
41
+
42
+
43
+ def main(argv: list[str] | None = None) -> int:
44
+ parser = argparse.ArgumentParser(
45
+ description=_CLI_DESCRIPTION,
46
+ epilog=_CLI_EPILOG,
47
+ formatter_class=argparse.RawDescriptionHelpFormatter,
48
+ prog="okstra stage-map")
49
+ parser.add_argument("task_key", metavar="task-key",
50
+ help="project-id:task-group:task-id")
51
+ parser.add_argument("--project-root", "--project", default="",
52
+ help="use this directory as PROJECT_ROOT")
53
+ parser.add_argument("--cwd", default=".",
54
+ help="resolve PROJECT_ROOT starting from here")
55
+ mode = parser.add_mutually_exclusive_group()
56
+ mode.add_argument("--json", dest="output_mode", action="store_const",
57
+ const="json", default="json", help="emit JSON (default)")
58
+ mode.add_argument("--text", dest="output_mode", action="store_const",
59
+ const="text", help="emit fixed text fields")
60
+ args = parser.parse_args(argv)
61
+
62
+ try:
63
+ resolved = resolve_project_root(explicit_root=args.project_root, cwd=args.cwd)
64
+ except ResolverError as exc:
65
+ emit({"ok": False, "stage": "resolve", "reason": str(exc)}, args.output_mode)
66
+ return 2
67
+
68
+ try:
69
+ snapshot = stage_map_read_side_snapshot(Path(resolved), args.task_key)
70
+ except StateError as exc:
71
+ emit({
72
+ "ok": False,
73
+ "stage": getattr(exc, "stage", None) or "stage_map",
74
+ "reason": str(exc),
75
+ }, args.output_mode)
76
+ return 1
77
+
78
+ emit({"ok": True, **snapshot}, args.output_mode)
79
+ return 0
80
+
81
+
82
+ if __name__ == "__main__":
83
+ raise SystemExit(main())
@@ -0,0 +1,65 @@
1
+ """stage-map inspect 용 read-side view.
2
+
3
+ Stage Map(okstra_ctl) 과 task identity(okstra_project) 를 한 자리에서 합친다.
4
+ 전에는 이 함수가 okstra_project.state 안에 있었다 — 하위 계층이 okstra_ctl 을
5
+ 함수 본문에서 지연 import 하는 배치였다. 그렇다고 stage_map 으로 옮기면
6
+ stage_map 이 okstra_project 를 끌어오고, okstra_project.state 가 다시
7
+ implementation_outcome → stage_map 으로 돌아와 순환이 된다.
8
+
9
+ 그래서 소비자 쪽 얇은 조립 모듈을 따로 둔다. 이 모듈은 아무도 import 하지
10
+ 않으므로 어느 순환에도 들어갈 수 없다. 소비자는 `okstra stage-map` 하나다.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+
16
+ from okstra_project import StateError, resolve_task_identity
17
+
18
+ from .consumers import read_stage_consumer_state
19
+ from .paths import RunRef
20
+ from .stage_map import (
21
+ PlanningDetail,
22
+ StageMapError,
23
+ load_latest_plan_stage_map,
24
+ load_planning_detail,
25
+ merge_planning_detail,
26
+ )
27
+
28
+
29
+ def stage_map_read_side_snapshot(project_root: Path, task_key: str) -> dict:
30
+ """stage-map inspect 용 read-side view: Stage Map + done 처리된 stage 번호.
31
+
32
+ Stage Map 파싱과 planning run-root 위치를 caller 에게 노출하지 않는다 —
33
+ Node inspect 가 okstra_ctl 의 private 심볼을 import 하거나 `runs/` 경로를
34
+ 직접 조립하지 않도록 하는 어댑터다.
35
+ """
36
+ identity = resolve_task_identity(project_root, task_key)
37
+ task_root = Path(identity["taskRoot"])
38
+ try:
39
+ stage_snapshot = load_latest_plan_stage_map(task_root)
40
+ detail = (
41
+ load_planning_detail(Path(stage_snapshot.source_plan_path))
42
+ if stage_snapshot.source_plan_path
43
+ else PlanningDetail({}, {})
44
+ )
45
+ except StageMapError as exc:
46
+ raise StateError(str(exc), stage=exc.code) from exc
47
+ plan_run_root = RunRef.from_task_root(
48
+ task_root, "implementation-planning"
49
+ ).run_dir
50
+ done: list[int] = []
51
+ if plan_run_root.is_dir():
52
+ done = sorted(
53
+ read_stage_consumer_state(
54
+ plan_run_root, recover_from_carry=True
55
+ ).done_stages
56
+ )
57
+ return {
58
+ "taskKey": identity["taskKey"],
59
+ "taskRoot": identity["taskRoot"],
60
+ "state": stage_snapshot.state,
61
+ "sourcePlanPath": stage_snapshot.source_plan_path,
62
+ "stages": merge_planning_detail(stage_snapshot.stages, detail),
63
+ "doneStages": done,
64
+ "planning": detail.task_narratives,
65
+ }
@@ -14,7 +14,10 @@ from dataclasses import dataclass, replace
14
14
  from pathlib import Path
15
15
  from typing import Any
16
16
 
17
+ from .plan_run_root import plan_run_root_from_approved_plan
18
+ from .prepare_error import PrepareError # noqa: F401 — 재노출(단일 참조점)
17
19
  from .stage_integrate import IntegrateResult
20
+ from okstra_project.slug import slugify
18
21
 
19
22
 
20
23
  RUN_STEP_BUDGET = 8
@@ -24,14 +27,6 @@ class StageTargetError(Exception):
24
27
  """Stage target selection or verification precondition failed."""
25
28
 
26
29
 
27
- class PrepareError(Exception):
28
- """surface to caller — task bundle prepare failed.
29
-
30
- Defined here(저수준 모듈)so both run.py(final-verification)와
31
- container.py 가 whole-task 통합 헬퍼의 실패를 동일 타입으로 받을 수 있다.
32
- run.py 는 이 클래스를 그대로 재노출한다(단일 참조점)."""
33
-
34
-
35
30
  @dataclass
36
31
  class FinalVerificationTarget:
37
32
  scope: str # "whole-task" | "single-stage"
@@ -671,7 +666,6 @@ def resolve_and_integrate_whole_task(
671
666
  ) -> dict[str, Any]:
672
667
  """Locked whole-task integration interface shared with the container."""
673
668
  from okstra_project.dirs import okstra_home
674
- from okstra_project.state import slugify
675
669
 
676
670
  from .locks import worktree_provision_mutex
677
671
 
@@ -734,7 +728,6 @@ def _read_final_verification_done_rows(
734
728
  registry_coordinates: tuple[str, str, str],
735
729
  ) -> list[dict[str, Any]]:
736
730
  from .consumers import backfill_done_from_carry, read_stage_consumer_state
737
- from .plan_run_root import plan_run_root_from_approved_plan
738
731
  from .stage_reconcile import auto_reconcile_best_effort
739
732
 
740
733
  plan_run_root = plan_run_root_from_approved_plan(request.approved_plan_path)
@@ -755,7 +748,6 @@ def _read_final_verification_done_rows(
755
748
  def _final_verification_registry_coordinates(
756
749
  request: FinalVerificationTargetRequest,
757
750
  ) -> tuple[str, str, str]:
758
- from okstra_project.state import slugify
759
751
 
760
752
  def segment(value: str) -> str:
761
753
  return slugify(value) or "_"
@@ -913,7 +905,6 @@ def acquire_final_verification_target(
913
905
  ) -> FinalVerificationTargetAcquisition:
914
906
  """Acquire a stable final-verification target behind one task-key lock."""
915
907
  from okstra_project.dirs import okstra_home
916
- from okstra_project.state import slugify
917
908
 
918
909
  from .locks import worktree_provision_mutex
919
910