oh-my-opencode 4.19.3 → 5.0.0-beta.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 (347) hide show
  1. package/.agents/command/get-unpublished-changes.md +2 -0
  2. package/.agents/command/omomomo.md +1 -1
  3. package/.agents/command/publish.md +7 -0
  4. package/.agents/skills/get-unpublished-changes/SKILL.md +2 -0
  5. package/.agents/skills/hyperplan/SKILL.md +3 -3
  6. package/.agents/skills/omomomo/SKILL.md +1 -1
  7. package/.agents/skills/publish/SKILL.md +7 -0
  8. package/.opencode/command/get-unpublished-changes.md +2 -0
  9. package/.opencode/command/omomomo.md +1 -1
  10. package/.opencode/command/publish.md +7 -0
  11. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  12. package/README.ja.md +1 -1
  13. package/README.ko.md +1 -1
  14. package/README.md +6 -5
  15. package/README.ru.md +1 -1
  16. package/README.zh-cn.md +1 -1
  17. package/bin/oh-my-opencode.js +14 -1
  18. package/bin/oh-my-opencode.test.ts +21 -0
  19. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  20. package/dist/cli/doctor/checks/deprecated-reasoning-keys.d.ts +2 -0
  21. package/dist/cli/index.js +2925 -1919
  22. package/dist/cli-node/index.js +2925 -1919
  23. package/dist/config/schema/agent-overrides.d.ts +1343 -15
  24. package/dist/config/schema/categories.d.ts +132 -0
  25. package/dist/config/schema/fallback-models.d.ts +50 -0
  26. package/dist/config/schema/oh-my-opencode-config.d.ts +1322 -11
  27. package/dist/config-migration/index.d.ts +1 -0
  28. package/dist/config-migration/migration-plans.d.ts +1 -0
  29. package/dist/config-migration/reasoning-unification.d.ts +3 -0
  30. package/dist/features/monitor/batcher.d.ts +3 -1
  31. package/dist/features/monitor/manager-internals.d.ts +1 -0
  32. package/dist/features/monitor/output-injector-types.d.ts +2 -0
  33. package/dist/features/monitor/output-injector.d.ts +6 -0
  34. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +2 -0
  35. package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +1 -0
  36. package/dist/hooks/model-fallback/next-fallback.d.ts +1 -0
  37. package/dist/hooks/runtime-fallback/constants.d.ts +1 -1
  38. package/dist/hooks/todo-continuation-enforcer/types.d.ts +1 -0
  39. package/dist/hooks/todo-continuation-enforcer/unrecoverable-request-error.d.ts +9 -0
  40. package/dist/hooks/tool-pair-validator/hook.test-support.d.ts +29 -0
  41. package/dist/hooks/tool-pair-validator/tool-part-ids.d.ts +14 -5
  42. package/dist/hooks/tool-pair-validator/tool-result-repair.d.ts +4 -3
  43. package/dist/hooks/tool-pair-validator/types.d.ts +5 -22
  44. package/dist/index.js +4864 -4011
  45. package/dist/mcp/lsp.d.ts +1 -0
  46. package/dist/oh-my-opencode.schema.json +3221 -222
  47. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +1 -0
  48. package/dist/shared/agent-variant.d.ts +11 -0
  49. package/dist/shared/session-prompt-params-helpers.d.ts +6 -1
  50. package/dist/shared/tmux/constants.d.ts +1 -1
  51. package/dist/skills/ast-grep/SOURCE +1 -1
  52. package/dist/skills/ast-grep/install.ps1 +2 -2
  53. package/dist/skills/ast-grep/install.sh +1 -1
  54. package/dist/skills/ast-grep/references/install.md +2 -2
  55. package/dist/skills/ast-grep/tests/smoke.sh +1 -1
  56. package/dist/skills/coding-agent-sessions/SKILL.md +4 -3
  57. package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  58. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  59. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  60. package/dist/skills/data-scientist/SKILL.md +243 -0
  61. package/dist/skills/data-scientist/references/common-scenarios.md +176 -0
  62. package/dist/skills/data-scientist/references/execution-templates.md +197 -0
  63. package/dist/skills/data-scientist/references/integration-patterns.md +153 -0
  64. package/dist/skills/data-scientist/references/performance-benchmarks.md +37 -0
  65. package/dist/skills/data-scientist/references/uv-setup.md +78 -0
  66. package/dist/skills/data-scientist/scripts/quick-query.py +111 -0
  67. package/dist/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  68. package/dist/skills/data-scientist/scripts/setup-uv.sh +60 -0
  69. package/dist/skills/debugging/SKILL.md +1 -1
  70. package/dist/skills/programming/SKILL.md +1 -2
  71. package/dist/skills/start-work/SKILL.md +54 -9
  72. package/dist/skills/ultimate-browsing/SKILL.md +2 -2
  73. package/dist/skills/ultimate-browsing/engine/__main__.py +8 -1
  74. package/dist/skills/ultimate-browsing/engine/bias_check.py +11 -0
  75. package/dist/skills/ultimate-browsing/engine/fetch_chain.py +90 -52
  76. package/dist/skills/ultimate-browsing/engine/result_schema.py +10 -1
  77. package/dist/skills/ultimate-browsing/engine/surrogate.py +214 -0
  78. package/dist/skills/ultimate-browsing/engine/surrogates.yaml +60 -0
  79. package/dist/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +7 -0
  80. package/dist/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +19 -0
  81. package/dist/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +1 -0
  82. package/dist/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +1128 -0
  83. package/dist/skills/ultimate-browsing/engine/tests/test_surrogate.py +252 -0
  84. package/dist/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +78 -0
  85. package/dist/skills/ultimate-browsing/engine/validators.py +46 -0
  86. package/dist/skills/ultimate-browsing/engine/waf_detector.py +1 -1
  87. package/dist/skills/ultimate-browsing/engine/waf_profiles.yaml +10 -5
  88. package/dist/skills/ultimate-browsing/references/agent-reach/social.md +1 -1
  89. package/dist/skills/ultimate-browsing/references/chrome-stealth.md +13 -11
  90. package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  91. package/dist/skills/ultimate-browsing/references/insane-search/cache-archive.md +51 -50
  92. package/dist/skills/ultimate-browsing/references/insane-search/fallback.md +1 -1
  93. package/dist/skills/ultimate-browsing/references/insane-search/jina.md +8 -2
  94. package/dist/skills/ultimate-browsing/references/insane-search/naver.md +1 -1
  95. package/dist/skills/ultimate-browsing/references/insane-search/twitter.md +3 -3
  96. package/dist/skills/ulw-plan/SKILL.md +2 -2
  97. package/dist/skills/ulw-plan/references/full-workflow.md +3 -3
  98. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  99. package/dist/skills/ulw-research/SKILL.md +128 -12
  100. package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
  101. package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
  102. package/dist/tools/delegate-task/constants.d.ts +1 -1
  103. package/dist/tui.js +1271 -1108
  104. package/docs/reference/web-terminal-visual-qa.md +1 -1
  105. package/package.json +27 -19
  106. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +2 -2
  107. package/packages/lsp-core/src/lsp/connection.ts +1 -1
  108. package/packages/lsp-daemon/dist/cli.js +27 -13
  109. package/packages/lsp-daemon/dist/client.js +49 -35
  110. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +1 -0
  111. package/packages/lsp-daemon/dist/ensure-daemon.js +18 -5
  112. package/packages/lsp-daemon/dist/index.js +34 -20
  113. package/packages/lsp-tools-mcp/dist/cli.js +1 -1
  114. package/packages/lsp-tools-mcp/dist/lsp/manager.js +1 -1
  115. package/packages/lsp-tools-mcp/dist/mcp.js +1 -1
  116. package/packages/lsp-tools-mcp/dist/tools.js +1 -1
  117. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  118. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +289 -95
  119. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  120. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  121. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +7 -7
  122. package/packages/omo-codex/plugin/components/bootstrap/src/worker.ts +3 -0
  123. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +385 -64
  124. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +336 -47
  125. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  126. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +6 -0
  127. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  128. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  129. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  130. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  131. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  132. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  133. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  134. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +56 -42
  135. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  136. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  137. package/packages/omo-codex/plugin/components/rules/dist/cli.js +1 -1
  138. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  139. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  140. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +1 -1
  141. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +1 -0
  142. package/packages/omo-codex/plugin/components/start-work-continuation/README.md +5 -1
  143. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +2 -1
  144. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +18 -0
  145. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  146. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  147. package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +21 -0
  148. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +105 -0
  149. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  150. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  151. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  152. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  153. package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +2 -0
  154. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +1 -1
  155. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
  156. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +1 -1
  157. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
  158. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-low.toml +1 -1
  159. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +1 -1
  160. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -2
  161. package/packages/omo-codex/plugin/components/ultrawork/directive.md +3 -2
  162. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  163. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  164. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +3 -2
  165. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -2
  166. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +3 -3
  167. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  168. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -1
  169. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +2 -0
  170. package/packages/omo-codex/plugin/components/ulw-loop/README.md +11 -11
  171. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -2
  172. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +1 -1
  173. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
  174. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +9 -9
  175. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +1 -1
  176. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +66 -66
  177. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +4 -4
  178. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.js +1 -1
  179. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -1
  180. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +1 -1
  181. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +1 -1
  182. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +2 -2
  183. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  184. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  185. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +3 -3
  186. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +22 -25
  187. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +1 -1
  188. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +9 -9
  189. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +1 -1
  190. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +1 -1
  191. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +4 -4
  192. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-hook.ts +1 -1
  193. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -1
  194. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +1 -1
  195. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +1 -1
  196. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +2 -2
  197. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-commands.test.ts +2 -2
  198. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +1 -1
  199. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-helpers.test.ts +2 -2
  200. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-kind-guidance.test.ts +1 -1
  201. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-hook.test.ts +2 -2
  202. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +1 -1
  203. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -5
  204. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +1 -1
  205. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-roles.test.ts +1 -1
  206. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +1 -1
  207. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +1 -1
  208. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  209. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  210. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  211. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  212. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  213. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  214. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  215. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  216. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  217. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  218. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  219. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  220. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  221. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  222. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  223. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  224. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  225. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  226. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  227. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  228. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  229. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  230. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  231. package/packages/omo-codex/plugin/package-lock.json +13 -13
  232. package/packages/omo-codex/plugin/package.json +1 -1
  233. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +8 -2
  234. package/packages/omo-codex/plugin/skills/ast-grep/SOURCE +1 -1
  235. package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +2 -2
  236. package/packages/omo-codex/plugin/skills/ast-grep/install.sh +1 -1
  237. package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
  238. package/packages/omo-codex/plugin/skills/ast-grep/tests/smoke.sh +1 -1
  239. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +4 -3
  240. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  241. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  242. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  243. package/packages/omo-codex/plugin/skills/data-scientist/SKILL.md +243 -0
  244. package/packages/omo-codex/plugin/skills/data-scientist/agents/openai.yaml +2 -0
  245. package/packages/omo-codex/plugin/skills/data-scientist/references/common-scenarios.md +176 -0
  246. package/packages/omo-codex/plugin/skills/data-scientist/references/execution-templates.md +197 -0
  247. package/packages/omo-codex/plugin/skills/data-scientist/references/integration-patterns.md +153 -0
  248. package/packages/omo-codex/plugin/skills/data-scientist/references/performance-benchmarks.md +37 -0
  249. package/packages/omo-codex/plugin/skills/data-scientist/references/uv-setup.md +78 -0
  250. package/packages/omo-codex/plugin/skills/data-scientist/scripts/quick-query.py +111 -0
  251. package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  252. package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.sh +60 -0
  253. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +1 -1
  254. package/packages/omo-codex/plugin/skills/programming/SKILL.md +1 -2
  255. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +54 -9
  256. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +2 -2
  257. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +8 -1
  258. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/bias_check.py +11 -0
  259. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/fetch_chain.py +90 -52
  260. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/result_schema.py +10 -1
  261. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/surrogate.py +214 -0
  262. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/surrogates.yaml +60 -0
  263. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +7 -0
  264. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +19 -0
  265. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +1 -0
  266. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +1128 -0
  267. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +252 -0
  268. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +78 -0
  269. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/validators.py +46 -0
  270. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/waf_detector.py +1 -1
  271. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/waf_profiles.yaml +10 -5
  272. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/agent-reach/social.md +1 -1
  273. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +13 -11
  274. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  275. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/cache-archive.md +51 -50
  276. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/fallback.md +1 -1
  277. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/jina.md +8 -2
  278. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/naver.md +1 -1
  279. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/twitter.md +3 -3
  280. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -2
  281. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +3 -3
  282. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +22 -25
  283. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -2
  284. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +3 -3
  285. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  286. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +127 -12
  287. package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +12 -12
  288. package/packages/omo-codex/plugin/test/bootstrap-orchestration.test.mjs +36 -4
  289. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +1 -1
  290. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +9 -2
  291. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +12 -0
  292. package/packages/omo-codex/scripts/install-bin-links.test.mjs +56 -2
  293. package/packages/omo-codex/scripts/install-delegated-command.test.mjs +6 -6
  294. package/packages/omo-codex/scripts/install-dist/install-local.mjs +138 -65
  295. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +4 -4
  296. package/packages/omo-codex/scripts/install-local.test.mjs +5 -2
  297. package/packages/shared-skills/index.mjs +19 -1
  298. package/packages/shared-skills/skills/ast-grep/SOURCE +1 -1
  299. package/packages/shared-skills/skills/ast-grep/install.ps1 +2 -2
  300. package/packages/shared-skills/skills/ast-grep/install.sh +1 -1
  301. package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
  302. package/packages/shared-skills/skills/ast-grep/tests/smoke.sh +1 -1
  303. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +4 -3
  304. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  305. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  306. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  307. package/packages/shared-skills/skills/data-scientist/SKILL.md +243 -0
  308. package/packages/shared-skills/skills/data-scientist/references/common-scenarios.md +176 -0
  309. package/packages/shared-skills/skills/data-scientist/references/execution-templates.md +197 -0
  310. package/packages/shared-skills/skills/data-scientist/references/integration-patterns.md +153 -0
  311. package/packages/shared-skills/skills/data-scientist/references/performance-benchmarks.md +37 -0
  312. package/packages/shared-skills/skills/data-scientist/references/uv-setup.md +78 -0
  313. package/packages/shared-skills/skills/data-scientist/scripts/quick-query.py +111 -0
  314. package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  315. package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.sh +60 -0
  316. package/packages/shared-skills/skills/debugging/SKILL.md +1 -1
  317. package/packages/shared-skills/skills/programming/SKILL.md +1 -2
  318. package/packages/shared-skills/skills/start-work/SKILL.md +54 -9
  319. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +2 -2
  320. package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +8 -1
  321. package/packages/shared-skills/skills/ultimate-browsing/engine/bias_check.py +11 -0
  322. package/packages/shared-skills/skills/ultimate-browsing/engine/fetch_chain.py +90 -52
  323. package/packages/shared-skills/skills/ultimate-browsing/engine/result_schema.py +10 -1
  324. package/packages/shared-skills/skills/ultimate-browsing/engine/surrogate.py +214 -0
  325. package/packages/shared-skills/skills/ultimate-browsing/engine/surrogates.yaml +60 -0
  326. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +7 -0
  327. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +19 -0
  328. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +1 -0
  329. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +1128 -0
  330. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_surrogate.py +252 -0
  331. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +78 -0
  332. package/packages/shared-skills/skills/ultimate-browsing/engine/validators.py +46 -0
  333. package/packages/shared-skills/skills/ultimate-browsing/engine/waf_detector.py +1 -1
  334. package/packages/shared-skills/skills/ultimate-browsing/engine/waf_profiles.yaml +10 -5
  335. package/packages/shared-skills/skills/ultimate-browsing/references/agent-reach/social.md +1 -1
  336. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +13 -11
  337. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  338. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/cache-archive.md +51 -50
  339. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/fallback.md +1 -1
  340. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/jina.md +8 -2
  341. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/naver.md +1 -1
  342. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/twitter.md +3 -3
  343. package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -2
  344. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +3 -3
  345. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +1 -1
  346. package/packages/shared-skills/skills/ulw-research/SKILL.md +128 -12
  347. package/postinstall.mjs +6 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ulw-research
3
- description: "Maximum-saturation research orchestration: parallel explore+librarian swarms across codebase, web, official docs, and OSS repos; a recursive EXPAND loop driven by leads workers return in message text; empirical verification by running code; cited synthesis and optional MD/HTML/PDF/PPTX reports. ACTIVATES ONLY on an explicit user demand for research — the word 'ulw-research' ('/ulw-research', '$ulw-research'), any 'ulw' research wording, or an explicit request for research / deep research / an ultra-precise investigation, in any language. Never self-activates for ordinary questions, debugging, or implementation context-gathering. While active it overrides exploration-bounding defaults: exhaustive coverage is the goal."
3
+ description: "Maximum-saturation research orchestration: ALWAYS proposes the final materials first (PDF+DOCX default), then parallel explore+librarian swarms across codebase, web, official docs, and OSS repos — max-roster teammode when the harness has it — with live journaling, a recursive EXPAND loop driven by leads workers return in message text, empirical verification by running code, and a cited synthesis with charts/Mermaid/assets behind a mandatory visual-QA gate. ACTIVATES ONLY on an explicit user demand for research — the word 'ulw-research' ('/ulw-research', '$ulw-research'), any 'ulw' research wording, 'ultradebate' or 'hyperdebate' research requests, or an explicit request for research / deep research / an ultra-precise investigation, in any language. Never self-activates for ordinary questions, debugging, or implementation context-gathering. While active it overrides exploration-bounding defaults: exhaustive coverage is the goal."
4
4
  ---
5
5
 
6
6
  ## Codex Harness Tool Compatibility
@@ -52,8 +52,11 @@ The research is done when all of these hold:
52
52
  - Claims that were contested, undocumented, or performance-shaped were proven or refuted by executed code.
53
53
  - Every claim in the deliverable cites a source or a verification artifact.
54
54
  - Every asserted claim is represented in the claim graph, tied to an intent-vs-reality diff when an expected truth exists, and backed by observation manifest entries from independent observation groups or a documented single-source exception; convergence or exception status is explicit.
55
- - Final materials follow the Phase 5 format default or the user's explicit format.
56
- - The session journal reconstructs what was searched, found, and expanded, wave by wave.
55
+ - The format-proposal gate was asked and answered BEFORE the first wave, and the final materials match that answer.
56
+ - The delivered artifact passed every delivery gate for this harness: visual QA on the rendered pages always, plus the proofread pass where the harness provides one (Phase 5).
57
+ - Every excursion opened during the run was closed by an EXIT rule, folded back into the claim or axis that triggered it, and recorded in both `excursion-log.md` and the ulw-loop ledger.
58
+ - The delivery message carries the closing briefing: how many sources the answer rests on (total + unique domains) and how many minutes the run took.
59
+ - The session journal reconstructs what was searched, found, and expanded, wave by wave, and it was written in real time rather than reconstructed at the end.
57
60
 
58
61
  ## Epistemic instrumentation
59
62
 
@@ -72,7 +75,12 @@ Observation candidates and claim candidates travel back from workers as message
72
75
  Saturation research defaults to teammode, not isolated fire-and-forget workers: a lead one worker surfaces almost always reshapes what another should search next. When your harness gives you real cooperating members — Codex: the `teammode` skill (MultiAgentV2 agents, or `codex_app` threads as its fallback); OpenCode: `team_mode` — run this swarm as a team. Fall back to the background-worker swarm below only when team mode is unavailable, or the axes are genuinely independent with no cross-pollination expected.
73
76
 
74
77
  - **One member per axis — by part, ownership, or perspective, never a job title.** Each Phase 0 axis is one member owning one concrete slice: a codebase part, a source territory, or a question lens. No two members share an angle. "Backend researcher" or "the web person" gives no real boundary and invites overlap — name what the member owns.
75
- - **Many teammates by default.** Prefer a larger roster, usually 5-8 teammates, whenever the axes can be made distinct. Add at least one skeptic or red-team perspective for hyperdebate/ultradebate: cross-critique claims, evidence quality, synthesis structure, and visual-report choices before they reach the final deliverable.
78
+ - **Always the maximum roster.** The team is not sized by taste: fill every member slot your harness allows (OpenCode `team_mode` caps members at 8; Codex teammode has its own ceiling) on every run. If you can only name five axes, split the broadest one — by source territory, by time window, by perspective until the roster is full. A half-empty team is a half-covered topic.
79
+ - **Compose deliberately across everything you can spawn.** Before writing the roster, enumerate what this session actually has — every delegation tier the harness or config defines and every spawnable role — and give each slot the cheapest tier that can do ITS job: broad recon on fast tiers, contested analysis on deep tiers, attack lanes on the strongest reasoning tier. Mixed tiers by design, never one tier across the whole board.
80
+ - **Routing words from the user are literal.** "quick", "fast", "deep", "all quick", "max parallel" — in any language — are hard instructions, not mood. Route exactly as asked and journal `requested tier -> spawned worker -> fallback reason` for every slot. Silently promoting a "quick" roster to a heavier tier is a defect, and so is dropping to a cheaper one without saying why.
81
+ - **Debate members are mandatory for ultradebate/hyperdebate, default otherwise.** At least one skeptic or red-team perspective attacks claims, evidence quality, source independence, synthesis structure, and report choices before they reach the deliverable. When the user says ultradebate or hyperdebate, run at least two attacking perspectives and give every contested claim a full round.
82
+
83
+ **One team, or a sequence — decided by scale and precision.** One team is the floor, not the ceiling. When the brief shows 6+ axes, several source territories, or a long final document — or a wrong claim is expensive (legal, medical, financial, procurement, public-facing) — run the research team to convergence, disband it, and stand up a REFINEMENT team from your strongest reasoning tiers whose only job is to attack and sharpen the synthesis before the document is written. A fresh premium team reading a finished journal reasons better than the same researchers grading their own homework. Build each team from a written brief and disband it before the next one starts; never leave two research teams live at once.
76
84
  - **The raise law — broadcast every lead the instant it surfaces.** Members over-communicate relentlessly: every new lead, finding, contradiction, and dead end is raised to you the moment it surfaces, never hoarded for a final dump. Through long passes they send `WORKING: <axis> - <phase>`, and `BLOCKED: <reason>` the moment progress stops, so you always know a member is alive. Too many small updates is correct here; going quiet is the only failure.
77
85
  - **You lead; expand on each raised lead.** Members raise via message text, never write session files. Journal each lead and spawn its expansion the instant it lands (Phase 2), not only when a member's final reply arrives.
78
86
 
@@ -81,7 +89,7 @@ Saturation research defaults to teammode, not isolated fire-and-forget workers:
81
89
  Research workers (explore, librarian, browsing) differ by harness, but assume:
82
90
 
83
91
  - **Read-only.** Most research workers cannot write files. Never ask a worker to write the journal or any session file — every journal write is yours.
84
- - **No recursion.** Workers cannot spawn their own subagents. Depth comes from your expansion waves, not from worker-side recursion.
92
+ - **No recursion — workers AND members.** Workers cannot spawn their own subagents, and team members must not re-orchestrate: a member researches its axis and reports; it never stands up its own team, loads this skill, or fans out a research swarm of its own. Depth comes from YOUR expansion waves. Say so in every spawn message — a member that starts its own research protocol burns the run's budget on duplicated orchestration and returns nothing you can cite.
85
93
  - **Built-in brakes.** Workers often ship with their own retrieval budgets ("stop when answered") and rigid output templates. Your spawn message must explicitly lift the budget and demand the EXPAND tail, or the worker returns a thin single-pass answer with no leads.
86
94
  - **Capability routing.** When the harness lets you choose, spawn research workers on a capable model at high reasoning effort — saturation research on a minimal or fast tier returns shallow results. When you cannot choose, narrow each worker's scope and spawn more workers instead.
87
95
 
@@ -111,7 +119,8 @@ Before spawning anything, decompose the query. Start from "what must be true if
111
119
  <analysis>
112
120
  Core question: <the actual information need>
113
121
  Axes (3+ orthogonal): <axis — what to search, where, why> ...
114
- Codebase relevant: <yes/no> · External: <yes/no> · Browsing: <yes/no> · Verification likely: <yes/no> · Final material format: <HTML/PDF default | explicit format | markdown only>
122
+ Codebase relevant: <yes/no> · External: <yes/no> · Browsing: <yes/no> · Verification likely: <yes/no>
123
+ Scale: <axis count, source territories, target document length> · Precision demand: <what a wrong claim costs here> → lifecycle: <single team | research team then refinement team>
115
124
  </analysis>
116
125
  ```
117
126
 
@@ -125,6 +134,7 @@ This is `$SESSION_DIR`. The orchestrator owns the journal: you write every file
125
134
 
126
135
  - `wave-<N>-<kind>-<axis>.md` — your digest of each worker return: key findings, sources with URLs, and the worker's EXPAND markers verbatim.
127
136
  - `expansion-log.md` — per wave: workers spawned, markers gained, leads opened and closed.
137
+ - `excursion-log.md` — one ENTER row and one EXIT row per excursion: `excursion_id`, parent claim or axis, ENTER trigger, depth, workers spent, the EXIT rule that closed it, what it changed in the top-level answer (`none` is a valid, required answer), and the ulw-loop steer/evidence id it was mirrored into.
128
138
  - `intent-diff.md` — orchestrator-owned expected-truth ledger comparing intent/spec/history to observed reality.
129
139
  - `claim-graph.md` — orchestrator-owned claim graph linking every final assertion to observations, counterevidence, dependencies, and verdict.
130
140
  - `observation-manifest.md` — orchestrator-owned observation manifest with `observed_at`, temporal validity, artifact paths, and contamination notes.
@@ -134,6 +144,21 @@ This is `$SESSION_DIR`. The orchestrator owns the journal: you write every file
134
144
 
135
145
  Append each digest the moment its worker returns, not in a batch at the end — the journal is your recovery point after context loss and the user's audit trail.
136
146
 
147
+ ### Run it as a loop, and journal in real time
148
+
149
+ ulw-loop is ON by default for this mode: when the `ulw-loop` skill is available, register the research axes as loop goals so the run has durable state and survives a compaction. The session directory's timestamp is the run's start clock — the closing briefing is computed from it, so create it once and never rename it. From that point every finding, source, quote, number, and lead is written into `$SESSION_DIR` **the instant it lands** — never held in the conversation for an end-of-run dump. After any context loss, re-read the brief and the journal before doing anything else, then resume from the open wave.
150
+
151
+ ### Format-proposal gate — ALWAYS ask, before the first wave
152
+
153
+ Never guess the shape of the deliverable. After the decomposition and before spawning wave 1, propose the final materials and WAIT for the user's answer:
154
+
155
+ - **Default pair: PDF + DOCX.** Offer both as the baseline for any report/document request.
156
+ - Name the alternatives that actually fit THIS domain — slides for a briefing, standalone HTML for a living page, Markdown for a working note, several at once when the audience differs.
157
+ - Propose the TEMPLATE too, chosen from the domain and the user's own context: section skeleton, citation style, length target, language, and any house style they have used before. A prior document the user points at is the strongest template signal — read it and mirror its structure and tagging.
158
+ - Ask once, compactly: proposed format + proposed template + what each option costs. Then stop and wait. Guessing here wastes the entire assembly pass.
159
+
160
+ Record the answer in the journal; Phase 5 opens by turning it into `design-spec.md`.
161
+
137
162
  ## Phase 1 — Saturation wave
138
163
 
139
164
  Launch the entire first wave in one turn — every axis at once, as team members if you formed a team, else as background workers. Sequential launches and "start with one and see" defeat the mode.
@@ -148,11 +173,13 @@ Scaling floor — more angles always justify more workers:
148
173
  | Multi-faceted | 4 | 6 | 2 | 2 | 14 |
149
174
  | Full due diligence | 4 | 6 | 3 | 2 | 15 |
150
175
 
176
+ **Disambiguate before you expand.** When the topic names something that could resolve several ways — a product, a person, a codename, a version — the first wave settles WHICH entity before any worker researches its history, benchmarks, or controversies: canonical name, first-party URL or account, whether it exists in the claimed category, and a confidence line. An unresolved entity never becomes a premise in a later wave's spawn message; that is exactly how a run starts inventing facts about something that does not exist.
177
+
151
178
  Role protocols — embed the relevant one in each spawn message; every worker gets a unique angle:
152
179
 
153
180
  - **Codebase (explore), 2-4 workers.** Grep with 3+ keyword variations; structural/AST search; LSP definitions and references; file-name globs; `git log --all -S '<keyword>'` and `--grep` for history including deleted code. Cross-validate hits across tools. Report absolute file paths, patterns with `file:line`, and how findings connect.
154
181
  - **Web (librarian), 3-6 workers.** At least 10 distinct websearch queries per worker, each with a different operator or angle (see Search craft); fetch the full page for every result that matters — snippets lie. Context7 with 3+ queries per known library. grep.app and `gh search code|repos|issues` for real-world usage. Official docs via sitemap discovery (`<base>/sitemap.xml`), then targeted pages.
155
- - **Browsing, 0-3 workers.** Pages plain fetch cannot read (WAF, 403, Cloudflare, dynamic rendering, login): the worker loads the `ultimate-browsing` skill and escalates through its tiers — Tier-1 insane-search engine first, then Tier-2 Chrome stealth — rather than abandoning the source. Capture screenshots when visual context matters. When one blocked territory hides many leads, fan out more browsing subagents in parallel for breadth instead of serializing one worker through them.
182
+ - **Browsing, 0-3 workers.** Pages plain fetch cannot read (WAF, 403, Cloudflare, dynamic rendering, login): the worker loads the `ultimate-browsing` skill and escalates through its tiers — Tier-1 insane-search engine first (including its Phase-2.5 archive surrogates), then Tier-2 Chrome stealth — rather than abandoning the source. Capture screenshots when visual context matters. **Provenance is part of the claim**: when a source came back with `provenance` of `snapshot` (an archive copy), cite it with its `snapshot_timestamp` and never state it as the current live page; content from a `proxy` route is `untrusted` and needs a second independent route before any claim rests on it. When one blocked territory hides many leads, fan out more browsing subagents in parallel for breadth instead of serializing one worker through them.
156
183
  - **Repo deep-dive (librarian), 0-2 workers.** Shallow-clone the most relevant repos to `${TMPDIR:-/tmp}`, pin the HEAD SHA, read core modules, follow call chains, return SHA-pinned permalinks.
157
184
 
158
185
  Example spawn (codebase axis; librarian, browsing, and repo-dive follow the same contract with their own protocol):
@@ -169,7 +196,7 @@ End your reply with the ## EXPAND tail: '- LEAD: <discovery> — WHY: <why> —
169
196
 
170
197
  This loop is what makes the mode research rather than search. Collect returns as they land — and in team mode, act on each lead the moment a member raises it, never waiting for the full wave or a member's final reply:
171
198
 
172
- 1. Journal the return: digest plus verbatim EXPAND markers into `wave-<N>-<kind>-<axis>.md`.
199
+ 1. Journal the return the moment it lands, never at the end of the wave: digest plus verbatim EXPAND markers into `wave-<N>-<kind>-<axis>.md`, appending each new source, quote, and number to the observation manifest in the same beat — after a compaction the journal, not your memory, is the state.
173
200
  2. Deduplicate new markers against `expansion-log.md` — every lead ever seen, not just confirmed ones, or rejected leads resurface each wave.
174
201
  3. Spawn an expansion worker immediately for each new unchecked lead:
175
202
 
@@ -180,7 +207,34 @@ PARENT: <which return surfaced it>. This is an explicit exhaustive-research assi
180
207
  End your reply with the ## EXPAND tail.")
181
208
  ```
182
209
 
210
+ ### Excursions — dive deep on a new find, then surface back out
211
+
212
+ The Phase 0 core question is the fixed goal of the run and never drifts. An excursion is a BOUNDED detour off the wave plan to chase something a return surfaced — you go deep, settle it, and come back up to the question you were hired to answer.
213
+
214
+ **ENTER (dive) only on a trigger.** One of these must hold, and you name which one:
215
+
216
+ 1. The find contradicts a claim already locked in `claim-graph.md`.
217
+ 2. It would change the final answer or a recommendation if it turned out to be true.
218
+ 3. It exposes a source territory no axis owns, so nobody else will ever reach it.
219
+ 4. The user's steering points at it — their words are the trigger, quoted verbatim.
220
+
221
+ Interest alone is not a trigger. Anything without one stays a queued lead in `expansion-log.md`, and the wave plan continues.
222
+
223
+ **Budget the dive before you take it.** State the worker count and the probe count for this level in the ENTER row. An excursion may spawn at most ONE nested sub-excursion; a third level means the thing has become its own research question — surface immediately and either promote it to a real axis with its own worker or record it as an out-of-scope gap in `SYNTHESIS.md`. When the `ulw-loop` skill is driving the run, a promotion is `omo-agent-toolkit ulw-loop steer --kind add_subgoal --title "<axis>" --objective "<what it must answer>" --evidence "<what surfaced it>" --rationale "<why the plan changes>"`.
224
+
225
+ **EXIT (surface) the moment any of these holds** — you do not need all of them:
226
+
227
+ - The ENTER trigger is resolved: the claim is confirmed, refuted, or its dependency is closed.
228
+ - Two consecutive probes changed nothing in the parent answer.
229
+ - The finding stops moving any claim's status — diminishing return is an exit, not a reason to push harder.
230
+ - The level's stated budget is spent.
231
+
232
+ **Fold back on the way out.** Every EXIT writes one line saying what the excursion changed in the top-level answer, and `none — <reason>` is a legitimate, required outcome; an excursion whose result is silently dropped is a lost run. Update the parent claim node or axis digest with the result, then mirror the whole excursion into the loop ledger — `omo-agent-toolkit ulw-loop steer --kind annotate_ledger --evidence "<what the excursion observed>" --rationale "<what it changed, or none>"`, and `omo-agent-toolkit ulw-loop record-evidence --goal-id <id> --criterion-id <id> --status pass|fail|blocked --evidence "<artifact>"` when it settled a criterion. After a compaction, `omo-agent-toolkit ulw-loop status --json` plus `excursion-log.md` tell you which excursions are still open.
233
+
234
+ **Anti-drift.** After every EXIT, re-read the core question in the journal and confirm the run still answers it. Three consecutive excursions that changed nothing end excursions for the run: converge on what you have.
235
+
183
236
  4. Record the wave in `expansion-log.md`: spawned, markers gained, leads opened/closed.
237
+ 5. **Relay the user's steering to everyone.** When the user changes scope, cadence, target sources, language, or format mid-run, broadcast it to every live worker and member immediately and record the exact wording in `expansion-log.md`. Steering only you saw silently splits the swarm's assignment from the user's actual ask.
184
238
 
185
239
  **Convergence — the only stop rules while this mode is active.** Run at least 2 expansion waves on any multi-faceted query before claiming convergence; then stop only when one holds:
186
240
 
@@ -188,6 +242,8 @@ End your reply with the ## EXPAND tail.")
188
242
  - 3 consecutive waves produced no new actionable leads.
189
243
  - Expansion depth reached 5 waves — pause, show the open leads, and ask the user whether to extend.
190
244
 
245
+ **Never end the run on a worker's completion.** Workers finishing is not the deliverable; your synthesis is. Reserve the last fifth of the run's context and time for Phases 4-5 and stop opening waves the moment that reserve is threatened. A converged answer with two open leads beats nine finished workers and no report.
246
+
191
247
  ## Phase 3 — Verify contested claims by running code
192
248
 
193
249
  Settle with executed code, not judgment, whenever sources disagree, a behavior is undocumented, a claim is performance- or compatibility-shaped, or the honest answer is "it should work". Spawn one verification worker per claim:
@@ -228,7 +284,7 @@ After convergence and all verifications, re-read the whole journal, start from `
228
284
 
229
285
  ```
230
286
  # ULW-Research Synthesis: <query>
231
- Workers: <total> · Waves: <count> · Sources: <count> · Verifications: <count>
287
+ Workers: <total> · Waves: <count> · Excursions: <count> · Sources: <count> (<unique domains> domains) · Verifications: <count> · Elapsed: <minutes> min
232
288
 
233
289
  ## Executive summary — 2-3 paragraphs answering the core question
234
290
  ## Findings by theme — per theme: consensus, evidence links, key quote (<20 words, attributed), verified yes/no
@@ -243,13 +299,56 @@ Workers: <total> · Waves: <count> · Sources: <count> · Verifications: <count>
243
299
 
244
300
  `SYNTHESIS.md` is the citation source of truth for final materials: every claim carries inline `[Source N]` citations, and every high-risk non-code claim you assert must be a verified-claims row from Phase 3b. Assert nothing the gate left in the unresolved/refuted annex.
245
301
 
302
+ **Write the skeleton early and fill it as claims lock.** The moment the format gate is answered, create the deliverable file with its approved section headings and a `STATUS: draft — <n> sections open` line at the top. An interrupted run must leave a partial report on disk, never an empty directory and a lost conversation.
303
+
304
+ **Keep sourced numbers, assumptions, and derived results visibly apart.** Every quantitative claim carries its lineage: `MEASURED` (a number a source states, cited), `ASSUMED` (a coefficient, distribution, or scope you chose — say why), `DERIVED` (computed from those, showing the formula), plus a sensitivity line whenever the assumption moves the answer. Presenting a derived estimate with the confidence of a measured one is the most damaging thing this mode can ship.
305
+
306
+ **Search in English, deliver in the user's language.** Retrieval stays English-first (Search craft), but the synthesis and every final material are written in the language the user wrote to you in unless they ask otherwise — and a translated report still quotes its original-language sources verbatim.
307
+
246
308
  ## Phase 5 — Final materials
247
309
 
248
- Default final materials to HTML/PDF unless the user explicitly asks for a different format: "report" / "document" → HTML first, with a PDF default available through weasyprint (`uv run --with weasyprint python`) · "pdf" → HTML first, then weasyprint · "slides" / "presentation" / "deck" → python-pptx · "html" / "webpage" → standalone HTML · "markdown only" → Markdown.
310
+ The format answered at the Phase 0 gate is binding. Absent an explicit user override, render **both PDF and DOCX**:
311
+
312
+ | Target | How |
313
+ |---|---|
314
+ | PDF (default) | Author the report as one self-contained HTML file, then print it headless: `chrome --headless --disable-gpu --no-pdf-header-footer --print-to-pdf=<out.pdf> file://<report.html>`. Embed the `design-spec.md` fonts as real webfonts (CJK included) instead of trusting system fallbacks. `uv run --with weasyprint python` is the fallback renderer. |
315
+ | DOCX (default) | `pandoc <report.md> -o <out.docx>`, adding `--reference-doc=<template.docx>` when the user has a house style; `uv run --with python-docx python` when pandoc is unavailable. Charts and Mermaid renders go in as images. |
316
+ | Slides / deck | `uv run --with python-pptx python` — one claim per slide, a chart or diagram per claim. |
317
+ | Standalone HTML / Markdown | The authored source itself. |
318
+
319
+ **Write `design-spec.md` the moment the format gate is answered — before any asset worker spawns.** It is the one design contract every asset and assembly worker receives: template family (a document the user pointed at is the strongest signal — mirror its structure and register; absent one, default to the clean analyst-report register — restrained accent palette, generous margins, styled section headings, no emoji, no clipart), accent palette, body/heading fonts (a real CJK webfont — Pretendard, Noto Sans KR — when the report language needs one), and the figure standard below. One font family and one palette govern prose, charts, Mermaid, and generated images alike; a diagram rendering in a random default font inside a styled report is a defect, not a style choice.
320
+
321
+ **The figure standard — binding for every image, chart, and diagram.** Each figure sits in a fixed-size container styled from the spec (border, background, caption); the image scales to fit entirely inside it with its original aspect ratio preserved — object-fit: contain semantics — never stretched, never cropped, never spilling out. Every chart carries a title, axis labels, units, and value labels in the report's language; a bare number the reader cannot name is a defect.
322
+
323
+ Asset workers (background, parallel, each fed `design-spec.md`) — visuals are the DEFAULT deliverable of this phase, not garnish the user must ask for; a delivered report without figures is an incomplete run:
324
+
325
+ - **Charts for every quantitative finding, computed from real data.** Pull the numbers into an actual table first (CSV/JSON under `$SESSION_DIR`), then plot from that table, never from prose. Follow the data-scientist tool doctrine — numpy always, Polars for filtering/sorting/transforms, DuckDB for joins/aggregations/window functions, never pandas — and load the `data-scientist` skill when this session has it: `uv run --with numpy --with polars --with duckdb --with pyarrow --with matplotlib python`. Keep `pyarrow` in that set — the DuckDB-to-Polars handoff (`.pl()`) fails without it, and `.df()` fails without pandas, so hand data across through `.pl()`, never `.df()`. Save to `$SESSION_DIR/assets/`.
326
+ - **Mermaid graphs** for process, architecture, argument, timeline, and evidence-flow structure, themed to the spec's fonts and palette. Render each to SVG and confirm the file exists before the document references it.
327
+ - **Generated visuals through the imagegen skill whenever the session has it:** a cover plus a concept illustration per major theme, prompted from the spec's style, palette, and mood — document-styled illustration, never generic stock art dropped into a designed page.
328
+ - **Full-page screenshots** of the top 5-10 sources (browsing worker) as provenance you can show.
329
+
330
+ **Verify the asset manifest before rendering.** List every asset the document references, assert each file exists and is non-empty on disk, and re-render whatever is missing. A document that renders with three broken diagrams is a document you will publish twice.
331
+
332
+ Assembly worker — `task(category="deep", load_skills=["frontend", "visual-qa", "open-design", "data-scientist", "imagegen", "ulw-loop"], run_in_background=true, ...)`: before writing, read every available design and visualization skill and apply it — the report is a designed artifact, not a text dump; the worker's prompt carries `design-spec.md`. Use the template the user approved; absent a stronger house style the default skeleton is executive summary → key findings by theme → detailed analysis (quotes under 20 words with attribution, charts, Mermaid graphs, generated visuals, SHA-pinned permalinks, verification results) → comparative analysis when options compete → numbered sources with access dates → methodology appendix (workers, waves, searches, verifications, debate rounds) → correction log naming what verification overturned. Write it long and specific: every claim cites `[Source N]`, and the sources section lists every source the run actually used rather than a curated few.
333
+
334
+ ### The delivery gate — visual QA must PASS
335
+
336
+ Nothing reaches the user until the gate passes:
337
+
338
+ **Visual QA (always).** Render the produced artifact back to images — PDF pages to PNG, the HTML in a real browser — and look at them: missing or broken figures, images stretched or spilling their containers, diagram or chart text rendered off the spec's font or palette, clipped tables, overflowing CJK text, blank pages, unlabeled chart values, wrong page breaks. Fix and re-render until the pages are clean. Reading the source markup is not visual QA; inspect the pixels.
339
+
340
+ Then deliver: the artifact plus a compact chat-readable summary of what it says — the answer in a few sentences, the numbers that matter, and what to look at first. The document is the deliverable; the summary is what gets it read.
341
+
342
+ ### The closing briefing — every run ends with it
343
+
344
+ The last thing the user reads states, in one compact block, what the answer is made of:
249
345
 
250
- Asset workers (background, parallel): actively use charts for quantitative findings (`uv run --with matplotlib --with plotly python`) saved by you to `$SESSION_DIR/assets/`; Mermaid graphs for process, architecture, argument, and evidence-flow structure; full-page screenshots of the top 5-10 sources (browsing skill); generated diagrams or editorial visuals with the imagegen skill when architecture, flows, or narrative framing benefit from bitmap assets.
346
+ - **Sources.** How many sources the answer rests on and how many distinct domains they come from, counted from the journal's source ledger, not estimated. Name how many were primary sources and how many claims went to the unresolved/refuted annex.
347
+ - **Effort.** Workers, waves, excursions, and verifications — the same counters as the `SYNTHESIS.md` header.
348
+ - **Elapsed time, always.** Minutes from the run's start to delivery, derived from the session directory's own timestamp so it cannot be guessed: `python3 -c "import datetime,os,sys; s=datetime.datetime.strptime(os.path.basename(sys.argv[1]),'%Y%m%d-%H%M%S'); print(round((datetime.datetime.now()-s).total_seconds()/60))" "$SESSION_DIR"`.
251
349
 
252
- Assembly worker `task(category="deep", load_skills=["frontend", "visual-qa", "open-design", "data-scientist", "imagegen", "ulw-loop"], run_in_background=true, ...)`: before writing, read every available design and visualization skill and apply it — the report is a designed artifact, not a text dump. Run HTML/PDF output through the ULW loop with frontend and visual-qa, then repair until the reviewer says no broken parts and gives approval. Structure: executive summary → key findings by theme → detailed analysis (quotes under 20 words with attribution, charts, Mermaid graphs, generated visuals, SHA-pinned permalinks, verification results) → comparative analysis when options compete → numbered sources with access dates → methodology appendix (workers, waves, searches, verifications). Every claim cites `[Source N]`.
350
+ Never ship the artifact without this block, and never fill it from memory every number in it is read off the journal.
351
+ If you stood up a team, disband it and confirm every worker is terminal before the final answer.
253
352
 
254
353
  ## Search craft
255
354
 
@@ -281,3 +380,19 @@ High-yield combinations: official docs (`site:<docs domain>`), GitHub implementa
281
380
  | Two workers given the same angle | One unique angle per worker, always |
282
381
  | Contested claim settled by judgment | Phase 3 — run code, capture output, verdict |
283
382
  | Deliverable claims without citations | Every claim cites a source or a verification artifact |
383
+ | Guessing the deliverable format instead of asking | The format gate is unconditional: propose PDF+DOCX plus the domain-fitting alternatives and the template, then wait before wave 1 |
384
+ | A roster smaller than the harness ceiling | Fill every member slot; split the broadest axis until the team is full |
385
+ | One tier across the whole roster | Mixed tiers by design — cheap breadth, premium attack |
386
+ | Silently re-routing a "quick"/"fast" instruction | Routing words are literal; journal requested -> spawned -> fallback per slot |
387
+ | A worker or member that starts its own research swarm | Members research one axis and report; orchestration is yours alone |
388
+ | Expanding on an entity the first wave never disambiguated | Settle canonical identity and first-party source before any later spawn asserts it |
389
+ | Batching findings into an end-of-run journal dump | Journal each return as it lands; the journal is what survives a compaction |
390
+ | Ending the run because every worker finished | Reserve the final fifth of the run for synthesis and materials |
391
+ | A derived estimate presented as a measured number | MEASURED / ASSUMED / DERIVED lineage on every quantitative claim, plus a sensitivity line |
392
+ | Delivering before the delivery gates pass | Visual QA on rendered pages always, plus the harness's proofread gate — a typo the user finds means a gate did not run |
393
+ | Referencing an asset that is not on disk | Verify the asset manifest before rendering; re-render whatever is missing |
394
+ | A figure stretched, cropped, or styled off the report's design language | `design-spec.md` binds every asset: fixed containers, contain-fit with aspect preserved, spec fonts and palette in charts and Mermaid |
395
+ | Chasing an interesting find with no ENTER trigger | Excursions need a named trigger; everything else stays a queued lead |
396
+ | An excursion that never came back, or drifted into a new mission | EXIT rules are unconditional; depth 3 means promote it to an axis or record it as a gap |
397
+ | An excursion whose result was never folded back | Every EXIT writes what it changed in the top-level answer, `none` included, and mirrors into the loop ledger |
398
+ | Delivering without the closing briefing | Source count, unique domains, and elapsed minutes are read off the journal and stated every time |
@@ -12,9 +12,9 @@ const { runBootstrapWorker, runWorkerSetup } = await import(CLI_URL.href);
12
12
  const MARKETPLACE_SOURCE_LINE = 'source = "https://github.com/code-yeongyu/lazycodex.git"';
13
13
  const COMPONENT_BIN_NAME = "omo-toolbox";
14
14
  const execFileAsync = promisify(execFile);
15
- const OMO_CLI_DEGRADED_ENTRY = {
16
- component: "omo-cli",
17
- hint: "use npx lazycodex-ai for the omo CLI",
15
+ const OMO_AGENT_TOOLKIT_DEGRADED_ENTRY = {
16
+ component: "omo-agent-toolkit",
17
+ hint: "use npx lazycodex-ai for the omo-agent-toolkit CLI",
18
18
  reason: "marketplace payload has no dist/cli",
19
19
  };
20
20
 
@@ -205,7 +205,7 @@ test("#given a completed v1 marker #when the worker runs against a v2 root #then
205
205
  });
206
206
  });
207
207
 
208
- test("#given platform win32 #when the worker setup links bins #then component bins become .cmd shims and the omo wrapper is omo.cmd", async () => {
208
+ test("#given platform win32 #when the worker setup links bins #then component bins become .cmd shims and the canonical wrapper is omo-agent-toolkit.cmd", async () => {
209
209
  await withBinLinkFixture(async (fixture) => {
210
210
  const bashPath = "C:\\Tools\\Git\\bin\\bash.exe";
211
211
  const pluginRoot = await writeVersionedRoot(fixture.root, "1.0.0", { withRuntimeCli: true });
@@ -226,19 +226,19 @@ test("#given platform win32 #when the worker setup links bins #then component bi
226
226
  assert.match(shim, /"%OMO_NODE_BINARY%"/);
227
227
  assert.match(shim, new RegExp(`"${escapeRegExp(componentEntrypoint)}" %\\*`));
228
228
  assert.doesNotMatch(shim, new RegExp(`node "${escapeRegExp(componentEntrypoint)}" %\\*`));
229
- const wrapper = await readFile(join(fixture.binDir, "omo.cmd"), "utf8");
229
+ const wrapper = await readFile(join(fixture.binDir, "omo-agent-toolkit.cmd"), "utf8");
230
230
  assert.ok(wrapper.includes(join(pluginRoot, "dist", "cli", "index.js")));
231
231
  await assert.rejects(() => lstat(join(fixture.binDir, COMPONENT_BIN_NAME)), "win32 must not leave posix symlinks behind");
232
232
  });
233
233
  });
234
234
 
235
- test("#given a legacy payload without root CLI dist #when the worker setup runs #then it records degraded omo-cli and leaves no broken link", async () => {
235
+ test("#given a legacy payload without root CLI dist #when the worker setup runs #then it records degraded omo-agent-toolkit and leaves no broken link", async () => {
236
236
  await withBinLinkFixture(async (fixture) => {
237
237
  const pluginRoot = await writeVersionedRoot(fixture.root, "1.0.0");
238
238
 
239
239
  const outcome = await runWorkerSetup(setupOptions(fixture, pluginRoot, { now: 7_000, platform: process.platform }));
240
240
 
241
- assert.deepEqual(outcome.degraded, [OMO_CLI_DEGRADED_ENTRY]);
241
+ assert.deepEqual(outcome.degraded, [OMO_AGENT_TOOLKIT_DEGRADED_ENTRY]);
242
242
  await assert.rejects(() => lstat(join(fixture.binDir, "omo")), "no omo wrapper may be written without dist/cli");
243
243
  await assert.rejects(() => lstat(join(fixture.binDir, "omo.cmd")), "no Windows omo wrapper may be written without dist/cli");
244
244
  await assertNoDanglingEntries(fixture.binDir);
@@ -246,15 +246,15 @@ test("#given a legacy payload without root CLI dist #when the worker setup runs
246
246
  const warning = JSON.parse(log)["warning"];
247
247
  assert.equal(typeof warning, "string", `bootstrap.log warning must be a string, got: ${log}`);
248
248
  assert.ok(
249
- warning.includes("skipped the omo runtime wrapper because ") &&
249
+ warning.includes("skipped the omo-agent-toolkit runtime wrapper because ") &&
250
250
  warning.includes(`${join("dist", "cli", "index.js")} is missing; `) &&
251
- warning.includes("omo ulw-loop commands will be unavailable until a package shipping dist/cli is installed"),
251
+ warning.includes("omo-agent-toolkit ulw-loop commands will be unavailable until a package shipping dist/cli is installed"),
252
252
  `bootstrap.log must carry the install-local warning text, got: ${log}`,
253
253
  );
254
254
  });
255
255
  });
256
256
 
257
- test("#given a payload shipping dist/cli #when the worker setup runs with no bin-dir override #then the omo wrapper lands in <codexHome>/bin without a degraded entry", async () => {
257
+ test("#given a payload shipping dist/cli #when the worker setup runs with no bin-dir override #then the omo-agent-toolkit wrapper lands in <codexHome>/bin without a degraded entry", async () => {
258
258
  await withBinLinkFixture(async (fixture) => {
259
259
  const pluginRoot = await writeVersionedRoot(fixture.root, "1.0.0", { withRuntimeCli: true });
260
260
 
@@ -262,12 +262,12 @@ test("#given a payload shipping dist/cli #when the worker setup runs with no bin
262
262
 
263
263
  assert.deepEqual(outcome.degraded, []);
264
264
  const defaultBinDir = join(fixture.codexHome, "bin");
265
- const wrapperName = process.platform === "win32" ? "omo.cmd" : "omo";
265
+ const wrapperName = process.platform === "win32" ? "omo-agent-toolkit.cmd" : "omo-agent-toolkit";
266
266
  const wrapperPath = join(defaultBinDir, wrapperName);
267
267
  const wrapper = await readFile(wrapperPath, "utf8");
268
268
  assert.ok(wrapper.includes(join(pluginRoot, "dist", "cli", "index.js")));
269
269
  if (process.platform !== "win32") {
270
- assert.ok((await stat(wrapperPath)).mode & 0o111, "the posix omo wrapper must be executable");
270
+ assert.ok((await stat(wrapperPath)).mode & 0o111, "the posix omo-agent-toolkit wrapper must be executable");
271
271
  assert.equal(
272
272
  await readlink(join(defaultBinDir, COMPONENT_BIN_NAME)),
273
273
  join(pluginRoot, "components", "toolbox", "dist", "cli.js"),
@@ -321,7 +321,7 @@ test("#given a completed marker #when the worker runs with --once #then the mark
321
321
  });
322
322
  });
323
323
 
324
- test("#given --only sg #when the worker runs #then only the matching step executes", async () => {
324
+ test("#given --only names an injected custom step #when the worker runs #then only the matching step executes", async () => {
325
325
  await withFixture(async (fixture) => {
326
326
  const ran = [];
327
327
  const step = (name) => ({
@@ -333,13 +333,45 @@ test("#given --only sg #when the worker runs #then only the matching step execut
333
333
  });
334
334
 
335
335
  const result = await runBootstrapWorker({
336
- argv: ["--only", "sg"],
336
+ argv: ["--only", "custom"],
337
337
  env: fixture.env,
338
- steps: [step("setup"), step("sg")],
338
+ steps: [step("other"), step("custom")],
339
339
  });
340
340
 
341
341
  assert.equal(result.ran, true);
342
- assert.deepEqual(ran, ["sg"]);
342
+ assert.deepEqual(ran, ["custom"]);
343
+ });
344
+ });
345
+
346
+ test("#given an unknown --only value #when the worker starts #then it rejects before steps or bootstrap artifacts", async () => {
347
+ await withFixture(async (fixture) => {
348
+ let stepRuns = 0;
349
+
350
+ await assert.rejects(
351
+ () =>
352
+ runBootstrapWorker({
353
+ argv: ["--only", "bogus"],
354
+ env: fixture.env,
355
+ steps: [
356
+ {
357
+ name: "custom",
358
+ run: async () => {
359
+ stepRuns += 1;
360
+ return { degraded: [] };
361
+ },
362
+ },
363
+ ],
364
+ }),
365
+ /unknown --only flag value: bogus/,
366
+ );
367
+ assert.equal(stepRuns, 0);
368
+ await assert.rejects(() => stat(join(fixture.pluginData, "bootstrap")));
369
+ await Promise.all([
370
+ assert.rejects(() => stat(join(fixture.pluginData, "bootstrap", "state.json"))),
371
+ assert.rejects(() => stat(join(fixture.pluginData, "bootstrap", "bootstrap.log"))),
372
+ assert.rejects(() => stat(join(fixture.pluginData, "bootstrap", "state.json.lock"))),
373
+ assert.rejects(() => stat(join(fixture.pluginData, "auto-update.json.lock"))),
374
+ ]);
343
375
  });
344
376
  });
345
377
 
@@ -261,7 +261,7 @@ test("#given start-work skill #when synced for Codex #then the difficulty-tier d
261
261
  const content = await readSkill("start-work");
262
262
 
263
263
  assert.match(content, /lazycodex-worker-medium/);
264
- assert.match(content, /Delegation by difficulty/);
264
+ assert.match(content, /Codex tier mapping for the delegation router/);
265
265
  assert.match(content, /Global Review and Debugging Gate/);
266
266
  assert.match(content, /full commit SHA/);
267
267
  assert.match(content, /re-read the ledger record/);
@@ -8,6 +8,7 @@ export const expectedSkills = [
8
8
  "ast-grep",
9
9
  "coding-agent-sessions",
10
10
  "comment-checker",
11
+ "data-scientist",
11
12
  "debugging",
12
13
  "frontend",
13
14
  "git-master",
@@ -89,9 +90,9 @@ const startWorkCodexCompletion = `When all top-level checkboxes in \`## TODOs\`
89
90
  4. Remove or mark the Boulder work as completed.
90
91
  5. Print an \`ORCHESTRATION COMPLETE\` block with the plan path, verification commands, Global Review and Debugging Gate verdict, artifacts, and cleanup receipts.`;
91
92
 
92
- const startWorkOriginalHardRule = "- 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 PR/branch implementation, review, or merge in the main worktree; use the task-owned git worktree.\n- No unprefixed session ids in Boulder state. Codex sessions are always `codex:<session_id>`.";
93
+ const startWorkOriginalHardRule = "- 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 PR/branch implementation, review, or merge in the main worktree; use the task-owned git worktree.\n- No unprefixed session ids in Boulder state. Sessions are always recorded as `codex:<session_id>`.";
93
94
 
94
- 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>`.";
95
+ 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. Sessions are always recorded as `codex:<session_id>`.";
95
96
 
96
97
  const reviewWorkCodexGate = `
97
98
  When \`review-work\` is used as a final implementation, PR, or \`$start-work\`
@@ -126,7 +127,13 @@ prefixes when identity is needed.
126
127
  `;
127
128
  const reviewWorkCodexGatePattern = new RegExp(reviewWorkCodexGate.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
128
129
 
130
+ const ulwResearchOriginalDeliveryGates = "### The delivery gates \u2014 every gate must PASS, in order\n\nNothing reaches the user until the gates pass:\n\n1. **Visual QA (always).** Render the produced artifact back to images \u2014 PDF pages to PNG, the HTML in a real browser \u2014 and look at them: missing or broken figures, images stretched or spilling their containers, diagram or chart text rendered off the spec's font or palette, clipped tables, overflowing CJK text, blank pages, unlabeled chart values, wrong page breaks. Fix and re-render until the pages are clean. Reading the source markup is not visual QA; inspect the pixels.\n2. **Proofread gate \u2014 `task(category=\"writing\", ...)`.** Hand the final text to a dedicated `writing` worker whose only job is language: grammar, spelling, punctuation, terminology consistency, and whether the prose reads NATIVELY in the report's own language. It returns a defect list; fix every item and re-run the gate on the delta. Deliver only on a clean pass \u2014 this gate runs BEFORE the first delivery, not after the user finds the typo.";
131
+ const ulwResearchCodexDeliveryGate = "### The delivery gate \u2014 visual QA must PASS\n\nNothing reaches the user until the gate passes:\n\n**Visual QA (always).** Render the produced artifact back to images \u2014 PDF pages to PNG, the HTML in a real browser \u2014 and look at them: missing or broken figures, images stretched or spilling their containers, diagram or chart text rendered off the spec's font or palette, clipped tables, overflowing CJK text, blank pages, unlabeled chart values, wrong page breaks. Fix and re-render until the pages are clean. Reading the source markup is not visual QA; inspect the pixels.";
132
+
129
133
  export function removeCodexSkillOverlays(skillName, content) {
134
+ if (skillName === "ulw-research") {
135
+ return content.replace(ulwResearchCodexDeliveryGate, ulwResearchOriginalDeliveryGates);
136
+ }
130
137
  if (skillName === "start-work") {
131
138
  return content
132
139
  .replace(startWorkCodexCompletion, startWorkOriginalCompletion)
@@ -331,6 +331,18 @@ test("#given packaged Codex ulw-plan surfaces #when inspected #then dangerous sa
331
331
  assert.doesNotMatch(componentWorkflow.content, dangerousBypassPattern, `${componentWorkflow.path} ships unsafe Codex bypass guidance`);
332
332
  });
333
333
 
334
+ test("#given packaged ulw-research skill #when Codex delivery gates are inspected #then the category-based proofread gate is stripped", async () => {
335
+ // given
336
+ const content = await readFile(join(root, "skills", "ulw-research", "SKILL.md"), "utf8");
337
+
338
+ // then the writing-category proofread gate never ships to Codex (no category concept there)
339
+ assert.doesNotMatch(content, /category="writing"/, "codex ulw-research ships the writing-category proofread gate");
340
+ assert.doesNotMatch(content, /Proofread gate/, "codex ulw-research still references the proofread gate");
341
+ // and the visual-QA delivery gate survives as the single gate
342
+ assert.match(content, /### The delivery gate \u2014 visual QA must PASS/u, "codex ulw-research lost the visual-QA delivery gate");
343
+ assert.match(content, /Visual QA \(always\)/, "codex ulw-research lost the visual-QA gate body");
344
+ });
345
+
334
346
  test("#given context-pressure-prone skills #when bundled for Codex #then the eagerly loaded payload stays budgeted", async () => {
335
347
  // given
336
348
  const skillsRoot = join(root, "skills");
@@ -26,14 +26,61 @@ async function writeRuntimeWrapperFixture({ withNodeCli = false } = {}) {
26
26
  }
27
27
  await mkdir(homeDir, { recursive: true });
28
28
  const link = await linkRootRuntimeBin({ binDir, codexHome, repoRoot, platform: "linux" });
29
- return { homeDir, link, repoRoot };
29
+ return { binDir, codexHome, homeDir, link, repoRoot };
30
30
  }
31
31
 
32
32
  function runtimeWrapperEnv(homeDir) {
33
33
  return { PATH: `${dirname(process.execPath)}:/usr/bin:/bin`, HOME: homeDir };
34
34
  }
35
35
 
36
- test("#given bun absent from PATH but present in ~/.bun/bin #when running the omo runtime wrapper #then resolves the bun fallback", async (t) => {
36
+ test("#given the generated installer #when linking the root runtime #then writes only the canonical wrapper with marker and edition exports", async () => {
37
+ const { binDir, link } = await writeRuntimeWrapperFixture();
38
+
39
+ assert.equal(link.name, "omo-agent-toolkit");
40
+ assert.equal(link.path, join(binDir, "omo-agent-toolkit"));
41
+ const wrapper = await readFile(link.path, "utf8");
42
+ assert.match(wrapper, /OMO_GENERATED_RUNTIME_WRAPPER/);
43
+ assert.match(wrapper, /export OMO_INVOCATION_NAME=omo-agent-toolkit/);
44
+ assert.match(wrapper, /export OMO_EDITION=codex/);
45
+ await assert.rejects(lstat(join(binDir, "omo")));
46
+ });
47
+
48
+ test("#given a marker-bearing legacy omo #when linking the root runtime #then deletes it", async () => {
49
+ const root = await makeTempDir();
50
+ const repoRoot = join(root, "repo");
51
+ const binDir = join(root, "bin");
52
+ await mkdir(join(repoRoot, "dist", "cli"), { recursive: true });
53
+ await mkdir(binDir, { recursive: true });
54
+ await writeFile(join(repoRoot, "dist", "cli", "index.js"), "");
55
+ await writeFile(join(binDir, "omo"), "#!/bin/sh\n# OMO_GENERATED_RUNTIME_WRAPPER\n");
56
+
57
+ await linkRootRuntimeBin({ binDir, codexHome: join(root, "codex"), repoRoot, platform: "linux" });
58
+
59
+ await assert.rejects(lstat(join(binDir, "omo")));
60
+ assert.match(await readFile(join(binDir, "omo-agent-toolkit"), "utf8"), /OMO_GENERATED_RUNTIME_WRAPPER/);
61
+ });
62
+
63
+ test("#given an unmarked user-owned omo #when linking twice #then preserves it byte-identical and remains idempotent", async () => {
64
+ const root = await makeTempDir();
65
+ const repoRoot = join(root, "repo");
66
+ const binDir = join(root, "bin");
67
+ const legacyPath = join(binDir, "omo");
68
+ const canonicalPath = join(binDir, "omo-agent-toolkit");
69
+ const userContent = Buffer.from("#!/bin/sh\necho user-owned\n");
70
+ await mkdir(join(repoRoot, "dist", "cli"), { recursive: true });
71
+ await mkdir(binDir, { recursive: true });
72
+ await writeFile(join(repoRoot, "dist", "cli", "index.js"), "");
73
+ await writeFile(legacyPath, userContent);
74
+
75
+ await linkRootRuntimeBin({ binDir, codexHome: join(root, "codex"), repoRoot, platform: "linux" });
76
+ const firstCanonical = await readFile(canonicalPath);
77
+ await linkRootRuntimeBin({ binDir, codexHome: join(root, "codex"), repoRoot, platform: "linux" });
78
+
79
+ assert.deepEqual(await readFile(legacyPath), userContent);
80
+ assert.deepEqual(await readFile(canonicalPath), firstCanonical);
81
+ });
82
+
83
+ test("#given bun absent from PATH but present in ~/.bun/bin #when running the omo-agent-toolkit runtime wrapper #then resolves the bun fallback", async (t) => {
37
84
  if (process.platform === "win32") return t.skip("posix wrapper execution");
38
85
  const { homeDir, link } = await writeRuntimeWrapperFixture();
39
86
  await mkdir(join(homeDir, ".bun", "bin"), { recursive: true });
@@ -114,7 +161,12 @@ test("#given Windows platform #when writing the omo runtime wrapper #then embeds
114
161
 
115
162
  const link = await linkRootRuntimeBin({ binDir, codexHome: join(root, "codex"), repoRoot, platform: "win32" });
116
163
 
164
+ assert.equal(link.name, "omo-agent-toolkit");
165
+ assert.equal(link.path, join(binDir, "omo-agent-toolkit.cmd"));
117
166
  const wrapper = await readFile(link.path, "utf8");
167
+ assert.match(wrapper, /OMO_GENERATED_RUNTIME_WRAPPER/);
168
+ assert.match(wrapper, /set "OMO_INVOCATION_NAME=omo-agent-toolkit"/);
169
+ assert.match(wrapper, /set "OMO_EDITION=codex"/);
118
170
  assert.match(wrapper, /OMO_RUNTIME/);
119
171
  assert.match(wrapper, /dist[\\/]cli-node[\\/]index\.js/);
120
172
  assert.ok(wrapper.indexOf("OMO_RUNTIME") < wrapper.indexOf("where bun"), "node override must precede bun discovery");
@@ -246,6 +298,7 @@ test("#given nested component declares reserved omo bin #when linking bins #then
246
298
  name: "@example/ulw-loop",
247
299
  bin: {
248
300
  omo: "./dist/cli.js",
301
+ "omo-agent-toolkit": "./dist/cli.js",
249
302
  "omo-ulw-loop": "./dist/cli.js",
250
303
  ulw: "./dist/cli.js",
251
304
  "ulw-loop": "./dist/cli.js",
@@ -261,6 +314,7 @@ test("#given nested component declares reserved omo bin #when linking bins #then
261
314
  { name: "ulw-loop", path: join(binDir, "ulw-loop"), target: join(componentRoot, "dist", "cli.js") },
262
315
  ]);
263
316
  await assert.rejects(readlink(join(binDir, "omo")));
317
+ await assert.rejects(readlink(join(binDir, "omo-agent-toolkit")));
264
318
  assert.equal(await readlink(join(binDir, "omo-ulw-loop")), join(componentRoot, "dist", "cli.js"));
265
319
  assert.equal(await readlink(join(binDir, "ulw")), join(componentRoot, "dist", "cli.js"));
266
320
  assert.equal(await readlink(join(binDir, "ulw-loop")), join(componentRoot, "dist", "cli.js"));
@@ -3,7 +3,7 @@ import test from "node:test";
3
3
 
4
4
  import { buildDelegatedOmoInvocation, runDelegatedOmoCommand } from "./install-local.mjs";
5
5
 
6
- test("#given a lazycodex passthrough command #when delegating to omo #then resets OMO_INVOCATION_NAME so the delegate does not re-enter the lazycodex path", async () => {
6
+ test("#given a lazycodex passthrough command #when delegating to omo-agent-toolkit #then resets OMO_INVOCATION_NAME so the delegate does not re-enter the lazycodex path", async () => {
7
7
  // given
8
8
  const parsed = { kind: "command", command: "boulder", args: [] };
9
9
  let received;
@@ -25,12 +25,12 @@ test("#given a lazycodex passthrough command #when delegating to omo #then reset
25
25
  assert.equal(received.runOptions.cwd, "/tmp/project");
26
26
  assert.equal(
27
27
  received.runOptions.env?.OMO_INVOCATION_NAME,
28
- "omo",
29
- "delegated omo command must run with OMO_INVOCATION_NAME=omo to avoid infinite recursion",
28
+ "omo-agent-toolkit",
29
+ "delegated command must run with OMO_INVOCATION_NAME=omo-agent-toolkit to avoid infinite recursion",
30
30
  );
31
31
  });
32
32
 
33
- test("#given OMO_INVOCATION_NAME=lazycodex in the parent env #when delegating a cleanup passthrough #then the child env overrides it to omo", async () => {
33
+ test("#given OMO_INVOCATION_NAME=lazycodex in the parent env #when delegating a cleanup passthrough #then the child env overrides it to omo-agent-toolkit", async () => {
34
34
  // given
35
35
  const previous = process.env.OMO_INVOCATION_NAME;
36
36
  process.env.OMO_INVOCATION_NAME = "lazycodex";
@@ -52,7 +52,7 @@ test("#given OMO_INVOCATION_NAME=lazycodex in the parent env #when delegating a
52
52
  }
53
53
 
54
54
  // then
55
- assert.equal(received.env.OMO_INVOCATION_NAME, "omo");
55
+ assert.equal(received.env.OMO_INVOCATION_NAME, "omo-agent-toolkit");
56
56
  });
57
57
 
58
58
  test("#given a dry-run doctor #when delegating #then routes to the Codex LazyCodex doctor workflow", async () => {
@@ -239,5 +239,5 @@ test("#given dry-run args with shell metacharacters #when delegating #then logs
239
239
  });
240
240
 
241
241
  // then
242
- assert.equal(logged, "npx --yes --package oh-my-openagent omo cleanup --platform=codex --project '/tmp/lazy codex'\\''s qa'");
242
+ assert.equal(logged, "npx --yes --package oh-my-openagent omo-agent-toolkit cleanup --platform=codex --project '/tmp/lazy codex'\\''s qa'");
243
243
  });