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
@@ -0,0 +1,605 @@
1
+ """프롬프트를 실제로 찍어 낸다 — run 에 매인 것과 독립 실행 두 갈래.
2
+
3
+ `--run-manifest` 가 있으면 run 갈래다: 매니페스트에서 신원을 확인하고, 필요하면
4
+ 동적 검증자를 예약하고, 그 run 의 산출물 경로로 프롬프트를 쓴다. 없으면 독립
5
+ 갈래다: 인자로 받은 audience·duty 를 스냅샷해 감사 가능한 명세를 만든다. 두
6
+ 갈래가 같은 파일에 있는 이유는 `_materialize` 가 둘 중 하나를 고르는 유일한
7
+ 분기점이기 때문이다.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import os
13
+ from pathlib import Path
14
+ import shutil
15
+ import tempfile
16
+ from typing import Any, Mapping, get_args
17
+
18
+ from ..invocation import (
19
+ AgentInstruction,
20
+ AgentInstructionSource,
21
+ AgentAudience,
22
+ AgentInvocationRequest,
23
+ AgentModelAssignment,
24
+ PreparedAgentInvocation,
25
+ agent_model_assignment_from_payload,
26
+ digest_duty_catalog,
27
+ invocation_execution_identity_from_manifest,
28
+ prepare_agent_invocation,
29
+ )
30
+ from ...assignment_environment import load_assignment_context
31
+ from ...assignment_resolver import AssignmentContext, resolve_dispatch_assignment
32
+ from ...path_hints import hydrate_active_run_context
33
+ from ...worker_prompt_headers import worker_prompt_headers
34
+ from ...final_report_paths import final_report_data_path
35
+ from ...report_inputs import report_narrative_path, uses_report_contract_v3
36
+ from ...report_synthesis_packet import (
37
+ ReportSynthesisPacketError,
38
+ materialize_report_synthesis_packet,
39
+ )
40
+ from ...worker_prompt_body import report_writer_input_lines
41
+ from ...dispatch_state import detect_terminal_backend
42
+ from .dynamic_verifier import (
43
+ _dynamic_verifier_source,
44
+ _reserve_dynamic_verifier_request,
45
+ )
46
+ from .inputs import (
47
+ AgentPromptCliError,
48
+ _authorized_path,
49
+ _exact_path,
50
+ _is_relative_to,
51
+ _is_slug,
52
+ _mapping,
53
+ _project_input,
54
+ _project_manifest_path,
55
+ _project_root,
56
+ _read_json_object,
57
+ _relative,
58
+ )
59
+ from .run_identity import _validate_run_identity
60
+
61
+
62
+ _AUDIENCES = frozenset(get_args(AgentAudience))
63
+ # 결과 파일과 별개의 워커 결과 포인터를 가진 audience. 이들만 `--audit-source` 로
64
+ # 두 번째 경로를 받고, 프롬프트에 `**Worker Result Path:**` 앵커가 생긴다.
65
+ # 나머지 audience 의 결과는 하나뿐이고 감사 사이드카는 그 이름에서 파생된다.
66
+ _POINTER_AUDIENCES = frozenset({"report-writer", "translator"})
67
+
68
+
69
+ def _materialize(args: argparse.Namespace) -> PreparedAgentInvocation:
70
+ project_root = _project_root(args.project_root)
71
+ if args.run_manifest:
72
+ return _materialize_run(args, project_root)
73
+ return _materialize_standalone(args, project_root)
74
+
75
+
76
+ def _materialize_run(
77
+ args: argparse.Namespace,
78
+ project_root: Path,
79
+ ) -> PreparedAgentInvocation:
80
+ required = {
81
+ "worker-id": args.worker_id,
82
+ "dispatch-kind": args.dispatch_kind,
83
+ "assignment-ref": args.assignment_ref,
84
+ "result": args.result,
85
+ }
86
+ missing = [name for name, value in required.items() if not value]
87
+ if missing:
88
+ raise AgentPromptCliError(
89
+ f"run materialization requires: {', '.join('--' + name for name in missing)}"
90
+ )
91
+ forbidden = {
92
+ "host-runtime": args.host_runtime,
93
+ "provider": args.provider,
94
+ "model-role": args.model_role,
95
+ "model": args.model,
96
+ "purpose": args.purpose,
97
+ }
98
+ present = [name for name, value in forbidden.items() if value]
99
+ if present:
100
+ raise AgentPromptCliError(
101
+ f"run materialization forbids: {', '.join('--' + name for name in present)}"
102
+ )
103
+ manifest_path = _project_input(project_root, args.run_manifest, "run manifest")
104
+ manifest = _read_json_object(manifest_path, "run manifest")
105
+ contract = _mapping(manifest.get("agentContract"), "run agent contract")
106
+ authorized = _mapping(contract.get("authorizedPaths"), "authorized paths")
107
+
108
+ instruction_path = _authorized_path(
109
+ project_root,
110
+ args.instruction,
111
+ authorized.get("instructionRoots"),
112
+ "instruction",
113
+ must_exist=True,
114
+ )
115
+ prompt_path = _authorized_path(
116
+ project_root,
117
+ args.prompt,
118
+ authorized.get("promptRoots"),
119
+ "prompt",
120
+ must_exist=False,
121
+ )
122
+ result_path = _authorized_path(
123
+ project_root,
124
+ args.result,
125
+ authorized.get("resultRoots"),
126
+ "result",
127
+ must_exist=False,
128
+ )
129
+ audit_source_path = (
130
+ _authorized_path(
131
+ project_root,
132
+ args.audit_source,
133
+ authorized.get("resultRoots"),
134
+ "audit source",
135
+ must_exist=False,
136
+ )
137
+ if args.audit_source
138
+ else None
139
+ )
140
+ if args.audience not in _POINTER_AUDIENCES:
141
+ # 결과가 하나뿐인 audience 다. 두 번째 경로는 워커(`**Result Path:**` 에
142
+ # 쓴다)와 수집기(jobs-file 의 workerResultPath 를 기다린다)에게 서로 다른
143
+ # 파일을 말하는 것밖에 못 한다. 실측(2026-09-02, fontsninja-v3-site
144
+ # dev-10626-1 error-analysis r04): 리드가 reverify 에 `-worker-` 이름을
145
+ # `--audit-source` 로 따로 넘겨 검증 디스패치 9건 중 8건이 결과를 다
146
+ # 쓰고도 `required worker artifact was not produced` 로 끝났다.
147
+ if (
148
+ audit_source_path is not None
149
+ and _normalized(audit_source_path) != _normalized(result_path)
150
+ ):
151
+ raise AgentPromptCliError(
152
+ f"{args.audience} materialization forbids --audit-source: this "
153
+ "audience writes one result and its audit sidecar derives from "
154
+ "that result's name. Name --result with the canonical `-worker-` "
155
+ "token instead (reverify: "
156
+ "<worker-id>-worker-reverify-r<N>-<task-type>-<seq>.md) and put "
157
+ "the same path in the jobs file's workerResultPath"
158
+ )
159
+ audit_source_path = result_path
160
+ elif args.audience == "report-writer":
161
+ if audit_source_path is None:
162
+ # The report writer is the one audience whose result path is not its
163
+ # own worker result: it writes the report body, while the audit
164
+ # sidecar is derived from its `.md`. With both collapsed into one
165
+ # value the prompt loses its `**Worker Result Path:**` anchor and the
166
+ # writer puts the report where the audit file belongs — silently,
167
+ # because every header is still present and well-formed. The roster
168
+ # path derives both from the manifest; a dynamic call has to name
169
+ # them.
170
+ #
171
+ # Which artifact `--result` names depends on the report contract, and
172
+ # `dispatch_state.dispatch_result_path` is what decides it. Naming
173
+ # only the v2 answer here sent v3 runs to the data.json, so the
174
+ # narrative `report-finalize` assembles from was never written and
175
+ # the phase failed later, at a place that does not point back here.
176
+ if uses_report_contract_v3(manifest):
177
+ expected = report_narrative_path(project_root, manifest)
178
+ raise AgentPromptCliError(
179
+ "report-writer materialization requires --audit-source: "
180
+ f"under report contract 3.0 --result is the narrative "
181
+ f"({expected}), which `report-finalize` assembles the report "
182
+ "data.json from, and --audit-source the worker-result .md "
183
+ "the audit sidecar is derived from"
184
+ )
185
+ raise AgentPromptCliError(
186
+ "report-writer materialization requires --audit-source: under "
187
+ "report contract 2.0 --result is the report data.json, and "
188
+ "--audit-source the worker-result .md the audit sidecar is "
189
+ "derived from"
190
+ )
191
+ _validate_report_writer_paths(
192
+ project_root,
193
+ manifest,
194
+ worker_id=args.worker_id,
195
+ result_path=result_path,
196
+ audit_source_path=audit_source_path,
197
+ )
198
+ elif audit_source_path is None:
199
+ audit_source_path = result_path
200
+ _validate_run_identity(
201
+ manifest,
202
+ worker_id=args.worker_id,
203
+ dispatch_kind=args.dispatch_kind,
204
+ assignment_ref=args.assignment_ref,
205
+ audience=args.audience,
206
+ )
207
+ assignments = _mapping(
208
+ manifest.get("invocationAssignments"),
209
+ "run invocation assignments",
210
+ )
211
+ if args.assignment_ref not in assignments:
212
+ # 유효값은 이 매니페스트 안에만 있고 리드는 그것을 추측할 수 없다.
213
+ # 이름만 거절하면 읽는 쪽이 매니페스트를 직접 열어 키를 세게 된다.
214
+ raise AgentPromptCliError(
215
+ f"assignment reference is missing from run manifest: "
216
+ f"{args.assignment_ref}; this run declares "
217
+ + (", ".join(sorted(assignments)) or "no invocation assignment")
218
+ )
219
+ assignment_payload = assignments[args.assignment_ref]
220
+ assignment = agent_model_assignment_from_payload(assignment_payload)
221
+ dynamic_source = _dynamic_verifier_source(args, manifest)
222
+ identity = (
223
+ None
224
+ if dynamic_source is not None
225
+ else invocation_execution_identity_from_manifest(
226
+ manifest,
227
+ assignment=assignment,
228
+ assignment_ref=args.assignment_ref,
229
+ duty_id=args.audience,
230
+ )
231
+ )
232
+ duty_root = _project_manifest_path(
233
+ project_root,
234
+ contract.get("dutyRootPath"),
235
+ "duty root",
236
+ must_exist=True,
237
+ )
238
+ if contract.get("catalogDigest") != digest_duty_catalog(duty_root):
239
+ raise AgentPromptCliError("run duty snapshot catalog digest does not match")
240
+ active_context_path = _project_manifest_path(
241
+ project_root,
242
+ manifest.get("activeRunContextPath"),
243
+ "active run context",
244
+ must_exist=True,
245
+ )
246
+ active_context = hydrate_active_run_context(
247
+ _read_json_object(active_context_path, "active run context")
248
+ )
249
+ prompt_rel = _relative(project_root, prompt_path)
250
+ result_rel = _relative(project_root, result_path)
251
+ audit_rel = _relative(project_root, audit_source_path)
252
+ anchor_lines = tuple(worker_prompt_headers(
253
+ project_root=project_root,
254
+ prompt_rel=prompt_rel,
255
+ result_rel=result_rel,
256
+ audit_source_rel=audit_rel,
257
+ worker_id=args.worker_id,
258
+ dispatch_kind=args.dispatch_kind,
259
+ manifest=manifest,
260
+ active_context=active_context,
261
+ ))
262
+ body = instruction_path.read_text(encoding="utf-8")
263
+ if args.audience == "report-writer":
264
+ body = _with_inputs_section(
265
+ body,
266
+ _report_writer_input_lines(
267
+ project_root,
268
+ manifest,
269
+ active_context,
270
+ narrative_path=result_path,
271
+ ),
272
+ )
273
+ request = AgentInvocationRequest(
274
+ invocation_id=args.invocation_id,
275
+ worker_id=args.worker_id if identity is None else None,
276
+ audience=args.audience,
277
+ assignment_ref=args.assignment_ref,
278
+ purpose=None,
279
+ assignment=assignment,
280
+ instruction=AgentInstruction(
281
+ anchor_lines=anchor_lines,
282
+ body=body,
283
+ source_paths=(AgentInstructionSource(
284
+ kind="project",
285
+ path=_relative(project_root, instruction_path),
286
+ ),),
287
+ ),
288
+ project_root=project_root,
289
+ run_manifest_path=manifest_path,
290
+ duty_root=duty_root,
291
+ prompt_path=prompt_path,
292
+ metadata_path=prompt_path.with_name(prompt_path.name + ".meta.json"),
293
+ dispatch_kind=args.dispatch_kind,
294
+ participant_ref=identity.participant_ref if identity is not None else None,
295
+ role_execution_ref=(
296
+ identity.role_execution_ref if identity is not None else None
297
+ ),
298
+ duty_id=identity.duty_id if identity is not None else None,
299
+ invocation_ref=args.invocation_id if identity is not None else None,
300
+ attempt=1,
301
+ replace_undispatched=args.replace_undispatched,
302
+ )
303
+ if dynamic_source is not None:
304
+ request = _reserve_dynamic_verifier_request(
305
+ request,
306
+ args=args,
307
+ manifest=manifest,
308
+ manifest_path=manifest_path,
309
+ source_role_execution_ref=dynamic_source,
310
+ )
311
+ return prepare_agent_invocation(request)
312
+
313
+
314
+ def _normalized(path: Path) -> Path:
315
+ return Path(os.path.normpath(path))
316
+
317
+
318
+ def _validate_report_writer_paths(
319
+ project_root: Path,
320
+ manifest: Mapping[str, Any],
321
+ *,
322
+ worker_id: str,
323
+ result_path: Path,
324
+ audit_source_path: Path,
325
+ ) -> None:
326
+ """report-writer 의 두 경로를 run 이 이미 정한 값에 못 박는다.
327
+
328
+ `--result` 는 조립이 읽는 서술문 경로다: 계약 3.0 이면 매니페스트의
329
+ `reportNarrativePath`, 2.0 이면 `expectedReportRecordPath` 의 data.json.
330
+ 다른 경로에 쓴 서술문은 `report-finalize` 가 읽지 않는다. `--audit-source`
331
+ 는 명부(team-state `workers[].resultPath`)의 워커 결과다: `okstra team
332
+ await` 는 그 파일이 있어야 명부 행을 completed 로 적는다. 실측(2026-09-02,
333
+ fontsninja-v3-site dev-10626-1 r04): 리드가 재시도마다 `-a2`/`-a3` 접미를
334
+ 붙인 경로를 넘겨 서술문 세 벌이 조립 밖에 쌓였고, 결국 손으로 `cp` 했다.
335
+ 교정 디스패치는 프롬프트 경로와 invocation ID 만 새로 하고 이 두 경로는
336
+ 그대로 쓴다.
337
+ """
338
+ if uses_report_contract_v3(manifest):
339
+ expected_result = report_narrative_path(project_root, manifest)
340
+ what = "the narrative report-finalize assembles from (run manifest reportNarrativePath)"
341
+ else:
342
+ expected_result = final_report_data_path(_project_manifest_path(
343
+ project_root,
344
+ manifest.get("expectedReportRecordPath"),
345
+ "expected report record",
346
+ must_exist=False,
347
+ ))
348
+ what = "the report data.json (run manifest expectedReportRecordPath)"
349
+ if _normalized(result_path) != _normalized(expected_result):
350
+ raise AgentPromptCliError(
351
+ f"report-writer --result must be {what}: expected {expected_result}, "
352
+ f"got {result_path}. A corrective dispatch reuses this path with a "
353
+ "fresh prompt path and invocation ID"
354
+ )
355
+ roster_result = _roster_result_path(project_root, manifest, worker_id)
356
+ if (
357
+ roster_result is not None
358
+ and _normalized(audit_source_path) != _normalized(roster_result)
359
+ ):
360
+ raise AgentPromptCliError(
361
+ "report-writer --audit-source must be the roster's worker result "
362
+ f"(team-state workers[].resultPath): expected {roster_result}, got "
363
+ f"{audit_source_path}. `okstra team await` records the roster row "
364
+ "completed only when that file exists"
365
+ )
366
+
367
+
368
+ def _load_team_state(project_root: Path, manifest: Mapping[str, Any]) -> Mapping[str, Any]:
369
+ """매니페스트가 가리키는 team-state. 없으면 빈 매핑."""
370
+ team_state_value = manifest.get("teamStatePath")
371
+ if not isinstance(team_state_value, str) or not team_state_value:
372
+ return {}
373
+ team_state_path = Path(team_state_value)
374
+ if not team_state_path.is_absolute():
375
+ team_state_path = project_root / team_state_path
376
+ if not team_state_path.is_file():
377
+ return {}
378
+ return _read_json_object(team_state_path, "team state")
379
+
380
+
381
+ def _report_writer_input_lines(
382
+ project_root: Path,
383
+ manifest: Mapping[str, Any],
384
+ active_context: Mapping[str, Any],
385
+ *,
386
+ narrative_path: Path,
387
+ ) -> list[str]:
388
+ """report-writer 의 `## Inputs` 줄. 로스터 경로(`worker_prompt_body`)와 같은 규칙.
389
+
390
+ 계약 3.0 이면 합성 묶음을 여기서 만들고 그 한 줄을 싣는다; 그 전 계약은
391
+ 옛 목록이다. 실측(2026-09-02, fontsninja-v3-site dev-10626-1 r04): run
392
+ 갈래의 프롬프트에는 이 절이 없어, 작성자가 "읽을 게 없다"며 서술문을
393
+ 거부했고 리드가 경로 22개를 손으로 열거하고서야 통과했다. 두 갈래가 같은
394
+ 입력 표면을 내야 하는 이유다.
395
+ """
396
+ team_state = _load_team_state(project_root, manifest)
397
+ if not uses_report_contract_v3(manifest):
398
+ return report_writer_input_lines(manifest, active_context, team_state)
399
+ try:
400
+ _, markdown_path = materialize_report_synthesis_packet(
401
+ project_root=project_root,
402
+ manifest=manifest,
403
+ active_context=active_context,
404
+ team_state=team_state,
405
+ narrative_path=narrative_path,
406
+ )
407
+ except ReportSynthesisPacketError as exc:
408
+ defects = "; ".join(
409
+ f"owner={issue.owner} source={issue.label} path={issue.path} "
410
+ f"reason={issue.reason}"
411
+ for issue in exc.issues
412
+ )
413
+ raise AgentPromptCliError(
414
+ f"report synthesis packet contract defects: {defects}"
415
+ ) from exc
416
+ return [f"- Report synthesis packet: `{_relative(project_root, markdown_path)}`"]
417
+
418
+
419
+ def _with_inputs_section(body: str, input_lines: list[str]) -> str:
420
+ """런타임 소유 입력 줄을 본문의 `## Inputs` 첫머리에 넣는다.
421
+
422
+ 리드가 이미 그 절을 썼으면 그 제목 바로 아래에 끼워 한 절로 두고, 없으면
423
+ 머리의 `**…:**` 헤더 블록 뒤에 절을 새로 연다.
424
+ """
425
+ lines = body.splitlines()
426
+ for index, line in enumerate(lines):
427
+ if line.strip() == "## Inputs":
428
+ merged = lines[: index + 1] + input_lines + lines[index + 1 :]
429
+ return "\n".join(merged) + "\n"
430
+ insert_at = 0
431
+ while insert_at < len(lines) and (
432
+ not lines[insert_at].strip() or lines[insert_at].startswith("**")
433
+ ):
434
+ insert_at += 1
435
+ section = ["## Inputs", *input_lines, ""]
436
+ if insert_at and lines[insert_at - 1].strip():
437
+ section = ["", *section]
438
+ return "\n".join(lines[:insert_at] + section + lines[insert_at:]) + "\n"
439
+
440
+
441
+ def _roster_result_path(
442
+ project_root: Path, manifest: Mapping[str, Any], worker_id: str,
443
+ ) -> Path | None:
444
+ """명부가 이 워커에 적어 둔 `resultPath`. 행이 없으면 None."""
445
+ workers = _load_team_state(project_root, manifest).get("workers")
446
+ if not isinstance(workers, list):
447
+ return None
448
+ for row in workers:
449
+ if not isinstance(row, Mapping) or row.get("workerId") != worker_id:
450
+ continue
451
+ value = row.get("resultPath")
452
+ if not isinstance(value, str) or not value.strip():
453
+ return None
454
+ path = Path(value.strip())
455
+ return path if path.is_absolute() else project_root / path
456
+ return None
457
+
458
+
459
+ def _materialize_standalone(
460
+ args: argparse.Namespace,
461
+ project_root: Path,
462
+ ) -> PreparedAgentInvocation:
463
+ required = {
464
+ "host-runtime": args.host_runtime,
465
+ "provider": args.provider,
466
+ "model-role": args.model_role,
467
+ "purpose": args.purpose,
468
+ }
469
+ missing = [name for name, value in required.items() if not value]
470
+ if missing:
471
+ raise AgentPromptCliError(
472
+ "standalone materialization requires: "
473
+ + ", ".join("--" + name for name in missing)
474
+ )
475
+ forbidden = {
476
+ "worker-id": args.worker_id,
477
+ "dispatch-kind": args.dispatch_kind,
478
+ "assignment-ref": args.assignment_ref,
479
+ "result": args.result,
480
+ "audit-source": args.audit_source,
481
+ "source-role-execution-ref": args.source_role_execution_ref,
482
+ }
483
+ present = [name for name, value in forbidden.items() if value]
484
+ if present:
485
+ raise AgentPromptCliError(
486
+ "standalone materialization forbids: "
487
+ + ", ".join("--" + name for name in present)
488
+ )
489
+ if not _is_slug(args.purpose) or not _is_slug(args.invocation_id):
490
+ raise AgentPromptCliError("standalone purpose and invocation ID must be slugs")
491
+ if args.audience not in _AUDIENCES:
492
+ raise AgentPromptCliError(f"unknown duty audience: {args.audience}")
493
+ root = project_root / ".okstra" / "agent-invocations" / args.purpose
494
+ if not root.is_dir():
495
+ raise AgentPromptCliError(f"standalone invocation root not found: {root}")
496
+ resolved_root = root.resolve(strict=True)
497
+ if not _is_relative_to(resolved_root, project_root):
498
+ raise AgentPromptCliError("standalone invocation root escapes project root")
499
+ root = resolved_root
500
+ expected_instruction = root / f"{args.invocation_id}.instructions.md"
501
+ expected_prompt = root / f"{args.invocation_id}.prompt.md"
502
+ instruction_path = _exact_path(args.instruction, expected_instruction, True, "instruction")
503
+ prompt_path = _exact_path(args.prompt, expected_prompt, False, "prompt")
504
+ assignment_context = load_assignment_context(
505
+ host_runtime=args.host_runtime,
506
+ terminal_backend=args.terminal_backend or detect_terminal_backend(),
507
+ execution_provider_ids=(args.provider,),
508
+ include_native_provider=False,
509
+ )
510
+ assignment = _standalone_assignment(
511
+ context=assignment_context,
512
+ provider=args.provider,
513
+ model_role=args.model_role,
514
+ model=args.model,
515
+ )
516
+ duty_root = root / f"{args.invocation_id}.duty-contracts"
517
+ _snapshot_standalone_duties(duty_root)
518
+ return prepare_agent_invocation(AgentInvocationRequest(
519
+ invocation_id=args.invocation_id,
520
+ worker_id=None,
521
+ audience=args.audience,
522
+ assignment_ref=None,
523
+ purpose=args.purpose,
524
+ assignment=assignment,
525
+ instruction=AgentInstruction(
526
+ anchor_lines=(),
527
+ body=instruction_path.read_text(encoding="utf-8"),
528
+ source_paths=(AgentInstructionSource(
529
+ kind="project",
530
+ path=_relative(project_root, instruction_path),
531
+ ),),
532
+ ),
533
+ project_root=project_root,
534
+ run_manifest_path=None,
535
+ duty_root=duty_root,
536
+ prompt_path=prompt_path,
537
+ metadata_path=prompt_path.with_name(prompt_path.name + ".meta.json"),
538
+ dispatch_kind="standalone",
539
+ participant_ref=None,
540
+ role_execution_ref=None,
541
+ duty_id=None,
542
+ invocation_ref=None,
543
+ attempt=1,
544
+ ))
545
+
546
+
547
+ def _standalone_assignment(
548
+ *,
549
+ context: AssignmentContext,
550
+ provider: str,
551
+ model_role: str,
552
+ model: str,
553
+ ) -> AgentModelAssignment:
554
+ host_runtime = context.environment.host_descriptor.id
555
+ resolved = resolve_dispatch_assignment(
556
+ context=context,
557
+ host_runtime=host_runtime,
558
+ role=model_role,
559
+ duty_id=model_role,
560
+ provider=provider,
561
+ model=model,
562
+ )
563
+ binding = resolved.binding
564
+ if binding is None:
565
+ raise AgentPromptCliError("standalone assignment requires a model binding")
566
+ return AgentModelAssignment(
567
+ provider=resolved.provider_id,
568
+ model=resolved.display_name,
569
+ model_execution_value=binding.resolved_execution_value,
570
+ runner=binding.runner,
571
+ host_runtime=host_runtime,
572
+ host_model_value=binding.host_model_value,
573
+ )
574
+
575
+
576
+ def _snapshot_standalone_duties(destination: Path) -> None:
577
+ # 부모 개수를 세면 체크아웃(`<root>/scripts/okstra_ctl/agent/prompt_cli/`)
578
+ # 에서만 맞는다 — 설치본은 패키지가 `~/.okstra/lib/python/` 이라 prompts 가
579
+ # 한 단계 위(`~/.okstra/prompts/`)에 있다. duties 를 실제로 가진 루트를
580
+ # 탐색해서 해소한다.
581
+ from ...paths import find_asset_root
582
+
583
+ duties_relative = ("prompts", "duties")
584
+ root = find_asset_root(duties_relative, is_present=Path.is_dir)
585
+ if root is None:
586
+ raise AgentPromptCliError(
587
+ "duty contract source not found: no prompts/duties under "
588
+ "OKSTRA_HOME or this checkout"
589
+ )
590
+ source = root.joinpath(*duties_relative)
591
+ if destination.exists():
592
+ if not destination.is_dir() or digest_duty_catalog(destination) != digest_duty_catalog(source):
593
+ raise AgentPromptCliError("standalone duty snapshot conflicts with existing files")
594
+ return
595
+ temporary = Path(tempfile.mkdtemp(prefix=f".{destination.name}.", dir=destination.parent))
596
+ try:
597
+ shutil.copytree(source, temporary, dirs_exist_ok=True)
598
+ try:
599
+ os.rename(temporary, destination)
600
+ except FileExistsError:
601
+ if digest_duty_catalog(destination) != digest_duty_catalog(source):
602
+ raise AgentPromptCliError("standalone duty snapshot publication conflict")
603
+ finally:
604
+ if temporary.exists():
605
+ shutil.rmtree(temporary)