oh-my-opencode 4.16.3 → 4.17.1

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 (220) hide show
  1. package/.agents/command/omomomo.md +1 -1
  2. package/.agents/command/publish.md +31 -58
  3. package/.agents/skills/hyperplan/SKILL.md +3 -3
  4. package/.agents/skills/omomomo/SKILL.md +1 -1
  5. package/.agents/skills/pre-publish-review/SKILL.md +1 -1
  6. package/.agents/skills/publish/SKILL.md +32 -59
  7. package/.opencode/command/omomomo.md +1 -1
  8. package/.opencode/command/publish.md +31 -58
  9. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  10. package/.opencode/skills/pre-publish-review/SKILL.md +4 -1
  11. package/README.ja.md +4 -4
  12. package/README.ko.md +4 -4
  13. package/README.md +2 -2
  14. package/README.ru.md +4 -4
  15. package/README.zh-cn.md +4 -4
  16. package/dist/cli/index.js +195 -76
  17. package/dist/cli-node/index.js +195 -76
  18. package/dist/features/team-mode/test-support/async-test-helpers.d.ts +2 -2
  19. package/dist/index.js +310 -299
  20. package/dist/plugin/chat-message/types.d.ts +4 -0
  21. package/dist/plugin/command-execute-before.d.ts +1 -0
  22. package/dist/plugin/stop-continuation.d.ts +17 -0
  23. package/dist/skills/remove-ai-slops/SKILL.md +2 -2
  24. package/dist/skills/review-work/SKILL.md +10 -2
  25. package/dist/skills/start-work/SKILL.md +6 -3
  26. package/dist/skills/ultimate-browsing/ATTRIBUTION.md +2 -2
  27. package/dist/skills/ultimate-browsing/engine/templates/package.json +1 -1
  28. package/dist/skills/ultimate-browsing/references/chrome-stealth.md +11 -11
  29. package/dist/skills/ulw-plan/SKILL.md +2 -1
  30. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  31. package/dist/skills/ulw-plan/references/intent-unclear.md +4 -4
  32. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  33. package/dist/skills/visual-qa/SKILL.md +9 -5
  34. package/dist/tui.js +79 -32
  35. package/package.json +15 -14
  36. package/packages/git-bash-mcp/package.json +27 -0
  37. package/packages/lsp-daemon/dist/cli.js +7 -13
  38. package/packages/lsp-daemon/dist/daemon-client.js +3 -5
  39. package/packages/lsp-daemon/dist/index.js +12 -18
  40. package/packages/lsp-daemon/dist/request-routing.js +6 -8
  41. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +3 -1
  42. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +1080 -1017
  43. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  44. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  46. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  47. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  48. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  49. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  50. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +2 -2
  51. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +6 -2
  52. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +6 -2
  53. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +2 -2
  54. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  55. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +6 -2
  56. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/cli.test.ts +1 -1
  57. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +67 -2
  58. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +14 -14
  59. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  60. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +0 -13
  62. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +2 -2
  63. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +3 -3
  64. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  65. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  66. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  67. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  68. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -79
  69. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +2 -2
  70. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  71. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  72. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +33 -16
  73. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +2 -1
  74. package/packages/omo-codex/plugin/components/teammode/test/v2-spawn-schema.test.ts +69 -0
  75. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  76. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +2 -2
  78. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
  79. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +6 -6
  80. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +5 -5
  81. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +26 -0
  82. package/packages/omo-codex/plugin/components/ultrawork/agents/{lazycodex-executor.toml → lazycodex-worker-low.toml} +6 -4
  83. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +26 -0
  84. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +2 -2
  85. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +7 -7
  86. package/packages/omo-codex/plugin/components/ultrawork/directive.md +76 -37
  87. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  88. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  89. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +76 -37
  90. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -1
  91. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  92. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-unclear.md +4 -4
  93. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  94. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +25 -0
  95. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -68
  96. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -1
  97. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +2 -0
  98. package/packages/omo-codex/plugin/components/ulw-loop/README.md +3 -1
  99. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +76 -37
  100. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +6 -1
  101. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +1 -1
  102. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -2
  103. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +405 -25
  104. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +12 -1
  105. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +4 -2
  106. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +7 -0
  107. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +16 -1
  108. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -0
  109. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.d.ts +6 -0
  110. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +20 -0
  111. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +1 -0
  112. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +12 -9
  113. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.d.ts +3 -0
  114. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +148 -0
  115. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.d.ts +2 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +209 -0
  117. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +25 -2
  118. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +14 -15
  120. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +30 -28
  121. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +6 -1
  122. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +14 -3
  123. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +10 -0
  124. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +12 -1
  125. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +4 -2
  126. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +27 -1
  127. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -0
  128. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +23 -0
  129. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +16 -9
  130. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +138 -0
  131. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +208 -0
  132. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +12 -0
  133. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +4 -1
  134. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +8 -35
  135. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +24 -13
  136. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -2
  137. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +43 -8
  138. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +55 -2
  139. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +228 -0
  140. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +193 -0
  141. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  142. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  143. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  144. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  145. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  146. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  147. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +18 -0
  151. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  157. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  158. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +17 -0
  159. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  160. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +2 -2
  161. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  164. package/packages/omo-codex/plugin/model-catalog.json +16 -7
  165. package/packages/omo-codex/plugin/package-lock.json +13 -13
  166. package/packages/omo-codex/plugin/package.json +1 -1
  167. package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +16 -7
  168. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +8 -2
  169. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +2 -2
  170. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +2 -2
  171. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +4 -4
  172. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +18 -4
  173. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +6 -3
  174. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +33 -16
  175. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +2 -1
  176. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +2 -2
  177. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +1 -1
  178. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +11 -11
  179. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +76 -37
  180. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +14 -15
  181. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +30 -28
  182. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
  183. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  184. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-unclear.md +4 -4
  185. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  186. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +2 -2
  187. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +11 -7
  188. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +76 -16
  189. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +23 -2
  190. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -1
  191. package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +4 -4
  192. package/packages/omo-codex/plugin/test/auto-update.test.mjs +4 -4
  193. package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +2 -2
  194. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +4 -101
  195. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +14 -14
  196. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +11 -0
  197. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -1
  198. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +5 -3
  199. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +25 -0
  200. package/packages/omo-codex/plugin/test/ulw-plan-scope-contract.test.mjs +24 -0
  201. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +9 -40
  202. package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +4 -277
  203. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +5 -5
  204. package/packages/omo-codex/scripts/install-config.test.mjs +22 -3
  205. package/packages/omo-codex/scripts/install-dist/install-local.mjs +98 -30
  206. package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +2 -2
  207. package/packages/shared-skills/skills/review-work/SKILL.md +10 -2
  208. package/packages/shared-skills/skills/start-work/SKILL.md +6 -3
  209. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +2 -2
  210. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +1 -1
  211. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +11 -11
  212. package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
  213. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  214. package/packages/shared-skills/skills/ulw-plan/references/intent-unclear.md +4 -4
  215. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  216. package/packages/shared-skills/skills/visual-qa/SKILL.md +9 -5
  217. package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  218. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +0 -70
  219. package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +0 -98
  220. package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
@@ -19,11 +19,11 @@ This skill may include examples copied from the OpenCode harness. In Codex, do n
19
19
 
20
20
  Role-specific behavior must be described in a self-contained `message`. Use `fork_context: false` to start the child with only the initial prompt (no parent history); use `fork_context: true` only when full parent history is truly required. Include any required conversation context, files, diffs, constraints, and requested skill names directly in the spawned agent's `message`. OMO installs these selectable agent roles into `~/.codex/agents/`: `explorer`, `librarian`, `plan`, `momus`, `metis`, `lazycodex-code-reviewer`, `lazycodex-qa-executor`, and `lazycodex-gate-reviewer` - pass the matching name as `agent_type` so the child gets that role's model and instructions. If the spawn tool exposes no `agent_type` parameter, omit it and describe the role inside `message`. If a code block below conflicts with this section, this section wins.
21
21
 
22
- Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. `agent_type` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
22
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If `multi_agent_v1.*` tools exist, use the table above as written. If instead a flat `spawn_agent` with a required `task_name` exists (`multi_agent_v2`), rewrite every `multi_agent_v1.*` example: `multi_agent_v1.spawn_agent({...,"fork_context":false})` becomes `spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})` (`"all"` only when full parent history is truly required); `send_input` becomes `send_message`; do not call `close_agent`/`resume_agent` (finished agents end on their own; `followup_task` re-tasks one, `interrupt_agent` stops one); `wait_agent` takes only `timeout_ms` and returns on any child mailbox activity. On the v2 surface `agent_type` may be ABSENT from the spawn schema (verified 2026-07-11: only `fork_turns`/`message`/`task_name`) — when absent, omit it and describe the role inside `message`; installed role TOMLs cannot be selected on that surface. If a code block below conflicts with this section, this section wins.
23
23
 
24
24
  When translating `load_skills=[...]`, include the requested skill names in the spawned agent's `message`. If a code block below conflicts with this section, this section wins.
25
25
 
26
- For work likely to exceed one wait cycle, require the child to send `WORKING: <task> - <current phase>` before long passes and `BLOCKED: <reason>` only when progress stops. A `multi_agent_v1.wait_agent` timeout only means no new mailbox update arrived. Treat a running child as alive. Fallback only when the child is completed without the deliverable, ack-only after followup, explicitly `BLOCKED:`, or no longer running.
26
+ For work likely to exceed one wait cycle, require the child to send `WORKING: <task> - <current phase>` before long passes and `BLOCKED: <reason>` only when progress stops. A `multi_agent_v1.wait_agent` timeout only means no new mailbox update arrived; back off between waits (double the timeout up to ~5 minutes) instead of spinning short cycles. Treat a running child as alive. Fallback only when the child is completed without the deliverable, ack-only after followup, explicitly `BLOCKED:`, or no longer running.
27
27
 
28
28
  # Visual QA - Dual-Oracle Web and TUI Verification
29
29
 
@@ -61,7 +61,11 @@ The verdict is per page. One failing page fails the whole surface, so "most page
61
61
 
62
62
  ### Evidence must be fresh
63
63
 
64
- Every gate runs on captures produced AFTER the last edit to the rendered source. If any screenshot, PDF, capture, or QA JSON is older than the source file it claims to verify, it is stale and invalid - regenerate it before trusting it. Never report a PASS from an artifact you did not just produce against the current build.
64
+ Every gate runs on captures produced AFTER the last edit to the rendered source. If any screenshot, PDF, capture, or QA JSON is older than the source file it claims to verify, it is stale and invalid - regenerate it before trusting it. Never report a PASS from an artifact you did not just produce against the current build. Between review rounds, re-capture only the pages a fix touched; the final approving round always judges a complete fresh set.
65
+
66
+ ### Capture hygiene - validate before dispatching reviewers
67
+
68
+ Before any reviewer sees an image, verify each capture yourself: the file signature matches its extension (a JPEG named `.png` is invalid), the frame is fully composited (no black or missing regions from the screenshot compositor), and dimensions match the requested viewport. A defective capture wastes an entire review round on the pipeline instead of the product - fix the capture tooling and re-shoot before dispatch, and record the tooling defect in the QA log instead of looping the reviewer on it.
65
69
 
66
70
  ### Web
67
71
 
@@ -125,7 +129,7 @@ Dispatch through your harness's own subagent tool. In OpenCode: `task(subagent_t
125
129
 
126
130
  Send BOTH calls in a single message so they run concurrently. Each oracle is read-only: it reviews and reports, it cannot modify files. Each returns PASS, REVISE, or FAIL with concrete, located findings. Pass A proves the surface is a real design-system implementation, not a mock-only or faked-image substitute. Pass B directly opens screenshots and inspects source/content for visual and CJK defects.
127
131
 
128
- Paste evidence directly into each prompt: source code, the plain-text TUI captures, the script JSON, and the screenshot paths plus your described observations for web. The two passes differ in depth by charter, not by any model or effort setting, which cannot be pinned per call.
132
+ Paste evidence directly into each prompt: source code, the plain-text TUI captures, the script JSON, and the screenshot paths plus your described observations for web. Never fork parent history into a reviewer - the message carries everything it needs. Require each blocking finding to be tagged `[product]` (the rendered UI is wrong) or `[evidence]` (the capture artifact is defective - wrong signature, partial compositing, stale file); the loop treats the two differently. The two passes differ in depth by charter, not by any model or effort setting, which cannot be pinned per call.
129
133
 
130
134
  ### Pass A - Design-system and functional integrity (deeper, strict)
131
135
 
@@ -169,7 +173,7 @@ OUTPUT:
169
173
  VERDICT: PASS | REVISE | FAIL
170
174
  CONFIDENCE: HIGH | MEDIUM | LOW
171
175
  SUMMARY: 1-3 sentences
172
- FINDINGS: for each, [dimension] [severity] what is wrong, where (file/line or capture region), and the concrete fix
176
+ FINDINGS: for each, [product|evidence] [dimension] [severity] what is wrong, where (file/line or capture region), and the concrete fix
173
177
  WHAT IS GOOD: correct aspects that must not regress
174
178
  BLOCKING: items that must be fixed; empty if PASS
175
179
  """
@@ -225,7 +229,7 @@ VERDICT: PASS | REVISE | FAIL
225
229
  CONFIDENCE: HIGH | MEDIUM | LOW
226
230
  SUMMARY: 1-3 sentences
227
231
  EVIDENCE TRACE: each hotspot or overflow line mapped to its visual cause
228
- FINDINGS: for each, [severity] what is wrong, where (hotspot grid or capture line:col), and the concrete fix
232
+ FINDINGS: for each, [product|evidence] [severity] what is wrong, where (hotspot grid or capture line:col), and the concrete fix
229
233
  BLOCKING: items that must be fixed; empty if PASS
230
234
  """
231
235
  )
@@ -243,7 +247,7 @@ This is a hard stop rule, not a guideline. The UI is NOT done until ALL of these
243
247
  - That reviewer judged a FRESH capture of every enumerated page from Step 2 - no stale artifacts, no skipped pages.
244
248
  - Every CJK and layout finding is resolved in the rendered output, not merely noted.
245
249
 
246
- If any page fails, you are not done: fix it, re-capture the full set, re-dispatch the reviewer, and repeat. Loop until the independent reviewer passes on the current build. Do not stop because the automated script reports zero issues - the script aims the reviewer, it does not replace it, and it routinely passes text while the rendered page is still broken. Do not stop because an earlier pass approved an older build. The only non-loop exit is to list the exact remaining gaps and get explicit user acceptance; never self-certify a silent PASS.
250
+ If any page fails, you are not done - but treat the two blocker kinds differently. `[product]` findings: fix the source, re-capture the pages the fix touched, and dispatch a FRESH reviewer (never a followup to the previous one - stale reviewer context re-litigates settled findings). `[evidence]` findings: the product is not implicated - repair the capture pipeline, re-shoot only the defective artifacts, verify them against the live build, and re-dispatch without touching product code. Loop until the independent reviewer passes on the current build, and make the final approving round judge a complete fresh capture set. Do not stop because the automated script reports zero issues - the script aims the reviewer, it does not replace it. Do not stop because an earlier pass approved an older build. The only non-loop exit is to list the exact remaining gaps and get explicit user acceptance; never self-certify a silent PASS.
247
251
 
248
252
  ```markdown
249
253
  # Visual QA - Verdict: GOOD | NEEDS WORK
@@ -19,16 +19,16 @@ const lazycodexAgentInvariants = new Map([
19
19
  [
20
20
  "explorer.toml",
21
21
  {
22
- model: "gpt-5.6-terra",
23
- effort: "medium",
22
+ model: "gpt-5.6-luna",
23
+ effort: "low",
24
24
  includes: [/Read-only/, /working tree/, /rg/],
25
25
  },
26
26
  ],
27
27
  [
28
28
  "librarian.toml",
29
29
  {
30
- model: "gpt-5.6-terra",
31
- effort: "medium",
30
+ model: "gpt-5.6-luna",
31
+ effort: "low",
32
32
  includes: [/Read-only/, /SHA-pinned GitHub permalink/, /external/],
33
33
  },
34
34
  ],
@@ -52,16 +52,32 @@ const lazycodexAgentInvariants = new Map([
52
52
  "plan.toml",
53
53
  {
54
54
  model: "gpt-5.6-sol",
55
- effort: "xhigh",
55
+ effort: "max",
56
56
  includes: [/strategic planning consultant/i, /\.omo\/plans\/<slug>\.md/, /never implements/i],
57
57
  },
58
58
  ],
59
59
  [
60
- "lazycodex-executor.toml",
60
+ "lazycodex-worker-low.toml",
61
61
  {
62
- model: "gpt-5.6-sol",
62
+ model: "gpt-5.6-luna",
63
63
  effort: "high",
64
- includes: [/EVIDENCE_RECORDED: <path>/, /scenario/i, /artifact/i],
64
+ includes: [/EVIDENCE_RECORDED: <path>/, /low-difficulty/i, /smallest correct change/i],
65
+ },
66
+ ],
67
+ [
68
+ "lazycodex-worker-medium.toml",
69
+ {
70
+ model: "gpt-5.6-luna",
71
+ effort: "max",
72
+ includes: [/EVIDENCE_RECORDED: <path>/, /medium-difficulty/i, /smallest correct change/i],
73
+ },
74
+ ],
75
+ [
76
+ "lazycodex-worker-high.toml",
77
+ {
78
+ model: "gpt-5.6-sol",
79
+ effort: "max",
80
+ includes: [/EVIDENCE_RECORDED: <path>/, /high-difficulty/i, /smallest correct change/i],
65
81
  },
66
82
  ],
67
83
  [
@@ -77,23 +93,23 @@ const lazycodexAgentInvariants = new Map([
77
93
  {
78
94
  model: "gpt-5.6-sol",
79
95
  effort: "xhigh",
80
- includes: [/codeQualityStatus/, /recommendation/, /\.omo\/evidence\/<goal>-code-review\.md/],
96
+ includes: [/codeQualityStatus/, /recommendation/, /<attemptDir>\/<goalId>-code-review\.md/, /currentAttemptDir/],
81
97
  },
82
98
  ],
83
99
  [
84
100
  "lazycodex-qa-executor.toml",
85
101
  {
86
- model: "gpt-5.6-terra",
87
- effort: "medium",
88
- includes: [/not_applicable/, /surfaceEvidence/, /adversarialCases/],
102
+ model: "gpt-5.6-luna",
103
+ effort: "high",
104
+ includes: [/not_applicable/, /surfaceEvidence/, /adversarialCases/, /<attemptDir>\/<goalId>-manual-qa\.md/],
89
105
  },
90
106
  ],
91
107
  [
92
108
  "lazycodex-gate-reviewer.toml",
93
109
  {
94
110
  model: "gpt-5.6-sol",
95
- effort: "xhigh",
96
- includes: [/APPROVE\/REJECT/, /blockers/, /\.omo\/evidence\/<goal>-gate-review\.md/],
111
+ effort: "high",
112
+ includes: [/APPROVE\/REJECT/, /blockers/, /<attemptDir>\/<goalId>-gate-review\.md/, /currentAttemptDir/],
97
113
  },
98
114
  ],
99
115
  ]);
@@ -114,9 +130,11 @@ test("#given bundled Codex agents #when components/ultrawork/agents directory is
114
130
  "explorer.toml",
115
131
  "lazycodex-clone-fidelity-reviewer.toml",
116
132
  "lazycodex-code-reviewer.toml",
117
- "lazycodex-executor.toml",
118
133
  "lazycodex-gate-reviewer.toml",
119
134
  "lazycodex-qa-executor.toml",
135
+ "lazycodex-worker-high.toml",
136
+ "lazycodex-worker-low.toml",
137
+ "lazycodex-worker-medium.toml",
120
138
  "librarian.toml",
121
139
  "metis.toml",
122
140
  "momus.toml",
@@ -139,11 +157,29 @@ test("#given bundled Codex agents #when components/ultrawork/agents directory is
139
157
  }
140
158
  });
141
159
 
160
+ test("#given bundled agent TOMLs #when nickname_candidates are inspected #then they use only the codex-accepted charset", async () => {
161
+ // given: codex_app_server ignores a role whose nickname has characters outside
162
+ // ASCII letters, digits, spaces, hyphens, underscores (observed live in task-15 QA)
163
+ const agentsDir = join(root, "components", "ultrawork", "agents");
164
+ const files = (await readdir(agentsDir)).filter((name) => name.endsWith(".toml"));
165
+
166
+ // when/then
167
+ for (const file of files) {
168
+ const text = await readFile(join(agentsDir, file), "utf8");
169
+ for (const match of text.matchAll(/nickname_candidates\s*=\s*\[([^\]]*)\]/g)) {
170
+ for (const nickname of match[1].matchAll(/"([^"]*)"/g)) {
171
+ assert.match(nickname[1], /^[A-Za-z0-9 _-]+$/, `${file}: nickname "${nickname[1]}"`);
172
+ }
173
+ }
174
+ }
175
+ });
176
+
142
177
  test("#given planner agent prompt #when inspected #then generated artifacts stay under .omo", async () => {
143
178
  const prompt = await readFile(join(root, "components", "ultrawork", "agents", "plan.toml"), "utf8");
144
179
 
145
180
  assert.match(prompt, /\.omo\/plans\/<slug>\.md/);
146
- assert.match(prompt, /\.omo\/evidence\/task-<N>-<slug>\.<ext>/);
181
+ assert.match(prompt, /<attemptDir>\/task-<N>-<slug>\.<ext>/);
182
+ assert.match(prompt, /\.omo\/evidence\/ulw\/<session>\/<goalId>\/a<attempt>/);
147
183
  assert.doesNotMatch(prompt, /(?<!\.omo\/)plans\/<slug>\.md/);
148
184
  assert.doesNotMatch(prompt, /(?<!\.omo\/)evidence\/task-/);
149
185
  });
@@ -207,3 +243,27 @@ test("#given LazyCodex reviewer prompts #when inspected #then anti-slop review c
207
243
  "gate reviewer must perform the overfit/slop pass directly before checking report coverage",
208
244
  );
209
245
  });
246
+
247
+ test("#given done-gate reviewer prompts #when inspected #then burden of proof is approve-unless-cited and reject priors are gone", async () => {
248
+ const agentsDir = join(root, "components", "ultrawork", "agents");
249
+ const gateReviewer = await readFile(join(agentsDir, "lazycodex-gate-reviewer.toml"), "utf8");
250
+ const qaExecutor = await readFile(join(agentsDir, "lazycodex-qa-executor.toml"), "utf8");
251
+ const codeReviewer = await readFile(join(agentsDir, "lazycodex-code-reviewer.toml"), "utf8");
252
+
253
+ assert.match(gateReviewer, /APPROVE unless you can cite/);
254
+ assert.match(gateReviewer, /violatedCriterion/);
255
+ assert.match(gateReviewer, /evidencePointer/);
256
+ assert.match(gateReviewer, /top blockers inline/);
257
+ assert.match(gateReviewer, /is a NOTE, not a blocker/);
258
+ assert.match(gateReviewer, /You do NOT check/);
259
+ assert.doesNotMatch(gateReviewer, /Assume the work has already failed/);
260
+ assert.doesNotMatch(gateReviewer, /Return exactly one recommendation: APPROVE\/REJECT\./);
261
+
262
+ assert.match(qaExecutor, /one-line reason/);
263
+ assert.match(qaExecutor, /rejecting a legitimately untriggered class is itself an error/);
264
+ assert.doesNotMatch(qaExecutor, /Trust nothing\./);
265
+
266
+ assert.match(codeReviewer, /MEDIUM by default/);
267
+ assert.doesNotMatch(codeReviewer, /Treat useless tests or needless production complexity as CRITICAL\/HIGH/);
268
+ });
269
+
@@ -48,6 +48,22 @@ test("#given isolated components #when hooks are inspected #then commands stay i
48
48
  assert.equal(await exists("scripts/migrate-codex-config.mjs"), true);
49
49
  });
50
50
 
51
+ test("#given aggregate Stop hooks #when inspected #then start-work continuation and ulw-loop resume are separate groups", async () => {
52
+ // given
53
+ const manifests = await readAggregateHookManifests();
54
+
55
+ // when
56
+ const stopCommands = manifests
57
+ .filter(({ hooks }) => hooks.hooks.Stop)
58
+ .flatMap(({ hooks }) => hooks.hooks.Stop)
59
+ .flatMap((group) => group.hooks.map((handler) => handler.command));
60
+
61
+ // then
62
+ assert.equal(stopCommands.length, 2);
63
+ assert.ok(stopCommands.some((command) => command.includes("start-work-continuation/dist/cli.js")));
64
+ assert.ok(stopCommands.some((command) => command.includes("ulw-loop/dist/cli.js\" hook stop")));
65
+ });
66
+
51
67
  test("#given aggregate SubagentStop hooks #when inspected #then start-work and LazyCodex executor verifier are separate groups", async () => {
52
68
  // given
53
69
  const manifests = await readAggregateHookManifests();
@@ -64,7 +80,7 @@ test("#given aggregate SubagentStop hooks #when inspected #then start-work and L
64
80
  // then
65
81
  assert.equal(subagentStopGroups.length, 2);
66
82
  assert.equal(subagentStopGroups[0]?.matcher, undefined);
67
- assert.equal(subagentStopGroups[1]?.matcher, "^lazycodex-executor$");
83
+ assert.equal(subagentStopGroups[1]?.matcher, "^lazycodex-worker-(low|medium|high)$");
68
84
  assert.equal(verifierGroups.length, 1);
69
85
  assert.equal(verifierGroups[0]?.groupIndex, 0);
70
86
  assert.equal(verifierGroups[0]?.handler.timeout, 10);
@@ -158,7 +174,12 @@ test("#given aggregate OMO plugin is enabled #when hooks are inspected #then she
158
174
  assert.match(text, /Resetting Git Bash MCP Reminder/);
159
175
  assert.match(text, /components\/ulw-loop\/dist\/cli\.js/);
160
176
  assert.match(text, /hook pre-tool-use/);
161
- assert.deepEqual(preToolUseGroups.map((group) => group.matcher), ["^Bash$", "^create_goal$"]);
177
+ assert.deepEqual(preToolUseGroups.map((group) => group.matcher), [
178
+ "^Bash$",
179
+ "^create_goal$",
180
+ "^(spawn_agent|collaborationspawn_agent|collaboration\\.spawn_agent)$",
181
+ ]);
182
+ assert.match(text, /hook pre-tool-use-spawn/);
162
183
  });
163
184
 
164
185
  test("#given aggregate OMO plugin has a dedicated ultrawork trigger #when hooks are inspected #then ulw-loop does not duplicate ultrawork injection", async () => {
@@ -17,7 +17,7 @@ test("#given aggregate plugin manifest #when inspected #then it owns the omo nam
17
17
  // then
18
18
  assert.equal(manifest.name, "omo");
19
19
  assert(Array.isArray(hookPaths));
20
- assert.equal(hookPaths.length, 21);
20
+ assert.equal(hookPaths.length, 23);
21
21
  assert(hookPaths.every((hookPath) => typeof hookPath === "string" && hookPath.startsWith("./hooks/")));
22
22
  assert(!hookPaths.includes("./hooks/hooks.json"));
23
23
  assert(!hookPaths.includes("./hooks/user-prompt-submit-selecting-lazycodex-workflow.json"));
@@ -8,17 +8,17 @@ import { root } from "./aggregate-plugin-fixture.mjs";
8
8
  test("#given bundled model catalog #when inspected #then default verifier and worker roles are pinned", async () => {
9
9
  const catalog = JSON.parse(await readFile(join(root, "model-catalog.json"), "utf8"));
10
10
 
11
- assert.equal(catalog.current.model, "gpt-5.5");
12
- assert.equal(catalog.current.model_context_window, 400000);
11
+ assert.equal(catalog.current.model, "gpt-5.6-sol");
12
+ assert.equal(catalog.current.model_context_window, 372000);
13
13
  assert.equal(catalog.current.model_reasoning_effort, "high");
14
14
  assert.equal(catalog.current.plan_mode_reasoning_effort, "xhigh");
15
15
  assert.deepEqual(catalog.roles.default, catalog.current);
16
16
  assert.deepEqual(catalog.roles.verifier, {
17
- model: "gpt-5.5",
17
+ model: "gpt-5.6-sol",
18
18
  model_reasoning_effort: "high",
19
19
  });
20
20
  assert.deepEqual(catalog.roles.worker, {
21
- model: "gpt-5.5",
21
+ model: "gpt-5.6-sol",
22
22
  model_reasoning_effort: "high",
23
23
  });
24
24
  });
@@ -225,7 +225,7 @@ test("#given test command override #when running check #then records state and l
225
225
  toVersion: "1.0.1",
226
226
  },
227
227
  ]);
228
- assert.match(await readFile(join(env.CODEX_HOME, "config.toml"), "utf8"), /model = "gpt-5\.5"/);
228
+ assert.match(await readFile(join(env.CODEX_HOME, "config.toml"), "utf8"), /model = "gpt-5\.6-sol"/);
229
229
  });
230
230
 
231
231
  test("#given failed waited update #when retry window passes #then next update is not blocked by success throttle", async () => {
@@ -279,7 +279,7 @@ test("#given active lock #when running check #then skips concurrent update", asy
279
279
 
280
280
  assert.equal(result.started, false);
281
281
  assert.equal(result.reason, "locked");
282
- assert.match(await readFile(join(root, "codex-home", "config.toml"), "utf8"), /model_context_window = 400000/);
282
+ assert.match(await readFile(join(root, "codex-home", "config.toml"), "utf8"), /model_context_window = 372000/);
283
283
  });
284
284
 
285
285
  test("#given stale lock #when running check #then removes lock and runs update", async () => {
@@ -645,8 +645,8 @@ test("#given throttled updater and stale Codex config #when running check #then
645
645
  const content = await readFile(join(codexHome, "config.toml"), "utf8");
646
646
  assert.equal(result.started, false);
647
647
  assert.equal(result.reason, "throttled");
648
- assert.match(content, /model = "gpt-5\.5"/);
649
- assert.match(content, /model_context_window = 400000/);
648
+ assert.match(content, /model = "gpt-5\.6-sol"/);
649
+ assert.match(content, /model_context_window = 372000/);
650
650
  assert.match(content, /model_reasoning_effort = "high"/);
651
651
  assert.match(content, /plan_mode_reasoning_effort = "xhigh"/);
652
652
  assert.doesNotMatch(content, /gpt-5\.2/);
@@ -180,12 +180,12 @@ export function componentHookContractCases(tempRoot) {
180
180
  },
181
181
  },
182
182
  {
183
- name: "lazycodex executor verifier subagent-stop blocks missing evidence",
183
+ name: "lazycodex worker verifier subagent-stop blocks missing evidence",
184
184
  component: "lazycodex-executor-verify",
185
185
  event: "subagent-stop",
186
186
  payload: {
187
187
  hook_event_name: "SubagentStop",
188
- agent_type: "lazycodex-executor",
188
+ agent_type: "lazycodex-worker-medium",
189
189
  agent_id: "agent-task12",
190
190
  session_id: "s-task12",
191
191
  transcript_path: join(tempRoot, "transcript.jsonl"),
@@ -7,109 +7,12 @@ import { fileURLToPath } from "node:url";
7
7
  const pluginRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
8
  const sharedSkillsRoot = join(pluginRoot, "..", "..", "shared-skills");
9
9
 
10
- async function readSkill(name) {
11
- return readFile(join(sharedSkillsRoot, "skills", name, "SKILL.md"), "utf8");
12
- }
13
-
14
- test("#given synced lcx-report-bug skill #when inspected #then it files LazyCodex bug issues with generated labels", async () => {
15
- // given
16
- const skillRoot = join(sharedSkillsRoot, "skills", "lcx-report-bug");
17
-
18
- // when
19
- const skill = await readFile(join(skillRoot, "SKILL.md"), "utf8");
20
- const interfaceMetadata = await readFile(join(skillRoot, "agents", "openai.yaml"), "utf8");
21
-
22
- // then
23
- assert.match(skill, /^---\r?\nname: lcx-report-bug\r?\n/m);
24
- assert.match(skill, /Never create a PR or push a branch against `code-yeongyu\/lazycodex`/);
25
- assert.match(skill, /gh pr create --repo openai\/codex/);
26
- assert.doesNotMatch(skill, /gh pr create --repo "\$TARGET_REPO"/);
27
- assert.doesNotMatch(skill, /gh pr create --repo code-yeongyu\/lazycodex/);
28
- assert.match(interfaceMetadata, /display_name: "lcx-report-bug \(omo\)"/);
29
- assert.match(interfaceMetadata, /- "lazycodex bug"/);
30
- assert.match(interfaceMetadata, /- "openai codex bug"/);
31
- });
32
-
33
- test("#given synced lcx-contribute-bug-fix skill #when inspected #then it delivers LazyCodex fixes as issues and upstream fixes as fork PRs", async () => {
34
- // given
35
- const skillRoot = join(sharedSkillsRoot, "skills", "lcx-contribute-bug-fix");
36
-
37
- // when
38
- const skill = await readFile(join(skillRoot, "SKILL.md"), "utf8");
39
- const interfaceMetadata = await readFile(join(skillRoot, "agents", "openai.yaml"), "utf8");
40
-
41
- // then
42
- assert.match(skill, /^---\r?\nname: lcx-contribute-bug-fix\r?\n/m);
43
- assert.match(skill, /NEVER open a PR or push a branch against this repo/);
44
- assert.match(skill, /gh issue create --repo code-yeongyu\/lazycodex/);
45
- assert.match(skill, /gh pr create --repo openai\/codex/);
46
- assert.doesNotMatch(skill, /gh pr create --repo "\$TARGET_REPO"/);
47
- assert.doesNotMatch(skill, /gh pr create --repo code-yeongyu\/lazycodex/);
48
- assert.match(interfaceMetadata, /display_name: "lcx-contribute-bug-fix \(omo\)"/);
49
- assert.match(interfaceMetadata, /- "contribute a bug fix"/);
50
- assert.match(interfaceMetadata, /- "fix bug pr"/);
51
- });
52
-
53
- test("#given synced lcx-doctor skill #when inspected #then it diagnoses installs against latest temp-root sources without mutating them", async () => {
54
- // given
55
- const skillRoot = join(sharedSkillsRoot, "skills", "lcx-doctor");
56
-
57
- // when
58
- const skill = await readFile(join(skillRoot, "SKILL.md"), "utf8");
59
- const interfaceMetadata = await readFile(join(skillRoot, "agents", "openai.yaml"), "utf8");
60
-
61
- // then
62
- assert.match(skill, /^---\r?\nname: lcx-doctor\r?\n/m);
63
- assert.match(interfaceMetadata, /display_name: "lcx-doctor \(omo\)"/);
64
- assert.match(interfaceMetadata, /- "lazycodex doctor"/);
65
- assert.match(interfaceMetadata, /- "lazycodex health check"/);
66
- });
67
-
68
- test("#given lcx source-sync skills #when inspected #then source caches are validated before reuse", async () => {
69
- // given
70
- const skillNames = ["lcx-doctor", "lcx-report-bug", "lcx-contribute-bug-fix"];
71
-
72
- for (const skillName of skillNames) {
10
+ test("#given lcx skills #when frontmatter is inspected #then each exposes the loader-parsed skill name", async () => {
11
+ for (const skillName of ["lcx-report-bug", "lcx-contribute-bug-fix", "lcx-doctor"]) {
73
12
  // when
74
- const skill = await readSkill(skillName);
13
+ const skill = await readFile(join(sharedSkillsRoot, "skills", skillName, "SKILL.md"), "utf8");
75
14
 
76
15
  // then
77
- assert.match(skill, /LAZYCODEX_SOURCE_ROOT="\$\{LAZYCODEX_SOURCE_ROOT:-\$\{TMPDIR:-\/tmp\}\/lazycodex-sources\}"/);
78
- assert.match(skill, /git -C "\$DEST" rev-parse --is-inside-work-tree/);
79
- assert.match(skill, /git -C "\$DEST" config --get remote\.origin\.url/);
80
- assert.match(skill, /DEST\.corrupt\.\$\(date \+%Y%m%d%H%M%S\)/);
81
- assert.doesNotMatch(skill, /if \[ ! -d "\$DEST\/\.git" \]; then/);
82
- assert.doesNotMatch(skill, /sync_latest_source code-yeongyu\/lazycodex \/tmp\/lazycodex-source/);
83
- assert.doesNotMatch(skill, /sync_latest_source openai\/codex \/tmp\/openai-codex-source/);
16
+ assert.match(skill, new RegExp(`^name: ${skillName}$`, "m"), `${skillName}: frontmatter must expose ${skillName}`);
84
17
  }
85
18
  });
86
-
87
- test("#given lcx-doctor skill #when inspected #then aggregate hook validation follows the current manifest", async () => {
88
- // given
89
- const skill = await readSkill("lcx-doctor");
90
-
91
- // then
92
- assert.match(skill, /\.codex-plugin\/plugin\.json/);
93
- assert.match(skill, /manifest declares a `hooks` array/);
94
- assert.match(skill, /validate every direct hook path declared by the manifest/);
95
- assert.match(skill, /`hooks\/hooks\.json` only when the manifest declares it/);
96
- assert.match(skill, /do not require retired paths such as `components\/workflow-selector`/);
97
- assert.match(skill, /`hooks\/user-prompt-submit-selecting-lazycodex-workflow\.json` unless the current manifest declares them/);
98
- assert.doesNotMatch(skill, /Plugin payload present and non-empty: `hooks\/hooks\.json`/);
99
- });
100
-
101
- test("#given lcx-doctor skill #when inspected #then runtime probes and materialized payloads distinguish expected rewrites from missing files", async () => {
102
- // given
103
- const skill = await readSkill("lcx-doctor");
104
-
105
- // then
106
- assert.match(skill, /manifest-declared runtime payload/);
107
- assert.match(skill, /`dist\/cli\/index\.js` and `dist\/cli-node\/index\.js`/);
108
- assert.match(skill, /install-time materialization rewrites as expected/);
109
- assert.match(skill, /absolute `\.mcp\.json` runtime paths/);
110
- assert.match(skill, /Missing or zero-byte rewritten targets are FAIL/);
111
- assert.match(skill, /Use the configured Codex default model/);
112
- assert.match(skill, /unless the user explicitly passed a model override/);
113
- assert.match(skill, /never force a guessed\/rejected model such as `gpt-5\.5-codex-mini`/);
114
- assert.doesNotMatch(skill, /--model gpt-5\.5-codex-mini/);
115
- });
@@ -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
  });
@@ -235,6 +235,15 @@ test("#given synced aggregate Codex skills #when they describe background orches
235
235
  }
236
236
  });
237
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
+
238
247
  test("#given review-work skill #when some lanes do not finish #then aggregate result remains bounded", async () => {
239
248
  const content = await readSkill("review-work");
240
249
 
@@ -245,6 +254,8 @@ test("#given review-work skill #when some lanes do not finish #then aggregate re
245
254
  assert.match(content, /Overall Verdict: PASSED \/ FAILED \/ INCONCLUSIVE/);
246
255
  assert.match(content, /PASS\/FAIL\/INCONCLUSIVE \| HIGH\/MED\/LOW/);
247
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/);
248
259
  assert.match(content, /Do not use `multi_agent_v1\.send_input` as an interrupt/);
249
260
  });
250
261
 
@@ -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,14 +252,16 @@ 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/],
262
- ["single long wait guard", /single long blocking wait/],
263
+ ["bounded plan/reviewer polling", /wait_agent.*cycles/],
264
+ ["exponential backoff wait guard", /double the timeout up to ~5 minutes/],
263
265
  ["git-master checkpointing", /git-master/],
264
266
  ["touched-path commit-style probe", /touched-path commit history/],
265
267
  ["verified work-unit commit", /verified work unit/],