Skip to content

Run a Command's sessions on Codex - #404

Merged
JacobStephens2 merged 4 commits into
issue-136from
issue-394
Oct 5, 2026
Merged

JacobStephens2 merged 4 commits into
issue-136from
issue-394

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

With harness codex, every session of a Command runs on Codex: the implement session, Repairs, a Resume, a Spec run's Ticket Runs and Spec review, and an Architect run's Architecture review. The "not supported yet" refusal is gone.

 Choice::check                         # before the Claim, the worktree and any log
   cli on PATH
-  codex → bail "not supported yet"
+  codex → codex debug models          # costs no tokens
+          GPT-6.1-Sol / Max  →  gpt-6.1-sol / max, else name the valid choices
   claude → test call on the Model
 Sessions::within
-  link skills → .claude/skills/
+  link skills → .claude/skills/ | .agents/skills/   (+ .git/info/exclude)
   Sessions::start
-    claude -p --permission-mode auto … <prompt>
+    codex exec --json --dangerously-bypass-approvals-and-sandbox
+      [-m <model>] [-c model_reasoning_effort="<effort>"]
+      -c project_doc_fallback_filenames=["CLAUDE.md"] [resume <thread id>] $thirdshift-<skill> …
+    stdin null; stream → progress::Stream (ClaudeProgress | CodexProgress)
CodexProgress (src/progress/codex.rs)
  thread.started   → session id, "session started"
  item.*           → one line per command (shell wrapper stripped), file change, tool call
  agent_message    → final message (last one)
  turn.completed   → "N input tokens (C cached), M output tokens"; commands still in_progress = killed background work → Resume
  turn.failed      → fails the Run with Codex's error; a retried `error` event alone doesn't

The settled names are what the Command log, Activity log, PR body (Built with codex · gpt-6.1-sol · max) and Run notification record, and what child Runs and a Base fix get. The fake gains a codex personality: debug models from a fixed catalog, exec --json running the same per-test scripts as the fake claude. prompts/ keeps the Claude rendering, with one note on Codex. README and --help describe the Codex sessions.

Evidence

  • Before: thirdshift harness codex <issue> → harness codex, chosen by the harness flag, is not supported yet.
    After: tests/codex_sessions.rs passes, and so does the full cargo test run (34 test binaries):
run(harness codex model GPT-6.1-Sol effort Max, #7), red CI then green
  → 2 codex sessions (implement + CI-fix Repair), argv = exec --json --dangerously-bypass… -m gpt-6.1-sol
    -c model_reasoning_effort="max" -c project_doc_fallback_filenames=["CLAUDE.md"] <prompt>
  → stdin_null, every thirdshift-* skill in .agents/skills/, git status empty, prompt starts "$thirdshift-implement"
  → stderr has "implement: $ cargo test", "…: 1200 input tokens (200 cached), 300 output tokens"
  → PR body ends "Built with codex · gpt-6.1-sol · max"; Session log is Codex's JSONL
command left in_progress → resume fake-thread-1 with every flag again
FAKE_CODEX_ERROR + exit 1 → "codex exited 1: …"; turn.failed → "codex's turn failed: …"; killed → "codex exited by signal: …"
model gpt-7 / GPT-5.5 effort Max → fails before Claim/worktree/log, "choose one of …"
architect harness codex → review, Tickets #9 #10, Spec review: 4 codex sessions, no claude
merge harness codex → PR merged

Merge Danger

Door: two-way

Revert restores the refusal. The only lasting side effects are the /.agents/skills/thirdshift-* line in .git/info/exclude, and Codex's own trust_level entry in ~/.codex/config.toml (ADR-0012).

Blast Radius: sessions

Claude sessions now go through the shared Stream trait and session_args. Their argv, prompts and prompts/ rendering are unchanged, and the Claude suites pass. Choice::check now settles names in place, so run_to_end, architect::run and pickup::run take &mut, and started.built_with is called after the run instead of before it.

Unaddressed findings

Standards

  • Choice::check mutates instead of returning a settled Choice. Skipped: the check has to run inside run (after the Launch directory checks), so the settled value would need the same plumbing back out to Asks and BaseFix either way.
  • A match harness in four modules (Repeated Switches / Shotgun Surgery). Skipped: each match sits where its concern lives (skills dir, stream, argv, check). One Harness trait would be speculative with two Harnesses.
  • ClaudeProgress doesn't override failed()/error() (Refused Bequest). Skipped: making a Claude is_error result fatal would change Claude's behaviour, which is out of scope.
  • Choice::codex_args and session::codex_args share a name. Skipped: this mirrors the existing Choice::claude_args / session::claude_args pair.
  • Duplicated Asks { harness, ..Asks::of_…() } in architect and pickup. Skipped: two three-line sites with different constructors; a helper would add more than it removes.
  • The fake codex borrows pub helpers from the fake claude. Skipped: moving them into a shared module is churn in test-only code for no behaviour change.

Spec

  • An Effort with no Model is checked against the union of every Model's levels, not the default Model's. Skipped: the catalog doesn't say which Model Codex will default to (that comes from the user's ~/.codex/config.toml). The union still catches typos and case, and the README documents it.
  • Resume, shell-unwrapping of progress lines, hiding visibility: hide Models from the choices list, and README notes on trust and ~/.claude/ were not asked for. Kept: each is needed for "just as it does on Claude" (the Resume and killed work go through the shared Sessions), or keeps the progress lines and error messages readable.
  • codex_prompt only rewrites a leading /thirdshift-. Kept: every Session prompt that loads a skill starts that way, and a unit test now covers each kind.
  • A failed check records the untranslated names in the notification. Skipped: nothing was settled, so the names as asked are the honest record.
  • A non-zero exit with no turn.failed can cite a stale retried error. Skipped: with no turn.failed, the last error Codex reported is the best cause on offer, and it beats none.

Closes #394

🤖 Generated with Claude Code

JacobStephens2 and others added 4 commits October 5, 2026 00:56
harness codex runs every session as codex exec --json, unsandboxed, with
stdin null, the Model and Effort as Codex names them and the CLAUDE.md
fallback. The Factory skills are linked into .agents/skills/, and each
prompt loads its skill as $thirdshift-<skill>. Codex's JSONL stream gives
the progress lines, session id, final message and failures. The Model and
Effort are checked against codex debug models before any work, replacing
the not-supported-yet refusal. The fake gains a codex personality.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name the Claude stream ClaudeProgress, settle a Codex Model and Effort
into a ModelAndEffort, give the token totals their own type, and cover a
Merge run, a bare non-zero exit and the prompts' sigil on Codex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setup's Harness question (#393) refuses Codex as not supported yet
through Harness::unavailable, and its Model check uses test_call. A
Run's check now runs Codex sessions, so it checks only that the CLI is
on PATH, then makes Claude's test call or settles Codex's names from its
catalog. Setup still refuses Codex until #396 turns it on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 233a95d into issue-136 Oct 5, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-394 branch October 5, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant