okstra 0.186.7 → 0.188.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (546) hide show
  1. package/README.md +1 -1
  2. package/dist/cli-registry.d.mts +88 -1
  3. package/dist/cli-registry.mjs +68 -111
  4. package/dist/cli-registry.mjs.map +1 -1
  5. package/dist/commands/execute/render-bundle.mjs +0 -1
  6. package/dist/commands/execute/render-bundle.mjs.map +1 -1
  7. package/dist/commands/execute/run.mjs +8 -3
  8. package/dist/commands/execute/run.mjs.map +1 -1
  9. package/dist/commands/lifecycle/check-project.mjs +1 -14
  10. package/dist/commands/lifecycle/check-project.mjs.map +1 -1
  11. package/dist/commands/lifecycle/config.mjs +38 -40
  12. package/dist/commands/lifecycle/config.mjs.map +1 -1
  13. package/dist/commands/lifecycle/doctor.d.mts +22 -7
  14. package/dist/commands/lifecycle/doctor.mjs +77 -49
  15. package/dist/commands/lifecycle/doctor.mjs.map +1 -1
  16. package/dist/commands/lifecycle/install.d.mts +12 -10
  17. package/dist/commands/lifecycle/install.mjs +107 -40
  18. package/dist/commands/lifecycle/install.mjs.map +1 -1
  19. package/dist/commands/lifecycle/paths.mjs +8 -3
  20. package/dist/commands/lifecycle/paths.mjs.map +1 -1
  21. package/dist/commands/lifecycle/preflight.mjs +2 -1
  22. package/dist/commands/lifecycle/preflight.mjs.map +1 -1
  23. package/dist/commands/lifecycle/setup.mjs +22 -36
  24. package/dist/commands/lifecycle/setup.mjs.map +1 -1
  25. package/dist/commands/lifecycle/uninstall.d.mts +4 -2
  26. package/dist/commands/lifecycle/uninstall.mjs +59 -15
  27. package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
  28. package/dist/commands/memory/memory.mjs +7 -1
  29. package/dist/commands/memory/memory.mjs.map +1 -1
  30. package/dist/lib/helper-scripts.d.mts +1 -1
  31. package/dist/lib/helper-scripts.mjs +10 -18
  32. package/dist/lib/helper-scripts.mjs.map +1 -1
  33. package/dist/lib/host-config.d.mts +72 -0
  34. package/dist/lib/host-config.mjs +404 -0
  35. package/dist/lib/host-config.mjs.map +1 -0
  36. package/dist/lib/host-registry-client.d.mts +2 -2
  37. package/dist/lib/host-registry-client.mjs +0 -3
  38. package/dist/lib/host-registry-client.mjs.map +1 -1
  39. package/dist/lib/install-assets.d.mts +1 -0
  40. package/dist/lib/install-assets.mjs +4 -0
  41. package/dist/lib/install-assets.mjs.map +1 -1
  42. package/dist/lib/proc.d.mts +2 -0
  43. package/dist/lib/proc.mjs +12 -0
  44. package/dist/lib/proc.mjs.map +1 -1
  45. package/dist/lib/python-command.d.mts +2 -0
  46. package/dist/lib/python-command.mjs +52 -0
  47. package/dist/lib/python-command.mjs.map +1 -0
  48. package/dist/lib/python-helper.d.mts +2 -5
  49. package/dist/lib/python-helper.mjs +3 -52
  50. package/dist/lib/python-helper.mjs.map +1 -1
  51. package/dist/lib/runtime-payload.d.mts +23 -0
  52. package/dist/lib/runtime-payload.mjs +57 -0
  53. package/dist/lib/runtime-payload.mjs.map +1 -0
  54. package/dist/lib/types.d.mts +24 -13
  55. package/docs/architecture/storage-model.md +21 -5
  56. package/docs/architecture.md +39 -38
  57. package/docs/cli.md +55 -76
  58. package/docs/coding-rules.md +295 -0
  59. package/docs/container.md +10 -36
  60. package/docs/contributor-change-matrix.md +1 -1
  61. package/docs/for-ai/skills/okstra-code-review.md +0 -1
  62. package/docs/for-ai/skills/okstra-container-build.md +8 -41
  63. package/docs/for-ai/skills/okstra-inspect.md +4 -4
  64. package/docs/for-ai/skills/okstra-manager.md +2 -2
  65. package/docs/for-ai/skills/okstra-rollup.md +0 -1
  66. package/docs/for-ai/skills/okstra-run.md +3 -3
  67. package/docs/for-ai/skills/okstra-user-response.md +0 -1
  68. package/docs/performance-improvement-plan-v2.md +1 -1
  69. package/docs/project-structure-overview.md +62 -62
  70. package/docs/task-process/README.md +3 -3
  71. package/docs/task-process/common-flow.md +1 -1
  72. package/docs/task-process/error-analysis.md +3 -3
  73. package/docs/task-process/implementation-planning.md +3 -1
  74. package/docs/task-process/release-handoff.md +1 -1
  75. package/package.json +3 -2
  76. package/runtime/BUILD.json +2 -2
  77. package/runtime/agents/workers/claude-worker.md +6 -11
  78. package/runtime/agents/workers/report-writer-worker.md +1 -1
  79. package/runtime/bin/lib/okstra/cli.sh +4 -0
  80. package/runtime/bin/lib/okstra/globals.sh +2 -0
  81. package/runtime/bin/lib/okstra/interactive.sh +26 -173
  82. package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
  83. package/runtime/bin/lib/okstra/usage.sh +3 -3
  84. package/runtime/bin/okstra-compact-reminder.sh +1 -5
  85. package/runtime/bin/okstra-error-log.py +19 -2
  86. package/runtime/bin/okstra-import-check.py +26 -0
  87. package/runtime/bin/okstra-inject-report-index.py +5 -4
  88. package/runtime/bin/okstra-provider-exec.py +22 -17
  89. package/runtime/bin/okstra-render-final-report.py +16 -6
  90. package/runtime/bin/okstra-render-report-views.py +47 -22
  91. package/runtime/bin/okstra-report-translate.py +30 -18
  92. package/runtime/bin/okstra-spawn-followups.py +4 -0
  93. package/runtime/bin/okstra-token-usage.py +3 -0
  94. package/runtime/bin/okstra.sh +4 -2
  95. package/runtime/bin/okstra_bootstrap.py +58 -0
  96. package/runtime/prompts/coding-preflight/overview.md +1 -1
  97. package/runtime/prompts/duties/planning-worker.md +1 -1
  98. package/runtime/prompts/launch.template.md +35 -9
  99. package/runtime/prompts/lead/adapters/cmux.md +3 -3
  100. package/runtime/prompts/lead/convergence.md +99 -88
  101. package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
  102. package/runtime/prompts/lead/plan-body-verification.md +65 -46
  103. package/runtime/prompts/lead/report-writer.md +35 -5
  104. package/runtime/prompts/lead/team-contract.md +4 -1
  105. package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
  106. package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
  107. package/runtime/prompts/profiles/_common-contract.md +3 -3
  108. package/runtime/prompts/profiles/_coverage-critic.md +1 -1
  109. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
  110. package/runtime/prompts/profiles/_implementation-executor.md +3 -1
  111. package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
  112. package/runtime/prompts/profiles/error-analysis.md +4 -3
  113. package/runtime/prompts/profiles/final-verification.md +2 -2
  114. package/runtime/prompts/profiles/implementation-planning.md +22 -17
  115. package/runtime/prompts/profiles/implementation.md +3 -2
  116. package/runtime/prompts/profiles/requirements-discovery.md +2 -1
  117. package/runtime/prompts/wizard/prompts.ko.json +29 -8
  118. package/runtime/python/okstra_ctl/__init__.py +6 -27
  119. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
  120. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
  121. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
  122. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +63 -22
  123. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +20 -21
  124. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
  125. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
  126. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
  127. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
  128. package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
  129. package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +16 -2
  130. package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
  131. package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
  132. package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
  133. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +228 -0
  134. package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
  135. package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
  136. package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
  137. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +605 -0
  138. package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
  139. package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
  140. package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
  141. package/runtime/python/okstra_ctl/approval_decisions.py +287 -10
  142. package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
  143. package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
  144. package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
  145. package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
  146. package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
  147. package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
  148. package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
  149. package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
  150. package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
  151. package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
  152. package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
  153. package/runtime/python/okstra_ctl/cmux.py +38 -31
  154. package/runtime/python/okstra_ctl/code_review_target.py +163 -1
  155. package/runtime/python/okstra_ctl/conformance.py +23 -0
  156. package/runtime/python/okstra_ctl/container.py +38 -421
  157. package/runtime/python/okstra_ctl/context_cost.py +32 -4
  158. package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
  159. package/runtime/python/okstra_ctl/convergence.py +107 -16
  160. package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
  161. package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
  162. package/runtime/python/okstra_ctl/convergence_store.py +5 -29
  163. package/runtime/python/okstra_ctl/design_prep.py +108 -12
  164. package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
  165. package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
  166. package/runtime/python/okstra_ctl/dispatch_core.py +236 -49
  167. package/runtime/python/okstra_ctl/dispatch_state.py +417 -41
  168. package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
  169. package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
  170. package/runtime/python/okstra_ctl/domain/worker_presentation.py +28 -1
  171. package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
  172. package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
  173. package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
  174. package/runtime/python/okstra_ctl/error_log_core.py +1 -1
  175. package/runtime/python/okstra_ctl/error_report.py +14 -0
  176. package/runtime/python/okstra_ctl/error_zip.py +18 -2
  177. package/runtime/python/okstra_ctl/exact_coverage.py +5 -0
  178. package/runtime/python/okstra_ctl/execution_identity.py +100 -7
  179. package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
  180. package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
  181. package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
  182. package/runtime/python/okstra_ctl/final_report_schema.py +319 -17
  183. package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
  184. package/runtime/python/okstra_ctl/handoff.py +24 -1
  185. package/runtime/python/okstra_ctl/ids.py +6 -16
  186. package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
  187. package/runtime/python/okstra_ctl/implementation_options.py +32 -13
  188. package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
  189. package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
  190. package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
  191. package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
  192. package/runtime/python/okstra_ctl/index.py +2 -2
  193. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
  194. package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
  195. package/runtime/python/okstra_ctl/json_boundary.py +10 -0
  196. package/runtime/python/okstra_ctl/listing.py +0 -91
  197. package/runtime/python/okstra_ctl/locks.py +5 -19
  198. package/runtime/python/okstra_ctl/log_report.py +14 -0
  199. package/runtime/python/okstra_ctl/manager_cli.py +22 -3
  200. package/runtime/python/okstra_ctl/manager_launch.py +4 -8
  201. package/runtime/python/okstra_ctl/material.py +2 -2
  202. package/runtime/python/okstra_ctl/migrate.py +23 -1
  203. package/runtime/python/okstra_ctl/model_cli.py +19 -5
  204. package/runtime/python/okstra_ctl/model_discovery.py +46 -47
  205. package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
  206. package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
  207. package/runtime/python/okstra_ctl/model_io/references.py +370 -0
  208. package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
  209. package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
  210. package/runtime/python/okstra_ctl/model_pool.py +10 -6
  211. package/runtime/python/okstra_ctl/models.py +8 -8
  212. package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
  213. package/runtime/python/okstra_ctl/next_phase.py +121 -69
  214. package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
  215. package/runtime/python/okstra_ctl/pane_title.py +11 -8
  216. package/runtime/python/okstra_ctl/path_hints.py +36 -11
  217. package/runtime/python/okstra_ctl/paths.py +76 -8
  218. package/runtime/python/okstra_ctl/plan_items.py +132 -16
  219. package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
  220. package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
  221. package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
  222. package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
  223. package/runtime/python/okstra_ctl/prepare_error.py +20 -0
  224. package/runtime/python/okstra_ctl/prior_planning.py +157 -0
  225. package/runtime/python/okstra_ctl/profile_show.py +18 -1
  226. package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
  227. package/runtime/python/okstra_ctl/recap.py +22 -1
  228. package/runtime/python/okstra_ctl/reconcile.py +84 -0
  229. package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
  230. package/runtime/python/okstra_ctl/render.py +98 -32
  231. package/runtime/python/okstra_ctl/render_final_report.py +4 -8
  232. package/runtime/python/okstra_ctl/report_assembly.py +297 -7
  233. package/runtime/python/okstra_ctl/report_contract.py +30 -7
  234. package/runtime/python/okstra_ctl/report_finalize.py +213 -16
  235. package/runtime/python/okstra_ctl/report_html/common.py +2 -2
  236. package/runtime/python/okstra_ctl/report_html/render.py +8 -12
  237. package/runtime/python/okstra_ctl/report_language.py +8 -4
  238. package/runtime/python/okstra_ctl/report_narrative.py +220 -34
  239. package/runtime/python/okstra_ctl/report_projections.py +8 -2
  240. package/runtime/python/okstra_ctl/report_synthesis_packet.py +393 -1
  241. package/runtime/python/okstra_ctl/report_translation.py +2 -35
  242. package/runtime/python/okstra_ctl/report_view_artifacts.py +42 -2
  243. package/runtime/python/okstra_ctl/report_views.py +4 -22
  244. package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
  245. package/runtime/python/okstra_ctl/rollup.py +13 -0
  246. package/runtime/python/okstra_ctl/run.py +639 -138
  247. package/runtime/python/okstra_ctl/run_audit.py +13 -0
  248. package/runtime/python/okstra_ctl/run_index_row.py +0 -12
  249. package/runtime/python/okstra_ctl/seeding.py +21 -11
  250. package/runtime/python/okstra_ctl/sequence.py +1 -1
  251. package/runtime/python/okstra_ctl/session.py +64 -14
  252. package/runtime/python/okstra_ctl/set_work_status.py +18 -0
  253. package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
  254. package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
  255. package/runtime/python/okstra_ctl/stage_map.py +57 -11
  256. package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
  257. package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
  258. package/runtime/python/okstra_ctl/stage_targets.py +3 -12
  259. package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
  260. package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
  261. package/runtime/python/okstra_ctl/task_target.py +4 -16
  262. package/runtime/python/okstra_ctl/team.py +49 -2
  263. package/runtime/python/okstra_ctl/time_report.py +17 -2
  264. package/runtime/python/okstra_ctl/usage_report.py +11 -0
  265. package/runtime/python/okstra_ctl/user_response.py +55 -218
  266. package/runtime/python/okstra_ctl/user_response_values.py +242 -0
  267. package/runtime/python/okstra_ctl/validation_contract.py +3 -0
  268. package/runtime/python/okstra_ctl/verdict_blocks.py +28 -8
  269. package/runtime/python/okstra_ctl/verification_target.py +68 -0
  270. package/runtime/python/okstra_ctl/wizard.py +457 -92
  271. package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
  272. package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
  273. package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
  274. package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
  275. package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
  276. package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
  277. package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
  278. package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
  279. package/runtime/python/okstra_ctl/worker_request.py +2 -1
  280. package/runtime/python/okstra_ctl/worker_runner.py +58 -17
  281. package/runtime/python/okstra_ctl/worker_state.py +16 -1
  282. package/runtime/python/okstra_ctl/workflow.py +18 -1
  283. package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
  284. package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
  285. package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
  286. package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
  287. package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
  288. package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
  289. package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
  290. package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
  291. package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
  292. package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
  293. package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
  294. package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
  295. package/runtime/python/okstra_ctl/write_policy.py +90 -213
  296. package/runtime/python/okstra_project/__init__.py +0 -4
  297. package/runtime/python/okstra_project/dirs.py +2 -2
  298. package/runtime/python/okstra_project/phase_pointer.py +84 -0
  299. package/runtime/python/okstra_project/slug.py +24 -0
  300. package/runtime/python/okstra_project/state.py +22 -230
  301. package/runtime/python/okstra_token_usage/antigravity.py +50 -12
  302. package/runtime/python/okstra_token_usage/claude.py +9 -1
  303. package/runtime/python/okstra_token_usage/cli.py +3 -1
  304. package/runtime/python/okstra_token_usage/collect.py +171 -36
  305. package/runtime/python/okstra_token_usage/cursor.py +2 -0
  306. package/runtime/python/okstra_token_usage/grok.py +1 -5
  307. package/runtime/python/okstra_token_usage/paths.py +26 -0
  308. package/runtime/python/okstra_token_usage/pricing.py +23 -8
  309. package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
  310. package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
  311. package/runtime/schemas/final-report-v2.0.schema.json +9 -26
  312. package/runtime/schemas/final-report-v3.0.schema.json +173 -41
  313. package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
  314. package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
  315. package/runtime/skills/okstra-container-build/SKILL.md +16 -47
  316. package/runtime/skills/okstra-inspect/facets/history.md +1 -1
  317. package/runtime/skills/okstra-inspect/facets/status.md +7 -6
  318. package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
  319. package/runtime/skills/okstra-run/SKILL.md +7 -5
  320. package/runtime/templates/report-writer-prompt-preamble.md +3 -1
  321. package/runtime/templates/reports/brief.template.md +6 -2
  322. package/runtime/templates/reports/error-analysis-input.template.md +2 -0
  323. package/runtime/templates/reports/final-verification-input.template.md +2 -0
  324. package/runtime/templates/reports/implementation-input.template.md +7 -1
  325. package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
  326. package/runtime/templates/reports/quick-input.template.md +2 -0
  327. package/runtime/templates/reports/release-handoff-input.template.md +6 -1
  328. package/runtime/templates/reports/report.js +20 -3
  329. package/runtime/templates/reports/schedule.template.md +2 -0
  330. package/runtime/templates/reports/settings.template.json +0 -10
  331. package/runtime/templates/reports/task-brief.template.md +3 -0
  332. package/runtime/templates/reports/user-response.template.md +7 -3
  333. package/runtime/validators/checks/fixtures-01.py +57 -0
  334. package/runtime/validators/checks/fixtures-02.py +565 -0
  335. package/runtime/validators/checks/runners-01.py +108 -0
  336. package/runtime/validators/checks/validate-assets-01.py +60 -0
  337. package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
  338. package/runtime/validators/checks/validate-tasks-01.py +46 -0
  339. package/runtime/validators/checks/validate-tasks-02.py +85 -0
  340. package/runtime/validators/checks/validate-tasks-03.py +61 -0
  341. package/runtime/validators/checks/validate-tasks-04.py +118 -0
  342. package/runtime/validators/forbidden_actions.py +73 -3
  343. package/runtime/validators/lib/common.sh +5 -0
  344. package/runtime/validators/lib/fixtures.sh +7 -591
  345. package/runtime/validators/lib/paths.sh +13 -0
  346. package/runtime/validators/lib/runners.sh +6 -104
  347. package/runtime/validators/lib/summary.sh +1 -1
  348. package/runtime/validators/lib/validate-assets.sh +6 -56
  349. package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
  350. package/runtime/validators/lib/validate-tasks.sh +9 -294
  351. package/runtime/validators/validate-implementation-plan-stages.py +4 -4
  352. package/runtime/validators/validate-run.py +1363 -2643
  353. package/runtime/validators/validate-workflow.sh +56 -16
  354. package/runtime/validators/validate_improvement_report.py +2 -1
  355. package/runtime/validators/validate_session_conformance.py +295 -49
  356. package/dist/commands/execute/agent-prompt.d.mts +0 -1
  357. package/dist/commands/execute/agent-prompt.mjs +0 -24
  358. package/dist/commands/execute/agent-prompt.mjs.map +0 -1
  359. package/dist/commands/execute/codex-dispatch.d.mts +0 -3
  360. package/dist/commands/execute/codex-dispatch.mjs +0 -6
  361. package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
  362. package/dist/commands/execute/codex-run.d.mts +0 -3
  363. package/dist/commands/execute/codex-run.mjs +0 -62
  364. package/dist/commands/execute/codex-run.mjs.map +0 -1
  365. package/dist/commands/execute/convergence.d.mts +0 -1
  366. package/dist/commands/execute/convergence.mjs +0 -38
  367. package/dist/commands/execute/convergence.mjs.map +0 -1
  368. package/dist/commands/execute/error-log.d.mts +0 -1
  369. package/dist/commands/execute/error-log.mjs +0 -18
  370. package/dist/commands/execute/error-log.mjs.map +0 -1
  371. package/dist/commands/execute/git-reconcile.d.mts +0 -1
  372. package/dist/commands/execute/git-reconcile.mjs +0 -30
  373. package/dist/commands/execute/git-reconcile.mjs.map +0 -1
  374. package/dist/commands/execute/handoff.d.mts +0 -1
  375. package/dist/commands/execute/handoff.mjs +0 -31
  376. package/dist/commands/execute/handoff.mjs.map +0 -1
  377. package/dist/commands/execute/incremental-carry.d.mts +0 -1
  378. package/dist/commands/execute/incremental-carry.mjs +0 -20
  379. package/dist/commands/execute/incremental-carry.mjs.map +0 -1
  380. package/dist/commands/execute/incremental-scope.d.mts +0 -1
  381. package/dist/commands/execute/incremental-scope.mjs +0 -29
  382. package/dist/commands/execute/incremental-scope.mjs.map +0 -1
  383. package/dist/commands/execute/integrate-stages.d.mts +0 -1
  384. package/dist/commands/execute/integrate-stages.mjs +0 -24
  385. package/dist/commands/execute/integrate-stages.mjs.map +0 -1
  386. package/dist/commands/execute/pane-title.d.mts +0 -1
  387. package/dist/commands/execute/pane-title.mjs +0 -20
  388. package/dist/commands/execute/pane-title.mjs.map +0 -1
  389. package/dist/commands/execute/plan-items.d.mts +0 -1
  390. package/dist/commands/execute/plan-items.mjs +0 -9
  391. package/dist/commands/execute/plan-items.mjs.map +0 -1
  392. package/dist/commands/execute/plan-validate.d.mts +0 -1
  393. package/dist/commands/execute/plan-validate.mjs +0 -68
  394. package/dist/commands/execute/plan-validate.mjs.map +0 -1
  395. package/dist/commands/execute/plan-verify.d.mts +0 -1
  396. package/dist/commands/execute/plan-verify.mjs +0 -43
  397. package/dist/commands/execute/plan-verify.mjs.map +0 -1
  398. package/dist/commands/execute/spawn-followups.d.mts +0 -1
  399. package/dist/commands/execute/spawn-followups.mjs +0 -22
  400. package/dist/commands/execute/spawn-followups.mjs.map +0 -1
  401. package/dist/commands/execute/team.d.mts +0 -3
  402. package/dist/commands/execute/team.mjs +0 -66
  403. package/dist/commands/execute/team.mjs.map +0 -1
  404. package/dist/commands/execute/token-usage.d.mts +0 -1
  405. package/dist/commands/execute/token-usage.mjs +0 -19
  406. package/dist/commands/execute/token-usage.mjs.map +0 -1
  407. package/dist/commands/execute/worker-audit-check.d.mts +0 -1
  408. package/dist/commands/execute/worker-audit-check.mjs +0 -34
  409. package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
  410. package/dist/commands/execute/worker-dispatch.d.mts +0 -7
  411. package/dist/commands/execute/worker-dispatch.mjs +0 -64
  412. package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
  413. package/dist/commands/execute/worker-state.d.mts +0 -1
  414. package/dist/commands/execute/worker-state.mjs +0 -28
  415. package/dist/commands/execute/worker-state.mjs.map +0 -1
  416. package/dist/commands/execute/worktree-lookup.d.mts +0 -1
  417. package/dist/commands/execute/worktree-lookup.mjs +0 -92
  418. package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
  419. package/dist/commands/execute/worktree-status.d.mts +0 -1
  420. package/dist/commands/execute/worktree-status.mjs +0 -121
  421. package/dist/commands/execute/worktree-status.mjs.map +0 -1
  422. package/dist/commands/inspect/code-review.d.mts +0 -1
  423. package/dist/commands/inspect/code-review.mjs +0 -32
  424. package/dist/commands/inspect/code-review.mjs.map +0 -1
  425. package/dist/commands/inspect/container.d.mts +0 -1
  426. package/dist/commands/inspect/container.mjs +0 -25
  427. package/dist/commands/inspect/container.mjs.map +0 -1
  428. package/dist/commands/inspect/context-cost.d.mts +0 -1
  429. package/dist/commands/inspect/context-cost.mjs +0 -25
  430. package/dist/commands/inspect/context-cost.mjs.map +0 -1
  431. package/dist/commands/inspect/design-prep.d.mts +0 -1
  432. package/dist/commands/inspect/design-prep.mjs +0 -22
  433. package/dist/commands/inspect/design-prep.mjs.map +0 -1
  434. package/dist/commands/inspect/error-report.d.mts +0 -1
  435. package/dist/commands/inspect/error-report.mjs +0 -25
  436. package/dist/commands/inspect/error-report.mjs.map +0 -1
  437. package/dist/commands/inspect/error-zip.d.mts +0 -1
  438. package/dist/commands/inspect/error-zip.mjs +0 -24
  439. package/dist/commands/inspect/error-zip.mjs.map +0 -1
  440. package/dist/commands/inspect/log-report.d.mts +0 -1
  441. package/dist/commands/inspect/log-report.mjs +0 -26
  442. package/dist/commands/inspect/log-report.mjs.map +0 -1
  443. package/dist/commands/inspect/model-io.d.mts +0 -1
  444. package/dist/commands/inspect/model-io.mjs +0 -25
  445. package/dist/commands/inspect/model-io.mjs.map +0 -1
  446. package/dist/commands/inspect/profile-show.d.mts +0 -1
  447. package/dist/commands/inspect/profile-show.mjs +0 -28
  448. package/dist/commands/inspect/profile-show.mjs.map +0 -1
  449. package/dist/commands/inspect/recap.d.mts +0 -1
  450. package/dist/commands/inspect/recap.mjs +0 -30
  451. package/dist/commands/inspect/recap.mjs.map +0 -1
  452. package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
  453. package/dist/commands/inspect/resolve-task-key.mjs +0 -24
  454. package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
  455. package/dist/commands/inspect/rollup.d.mts +0 -1
  456. package/dist/commands/inspect/rollup.mjs +0 -25
  457. package/dist/commands/inspect/rollup.mjs.map +0 -1
  458. package/dist/commands/inspect/run-audit.d.mts +0 -1
  459. package/dist/commands/inspect/run-audit.mjs +0 -25
  460. package/dist/commands/inspect/run-audit.mjs.map +0 -1
  461. package/dist/commands/inspect/set-work-status.d.mts +0 -1
  462. package/dist/commands/inspect/set-work-status.mjs +0 -29
  463. package/dist/commands/inspect/set-work-status.mjs.map +0 -1
  464. package/dist/commands/inspect/stage-map.d.mts +0 -1
  465. package/dist/commands/inspect/stage-map.mjs +0 -131
  466. package/dist/commands/inspect/stage-map.mjs.map +0 -1
  467. package/dist/commands/inspect/task-list.d.mts +0 -1
  468. package/dist/commands/inspect/task-list.mjs +0 -149
  469. package/dist/commands/inspect/task-list.mjs.map +0 -1
  470. package/dist/commands/inspect/task-show.d.mts +0 -1
  471. package/dist/commands/inspect/task-show.mjs +0 -108
  472. package/dist/commands/inspect/task-show.mjs.map +0 -1
  473. package/dist/commands/inspect/time-report.d.mts +0 -1
  474. package/dist/commands/inspect/time-report.mjs +0 -24
  475. package/dist/commands/inspect/time-report.mjs.map +0 -1
  476. package/dist/commands/inspect/usage-report.d.mts +0 -1
  477. package/dist/commands/inspect/usage-report.mjs +0 -23
  478. package/dist/commands/inspect/usage-report.mjs.map +0 -1
  479. package/dist/commands/inspect/user-response.d.mts +0 -1
  480. package/dist/commands/inspect/user-response.mjs +0 -35
  481. package/dist/commands/inspect/user-response.mjs.map +0 -1
  482. package/dist/commands/inspect/worker-liveness.d.mts +0 -1
  483. package/dist/commands/inspect/worker-liveness.mjs +0 -45
  484. package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
  485. package/dist/commands/lifecycle/contract-check.d.mts +0 -1
  486. package/dist/commands/lifecycle/contract-check.mjs +0 -18
  487. package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
  488. package/dist/commands/lifecycle/migrate.d.mts +0 -1
  489. package/dist/commands/lifecycle/migrate.mjs +0 -30
  490. package/dist/commands/lifecycle/migrate.mjs.map +0 -1
  491. package/dist/commands/lifecycle/model.d.mts +0 -1
  492. package/dist/commands/lifecycle/model.mjs +0 -22
  493. package/dist/commands/lifecycle/model.mjs.map +0 -1
  494. package/dist/commands/manager.d.mts +0 -3
  495. package/dist/commands/manager.mjs +0 -50
  496. package/dist/commands/manager.mjs.map +0 -1
  497. package/dist/commands/report/agent-activity.d.mts +0 -1
  498. package/dist/commands/report/agent-activity.mjs +0 -20
  499. package/dist/commands/report/agent-activity.mjs.map +0 -1
  500. package/dist/commands/report/approval-decision.d.mts +0 -1
  501. package/dist/commands/report/approval-decision.mjs +0 -21
  502. package/dist/commands/report/approval-decision.mjs.map +0 -1
  503. package/dist/commands/report/design-snapshot.d.mts +0 -1
  504. package/dist/commands/report/design-snapshot.mjs +0 -19
  505. package/dist/commands/report/design-snapshot.mjs.map +0 -1
  506. package/dist/commands/report/finalize.d.mts +0 -4
  507. package/dist/commands/report/finalize.mjs +0 -64
  508. package/dist/commands/report/finalize.mjs.map +0 -1
  509. package/dist/commands/report/inject-report-index.d.mts +0 -1
  510. package/dist/commands/report/inject-report-index.mjs +0 -21
  511. package/dist/commands/report/inject-report-index.mjs.map +0 -1
  512. package/dist/commands/report/render-final-report.d.mts +0 -1
  513. package/dist/commands/report/render-final-report.mjs +0 -23
  514. package/dist/commands/report/render-final-report.mjs.map +0 -1
  515. package/dist/commands/report/render-views.d.mts +0 -1
  516. package/dist/commands/report/render-views.mjs +0 -27
  517. package/dist/commands/report/render-views.mjs.map +0 -1
  518. package/dist/commands/report/translate.d.mts +0 -1
  519. package/dist/commands/report/translate.mjs +0 -33
  520. package/dist/commands/report/translate.mjs.map +0 -1
  521. package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
  522. package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
  523. package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
  524. package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
  525. package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
  526. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
  527. package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
  528. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
  529. package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
  530. package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
  531. package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
  532. package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
  533. package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
  534. package/runtime/bin/okstra-central.sh +0 -152
  535. package/runtime/bin/okstra-incremental-carry.py +0 -10
  536. package/runtime/bin/okstra-incremental-scope.py +0 -10
  537. package/runtime/bin/okstra-team-reconcile.sh +0 -36
  538. package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
  539. package/runtime/python/okstra_ctl/batch.py +0 -60
  540. package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
  541. package/runtime/python/okstra_ctl/container_registry.py +0 -72
  542. package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
  543. package/runtime/python/okstra_ctl/resolver.py +0 -54
  544. package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
  545. package/runtime/python/okstra_ctl/tmux.py +0 -134
  546. package/runtime/python/okstra_ctl/worktree.py +0 -1099
@@ -2,6 +2,7 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import json
5
+ import sys
5
6
  import os
6
7
  from datetime import datetime, timezone
7
8
  from pathlib import Path
@@ -20,7 +21,12 @@ from .codex import (
20
21
  codex_session_total,
21
22
  find_codex_sessions,
22
23
  )
23
- from .antigravity import antigravity_session_total, find_antigravity_sessions
24
+ from .antigravity import (
25
+ antigravity_session_total,
26
+ antigravity_status_total,
27
+ find_antigravity_sessions,
28
+ status_carries_usage,
29
+ )
24
30
  from .grok import (
25
31
  find_grok_sessions,
26
32
  grok_session_is_non_interactive,
@@ -113,22 +119,115 @@ def _aggregate_totals(items: list[dict]) -> dict:
113
119
  def run_artifact_suffix(team_state_path: Path) -> str | None:
114
120
  """``team-state-<task-type>-<seq>.json`` → ``<task-type>-<seq>``.
115
121
 
116
- 이 접미사로 *같은 run* 의 run-manifest / status 를 정확히 짚는다. task 디렉토리
117
- 한 곳에 여러 run(재시도·이전 phase·레거시 타임스탬프)의 산출물이 섞여 있어,
118
- glob 으로 아무거나 집으면 엉뚱한 run 의 시각을 쓰게 된다(관측: 가장 오래된
119
- 레거시 manifest 의 createdAt 을 집어 윈도우가 한 달로 벌어짐)."""
122
+ task 디렉토리 한 곳에 여러 run(재시도·이전 phase·레거시 타임스탬프)의 산출물이
123
+ 섞여 있어, glob 으로 아무거나 집으면 엉뚱한 run 의 시각을 쓰게 된다(관측: 가장
124
+ 오래된 레거시 manifest 의 createdAt 을 집어 윈도우가 한 달로 벌어짐).
125
+
126
+ **접미사가 곧 매니페스트 이름은 아니다.** okstra 는 카테고리별로 seq 를 따로
127
+ 매기므로 한 run 이 `manifests: 025` 와 `state: 023` 을 동시에 가질 수 있다.
128
+ run-manifest 는 `_run_manifest_for_team_state` 가 매니페스트의 `teamStatePath`
129
+ 역참조로 짚는다. status 산출물은 team-state 와 같은 카테고리라 이 접미사로
130
+ 계속 짚는다."""
120
131
  name = team_state_path.name
121
132
  if not (name.startswith("team-state-") and name.endswith(".json")):
122
133
  return None
123
134
  return name[len("team-state-"):-len(".json")]
124
135
 
125
136
 
126
- def _run_manifest_created_at(run_dir: Path, suffix: str) -> str | None:
127
- p = run_dir / "manifests" / f"run-manifest-{suffix}.json"
137
+ def _read_json_or_none(path: Path) -> dict | None:
128
138
  try:
129
- return json.loads(p.read_text()).get("createdAt")
139
+ data = json.loads(path.read_text())
130
140
  except (OSError, json.JSONDecodeError):
131
141
  return None
142
+ return data if isinstance(data, dict) else None
143
+
144
+
145
+ def _run_manifest_for_team_state(
146
+ run_dir: Path, suffix: str, team_state_path: Path
147
+ ) -> dict | None:
148
+ """이 team-state 가 속한 run 의 매니페스트.
149
+
150
+ 접미사가 곧 매니페스트 이름이라는 전제는 카테고리별 seq 가 갈리는 순간
151
+ 깨진다. 실측(2026-08-27, fontsninja-nlpvibe `nestjs-migration-…`): 한 run 의
152
+ `manifests` 가 025, `state`·`prompts`·`worker-results` 가 023, `reports` 가
153
+ 017 이었다. team-state 이름에서 뽑은 `023` 으로 매니페스트를 집으면 **12시간
154
+ 전 run** 의 `createdAt`(04:45:34Z) 이 창의 시작이 되고, 그 사이 같은 세션이
155
+ 돈 이전 run 들이 통째로 창에 들어온다 — `relax_start=False` 가 막으려던 바로
156
+ 그 오탐이다(그 run 에서 로스터에 없는 워커 5명이 이 run 의 위반으로 보고됐다).
157
+
158
+ 매니페스트는 자기 team-state 를 `teamStatePath` 에 적으므로 그것으로 맞춘다.
159
+ 맞는 것이 없으면 접미사 방식으로 돌아간다 — 그 필드가 없던 옛 매니페스트다.
160
+ """
161
+ direct = run_dir / "manifests" / f"run-manifest-{suffix}.json"
162
+ # `Path.glob` 는 디렉터리가 없어도 빈 결과를 낸다 — 감쌀 예외가 없다.
163
+ candidates = [
164
+ direct,
165
+ *sorted((run_dir / "manifests").glob("run-manifest-*.json")),
166
+ ]
167
+ for path in candidates:
168
+ data = _read_json_or_none(path)
169
+ if data is None:
170
+ continue
171
+ recorded = str(data.get("teamStatePath") or "")
172
+ if recorded and Path(recorded).name == team_state_path.name:
173
+ return data
174
+ return _read_json_or_none(direct)
175
+
176
+
177
+ def _created_at_by_suffix(run_dir: Path, suffix: str) -> str | None:
178
+ """접미사가 곧 매니페스트 이름인 조회.
179
+
180
+ **다른** run 을 접미사로 지목할 때만 쓴다(`_previous_run_end`). 이번 run 은
181
+ team-state 로 맞춰야 한다 — 두 접미사가 갈리는 run 이 실재한다.
182
+ """
183
+ data = _read_json_or_none(run_dir / "manifests" / f"run-manifest-{suffix}.json")
184
+ return None if data is None else data.get("createdAt")
185
+
186
+
187
+ def relaxation_floor(run_dir: Path, suffix: str, manifest: dict | None) -> str | None:
188
+ """since 완화가 내려갈 수 있는 하한.
189
+
190
+ in-session 리드(`entryMode: current-session`)의 세션은 run 을 위해 태어난 것이
191
+ 아니다 — 한 세션이 여러 날에 걸쳐 여러 task 의 run 을 돌린다(관측 2026-09-02,
192
+ dev-10626 error-analysis r04: 세션 첫 ts 08-17T08:00Z, run createdAt
193
+ 09-02T20:03Z, 리드 소요 398h / $292 로 보고). 그 세션의 첫 ts 는 이 run 의
194
+ 시작에 대해 아무것도 말하지 않으므로 하한은 매니페스트 createdAt 자체다 —
195
+ 완화가 일어나지 않는다. 프로세스를 새로 띄운 리드는 세션이 prep 직전에
196
+ 태어나므로 종전대로 직전 run 의 종료가 하한이다(`_previous_run_end`).
197
+ """
198
+ if manifest is not None and manifest.get("entryMode") == "current-session":
199
+ created = manifest.get("createdAt")
200
+ if created:
201
+ return str(created)
202
+ return _previous_run_end(run_dir, suffix)
203
+
204
+
205
+ def window_start_is_pinned(since: str | None, floor: str | None) -> bool:
206
+ """하한이 시작점 이상이면 완화할 여지가 없다 — 세션 jsonl 을 훑을 이유도 없다."""
207
+ return bool(since and floor and floor >= since)
208
+
209
+
210
+ def relax_window_start(
211
+ since: str | None, earliest: str | None, floor: str | None
212
+ ) -> str | None:
213
+ """`earliest` 가 `since` 보다 앞서면 하한 안에서 시작점을 앞당긴다."""
214
+ if not earliest:
215
+ return since
216
+ if floor and earliest < floor:
217
+ earliest = floor
218
+ if since is None or earliest < since:
219
+ return earliest
220
+ return since
221
+
222
+
223
+ def run_window_relaxation_floor(team_state_path: Path) -> str | None:
224
+ """collect 의 found-sessions 완화가 쓰는 하한 — `resolve_run_window` 와 같은 규칙."""
225
+ suffix = run_artifact_suffix(team_state_path)
226
+ if not suffix:
227
+ return None
228
+ run_dir = team_state_path.parent.parent
229
+ manifest = _run_manifest_for_team_state(run_dir, suffix, team_state_path)
230
+ return relaxation_floor(run_dir, suffix, manifest)
132
231
 
133
232
 
134
233
  def _run_end_estimate(run_dir: Path, suffix: str) -> str | None:
@@ -233,9 +332,15 @@ def _previous_run_end(run_dir: Path, suffix: str) -> str | None:
233
332
  ended_at = prev_state.get("runEndedAt")
234
333
  if ended_at:
235
334
  return str(ended_at)
236
- except (OSError, json.JSONDecodeError):
335
+ except OSError:
336
+ # expected-miss: 직전 run 이 team-state 를 남기지 않고 끝났으면 파일이
337
+ # 없다. 아래 두 추정 경로가 바로 그 경우를 위한 폴백이다.
237
338
  pass
238
- return _run_end_estimate(run_dir, prev) or _run_manifest_created_at(run_dir, prev)
339
+ except json.JSONDecodeError as exc:
340
+ # 파일은 있는데 깨진 것은 폴백이 상정한 상황이 아니다.
341
+ print(f"token-usage: team-state for the previous run ({prev}) is not "
342
+ f"valid JSON, estimating instead ({exc})", file=sys.stderr)
343
+ return _run_end_estimate(run_dir, prev) or _created_at_by_suffix(run_dir, prev)
239
344
 
240
345
 
241
346
  def resolve_run_window(
@@ -258,7 +363,10 @@ def resolve_run_window(
258
363
  since 를 앞당긴다. `leadSessionIds` 부재(legacy)면 createdAt 그대로 — 기존
259
364
  동작 불변. 완화 하한: 직전 run 의 종료 시각 이전으로는 내려가지 않는다
260
365
  (`_previous_run_end`). 세션 jsonl 은 run 마다 새로 생기지 않으므로 하한이
261
- 없으면 같은 세션의 이전 run 이 이 윈도우에 통째로 들어온다.
366
+ 없으면 같은 세션의 이전 run 이 이 윈도우에 통째로 들어온다. in-session
367
+ 리드(`entryMode: current-session`)는 완화하지 않는다 — 그 세션은 run 보다
368
+ 먼저 태어났고 다른 task 의 run 도 돌렸으므로 첫 ts 는 세션 탄생일이다
369
+ (`relaxation_floor`).
262
370
 
263
371
  `relax_start=False` (세션 스코프 검증기 전용 — session-conformance /
264
372
  forbidden-actions): 완화를 건너뛰고 since 를 createdAt 에 고정한다. createdAt
@@ -271,14 +379,13 @@ def resolve_run_window(
271
379
  if not suffix:
272
380
  return None, None
273
381
  run_dir = team_state_path.parent.parent
274
- since = _run_manifest_created_at(run_dir, suffix)
382
+ manifest = _run_manifest_for_team_state(run_dir, suffix, team_state_path)
383
+ since = None if manifest is None else manifest.get("createdAt")
275
384
  if relax_start:
276
- earliest = _earliest_lead_session_ts(state, team_state_path)
277
- if earliest and (since is None or earliest < since):
278
- since = earliest
279
- floor = _previous_run_end(run_dir, suffix)
280
- if floor and since and since < floor:
281
- since = floor
385
+ floor = relaxation_floor(run_dir, suffix, manifest)
386
+ if not window_start_is_pinned(since, floor):
387
+ earliest = _earliest_lead_session_ts(state, team_state_path)
388
+ since = relax_window_start(since, earliest, floor)
282
389
  until = state.get("runEndedAt") or _run_end_estimate(run_dir, suffix) or utc_now()
283
390
  return since, until
284
391
 
@@ -455,10 +562,18 @@ def _codex_worker_windows(project_root: Path, state: dict) -> dict[str, list[tup
455
562
  return windows
456
563
 
457
564
 
565
+ # Claude 의 트랜스크립트는 세션 jsonl 이고, 그것은 이 모듈의 Claude 경로가
566
+ # 읽는다. 별도의 CLI 트랜스크립트가 없으므로 CLI 경로에 태우면 매번
567
+ # `no transcript was found` 가 붙는다 — 세션을 찾아 토큰을 붙인 행에도.
568
+ _SESSION_JSONL_PROVIDERS = frozenset({"claude"})
569
+
570
+
458
571
  def _cli_assignment_provider(worker: dict) -> str:
459
572
  worker_id = str(worker.get("workerId") or "").strip()
460
573
  explicit_provider = str(worker.get("provider") or "").strip()
461
574
  provider = explicit_provider or str(worker.get("agent") or "").strip()
575
+ if provider in _SESSION_JSONL_PROVIDERS:
576
+ return ""
462
577
  runner = str(worker.get("runner") or "").strip()
463
578
  if runner and runner != "cli-wrapper":
464
579
  return ""
@@ -496,7 +611,11 @@ def _cli_session_totals(provider: str, session_paths: list[Path]) -> list[dict]:
496
611
  if provider == "codex":
497
612
  total = codex_session_total(session_path)
498
613
  elif provider == "antigravity":
499
- total = antigravity_session_total(session_path)
614
+ total = (
615
+ antigravity_status_total(session_path)
616
+ if status_carries_usage(session_path)
617
+ else antigravity_session_total(session_path)
618
+ )
500
619
  elif provider == "grok":
501
620
  total = grok_session_total(session_path)
502
621
  else:
@@ -642,12 +761,23 @@ def _worker_cli_windows(
642
761
  return fallback_windows, status_path, True
643
762
 
644
763
 
645
- def _antigravity_log_paths(
764
+ def _antigravity_usage_sources(
646
765
  project_root: Path,
647
766
  worker: dict,
648
767
  status_path: Path | None,
649
768
  ) -> list[Path]:
650
- """agy stream-json 은 워커 로그에 남는다. 홈 세션 디렉터리가 아니다."""
769
+ """agy 의 토큰 스냅샷이 있는 곳. 홈 세션 디렉터리가 아니다.
770
+
771
+ 정본은 래퍼 status 사이드카의 `usage` 다 — 러너가 스트림의 마지막
772
+ `result.usage` 를 종료 시점에 적는다(`worker_runner`). 워커 로그는 러너가
773
+ 사람이 읽는 줄로 옮겨 적은 것이라 stream-json 이 아니고, 그 로그에서
774
+ usage 를 찾던 동안 antigravity 워커 전부가 `transcript found but no final
775
+ token snapshot was recorded` 였다(관측 2026-09-02, dev-10626 r01·r02·r04).
776
+ 로그를 읽는 경로는 러너 이전의 raw stream-json 로그를 위해 남긴다 — 스냅샷이
777
+ 있는 run 의 로그는 옮겨 적은 텍스트라 트랜스크립트로 나열하지 않는다.
778
+ """
779
+ if status_path is not None and status_carries_usage(status_path):
780
+ return [status_path]
651
781
  paths: list[Path] = []
652
782
  prompt_path = _resolve_project_path(project_root, str(worker.get("promptPath") or ""))
653
783
  if prompt_path is not None:
@@ -676,9 +806,9 @@ def _worker_cli_usage_block(
676
806
  )
677
807
  session_paths = _cli_sessions_for_windows(provider, project_root, windows)
678
808
  if provider == "antigravity":
679
- for log_path in _antigravity_log_paths(project_root, worker, status_path):
680
- if log_path not in session_paths:
681
- session_paths.append(log_path)
809
+ for source in _antigravity_usage_sources(project_root, worker, status_path):
810
+ if source not in session_paths:
811
+ session_paths.append(source)
682
812
  return collect_cli_usage(
683
813
  provider=provider,
684
814
  status_path=status_path,
@@ -701,8 +831,8 @@ def _attach_cli_usage(
701
831
  window produces several rollout jsonls; pricing only the latest (the old
702
832
  ``find_codex_session`` -> ``sessions[-1]`` behavior) dropped the earlier
703
833
  attempts' tokens. We sum every in-window session here so the redispatched
704
- CLI spend is fully reported. antigravity 는 워커 로그의 stream-json
705
- ``result.usage`` 를 같은 합산 경로에 붙인다.
834
+ CLI spend is fully reported. antigravity 는 status 사이드카의 ``usage``
835
+ 스냅샷을 같은 합산 경로에 붙인다.
706
836
  """
707
837
  session_paths = _cli_sessions_for_windows(provider, project_root, windows)
708
838
  cli = collect_cli_usage(
@@ -733,7 +863,8 @@ def _collect_cli_runtime_usage(
733
863
  provider = _cli_assignment_provider(worker)
734
864
  if not provider:
735
865
  worker["usage"] = na_block(
736
- "worker is host-native or has no registered CLI provider: "
866
+ "worker usage is not read from a provider CLI transcript "
867
+ "(host-native, session-jsonl provider, or no registered CLI provider): "
737
868
  f"{worker.get('provider') or worker.get('agent') or worker.get('workerId')}"
738
869
  )
739
870
  continue
@@ -963,16 +1094,20 @@ def collect_claude_runtime_usage(
963
1094
  claude_sessions = find_claude_team_sessions(cwd, team_needles, lead_sid,
964
1095
  incremental=incremental)
965
1096
  # Task 9: 축1 기록(observed-team-names)으로 발견한 세션들의 min first-ts 로
966
- # run_since 를 완화한다. session-* needle 은 세션 고유라 발견 세션은 이 run
967
- # 것이므로 안전. 불확실한 reconstruct/none source 는 완화하지 않는다(오귀속 방지).
1097
+ # run_since 를 완화한다. 불확실한 reconstruct/none source 는 완화하지
1098
+ # 않는다(오귀속 방지). 하한은 `resolve_run_window` 와 같다 — 발견 세션에는
1099
+ # 리드 세션 자신이 들어 있고, in-session 리드의 세션 첫 ts 는 run 이 아니라
1100
+ # 세션의 탄생일이다(관측: 16일 전). 하한 없이 두던 동안 리드 소요 시간과
1101
+ # 비용이 세션 수명 전체로 보고됐다.
968
1102
  if needle_source == "observed-team-names":
969
- found_earliest = None
970
- for path in claude_sessions.values():
971
- ts = _session_first_ts(path)
972
- if ts and (found_earliest is None or ts < found_earliest):
973
- found_earliest = ts
974
- if found_earliest and (run_since is None or found_earliest < run_since):
975
- run_since = found_earliest
1103
+ floor = run_window_relaxation_floor(team_state_path)
1104
+ if not window_start_is_pinned(run_since, floor):
1105
+ found_earliest = None
1106
+ for path in claude_sessions.values():
1107
+ ts = _session_first_ts(path)
1108
+ if ts and (found_earliest is None or ts < found_earliest):
1109
+ found_earliest = ts
1110
+ run_since = relax_window_start(run_since, found_earliest, floor)
976
1111
  by_agent: dict[str, list[tuple[str, Path, dict]]] = {}
977
1112
  lead_path: Path | None = None
978
1113
  # Team-tagged non-lead sessions that carry no agentName. These are almost
@@ -92,4 +92,6 @@ def save_cache(jsonl_path: Path, cache: dict) -> None:
92
92
  tmp.write_text(json.dumps(cache, ensure_ascii=False, separators=(",", ":")))
93
93
  os.replace(tmp, p)
94
94
  except OSError:
95
+ # expected-miss: 이 캐시는 jsonl 에서 언제든 다시 만들 수 있는 파생물이다.
96
+ # 저장에 실패해도 집계 결과가 달라지지 않으므로 없는 것이 정상 분기다.
95
97
  pass
@@ -11,7 +11,7 @@ import os
11
11
  from datetime import datetime, timezone
12
12
  from pathlib import Path
13
13
  from typing import Mapping
14
- from urllib.parse import quote, unquote
14
+ from urllib.parse import unquote
15
15
 
16
16
  from .jsonl_io import iter_jsonl
17
17
  from .paths import ts_in_window
@@ -132,10 +132,6 @@ def find_grok_sessions(
132
132
  return sorted(matches)
133
133
 
134
134
 
135
- def grok_session_dir_name(cwd: Path) -> str:
136
- return quote(str(cwd), safe="")
137
-
138
-
139
135
  def grok_session_is_non_interactive(updates_path: Path) -> bool:
140
136
  """워커 래퍼 세션은 prompt_context.is_non_interactive 가 true 이다."""
141
137
  context_path = updates_path.parent / "prompt_context.json"
@@ -72,3 +72,29 @@ def claude_project_dir(cwd: Path, projects_root: Path | None = None) -> Path:
72
72
  # `projects_root` 는 테스트/진단용 주입 시드 — 기본은 실제 ~/.claude/projects.
73
73
  encoded = "-" + str(cwd).strip("/").replace("/", "-")
74
74
  return (projects_root or CLAUDE_PROJECTS) / encoded
75
+
76
+
77
+ def find_session_jsonl(
78
+ session_id: str, cwd: Path, projects_root: Path | None = None
79
+ ) -> Path | None:
80
+ """세션 id 하나의 transcript 경로. 못 찾으면 None.
81
+
82
+ 프로젝트로 인코딩된 디렉터리를 먼저 보고, 없으면 projects_root 아래의
83
+ 모든 인코딩 디렉터리를 훑는다. 리드 세션의 cwd 가 대상 프로젝트 루트와
84
+ 다를 수 있기 때문이다 — `okstra preflight --cwd <다른 프로젝트>` 는
85
+ 지원되는 실행 방식이고, 그때 transcript 는 **리드 세션의 cwd** 로 인코딩된
86
+ 디렉터리에 놓인다. 프로젝트 디렉터리만 보면 실재하는 세션을 없다고 답한다.
87
+
88
+ 세션 id 는 전역 유일하므로 디렉터리를 넓혀도 다른 세션을 잘못 집지 않는다.
89
+ 파일 내용을 읽지 않는 경로 조회다.
90
+ """
91
+ if not session_id:
92
+ return None
93
+ direct = claude_project_dir(cwd, projects_root) / f"{session_id}.jsonl"
94
+ if direct.is_file():
95
+ return direct
96
+ root = projects_root or CLAUDE_PROJECTS
97
+ try:
98
+ return next(iter(sorted(root.glob(f"*/{session_id}.jsonl"))), None)
99
+ except OSError:
100
+ return None
@@ -4,18 +4,19 @@ Pricing is matched by substring against the model id recorded in the session
4
4
  transcript, so keys must reflect the *actual* model id form emitted by each
5
5
  provider:
6
6
 
7
- * Anthropic — `claude-fable-5*`, `claude-opus-5*`, `claude-opus-4-*`, `claude-sonnet-5*`, `claude-sonnet-4-*`,
7
+ * Anthropic — `claude-fable-5-1*`, `claude-fable-5*`, `claude-opus-5*`, `claude-opus-4-*`, `claude-sonnet-5*`, `claude-sonnet-4-*`,
8
8
  `claude-haiku-4-5-*`, `claude-3-5-sonnet-*`, `claude-3-5-haiku-*`,
9
9
  `claude-3-opus-*`, `claude-3-haiku-*`.
10
10
  * OpenAI / Codex — `gpt-5*`, `gpt-4o*`, `gpt-4*`.
11
- * Google / Gemini — `gemini-3.6-flash*`, `gemini-3.5-flash*`, `gemini-3.1-pro*`, `gemini-3-flash*`,
11
+ * Google / Gemini — `gemini-3.7-flash*`, `gemini-3.6-flash*`, `gemini-3.5-flash*`, `gemini-3.1-pro*`, `gemini-3-flash*`,
12
12
  `gemini-2.5-pro*`, `gemini-2.5-flash*`, `gemini-2.0-flash*`.
13
13
 
14
14
  Matching prefers the longest key that is a substring of the model id (most
15
15
  specific wins), so table ordering does not affect the result. Update when
16
16
  providers change list pricing.
17
17
 
18
- Sources (last verified 2026-08-03, public list prices, USD per 1M tokens):
18
+ Sources (last verified 2026-08-03 — Anthropic Fable 5.1 re-verified 2026-09-02,
19
+ Google table re-verified 2026-08-26, public list prices, USD per 1M tokens):
19
20
  * Anthropic: https://www.anthropic.com/pricing
20
21
  * OpenAI: https://openai.com/api/pricing
21
22
  * Google: https://ai.google.dev/gemini-api/docs/pricing
@@ -49,6 +50,9 @@ CLAUDE_PRICING = {
49
50
  "3-sonnet": (3.0, 3.75, 0.30, 15.0), # legacy 3 Sonnet
50
51
  "3-haiku": (0.25, 0.30, 0.03, 1.25), # Haiku 3
51
52
 
53
+ # Claude Fable 5.1 — input/output match Fable 5; cache_read is 75% lower
54
+ # ($0.25, not the 0.1x ratio). Longest-key match so this beats "fable-5".
55
+ "fable-5-1": (10.0, 12.5, 0.25, 50.0), # Fable 5.1 (Anthropic 2026-09-01)
52
56
  # Claude Fable 5 (tier above Opus).
53
57
  "fable-5": (10.0, 12.5, 1.0, 50.0), # Fable 5 (cache prices derived from ratios)
54
58
 
@@ -74,9 +78,10 @@ CLAUDE_PRICING = {
74
78
  # Anthropic 1h ephemeral cache_creation multiplier on the base input rate.
75
79
  CLAUDE_CACHE_CREATE_1H_MULT = 2.0
76
80
 
77
- # Legacy / non-selectable codex prices. Selectable-model prices (the aliases the
78
- # picker offers) live in the model catalog and are merged in below via
79
- # `catalog_pricing("codex")`. Substring match order is now longest-key-first
81
+ # Codex prices for slugs the provider catalog no longer offers. Prices for
82
+ # registered models -- selectable or hidden -- live in the model catalog and are
83
+ # merged in below via `catalog_pricing("codex")`. Substring match order is
84
+ # longest-key-first
80
85
  # (see `_match_pricing`), so this dict's ordering no longer affects matching.
81
86
  # For models with no published cached-input rate (o1-pro, o3-pro), cached is set
82
87
  # equal to input as a conservative no-discount default.
@@ -84,6 +89,10 @@ _LEGACY_CODEX_PRICING = {
84
89
  # model substring -> (input USD/1M, cached_input USD/1M, output USD/1M).
85
90
 
86
91
  # GPT-5 series.
92
+ # gpt-5.6 was dropped from the provider catalog; its rate stays so past runs
93
+ # keep pricing. Substring matching means sol / terra / luna resolve here too
94
+ # -- the 5.6 family rate, not a per-variant price we have a source for.
95
+ "gpt-5.6": (5.00, 0.50, 30.0),
87
96
  "gpt-5.2-pro": (21.0, 2.10, 168.0),
88
97
  "gpt-5.1": (1.25, 0.125, 10.0),
89
98
  "gpt-5-mini": (0.25, 0.025, 2.00),
@@ -126,8 +135,14 @@ GEMINI_PRICING = {
126
135
  # forms appear in the wild, so include both for the new 3.x families.
127
136
 
128
137
  # Gemini 3 series.
129
- "3.6-flash": (1.50, 7.50),
130
- "3-6-flash": (1.50, 7.50),
138
+ # 3.7 Flash 와 3.6 Flash 는 2026-12-31 까지 도입 요율 $0.75/$3.75 이고
139
+ # 2027-01-01 에 표준 요율 $1.50/$7.50 으로 오른다(2026-08-26 확인). 이 표에는
140
+ # 시점 개념이 없으므로 지금 청구되는 값을 담는다 — 그날 이후 두 계열을
141
+ # (1.50, 7.50) 으로 갱신해야 한다.
142
+ "3.7-flash": (0.75, 3.75),
143
+ "3-7-flash": (0.75, 3.75),
144
+ "3.6-flash": (0.75, 3.75),
145
+ "3-6-flash": (0.75, 3.75),
131
146
  "3.5-flash": (1.50, 9.00),
132
147
  "3-5-flash": (1.50, 9.00),
133
148
  "3.1-pro": (2.00, 12.0),
@@ -31,6 +31,11 @@
31
31
  "items": { "type": "string", "pattern": "\\S" }
32
32
  },
33
33
  "originEvidence": { "type": "string", "pattern": "\\S" },
34
+ "duplicateOf": {
35
+ "type": "string",
36
+ "pattern": "\\S",
37
+ "description": "The existing finding id (`F-NNN`) this candidate overlaps. A declared duplicate is recorded and skips gap verification entirely; restating the finding instead costs one verification round to reject it. The shape stays free-form because `findingId` is free-form in convergence-groups-v2.0; `okstra convergence apply-critic-gaps` owns the check that the id names a finding the state actually carries."
38
+ },
34
39
  "severity": { "enum": ["critical", "major", "minor"] },
35
40
  "evidenceArtifacts": {
36
41
  "type": "array",
@@ -236,10 +236,7 @@
236
236
  "dutyId",
237
237
  "dispatchKind",
238
238
  "round",
239
- "inputDigest",
240
- "writePolicy",
241
- "writePolicyDigest",
242
- "writeEnforcement"
239
+ "inputDigest"
243
240
  ],
244
241
  "properties": {
245
242
  "invocationRef": {"type": "string", "pattern": "\\S"},
@@ -250,10 +247,7 @@
250
247
  "dutyId": {"type": "string", "pattern": "\\S"},
251
248
  "dispatchKind": {"type": "string", "pattern": "\\S"},
252
249
  "round": {"type": "integer", "minimum": 1},
253
- "inputDigest": {"$ref": "#/$defs/Digest"},
254
- "writePolicy": {"$ref": "#/$defs/WritePolicy"},
255
- "writePolicyDigest": {"$ref": "#/$defs/Digest"},
256
- "writeEnforcement": {"$ref": "#/$defs/WriteEnforcement"}
250
+ "inputDigest": {"$ref": "#/$defs/Digest"}
257
251
  },
258
252
  "additionalProperties": false
259
253
  },
@@ -268,7 +262,10 @@
268
262
  "resultPath",
269
263
  "errorPath",
270
264
  "changeSummary",
271
- "evidenceSealRef"
265
+ "evidenceSealRef",
266
+ "writePolicy",
267
+ "writePolicyDigest",
268
+ "writeEnforcement"
272
269
  ],
273
270
  "properties": {
274
271
  "invocationRef": {"type": "string", "pattern": "\\S"},
@@ -279,7 +276,10 @@
279
276
  "resultPath": {"type": ["string", "null"]},
280
277
  "errorPath": {"type": ["string", "null"]},
281
278
  "changeSummary": {"type": "object"},
282
- "evidenceSealRef": {"type": ["string", "null"]}
279
+ "evidenceSealRef": {"type": ["string", "null"]},
280
+ "writePolicy": {"$ref": "#/$defs/WritePolicy"},
281
+ "writePolicyDigest": {"$ref": "#/$defs/Digest"},
282
+ "writeEnforcement": {"$ref": "#/$defs/WriteEnforcement"}
283
283
  },
284
284
  "additionalProperties": false
285
285
  },
@@ -251,7 +251,7 @@
251
251
  },
252
252
  "verdictCard": {
253
253
  "type": "object",
254
- "description": "Top-of-report at-a-glance card. It carries no verdict token — the token lives once, in finalVerdict. `direction` and `nextStep` MUST byte-match finalVerdict and recommendedNextSteps[0].",
254
+ "description": "Top-of-report at-a-glance card. It carries no verdict token — the token lives once, in finalVerdict, and additionalProperties:false keeps it from being copied here. Authoring guideline: write `direction` to agree with finalVerdict.direction and `nextStep` to agree with recommendedNextSteps[0]. Nothing compares the two copies afterwards — assembly rewrites `nextStep` on both cards together when the plan-body gate passes, and leaves `direction` as the writer wrote it — so a disagreement between them is published as written.",
255
255
  "required": [
256
256
  "finalConclusion",
257
257
  "direction",
@@ -312,9 +312,8 @@
312
312
  },
313
313
  "summary": {
314
314
  "type": "array",
315
- "description": "## Summary of the Problem or Verification Target. 3-5 rows.",
315
+ "description": "## Summary of the Problem or Verification Target. Usually 3-5 rows; no upper limit.",
316
316
  "minItems": 1,
317
- "maxItems": 8,
318
317
  "items": {
319
318
  "$ref": "#/$defs/SummaryRow"
320
319
  }
@@ -3897,8 +3896,8 @@
3897
3896
  },
3898
3897
  "summary": {
3899
3898
  "type": "string",
3900
- "minLength": 1,
3901
- "maxLength": 120
3899
+ "minLength": 1
3900
+
3902
3901
  },
3903
3902
  "details": {
3904
3903
  "type": "string",
@@ -7527,8 +7526,8 @@
7527
7526
  },
7528
7527
  "summary": {
7529
7528
  "type": "string",
7530
- "minLength": 1,
7531
- "maxLength": 120
7529
+ "minLength": 1
7530
+
7532
7531
  },
7533
7532
  "details": {
7534
7533
  "type": "string",
@@ -9030,24 +9029,6 @@
9030
9029
  "type": "string",
9031
9030
  "minLength": 1
9032
9031
  }
9033
- },
9034
- "rewrittenPaths": {
9035
- "type": "array",
9036
- "description": "data.json paths this round actually rewrote for this cause group. A self-fix round is contractually a targeted correction, not a full draft regeneration; recording the reach is what makes that claim checkable later.",
9037
- "uniqueItems": true,
9038
- "items": {
9039
- "type": "string",
9040
- "minLength": 1
9041
- }
9042
- },
9043
- "outsideScopePaths": {
9044
- "type": "array",
9045
- "description": "The subset of rewrittenPaths that lies outside the sections this group's itemIds point at. Carrying a correction to its contradictions legitimately reaches further than the flagged item, so this is a measurement, not a violation.",
9046
- "uniqueItems": true,
9047
- "items": {
9048
- "type": "string",
9049
- "minLength": 1
9050
- }
9051
9032
  }
9052
9033
  }
9053
9034
  },
@@ -9569,10 +9550,12 @@
9569
9550
  "minLength": 1
9570
9551
  },
9571
9552
  "verdict": {
9553
+ "description": "`not-run` is not a rejection: the verifier produced no verdict at all (timeout, error, usage limit). `FAIL` rejects the diff and opens the stage fix cycle, so spending it on a verifier that never ran manufactures a defect nobody found \u2014 see `_implementation-verifier.md` \"All-verifier-failure policy\".",
9572
9554
  "enum": [
9573
9555
  "PASS",
9574
9556
  "CONCERNS",
9575
- "FAIL"
9557
+ "FAIL",
9558
+ "not-run"
9576
9559
  ]
9577
9560
  },
9578
9561
  "readOnlyCommandLog": {