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
@@ -12,7 +12,13 @@ done
12
12
  SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" && pwd)"
13
13
  WORKSPACE_ROOT="$(cd -P "$SCRIPT_DIR/.." && pwd)"
14
14
  PROJECT_ID="okstra-validation"
15
- PROJECT_ROOT="${OKSTRA_VALIDATION_PROJECT_ROOT:-/tmp/okstra-validate.workflow}"
15
+
16
+ # shellcheck source=lib/common.sh
17
+ source "$SCRIPT_DIR/lib/common.sh"
18
+ # shellcheck source=lib/paths.sh
19
+ source "$SCRIPT_DIR/lib/paths.sh"
20
+
21
+ PROJECT_ROOT="$(resolve_validation_project_root)"
16
22
  export OKSTRA_HOME="${OKSTRA_HOME:-$PROJECT_ROOT/.okstra-home}"
17
23
 
18
24
  WORKSPACE_APP_PATH="$PROJECT_ROOT"
@@ -34,22 +40,17 @@ SECONDARY_BRIEF_FILENAME="validation-brief-secondary.md"
34
40
 
35
41
  # Workflow validation is a hermetic contract test for bundle prep / discovery
36
42
  # artifacts / validator round-trips. It must not depend on the developer or CI
37
- # machine having a preinstalled ~/.okstra runtime, and it must not reconcile or
38
- # backfill against unrelated local state.
43
+ # machine having a preinstalled ~/.okstra runtime. The synthetic prepare below
44
+ # reconciles the run index (okstra_ctl.run._reconcile_prior_runs); it stays off
45
+ # unrelated local state because OKSTRA_HOME is redirected above and the
46
+ # reconcile is scoped to this fixture's project id.
39
47
  export OKSTRA_SKIP_INSTALL_CHECK="${OKSTRA_SKIP_INSTALL_CHECK:-1}"
40
- export OKSTRA_CTL_SKIP_RECONCILE="${OKSTRA_CTL_SKIP_RECONCILE:-1}"
41
- export OKSTRA_CTL_SKIP_BACKFILL="${OKSTRA_CTL_SKIP_BACKFILL:-1}"
42
- # Same reason as the three above: the synthetic run must render the same way on
43
- # every machine. cmux outranks tmux when present, so a maintainer running this
44
- # from inside cmux would otherwise get the cmux adapter and a lead-session
45
- # requirement this fixture never simulates. The cmux path has its own coverage
48
+ # Same reason as the one above: the synthetic run must render the same way on
49
+ # every machine. A maintainer running this from inside cmux would otherwise get
50
+ # the cmux adapter and a lead-session requirement this fixture never simulates. The cmux path has its own coverage
46
51
  # in tests/run/test_cmux*.py and tests/contract/test_validate_session_conformance.py.
47
52
  export CMUX_WORKSPACE_ID=""
48
53
 
49
- # shellcheck source=lib/common.sh
50
- source "$SCRIPT_DIR/lib/common.sh"
51
- # shellcheck source=lib/paths.sh
52
- source "$SCRIPT_DIR/lib/paths.sh"
53
54
  LATEST_TASK_RELATIVE_PATH="$OKSTRA_DIR/discovery/latest-task.json"
54
55
  TASK_CATALOG_RELATIVE_PATH="$OKSTRA_DIR/discovery/task-catalog.json"
55
56
  # shellcheck source=lib/fixtures.sh
@@ -67,6 +68,23 @@ source "$SCRIPT_DIR/lib/summary.sh"
67
68
 
68
69
  trap 'on_error "$LINENO"' ERR
69
70
 
71
+ # 기본(mktemp) 루트는 이 실행이 소유한다: 성공 시 제거, 실패 시 진단을 위해
72
+ # 보존. OKSTRA_VALIDATION_PROJECT_ROOT 가 지정된 루트의 수명은 호출자 소유.
73
+ cleanup_validation_root() {
74
+ local exit_code=$?
75
+
76
+ if [[ -n "${OKSTRA_VALIDATION_PROJECT_ROOT:-}" ]]; then
77
+ return
78
+ fi
79
+
80
+ if [[ "$exit_code" -eq 0 ]]; then
81
+ rm -rf "$PROJECT_ROOT"
82
+ else
83
+ printf '[INFO] Validation root retained for inspection: %s\n' "$PROJECT_ROOT" >&2
84
+ fi
85
+ }
86
+ trap cleanup_validation_root EXIT
87
+
70
88
  require_file "$OKSTRA_SCRIPT"
71
89
  require_file "$RUN_VALIDATOR_PATH"
72
90
 
@@ -134,19 +152,19 @@ step "Preparing validator fixture artifacts for the primary task"
134
152
  # repo root so the heredoc can import okstra_ctl.report_views and load
135
153
  # inline assets from templates/reports/.
136
154
  export OKSTRA_WORKSPACE_ROOT_FOR_FIXTURE="$WORKSPACE_ROOT"
137
- if ! prepare_run_validator_fixture "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" codex; then
155
+ if ! prepare_run_validator_fixture "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" codex-analyser; then
138
156
  fail "Failed to prepare validator fixture artifacts for the primary task"
139
157
  fi
140
158
  pass "Primary task validator fixture prepared with an intentionally missing Codex prompt history file"
141
159
 
142
160
  step "Ensuring the run validator rejects attempted workers without prompt history"
143
- if ! run_validator_expectation "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" failed "Codex worker with status \`timeout\` is missing worker prompt history file"; then
161
+ if ! run_validator_expectation "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" failed "Codex analyser with status \`timeout\` is missing worker prompt history file"; then
144
162
  fail "Run validator did not reject the missing worker prompt history artifact"
145
163
  fi
146
164
  pass "Run validator rejected the missing worker prompt history artifact as expected"
147
165
 
148
166
  step "Restoring the missing worker prompt history file and rerunning the validator"
149
- if ! write_worker_prompt_history_fixture "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" codex; then
167
+ if ! write_worker_prompt_history_fixture "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" codex-analyser; then
150
168
  fail "Failed to write the missing worker prompt history fixture"
151
169
  fi
152
170
  if ! run_validator_expectation "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" passed; then
@@ -154,6 +172,28 @@ if ! run_validator_expectation "$PRIMARY_TASK_GROUP" "$PRIMARY_TASK_ID" passed;
154
172
  fi
155
173
  pass "Run validator passed after the missing worker prompt history artifact was restored"
156
174
 
175
+ step "Checking that the validated run is settled in the central run index"
176
+ # The global index (~/.okstra) records a run's start via record_start and never
177
+ # its end; validate-run is what writes the end
178
+ # (record_validation_in_central_index). Without that edge the row stays
179
+ # `prepared`/`running` forever and run-audit reads it as a run that has not
180
+ # produced its report yet. This run was validated twice here — `failed`, then
181
+ # `passed` — so a passing row also proves the second verdict overwrote the first.
182
+ RECENT_INDEX="$OKSTRA_HOME/recent.jsonl"
183
+ require_file "$RECENT_INDEX"
184
+ PRIMARY_RUN_ID="$PROJECT_ID/$PRIMARY_TASK_GROUP/$PRIMARY_TASK_ID/$TASK_TYPE/r01"
185
+ PRIMARY_INDEX_ROW="$(grep -F "\"runId\":\"$PRIMARY_RUN_ID\"" "$RECENT_INDEX" || true)"
186
+ if [[ -z "$PRIMARY_INDEX_ROW" ]]; then
187
+ fail "No central run-index row for $PRIMARY_RUN_ID"
188
+ fi
189
+ if ! printf '%s' "$PRIMARY_INDEX_ROW" | grep -qF '"status":"completed"'; then
190
+ fail "Central run-index row for $PRIMARY_RUN_ID is not completed: $PRIMARY_INDEX_ROW"
191
+ fi
192
+ if ! printf '%s' "$PRIMARY_INDEX_ROW" | grep -qF '"validation":"passed"'; then
193
+ fail "Central run-index row for $PRIMARY_RUN_ID does not carry the passing verdict: $PRIMARY_INDEX_ROW"
194
+ fi
195
+ pass "Central run-index row for the primary run reads completed/passed"
196
+
157
197
  step "Running a second render-only okstra validation for the secondary task"
158
198
  if ! run_okstra "$SECONDARY_TASK_GROUP" "$SECONDARY_TASK_ID" "$SECONDARY_BRIEF_FILENAME"; then
159
199
  fail "Render-only validation for the secondary task failed"
@@ -26,6 +26,7 @@ from okstra_ctl.improvement_lenses import (
26
26
  ABSOLUTE_CANDIDATE_CAP,
27
27
  SOURCE_WORKERS,
28
28
  )
29
+ from okstra_ctl.final_report_paths import final_report_data_path
29
30
  from okstra_ctl.final_report_schema import (
30
31
  load_schema_version,
31
32
  validate as validate_final_report_data,
@@ -349,7 +350,7 @@ def validate_improvement_report(
349
350
  errors.append(f"report file not found: {report_path}")
350
351
  return ValidationResult(ok=False, errors=errors)
351
352
 
352
- data_path = report_path.with_suffix(".data.json")
353
+ data_path = final_report_data_path(report_path)
353
354
  if data_path.exists():
354
355
  try:
355
356
  data = json.loads(data_path.read_text(encoding="utf-8"))
@@ -45,14 +45,13 @@ from okstra_ctl.lead_events import ( # noqa: E402
45
45
  LeadEventParseError,
46
46
  read_lead_events,
47
47
  )
48
- from okstra_ctl.agent_activity import ACTIVITY_FIELDS # noqa: E402
48
+ from okstra_ctl.agent.activity import ACTIVITY_FIELDS # noqa: E402
49
49
  from okstra_ctl.domain.host import HostNotRegistered # noqa: E402
50
50
  from okstra_ctl.final_report_paths import final_report_data_path # noqa: E402
51
51
  from okstra_ctl.registry.host_registry import default_host_registry # noqa: E402
52
52
  from okstra_ctl.wrapper_status import read_wrapper_status # noqa: E402
53
53
 
54
54
  _DISPATCHED_STATUSES = {"completed", "timeout", "error", "in-progress"}
55
- _WORKER_DISPATCH_MODES = {"cli-wrapper", "mixed"}
56
55
  _ATTEMPTED_STATUSES = {"completed", "timeout", "error"}
57
56
  _LIVENESS_AUDIT_HEARTBEAT = "audit-heartbeat"
58
57
  _LIVENESS_WRAPPER_STATUS = "wrapper-status"
@@ -90,6 +89,8 @@ _IN_STAGE_PREFIX = IN_STAGE_PREFIX
90
89
 
91
90
  _LEAD_CONTRACT_CITE = "prompts/lead/okstra-lead-contract.md 'Entry guard (BLOCKING)'"
92
91
  _HOST_RULES_CITE = "prompts/launch.template.md 'Host Orchestration Rules'"
92
+ _CONVERGENCE_CITE = "prompts/lead/convergence.md"
93
+ _PLAN_BODY_CITE = "prompts/lead/plan-body-verification.md"
93
94
 
94
95
 
95
96
  class _EntryGuardRead(NamedTuple):
@@ -102,17 +103,29 @@ class _EntryGuardRead(NamedTuple):
102
103
  cite: str
103
104
  # True 면 run 이 이 파일을 instruction-set 에 스테이징했을 때만 요구한다.
104
105
  staged_per_run: bool = False
106
+ # 이 행이 도는 task-type. None 이면 task-type 을 가리지 않는다.
107
+ task_types: frozenset[str] | None = None
108
+ # 이 PROGRESS phase 가 run 에 실제로 나타났을 때만 요구한다. None 이면
109
+ # 무조건 요구한다. task-type 목록을 손으로 적는 것보다 정확하다 — 수렴은
110
+ # 여러 phase 에서 돌고, 어느 run 이 실제로 돌렸는지는 그 run 의 체크포인트가
111
+ # 말해 준다.
112
+ required_when: str | None = None
105
113
 
106
114
 
115
+ _IMPLEMENTATION_ONLY = frozenset({"implementation"})
116
+
107
117
  _ENTRY_GUARD_READS = (
108
118
  _EntryGuardRead(
109
- "_implementation-executor.md", ("phase-6-synthesis", "at Phase 5"), _LEAD_CONTRACT_CITE
119
+ "_implementation-executor.md", ("phase-6-synthesis", "at Phase 5"),
120
+ _LEAD_CONTRACT_CITE, task_types=_IMPLEMENTATION_ONLY,
110
121
  ),
111
122
  _EntryGuardRead(
112
- "_implementation-verifier.md", ("phase-6-synthesis", "at Phase 5"), _LEAD_CONTRACT_CITE
123
+ "_implementation-verifier.md", ("phase-6-synthesis", "at Phase 5"),
124
+ _LEAD_CONTRACT_CITE, task_types=_IMPLEMENTATION_ONLY,
113
125
  ),
114
126
  _EntryGuardRead(
115
- "_implementation-deliverable.md", ("phase-7-persist", "at Phase 6"), _LEAD_CONTRACT_CITE
127
+ "_implementation-deliverable.md", ("phase-7-persist", "at Phase 6"),
128
+ _LEAD_CONTRACT_CITE, task_types=_IMPLEMENTATION_ONLY,
116
129
  ),
117
130
  # lead 프로파일 sidecar 가 아니라, run 이 instruction-set 에 스테이징한 host
118
131
  # orchestration 규칙이다. 세 가지가 sidecar 와 다르다.
@@ -126,7 +139,26 @@ _ENTRY_GUARD_READS = (
126
139
  # 파일 때문에 막히면 안 된다. 규칙이 없는 task-type 에 conformance 가 규칙을
127
140
  # 요구하지 않는다는 원칙과 같은 판단이다.
128
141
  _EntryGuardRead(
129
- "host-orchestration-rules.md", None, _HOST_RULES_CITE, staged_per_run=True
142
+ "host-orchestration-rules.md", None, _HOST_RULES_CITE, staged_per_run=True,
143
+ task_types=_IMPLEMENTATION_ONLY,
144
+ ),
145
+ # 수렴과 계획-본문 검증은 규칙 문서가 lazy 로 남는다 — Phase 1 intake 가
146
+ # 읽지 않으므로, 읽지 않은 lead 는 그 라운드를 자기 기억으로 돌린다. 그
147
+ # 두 문서에 lead 대상 MUST 의 절반 이상이 들어 있다.
148
+ #
149
+ # task-type 이 아니라 체크포인트로 조건을 건다: 라운드를 실제로 돌린 run 만
150
+ # 그 문서를 요구받는다.
151
+ _EntryGuardRead(
152
+ "convergence.md",
153
+ ("phase-5.5-convergence", "before the first convergence round"),
154
+ _CONVERGENCE_CITE,
155
+ required_when="phase-5.5-convergence",
156
+ ),
157
+ _EntryGuardRead(
158
+ "plan-body-verification.md",
159
+ ("phase-5.5.9-plan-verify", "before the first plan-verify round"),
160
+ _PLAN_BODY_CITE,
161
+ required_when="phase-5.5.9-plan-verify",
130
162
  ),
131
163
  )
132
164
 
@@ -325,7 +357,7 @@ def _collect_lead_evidence(
325
357
  resolve_run_window,
326
358
  resolve_team_needles_with_source,
327
359
  )
328
- from okstra_token_usage.paths import claude_project_dir
360
+ from okstra_token_usage.paths import claude_project_dir, find_session_jsonl
329
361
 
330
362
  since, until = resolve_run_window(team_state_path, team_state, relax_start=False)
331
363
  lead_sid = (team_state.get("lead") or {}).get("sessionId") or ""
@@ -337,12 +369,17 @@ def _collect_lead_evidence(
337
369
  )
338
370
  # `claude --resume` 로 fork 되거나 leadSessionIds[] 에 기록된 lead 세대는
339
371
  # team 태그 needle 로 안 잡힐 수 있어 명시적으로 후보에 추가한다.
372
+ #
373
+ # 기록된 id 는 프로젝트 디렉터리 밖에 있을 수 있다 — 리드 세션의 cwd 가
374
+ # 대상 프로젝트 루트와 다르면(`okstra preflight --cwd <다른 프로젝트>`)
375
+ # transcript 는 리드 cwd 로 인코딩된 디렉터리에 놓인다. 그래서 id 로 찾는다.
340
376
  proj_dir = claude_project_dir(project_root, projects_root)
341
- for sid in (team_state.get("leadSessionIds") or []):
377
+ recorded_ids = [lead_sid, *(team_state.get("leadSessionIds") or [])]
378
+ for sid in recorded_ids:
342
379
  if not isinstance(sid, str) or not sid:
343
380
  continue
344
- candidate = proj_dir / f"{sid}.jsonl"
345
- if candidate.is_file():
381
+ candidate = find_session_jsonl(sid, project_root, projects_root)
382
+ if candidate is not None:
346
383
  sessions.setdefault(sid, candidate)
347
384
  evidence = _LeadEvidence(window=(since, until))
348
385
  for sid, path in sorted(sessions.items()):
@@ -355,7 +392,8 @@ def _collect_lead_evidence(
355
392
  evidence.sidecar_reads.setdefault(base, []).extend(ts_list)
356
393
  if not evidence.scanned_files:
357
394
  return None, (
358
- f"lead session jsonl not found under {proj_dir} "
395
+ f"lead session jsonl not found under {proj_dir} nor anywhere under "
396
+ f"{(projects_root or proj_dir.parent)} by recorded session id "
359
397
  f"(lead.sessionId={lead_sid or '<empty>'}) — selected adapter evidence "
360
398
  "source `claude-jsonl` cannot verify the PROGRESS checkpoint / "
361
399
  "implementation entry-guard conformance cannot be verified, which "
@@ -556,10 +594,22 @@ def _collect_artifact_lead_evidence(
556
594
  def _conformance_evidence_source(
557
595
  team_state: dict,
558
596
  ) -> tuple[str | None, str | None]:
559
- dispatch_mode = str(team_state.get("dispatchMode", "")).strip()
560
- if dispatch_mode in _WORKER_DISPATCH_MODES:
561
- return "artifact-only", None
562
-
597
+ """리드의 증거가 어디 있는지 — 리드 회계가 정한다.
598
+
599
+ `dispatchMode` 는 **워커**를 어떻게 띄우는지를 말하는 필드다. 그 값이
600
+ 리드 세션의 전사 여부를 바꾸지는 않는다: cmux 판으로 워커를 내보낸
601
+ Claude Code 리드도 자기 전사에 Read 와 PROGRESS 를 그대로 남긴다.
602
+ 어댑터 자신도 그렇게 선언한다 —
603
+ `prompts/lead/adapters/cmux.md:18` 의 `sessionAccounting | unchanged`.
604
+
605
+ 종전에는 이 함수가 어댑터 메타데이터를 보기 전에 `dispatchMode` 로
606
+ 단락해 `artifact-only` 를 확정했다. 그러면 수집기가 `lead-events-*.jsonl`
607
+ 만 보는데 거기에는 `okstra agent-activity append` 가 쓴 활동 레코드만
608
+ 들어가고 리드 툴셋에는 PROGRESS 나 파일 읽기를 그리로 보낼 경로가 없다 —
609
+ 실측(`fontsninja-nlpvibe` stage 10): artifact-only 로 progress 0·reads 0,
610
+ 같은 run 을 리드 전사로 읽으면 progress 21·reads 5(entry guard 가 요구하는
611
+ 네 파일 전부). 즉 어떤 리드가 무엇을 해도 통과할 수 없는 검사였다.
612
+ """
563
613
  adapter = team_state.get("leadAdapter")
564
614
  if isinstance(adapter, dict):
565
615
  accounting = str(adapter.get("sessionAccounting", "")).strip()
@@ -591,6 +641,85 @@ def _convergence_rounds_ran(run_dir: Path, suffix: str | None) -> bool:
591
641
  return isinstance(doc, dict) and (doc.get("totalRounds") or 0) >= 1
592
642
 
593
643
 
644
+ def _verification_error_workers(run_dir: Path, suffix: str | None) -> set[str]:
645
+ """이 run 의 convergence 에서 `verification-error` 를 낸 워커들."""
646
+ if not suffix:
647
+ return set()
648
+ path = run_dir / "state" / f"convergence-{suffix}.json"
649
+ try:
650
+ doc = json.loads(path.read_text(encoding="utf-8"))
651
+ except (OSError, json.JSONDecodeError):
652
+ return set()
653
+ if not isinstance(doc, dict):
654
+ return set()
655
+ workers: set[str] = set()
656
+ for finding in doc.get("findings") or []:
657
+ if not isinstance(finding, dict):
658
+ continue
659
+ for entry in finding.get("rounds") or []:
660
+ if not isinstance(entry, dict):
661
+ continue
662
+ for worker, vote in (entry.get("votes") or {}).items():
663
+ if isinstance(vote, dict) and vote.get("verdict") == "verification-error":
664
+ workers.add(str(worker))
665
+ return workers
666
+
667
+
668
+ def _contract_violation_agents(run_dir: Path, suffix: str | None) -> set[str]:
669
+ """run 에러 로그에 `contract-violation` 으로 기록된 agent 들."""
670
+ if not suffix:
671
+ return set()
672
+ path = run_dir / "logs" / f"errors-{suffix}.jsonl"
673
+ try:
674
+ raw = path.read_text(encoding="utf-8")
675
+ except OSError:
676
+ return set()
677
+ agents: set[str] = set()
678
+ for line in raw.splitlines():
679
+ line = line.strip()
680
+ if not line:
681
+ continue
682
+ try:
683
+ row = json.loads(line)
684
+ except json.JSONDecodeError:
685
+ continue
686
+ if isinstance(row, dict) and row.get("errorType") == "contract-violation":
687
+ agent = row.get("agent") or row.get("workerId")
688
+ if agent:
689
+ agents.add(str(agent))
690
+ return agents
691
+
692
+
693
+ def _check_verification_errors_are_logged(
694
+ run_dir: Path, suffix: str | None, advisories: list[str]
695
+ ) -> None:
696
+ """`verification-error` 를 낸 워커는 에러 로그에도 남아야 한다.
697
+
698
+ 계약(okstra-lead-contract.md §"Phase 5.5: Convergence loop")은 재검증 배치가
699
+ `verification-error` 로 끝나면 lead 가 위반마다 한 건씩
700
+ `okstra error-log append-observed --error-type contract-violation` 으로
701
+ 기록하라고 요구한다. 기록이 없으면 워커 실패가 감사 흔적 없이 지나가고,
702
+ 그 run 의 합의는 실제보다 적은 검증 위에 서 있다.
703
+
704
+ advisory 로 낸다. 이 파일의 구분을 따른다 — 검증기가 도는 시점에는 그 기록을
705
+ 더 이상 쓸 수 없고(세션이 끝났다), 빠진 기록은 run 을 나중에 따라가기 어렵게
706
+ 만들지 그 run 의 작업이 틀렸다는 뜻은 아니다.
707
+ """
708
+ failed = _verification_error_workers(run_dir, suffix)
709
+ if not failed:
710
+ return
711
+ logged = _contract_violation_agents(run_dir, suffix)
712
+ missing = sorted(failed - logged)
713
+ if missing:
714
+ advisories.append(
715
+ "convergence verification-error not recorded in the run error log for: "
716
+ + ", ".join(missing)
717
+ + " — prompts/lead/okstra-lead-contract.md 'Phase 5.5: Convergence loop' "
718
+ "requires one `okstra error-log append-observed --error-type "
719
+ "contract-violation` event per violation."
720
+ )
721
+
722
+
594
723
  def _plan_body_rounds_ran(run_dir: Path, suffix: str | None) -> int:
595
724
  """이 run 의 plan-body 검증이 실제로 돈 라운드 수.
596
725
 
@@ -852,19 +981,60 @@ def _allowed_automatic_rounds(self_fix_rounds: int, *, gating: bool = True) -> i
852
981
  return 2 + max(self_fix_rounds, 0)
853
982
 
854
983
 
855
- def _self_fix_rounds_from_state(run_dir: Path, suffix: str | None) -> int | None:
856
- """이번 런 상태 파일의 자가수정 횟수. 없으면 None — 호출자가 리포트로 폴백.
857
-
858
- 리포트 칸은 이어진 seq 의 누적이라, 이번 창의 `self-fix-applied` 건수와
859
- 비교하면 어긋난다.
860
- """
984
+ def _plan_body_state_document(run_dir: Path, suffix: str | None) -> dict | None:
861
985
  if not suffix:
862
986
  return None
863
987
  try:
864
988
  doc = json.loads(_plan_body_state_path(run_dir, suffix).read_text())
865
989
  except (OSError, json.JSONDecodeError):
866
990
  return None
867
- if not isinstance(doc, dict):
991
+ return doc if isinstance(doc, dict) else None
992
+
993
+
994
+ def _plan_body_round_history_len(run_dir: Path, suffix: str | None) -> int | None:
995
+ doc = _plan_body_state_document(run_dir, suffix)
996
+ if doc is None:
997
+ return None
998
+ history = doc.get("roundHistory")
999
+ if not isinstance(history, list):
1000
+ return None
1001
+ return len(history)
1002
+
1003
+
1004
+ def _self_fix_event_expectation(
1005
+ run_dir: Path, suffix: str | None, self_fix_rounds: int,
1006
+ ) -> int:
1007
+ """selfFixRoundsApplied 는 마지막 라운드 번호다. 활동 건수는 그룹 라운드 수.
1008
+
1009
+ 라운드 3 에 원인 그룹 4개를 한 번에 쓰면 필드 값은 3, 이벤트는 1이다.
1010
+ """
1011
+ doc = _plan_body_state_document(run_dir, suffix)
1012
+ if doc is None:
1013
+ return self_fix_rounds
1014
+ projection = doc.get("planBodyVerification")
1015
+ groups = (
1016
+ projection.get("selfFixGroups")
1017
+ if isinstance(projection, dict)
1018
+ else None
1019
+ )
1020
+ if not isinstance(groups, list) or not groups:
1021
+ return self_fix_rounds
1022
+ rounds = {
1023
+ row.get("round")
1024
+ for row in groups
1025
+ if isinstance(row, dict) and isinstance(row.get("round"), int)
1026
+ }
1027
+ return len(rounds) if rounds else self_fix_rounds
1028
+
1029
+
1030
+ def _self_fix_rounds_from_state(run_dir: Path, suffix: str | None) -> int | None:
1031
+ """이번 런 상태 파일의 자가수정 횟수. 없으면 None — 호출자가 리포트로 폴백.
1032
+
1033
+ 리포트 칸은 이어진 seq 의 누적이라, 이번 창의 `self-fix-applied` 건수와
1034
+ 비교하면 어긋난다.
1035
+ """
1036
+ doc = _plan_body_state_document(run_dir, suffix)
1037
+ if doc is None:
868
1038
  return None
869
1039
  projection = doc.get("planBodyVerification")
870
1040
  value = (
@@ -886,7 +1056,11 @@ def _is_plan_body_verification_activity(event: LeadEvent) -> bool:
886
1056
  본문이 아니다. 픽스처 요약(`verification-round-completed for …`)은 본문이다.
887
1057
  """
888
1058
  summary = str(event.details.get("summary") or "").lower()
889
- return "adversarial reverify" not in summary and "adversarial re-verify" not in summary
1059
+ if "adversarial reverify" in summary or "adversarial re-verify" in summary:
1060
+ return False
1061
+ if "critic-gap" in summary:
1062
+ return False
1063
+ return True
890
1064
 
891
1065
 
892
1066
  def _plan_body_verification(report_data: Mapping[str, Any]) -> Mapping[str, Any] | None:
@@ -1036,7 +1210,11 @@ def _check_activity_round_counts(
1036
1210
  f"roundCount={verification_rounds}, "
1037
1211
  f"humanReverificationRounds={sorted(user_reverification_rounds)}."
1038
1212
  )
1039
- if recorded_verifications != automatic_rounds:
1213
+ history_len = _plan_body_round_history_len(run_dir, suffix)
1214
+ if (
1215
+ recorded_verifications != automatic_rounds
1216
+ and history_len != verification_rounds
1217
+ ):
1040
1218
  errors.append(
1041
1219
  "activity contract: `verification-round-completed` count must match "
1042
1220
  f"automatic plan-body rounds={automatic_rounds} from "
@@ -1045,10 +1223,13 @@ def _check_activity_round_counts(
1045
1223
  f"recorded={recorded_verifications}."
1046
1224
  )
1047
1225
  recorded_self_fixes = len(indexed.get("self-fix-applied", []))
1048
- if recorded_self_fixes != self_fix_rounds:
1226
+ expected_self_fixes = _self_fix_event_expectation(
1227
+ run_dir, suffix, self_fix_rounds,
1228
+ )
1229
+ if recorded_self_fixes != expected_self_fixes:
1049
1230
  errors.append(
1050
1231
  "activity contract: `self-fix-applied` count must match "
1051
- f"selfFixRoundsApplied={self_fix_rounds}; recorded={recorded_self_fixes}."
1232
+ f"selfFixRoundsApplied={expected_self_fixes}; recorded={recorded_self_fixes}."
1052
1233
  )
1053
1234
 
1054
1235
 
@@ -1254,6 +1435,7 @@ def _check_progress_checkpoints(
1254
1435
  run_dir: Path,
1255
1436
  suffix: str | None,
1256
1437
  report_path: Path,
1438
+ task_type: str,
1257
1439
  errors: list[str],
1258
1440
  advisories: list[str] | None = None,
1259
1441
  ) -> None:
@@ -1293,7 +1475,7 @@ def _check_progress_checkpoints(
1293
1475
  "phase-3-team-create",
1294
1476
  any_dispatched,
1295
1477
  "in Phase 3 after recording teamCreate in team-state — the "
1296
- "`using implicit team (split-pane=<on|off>)` line, or the "
1478
+ "`using implicit team` line, or the "
1297
1479
  "`skipped (concurrent-run)` variant in the concurrent-run path",
1298
1480
  )
1299
1481
  _check_worker_checkpoint_lines(by_phase, analysis_workers, narration)
@@ -1313,6 +1495,28 @@ def _check_progress_checkpoints(
1313
1495
  report_writer_dispatched,
1314
1496
  "at the start of Phase 6 (report-writer dispatch)",
1315
1497
  )
1498
+ if task_type == "implementation":
1499
+ # 이 run 이 실행하는 stage 의 공지/완료 라인 — implementation 전용 두
1500
+ # 체크포인트. 완료 라인은 carry 증거를 파싱한 뒤에만 낼 수 있으므로
1501
+ # implementer 가 완주한 run 에만 요구한다.
1502
+ require(
1503
+ "phase-5-stage",
1504
+ any_dispatched,
1505
+ "immediately before the Executor dispatch — `stage=<N> title=<title> "
1506
+ "steps=<count>` from the approved plan's Stage Map",
1507
+ )
1508
+ implementer_completed = any(
1509
+ "implementer"
1510
+ in _norm(str(w.get("workerId") or "") + str(w.get("role") or ""))
1511
+ and str(w.get("status", "")).strip() == "completed"
1512
+ for w in workers
1513
+ )
1514
+ require(
1515
+ "phase-5-stage-complete",
1516
+ implementer_completed,
1517
+ "immediately after parsing the Executor's `### Stage Carry Evidence` "
1518
+ "block — `stage=<N> steps=<done>/<count>` from its stepResults",
1519
+ )
1316
1520
  require("phase-7-persist", True, "at the start of Phase 7")
1317
1521
  _check_batch_cleanup_checkpoints(
1318
1522
  by_phase, convergence_ran, report_writer_dispatched, narration
@@ -1375,6 +1579,25 @@ def _check_heartbeat_sidecar(path: Path, worker_id: str, errors: list[str]) -> N
1375
1579
  f"although the worker result `{result_file.name}` exists — every "
1376
1580
  "stage must append its own line (agents/workers/claude-worker.md 'Heartbeat')."
1377
1581
  )
1582
+ # implementer(Executor)는 지금 실행 중인 plan step 을 하트비트로 알린다
1583
+ # (_implementation-executor.md 'Per-step heartbeat'). result 를 남기고 완주한
1584
+ # executor 의 사이드카에 step 라인이 하나도 없으면 이 run 의 유일한 스텝 단위
1585
+ # 진행 신호가 없었던 것이다. cli-wrapper executor 는 wrapper-status 경로라
1586
+ # 이 함수에 들어오지 않는다.
1587
+ if (
1588
+ "implementer" in _norm(worker_id)
1589
+ and result_file.exists()
1590
+ and not any(
1591
+ stage.removeprefix(_IN_STAGE_PREFIX).startswith("step-")
1592
+ for stage, _ in entries
1593
+ )
1594
+ ):
1595
+ errors.append(
1596
+ f"`{rel}`: implementer heartbeat has no `- PROGRESS: step-<k> <ISO>` "
1597
+ "line although the worker result exists — the executor MUST announce "
1598
+ "each plan step as it starts "
1599
+ "(prompts/profiles/_implementation-executor.md 'Per-step heartbeat')."
1600
+ )
1378
1601
  prev: datetime | None = None
1379
1602
  prev_stage = ""
1380
1603
  for stage, raw_ts in entries:
@@ -1483,10 +1706,10 @@ def _check_cmux_adapter_read(
1483
1706
  return
1484
1707
  if evidence.sidecar_reads.get(CMUX_ADAPTER_BASENAME):
1485
1708
  return
1486
- dispatch_mode = str(team_state.get("dispatchMode", "")).strip()
1487
- if dispatch_mode in _WORKER_DISPATCH_MODES:
1488
- # grok 같은 artifact-only 호스트는 Read 도구 기록이 없다. 워커가
1489
- # okstra 디스패치로 나갔으면 어댑터가 가리키는 경로를 탄 것이다.
1709
+ source, _error = _conformance_evidence_source(team_state)
1710
+ if source == "artifact-only":
1711
+ # grok 같은 artifact-only 호스트는 Read 도구 기록이 없다. 면제 사유는
1712
+ # 리드가 읽기를 남기지 못한다는 것이지 워커를 어떻게 띄웠는지가 아니다.
1490
1713
  return
1491
1714
  errors.append(
1492
1715
  f"cmux adapter: no read of `{CMUX_ADAPTER_BASENAME}` (a `Read` call or a "
@@ -1497,32 +1720,53 @@ def _check_cmux_adapter_read(
1497
1720
  )
1498
1721
 
1499
1722
 
1500
- def _check_implementation_entry_guard(
1501
- evidence: _LeadEvidence, errors: list[str], instruction_set: Path | None
1723
+ def _entry_guard_rows(
1724
+ task_type: str, seen_phases: set[str], instruction_set: Path | None
1725
+ ) -> list[_EntryGuardRead]:
1726
+ """이 run 에 실제로 걸리는 entry-guard 행."""
1727
+ rows = []
1728
+ for row in _ENTRY_GUARD_READS:
1729
+ if row.task_types is not None and task_type not in row.task_types:
1730
+ continue
1731
+ if row.required_when is not None and row.required_when not in seen_phases:
1732
+ # 그 라운드를 돌지 않은 run 이다. 돌지 않은 일의 규칙을 요구하지 않는다.
1733
+ continue
1734
+ if row.staged_per_run and not (
1735
+ instruction_set and (instruction_set / row.basename).is_file()
1736
+ ):
1737
+ continue
1738
+ rows.append(row)
1739
+ return rows
1740
+
1741
+
1742
+ def _check_entry_guard_reads(
1743
+ evidence: _LeadEvidence,
1744
+ errors: list[str],
1745
+ instruction_set: Path | None,
1746
+ task_type: str,
1502
1747
  ) -> None:
1503
1748
  """검사 3 — entry guard. fresh-read 규칙(이전 run 기억으로 갈음 불가)은 run
1504
1749
  윈도우 스코핑이 보장한다: 이번 윈도우 안의 Read 만 증거로 인정된다.
1505
1750
 
1506
1751
  anchor 가 있는 행은 존재 + 순서를, anchor 가 `None` 인 행은 존재만 본다.
1507
- `staged_per_run` 행은 이 run 의 instruction-set 에 실제로 있을 때만 요구한다."""
1752
+ `staged_per_run` 행은 이 run 의 instruction-set 에 실제로 있을 때만,
1753
+ `required_when` 행은 그 PROGRESS 체크포인트가 이 run 에 있을 때만 요구한다."""
1754
+ seen_phases = {phase for _ts, phase, _line in evidence.progress}
1755
+ anchor_phases = {row.anchor[0] for row in _ENTRY_GUARD_READS if row.anchor}
1508
1756
  anchors: dict[str, str] = {}
1509
1757
  for ts, phase, _line in evidence.progress: # progress 는 ts 정렬 — 첫 항목이 최초 발생
1510
- if phase in ("phase-6-synthesis", "phase-7-persist") and ts:
1758
+ if phase in anchor_phases and ts:
1511
1759
  anchors.setdefault(phase, ts)
1512
- for row in _ENTRY_GUARD_READS:
1513
- if row.staged_per_run and not (
1514
- instruction_set and (instruction_set / row.basename).is_file()
1515
- ):
1516
- continue
1760
+ for row in _entry_guard_rows(task_type, seen_phases, instruction_set):
1517
1761
  ts_list = evidence.sidecar_reads.get(row.basename) or []
1518
1762
  if not ts_list:
1519
1763
  demand = (
1520
- f"it MUST be read fresh {row.anchor[1]} of every implementation run"
1764
+ f"it MUST be read fresh {row.anchor[1]}"
1521
1765
  if row.anchor
1522
1766
  else "the staged copy MUST be read at least once inside that window"
1523
1767
  )
1524
1768
  errors.append(
1525
- f"implementation entry guard: no read of `{row.basename}` (a `Read` call "
1769
+ f"entry guard: no read of `{row.basename}` (a `Read` call "
1526
1770
  f"or a shell command naming it) found in "
1527
1771
  f"the selected adapter evidence source within this run's window — "
1528
1772
  f"{demand} ({row.cite})."
@@ -1534,7 +1778,7 @@ def _check_implementation_entry_guard(
1534
1778
  anchor_ts = anchors.get(anchor_phase)
1535
1779
  if anchor_ts and min(ts_list) >= anchor_ts:
1536
1780
  errors.append(
1537
- f"implementation entry guard: `{row.basename}` was first read at "
1781
+ f"entry guard: `{row.basename}` was first read at "
1538
1782
  f"{min(ts_list)}, not before the first `PROGRESS: {anchor_phase}` "
1539
1783
  f"line ({anchor_ts}) — it must be loaded {when}, before "
1540
1784
  f"that checkpoint ({row.cite})."
@@ -1595,9 +1839,11 @@ def validate_session_conformance(
1595
1839
  run_dir,
1596
1840
  suffix,
1597
1841
  report_path,
1842
+ task_type,
1598
1843
  result.errors,
1599
1844
  result.advisories,
1600
1845
  )
1846
+ _check_verification_errors_are_logged(run_dir, suffix, result.advisories)
1601
1847
  _check_activity_contract(
1602
1848
  evidence,
1603
1849
  team_state,
@@ -1608,10 +1854,10 @@ def validate_session_conformance(
1608
1854
  result.errors,
1609
1855
  )
1610
1856
  _check_cmux_adapter_read(evidence, team_state, result.errors)
1611
- if task_type == "implementation":
1612
- _check_implementation_entry_guard(
1613
- evidence,
1614
- result.errors,
1615
- _instruction_set_dir(run_dir, suffix, project_root),
1616
- )
1857
+ _check_entry_guard_reads(
1858
+ evidence,
1859
+ result.errors,
1860
+ _instruction_set_dir(run_dir, suffix, project_root),
1861
+ task_type,
1862
+ )
1617
1863
  return result