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