| name | codex-goal-overseer |
| description | Read-only overseer for a Codex CLI session running /goal autonomously. A dedicated Claude Code session can either LAUNCH a fresh, fully-autonomous Codex as a subagent (codex --dangerously-bypass-approvals-and-sandbox, optionally seeded with a /goal) or ATTACH to one already running, then audits it on a self-managed cron tick using read-only Explore subagents (model opus). It derives the project's drift criteria from the target's own transcript and, on a finding, sends a single Escape to soft-stop the run, lands it idle, logs the finding to the terminal and a log file, and PAUSES -- handing off to the human. After the human reviews and directs a new instruction, the overseer types and submits it into the interrupted /goal so it resumes, then optionally auto-resumes the audit. The only writes to the target are the Esc and the human-directed instruction; everything else is read-only. Use to supervise an autonomous Codex /goal run for direction drift, fabricated data, fake/stub implementations, and broken project invariants. Triggers on "monitor codex goal", "launch and watch a codex goal", "oversee codex /goal", "watch the codex session", "babysit the codex goal run", or any request to launch, supervise, interrupt, or hand off an autonomous Codex build loop from a separate session. |
| argument-hint | [--launch "<objective>" | <codex-session-id> <session:window.pane>] [--cadence 1h] [--principles "<rules>"] [--no-resume] |
| user-invocable | true |
Codex Goal Overseer
A dedicated Claude Code session pairs with a Codex CLI session that is
autonomously running /goal (a long-running, self-driving build loop) and audits
it for trouble. It never writes the target's code. When it finds a problem it does
exactly two things to the target -- soft-stop it (Escape) and, after the human
reviews, type the human's new instruction back in -- and otherwise only reads.
The session is an auditor with a kill-switch and a handoff, not an automation
agent. Explicit locators, runtime-derived criteria, and a human-in-the-loop on
every course correction are what make it reusable without making it dangerous.
It can also start the Codex it watches, but the human, not the overseer,
decides every new direction.
Two ways to get a target
- Launch mode (new). The overseer creates its own tmux pane, starts a
fully-autonomous Codex there, optionally seeds it with an initial
/goal, then
resolves the pane + session id and begins auditing. The launch uses exactly
codex --dangerously-bypass-approvals-and-sandbox. See Launch mode.
- Attach mode (existing). You already have a Codex
/goal running; you pass
its session id and tmux pane, and the overseer attaches and audits. See
Attach mode.
Either way, once a (session-id, pane) pairing is established the audit loop is
identical (see The audit tick). The skill applies to any Codex /goal
session: it needs only where to read (the transcript) and where to act (the tmux
pane), and it bootstraps everything else -- including what counts as "drift" --
from the target itself.
Invocation
/codex-goal-overseer --launch "<objective>" [--cadence 1h] [--principles "<rules>"] [--no-resume]
/codex-goal-overseer <codex-session-id> <session:window.pane> [--cadence 1h] [--principles "<rules>"] [--no-resume]
--launch "<objective>" -- Launch mode. Create a new pane, start
codex --dangerously-bypass-approvals-and-sandbox, seed it with
/goal <objective>, resolve the pane + session id, then audit. Omit the
objective text (--launch) to start Codex without seeding a goal (you will
seed it yourself).
<codex-session-id> -- Attach mode. The Codex session UUID. Resolves the
transcript at ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<id>.jsonl, the
read surface (progress + the human's prior steering).
<session:window.pane> -- Attach mode. The tmux pane where the Codex TUI
runs, e.g. Z-manage:main.2. Codex usually occupies a single pane inside a
window, so the locator is pane-precise. This is the act surface (Esc + the
human's instruction).
--cadence -- audit tick interval (default 1h).
--principles "..." -- extra drift rules to merge on top of what is derived
from the transcript (for rules the human holds that are not in the transcript).
--no-resume -- after a human-directed instruction is delivered, do not
auto-resume the audit cron; leave it paused for the human to resume. Default is
to resume the audit automatically.
If you are not sure which pane/session is the live Codex, run
scripts/locate-codex.sh first: it sweeps every tmux pane, flags the ones that
look like a live Codex TUI, and lists recent Codex /goal transcripts, so you can
confirm the exact (session-id, pane) before attaching.
The overseer must run in its own pane on the same tmux server as the target.
Alerting (headless host)
This host is headless (no desktop, no audio), reached over SSH, so alerting is
terminal output plus a log file -- there are no phone or push notifications.
Every finding, interrupt, handoff, terminal condition, and delivered instruction
is both printed in the overseer's terminal and appended to:
./temp/codex-goal-overseer/monitor.log
Each log line is a structured, self-contained record (timestamp, tick id, what,
and the evidence pointer -- a transcript turn/timestamp or a git path), so the log
alone reconstructs why the overseer acted. The inject-steer.sh and
interrupt-target.sh evidence dumps land under the same ./temp/codex-goal-overseer/
root, so a finding and the keystrokes it triggered are auditable together.
Read-only safety contract
This is load-bearing, not advisory. The overseer's value depends on it being
unable to corrupt what it watches.
The only writes the skill makes to the target are two keystroke actions, each
through a hardened script after verify-target confirms a live Codex pane:
- the Escape interrupt on a finding (
scripts/interrupt-target.sh), and
- the human-directed instruction, typed and submitted only after the human
has reviewed and directed it (
scripts/inject-steer.sh).
Everything else the overseer does is read-only.
Allowed (read-only): git status/diff/log/show, rg/grep, jq, tail,
head, sed (read), ls, find, stat, wc, python for parsing only,
tmux capture-pane / list-panes / display-message.
Banned: any write into the target repo's tracked files; apply_patch or any
editor; git add/commit/reset/checkout/clean; builds or tests that mutate or
produce artifacts; package installs; deletions; modifying the target
transcript; and any raw tmux send-keys to the target other than the two
sanctioned actions above. There is no autonomous steering: the overseer never
composes and sends a course correction on its own -- a human authors every new
instruction.
Subagents are Explore-type only -- they physically lack Edit/Write/build
tools. Their prompts must also state, in words, that they may only read.
The only other writes the skill performs: (a) its own cron via
CronCreate/CronDelete, and (b) the log + evidence dumps under
./temp/codex-goal-overseer/. Neither touches the target repo or transcript.
Launch mode
Launch mode lets the overseer run Codex itself as a fully-autonomous subagent and
then watch it.
FULL HOST PERMISSIONS, NO SANDBOX. Launch runs exactly
codex --dangerously-bypass-approvals-and-sandbox: maximum permissions, no
approval prompts, no sandbox. The launched Codex can read, write, and run
anything you can. This is the user's explicit, authorized automation choice
and is appropriate only for goals the user trusts to run unattended. The
overseer that audits the result is the safety rail; the launch itself has none.
Do not use launch mode for an untrusted or exploratory objective -- use Codex's
normal sandboxed flow for those.
Flow:
- Confirm intent. Launch mode starts a no-sandbox, no-approval agent. State
that plainly and proceed on the user's explicit request (the
--launch
invocation is that request).
- Launch. Run
scripts/launch-codex.sh --goal "<objective>" (drop --goal
to start without seeding). It creates a fresh tmux window, starts
codex --dangerously-bypass-approvals-and-sandbox, waits until the pane is a
live Codex TUI (same verify-target gate the rest of the skill uses), submits
/goal <objective> via inject-steer.sh send when an objective is given, then
resolves the newest rollout session id that appeared after launch.
- Capture the pairing. Parse the script's
PANE= and SESSION_ID= lines.
That (session-id, pane) is exactly what attach mode would have taken.
- Audit. Continue into Loop and cadence with that pairing -- run the
light preflight, one tick immediately, then schedule the cron.
scripts/launch-codex.sh exit codes: 0 launched (id resolved), 10 tmux
unavailable / pane not created, 11 codex CLI not found, 12 pane never became a
live Codex TUI, 13 initial /goal seed failed to submit (pane is live -- seed
it by hand, then attach), 14 live but no new rollout id resolved yet (re-scan
~/.codex/sessions or run locate-codex.sh), 64 usage error. On 13/14 the
pane is alive, so prefer attaching to it over relaunching (relaunching would spawn
a second autonomous Codex).
Attach mode
You already have a Codex /goal running. Pass its <codex-session-id> and
<session:window.pane>. The overseer:
- Resolves the transcript from the session id and confirms the pane with
inject-steer.sh verify-target (a live Codex TUI, not a shell, not the
overseer's own Claude pane, not in copy mode).
- If either is missing/ambiguous (transcript not found or rotated, pane gone or
not Codex), it stops and asks you to re-specify -- it never guesses a different
transcript or pane.
scripts/locate-codex.sh lists the candidates.
- Proceeds into Loop and cadence with that pairing.
The audit tick
Each cron firing re-enters this skill fresh. Subagents do the heavy transcript
reading and return only conclusions, so the overseer's own context stays lean
across a multi-hour run. The tick, in order:
- Re-locate the target -- resolve the transcript from the session id; confirm
the tmux pane via
inject-steer.sh verify-target. If the transcript is
missing/ambiguous/rotated, or the session ended, or /goal is complete or
blocked, or the pane is gone/renamed/not a live Codex TUI -> log the diagnosis
to terminal + monitor.log and, for terminal conditions, offer CronDelete.
- Refresh drift criteria -- re-derive from the transcript (see below), newer
steering overriding older, merged with
--principles.
- Dispatch three read-only Explore subagents in parallel (see Audit
dimensions), each on the Opus model (dispatch with
model: opus).
Strongly restate read-only in each prompt.
- Synthesize against the four judgment criteria.
- Decide: clean -> append a one-line "clean" record to
monitor.log and
leave a one-line conclusion in the session; no interrupt. Finding -> On a
finding.
A clean tick is quiet: a single log line, no interrupt, no handoff.
Drift-criteria derivation
The strength of this skill is that "what counts as drift" is not hardcoded -- it
is read from the target session's own human steering history each tick.
- Parse the transcript line-by-line (it can be tens of MB; never read it
whole). Each line is JSON with
type and payload; payload.type is one of
user_message, agent_message, reasoning, function_call,
patch_apply_end, thread_goal_updated. Timestamps are ISO UTC.
- Extract the human's
user_message entries and distill the project's
first-principles rules from them.
- Newer steering overrides older. Each derived rule should cite its
originating turn/timestamp. When two human instructions genuinely conflict,
prefer the most recent explicit one; if still ambiguous, ask the human -- do
not interrupt Codex on a guess.
- Merge any
--principles the human passed at invocation.
These derived rules are intentionally not persisted -- re-deriving each tick
keeps them in sync as the human's priorities evolve and avoids a second source of
truth.
Audit dimensions
Three read-only Explore subagents per tick, each blind to the others, each
dispatched on the Opus model (the audit judgement needs the strongest model; do
not downgrade these to a smaller tier):
- Transcript progress -- what Codex did since the last checkpoint, what it
claims, whether it ran its own independent audits or self-certified, and the
/goal state (active / blocked / complete, token + time burn).
- Git commits + worktree diff since the last checkpoint HEAD -- red flags:
hardcoded magic numbers used as simulation/expected RESULTS baked into producer
code or tests; pass/status with no real artifact behind it; disabled or skipped
functional tests; stub returns dressed as real computation; whether the
unpushed-batch-then-review discipline is being followed; and commit-message
hygiene.
- Artifact reasonableness -- the invariants here are derived from the
transcript/repo, not hardcoded. Typical shapes: distinct kernels/apps must
produce distinct numbers; results must trace to a real report (not fabricated,
not all-zero, missing-evidence not written as zero); a constrained model must
not be more optimistic than its idealized baseline; first-principles
monotonicity (e.g. longer input must cost more).
Judgment criteria
- Drift -- violates the human's established principles. But deep-diving or
optimizing a fundamental design decision is NOT drift -- it is exactly the
kind of foundational work the human wants; only a genuine departure from stated
principles counts.
- Fabricated data -- hardcoded numbers as results, pass-without-evidence, or
an artifact that exists but whose data is not reasonable.
- Fake / stub implementation -- plausible-looking returns claimed as real
hardware-aware / functional behavior.
- Project-specific invariant breaks -- the derived invariants above, most
notably distinct things reporting identical numbers, where the target's own
audit may have let it through.
On a finding: interrupt, log, hand off
When the synthesis is a real finding (not foundational work), the overseer does
not decide the fix. It stops Codex and hands off to the human.
- Verify the target, then soft-stop it --
scripts/interrupt-target.sh <pane>: verify-target -> send a single Escape -> confirm the pane left
its Working / streaming state and is back at an idle composer. Escape is a
soft interrupt: the Codex process stays alive and the goal context is intact;
only the in-flight turn stops, so the human can review and re-instruct without
losing the thread. If verify-target says the pane is not a live Codex (or
is gone), refuse to send Escape -- log the diagnosis and stop. A wrong pane is
never interrupted.
- Report the finding to the terminal and append it to
monitor.log,
structured: what tripped which judgment criterion, with the evidence pointer
(the transcript turn/timestamp or the git path), and the interrupt-target.sh
exit code.
- Pause the audit loop -- stop auto-ticking (
CronDelete the audit job, or
suspend it) so the overseer is not re-auditing a deliberately-stopped run. The
overseer now waits for the human; it does not compose or send any
instruction on its own.
interrupt-target.sh exit codes: 0 interrupted and idle, 10 target gone, 11
not a live Codex TUI (refused), 20 pane unstable, 40 still Working after
Escape (soft stop did not take), 64 usage error. On 40, do not assume the
turn stopped: surface the evidence dump, report that the soft stop did not take,
and leave it to the human (a second Escape may be warranted, or Codex may be
mid-tool-call). On 10/11, treat as a re-locate failure -- never send a
follow-up instruction to a pane you could not interrupt.
After the handoff: deliver the human's instruction
The human reviews the interrupted run in the terminal and directs a new
instruction. The overseer is the mechanical delivery arm for that
human-authored instruction -- it carries intent the human supplied; it does not
invent the course correction.
- Verify the target again (
inject-steer.sh verify-target). If the pane is
no longer a live Codex, refuse and tell the human; never type into a wrong
pane.
- Compose for the goal contract. Take the human's instruction and make it a
well-formed message for the active
/goal thread (see Composing the
instruction). Preserve the human's intent; add the contract structure. If the
instruction contradicts the active goal or an established principle, surface
that and confirm rather than silently delivering something incoherent.
- Deliver it --
inject-steer.sh type <pane> <file> lands the text in
Codex's composer unsent; the human may eyeball it in the pane; then
inject-steer.sh submit <pane> <file> presses Enter and verifies the send. (Use
inject-steer.sh send for type+submit in one step if the human said "just send
it".) Text is always passed via a file, never argv. Report the exit code;
on a nonzero exit, surface the evidence dump and do not assume the
instruction was delivered -- hand it back to the human.
- Resume the audit -- by default,
CronCreate the audit tick again so the
overseer watches the now-continuing /goal. With --no-resume, leave it
paused for the human to resume manually.
The interrupted /goal continues from the human's instruction; the overseer goes
back to read-only auditing.
Composing the instruction
The human-directed instruction is a message injected into an active Codex
/goal thread, so it must speak the goal contract's language, not just say "keep
going" or dump a vague ask. Before composing, you may consult the bundled
reference references/codex-goal-cookbook.md (the guide to Codex Goals) to keep
the instruction consistent with how a goal is defined and audited. It is an
optional phrasing aid, not a gate.
A good instruction:
- Names the outcome / end state it wants, in terms the live goal can audit.
- Points at the verification surface -- the test, benchmark, report, artifact,
or evidence that proves it -- never "trust me, it is done".
- Restates the constraints that must not regress (the project's established
principles: SSOT, no fabricated data, no fake/stub, and so on).
- Respects the goal's boundaries (the files, tools, and scope already in play).
- Says how Codex should choose the next action, and when to treat itself as
blocked rather than declare false success.
- Stays narrow enough to audit but open enough for Codex to investigate.
Keep it tight and single-purpose: the instruction augments the live goal, it
does not restate or redefine the whole goal. The human's intent always governs;
the contract structure is just how it is phrased.
The scripts
They all live under ~/.claude/skills/codex-goal-overseer/scripts/. The two that
write to the target make the keystroke dance deterministic instead of
LLM-improvised: a TUI's render timing is racy, and reacting to pane text by hand
risks fumbling quoting on CJK/quotes/newlines or double-submitting. Each turns
"looks like it worked, probably" into a checkable state machine with exit codes,
and the orchestrating session branches on the exit code -- it never eyeballs pane
text to decide success.
launch-codex.sh (launch mode)
Creates a fresh tmux window, runs exactly
codex --dangerously-bypass-approvals-and-sandbox, waits until the pane is a live
Codex TUI (same verify-target detection), optionally submits an initial
/goal <objective>, resolves the newest post-launch rollout session id, and
prints PANE= / SESSION_ID=. Exit codes as in Launch mode. The no-sandbox /
no-approvals flag string is a single constant in the script so the launch contract
cannot drift.
interrupt-target.sh (the Escape)
verify-target -> send a single Escape -> verify the pane is no longer in its
Working / streaming state. Non-mutating beyond the one Escape. Exit codes as in
On a finding. Refuses (exit 11) anything that is not a live Codex pane.
inject-steer.sh (the human-directed instruction)
The hardened type/submit machinery for delivering the human's instruction. Text is
always passed via a file, never argv.
| Subcommand | Contract |
|---|
verify-target <pane> | Non-mutating health check: pane resolves, not dead, input not off, not in copy mode, identified as a live Codex process, capture nonempty and stable. (Codex runs on the PRIMARY screen, so the target is matched by process tree, not alternate-screen -- which is what stops the instruction from landing in a plain shell or the overseer's own Claude pane.) |
type <pane> <file> | load-buffer then paste-buffer -p -r (bracketed paste; -r stops LF becoming Enter), then verify the text landed near the input -- either as a normalized tail-signature (short pastes render inline) or as a collapsed [Pasted Content N chars] placeholder whose count matches the file (long pastes; Codex collapses them in the composer). No Enter. |
submit <pane> [file] | Press Enter, verify the instruction left the live input line (literal signature or the collapsed placeholder), retry Enter once only if there was no pane delta and the text is still in the input region; stop on any ambiguity. At most two Enters. |
send <pane> <file> | Atomic type + submit. Used both by launch-codex.sh to seed the initial /goal and to deliver a "just send it" instruction. |
Exit codes: 0 success, 10 target missing/dead, 11 not a live Codex TUI, 20
pane busy/unstable, 30 paste failed, 31 text-landed verify failed, 40 submit
verify failed, 41 ambiguous post-Enter (retry suppressed), 64 usage error. A
nonzero exit means do not assume the instruction was sent: surface the
evidence dump and hand it back to the human.
Every run of either writing script dumps evidence (target metadata,
before/after-paste/after-enter captures, byte count + hash) under
./temp/codex-goal-overseer/, alongside the finding log -- so each keystroke is
auditable next to the finding that caused it.
locate-codex.sh (discovery, read-only)
Sweeps every tmux pane, flags the ones that look like a live Codex TUI, and lists
recent Codex /goal transcripts so you can confirm the exact (session-id, pane)
before attaching. Strictly read-only: no tmux state mutated, no files written.
Loop and cadence
The overseer self-manages its own cron. On first invocation (after a launch or
an attach): run a light preflight (confirm the pairing resolves and verify-target
passes), one audit tick immediately, then CronCreate a recurring job (default
hourly, on an off-the-hour minute to dodge top-of-hour congestion). Report the
cron id so the human can stop it. The job re-enters this skill each firing.
The cron is paused on a finding (see On a finding) and resumed after the
human's instruction is delivered (unless --no-resume). CronDelete the job on
termination, when /goal completes, or when the target is gone.
The cron lives in the overseer session's memory: closing the overseer session or
its tmux pane stops the loop. The overseer pane must stay alive. (In launch mode,
the overseer pane and the Codex pane are different panes on the same server;
closing the overseer pane stops the auditing, not the launched Codex.)
Failure modes
- Transcript not found / multiple matches / rotated -> halt the tick, log, ask the
human to re-specify; never guess a different transcript.
- Session ended, or
/goal complete or blocked -> terminal-condition log, offer
CronDelete.
- Pane missing / renamed / reused, or
verify-target fails (not Codex, in copy
mode, input off, dead) -> never Escape, never type; log and ask.
- Multiple
/goal candidates -> locate-codex.sh lists them; the human confirms.
- Derived-criteria conflict -> newer human instruction wins; genuine ambiguity ->
ask the human, do not interrupt.
interrupt-target.sh exit 40 (still Working) -> do not assume stopped; surface
evidence; leave to the human.
inject-steer.sh nonzero exit -> do not assume the instruction was sent; surface
evidence; hand back to the human.
- Launch:
12 pane never became Codex -> kill the stray window and retry; 13/14
pane is live but seed/id incomplete -> attach to the live pane, do not relaunch
(avoid a second autonomous Codex).
Discipline (the invariants)
- Everything read-only except the two sanctioned keystroke actions; never modify
the target repo's tracked files or its transcript.
- The overseer observes, interrupts on a finding, and delivers the human's
instruction; it never authors or sends a course correction on its own.
- No keystrokes to Codex except
interrupt-target.sh (the Escape) and
inject-steer.sh (the human-directed instruction), each after verify-target.
- A wrong / non-Codex pane is never interrupted and never typed into.
- A clean tick is quiet: one log line, no interrupt, no handoff.
- Deep-diving a fundamental design is not drift -- do not cry wolf on foundational
work.
- Launch mode runs Codex with full host permissions and no sandbox; use it only
for goals the user has explicitly authorized to run unattended.