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
@@ -0,0 +1,565 @@
1
+ """fixtures-02 — 셸 시나리오에서 꺼낸 단언 블록.
2
+
3
+ 호출부: validators/lib/fixtures.sh:110
4
+ 규칙 R1 은 실행될 소스를 문자열/heredoc 이 아니라 실파일에 두게 한다.
5
+ 이 파일의 내용은 꺼내기 전과 같다 — 옮기기만 했다.
6
+ """
7
+ from datetime import datetime, timezone
8
+ from pathlib import Path
9
+ import json
10
+ import sys
11
+
12
+ project_root = Path(sys.argv[1])
13
+ task_manifest_path = project_root / sys.argv[2]
14
+ omitted_worker_id = sys.argv[3]
15
+
16
+
17
+ def load_json(path: Path) -> dict:
18
+ return json.loads(path.read_text())
19
+
20
+
21
+ def write_json(path: Path, payload: dict) -> None:
22
+ path.parent.mkdir(parents=True, exist_ok=True)
23
+ path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n")
24
+
25
+
26
+ task_manifest = load_json(task_manifest_path)
27
+ timeline_path = project_root / task_manifest["historyTimelinePath"]
28
+ timeline = load_json(timeline_path)
29
+ runs = timeline.get("runs", [])
30
+ latest_run = None
31
+ if isinstance(runs, list):
32
+ for item in reversed(runs):
33
+ if isinstance(item, dict):
34
+ latest_run = item
35
+ break
36
+
37
+ if latest_run is None:
38
+ raise SystemExit("timeline does not contain a latest run entry")
39
+
40
+ run_manifest_path = project_root / latest_run["runManifestPath"]
41
+ run_manifest = load_json(run_manifest_path)
42
+ team_state_path = project_root / run_manifest["teamStatePath"]
43
+ team_state = load_json(team_state_path)
44
+ report_path = project_root / run_manifest["expectedReportRecordPath"]
45
+ final_status_path = project_root / run_manifest["expectedStatusPath"]
46
+
47
+ # 로스터 슬롯 id 는 `<provider>-<역할>` 이므로(run.py `_canonical_worker_id`)
48
+ # provider 이름으로 직접 조회할 수 없다. 분석 슬롯만 provider 로 접어서 찾고,
49
+ # 고정 id 인 report-writer 는 그대로, 나머지(critic 등)는 기본값으로 둔다.
50
+ status_by_fixture_key = {
51
+ "claude": "completed",
52
+ "codex": "timeout",
53
+ "antigravity": "error",
54
+ "report-writer": "completed",
55
+ }
56
+ reason_by_fixture_key = {
57
+ "claude": "",
58
+ "codex": "Validation fixture timeout",
59
+ "antigravity": "Validation fixture execution error",
60
+ "report-writer": "",
61
+ }
62
+
63
+
64
+ def fixture_key(worker: dict, worker_id: str) -> str:
65
+ if worker_id == "report-writer":
66
+ return "report-writer"
67
+ if worker_id.rsplit("-", 1)[-1] == "analyser":
68
+ return str(worker.get("provider", "")).strip()
69
+ return ""
70
+
71
+ for worker in team_state.get("workers", []):
72
+ if not isinstance(worker, dict):
73
+ continue
74
+ worker_id = str(worker.get("workerId", "")).strip()
75
+ if not worker_id:
76
+ continue
77
+ key = fixture_key(worker, worker_id)
78
+ worker["status"] = status_by_fixture_key.get(key, "not-run")
79
+ worker["reason"] = reason_by_fixture_key.get(key, "Validation fixture not used")
80
+
81
+ prompt_relative = str(worker.get("promptPath", "")).strip()
82
+ if prompt_relative:
83
+ prompt_path = project_root / prompt_relative
84
+ prompt_path.parent.mkdir(parents=True, exist_ok=True)
85
+ prompt_lines = [
86
+ "**Worker Error Contract Path:** /okstra/templates/worker-error-contract.md",
87
+ f"**Errors log path:** {project_root}/run/logs/errors.jsonl",
88
+ f"**Errors sidecar path:** {project_root}/run/worker-results/"
89
+ f"{worker_id}-errors.json",
90
+ f"Assigned worker prompt history path: {prompt_relative}",
91
+ "**Prompt Delivery Mode:** eager-include",
92
+ f"**Model:** Fixture worker, {worker.get('modelExecutionValue', '')}",
93
+ ]
94
+ if worker_id != "report-writer":
95
+ prompt_lines.append(
96
+ f"- Primary analysis packet: `{run_manifest.get('analysisPacketPath', '')}`"
97
+ )
98
+ prompt_lines.extend([
99
+ "",
100
+ "# Initial Analysis Prompt",
101
+ f"Task Key: {task_manifest.get('taskKey', '')}",
102
+ "Validation fixture prompt body.",
103
+ ])
104
+ else:
105
+ prompt_lines.extend(["", "# Report Writer Prompt"])
106
+ prompt_path.write_text(
107
+ "\n".join(prompt_lines) + "\n"
108
+ )
109
+
110
+ result_relative = str(worker.get("resultPath", "")).strip()
111
+ if worker["status"] == "completed" and result_relative:
112
+ result_path = project_root / result_relative
113
+ result_path.parent.mkdir(parents=True, exist_ok=True)
114
+ result_path.write_text(
115
+ "\n".join(
116
+ [
117
+ "# Findings",
118
+ "- Validation fixture finding.",
119
+ "",
120
+ "# Missing Information or Assumptions",
121
+ "- None.",
122
+ "",
123
+ "# Safe or Reasonable Areas",
124
+ "- Fixture output format is valid.",
125
+ "",
126
+ "# Uncertain Points",
127
+ "- None.",
128
+ "",
129
+ "# Recommended Next Actions",
130
+ "- Continue validator coverage.",
131
+ ]
132
+ )
133
+ + "\n"
134
+ )
135
+ # Mirror the audit sidecar contract — every completed worker-results
136
+ # file ships alongside `<worker>-audit-<task-type>-<seq>.md` carrying
137
+ # the Reading Confirmation block. Derive the sidecar path by
138
+ # inserting `-audit` after the worker-role segment of the
139
+ # result-file stem.
140
+ result_stem = result_path.stem # e.g. claude-worker-error-analysis-001
141
+ audit_stem = result_stem.replace("-worker-", "-worker-audit-", 1)
142
+ audit_path = result_path.with_name(f"{audit_stem}{result_path.suffix}")
143
+ audit_lines = [
144
+ f"# {worker.get('role', worker_id)} Audit",
145
+ "",
146
+ "- Read task-brief.md end-to-end (validation fixture).",
147
+ ]
148
+ if key == "claude":
149
+ # Heartbeat 계약 (agents/workers/claude-worker.md "Heartbeat") —
150
+ # validate_session_conformance 가 claude-worker audit 사이드카의
151
+ # `- PROGRESS:` cadence 를 검사하므로 fixture 도 계약을 준수한다.
152
+ hb_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
153
+ audit_lines += [
154
+ "",
155
+ f"- PROGRESS: started {hb_ts}",
156
+ f"- PROGRESS: read-task-brief.md {hb_ts}",
157
+ f"- PROGRESS: analysis-start {hb_ts}",
158
+ f"- PROGRESS: findings-draft-complete {hb_ts}",
159
+ f"- PROGRESS: write-result-start {hb_ts}",
160
+ ]
161
+ audit_path.write_text("\n".join(audit_lines) + "\n")
162
+
163
+ lead = team_state.get("lead")
164
+ if isinstance(lead, dict):
165
+ lead["status"] = "completed"
166
+ # render-only fixture 에는 실 Claude 세션이 없어 sessionId 가 비어 있을 수
167
+ # 있다 — session-conformance fixture jsonl 의 파일명과 맞춘 고정 id 를 부여.
168
+ if not str(lead.get("sessionId") or "").strip():
169
+ lead["sessionId"] = "fixture-lead-session-0001"
170
+ team_state["workflowState"] = "worker-results-collected"
171
+
172
+ # validate-run.py requires team-state.teamCreate.status == "implicit" once any
173
+ # worker has been dispatched (CC v2.1.178 removed TeamCreate; every session has
174
+ # an implicit team). Mirror that here so the fixture is a valid post-Phase-3 state.
175
+ team_state["teamCreate"] = {
176
+ "attempted": False,
177
+ "status": "implicit",
178
+ }
179
+
180
+ # Phase 7 token-usage collection is normally produced by okstra-token-usage.py.
181
+ # The validator (`team-state.usageSummary is empty`) treats absence as a contract
182
+ # violation, so the fixture must mirror that step with a synthetic-but-valid object.
183
+ team_state["usageSummary"] = {
184
+ "leadTotalTokens": 0,
185
+ "workerTotalTokens": 0,
186
+ "grandTotalTokens": 0,
187
+ "leadBillableEquivalentTokens": 0,
188
+ "workerBillableEquivalentTokens": 0,
189
+ "grandBillableEquivalentTokens": 0,
190
+ "estimatedCostUsd": {
191
+ "lead": 0.0,
192
+ "claudeWorkers": 0.0,
193
+ "cliWorkers": 0.0,
194
+ "grandTotal": 0.0,
195
+ },
196
+ "collectedAt": "1970-01-01T00:00:00Z",
197
+ "teamName": team_state.get("teamName", "validation-fixture"),
198
+ "sessionsFound": 0,
199
+ "definitions": {
200
+ "totalTokens": "Validation fixture placeholder.",
201
+ "billableEquivalentTokens": "Validation fixture placeholder.",
202
+ "estimatedCostUsd": "Validation fixture placeholder.",
203
+ },
204
+ "intake": {
205
+ "totalTokens": 0,
206
+ "billableEquivalentTokens": 0,
207
+ "estimatedCostUsd": 0,
208
+ },
209
+ }
210
+
211
+ required_status_entries = run_manifest.get("teamContract", {}).get(
212
+ "requiredAgentStatusEntries", []
213
+ )
214
+ if not isinstance(required_status_entries, list):
215
+ required_status_entries = []
216
+
217
+ report_lines = [
218
+ "# Validation Fixture Report",
219
+ "",
220
+ # Top-of-report Index — the renderer injects this on real runs; the
221
+ # hand-crafted fixture mirrors it so validate_report's index/anchor
222
+ # contract (introduced with the clickable-ID pass) is satisfied.
223
+ '## Index <a id="report-index"></a>',
224
+ "",
225
+ "### Sections",
226
+ "",
227
+ "- [Verdict Card](#verdict-card)",
228
+ "",
229
+ "## Verdict Card",
230
+ "",
231
+ "| 항목 | 값 |",
232
+ "|------|----|",
233
+ "| Final Conclusion | validation fixture |",
234
+ "| Verdict Token | `accepted` |",
235
+ "| Direction | `continue-investigation` |",
236
+ "| Approval Required? | `no` |",
237
+ "| Next Step | fixture |",
238
+ "",
239
+ "## Agent Execution Status",
240
+ ]
241
+ for label in required_status_entries:
242
+ if isinstance(label, str) and label.strip():
243
+ report_lines.append(f"- {label}: fixture status recorded")
244
+ report_lines.extend(
245
+ [
246
+ "",
247
+ "## Token Usage Summary",
248
+ "",
249
+ "| 항목 | 처리 토큰 | 환산 토큰 | 비용 (USD) |",
250
+ "|------|-----------|-----------|------------|",
251
+ "| Lead | `1` | `1` | `$0.01` |",
252
+ "| Worker 합계 | `1` | `1` | `$0.01` |",
253
+ "| **전체 합계** | **`2`** | **`2`** | **`$0.02`** |",
254
+ "| Codex/Antigravity CLI 추가 비용 | | | `$0.00` |",
255
+ "",
256
+ "## 7. Final Verdict",
257
+ "",
258
+ "| 항목 | 값 |",
259
+ "|------|----|",
260
+ "| Verdict Token | `accepted` |",
261
+ "",
262
+ "## 5.8 Final Verification Deliverables",
263
+ "",
264
+ "Source Implementation Report / Acceptance Blockers / Residual Risk / "
265
+ "Validation Evidence / Read-only Command Log / Conditional Acceptance "
266
+ "Conditions / Routing Recommendation: fixture stub.",
267
+ ]
268
+ )
269
+ report_path.parent.mkdir(parents=True, exist_ok=True)
270
+ _report_name = report_path.name
271
+ if _report_name.endswith(".data.json"):
272
+ data_path = report_path
273
+ md_path = report_path.with_name(_report_name[: -len(".data.json")] + ".md")
274
+ elif _report_name.endswith(".md"):
275
+ md_path = report_path
276
+ data_path = report_path.with_name(_report_name[: -len(".md")] + ".data.json")
277
+ else:
278
+ md_path = report_path
279
+ data_path = report_path.with_suffix(".data.json")
280
+ md_path.write_text("\n".join(report_lines) + "\n")
281
+
282
+ # Phase 7 step 1.5 (BLOCKING) — render the html sibling artifact next
283
+ # to the final-report so validate-run.py's report-views hook passes.
284
+ # The workflow validator's fixture predates that step; we materialise
285
+ # the html file in-place using the same single-reference-point helper
286
+ # the CLI uses.
287
+ import os
288
+ WORKSPACE_ROOT = os.environ.get("OKSTRA_WORKSPACE_ROOT_FOR_FIXTURE", "")
289
+ if WORKSPACE_ROOT:
290
+ # Write final-report .data.json SSOT next to the markdown. The validator's
291
+ # validate_final_report_data() reads this via _data_path_for(report_path)
292
+ # and the renderer treats it as the canonical source — the markdown alone
293
+ # is no longer a valid run artifact (dual-format final-report rollout).
294
+ # Sample bundled in tests/fixtures is patched with this run's task identity.
295
+ task_type = str(task_manifest.get("taskType", ""))
296
+ sample_path = (
297
+ Path(WORKSPACE_ROOT)
298
+ / "tests" / "fixtures" / "final-report-data-v2"
299
+ / f"{task_type}-001.data.json"
300
+ )
301
+ if sample_path.is_file():
302
+ sample = json.loads(sample_path.read_text(encoding="utf-8"))
303
+ sample["frontmatter"]["taskGroup"] = str(task_manifest.get("taskGroup", ""))
304
+ sample["frontmatter"]["taskId"] = str(task_manifest.get("taskId", ""))
305
+ sample["frontmatter"]["taskType"] = task_type
306
+ sample["frontmatter"]["projectId"] = str(task_manifest.get("projectId", ""))
307
+ sample["header"]["taskKey"] = str(task_manifest.get("taskKey", ""))
308
+ sample["header"]["taskType"] = task_type
309
+ # The shipped fixture carries its own roster; this run's contract names
310
+ # a different one, and the validator compares the report's agent rows
311
+ # against that contract. Restate the rows under the contract's names so
312
+ # the fixture exercises the check instead of tripping over it.
313
+ _required = (
314
+ (run_manifest.get("teamContract") or {}).get("requiredAgentStatusEntries")
315
+ or (task_manifest.get("resultContract") or {}).get(
316
+ "requiredAgentStatusEntries"
317
+ )
318
+ or []
319
+ )
320
+ # `_required` 는 리포트 마크다운의 `## Agent Execution Status` 절이
321
+ # 이름으로 요구하는 목록이고, validate-run 의 완결성 검사는 그와 별개로
322
+ # **team-state 워커 전원**이 executionStatus 행을 갖는지 본다. 옵션
323
+ # 역할(critic)은 required 목록에 없으므로 로스터 라벨을 합쳐 둔다 —
324
+ # 합치지 않으면 critic 을 해소한 런이 fixture 단계에서 먼저 실패해
325
+ # 정작 검사하려던 프롬프트 이력 누락에 도달하지 못한다.
326
+ _roster_labels = [
327
+ str(row.get("role") or "").strip()
328
+ for row in team_state.get("workers", [])
329
+ if isinstance(row, dict) and str(row.get("role") or "").strip()
330
+ ]
331
+ _labels = list(dict.fromkeys([*_required, *_roster_labels]))
332
+ _rows = sample.get("executionStatus") or []
333
+ if _labels and _rows:
334
+ # `agent` is the runtime (a schema enum); `role` carries the label the
335
+ # validator looks for in the rendered markdown.
336
+ sample["executionStatus"] = [
337
+ {**_rows[min(i, len(_rows) - 1)], "role": role}
338
+ for i, role in enumerate(_labels)
339
+ ]
340
+ data_path.write_text(
341
+ json.dumps(sample, indent=2, ensure_ascii=False) + "\n",
342
+ encoding="utf-8",
343
+ )
344
+
345
+ import sys as _sys
346
+ _sys.path.insert(0, str(Path(WORKSPACE_ROOT) / "scripts"))
347
+ # The markdown seeded above this block predates the data.json contract, so
348
+ # re-render it from the data.json the fixture just wrote. Otherwise the pair
349
+ # disagrees and the validator's AI-handoff heading scan fails on a fixture
350
+ # that never claimed to be hand-authored.
351
+ if sample_path.is_file():
352
+ try:
353
+ from okstra_ctl.render_final_report import render_to_file
354
+
355
+ render_to_file(data_path, md_path)
356
+ except Exception as exc: # pragma: no cover — fixture path only
357
+ raise SystemExit(f"failed to render final report in fixture: {exc}")
358
+ # Go through the same CLI the run uses, so the fixture picks the schema's
359
+ # own view (v2 task template) rather than a second copy of that choice.
360
+ import subprocess as _subprocess
361
+ _views = _subprocess.run(
362
+ [
363
+ _sys.executable,
364
+ str(Path(WORKSPACE_ROOT) / "scripts" / "okstra-render-report-views.py"),
365
+ str(data_path),
366
+ "--task-key", str(task_manifest.get("taskKey", "validation/fixture")),
367
+ "--task-type", str(task_manifest.get("taskType", "validation")),
368
+ "--seq", "001",
369
+ ],
370
+ capture_output=True,
371
+ text=True,
372
+ )
373
+ if _views.returncode != 0:
374
+ raise SystemExit(
375
+ f"failed to render report views in fixture: {_views.stderr or _views.stdout}"
376
+ )
377
+
378
+ if final_status_path.exists():
379
+ final_status_path.unlink()
380
+
381
+ # session-conformance fixture — validate_session_conformance 는 lead 세션
382
+ # jsonl 에서 run 윈도우 내 PROGRESS 체크포인트를 스캔한다. 계약을 준수한
383
+ # 합성 jsonl 을 주입 시드 디렉터리(.claude-projects-fixture)에 만들어 두고,
384
+ # 러너(run_validator_expectation)가 --claude-projects-dir 로 넘긴다.
385
+ lead_sid = str((team_state.get("lead") or {}).get("sessionId") or "").strip()
386
+ if lead_sid:
387
+ now_iso = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
388
+ progress_lines = [
389
+ "PROGRESS: phase-1-intake reading task bundle",
390
+ "PROGRESS: phase-1-intake complete",
391
+ f"PROGRESS: phase-2-prompts preparing {len(team_state.get('workers') or [])} worker prompts",
392
+ "PROGRESS: phase-3-team-create using implicit team",
393
+ ]
394
+ for worker in team_state.get("workers", []):
395
+ if not isinstance(worker, dict):
396
+ continue
397
+ worker_id = str(worker.get("workerId", "")).strip()
398
+ status = str(worker.get("status", "")).strip()
399
+ if not worker_id or worker_id == "report-writer":
400
+ continue
401
+ if status in ("completed", "timeout", "error"):
402
+ progress_lines.append(
403
+ f"PROGRESS: phase-4-dispatch worker={worker_id}-worker model=fixture"
404
+ )
405
+ if status == "completed":
406
+ progress_lines.append(
407
+ f"PROGRESS: phase-5-collect worker={worker_id}-worker status=completed"
408
+ )
409
+ progress_lines.append("PROGRESS: phase-batch-cleanup panes=1")
410
+ progress_lines.append("PROGRESS: phase-6-synthesis dispatching report-writer-worker")
411
+ progress_lines.append("PROGRESS: phase-7-persist updating manifests")
412
+ records = [
413
+ {
414
+ "type": "assistant",
415
+ "timestamp": now_iso,
416
+ "message": {"content": [{"type": "text", "text": line}]},
417
+ }
418
+ for line in progress_lines
419
+ ]
420
+ # 인코딩 기준은 validator 가 쓰는 task-manifest.projectRoot — macOS 의
421
+ # /tmp 심링크 때문에 셸의 $PROJECT_ROOT(/tmp/...)와 manifest 의
422
+ # projectRoot(/private/tmp/...)가 다른 문자열일 수 있다.
423
+ manifest_project_root = str(task_manifest.get("projectRoot") or project_root)
424
+ encoded_cwd = "-" + manifest_project_root.strip("/").replace("/", "-")
425
+ session_dir = project_root / ".claude-projects-fixture" / encoded_cwd
426
+ session_dir.mkdir(parents=True, exist_ok=True)
427
+ (session_dir / f"{lead_sid}.jsonl").write_text(
428
+ "".join(json.dumps(r, ensure_ascii=False) + "\n" for r in records)
429
+ )
430
+
431
+ write_json(team_state_path, team_state)
432
+ write_json(run_manifest_path, run_manifest)
433
+ write_json(task_manifest_path, task_manifest)
434
+
435
+ # The synthetic fixture accepts lead and completed-worker results, so it must
436
+ # create the same verified invocation records and result links as a real run.
437
+ # Keep the deliberately omitted worker prompt absent: the first validator pass
438
+ # still exercises the historical prompt-history failure before the helper
439
+ # restores that file.
440
+ if WORKSPACE_ROOT:
441
+ from okstra_ctl.agent.invocation import (
442
+ AgentInstruction,
443
+ AgentInstructionSource,
444
+ AgentInvocationRequest,
445
+ agent_model_assignment_from_payload,
446
+ invocation_execution_identity_from_manifest,
447
+ prepare_agent_invocation,
448
+ )
449
+ from okstra_ctl.dispatch_state import (
450
+ link_agent_dispatch_result,
451
+ record_verified_agent_dispatch,
452
+ )
453
+ # The analysis duty depends on the task type, exactly as it does in a real
454
+ # run — a fixture that assumed one audience for every phase would be
455
+ # rejected by the manifest's own allowlist.
456
+ from okstra_ctl.worker_prompt_policy import ANALYSIS_DUTY_BY_TASK_TYPE
457
+
458
+ analysis_audience = ANALYSIS_DUTY_BY_TASK_TYPE.get(
459
+ str(run_manifest.get("taskType") or ""), "analysis-worker"
460
+ )
461
+
462
+ lead_record = record_verified_agent_dispatch(
463
+ project_root=project_root,
464
+ run_manifest_path=run_manifest_path,
465
+ metadata_path=project_root / run_manifest["leadPromptMetadataPath"],
466
+ enforcement_mode="host-native-spec-link-gate",
467
+ )
468
+ link_agent_dispatch_result(
469
+ project_root=project_root,
470
+ run_manifest_path=run_manifest_path,
471
+ dispatch_id=lead_record["dispatchId"],
472
+ result_path=report_path,
473
+ )
474
+
475
+ assignments = run_manifest["invocationAssignments"]
476
+ contract = run_manifest["agentContract"]
477
+ reservation_root = project_root / contract["invocationReservationRootPath"]
478
+ for worker in team_state.get("workers", []):
479
+ if not isinstance(worker, dict):
480
+ continue
481
+ worker_id = str(worker.get("workerId") or "").strip()
482
+ prompt_relative = str(worker.get("promptPath") or "").strip()
483
+ result_relative = str(worker.get("resultPath") or "").strip()
484
+ if not worker_id or not prompt_relative or not result_relative:
485
+ continue
486
+ source_prompt = project_root / prompt_relative
487
+ if not source_prompt.is_file():
488
+ continue
489
+ assignment_ref = f"initial/{worker_id}"
490
+ invocation_id = f"validation-fixture-{worker_id}"
491
+ invocation_prompt = reservation_root / f"{invocation_id}.prompt.md"
492
+ audience = (
493
+ "report-writer" if worker_id == "report-writer"
494
+ else analysis_audience
495
+ )
496
+ assignment = agent_model_assignment_from_payload(
497
+ assignments[assignment_ref]
498
+ )
499
+ identity = invocation_execution_identity_from_manifest(
500
+ run_manifest,
501
+ assignment=assignment,
502
+ assignment_ref=assignment_ref,
503
+ duty_id=audience,
504
+ )
505
+ identity_args = {
506
+ "participant_ref": (
507
+ identity.participant_ref if identity is not None else None
508
+ ),
509
+ "role_execution_ref": (
510
+ identity.role_execution_ref if identity is not None else None
511
+ ),
512
+ "duty_id": identity.duty_id if identity is not None else None,
513
+ "invocation_ref": invocation_id if identity is not None else None,
514
+ "attempt": 1,
515
+ }
516
+ prepared = prepare_agent_invocation(AgentInvocationRequest(
517
+ invocation_id=invocation_id,
518
+ worker_id=worker_id if identity is None else None,
519
+ audience=audience,
520
+ assignment_ref=assignment_ref,
521
+ purpose=None,
522
+ assignment=assignment,
523
+ instruction=AgentInstruction(
524
+ anchor_lines=(),
525
+ body=source_prompt.read_text(encoding="utf-8"),
526
+ source_paths=(
527
+ AgentInstructionSource("project", prompt_relative),
528
+ ),
529
+ ),
530
+ project_root=project_root,
531
+ run_manifest_path=run_manifest_path,
532
+ duty_root=project_root / contract["dutyRootPath"],
533
+ prompt_path=invocation_prompt,
534
+ metadata_path=invocation_prompt.with_name(
535
+ invocation_prompt.name + ".meta.json"
536
+ ),
537
+ dispatch_kind="validation-fixture",
538
+ **identity_args,
539
+ ))
540
+ dispatch = record_verified_agent_dispatch(
541
+ project_root=project_root,
542
+ run_manifest_path=run_manifest_path,
543
+ metadata_path=prepared.metadata_path,
544
+ enforcement_mode=(
545
+ "host-native-spec-link-gate"
546
+ if prepared.assignment.runner == "native-session"
547
+ else "core-pre-dispatch"
548
+ ),
549
+ )
550
+ if worker.get("status") == "completed":
551
+ link_agent_dispatch_result(
552
+ project_root=project_root,
553
+ run_manifest_path=run_manifest_path,
554
+ dispatch_id=dispatch["dispatchId"],
555
+ result_path=project_root / result_relative,
556
+ )
557
+
558
+ # Preserve the workflow's historical missing-prompt negative after the v2
559
+ # invocation record has captured the prompt bytes that were actually sent.
560
+ for worker in team_state.get("workers", []):
561
+ if isinstance(worker, dict) and worker.get("workerId") == omitted_worker_id:
562
+ omitted_prompt = str(worker.get("promptPath") or "").strip()
563
+ if omitted_prompt:
564
+ (project_root / omitted_prompt).unlink(missing_ok=True)
565
+ break
@@ -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")