oh-my-opencode 4.18.1 → 4.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +39 -16
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
  3. package/README.ja.md +2 -2
  4. package/README.ko.md +2 -2
  5. package/README.md +4 -4
  6. package/README.ru.md +2 -2
  7. package/README.zh-cn.md +2 -2
  8. package/bin/AGENTS.md +33 -0
  9. package/dist/agents/atlas/agent.d.ts +6 -5
  10. package/dist/agents/sisyphus/index.d.ts +5 -0
  11. package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
  12. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  13. package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
  14. package/dist/agents/sisyphus-junior/index.d.ts +2 -0
  15. package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
  16. package/dist/agents/types.d.ts +2 -2
  17. package/dist/cli/index.js +690 -139
  18. package/dist/cli-node/index.js +690 -139
  19. package/dist/config/index.d.ts +1 -1
  20. package/dist/config/schema/browser-automation.d.ts +1 -0
  21. package/dist/config/schema/commands.d.ts +1 -3
  22. package/dist/config/schema/default-mode.d.ts +1 -1
  23. package/dist/config/schema/goal.d.ts +7 -0
  24. package/dist/config/schema/hooks.d.ts +1 -1
  25. package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
  26. package/dist/config/schema/ralph-loop.d.ts +1 -1
  27. package/dist/config/schema.d.ts +1 -1
  28. package/dist/create-hooks.d.ts +1 -1
  29. package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
  30. package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
  31. package/dist/features/builtin-commands/types.d.ts +1 -1
  32. package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
  33. package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
  34. package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
  35. package/dist/hooks/comment-checker/hook.d.ts +8 -1
  36. package/dist/hooks/goal/command-arguments.d.ts +13 -0
  37. package/dist/hooks/goal/controller.d.ts +26 -0
  38. package/dist/hooks/goal/index.d.ts +23 -0
  39. package/dist/hooks/goal/prompt.d.ts +3 -0
  40. package/dist/hooks/goal/store.d.ts +8 -0
  41. package/dist/hooks/goal/tools.d.ts +10 -0
  42. package/dist/hooks/goal/types.d.ts +93 -0
  43. package/dist/hooks/goal/validation.d.ts +4 -0
  44. package/dist/hooks/index.d.ts +1 -0
  45. package/dist/hooks/keyword-detector/hook.d.ts +1 -2
  46. package/dist/hooks/ralph-loop/types.d.ts +1 -1
  47. package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
  48. package/dist/index.js +3706 -4329
  49. package/dist/oh-my-opencode.schema.json +23 -19
  50. package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
  51. package/dist/plugin/chat-message/types.d.ts +23 -10
  52. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
  53. package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
  54. package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
  55. package/dist/plugin/messages-transform.d.ts +1 -0
  56. package/dist/plugin/stop-continuation.d.ts +2 -2
  57. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
  58. package/dist/shared/system-directive.d.ts +0 -1
  59. package/dist/skills/ulw-plan/SKILL.md +11 -7
  60. package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
  61. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  62. package/dist/skills/ulw-research/SKILL.md +3 -3
  63. package/dist/tools/delegate-task/types.d.ts +0 -1
  64. package/dist/tui.js +115 -28
  65. package/package.json +14 -14
  66. package/packages/git-bash-mcp/dist/cli.js +81 -19
  67. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
  68. package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
  69. package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
  70. package/packages/lsp-daemon/dist/cli.js +262 -63
  71. package/packages/lsp-daemon/dist/client.js +194 -50
  72. package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
  73. package/packages/lsp-daemon/dist/daemon-client.js +26 -2
  74. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
  75. package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
  76. package/packages/lsp-daemon/dist/index.js +262 -63
  77. package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
  78. package/packages/lsp-daemon/dist/proxy.js +79 -23
  79. package/packages/lsp-tools-mcp/dist/cli.js +82 -20
  80. package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
  81. package/packages/lsp-tools-mcp/dist/tools.js +1 -1
  82. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  83. package/packages/omo-codex/plugin/.mcp.json +2 -1
  84. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
  85. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  86. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  87. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
  88. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +195 -56
  89. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
  90. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  91. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
  92. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
  93. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
  94. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
  95. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
  96. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  97. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  98. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  99. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  100. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  101. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  102. package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
  103. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
  104. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  105. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
  106. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  107. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  108. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
  109. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
  110. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
  111. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  112. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  113. package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
  114. package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
  115. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  116. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +148 -61
  117. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  118. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  119. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +4 -72
  120. package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
  121. package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
  122. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +213 -24
  123. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
  124. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +25 -4
  125. package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
  126. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  127. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  128. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  129. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  130. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
  131. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
  132. package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
  133. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  134. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  135. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
  136. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -7
  137. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
  138. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  139. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
  140. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
  141. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
  142. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  143. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  144. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
  145. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
  146. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
  147. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  157. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  158. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  159. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  160. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  165. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  167. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  170. package/packages/omo-codex/plugin/package-lock.json +13 -13
  171. package/packages/omo-codex/plugin/package.json +1 -1
  172. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
  173. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
  174. package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
  175. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +13 -20
  176. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
  177. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
  178. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
  179. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
  180. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
  181. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
  182. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  183. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
  184. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
  185. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
  186. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
  187. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
  188. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
  189. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
  190. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
  191. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +38 -22
  192. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
  193. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
  194. package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
  195. package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
  196. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
  197. package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
  198. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
  199. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
  200. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
  201. package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
  202. package/dist/skills/ultraresearch/SKILL.md +0 -10
  203. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
  204. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
  205. package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
  206. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
  207. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
  208. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
  209. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
  210. package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
  211. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
  212. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
  213. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
  214. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
  215. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
  216. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
  217. /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
@@ -107,11 +107,73 @@ test("#given parseArgs #when the slug is missing or unsafe #then it throws, and
107
107
  assert.equal(ok.intent, "unclear");
108
108
  assert.equal(ok.reset, true);
109
109
  assert.equal(ok.force, true);
110
+ assert.equal(ok.draftOnly, false);
111
+ assert.equal(ok.reviewRequired, false);
112
+ const draftOnly = parseArgs(["node", "s", "my-plan", "--draft-only"]);
113
+ assert.equal(draftOnly.draftOnly, true);
114
+ const reviewRequired = parseArgs(["node", "s", "my-plan", "--draft-only", "--review-required"]);
115
+ assert.equal(reviewRequired.reviewRequired, true);
110
116
  const plain = parseArgs(["node", "s", "my-plan"]);
111
117
  assert.equal(plain.reset, false);
112
118
  assert.equal(plain.force, false);
113
119
  });
114
120
 
121
+ test("#given an explicit review modifier at startup #when draft-only scaffold runs #then the first durable write contains the complete pending review request", async () => {
122
+ const { scaffold } = await import(scriptUrl);
123
+ const dir = await mkdtemp(join(tmpdir(), "ulwp-"));
124
+ try {
125
+ await scaffold(dir, { slug: "demo", intent: "clear", draftOnly: true, reviewRequired: true });
126
+ const draft = await readFile(join(dir, ".omo", "drafts", "demo.md"), "utf8");
127
+ assert.match(draft, /review_required: true/);
128
+ assert.match(draft, /plan_path: \.omo\/plans\/demo\.md/);
129
+ assert.match(draft, /pending-action: write and review \.omo\/plans\/demo\.md/);
130
+ assert.match(draft, /momus:\n\s+status: pending/);
131
+ assert.match(draft, /independent:\n\s+status: pending/);
132
+ await assert.rejects(() => readFile(join(dir, ".omo", "plans", "demo.md"), "utf8"), /ENOENT/);
133
+ } finally {
134
+ await rm(dir, { recursive: true, force: true });
135
+ }
136
+ });
137
+
138
+ test("#given automatic review for non-Trivial UNCLEAR intent #when draft-only scaffold runs #then the first durable write contains the complete pending review request", async () => {
139
+ const { scaffold } = await import(scriptUrl);
140
+ const dir = await mkdtemp(join(tmpdir(), "ulwp-"));
141
+ try {
142
+ await scaffold(dir, { slug: "demo", intent: "unclear", draftOnly: true, reviewRequired: true });
143
+ const draft = await readFile(join(dir, ".omo", "drafts", "demo.md"), "utf8");
144
+ assert.match(draft, /intent: unclear/);
145
+ assert.match(draft, /review_required: true/);
146
+ assert.match(draft, /plan_path: \.omo\/plans\/demo\.md/);
147
+ assert.match(draft, /plan_sha256: null/);
148
+ assert.match(draft, /review_round_id: null/);
149
+ assert.match(draft, /pending-action: write and review \.omo\/plans\/demo\.md/);
150
+ assert.match(draft, /momus:\n\s+status: pending[\s\S]*?target: \.omo\/plans\/demo\.md[\s\S]*?result: null/);
151
+ assert.match(draft, /independent:\n\s+status: pending[\s\S]*?target: \.omo\/plans\/demo\.md[\s\S]*?result: null/);
152
+ await assert.rejects(() => readFile(join(dir, ".omo", "plans", "demo.md"), "utf8"), /ENOENT/);
153
+ } finally {
154
+ await rm(dir, { recursive: true, force: true });
155
+ }
156
+ });
157
+
158
+ test("#given Trivial UNCLEAR pre-approval planning #when draft-only scaffold runs #then it creates a review-optional draft without creating a plan", async () => {
159
+ const { scaffold } = await import(scriptUrl);
160
+ const dir = await mkdtemp(join(tmpdir(), "ulwp-"));
161
+ try {
162
+ const first = await scaffold(dir, { slug: "demo", intent: "unclear", draftOnly: true });
163
+ assert.equal(first.length, 1);
164
+ assert.equal(first[0].status, "created");
165
+ assert.match(await readFile(join(dir, ".omo", "drafts", "demo.md"), "utf8"), /intent: unclear/);
166
+ assert.match(await readFile(join(dir, ".omo", "drafts", "demo.md"), "utf8"), /review_required: false/);
167
+ await assert.rejects(() => readFile(join(dir, ".omo", "plans", "demo.md"), "utf8"), /ENOENT/);
168
+
169
+ const afterApproval = await scaffold(dir, { slug: "demo", intent: "unclear" });
170
+ assert.equal(afterApproval[0].status, "exists");
171
+ assert.equal(afterApproval[1].status, "created");
172
+ } finally {
173
+ await rm(dir, { recursive: true, force: true });
174
+ }
175
+ });
176
+
115
177
  test("#given an already-scaffolded plan #when the script is re-run plain #then it is a no-op that preserves appended todos (resume-safe, no crash, no clobber)", async () => {
116
178
  // given
117
179
  const { scaffold } = await import(scriptUrl);
@@ -1,4 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
+ import { spawnSync } from "node:child_process";
2
3
  import { mkdtemp, readFile, writeFile } from "node:fs/promises";
3
4
  import { tmpdir } from "node:os";
4
5
  import { join } from "node:path";
@@ -6,7 +7,26 @@ import test from "node:test";
6
7
 
7
8
  import { migrateConfigFile } from "../scripts/migrate-codex-config.mjs";
8
9
 
9
- test("#given SessionStart config migration sees a low subagent cap #when migrating #then raises it to 1000", async () => {
10
+ function parseTomlWithPython(config) {
11
+ const python = resolvePython();
12
+ const result = spawnSync(
13
+ python,
14
+ ["-c", "import json, sys, tomllib; print(json.dumps(tomllib.loads(sys.stdin.read())))"],
15
+ { encoding: "utf8", input: config },
16
+ );
17
+ assert.equal(result.status, 0, result.stderr);
18
+ return JSON.parse(result.stdout);
19
+ }
20
+
21
+ function resolvePython() {
22
+ for (const command of ["python3", "python"]) {
23
+ const result = spawnSync(command, ["-c", "import tomllib"], { encoding: "utf8" });
24
+ if (result.status === 0) return command;
25
+ }
26
+ assert.fail("Python with tomllib is required for TOML parse assertions");
27
+ }
28
+
29
+ test("#given SessionStart migration sees an inline-commented V2 cap #when migrating twice #then preserves the line and ordering byte-for-byte", async () => {
10
30
  const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-migration-"));
11
31
  const configPath = join(root, "config.toml");
12
32
  await writeFile(
@@ -26,24 +46,240 @@ test("#given SessionStart config migration sees a low subagent cap #when migrati
26
46
  "",
27
47
  "[features.multi_agent_v2]",
28
48
  "enabled = false",
29
- "max_concurrent_threads_per_session = 6",
49
+ "usage_hint_enabled = false",
50
+ "max_concurrent_threads_per_session = 7 # user cap",
51
+ "show_tool_use = false",
30
52
  "",
31
53
  ].join("\n"),
32
54
  );
33
55
 
34
- const result = await migrateConfigFile(configPath);
56
+ const firstResult = await migrateConfigFile(configPath);
57
+ const firstPass = await readFile(configPath, "utf8");
58
+ const secondResult = await migrateConfigFile(configPath);
59
+
60
+ const secondPass = await readFile(configPath, "utf8");
61
+ assert.equal(firstResult.changed, true);
62
+ assert.equal(secondResult.changed, false);
63
+ assert.equal(secondPass, firstPass);
64
+ assert.match(
65
+ secondPass,
66
+ /usage_hint_enabled = false\nmax_concurrent_threads_per_session = 7 # user cap\nshow_tool_use = false/,
67
+ );
68
+ assert.match(secondPass, /\[agents\][\s\S]*?max_threads = 1000/);
69
+ assert.match(secondPass, /max_depth = 4/);
70
+ assert.match(secondPass, /\[agents\.explorer\]\nconfig_file = "\.\/agents\/explorer\.toml"/);
71
+ assert.match(secondPass, /\[features\.multi_agent_v2\][\s\S]*?enabled = false/);
72
+ assert.doesNotMatch(secondPass, /^max_threads\s*=\s*6$/m);
73
+ });
74
+
75
+ for (const header of [
76
+ '["features"."multi_agent_v2"]',
77
+ "[ features . multi_agent_v2 ]",
78
+ '["features"."multi_agent_v\\u0032"]',
79
+ ]) {
80
+ test(`#given SessionStart migration sees equivalent V2 header ${header} #when migrating twice #then preserves one valid table and the explicit cap`, async () => {
81
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-equivalent-header-"));
82
+ const configPath = join(root, "config.toml");
83
+ await writeFile(
84
+ configPath,
85
+ [
86
+ 'model = "gpt-5.4"',
87
+ "",
88
+ header,
89
+ "usage_hint_enabled = false",
90
+ "max_concurrent_threads_per_session = 7 # user cap",
91
+ "show_tool_use = false",
92
+ "",
93
+ ].join("\n"),
94
+ );
95
+
96
+ await migrateConfigFile(configPath);
97
+ const firstPass = await readFile(configPath, "utf8");
98
+ const parsed = parseTomlWithPython(firstPass);
99
+ const secondResult = await migrateConfigFile(configPath);
100
+ const secondPass = await readFile(configPath, "utf8");
101
+
102
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
103
+ assert.equal(secondResult.changed, false);
104
+ assert.equal(secondPass, firstPass);
105
+ assert.equal((secondPass.match(/max_concurrent_threads_per_session\s*=/g) ?? []).length, 1);
106
+ assert.match(
107
+ secondPass,
108
+ /usage_hint_enabled = false\nmax_concurrent_threads_per_session = 7 # user cap\nshow_tool_use = false/,
109
+ );
110
+ assert.match(secondPass, new RegExp(header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
111
+ });
112
+ }
113
+
114
+ for (const fixture of [
115
+ {
116
+ name: "quoted cap key",
117
+ lines: [
118
+ "[features.multi_agent_v2]",
119
+ '"max_concurrent_threads_per_session" = 7 # user cap',
120
+ ],
121
+ preservedLine: '"max_concurrent_threads_per_session" = 7 # user cap',
122
+ },
123
+ {
124
+ name: "escaped quoted cap key",
125
+ lines: [
126
+ "[features.multi_agent_v2]",
127
+ '"max_concurrent_threads_per_sessio\\u006e" = 7 # user cap',
128
+ ],
129
+ preservedLine: '"max_concurrent_threads_per_sessio\\u006e" = 7 # user cap',
130
+ },
131
+ {
132
+ name: "dotted cap key",
133
+ lines: [
134
+ "[features]",
135
+ "multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap",
136
+ ],
137
+ preservedLine: "multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap",
138
+ },
139
+ {
140
+ name: "root-qualified dotted cap key",
141
+ lines: ["features.multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap"],
142
+ preservedLine: "features.multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap",
143
+ },
144
+ ]) {
145
+ test(`#given SessionStart migration sees a ${fixture.name} #when migrating twice #then preserves the semantic cap without duplication`, async () => {
146
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-semantic-key-"));
147
+ const configPath = join(root, "config.toml");
148
+ await writeFile(configPath, ['model = "gpt-5.4"', "", ...fixture.lines, ""].join("\n"));
149
+
150
+ await migrateConfigFile(configPath);
151
+ const firstPass = await readFile(configPath, "utf8");
152
+ const parsed = parseTomlWithPython(firstPass);
153
+ const secondResult = await migrateConfigFile(configPath);
154
+ const secondPass = await readFile(configPath, "utf8");
155
+
156
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
157
+ assert.equal(secondResult.changed, false);
158
+ assert.equal(secondPass, firstPass);
159
+ assert.match(secondPass, new RegExp(fixture.preservedLine.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
160
+ assert.doesNotMatch(secondPass, /^max_concurrent_threads_per_session = 16$/m);
161
+ if (fixture.name === "root-qualified dotted cap key") {
162
+ assert.equal(parsed.features.multi_agent_v2.enabled, false);
163
+ }
164
+ });
165
+ }
166
+
167
+ test("#given multiline string contains V2 cap lookalikes #when SessionStart migrates #then writes the absent semantic default", async () => {
168
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-multiline-lookalike-"));
169
+ const configPath = join(root, "config.toml");
170
+ await writeFile(
171
+ configPath,
172
+ [
173
+ 'model = "gpt-5.4"',
174
+ 'notes = """',
175
+ "[features.multi_agent_v2]",
176
+ "max_concurrent_threads_per_session = 7",
177
+ '"""',
178
+ "",
179
+ ].join("\n"),
180
+ );
35
181
 
182
+ await migrateConfigFile(configPath);
36
183
  const content = await readFile(configPath, "utf8");
37
- assert.equal(result.changed, true);
38
- assert.match(content, /\[agents\][\s\S]*?max_threads = 1000/);
39
- assert.match(content, /max_depth = 4/);
40
- assert.match(content, /\[agents\.explorer\]\nconfig_file = "\.\/agents\/explorer\.toml"/);
41
- assert.match(content, /\[features\.multi_agent_v2\][\s\S]*?enabled = false/);
42
- assert.match(content, /max_concurrent_threads_per_session = 1000/);
43
- assert.doesNotMatch(content, /^max_threads\s*=\s*6$/m);
184
+ const parsed = parseTomlWithPython(content);
185
+
186
+ assert.match(parsed.notes, /max_concurrent_threads_per_session = 7/);
187
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 16);
188
+ });
189
+
190
+ test("#given V2 section multiline value contains a cap lookalike #when SessionStart migrates #then writes the absent default", async () => {
191
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-section-multiline-lookalike-"));
192
+ const configPath = join(root, "config.toml");
193
+ await writeFile(
194
+ configPath,
195
+ [
196
+ 'model = "gpt-5.4"',
197
+ "[features.multi_agent_v2]",
198
+ 'notes = """',
199
+ "max_concurrent_threads_per_session = 7",
200
+ '"""',
201
+ "",
202
+ ].join("\n"),
203
+ );
204
+
205
+ await migrateConfigFile(configPath);
206
+ const parsed = parseTomlWithPython(await readFile(configPath, "utf8"));
207
+
208
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 16);
44
209
  });
45
210
 
46
- test("#given gpt-5.6 session model with no models_cache #when migrating #then does not write agents.max_threads", async () => {
211
+ test("#given V2 root-dotted disable and cap #when gpt-5.6 SessionStart migrates #then removes disable and V1 agents cap", async () => {
212
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-root-dotted-v2-cleanup-"));
213
+ const configPath = join(root, "config.toml");
214
+ await writeFile(
215
+ configPath,
216
+ [
217
+ 'model = "gpt-5.6-terra"',
218
+ "features.multi_agent_v2.max_concurrent_threads_per_session = 7",
219
+ "features.multi_agent_v2.enabled = false",
220
+ "",
221
+ "[agents]",
222
+ "max_threads = 1000",
223
+ "",
224
+ ].join("\n"),
225
+ );
226
+
227
+ await migrateConfigFile(configPath, { sessionModel: "gpt-5.6-terra" });
228
+ const content = await readFile(configPath, "utf8");
229
+ const parsed = parseTomlWithPython(content);
230
+
231
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
232
+ assert.equal(parsed.features.multi_agent_v2.enabled, undefined);
233
+ assert.equal(parsed.agents, undefined);
234
+ });
235
+
236
+ test("#given quoted dotted V1 keys under features #when SessionStart migrates #then replaces enabled without a duplicate", async () => {
237
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-quoted-dotted-v1-"));
238
+ const configPath = join(root, "config.toml");
239
+ await writeFile(
240
+ configPath,
241
+ [
242
+ 'model = "gpt-5.4"',
243
+ "",
244
+ "[features]",
245
+ '"multi_agent_v2".enabled = true',
246
+ '"multi_agent_v2".max_concurrent_threads_per_session = 7',
247
+ "",
248
+ ].join("\n"),
249
+ );
250
+
251
+ await migrateConfigFile(configPath);
252
+ const content = await readFile(configPath, "utf8");
253
+ const parsed = parseTomlWithPython(content);
254
+
255
+ assert.equal(parsed.features.multi_agent_v2.enabled, false);
256
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
257
+ assert.equal((content.match(/enabled\s*=/g) ?? []).length, 1);
258
+ });
259
+
260
+ test("#given multiline string closes after an escaped quote #when SessionStart migrates #then preserves the following explicit cap", async () => {
261
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-overlapping-closer-"));
262
+ const configPath = join(root, "config.toml");
263
+ await writeFile(
264
+ configPath,
265
+ [
266
+ 'model = "gpt-5.4"',
267
+ 'notes = """abc\\\""""',
268
+ "[features.multi_agent_v2]",
269
+ "max_concurrent_threads_per_session = 7",
270
+ "",
271
+ ].join("\n"),
272
+ );
273
+
274
+ await migrateConfigFile(configPath);
275
+ const content = await readFile(configPath, "utf8");
276
+ const parsed = parseTomlWithPython(content);
277
+
278
+ assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
279
+ assert.equal((content.match(/^\[features\.multi_agent_v2\]$/gm) ?? []).length, 1);
280
+ });
281
+
282
+ test("#given gpt-5.6 session model with no models_cache and an explicit V2 cap #when migrating #then removes agents.max_threads but preserves the cap", async () => {
47
283
  const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-gpt56-nocache-"));
48
284
  const configPath = join(root, "config.toml");
49
285
  await writeFile(
@@ -58,7 +294,7 @@ test("#given gpt-5.6 session model with no models_cache #when migrating #then do
58
294
  "",
59
295
  "[features.multi_agent_v2]",
60
296
  "enabled = false",
61
- "max_concurrent_threads_per_session = 1000",
297
+ "max_concurrent_threads_per_session = 6",
62
298
  "",
63
299
  ].join("\n"),
64
300
  );
@@ -73,7 +309,28 @@ test("#given gpt-5.6 session model with no models_cache #when migrating #then do
73
309
  assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
74
310
  assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
75
311
  assert.match(content, /max_depth = 4/);
76
- assert.match(content, /max_concurrent_threads_per_session = 1000/);
312
+ assert.match(content, /max_concurrent_threads_per_session = 6/);
313
+ });
314
+
315
+ test("#given SessionStart config migration has no V2 cap #when migrating #then writes the conservative managed default", async () => {
316
+ const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-missing-cap-"));
317
+ const configPath = join(root, "config.toml");
318
+ await writeFile(
319
+ configPath,
320
+ [
321
+ 'model = "gpt-5.4"',
322
+ "",
323
+ "[features.multi_agent_v2]",
324
+ "enabled = false",
325
+ "",
326
+ ].join("\n"),
327
+ );
328
+
329
+ await migrateConfigFile(configPath);
330
+
331
+ const content = await readFile(configPath, "utf8");
332
+ assert.match(content, /max_concurrent_threads_per_session = 16/);
333
+ assert.doesNotMatch(content, /max_concurrent_threads_per_session = 1000/);
77
334
  });
78
335
 
79
336
  test("#given config without any model #when migrating #then does not introduce agents.max_threads", async () => {
@@ -89,7 +346,7 @@ test("#given config without any model #when migrating #then does not introduce a
89
346
  const content = await readFile(configPath, "utf8");
90
347
  assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
91
348
  assert.match(content, /max_depth = 4/);
92
- assert.match(content, /max_concurrent_threads_per_session = 1000/);
349
+ assert.match(content, /max_concurrent_threads_per_session = 16/);
93
350
  });
94
351
 
95
352
  test("#given config without any model but an existing low cap #when migrating #then still raises the existing cap", async () => {
@@ -241,6 +241,9 @@ test("#given start-work skill #when synced for Codex #then the difficulty-tier d
241
241
  assert.match(content, /lazycodex-worker-medium/);
242
242
  assert.match(content, /Delegation by difficulty/);
243
243
  assert.match(content, /Global Review and Debugging Gate/);
244
+ assert.match(content, /full commit SHA/);
245
+ assert.match(content, /re-read the ledger record/);
246
+ assert.match(content, /exact lane\/SHA pair/);
244
247
  assert.doesNotMatch(content, /works the same on both surfaces/);
245
248
  });
246
249
 
@@ -256,6 +259,10 @@ test("#given review-work skill #when some lanes do not finish #then aggregate re
256
259
  assert.match(content, /Do not spin in repeated/);
257
260
  assert.match(content, /bare REJECT\/FAIL token without findings is not a verdict/);
258
261
  assert.match(content, /cites the violated goal criterion/);
262
+ assert.match(content, /append a durable task-evidence record/);
263
+ assert.match(content, /full commit SHA/);
264
+ assert.match(content, /re-read that record/);
265
+ assert.match(content, /exact lane\/SHA pair/);
259
266
  assert.match(content, /Do not use `multi_agent_v1\.send_input` as an interrupt/);
260
267
  });
261
268
 
@@ -32,10 +32,11 @@ export const expectedSkills = [
32
32
  "visual-qa",
33
33
  ];
34
34
 
35
- export const hiddenSharedSkills = ["ultraresearch"];
36
-
37
35
  export const componentSkillSources = [
38
36
  ["comment-checker", "components/comment-checker/skills/comment-checker"],
37
+ ["lcx-contribute-bug-fix", "components/lcx/skills/lcx-contribute-bug-fix"],
38
+ ["lcx-doctor", "components/lcx/skills/lcx-doctor"],
39
+ ["lcx-report-bug", "components/lcx/skills/lcx-report-bug"],
39
40
  ["lsp", "components/lsp/skills/lsp"],
40
41
  ["rules", "components/rules/skills/rules"],
41
42
  ["teammode", "components/teammode/skills/teammode"],
@@ -77,9 +78,10 @@ const startWorkOriginalCompletion = `When all top-level checkboxes in \`## TODOs
77
78
  const startWorkCodexCompletion = `When all top-level checkboxes in \`## TODOs\` and \`## Final Verification Wave\` are complete:
78
79
 
79
80
  1. Run the plan's final verification commands.
80
- 2. Complete the **Global Review and Debugging Gate** before any completion claim, PR creation, PR handoff, branch handoff, or merge. The gate's pass binds to the commit SHA it reviewed and covers every later checkpoint at that same SHA — never re-run it on an already-passed SHA; re-run only when new commits land:
81
+ 2. Complete the **Global Review and Debugging Gate** before any completion claim, PR creation, PR handoff, branch handoff, or merge:
81
82
  - Invoke the \`review-work\` skill with the final diff, changed files, user goal, constraints, run command, and verification evidence. All five review lanes must return PASS. A timeout, missing deliverable, ack-only child, \`BLOCKED:\`, or inconclusive lane is a gate failure, not approval.
82
- - Run a debugging-oriented runtime audit even when the review passes: name at least three plausible failure hypotheses for the changed surface, run the distinguishing checks against the actual artifact, and append the ruled-out or confirmed result to \`.omo/start-work/ledger.jsonl\`.
83
+ - Each passing review lane binds to the exact full commit SHA it reviewed. Immediately append a durable record to \`.omo/start-work/ledger.jsonl\` with the lane name, full SHA, PASS verdict, and report artifact/source. Before same-SHA reuse after any continuation or compaction, re-read the ledger record and require the exact lane/SHA pair; memory, chat history, or an unstamped report is not coverage. New commits require fresh applicable lane coverage.
84
+ - Run a debugging-oriented runtime audit even when the review passes: name at least three plausible failure hypotheses for the changed surface, run the distinguishing checks against the actual artifact, and append a separate durable record with the audit name, exact full SHA, verdict, and evidence artifact/source to \`.omo/start-work/ledger.jsonl\`. Reuse it only after re-reading an exact audit/SHA match.
83
85
  - If any review lane or debugging hypothesis fails, invoke the \`debugging\` skill, confirm root cause with runtime evidence, add the minimal failing test or reproduction, fix it, rerun the affected verification, then rerun the Global Review and Debugging Gate.
84
86
  - Evidence hygiene is mandatory: redact or mask secrets and sensitive user data before writing \`.omo/start-work/ledger.jsonl\`, a PR body, or a handoff. Never include raw tokens, credentials, auth headers, cookies, API keys, env dumps, private logs, or PII; use concise summaries, lengths, hashes, or short non-sensitive prefixes instead.
85
87
  - If the work includes creating, updating, or handing off a PR, refresh \`git status\` and the PR/branch state from the task-owned worktree after the gate, and include only redacted review/debugging evidence in the PR body or handoff.
@@ -91,26 +93,40 @@ const startWorkOriginalHardRule = "- No completion claim while an applicable ult
91
93
 
92
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>`.";
93
95
 
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\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
+ const reviewWorkCodexGate = `
97
+ When \`review-work\` is used as a final implementation, PR, or \`$start-work\`
98
+ gate, it is blocking. A timeout, missing deliverable, ack-only response,
99
+ explicit \`BLOCKED:\`, or inconclusive lane is not a pass. Treat that lane as
100
+ failed, investigate the underlying uncertainty with the \`debugging\` skill when
101
+ runtime behavior may be wrong, fix with evidence, and rerun the affected lane
102
+ before claiming completion, creating or handing off a PR, or merging.
103
+
104
+ After each lane reaches PASS, immediately append a durable task-evidence record
105
+ to the active ledger with the lane name, exact full commit SHA, PASS verdict,
106
+ and report artifact/source. Before reusing coverage after continuation or
107
+ compaction, re-read that record and require the exact lane/SHA pair. Memory,
108
+ chat history, or an unstamped report is not coverage; a new commit requires
109
+ fresh applicable lane records.
110
+
111
+ A rejecting lane must name its blockers inline in its final message — each
112
+ blocker cites the violated goal criterion or requirement plus an evidence
113
+ pointer. A bare REJECT/FAIL token without findings is not a verdict; treat it
114
+ as an inconclusive lane (one bounded respawn, then record it inconclusive with
115
+ that reason).
116
+
117
+ When reviewing a PR or branch, collect diff, file contents, and verification
118
+ results from a dedicated review worktree attached to that branch. Never
119
+ checkout, test, or edit the review branch in the main worktree.
120
+
121
+ Review evidence must be safe to share. Redact or mask secrets and sensitive
122
+ user data before including evidence in logs, PR bodies, or handoffs. Never
123
+ include raw tokens, credentials, auth headers, cookies, API keys, env dumps,
124
+ private logs, or PII; summarize with lengths, hashes, and short non-sensitive
125
+ prefixes when identity is needed.
126
+ `;
127
+ const reviewWorkCodexGatePattern = new RegExp(reviewWorkCodexGate.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
96
128
 
97
129
  export function removeCodexSkillOverlays(skillName, content) {
98
- if (skillName === "ulw-research") {
99
- return content
100
- .replace(
101
- ", any 'ulw' research wording,",
102
- ", the legacy alias 'ultraresearch', any 'ulw' research wording,",
103
- )
104
- .replace(
105
- "the word \"ulw-research\" (also `/ulw-research`, `$ulw-research`), ",
106
- "the word \"ulw-research\" (also `/ulw-research`, `$ulw-research`), the legacy alias \"ultraresearch\" (also `/ultraresearch`, `$ultraresearch`), ",
107
- )
108
- .replace(
109
- "answer those normally, and mention that `ulw-research` is available when a question would clearly benefit from it.",
110
- "answer those normally, and mention that `ulw-research` is available (legacy alias: `ultraresearch`) when a question would clearly benefit from it.",
111
- )
112
- .replace("# ULW-Research Synthesis: <query>", "# Ultraresearch Synthesis: <query>");
113
- }
114
130
  if (skillName === "start-work") {
115
131
  return content
116
132
  .replace(startWorkCodexCompletion, startWorkOriginalCompletion)
@@ -9,7 +9,6 @@ import {
9
9
  assertPackagedContentMatches,
10
10
  componentSkillSources,
11
11
  expectedSkills,
12
- hiddenSharedSkills,
13
12
  listSkillFiles,
14
13
  removeCodexCompatibilityGuidance,
15
14
  removeCodexSkillOverlays,
@@ -113,7 +112,6 @@ test("#given shared skill package source #when aggregate Codex shared skills are
113
112
  // when / then
114
113
  for (const skillName of sharedSkillNames) {
115
114
  if (componentSkillNames.has(skillName)) continue;
116
- if (hiddenSharedSkills.includes(skillName)) continue;
117
115
  const sharedContent = await readFile(join(sharedSkillsRoot, skillName, "SKILL.md"), "utf8");
118
116
  const aggregateContent = await readFile(join(aggregateSkillsRoot, skillName, "SKILL.md"), "utf8");
119
117
  assert.equal(
@@ -213,11 +211,8 @@ test("#given synced ulw-loop skill #when Codex hint metadata is inspected #then
213
211
  test("#given shipped Codex skill payloads #when legacy ultraresearch alias is inspected #then it is not packaged", async () => {
214
212
  // given
215
213
  const skillsRoot = join(root, "skills");
216
- const skillRoot = join(skillsRoot, "ultraresearch");
217
214
 
218
215
  // then
219
- await assert.rejects(readFile(join(skillRoot, "SKILL.md"), "utf8"), { code: "ENOENT" });
220
- await assert.rejects(readFile(join(skillRoot, "agents", "openai.yaml"), "utf8"), { code: "ENOENT" });
221
216
  await assertNoLegacyResearchAliasInTree(skillsRoot, "skills");
222
217
  for (const [skillName, sourcePath] of componentSkillSources) {
223
218
  await assertNoLegacyResearchAliasInTree(join(root, sourcePath), `components/${skillName}`);