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.
- package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +39 -16
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
- package/README.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +4 -4
- package/README.ru.md +2 -2
- package/README.zh-cn.md +2 -2
- package/bin/AGENTS.md +33 -0
- package/dist/agents/atlas/agent.d.ts +6 -5
- package/dist/agents/sisyphus/index.d.ts +5 -0
- package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
- package/dist/agents/sisyphus-junior/index.d.ts +2 -0
- package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +690 -139
- package/dist/cli-node/index.js +690 -139
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/browser-automation.d.ts +1 -0
- package/dist/config/schema/commands.d.ts +1 -3
- package/dist/config/schema/default-mode.d.ts +1 -1
- package/dist/config/schema/goal.d.ts +7 -0
- package/dist/config/schema/hooks.d.ts +1 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
- package/dist/config/schema/ralph-loop.d.ts +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/create-hooks.d.ts +1 -1
- package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
- package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
- package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
- package/dist/hooks/comment-checker/hook.d.ts +8 -1
- package/dist/hooks/goal/command-arguments.d.ts +13 -0
- package/dist/hooks/goal/controller.d.ts +26 -0
- package/dist/hooks/goal/index.d.ts +23 -0
- package/dist/hooks/goal/prompt.d.ts +3 -0
- package/dist/hooks/goal/store.d.ts +8 -0
- package/dist/hooks/goal/tools.d.ts +10 -0
- package/dist/hooks/goal/types.d.ts +93 -0
- package/dist/hooks/goal/validation.d.ts +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/keyword-detector/hook.d.ts +1 -2
- package/dist/hooks/ralph-loop/types.d.ts +1 -1
- package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
- package/dist/index.js +3706 -4329
- package/dist/oh-my-opencode.schema.json +23 -19
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
- package/dist/plugin/chat-message/types.d.ts +23 -10
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
- package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
- package/dist/plugin/messages-transform.d.ts +1 -0
- package/dist/plugin/stop-continuation.d.ts +2 -2
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
- package/dist/shared/system-directive.d.ts +0 -1
- package/dist/skills/ulw-plan/SKILL.md +11 -7
- package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/dist/skills/ulw-research/SKILL.md +3 -3
- package/dist/tools/delegate-task/types.d.ts +0 -1
- package/dist/tui.js +115 -28
- package/package.json +14 -14
- package/packages/git-bash-mcp/dist/cli.js +81 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
- package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
- package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
- package/packages/lsp-daemon/dist/cli.js +262 -63
- package/packages/lsp-daemon/dist/client.js +194 -50
- package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
- package/packages/lsp-daemon/dist/daemon-client.js +26 -2
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
- package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
- package/packages/lsp-daemon/dist/index.js +262 -63
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +79 -23
- package/packages/lsp-tools-mcp/dist/cli.js +82 -20
- package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
- package/packages/lsp-tools-mcp/dist/tools.js +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +195 -56
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/lsp/AGENTS.md +1 -0
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +148 -61
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +4 -72
- package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +213 -24
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +25 -4
- package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +10 -10
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +13 -20
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +38 -22
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
- package/dist/skills/ultraresearch/SKILL.md +0 -10
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
- /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
|
-
|
|
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
|
-
"
|
|
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
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
assert.match(
|
|
40
|
-
assert.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
-
|
|
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
|
|
95
|
-
|
|
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}`);
|