okstra 0.186.7 → 0.188.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 (546) 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 +107 -40
  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 +39 -38
  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 +47 -22
  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/adapters/cmux.md +3 -3
  100. package/runtime/prompts/lead/convergence.md +99 -88
  101. package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
  102. package/runtime/prompts/lead/plan-body-verification.md +65 -46
  103. package/runtime/prompts/lead/report-writer.md +35 -5
  104. package/runtime/prompts/lead/team-contract.md +4 -1
  105. package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
  106. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  107. package/runtime/prompts/profiles/_common-contract.md +3 -3
  108. package/runtime/prompts/profiles/_coverage-critic.md +1 -1
  109. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
  110. package/runtime/prompts/profiles/_implementation-executor.md +3 -1
  111. package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
  112. package/runtime/prompts/profiles/error-analysis.md +4 -3
  113. package/runtime/prompts/profiles/final-verification.md +2 -2
  114. package/runtime/prompts/profiles/implementation-planning.md +22 -17
  115. package/runtime/prompts/profiles/implementation.md +3 -2
  116. package/runtime/prompts/profiles/requirements-discovery.md +2 -1
  117. package/runtime/prompts/wizard/prompts.ko.json +29 -8
  118. package/runtime/python/okstra_ctl/__init__.py +6 -27
  119. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
  120. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
  121. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
  122. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +63 -22
  123. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +20 -21
  124. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
  125. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
  126. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
  127. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
  128. package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
  129. package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +16 -2
  130. package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
  131. package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
  132. package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
  133. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +228 -0
  134. package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
  135. package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
  136. package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
  137. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +605 -0
  138. package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
  139. package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
  140. package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
  141. package/runtime/python/okstra_ctl/approval_decisions.py +287 -10
  142. package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
  143. package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
  144. package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
  145. package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
  146. package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
  147. package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
  148. package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
  149. package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
  150. package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
  151. package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
  152. package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
  153. package/runtime/python/okstra_ctl/cmux.py +38 -31
  154. package/runtime/python/okstra_ctl/code_review_target.py +163 -1
  155. package/runtime/python/okstra_ctl/conformance.py +23 -0
  156. package/runtime/python/okstra_ctl/container.py +38 -421
  157. package/runtime/python/okstra_ctl/context_cost.py +32 -4
  158. package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
  159. package/runtime/python/okstra_ctl/convergence.py +107 -16
  160. package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
  161. package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
  162. package/runtime/python/okstra_ctl/convergence_store.py +5 -29
  163. package/runtime/python/okstra_ctl/design_prep.py +108 -12
  164. package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
  165. package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
  166. package/runtime/python/okstra_ctl/dispatch_core.py +236 -49
  167. package/runtime/python/okstra_ctl/dispatch_state.py +417 -41
  168. package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
  169. package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
  170. package/runtime/python/okstra_ctl/domain/worker_presentation.py +28 -1
  171. package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
  172. package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
  173. package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
  174. package/runtime/python/okstra_ctl/error_log_core.py +1 -1
  175. package/runtime/python/okstra_ctl/error_report.py +14 -0
  176. package/runtime/python/okstra_ctl/error_zip.py +18 -2
  177. package/runtime/python/okstra_ctl/exact_coverage.py +5 -0
  178. package/runtime/python/okstra_ctl/execution_identity.py +100 -7
  179. package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
  180. package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
  181. package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
  182. package/runtime/python/okstra_ctl/final_report_schema.py +319 -17
  183. package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
  184. package/runtime/python/okstra_ctl/handoff.py +24 -1
  185. package/runtime/python/okstra_ctl/ids.py +6 -16
  186. package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
  187. package/runtime/python/okstra_ctl/implementation_options.py +32 -13
  188. package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
  189. package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
  190. package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
  191. package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
  192. package/runtime/python/okstra_ctl/index.py +2 -2
  193. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
  194. package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
  195. package/runtime/python/okstra_ctl/json_boundary.py +10 -0
  196. package/runtime/python/okstra_ctl/listing.py +0 -91
  197. package/runtime/python/okstra_ctl/locks.py +5 -19
  198. package/runtime/python/okstra_ctl/log_report.py +14 -0
  199. package/runtime/python/okstra_ctl/manager_cli.py +22 -3
  200. package/runtime/python/okstra_ctl/manager_launch.py +4 -8
  201. package/runtime/python/okstra_ctl/material.py +2 -2
  202. package/runtime/python/okstra_ctl/migrate.py +23 -1
  203. package/runtime/python/okstra_ctl/model_cli.py +19 -5
  204. package/runtime/python/okstra_ctl/model_discovery.py +46 -47
  205. package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
  206. package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
  207. package/runtime/python/okstra_ctl/model_io/references.py +370 -0
  208. package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
  209. package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
  210. package/runtime/python/okstra_ctl/model_pool.py +10 -6
  211. package/runtime/python/okstra_ctl/models.py +8 -8
  212. package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
  213. package/runtime/python/okstra_ctl/next_phase.py +121 -69
  214. package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
  215. package/runtime/python/okstra_ctl/pane_title.py +11 -8
  216. package/runtime/python/okstra_ctl/path_hints.py +36 -11
  217. package/runtime/python/okstra_ctl/paths.py +76 -8
  218. package/runtime/python/okstra_ctl/plan_items.py +132 -16
  219. package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
  220. package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
  221. package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
  222. package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
  223. package/runtime/python/okstra_ctl/prepare_error.py +20 -0
  224. package/runtime/python/okstra_ctl/prior_planning.py +157 -0
  225. package/runtime/python/okstra_ctl/profile_show.py +18 -1
  226. package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
  227. package/runtime/python/okstra_ctl/recap.py +22 -1
  228. package/runtime/python/okstra_ctl/reconcile.py +84 -0
  229. package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
  230. package/runtime/python/okstra_ctl/render.py +98 -32
  231. package/runtime/python/okstra_ctl/render_final_report.py +4 -8
  232. package/runtime/python/okstra_ctl/report_assembly.py +297 -7
  233. package/runtime/python/okstra_ctl/report_contract.py +30 -7
  234. package/runtime/python/okstra_ctl/report_finalize.py +213 -16
  235. package/runtime/python/okstra_ctl/report_html/common.py +2 -2
  236. package/runtime/python/okstra_ctl/report_html/render.py +8 -12
  237. package/runtime/python/okstra_ctl/report_language.py +8 -4
  238. package/runtime/python/okstra_ctl/report_narrative.py +220 -34
  239. package/runtime/python/okstra_ctl/report_projections.py +8 -2
  240. package/runtime/python/okstra_ctl/report_synthesis_packet.py +393 -1
  241. package/runtime/python/okstra_ctl/report_translation.py +2 -35
  242. package/runtime/python/okstra_ctl/report_view_artifacts.py +42 -2
  243. package/runtime/python/okstra_ctl/report_views.py +4 -22
  244. package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
  245. package/runtime/python/okstra_ctl/rollup.py +13 -0
  246. package/runtime/python/okstra_ctl/run.py +639 -138
  247. package/runtime/python/okstra_ctl/run_audit.py +13 -0
  248. package/runtime/python/okstra_ctl/run_index_row.py +0 -12
  249. package/runtime/python/okstra_ctl/seeding.py +21 -11
  250. package/runtime/python/okstra_ctl/sequence.py +1 -1
  251. package/runtime/python/okstra_ctl/session.py +64 -14
  252. package/runtime/python/okstra_ctl/set_work_status.py +18 -0
  253. package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
  254. package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
  255. package/runtime/python/okstra_ctl/stage_map.py +57 -11
  256. package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
  257. package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
  258. package/runtime/python/okstra_ctl/stage_targets.py +3 -12
  259. package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
  260. package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
  261. package/runtime/python/okstra_ctl/task_target.py +4 -16
  262. package/runtime/python/okstra_ctl/team.py +49 -2
  263. package/runtime/python/okstra_ctl/time_report.py +17 -2
  264. package/runtime/python/okstra_ctl/usage_report.py +11 -0
  265. package/runtime/python/okstra_ctl/user_response.py +55 -218
  266. package/runtime/python/okstra_ctl/user_response_values.py +242 -0
  267. package/runtime/python/okstra_ctl/validation_contract.py +3 -0
  268. package/runtime/python/okstra_ctl/verdict_blocks.py +28 -8
  269. package/runtime/python/okstra_ctl/verification_target.py +68 -0
  270. package/runtime/python/okstra_ctl/wizard.py +457 -92
  271. package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
  272. package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
  273. package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
  274. package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
  275. package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
  276. package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
  277. package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
  278. package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
  279. package/runtime/python/okstra_ctl/worker_request.py +2 -1
  280. package/runtime/python/okstra_ctl/worker_runner.py +58 -17
  281. package/runtime/python/okstra_ctl/worker_state.py +16 -1
  282. package/runtime/python/okstra_ctl/workflow.py +18 -1
  283. package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
  284. package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
  285. package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
  286. package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
  287. package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
  288. package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
  289. package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
  290. package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
  291. package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
  292. package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
  293. package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
  294. package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
  295. package/runtime/python/okstra_ctl/write_policy.py +90 -213
  296. package/runtime/python/okstra_project/__init__.py +0 -4
  297. package/runtime/python/okstra_project/dirs.py +2 -2
  298. package/runtime/python/okstra_project/phase_pointer.py +84 -0
  299. package/runtime/python/okstra_project/slug.py +24 -0
  300. package/runtime/python/okstra_project/state.py +22 -230
  301. package/runtime/python/okstra_token_usage/antigravity.py +50 -12
  302. package/runtime/python/okstra_token_usage/claude.py +9 -1
  303. package/runtime/python/okstra_token_usage/cli.py +3 -1
  304. package/runtime/python/okstra_token_usage/collect.py +171 -36
  305. package/runtime/python/okstra_token_usage/cursor.py +2 -0
  306. package/runtime/python/okstra_token_usage/grok.py +1 -5
  307. package/runtime/python/okstra_token_usage/paths.py +26 -0
  308. package/runtime/python/okstra_token_usage/pricing.py +23 -8
  309. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
  310. package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
  311. package/runtime/schemas/final-report-v2.0.schema.json +9 -26
  312. package/runtime/schemas/final-report-v3.0.schema.json +173 -41
  313. package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
  314. package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
  315. package/runtime/skills/okstra-container-build/SKILL.md +16 -47
  316. package/runtime/skills/okstra-inspect/facets/history.md +1 -1
  317. package/runtime/skills/okstra-inspect/facets/status.md +7 -6
  318. package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
  319. package/runtime/skills/okstra-run/SKILL.md +7 -5
  320. package/runtime/templates/report-writer-prompt-preamble.md +3 -1
  321. package/runtime/templates/reports/brief.template.md +6 -2
  322. package/runtime/templates/reports/error-analysis-input.template.md +2 -0
  323. package/runtime/templates/reports/final-verification-input.template.md +2 -0
  324. package/runtime/templates/reports/implementation-input.template.md +7 -1
  325. package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
  326. package/runtime/templates/reports/quick-input.template.md +2 -0
  327. package/runtime/templates/reports/release-handoff-input.template.md +6 -1
  328. package/runtime/templates/reports/report.js +20 -3
  329. package/runtime/templates/reports/schedule.template.md +2 -0
  330. package/runtime/templates/reports/settings.template.json +0 -10
  331. package/runtime/templates/reports/task-brief.template.md +3 -0
  332. package/runtime/templates/reports/user-response.template.md +7 -3
  333. package/runtime/validators/checks/fixtures-01.py +57 -0
  334. package/runtime/validators/checks/fixtures-02.py +565 -0
  335. package/runtime/validators/checks/runners-01.py +108 -0
  336. package/runtime/validators/checks/validate-assets-01.py +60 -0
  337. package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
  338. package/runtime/validators/checks/validate-tasks-01.py +46 -0
  339. package/runtime/validators/checks/validate-tasks-02.py +85 -0
  340. package/runtime/validators/checks/validate-tasks-03.py +61 -0
  341. package/runtime/validators/checks/validate-tasks-04.py +118 -0
  342. package/runtime/validators/forbidden_actions.py +73 -3
  343. package/runtime/validators/lib/common.sh +5 -0
  344. package/runtime/validators/lib/fixtures.sh +7 -591
  345. package/runtime/validators/lib/paths.sh +13 -0
  346. package/runtime/validators/lib/runners.sh +6 -104
  347. package/runtime/validators/lib/summary.sh +1 -1
  348. package/runtime/validators/lib/validate-assets.sh +6 -56
  349. package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
  350. package/runtime/validators/lib/validate-tasks.sh +9 -294
  351. package/runtime/validators/validate-implementation-plan-stages.py +4 -4
  352. package/runtime/validators/validate-run.py +1363 -2643
  353. package/runtime/validators/validate-workflow.sh +56 -16
  354. package/runtime/validators/validate_improvement_report.py +2 -1
  355. package/runtime/validators/validate_session_conformance.py +295 -49
  356. package/dist/commands/execute/agent-prompt.d.mts +0 -1
  357. package/dist/commands/execute/agent-prompt.mjs +0 -24
  358. package/dist/commands/execute/agent-prompt.mjs.map +0 -1
  359. package/dist/commands/execute/codex-dispatch.d.mts +0 -3
  360. package/dist/commands/execute/codex-dispatch.mjs +0 -6
  361. package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
  362. package/dist/commands/execute/codex-run.d.mts +0 -3
  363. package/dist/commands/execute/codex-run.mjs +0 -62
  364. package/dist/commands/execute/codex-run.mjs.map +0 -1
  365. package/dist/commands/execute/convergence.d.mts +0 -1
  366. package/dist/commands/execute/convergence.mjs +0 -38
  367. package/dist/commands/execute/convergence.mjs.map +0 -1
  368. package/dist/commands/execute/error-log.d.mts +0 -1
  369. package/dist/commands/execute/error-log.mjs +0 -18
  370. package/dist/commands/execute/error-log.mjs.map +0 -1
  371. package/dist/commands/execute/git-reconcile.d.mts +0 -1
  372. package/dist/commands/execute/git-reconcile.mjs +0 -30
  373. package/dist/commands/execute/git-reconcile.mjs.map +0 -1
  374. package/dist/commands/execute/handoff.d.mts +0 -1
  375. package/dist/commands/execute/handoff.mjs +0 -31
  376. package/dist/commands/execute/handoff.mjs.map +0 -1
  377. package/dist/commands/execute/incremental-carry.d.mts +0 -1
  378. package/dist/commands/execute/incremental-carry.mjs +0 -20
  379. package/dist/commands/execute/incremental-carry.mjs.map +0 -1
  380. package/dist/commands/execute/incremental-scope.d.mts +0 -1
  381. package/dist/commands/execute/incremental-scope.mjs +0 -29
  382. package/dist/commands/execute/incremental-scope.mjs.map +0 -1
  383. package/dist/commands/execute/integrate-stages.d.mts +0 -1
  384. package/dist/commands/execute/integrate-stages.mjs +0 -24
  385. package/dist/commands/execute/integrate-stages.mjs.map +0 -1
  386. package/dist/commands/execute/pane-title.d.mts +0 -1
  387. package/dist/commands/execute/pane-title.mjs +0 -20
  388. package/dist/commands/execute/pane-title.mjs.map +0 -1
  389. package/dist/commands/execute/plan-items.d.mts +0 -1
  390. package/dist/commands/execute/plan-items.mjs +0 -9
  391. package/dist/commands/execute/plan-items.mjs.map +0 -1
  392. package/dist/commands/execute/plan-validate.d.mts +0 -1
  393. package/dist/commands/execute/plan-validate.mjs +0 -68
  394. package/dist/commands/execute/plan-validate.mjs.map +0 -1
  395. package/dist/commands/execute/plan-verify.d.mts +0 -1
  396. package/dist/commands/execute/plan-verify.mjs +0 -43
  397. package/dist/commands/execute/plan-verify.mjs.map +0 -1
  398. package/dist/commands/execute/spawn-followups.d.mts +0 -1
  399. package/dist/commands/execute/spawn-followups.mjs +0 -22
  400. package/dist/commands/execute/spawn-followups.mjs.map +0 -1
  401. package/dist/commands/execute/team.d.mts +0 -3
  402. package/dist/commands/execute/team.mjs +0 -66
  403. package/dist/commands/execute/team.mjs.map +0 -1
  404. package/dist/commands/execute/token-usage.d.mts +0 -1
  405. package/dist/commands/execute/token-usage.mjs +0 -19
  406. package/dist/commands/execute/token-usage.mjs.map +0 -1
  407. package/dist/commands/execute/worker-audit-check.d.mts +0 -1
  408. package/dist/commands/execute/worker-audit-check.mjs +0 -34
  409. package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
  410. package/dist/commands/execute/worker-dispatch.d.mts +0 -7
  411. package/dist/commands/execute/worker-dispatch.mjs +0 -64
  412. package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
  413. package/dist/commands/execute/worker-state.d.mts +0 -1
  414. package/dist/commands/execute/worker-state.mjs +0 -28
  415. package/dist/commands/execute/worker-state.mjs.map +0 -1
  416. package/dist/commands/execute/worktree-lookup.d.mts +0 -1
  417. package/dist/commands/execute/worktree-lookup.mjs +0 -92
  418. package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
  419. package/dist/commands/execute/worktree-status.d.mts +0 -1
  420. package/dist/commands/execute/worktree-status.mjs +0 -121
  421. package/dist/commands/execute/worktree-status.mjs.map +0 -1
  422. package/dist/commands/inspect/code-review.d.mts +0 -1
  423. package/dist/commands/inspect/code-review.mjs +0 -32
  424. package/dist/commands/inspect/code-review.mjs.map +0 -1
  425. package/dist/commands/inspect/container.d.mts +0 -1
  426. package/dist/commands/inspect/container.mjs +0 -25
  427. package/dist/commands/inspect/container.mjs.map +0 -1
  428. package/dist/commands/inspect/context-cost.d.mts +0 -1
  429. package/dist/commands/inspect/context-cost.mjs +0 -25
  430. package/dist/commands/inspect/context-cost.mjs.map +0 -1
  431. package/dist/commands/inspect/design-prep.d.mts +0 -1
  432. package/dist/commands/inspect/design-prep.mjs +0 -22
  433. package/dist/commands/inspect/design-prep.mjs.map +0 -1
  434. package/dist/commands/inspect/error-report.d.mts +0 -1
  435. package/dist/commands/inspect/error-report.mjs +0 -25
  436. package/dist/commands/inspect/error-report.mjs.map +0 -1
  437. package/dist/commands/inspect/error-zip.d.mts +0 -1
  438. package/dist/commands/inspect/error-zip.mjs +0 -24
  439. package/dist/commands/inspect/error-zip.mjs.map +0 -1
  440. package/dist/commands/inspect/log-report.d.mts +0 -1
  441. package/dist/commands/inspect/log-report.mjs +0 -26
  442. package/dist/commands/inspect/log-report.mjs.map +0 -1
  443. package/dist/commands/inspect/model-io.d.mts +0 -1
  444. package/dist/commands/inspect/model-io.mjs +0 -25
  445. package/dist/commands/inspect/model-io.mjs.map +0 -1
  446. package/dist/commands/inspect/profile-show.d.mts +0 -1
  447. package/dist/commands/inspect/profile-show.mjs +0 -28
  448. package/dist/commands/inspect/profile-show.mjs.map +0 -1
  449. package/dist/commands/inspect/recap.d.mts +0 -1
  450. package/dist/commands/inspect/recap.mjs +0 -30
  451. package/dist/commands/inspect/recap.mjs.map +0 -1
  452. package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
  453. package/dist/commands/inspect/resolve-task-key.mjs +0 -24
  454. package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
  455. package/dist/commands/inspect/rollup.d.mts +0 -1
  456. package/dist/commands/inspect/rollup.mjs +0 -25
  457. package/dist/commands/inspect/rollup.mjs.map +0 -1
  458. package/dist/commands/inspect/run-audit.d.mts +0 -1
  459. package/dist/commands/inspect/run-audit.mjs +0 -25
  460. package/dist/commands/inspect/run-audit.mjs.map +0 -1
  461. package/dist/commands/inspect/set-work-status.d.mts +0 -1
  462. package/dist/commands/inspect/set-work-status.mjs +0 -29
  463. package/dist/commands/inspect/set-work-status.mjs.map +0 -1
  464. package/dist/commands/inspect/stage-map.d.mts +0 -1
  465. package/dist/commands/inspect/stage-map.mjs +0 -131
  466. package/dist/commands/inspect/stage-map.mjs.map +0 -1
  467. package/dist/commands/inspect/task-list.d.mts +0 -1
  468. package/dist/commands/inspect/task-list.mjs +0 -149
  469. package/dist/commands/inspect/task-list.mjs.map +0 -1
  470. package/dist/commands/inspect/task-show.d.mts +0 -1
  471. package/dist/commands/inspect/task-show.mjs +0 -108
  472. package/dist/commands/inspect/task-show.mjs.map +0 -1
  473. package/dist/commands/inspect/time-report.d.mts +0 -1
  474. package/dist/commands/inspect/time-report.mjs +0 -24
  475. package/dist/commands/inspect/time-report.mjs.map +0 -1
  476. package/dist/commands/inspect/usage-report.d.mts +0 -1
  477. package/dist/commands/inspect/usage-report.mjs +0 -23
  478. package/dist/commands/inspect/usage-report.mjs.map +0 -1
  479. package/dist/commands/inspect/user-response.d.mts +0 -1
  480. package/dist/commands/inspect/user-response.mjs +0 -35
  481. package/dist/commands/inspect/user-response.mjs.map +0 -1
  482. package/dist/commands/inspect/worker-liveness.d.mts +0 -1
  483. package/dist/commands/inspect/worker-liveness.mjs +0 -45
  484. package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
  485. package/dist/commands/lifecycle/contract-check.d.mts +0 -1
  486. package/dist/commands/lifecycle/contract-check.mjs +0 -18
  487. package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
  488. package/dist/commands/lifecycle/migrate.d.mts +0 -1
  489. package/dist/commands/lifecycle/migrate.mjs +0 -30
  490. package/dist/commands/lifecycle/migrate.mjs.map +0 -1
  491. package/dist/commands/lifecycle/model.d.mts +0 -1
  492. package/dist/commands/lifecycle/model.mjs +0 -22
  493. package/dist/commands/lifecycle/model.mjs.map +0 -1
  494. package/dist/commands/manager.d.mts +0 -3
  495. package/dist/commands/manager.mjs +0 -50
  496. package/dist/commands/manager.mjs.map +0 -1
  497. package/dist/commands/report/agent-activity.d.mts +0 -1
  498. package/dist/commands/report/agent-activity.mjs +0 -20
  499. package/dist/commands/report/agent-activity.mjs.map +0 -1
  500. package/dist/commands/report/approval-decision.d.mts +0 -1
  501. package/dist/commands/report/approval-decision.mjs +0 -21
  502. package/dist/commands/report/approval-decision.mjs.map +0 -1
  503. package/dist/commands/report/design-snapshot.d.mts +0 -1
  504. package/dist/commands/report/design-snapshot.mjs +0 -19
  505. package/dist/commands/report/design-snapshot.mjs.map +0 -1
  506. package/dist/commands/report/finalize.d.mts +0 -4
  507. package/dist/commands/report/finalize.mjs +0 -64
  508. package/dist/commands/report/finalize.mjs.map +0 -1
  509. package/dist/commands/report/inject-report-index.d.mts +0 -1
  510. package/dist/commands/report/inject-report-index.mjs +0 -21
  511. package/dist/commands/report/inject-report-index.mjs.map +0 -1
  512. package/dist/commands/report/render-final-report.d.mts +0 -1
  513. package/dist/commands/report/render-final-report.mjs +0 -23
  514. package/dist/commands/report/render-final-report.mjs.map +0 -1
  515. package/dist/commands/report/render-views.d.mts +0 -1
  516. package/dist/commands/report/render-views.mjs +0 -27
  517. package/dist/commands/report/render-views.mjs.map +0 -1
  518. package/dist/commands/report/translate.d.mts +0 -1
  519. package/dist/commands/report/translate.mjs +0 -33
  520. package/dist/commands/report/translate.mjs.map +0 -1
  521. package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
  522. package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
  523. package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
  524. package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
  525. package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
  526. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
  527. package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
  528. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
  529. package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
  530. package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
  531. package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
  532. package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
  533. package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
  534. package/runtime/bin/okstra-central.sh +0 -152
  535. package/runtime/bin/okstra-incremental-carry.py +0 -10
  536. package/runtime/bin/okstra-incremental-scope.py +0 -10
  537. package/runtime/bin/okstra-team-reconcile.sh +0 -36
  538. package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
  539. package/runtime/python/okstra_ctl/batch.py +0 -60
  540. package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
  541. package/runtime/python/okstra_ctl/container_registry.py +0 -72
  542. package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
  543. package/runtime/python/okstra_ctl/resolver.py +0 -54
  544. package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
  545. package/runtime/python/okstra_ctl/tmux.py +0 -134
  546. package/runtime/python/okstra_ctl/worktree.py +0 -1099
@@ -10,6 +10,7 @@ SIGTERM a healthy worker in the middle of a long reasoning stretch.
10
10
  """
11
11
  from __future__ import annotations
12
12
 
13
+ import dataclasses
13
14
  import json
14
15
  import os
15
16
  import selectors
@@ -21,6 +22,7 @@ from typing import Any, Callable, Mapping
21
22
 
22
23
  from .domain.provider import ServedModelAttestation, ServedModelNormalizer
23
24
  from .domain.worker_exec import (
25
+ NO_RESULT_EXIT_CODE,
24
26
  SERVED_MODEL_MISMATCH_EXIT_CODE,
25
27
  ExecCommand,
26
28
  ExecutionStrategy,
@@ -71,7 +73,7 @@ def run_worker(
71
73
 
72
74
  try:
73
75
  with guard:
74
- exit_code, timed_out, idle_seconds, raw_model = _launch(
76
+ exit_code, timed_out, idle_seconds, raw_model, usage = _launch(
75
77
  command,
76
78
  log_path,
77
79
  presentation=presentation,
@@ -92,6 +94,11 @@ def run_worker(
92
94
  raise
93
95
 
94
96
  status = _closed(status, started_monotonic)
97
+ if usage is not None:
98
+ # 스트림이 마지막으로 보고한 토큰 스냅샷, 공급자 어휘 그대로. 토큰
99
+ # 수집기(`okstra_token_usage`)가 홈 트랜스크립트가 없는 공급자의
100
+ # 사용량을 여기서 읽는다.
101
+ status["usage"] = dict(usage)
95
102
  if served_model_normalizer is not None:
96
103
  attestation = served_model_normalizer(raw_model)
97
104
  status["servedModelAttestation"] = _attestation_payload(attestation)
@@ -170,11 +177,14 @@ def _launch(
170
177
  presentation: str,
171
178
  idle_timeout_seconds: int,
172
179
  on_spawn: Callable[[subprocess.Popen[bytes]], None],
173
- ) -> tuple[int, bool, int, str | None]:
180
+ ) -> tuple[int, bool, int, str | None, Mapping[str, Any] | None]:
174
181
  live = presentation == LIVE
175
182
  transcript = SessionTranscript(log_path, live=live)
176
183
  observation = _ServedModelObservation()
177
- strategy = _with_served_model_observation(command.presentation, observation)
184
+ usage_observation = _UsageObservation()
185
+ strategy = _with_stream_observations(
186
+ command.presentation, observation, usage_observation
187
+ )
178
188
  try:
179
189
  # The strategy decided where this provider runs — some CLIs work in the
180
190
  # stage tree, others in the project root and reach the tree by flag.
@@ -203,34 +213,43 @@ def _launch(
203
213
  idle_timeout_seconds=idle_timeout_seconds,
204
214
  stdin_text=command.stdin_text,
205
215
  )
206
- return exit_code, timed_out, idle_seconds, observation.raw_model
216
+ return (
217
+ exit_code,
218
+ timed_out,
219
+ idle_seconds,
220
+ observation.raw_model,
221
+ usage_observation.usage,
222
+ )
207
223
  finally:
208
224
  transcript.close()
209
225
 
210
226
 
211
- def _with_served_model_observation(
227
+ def _with_stream_observations(
212
228
  presentation: Presentation,
213
229
  observation: _ServedModelObservation,
230
+ usage_observation: _UsageObservation,
214
231
  ) -> Presentation:
215
- """JSON 경로의 서빙 모델 관측을 러너가 모아 둔다.
232
+ """JSON 경로의 서빙 모델·토큰 사용량 관측을 러너가 모아 둔다.
216
233
 
217
- 해석 전략은 이벤트를 보고 모델 문자열만 돌려준다. 그 값을 사이드카에
218
- 적는 일은 러너의 것이라, 여기서 한 번 감싼다.
234
+ 해석 전략은 이벤트를 보고 모델 문자열과 사용량 스냅샷만 돌려준다. 그 값을
235
+ 사이드카에 적는 일은 러너의 것이라, 여기서 한 번 감싼다. 사용량은 어댑터가
236
+ 읽는 법을 넘긴 어휘에서만 관측된다.
219
237
  """
220
238
  if not isinstance(presentation, JsonEvents):
221
239
  return presentation
222
240
  original = presentation.observe
241
+ original_usage = presentation.observe_usage
223
242
 
224
243
  def observe(event: Mapping[str, Any]) -> str | None:
225
244
  observed = original(event)
226
245
  observation.record(observed)
246
+ if original_usage is not None:
247
+ usage_observation.record(original_usage(event))
227
248
  return observed
228
249
 
229
- return JsonEvents(
230
- normalise=presentation.normalise,
231
- observe=observe,
232
- join_adjacent_text=presentation.join_adjacent_text,
233
- )
250
+ # 필드를 손으로 옮기지 않는다 — 그렇게 하던 동안 어댑터가 선언한
251
+ # `is_conclusion` 이 여기서 떨어져 나가, grok 의 종결 판정이 무효였다.
252
+ return dataclasses.replace(presentation, observe=observe)
234
253
 
235
254
 
236
255
  class _ServedModelObservation:
@@ -242,6 +261,21 @@ class _ServedModelObservation:
242
261
  self.raw_model = raw_model
243
262
 
244
263
 
264
+ class _UsageObservation:
265
+ """스트림이 보고한 마지막 사용량 스냅샷.
266
+
267
+ 첫 값이 아니라 마지막 값이다 — 단계별 스냅샷을 내는 CLI 도 종결 이벤트에
268
+ 합계를 싣고, 그 이벤트가 스트림의 끝에 온다.
269
+ """
270
+
271
+ def __init__(self) -> None:
272
+ self.usage: Mapping[str, Any] | None = None
273
+
274
+ def record(self, usage: Mapping[str, Any] | None) -> None:
275
+ if isinstance(usage, Mapping) and usage:
276
+ self.usage = usage
277
+
278
+
245
279
  class _AbnormalExit:
246
280
  """Close the status sidecar for the exits that raise nothing at all.
247
281
 
@@ -393,11 +427,16 @@ def _pump(
393
427
  strategy.flush(transcript)
394
428
 
395
429
  exit_code = process.wait()
396
- if closing_text is None and isinstance(strategy, JsonEvents):
397
- # JSON 스트림은 결과 이벤트로 끝나야 한다. 텍스트를 흘리는 CLI 에는
398
- # 놓칠 결과 이벤트가 없다.
430
+ if isinstance(strategy, JsonEvents) and not strategy.concluded():
431
+ # JSON 스트림은 그 어휘의 종결 이벤트로 끝나야 한다. 텍스트를 흘리는
432
+ # CLI 에는 놓칠 종결 이벤트가 없다. 무엇이 종결인지는 전략이 안다.
399
433
  transcript.note("no result event in the CLI's output")
400
- elif closing_text is not None:
434
+ if exit_code == 0 and not timed_out:
435
+ # 실측(dev-10341, 한 CLI 4회): 종결 이벤트도 산출물도 없이 exit 0.
436
+ # 그대로 돌려주면 사이드카와 셸 소비자에게는 성공으로 읽힌다.
437
+ # CLI 자신의 비영 코드는 보존한다 — 그쪽이 더 구체적인 사실이다.
438
+ exit_code = NO_RESULT_EXIT_CODE
439
+ if closing_text is not None:
401
440
  # Result 는 format_* 가 줄을 만들지 않는다. 아카이브 결론은 러너가 적는다.
402
441
  # 상한 밖에 둔다 — 잘린 결론은 사후 분석 전체를 잃게 한다.
403
442
  if isinstance(strategy, JsonEvents):
@@ -559,6 +598,8 @@ def _terminate(process: subprocess.Popen[bytes]) -> None:
559
598
  try:
560
599
  os.killpg(process.pid, signal.SIGKILL)
561
600
  except ProcessLookupError:
601
+ # expected-miss: SIGTERM 유예 사이에 그룹이 스스로 끝난 경우다.
602
+ # 죽이려던 대상이 이미 없는 것은 성공이지 실패가 아니다.
562
603
  pass
563
604
  process.wait()
564
605
 
@@ -18,8 +18,23 @@ def _validated_team_state_path(value: str) -> Path:
18
18
  return path
19
19
 
20
20
 
21
+ _CLI_EPILOG = r"""Usage:
22
+ okstra worker-state transition --team-state <path> --worker <worker-id> \
23
+ --status <in-progress|completed|timeout|error|not-run> \
24
+ [--reason <text>] [--model <execution-value>]
25
+
26
+ in-progress records startedAt and clears endedAt. completed, timeout, and error
27
+ record endedAt. timeout, error, and not-run require --reason.
28
+ """
29
+ _CLI_DESCRIPTION = "Atomically transition persisted worker status."
30
+
31
+
21
32
  def _parser() -> argparse.ArgumentParser:
22
- parser = argparse.ArgumentParser(prog="okstra worker-state")
33
+ parser = argparse.ArgumentParser(
34
+ description=_CLI_DESCRIPTION,
35
+ epilog=_CLI_EPILOG,
36
+ formatter_class=argparse.RawDescriptionHelpFormatter,
37
+ prog="okstra worker-state")
23
38
  commands = parser.add_subparsers(dest="command", required=True)
24
39
  transition = commands.add_parser(
25
40
  "transition", help="atomically transition one worker status"
@@ -54,7 +54,7 @@ PHASE_RULES: dict[str, dict[str, str]] = {
54
54
  " - top-N ranking (default 8, brief `candidate-cap` overrides within 1..12)\n"
55
55
  " - per-candidate columns Lens / Scope / Severity / Effort / Consensus / Source workers / Recommended next-phase / Expected behavior after / Evidence (path:line)\n"
56
56
  " - Phase 1.5 reflect-back grilling log at `runs/improvement-discovery/<seq>/state/phase-1.5-grilling.md`\n"
57
- " - Phase 5.5 consensus classification (full / partial / contested / worker-unique)"
57
+ " - Phase 5.5 consensus classification (full / partial / contested / worker-unique / unverified)"
58
58
  ),
59
59
  },
60
60
  "project-analysis": {
@@ -173,6 +173,22 @@ def load_phase_forbidden(workspace_root: Path) -> dict[str, str]:
173
173
  return {phase: render_forbidden(items) for phase, items in data.items()}
174
174
 
175
175
 
176
+ def phase_position(task_type: str) -> str:
177
+ """이 phase 가 생애주기 몇 번째인지 — `"5 of 7"`.
178
+
179
+ 리드는 자기가 어느 칸을 도는지만 알고 지도를 못 본다. 다음 phase 이름은
180
+ 이 run 의 리포트가 정하므로 계속 감추지만(`WORKFLOW_NEXT_RECOMMENDED_PHASE`
181
+ 가 빈 문자열인 이유), 위치를 감출 이유는 없다 — 위치를 모르면 중간 단계를
182
+ 도는 run 이 자기가 마지막인 것처럼 결론을 낸다.
183
+
184
+ `PHASE_SEQUENCE` 밖의 사이드트랙 task-type(improvement-discovery 등)은
185
+ 생애주기 칸이 없으므로 빈 문자열이다.
186
+ """
187
+ if task_type not in PHASE_SEQUENCE:
188
+ return ""
189
+ return f"{PHASE_SEQUENCE.index(task_type) + 1} of {len(PHASE_SEQUENCE)}"
190
+
191
+
176
192
  def compute_workflow_state(
177
193
  *,
178
194
  task_type: str,
@@ -219,6 +235,7 @@ def compute_workflow_state(
219
235
  "WORKFLOW_WORK_CATEGORY": resolved_work_category,
220
236
  "WORKFLOW_CURRENT_PHASE": task_type,
221
237
  "WORKFLOW_CURRENT_PHASE_STATE": phase_state,
238
+ "WORKFLOW_PHASE_POSITION": phase_position(task_type),
222
239
  # 값이 아니라 자리표시자다. 실제 포인터는 리포트 라우팅에서 투영돼
223
240
  # 매니페스트의 `workflow.nextRecommendedPhase` 구조체로 저장된다.
224
241
  # 이 ctx 값은 그 구조체로 대체되지 않는다 — 남은 소비자는
@@ -0,0 +1,92 @@
1
+ """Per-task git worktree provisioning.
2
+
3
+ Every okstra task — regardless of task-type/phase — runs inside an
4
+ isolated git worktree rooted at
5
+ `~/.okstra/worktrees/<project_id>/<task_group>/<task_id>/`. The same
6
+ worktree is reused across all phases of one task-key (requirements-
7
+ discovery → error-analysis → implementation-option-selection →
8
+ implementation-planning → implementation), so phase N picks up exactly
9
+ the working-tree state phase N-1 left behind.
10
+
11
+ A global registry (`worktree_registry.py`) maps task-keys to the
12
+ on-disk path + branch and serialises reservations, so two concurrent
13
+ okstra runs cannot collide on the same path or branch name.
14
+
15
+ Pre-conditions handled here:
16
+ - Skip when `project_root` is not a git repo (degrade gracefully).
17
+ - Skip when `project_root` itself is already a non-main worktree
18
+ (caller's tree is already an isolated workspace; reuse it).
19
+ - Re-entry of the same task-key returns the existing worktree.
20
+ - Branch / path collisions across task-keys raise PrepareError-like
21
+ RuntimeError.
22
+
23
+ Side effects:
24
+ - `git worktree add -b <branch> <path> <base_ref>` invoked in the
25
+ main worktree of `project_root` (NOT `project_root` itself when it
26
+ is also a worktree — base must be the main checkout).
27
+ - Per-task sync dirs (.project-docs, .scratch, graphify-out by
28
+ default) symlinked from the **main worktree** into the new
29
+ worktree so every task sees the same shared state, irrespective of
30
+ which worktree the caller invoked okstra from.
31
+ - The function does NOT chdir.
32
+
33
+ 구현은 여섯 층으로 나뉜다. 바닥의 세 층 — `naming`(문자열 계산),
34
+ `sync_config`(무엇을 옮길지), `git_ops`(git 사실) — 은 서로를 모른다. 그 위에
35
+ `cleanliness`(무엇이 더러운가) · `linking`(실제로 링크 걸기) ·
36
+ `decisions`(무엇이 일어날지 미리 답하기)가 있고, 부수 효과를 내는 것은
37
+ `provision` 하나뿐이다. 이 파일은 밖으로 나가는 이름만 모아 둔다.
38
+ """
39
+ from __future__ import annotations
40
+
41
+ from .cleanliness import (
42
+ dirty_entries_excluding_okstra,
43
+ is_dirty_excluding_okstra,
44
+ nested_worktree_excludes,
45
+ okstra_clean_gate_excludes,
46
+ )
47
+ from .decisions import (
48
+ StageWorktreeDecision,
49
+ WorktreeDecision,
50
+ preview_worktree_decision,
51
+ resolve_stage_worktree_decision,
52
+ )
53
+ from .git_ops import (
54
+ MergeError,
55
+ _git,
56
+ _resolve_commit_sha,
57
+ is_ancestor,
58
+ is_git_work_tree,
59
+ main_worktree_path,
60
+ merge_branch,
61
+ remove_worktree_force,
62
+ )
63
+ from .naming import compute_branch_name, compute_worktree_path
64
+ from .provision import (
65
+ WorktreeProvision,
66
+ provision_stage_worktree,
67
+ provision_task_worktree,
68
+ )
69
+
70
+ __all__ = [
71
+ "MergeError",
72
+ "StageWorktreeDecision",
73
+ "WorktreeDecision",
74
+ "WorktreeProvision",
75
+ "_git",
76
+ "_resolve_commit_sha",
77
+ "compute_branch_name",
78
+ "compute_worktree_path",
79
+ "dirty_entries_excluding_okstra",
80
+ "is_ancestor",
81
+ "is_dirty_excluding_okstra",
82
+ "is_git_work_tree",
83
+ "main_worktree_path",
84
+ "merge_branch",
85
+ "nested_worktree_excludes",
86
+ "okstra_clean_gate_excludes",
87
+ "preview_worktree_decision",
88
+ "provision_stage_worktree",
89
+ "provision_task_worktree",
90
+ "remove_worktree_force",
91
+ "resolve_stage_worktree_decision",
92
+ ]
@@ -0,0 +1,89 @@
1
+ """okstra 기준으로 worktree 가 깨끗한지 판정한다.
2
+
3
+ `git status` 가 보는 더러움과 okstra 가 신경 쓰는 더러움은 다르다. okstra 는
4
+ 자기 산출물(`.okstra/`), 자기가 걸어 둔 sync 심링크, 자기가 만든 중첩 worktree
5
+ 를 제외하고 남는 변경만 사용자의 것으로 본다. 그 제외 목록을 만드는 곳이
6
+ 여기이고, 그래서 `sync_config`(무엇을 걸었나)와 `git_ops`(지금 무엇이 바뀌었나)
7
+ 양쪽을 쓴다.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ from .git_ops import _git
15
+ from .sync_config import (
16
+ _resolve_snapshot_files,
17
+ _resolve_sync_dirs,
18
+ _resolve_sync_files,
19
+ )
20
+
21
+
22
+ def okstra_clean_gate_excludes(project_root: Optional[Path] = None) -> tuple[str, ...]:
23
+ """Project-relative paths okstra owns and source clean gates should ignore."""
24
+ out: list[str] = []
25
+ seen: set[str] = set()
26
+ for rel in (
27
+ ".okstra",
28
+ *_resolve_sync_dirs(project_root),
29
+ *_resolve_sync_files(project_root),
30
+ *_resolve_snapshot_files(project_root),
31
+ ):
32
+ cleaned = rel.strip().removeprefix("./").rstrip("/")
33
+ if not cleaned or cleaned == "." or cleaned in seen:
34
+ continue
35
+ seen.add(cleaned)
36
+ out.append(cleaned)
37
+ return tuple(out)
38
+
39
+
40
+ def nested_worktree_excludes(worktree_path) -> tuple[str, ...]:
41
+ """Relative paths of git worktrees nested under `worktree_path`.
42
+
43
+ implementation stage worktrees live inside the task worktree
44
+ (`<task-id>/stage-<N>/`), so the parent's `git status --short` reports
45
+ each as an untracked `?? stage-N/`. They are git-registered worktrees,
46
+ not user source changes, so clean gates must ignore them. Derived from
47
+ `git worktree list` rather than a hardcoded pattern so it tracks exactly
48
+ what git registered.
49
+ """
50
+ root = Path(worktree_path).resolve()
51
+ r = _git(root, "worktree", "list", "--porcelain")
52
+ if r.returncode != 0:
53
+ return ()
54
+ out: list[str] = []
55
+ for line in r.stdout.splitlines():
56
+ if not line.startswith("worktree "):
57
+ continue
58
+ wt = Path(line[len("worktree "):].strip()).resolve()
59
+ if wt == root:
60
+ continue
61
+ try:
62
+ rel = wt.relative_to(root)
63
+ except ValueError:
64
+ continue
65
+ out.append(str(rel))
66
+ return tuple(out)
67
+
68
+
69
+ def dirty_entries_excluding_okstra(cwd) -> list[str]:
70
+ """`git status --short` rows for changes outside okstra-owned paths.
71
+
72
+ okstra-owned = `okstra_clean_gate_excludes` (e.g. `.okstra`, synced dirs)
73
+ plus `nested_worktree_excludes` (stage worktrees nested under `cwd`).
74
+
75
+ This is the clean-worktree question every okstra gate asks, and the one a
76
+ plan step must ask through `okstra worktree-status` rather than through a
77
+ bare `git status --porcelain`: okstra provisions `.okstra` plus the synced
78
+ entries into every task worktree, so a bare status is never empty there and
79
+ an assertion built on it fails on okstra's own scaffolding.
80
+ """
81
+ owned = (*okstra_clean_gate_excludes(Path(cwd)), *nested_worktree_excludes(cwd))
82
+ excludes = [f":(exclude){p}" for p in owned]
83
+ out = _git(Path(cwd), "status", "--short", "--", ".", *excludes).stdout
84
+ return [line for line in out.splitlines() if line.strip()]
85
+
86
+
87
+ def is_dirty_excluding_okstra(cwd) -> bool:
88
+ """True iff the worktree has changes outside okstra-owned paths."""
89
+ return bool(dirty_entries_excluding_okstra(cwd))
@@ -0,0 +1,127 @@
1
+ """provision 하기 전에 무엇이 일어날지 답한다.
2
+
3
+ 위저드는 사용자에게 경로와 브랜치를 보여 준 뒤 확인을 받는다. 그 시점에 이미
4
+ worktree 를 만들어 버리면 안 되므로, 판단(재사용인가 신규인가, 어느 경로인가)만
5
+ 떼어 여기에 둔다. 레지스트리를 읽기는 하지만 예약하지는 않는다 — 예약은
6
+ `provision` 의 몫이다.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+
13
+ from .. import worktree_registry
14
+ from .git_ops import _is_inside_non_main_worktree, is_git_work_tree
15
+ from .naming import _safe_segment, compute_branch_name, compute_worktree_path
16
+
17
+
18
+ @dataclass
19
+ class WorktreeDecision:
20
+ """Side-effect-free preview of what `provision_task_worktree` would do.
21
+
22
+ status:
23
+ - "new": no active registry entry; a fresh worktree would be created
24
+ - "reused": registry already has this task-key; existing path/branch returned
25
+ - "skipped-in-worktree": project_root is itself a non-main worktree
26
+ - "skipped-not-git": project_root has no .git
27
+ """
28
+ status: str
29
+ path: str # worktree path (new: prospective; reuse: existing; skip: project_root)
30
+ branch: str = "" # new: prospective branch; reused: existing branch
31
+ base_ref: str = "" # new: requested base_ref; reused: existing base
32
+
33
+
34
+ def preview_worktree_decision(
35
+ *,
36
+ project_root,
37
+ project_id: str,
38
+ task_group_segment: str,
39
+ task_id_segment: str,
40
+ work_category: str,
41
+ base_ref: str = "",
42
+ ) -> "WorktreeDecision":
43
+ """Side-effect-free: what provision_task_worktree WOULD do, without touching disk.
44
+
45
+ Mirrors provision's decision branches exactly; reuses the same read-only
46
+ helpers so preview never diverges from the actual provisioning result.
47
+ """
48
+ project_root = Path(project_root)
49
+ if not is_git_work_tree(project_root):
50
+ return WorktreeDecision(status="skipped-not-git", path=str(project_root))
51
+ if _is_inside_non_main_worktree(project_root):
52
+ return WorktreeDecision(status="skipped-in-worktree", path=str(project_root))
53
+ safe_project = _safe_segment(project_id)
54
+ safe_group = _safe_segment(task_group_segment)
55
+ safe_task = _safe_segment(task_id_segment)
56
+ existing = worktree_registry.lookup(safe_project, safe_group, safe_task)
57
+ if existing is not None and existing.status == "active":
58
+ return WorktreeDecision(
59
+ status="reused", path=existing.worktree_path,
60
+ branch=existing.branch, base_ref=existing.base_ref,
61
+ )
62
+ return WorktreeDecision(
63
+ status="new",
64
+ path=str(compute_worktree_path(
65
+ project_id=safe_project, task_group_segment=safe_group,
66
+ task_id_segment=safe_task)),
67
+ branch=compute_branch_name(work_category=work_category, task_id_segment=safe_task),
68
+ base_ref=base_ref,
69
+ )
70
+
71
+
72
+ @dataclass
73
+ class StageWorktreeDecision:
74
+ """Side-effect-free decision for one concrete implementation stage."""
75
+
76
+ status: str
77
+ path: str
78
+ branch: str = ""
79
+ base_ref: str = ""
80
+
81
+
82
+ def resolve_stage_worktree_decision(
83
+ *,
84
+ project_id: str,
85
+ task_group_segment: str,
86
+ task_id_segment: str,
87
+ work_category: str,
88
+ stage_number: int,
89
+ ) -> StageWorktreeDecision:
90
+ """Resolve whether one concrete stage worktree is new or reusable."""
91
+ safe_project = _safe_segment(project_id)
92
+ safe_group = _safe_segment(task_group_segment)
93
+ safe_task = _safe_segment(task_id_segment)
94
+ existing = worktree_registry.lookup(
95
+ safe_project, safe_group, safe_task, stage_number=stage_number)
96
+ if existing is not None and _stage_entry_is_reusable(existing):
97
+ return StageWorktreeDecision(
98
+ status="reused",
99
+ path=existing.worktree_path,
100
+ branch=existing.branch,
101
+ base_ref=existing.base_ref,
102
+ )
103
+ return StageWorktreeDecision(
104
+ status="new",
105
+ path=str(compute_worktree_path(
106
+ project_id=safe_project, task_group_segment=safe_group,
107
+ task_id_segment=safe_task, stage_number=stage_number)),
108
+ branch=compute_branch_name(
109
+ work_category=work_category, task_id_segment=safe_task,
110
+ stage_number=stage_number),
111
+ )
112
+
113
+
114
+ def _stage_entry_is_reusable(entry: worktree_registry.WorktreeEntry) -> bool:
115
+ """Whether a registered stage worktree can be entered by this run.
116
+
117
+ `active` is the live-run case. `released` with the directory still on disk is
118
+ the fix-run case: a stage whose verifier returned FAIL records a `failed`
119
+ consumers row, which frees the occupancy but deliberately keeps the worktree
120
+ and its branch as the reviewable stack. Re-entry MUST reuse that tree —
121
+ provisioning anew refuses on the existing path and branch. After whole-task
122
+ final-verification removes the directory the entry stops being reusable, so
123
+ the stage provisions from scratch.
124
+ """
125
+ if entry.status == "active":
126
+ return True
127
+ return entry.status == "released" and Path(entry.worktree_path).is_dir()