okstra 0.186.7 → 0.187.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (541) hide show
  1. package/README.md +1 -1
  2. package/dist/cli-registry.d.mts +88 -1
  3. package/dist/cli-registry.mjs +68 -111
  4. package/dist/cli-registry.mjs.map +1 -1
  5. package/dist/commands/execute/render-bundle.mjs +0 -1
  6. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  7. package/dist/commands/execute/run.mjs +8 -3
  8. package/dist/commands/execute/run.mjs.map +1 -1
  9. package/dist/commands/lifecycle/check-project.mjs +1 -14
  10. package/dist/commands/lifecycle/check-project.mjs.map +1 -1
  11. package/dist/commands/lifecycle/config.mjs +38 -40
  12. package/dist/commands/lifecycle/config.mjs.map +1 -1
  13. package/dist/commands/lifecycle/doctor.d.mts +22 -7
  14. package/dist/commands/lifecycle/doctor.mjs +77 -49
  15. package/dist/commands/lifecycle/doctor.mjs.map +1 -1
  16. package/dist/commands/lifecycle/install.d.mts +12 -10
  17. package/dist/commands/lifecycle/install.mjs +104 -39
  18. package/dist/commands/lifecycle/install.mjs.map +1 -1
  19. package/dist/commands/lifecycle/paths.mjs +8 -3
  20. package/dist/commands/lifecycle/paths.mjs.map +1 -1
  21. package/dist/commands/lifecycle/preflight.mjs +2 -1
  22. package/dist/commands/lifecycle/preflight.mjs.map +1 -1
  23. package/dist/commands/lifecycle/setup.mjs +22 -36
  24. package/dist/commands/lifecycle/setup.mjs.map +1 -1
  25. package/dist/commands/lifecycle/uninstall.d.mts +4 -2
  26. package/dist/commands/lifecycle/uninstall.mjs +59 -15
  27. package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
  28. package/dist/commands/memory/memory.mjs +7 -1
  29. package/dist/commands/memory/memory.mjs.map +1 -1
  30. package/dist/lib/helper-scripts.d.mts +1 -1
  31. package/dist/lib/helper-scripts.mjs +10 -18
  32. package/dist/lib/helper-scripts.mjs.map +1 -1
  33. package/dist/lib/host-config.d.mts +72 -0
  34. package/dist/lib/host-config.mjs +404 -0
  35. package/dist/lib/host-config.mjs.map +1 -0
  36. package/dist/lib/host-registry-client.d.mts +2 -2
  37. package/dist/lib/host-registry-client.mjs +0 -3
  38. package/dist/lib/host-registry-client.mjs.map +1 -1
  39. package/dist/lib/install-assets.d.mts +1 -0
  40. package/dist/lib/install-assets.mjs +4 -0
  41. package/dist/lib/install-assets.mjs.map +1 -1
  42. package/dist/lib/proc.d.mts +2 -0
  43. package/dist/lib/proc.mjs +12 -0
  44. package/dist/lib/proc.mjs.map +1 -1
  45. package/dist/lib/python-command.d.mts +2 -0
  46. package/dist/lib/python-command.mjs +52 -0
  47. package/dist/lib/python-command.mjs.map +1 -0
  48. package/dist/lib/python-helper.d.mts +2 -5
  49. package/dist/lib/python-helper.mjs +3 -52
  50. package/dist/lib/python-helper.mjs.map +1 -1
  51. package/dist/lib/runtime-payload.d.mts +23 -0
  52. package/dist/lib/runtime-payload.mjs +57 -0
  53. package/dist/lib/runtime-payload.mjs.map +1 -0
  54. package/dist/lib/types.d.mts +24 -13
  55. package/docs/architecture/storage-model.md +21 -5
  56. package/docs/architecture.md +37 -36
  57. package/docs/cli.md +55 -76
  58. package/docs/coding-rules.md +295 -0
  59. package/docs/container.md +10 -36
  60. package/docs/contributor-change-matrix.md +1 -1
  61. package/docs/for-ai/skills/okstra-code-review.md +0 -1
  62. package/docs/for-ai/skills/okstra-container-build.md +8 -41
  63. package/docs/for-ai/skills/okstra-inspect.md +4 -4
  64. package/docs/for-ai/skills/okstra-manager.md +2 -2
  65. package/docs/for-ai/skills/okstra-rollup.md +0 -1
  66. package/docs/for-ai/skills/okstra-run.md +3 -3
  67. package/docs/for-ai/skills/okstra-user-response.md +0 -1
  68. package/docs/performance-improvement-plan-v2.md +1 -1
  69. package/docs/project-structure-overview.md +62 -62
  70. package/docs/task-process/README.md +3 -3
  71. package/docs/task-process/common-flow.md +1 -1
  72. package/docs/task-process/error-analysis.md +3 -3
  73. package/docs/task-process/implementation-planning.md +3 -1
  74. package/docs/task-process/release-handoff.md +1 -1
  75. package/package.json +3 -2
  76. package/runtime/BUILD.json +2 -2
  77. package/runtime/agents/workers/claude-worker.md +6 -11
  78. package/runtime/agents/workers/report-writer-worker.md +1 -1
  79. package/runtime/bin/lib/okstra/cli.sh +4 -0
  80. package/runtime/bin/lib/okstra/globals.sh +2 -0
  81. package/runtime/bin/lib/okstra/interactive.sh +26 -173
  82. package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
  83. package/runtime/bin/lib/okstra/usage.sh +3 -3
  84. package/runtime/bin/okstra-compact-reminder.sh +1 -5
  85. package/runtime/bin/okstra-error-log.py +19 -2
  86. package/runtime/bin/okstra-import-check.py +26 -0
  87. package/runtime/bin/okstra-inject-report-index.py +5 -4
  88. package/runtime/bin/okstra-provider-exec.py +22 -17
  89. package/runtime/bin/okstra-render-final-report.py +16 -6
  90. package/runtime/bin/okstra-render-report-views.py +25 -18
  91. package/runtime/bin/okstra-report-translate.py +30 -18
  92. package/runtime/bin/okstra-spawn-followups.py +4 -0
  93. package/runtime/bin/okstra-token-usage.py +3 -0
  94. package/runtime/bin/okstra.sh +4 -2
  95. package/runtime/bin/okstra_bootstrap.py +58 -0
  96. package/runtime/prompts/coding-preflight/overview.md +1 -1
  97. package/runtime/prompts/duties/planning-worker.md +1 -1
  98. package/runtime/prompts/launch.template.md +35 -9
  99. package/runtime/prompts/lead/convergence.md +96 -87
  100. package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
  101. package/runtime/prompts/lead/plan-body-verification.md +65 -46
  102. package/runtime/prompts/lead/report-writer.md +34 -4
  103. package/runtime/prompts/lead/team-contract.md +4 -1
  104. package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
  105. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  106. package/runtime/prompts/profiles/_common-contract.md +3 -3
  107. package/runtime/prompts/profiles/_coverage-critic.md +1 -1
  108. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
  109. package/runtime/prompts/profiles/_implementation-executor.md +3 -1
  110. package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
  111. package/runtime/prompts/profiles/error-analysis.md +4 -3
  112. package/runtime/prompts/profiles/final-verification.md +2 -2
  113. package/runtime/prompts/profiles/implementation-planning.md +22 -17
  114. package/runtime/prompts/profiles/implementation.md +3 -2
  115. package/runtime/prompts/profiles/requirements-discovery.md +2 -1
  116. package/runtime/prompts/wizard/prompts.ko.json +29 -8
  117. package/runtime/python/okstra_ctl/__init__.py +6 -27
  118. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
  119. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
  120. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
  121. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +40 -22
  122. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +14 -21
  123. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
  124. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
  125. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
  126. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
  127. package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
  128. package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +12 -1
  129. package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
  130. package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
  131. package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
  132. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +215 -0
  133. package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
  134. package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
  135. package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
  136. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +408 -0
  137. package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
  138. package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
  139. package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
  140. package/runtime/python/okstra_ctl/approval_decisions.py +260 -8
  141. package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
  142. package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
  143. package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
  144. package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
  145. package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
  146. package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
  147. package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
  148. package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
  149. package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
  150. package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
  151. package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
  152. package/runtime/python/okstra_ctl/cmux.py +38 -31
  153. package/runtime/python/okstra_ctl/code_review_target.py +163 -1
  154. package/runtime/python/okstra_ctl/conformance.py +23 -0
  155. package/runtime/python/okstra_ctl/container.py +38 -421
  156. package/runtime/python/okstra_ctl/context_cost.py +16 -2
  157. package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
  158. package/runtime/python/okstra_ctl/convergence.py +101 -15
  159. package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
  160. package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
  161. package/runtime/python/okstra_ctl/convergence_store.py +5 -29
  162. package/runtime/python/okstra_ctl/design_prep.py +108 -12
  163. package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
  164. package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
  165. package/runtime/python/okstra_ctl/dispatch_core.py +102 -25
  166. package/runtime/python/okstra_ctl/dispatch_state.py +268 -39
  167. package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
  168. package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
  169. package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -1
  170. package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
  171. package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
  172. package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
  173. package/runtime/python/okstra_ctl/error_log_core.py +1 -1
  174. package/runtime/python/okstra_ctl/error_report.py +14 -0
  175. package/runtime/python/okstra_ctl/error_zip.py +18 -2
  176. package/runtime/python/okstra_ctl/execution_identity.py +100 -7
  177. package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
  178. package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
  179. package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
  180. package/runtime/python/okstra_ctl/final_report_schema.py +90 -16
  181. package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
  182. package/runtime/python/okstra_ctl/handoff.py +24 -1
  183. package/runtime/python/okstra_ctl/ids.py +6 -16
  184. package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
  185. package/runtime/python/okstra_ctl/implementation_options.py +25 -11
  186. package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
  187. package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
  188. package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
  189. package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
  190. package/runtime/python/okstra_ctl/index.py +2 -2
  191. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
  192. package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
  193. package/runtime/python/okstra_ctl/json_boundary.py +10 -0
  194. package/runtime/python/okstra_ctl/listing.py +0 -91
  195. package/runtime/python/okstra_ctl/locks.py +5 -19
  196. package/runtime/python/okstra_ctl/log_report.py +14 -0
  197. package/runtime/python/okstra_ctl/manager_cli.py +22 -3
  198. package/runtime/python/okstra_ctl/manager_launch.py +4 -8
  199. package/runtime/python/okstra_ctl/material.py +2 -2
  200. package/runtime/python/okstra_ctl/migrate.py +23 -1
  201. package/runtime/python/okstra_ctl/model_cli.py +19 -5
  202. package/runtime/python/okstra_ctl/model_discovery.py +46 -47
  203. package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
  204. package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
  205. package/runtime/python/okstra_ctl/model_io/references.py +370 -0
  206. package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
  207. package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
  208. package/runtime/python/okstra_ctl/model_pool.py +10 -6
  209. package/runtime/python/okstra_ctl/models.py +8 -8
  210. package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
  211. package/runtime/python/okstra_ctl/next_phase.py +121 -69
  212. package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
  213. package/runtime/python/okstra_ctl/pane_title.py +11 -8
  214. package/runtime/python/okstra_ctl/path_hints.py +36 -11
  215. package/runtime/python/okstra_ctl/paths.py +76 -8
  216. package/runtime/python/okstra_ctl/plan_items.py +132 -16
  217. package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
  218. package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
  219. package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
  220. package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
  221. package/runtime/python/okstra_ctl/prepare_error.py +20 -0
  222. package/runtime/python/okstra_ctl/prior_planning.py +157 -0
  223. package/runtime/python/okstra_ctl/profile_show.py +18 -1
  224. package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
  225. package/runtime/python/okstra_ctl/recap.py +22 -1
  226. package/runtime/python/okstra_ctl/reconcile.py +84 -0
  227. package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
  228. package/runtime/python/okstra_ctl/render.py +98 -32
  229. package/runtime/python/okstra_ctl/render_final_report.py +3 -7
  230. package/runtime/python/okstra_ctl/report_assembly.py +297 -7
  231. package/runtime/python/okstra_ctl/report_contract.py +30 -7
  232. package/runtime/python/okstra_ctl/report_finalize.py +206 -15
  233. package/runtime/python/okstra_ctl/report_html/common.py +2 -2
  234. package/runtime/python/okstra_ctl/report_html/render.py +8 -12
  235. package/runtime/python/okstra_ctl/report_language.py +8 -4
  236. package/runtime/python/okstra_ctl/report_narrative.py +90 -3
  237. package/runtime/python/okstra_ctl/report_projections.py +8 -2
  238. package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
  239. package/runtime/python/okstra_ctl/report_translation.py +2 -35
  240. package/runtime/python/okstra_ctl/report_views.py +4 -22
  241. package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
  242. package/runtime/python/okstra_ctl/rollup.py +13 -0
  243. package/runtime/python/okstra_ctl/run.py +639 -138
  244. package/runtime/python/okstra_ctl/run_audit.py +13 -0
  245. package/runtime/python/okstra_ctl/run_index_row.py +0 -12
  246. package/runtime/python/okstra_ctl/seeding.py +21 -11
  247. package/runtime/python/okstra_ctl/sequence.py +1 -1
  248. package/runtime/python/okstra_ctl/session.py +64 -14
  249. package/runtime/python/okstra_ctl/set_work_status.py +18 -0
  250. package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
  251. package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
  252. package/runtime/python/okstra_ctl/stage_map.py +57 -11
  253. package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
  254. package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
  255. package/runtime/python/okstra_ctl/stage_targets.py +3 -12
  256. package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
  257. package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
  258. package/runtime/python/okstra_ctl/task_target.py +4 -16
  259. package/runtime/python/okstra_ctl/team.py +49 -2
  260. package/runtime/python/okstra_ctl/time_report.py +17 -2
  261. package/runtime/python/okstra_ctl/usage_report.py +11 -0
  262. package/runtime/python/okstra_ctl/user_response.py +55 -218
  263. package/runtime/python/okstra_ctl/user_response_values.py +242 -0
  264. package/runtime/python/okstra_ctl/validation_contract.py +3 -0
  265. package/runtime/python/okstra_ctl/verification_target.py +68 -0
  266. package/runtime/python/okstra_ctl/wizard.py +457 -92
  267. package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
  268. package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
  269. package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
  270. package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
  271. package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
  272. package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
  273. package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
  274. package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
  275. package/runtime/python/okstra_ctl/worker_request.py +2 -1
  276. package/runtime/python/okstra_ctl/worker_runner.py +16 -9
  277. package/runtime/python/okstra_ctl/worker_state.py +16 -1
  278. package/runtime/python/okstra_ctl/workflow.py +18 -1
  279. package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
  280. package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
  281. package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
  282. package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
  283. package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
  284. package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
  285. package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
  286. package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
  287. package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
  288. package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
  289. package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
  290. package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
  291. package/runtime/python/okstra_ctl/write_policy.py +90 -213
  292. package/runtime/python/okstra_project/__init__.py +0 -4
  293. package/runtime/python/okstra_project/dirs.py +2 -2
  294. package/runtime/python/okstra_project/phase_pointer.py +84 -0
  295. package/runtime/python/okstra_project/slug.py +24 -0
  296. package/runtime/python/okstra_project/state.py +22 -230
  297. package/runtime/python/okstra_token_usage/claude.py +9 -1
  298. package/runtime/python/okstra_token_usage/cli.py +3 -1
  299. package/runtime/python/okstra_token_usage/collect.py +71 -10
  300. package/runtime/python/okstra_token_usage/cursor.py +2 -0
  301. package/runtime/python/okstra_token_usage/grok.py +1 -5
  302. package/runtime/python/okstra_token_usage/paths.py +26 -0
  303. package/runtime/python/okstra_token_usage/pricing.py +19 -7
  304. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
  305. package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
  306. package/runtime/schemas/final-report-v2.0.schema.json +9 -26
  307. package/runtime/schemas/final-report-v3.0.schema.json +173 -41
  308. package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
  309. package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
  310. package/runtime/skills/okstra-container-build/SKILL.md +16 -47
  311. package/runtime/skills/okstra-inspect/facets/history.md +1 -1
  312. package/runtime/skills/okstra-inspect/facets/status.md +7 -6
  313. package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
  314. package/runtime/skills/okstra-run/SKILL.md +7 -5
  315. package/runtime/templates/report-writer-prompt-preamble.md +1 -1
  316. package/runtime/templates/reports/brief.template.md +6 -2
  317. package/runtime/templates/reports/error-analysis-input.template.md +2 -0
  318. package/runtime/templates/reports/final-verification-input.template.md +2 -0
  319. package/runtime/templates/reports/implementation-input.template.md +7 -1
  320. package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
  321. package/runtime/templates/reports/quick-input.template.md +2 -0
  322. package/runtime/templates/reports/release-handoff-input.template.md +6 -1
  323. package/runtime/templates/reports/report.js +20 -3
  324. package/runtime/templates/reports/schedule.template.md +2 -0
  325. package/runtime/templates/reports/settings.template.json +0 -10
  326. package/runtime/templates/reports/task-brief.template.md +3 -0
  327. package/runtime/templates/reports/user-response.template.md +7 -3
  328. package/runtime/validators/checks/fixtures-01.py +57 -0
  329. package/runtime/validators/checks/fixtures-02.py +565 -0
  330. package/runtime/validators/checks/runners-01.py +108 -0
  331. package/runtime/validators/checks/validate-assets-01.py +60 -0
  332. package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
  333. package/runtime/validators/checks/validate-tasks-01.py +46 -0
  334. package/runtime/validators/checks/validate-tasks-02.py +85 -0
  335. package/runtime/validators/checks/validate-tasks-03.py +61 -0
  336. package/runtime/validators/checks/validate-tasks-04.py +118 -0
  337. package/runtime/validators/forbidden_actions.py +73 -3
  338. package/runtime/validators/lib/common.sh +5 -0
  339. package/runtime/validators/lib/fixtures.sh +7 -591
  340. package/runtime/validators/lib/paths.sh +13 -0
  341. package/runtime/validators/lib/runners.sh +6 -104
  342. package/runtime/validators/lib/summary.sh +1 -1
  343. package/runtime/validators/lib/validate-assets.sh +6 -56
  344. package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
  345. package/runtime/validators/lib/validate-tasks.sh +9 -294
  346. package/runtime/validators/validate-implementation-plan-stages.py +4 -4
  347. package/runtime/validators/validate-run.py +1369 -2654
  348. package/runtime/validators/validate-workflow.sh +56 -16
  349. package/runtime/validators/validate_improvement_report.py +2 -1
  350. package/runtime/validators/validate_session_conformance.py +295 -49
  351. package/dist/commands/execute/agent-prompt.d.mts +0 -1
  352. package/dist/commands/execute/agent-prompt.mjs +0 -24
  353. package/dist/commands/execute/agent-prompt.mjs.map +0 -1
  354. package/dist/commands/execute/codex-dispatch.d.mts +0 -3
  355. package/dist/commands/execute/codex-dispatch.mjs +0 -6
  356. package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
  357. package/dist/commands/execute/codex-run.d.mts +0 -3
  358. package/dist/commands/execute/codex-run.mjs +0 -62
  359. package/dist/commands/execute/codex-run.mjs.map +0 -1
  360. package/dist/commands/execute/convergence.d.mts +0 -1
  361. package/dist/commands/execute/convergence.mjs +0 -38
  362. package/dist/commands/execute/convergence.mjs.map +0 -1
  363. package/dist/commands/execute/error-log.d.mts +0 -1
  364. package/dist/commands/execute/error-log.mjs +0 -18
  365. package/dist/commands/execute/error-log.mjs.map +0 -1
  366. package/dist/commands/execute/git-reconcile.d.mts +0 -1
  367. package/dist/commands/execute/git-reconcile.mjs +0 -30
  368. package/dist/commands/execute/git-reconcile.mjs.map +0 -1
  369. package/dist/commands/execute/handoff.d.mts +0 -1
  370. package/dist/commands/execute/handoff.mjs +0 -31
  371. package/dist/commands/execute/handoff.mjs.map +0 -1
  372. package/dist/commands/execute/incremental-carry.d.mts +0 -1
  373. package/dist/commands/execute/incremental-carry.mjs +0 -20
  374. package/dist/commands/execute/incremental-carry.mjs.map +0 -1
  375. package/dist/commands/execute/incremental-scope.d.mts +0 -1
  376. package/dist/commands/execute/incremental-scope.mjs +0 -29
  377. package/dist/commands/execute/incremental-scope.mjs.map +0 -1
  378. package/dist/commands/execute/integrate-stages.d.mts +0 -1
  379. package/dist/commands/execute/integrate-stages.mjs +0 -24
  380. package/dist/commands/execute/integrate-stages.mjs.map +0 -1
  381. package/dist/commands/execute/pane-title.d.mts +0 -1
  382. package/dist/commands/execute/pane-title.mjs +0 -20
  383. package/dist/commands/execute/pane-title.mjs.map +0 -1
  384. package/dist/commands/execute/plan-items.d.mts +0 -1
  385. package/dist/commands/execute/plan-items.mjs +0 -9
  386. package/dist/commands/execute/plan-items.mjs.map +0 -1
  387. package/dist/commands/execute/plan-validate.d.mts +0 -1
  388. package/dist/commands/execute/plan-validate.mjs +0 -68
  389. package/dist/commands/execute/plan-validate.mjs.map +0 -1
  390. package/dist/commands/execute/plan-verify.d.mts +0 -1
  391. package/dist/commands/execute/plan-verify.mjs +0 -43
  392. package/dist/commands/execute/plan-verify.mjs.map +0 -1
  393. package/dist/commands/execute/spawn-followups.d.mts +0 -1
  394. package/dist/commands/execute/spawn-followups.mjs +0 -22
  395. package/dist/commands/execute/spawn-followups.mjs.map +0 -1
  396. package/dist/commands/execute/team.d.mts +0 -3
  397. package/dist/commands/execute/team.mjs +0 -66
  398. package/dist/commands/execute/team.mjs.map +0 -1
  399. package/dist/commands/execute/token-usage.d.mts +0 -1
  400. package/dist/commands/execute/token-usage.mjs +0 -19
  401. package/dist/commands/execute/token-usage.mjs.map +0 -1
  402. package/dist/commands/execute/worker-audit-check.d.mts +0 -1
  403. package/dist/commands/execute/worker-audit-check.mjs +0 -34
  404. package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
  405. package/dist/commands/execute/worker-dispatch.d.mts +0 -7
  406. package/dist/commands/execute/worker-dispatch.mjs +0 -64
  407. package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
  408. package/dist/commands/execute/worker-state.d.mts +0 -1
  409. package/dist/commands/execute/worker-state.mjs +0 -28
  410. package/dist/commands/execute/worker-state.mjs.map +0 -1
  411. package/dist/commands/execute/worktree-lookup.d.mts +0 -1
  412. package/dist/commands/execute/worktree-lookup.mjs +0 -92
  413. package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
  414. package/dist/commands/execute/worktree-status.d.mts +0 -1
  415. package/dist/commands/execute/worktree-status.mjs +0 -121
  416. package/dist/commands/execute/worktree-status.mjs.map +0 -1
  417. package/dist/commands/inspect/code-review.d.mts +0 -1
  418. package/dist/commands/inspect/code-review.mjs +0 -32
  419. package/dist/commands/inspect/code-review.mjs.map +0 -1
  420. package/dist/commands/inspect/container.d.mts +0 -1
  421. package/dist/commands/inspect/container.mjs +0 -25
  422. package/dist/commands/inspect/container.mjs.map +0 -1
  423. package/dist/commands/inspect/context-cost.d.mts +0 -1
  424. package/dist/commands/inspect/context-cost.mjs +0 -25
  425. package/dist/commands/inspect/context-cost.mjs.map +0 -1
  426. package/dist/commands/inspect/design-prep.d.mts +0 -1
  427. package/dist/commands/inspect/design-prep.mjs +0 -22
  428. package/dist/commands/inspect/design-prep.mjs.map +0 -1
  429. package/dist/commands/inspect/error-report.d.mts +0 -1
  430. package/dist/commands/inspect/error-report.mjs +0 -25
  431. package/dist/commands/inspect/error-report.mjs.map +0 -1
  432. package/dist/commands/inspect/error-zip.d.mts +0 -1
  433. package/dist/commands/inspect/error-zip.mjs +0 -24
  434. package/dist/commands/inspect/error-zip.mjs.map +0 -1
  435. package/dist/commands/inspect/log-report.d.mts +0 -1
  436. package/dist/commands/inspect/log-report.mjs +0 -26
  437. package/dist/commands/inspect/log-report.mjs.map +0 -1
  438. package/dist/commands/inspect/model-io.d.mts +0 -1
  439. package/dist/commands/inspect/model-io.mjs +0 -25
  440. package/dist/commands/inspect/model-io.mjs.map +0 -1
  441. package/dist/commands/inspect/profile-show.d.mts +0 -1
  442. package/dist/commands/inspect/profile-show.mjs +0 -28
  443. package/dist/commands/inspect/profile-show.mjs.map +0 -1
  444. package/dist/commands/inspect/recap.d.mts +0 -1
  445. package/dist/commands/inspect/recap.mjs +0 -30
  446. package/dist/commands/inspect/recap.mjs.map +0 -1
  447. package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
  448. package/dist/commands/inspect/resolve-task-key.mjs +0 -24
  449. package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
  450. package/dist/commands/inspect/rollup.d.mts +0 -1
  451. package/dist/commands/inspect/rollup.mjs +0 -25
  452. package/dist/commands/inspect/rollup.mjs.map +0 -1
  453. package/dist/commands/inspect/run-audit.d.mts +0 -1
  454. package/dist/commands/inspect/run-audit.mjs +0 -25
  455. package/dist/commands/inspect/run-audit.mjs.map +0 -1
  456. package/dist/commands/inspect/set-work-status.d.mts +0 -1
  457. package/dist/commands/inspect/set-work-status.mjs +0 -29
  458. package/dist/commands/inspect/set-work-status.mjs.map +0 -1
  459. package/dist/commands/inspect/stage-map.d.mts +0 -1
  460. package/dist/commands/inspect/stage-map.mjs +0 -131
  461. package/dist/commands/inspect/stage-map.mjs.map +0 -1
  462. package/dist/commands/inspect/task-list.d.mts +0 -1
  463. package/dist/commands/inspect/task-list.mjs +0 -149
  464. package/dist/commands/inspect/task-list.mjs.map +0 -1
  465. package/dist/commands/inspect/task-show.d.mts +0 -1
  466. package/dist/commands/inspect/task-show.mjs +0 -108
  467. package/dist/commands/inspect/task-show.mjs.map +0 -1
  468. package/dist/commands/inspect/time-report.d.mts +0 -1
  469. package/dist/commands/inspect/time-report.mjs +0 -24
  470. package/dist/commands/inspect/time-report.mjs.map +0 -1
  471. package/dist/commands/inspect/usage-report.d.mts +0 -1
  472. package/dist/commands/inspect/usage-report.mjs +0 -23
  473. package/dist/commands/inspect/usage-report.mjs.map +0 -1
  474. package/dist/commands/inspect/user-response.d.mts +0 -1
  475. package/dist/commands/inspect/user-response.mjs +0 -35
  476. package/dist/commands/inspect/user-response.mjs.map +0 -1
  477. package/dist/commands/inspect/worker-liveness.d.mts +0 -1
  478. package/dist/commands/inspect/worker-liveness.mjs +0 -45
  479. package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
  480. package/dist/commands/lifecycle/contract-check.d.mts +0 -1
  481. package/dist/commands/lifecycle/contract-check.mjs +0 -18
  482. package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
  483. package/dist/commands/lifecycle/migrate.d.mts +0 -1
  484. package/dist/commands/lifecycle/migrate.mjs +0 -30
  485. package/dist/commands/lifecycle/migrate.mjs.map +0 -1
  486. package/dist/commands/lifecycle/model.d.mts +0 -1
  487. package/dist/commands/lifecycle/model.mjs +0 -22
  488. package/dist/commands/lifecycle/model.mjs.map +0 -1
  489. package/dist/commands/manager.d.mts +0 -3
  490. package/dist/commands/manager.mjs +0 -50
  491. package/dist/commands/manager.mjs.map +0 -1
  492. package/dist/commands/report/agent-activity.d.mts +0 -1
  493. package/dist/commands/report/agent-activity.mjs +0 -20
  494. package/dist/commands/report/agent-activity.mjs.map +0 -1
  495. package/dist/commands/report/approval-decision.d.mts +0 -1
  496. package/dist/commands/report/approval-decision.mjs +0 -21
  497. package/dist/commands/report/approval-decision.mjs.map +0 -1
  498. package/dist/commands/report/design-snapshot.d.mts +0 -1
  499. package/dist/commands/report/design-snapshot.mjs +0 -19
  500. package/dist/commands/report/design-snapshot.mjs.map +0 -1
  501. package/dist/commands/report/finalize.d.mts +0 -4
  502. package/dist/commands/report/finalize.mjs +0 -64
  503. package/dist/commands/report/finalize.mjs.map +0 -1
  504. package/dist/commands/report/inject-report-index.d.mts +0 -1
  505. package/dist/commands/report/inject-report-index.mjs +0 -21
  506. package/dist/commands/report/inject-report-index.mjs.map +0 -1
  507. package/dist/commands/report/render-final-report.d.mts +0 -1
  508. package/dist/commands/report/render-final-report.mjs +0 -23
  509. package/dist/commands/report/render-final-report.mjs.map +0 -1
  510. package/dist/commands/report/render-views.d.mts +0 -1
  511. package/dist/commands/report/render-views.mjs +0 -27
  512. package/dist/commands/report/render-views.mjs.map +0 -1
  513. package/dist/commands/report/translate.d.mts +0 -1
  514. package/dist/commands/report/translate.mjs +0 -33
  515. package/dist/commands/report/translate.mjs.map +0 -1
  516. package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
  517. package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
  518. package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
  519. package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
  520. package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
  521. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
  522. package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
  523. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
  524. package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
  525. package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
  526. package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
  527. package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
  528. package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
  529. package/runtime/bin/okstra-central.sh +0 -152
  530. package/runtime/bin/okstra-incremental-carry.py +0 -10
  531. package/runtime/bin/okstra-incremental-scope.py +0 -10
  532. package/runtime/bin/okstra-team-reconcile.sh +0 -36
  533. package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
  534. package/runtime/python/okstra_ctl/batch.py +0 -60
  535. package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
  536. package/runtime/python/okstra_ctl/container_registry.py +0 -72
  537. package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
  538. package/runtime/python/okstra_ctl/resolver.py +0 -54
  539. package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
  540. package/runtime/python/okstra_ctl/tmux.py +0 -134
  541. package/runtime/python/okstra_ctl/worktree.py +0 -1099
@@ -11,6 +11,17 @@ from pathlib import Path
11
11
  from typing import Any, Mapping
12
12
 
13
13
  from .json_boundary import JsonBoundaryError, load_owned_object, write_owned_object_atomic
14
+ from .implementation_options import (
15
+ CANDIDATE_COMPARISON_ROUTING,
16
+ EVALUATION_CRITERIA,
17
+ MAX_CRITERION_VALUE,
18
+ MAX_RANKED_OPTIONS,
19
+ MIN_CRITERION_VALUE,
20
+ MIN_FEASIBLE_VOTES,
21
+ NO_VALID_OPTIONS_ROUTING,
22
+ )
23
+ from .report_narrative import writer_owned_data
24
+ from .scope_provenance import brief_end_state_id_sequence
14
25
 
15
26
 
16
27
  @dataclass(frozen=True)
@@ -52,6 +63,97 @@ class ReportSynthesisPacket:
52
63
  result_path: str
53
64
  sources: tuple[ReportSynthesisSource, ...]
54
65
  accounting_snapshot: dict[str, Any]
66
+ original_requirement_ids: tuple[str, ...] = ()
67
+ participating_analysers: tuple[str, ...] = ()
68
+ # 증분 판정. 저작 계약이 이번 run 의 stage 번호를 말할 수 있는 유일한 출처다.
69
+ incremental_decision: dict[str, Any] | None = None
70
+
71
+ def _carry_instructions(self) -> list[str]:
72
+ """이번 run 의 이월 지시. 증분이 아니면 빈 목록.
73
+
74
+ 일반 규칙으로 두면 계획 분석자 프로필의 한 줄이 되고, 그 줄은 이번
75
+ run 의 `carry_stages` 값을 말하지 못한다. 번호를 박아 넣는 자리는
76
+ 판정을 아는 여기뿐이다.
77
+ """
78
+ decision = self.incremental_decision
79
+ if not decision:
80
+ return []
81
+ carried = ", ".join(str(stage) for stage in decision.get("carryStages", []))
82
+ reverify = ", ".join(str(stage) for stage in decision.get("reverifyStages", []))
83
+ return [
84
+ "This run is an incremental re-plan. Record the decision verbatim as "
85
+ "`implementationPlanning.incrementalDecision` with keys mode, "
86
+ "reverifyStages, carryStages, reason.",
87
+ f"Copy stage rows {carried} from the \"{CARRIED_STAGES_LABEL}\" source "
88
+ f"into `implementationPlanning.stages` unchanged - every field, byte for "
89
+ f"byte. Author only stage(s) {reverify}. `okstra incremental-carry` "
90
+ f"rejects a carried stage that changed or is missing.",
91
+ ]
92
+
93
+ def _carry_validation_rules(self) -> list[str]:
94
+ return ["carried-stages-unchanged"] if self.incremental_decision else []
95
+
96
+ def _task_instructions(self) -> list[str]:
97
+ if self.task_type == "implementation-planning":
98
+ return self._planning_requirement_instructions()
99
+ if self.task_type != "implementation-option-selection":
100
+ return []
101
+ criteria = ", ".join(f"`{item}`" for item in EVALUATION_CRITERIA)
102
+ requirement_ids = ", ".join(
103
+ f"`{item}`" for item in self.original_requirement_ids
104
+ )
105
+ analysers = ", ".join(
106
+ f"`{item}`" for item in self.participating_analysers
107
+ )
108
+ return [
109
+ f"Original requirement ids, in order: {requirement_ids}.",
110
+ f"Participating analysers: {analysers}.",
111
+ f"Evaluation criteria, in exact order: {criteria}.",
112
+ "Every `rankedOptions` and `candidateAudit` row must contain one "
113
+ "`requirementCoverage` row per original requirement id and one "
114
+ "`criterionScores` row per evaluation criterion. Weights and scores "
115
+ f"are integers from {MIN_CRITERION_VALUE} through {MAX_CRITERION_VALUE}; "
116
+ "`weightedScore` is the recalculated weighted mean.",
117
+ "A ranked option is valid only when every participating analyser "
118
+ "supplied one feasibility vote, at least "
119
+ f"{MIN_FEASIBLE_VOTES} votes are `feasible`, and both `safetyBlockers` "
120
+ "and `unresolvedFeasibilityFacts` are empty.",
121
+ f"Rank at most {MAX_RANKED_OPTIONS} valid options by descending "
122
+ "`requirement-fit`, `correctness-risk`, `architecture-fit`, "
123
+ "`weightedScore`, then id. With valid options set routing to "
124
+ f"`{CANDIDATE_COMPARISON_ROUTING}`; without a valid option use "
125
+ f"`{NO_VALID_OPTIONS_ROUTING}`.",
126
+ "In `candidate-comparison`, keep `preselectedDirection` null and do "
127
+ "not route directly to `implementation-planning`. In "
128
+ "`preselected-validation`, emit exactly one validated option, an empty "
129
+ "`candidateAudit`, a cited `preselectedDirection`, and routing "
130
+ "`implementation-planning`.",
131
+ ]
132
+
133
+ def _planning_requirement_instructions(self) -> list[str]:
134
+ """계획 저작 계약에 브리프 요구사항 id 순서를 박아 넣는다.
135
+
136
+ `validate_selected_direction_plan` 은 `requirementCoverage` 행 순서가
137
+ taskBriefPath 의 end-state id 순서와 정확히 한 번씩 일치할 것을
138
+ 요구한다 (`implementation_direction._coverage_summary_errors`). 그
139
+ 순서가 저작 계약에 없으면 작성자는 브리프 원문에서 재유도해야 하고,
140
+ 오차는 §5.5.9 라운드 낭비와 조립 거부로 돌아온다. 레거시 브리프
141
+ (end-state id 없음)는 빈 목록이므로 지시도 싣지 않는다 — 검증기도
142
+ 같은 조건으로 자기 비활성화한다.
143
+ """
144
+ if not self.original_requirement_ids:
145
+ return []
146
+ requirement_ids = ", ".join(
147
+ f"`{item}`" for item in self.original_requirement_ids
148
+ )
149
+ return [
150
+ f"Original requirement ids, in order: {requirement_ids}.",
151
+ "`implementationPlanning.requirementCoverage` must contain exactly "
152
+ "one row per original requirement id (`originalRequirementId`), in "
153
+ "that exact order.",
154
+ "`endStateCoverage` must contain exactly one row per original "
155
+ "requirement id; an `addressed` row names its `coveredBy` anchor.",
156
+ ]
55
157
 
56
158
  def to_dict(self) -> dict[str, Any]:
57
159
  return {
@@ -67,6 +169,9 @@ class ReportSynthesisPacket:
67
169
  "Preserve settled source values, identities, dissent, and user responses.",
68
170
  "Do not invent a value when a source is missing or contradictory.",
69
171
  "Write only the narrative, pointer, and audit artifacts named by the dispatch.",
172
+ *_INTERNAL_IDENTIFIER_INSTRUCTION,
173
+ *self._task_instructions(),
174
+ *self._carry_instructions(),
70
175
  ],
71
176
  "runtimeOwnedContent": [
72
177
  "session identifiers",
@@ -78,6 +183,7 @@ class ReportSynthesisPacket:
78
183
  "source-digest-match",
79
184
  "writer-owned-fields-only",
80
185
  "all-defects-collected",
186
+ *self._carry_validation_rules(),
81
187
  ],
82
188
  },
83
189
  "accountingSnapshot": self.accounting_snapshot,
@@ -99,6 +205,11 @@ class ReportSynthesisPacket:
99
205
  "user response carry-in",
100
206
  "- Validation: source digest match, writer-owned fields, all defects collected",
101
207
  ]
208
+ # 작성자가 실제로 읽는 것은 마크다운이다. JSON 정본에만 실으면 지시가
209
+ # 도달하지 않는다.
210
+ lines.extend(f"- {text}" for text in _INTERNAL_IDENTIFIER_INSTRUCTION)
211
+ lines.extend(f"- {text}" for text in self._task_instructions())
212
+ lines.extend(f"- {text}" for text in self._carry_instructions())
102
213
  lines.extend(_accounting_markdown(self.accounting_snapshot))
103
214
  for source in self.sources:
104
215
  lines.extend(_source_markdown(source))
@@ -190,12 +301,129 @@ def _context_path(
190
301
  return _string(manifest.get(key))
191
302
 
192
303
 
304
+ # 패킷은 워커 결과를 통째로 나른다. 그 안의 finding id(`F-001`)는 수렴 상태에만
305
+ # 있고 리포트는 그 id 를 정의하는 객체를 싣지 않는다 — 인용하면 독자가 해소할 수
306
+ # 없는 참조가 된다. 규칙은 여기에 둔다: 워커 결과를 보여 주는 자리가 여기뿐이다.
307
+ # 집행 코드는 없다(리포트 산문은 자유 서술이다). 저자 지시로만 전달한다.
308
+ _INTERNAL_IDENTIFIER_INSTRUCTION = (
309
+ "Do not cite worker-internal identifiers in reader-facing prose - finding "
310
+ "ids (`F-NNN`), worker ids, round labels. This report defines none of them, "
311
+ "so the reader cannot resolve them. State the substance instead, and cite "
312
+ "only identifiers the report itself defines (`R-NNN`, `C-NNN`, `A-NNN`, "
313
+ "stage numbers).",
314
+ )
315
+
316
+
317
+ CARRIED_STAGES_LABEL = "Carried plan stages"
318
+
319
+
320
+ def read_incremental_decision(
321
+ project_root: Path, manifest: Mapping[str, Any],
322
+ ) -> dict | None:
323
+ """이 run 의 증분 판정. 없거나 증분이 아니면 ``None``.
324
+
325
+ 레코드가 없으면 full 로 읽는다 — 그것이 이 기능이 없던 동안의 동작이고,
326
+ "판정을 못 찾았으니 좁혀도 된다"는 반대 방향의 실패보다 안전하다.
327
+ """
328
+ value = _string(manifest.get("incrementalDecisionPath"))
329
+ if not value:
330
+ return None
331
+ path = _resolve(project_root, value)
332
+ if not path.is_file():
333
+ return None
334
+ try:
335
+ record = load_owned_object(path, artifact="incremental scope decision")
336
+ except (OSError, UnicodeError, JsonBoundaryError):
337
+ return None
338
+ if not isinstance(record, Mapping) or record.get("mode") != "incremental":
339
+ return None
340
+ stages = record.get("carryStages")
341
+ if not isinstance(stages, list) or not stages:
342
+ return None
343
+ return dict(record)
344
+
345
+
346
+ def carried_stage_rows(prior_data: Mapping[str, Any], carry_stages: list) -> list[dict]:
347
+ """직전 리포트에서 이월 대상 stage 의 **본문 행**을 뽑는다.
348
+
349
+ Stage Ledger 가 싣는 것은 `stage`/`title`/`status`/`dependsOn`/`headCommit`
350
+ 뿐이다. narrative 의 `stages[]` 한 행은 `sliceValue`·`acceptance`·`carryIn`·
351
+ `stepwiseExecution`·`exitContract`·`stageValidation` 을 요구하므로, 원장만
352
+ 받은 작성자는 이월 행을 옮길 수 없다.
353
+ """
354
+ # 작성자에게 넘기는 행은 작성자 소유 필드만이어야 한다. 완성 리포트의
355
+ # stage 행에는 `designSurfaceCoverage` 같은 assembly 소유 필드가 함께 있고,
356
+ # 그것까지 실으면 "옮겨 적으라"는 지시가 곧 소유권 위반 지시가 된다.
357
+ planning = writer_owned_data(prior_data).get("implementationPlanning")
358
+ rows = planning.get("stages") if isinstance(planning, Mapping) else None
359
+ if not isinstance(rows, list):
360
+ return []
361
+ wanted = {int(stage) for stage in carry_stages if isinstance(stage, int)}
362
+ return [
363
+ dict(row)
364
+ for row in rows
365
+ if isinstance(row, Mapping) and row.get("stage") in wanted
366
+ ]
367
+
368
+
369
+ def carried_stages_path(narrative_path: Path) -> Path:
370
+ _, markdown_path = report_synthesis_packet_paths(narrative_path)
371
+ return markdown_path.with_name(
372
+ markdown_path.name.replace(
373
+ "report-writer-synthesis-packet", "report-writer-carried-stages"
374
+ )
375
+ ).with_suffix(".json")
376
+
377
+
378
+ def materialize_carried_stages(
379
+ *,
380
+ project_root: Path,
381
+ manifest: Mapping[str, Any],
382
+ narrative_path: Path,
383
+ ) -> tuple[str, dict] | None:
384
+ """이월 stage 본문을 파일로 남기고 (프로젝트 상대 경로, 판정) 을 돌려준다.
385
+
386
+ 묶음의 source 는 파일과 그 다이제스트다. 파생 내용을 특례로 인라인하지 않고
387
+ 실제 파일로 만드는 이유는 두 가지다 — 다이제스트가 다른 source 와 같은
388
+ 의미를 갖고, 어긋났을 때 사람이 열어 볼 수 있다.
389
+ """
390
+ decision = read_incremental_decision(project_root, manifest)
391
+ if decision is None:
392
+ return None
393
+ prior = _resolve(project_root, _string(decision.get("prevDataPath")))
394
+ if not prior.is_file():
395
+ return None
396
+ try:
397
+ prior_data = load_owned_object(prior, artifact="prior planning report")
398
+ except (OSError, UnicodeError, JsonBoundaryError):
399
+ return None
400
+ rows = carried_stage_rows(prior_data, decision["carryStages"])
401
+ if not rows:
402
+ return None
403
+ path = carried_stages_path(narrative_path)
404
+ write_owned_object_atomic(
405
+ path,
406
+ {
407
+ "schemaVersion": "1.0",
408
+ "sourcePlanPath": _string(decision.get("prevDataPath")),
409
+ "carryStages": list(decision["carryStages"]),
410
+ "stages": rows,
411
+ },
412
+ artifact="carried plan stages",
413
+ )
414
+ try:
415
+ return str(path.resolve().relative_to(project_root.resolve())), decision
416
+ except ValueError:
417
+ return str(path), decision
418
+
419
+
193
420
  def _source_specs(
194
421
  project_root: Path,
195
422
  manifest: Mapping[str, Any],
196
423
  active_context: Mapping[str, Any],
197
424
  team_state: Mapping[str, Any],
198
425
  narrative_path: Path,
426
+ carried_source: str,
199
427
  ) -> list[tuple[str, str, str, bool]]:
200
428
  specs: list[tuple[str, str, str, bool]] = []
201
429
  for label, owner, section, key, required in _SOURCE_FIELDS:
@@ -212,6 +440,8 @@ def _source_specs(
212
440
  path = str(Path(instruction_set) / "final-report-schema.json")
213
441
  if path or required:
214
442
  specs.append((label, owner, path, required))
443
+ if carried_source:
444
+ specs.append((CARRIED_STAGES_LABEL, "orchestrator", carried_source, True))
215
445
  specs.extend(_user_response_specs(project_root, narrative_path))
216
446
  attempt_specs = _attempt_result_specs(manifest)
217
447
  if attempt_specs:
@@ -382,6 +612,9 @@ def build_report_synthesis_packet(
382
612
  team_state: Mapping[str, Any],
383
613
  narrative_path: Path,
384
614
  ) -> ReportSynthesisPacket:
615
+ carried = materialize_carried_stages(
616
+ project_root=project_root, manifest=manifest, narrative_path=narrative_path,
617
+ )
385
618
  sources = _read_sources(
386
619
  project_root,
387
620
  _source_specs(
@@ -390,14 +623,38 @@ def build_report_synthesis_packet(
390
623
  active_context,
391
624
  team_state,
392
625
  narrative_path,
626
+ carried[0] if carried else "",
393
627
  ),
394
628
  )
629
+ task_type = _string(manifest.get("taskType"))
630
+ original_requirement_ids: tuple[str, ...] = ()
631
+ participating_analysers: tuple[str, ...] = ()
632
+ # 계획 저작도 같은 id 순서를 계약으로 받는다 — 커버리지 행 순서 검증
633
+ # (`validate_selected_direction_plan`)의 기준값이 이 목록이다.
634
+ if task_type in ("implementation-option-selection", "implementation-planning"):
635
+ brief_value = _context_path(
636
+ manifest, active_context, "instructionSet", "taskBriefPath"
637
+ )
638
+ original_requirement_ids = brief_end_state_id_sequence(
639
+ _resolve(project_root, brief_value)
640
+ )
641
+ if task_type == "implementation-option-selection":
642
+ roster = manifest.get("recommendedWorkers")
643
+ if isinstance(roster, list):
644
+ participating_analysers = tuple(
645
+ _string(worker)
646
+ for worker in roster
647
+ if _string(worker) and _string(worker) != "report-writer"
648
+ )
395
649
  return ReportSynthesisPacket(
396
650
  task_key=_string(manifest.get("taskKey")),
397
- task_type=_string(manifest.get("taskType")),
651
+ task_type=task_type,
398
652
  result_path=_relative(project_root, narrative_path),
399
653
  sources=sources,
400
654
  accounting_snapshot=_accounting_snapshot(team_state),
655
+ original_requirement_ids=original_requirement_ids,
656
+ participating_analysers=participating_analysers,
657
+ incremental_decision=carried[1] if carried else None,
401
658
  )
402
659
 
403
660
 
@@ -69,6 +69,7 @@ PROSE_KEYS = frozenset({
69
69
  "expectedForm",
70
70
  "expectedOutcome",
71
71
  "expectedResult",
72
+ "expectedVerification",
72
73
  "failureHandling",
73
74
  "finalConclusion",
74
75
  "headline",
@@ -108,6 +109,7 @@ PROSE_KEYS = frozenset({
108
109
  "rolloutCost",
109
110
  "routingRecommendation",
110
111
  "selfFixNote",
112
+ "satisfaction",
111
113
  "stageTitle",
112
114
  "stageValidation",
113
115
  "statement",
@@ -293,10 +295,6 @@ STRUCTURAL_KEYS = frozenset({
293
295
  })
294
296
 
295
297
 
296
- class UnclassifiedKeys(ValueError):
297
- """A schema string field belongs to neither set."""
298
-
299
-
300
298
  # A key name says what a field is *for*; these say what one value actually is.
301
299
  # `evidence` is the largest prose bucket in a real report and also holds bare
302
300
  # `src/server.ts:33` citations — the key cannot separate them, the value can.
@@ -429,17 +427,6 @@ def hangul_share(data: Mapping[str, Any]) -> tuple[float, int]:
429
427
  return hangul / len(text), len(text)
430
428
 
431
429
 
432
- def prose_is_english(data: Mapping[str, Any]) -> bool:
433
- """Whether the data.json was authored in English, as the SSOT contract requires.
434
-
435
- The report language names what the human HTML renders in, not what the
436
- worker writes. A worker that authors Korean anyway hands every later phase,
437
- validator and agent a record in a language they do not read.
438
- """
439
- share, _ = hangul_share(data)
440
- return share < HANGUL_PROSE_LIMIT
441
-
442
-
443
430
  def _schema_string_keys(schema: Mapping[str, Any], *, enums: bool) -> set[str]:
444
431
  found: set[str] = set()
445
432
 
@@ -465,23 +452,3 @@ def _schema_string_keys(schema: Mapping[str, Any], *, enums: bool) -> set[str]:
465
452
  walk(definition)
466
453
  walk(schema)
467
454
  return found
468
-
469
-
470
- def schema_string_keys(schema: Mapping[str, Any]) -> set[str]:
471
- """Every leaf key in the schema whose value is a free-form string.
472
-
473
- The drift guard compares this against the two sets above, so adding a
474
- report field forces a decision about whether a translator may touch it.
475
- """
476
- return _schema_string_keys(schema, enums=False)
477
-
478
-
479
- def schema_enum_keys(schema: Mapping[str, Any]) -> set[str]:
480
- """Every leaf key the schema constrains to an enum, anywhere.
481
-
482
- A name can be an enum in one definition and free prose in another —
483
- `risk` is `high|medium|low` on a hotspot and a sentence on a risk row.
484
- The renderer reaches for the same name either way (`tone-{{ row.risk }}`),
485
- so one enum occurrence makes the name structural everywhere.
486
- """
487
- return _schema_string_keys(schema, enums=True)
@@ -34,6 +34,10 @@ from dataclasses import dataclass
34
34
  from pathlib import Path
35
35
  from typing import Iterable, Optional
36
36
 
37
+ from .user_response_values import ( # noqa: F401 — 재노출(값 타입은 leaf 소유)
38
+ UserPlanDecision,
39
+ UserResponseEntry,
40
+ )
37
41
  from .final_report_paths import final_report_data_path
38
42
  from .json_boundary import JsonBoundaryError, load_owned_object
39
43
  from .report_html.view_models.implementation_planning import (
@@ -1077,28 +1081,6 @@ def _slugify(text: str) -> str:
1077
1081
  # templates/reports/report.js implements the byte-identical client side).
1078
1082
  # --------------------------------------------------------------------------- #
1079
1083
 
1080
- @dataclass(frozen=True)
1081
- class UserResponseEntry:
1082
- response_id: str
1083
- kind: str
1084
- value: str
1085
- rationale: Optional[str] = None
1086
- disposition: str = "answer"
1087
-
1088
-
1089
- @dataclass(frozen=True)
1090
- class UserPlanDecision:
1091
- """HTML 계획 결정 위젯의 Export 결과.
1092
-
1093
- ``status`` 가 승인이 아니면 ``reason`` 이 필수다 — 사유 없는 반려는 다음
1094
- run 이 무엇을 고쳐야 하는지 알 수 없어 되돌아올 수밖에 없다.
1095
- ``implementation_option`` 이 빈 문자열이면 라인을 생략한다 (소비 측은
1096
- Recommended Option 폴백)."""
1097
- status: str
1098
- implementation_option: str = ""
1099
- reason: str = ""
1100
-
1101
-
1102
1084
  @dataclass(frozen=True)
1103
1085
  class UserDirectionSelection:
1104
1086
  option_id: str
@@ -19,8 +19,21 @@ from okstra_project import (
19
19
  )
20
20
 
21
21
 
22
+ _CLI_EPILOG = r"""Usage:
23
+ okstra resolve-task-key <task-id> [--task-group <g>] [--project-root <dir>] [--cwd <dir>] [--json]
24
+
25
+ Output: JSON { ok, projectRoot, matches: [ { taskKey, taskGroup, taskId,
26
+ taskGroupPathSegment, taskIdPathSegment, taskManifestPath, ... } ] }.
27
+ matches.length 0 = not found, 1 = unambiguous, N = ambiguous (caller asks).
28
+ This is read-only; it never mutates task state.
29
+ """
30
+
31
+
22
32
  def main(argv: list[str] | None = None) -> int:
23
33
  parser = argparse.ArgumentParser(
34
+ prog="okstra resolve-task-key",
35
+ epilog=_CLI_EPILOG,
36
+ formatter_class=argparse.RawDescriptionHelpFormatter,
24
37
  description="Resolve a bare token (task-id or task-group) to candidate "
25
38
  "task-keys from the catalog."
26
39
  )
@@ -139,8 +139,21 @@ def build_rollup(project_root: Path, task_group: str | None) -> dict:
139
139
  }
140
140
 
141
141
 
142
+ _CLI_EPILOG = r"""Usage:
143
+ okstra rollup [--task-group <group>] [--project-root <dir>] [--cwd <dir>]
144
+
145
+ Read-only. Prints a JSON roll-up of every catalog task (optionally scoped to one
146
+ task-group): per-task run count, elapsed time (raw ms), error count, and the
147
+ latest report path, plus group-level totals and status/category/phase tallies.
148
+ With no --task-group it covers the whole project catalog. Time is raw ms; the
149
+ caller formats HH:MM:SS and synthesizes the prose digest from the report files.
150
+ """
151
+
152
+
142
153
  def main(argv: list[str] | None = None) -> int:
143
154
  parser = argparse.ArgumentParser(
155
+ epilog=_CLI_EPILOG,
156
+ formatter_class=argparse.RawDescriptionHelpFormatter,
144
157
  prog="okstra rollup",
145
158
  description="Roll up run results across a task-group's tasks (raw ms).")
146
159
  parser.add_argument("--task-group", default="",