---
name: codex-cli-bridge
description: Delegate work to the OpenAI Codex CLI (`codex`) under a ChatGPT subscription — run plan/instruction EXECUTION in a sandboxed workspace, or get a read-only ADVISORY review of a plan or working-tree diff — as a second delegated-execution backend beside Antigravity. Use when the user wants to hand a bounded coding task or plan to `codex exec`, get a second-opinion review from codex, install or authenticate Codex CLI, understand its sandbox/network/approval policy, drive codex efficiently from the main agent (exec vs review, resume, the commit boundary), bridge project context (`AGENTS.md`) into codex, or troubleshoot codex flags, models, auth, or its no-TTY headless behaviour.
metadata:
  version: '3.5.0'
---

# codex-cli-bridge

Bridges the main agent to the **OpenAI Codex CLI** (`codex`) as a **delegated-execution backend**
beside Antigravity. The main agent stays the orchestrator — owning decisions, the edits it accepts,
verification, and user-facing claims — and hands `codex` a bounded sub-task answered from a **ChatGPT
subscription** (no pay-as-you-go billing). Codex has two roles here: a **sandboxed executor** that
edits a repo under a fixed policy (`codex-exec`), and a **read-only reviewer** that critiques a plan
or a working-tree diff and only emits findings (`codex-review`).

## Overview / when to use

Use this skill when the user wants to:

- Delegate plan or instruction EXECUTION to `codex` in a workspace-write sandbox (network OFF).
- Get a second-opinion ADVISORY review of an implementation plan or the current diff.
- Install, authenticate, smoke-test, or troubleshoot `codex`, or understand its sandbox/flags/models.
- Drive codex efficiently from the main agent (exec vs review, `resume`, the commit boundary).

Do **not** use it to bundle secrets, bypass subscription auth, use api-key billing, or let codex
commit / push on its own.

## Install

Clean-machine setup is in [`setup/README.md`](setup/README.md). In short: install the `codex`
binary, run `codex login` once under a ChatGPT subscription, then expose this skill's two wrappers on
`PATH` as `codex-exec` ([`bin/codex-exec.sh`](bin/codex-exec.sh)) and `codex-review`
([`bin/codex-review.sh`](bin/codex-review.sh)).

## Auth — subscription only (invariant)

`codex` authenticates with the cached **ChatGPT login** under `CODEX_HOME` (`~/.codex`). Never read,
print, copy, commit, or package `~/.codex/auth.json` — it is personal and is **never bundled** with
this skill. Both wrappers enforce the subscription path before invoking codex:

- they **unset every `*_API_KEY`** (plus `OPENAI_API_KEY` / `CODEX_API_KEY` / `OPENAI_BASE_URL`) so a
  stray key can never silently switch you to paid api-key billing;
- they pass **`--ignore-user-config`** so a personal `~/.codex/config.toml` cannot change model,
  sandbox, or approval behaviour (auth still works — codex reads the login from `CODEX_HOME`
  regardless of that flag);
- they **preflight `codex login status`** and refuse to run unless it reports `Logged in using ChatGPT`.

## Models quality-first pinned

Delegated codex work ALWAYS runs on the **PINNED model at maximum reasoning effort**: the wrappers
**pin** `gpt-5.6-sol` / `xhigh` and **refuse** (exit 2, loud) a non-default `CODEX_MODEL` / `CODEX_EFFORT`
— knowingly-worse output is never traded for quota. The pin is deliberate: an explicit `-m gpt-5.6-sol`
names one model rather than following the CLI's current default. That the pinned id is also the
*strongest* selectable Codex model is a **separate, time-bounded claim**, checked BY HAND against
<https://developers.openai.com/codex/models>. There is **no automated gate and this file records no
date for that check**, so treat "strongest" as unverified until someone looks. Upstream can move
either way between releases:
- **retirement** is loud — the pinned id stops being served and codex fails with an error naming the
  model. That is an upstream failure, not a wrapper refusal; the fix is a pin bump in its own reviewed
  commit, never a `CODEX_PROBE=1` run, whose output is never delegated work.
- **a newer, stronger model is SILENT** — nothing fails, and the pin simply stops being the best
  available until someone re-checks the page.

The guard is therefore a **pin-integrity** guard, not a quality guarantee: it enforces that runs use
the id named here, and it refuses a *stronger* model exactly as it refuses a weaker one. Read a
refusal as "not the pinned id", never as "a downgrade was prevented".

Economy comes only from **quality-neutral waste removal** (clean capture, a hard timeout,
a precomputed review diff, `resume` instead of re-sending context), never from a downgrade.

The ONLY escape is a **throwaway probe** whose result is effort-independent (a reachability / smoke
check): set `CODEX_PROBE=1` (echoed loudly) to relax the model/effort guard. Never use a probe run's
output as real delegated work.

| Variable | Default | Effect |
|---|---|---|
| `CODEX_MODEL` | `gpt-5.6-sol` (pinned) | model passed to `-m`; a non-default is REFUSED unless `CODEX_PROBE=1` |
| `CODEX_EFFORT` | `xhigh` (pinned) | reasoning effort (`-c model_reasoning_effort=…`); non-default REFUSED unless `CODEX_PROBE=1` |

`codex --version` reports the CLI version, **not** the model list. Quota is metered in **messages**
(a rolling 5h window + a weekly cap), not raw tokens — which is why the levers above are about removing
waste, never lowering quality. Full knob list: [§ Environment knobs](#environment-knobs).

## Usage

> **The machine-readable mode catalog lives in [`capability.json`](capability.json) `modeCatalog`** —
> every documented mode with its purpose, when to use it (and when not), the exact invocation form
> with its operand slots, and the guardrails that really apply. The catalog tracks **the documented
> wrapper mode set** (never "the CLI's modes"): an upstream Codex CLI change reaches it through a
> bridge release, where the source-level drift tests fail loudly until the catalog is updated.
> Nothing probes a live CLI. The prose below stays the human tour.

Drive codex only through the two wrappers (installed on `PATH`), run from the target project root:

```bash
# EXECUTION (workspace-write sandbox, network OFF, never prompts):
codex-exec docs/plans/<slug>.md                 # drive a plan file
echo "apply review fix: ..." | codex-exec -      # ad-hoc instruction from stdin
codex-exec <file|-> -- <extra codex flags...>     # GUARDED passthrough after `--` (policy/model/capture flags rejected; some relaxed only under CODEX_PROBE=1)

# RESUME (iterate on the SAME session without re-sending context):
codex-exec --resume-last docs/plans/<slug>.md    # continue the last session (id from the sidecar)
echo "now do step 2 ..." | codex-exec --resume <session-id> -

# ACCOUNTED EXECUTION (the delegation ledger's exec lane — see "Dispatch identity" below):
codex-exec --nonce <n> docs/plans/<slug>-dispatch.md   # mints a fail-closed exec receipt

# REVIEW (read-only sandbox — codex cannot edit anything, only emits findings):
codex-review plan docs/plans/<slug>.md           # critique a plan
codex-review code                                # review the current working-tree diff (precomputed)
codex-review code "focus on the new reducer"     # review with extra focus
```

**Honesty + posture (D4/D5):** a run whose final message has no recognized
`Verdict: <ship|revise|rethink>` line — empty or missing output included — **exits 4 with NO
receipt**: treat it as a *failed review to re-run*, never a fatal session error. One stderr banner
states the actual posture (`review posture: model=… effort=… tier=… timeout=…`) and the receipt
records the same `posture {model, effort, tier}` (tier `null` on the standard tier); control bytes
in a posture value refuse pre-spend in every mode. `codex-exec` states its posture the same way —
ONE `exec posture: model=… effort=… tier=… sandbox=workspace-write session=fresh|resume:<id>
timeout=…` stderr line before dispatch (the resume id validated pre-spend). The `timeout=` field
is **banner-only** (exactly the duration handed to `timeout(1)`; on exec `uncapped` without a
capping binary, while `codex-review` **fails CLOSED pre-spend** there) — informational, never a
receipt field. **Quote the posture banner verbatim** when labeling a dispatch.

`codex exec` is headless: there is **no TTY**, so `approval_policy=never` — anything needing
escalation is refused and reported, never interactively approved. The wrappers capture only codex's
**final message** (`-o`), so output is clean; the JSON event stream + reasoning go to a run trace
that is **read before it is discarded**. Fresh and resumed runs share **ONE** capture posture
(`-o` + `--json`, everything redirected into the trace; `--color never` rides the fresh lane only —
`codex exec resume` does not accept it), so both modes have the
same evidence surface — and the wrapper scans that surface on **every completed run**: a run that
SURVIVES a nested-sandbox failure exits 0 with an ungrounded answer, so on `rc == 0` it prints the
answer first and then warns loudly on stderr when one `command_execution` item with a **proven**
failure carries both a sandbox-mechanism and a permission/read-only token in its `aggregated_output`.
The exit status stays 0 there (warning, not gate) — read the stderr line. A successful **non-resume**
`codex-exec` also records the session id to a sidecar
(`${CODEX_SESSION_FILE:-./.codex-last-session}`) so `--resume-last` can find it. Extra `codex` flags
go after a literal `--`; the wrapper rejects any that would defeat the policy or the pinned model (see
[§ Environment knobs](#environment-knobs) and the flag tiers in
[`references/sandbox-and-flags.md`](references/sandbox-and-flags.md)); args without the separator are
rejected, never silently dropped.

## Environment knobs

All optional; the defaults are the supported path. Anything that would lower quality (model/effort) or
defeat a policy is guarded — see [§ Models](#models-quality-first-pinned).

| Variable | Default | Effect |
|---|---|---|
| `CODEX_MODEL` | `gpt-5.6-sol` (pinned) | model; non-default REFUSED unless `CODEX_PROBE=1` |
| `CODEX_EFFORT` | `xhigh` (pinned) | reasoning effort; non-default REFUSED unless `CODEX_PROBE=1` |
| `CODEX_HARD_TIMEOUT` | `3600` (exec) / `1800` (review) | hard wall-clock cap (seconds) via `timeout`/`gtimeout`; exit 124/137 ⇒ "exceeded hard cap". No `timeout` binary ⇒ a nonce-less exec warns loudly + runs uncapped, a **nonced** exec REFUSES pre-spend (an accounted dispatch that cannot be capped can never honour the terminal-exit rule), and `codex-review` REFUSES pre-spend (fail-closed preflight). |
| `CODEX_SERVICE_TIER` | unset (standard tier) | **SPEND knob**: `priority` (catalog name "Fast") = ~1.5× token speed at a **2.5× credit rate** on gpt-5.6-sol — quality-neutral (same model). codex accepts any `-c service_tier` string silently (probe-pinned 2026-07-05), so the wrapper validates: an unsupported value warns and runs standard. Env or settings file. |
| `CODEX_SESSION_FILE` | `./.codex-last-session` | where `codex-exec` records the session id and where `--resume-last` reads it |
| `CODEX_REVIEW_MAX_TOTAL_BYTES` | `1500000` | `codex-review code`: above this the assembled diff goes via a git-dir temp file instead of inline — never truncated |
| `AW_REVIEW_NONCE` | unset | the flow dispatch nonce (safe grammar `[A-Za-z0-9._-]{1,64}` — anything else refuses pre-spend). `codex-review … --nonce <n>` is the plain-argument equivalent (one seam; flag and a non-empty env must agree, a disagreeing pair refuses pre-spend) — the lane for hosts whose dispatch policy has no env-prefix form. When supplied, a successful review first mints the finding MANIFEST `agent-workflow-finding-manifest-codex-<nonce>.json` beside the receipts file (atomic, no-clobber, ORDERED before the receipt append) — a failed mint EXCLUDES the receipt, so a nonce-supplied dispatch never lands a receipt without its readable manifest; nonce-less runs add no nonce field and mint nothing (the `wrapperVersion` field every receipt carries moves with each release) |
| `AW_DISPATCH_NONCE` | unset | the **delegation** dispatch nonce (same safe grammar; anything else refuses pre-spend). `codex-exec [--nonce <n>] <plan-file>` is the plain-argument equivalent — ONE seam, recognised only BEFORE the prompt operand (after it, or after a literal `--`, it is passthrough payload). When supplied, the run is ACCOUNTED: see [§ Dispatch identity](#dispatch-identity-the-accounted-exec-lane). |
| `AW_DELEGATION_STORE` | unset (the git common dir) | absolute path of the delegation ledger; its **dirname** is where a nonced run's receipt and report land. Relative, or ending in a path separator, refuses pre-spend — the same rule the kit's store applies. |
| `CODEX_REVIEW_SCHEMA` | unset | `codex-review`: `=1` returns findings as a validated JSON object (`--output-schema`), with a raw-text fallback. Default off. |
| `CODEX_PROBE` | unset | `=1` ⇒ throwaway-probe mode: relaxes the model/effort guard AND the tier-2 passthrough guard (echoed loudly). Never for real work. |

The git-write shim, `--ignore-user-config`, and the `*_API_KEY` scrub are NOT env-tunable — they are
fixed invariants.

### Dispatch identity — the accounted exec lane

A **nonced** `codex-exec` run has an identity the delegation ledger can absorb. Everything here is
skipped entirely without a nonce: the wrapper is byte-unchanged, writes no artifact and needs no
`node`.

- **Pre-spend, it RESERVES the nonce.** Immediately before the CLI runs — after every preflight and
  after the posture banner — it publishes `agent-workflow-exec-receipt-<len>-<backend>-<nonce>.json`
  in state `reserved`, atomically and **no-clobber**, beside the delegation store. A second dispatch
  on the same nonce (or a leftover report under that name) refuses **before any spend**. So does a
  run with no capping binary, without `node`, with the prompt on stdin instead of a contract file, or
  with a file carrying no ` ```aw-dispatch-contract ` block.
- **`contractDigest` is computed HERE**, by the wrapper, from the dispatch file it was handed — an
  independent value, never a copy of what the ledger holds, so `dispatch return` can refuse a run
  that executed a *different* contract than the one it opened.
- **At exit it publishes, in this ORDER**: verify the reservation is still ours → write the
  delegate's final message to `agent-workflow-exec-report-<len>-<backend>-<nonce>.txt` → verify
  again → replace the reservation with the `terminal` receipt. An artifact that has arrived therefore
  always has a complete report behind it, and a **foreign owner publishes nothing at all**.
- **The outcome is a SUBSET** the run can prove about itself: exit 0 with a session id → `success`,
  exit 0 without one → `missing-identity`, any nonzero exit (124/137 included) →
  `transport-failure`. Every orchestrator judgment is recorded later, at absorb time.
- **FAIL-CLOSED, unlike the review receipt.** A review receipt that cannot be written only warns; an
  exec receipt that cannot be written leaves an EDITED tree with no accounting, so the wrapper exits
  nonzero and calls the tree partial/dirtied — never untouched. The two statuses carry **different**
  recoveries:
  - **71** — a publication stopped. The reservation is the run's own, so absorb the thread with
    `dispatch return --nonce <n> --no-receipt --exit-status <n> --outcome <o>`. The message says
    whether the report reached disk: if it did the absorb reads it, if it did not the absorb records
    `reportLength 0` and the metric is ineligible by the name `empty-report`.
  - **70** — the reservation could not be verified *before* anything was published, so nothing was.
    `--no-receipt` is **not** the recovery here: it would source `wrapperVersion` and `posture` from
    an artifact that belongs to another run. Establish what replaced the reservation first.

### Settings file (host-level, survives kit upgrades)

`${XDG_CONFIG_HOME:-~/.config}/agent-workflow/bridge-settings.conf` holds `KEY=VALUE` lines,
**parsed, never sourced** — a file line can never execute code. Precedence: explicit env (even
empty — `KEY=` disables a knob for one run) > file > built-in default. File-settable keys for this
bridge: `CODEX_SERVICE_TIER` (the Fast tier — **2.5× credit rate**; enabling it is a consented
per-host spend decision, never a default), `CODEX_HARD_TIMEOUT`, `CODEX_REVIEW_MAX_TOTAL_BYTES` —
exactly the manifest `settings` block (the single source; the wrapper constants and `--help` are
drift-guarded against it). Model/effort keys are **not** file-settable — the quality guard above
is untouched. The file lives **outside every kit-managed tree**, so a kit refresh/upgrade can
never wipe it; edit it by hand or via `/agent-workflow-kit bridge-settings` (preview-first,
consent-gated).

## Project context (how `codex` sees the repo)

`codex` auto-**merges** `AGENTS.md` (root→cwd, plus a global `~/.codex/AGENTS.md`) straight into its
developer context, truncated at `project_doc_max_bytes` (default 32 KiB) — so when you run a wrapper
from a project root, the project's Hard Constraints are already in front of the model with no wiring (a
probe confirmed codex returned a repo's declared dialogue language from `AGENTS.md`). The wrappers
therefore **hardcode no project rules**, and the orchestrator contract is **lean**: it tells codex to
*obey* the already-merged `AGENTS.md` Hard Constraints + declared gates — it does NOT waste a step
telling codex to go *read* a file that is already in context.

**Fallback is strict.** Both wrappers preflight that they run inside a git work tree and that a root
`AGENTS.md` exists — if either is missing they **STOP and report** (a wasted subscription run is
avoided). And the execution contract tells codex: if the project declares **no** verification/gate
set, **STOP and report** rather than invent checks. Pass `--skip-git-repo-check` to codex only when
you truly mean it.

## How the main agent drives `codex` efficiently

See [`references/driving-codex.md`](references/driving-codex.md) for the full playbook. Essentials:

- **`codex-exec` for doing, `codex-review` for judging.** Use exec to implement a plan/fix under the
  sandbox; use review to get advisory findings on a plan or diff without any edits.
- **The orchestrator commits — codex never does.** The execution contract forbids every git write
  (branch/add/commit/stash/reset/checkout/tag/rewrite), and `codex-exec` additionally enforces it with
  a physical **git-write shim** on the codex subprocess's `PATH`: read-only git verbs pass through,
  every write/unknown verb is blocked (codex spawns `git` via `execve`, which bypasses shell
  functions — so the boundary must be a real file). You review codex's diff, then commit yourself.
- **Treat output as advisory** and verify before acting — re-run the project's gates yourself, reject
  advice that conflicts with user instructions or repo rules.
- **Hand codex a self-contained task.** It cannot see your conversation — for an ad-hoc instruction,
  embed the goal, the relevant paths, and the expected result; codex reads `AGENTS.md` for the rules.
- **Iterate with `codex-exec --resume-last` / `--resume <id>`** instead of re-sending context. The
  resume entrypoint re-establishes EVERY wrapper invariant (subscription-only, `--ignore-user-config`,
  the pinned model/effort) and **restates the full posture via `-c`** — `codex exec resume` resets the
  sandbox/approval/network posture and rejects the `-s`/`--add-dir`/`-C` posture flags, so the wrapper
  sets `sandbox_mode=workspace-write` + `approval_policy=never` +
  `sandbox_workspace_write.network_access=false` explicitly. It reads the session id from the sidecar
  (`--resume-last`) or takes it as an argument.
- **Network is OFF in exec.** New dependencies and any network step are installed by hand, then codex
  is re-dispatched.
- **`codex-review code` precomputes the diff.** The wrapper assembles the change set (repo map, status,
  staged + unstaged diff, untracked file contents) and feeds it in, so codex does not roam the
  filesystem rediscovering it; a clean tree exits 0 before a run is spent. Native `codex review` is
  deliberately NOT used — it rejects `--ignore-user-config` and would load a personal config.toml,
  breaking the subscription/config-isolation invariant.

## Complementary skills (optional, standalone-first)

The wrappers work in any git repo where `codex` is installed and authenticated. The skills below are
**not required** — surface them only when they actually help.

- **`antigravity-cli-bridge`** (sibling backend, Google `agy`) — recommend **by actual presence**: if
  `~/.claude/skills/antigravity-cli-bridge/` exists you have a **second delegated engine** (codex for
  sandboxed repo edits with gates; `agy` for subscription-quota Gemini/Claude/GPT-OSS reasoning). If
  it is **not** installed, treat it as a planned sibling — don't assume it exists.
- **`agent-workflow-memory`** (family **context provider**) — if the target project has **no**
  `AGENTS.md` + `docs/ai/`, codex has no root context to read (and the wrappers' preflight will
  STOP). The memory substrate is what creates that context. Soft-recommend it (only when the user
  wants the memory workflow): `npx @sabaiway/agent-workflow-memory@latest init`, or bootstrap the whole
  family via the **`agent-workflow-kit`** orchestrator (`npx @sabaiway/agent-workflow-kit@latest init`),
  which delegates substrate deployment to memory and injects the workflow methodology. Never a
  prerequisite.

## Known limitations

- **Network is OFF** in `codex-exec` (`sandbox_workspace_write.network_access=false`): codex cannot
  install dependencies or reach the network — do that by hand, then re-dispatch.
- **No live approvals** — `codex exec` has no TTY, so `approval_policy=never`; an action that would
  need escalation is reported, not approved interactively.
- **`resume` resets the posture** — `codex exec resume` rejects `-s`/`--add-dir`/`-C` and forgets the
  original sandbox/approval/network policy. The `codex-exec --resume`/`--resume-last` entrypoint
  restates it via `-c`; only a *raw* `codex exec resume` (bypassing the wrapper) loses the posture.
- **Hard timeout** — a hung run is killed at `CODEX_HARD_TIMEOUT` (exec 3600s / review 1800s) and
  reported (exit 124/137); raise it for a known-healthy slow run. If neither `timeout` nor `gtimeout`
  is on `PATH`, a nonce-less `codex-exec` warns loudly and runs uncapped, a **nonced** one refuses
  pre-spend, and `codex-review` refuses pre-spend (the fail-closed preflight — an uncapped review run
  no longer exists).
- **The wrapper cannot enforce an ABSOLUTE deadline** — it applies its own cap from ITS start and
  never reads the ledger, so a dispatch started long after `dispatch open` is caught at absorb time
  (the return refuses a late receipt), not pre-spend. Keeping that window small is the
  orchestrator's rule: `open` is the last act before the dispatch.
- **The gate output is not accounted** — the run trace is a temp file the EXIT trap removes, so a
  nonced run's report carries the delegate's final message only; the metric counts the returned
  change set, never what the gates printed.
- **Native `codex review` is out of scope** — it rejects `--ignore-user-config` (would load a personal
  `config.toml` and break the subscription/config-isolation invariant) and can't be cleanly captured;
  `codex-review` runs `codex exec` over a precomputed diff instead.
- **bubblewrap** — on Linux, if `bubblewrap` is not on `PATH` codex prints a warning and uses a
  bundled copy; install it via your package manager to silence the warning.
- codex output is advisory and may be incomplete or out of date — the main agent verifies before
  acting.
