okstra 0.186.6 → 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 (548) 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 +487 -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 +32 -7
  232. package/runtime/python/okstra_ctl/report_finalize.py +206 -15
  233. package/runtime/python/okstra_ctl/report_html/common.py +4 -9
  234. package/runtime/python/okstra_ctl/report_html/render.py +11 -12
  235. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -5
  236. package/runtime/python/okstra_ctl/report_language.py +8 -4
  237. package/runtime/python/okstra_ctl/report_narrative.py +90 -3
  238. package/runtime/python/okstra_ctl/report_projections.py +8 -2
  239. package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
  240. package/runtime/python/okstra_ctl/report_translation.py +2 -35
  241. package/runtime/python/okstra_ctl/report_views.py +4 -22
  242. package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
  243. package/runtime/python/okstra_ctl/rollup.py +13 -0
  244. package/runtime/python/okstra_ctl/run.py +639 -138
  245. package/runtime/python/okstra_ctl/run_audit.py +13 -0
  246. package/runtime/python/okstra_ctl/run_index_row.py +0 -12
  247. package/runtime/python/okstra_ctl/seeding.py +21 -11
  248. package/runtime/python/okstra_ctl/sequence.py +1 -1
  249. package/runtime/python/okstra_ctl/session.py +64 -14
  250. package/runtime/python/okstra_ctl/set_work_status.py +18 -0
  251. package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
  252. package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
  253. package/runtime/python/okstra_ctl/stage_map.py +57 -11
  254. package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
  255. package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
  256. package/runtime/python/okstra_ctl/stage_targets.py +3 -12
  257. package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
  258. package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
  259. package/runtime/python/okstra_ctl/task_target.py +4 -16
  260. package/runtime/python/okstra_ctl/team.py +49 -2
  261. package/runtime/python/okstra_ctl/time_report.py +17 -2
  262. package/runtime/python/okstra_ctl/usage_report.py +11 -0
  263. package/runtime/python/okstra_ctl/user_response.py +55 -218
  264. package/runtime/python/okstra_ctl/user_response_values.py +242 -0
  265. package/runtime/python/okstra_ctl/validation_contract.py +3 -0
  266. package/runtime/python/okstra_ctl/verification_target.py +68 -0
  267. package/runtime/python/okstra_ctl/wizard.py +457 -92
  268. package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
  269. package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
  270. package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
  271. package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
  272. package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
  273. package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
  274. package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
  275. package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
  276. package/runtime/python/okstra_ctl/worker_request.py +2 -1
  277. package/runtime/python/okstra_ctl/worker_runner.py +16 -9
  278. package/runtime/python/okstra_ctl/worker_state.py +16 -1
  279. package/runtime/python/okstra_ctl/workflow.py +18 -1
  280. package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
  281. package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
  282. package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
  283. package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
  284. package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
  285. package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
  286. package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
  287. package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
  288. package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
  289. package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
  290. package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
  291. package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
  292. package/runtime/python/okstra_ctl/write_policy.py +90 -213
  293. package/runtime/python/okstra_project/__init__.py +0 -4
  294. package/runtime/python/okstra_project/dirs.py +2 -2
  295. package/runtime/python/okstra_project/phase_pointer.py +84 -0
  296. package/runtime/python/okstra_project/slug.py +24 -0
  297. package/runtime/python/okstra_project/state.py +22 -230
  298. package/runtime/python/okstra_token_usage/claude.py +9 -1
  299. package/runtime/python/okstra_token_usage/cli.py +3 -1
  300. package/runtime/python/okstra_token_usage/collect.py +71 -10
  301. package/runtime/python/okstra_token_usage/cursor.py +2 -0
  302. package/runtime/python/okstra_token_usage/grok.py +1 -5
  303. package/runtime/python/okstra_token_usage/paths.py +26 -0
  304. package/runtime/python/okstra_token_usage/pricing.py +19 -7
  305. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
  306. package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
  307. package/runtime/schemas/final-report-v2.0.schema.json +13 -26
  308. package/runtime/schemas/final-report-v3.0.schema.json +177 -41
  309. package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
  310. package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
  311. package/runtime/skills/okstra-container-build/SKILL.md +16 -47
  312. package/runtime/skills/okstra-inspect/facets/history.md +1 -1
  313. package/runtime/skills/okstra-inspect/facets/status.md +7 -6
  314. package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
  315. package/runtime/skills/okstra-run/SKILL.md +7 -5
  316. package/runtime/templates/report-writer-prompt-preamble.md +1 -1
  317. package/runtime/templates/reports/brief.template.md +6 -2
  318. package/runtime/templates/reports/error-analysis-input.template.md +2 -0
  319. package/runtime/templates/reports/final-verification-input.template.md +2 -0
  320. package/runtime/templates/reports/html/base.template.html +13 -0
  321. package/runtime/templates/reports/html/i18n/en.json +19 -0
  322. package/runtime/templates/reports/html/i18n/ko.json +19 -0
  323. package/runtime/templates/reports/html/tasks/final-verification.template.html +2 -2
  324. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +19 -9
  325. package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
  326. package/runtime/templates/reports/implementation-input.template.md +7 -1
  327. package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
  328. package/runtime/templates/reports/quick-input.template.md +2 -0
  329. package/runtime/templates/reports/release-handoff-input.template.md +6 -1
  330. package/runtime/templates/reports/report.js +20 -3
  331. package/runtime/templates/reports/schedule.template.md +2 -0
  332. package/runtime/templates/reports/settings.template.json +0 -10
  333. package/runtime/templates/reports/task-brief.template.md +3 -0
  334. package/runtime/templates/reports/user-response.template.md +7 -3
  335. package/runtime/validators/checks/fixtures-01.py +57 -0
  336. package/runtime/validators/checks/fixtures-02.py +565 -0
  337. package/runtime/validators/checks/runners-01.py +108 -0
  338. package/runtime/validators/checks/validate-assets-01.py +60 -0
  339. package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
  340. package/runtime/validators/checks/validate-tasks-01.py +46 -0
  341. package/runtime/validators/checks/validate-tasks-02.py +85 -0
  342. package/runtime/validators/checks/validate-tasks-03.py +61 -0
  343. package/runtime/validators/checks/validate-tasks-04.py +118 -0
  344. package/runtime/validators/forbidden_actions.py +73 -3
  345. package/runtime/validators/lib/common.sh +5 -0
  346. package/runtime/validators/lib/fixtures.sh +7 -591
  347. package/runtime/validators/lib/paths.sh +13 -0
  348. package/runtime/validators/lib/runners.sh +6 -104
  349. package/runtime/validators/lib/summary.sh +1 -1
  350. package/runtime/validators/lib/validate-assets.sh +6 -56
  351. package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
  352. package/runtime/validators/lib/validate-tasks.sh +9 -294
  353. package/runtime/validators/validate-implementation-plan-stages.py +4 -4
  354. package/runtime/validators/validate-run.py +1369 -2654
  355. package/runtime/validators/validate-workflow.sh +56 -16
  356. package/runtime/validators/validate_improvement_report.py +2 -1
  357. package/runtime/validators/validate_session_conformance.py +295 -49
  358. package/dist/commands/execute/agent-prompt.d.mts +0 -1
  359. package/dist/commands/execute/agent-prompt.mjs +0 -24
  360. package/dist/commands/execute/agent-prompt.mjs.map +0 -1
  361. package/dist/commands/execute/codex-dispatch.d.mts +0 -3
  362. package/dist/commands/execute/codex-dispatch.mjs +0 -6
  363. package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
  364. package/dist/commands/execute/codex-run.d.mts +0 -3
  365. package/dist/commands/execute/codex-run.mjs +0 -62
  366. package/dist/commands/execute/codex-run.mjs.map +0 -1
  367. package/dist/commands/execute/convergence.d.mts +0 -1
  368. package/dist/commands/execute/convergence.mjs +0 -38
  369. package/dist/commands/execute/convergence.mjs.map +0 -1
  370. package/dist/commands/execute/error-log.d.mts +0 -1
  371. package/dist/commands/execute/error-log.mjs +0 -18
  372. package/dist/commands/execute/error-log.mjs.map +0 -1
  373. package/dist/commands/execute/git-reconcile.d.mts +0 -1
  374. package/dist/commands/execute/git-reconcile.mjs +0 -30
  375. package/dist/commands/execute/git-reconcile.mjs.map +0 -1
  376. package/dist/commands/execute/handoff.d.mts +0 -1
  377. package/dist/commands/execute/handoff.mjs +0 -31
  378. package/dist/commands/execute/handoff.mjs.map +0 -1
  379. package/dist/commands/execute/incremental-carry.d.mts +0 -1
  380. package/dist/commands/execute/incremental-carry.mjs +0 -20
  381. package/dist/commands/execute/incremental-carry.mjs.map +0 -1
  382. package/dist/commands/execute/incremental-scope.d.mts +0 -1
  383. package/dist/commands/execute/incremental-scope.mjs +0 -29
  384. package/dist/commands/execute/incremental-scope.mjs.map +0 -1
  385. package/dist/commands/execute/integrate-stages.d.mts +0 -1
  386. package/dist/commands/execute/integrate-stages.mjs +0 -24
  387. package/dist/commands/execute/integrate-stages.mjs.map +0 -1
  388. package/dist/commands/execute/pane-title.d.mts +0 -1
  389. package/dist/commands/execute/pane-title.mjs +0 -20
  390. package/dist/commands/execute/pane-title.mjs.map +0 -1
  391. package/dist/commands/execute/plan-items.d.mts +0 -1
  392. package/dist/commands/execute/plan-items.mjs +0 -9
  393. package/dist/commands/execute/plan-items.mjs.map +0 -1
  394. package/dist/commands/execute/plan-validate.d.mts +0 -1
  395. package/dist/commands/execute/plan-validate.mjs +0 -68
  396. package/dist/commands/execute/plan-validate.mjs.map +0 -1
  397. package/dist/commands/execute/plan-verify.d.mts +0 -1
  398. package/dist/commands/execute/plan-verify.mjs +0 -43
  399. package/dist/commands/execute/plan-verify.mjs.map +0 -1
  400. package/dist/commands/execute/spawn-followups.d.mts +0 -1
  401. package/dist/commands/execute/spawn-followups.mjs +0 -22
  402. package/dist/commands/execute/spawn-followups.mjs.map +0 -1
  403. package/dist/commands/execute/team.d.mts +0 -3
  404. package/dist/commands/execute/team.mjs +0 -66
  405. package/dist/commands/execute/team.mjs.map +0 -1
  406. package/dist/commands/execute/token-usage.d.mts +0 -1
  407. package/dist/commands/execute/token-usage.mjs +0 -19
  408. package/dist/commands/execute/token-usage.mjs.map +0 -1
  409. package/dist/commands/execute/worker-audit-check.d.mts +0 -1
  410. package/dist/commands/execute/worker-audit-check.mjs +0 -34
  411. package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
  412. package/dist/commands/execute/worker-dispatch.d.mts +0 -7
  413. package/dist/commands/execute/worker-dispatch.mjs +0 -64
  414. package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
  415. package/dist/commands/execute/worker-state.d.mts +0 -1
  416. package/dist/commands/execute/worker-state.mjs +0 -28
  417. package/dist/commands/execute/worker-state.mjs.map +0 -1
  418. package/dist/commands/execute/worktree-lookup.d.mts +0 -1
  419. package/dist/commands/execute/worktree-lookup.mjs +0 -92
  420. package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
  421. package/dist/commands/execute/worktree-status.d.mts +0 -1
  422. package/dist/commands/execute/worktree-status.mjs +0 -121
  423. package/dist/commands/execute/worktree-status.mjs.map +0 -1
  424. package/dist/commands/inspect/code-review.d.mts +0 -1
  425. package/dist/commands/inspect/code-review.mjs +0 -32
  426. package/dist/commands/inspect/code-review.mjs.map +0 -1
  427. package/dist/commands/inspect/container.d.mts +0 -1
  428. package/dist/commands/inspect/container.mjs +0 -25
  429. package/dist/commands/inspect/container.mjs.map +0 -1
  430. package/dist/commands/inspect/context-cost.d.mts +0 -1
  431. package/dist/commands/inspect/context-cost.mjs +0 -25
  432. package/dist/commands/inspect/context-cost.mjs.map +0 -1
  433. package/dist/commands/inspect/design-prep.d.mts +0 -1
  434. package/dist/commands/inspect/design-prep.mjs +0 -22
  435. package/dist/commands/inspect/design-prep.mjs.map +0 -1
  436. package/dist/commands/inspect/error-report.d.mts +0 -1
  437. package/dist/commands/inspect/error-report.mjs +0 -25
  438. package/dist/commands/inspect/error-report.mjs.map +0 -1
  439. package/dist/commands/inspect/error-zip.d.mts +0 -1
  440. package/dist/commands/inspect/error-zip.mjs +0 -24
  441. package/dist/commands/inspect/error-zip.mjs.map +0 -1
  442. package/dist/commands/inspect/log-report.d.mts +0 -1
  443. package/dist/commands/inspect/log-report.mjs +0 -26
  444. package/dist/commands/inspect/log-report.mjs.map +0 -1
  445. package/dist/commands/inspect/model-io.d.mts +0 -1
  446. package/dist/commands/inspect/model-io.mjs +0 -25
  447. package/dist/commands/inspect/model-io.mjs.map +0 -1
  448. package/dist/commands/inspect/profile-show.d.mts +0 -1
  449. package/dist/commands/inspect/profile-show.mjs +0 -28
  450. package/dist/commands/inspect/profile-show.mjs.map +0 -1
  451. package/dist/commands/inspect/recap.d.mts +0 -1
  452. package/dist/commands/inspect/recap.mjs +0 -30
  453. package/dist/commands/inspect/recap.mjs.map +0 -1
  454. package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
  455. package/dist/commands/inspect/resolve-task-key.mjs +0 -24
  456. package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
  457. package/dist/commands/inspect/rollup.d.mts +0 -1
  458. package/dist/commands/inspect/rollup.mjs +0 -25
  459. package/dist/commands/inspect/rollup.mjs.map +0 -1
  460. package/dist/commands/inspect/run-audit.d.mts +0 -1
  461. package/dist/commands/inspect/run-audit.mjs +0 -25
  462. package/dist/commands/inspect/run-audit.mjs.map +0 -1
  463. package/dist/commands/inspect/set-work-status.d.mts +0 -1
  464. package/dist/commands/inspect/set-work-status.mjs +0 -29
  465. package/dist/commands/inspect/set-work-status.mjs.map +0 -1
  466. package/dist/commands/inspect/stage-map.d.mts +0 -1
  467. package/dist/commands/inspect/stage-map.mjs +0 -131
  468. package/dist/commands/inspect/stage-map.mjs.map +0 -1
  469. package/dist/commands/inspect/task-list.d.mts +0 -1
  470. package/dist/commands/inspect/task-list.mjs +0 -149
  471. package/dist/commands/inspect/task-list.mjs.map +0 -1
  472. package/dist/commands/inspect/task-show.d.mts +0 -1
  473. package/dist/commands/inspect/task-show.mjs +0 -108
  474. package/dist/commands/inspect/task-show.mjs.map +0 -1
  475. package/dist/commands/inspect/time-report.d.mts +0 -1
  476. package/dist/commands/inspect/time-report.mjs +0 -24
  477. package/dist/commands/inspect/time-report.mjs.map +0 -1
  478. package/dist/commands/inspect/usage-report.d.mts +0 -1
  479. package/dist/commands/inspect/usage-report.mjs +0 -23
  480. package/dist/commands/inspect/usage-report.mjs.map +0 -1
  481. package/dist/commands/inspect/user-response.d.mts +0 -1
  482. package/dist/commands/inspect/user-response.mjs +0 -35
  483. package/dist/commands/inspect/user-response.mjs.map +0 -1
  484. package/dist/commands/inspect/worker-liveness.d.mts +0 -1
  485. package/dist/commands/inspect/worker-liveness.mjs +0 -45
  486. package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
  487. package/dist/commands/lifecycle/contract-check.d.mts +0 -1
  488. package/dist/commands/lifecycle/contract-check.mjs +0 -18
  489. package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
  490. package/dist/commands/lifecycle/migrate.d.mts +0 -1
  491. package/dist/commands/lifecycle/migrate.mjs +0 -30
  492. package/dist/commands/lifecycle/migrate.mjs.map +0 -1
  493. package/dist/commands/lifecycle/model.d.mts +0 -1
  494. package/dist/commands/lifecycle/model.mjs +0 -22
  495. package/dist/commands/lifecycle/model.mjs.map +0 -1
  496. package/dist/commands/manager.d.mts +0 -3
  497. package/dist/commands/manager.mjs +0 -50
  498. package/dist/commands/manager.mjs.map +0 -1
  499. package/dist/commands/report/agent-activity.d.mts +0 -1
  500. package/dist/commands/report/agent-activity.mjs +0 -20
  501. package/dist/commands/report/agent-activity.mjs.map +0 -1
  502. package/dist/commands/report/approval-decision.d.mts +0 -1
  503. package/dist/commands/report/approval-decision.mjs +0 -21
  504. package/dist/commands/report/approval-decision.mjs.map +0 -1
  505. package/dist/commands/report/design-snapshot.d.mts +0 -1
  506. package/dist/commands/report/design-snapshot.mjs +0 -19
  507. package/dist/commands/report/design-snapshot.mjs.map +0 -1
  508. package/dist/commands/report/finalize.d.mts +0 -4
  509. package/dist/commands/report/finalize.mjs +0 -64
  510. package/dist/commands/report/finalize.mjs.map +0 -1
  511. package/dist/commands/report/inject-report-index.d.mts +0 -1
  512. package/dist/commands/report/inject-report-index.mjs +0 -21
  513. package/dist/commands/report/inject-report-index.mjs.map +0 -1
  514. package/dist/commands/report/render-final-report.d.mts +0 -1
  515. package/dist/commands/report/render-final-report.mjs +0 -23
  516. package/dist/commands/report/render-final-report.mjs.map +0 -1
  517. package/dist/commands/report/render-views.d.mts +0 -1
  518. package/dist/commands/report/render-views.mjs +0 -27
  519. package/dist/commands/report/render-views.mjs.map +0 -1
  520. package/dist/commands/report/translate.d.mts +0 -1
  521. package/dist/commands/report/translate.mjs +0 -33
  522. package/dist/commands/report/translate.mjs.map +0 -1
  523. package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
  524. package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
  525. package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
  526. package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
  527. package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
  528. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
  529. package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
  530. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
  531. package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
  532. package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
  533. package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
  534. package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
  535. package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
  536. package/runtime/bin/okstra-central.sh +0 -152
  537. package/runtime/bin/okstra-incremental-carry.py +0 -10
  538. package/runtime/bin/okstra-incremental-scope.py +0 -10
  539. package/runtime/bin/okstra-team-reconcile.sh +0 -36
  540. package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
  541. package/runtime/python/okstra_ctl/batch.py +0 -60
  542. package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
  543. package/runtime/python/okstra_ctl/container_registry.py +0 -72
  544. package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
  545. package/runtime/python/okstra_ctl/resolver.py +0 -54
  546. package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
  547. package/runtime/python/okstra_ctl/tmux.py +0 -134
  548. package/runtime/python/okstra_ctl/worktree.py +0 -1099
@@ -0,0 +1,108 @@
1
+ """runners-01 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/runners.sh:44
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from pathlib import Path
8
+ import json
9
+ import subprocess
10
+ import sys
11
+
12
+ project_root = Path(sys.argv[1])
13
+ task_manifest_path = project_root / sys.argv[2]
14
+ validator_script = Path(sys.argv[3])
15
+ expected_status = sys.argv[4]
16
+ expected_failure_substring = sys.argv[5]
17
+
18
+ task_manifest = json.loads(task_manifest_path.read_text())
19
+ timeline_path = project_root / task_manifest["historyTimelinePath"]
20
+ timeline = json.loads(timeline_path.read_text())
21
+ runs = timeline.get("runs", [])
22
+ latest_run = None
23
+ if isinstance(runs, list):
24
+ for item in reversed(runs):
25
+ if isinstance(item, dict):
26
+ latest_run = item
27
+ break
28
+
29
+ if latest_run is None:
30
+ raise SystemExit("timeline does not contain a latest run entry")
31
+
32
+ run_manifest_path = project_root / latest_run["runManifestPath"]
33
+ run_manifest = json.loads(run_manifest_path.read_text())
34
+ team_state_path = project_root / run_manifest["teamStatePath"]
35
+ report_path = project_root / run_manifest["expectedReportRecordPath"]
36
+ final_status_path = project_root / run_manifest["expectedStatusPath"]
37
+
38
+ process = subprocess.run(
39
+ [
40
+ sys.executable,
41
+ str(validator_script),
42
+ "--team-state",
43
+ str(team_state_path),
44
+ "--report",
45
+ str(report_path),
46
+ "--run-manifest",
47
+ str(run_manifest_path),
48
+ "--task-manifest",
49
+ str(task_manifest_path),
50
+ "--final-status",
51
+ str(final_status_path),
52
+ # session-conformance 주입 시드 — prepare_run_validator_fixture 가
53
+ # 만든 합성 lead jsonl 디렉터리 (실제 ~/.claude/projects 미오염).
54
+ "--claude-projects-dir",
55
+ str(project_root / ".claude-projects-fixture"),
56
+ ],
57
+ capture_output=True,
58
+ text=True,
59
+ )
60
+
61
+ if not process.stdout.strip():
62
+ raise SystemExit("validator did not produce JSON output")
63
+
64
+ payload = json.loads(process.stdout)
65
+ actual_status = payload.get("validationStatus")
66
+ if actual_status != expected_status:
67
+ extra = ""
68
+ if expected_status == "passed":
69
+ listed = payload.get("failures") or []
70
+ extra = "\n" + "\n".join(str(item) for item in listed)
71
+ raise SystemExit(
72
+ f"validator status mismatch: expected {expected_status}, got {actual_status}{extra}"
73
+ )
74
+
75
+ if expected_status == "passed" and process.returncode != 0:
76
+ raise SystemExit(f"validator returned non-zero on success: {process.stderr}")
77
+ if expected_status == "failed" and process.returncode == 0:
78
+ raise SystemExit("validator unexpectedly succeeded")
79
+
80
+ if expected_failure_substring:
81
+ failures = payload.get("failures", [])
82
+ if not isinstance(failures, list) or not any(
83
+ expected_failure_substring in str(item) for item in failures
84
+ ):
85
+ raise SystemExit(
86
+ f"validator failure output did not include expected text: {expected_failure_substring}"
87
+ )
88
+
89
+ if not final_status_path.is_file():
90
+ raise SystemExit(f"validator did not write final status file: {final_status_path}")
91
+
92
+ final_status = final_status_path.read_text().strip()
93
+ expected_final_status = "completed" if expected_status == "passed" else "contract-violated"
94
+ if final_status != expected_final_status:
95
+ raise SystemExit(
96
+ f"final status file mismatch: expected {expected_final_status}, got {final_status}"
97
+ )
98
+
99
+ updated_run_manifest = json.loads(run_manifest_path.read_text())
100
+ updated_task_manifest = json.loads(task_manifest_path.read_text())
101
+ updated_team_state = json.loads(team_state_path.read_text())
102
+
103
+ if updated_run_manifest.get("validation", {}).get("status") != expected_status:
104
+ raise SystemExit("run manifest validation status was not updated correctly")
105
+ if updated_task_manifest.get("contractValidation", {}).get("status") != expected_status:
106
+ raise SystemExit("task manifest validation status was not updated correctly")
107
+ if updated_team_state.get("validator", {}).get("status") != expected_status:
108
+ raise SystemExit("team-state validator status was not updated correctly")
@@ -0,0 +1,60 @@
1
+ """validate-assets-01 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/validate-assets.sh:23
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from pathlib import Path
8
+ import sys
9
+
10
+ agents_source_root = Path(sys.argv[1]) # <repo>/agents
11
+ skills_source_root = Path(sys.argv[2]) # <repo>/skills
12
+ runtime_root = Path(sys.argv[3]) # <repo>/runtime
13
+ validation_mode = sys.argv[4]
14
+ errors = []
15
+
16
+
17
+ def check(source_path: Path, target_path: Path) -> None:
18
+ if not target_path.is_file():
19
+ errors.append(f"missing build-output asset: {target_path}")
20
+ return
21
+ if validation_mode == "match" and target_path.read_bytes() != source_path.read_bytes():
22
+ errors.append(f"build-output asset does not match source: {target_path}")
23
+
24
+
25
+ # 1. Worker agent files: agents/workers/*-worker.md -> runtime/agents/workers/*-worker.md
26
+ #
27
+ workers_source = agents_source_root / "workers"
28
+ workers_target = runtime_root / "agents" / "workers"
29
+ if not workers_source.is_dir():
30
+ errors.append(f"missing agents/workers source directory: {workers_source}")
31
+ else:
32
+ for source_path in sorted(workers_source.glob("*.md")):
33
+ check(source_path, workers_target / source_path.name)
34
+
35
+ # 2. Lead contract + internal lead resources: prompts/lead/* + prompts/coding-preflight/*
36
+ # -> runtime/prompts/*. These ship as runtime resources (okstra install copies
37
+ # runtime/prompts -> ~/.okstra/prompts), no longer as agent skills.
38
+ prompts_source_root = agents_source_root.parent / "prompts"
39
+ for sub in ("lead", "coding-preflight"):
40
+ sub_root = prompts_source_root / sub
41
+ if not sub_root.is_dir():
42
+ errors.append(f"missing prompts source directory: {sub_root}")
43
+ continue
44
+ for source_path in sorted(sub_root.rglob("*.md")):
45
+ relative = source_path.relative_to(prompts_source_root)
46
+ check(source_path, runtime_root / "prompts" / relative)
47
+
48
+ # 3. Skill packages: skills/<name>/SKILL.md -> runtime/skills/<name>/SKILL.md
49
+ if not skills_source_root.is_dir():
50
+ errors.append(f"missing skills source directory: {skills_source_root}")
51
+ else:
52
+ for skill_dir in sorted(p for p in skills_source_root.iterdir() if p.is_dir()):
53
+ for source_path in sorted(skill_dir.rglob("*.md")):
54
+ relative = source_path.relative_to(skills_source_root)
55
+ check(source_path, runtime_root / "skills" / relative)
56
+
57
+ if errors:
58
+ for error in errors:
59
+ print(error, file=sys.stderr)
60
+ sys.exit(1)
@@ -0,0 +1,261 @@
1
+ """validate-prompt-metadata-01 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/validate-prompt-metadata.sh:10
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from pathlib import Path
8
+ import json
9
+ import sys
10
+
11
+ project_root = Path(sys.argv[1])
12
+ task_manifest_path = project_root / sys.argv[2]
13
+ errors = []
14
+
15
+
16
+ def load_json(path: Path) -> dict:
17
+ return json.loads(path.read_text())
18
+
19
+
20
+ def validate_prompt_contract(
21
+ prefix: str,
22
+ worker_ids: list[str],
23
+ prompt_dir: str,
24
+ prompt_map: object,
25
+ required_worker_roles: object,
26
+ ) -> None:
27
+ if not prompt_dir:
28
+ errors.append(f"{prefix} is missing worker prompt directory metadata")
29
+ if not isinstance(prompt_map, dict):
30
+ errors.append(f"{prefix} worker prompt map is missing or invalid")
31
+ prompt_map = {}
32
+ if not isinstance(required_worker_roles, list):
33
+ errors.append(f"{prefix} requiredWorkerRoles is missing or invalid")
34
+ required_worker_roles = []
35
+
36
+ expected_dir_prefix = prompt_dir.rstrip("/") + "/" if prompt_dir else ""
37
+ for worker_id in worker_ids:
38
+ if worker_id not in prompt_map:
39
+ errors.append(f"{prefix} worker prompt map is missing selected worker: {worker_id}")
40
+
41
+ for worker in required_worker_roles:
42
+ if not isinstance(worker, dict):
43
+ errors.append(f"{prefix} requiredWorkerRoles contains a non-object entry")
44
+ continue
45
+ worker_id = str(worker.get("workerId", "")).strip()
46
+ prompt_relative = str(worker.get("promptPath", "")).strip()
47
+ if not worker_id:
48
+ errors.append(f"{prefix} requiredWorkerRoles contains an entry without workerId")
49
+ continue
50
+ if not prompt_relative:
51
+ errors.append(
52
+ f"{prefix} required worker is missing promptPath: {worker_id}"
53
+ )
54
+ continue
55
+ if prompt_map.get(worker_id) != prompt_relative:
56
+ errors.append(
57
+ f"{prefix} worker prompt map does not match requiredWorkerRoles for {worker_id}"
58
+ )
59
+ if expected_dir_prefix and not prompt_relative.startswith(expected_dir_prefix):
60
+ errors.append(
61
+ f"{prefix} worker prompt path is outside the prompt directory: {prompt_relative}"
62
+ )
63
+
64
+
65
+ if not task_manifest_path.is_file():
66
+ errors.append(f"task manifest is missing: {task_manifest_path}")
67
+ else:
68
+ task_manifest = load_json(task_manifest_path)
69
+ selected_workers = task_manifest.get("recommendedWorkers", [])
70
+ if not isinstance(selected_workers, list):
71
+ errors.append("task manifest recommendedWorkers is missing or invalid")
72
+ selected_workers = []
73
+
74
+ task_prompt_dir = str(task_manifest.get("latestRunPromptsPath", "")).strip()
75
+ if not task_prompt_dir:
76
+ errors.append("task manifest is missing latestRunPromptsPath")
77
+
78
+ task_artifacts = task_manifest.get("artifacts", {})
79
+ if not isinstance(task_artifacts, dict):
80
+ errors.append("task manifest artifacts is missing or invalid")
81
+ task_artifacts = {}
82
+
83
+ validate_prompt_contract(
84
+ "task manifest",
85
+ selected_workers,
86
+ str(task_artifacts.get("workerPromptsDirectoryPath", "")).strip(),
87
+ task_artifacts.get("workerPromptPathByWorkerId"),
88
+ task_manifest.get("resultContract", {}).get("requiredWorkerRoles"),
89
+ )
90
+
91
+ if (
92
+ task_prompt_dir
93
+ and str(task_artifacts.get("workerPromptsDirectoryPath", "")).strip()
94
+ and task_prompt_dir
95
+ != str(task_artifacts.get("workerPromptsDirectoryPath", "")).strip()
96
+ ):
97
+ errors.append(
98
+ "task manifest latestRunPromptsPath does not match artifacts.workerPromptsDirectoryPath"
99
+ )
100
+
101
+ timeline_relative_path = str(
102
+ task_manifest.get("historyTimelinePath", "")
103
+ ).strip()
104
+ if not timeline_relative_path:
105
+ errors.append("task manifest is missing historyTimelinePath")
106
+ else:
107
+ timeline_path = project_root / timeline_relative_path
108
+ if not timeline_path.is_file():
109
+ errors.append(f"timeline file is missing: {timeline_path}")
110
+ else:
111
+ timeline = load_json(timeline_path)
112
+ runs = timeline.get("runs", [])
113
+ latest_run = None
114
+ if isinstance(runs, list):
115
+ for item in reversed(runs):
116
+ if isinstance(item, dict):
117
+ latest_run = item
118
+ break
119
+ if latest_run is None:
120
+ errors.append("timeline does not contain a latest run entry")
121
+ else:
122
+ latest_run_prompt_dir = str(
123
+ latest_run.get("workerPromptDirectoryPath", "")
124
+ ).strip()
125
+ if not latest_run_prompt_dir:
126
+ errors.append(
127
+ "latest timeline entry is missing workerPromptDirectoryPath"
128
+ )
129
+ if task_prompt_dir and latest_run_prompt_dir and task_prompt_dir != latest_run_prompt_dir:
130
+ errors.append(
131
+ "timeline latest run prompt directory does not match task manifest latestRunPromptsPath"
132
+ )
133
+ prompt_map = latest_run.get("workerPromptPathByWorkerId")
134
+ if not isinstance(prompt_map, dict):
135
+ errors.append(
136
+ "latest timeline entry is missing workerPromptPathByWorkerId"
137
+ )
138
+ else:
139
+ for worker_id in selected_workers:
140
+ if worker_id not in prompt_map:
141
+ errors.append(
142
+ f"latest timeline entry is missing worker prompt path for {worker_id}"
143
+ )
144
+
145
+ run_manifest_relative_path = str(
146
+ latest_run.get("runManifestPath", "")
147
+ ).strip()
148
+ if not run_manifest_relative_path:
149
+ errors.append(
150
+ "latest timeline entry is missing runManifestPath"
151
+ )
152
+ else:
153
+ run_manifest_path = project_root / run_manifest_relative_path
154
+ if not run_manifest_path.is_file():
155
+ errors.append(f"latest run manifest is missing: {run_manifest_path}")
156
+ else:
157
+ run_manifest = load_json(run_manifest_path)
158
+ run_prompt_dir = str(
159
+ run_manifest.get("workerPromptsDirectoryPath", "")
160
+ ).strip()
161
+ if task_prompt_dir and run_prompt_dir and task_prompt_dir != run_prompt_dir:
162
+ errors.append(
163
+ "run manifest workerPromptsDirectoryPath does not match task manifest latestRunPromptsPath"
164
+ )
165
+ validate_prompt_contract(
166
+ "run manifest",
167
+ selected_workers,
168
+ run_prompt_dir,
169
+ run_manifest.get("workerPromptPathByWorkerId"),
170
+ run_manifest.get("teamContract", {}).get("requiredWorkerRoles"),
171
+ )
172
+
173
+ team_state_relative_path = str(
174
+ run_manifest.get("teamStatePath")
175
+ or task_manifest.get("teamStatePath", "")
176
+ ).strip()
177
+ if not team_state_relative_path:
178
+ errors.append(
179
+ "team state relative path is missing from run/task manifest"
180
+ )
181
+ else:
182
+ team_state_path = project_root / team_state_relative_path
183
+ if not team_state_path.is_file():
184
+ errors.append(f"team-state file is missing: {team_state_path}")
185
+ else:
186
+ team_state = load_json(team_state_path)
187
+ team_artifacts = team_state.get("artifacts", {})
188
+ if not isinstance(team_artifacts, dict):
189
+ errors.append(
190
+ "team-state artifacts is missing or invalid"
191
+ )
192
+ team_artifacts = {}
193
+ team_prompt_dir = str(
194
+ team_artifacts.get(
195
+ "workerPromptsDirectoryPath", ""
196
+ )
197
+ ).strip()
198
+ if run_prompt_dir and team_prompt_dir and run_prompt_dir != team_prompt_dir:
199
+ errors.append(
200
+ "team-state worker prompt directory does not match run manifest"
201
+ )
202
+ workers = team_state.get("workers", [])
203
+ if not isinstance(workers, list):
204
+ errors.append("team-state workers is missing or invalid")
205
+ else:
206
+ team_workers = {}
207
+ for worker in workers:
208
+ if not isinstance(worker, dict):
209
+ errors.append(
210
+ "team-state workers contains a non-object entry"
211
+ )
212
+ continue
213
+ worker_id = str(worker.get("workerId", "")).strip()
214
+ if not worker_id:
215
+ errors.append(
216
+ "team-state workers contains an entry without workerId"
217
+ )
218
+ continue
219
+ team_workers[worker_id] = worker
220
+
221
+ run_prompt_map = run_manifest.get(
222
+ "workerPromptPathByWorkerId", {}
223
+ )
224
+ if not isinstance(run_prompt_map, dict):
225
+ run_prompt_map = {}
226
+
227
+ for worker_id in selected_workers:
228
+ worker = team_workers.get(worker_id)
229
+ if worker is None:
230
+ errors.append(
231
+ f"team-state is missing selected worker entry: {worker_id}"
232
+ )
233
+ continue
234
+ prompt_relative = str(
235
+ worker.get("promptPath", "")
236
+ ).strip()
237
+ if not prompt_relative:
238
+ errors.append(
239
+ f"team-state worker is missing promptPath: {worker_id}"
240
+ )
241
+ continue
242
+ if run_prompt_map.get(worker_id) != prompt_relative:
243
+ errors.append(
244
+ f"team-state worker prompt path does not match run manifest for {worker_id}"
245
+ )
246
+ expected_dir_prefix = (
247
+ run_prompt_dir.rstrip("/") + "/"
248
+ if run_prompt_dir
249
+ else ""
250
+ )
251
+ if expected_dir_prefix and not prompt_relative.startswith(
252
+ expected_dir_prefix
253
+ ):
254
+ errors.append(
255
+ f"team-state worker prompt path is outside the run prompt directory: {prompt_relative}"
256
+ )
257
+
258
+ if errors:
259
+ for error in errors:
260
+ print(error, file=sys.stderr)
261
+ sys.exit(1)
@@ -0,0 +1,46 @@
1
+ """validate-tasks-01 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/validate-tasks.sh:8
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from pathlib import Path
8
+ import sys
9
+
10
+ brief_path = Path(sys.argv[1])
11
+ reference_path = Path(sys.argv[2])
12
+ expected_task_key = sys.argv[3]
13
+ brief_lines = brief_path.read_text().splitlines()
14
+ section_map = {
15
+ "Configuration References and Expected Values": "config",
16
+ "Deployment Manifests and Expected Values": "deployment",
17
+ }
18
+ captured = {"config": [], "deployment": []}
19
+ current_section = None
20
+
21
+ for line in brief_lines:
22
+ if line.startswith("## "):
23
+ current_section = section_map.get(line[3:].strip())
24
+ continue
25
+ if current_section and line.strip():
26
+ captured[current_section].append(line)
27
+
28
+ errors = []
29
+
30
+ if not reference_path.is_file():
31
+ errors.append(f"reference expectations file is missing: {reference_path}")
32
+ reference_content = ""
33
+ else:
34
+ reference_content = reference_path.read_text()
35
+
36
+ for line in captured["config"] + captured["deployment"]:
37
+ if line not in reference_content:
38
+ errors.append(f"reference expectations file is missing brief line: {line}")
39
+
40
+ if f"- Task Key: `{expected_task_key}`" not in reference_content:
41
+ errors.append("reference expectations file is missing the task key header")
42
+
43
+ if errors:
44
+ for error in errors:
45
+ print(error, file=sys.stderr)
46
+ sys.exit(1)
@@ -0,0 +1,85 @@
1
+ """validate-tasks-02 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/validate-tasks.sh:22
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from pathlib import Path
8
+ import json
9
+ import sys
10
+
11
+ project_root = Path(sys.argv[1])
12
+ expected_task_key = sys.argv[2]
13
+ task_manifest_relative_path = sys.argv[3]
14
+ expected_reference_relative_path = sys.argv[4]
15
+ expected_task_catalog_relative_path = sys.argv[5]
16
+ errors = []
17
+
18
+ task_manifest_path = project_root / task_manifest_relative_path
19
+ if not task_manifest_path.is_file():
20
+ errors.append(f"task manifest is missing: {task_manifest_path}")
21
+ else:
22
+ task_manifest = json.loads(task_manifest_path.read_text())
23
+
24
+ if task_manifest.get("taskKey") != expected_task_key:
25
+ errors.append("task manifest does not contain the expected task key")
26
+
27
+ if task_manifest.get("referenceExpectationsPath") != expected_reference_relative_path:
28
+ errors.append("task manifest does not expose referenceExpectationsPath")
29
+
30
+ if task_manifest.get("taskCatalogPath") != expected_task_catalog_relative_path:
31
+ errors.append("task manifest does not expose taskCatalogPath")
32
+
33
+ if (
34
+ task_manifest.get("artifacts", {}).get("referenceExpectationsPath")
35
+ != expected_reference_relative_path
36
+ ):
37
+ errors.append("task manifest artifacts do not expose referenceExpectationsPath")
38
+
39
+ timeline_relative_path = task_manifest.get("historyTimelinePath", "")
40
+ if not timeline_relative_path:
41
+ errors.append("task manifest is missing historyTimelinePath")
42
+ else:
43
+ timeline_path = project_root / timeline_relative_path
44
+ if not timeline_path.is_file():
45
+ errors.append(f"timeline file is missing: {timeline_path}")
46
+ else:
47
+ timeline = json.loads(timeline_path.read_text())
48
+ runs = timeline.get("runs", [])
49
+ latest_run = None
50
+ if isinstance(runs, list):
51
+ for item in reversed(runs):
52
+ if isinstance(item, dict):
53
+ latest_run = item
54
+ break
55
+ if latest_run is None:
56
+ errors.append("timeline does not contain a latest run entry")
57
+ else:
58
+ run_manifest_relative_path = latest_run.get("runManifestPath", "")
59
+ if not run_manifest_relative_path:
60
+ errors.append("latest timeline entry is missing runManifestPath")
61
+ else:
62
+ run_manifest_path = project_root / run_manifest_relative_path
63
+ if not run_manifest_path.is_file():
64
+ errors.append(f"latest run manifest is missing: {run_manifest_path}")
65
+ else:
66
+ run_manifest = json.loads(run_manifest_path.read_text())
67
+ if run_manifest.get("taskKey") != expected_task_key:
68
+ errors.append("run manifest does not contain the expected task key")
69
+ if (
70
+ run_manifest.get("referenceExpectationsPath")
71
+ != expected_reference_relative_path
72
+ ):
73
+ errors.append(
74
+ "run manifest does not expose referenceExpectationsPath"
75
+ )
76
+ if (
77
+ run_manifest.get("taskCatalogPath")
78
+ != expected_task_catalog_relative_path
79
+ ):
80
+ errors.append("run manifest does not expose taskCatalogPath")
81
+
82
+ if errors:
83
+ for error in errors:
84
+ print(error, file=sys.stderr)
85
+ sys.exit(1)
@@ -0,0 +1,61 @@
1
+ """validate-tasks-03 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/validate-tasks.sh:36
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from pathlib import Path
8
+ import json
9
+ import sys
10
+
11
+ project_root = Path(sys.argv[1])
12
+ discovery_path = Path(sys.argv[2])
13
+ expected_task_key = sys.argv[3]
14
+ expected_task_manifest_relative_path = sys.argv[4]
15
+ expected_reference_relative_path = sys.argv[5]
16
+ expected_task_catalog_relative_path = sys.argv[6]
17
+ errors = []
18
+
19
+ if not discovery_path.is_file():
20
+ errors.append(f"latest-task discovery file is missing: {discovery_path}")
21
+ else:
22
+ discovery = json.loads(discovery_path.read_text())
23
+
24
+ if discovery.get("taskKey") != expected_task_key:
25
+ errors.append("latest-task discovery does not point to the expected task key")
26
+
27
+ if discovery.get("taskManifestPath") != expected_task_manifest_relative_path:
28
+ errors.append("latest-task discovery does not point to the expected task manifest")
29
+
30
+ if discovery.get("referenceExpectationsPath") != expected_reference_relative_path:
31
+ errors.append("latest-task discovery does not expose the expected reference expectations path")
32
+
33
+ if discovery.get("taskCatalogPath") != expected_task_catalog_relative_path:
34
+ errors.append("latest-task discovery does not expose taskCatalogPath")
35
+
36
+ latest_run_prompts_relative_path = discovery.get("latestRunPromptsPath", "")
37
+ if not latest_run_prompts_relative_path:
38
+ errors.append("latest-task discovery is missing latestRunPromptsPath")
39
+
40
+ latest_run_manifest_relative_path = discovery.get("latestRunManifestPath", "")
41
+ if not latest_run_manifest_relative_path:
42
+ errors.append("latest-task discovery is missing latestRunManifestPath")
43
+ else:
44
+ latest_run_manifest_path = project_root / latest_run_manifest_relative_path
45
+ if not latest_run_manifest_path.is_file():
46
+ errors.append(f"latest run manifest is missing: {latest_run_manifest_path}")
47
+ else:
48
+ run_manifest = json.loads(latest_run_manifest_path.read_text())
49
+ if (
50
+ latest_run_prompts_relative_path
51
+ and run_manifest.get("workerPromptsDirectoryPath")
52
+ != latest_run_prompts_relative_path
53
+ ):
54
+ errors.append(
55
+ "latest-task discovery latestRunPromptsPath does not match the latest run manifest"
56
+ )
57
+
58
+ if errors:
59
+ for error in errors:
60
+ print(error, file=sys.stderr)
61
+ sys.exit(1)