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
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import argparse
10
10
  import json
11
+ import os
11
12
  import re
12
13
  import sys
13
14
  from pathlib import Path
@@ -15,7 +16,7 @@ from typing import Iterable
15
16
 
16
17
  from okstra_ctl.paths import RunRef, runs_dir_of, task_manifest_file
17
18
  from okstra_ctl.task_target import resolve_task_root, project_rel
18
- from okstra_ctl.json_boundary import load_owned_object
19
+ from okstra_ctl.json_boundary import JsonBoundaryError, load_owned_object
19
20
 
20
21
 
21
22
  INPUT_FILES = (
@@ -85,7 +86,7 @@ def _load_json(path: Path) -> dict:
85
86
  return {}
86
87
  try:
87
88
  data = load_owned_object(path, artifact="context cost input")
88
- except Exception:
89
+ except JsonBoundaryError:
89
90
  return {}
90
91
  return data if isinstance(data, dict) else {}
91
92
 
@@ -143,6 +144,19 @@ def _is_timestamped_legacy_artifact(path: Path) -> bool:
143
144
  return bool(TIMESTAMPED_ARTIFACT_RE.search(path.name))
144
145
 
145
146
 
147
+ def _okstra_asset_home() -> Path:
148
+ """자산을 읽을 okstra 홈. `OKSTRA_HOME` 이 있으면 그쪽이다.
149
+
150
+ 종전에는 `Path.home() / ".okstra"` 를 그대로 박아, 홈을 갈아끼운 호출자가
151
+ 실제 사용자 설치본을 읽었다. `find_asset_root` 의 docstring 이 규정한 대로
152
+ `OKSTRA_HOME` 이 유일한 격리 이음새이므로 여기서도 그것을 먼저 본다.
153
+ 실측: e2e scenario-23 이 샌드박스 홈을 지정하고도 `~/.okstra/templates/`
154
+ 를 보고해, 설치본이 있는 기계와 없는 기계의 출력이 달랐다.
155
+ """
156
+ override = os.environ.get("OKSTRA_HOME")
157
+ return Path(override) if override else Path.home() / ".okstra"
158
+
159
+
146
160
  def _installed_or_dev(installed: Path, dev_relative: str) -> Path:
147
161
  """Prefer the user-machine install (what production runs read); fall back
148
162
  to the repo dev tree when running from an uninstalled checkout."""
@@ -153,7 +167,7 @@ def _installed_or_dev(installed: Path, dev_relative: str) -> Path:
153
167
 
154
168
  def _runtime_template(filename: str) -> Path:
155
169
  return _installed_or_dev(
156
- Path.home() / ".okstra" / "templates" / filename,
170
+ _okstra_asset_home() / "templates" / filename,
157
171
  f"templates/{filename}",
158
172
  )
159
173
 
@@ -223,7 +237,7 @@ def _skill_assets_metric() -> dict:
223
237
  skill bodies + worker agent specs. These dominate the fixed per-run
224
238
  instruction footprint and are the prompt-diet ranking input."""
225
239
  entries = []
226
- okstra_home = Path.home() / ".okstra"
240
+ okstra_home = _okstra_asset_home()
227
241
  claude_home = Path.home() / ".claude"
228
242
  for name in HOT_PATH_LEAD_RESOURCES:
229
243
  path = _installed_or_dev(
@@ -481,8 +495,22 @@ def analyze_task_bundle(task_root: Path, project_root: Path) -> dict:
481
495
  }
482
496
 
483
497
 
498
+ _CLI_EPILOG = r"""Usage:
499
+ okstra context-cost <task-root>
500
+ okstra context-cost <task-key> --project-root <dir>
501
+ okstra context-cost <task-key> --cwd <dir>
502
+
503
+ Output: JSON with task file counts, current-run counts, lead Phase 1 read
504
+ surface, analysis-worker initial read surface, and report-writer synthesis
505
+ surface. This is read-only; it never mutates task artifacts.
506
+ """
507
+
508
+
484
509
  def main(argv: list[str] | None = None) -> int:
485
510
  parser = argparse.ArgumentParser(
511
+ prog="okstra context-cost",
512
+ epilog=_CLI_EPILOG,
513
+ formatter_class=argparse.RawDescriptionHelpFormatter,
486
514
  description="Estimate context/file-read cost for an okstra task bundle."
487
515
  )
488
516
  parser.add_argument("target", help="task root path or task-key")
@@ -15,8 +15,15 @@ from .contract_graph import (
15
15
  )
16
16
 
17
17
 
18
+ _CLI_EPILOG = r"""Usage:
19
+ okstra contract-check --root <repo-or-staging-root> [--profile <id> | --operation <id>]
20
+ """
21
+
22
+
18
23
  def _parser() -> argparse.ArgumentParser:
19
24
  parser = argparse.ArgumentParser(
25
+ epilog=_CLI_EPILOG,
26
+ formatter_class=argparse.RawDescriptionHelpFormatter,
20
27
  prog="okstra contract-check",
21
28
  description="Validate runtime contract documents and dependencies.",
22
29
  )
@@ -9,7 +9,8 @@ from pathlib import Path, PurePosixPath
9
9
  import sys
10
10
  from typing import Any
11
11
 
12
- from okstra_project.state import StateError, parse_task_key, slugify
12
+ from okstra_project.slug import slugify
13
+ from okstra_project.state import StateError, parse_task_key
13
14
 
14
15
  from .convergence_engine import (
15
16
  ConvergenceContractError,
@@ -21,6 +22,11 @@ from .convergence_engine import (
21
22
  validate_final_state,
22
23
  validate_working_state,
23
24
  )
25
+ from .convergence_critic_prompt import (
26
+ analyser_results,
27
+ covered_index,
28
+ critic_prompt_body,
29
+ )
24
30
  from .convergence_migration import (
25
31
  SeedDecision,
26
32
  archive_state_bytes,
@@ -63,7 +69,12 @@ class RunArtifactAuthority:
63
69
 
64
70
  def validated_run_authority(manifest_path: Path) -> RunArtifactAuthority:
65
71
  supplied = Path(manifest_path)
66
- if not supplied.is_absolute() or supplied.absolute() != supplied.resolve():
72
+ if not supplied.is_absolute():
73
+ # 같은 세션의 `team dispatch`·`agent-prompt materialize`·`convergence
74
+ # prepare-groups` 는 상대경로를 받는다. 이 명령만 거절하면 같은 값이
75
+ # 명령마다 다르게 취급된다. 심링크 없는 절대경로라는 요구는 그대로다.
76
+ supplied = Path.cwd() / supplied
77
+ if supplied.absolute() != supplied.resolve():
67
78
  raise ConvergenceContractError("run manifest path is not canonical")
68
79
  payload = load_owned_json_object(supplied)
69
80
  execution_manifest = None
@@ -206,7 +217,18 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
206
217
  "ticketIds": ["TASK-001"],
207
218
  "originEvidence": "No rollback query result is present",
208
219
  "evidenceArtifacts": [_EVIDENCE_ARTIFACT],
209
- }
220
+ },
221
+ {
222
+ # 이미 있는 finding 과 겹치면 다시 쓰지 않고 그 id 를 댄다.
223
+ # 다시 쓴 finding 은 기각에 검증 라운드 하나를 쓰고, 선언된
224
+ # 중복은 하나도 쓰지 않는다.
225
+ "candidateId": "CG-002",
226
+ "summary": "The migration assumption needs a live row",
227
+ "category": "missing",
228
+ "ticketIds": ["TASK-001"],
229
+ "originEvidence": "The same live row F-001 already cites",
230
+ "duplicateOf": "F-001",
231
+ },
210
232
  ],
211
233
  },
212
234
  "coverage-batch": {
@@ -215,11 +237,14 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
215
237
  "mode": "coverage",
216
238
  # The critic's own provider never votes on its gaps, so `provider` here
217
239
  # is deliberately absent from `dispatches[]`.
240
+ #
241
+ # 검증 대상 gap 이 하나이므로 배정도 한 명이다 — 로스터 순서 위
242
+ # round-robin 이 G-001 을 claude-worker 에게 준다. codex-worker 는 이
243
+ # 라운드에서 열리지 않는다.
218
244
  "provider": "antigravity",
219
245
  "modelExecutionValue": "gemini-3.1-pro",
220
246
  "dispatches": [
221
247
  {"worker": "claude-worker", "status": "completed", "durationMs": 1250},
222
- {"worker": "codex-worker", "status": "completed", "durationMs": 980},
223
248
  ],
224
249
  "gaps": [
225
250
  {
@@ -235,28 +260,50 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
235
260
  "disagreeBasis": None,
236
261
  "explanation": "The plan cites no rollback verification step",
237
262
  },
238
- "codex-worker": {
239
- "verdict": "agree",
240
- "disagreeBasis": None,
241
- "explanation": "No rollback assertion appears in the stage map",
242
- },
243
263
  },
244
- }
264
+ },
265
+ {
266
+ # 선언된 중복은 검증 라운드에 들어가지 않으므로 표가 없다.
267
+ "gapId": "G-002",
268
+ "summary": "The migration assumption needs a live row",
269
+ "category": "missing",
270
+ "ticketIds": ["TASK-001"],
271
+ "originEvidence": "The same live row F-001 already cites",
272
+ "duplicateOf": "F-001",
273
+ "votes": {},
274
+ },
245
275
  ],
246
276
  },
247
277
  }
248
278
 
249
279
 
280
+ _CLI_EPILOG = r"""Usage:
281
+ okstra convergence seed --groups <path> --work-state <path> \
282
+ --final-state <path> --migration-dir <path> [--restart-from-round0]
283
+ okstra convergence plan-round --work-state <path> --plan <path>
284
+ okstra convergence apply-round --work-state <path> --plan <path> \
285
+ --results <path>
286
+ okstra convergence critic-prompt --run-manifest <path>
287
+ okstra convergence apply-critic-gaps --work-state <path> --results <path>
288
+ okstra convergence finalize --work-state <path> --output <path>
289
+ okstra convergence validate --state <path> --kind <working|final>
290
+ okstra convergence example --kind <groups|round-results|critic-results>
291
+
292
+ Working lifecycle:
293
+ groups v1.0/v2.0 → work v1.0/v2.0 → round-plan/results v1.0 → final v1.3
294
+ historical final readers: v1.0, v1.1, v1.2
295
+
296
+ This internal admin command is consumed by the Phase 5.5 lead contract. It
297
+ does not install or revive the obsolete okstra-convergence skill.
298
+ """
299
+
300
+
250
301
  def _parser() -> argparse.ArgumentParser:
251
302
  parser = argparse.ArgumentParser(
303
+ epilog=_CLI_EPILOG,
304
+ formatter_class=argparse.RawDescriptionHelpFormatter,
252
305
  prog="okstra convergence",
253
306
  description="Advance and validate deterministic re-verification state.",
254
- epilog=(
255
- "Working lifecycle: groups v1.0/v2.0 → work v1.0/v2.0 → "
256
- "round-plan/results v1.0 → final v1.3\n"
257
- "historical final readers: v1.0, v1.1, v1.2"
258
- ),
259
- formatter_class=argparse.RawDescriptionHelpFormatter,
260
307
  )
261
308
  subparsers = parser.add_subparsers(dest="operation", required=True)
262
309
 
@@ -323,6 +370,21 @@ def _parser() -> argparse.ArgumentParser:
323
370
  apply.add_argument("--plan", type=Path, required=True)
324
371
  apply.add_argument("--results", type=Path, required=True)
325
372
 
373
+ critic_prompt = subparsers.add_parser(
374
+ "critic-prompt",
375
+ help="render the coverage-critic task instructions to stdout",
376
+ description=(
377
+ "Print the coverage-critic seed body. The lead writes it verbatim "
378
+ "to the task-instructions file the prompt materializer's "
379
+ "`--instruction` takes — it carries the Round 0 finding list, one "
380
+ "line per Phase 4 analyser result, the prior plan's already-covered "
381
+ "id index when this task has one, and the two mandates. Nothing "
382
+ "here is hand-written."
383
+ ),
384
+ formatter_class=argparse.RawDescriptionHelpFormatter,
385
+ )
386
+ critic_prompt.add_argument("--run-manifest", type=Path, required=True)
387
+
326
388
  apply_critic = subparsers.add_parser(
327
389
  "apply-critic-gaps",
328
390
  help="apply one coverage-critic verification batch",
@@ -1189,6 +1251,32 @@ def _convergence_config(authority: RunArtifactAuthority) -> Mapping[str, Any]:
1189
1251
  return config
1190
1252
 
1191
1253
 
1254
+ def _critic_prompt(args: argparse.Namespace) -> str:
1255
+ """coverage-critic 지시문 본문. 입력은 이 run 의 매니페스트 하나뿐이다."""
1256
+ authority = validated_run_authority(args.run_manifest)
1257
+ groups_path = authority.run_dir / "state" / _canonical_run_artifact_name(
1258
+ "convergence-groups", authority.task_type, authority.state_sequence
1259
+ )
1260
+ if not groups_path.is_file():
1261
+ raise ConvergenceContractError(
1262
+ "the critic seed needs the Round 0 grouping; run "
1263
+ f"`okstra convergence prepare-groups` first: {groups_path}"
1264
+ )
1265
+ groups = load_owned_json_object(groups_path)
1266
+ return critic_prompt_body(
1267
+ groups,
1268
+ analysis_packet_path=_manifest_authority_string(
1269
+ authority.payload, "analysisPacketPath"
1270
+ ),
1271
+ results=analyser_results(
1272
+ groups,
1273
+ project_root=authority.project_root,
1274
+ run_dir=authority.run_dir,
1275
+ ),
1276
+ covered=covered_index(authority.project_root, authority.run_dir),
1277
+ )
1278
+
1279
+
1192
1280
  def _prepare_groups(args: argparse.Namespace) -> tuple[str, Path]:
1193
1281
  authority = validated_run_authority(args.run_manifest)
1194
1282
  manifest = authority.payload
@@ -1253,6 +1341,9 @@ def main(argv: list[str] | None = None) -> int:
1253
1341
  )
1254
1342
  return 0
1255
1343
  try:
1344
+ if args.operation == "critic-prompt":
1345
+ print(_critic_prompt(args), end="")
1346
+ return 0
1256
1347
  action, path = _execute(args)
1257
1348
  except (ConvergenceContractError, VerdictBlockError,
1258
1349
  json.JSONDecodeError, ValueError) as exc:
@@ -0,0 +1,345 @@
1
+ """Coverage-critic 시드 프롬프트를 디스크의 사실에서 결정적으로 렌더한다.
2
+
3
+ Phase 5.5 의 critic 은 리드가 손으로 쓴 지시문을 받아 왔다. 시드가 Round 0
4
+ finding 목록뿐이라 critic 은 "이미 다뤄진 것"을 알 방법이 없었고, 직전 리포트가
5
+ 이미 덮은 항목을 갭으로 다시 제안했다. 그 재제안 한 건의 값은 gap 검증 라운드
6
+ 하나이고, 그 라운드는 직렬 경로 위에 있다.
7
+
8
+ 이 모듈은 그 시드를 코드로 만든다. 네 덩어리를 싣는다.
9
+
10
+ - Round 0 통합 finding 목록 (리드가 붙여 넣던 것과 같은 내용).
11
+ - Phase 4 분석자별 결과 파일 경로와 그 워커가 낸 finding id — "이름을 댄 결과를
12
+ 열어라"는 지시가 실제 파일을 가리키게 한다.
13
+ - 직전 implementation-planning 리포트가 있으면(재실행) 그 리포트의 커버리지 행
14
+ id, clarification 행 id, stage 제목만 뽑은 "이미 다뤄진 것" 색인. **본문은
15
+ 절대 싣지 않는다** — 283K 리포트를 통째로 실었다가 report-writer 가 timeout
16
+ 난 적이 있다.
17
+ - 두 갈래 지시문(누락 / `unrequested-scope`)과 중복 선언 규칙.
18
+
19
+ 산출물은 프롬프트 materializer 의 `--instruction` 이 받는 task 지시문 파일
20
+ 본문이다(`prompts/lead/convergence.md` §"Dispatch (fresh one-shot)"). 그래서 그
21
+ 계약이 BLOCKING 으로 요구하는 두 줄(`**Prompt Delivery Mode:**`, `## Inputs`
22
+ 아래의 Primary analysis packet)도 이 본문이 직접 낸다 — 리드가 손으로 덧붙이던
23
+ 자리다.
24
+ """
25
+ from __future__ import annotations
26
+
27
+ from dataclasses import dataclass
28
+ from pathlib import Path
29
+ from typing import Any, Mapping, Sequence
30
+
31
+ from .convergence_provenance import worker_result_suffix
32
+ from .fixed_text import line
33
+ from .json_boundary import JsonBoundaryError, load_owned_object
34
+ from .paths import RunRef
35
+ from .plan_run_root import list_implementation_planning_reports
36
+ from .stage_map import StageMapError, parse_stage_map_file
37
+
38
+
39
+ PROMPT_DELIVERY_MODE = "eager-include"
40
+ PLANNING_TASK_TYPE = "implementation-planning"
41
+
42
+
43
+ class CriticPromptError(ValueError):
44
+ """시드를 결정적으로 만들 수 없다."""
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class AnalyserResult:
49
+ """한 Phase 4 분석자가 남긴 결과 파일과 그 워커가 낸 finding id."""
50
+
51
+ worker_id: str
52
+ result_path: str
53
+ finding_ids: tuple[str, ...]
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class CoveredIndex:
58
+ """직전 계획이 이미 덮은 것의 id 색인. 본문은 담지 않는다.
59
+
60
+ ``stage_titles`` 의 ``None`` 은 "Stage Map 을 못 읽었다" 이고 빈 튜플과 다르다
61
+ — 계획의 stage 번호는 `1..N` 이라 0 개인 계획은 없으므로, 못 읽은 것을 0 으로
62
+ 쓰면 critic 에게 거짓을 말하게 된다(`stage_ledger.build_stage_ledger` 가 같은
63
+ 이유로 세 결과를 구분한다).
64
+ """
65
+
66
+ plan_path: str
67
+ requirement_ids: tuple[str, ...]
68
+ clarification_ids: tuple[str, ...]
69
+ stage_titles: tuple[tuple[int, str], ...] | None
70
+
71
+
72
+ def _nonempty_string(value: Any) -> str:
73
+ return value if isinstance(value, str) and value.strip() else ""
74
+
75
+
76
+ def _project_relative(project_root: Path, path: Path) -> str:
77
+ try:
78
+ return path.resolve().relative_to(Path(project_root).resolve()).as_posix()
79
+ except ValueError:
80
+ # expected-miss: 프로젝트 루트 밖의 경로 = 상대화 대상이 아니다. run 트리
81
+ # 밖을 가리키는 것은 정상 분기이며, 그때는 있는 그대로 적는다.
82
+ return path.as_posix()
83
+
84
+
85
+ def _analysis_roster(groups: Mapping[str, Any]) -> list[str]:
86
+ workers = groups.get("workers")
87
+ if not isinstance(workers, list):
88
+ return []
89
+ return [
90
+ worker["workerId"]
91
+ for worker in workers
92
+ if isinstance(worker, Mapping)
93
+ and worker.get("audience") == "analysis"
94
+ and _nonempty_string(worker.get("workerId"))
95
+ ]
96
+
97
+
98
+ def _findings_by_worker(groups: Mapping[str, Any]) -> dict[str, list[str]]:
99
+ """워커 → 그 워커가 출처인 finding id. 그룹 순서를 유지한다.
100
+
101
+ `sourceItems` 와 `discoveredBy` 를 합집합으로 본다 — 그룹 파서
102
+ (`convergence_engine._parse_group`)가 출처 워커를 그렇게 센다. 한쪽만 보면
103
+ 다른 쪽에만 적힌 워커의 결과 파일이 "낸 것 없음"으로 보인다.
104
+ """
105
+ by_worker: dict[str, list[str]] = {}
106
+
107
+ def _record(worker: Any, finding_id: str) -> None:
108
+ name = _nonempty_string(worker)
109
+ if name and finding_id not in by_worker.setdefault(name, []):
110
+ by_worker[name].append(finding_id)
111
+
112
+ for group in groups.get("groups") or []:
113
+ if not isinstance(group, Mapping):
114
+ continue
115
+ finding_id = _nonempty_string(group.get("findingId"))
116
+ if not finding_id:
117
+ continue
118
+ for item in group.get("sourceItems") or []:
119
+ source = item.get("worker") if isinstance(item, Mapping) else None
120
+ _record(source, finding_id)
121
+ discovered = group.get("discoveredBy")
122
+ for worker in discovered if isinstance(discovered, Mapping) else {}:
123
+ _record(worker, finding_id)
124
+ return by_worker
125
+
126
+
127
+ def analyser_results(
128
+ groups: Mapping[str, Any],
129
+ *,
130
+ project_root: Path,
131
+ run_dir: Path,
132
+ ) -> list[AnalyserResult]:
133
+ """분석 audience 워커마다 결과 파일 경로와 finding id 를 짝지어 돌려준다.
134
+
135
+ 결과 파일명은 `convergence_provenance` 가 이미 소유한 규칙을 그대로 쓴다 —
136
+ `<worker>-worker-<task-type>-<workerResults seq>.md`. state 시퀀스와 다른
137
+ 카운터라 그룹 파일명에서 접미사를 빌려 오면 빗나간다.
138
+ """
139
+ suffix = worker_result_suffix(Path(run_dir), groups)
140
+ if suffix is None:
141
+ raise CriticPromptError(
142
+ "cannot resolve the worker-result suffix from the grouping's "
143
+ "runManifestPath"
144
+ )
145
+ results_dir = Path(run_dir) / "worker-results"
146
+ by_worker = _findings_by_worker(groups)
147
+ return [
148
+ AnalyserResult(
149
+ worker_id=worker,
150
+ result_path=_project_relative(
151
+ project_root, results_dir / f"{worker}-worker-{suffix}.md"
152
+ ),
153
+ finding_ids=tuple(by_worker.get(worker, ())),
154
+ )
155
+ for worker in _analysis_roster(groups)
156
+ ]
157
+
158
+
159
+ def covered_index(project_root: Path, run_dir: Path) -> CoveredIndex | None:
160
+ """직전 implementation-planning 리포트의 id 색인. 없으면 ``None``.
161
+
162
+ Phase 5.5 는 Phase 6 보다 앞이라 이 run 의 리포트는 아직 없다. 그래서
163
+ reports 디렉터리의 최신 리포트는 언제나 **직전** run 의 것이다.
164
+
165
+ 계획 run 에서만 싣는다. 다른 phase 의 finding 은 계획의 커버리지 행이 덮는
166
+ 대상이 아니라서, 같은 목록을 "이미 다뤄진 것"으로 보여 주면 critic 이 실재하는
167
+ 갭을 중복으로 접는다.
168
+ """
169
+ try:
170
+ ref = RunRef.from_run_dir(Path(run_dir))
171
+ except ValueError:
172
+ # expected-miss: `runs/` 앵커가 없는 경로 = okstra run 트리가 아니다.
173
+ # 그런 곳에는 직전 계획도 없다.
174
+ return None
175
+ if ref.task_type != PLANNING_TASK_TYPE:
176
+ return None
177
+ reports_dir = RunRef.from_task_root(
178
+ ref.task_root, PLANNING_TASK_TYPE
179
+ ).reports_dir
180
+ if not reports_dir.is_dir():
181
+ return None
182
+ reports = list_implementation_planning_reports(reports_dir)
183
+ if not reports:
184
+ return None
185
+ report = reports[0]
186
+ try:
187
+ data = load_owned_object(report, artifact="planning final report")
188
+ except (OSError, UnicodeError, JsonBoundaryError) as exc:
189
+ raise CriticPromptError(
190
+ f"prior planning report is unreadable: {report} ({exc})"
191
+ ) from exc
192
+ if not isinstance(data, Mapping):
193
+ raise CriticPromptError(f"prior planning report is not an object: {report}")
194
+ planning = data.get("implementationPlanning")
195
+ planning = planning if isinstance(planning, Mapping) else {}
196
+ try:
197
+ stages: list[Any] | None = parse_stage_map_file(report)
198
+ except StageMapError:
199
+ # expected-miss: schema v1 마크다운 계획이거나 Stage Map 이 깨졌다. 둘 다
200
+ # 나머지 색인(요구사항·clarification id)은 그대로 쓸 수 있는 분기이므로,
201
+ # 여기서 멈추는 대신 "못 읽었다"를 그대로 실어 보낸다.
202
+ stages = None
203
+ return CoveredIndex(
204
+ plan_path=_project_relative(project_root, report),
205
+ requirement_ids=tuple(
206
+ _nonempty_string(row.get("id"))
207
+ for row in planning.get("requirementCoverage") or []
208
+ if isinstance(row, Mapping) and _nonempty_string(row.get("id"))
209
+ ),
210
+ clarification_ids=tuple(
211
+ _nonempty_string(row.get("id"))
212
+ for row in data.get("clarificationItems") or []
213
+ if isinstance(row, Mapping) and _nonempty_string(row.get("id"))
214
+ ),
215
+ stage_titles=None if stages is None else tuple(
216
+ (stage.stage_number, stage.title) for stage in stages
217
+ ),
218
+ )
219
+
220
+
221
+ _MANDATES = """
222
+ Your job has exactly two halves. Answer both.
223
+
224
+ (1) MISSING — name what nobody covered:
225
+ - files / directories / execution paths nobody inspected,
226
+ - requirements or acceptance points with zero findings,
227
+ - claims raised but never verified.
228
+ For each, emit a NEW finding with evidence (file:line or the requirement quote).
229
+
230
+ (2) UNREQUESTED — name work these findings propose that no requirement asked for:
231
+ - a finding whose proposed change serves no requirement, scope item, or
232
+ acceptance point you can QUOTE from the analysis packet,
233
+ - an abstraction, configuration knob, or generalization proposed for a caller or
234
+ a case nobody has stated,
235
+ - a rewrite, migration, or cleanup of code the requirements never mention.
236
+ For each, emit a candidate with `category: "unrequested-scope"`, quote the
237
+ proposed work verbatim, and state which requirement you searched for and did not
238
+ find.
239
+
240
+ Do NOT restate an existing finding. An overlap with an existing finding is
241
+ declared as `duplicateOf` naming that finding id, not restated as a new
242
+ candidate — a restated finding costs a whole verification round to reject, while
243
+ a declared duplicate costs none. Judge (2) against the analysis packet's
244
+ requirements and scope, never against your own preference for how the code
245
+ should look — "I would have done it differently" is not unrequested work, and
246
+ neither is work the packet authorizes but you consider unnecessary. If a half
247
+ has nothing, say so explicitly for that half; silence on one half is an
248
+ incomplete result.
249
+
250
+ Read before proposing anything: the analysis packet named above for requirements
251
+ and phase scope, the Round 0 findings below, and the named result file of any
252
+ analyser whose coverage you intend to call incomplete. Do NOT infer missing
253
+ verification from a one-line summary.
254
+ """.strip()
255
+
256
+
257
+ def critic_prompt_body(
258
+ groups: Mapping[str, Any],
259
+ *,
260
+ analysis_packet_path: str,
261
+ results: Sequence[AnalyserResult],
262
+ covered: CoveredIndex | None,
263
+ ) -> str:
264
+ """critic 지시문 파일 본문. 같은 입력이면 같은 바이트를 낸다."""
265
+ task_key = _nonempty_string(groups.get("taskKey"))
266
+ if not task_key:
267
+ raise CriticPromptError("convergence groups carry no taskKey")
268
+ packet = _nonempty_string(analysis_packet_path)
269
+ if not packet.endswith("analysis-packet.md"):
270
+ raise CriticPromptError(
271
+ "run manifest analysisPacketPath must name analysis-packet.md; "
272
+ f"found `{analysis_packet_path}`"
273
+ )
274
+ rows: list[str] = [
275
+ f"**Prompt Delivery Mode:** {PROMPT_DELIVERY_MODE}\n",
276
+ "\n",
277
+ f"# Coverage critic — {task_key}\n",
278
+ "\n",
279
+ "## Inputs\n",
280
+ f"- Primary analysis packet: `{packet}`\n",
281
+ "\n",
282
+ "## Mandate\n",
283
+ "\n",
284
+ _MANDATES,
285
+ "\n\n",
286
+ ]
287
+ rows.extend(_round0_rows(groups))
288
+ rows.extend(_analyser_rows(results))
289
+ rows.extend(_covered_rows(covered))
290
+ return "".join(rows)
291
+
292
+
293
+ def _round0_rows(groups: Mapping[str, Any]) -> list[str]:
294
+ findings = [
295
+ group for group in groups.get("groups") or [] if isinstance(group, Mapping)
296
+ ]
297
+ rows = ["## Round 0 consolidated findings\n", line("Finding count", len(findings))]
298
+ for group in findings:
299
+ rows.append(f"\n### {_nonempty_string(group.get('findingId')) or '-'}\n")
300
+ rows.append(line("Summary", group.get("summary")))
301
+ rows.append(line("Category", group.get("category")))
302
+ rows.append(line("Ticket ids", ", ".join(group.get("ticketIds") or [])))
303
+ rows.append(line("Origin worker", group.get("originWorker")))
304
+ rows.append(line("Origin evidence", group.get("originEvidence")))
305
+ rows.append("\n")
306
+ return rows
307
+
308
+
309
+ def _analyser_rows(results: Sequence[AnalyserResult]) -> list[str]:
310
+ rows = [
311
+ "## Phase 4 analyser results\n",
312
+ line("Analyser count", len(results)),
313
+ ]
314
+ for index, result in enumerate(results, 1):
315
+ rows.append(line(f"Analyser {index} worker id", result.worker_id))
316
+ rows.append(line(f"Analyser {index} result path", result.result_path))
317
+ rows.append(
318
+ line(f"Analyser {index} finding ids", ", ".join(result.finding_ids))
319
+ )
320
+ rows.append("\n")
321
+ return rows
322
+
323
+
324
+ def _covered_rows(covered: CoveredIndex | None) -> list[str]:
325
+ if covered is None:
326
+ return []
327
+ rows = [
328
+ "## Already covered by the prior plan\n",
329
+ "\n",
330
+ "Ids and titles only. A candidate that repeats one of these is a "
331
+ "duplicate: declare it with `duplicateOf`, or do not raise it.\n",
332
+ "\n",
333
+ line("Prior plan path", covered.plan_path),
334
+ line("Requirement coverage row ids", ", ".join(covered.requirement_ids)),
335
+ line("Clarification row ids", ", ".join(covered.clarification_ids)),
336
+ ]
337
+ if covered.stage_titles is None:
338
+ rows.append(line("Stage map", "unreadable"))
339
+ rows.append("\n")
340
+ return rows
341
+ rows.append(line("Stage count", len(covered.stage_titles)))
342
+ for number, title in covered.stage_titles:
343
+ rows.append(line(f"Stage {number} title", title))
344
+ rows.append("\n")
345
+ return rows