# `lib/codex` — Claude→Codex projection

Turns a harness's already-materialized Claude artifacts into their OpenAI Codex counterparts, so one harnessed repo works under **both** Claude Code and Codex. Consumed by both shippers — the installer (`bin/etna.js`) and the marketplace publisher (`.github/scripts/publish.mjs`) — through one module, so the two channels can't drift (the same "keep in agreement" rule as the `evals/`/`fixtures/` ship-filter).

Owning doc: [`docs/features/codex-support.md`](../../../../docs/features/codex-support.md).

## What it emits

| Source | Target | File |
| --- | --- | --- |
| `CLAUDE.md` | `AGENTS.md` (byte-for-byte copy + generated adapter) | `agents-md.mjs` |
| `.claude/skills/` | `.agents/skills/` (mirror) | `skills.mjs` |
| bundle `codex/agents/*.toml` | `.codex/agents/*.toml` (copy) | `subagents.mjs` |
| `hooks.json` | `.codex/hooks.json` (`type:"command"`, command translated) | `hooks.mjs` |
| `.claude-plugin/*` | `.codex-plugin/plugin.json` + `.agents/plugins/marketplace.json` | `plugin-manifest.mjs` |

`index.mjs` orchestrates the install-time projectors and returns `{installed, notices, warnings, owned}`; `owned` is recorded in `.claude/harness.json` → `owned.codex` for prune-on-refresh.

**Out of scope (owner decision 2026-07-13):** MCP projection and any Codex `settings.json`/`config.toml` equivalent. There is no user-editable Codex file the harness co-owns, so every Codex path is a clean full-overwrite — no managed-block merge.

## Runtime adaptation the projection performs

The Claude source is runtime-neutral in intent, but three edges do not survive a mechanical copy — the projectors adapt them so a Codex install works out of the box:

- **Policy adapter (`agents-md.mjs`).** `AGENTS.md` is the `CLAUDE.md` policy copied byte-for-byte, then a compact **generated Codex runtime adapter** appended: it names the native skill (`.agents/skills/`) and agent (`.codex/agents/`) locations and, crucially, routes Codex to the `.claude/rules/*.md` files Codex does **not** auto-load. The list is derived from the bundled harness inventory (`bundledHarnessDir`), carries no user state, and is replaced whole on reinstall. It stays well under Codex's 32 KiB combined-instruction cap.
- **Hook-path translation (`hooks.mjs`, install) & (`publish.mjs`, plugin).** A hook command locating its script via `$CLAUDE_PROJECT_DIR` fails under Codex (Codex sets no such variable). For a **CLI install**, `toCodexCommand` prefixes a runtime-neutral root resolution (`git rev-parse --show-toplevel`, `pwd` fallback) and re-exports `CLAUDE_PROJECT_DIR`, so the hook resolves from the project root or any nested dir. For a **marketplace plugin**, `publish.mjs` rewrites the prefix to `$CLAUDE_PLUGIN_ROOT/hooks/` — the plugin-root variable both runtimes export.
- **Agent validation (`validate-codex-agents.mjs`).** A committed twin must be valid TOML with the required keys and carry no Claude-only primitive (`Task tool`, `subagent_type`, or a fixed model tier). Because a twin ships to consumers unparsed, this standalone validator is the gate — the authoring procedure mandates it.

## CLI vs plugin capability surface

The two channels do not install the same set. The **CLI** writes the full Codex footprint — `AGENTS.md`, `.agents/skills/`, `.codex/agents/*.toml`, `.codex/hooks.json`. The **marketplace plugin** carries only what `.codex-plugin/plugin.json` supports — **skills + hooks**; it exposes no project custom agents (Codex custom agents are a CLI-only feature) and no root policy. See [`codex-support.md`](../../../../docs/features/codex-support.md) → _Supported Codex plugin surface_.

## Subagents are authored, not derived at install

`.claude/agents/*.md → .codex/agents/*.toml` is a semantic conversion, so the twin is **authored from the `.md` by following** [`docs/playbooks/agents.md`](../../../../docs/playbooks/agents.md) — the model that writes or edits the agent produces the twin inline (frontmatter → `name`/`description`, body verbatim → `developer_instructions`, `tools:` dropped to a comment, Claude model aliases omitted), with **no external CLI**. The committed `agents/<name>.codex.toml` is checked by the validation gate:

```bash
node packages/cli/lib/codex/validate-codex-agents.mjs   # or: npm run validate:codex-agents
```

`build-data.js` and `publish.mjs` then ship the committed TOML; the installer copies it, so **consumers need no Codex CLI**. Re-author an agent's TOML in the same change that edits its `.md` (source↔twin parity).
