okstra 0.173.0 → 0.175.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. package/README.md +18 -9
  2. package/bin/okstra +8 -4
  3. package/dist/cli-registry.d.mts +16 -0
  4. package/dist/cli-registry.mjs +560 -0
  5. package/dist/cli-registry.mjs.map +1 -0
  6. package/dist/commands/execute/agent-prompt.d.mts +1 -0
  7. package/{src → dist}/commands/execute/agent-prompt.mjs +10 -11
  8. package/dist/commands/execute/agent-prompt.mjs.map +1 -0
  9. package/dist/commands/execute/codex-dispatch.d.mts +3 -0
  10. package/dist/commands/execute/codex-dispatch.mjs +6 -0
  11. package/dist/commands/execute/codex-dispatch.mjs.map +1 -0
  12. package/dist/commands/execute/codex-run.d.mts +3 -0
  13. package/{src → dist}/commands/execute/codex-run.mjs +30 -37
  14. package/dist/commands/execute/codex-run.mjs.map +1 -0
  15. package/dist/commands/execute/convergence.d.mts +1 -0
  16. package/{src → dist}/commands/execute/convergence.mjs +15 -16
  17. package/dist/commands/execute/convergence.mjs.map +1 -0
  18. package/dist/commands/execute/error-log.d.mts +1 -0
  19. package/{src → dist}/commands/execute/error-log.mjs +2 -3
  20. package/dist/commands/execute/error-log.mjs.map +1 -0
  21. package/dist/commands/execute/git-reconcile.d.mts +1 -0
  22. package/{src → dist}/commands/execute/git-reconcile.mjs +10 -11
  23. package/dist/commands/execute/git-reconcile.mjs.map +1 -0
  24. package/dist/commands/execute/handoff.d.mts +1 -0
  25. package/{src → dist}/commands/execute/handoff.mjs +10 -11
  26. package/dist/commands/execute/handoff.mjs.map +1 -0
  27. package/dist/commands/execute/incremental-carry.d.mts +1 -0
  28. package/{src → dist}/commands/execute/incremental-carry.mjs +2 -3
  29. package/dist/commands/execute/incremental-carry.mjs.map +1 -0
  30. package/dist/commands/execute/incremental-scope.d.mts +1 -0
  31. package/{src → dist}/commands/execute/incremental-scope.mjs +2 -3
  32. package/dist/commands/execute/incremental-scope.mjs.map +1 -0
  33. package/dist/commands/execute/integrate-stages.d.mts +1 -0
  34. package/{src → dist}/commands/execute/integrate-stages.mjs +10 -11
  35. package/dist/commands/execute/integrate-stages.mjs.map +1 -0
  36. package/dist/commands/execute/pane-title.d.mts +1 -0
  37. package/dist/commands/execute/pane-title.mjs +20 -0
  38. package/dist/commands/execute/pane-title.mjs.map +1 -0
  39. package/dist/commands/execute/plan-items.d.mts +1 -0
  40. package/dist/commands/execute/plan-items.mjs +9 -0
  41. package/dist/commands/execute/plan-items.mjs.map +1 -0
  42. package/dist/commands/execute/plan-validate.d.mts +1 -0
  43. package/dist/commands/execute/plan-validate.mjs +68 -0
  44. package/dist/commands/execute/plan-validate.mjs.map +1 -0
  45. package/dist/commands/execute/plan-verify.d.mts +1 -0
  46. package/{src → dist}/commands/execute/plan-verify.mjs +13 -16
  47. package/dist/commands/execute/plan-verify.mjs.map +1 -0
  48. package/dist/commands/execute/render-bundle.d.mts +3 -0
  49. package/{src → dist}/commands/execute/render-bundle.mjs +65 -61
  50. package/dist/commands/execute/render-bundle.mjs.map +1 -0
  51. package/dist/commands/execute/run.d.mts +24 -0
  52. package/dist/commands/execute/run.mjs +196 -0
  53. package/dist/commands/execute/run.mjs.map +1 -0
  54. package/dist/commands/execute/spawn-followups.d.mts +1 -0
  55. package/{src → dist}/commands/execute/spawn-followups.mjs +6 -7
  56. package/dist/commands/execute/spawn-followups.mjs.map +1 -0
  57. package/dist/commands/execute/team.d.mts +3 -0
  58. package/dist/commands/execute/team.mjs +56 -0
  59. package/dist/commands/execute/team.mjs.map +1 -0
  60. package/dist/commands/execute/token-usage.d.mts +1 -0
  61. package/{src → dist}/commands/execute/token-usage.mjs +2 -3
  62. package/dist/commands/execute/token-usage.mjs.map +1 -0
  63. package/dist/commands/execute/wizard.d.mts +8 -0
  64. package/dist/commands/execute/wizard.mjs +191 -0
  65. package/dist/commands/execute/wizard.mjs.map +1 -0
  66. package/dist/commands/execute/worker-audit-check.d.mts +1 -0
  67. package/{src → dist}/commands/execute/worker-audit-check.mjs +11 -12
  68. package/dist/commands/execute/worker-audit-check.mjs.map +1 -0
  69. package/dist/commands/execute/worker-dispatch.d.mts +7 -0
  70. package/{src → dist}/commands/execute/worker-dispatch.mjs +34 -46
  71. package/dist/commands/execute/worker-dispatch.mjs.map +1 -0
  72. package/dist/commands/execute/worker-state.d.mts +1 -0
  73. package/{src → dist}/commands/execute/worker-state.mjs +15 -16
  74. package/dist/commands/execute/worker-state.mjs.map +1 -0
  75. package/dist/commands/execute/worktree-lookup.d.mts +1 -0
  76. package/{src → dist}/commands/execute/worktree-lookup.mjs +38 -37
  77. package/dist/commands/execute/worktree-lookup.mjs.map +1 -0
  78. package/dist/commands/execute/worktree-status.d.mts +1 -0
  79. package/dist/commands/execute/worktree-status.mjs +115 -0
  80. package/dist/commands/execute/worktree-status.mjs.map +1 -0
  81. package/dist/commands/inspect/code-review.d.mts +1 -0
  82. package/{src → dist}/commands/inspect/code-review.mjs +16 -19
  83. package/dist/commands/inspect/code-review.mjs.map +1 -0
  84. package/dist/commands/inspect/container.d.mts +1 -0
  85. package/{src → dist}/commands/inspect/container.mjs +11 -13
  86. package/dist/commands/inspect/container.mjs.map +1 -0
  87. package/dist/commands/inspect/context-cost.d.mts +1 -0
  88. package/{src → dist}/commands/inspect/context-cost.mjs +11 -13
  89. package/dist/commands/inspect/context-cost.mjs.map +1 -0
  90. package/dist/commands/inspect/design-prep.d.mts +1 -0
  91. package/{src → dist}/commands/inspect/design-prep.mjs +10 -11
  92. package/dist/commands/inspect/design-prep.mjs.map +1 -0
  93. package/dist/commands/inspect/error-report.d.mts +1 -0
  94. package/{src → dist}/commands/inspect/error-report.mjs +11 -13
  95. package/dist/commands/inspect/error-report.mjs.map +1 -0
  96. package/dist/commands/inspect/error-zip.d.mts +1 -0
  97. package/{src → dist}/commands/inspect/error-zip.mjs +11 -12
  98. package/dist/commands/inspect/error-zip.mjs.map +1 -0
  99. package/dist/commands/inspect/log-report.d.mts +1 -0
  100. package/{src → dist}/commands/inspect/log-report.mjs +11 -13
  101. package/dist/commands/inspect/log-report.mjs.map +1 -0
  102. package/dist/commands/inspect/profile-show.d.mts +1 -0
  103. package/{src → dist}/commands/inspect/profile-show.mjs +10 -11
  104. package/dist/commands/inspect/profile-show.mjs.map +1 -0
  105. package/dist/commands/inspect/recap.d.mts +1 -0
  106. package/{src → dist}/commands/inspect/recap.mjs +11 -13
  107. package/dist/commands/inspect/recap.mjs.map +1 -0
  108. package/dist/commands/inspect/resolve-task-key.d.mts +1 -0
  109. package/{src → dist}/commands/inspect/resolve-task-key.mjs +11 -13
  110. package/dist/commands/inspect/resolve-task-key.mjs.map +1 -0
  111. package/dist/commands/inspect/rollup.d.mts +1 -0
  112. package/{src → dist}/commands/inspect/rollup.mjs +11 -13
  113. package/dist/commands/inspect/rollup.mjs.map +1 -0
  114. package/dist/commands/inspect/run-audit.d.mts +1 -0
  115. package/{src → dist}/commands/inspect/run-audit.mjs +11 -12
  116. package/dist/commands/inspect/run-audit.mjs.map +1 -0
  117. package/dist/commands/inspect/set-work-status.d.mts +1 -0
  118. package/{src → dist}/commands/inspect/set-work-status.mjs +11 -13
  119. package/dist/commands/inspect/set-work-status.mjs.map +1 -0
  120. package/dist/commands/inspect/stage-map.d.mts +1 -0
  121. package/dist/commands/inspect/stage-map.mjs +110 -0
  122. package/dist/commands/inspect/stage-map.mjs.map +1 -0
  123. package/dist/commands/inspect/task-list.d.mts +1 -0
  124. package/dist/commands/inspect/task-list.mjs +103 -0
  125. package/dist/commands/inspect/task-list.mjs.map +1 -0
  126. package/dist/commands/inspect/task-show.d.mts +1 -0
  127. package/dist/commands/inspect/task-show.mjs +108 -0
  128. package/dist/commands/inspect/task-show.mjs.map +1 -0
  129. package/dist/commands/inspect/time-report.d.mts +1 -0
  130. package/{src → dist}/commands/inspect/time-report.mjs +11 -13
  131. package/dist/commands/inspect/time-report.mjs.map +1 -0
  132. package/dist/commands/inspect/usage-report.d.mts +1 -0
  133. package/{src → dist}/commands/inspect/usage-report.mjs +11 -13
  134. package/dist/commands/inspect/usage-report.mjs.map +1 -0
  135. package/dist/commands/inspect/user-response.d.mts +1 -0
  136. package/{src → dist}/commands/inspect/user-response.mjs +10 -11
  137. package/dist/commands/inspect/user-response.mjs.map +1 -0
  138. package/dist/commands/inspect/worker-liveness.d.mts +1 -0
  139. package/{src → dist}/commands/inspect/worker-liveness.mjs +11 -13
  140. package/dist/commands/inspect/worker-liveness.mjs.map +1 -0
  141. package/dist/commands/lifecycle/check-project.d.mts +21 -0
  142. package/dist/commands/lifecycle/check-project.mjs +175 -0
  143. package/dist/commands/lifecycle/check-project.mjs.map +1 -0
  144. package/dist/commands/lifecycle/config.d.mts +1 -0
  145. package/dist/commands/lifecycle/config.mjs +369 -0
  146. package/dist/commands/lifecycle/config.mjs.map +1 -0
  147. package/dist/commands/lifecycle/doctor.d.mts +10 -0
  148. package/dist/commands/lifecycle/doctor.mjs +317 -0
  149. package/dist/commands/lifecycle/doctor.mjs.map +1 -0
  150. package/dist/commands/lifecycle/install.d.mts +17 -0
  151. package/dist/commands/lifecycle/install.mjs +1077 -0
  152. package/dist/commands/lifecycle/install.mjs.map +1 -0
  153. package/dist/commands/lifecycle/migrate.d.mts +1 -0
  154. package/{src → dist}/commands/lifecycle/migrate.mjs +10 -11
  155. package/dist/commands/lifecycle/migrate.mjs.map +1 -0
  156. package/dist/commands/lifecycle/model.d.mts +1 -0
  157. package/dist/commands/lifecycle/model.mjs +22 -0
  158. package/dist/commands/lifecycle/model.mjs.map +1 -0
  159. package/dist/commands/lifecycle/paths.d.mts +1 -0
  160. package/dist/commands/lifecycle/paths.mjs +102 -0
  161. package/dist/commands/lifecycle/paths.mjs.map +1 -0
  162. package/dist/commands/lifecycle/preflight.d.mts +1 -0
  163. package/dist/commands/lifecycle/preflight.mjs +124 -0
  164. package/dist/commands/lifecycle/preflight.mjs.map +1 -0
  165. package/dist/commands/lifecycle/setup.d.mts +1 -0
  166. package/dist/commands/lifecycle/setup.mjs +297 -0
  167. package/dist/commands/lifecycle/setup.mjs.map +1 -0
  168. package/dist/commands/lifecycle/uninstall.d.mts +4 -0
  169. package/dist/commands/lifecycle/uninstall.mjs +262 -0
  170. package/dist/commands/lifecycle/uninstall.mjs.map +1 -0
  171. package/dist/commands/manager.d.mts +3 -0
  172. package/{src → dist}/commands/manager.mjs +25 -32
  173. package/dist/commands/manager.mjs.map +1 -0
  174. package/dist/commands/memory/memory.d.mts +1 -0
  175. package/dist/commands/memory/memory.mjs +569 -0
  176. package/dist/commands/memory/memory.mjs.map +1 -0
  177. package/dist/commands/pr/pr.d.mts +33 -0
  178. package/dist/commands/pr/pr.mjs +296 -0
  179. package/dist/commands/pr/pr.mjs.map +1 -0
  180. package/dist/commands/report/agent-activity.d.mts +1 -0
  181. package/{src → dist}/commands/report/agent-activity.mjs +11 -12
  182. package/dist/commands/report/agent-activity.mjs.map +1 -0
  183. package/dist/commands/report/finalize.d.mts +4 -0
  184. package/dist/commands/report/finalize.mjs +64 -0
  185. package/dist/commands/report/finalize.mjs.map +1 -0
  186. package/dist/commands/report/inject-report-index.d.mts +1 -0
  187. package/{src → dist}/commands/report/inject-report-index.mjs +6 -7
  188. package/dist/commands/report/inject-report-index.mjs.map +1 -0
  189. package/dist/commands/report/render-final-report.d.mts +1 -0
  190. package/{src → dist}/commands/report/render-final-report.mjs +6 -7
  191. package/dist/commands/report/render-final-report.mjs.map +1 -0
  192. package/dist/commands/report/render-views.d.mts +1 -0
  193. package/{src → dist}/commands/report/render-views.mjs +6 -7
  194. package/dist/commands/report/render-views.mjs.map +1 -0
  195. package/dist/commands/report/translate.d.mts +1 -0
  196. package/{src → dist}/commands/report/translate.mjs +6 -7
  197. package/dist/commands/report/translate.mjs.map +1 -0
  198. package/dist/lib/helper-scripts.d.mts +3 -0
  199. package/{src → dist}/lib/helper-scripts.mjs +11 -14
  200. package/dist/lib/helper-scripts.mjs.map +1 -0
  201. package/dist/lib/host-registry-client.d.mts +25 -0
  202. package/dist/lib/host-registry-client.mjs +152 -0
  203. package/dist/lib/host-registry-client.mjs.map +1 -0
  204. package/dist/lib/install-assets.d.mts +8 -0
  205. package/{src → dist}/lib/install-assets.mjs +17 -20
  206. package/dist/lib/install-assets.mjs.map +1 -0
  207. package/dist/lib/okstra-dirs.d.mts +4 -0
  208. package/{src → dist}/lib/okstra-dirs.mjs +3 -6
  209. package/dist/lib/okstra-dirs.mjs.map +1 -0
  210. package/dist/lib/paths.d.mts +4 -0
  211. package/dist/lib/paths.mjs +78 -0
  212. package/dist/lib/paths.mjs.map +1 -0
  213. package/dist/lib/proc.d.mts +3 -0
  214. package/dist/lib/proc.mjs +31 -0
  215. package/dist/lib/proc.mjs.map +1 -0
  216. package/dist/lib/python-helper.d.mts +8 -0
  217. package/dist/lib/python-helper.mjs +123 -0
  218. package/dist/lib/python-helper.mjs.map +1 -0
  219. package/dist/lib/runtime-manifest.d.mts +7 -0
  220. package/dist/lib/runtime-manifest.mjs +73 -0
  221. package/dist/lib/runtime-manifest.mjs.map +1 -0
  222. package/dist/lib/skill-catalog.d.mts +4 -0
  223. package/dist/lib/skill-catalog.mjs +50 -0
  224. package/dist/lib/skill-catalog.mjs.map +1 -0
  225. package/dist/lib/types.d.mts +129 -0
  226. package/dist/lib/types.mjs +2 -0
  227. package/dist/lib/types.mjs.map +1 -0
  228. package/dist/lib/version.d.mts +2 -0
  229. package/dist/lib/version.mjs +27 -0
  230. package/dist/lib/version.mjs.map +1 -0
  231. package/docs/architecture/storage-model.md +17 -3
  232. package/docs/architecture.md +12 -22
  233. package/docs/cli.md +29 -8
  234. package/docs/container.md +1 -1
  235. package/docs/contributor-change-matrix.md +3 -3
  236. package/docs/project-structure-overview.md +29 -20
  237. package/docs/task-process/README.md +3 -1
  238. package/docs/task-process/implementation-planning.md +2 -2
  239. package/docs/task-process/implementation.md +1 -1
  240. package/package.json +11 -5
  241. package/runtime/BUILD.json +2 -2
  242. package/runtime/agents/workers/claude-worker.md +3 -2
  243. package/runtime/agents/workers/report-writer-worker.md +3 -1
  244. package/runtime/agents/workers/translator-worker.md +3 -1
  245. package/runtime/bin/lib/okstra/cli.sh +12 -0
  246. package/runtime/bin/lib/okstra/globals.sh +4 -1
  247. package/runtime/bin/lib/okstra/usage.sh +4 -4
  248. package/runtime/bin/okstra-error-log.py +27 -0
  249. package/runtime/bin/okstra-provider-exec.py +245 -16
  250. package/runtime/bin/okstra-trace-cleanup.sh +58 -129
  251. package/runtime/bin/okstra.sh +10 -0
  252. package/runtime/prompts/lead/adapters/cmux.md +2 -0
  253. package/runtime/prompts/lead/convergence.md +27 -18
  254. package/runtime/prompts/lead/okstra-lead-contract.md +4 -2
  255. package/runtime/prompts/lead/plan-body-verification.md +13 -6
  256. package/runtime/prompts/lead/report-writer.md +6 -6
  257. package/runtime/prompts/lead/team-contract.md +1 -1
  258. package/runtime/prompts/profiles/_common-contract.md +2 -2
  259. package/runtime/prompts/profiles/_implementation-executor.md +2 -0
  260. package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
  261. package/runtime/prompts/profiles/change-impact-analysis.md +16 -0
  262. package/runtime/prompts/profiles/error-analysis.md +21 -1
  263. package/runtime/prompts/profiles/feature-analysis.md +16 -0
  264. package/runtime/prompts/profiles/final-verification.md +16 -0
  265. package/runtime/prompts/profiles/implementation-option-selection.md +16 -0
  266. package/runtime/prompts/profiles/implementation-planning.md +32 -9
  267. package/runtime/prompts/profiles/implementation.md +17 -1
  268. package/runtime/prompts/profiles/improvement-discovery.md +16 -0
  269. package/runtime/prompts/profiles/project-analysis.md +16 -0
  270. package/runtime/prompts/profiles/release-handoff.md +5 -1
  271. package/runtime/prompts/profiles/requirements-discovery.md +20 -0
  272. package/runtime/prompts/wizard/prompts.ko.json +18 -0
  273. package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
  274. package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +8 -0
  275. package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +8 -0
  276. package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
  277. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +2 -2
  278. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +25 -5
  279. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  280. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +4 -1
  281. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
  282. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  283. package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +2 -2
  284. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +79 -11
  285. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +100 -25
  286. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +59 -14
  287. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +55 -9
  288. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +51 -12
  289. package/runtime/python/okstra_ctl/agent_activity.py +5 -1
  290. package/runtime/python/okstra_ctl/agent_invocation.py +608 -14
  291. package/runtime/python/okstra_ctl/agent_prompt_cli.py +249 -50
  292. package/runtime/python/okstra_ctl/application/resolve_assignment.py +53 -12
  293. package/runtime/python/okstra_ctl/assignment_environment.py +102 -0
  294. package/runtime/python/okstra_ctl/assignment_resolver.py +790 -0
  295. package/runtime/python/okstra_ctl/attempt_evidence.py +342 -0
  296. package/runtime/python/okstra_ctl/conformance.py +68 -0
  297. package/runtime/python/okstra_ctl/convergence.py +409 -3
  298. package/runtime/python/okstra_ctl/convergence_engine.py +248 -15
  299. package/runtime/python/okstra_ctl/convergence_store.py +170 -1
  300. package/runtime/python/okstra_ctl/dispatch_core.py +1181 -102
  301. package/runtime/python/okstra_ctl/dispatch_state.py +835 -22
  302. package/runtime/python/okstra_ctl/doctor.py +16 -2
  303. package/runtime/python/okstra_ctl/domain/host.py +32 -1
  304. package/runtime/python/okstra_ctl/domain/provider.py +212 -6
  305. package/runtime/python/okstra_ctl/domain/role.py +125 -0
  306. package/runtime/python/okstra_ctl/domain/wizard/interaction.py +13 -0
  307. package/runtime/python/okstra_ctl/domain/worker_exec.py +39 -1
  308. package/runtime/python/okstra_ctl/domain/worker_role.py +13 -3
  309. package/runtime/python/okstra_ctl/domain/worker_stream.py +24 -0
  310. package/runtime/python/okstra_ctl/error_log_write.py +9 -0
  311. package/runtime/python/okstra_ctl/error_report.py +21 -1
  312. package/runtime/python/okstra_ctl/execution_identity.py +529 -0
  313. package/runtime/python/okstra_ctl/execution_manifest.py +1281 -0
  314. package/runtime/python/okstra_ctl/execution_mutation_audit.py +619 -0
  315. package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
  316. package/runtime/python/okstra_ctl/index.py +6 -2
  317. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +43 -3
  318. package/runtime/python/okstra_ctl/legacy_model_selection.py +490 -0
  319. package/runtime/python/okstra_ctl/model_cli.py +278 -0
  320. package/runtime/python/okstra_ctl/model_defaults.py +46 -0
  321. package/runtime/python/okstra_ctl/model_discovery.py +26 -7
  322. package/runtime/python/okstra_ctl/model_pool.py +501 -0
  323. package/runtime/python/okstra_ctl/models.py +47 -27
  324. package/runtime/python/okstra_ctl/mutation_recovery.py +293 -0
  325. package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
  326. package/runtime/python/okstra_ctl/pane_title.py +151 -0
  327. package/runtime/python/okstra_ctl/ports/host_model.py +48 -17
  328. package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
  329. package/runtime/python/okstra_ctl/registry/provider_registry.py +8 -4
  330. package/runtime/python/okstra_ctl/render.py +78 -10
  331. package/runtime/python/okstra_ctl/render_final_report.py +34 -19
  332. package/runtime/python/okstra_ctl/report_contract.py +39 -1
  333. package/runtime/python/okstra_ctl/report_finalize.py +84 -9
  334. package/runtime/python/okstra_ctl/report_html/render.py +1 -0
  335. package/runtime/python/okstra_ctl/role_requirements.py +217 -0
  336. package/runtime/python/okstra_ctl/run.py +1250 -260
  337. package/runtime/python/okstra_ctl/run_index_row.py +7 -1
  338. package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
  339. package/runtime/python/okstra_ctl/scope_provenance.py +1 -1
  340. package/runtime/python/okstra_ctl/session.py +69 -12
  341. package/runtime/python/okstra_ctl/team.py +51 -25
  342. package/runtime/python/okstra_ctl/time_report.py +38 -4
  343. package/runtime/python/okstra_ctl/tmux.py +19 -149
  344. package/runtime/python/okstra_ctl/usage_identity.py +86 -0
  345. package/runtime/python/okstra_ctl/usage_report.py +15 -3
  346. package/runtime/python/okstra_ctl/wizard.py +1253 -62
  347. package/runtime/python/okstra_ctl/worker_audit_ledger.py +68 -65
  348. package/runtime/python/okstra_ctl/worker_request.py +9 -1
  349. package/runtime/python/okstra_ctl/worker_runner.py +130 -5
  350. package/runtime/python/okstra_ctl/workers.py +7 -5
  351. package/runtime/python/okstra_ctl/worktree.py +69 -3
  352. package/runtime/python/okstra_ctl/write_policy.py +571 -0
  353. package/runtime/python/okstra_token_usage/cli.py +1 -1
  354. package/runtime/python/okstra_token_usage/collect.py +66 -6
  355. package/runtime/schemas/convergence-groups-v2.0.schema.json +128 -0
  356. package/runtime/schemas/execution-manifest-v2.schema.json +340 -0
  357. package/runtime/schemas/final-report-v2.0.schema.json +25 -0
  358. package/runtime/skills/okstra-run/SKILL.md +9 -9
  359. package/runtime/skills/okstra-setup/references/project-config.md +11 -0
  360. package/runtime/templates/report-writer-prompt-preamble.md +2 -0
  361. package/runtime/templates/reports/final-report-v2.template.md +7 -1
  362. package/runtime/templates/reports/html/base.template.html +21 -0
  363. package/runtime/templates/reports/html/i18n/en.json +4 -0
  364. package/runtime/templates/reports/html/i18n/ko.json +4 -0
  365. package/runtime/templates/reports/settings.template.json +0 -24
  366. package/runtime/templates/worker-prompt-preamble.md +6 -2
  367. package/runtime/validators/lib/fixtures.sh +98 -33
  368. package/runtime/validators/validate-implementation-plan-stages.py +63 -3
  369. package/runtime/validators/validate-run.py +443 -508
  370. package/runtime/validators/validate_session_conformance.py +26 -8
  371. package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
  372. package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
  373. package/runtime/templates/reports/final-report.template.md +0 -1258
  374. package/src/cli-registry.mjs +0 -532
  375. package/src/commands/execute/codex-dispatch.mjs +0 -10
  376. package/src/commands/execute/plan-items.mjs +0 -9
  377. package/src/commands/execute/plan-validate.mjs +0 -68
  378. package/src/commands/execute/run.mjs +0 -208
  379. package/src/commands/execute/team.mjs +0 -63
  380. package/src/commands/execute/wizard.mjs +0 -167
  381. package/src/commands/inspect/stage-map.mjs +0 -98
  382. package/src/commands/inspect/task-list.mjs +0 -97
  383. package/src/commands/inspect/task-show.mjs +0 -100
  384. package/src/commands/lifecycle/check-project.mjs +0 -187
  385. package/src/commands/lifecycle/config.mjs +0 -385
  386. package/src/commands/lifecycle/doctor.mjs +0 -308
  387. package/src/commands/lifecycle/install.mjs +0 -1172
  388. package/src/commands/lifecycle/paths.mjs +0 -104
  389. package/src/commands/lifecycle/preflight.mjs +0 -125
  390. package/src/commands/lifecycle/setup.mjs +0 -333
  391. package/src/commands/lifecycle/uninstall.mjs +0 -279
  392. package/src/commands/memory/memory.mjs +0 -524
  393. package/src/commands/pr/pr.mjs +0 -261
  394. package/src/commands/report/finalize.mjs +0 -64
  395. package/src/lib/host-registry-client.mjs +0 -176
  396. package/src/lib/paths.mjs +0 -86
  397. package/src/lib/proc.mjs +0 -31
  398. package/src/lib/python-helper.mjs +0 -142
  399. package/src/lib/runtime-manifest.mjs +0 -70
  400. package/src/lib/skill-catalog.mjs +0 -53
  401. package/src/lib/version.mjs +0 -20
  402. /package/{src → dist}/commands/pr/default.md +0 -0
@@ -0,0 +1,342 @@
1
+ """Atomic attempt evidence seals and orchestrator-owned host event streams."""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ from dataclasses import dataclass
7
+ from datetime import datetime, timezone
8
+ from pathlib import Path
9
+ from typing import Any, Mapping, Sequence
10
+
11
+ from .domain.worker_stream import host_event_row
12
+ from .execution_identity import Attempt, ExecutionManifestError
13
+ from .execution_manifest import read_execution_manifest
14
+ from .execution_manifest import _replace_manifest, _write_manifest_over_existing
15
+ from .run_context import task_mutex
16
+
17
+
18
+ class AttemptEvidenceError(ValueError):
19
+ """Raised when an attempt cannot be sealed from the observed terminal state."""
20
+
21
+
22
+ def _sha(data: bytes) -> str:
23
+ return "sha256:" + hashlib.sha256(data).hexdigest()
24
+
25
+
26
+ def _utc_now() -> str:
27
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class TerminalObservation:
32
+ status: str
33
+ source_diff_digest: str
34
+ git_projection_digest: str
35
+ changed_paths: tuple[str, ...]
36
+ artifact_paths: tuple[Path, ...]
37
+ host_event_stream_path: Path
38
+ project_root: Path
39
+ worktree: Path | None
40
+ interrupted_before_record: bool = False
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class AttemptEvidenceSeal:
45
+ seal_ref: str
46
+ attempt_ref: str
47
+ captured_at: str
48
+ source_diff_digest: str
49
+ git_projection_digest: str
50
+ changed_paths: tuple[str, ...]
51
+ host_event_stream: Mapping[str, Any]
52
+ artifacts: tuple[Mapping[str, Any], ...]
53
+
54
+ def to_payload(self) -> dict[str, Any]:
55
+ return {
56
+ "sealRef": self.seal_ref,
57
+ "attemptRef": self.attempt_ref,
58
+ "capturedAt": self.captured_at,
59
+ "sourceDiffDigest": self.source_diff_digest,
60
+ "gitProjectionDigest": self.git_projection_digest,
61
+ "changedPaths": list(self.changed_paths),
62
+ "hostEventStream": dict(self.host_event_stream),
63
+ "hostEventStreamRef": self.host_event_stream.get("path"),
64
+ "artifacts": [dict(row) for row in self.artifacts],
65
+ }
66
+
67
+ @classmethod
68
+ def from_payload(cls, payload: Mapping[str, Any]) -> "AttemptEvidenceSeal":
69
+ stream = payload.get("hostEventStream")
70
+ if not isinstance(stream, Mapping):
71
+ stream = {}
72
+ return cls(
73
+ seal_ref=str(payload["sealRef"]),
74
+ attempt_ref=str(payload["attemptRef"]),
75
+ captured_at=str(payload["capturedAt"]),
76
+ source_diff_digest=str(payload["sourceDiffDigest"]),
77
+ git_projection_digest=str(payload["gitProjectionDigest"]),
78
+ changed_paths=tuple(payload.get("changedPaths") or ()),
79
+ host_event_stream=dict(stream),
80
+ artifacts=tuple(payload.get("artifacts") or ()),
81
+ )
82
+
83
+
84
+ class HostEventStreamWriter:
85
+ """Append-only JSONL writer for orchestrator-owned host events."""
86
+
87
+ def __init__(
88
+ self, path: Path, *, invocation_ref: str, attempt: int
89
+ ) -> None:
90
+ self.path = path
91
+ self.invocation_ref = invocation_ref
92
+ self.attempt = attempt
93
+ self._sequence = 0
94
+ path.parent.mkdir(parents=True, exist_ok=True)
95
+
96
+ def append(self, event_type: str, payload: Mapping[str, Any]) -> dict[str, Any]:
97
+ self._sequence += 1
98
+ row = host_event_row(
99
+ event_id=f"evt-{self._sequence:04d}",
100
+ sequence=self._sequence,
101
+ event_type=event_type,
102
+ invocation_ref=self.invocation_ref,
103
+ attempt=self.attempt,
104
+ payload=payload,
105
+ timestamp=_utc_now(),
106
+ )
107
+ with self.path.open("a", encoding="utf-8") as handle:
108
+ handle.write(json.dumps(row, ensure_ascii=False) + "\n")
109
+ return row
110
+
111
+
112
+ def _split_attempt_ref(attempt_ref: str) -> tuple[str, int]:
113
+ invocation_ref, sep, raw = attempt_ref.rpartition(":")
114
+ kind, dash, number = raw.partition("-")
115
+ if (
116
+ not sep
117
+ or kind != "attempt"
118
+ or dash != "-"
119
+ or not invocation_ref
120
+ or not number.isdigit()
121
+ ):
122
+ raise AttemptEvidenceError("attemptRef is malformed")
123
+ return invocation_ref, int(number)
124
+
125
+
126
+ def _artifact_rows(paths: Sequence[Path]) -> tuple[dict[str, Any], ...]:
127
+ rows: list[dict[str, Any]] = []
128
+ for path in paths:
129
+ if path.is_symlink():
130
+ raise AttemptEvidenceError("sealed artifact must not be a symlink")
131
+ data = path.read_bytes() if path.is_file() else b""
132
+ rows.append({
133
+ "kind": "artifact",
134
+ "normalizedPath": str(path),
135
+ "byteLength": len(data),
136
+ "sha256": _sha(data),
137
+ })
138
+ return tuple(rows)
139
+
140
+
141
+ def _stream_payload(path: Path) -> dict[str, Any]:
142
+ data = path.read_bytes() if path.is_file() else b""
143
+ sequences = []
144
+ event_ids = []
145
+ for line in data.decode("utf-8").splitlines():
146
+ if not line.strip():
147
+ continue
148
+ row = json.loads(line)
149
+ sequences.append(int(row["sequence"]))
150
+ event_ids.append(str(row["eventId"]))
151
+ low = sequences[0] if sequences else 0
152
+ high = sequences[-1] if sequences else 0
153
+ return {
154
+ "path": str(path),
155
+ "schemaVersion": "1.0",
156
+ "byteLength": len(data),
157
+ "sha256": _sha(data),
158
+ "sequenceRange": [low, high],
159
+ "eventIds": event_ids,
160
+ }
161
+
162
+
163
+ def _build_seal(
164
+ attempt_ref: str, terminal: TerminalObservation
165
+ ) -> AttemptEvidenceSeal:
166
+ stream = _stream_payload(terminal.host_event_stream_path)
167
+ captured = _utc_now()
168
+ digest = _sha(
169
+ json.dumps(
170
+ {
171
+ "attemptRef": attempt_ref,
172
+ "source": terminal.source_diff_digest,
173
+ "git": terminal.git_projection_digest,
174
+ "stream": stream["sha256"],
175
+ },
176
+ sort_keys=True,
177
+ separators=(",", ":"),
178
+ ).encode("utf-8")
179
+ )
180
+ return AttemptEvidenceSeal(
181
+ seal_ref=f"seal-{digest[7:23]}",
182
+ attempt_ref=attempt_ref,
183
+ captured_at=captured,
184
+ source_diff_digest=terminal.source_diff_digest,
185
+ git_projection_digest=terminal.git_projection_digest,
186
+ changed_paths=terminal.changed_paths,
187
+ host_event_stream=stream,
188
+ artifacts=_artifact_rows(terminal.artifact_paths),
189
+ )
190
+
191
+
192
+ def _same_observation(seal: AttemptEvidenceSeal, terminal: TerminalObservation) -> bool:
193
+ stream = _stream_payload(terminal.host_event_stream_path)
194
+ return (
195
+ seal.source_diff_digest == terminal.source_diff_digest
196
+ and seal.git_projection_digest == terminal.git_projection_digest
197
+ and seal.host_event_stream.get("sha256") == stream["sha256"]
198
+ )
199
+
200
+
201
+ def finalize_attempt(
202
+ manifest_path: Path,
203
+ attempt_ref: str,
204
+ terminal: TerminalObservation,
205
+ *,
206
+ task_key: str,
207
+ ) -> AttemptEvidenceSeal:
208
+ """Seal host events, artifacts, source diff and git projection atomically."""
209
+ invocation_ref, attempt_number = _split_attempt_ref(attempt_ref)
210
+ with task_mutex(task_key):
211
+ manifest = read_execution_manifest(manifest_path)
212
+ index = next(
213
+ (
214
+ offset
215
+ for offset, row in enumerate(manifest.attempts)
216
+ if row.invocation_ref == invocation_ref and row.attempt == attempt_number
217
+ ),
218
+ None,
219
+ )
220
+ if index is None:
221
+ raise AttemptEvidenceError("attempt to seal is unknown")
222
+ existing = manifest.attempts[index]
223
+ if terminal.interrupted_before_record:
224
+ closed = Attempt(
225
+ invocation_ref=existing.invocation_ref,
226
+ attempt=existing.attempt,
227
+ started_at=existing.started_at,
228
+ finished_at=_utc_now(),
229
+ status="evidence-unsealed",
230
+ result_path=existing.result_path,
231
+ error_path=existing.error_path,
232
+ change_summary=dict(existing.change_summary),
233
+ evidence_seal_ref=None,
234
+ )
235
+ attempts = list(manifest.attempts)
236
+ attempts[index] = closed
237
+ _write_manifest_over_existing(
238
+ manifest_path,
239
+ _replace_manifest(manifest, attempts=tuple(attempts)),
240
+ )
241
+ raise AttemptEvidenceError("attempt closed as evidence-unsealed")
242
+ seal = _build_seal(attempt_ref, terminal)
243
+ existing_seal = next(
244
+ (
245
+ AttemptEvidenceSeal.from_payload(row)
246
+ for row in manifest.attempt_evidence_seals
247
+ if row.get("attemptRef") == attempt_ref
248
+ ),
249
+ None,
250
+ )
251
+ if existing_seal is not None:
252
+ if not _same_observation(existing_seal, terminal):
253
+ raise AttemptEvidenceError("attempt already sealed with different observation")
254
+ return existing_seal
255
+ closed = Attempt(
256
+ invocation_ref=existing.invocation_ref,
257
+ attempt=existing.attempt,
258
+ started_at=existing.started_at,
259
+ finished_at=existing.finished_at or _utc_now(),
260
+ status=terminal.status,
261
+ result_path=existing.result_path,
262
+ error_path=existing.error_path,
263
+ change_summary=dict(existing.change_summary),
264
+ evidence_seal_ref=seal.seal_ref,
265
+ )
266
+ attempts = list(manifest.attempts)
267
+ attempts[index] = closed
268
+ updated = _replace_manifest(
269
+ manifest,
270
+ attempts=tuple(attempts),
271
+ attempt_evidence_seals=(
272
+ *manifest.attempt_evidence_seals,
273
+ seal.to_payload(),
274
+ ),
275
+ )
276
+ _write_manifest_over_existing(manifest_path, updated)
277
+ return seal
278
+
279
+
280
+ def reject_unsealed_attempt(
281
+ manifest_path: Path,
282
+ attempt_ref: str,
283
+ original_baseline_digest: str,
284
+ *,
285
+ task_key: str,
286
+ ):
287
+ """Close an unsealed attempt and record a cleanup-only reject."""
288
+ from .mutation_recovery import MutationRecoveryDecision
289
+
290
+ invocation_ref, attempt_number = _split_attempt_ref(attempt_ref)
291
+ with task_mutex(task_key):
292
+ manifest = read_execution_manifest(manifest_path)
293
+ index = next(
294
+ (
295
+ offset
296
+ for offset, row in enumerate(manifest.attempts)
297
+ if row.invocation_ref == invocation_ref and row.attempt == attempt_number
298
+ ),
299
+ None,
300
+ )
301
+ if index is None:
302
+ raise AttemptEvidenceError("attempt to reject is unknown")
303
+ existing = manifest.attempts[index]
304
+ closed = Attempt(
305
+ invocation_ref=existing.invocation_ref,
306
+ attempt=existing.attempt,
307
+ started_at=existing.started_at,
308
+ finished_at=existing.finished_at or _utc_now(),
309
+ status="evidence-unsealed",
310
+ result_path=existing.result_path,
311
+ error_path=existing.error_path,
312
+ change_summary=dict(existing.change_summary),
313
+ evidence_seal_ref=None,
314
+ )
315
+ decision = MutationRecoveryDecision(
316
+ recovery_ref=f"recovery-unsealed-{attempt_number}",
317
+ source_invocation_ref=invocation_ref,
318
+ source_attempt_ref=attempt_ref,
319
+ source_evidence_seal_ref=None,
320
+ original_baseline_digest=original_baseline_digest,
321
+ terminal_source_diff_digest="",
322
+ terminal_git_chain_digest="",
323
+ verifier_role_execution_ref=None,
324
+ verifier_invocation_ref=None,
325
+ recovered_evidence_refs=(),
326
+ recovered_out_of_plan_edits=(),
327
+ unmet_obligations=("evidence-unsealed",),
328
+ decision="reject",
329
+ reason="evidence-unsealed",
330
+ )
331
+ attempts = list(manifest.attempts)
332
+ attempts[index] = closed
333
+ updated = _replace_manifest(
334
+ manifest,
335
+ attempts=tuple(attempts),
336
+ mutation_recovery_decisions=(
337
+ *manifest.mutation_recovery_decisions,
338
+ decision.to_payload(),
339
+ ),
340
+ )
341
+ _write_manifest_over_existing(manifest_path, updated)
342
+ return decision
@@ -25,6 +25,74 @@ EXTERNAL_ADVISORY_CAPABILITIES: frozenset[str] = frozenset(
25
25
  )
26
26
 
27
27
 
28
+ # --- 계획 산출물의 stage conformance 선언 파싱 ---
29
+ #
30
+ # 정본 형식은 prompts/profiles/implementation-planning.md 의
31
+ # `Conformance tests: stage-<N> — <script> (requires=[db|io|http|external,...])`
32
+ # 이고, data.json 의 `conformanceTests` 는 그 줄에서 `stage-<N> — ` 접두사를 뗀
33
+ # 나머지다. 승인 경계(run.py `_validate_approved_plan`)와 구현 런 끝
34
+ # (validators/validate-run.py)이 이 한 규칙을 같이 읽는다 — 규칙을 두 곳에
35
+ # 복제하면 승인은 통과하는데 구현 런은 막히는 상태가 다시 만들어진다.
36
+ _CONFORMANCE_TESTS_RE = re.compile(
37
+ r"^(?P<script>\S+)\s+\(requires=\[(?P<requires>[^\]]+)\]\)$"
38
+ )
39
+
40
+
41
+ def normalize_conformance_script(script: str) -> str:
42
+ """`<task_root>/` 접두사를 뗀다. 선언과 매니페스트 entry 가 같은 스크립트를
43
+ 서로 다른 접두사로 적을 수 있어, 대조 전에 양쪽을 이 형태로 맞춘다."""
44
+ prefix = "<task_root>/"
45
+ return script[len(prefix):] if script.startswith(prefix) else script
46
+
47
+
48
+ def parse_conformance_tests(value: object) -> tuple[str, frozenset[str]] | None:
49
+ """stage 선언의 `<script> (requires=[cap,...])` 를 파싱. 형식이 아니면 None."""
50
+ if not isinstance(value, str):
51
+ return None
52
+ match = _CONFORMANCE_TESTS_RE.fullmatch(value.strip())
53
+ if match is None:
54
+ return None
55
+ script = normalize_conformance_script(match.group("script"))
56
+ capabilities = [part.strip() for part in match.group("requires").split(",")]
57
+ if (
58
+ not script
59
+ or any(not capability for capability in capabilities)
60
+ or len(set(capabilities)) != len(capabilities)
61
+ or any(capability not in CAPABILITY_WHITELIST for capability in capabilities)
62
+ ):
63
+ return None
64
+ return script, frozenset(capabilities)
65
+
66
+
67
+ def malformed_conformance_stages(data: object) -> list[int]:
68
+ """`conformanceTests` 를 선언했으나 파싱되지 않는 stage 번호들.
69
+
70
+ 선언이 아예 없는 stage 는 대상이 아니다 — 둘 중 하나를 요구하는 XOR 은
71
+ validators/validate-implementation-plan-stages.py 의 check S11 이 계획
72
+ 단계에서 이미 판정한다. `stage` 가 양의 정수가 아닌 경우도 여기 몫이 아니다:
73
+ 스키마(`stage: integer, minimum 1`)가 계획 런의 validate-run 에서 먼저 막는다.
74
+ `conformanceTests` 가 문자열이 아닌 경우도 같은 이유로 뺀다 — 스키마가
75
+ `type: string, minLength: 1` 로 못박아 그 값은 계획 런의 validate-run 을
76
+ 통과하지 못한다. 여기서 그것까지 malformed 로 세면 스키마가 이미 낸 판정을
77
+ 이 게이트가 다른 문구로 되풀이한다.
78
+ """
79
+ planning = data.get("implementationPlanning") if isinstance(data, dict) else None
80
+ stages = planning.get("stages") if isinstance(planning, dict) else None
81
+ bad: list[int] = []
82
+ for stage in stages if isinstance(stages, list) else []:
83
+ if not isinstance(stage, dict):
84
+ continue
85
+ declared = stage.get("conformanceTests")
86
+ if not isinstance(declared, str) or not declared.strip():
87
+ continue
88
+ number = stage.get("stage")
89
+ if not isinstance(number, int) or isinstance(number, bool) or number < 1:
90
+ continue
91
+ if parse_conformance_tests(declared) is None:
92
+ bad.append(number)
93
+ return bad
94
+
95
+
28
96
  def is_advisory_conformance_entry(entry: object) -> bool:
29
97
  """Return whether one entry depends on user-owned external QA."""
30
98
  if not isinstance(entry, dict):