The synced Codex skills (refactor, review-work, remove-ai-slops) emit `spawn_agent(agent_type="explorer"/"librarian"/"plan", ...)` guidance that sync-skills.mjs injects into every skill containing OpenCode-only orchestration calls. Only codex-ultrawork-reviewer.toml was bundled, so Codex had no matching agent role to dispatch. An older omo-codex release shipped explorer/librarian/plan TOMLs but without the required top-level `name` field, leaving Codex to warn: Ignoring malformed agent role definition: agent role file at ~/.codex/agents/<name>.toml must define a non-empty `name` This commit bundles three correctly-formed TOMLs into components/ultrawork/agents/. Each has the full schema Codex parses: `name`, `description`, `nickname_candidates`, `model`, `model_reasoning_effort`, `service_tier`, `developer_instructions`. The existing sync-agents.py SessionStart hook installs them via rglob into CODEX_HOME/agents/. Models match the original design: explorer + librarian on gpt-5.4-mini low effort (fast contextual + external research); plan on gpt-5.5 xhigh effort (deep reasoning + interview-style planning). Tests: - test/bundled-agents.test.mjs: locks the sync-hook contract by running sync-agents.py against a temp CODEX_HOME and verifying each TOML lands with the expected name + schema. - test/aggregate.test.mjs: locks the schema keys on every bundled TOML and the spawn_agent contract (every in-scope agent_type referenced by a synced skill has a matching bundle). Follow-up: the sync-skills.mjs compatibility table also references `spawn_agent(agent_type="worker", ...)`. No worker.toml is present in CODEX_HOME and Codex does not warn about its absence, suggesting worker is a built-in Codex role. Confirm and ship worker.toml if not.
codex-ultrawork
Codex plugin that injects a compact orchestration directive (the ultrawork prompt) when the user prompt contains ultrawork or ulw (word-bounded, case-insensitive). It also syncs the bundled codex-ultrawork-reviewer agent role into CODEX_HOME/agents on SessionStart.
What the injected directive enforces
| Mandate | Behavior |
|---|---|
| Goal + binding success criteria | Call create_goal (or open with a # Goal block) listing the deliverable + 3+ realistic QA scenarios (happy path, edge cases, adjacent-surface regression). Each scenario MUST name which Manual-QA channel it will use. "Tests pass" is supporting signal, NEVER completion proof. |
| Manual-QA channels (TESTS ALONE NEVER PROVE DONE) | A dedicated top-level section enumerates the four channels you can use to verify a criterion in reality: (1) HTTP call (curl -i / Playwright APIRequestContext), (2) tmux (tmux new-session + send-keys + capture-pane), (3) Browser use (Playwright / puppeteer / Chromium driving the real page), (4) Computer use (OS-level GUI automation against the running app). Every criterion picks one channel, builds a real-usage scenario, runs it, and captures the artifact — every time. Aux surfaces (CLI stdout / DB diff / parsed config) only count for genuinely CLI- or data-shaped criteria. |
| Surface + paired cleanup | Execution loop step 4 (SURFACE-AS-SCENARIO) runs the chosen channel scenario end-to-end. Step 5 (CLEANUP, PAIRED) tears down every QA-spawned process / tmux session / browser context / container / port / temp dir, with a one-line receipt appended to the notepad. Leftover state → NOT done. |
| Durable /tmp notepad | mktemp -t ulw-$(date +%Y%m%d-%H%M%S).XXXXXX.md with sections Plan, Success criteria + QA scenarios, Now, Todo, Findings, Learnings. Append, never rewrite. |
| Obsessive atomic todos | Every action — even one-line edits, ls, single test runs — becomes a todo. Format: path: <action> for <criterion> — verify by <check>. One in_progress at a time, mark completed immediately. |
| GPT-5.2 xhigh verification gate | Triggered automatically on user-requested rigor, 3+ files, 20+ turns, 30+ minutes, or refactor/migration/perf/security work. Use the bundled codex-ultrawork-reviewer agent role when available. Reviewer verdict is binding — no "false positive", no minimising, no arguing. Loop until unconditional approval. "Looks good but…" = REJECTION. |
The directive is currently 11,005 chars / 232 lines and follows the GPT-5.5 prompting structure (Role / Goal / Manual-QA channels / Bootstrap / Execution loop / Verification gate / Commits / Constraints / Output / Stop rules).
Install (via this marketplace)
codex plugin marketplace add /path/to/codex-plugins
node /path/to/codex-plugins/scripts/install-local.mjs /path/to/codex-plugins
The installer copies the plugin into ~/.codex/plugins/cache/code-yeongyu-codex-plugins/omo/0.1.0, enables it in ~/.codex/config.toml, and registers the UserPromptSubmit and SessionStart hooks.
How it works
hooks/hooks.json registers a UserPromptSubmit hook running:
python3 ${PLUGIN_ROOT}/hooks/ultrawork-detector.py
Codex passes the prompt payload on stdin. When the pattern \b(?:ultrawork|ulw)\b (case-insensitive) matches, the hook writes the directive to stdout — Codex injects non-JSON stdout as additional_context for the next turn. Otherwise the hook writes nothing and exits 0. Malformed input also exits 0 to never block the turn.
It also registers a SessionStart hook running:
python3 ${PLUGIN_ROOT}/hooks/sync-agents.py
That hook copies bundled agents/*.toml files into CODEX_HOME/agents. It writes nothing on success and exits 0 even on malformed input.
Smoke test
PAYLOAD='{"cwd":"/tmp","hook_event_name":"UserPromptSubmit","model":"gpt-5.5","permission_mode":"default","session_id":"x","transcript_path":"","turn_id":"y","prompt":"please ultrawork"}'
echo "$PAYLOAD" | python3 hooks/ultrawork-detector.py | head -3
Expect <ultrawork-mode> ... directive body.
Agent role smoke test
CODEX_HOME="$(mktemp -d)"
echo '{"hook_event_name":"SessionStart"}' | CODEX_HOME="$CODEX_HOME" python3 hooks/sync-agents.py
Expect CODEX_HOME/agents/codex-ultrawork-reviewer.toml to exist.
License
MIT. See LICENSE.
Privacy
This plugin only reads local hook payloads, emits the bundled directive text on keyword match, and syncs bundled agent TOML files locally. It does not perform network requests or telemetry.