oh-my-codex 0.18.13 → 0.18.15

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 (360) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +41 -18
  4. package/dist/agents/__tests__/native-config.test.js +62 -0
  5. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  6. package/dist/agents/native-config.d.ts.map +1 -1
  7. package/dist/agents/native-config.js +15 -2
  8. package/dist/agents/native-config.js.map +1 -1
  9. package/dist/autopilot/__tests__/fsm.test.js +7 -0
  10. package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
  11. package/dist/autopilot/__tests__/planner-routing.test.d.ts +2 -0
  12. package/dist/autopilot/__tests__/planner-routing.test.d.ts.map +1 -0
  13. package/dist/autopilot/__tests__/planner-routing.test.js +74 -0
  14. package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -0
  15. package/dist/autopilot/__tests__/ralplan-gate.test.js +315 -0
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  17. package/dist/autopilot/completion-gate.js +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +2 -2
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autopilot/fsm.js +3 -0
  22. package/dist/autopilot/fsm.js.map +1 -1
  23. package/dist/autopilot/planner-routing.d.ts +21 -0
  24. package/dist/autopilot/planner-routing.d.ts.map +1 -0
  25. package/dist/autopilot/planner-routing.js +59 -0
  26. package/dist/autopilot/planner-routing.js.map +1 -0
  27. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  28. package/dist/autopilot/ralplan-gate.js +30 -0
  29. package/dist/autopilot/ralplan-gate.js.map +1 -1
  30. package/dist/autoresearch/goal.d.ts.map +1 -1
  31. package/dist/autoresearch/goal.js +10 -1
  32. package/dist/autoresearch/goal.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch-goal.test.js +33 -0
  34. package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
  35. package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
  36. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  37. package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -0
  38. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts +2 -0
  40. package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts.map +1 -0
  41. package/dist/cli/__tests__/doctor-artifact-ownership.test.js +158 -0
  42. package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -0
  43. package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
  44. package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
  45. package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
  46. package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
  47. package/dist/cli/__tests__/doctor-spark-routing.test.js +13 -0
  48. package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
  49. package/dist/cli/__tests__/doctor-warning-copy.test.js +132 -2
  50. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  51. package/dist/cli/__tests__/index.test.js +5 -0
  52. package/dist/cli/__tests__/index.test.js.map +1 -1
  53. package/dist/cli/__tests__/mcp-parity.test.js +40 -0
  54. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  55. package/dist/cli/__tests__/nested-help-routing.test.js +4 -0
  56. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  57. package/dist/cli/__tests__/package-bin-contract.test.js +7 -1
  58. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/performance-goal.test.js +22 -0
  60. package/dist/cli/__tests__/performance-goal.test.js.map +1 -1
  61. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
  62. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
  63. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
  64. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
  65. package/dist/cli/__tests__/resume.test.js +96 -0
  66. package/dist/cli/__tests__/resume.test.js.map +1 -1
  67. package/dist/cli/__tests__/session-scoped-runtime.test.js +162 -0
  68. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  69. package/dist/cli/__tests__/session-search.test.js +36 -0
  70. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  71. package/dist/cli/__tests__/setup-agents-overwrite.test.js +70 -1
  72. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  73. package/dist/cli/__tests__/setup-install-mode.test.js +33 -0
  74. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  75. package/dist/cli/__tests__/team.test.js +17 -3
  76. package/dist/cli/__tests__/team.test.js.map +1 -1
  77. package/dist/cli/__tests__/ultragoal.test.js +37 -0
  78. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  79. package/dist/cli/__tests__/update.test.js +81 -2
  80. package/dist/cli/__tests__/update.test.js.map +1 -1
  81. package/dist/cli/__tests__/url.test.d.ts +2 -0
  82. package/dist/cli/__tests__/url.test.d.ts.map +1 -0
  83. package/dist/cli/__tests__/url.test.js +82 -0
  84. package/dist/cli/__tests__/url.test.js.map +1 -0
  85. package/dist/cli/autoresearch-goal.d.ts.map +1 -1
  86. package/dist/cli/autoresearch-goal.js +2 -1
  87. package/dist/cli/autoresearch-goal.js.map +1 -1
  88. package/dist/cli/autoresearch-intake.d.ts.map +1 -1
  89. package/dist/cli/autoresearch-intake.js +2 -0
  90. package/dist/cli/autoresearch-intake.js.map +1 -1
  91. package/dist/cli/doctor.d.ts +39 -1
  92. package/dist/cli/doctor.d.ts.map +1 -1
  93. package/dist/cli/doctor.js +460 -40
  94. package/dist/cli/doctor.js.map +1 -1
  95. package/dist/cli/index.d.ts +2 -2
  96. package/dist/cli/index.d.ts.map +1 -1
  97. package/dist/cli/index.js +71 -4
  98. package/dist/cli/index.js.map +1 -1
  99. package/dist/cli/performance-goal.d.ts.map +1 -1
  100. package/dist/cli/performance-goal.js +4 -2
  101. package/dist/cli/performance-goal.js.map +1 -1
  102. package/dist/cli/plugin-marketplace.d.ts +1 -0
  103. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  104. package/dist/cli/plugin-marketplace.js +88 -67
  105. package/dist/cli/plugin-marketplace.js.map +1 -1
  106. package/dist/cli/project-runtime-codex-homes.d.ts +2 -0
  107. package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -1
  108. package/dist/cli/project-runtime-codex-homes.js +112 -5
  109. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  110. package/dist/cli/setup.d.ts.map +1 -1
  111. package/dist/cli/setup.js +15 -6
  112. package/dist/cli/setup.js.map +1 -1
  113. package/dist/cli/ultragoal.d.ts.map +1 -1
  114. package/dist/cli/ultragoal.js +5 -1
  115. package/dist/cli/ultragoal.js.map +1 -1
  116. package/dist/cli/update.d.ts.map +1 -1
  117. package/dist/cli/update.js +23 -4
  118. package/dist/cli/update.js.map +1 -1
  119. package/dist/cli/url.d.ts +8 -0
  120. package/dist/cli/url.d.ts.map +1 -0
  121. package/dist/cli/url.js +69 -0
  122. package/dist/cli/url.js.map +1 -0
  123. package/dist/config/__tests__/codex-hooks.test.js +20 -4
  124. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  125. package/dist/config/__tests__/models.test.js +21 -1
  126. package/dist/config/__tests__/models.test.js.map +1 -1
  127. package/dist/config/codex-hooks.d.ts.map +1 -1
  128. package/dist/config/codex-hooks.js +1 -4
  129. package/dist/config/codex-hooks.js.map +1 -1
  130. package/dist/config/models.d.ts +5 -0
  131. package/dist/config/models.d.ts.map +1 -1
  132. package/dist/config/models.js +23 -0
  133. package/dist/config/models.js.map +1 -1
  134. package/dist/goal-workflows/codex-goal-snapshot.d.ts +2 -0
  135. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  136. package/dist/goal-workflows/codex-goal-snapshot.js +10 -0
  137. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  138. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +7 -0
  139. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  140. package/dist/hooks/__tests__/keyword-detector.test.js +46 -0
  141. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  142. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +100 -0
  143. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  144. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +75 -0
  145. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  146. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +225 -0
  147. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  148. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +75 -0
  149. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  150. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +563 -0
  151. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  152. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +187 -12
  153. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  154. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +225 -0
  155. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  156. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
  157. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  158. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +75 -0
  159. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  160. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  161. package/dist/hooks/keyword-detector.js +4 -0
  162. package/dist/hooks/keyword-detector.js.map +1 -1
  163. package/dist/hud/__tests__/reconcile.test.js +137 -2
  164. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  165. package/dist/hud/__tests__/state.test.js +107 -0
  166. package/dist/hud/__tests__/state.test.js.map +1 -1
  167. package/dist/hud/__tests__/tmux.test.js +17 -0
  168. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  169. package/dist/hud/reconcile.d.ts.map +1 -1
  170. package/dist/hud/reconcile.js +21 -7
  171. package/dist/hud/reconcile.js.map +1 -1
  172. package/dist/hud/render.d.ts.map +1 -1
  173. package/dist/hud/render.js +9 -0
  174. package/dist/hud/render.js.map +1 -1
  175. package/dist/hud/state.d.ts.map +1 -1
  176. package/dist/hud/state.js +43 -5
  177. package/dist/hud/state.js.map +1 -1
  178. package/dist/hud/types.d.ts +6 -0
  179. package/dist/hud/types.d.ts.map +1 -1
  180. package/dist/hud/types.js.map +1 -1
  181. package/dist/index.d.ts +1 -0
  182. package/dist/index.d.ts.map +1 -1
  183. package/dist/index.js +1 -0
  184. package/dist/index.js.map +1 -1
  185. package/dist/mcp/__tests__/bootstrap.test.js +24 -1
  186. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  187. package/dist/mcp/__tests__/state-paths.test.js +53 -0
  188. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  189. package/dist/mcp/bootstrap.d.ts +2 -0
  190. package/dist/mcp/bootstrap.d.ts.map +1 -1
  191. package/dist/mcp/bootstrap.js +32 -6
  192. package/dist/mcp/bootstrap.js.map +1 -1
  193. package/dist/mcp/state-paths.d.ts.map +1 -1
  194. package/dist/mcp/state-paths.js +11 -5
  195. package/dist/mcp/state-paths.js.map +1 -1
  196. package/dist/modes/__tests__/base-autopilot-gates.test.js +48 -0
  197. package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
  198. package/dist/modes/base.d.ts.map +1 -1
  199. package/dist/modes/base.js +1 -0
  200. package/dist/modes/base.js.map +1 -1
  201. package/dist/notifications/__tests__/http-client.test.js +9 -0
  202. package/dist/notifications/__tests__/http-client.test.js.map +1 -1
  203. package/dist/notifications/http-client.js +1 -1
  204. package/dist/notifications/http-client.js.map +1 -1
  205. package/dist/performance-goal/artifacts.d.ts.map +1 -1
  206. package/dist/performance-goal/artifacts.js +10 -1
  207. package/dist/performance-goal/artifacts.js.map +1 -1
  208. package/dist/ralplan/__tests__/consensus-gate.test.js +112 -0
  209. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  210. package/dist/ralplan/consensus-gate.d.ts +23 -0
  211. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  212. package/dist/ralplan/consensus-gate.js +133 -21
  213. package/dist/ralplan/consensus-gate.js.map +1 -1
  214. package/dist/scripts/__tests__/codex-native-hook.test.js +1251 -13
  215. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  216. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  217. package/dist/scripts/codex-native-hook.js +601 -24
  218. package/dist/scripts/codex-native-hook.js.map +1 -1
  219. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
  220. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  221. package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
  222. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  223. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  224. package/dist/scripts/notify-hook/team-tmux-guard.js +87 -8
  225. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  226. package/dist/scripts/notify-hook/tmux-injection.js +1 -1
  227. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  228. package/dist/scripts/run-compiled-ci.d.ts +2 -0
  229. package/dist/scripts/run-compiled-ci.d.ts.map +1 -0
  230. package/dist/scripts/run-compiled-ci.js +77 -0
  231. package/dist/scripts/run-compiled-ci.js.map +1 -0
  232. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  233. package/dist/scripts/sync-plugin-mirror.js +1 -0
  234. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  235. package/dist/session-history/search.d.ts.map +1 -1
  236. package/dist/session-history/search.js +24 -9
  237. package/dist/session-history/search.js.map +1 -1
  238. package/dist/sidecar/__tests__/args.test.d.ts +2 -0
  239. package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
  240. package/dist/sidecar/__tests__/args.test.js +18 -0
  241. package/dist/sidecar/__tests__/args.test.js.map +1 -0
  242. package/dist/sidecar/index.d.ts.map +1 -1
  243. package/dist/sidecar/index.js +12 -4
  244. package/dist/sidecar/index.js.map +1 -1
  245. package/dist/state/__tests__/operations.test.js +107 -3
  246. package/dist/state/__tests__/operations.test.js.map +1 -1
  247. package/dist/state/operations.d.ts.map +1 -1
  248. package/dist/state/operations.js +57 -2
  249. package/dist/state/operations.js.map +1 -1
  250. package/dist/team/__tests__/delivery-e2e-smoke.test.js +22 -1
  251. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  252. package/dist/team/__tests__/model-contract.test.js +57 -1
  253. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  254. package/dist/team/__tests__/runtime.test.js +1468 -110
  255. package/dist/team/__tests__/runtime.test.js.map +1 -1
  256. package/dist/team/__tests__/scaling.test.js +63 -0
  257. package/dist/team/__tests__/scaling.test.js.map +1 -1
  258. package/dist/team/__tests__/state.test.js +24 -1
  259. package/dist/team/__tests__/state.test.js.map +1 -1
  260. package/dist/team/__tests__/tmux-session.test.js +512 -0
  261. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  262. package/dist/team/__tests__/worker-bootstrap.test.js +42 -9
  263. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  264. package/dist/team/model-contract.d.ts +16 -0
  265. package/dist/team/model-contract.d.ts.map +1 -1
  266. package/dist/team/model-contract.js +41 -1
  267. package/dist/team/model-contract.js.map +1 -1
  268. package/dist/team/runtime.d.ts.map +1 -1
  269. package/dist/team/runtime.js +116 -30
  270. package/dist/team/runtime.js.map +1 -1
  271. package/dist/team/scaling.d.ts.map +1 -1
  272. package/dist/team/scaling.js +9 -1
  273. package/dist/team/scaling.js.map +1 -1
  274. package/dist/team/state/types.d.ts +2 -0
  275. package/dist/team/state/types.d.ts.map +1 -1
  276. package/dist/team/state/types.js.map +1 -1
  277. package/dist/team/state.d.ts +3 -0
  278. package/dist/team/state.d.ts.map +1 -1
  279. package/dist/team/state.js +20 -0
  280. package/dist/team/state.js.map +1 -1
  281. package/dist/team/tmux-session.d.ts +38 -3
  282. package/dist/team/tmux-session.d.ts.map +1 -1
  283. package/dist/team/tmux-session.js +275 -36
  284. package/dist/team/tmux-session.js.map +1 -1
  285. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  286. package/dist/team/worker-bootstrap.js +26 -1
  287. package/dist/team/worker-bootstrap.js.map +1 -1
  288. package/dist/ultragoal/__tests__/artifacts.test.js +388 -0
  289. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  290. package/dist/ultragoal/__tests__/docs-contract.test.js +3 -0
  291. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  292. package/dist/ultragoal/artifacts.d.ts +23 -0
  293. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  294. package/dist/ultragoal/artifacts.js +275 -12
  295. package/dist/ultragoal/artifacts.js.map +1 -1
  296. package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
  297. package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
  298. package/dist/url-reader/__tests__/url-reader.test.js +433 -0
  299. package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
  300. package/dist/url-reader/index.d.ts +4 -0
  301. package/dist/url-reader/index.d.ts.map +1 -0
  302. package/dist/url-reader/index.js +522 -0
  303. package/dist/url-reader/index.js.map +1 -0
  304. package/dist/url-reader/types.d.ts +41 -0
  305. package/dist/url-reader/types.d.ts.map +1 -0
  306. package/dist/url-reader/types.js +2 -0
  307. package/dist/url-reader/types.js.map +1 -0
  308. package/dist/utils/__tests__/agents-md.test.js +39 -1
  309. package/dist/utils/__tests__/agents-md.test.js.map +1 -1
  310. package/dist/utils/__tests__/agents-model-table.test.js +28 -0
  311. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  312. package/dist/utils/agents-md.d.ts +4 -0
  313. package/dist/utils/agents-md.d.ts.map +1 -1
  314. package/dist/utils/agents-md.js +28 -0
  315. package/dist/utils/agents-md.js.map +1 -1
  316. package/dist/utils/agents-model-table.d.ts +7 -3
  317. package/dist/utils/agents-model-table.d.ts.map +1 -1
  318. package/dist/utils/agents-model-table.js +16 -8
  319. package/dist/utils/agents-model-table.js.map +1 -1
  320. package/dist/utils/platform-command.d.ts +7 -1
  321. package/dist/utils/platform-command.d.ts.map +1 -1
  322. package/dist/utils/platform-command.js +12 -1
  323. package/dist/utils/platform-command.js.map +1 -1
  324. package/dist/verification/__tests__/ci-rust-gates.test.js +4 -0
  325. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  326. package/dist/vscode/__tests__/index.test.d.ts +2 -0
  327. package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
  328. package/dist/vscode/__tests__/index.test.js +236 -0
  329. package/dist/vscode/__tests__/index.test.js.map +1 -0
  330. package/dist/vscode/index.d.ts +76 -0
  331. package/dist/vscode/index.d.ts.map +1 -0
  332. package/dist/vscode/index.js +196 -0
  333. package/dist/vscode/index.js.map +1 -0
  334. package/package.json +3 -3
  335. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  336. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +59 -8
  337. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +9 -5
  338. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +3 -0
  339. package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +3 -0
  340. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
  341. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +22 -4
  342. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -2
  343. package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +61 -0
  344. package/skills/autopilot/SKILL.md +9 -5
  345. package/skills/autoresearch-goal/SKILL.md +3 -0
  346. package/skills/ecomode/SKILL.md +1 -1
  347. package/skills/ecomode/references/agent-tiers.md +61 -0
  348. package/skills/performance-goal/SKILL.md +3 -0
  349. package/skills/ralplan/SKILL.md +1 -1
  350. package/skills/ultragoal/SKILL.md +22 -4
  351. package/skills/ultrawork/SKILL.md +2 -2
  352. package/skills/ultrawork/references/agent-tiers.md +61 -0
  353. package/src/scripts/__tests__/codex-native-hook.test.ts +1560 -181
  354. package/src/scripts/codex-native-hook.ts +663 -24
  355. package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
  356. package/src/scripts/notify-hook/team-tmux-guard.ts +92 -8
  357. package/src/scripts/notify-hook/tmux-injection.ts +1 -1
  358. package/src/scripts/run-compiled-ci.ts +85 -0
  359. package/src/scripts/sync-plugin-mirror.ts +1 -0
  360. package/templates/AGENTS.md +6 -0
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { appendFile, mkdir, open, readFile, rename, rm, writeFile } from 'node:fs/promises';
3
3
  import { join, relative } from 'node:path';
4
- import { formatCodexGoalReconciliation, parseCodexGoalSnapshot, reconcileCodexGoalSnapshot, } from '../goal-workflows/codex-goal-snapshot.js';
4
+ import { formatCodexGoalReconciliation, buildCompletedCodexGoalRemediation, parseCodexGoalSnapshot, reconcileCodexGoalSnapshot, } from '../goal-workflows/codex-goal-snapshot.js';
5
5
  export const ULTRAGOAL_DIR = '.omx/ultragoal';
6
6
  export const ULTRAGOAL_BRIEF = 'brief.md';
7
7
  export const ULTRAGOAL_GOALS = 'goals.json';
@@ -207,6 +207,22 @@ async function snapshotObjectiveMapsToUltragoalPlan(cwd, snapshotObjective) {
207
207
  return false;
208
208
  }
209
209
  }
210
+ function unresolvedReviewBlockedGoals(plan) {
211
+ return plan.goals.filter((candidate) => candidate.status === 'review_blocked' && !isReviewBlockedResolved(candidate, plan));
212
+ }
213
+ function isDesignatedReviewBlockerResolver(goal, parent) {
214
+ return parent?.status === 'review_blocked'
215
+ && goal.resolvesReviewBlockedGoalId === parent.id
216
+ && parent.reviewBlockerResolution?.resolverGoalId === goal.id;
217
+ }
218
+ function canUseCleanFinalResolverPathForReviewBlockedParent(plan, goal, finalRunCheckpoint, allowActiveFinalCodexGoal) {
219
+ const unresolvedReviewBlocked = unresolvedReviewBlockedGoals(plan);
220
+ if (unresolvedReviewBlocked.length !== 1)
221
+ return false;
222
+ return finalRunCheckpoint
223
+ && !allowActiveFinalCodexGoal
224
+ && isDesignatedReviewBlockerResolver(goal, unresolvedReviewBlocked[0]);
225
+ }
210
226
  async function canReconcileCompletedTaskScopedAggregateSnapshot(cwd, plan, goal, snapshotObjective, evidence) {
211
227
  if (codexGoalMode(plan) !== 'aggregate')
212
228
  return false;
@@ -295,16 +311,29 @@ function isSupersededResolved(goal, plan) {
295
311
  return replacement !== undefined && isResolvedStatus(replacement.status);
296
312
  });
297
313
  }
314
+ function isReviewBlockedResolved(goal, plan) {
315
+ if (goal.status !== 'review_blocked')
316
+ return false;
317
+ const resolverId = goal.reviewBlockerResolution?.resolverGoalId;
318
+ if (!resolverId || goal.reviewBlockerResolution?.status !== 'complete')
319
+ return false;
320
+ const resolver = plan.goals.find((candidate) => candidate.id === resolverId);
321
+ return resolver?.status === 'complete';
322
+ }
298
323
  function isCompletionBlocking(goal, plan) {
299
324
  if (goal.steeringStatus === 'superseded')
300
325
  return !isSupersededResolved(goal, plan);
301
326
  if (goal.steeringStatus === 'blocked')
302
327
  return true;
328
+ if (goal.status === 'review_blocked')
329
+ return !isReviewBlockedResolved(goal, plan);
303
330
  return !isResolvedStatus(goal.status);
304
331
  }
305
332
  function isCompletionBlockingForFinalCandidate(candidate, finalCandidate, plan) {
306
333
  if (candidate.id === finalCandidate.id)
307
334
  return false;
335
+ if (candidate.status === 'review_blocked' && candidate.reviewBlockerResolution?.resolverGoalId === finalCandidate.id)
336
+ return false;
308
337
  if (candidate.steeringStatus === 'superseded') {
309
338
  const replacements = candidate.supersededBy ?? [];
310
339
  if (replacements.length === 0)
@@ -488,13 +517,15 @@ export async function createUltragoalPlan(cwd, options) {
488
517
  });
489
518
  }
490
519
  export function summarizeUltragoalPlan(plan) {
520
+ const activeReviewBlocked = plan.goals.filter((goal) => goal.status === 'review_blocked' && !isReviewBlockedResolved(goal, plan)).length;
491
521
  return {
492
522
  total: plan.goals.length,
493
523
  pending: plan.goals.filter((goal) => goal.status === 'pending').length,
494
524
  inProgress: plan.goals.filter((goal) => goal.status === 'in_progress').length,
495
525
  complete: plan.goals.filter((goal) => goal.status === 'complete').length,
496
526
  failed: plan.goals.filter((goal) => goal.status === 'failed').length,
497
- reviewBlocked: plan.goals.filter((goal) => goal.status === 'review_blocked').length,
527
+ reviewBlocked: activeReviewBlocked,
528
+ historicalReviewBlocked: plan.goals.filter((goal) => goal.status === 'review_blocked').length - activeReviewBlocked,
498
529
  needsUserDecision: plan.goals.filter((goal) => goal.status === 'needs_user_decision').length,
499
530
  superseded: plan.goals.filter((goal) => goal.steeringStatus === 'superseded').length,
500
531
  steeringBlocked: plan.goals.filter((goal) => goal.steeringStatus === 'blocked').length,
@@ -548,6 +579,7 @@ function appendGoalToPlan(plan, options, nowOverride) {
548
579
  createdAt: now,
549
580
  updatedAt: now,
550
581
  evidence: options.evidence,
582
+ resolvesReviewBlockedGoalId: options.resolvesReviewBlockedGoalId,
551
583
  };
552
584
  plan.goals.push(goal);
553
585
  plan.updatedAt = now;
@@ -909,9 +941,160 @@ export async function steerUltragoal(cwd, proposal, options = {}) {
909
941
  return { plan, accepted: invariant.accepted, audit, rejectedReasons: invariant.rejectedReasons, deduped: false };
910
942
  });
911
943
  }
912
- function validateQualityGate(value) {
944
+ function normalizeInvariantText(value) {
945
+ return value.replace(/[`*_~]/g, '').replace(/\s+/g, ' ').trim().toLowerCase();
946
+ }
947
+ function requiredInvariantSourceKey(invariant) {
948
+ return `${normalizeInvariantText(invariant.invariant)}\u0000${invariant.sourceArtifact}`;
949
+ }
950
+ function uniqueRequiredArchitectureInvariants(invariants) {
951
+ const seen = new Set();
952
+ const unique = [];
953
+ for (const invariant of invariants) {
954
+ const key = requiredInvariantSourceKey(invariant);
955
+ if (seen.has(key))
956
+ continue;
957
+ seen.add(key);
958
+ unique.push(invariant);
959
+ }
960
+ return unique;
961
+ }
962
+ function architectureInvariantSectionSlug(label) {
963
+ return label
964
+ .replace(/[`*_~]/g, '')
965
+ .toLowerCase()
966
+ .replace(/[^a-z0-9]+/g, '-')
967
+ .replace(/^-|-$/g, '') || 'architecture-invariants';
968
+ }
969
+ function normalizeSourceArtifact(value) {
970
+ return value.trim().split('#', 1)[0]?.replace(/\\/g, '/') ?? '';
971
+ }
972
+ function sourceReferencesArtifact(source, artifact) {
973
+ return normalizeSourceArtifact(source) === normalizeSourceArtifact(artifact);
974
+ }
975
+ function sourceReferencesAnyArtifact(source, artifacts) {
976
+ return artifacts.some((artifact) => sourceReferencesArtifact(source, artifact));
977
+ }
978
+ function invariantFromInlineDeclaration(line) {
979
+ const trimmed = cleanLine(line).replace(/^['"]|['"]$/g, '').trim();
980
+ const match = /\b(?:(?:non-negotiable|required)\s+)?(?:architecture|architectural|domain)\s+(?:invariants?|constraints?|non-negotiables?)\s*:\s*(.+)$/i.exec(trimmed)
981
+ ?? /\bnon-negotiables?\s+(?:architecture|architectural|domain)\s+(?:invariants?|constraints?)\s*:\s*(.+)$/i.exec(trimmed);
982
+ const invariant = match?.[1]?.trim().replace(/[.;]\s*$/, '').trim();
983
+ return invariant || undefined;
984
+ }
985
+ function extractArchitectureInvariantsFromArtifact(text, sourceArtifact, sourcePrefix) {
986
+ const lines = text.split(/\r?\n/);
987
+ const invariants = [];
988
+ let inInvariantSection = false;
989
+ let sectionSlug = 'architecture-invariants';
990
+ for (const line of lines) {
991
+ const heading = line.match(/^\s{0,3}#{1,6}\s+(.+?)\s*#*\s*$/);
992
+ if (heading) {
993
+ const label = heading[1] ?? '';
994
+ const normalizedLabel = label.toLowerCase();
995
+ inInvariantSection = /\b(?:architecture|architectural|domain|non-negotiable)\b/.test(normalizedLabel) && /\binvariants?\b|\bconstraints?\b|\bnon-negotiables?\b/.test(normalizedLabel);
996
+ if (inInvariantSection)
997
+ sectionSlug = architectureInvariantSectionSlug(label);
998
+ continue;
999
+ }
1000
+ const inline = invariantFromInlineDeclaration(line);
1001
+ if (inline) {
1002
+ invariants.push({ invariant: inline, sourceArtifact, source: `${sourceArtifact}#${sourcePrefix ?? 'inline-architecture-invariant'}` });
1003
+ continue;
1004
+ }
1005
+ if (!inInvariantSection)
1006
+ continue;
1007
+ const item = cleanLine(line);
1008
+ if (!item || item === line.trim())
1009
+ continue;
1010
+ invariants.push({ invariant: item, sourceArtifact, source: `${sourceArtifact}#${sourcePrefix ? `${sourcePrefix}-${sectionSlug}` : sectionSlug}` });
1011
+ }
1012
+ return uniqueRequiredArchitectureInvariants(invariants.map((item) => ({ ...item, invariant: item.invariant.trim() })).filter((item) => item.invariant));
1013
+ }
1014
+ function extractArchitectureInvariantsFromBrief(brief) {
1015
+ return extractArchitectureInvariantsFromArtifact(brief, `${ULTRAGOAL_DIR}/${ULTRAGOAL_BRIEF}`);
1016
+ }
1017
+ function extractArchitectureInvariantsFromAcceptedSteering(entries) {
1018
+ const invariants = [];
1019
+ for (const [index, entry] of entries.entries()) {
1020
+ if (entry.event !== 'steering_accepted' || !entry.steering?.invariant.accepted)
1021
+ continue;
1022
+ const sourcePrefix = `steering-${index + 1}`;
1023
+ const steering = entry.steering;
1024
+ const texts = [
1025
+ entry.evidence,
1026
+ entry.message,
1027
+ steering.evidence,
1028
+ steering.rationale,
1029
+ steering.directiveText,
1030
+ ].filter((value) => typeof value === 'string' && value.trim().length > 0);
1031
+ for (const text of texts) {
1032
+ invariants.push(...extractArchitectureInvariantsFromArtifact(text, `${ULTRAGOAL_DIR}/${ULTRAGOAL_LEDGER}`, sourcePrefix));
1033
+ }
1034
+ }
1035
+ return uniqueRequiredArchitectureInvariants(invariants);
1036
+ }
1037
+ async function collectRequiredArchitectureInvariants(cwd) {
1038
+ const briefInvariants = extractArchitectureInvariantsFromBrief(await readFile(ultragoalBriefPath(cwd), 'utf-8'));
1039
+ const steeringInvariants = extractArchitectureInvariantsFromAcceptedSteering(await readSteeringLedgerEntries(cwd));
1040
+ return uniqueRequiredArchitectureInvariants([...briefInvariants, ...steeringInvariants]);
1041
+ }
1042
+ function validateArchitectureInvariantGate(gate, requiredInvariants) {
1043
+ const invariantGate = gate.architectureInvariantGate;
1044
+ if (!invariantGate || typeof invariantGate !== 'object') {
1045
+ throw new UltragoalError('Final quality gate is missing architectureInvariantGate evidence; include derived architecture/domain invariants, source artifacts, implementation/test/review evidence, or record final blockers for unproved invariants.');
1046
+ }
1047
+ if (invariantGate.status !== 'passed') {
1048
+ throw new UltragoalError('Final architecture-invariant gate requires architectureInvariantGate.status="passed"; record blocker-resolution work for unproved invariants.');
1049
+ }
1050
+ if (!Array.isArray(invariantGate.sourceArtifacts)) {
1051
+ throw new UltragoalError('Final architecture-invariant gate requires architectureInvariantGate.sourceArtifacts.');
1052
+ }
1053
+ const sourceArtifacts = invariantGate.sourceArtifacts.map((source) => assertNonEmpty(source, 'architectureInvariantGate.sourceArtifacts[]'));
1054
+ for (const required of requiredInvariants) {
1055
+ if (!sourceArtifacts.some((source) => sourceReferencesArtifact(source, required.sourceArtifact))) {
1056
+ throw new UltragoalError(`Final architecture-invariant gate sourceArtifacts must include required invariant source artifact: ${required.sourceArtifact}`);
1057
+ }
1058
+ }
1059
+ assertNonEmpty(invariantGate.evidence, 'architectureInvariantGate.evidence');
1060
+ if (!Array.isArray(invariantGate.invariants)) {
1061
+ throw new UltragoalError('Final architecture-invariant gate requires architectureInvariantGate.invariants.');
1062
+ }
1063
+ const provided = new Map();
1064
+ for (const invariant of invariantGate.invariants) {
1065
+ if (!invariant || typeof invariant !== 'object')
1066
+ throw new UltragoalError('Final architecture-invariant gate invariants must be objects.');
1067
+ const record = invariant;
1068
+ const text = assertNonEmpty(record.invariant, 'architectureInvariantGate.invariants[].invariant');
1069
+ const source = assertNonEmpty(record.source, 'architectureInvariantGate.invariants[].source');
1070
+ if (!sourceReferencesAnyArtifact(source, sourceArtifacts)) {
1071
+ throw new UltragoalError(`Final architecture invariant "${text}" source must reference one of architectureInvariantGate.sourceArtifacts; decorative provenance labels are not sufficient.`);
1072
+ }
1073
+ if (record.status !== 'proved')
1074
+ throw new UltragoalError(`Final architecture invariant "${text}" is not proved; record blocker-resolution work before final completion.`);
1075
+ if (record.blockers !== undefined)
1076
+ throw new UltragoalError(`Final architecture invariant "${text}" has blockers; record blocker-resolution work before final completion.`);
1077
+ assertNonEmpty(record.implementationEvidence, 'architectureInvariantGate.invariants[].implementationEvidence');
1078
+ assertNonEmpty(record.testEvidence, 'architectureInvariantGate.invariants[].testEvidence');
1079
+ assertNonEmpty(record.reviewEvidence, 'architectureInvariantGate.invariants[].reviewEvidence');
1080
+ const key = normalizeInvariantText(text);
1081
+ const records = provided.get(key) ?? [];
1082
+ records.push(record);
1083
+ provided.set(key, records);
1084
+ }
1085
+ for (const required of requiredInvariants) {
1086
+ const matches = provided.get(normalizeInvariantText(required.invariant)) ?? [];
1087
+ if (matches.length === 0) {
1088
+ throw new UltragoalError(`Final architecture-invariant gate is missing proof for required invariant from ${required.sourceArtifact}: ${required.invariant}`);
1089
+ }
1090
+ if (!matches.some((record) => sourceReferencesArtifact(record.source, required.sourceArtifact))) {
1091
+ throw new UltragoalError(`Final architecture-invariant gate proof for required invariant must reference ${required.sourceArtifact}: ${required.invariant}`);
1092
+ }
1093
+ }
1094
+ }
1095
+ function validateQualityGate(value, requiredInvariants = []) {
913
1096
  if (!value || typeof value !== 'object') {
914
- throw new UltragoalError('Final ultragoal completion requires --quality-gate-json with ai-slop-cleaner, verification, and code-review evidence.');
1097
+ throw new UltragoalError('Final ultragoal completion requires --quality-gate-json with ai-slop-cleaner, verification, code-review, and architecture-invariant evidence.');
915
1098
  }
916
1099
  const gate = value;
917
1100
  const cleaner = gate.aiSlopCleaner;
@@ -960,6 +1143,7 @@ function validateQualityGate(value) {
960
1143
  throw new UltragoalError('Final code-review must use an independent architect subagent; self-review or default/authoring-lane review cannot approve the ultragoal gate.');
961
1144
  }
962
1145
  assertNonEmpty(architect.evidence, 'codeReview.independentReview.architect.evidence');
1146
+ validateArchitectureInvariantGate(gate, requiredInvariants);
963
1147
  return gate;
964
1148
  }
965
1149
  export async function startNextUltragoal(cwd, options = {}) {
@@ -1059,6 +1243,7 @@ export async function checkpointUltragoal(cwd, options) {
1059
1243
  return plan;
1060
1244
  }
1061
1245
  let aggregateCompletion;
1246
+ let normalFinalAggregateCompletion;
1062
1247
  if (options.status === 'complete') {
1063
1248
  const expectedObjective = expectedCodexObjective(plan, goal);
1064
1249
  const aggregateMode = codexGoalMode(plan) === 'aggregate';
@@ -1080,12 +1265,19 @@ export async function checkpointUltragoal(cwd, options) {
1080
1265
  && normalizeObjective(snapshot.objective ?? '') !== normalizeObjective(expectedObjective)
1081
1266
  && await canReconcileCompletedTaskScopedAggregateSnapshot(cwd, plan, goal, snapshot.objective ?? '', options.evidence);
1082
1267
  if (completedTaskScopedAggregateSnapshot) {
1083
- aggregateCompletion = {
1084
- status: 'complete',
1085
- completedAt: now,
1086
- evidence: assertNonEmpty(options.evidence, '--evidence'),
1087
- codexGoal: options.codexGoal,
1088
- };
1268
+ if (unresolvedReviewBlockedGoals(plan).length > 0) {
1269
+ if (!canUseCleanFinalResolverPathForReviewBlockedParent(plan, goal, finalRunCheckpoint, options.allowActiveFinalCodexGoal)) {
1270
+ throw new UltragoalError('Completed task-scoped aggregate reconciliation is not allowed while unresolved review_blocked parent goals exist; only the parent\'s designated resolver may continue through the clean final quality gate path.');
1271
+ }
1272
+ }
1273
+ else {
1274
+ aggregateCompletion = {
1275
+ status: 'complete',
1276
+ completedAt: now,
1277
+ evidence: assertNonEmpty(options.evidence, '--evidence'),
1278
+ codexGoal: options.codexGoal,
1279
+ };
1280
+ }
1089
1281
  }
1090
1282
  else {
1091
1283
  const taskScopedRequirement = aggregateMode && snapshot?.status === 'complete' && Boolean(snapshot.objective)
@@ -1102,11 +1294,25 @@ export async function checkpointUltragoal(cwd, options) {
1102
1294
  throw new UltragoalError(`${formatCodexGoalReconciliation(reconciliation)}${taskScopedRequirement}${remediation}`);
1103
1295
  }
1104
1296
  }
1297
+ const designatedReviewBlockerResolver = goal.resolvesReviewBlockedGoalId
1298
+ ? isDesignatedReviewBlockerResolver(goal, plan.goals.find((candidate) => candidate.id === goal.resolvesReviewBlockedGoalId))
1299
+ : false;
1300
+ if (aggregateMode && finalRunCheckpoint && !options.allowActiveFinalCodexGoal && designatedReviewBlockerResolver) {
1301
+ normalFinalAggregateCompletion = {
1302
+ status: 'complete',
1303
+ completedAt: now,
1304
+ evidence: assertNonEmpty(options.evidence, '--evidence'),
1305
+ codexGoal: options.codexGoal,
1306
+ };
1307
+ }
1105
1308
  if (finalRunCheckpoint && !options.allowActiveFinalCodexGoal)
1106
1309
  goal.evidence = options.evidence;
1107
1310
  }
1311
+ const requiredArchitectureInvariants = options.status === 'complete' && (aggregateCompletion !== undefined || (isFinalRunCompletionCandidate(plan, goal) && !options.allowActiveFinalCodexGoal))
1312
+ ? await collectRequiredArchitectureInvariants(cwd)
1313
+ : [];
1108
1314
  const qualityGate = options.status === 'complete' && (aggregateCompletion !== undefined || (isFinalRunCompletionCandidate(plan, goal) && !options.allowActiveFinalCodexGoal))
1109
- ? validateQualityGate(options.qualityGate)
1315
+ ? validateQualityGate(options.qualityGate, requiredArchitectureInvariants)
1110
1316
  : undefined;
1111
1317
  if (aggregateCompletion) {
1112
1318
  plan.aggregateCompletion = aggregateCompletion;
@@ -1134,6 +1340,23 @@ export async function checkpointUltragoal(cwd, options) {
1134
1340
  goal.failureReason = undefined;
1135
1341
  goal.failedAt = undefined;
1136
1342
  clearGoalBlockerFields(goal);
1343
+ if (normalFinalAggregateCompletion)
1344
+ plan.aggregateCompletion = normalFinalAggregateCompletion;
1345
+ const resolvedParent = goal.resolvesReviewBlockedGoalId
1346
+ ? plan.goals.find((candidate) => candidate.id === goal.resolvesReviewBlockedGoalId)
1347
+ : undefined;
1348
+ if (resolvedParent?.status === 'review_blocked' && resolvedParent.reviewBlockerResolution?.resolverGoalId === goal.id && qualityGate) {
1349
+ resolvedParent.status = 'complete';
1350
+ resolvedParent.completedAt = now;
1351
+ resolvedParent.updatedAt = now;
1352
+ resolvedParent.reviewBlockerResolution = {
1353
+ resolverGoalId: goal.id,
1354
+ status: 'complete',
1355
+ resolvedAt: now,
1356
+ evidence: options.evidence,
1357
+ };
1358
+ clearGoalBlockerFields(resolvedParent);
1359
+ }
1137
1360
  if (plan.activeGoalId === goal.id)
1138
1361
  delete plan.activeGoalId;
1139
1362
  }
@@ -1173,6 +1396,33 @@ export async function checkpointUltragoal(cwd, options) {
1173
1396
  ? `Blocked on repeated external authorization. Required decision: ${goal.requiredExternalDecision}.`
1174
1397
  : undefined,
1175
1398
  });
1399
+ if (options.status === 'complete' && goal.resolvesReviewBlockedGoalId) {
1400
+ const resolvedParent = plan.goals.find((candidate) => candidate.id === goal.resolvesReviewBlockedGoalId);
1401
+ if (resolvedParent?.reviewBlockerResolution?.status === 'complete' && resolvedParent.reviewBlockerResolution.resolverGoalId === goal.id) {
1402
+ await appendLedger(cwd, {
1403
+ ts: now,
1404
+ event: 'goal_completed',
1405
+ goalId: resolvedParent.id,
1406
+ status: resolvedParent.status,
1407
+ evidence: options.evidence,
1408
+ codexGoal: options.codexGoal,
1409
+ qualityGate,
1410
+ message: `Review-blocked final story resolved by ${goal.id}; original failed review remains in prior final_review_failed/goal_review_blocked ledger entries.`,
1411
+ });
1412
+ }
1413
+ }
1414
+ if (normalFinalAggregateCompletion) {
1415
+ await appendLedger(cwd, {
1416
+ ts: now,
1417
+ event: 'aggregate_completed',
1418
+ goalId: goal.id,
1419
+ status: goal.status,
1420
+ evidence: options.evidence,
1421
+ codexGoal: options.codexGoal,
1422
+ qualityGate,
1423
+ message: 'Aggregate ultragoal plan completed with a clean final quality gate.',
1424
+ });
1425
+ }
1176
1426
  return plan;
1177
1427
  });
1178
1428
  }
@@ -1202,7 +1452,7 @@ export async function recordFinalReviewBlockers(cwd, options) {
1202
1452
  if (!reconciliation.ok) {
1203
1453
  throw new UltragoalError(formatCodexGoalReconciliation(reconciliation));
1204
1454
  }
1205
- const addedGoal = appendGoalToPlan(plan, { ...options, now: options.now });
1455
+ const addedGoal = appendGoalToPlan(plan, { ...options, now: options.now, resolvesReviewBlockedGoalId: goal.id });
1206
1456
  goal.status = 'review_blocked';
1207
1457
  goal.reviewBlockedAt = now;
1208
1458
  goal.updatedAt = now;
@@ -1210,6 +1460,11 @@ export async function recordFinalReviewBlockers(cwd, options) {
1210
1460
  goal.failedAt = undefined;
1211
1461
  goal.failureReason = undefined;
1212
1462
  goal.evidence = options.evidence;
1463
+ goal.reviewBlockerResolution = {
1464
+ resolverGoalId: addedGoal.id,
1465
+ status: 'pending',
1466
+ evidence: options.evidence,
1467
+ };
1213
1468
  if (plan.activeGoalId === goal.id)
1214
1469
  delete plan.activeGoalId;
1215
1470
  plan.updatedAt = now;
@@ -1263,6 +1518,7 @@ function buildPerStoryCodexGoalInstruction(goal, plan) {
1263
1518
  '',
1264
1519
  'Codex goal integration constraints:',
1265
1520
  '- First call get_goal. If no active goal exists, call create_goal with the payload below.',
1521
+ `- If get_goal reports status complete before create_goal, do not call create_goal over it. ${buildCompletedCodexGoalRemediation('Ultragoal preflight')}`,
1266
1522
  '- If a different active Codex goal exists, finish/checkpoint that goal before starting this ultragoal.',
1267
1523
  '- Ultragoal cannot call /goal clear from the model/shell tool surface. For another per-story goal in the same session/thread after a completed Codex goal, manually run /goal clear in the Codex UI before creating the next goal.',
1268
1524
  '- If get_goal returns a different completed legacy/thread goal and create_goal rejects because this thread already has a completed goal, continue only from a Codex goal context with no active/completed conflicting goal in the same repo/worktree and create the payload there.',
@@ -1289,6 +1545,9 @@ function buildPerStoryCodexGoalInstruction(goal, plan) {
1289
1545
  finalStory
1290
1546
  ? ` omx ultragoal checkpoint --goal-id ${goal.id} --status complete --evidence "<tests/files/PR evidence>" --codex-goal-json "<fresh complete get_goal JSON or path>" --quality-gate-json "<quality gate JSON or path>"`
1291
1547
  : null,
1548
+ finalStory
1549
+ ? '- After the final checkpoint command succeeds, treat `/goal clear` as the explicit terminal cleanup step before another same-thread goal.'
1550
+ : null,
1292
1551
  '- If blocked or failed, checkpoint with --status failed and the failure evidence; rerun complete-goals --retry-failed to resume.',
1293
1552
  '',
1294
1553
  'create_goal payload:',
@@ -1313,6 +1572,7 @@ function buildAggregateCodexGoalInstruction(goal, plan) {
1313
1572
  '- Codex goal = the whole ultragoal run; OMX G001/G002/etc. = ledger stories.',
1314
1573
  '- First call get_goal. If no active goal exists, call create_goal with the aggregate payload below.',
1315
1574
  '- If get_goal reports the same aggregate objective as active, continue this OMX story without creating a new Codex goal.',
1575
+ `- If get_goal reports status complete before create_goal, do not call create_goal over it. ${buildCompletedCodexGoalRemediation('Ultragoal preflight')}`,
1316
1576
  '- If a different active or incomplete Codex goal exists, finish/checkpoint that goal before starting this ultragoal; do not replace hidden Codex state from the shell.',
1317
1577
  '- Ultragoal does not call /goal clear. After a completed aggregate run, manually run /goal clear in the Codex UI before starting another ultragoal run in the same session/thread.',
1318
1578
  finalStory
@@ -1330,6 +1590,9 @@ function buildAggregateCodexGoalInstruction(goal, plan) {
1330
1590
  finalStory
1331
1591
  ? '- If final $code-review is clean (APPROVE + CLEAR + independent code-reviewer and architect subagent evidence), call update_goal({status: "complete"}), call get_goal again for a fresh complete snapshot, then checkpoint with --quality-gate-json.'
1332
1592
  : null,
1593
+ finalStory
1594
+ ? '- After the final checkpoint command succeeds, treat `/goal clear` as the explicit terminal cleanup step before another same-thread goal.'
1595
+ : null,
1333
1596
  `- Checkpoint this OMX story with a fresh get_goal snapshot whose objective matches the aggregate payload and whose status is ${checkpointStatus}:`,
1334
1597
  finalStory
1335
1598
  ? ` omx ultragoal checkpoint --goal-id ${goal.id} --status complete --evidence "<tests/files/PR evidence>" --codex-goal-json "<fresh complete get_goal JSON or path>" --quality-gate-json "<quality gate JSON or path>"`