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
@@ -7,10 +7,12 @@ import subprocess
7
7
  import time
8
8
  from dataclasses import dataclass, field, replace
9
9
  from datetime import datetime, timezone
10
+ import os
10
11
  from pathlib import Path
11
12
  from typing import Any, Mapping, Sequence
12
13
 
13
14
  from .dispatch_state import (
15
+ CompletedWithoutResultError,
14
16
  BACKEND_CLI_WRAPPER,
15
17
  BACKEND_CMUX_PANE,
16
18
  BACKEND_MIXED,
@@ -27,6 +29,7 @@ from .dispatch_state import (
27
29
  load_json_object as _load_json_object,
28
30
  link_agent_dispatch_result as _link_agent_dispatch_result,
29
31
  missing_completion_paths as _missing_completion_paths,
32
+ _plan_verify_result_aliases,
30
33
  mutate_team_state as _mutate_team_state,
31
34
  require_string as _require_string,
32
35
  resolve_project_path as _resolve_project_path,
@@ -54,7 +57,7 @@ from .assignment_resolver import (
54
57
  from .application.open_worker import open_worker
55
58
  from .domain.host import HostSessionContext
56
59
  from .domain.provider import HostModelBinding, ServedModelAttestation
57
- from .domain.worker_exec import SERVED_MODEL_MISMATCH_EXIT_CODE
60
+ from .domain.worker_exec import NO_RESULT_EXIT_CODE, SERVED_MODEL_MISMATCH_EXIT_CODE
58
61
  from .domain.worker_runtime import (
59
62
  SURFACE_CMUX_PANE,
60
63
  EnvironmentBlocked,
@@ -90,7 +93,7 @@ from .initial_prompt_materialization import (
90
93
  PromptDeliveryMode,
91
94
  materialize_initial_prompts,
92
95
  )
93
- from .agent_invocation import AgentInvocationError, materialize_retry_invocation
96
+ from .agent.invocation import AgentInvocationError, materialize_retry_invocation
94
97
  from .path_hints import hydrate_active_run_context
95
98
  from .schema_excerpt import (
96
99
  bundle_excerpt_path,
@@ -102,6 +105,7 @@ from .worker_audit_ledger import (
102
105
  check_worker_results_audit,
103
106
  parse_worker_result_name,
104
107
  )
108
+ from .worker_artifacts import worker_provider_id
105
109
  from .worker_prompt_body import REPORT_WRITER_WORKER_ID
106
110
  from .worker_prompt_headers import (
107
111
  WorkerPromptHeaderError,
@@ -121,6 +125,7 @@ from .write_policy import (
121
125
  WritePolicy,
122
126
  build_invocation_write_contract,
123
127
  planned_paths_from_run_manifest,
128
+ task_root_from_run_manifest,
124
129
  write_enforcement_from_payload,
125
130
  write_policy_from_payload,
126
131
  )
@@ -914,24 +919,32 @@ def _select_workers(
914
919
  options,
915
920
  )
916
921
  supported = set(options.supported_worker_wrappers)
922
+ state = team_state or {}
923
+
924
+ def _wrapper_supported(worker_id: str) -> bool:
925
+ return _worker_provider(state, worker_id, options) in supported
926
+
917
927
  selected = (
918
928
  list(requested_workers)
919
929
  if requested_workers
920
930
  else [
921
931
  worker
922
932
  for worker in recommended
923
- if worker in supported and worker != REPORT_WRITER_WORKER_ID
933
+ if _wrapper_supported(worker) and worker != REPORT_WRITER_WORKER_ID
924
934
  ]
925
935
  )
926
936
  unknown = [worker for worker in selected if worker not in recommended]
927
937
  if unknown:
928
938
  raise DispatchError("requested worker(s) are not in this run roster: " + ", ".join(unknown))
929
- unsupported = [worker for worker in selected if worker not in supported]
939
+ unsupported = [worker for worker in selected if not _wrapper_supported(worker)]
930
940
  if unsupported:
931
941
  allowed = ", ".join(sorted(supported))
932
942
  raise DispatchError(
933
943
  f"unsupported {options.unsupported_worker_label} worker(s): "
934
- + ", ".join(unsupported)
944
+ + ", ".join(
945
+ f"{worker} (provider {_worker_provider(state, worker, options)})"
946
+ for worker in unsupported
947
+ )
935
948
  + f" (dispatcher supports: {allowed})"
936
949
  )
937
950
  return selected
@@ -1006,28 +1019,20 @@ def _translator_job_from_reservation(
1006
1019
  for row in collection
1007
1020
  if isinstance(row, Mapping)
1008
1021
  }
1009
- candidates: list[Mapping[str, Any]] = []
1010
- for path in sorted(reservation_root.glob("*.json")):
1011
- reservation = _load_json_object(path, "agent invocation reservation")
1012
- if (
1013
- set(reservation) == _TRANSLATOR_RESERVATION_KEYS
1014
- and reservation.get("schemaVersion") == "2.0"
1015
- and reservation.get("executionIdentityVersion") == 2
1016
- and reservation.get("assignmentRef") == "translator"
1017
- and reservation.get("audience") == "translator"
1018
- and reservation.get("dispatchKind") == "translator"
1019
- and reservation.get("dutyId") == "translator"
1020
- and reservation.get("participantRef") == execution.get("participantRef")
1021
- and reservation.get("roleExecutionRef") == execution.get("roleExecutionRef")
1022
- and _build_dispatch_id(
1023
- str(reservation.get("invocationId") or ""),
1024
- int(reservation.get("attempt") or 0),
1025
- ) not in dispatched_ids
1026
- ):
1027
- candidates.append(reservation)
1022
+ run_manifest_rel = _string_value(manifest.get("runManifestPath"))
1023
+ candidates, seen = _translator_reservation_candidates(
1024
+ project_root,
1025
+ reservation_root,
1026
+ execution=execution,
1027
+ dispatched_ids=dispatched_ids,
1028
+ run_manifest_rel=run_manifest_rel,
1029
+ )
1028
1030
  if len(candidates) != 1:
1031
+ run_label = run_manifest_rel or "run manifest path unknown"
1029
1032
  raise DispatchError(
1030
- "translator dispatch requires exactly one canonical invocation reservation"
1033
+ "translator dispatch requires exactly one canonical invocation "
1034
+ f"reservation for this run ({run_label}); found {len(candidates)}. "
1035
+ "Translator reservations seen: " + ("; ".join(seen) if seen else "none")
1031
1036
  )
1032
1037
  reservation = candidates[0]
1033
1038
  prompt_path = _resolve_project_path(
@@ -1087,6 +1092,86 @@ def _translator_job_from_reservation(
1087
1092
  )
1088
1093
 
1089
1094
 
1095
+ def _translator_reservation_candidates(
1096
+ project_root: Path,
1097
+ reservation_root: Path,
1098
+ *,
1099
+ execution: Mapping[str, Any],
1100
+ dispatched_ids: set[str],
1101
+ run_manifest_rel: str,
1102
+ ) -> tuple[list[Mapping[str, Any]], list[str]]:
1103
+ """이 run 이 디스패치할 수 있는 translator 예약과, 본 예약 전부의 요약.
1104
+
1105
+ 예약 디렉터리(`prompts/.agent-invocations/`)는 run 이 아니라 task type 단위라
1106
+ 같은 task 의 이전 run 이 남긴 translator 예약이 함께 놓인다. translator 는
1107
+ `assignmentRef` 가 접미 없는 `translator` 하나이고, 이전 run 의 디스패치는
1108
+ 이번 run 의 team-state 에 없으므로 "아직 디스패치 안 됨" 으로 읽혀 후보가
1109
+ 둘이 됐다 — 두 번째 run 부터 번역이 영영 막혔다(2026-09-03 실측,
1110
+ fontsninja-v3-site dev-10626-1 r02·r04). 예약 자체는 run 을 모르지만 그 옆의
1111
+ `.meta.json` 은 `contractSource.runManifestPath` 로 자기 run 을 안다. 그
1112
+ 값을 이번 run 의 `runManifestPath` 와 대조한다. 매니페스트가 그 경로를 안
1113
+ 가진 레거시 run 은 종전대로 run 을 가리지 않는다.
1114
+ """
1115
+ candidates: list[Mapping[str, Any]] = []
1116
+ seen: list[str] = []
1117
+ for path in sorted(reservation_root.glob("*.json")):
1118
+ reservation = _load_json_object(path, "agent invocation reservation")
1119
+ if not (
1120
+ set(reservation) == _TRANSLATOR_RESERVATION_KEYS
1121
+ and reservation.get("schemaVersion") == "2.0"
1122
+ and reservation.get("executionIdentityVersion") == 2
1123
+ and reservation.get("assignmentRef") == "translator"
1124
+ and reservation.get("audience") == "translator"
1125
+ and reservation.get("dispatchKind") == "translator"
1126
+ and reservation.get("dutyId") == "translator"
1127
+ ):
1128
+ continue
1129
+ invocation_id = str(reservation.get("invocationId") or "")
1130
+ reservation_run = _reservation_run_manifest_rel(project_root, reservation)
1131
+ dispatched = _build_dispatch_id(
1132
+ invocation_id, int(reservation.get("attempt") or 0),
1133
+ ) in dispatched_ids
1134
+ seen.append(
1135
+ f"{invocation_id} (run {reservation_run or 'unknown'}"
1136
+ + (", dispatched" if dispatched else "")
1137
+ + ")"
1138
+ )
1139
+ if dispatched:
1140
+ continue
1141
+ if (
1142
+ reservation.get("participantRef") != execution.get("participantRef")
1143
+ or reservation.get("roleExecutionRef") != execution.get("roleExecutionRef")
1144
+ ):
1145
+ continue
1146
+ if run_manifest_rel and (
1147
+ reservation_run is None
1148
+ or os.path.normpath(reservation_run) != os.path.normpath(run_manifest_rel)
1149
+ ):
1150
+ continue
1151
+ candidates.append(reservation)
1152
+ return candidates, seen
1153
+
1154
+
1155
+ def _reservation_run_manifest_rel(
1156
+ project_root: Path, reservation: Mapping[str, Any],
1157
+ ) -> str | None:
1158
+ """예약 옆 `.meta.json` 의 `contractSource.runManifestPath`. 못 읽으면 None."""
1159
+ metadata_value = _string_value(reservation.get("metadataPath"))
1160
+ if not metadata_value:
1161
+ return None
1162
+ try:
1163
+ metadata = _load_json_object(
1164
+ _resolve_project_path(project_root, metadata_value),
1165
+ "agent invocation metadata",
1166
+ )
1167
+ except DispatchError:
1168
+ return None
1169
+ source = metadata.get("contractSource")
1170
+ if not isinstance(source, Mapping):
1171
+ return None
1172
+ return _string_value(source.get("runManifestPath")) or None
1173
+
1174
+
1090
1175
  def _translator_prompt_outputs(
1091
1176
  project_root: Path, prompt_path: Path,
1092
1177
  ) -> tuple[Path, Path]:
@@ -1162,13 +1247,11 @@ def _is_cli_wrapper_assignment(
1162
1247
  options: _BuildOptions,
1163
1248
  ) -> bool:
1164
1249
  state = _worker_state(team_state, worker_id)
1165
- provider = _string_value(state.get("provider")) or _provider_for_worker(
1166
- worker_id,
1167
- options,
1168
- )
1169
1250
  return (
1170
1251
  _string_value(state.get("runner")) or BACKEND_CLI_WRAPPER
1171
- ) == BACKEND_CLI_WRAPPER and provider in options.supported_worker_wrappers
1252
+ ) == BACKEND_CLI_WRAPPER and _worker_provider(
1253
+ team_state, worker_id, options
1254
+ ) in options.supported_worker_wrappers
1172
1255
 
1173
1256
 
1174
1257
  def _runner_for_worker(team_state: Mapping[str, Any], worker_id: str) -> str:
@@ -1203,6 +1286,7 @@ def _job_from_roster_worker(
1203
1286
  completion_paths=_completion_paths(
1204
1287
  fact.worker_id, result_path, manifest, project_root
1205
1288
  ),
1289
+ result_aliases=_plan_verify_result_aliases(result_path, manifest),
1206
1290
  worktree_path=_worktree_path(manifest, active_context),
1207
1291
  role=fact.role,
1208
1292
  idle_timeout_seconds=options.idle_timeout_seconds,
@@ -1484,26 +1568,28 @@ def _persisted_write_contract(
1484
1568
  if not job.has_execution_identity:
1485
1569
  return None
1486
1570
  manifest = read_execution_manifest(plan.manifest_path)
1487
- invocation = next(
1571
+ # 계약은 attempt 소유다 — 재시도는 attempt 마다 다른 프롬프트 파생 파일을
1572
+ # 쓰므로, invocation 으로 찾으면 1회차의 허용 경로를 2회차에 물린다.
1573
+ attempt = next(
1488
1574
  (
1489
- row for row in manifest.invocations
1490
- if row.invocation_ref == job.invocation_ref
1575
+ row for row in manifest.attempts
1576
+ if row.invocation_ref == job.invocation_ref and row.attempt == job.attempt
1491
1577
  ),
1492
1578
  None,
1493
1579
  )
1494
- if invocation is None:
1495
- raise DispatchError("worker invocation write policy was not persisted")
1580
+ if attempt is None:
1581
+ raise DispatchError("worker attempt write policy was not persisted")
1496
1582
  try:
1497
1583
  enforcement = write_enforcement_from_payload(
1498
- invocation.write_enforcement
1584
+ attempt.write_enforcement
1499
1585
  )
1500
1586
  policy = replace(
1501
- write_policy_from_payload(invocation.write_policy),
1587
+ write_policy_from_payload(attempt.write_policy),
1502
1588
  maximum_boundary_precision=enforcement.boundary_precision,
1503
1589
  )
1504
1590
  except (TypeError, ValueError) as exc:
1505
1591
  raise DispatchError(f"persisted worker write policy is invalid: {exc}") from exc
1506
- if policy.digest != invocation.write_policy_digest:
1592
+ if policy.digest != attempt.write_policy_digest:
1507
1593
  raise DispatchError("persisted worker write policy digest changed")
1508
1594
  return policy, enforcement
1509
1595
 
@@ -1555,9 +1641,6 @@ def _record_execution_attempt(plan: DispatchPlan, job: WorkerJob) -> None:
1555
1641
  dispatch_kind=job.dispatch_kind,
1556
1642
  round=_dispatch_round(job.dispatch_kind),
1557
1643
  input_digest=job.prompt_digest,
1558
- write_policy=write_policy.to_payload(),
1559
- write_policy_digest=write_policy.digest,
1560
- write_enforcement=write_enforcement.to_payload(),
1561
1644
  ),
1562
1645
  Attempt(
1563
1646
  invocation_ref=job.invocation_ref,
@@ -1569,6 +1652,9 @@ def _record_execution_attempt(plan: DispatchPlan, job: WorkerJob) -> None:
1569
1652
  error_path=None,
1570
1653
  change_summary={},
1571
1654
  evidence_seal_ref=None,
1655
+ write_policy=write_policy.to_payload(),
1656
+ write_policy_digest=write_policy.digest,
1657
+ write_enforcement=write_enforcement.to_payload(),
1572
1658
  ),
1573
1659
  task_key=task_key,
1574
1660
  )
@@ -1603,6 +1689,9 @@ def _canonical_write_contract(
1603
1689
  project_root=plan.project_root,
1604
1690
  worktree=worktree,
1605
1691
  artifact_paths=artifacts,
1692
+ task_root=task_root_from_run_manifest(
1693
+ plan.project_root, plan.manifest
1694
+ ),
1606
1695
  maximum_precision=capability.max_boundary_precision,
1607
1696
  planned_paths=planned[0],
1608
1697
  planned_paths_declared=planned[1],
@@ -1913,10 +2002,10 @@ def _finish_attempt(
1913
2002
  "mutation-present-unresolved",
1914
2003
  }:
1915
2004
  reason = _mutation_failure_reason(mutation)
1916
- _transition_job_status(plan, job, "error", reason)
1917
2005
  _update_dispatch_status(
1918
2006
  plan.team_state_path, job, attempt, "error", reason
1919
2007
  )
2008
+ _transition_job_status(plan, job, "error", reason)
1920
2009
  details = _failure_details(job, attempt, outcome, reason)
1921
2010
  details["mutationAudit"] = mutation.change_summary()
1922
2011
  _append_event(plan, "worker-failed", details)
@@ -1925,13 +2014,20 @@ def _finish_attempt(
1925
2014
  settlement = _settle(plan, job, attempt, outcome)
1926
2015
  if settlement.completed:
1927
2016
  result_link = _link_result(plan, job, attempt)
1928
- _transition_job_status(plan, job, "completed", "")
2017
+ # 디스패치 행을 먼저 종결한다. 명부 전이가 거절될 수 있고, 그 거절이
2018
+ # 행보다 앞서면 행은 `running` 으로 남아 다음 await 가 같은 sidecar 를
2019
+ # 다시 읽고 같은 자리에서 죽는다(`_link_result` 와 같은 결함 형태).
2020
+ roster_refusal = _transition_roster_completed(plan, job, attempt)
1929
2021
  _update_dispatch_status(
1930
2022
  plan.team_state_path,
1931
2023
  job,
1932
2024
  attempt,
1933
2025
  "completed",
1934
- "; ".join(part for part in (settlement.note, result_link["reason"]) if part),
2026
+ "; ".join(
2027
+ part
2028
+ for part in (settlement.note, result_link["reason"], roster_refusal)
2029
+ if part
2030
+ ),
1935
2031
  )
1936
2032
  details = _result_details(job, attempt, outcome)
1937
2033
  details["resultLink"] = result_link
@@ -1946,8 +2042,8 @@ def _finish_attempt(
1946
2042
  return False
1947
2043
  reason = settlement.reason
1948
2044
  status = "timeout" if outcome.timeout else "error"
1949
- _transition_job_status(plan, job, status, reason)
1950
2045
  _update_dispatch_status(plan.team_state_path, job, attempt, status, reason)
2046
+ _transition_job_status(plan, job, status, reason)
1951
2047
  details = _failure_details(job, attempt, outcome, reason)
1952
2048
  if settlement.error_log_append is not None:
1953
2049
  details["errorLogAppend"] = settlement.error_log_append
@@ -1958,6 +2054,35 @@ def _finish_attempt(
1958
2054
  return mutation.retry_allowed if mutation is not None else True
1959
2055
 
1960
2056
 
2057
+ def _transition_roster_completed(
2058
+ plan: DispatchPlan, job: WorkerJob, attempt: int,
2059
+ ) -> str:
2060
+ """명부 행을 completed 로 올리되, 거절은 이벤트와 사유로 돌려세운다.
2061
+
2062
+ 명부는 `resultPath` 파일이 있어야 completed 를 받는다
2063
+ (`_reject_completed_without_result`). 디스패치의 산출물은 전부 있는데 그
2064
+ 파일이 다른 경우 — 리드가 명부와 다른 포인터 경로로 재디스패치한 뒤 —
2065
+ 그 거절이 예외로 `await` 를 죽였고, 행이 `running` 으로 남아 다음 await 도
2066
+ 같은 자리에서 죽었다. 실측(2026-09-02~03, fontsninja-v3-site dev-10626-1
2067
+ r04): report-writer a2·a3 행이 그렇게 남아, 정본 경로로 보낸 a4 가 도는
2068
+ 동안에도 `await` 가 즉시 exit 2 였다.
2069
+
2070
+ 명부는 손대지 않는다 — 그 불변식(completed ⇒ 파일 있음)은 그대로다. 정본
2071
+ 경로를 쓰는 다음 디스패치가 그 파일을 쓰면 그때 completed 가 된다.
2072
+ """
2073
+ try:
2074
+ _transition_job_status(plan, job, "completed", "")
2075
+ except CompletedWithoutResultError as exc:
2076
+ reason = f"roster not updated: {exc}"
2077
+ _append_event(plan, "worker-roster-not-updated", {
2078
+ **_event_execution_identity(job, attempt),
2079
+ "dispatchWorkerResultPath": str(job.worker_result_path),
2080
+ "reason": reason,
2081
+ })
2082
+ return reason
2083
+ return ""
2084
+
2085
+
1961
2086
  def _audit_attempt(
1962
2087
  job: WorkerJob,
1963
2088
  outcome: WorkerOutcome,
@@ -2573,6 +2698,7 @@ def _dispatch_record(
2573
2698
  "modelExecutionValue": job.model_execution_value,
2574
2699
  "wrapperPath": str(job.wrapper_path),
2575
2700
  "completionPaths": [str(path) for path in job.completion_paths],
2701
+ "resultAliases": [str(path) for path in job.result_aliases],
2576
2702
  "worktreePath": job.worktree_path,
2577
2703
  }
2578
2704
  if job.invocation_id:
@@ -2713,12 +2839,14 @@ def _skip_reasons(
2713
2839
  if worker_id not in set(selected)
2714
2840
  }
2715
2841
  supported = set(options.supported_worker_wrappers)
2842
+ state = team_state or {}
2716
2843
  return {
2717
2844
  worker_id: (
2718
2845
  "skipped by worker dispatch default: worker is not supported by this dispatcher"
2719
2846
  )
2720
2847
  for worker_id in recommended
2721
- if worker_id not in set(selected) and worker_id not in supported
2848
+ if worker_id not in set(selected)
2849
+ and _worker_provider(state, worker_id, options) not in supported
2722
2850
  }
2723
2851
 
2724
2852
 
@@ -2857,7 +2985,7 @@ def _retry_details(job: WorkerJob, attempt: int, outcome: WorkerOutcome) -> dict
2857
2985
  def _outcome_from_status(record: Mapping[str, Any], status) -> WorkerOutcome:
2858
2986
  return WorkerOutcome(
2859
2987
  returncode=status.exit_code if status.exit_code is not None else 1,
2860
- missing_completion_paths=tuple(path for path in _record_completion_paths(record) if not path.is_file()),
2988
+ missing_completion_paths=_record_missing_completion_paths(record),
2861
2989
  pane_id=_string_value(record.get("paneId")),
2862
2990
  status_sidecar_path=status.path,
2863
2991
  timeout=status.timeout,
@@ -2925,8 +3053,43 @@ def _record_completion_paths(record: Mapping[str, Any]) -> list[Path]:
2925
3053
  return [Path(path) for path in _string_list(record.get("completionPaths"))]
2926
3054
 
2927
3055
 
3056
+ def _record_missing_completion_paths(record: Mapping[str, Any]) -> tuple[Path, ...]:
3057
+ aliases = {Path(path) for path in _string_list(record.get("resultAliases"))}
3058
+ result_path = Path(_string_value(record.get("resultPath")))
3059
+ worker_result = Path(_string_value(record.get("workerResultPath")))
3060
+ missing: list[Path] = []
3061
+ for path in _record_completion_paths(record):
3062
+ if path.is_file():
3063
+ continue
3064
+ if path in {result_path, worker_result} and any(alias.is_file() for alias in aliases):
3065
+ continue
3066
+ missing.append(path)
3067
+ return tuple(missing)
3068
+
3069
+
2928
3070
  def _should_retry(outcome: WorkerOutcome, attempt: int) -> bool:
2929
- return outcome.returncode == 0 and bool(outcome.missing_completion_paths) and attempt < MAX_WORKER_ATTEMPTS
3071
+ """결과가 없으면 재시도한다 — 기준은 종료 코드가 아니라 산출물이다.
3072
+
3073
+ 종전에는 `returncode == 0` 을 요구했다. 그래서 시작하자마자 죽은 CLI 는
3074
+ 재시도를 **한 번도** 못 받았고, 150초를 태우고 0 으로 끝난 CLI 만 받았다.
3075
+ 비용 순서가 거꾸로였다.
3076
+
3077
+ 실측(fontsninja-v3-site `dev-10341`, grok 워커 13회 디스패치): 세션 생성이
3078
+ 거부돼 1.6~4.7초 만에 코드 1 로 죽은 경우가 4회다. 그중
3079
+ `requirements-discovery-002` 는 그것이 1차 시도였는데 재시도가 없어 분석자
3080
+ 한 명이 그대로 사라졌고, 바로 다음 디스패치(`-003`)는 222초를 정상 수행했다.
3081
+ 같은 명령을 지금 돌리면 동시 3회까지 전부 성공한다 — 조건은 분 단위로
3082
+ 풀리는 일시적인 것이고, 재시도 비용은 2초다.
3083
+
3084
+ 산출물이 이미 나왔으면 종료 코드가 무엇이든 재시도하지 않는다. 일이 끝났기
3085
+ 때문이다. 타임아웃도 재시도하지 않는다 — 그것은 예산을 다 쓴 실패라
3086
+ 재시도가 같은 예산을 한 번 더 쓴다.
3087
+ """
3088
+ if outcome.timeout:
3089
+ return False
3090
+ if attempt >= MAX_WORKER_ATTEMPTS:
3091
+ return False
3092
+ return bool(outcome.missing_completion_paths)
2930
3093
 
2931
3094
 
2932
3095
  def _job_for_next_attempt(job: WorkerJob) -> WorkerJob:
@@ -2946,6 +3109,8 @@ def _job_for_next_attempt(job: WorkerJob) -> WorkerJob:
2946
3109
  def _failure_reason(outcome: WorkerOutcome) -> str:
2947
3110
  if outcome.timeout:
2948
3111
  return "worker wrapper timed out"
3112
+ if outcome.returncode == NO_RESULT_EXIT_CODE:
3113
+ return "CLI ended cleanly without emitting a result event"
2949
3114
  if outcome.returncode != 0:
2950
3115
  return f"wrapper exited with code {outcome.returncode}"
2951
3116
  if outcome.missing_completion_paths:
@@ -2982,6 +3147,28 @@ def _resolve_wrapper(provider: str, workspace_root: Path, options: _BuildOptions
2982
3147
  raise DispatchError(f"{script} not found (searched: {', '.join(str(c) for c in candidates)})")
2983
3148
 
2984
3149
 
3150
+ def _worker_provider(
3151
+ team_state: Mapping[str, Any],
3152
+ worker_id: str,
3153
+ options: _BuildOptions | None = None,
3154
+ ) -> str:
3155
+ """이 슬롯이 실제로 실행할 provider.
3156
+
3157
+ 로스터의 worker_id 는 provider 이름이 아니다. 한 provider 가 두 역할을 맡으면
3158
+ `grok-implementer` · `grok-verifier` 처럼 역할 접미사가 붙기 때문이다. 지원 여부는
3159
+ 항상 이 해석을 거친 provider 로 판정한다 — id 를 provider 집합과 직접 비교하면
3160
+ 역할 접미사가 붙은 슬롯이 전부 미지원으로 떨어진다.
3161
+ """
3162
+ workers = team_state.get("workers")
3163
+ recorded = ""
3164
+ if isinstance(workers, list):
3165
+ for worker in workers:
3166
+ if isinstance(worker, Mapping) and worker.get("workerId") == worker_id:
3167
+ recorded = _string_value(worker.get("provider"))
3168
+ break
3169
+ return recorded or _provider_for_worker(worker_id, options)
3170
+
3171
+
2985
3172
  def _provider_for_worker(
2986
3173
  worker_id: str,
2987
3174
  options: _BuildOptions | None = None,
@@ -2992,7 +3179,7 @@ def _provider_for_worker(
2992
3179
  return configured
2993
3180
  if worker_id == REPORT_WRITER_WORKER_ID:
2994
3181
  return "claude"
2995
- return worker_id
3182
+ return worker_provider_id(worker_id)
2996
3183
 
2997
3184
 
2998
3185