Skip to content

Harness support

clownhead oversees two coding agents, Claude Code and Codex, on one board. This page is the ledger: every capability the board has, and what each agent can answer for it.

A cell reads yes, partial, or no. partial always says what the limit is.

Verified against Claude Code and codex-cli 0.153.4.

Where the two differ at the root

Claude Code answers claude agents --json and keeps state under ~/.claude. Codex has no equivalent command. Its state lives under ~/.codex, and the only listing that includes live sessions comes from the app-server daemon over a Unix socket at ~/.codex/app-server-control/app-server-control.sock.

That socket speaks WebSocket. A plain GET / is answered with silence and newline-delimited JSON-RPC is closed without a reply; an Upgrade: websocket request gets HTTP/1.1 101 Switching Protocols, after which JSON-RPC travels in text frames. clownhead.websocket is the client half of that.

The daemon has to be running, and clownhead starts one when nothing answers the socket. codex app-server daemon start is a no-op against a daemon already running, and it is given the Codex home clownhead reads, since the daemon holds that directory for every session it goes on to report. codex app-server daemon bootstrap is the durable form, which keeps one running across logins. A start that fails is attempted once per clownhead run, and clownhead doctor names the command to try by hand. A machine with no Codex installed loses the HARNESS column too, since a column reading claude on every row answers a question nobody there can ask.

Discovery takes two calls, because no single one covers both halves. thread/list reads the persisted index and omits sessions that are still running: a session opened seconds earlier, newer than every row returned, was absent from a thread/list of eight while thread/loaded/list named it. So live sessions come from thread/loaded/list followed by thread/read per id, and ended ones come from thread/list. This is the same split clownhead already makes for Claude Code, where the CLI answers for what is live and the transcripts answer for what has ended.

Capabilities

Capability Claude Code Codex
List live sessions claude agents --json yes, thread/loaded/list then thread/read
List ended sessions registry and transcripts yes, thread/list
Status vocabulary status, waitingFor, state yes, ThreadStatus with activeFlags
Account usage in the header local cachedUsageUtilization; older readings marked stale local app-server account/rateLimits/read, falling back to rollout rate-limit snapshots
Split busy from shell registry beat, discovery.refine no, the protocol has no such state
Working directory payload cwd yes, Thread.cwd
Session name payload name, --name at launch partial, model-written and null until the session ends
PID and TTY registry pid joined to ps partial, joined through lsof on the working directory
Interactive vs background payload kind partial, every thread reads as interactive
Trust before making a worktree hasTrustDialogAccepted yes, trust_level in config.toml
Relocated config directory CLAUDE_CONFIG_DIR yes, CODEX_HOME
Resume claude --resume <id> yes, codex resume <id>
Fork --fork-session yes, codex fork <id>
Switch harness on resume/fork New conversation with recent context and transcript references yes, in the same checkout
Start a session for a PR or issue --worktree, --name, plan mode yes, native --enable worktrees --worktree (Codex 0.154.0+)
Send a message to a live session control socket yes, codex queue --thread --message
Rename control socket yes, thread/name/set
Terminate, close the tab SIGTERM and SIGHUP to the pid partial, needs the pid join above
Archive clownhead's own archived.json yes, the same file
History pane transcript tail yes, thread/items/list
Scan transcripts for PR and issue mentions projects/*/*.jsonl yes, the rollout file Thread.path names
Worktree cleanup git, per repository yes, the same worktrees
Paint tabs, bell, notify, foreground OSC to the session TTY partial, needs the pid join above
Detect clownhead running inside a session argv0 contains claude yes, argv0 contains codex

Statuses

clownhead keeps one vocabulary and maps each agent onto it.

clownhead Claude Code Codex
busy busy active with no flags
shell registry shell no equivalent
idle idle idle
waiting waitingFor is set active with waitingOnUserInput
blocked blocked active with waitingOnApproval
failed failed systemError
completed state: completed last TurnStatus is completed
closed in the registry, absent from the listing notLoaded
archived clownhead state only clownhead state only

What is on disk

Claude Code Codex
Config directory ~/.claude, moved by CLAUDE_CONFIG_DIR ~/.codex, moved by CODEX_HOME
Trust ~/.claude.json ~/.codex/config.toml
Transcripts <config>/projects/<project>/<id>.jsonl <config>/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl
Live session channel one socket per process in /tmp/cc-socks one daemon socket in <config>/app-server-control
Session index the sessions registry directory Thread.path from thread/list
Worktrees <repo>/.claude/worktrees/<name> $CODEX_HOME/worktrees/… (default ~/.codex/worktrees/…), plus legacy clownhead checkouts

A Codex rollout file opens with a session_meta record naming the thread id and cwd, then carries event_msg records for turn boundaries and response_item records for what was said.

The three gaps, and why

No shell state for Codex. The protocol reports a thread as active without saying whether the model is working or a background command is. Codex sessions show busy for both, which is what claude agents --json does before discovery.refine puts the distinction back from the registry. Codex publishes no equivalent record to read it out of.

A pid only where the pairing is unambiguous. The app-server names no process, so the join is the working directory: ps says which processes are Codex and lsof says where each one is. A pairing is taken only where one session and one process share a directory. Three threads and one terminal in one checkout is ordinary once an editor is also running Codex there, and the directory says nothing about which of the three the terminal holds. A pid is what terminating and signalling act on, so a wrong one costs somebody else's session, and no answer is the better one. Terminate, close-tab and the tab signalling all wait on this.

Names arrive late. Thread.name is written by the model and stays null while a session runs, so a live Codex row falls back to its directory and short id. Renaming from the board works at any point and sticks.

What Codex has that clownhead does not use

turn/interrupt stops a running turn without touching the process, and thread/archive archives a session inside Codex itself. Neither has a Claude Code counterpart, and clownhead's own archive already covers both agents, so the board drives neither.