oh-my-opencode 4.16.2 → 4.17.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 (234) hide show
  1. package/.agents/command/omomomo.md +1 -1
  2. package/.agents/skills/hyperplan/SKILL.md +3 -3
  3. package/.agents/skills/omomomo/SKILL.md +1 -1
  4. package/.agents/skills/pre-publish-review/SKILL.md +3 -0
  5. package/.opencode/command/omomomo.md +1 -1
  6. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  7. package/.opencode/skills/pre-publish-review/SKILL.md +3 -0
  8. package/README.ja.md +4 -4
  9. package/README.ko.md +4 -4
  10. package/README.md +4 -4
  11. package/README.ru.md +4 -4
  12. package/README.zh-cn.md +4 -4
  13. package/dist/agents/hephaestus/agent.d.ts +1 -1
  14. package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
  15. package/dist/agents/momus-gpt-5-6.d.ts +1 -0
  16. package/dist/agents/types.d.ts +2 -0
  17. package/dist/cli/index.js +947 -567
  18. package/dist/cli-node/index.js +947 -567
  19. package/dist/features/background-agent/error-classifier.d.ts +5 -0
  20. package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
  21. package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
  22. package/dist/features/team-mode/test-support/async-test-helpers.d.ts +2 -2
  23. package/dist/index.js +523 -81
  24. package/dist/plugin/chat-message/types.d.ts +4 -0
  25. package/dist/plugin/command-execute-before.d.ts +1 -0
  26. package/dist/plugin/stop-continuation.d.ts +17 -0
  27. package/dist/shared/live-server-route.d.ts +1 -1
  28. package/dist/skills/remove-ai-slops/SKILL.md +2 -2
  29. package/dist/skills/review-work/SKILL.md +1 -1
  30. package/dist/skills/start-work/SKILL.md +5 -2
  31. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  32. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  33. package/dist/skills/ulw-research/SKILL.md +2 -2
  34. package/dist/skills/visual-qa/SKILL.md +1 -1
  35. package/dist/tui.js +195 -41
  36. package/package.json +15 -14
  37. package/packages/git-bash-mcp/package.json +27 -0
  38. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +3 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +465 -248
  40. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  44. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +2 -2
  48. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +6 -2
  49. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +6 -2
  50. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +2 -2
  51. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  52. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +6 -2
  53. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/cli.test.ts +1 -1
  54. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +67 -2
  55. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  56. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +0 -13
  58. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
  59. package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
  60. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  61. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +2 -1
  63. package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
  64. package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
  65. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
  66. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
  67. package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
  68. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  69. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -79
  71. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
  72. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  73. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  74. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +136 -76
  75. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
  76. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
  77. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
  78. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
  79. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
  80. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  81. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  82. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +23 -40
  83. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
  84. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +3 -3
  85. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +7 -7
  86. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +5 -5
  87. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +26 -0
  88. package/packages/omo-codex/plugin/components/ultrawork/agents/{lazycodex-executor.toml → lazycodex-worker-low.toml} +7 -5
  89. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +26 -0
  90. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +20 -48
  91. package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
  92. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
  93. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +22 -43
  94. package/packages/omo-codex/plugin/components/ultrawork/directive.md +55 -28
  95. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  96. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  97. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +55 -28
  98. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  99. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  100. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -0
  101. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -68
  102. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -1
  103. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +2 -0
  104. package/packages/omo-codex/plugin/components/ulw-loop/README.md +3 -1
  105. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +55 -28
  106. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +6 -1
  107. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +1 -1
  108. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -2
  109. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +405 -25
  110. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +12 -1
  111. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +4 -2
  112. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +7 -0
  113. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +16 -1
  114. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -0
  115. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.d.ts +6 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +20 -0
  117. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +1 -0
  118. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +12 -9
  119. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.d.ts +3 -0
  120. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +148 -0
  121. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.d.ts +2 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +209 -0
  123. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +25 -2
  124. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  125. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +14 -15
  126. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +29 -27
  127. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +6 -1
  128. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +14 -3
  129. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +10 -0
  130. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +12 -1
  131. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +4 -2
  132. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +27 -1
  133. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -0
  134. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +23 -0
  135. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +16 -9
  136. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +138 -0
  137. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +208 -0
  138. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +12 -0
  139. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +4 -1
  140. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +8 -35
  141. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +24 -13
  142. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -2
  143. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +43 -8
  144. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +55 -2
  145. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +228 -0
  146. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +193 -0
  147. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +18 -0
  157. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  158. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  159. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  160. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +17 -0
  165. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +2 -2
  167. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  170. package/packages/omo-codex/plugin/model-catalog.json +16 -7
  171. package/packages/omo-codex/plugin/package-lock.json +13 -13
  172. package/packages/omo-codex/plugin/package.json +1 -1
  173. package/packages/omo-codex/plugin/scripts/auto-update.mjs +9 -2
  174. package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
  175. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
  176. package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +16 -7
  177. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +39 -4
  178. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +24 -4
  179. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +3 -3
  180. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
  181. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
  182. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -6
  183. package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
  184. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +3 -3
  185. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +3 -3
  186. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +5 -5
  187. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +9 -3
  188. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +6 -3
  189. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
  190. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
  191. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
  192. package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
  193. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
  194. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +55 -28
  195. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +14 -15
  196. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +29 -27
  197. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  198. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  199. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +4 -4
  200. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +4 -4
  201. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +116 -10
  202. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +23 -2
  203. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -1
  204. package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +4 -4
  205. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
  206. package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
  207. package/packages/omo-codex/plugin/test/auto-update.test.mjs +4 -4
  208. package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +2 -2
  209. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +4 -101
  210. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +160 -23
  211. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
  212. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +32 -0
  213. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +20 -2
  214. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -1
  215. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -2
  216. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
  217. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +9 -40
  218. package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +4 -277
  219. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +5 -5
  220. package/packages/omo-codex/scripts/install-config.test.mjs +22 -3
  221. package/packages/omo-codex/scripts/install-dist/install-local.mjs +508 -286
  222. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
  223. package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +2 -2
  224. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  225. package/packages/shared-skills/skills/start-work/SKILL.md +5 -2
  226. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  227. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  228. package/packages/shared-skills/skills/ulw-research/SKILL.md +2 -2
  229. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  230. package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  231. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +0 -70
  232. package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +0 -98
  233. package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  234. /package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +0 -0
@@ -54,8 +54,8 @@ test("#given stale root reasoning config #when ensuring config #then replaces st
54
54
  assert.equal(result.match(/^model_context_window\s*=/gm)?.length, 1);
55
55
  assert.equal(result.match(/^model_reasoning_effort\s*=/gm)?.length, 1);
56
56
  assert.equal(result.match(/^plan_mode_reasoning_effort\s*=/gm)?.length, 1);
57
- assert.match(result, /model = "gpt-5\.5"/);
58
- assert.match(result, /model_context_window = 400000/);
57
+ assert.match(result, /model = "gpt-5\.6-sol"/);
58
+ assert.match(result, /model_context_window = 372000/);
59
59
  assert.match(result, /model_reasoning_effort = "high"/);
60
60
  assert.match(result, /plan_mode_reasoning_effort = "xhigh"/);
61
61
  assert.doesNotMatch(result, /gpt-5\.2/);
@@ -79,8 +79,8 @@ test("#given section settings reuse managed root keys #when ensuring config #the
79
79
  ].join("\n"),
80
80
  );
81
81
 
82
- assert.match(result, /^model = "gpt-5\.5"$/m);
83
- assert.match(result, /^model_context_window = 400000$/m);
82
+ assert.match(result, /^model = "gpt-5\.6-sol"$/m);
83
+ assert.match(result, /^model_context_window = 372000$/m);
84
84
  assert.match(result, /\[model_providers\.openai\]\nmodel = "provider-scoped-value"\nmodel_context_window = 123456/);
85
85
  assert.match(result, /\[profiles\.review\]\nmodel_reasoning_effort = "medium"\nplan_mode_reasoning_effort = "medium"/);
86
86
  });
@@ -161,8 +161,8 @@ test("#given global and project-local stale Codex configs #when migrating #then
161
161
  });
162
162
 
163
163
  assert.deepEqual(result.changed.sort(), [join(codexHome, "config.toml"), projectConfig].sort());
164
- assert.match(await readFile(join(codexHome, "config.toml"), "utf8"), /model = "gpt-5\.5"/);
165
- assert.match(await readFile(projectConfig, "utf8"), /model_context_window = 400000/);
164
+ assert.match(await readFile(join(codexHome, "config.toml"), "utf8"), /model = "gpt-5\.6-sol"/);
165
+ assert.match(await readFile(projectConfig, "utf8"), /model_context_window = 372000/);
166
166
  });
167
167
 
168
168
  test("#given model catalog is unavailable and stale 272k config #when migrating #then fallback catalog still upgrades it", async () => {
@@ -183,8 +183,8 @@ test("#given model catalog is unavailable and stale 272k config #when migrating
183
183
 
184
184
  const content = await readFile(join(codexHome, "config.toml"), "utf8");
185
185
  assert.deepEqual(result.changed, [join(codexHome, "config.toml")]);
186
- assert.match(content, /model = "gpt-5\.5"/);
187
- assert.match(content, /model_context_window = 400000/);
186
+ assert.match(content, /model = "gpt-5\.6-sol"/);
187
+ assert.match(content, /model_context_window = 372000/);
188
188
  });
189
189
 
190
190
  test("#given model catalog is malformed and stale config #when migrating #then fallback catalog still upgrades it", async () => {
@@ -206,8 +206,8 @@ test("#given model catalog is malformed and stale config #when migrating #then f
206
206
 
207
207
  const content = await readFile(join(codexHome, "config.toml"), "utf8");
208
208
  assert.deepEqual(result.changed, [join(codexHome, "config.toml")]);
209
- assert.match(content, /model = "gpt-5\.5"/);
210
- assert.match(content, /model_context_window = 400000/);
209
+ assert.match(content, /model = "gpt-5\.6-sol"/);
210
+ assert.match(content, /model_context_window = 372000/);
211
211
  });
212
212
 
213
213
  test("#given user-customized Codex model config #when migrating #then user values are preserved without root multi-agent mode", async () => {
@@ -262,7 +262,7 @@ test("#given managed config state is malformed #when migrating #then migration i
262
262
  const content = await readFile(join(codexHome, "config.toml"), "utf8");
263
263
  const state = JSON.parse(await readFile(statePath, "utf8"));
264
264
  assert.deepEqual(result.changed, [join(codexHome, "config.toml")]);
265
- assert.match(content, /model_context_window = 400000/);
265
+ assert.match(content, /model_context_window = 372000/);
266
266
  assert.equal(state.files[join(codexHome, "config.toml")].managed, true);
267
267
  });
268
268
 
@@ -670,15 +670,15 @@ test("#given global config starts with inline-comment features table #when full
670
670
  const content = await readFile(configPath, "utf8");
671
671
  const parsed = parseTomlWithPython(content);
672
672
  assert.equal("multi_agent_mode" in parsed, false);
673
- assert.equal(parsed.model, "gpt-5.5");
674
- assert.equal(parsed.model_context_window, 400000);
673
+ assert.equal(parsed.model, "gpt-5.6-sol");
674
+ assert.equal(parsed.model_context_window, 372000);
675
675
  assert.equal(parsed.model_reasoning_effort, "high");
676
676
  assert.equal(parsed.plan_mode_reasoning_effort, "xhigh");
677
677
  assert.equal(parsed.features.plugins, true);
678
678
  assert.equal("multi_agent_mode" in parsed.features, false);
679
679
  assert.equal("model" in parsed.features, false);
680
680
  assert.equal("model_context_window" in parsed.features, false);
681
- assert.match(content, /^model = "gpt-5\.5"\nmodel_context_window = 400000/m);
681
+ assert.match(content, /^model = "gpt-5\.6-sol"\nmodel_context_window = 372000/m);
682
682
  assert.doesNotMatch(content, /^\s*multi_agent_mode\s*=/m);
683
683
  assert.match(content, /\[features\] # keep comment\nplugins = true/);
684
684
  });
@@ -756,7 +756,7 @@ test("#given global config with forced multi_agent_v2 #when full migration runs
756
756
  test("#given enabled = true with an inline comment #when forcing disable #then flips to false and preserves the comment", () => {
757
757
  const config = ["[features.multi_agent_v2]", "enabled = true # tuned by me", ""].join("\n");
758
758
 
759
- const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
759
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
760
760
 
761
761
  assert.match(result, /^enabled = false # tuned by me$/m);
762
762
  assert.doesNotMatch(result, /enabled = true/);
@@ -767,7 +767,7 @@ test("#given enabled = true with an inline comment #when forcing disable #then f
767
767
  test("#given a section header with an inline comment #when forcing disable #then patches in place without duplicating the table", () => {
768
768
  const config = ["[features.multi_agent_v2] # pinned by me", "enabled = true", ""].join("\n");
769
769
 
770
- const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
770
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
771
771
 
772
772
  assert.equal((result.match(/\[features\.multi_agent_v2\]/g) ?? []).length, 1);
773
773
  assert.match(result, /enabled = false/);
@@ -779,19 +779,19 @@ test("#given an already-guarded commented config #when re-running #then output i
779
779
  const configA = ["[features.multi_agent_v2]", "enabled = true # tuned by me", ""].join("\n");
780
780
  const configB = ["[features.multi_agent_v2] # pinned by me", "enabled = true", ""].join("\n");
781
781
 
782
- const firstA = forceDisableMultiAgentV2(configA, { multiAgentVersion: null });
783
- const rerunA = forceDisableMultiAgentV2(firstA, { multiAgentVersion: null });
782
+ const firstA = forceDisableMultiAgentV2(configA, { multiAgentVersion: "v1" });
783
+ const rerunA = forceDisableMultiAgentV2(firstA, { multiAgentVersion: "v1" });
784
784
  assert.equal(rerunA, firstA);
785
785
 
786
- const firstB = forceDisableMultiAgentV2(configB, { multiAgentVersion: null });
787
- const rerunB = forceDisableMultiAgentV2(firstB, { multiAgentVersion: null });
786
+ const firstB = forceDisableMultiAgentV2(configB, { multiAgentVersion: "v1" });
787
+ const rerunB = forceDisableMultiAgentV2(firstB, { multiAgentVersion: "v1" });
788
788
  assert.equal(rerunB, firstB);
789
789
  });
790
790
 
791
791
  test("#given user-disabled with an inline comment #when forcing disable #then returns config unchanged", () => {
792
792
  const config = ["[features.multi_agent_v2]", "enabled = false # I turned this off myself", ""].join("\n");
793
793
 
794
- const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
794
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
795
795
 
796
796
  assert.equal(result, config);
797
797
  });
@@ -799,7 +799,7 @@ test("#given user-disabled with an inline comment #when forcing disable #then re
799
799
  test("#given [features] shorthand true with an inline comment #when forcing disable #then removes the shorthand and appends one disabled table", () => {
800
800
  const config = ["[features]", "plugins = true", "multi_agent_v2 = true # legacy", ""].join("\n");
801
801
 
802
- const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
802
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
803
803
 
804
804
  assert.doesNotMatch(result, /^\s*multi_agent_v2\s*=/m);
805
805
  assert.equal((result.match(/\[features\.multi_agent_v2\]/g) ?? []).length, 1);
@@ -816,7 +816,7 @@ test("#given a following section header with an inline comment #when inserting e
816
816
  "",
817
817
  ].join("\n");
818
818
 
819
- const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
819
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
820
820
 
821
821
  assert.match(result, /\[features\.multi_agent_v2\]\nenabled = false\n/);
822
822
  assert.match(result, /\[mcp_servers\.x\][^\n]*\nenabled = true/);
@@ -1034,6 +1034,41 @@ test("#given legacy shorthand and no session model on hook path #when full migra
1034
1034
  assert.equal("enabled" in parsed.features.multi_agent_v2, false);
1035
1035
  });
1036
1036
 
1037
+ test("#given SoT migration failure #when hook migration runs #then config.toml repair still happens", async () => {
1038
+ const { runAutoUpdateCheck } = await import("../scripts/auto-update.mjs");
1039
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-sot-isolation-"));
1040
+ const codexHome = join(root, "codex-home");
1041
+ await mkdir(codexHome, { recursive: true });
1042
+ const configPath = join(codexHome, "config.toml");
1043
+ await writeFile(
1044
+ configPath,
1045
+ ['model = "gpt-5.6-sol"', "", "[features.multi_agent_v2]", "enabled = false", ""].join("\n"),
1046
+ );
1047
+ await writeFile(
1048
+ join(codexHome, "models_cache.json"),
1049
+ JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v2" }] }),
1050
+ );
1051
+ // HOME pointing at a FILE makes migrateOmoSotConfig's mkdir(~/.omo) throw.
1052
+ const brokenHome = join(root, "not-a-dir");
1053
+ await writeFile(brokenHome, "");
1054
+
1055
+ await runAutoUpdateCheck({
1056
+ env: {
1057
+ CODEX_HOME: codexHome,
1058
+ HOME: brokenHome,
1059
+ USERPROFILE: brokenHome,
1060
+ LAZYCODEX_AUTO_UPDATE_DISABLED: "1",
1061
+ LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json"),
1062
+ LAZYCODEX_AUTO_UPDATE_STATE_PATH: join(root, "state.json"),
1063
+ },
1064
+ sessionModel: "gpt-5.6-sol",
1065
+ requireSessionModel: true,
1066
+ });
1067
+
1068
+ const content = await readFile(configPath, "utf8");
1069
+ assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
1070
+ });
1071
+
1037
1072
  test("#given config default gpt-5.5 #when full migration gets SessionStart gpt-5.6-terra #then clears disable using session model", async () => {
1038
1073
  const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-session-model-"));
1039
1074
  const codexHome = join(root, "codex-home");
@@ -1078,6 +1113,47 @@ test("#given config default gpt-5.5 #when full migration gets SessionStart gpt-5
1078
1113
  assert.match(content, /max_concurrent_threads_per_session = 1000/);
1079
1114
  });
1080
1115
 
1116
+ test("#given no session model and no root model #when forcing disable #then leaves the enable state untouched", () => {
1117
+ const config = ["[features]", "plugins = true", "", "[features.multi_agent_v2]", "enabled = true", ""].join("\n");
1118
+
1119
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null, sessionModel: null });
1120
+
1121
+ assert.match(result, /enabled = true/);
1122
+ assert.doesNotMatch(result, /enabled = false/);
1123
+ assert.doesNotMatch(result, /openai\/codex#26753/);
1124
+ });
1125
+
1126
+ test("#given no session model and no root model #when config has no multi_agent_v2 section #then does not append a disable", () => {
1127
+ const config = ["[features]", "plugins = true", ""].join("\n");
1128
+
1129
+ const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null, sessionModel: null });
1130
+
1131
+ assert.equal(result, config);
1132
+ assert.doesNotMatch(result, /\[features\.multi_agent_v2\]/);
1133
+ });
1134
+
1135
+ test("#given user-modified config without root model #when full non-hook migration runs #then writes no disable and no new agents.max_threads", async () => {
1136
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-desktop-no-model-"));
1137
+ const codexHome = join(root, "codex-home");
1138
+ await mkdir(codexHome, { recursive: true });
1139
+ const configPath = join(codexHome, "config.toml");
1140
+ // Codex Desktop sessions select the model in the UI; config.toml has no root
1141
+ // `model`, so migration cannot prove the session is not a GPT-5.6
1142
+ // reserved-schema model (#6002).
1143
+ await writeFile(configPath, ['model_reasoning_effort = "high"', "", "[features]", "plugins = true", ""].join("\n"));
1144
+
1145
+ await migrateCodexConfig({
1146
+ env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
1147
+ cwd: root,
1148
+ });
1149
+
1150
+ const content = await readFile(configPath, "utf8");
1151
+ assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
1152
+ assert.doesNotMatch(content, /openai\/codex#26753/);
1153
+ assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
1154
+ assert.match(content, /max_concurrent_threads_per_session = 1000/);
1155
+ });
1156
+
1081
1157
  async function canCreateSymlink(type) {
1082
1158
  const root = await mkdtemp(join(tmpdir(), "lazycodex-symlink-capability-"));
1083
1159
  const target = join(root, "target");
@@ -1103,3 +1179,64 @@ async function canCreateSymlink(type) {
1103
1179
  return false;
1104
1180
  }
1105
1181
  }
1182
+
1183
+ test("#given model_catalog_json declares a v2 model as v1 #when full migration runs #then keeps the managed disable and max_threads", async () => {
1184
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-catalog-override-"));
1185
+ const codexHome = join(root, "codex-home");
1186
+ await mkdir(codexHome, { recursive: true });
1187
+ const configPath = join(codexHome, "config.toml");
1188
+ const catalogPath = join(root, "custom-catalog.json");
1189
+ // Codex Desktop user forces gpt-5.6-sol to v1 via an explicit replacement catalog.
1190
+ await writeFile(configPath, [
1191
+ 'model = "gpt-5.6-sol"',
1192
+ `model_catalog_json = "${catalogPath}"`,
1193
+ "",
1194
+ "[agents]",
1195
+ "max_threads = 1000",
1196
+ "max_depth = 2",
1197
+ "",
1198
+ "[features.multi_agent_v2]",
1199
+ "enabled = false",
1200
+ "max_concurrent_threads_per_session = 1000",
1201
+ "",
1202
+ ].join("\n"));
1203
+ // models_cache.json STILL says v2 (stale), but the explicit catalog wins and says v1.
1204
+ await writeFile(join(codexHome, "models_cache.json"), JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v2" }] }));
1205
+ await writeFile(catalogPath, JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v1" }] }));
1206
+
1207
+ await migrateCodexConfig({
1208
+ env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
1209
+ cwd: root,
1210
+ });
1211
+
1212
+ const content = await readFile(configPath, "utf8");
1213
+ assert.match(content, /enabled = false/, "explicit v1 catalog must keep the managed disable");
1214
+ assert.match(content, /max_threads = 1000/, "explicit v1 catalog must keep agents.max_threads");
1215
+ assert.match(content, /max_concurrent_threads_per_session = 1000/);
1216
+ assert.match(content, /max_depth = 2/);
1217
+ });
1218
+
1219
+ test("#given model_catalog_json declares a model as v2 #when full migration runs #then clears the managed disable", async () => {
1220
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-catalog-v2-"));
1221
+ const codexHome = join(root, "codex-home");
1222
+ await mkdir(codexHome, { recursive: true });
1223
+ const configPath = join(codexHome, "config.toml");
1224
+ const catalogPath = join(root, "custom-catalog.json");
1225
+ await writeFile(configPath, [
1226
+ 'model = "custom-model"',
1227
+ `model_catalog_json = "${catalogPath}"`,
1228
+ "",
1229
+ "[features.multi_agent_v2]",
1230
+ "enabled = false",
1231
+ "",
1232
+ ].join("\n"));
1233
+ await writeFile(catalogPath, JSON.stringify({ models: [{ slug: "custom-model", multi_agent_version: "v2" }] }));
1234
+
1235
+ await migrateCodexConfig({
1236
+ env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
1237
+ cwd: root,
1238
+ });
1239
+
1240
+ const content = await readFile(configPath, "utf8");
1241
+ assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m, "explicit v2 catalog must clear the disable");
1242
+ });
@@ -0,0 +1,71 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import test from "node:test";
6
+
7
+ import { migrateCodexConfig } from "../scripts/migrate-codex-config.mjs";
8
+
9
+ test("#given relative model_catalog_json declares gpt-5.6 model as v1 #when migrating #then resolves catalog from config directory", async () => {
10
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-v2-relative-catalog-"));
11
+ const codexHome = join(root, "codex-home");
12
+ await mkdir(codexHome, { recursive: true });
13
+ const configPath = join(codexHome, "config.toml");
14
+ await writeFile(
15
+ configPath,
16
+ [
17
+ 'model = "gpt-5.6-sol"',
18
+ 'model_catalog_json = "custom-catalog.json"',
19
+ "",
20
+ "[agents]",
21
+ "max_threads = 1000",
22
+ "",
23
+ "[features.multi_agent_v2]",
24
+ "enabled = false",
25
+ "max_concurrent_threads_per_session = 1000",
26
+ "",
27
+ ].join("\n"),
28
+ );
29
+ await writeFile(join(codexHome, "models_cache.json"), JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v2" }] }));
30
+ await writeFile(join(codexHome, "custom-catalog.json"), JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v1" }] }));
31
+
32
+ await migrateCodexConfig({
33
+ env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
34
+ cwd: root,
35
+ });
36
+
37
+ const content = await readFile(configPath, "utf8");
38
+ assert.match(content, /enabled = false/);
39
+ assert.match(content, /max_threads = 1000/);
40
+ });
41
+
42
+ test("#given no SessionStart model and root gpt-5.6 model without catalog #when migrating #then clears stale V2 disable", async () => {
43
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-v2-root-gpt56-nocache-"));
44
+ const codexHome = join(root, "codex-home");
45
+ await mkdir(codexHome, { recursive: true });
46
+ const configPath = join(codexHome, "config.toml");
47
+ await writeFile(
48
+ configPath,
49
+ [
50
+ 'model = "gpt-5.6-terra"',
51
+ "",
52
+ "[agents]",
53
+ "max_threads = 1000",
54
+ "",
55
+ "[features.multi_agent_v2]",
56
+ "enabled = false",
57
+ "max_concurrent_threads_per_session = 1000",
58
+ "",
59
+ ].join("\n"),
60
+ );
61
+
62
+ await migrateCodexConfig({
63
+ env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
64
+ cwd: root,
65
+ });
66
+
67
+ const content = await readFile(configPath, "utf8");
68
+ assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
69
+ assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
70
+ assert.match(content, /max_concurrent_threads_per_session = 1000/);
71
+ });
@@ -75,3 +75,35 @@ test("#given gpt-5.6 session model with no models_cache #when migrating #then do
75
75
  assert.match(content, /max_depth = 4/);
76
76
  assert.match(content, /max_concurrent_threads_per_session = 1000/);
77
77
  });
78
+
79
+ test("#given config without any model #when migrating #then does not introduce agents.max_threads", async () => {
80
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-no-model-"));
81
+ const configPath = join(root, "config.toml");
82
+ await writeFile(
83
+ configPath,
84
+ ['model_reasoning_effort = "high"', "", "[agents]", "max_depth = 4", ""].join("\n"),
85
+ );
86
+
87
+ await migrateConfigFile(configPath, { env: { CODEX_HOME: root } });
88
+
89
+ const content = await readFile(configPath, "utf8");
90
+ assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
91
+ assert.match(content, /max_depth = 4/);
92
+ assert.match(content, /max_concurrent_threads_per_session = 1000/);
93
+ });
94
+
95
+ test("#given config without any model but an existing low cap #when migrating #then still raises the existing cap", async () => {
96
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-no-model-raise-"));
97
+ const configPath = join(root, "config.toml");
98
+ await writeFile(
99
+ configPath,
100
+ ['model_reasoning_effort = "high"', "", "[agents]", "max_threads = 6", "max_depth = 4", ""].join("\n"),
101
+ );
102
+
103
+ await migrateConfigFile(configPath, { env: { CODEX_HOME: root } });
104
+
105
+ const content = await readFile(configPath, "utf8");
106
+ assert.match(content, /max_threads = 1000/);
107
+ assert.doesNotMatch(content, /max_threads = 6/);
108
+ assert.match(content, /max_depth = 4/);
109
+ });
@@ -3,7 +3,7 @@ import { readdir, readFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import test from "node:test";
5
5
  import { fileURLToPath } from "node:url";
6
- import { insertCodexCompatibilityGuidance } from "../scripts/sync-skills.mjs";
6
+ import { codexHarnessToolCompatibility, insertCodexCompatibilityGuidance } from "../scripts/sync-skills.mjs";
7
7
 
8
8
  const root = dirname(dirname(fileURLToPath(import.meta.url)));
9
9
 
@@ -207,7 +207,8 @@ test("#given synced aggregate Codex skills #when they describe background orches
207
207
  ["working progress message", /WORKING:/],
208
208
  ["blocked progress message", /BLOCKED:/],
209
209
  ["mailbox timeout framing", /timeout only means no new mailbox update arrived/],
210
- ["multi_agent_v1.wait_agent ref", /multi_agent_v1\.wait_agent/],
210
+ // Skills route by session tool surface: the namespaced V1 tool or the flat V2 tool both count.
211
+ ["wait-agent tool ref", /multi_agent_v1\.wait_agent|`wait_agent`/],
211
212
  ["explicit fallback conditions", /Fallback only when|Mark a file for retry only when/],
212
213
  ];
213
214
  const bannedPatterns = [
@@ -234,6 +235,15 @@ test("#given synced aggregate Codex skills #when they describe background orches
234
235
  }
235
236
  });
236
237
 
238
+ test("#given start-work skill #when synced for Codex #then the difficulty-tier delegation guidance survives the overlay", async () => {
239
+ const content = await readSkill("start-work");
240
+
241
+ assert.match(content, /lazycodex-worker-medium/);
242
+ assert.match(content, /Delegation by difficulty/);
243
+ assert.match(content, /Global Review and Debugging Gate/);
244
+ assert.doesNotMatch(content, /works the same on both surfaces/);
245
+ });
246
+
237
247
  test("#given review-work skill #when some lanes do not finish #then aggregate result remains bounded", async () => {
238
248
  const content = await readSkill("review-work");
239
249
 
@@ -244,6 +254,8 @@ test("#given review-work skill #when some lanes do not finish #then aggregate re
244
254
  assert.match(content, /Overall Verdict: PASSED \/ FAILED \/ INCONCLUSIVE/);
245
255
  assert.match(content, /PASS\/FAIL\/INCONCLUSIVE \| HIGH\/MED\/LOW/);
246
256
  assert.match(content, /Do not spin in repeated/);
257
+ assert.match(content, /bare REJECT\/FAIL token without findings is not a verdict/);
258
+ assert.match(content, /cites the violated goal criterion/);
247
259
  assert.match(content, /Do not use `multi_agent_v1\.send_input` as an interrupt/);
248
260
  });
249
261
 
@@ -265,3 +277,9 @@ test("#given PR and review skills #when synced for Codex #then worktree lifecycl
265
277
  assert.match(reviewWork, /dedicated review worktree attached to that branch/);
266
278
  assert.match(reviewWork, /Never\s+checkout, test, or edit the review branch in the main worktree/);
267
279
  });
280
+
281
+ test("#given generated Codex compatibility guidance #when multi-agent lifecycle tools are mentioned #then optional tools are guarded by the active tools list", () => {
282
+ assert.match(codexHarnessToolCompatibility, /when exposed in the active tools list/, "send_input/close_agent must be marked optional (lazycodex#116)");
283
+ assert.match(codexHarnessToolCompatibility, /multi_agent_v1\.spawn_agent/);
284
+ assert.match(codexHarnessToolCompatibility, /multi_agent_v1\.wait_agent/);
285
+ });
@@ -92,7 +92,7 @@ const startWorkOriginalHardRule = "- No completion claim while an applicable ult
92
92
  const startWorkCodexHardRule = "- No completion claim while an applicable ultraqa adversarial class was never probed. Each applicable class needs a captured observable result; each skipped class needs a one-line not-applicable reason in the ledger.\n- No `ORCHESTRATION COMPLETE`, final response, PR creation, PR handoff, or merge before the Global Review and Debugging Gate passes with recorded evidence.\n- No PR/branch implementation or review in the main worktree; create or use a task-owned git worktree first.\n- No unprefixed session ids in Boulder state. Codex sessions are always `codex:<session_id>`.";
93
93
 
94
94
  const reviewWorkCodexGatePattern =
95
- /\nWhen `review-work` is used as a final implementation, PR, or `\$start-work`\ngate, it is blocking\. A timeout, missing deliverable, ack-only response,\nexplicit `BLOCKED:`, or inconclusive lane is not a pass\. Treat that lane as\nfailed, investigate the underlying uncertainty with the `debugging` skill when\nruntime behavior may be wrong, fix with evidence, and rerun the affected lane\nbefore claiming completion, creating or handing off a PR, or merging\.\n\nWhen reviewing a PR or branch, collect diff, file contents, and verification\nresults from a dedicated review worktree attached to that branch\. Never\ncheckout, test, or edit the review branch in the main worktree\.\n\nReview evidence must be safe to share\. Redact or mask secrets and sensitive\nuser data before including evidence in logs, PR bodies, or handoffs\. Never\ninclude raw tokens, credentials, auth headers, cookies, API keys, env dumps,\nprivate logs, or PII; summarize with lengths, hashes, and short non-sensitive\nprefixes when identity is needed\.\n/;
95
+ /\nWhen `review-work` is used as a final implementation, PR, or `\$start-work`\ngate, it is blocking\. A timeout, missing deliverable, ack-only response,\nexplicit `BLOCKED:`, or inconclusive lane is not a pass\. Treat that lane as\nfailed, investigate the underlying uncertainty with the `debugging` skill when\nruntime behavior may be wrong, fix with evidence, and rerun the affected lane\nbefore claiming completion, creating or handing off a PR, or merging\.\n\nA rejecting lane must name its blockers inline in its final message — each\nblocker cites the violated goal criterion or requirement plus an evidence\npointer\. A bare REJECT\/FAIL token without findings is not a verdict; treat it\nas an inconclusive lane \(one bounded respawn, then record it inconclusive with\nthat reason\)\.\n\nWhen reviewing a PR or branch, collect diff, file contents, and verification\nresults from a dedicated review worktree attached to that branch\. Never\ncheckout, test, or edit the review branch in the main worktree\.\n\nReview evidence must be safe to share\. Redact or mask secrets and sensitive\nuser data before including evidence in logs, PR bodies, or handoffs\. Never\ninclude raw tokens, credentials, auth headers, cookies, API keys, env dumps,\nprivate logs, or PII; summarize with lengths, hashes, and short non-sensitive\nprefixes when identity is needed\.\n/;
96
96
 
97
97
  export function removeCodexSkillOverlays(skillName, content) {
98
98
  if (skillName === "ulw-research") {
@@ -252,13 +252,15 @@ test("#given synced ulw-loop skill #when worker guidance is inspected #then cont
252
252
  );
253
253
  const syncedSkill = await readFile(join(root, "skills", "ulw-loop", "SKILL.md"), "utf8");
254
254
  const syncedWorkflow = await readFile(join(root, "skills", "ulw-loop", "references", "full-workflow.md"), "utf8");
255
+ // ulw-loop is V2-primary (gpt-5.6 sol/terra use the flat `wait_agent`); the `multi_agent_v1.*`
256
+ // namespace is documented only as the v1 fallback, so the wait_agent refs accept the bare token.
255
257
  const requiredPatterns = [
256
- ["multi_agent_v1.wait_agent ref", /multi_agent_v1\.wait_agent/],
258
+ ["wait_agent ref", /\bwait_agent\b/],
257
259
  ["local spawned-name tracking", /Track spawned agent names locally/],
258
260
  ["wait_agent mailbox path", /wait_agent.*mailbox signals/],
259
261
  ["progress status contract", /WORKING:/],
260
262
  ["long-running plan/reviewer background guidance", /Plan and reviewer agents may run for a long time/],
261
- ["bounded plan/reviewer polling", /multi_agent_v1\.wait_agent.*cycles/],
263
+ ["bounded plan/reviewer polling", /wait_agent.*cycles/],
262
264
  ["single long wait guard", /single long blocking wait/],
263
265
  ["git-master checkpointing", /git-master/],
264
266
  ["touched-path commit-style probe", /touched-path commit history/],