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
@@ -33,30 +33,28 @@ ANTIGRAVITY = {
33
33
  "gemini-3.1-pro", "Gemini 3.1 Pro", "gemini-3.1-pro",
34
34
  aliases=("gemini 3.1 pro",),
35
35
  ),
36
- "gemini-3.6-flash": ModelSpec(
37
- "gemini-3.6-flash", "Gemini 3.6 Flash", "gemini-3.6-flash",
38
- aliases=("gemini 3.6 flash",),
39
- ),
40
- "gemini-3.5-flash": ModelSpec(
41
- "gemini-3.5-flash", "Gemini 3.5 Flash", "gemini-3.5-flash",
42
- aliases=("gemini 3.5 flash",),
36
+ "gemini-3.7-flash": ModelSpec(
37
+ "gemini-3.7-flash", "Gemini 3.7 Flash", "gemini-3.7-flash",
38
+ aliases=("gemini 3.7 flash",),
43
39
  ),
44
40
  }
45
41
 
46
42
 
47
43
  def _catalog_model_id(observed: str) -> str:
48
- """Undo the dispatch-time tier suffix so the id is catalog-comparable.
44
+ """Undo the tier suffix so the id is catalog-comparable.
49
45
 
50
- Dispatch sends `gemini-3.1-pro-low`; agy serves that identity back
46
+ Dispatch sends `gemini-3.1-pro-high`; agy serves that identity back
51
47
  verbatim. The catalog holds the bare `gemini-3.1-pro`, so reading the
52
48
  observation as-is made the served-model gate reject a model okstra itself
53
- had asked for. Only suffixes dispatch can append are stripped, and only
54
- when what remains is a catalog entry — anything else passes through for
55
- the gate to judge.
49
+ had asked for. Only agy's tier suffixes are stripped, and only when what
50
+ remains is a catalog entry — anything else passes through for the gate to
51
+ judge. The stripped set is agy's tier vocabulary, not the tier this run
52
+ asked for: a task provisioned before the tier policy changed still reports
53
+ the tier it was dispatched with, and its gate must keep passing.
56
54
  """
57
55
  if observed in ANTIGRAVITY:
58
56
  return observed # identity already carries its tier (agy's Claude models)
59
- for suffix in model_discovery.dispatch_tier_suffixes():
57
+ for suffix in model_discovery.agy_tier_suffixes():
60
58
  bare = observed.removesuffix(f"-{suffix}")
61
59
  if bare != observed and bare in ANTIGRAVITY:
62
60
  return bare
@@ -91,6 +89,18 @@ def observe_served_model(event: Mapping[str, Any]) -> str | None:
91
89
  # kill a healthy worker mid-run. Idle is the runner's watchdog to enforce.
92
90
  _PRINT_TIMEOUT = "7200s"
93
91
 
92
+ # 본문 대신 결과 요약을 돌려주는 것으로 실측된 도구. agy 는 `view_file` 을
93
+ # `6 lines, 46 bytes` 로 닫는다 — 파일 내용은 스트림 어디에도 오지 않는다.
94
+ # 실측 2026-08-27, agy 1.1.21 프로브: 이벤트 10건 전량을 훑어 본문 0건이고,
95
+ # `--log-file` 은 language server 로그라 도구 출력을 담지 않는다. 같은 날
96
+ # fontsninja-v3-site 의 antigravity 로그 4개에서 `view_file` 47건 중 47건.
97
+ #
98
+ # 값을 보고 판정하지 않는다 — 요약처럼 생긴 파일 내용은 얼마든지 있다.
99
+ # 판정 근거는 도구 이름이고, 그 이름은 agy 자신이 보낸다. 같은 프로브에서
100
+ # `list_dir` / `grep_search` / `find_by_name` 은 실제 출력을 실어 보냈으므로
101
+ # 이 집합에 넣지 않는다.
102
+ _DESCRIBES_RESULT_TOOLS = frozenset({"view_file"})
103
+
94
104
  _STEP_UPDATE = "step_update"
95
105
  _RESULT = "result"
96
106
  _CALL_STARTED = "ACTIVE"
@@ -113,13 +123,6 @@ class AntigravityExecutionNormalizer:
113
123
  )
114
124
 
115
125
 
116
- def normalize_execution(execution: str, role: str) -> str:
117
- """Compatibility entry point for direct adapter callers."""
118
- return model_discovery.normalize_execution_for_dispatch(
119
- provider="antigravity", execution=execution, role=role
120
- )
121
-
122
-
123
126
  def step_update_events(event: Mapping[str, Any]) -> tuple[StreamEvent, ...]:
124
127
  """Normalise this CLI's stream-json, which shares no key with the other four.
125
128
 
@@ -159,11 +162,26 @@ def _tool_events(step: Mapping[str, Any]) -> tuple[StreamEvent, ...]:
159
162
  if step.get("state") != _CALL_FINISHED:
160
163
  return ()
161
164
  output = str(info.get("output", ""))
165
+ # `output` 키가 아예 없는 것과 빈 문자열은 다른 사건이다. 실측(2026-08-27,
166
+ # agy 1.1.21): `write_to_file` 과 `replace_file_content` 는 키를 보내지
167
+ # 않는다 — 아무것도 보고하지 않은 것이다. 그것을 빈 본문으로 접으면
168
+ # `← done (0 bytes)` 가 되어, 출력이 없던 명령과 보고 자체가 없던 쓰기가
169
+ # 같은 줄이 된다.
170
+ reported_nothing = "output" not in info
162
171
  # No outcome field: a `run_command` whose command exited non-zero was
163
172
  # measured closing as `DONE` with the failure text in `output` and nothing
164
173
  # else to distinguish it, so the outcome is left unreported rather than
165
- # rendered as success.
166
- return (ToolResult(body=output, size_bytes=len(output.encode("utf-8"))),)
174
+ # rendered as success. Re-measured 2026-08-27 on agy 1.1.21: still no such
175
+ # field — a failing `cat` closes `DONE` with the error text in `output`.
176
+ return (
177
+ ToolResult(
178
+ body=output,
179
+ size_bytes=len(output.encode("utf-8")),
180
+ body_describes_result=(
181
+ reported_nothing or name in _DESCRIBES_RESULT_TOOLS
182
+ ),
183
+ ),
184
+ )
167
185
 
168
186
 
169
187
  def _argument(parameters: Any) -> str:
@@ -20,50 +20,43 @@ CLAUDE = {
20
20
  "fable": ModelSpec(
21
21
  "fable", "fable", "fable", version_kind="channel", channel_family="fable"
22
22
  ),
23
+ # picker 에서는 감춘다: fable 채널이 서빙하는 실체와 같은 모델이라
24
+ # 목록에 두 줄로 뜬다. 엔트리는 남긴다 — served-model 게이트가 관측값
25
+ # claude-fable-5 를 카탈로그에서 못 찾으면 dispatch 가 실패한다.
23
26
  "fable-5": ModelSpec(
24
27
  "fable-5", "fable-5", "claude-fable-5", aliases=("claude-fable-5",),
25
- channel_family="fable",
28
+ channel_family="fable", selectable=False,
26
29
  ),
27
30
  "opus": ModelSpec(
28
31
  "opus", "opus", "opus", version_kind="channel", channel_family="opus"
29
32
  ),
33
+ # picker 에서는 감춘다(사유는 fable-5 와 동일).
30
34
  "opus-5": ModelSpec(
31
35
  "opus-5", "opus-5", "claude-opus-5", aliases=("claude-opus-5",),
32
- channel_family="opus",
33
- ),
34
- "opus-4-8": ModelSpec(
35
- "opus-4-8", "opus-4-8", "claude-opus-4-8", aliases=("claude-opus-4-8",),
36
- channel_family="opus",
37
- ),
38
- "opus-4-7": ModelSpec(
39
- "opus-4-7", "opus-4-7", "claude-opus-4-7", aliases=("claude-opus-4-7",),
40
- channel_family="opus",
41
- ),
42
- "opus-4-6": ModelSpec(
43
- "opus-4-6", "opus-4-6", "claude-opus-4-6", aliases=("claude-opus-4-6",),
44
- channel_family="opus",
36
+ channel_family="opus", selectable=False,
45
37
  ),
46
38
  "sonnet": ModelSpec(
47
39
  "sonnet", "sonnet", "sonnet", version_kind="channel", channel_family="sonnet"
48
40
  ),
41
+ # picker 에서는 감춘다(사유는 fable-5 와 동일).
49
42
  "sonnet-5": ModelSpec(
50
43
  "sonnet-5", "sonnet-5", "claude-sonnet-5", aliases=("claude-sonnet-5",),
51
- channel_family="sonnet",
52
- ),
53
- "sonnet-4-6": ModelSpec(
54
- "sonnet-4-6", "sonnet-4-6", "claude-sonnet-4-6", aliases=("claude-sonnet-4-6",),
55
- channel_family="sonnet",
44
+ channel_family="sonnet", selectable=False,
56
45
  ),
57
46
  "haiku": ModelSpec(
58
47
  "haiku", "haiku", "haiku", version_kind="channel", channel_family="haiku"
59
48
  ),
49
+ # picker 에서는 감춘다(사유는 fable-5 와 동일).
60
50
  "haiku-4-5": ModelSpec(
61
51
  "haiku-4-5", "haiku-4-5", "claude-haiku-4-5", aliases=("claude-haiku-4-5",),
62
- channel_family="haiku",
52
+ channel_family="haiku", selectable=False,
63
53
  ),
54
+ # 구버전이 아니라 haiku 채널이 실제로 서빙하는 날짜-고정 id 다. 이 엔트리가
55
+ # 없으면 served-model 게이트가 관측값을 카탈로그에서 못 찾아 dispatch 가
56
+ # "served model is not in the catalog" 로 실패한다(dispatch_core.py).
64
57
  "haiku-4-5-20251001": ModelSpec(
65
58
  "haiku-4-5-20251001", "haiku-4-5", "claude-haiku-4-5-20251001",
66
- channel_family="haiku",
59
+ channel_family="haiku", selectable=False,
67
60
  ),
68
61
  }
69
62
 
@@ -16,14 +16,19 @@ from okstra_ctl.domain.worker_presentation import SplitText
16
16
  import okstra_ctl.model_discovery as model_discovery
17
17
 
18
18
 
19
+ # Slugs and order follow the provider catalog the codex CLI caches in
20
+ # `~/.codex/models_cache.json` (read 2026-08-26, client 0.150.0): sol / terra /
21
+ # luna are the three visible 5.6 entries, in the priority the provider assigns.
22
+ # `gpt-5.6` is not a slug that catalog offers at all, so it is gone from here;
23
+ # its rate moved to `_LEGACY_CODEX_PRICING` so past runs still price.
19
24
  CODEX = {
20
- # ChatGPT-account hosts serve this model without per-token billing.
25
+ # ChatGPT-account hosts serve these models without per-token billing.
21
26
  "gpt-5.6-sol": ModelSpec("gpt-5.6-sol", "gpt-5.6-sol", "gpt-5.6-sol"),
22
- "gpt-5.6": ModelSpec("gpt-5.6", "gpt-5.6", "gpt-5.6", pricing=(5.00, 0.50, 30.0)),
23
- "gpt-5.5": ModelSpec("gpt-5.5", "gpt-5.5", "gpt-5.5", pricing=(5.00, 0.50, 30.0)),
24
- "gpt-5.4": ModelSpec("gpt-5.4", "gpt-5.4", "gpt-5.4", aliases=("gpt-5.3-codex",), pricing=(2.50, 0.25, 15.00)),
25
- "gpt-5.4-mini": ModelSpec("gpt-5.4-mini", "gpt-5.4-mini", "gpt-5.4-mini", pricing=(0.75, 0.075, 4.50)),
26
- "gpt-5.2": ModelSpec("gpt-5.2", "gpt-5.2", "gpt-5.2", pricing=(1.75, 0.175, 14.0)),
27
+ "gpt-5.6-terra": ModelSpec("gpt-5.6-terra", "gpt-5.6-terra", "gpt-5.6-terra"),
28
+ "gpt-5.6-luna": ModelSpec("gpt-5.6-luna", "gpt-5.6-luna", "gpt-5.6-luna"),
29
+ # picker 에서는 감춘다. 엔트리는 남긴다 — 과거 run 의 토큰 사용량을
30
+ # 정산할 때 pricing 을 이 표에서 찾는다.
31
+ "gpt-5.4-mini": ModelSpec("gpt-5.4-mini", "gpt-5.4-mini", "gpt-5.4-mini", pricing=(0.75, 0.075, 4.50), selectable=False),
27
32
  "codex-auto-review": ModelSpec("codex-auto-review", "codex-auto-review", "codex-auto-review", selectable=False),
28
33
  }
29
34
 
@@ -52,21 +57,29 @@ class CodexExecutionNormalizer:
52
57
  )
53
58
 
54
59
 
55
- def normalize_execution(execution: str, role: str) -> str:
56
- """Compatibility entry point for direct adapter callers."""
57
- return model_discovery.normalize_execution_for_dispatch(
58
- provider="codex", execution=execution, role=role
59
- )
60
-
61
-
62
60
  class CodexExecution:
63
61
  """codex CLI invocation.
64
62
 
65
- The sandbox stays `workspace-write` even when the approval gate is opened:
66
- removing the gate is not the same as removing the boundary. A non-interactive
67
- codex run under the default `on-request` policy blocks on the first approval
68
- it wants, and with no TTY to answer it ends the turn with exit 0 and no
69
- output — a success that produced nothing.
63
+ No sandbox bounds this worker, which is what every other provider CLI
64
+ already does: grok and kimi expose no sandbox flag at all, antigravity's
65
+ `--sandbox` was measured not to block writes, and the claude CLI applies
66
+ none. codex was the only worker running behind an enforced boundary, and a
67
+ boundary carried by one provider out of five decided whether the same task
68
+ passed by which CLI drew it, not by the code under test.
69
+
70
+ The approval gate still has to be open. A non-interactive codex run under
71
+ the default `on-request` policy blocks on the first approval it wants, and
72
+ with no TTY to answer it ends the turn with exit 0 and no output — a success
73
+ that produced nothing.
74
+
75
+ `workspace-write` also closed every socket. Measured 2026-08-26 (macOS):
76
+ under it `socket.bind(("127.0.0.1", 0))`, an outbound `connect`, and an
77
+ `AF_UNIX` connect all raise `PermissionError [Errno 1]`; outside it all
78
+ three succeed. A verifier that cannot open a port cannot run the suite, the
79
+ HTTP contract test, or the DB test its own profile requires, so it returned
80
+ FAIL for a reason that was never about the code. Dropping the sandbox
81
+ removes that failure mode at its source, so no per-role network grant is
82
+ needed any more.
70
83
 
71
84
  Working directory is the project root even when a stage worktree exists:
72
85
  this CLI attaches the worktree with `--add-dir` and runs from the root,
@@ -78,7 +91,7 @@ class CodexExecution:
78
91
  for directory in request.policy.write_scope:
79
92
  if directory != request.project_root:
80
93
  argv += ["--add-dir", str(directory)]
81
- argv += ["--model", request.model, "--sandbox", "workspace-write"]
94
+ argv += ["--model", request.model, "--sandbox", "danger-full-access"]
82
95
  if request.policy.auto_approve:
83
96
  argv += ["-c", "approval_policy=never"]
84
97
  argv.append("-")
@@ -90,7 +103,11 @@ class CodexExecution:
90
103
  )
91
104
 
92
105
  def policy_support(self) -> PolicySupport:
93
- return PolicySupport(can_auto_approve=True, can_bound_write_scope=True)
106
+ return PolicySupport(
107
+ can_auto_approve=True,
108
+ can_bound_write_scope=False,
109
+ note="this CLI no longer runs behind a sandbox boundary",
110
+ )
94
111
 
95
112
 
96
113
  def create_provider() -> ProviderSpec:
@@ -22,8 +22,8 @@ from okstra_ctl.domain.worker_stream import StreamEvent, Text, ToolCall, ToolRes
22
22
 
23
23
  GROK = {
24
24
  "grok-4.6": ModelSpec("grok-4.6", "grok-4.6", "grok-4.6", pricing=(2.00, 0.50, 6.00)),
25
- "grok-4.5": ModelSpec("grok-4.5", "grok-4.5", "grok-4.5", pricing=(2.00, 0.30, 6.00)),
26
- "grok-build-0.1": ModelSpec("grok-build-0.1", "grok-build-0.1", "grok-build-0.1", pricing=(1.00, 0.20, 2.00)),
25
+ # picker 에서는 감춘다(사유는 codex 의 gpt-5.4-mini 와 동일).
26
+ "grok-build-0.1": ModelSpec("grok-build-0.1", "grok-build-0.1", "grok-build-0.1", pricing=(1.00, 0.20, 2.00), selectable=False),
27
27
  }
28
28
 
29
29
 
@@ -31,8 +31,7 @@ def _catalog_model_id(observed: str) -> str:
31
31
  """Undo the `-build` suffix grok reports its served model under.
32
32
 
33
33
  Measured on grok 1.x: asking for `grok-4.6` closes with
34
- `modelUsage: {"grok-4.6-build": ...}`, and `grok-4.5` with
35
- `grok-4.5-build`. The catalog holds the requested ids, so reading the
34
+ `modelUsage: {"grok-4.6-build": ...}`. The catalog holds the requested ids, so reading the
36
35
  reported one as-is would fail the served-model gate on the very model
37
36
  okstra asked for. A catalog entry that already carries the suffix
38
37
  (`grok-build-0.1`) is matched first and left alone.
@@ -110,6 +109,16 @@ def observe_served_model(event: Mapping[str, Any]) -> str | None:
110
109
  return None
111
110
 
112
111
 
112
+ def run_concluded(event: Mapping[str, Any]) -> bool:
113
+ """이 어휘의 정상 종결 — `end` 이벤트.
114
+
115
+ grok 은 Result 이벤트를 내지 않는다. 실측(status 사이드카 100건): 건강한
116
+ run 71건이 `end.modelUsage` 에서만 나오는 observedModel 을 갖고, result
117
+ 이벤트 없이 exit 0 으로 끝난 실패 run 들에는 `end` 가 없다.
118
+ """
119
+ return event.get("type") == "end"
120
+
121
+
113
122
  def stream_events(event: Mapping[str, Any]) -> tuple[StreamEvent, ...]:
114
123
  """grok `--output-format streaming-json` 한 줄을 공통 이벤트로 옮긴다.
115
124
 
@@ -150,23 +159,85 @@ def _call_detail(event: Mapping[str, Any]) -> str:
150
159
  return str(title) if isinstance(title, str) else ""
151
160
 
152
161
 
162
+ # `rawOutput` 의 첫 키는 도구 판별자다 — `ReadFile`, `ListDir`, `GrepSearch`.
163
+ # 값 중 첫 문자열을 고르던 동안에는 언제나 이 이름표가 잡혀, 46KB 를 읽은
164
+ # 호출도 로그와 pane 에 `← done (8 bytes)` 한 줄로만 남았다.
165
+ _TOOL_DISCRIMINATOR = "type"
166
+ _FINISHED = ("completed", "failed")
167
+
168
+
153
169
  def _completed_tool(event: Mapping[str, Any]) -> tuple[StreamEvent, ...]:
154
- if event.get("status") != "completed":
170
+ """끝난 도구 호출 하나를 본문과 성패까지 담아 옮긴다.
171
+
172
+ `failed` 를 실제로 읽는다. 상태를 무시하고 `completed` 만 통과시키던
173
+ 동안에는 존재하지 않는 파일을 읽은 호출이 스트림에서 통째로 사라져,
174
+ 워커가 왜 다음 수를 바꿨는지 기록에 남지 않았다(실측 2026-08-26,
175
+ grok 1.x: 완료 76건 중 `failed` 2건).
176
+ """
177
+ status = event.get("status")
178
+ if status not in _FINISHED:
155
179
  return ()
156
- body = _output_body(event.get("rawOutput"))
157
- return (ToolResult(body=body, size_bytes=len(body.encode("utf-8"))),)
180
+ body = _finished_body(event)
181
+ return (
182
+ ToolResult(
183
+ body=body,
184
+ size_bytes=len(body.encode("utf-8")),
185
+ failed=status == "failed",
186
+ ),
187
+ )
188
+
189
+
190
+ def _finished_body(event: Mapping[str, Any]) -> str:
191
+ """이 호출이 실제로 돌려준 것.
192
+
193
+ ACP `content` 블록이 정본이다 — 실측한 완료 76건 중 70건이 도구 출력을
194
+ 그대로 담았다. 남은 6건(`list_dir`)은 블록 없이 `rawOutput` 만 보내므로
195
+ 그쪽을 폴백으로 둔다.
196
+ """
197
+ blocks = event.get("content")
198
+ if isinstance(blocks, list):
199
+ rendered = [text for block in blocks if (text := _content_text(block))]
200
+ if rendered:
201
+ return "\n".join(rendered)
202
+ return _output_body(event.get("rawOutput"))
203
+
204
+
205
+ def _content_text(block: Any) -> str:
206
+ if not isinstance(block, Mapping):
207
+ return ""
208
+ kind = block.get("type")
209
+ if kind == "content":
210
+ inner = block.get("content")
211
+ return str(inner.get("text", "")) if isinstance(inner, Mapping) else ""
212
+ if kind == "diff":
213
+ # 파일을 고친 호출은 텍스트가 아니라 diff 로 닫는다. 이 호출이 남긴
214
+ # 것은 새 본문이므로 경로와 함께 그것을 적는다.
215
+ return f"--- {block.get('path', '')}\n{block.get('newText', '')}"
216
+ return ""
158
217
 
159
218
 
160
219
  def _output_body(output: Any) -> str:
220
+ """`rawOutput` 에서 사람이 읽을 것을 꺼낸다.
221
+
222
+ 판별자 키를 건너뛴 다음, 한 겹 안쪽까지 문자열을 찾는다 — `list_dir` 은
223
+ `Content.content`, `read_file` 은 `FileContent.content` 에 본문을 둔다.
224
+ """
161
225
  if isinstance(output, str):
162
226
  return output
163
- if isinstance(output, Mapping):
164
- text = next(
165
- (str(value) for value in output.values() if isinstance(value, str) and value),
166
- "",
167
- )
168
- return text or json.dumps(output, ensure_ascii=False)
169
- return "" if output is None else str(output)
227
+ if not isinstance(output, Mapping):
228
+ return "" if output is None else str(output)
229
+ for key, value in output.items():
230
+ if key == _TOOL_DISCRIMINATOR:
231
+ continue
232
+ if isinstance(value, str) and value:
233
+ return value
234
+ if isinstance(value, Mapping):
235
+ nested = next(
236
+ (item for item in value.values() if isinstance(item, str) and item), ""
237
+ )
238
+ if nested:
239
+ return nested
240
+ return json.dumps(output, ensure_ascii=False)
170
241
 
171
242
  GROK_LEAD_LAUNCH = LeadLaunchSpec(
172
243
  executable="grok",
@@ -205,6 +276,7 @@ class GrokExecution:
205
276
  normalise=stream_events,
206
277
  observe=observe_served_model,
207
278
  join_adjacent_text=True,
279
+ is_conclusion=run_concluded,
208
280
  ),
209
281
  )
210
282
 
@@ -17,11 +17,11 @@ from okstra_ctl.domain.worker_stream import content_block_events
17
17
 
18
18
 
19
19
  KIMI = {
20
- "kimi-k2.7-code": ModelSpec("kimi-k2.7-code", "Kimi K2.7 Code", "kimi-k2.7-code", aliases=("kimi k2.7 code",), pricing=(0.95, 0.19, 4.00)),
21
- "kimi-for-coding": ModelSpec("kimi-for-coding", "Kimi K2.7 Code", "kimi-for-coding"),
22
20
  "kimi-k3": ModelSpec("kimi-k3", "Kimi K3", "kimi-k3", aliases=("kimi k3",), pricing=(3.00, 0.30, 15.00)),
23
- "k3": ModelSpec("k3", "Kimi K3", "k3"),
24
- "k3-256k": ModelSpec("k3-256k", "Kimi K3 256K", "k3-256k", aliases=("kimi k3 256k",)),
21
+ # picker 에서는 감춘다: kimi-k3 와 표시명·실체가 같아 목록에 두 줄로 뜬다.
22
+ "k3": ModelSpec("k3", "Kimi K3", "k3", selectable=False),
23
+ # picker 에서는 감춘다(사유는 codex 의 gpt-5.4-mini 와 동일).
24
+ "k3-256k": ModelSpec("k3-256k", "Kimi K3 256K", "k3-256k", aliases=("kimi k3 256k",), selectable=False),
25
25
  }
26
26
 
27
27
 
@@ -93,15 +93,15 @@ def create_provider() -> ProviderSpec:
93
93
  display_label="Kimi",
94
94
  models=KIMI,
95
95
  default_models={
96
- "lead": "kimi-k2.7-code",
97
- "analyser": "kimi-k2.7-code",
96
+ "lead": "kimi-k3",
97
+ "analyser": "kimi-k3",
98
98
  "critic": "kimi-k3",
99
- "designer": "kimi-k2.7-code",
100
- "planner": "kimi-k2.7-code",
101
- "executor": "kimi-k2.7-code",
102
- "verifier": "kimi-k2.7-code",
103
- "report-writer": "kimi-k2.7-code",
104
- "translator": "kimi-k2.7-code",
99
+ "designer": "kimi-k3",
100
+ "planner": "kimi-k3",
101
+ "executor": "kimi-k3",
102
+ "verifier": "kimi-k3",
103
+ "report-writer": "kimi-k3",
104
+ "translator": "kimi-k3",
105
105
  },
106
106
  wrapper="okstra-kimi-exec.sh",
107
107
  supported_roles=ALL_ROLE_TOKENS,
@@ -22,17 +22,14 @@ class CmuxRuntime:
22
22
  if not workspace:
23
23
  reason = cmux.unreachable_reason()
24
24
  if reason in (cmux.LOST_ENVIRONMENT, cmux.LOST_DENIED):
25
- observed = (
26
- "this process has no cmux environment left — a sandbox sanitized it"
27
- if reason == cmux.LOST_ENVIRONMENT
28
- else "cmux is running but this process may not connect to its socket"
29
- )
25
+ # 두 사유는 처방이 다르고, 그 문구는 prepare 의 강등 거부와
26
+ # 공유한다 — cmux.unreachable_advice 가 정본이다.
27
+ observed, remedy = cmux.unreachable_advice(reason)
30
28
  raise EnvironmentBlocked(
31
29
  "this run was prepared for cmux panes, but "
32
30
  f"{observed}. The same sandbox blocks worker CLIs from "
33
31
  "their own config, so falling back to blocking workers "
34
- "would fail too. Relaunch the lead outside a sandbox "
35
- "(use a full-access lead sandbox)."
32
+ f"would fail too. {remedy}"
36
33
  )
37
34
  raise SurfaceUnavailable("cmux lead workspace is gone")
38
35
  try:
@@ -0,0 +1 @@
1
+ """에이전트 호출 명세, 프롬프트 CLI, 활동 기록을 모은 패키지."""
@@ -373,8 +373,19 @@ def _project(args: argparse.Namespace) -> int:
373
373
  return 0
374
374
 
375
375
 
376
+ _CLI_EPILOG = r"""Usage:
377
+ okstra agent-activity append --project-root <dir> --run-manifest <path> --summary-file <md> [--command <text> --command-cwd <dir> --command-exit-code <n> --command-output-file <md>]
378
+ okstra agent-activity project --project-root <dir> --run-manifest <path> --data <data.json> # v2 diagnostic compatibility
379
+ """
380
+ _CLI_DESCRIPTION = "Record and project structured lead activity."
381
+
382
+
376
383
  def _parser() -> argparse.ArgumentParser:
377
- parser = argparse.ArgumentParser(prog="okstra agent-activity")
384
+ parser = argparse.ArgumentParser(
385
+ description=_CLI_DESCRIPTION,
386
+ epilog=_CLI_EPILOG,
387
+ formatter_class=argparse.RawDescriptionHelpFormatter,
388
+ prog="okstra agent-activity")
378
389
  subparsers = parser.add_subparsers(dest="command", required=True)
379
390
  append_parser = subparsers.add_parser("append")
380
391
  append_parser.add_argument("--project-root", type=Path, required=True)
@@ -14,8 +14,8 @@ import re
14
14
  import tempfile
15
15
  from typing import Iterator, Literal, Mapping, get_args
16
16
 
17
- from .domain.role import RoleCatalogError, role_for_duty
18
- from .json_boundary import (
17
+ from ..domain.role import RoleCatalogError, role_for_duty
18
+ from ..json_boundary import (
19
19
  JsonBoundaryError,
20
20
  external_invocation_json_source,
21
21
  load_external_json,
@@ -1214,14 +1214,28 @@ def _render_prompt(
1214
1214
  duty: DutyContract,
1215
1215
  ) -> str:
1216
1216
  assignment = request.assignment
1217
+ task_body = request.instruction.body.rstrip("\n")
1218
+ task_declares_model = any(
1219
+ line.strip().startswith("**Model:**") for line in task_body.splitlines()
1220
+ )
1217
1221
  header = [
1218
1222
  *request.instruction.anchor_lines,
1219
1223
  f"**Provider:** {assignment.provider}",
1220
- f"**Model:** {assignment.model}",
1221
1224
  f"**Model execution value:** {assignment.model_execution_value}",
1222
1225
  f"**Runner:** {assignment.runner}",
1223
1226
  f"**Host runtime:** {assignment.host_runtime}",
1224
1227
  ]
1228
+ if not task_declares_model:
1229
+ header.insert(
1230
+ len(request.instruction.anchor_lines) + 1,
1231
+ # 실행값이다. 카탈로그 id 가 아니다. 두 값은 antigravity 에서만
1232
+ # 갈라진다 — agy 는 tier 를 모델 식별자에 붙여야만 받으므로
1233
+ # `gemini-3.1-pro` 의 실행값은 `gemini-3.1-pro-high` 다. 여기에
1234
+ # 카탈로그 id 를 쓰면 이 헤더만 실제로 돌아가는 모델과 다른 값을
1235
+ # 가리키고, 리드가 이 줄을 읽어 재검증 프롬프트에 옮길 때 그
1236
+ # 어긋남이 다음 라운드로 옮겨 간다.
1237
+ f"**Model:** {assignment.model_execution_value}",
1238
+ )
1225
1239
  # The dispatch prompt contract requires exactly one of these on every prompt
1226
1240
  # it checks, and a critic pass is checked under the same contract as the
1227
1241
  # initial analysis it audits — but only the initial-prompt path emitted one,
@@ -1242,7 +1256,6 @@ def _render_prompt(
1242
1256
  if assignment.host_model_value is not None:
1243
1257
  header.append(f"**Host model value:** {assignment.host_model_value}")
1244
1258
  duty_body = f"{common.body.rstrip()}\n\n{duty.body.rstrip()}"
1245
- task_body = request.instruction.body.rstrip("\n")
1246
1259
  return (
1247
1260
  "\n".join(header)
1248
1261
  + "\n\n## Duty Contract\n\n"
@@ -1773,7 +1786,6 @@ def _verify_model_header(
1773
1786
  prompt = prompt_bytes.decode("utf-8")
1774
1787
  required = {
1775
1788
  f"**Provider:** {assignment['provider']}",
1776
- f"**Model:** {assignment['model']}",
1777
1789
  f"**Model execution value:** {assignment['modelExecutionValue']}",
1778
1790
  f"**Runner:** {assignment['runner']}",
1779
1791
  f"**Host runtime:** {assignment['hostRuntime']}",
@@ -1784,6 +1796,23 @@ def _verify_model_header(
1784
1796
  prefix = prompt.split("\n\n## Duty Contract\n\n", 1)[0]
1785
1797
  if not required.issubset(set(prefix.splitlines())):
1786
1798
  return ["prompt model header does not match model assignment"]
1799
+ model_values = [
1800
+ line.split("**Model:**", 1)[1].strip()
1801
+ for line in prompt.splitlines()
1802
+ if line.strip().startswith("**Model:**")
1803
+ ]
1804
+ # 프롬프트의 `**Model:**` 이 싣는 값은 실행값이다. 리드가 지시문에 직접
1805
+ # 쓰는 형식(`**Model:** <role>, <modelExecutionValue>`)도, 지시문에 그 줄이
1806
+ # 없을 때 materialize 가 채우는 형식도 실행값 한 가지다. 카탈로그 id 로
1807
+ # 비교하면 두 값이 같은 provider 에서는 통과하고 antigravity 에서만
1808
+ # 실패하므로, 규칙이 아니라 provider 를 판정하게 된다.
1809
+ expected_model = str(assignment["modelExecutionValue"])
1810
+ if (
1811
+ len(model_values) != 1
1812
+ or not model_values[0]
1813
+ or model_values[0].rsplit(",", 1)[-1].strip() != expected_model
1814
+ ):
1815
+ return ["prompt model header does not match model assignment"]
1787
1816
  return []
1788
1817
 
1789
1818
 
@@ -0,0 +1,19 @@
1
+ """Materialize and verify one auditable LLM invocation specification.
2
+
3
+ 구현은 네 층이다. 바닥의 `inputs`(경로를 믿지 않고 해석한다)와 `emit`(결과를
4
+ 내보낸다)은 서로를 모른다. 그 위에 `dynamic_verifier`(추가 검증자 예약)와
5
+ `run_identity`(요청한 신원이 이 run 의 것인가)가 있고, `materialize`(명세를
6
+ 만든다)와 `results`(만들어진 명세와 결과를 잇는다)가 그것들을 쓴다. `cli` 는
7
+ argparse 표면과 분기뿐이다.
8
+
9
+ 이 파일은 밖으로 나가는 이름만 모아 둔다.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ from .cli import main
14
+ from .inputs import AgentPromptCliError
15
+
16
+ __all__ = [
17
+ "AgentPromptCliError",
18
+ "main",
19
+ ]
@@ -0,0 +1,11 @@
1
+ """`python3 -m okstra_ctl.agent.prompt_cli` 진입점.
2
+
3
+ CLI 레지스트리(`src/cli-registry.mts`)가 이 모듈을 `-m` 으로 띄운다. 단일
4
+ 파일이던 시절에는 모듈 본문 끝의 `__name__` 가드가 그 역할을 했고, 패키지가
5
+ 되면서 그 자리가 여기로 왔다.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from .cli import main
10
+
11
+ raise SystemExit(main())