# Codex (limited)

Codex is OpenAI's coding CLI. It is **supported, not the main harness**: Claude Code is the harness the rest of this handbook assumes, and this section holds only what differs when you run Codex instead. Read Start here first; its Step 2 has a Codex variant, shown below this chapter.

## What a Codex seat is

A Codex seat is the native `codex` binary running in a herdr pane. It carries the UCC skills through one symlink and reaches the daemon through Codex's own hooks, so after its first turn it has a card and a live row in the fleet like any other seat. What it does not have is the Meridian MCP server: no `put`, no `sql`, no board or memo writes, no fleet verbs. Its record access is `mrd read`, its output is a report file, and its human contact is a Codex tool, not the fleet's Telegram path.

```mermaid
flowchart LR
  you[You] -->|herdr agent start --kind codex| seat[Codex seat]
  seat -->|hooks.json| daemon[ccc-statusd]
  daemon --> card[Card + fleet row]
  seat -->|mrd read root:path| root[(Sessions root)]
  seat -->|curl, with network_access| pages[page.sui.pics records]
  seat --> report[Report file]
  seat --x|no put, no sql, no ccc-agent| root
```

Two directions share the name, so keep them apart:

| You run | What it is | Where it belongs |
|---|---|---|
| `codex` inside herdr | the native Codex CLI — this section | short, read-only work: records read, a report file written |
| `ucc-codex` and your `codex-gpt*` profiles | Claude Code on a GPT model through a local proxy (`ucc-cli codex ensure` starts it; no `/login`) | the Start here chapter — a normal Claude Code seat with the full MCP |

The recommended way to take part in the fleet is the second row: a UCC gateway profile is a Claude Code seat on your ChatGPT-backed model and gets every Meridian tool. Native Codex is for when you want Codex itself.

## What is installed for Codex

| Piece | Where it comes from | Shows it |
|---|---|---|
| Codex CLI binary | OpenAI's install | `codex --version` |
| `~/.codex/skills/ucc` → `$UCC_HOME/shared/skills` | the ucc installer, refreshed by every `ucc-cli update` | `readlink ~/.codex/skills/ucc` |
| `[features] hooks = true` in `~/.codex/config.toml` | you, once, or `herdr integration install codex` writes it; without it the installer prints one WARN and writes nothing | `grep -n 'hooks = true' ~/.codex/config.toml` |
| the three `_ccc` entries in `~/.codex/hooks.json` | the ucc installer, at the end of `SessionStart`, `UserPromptSubmit`, `Stop` | `grep -c '"_ccc"' ~/.codex/hooks.json` → `3` |
| herdr integration | `herdr integration install codex`, once per account and again after a herdr update | `herdr integration status` → `codex: current (v<n>)` |

The skills link goes *into* `~/.codex/skills`, never over it: `~/.codex/skills` is a real directory holding Codex's own skills. Codex reads skills at session start, so a skill added to the tree is visible to the next `codex`, not the running one. Instructions come from `AGENTS.md`, never `CLAUDE.md`, found by walking up from the pane's working directory — start the pane in the tree you are working on, not in a wiki or sessions directory.

`hooks.json` accepts only `description` and `hooks` at the top level. One foreign top-level key makes Codex refuse the whole file with a single startup line, and then no hook runs at all — no card, no fleet row, no dialog.

## The network setting

Codex runs commands in a sandbox. Under `sandbox_mode = "workspace-write"` the sandbox has **no network**: a prompt that names a URL fails with `curl: (6) Could not resolve host`. The key that opens the network and nothing else is:

```toml
[sandbox_workspace_write]
network_access = true
```

in `~/.codex/config.toml` (the same key per invocation: `codex -c sandbox_workspace_write.network_access=true`). Every handbook prompt ends with the web address it lives at, which the seat re-reads, so set it before your first session. `sandbox_mode = "danger-full-access"` also passes but grants far more than a fetch needs. Codex's own web tool is not the path that carries these fetches; `curl` in the sandboxed shell is, and that is exactly what this key governs.

## Starting a seat

A native seat starts **inside herdr**, never through `ccc-agent spawn` (it appends `--session-id`, which Codex refuses) and never through a `ucc-codex*` wrapper (that is Claude Code on a proxy). The pane sequence, run from any herdr pane:

```bash
herdr pane split --current --direction right --cwd <tree under work> --no-focus \
  --env HERDR_AGENT= --env UCC_PROFILE= --env CLAUDE_CONFIG_DIR= --env CCC_ROLE= --env CCC_CARD_SEED=
herdr agent start <name> --kind codex --pane <pane id>
herdr agent read <name> --source visible          # a dialog may be up
herdr agent prompt <name> "<one sentence>" --wait --timeout 180000
herdr agent get <name>                            # .result.agent.agent_session.value = the Codex session id
herdr pane close <pane id>
```

The name matches `[a-z][a-z0-9_-]{0,31}`. Clearing the five environment variables matters: empty reads as absent, so the card is born `role: worker` with no leaked seed.

Two dialogs can sit on the screen while `herdr agent start` already reports `interactive_ready: true`, so read the pane after every start:

- **Hooks need review — N hooks are new or changed**, once per host and again whenever a hook entry's bytes change. Answer option 2, *Trust all and continue*: `herdr agent send-keys <name> down enter`. Option 3 persists nothing and asks again next time.
- **Do you trust the contents of this directory?**, once per directory Codex has not seen. Answer option 1, *Yes, continue*: `herdr agent send-keys <name> enter`.

Codex signs in with your ChatGPT account through its own sign-in, not through `/login`.

## Identity

`SessionStart` fires at the first prompt, not at process start. Until the seat's first turn there is no card, no id in `herdr agent get` and no row in `ccc-agent list --table`. After it, the seat is a `daemon`/`live`/`worker` row.

Codex session ids are UUIDv7. The short id is the **last 8 hex characters**, not the first 8: the first 8 are a timestamp every seat born in the same minute shares.

## What the seat can and cannot do

Can:

- **Read any record** — `mrd read 'sessions:<path>'` (the root-qualified form works from any working directory), `--section <n>` for one section. The transcript in the Codex variant of Step 2 below shows it.
- **Fetch a handbook record** by URL with `curl`, once the network key is set.
- **Be a fleet seat** — card, daemon row, the developer-message context the daemon injects each turn.
- **Ask its own user and run its own sub-agents** through Codex-native tools (`request_user_input`; the `collaboration.*` family). These reach Codex only: they are not fleet verbs and do not reach the fleet's Telegram relay.
- **Write a report file** in its working directory. That file is the proof of its work.

Cannot:

- **Write any record.** There is no Meridian `put` tool in the seat — no memos, cards, rosters, board moves. The `mrd` CLI's write doors refuse or need a workspace the pane does not sit in; an able model driving it landed nothing in four attempts.
- **Use any `ccc-agent` verb** (`spawn`, `send`, `screen`, `transcript`, `wait`, `get`, `list`, `kill`) or any lifecycle verb (`session killme`, `restart`, `forkme`, `schedule`). Its exit is the spawner's `herdr pane close`.
- **Reach the fleet's AskUserQuestion-to-Telegram path** or receive hook sensors (`tokens>`, `idle>`, `auq>`). A question for a human that its own tool cannot settle becomes an open item in the report.
- **Be prompted from outside** except by `herdr agent prompt`. A `ccc-statusd session send` from its shell arrives as `from="cli"`, so the reply line must carry the agent name and the report path.

`skill:` `ccc-native` — the seat's own contract: its verbs, its must-not list, the reply protocol, and `references/codex.md` for every setup detail above.
