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
@@ -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,
@@ -206,7 +212,18 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
206
212
  "ticketIds": ["TASK-001"],
207
213
  "originEvidence": "No rollback query result is present",
208
214
  "evidenceArtifacts": [_EVIDENCE_ARTIFACT],
209
- }
215
+ },
216
+ {
217
+ # 이미 있는 finding 과 겹치면 다시 쓰지 않고 그 id 를 댄다.
218
+ # 다시 쓴 finding 은 기각에 검증 라운드 하나를 쓰고, 선언된
219
+ # 중복은 하나도 쓰지 않는다.
220
+ "candidateId": "CG-002",
221
+ "summary": "The migration assumption needs a live row",
222
+ "category": "missing",
223
+ "ticketIds": ["TASK-001"],
224
+ "originEvidence": "The same live row F-001 already cites",
225
+ "duplicateOf": "F-001",
226
+ },
210
227
  ],
211
228
  },
212
229
  "coverage-batch": {
@@ -215,11 +232,14 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
215
232
  "mode": "coverage",
216
233
  # The critic's own provider never votes on its gaps, so `provider` here
217
234
  # is deliberately absent from `dispatches[]`.
235
+ #
236
+ # 검증 대상 gap 이 하나이므로 배정도 한 명이다 — 로스터 순서 위
237
+ # round-robin 이 G-001 을 claude-worker 에게 준다. codex-worker 는 이
238
+ # 라운드에서 열리지 않는다.
218
239
  "provider": "antigravity",
219
240
  "modelExecutionValue": "gemini-3.1-pro",
220
241
  "dispatches": [
221
242
  {"worker": "claude-worker", "status": "completed", "durationMs": 1250},
222
- {"worker": "codex-worker", "status": "completed", "durationMs": 980},
223
243
  ],
224
244
  "gaps": [
225
245
  {
@@ -235,28 +255,50 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
235
255
  "disagreeBasis": None,
236
256
  "explanation": "The plan cites no rollback verification step",
237
257
  },
238
- "codex-worker": {
239
- "verdict": "agree",
240
- "disagreeBasis": None,
241
- "explanation": "No rollback assertion appears in the stage map",
242
- },
243
258
  },
244
- }
259
+ },
260
+ {
261
+ # 선언된 중복은 검증 라운드에 들어가지 않으므로 표가 없다.
262
+ "gapId": "G-002",
263
+ "summary": "The migration assumption needs a live row",
264
+ "category": "missing",
265
+ "ticketIds": ["TASK-001"],
266
+ "originEvidence": "The same live row F-001 already cites",
267
+ "duplicateOf": "F-001",
268
+ "votes": {},
269
+ },
245
270
  ],
246
271
  },
247
272
  }
248
273
 
249
274
 
275
+ _CLI_EPILOG = r"""Usage:
276
+ okstra convergence seed --groups <path> --work-state <path> \
277
+ --final-state <path> --migration-dir <path> [--restart-from-round0]
278
+ okstra convergence plan-round --work-state <path> --plan <path>
279
+ okstra convergence apply-round --work-state <path> --plan <path> \
280
+ --results <path>
281
+ okstra convergence critic-prompt --run-manifest <path>
282
+ okstra convergence apply-critic-gaps --work-state <path> --results <path>
283
+ okstra convergence finalize --work-state <path> --output <path>
284
+ okstra convergence validate --state <path> --kind <working|final>
285
+ okstra convergence example --kind <groups|round-results|critic-results>
286
+
287
+ Working lifecycle:
288
+ groups v1.0/v2.0 → work v1.0/v2.0 → round-plan/results v1.0 → final v1.3
289
+ historical final readers: v1.0, v1.1, v1.2
290
+
291
+ This internal admin command is consumed by the Phase 5.5 lead contract. It
292
+ does not install or revive the obsolete okstra-convergence skill.
293
+ """
294
+
295
+
250
296
  def _parser() -> argparse.ArgumentParser:
251
297
  parser = argparse.ArgumentParser(
298
+ epilog=_CLI_EPILOG,
299
+ formatter_class=argparse.RawDescriptionHelpFormatter,
252
300
  prog="okstra convergence",
253
301
  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
302
  )
261
303
  subparsers = parser.add_subparsers(dest="operation", required=True)
262
304
 
@@ -323,6 +365,21 @@ def _parser() -> argparse.ArgumentParser:
323
365
  apply.add_argument("--plan", type=Path, required=True)
324
366
  apply.add_argument("--results", type=Path, required=True)
325
367
 
368
+ critic_prompt = subparsers.add_parser(
369
+ "critic-prompt",
370
+ help="render the coverage-critic task instructions to stdout",
371
+ description=(
372
+ "Print the coverage-critic seed body. The lead writes it verbatim "
373
+ "to the task-instructions file the prompt materializer's "
374
+ "`--instruction` takes — it carries the Round 0 finding list, one "
375
+ "line per Phase 4 analyser result, the prior plan's already-covered "
376
+ "id index when this task has one, and the two mandates. Nothing "
377
+ "here is hand-written."
378
+ ),
379
+ formatter_class=argparse.RawDescriptionHelpFormatter,
380
+ )
381
+ critic_prompt.add_argument("--run-manifest", type=Path, required=True)
382
+
326
383
  apply_critic = subparsers.add_parser(
327
384
  "apply-critic-gaps",
328
385
  help="apply one coverage-critic verification batch",
@@ -1189,6 +1246,32 @@ def _convergence_config(authority: RunArtifactAuthority) -> Mapping[str, Any]:
1189
1246
  return config
1190
1247
 
1191
1248
 
1249
+ def _critic_prompt(args: argparse.Namespace) -> str:
1250
+ """coverage-critic 지시문 본문. 입력은 이 run 의 매니페스트 하나뿐이다."""
1251
+ authority = validated_run_authority(args.run_manifest)
1252
+ groups_path = authority.run_dir / "state" / _canonical_run_artifact_name(
1253
+ "convergence-groups", authority.task_type, authority.state_sequence
1254
+ )
1255
+ if not groups_path.is_file():
1256
+ raise ConvergenceContractError(
1257
+ "the critic seed needs the Round 0 grouping; run "
1258
+ f"`okstra convergence prepare-groups` first: {groups_path}"
1259
+ )
1260
+ groups = load_owned_json_object(groups_path)
1261
+ return critic_prompt_body(
1262
+ groups,
1263
+ analysis_packet_path=_manifest_authority_string(
1264
+ authority.payload, "analysisPacketPath"
1265
+ ),
1266
+ results=analyser_results(
1267
+ groups,
1268
+ project_root=authority.project_root,
1269
+ run_dir=authority.run_dir,
1270
+ ),
1271
+ covered=covered_index(authority.project_root, authority.run_dir),
1272
+ )
1273
+
1274
+
1192
1275
  def _prepare_groups(args: argparse.Namespace) -> tuple[str, Path]:
1193
1276
  authority = validated_run_authority(args.run_manifest)
1194
1277
  manifest = authority.payload
@@ -1253,6 +1336,9 @@ def main(argv: list[str] | None = None) -> int:
1253
1336
  )
1254
1337
  return 0
1255
1338
  try:
1339
+ if args.operation == "critic-prompt":
1340
+ print(_critic_prompt(args), end="")
1341
+ return 0
1256
1342
  action, path = _execute(args)
1257
1343
  except (ConvergenceContractError, VerdictBlockError,
1258
1344
  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