feat: add Codex agent adapter (agent_provider: claude | codex) - #21
Merged
Conversation
Review lenses previously diffed each changed file individually via `git diff 'origin/<base>...HEAD' -- '<path>'`. In a `pull/N/merge` checkout (shallow, no base tracking ref, persist-credentials: false) that command fails three ways: `origin/<base>` does not exist, the merge commit's parents are absent as objects, and git cannot authenticate a fetch. Each lens then burned turns flailing through failing git commands at the start of the run. Provision the base branch in-process before the lenses run: prepareReviewBase injects the App installation token into the remote URL and fetches the base tip into refs/remotes/origin/<base> at depth 1. The lens prompts now run a single two-dot `git diff 'origin/<base>' HEAD` for the whole PR, replacing the per-file three-dot diffs. Two-dot needs no shared ancestry, so a depth-1 fetch suffices on the shallow merge commit, and one batched command removes the per-file round trips. No consumer workflow change is required: the action fetches the base itself using the App token, so persist-credentials: false and the existing App credentials are sufficient.
Contributor
There was a problem hiding this comment.
Shopfloor agent review: clean across 4/4 reviewers.
- No compliance violations against CLAUDE.md: the new files match the documented layout (
src/agents/codex.ts,src/agents/mcp-http-bridge.ts,src/config/codex-options.ts,src/setup/ensure-codex-cli.ts), CLAUDE.md and the Key Files table are updated alongside the code, the design doc lives underdocs/superpowers/specs/<date>-...-design.md, tests live undertest/, the example workflow is inexamples/, new action inputs are declared inaction.yml, anddist/index.cjsis rebuilt and committed. - No traceable correctness bugs found. The Codex adapter, MCP HTTP bridge, options builder, review-base provisioning, and tool-shape retype all match the spec; abort/timeout, error-kind mapping, auth branching, and bridge bearer-token gating are wired consistently with the existing Claude adapter patterns.
- No exploitable security issues found: the new MCP bridge is loopback-only with a 256-bit bearer token, the Codex auth.json is written 0600 inside a 0700 mkdtemp dir, all new git invocations use array args with non-attacker-controlled refs, and no user input flows into shell strings,
eval, file paths, SQL, or outbound URLs. - No code smells worth flagging at the smell-reviewer confidence threshold; the four-way lens prompt duplication is pre-existing on release/v2 and not introduced by this PR.
- Defer the missing-credential check from buildCodexOptions (construction) to
CodexAgentAdapter.runStage (first use). entry.ts builds the adapter
unconditionally, including for mode=resolve router jobs that never run an
agent, so a construction-time throw deadlocked split-runner workflows that
scope Codex creds to the execute job. Now symmetric with the Claude env
builder, which never throws.
- Distinguish our own timeout (agent_timeout) from a caller-initiated
abortController abort (agent_execution) via a timedOut flag, instead of
labelling every aborted signal a timeout.
- Latch the "caps are ignored" warning to once per adapter instance; budgetUsd
always carries a numeric default, so the unconditional warn spammed every
stage.
- Share the subprocess env allowlist: export collectPassthroughEnv from
agent-env.ts and reuse it in codex-options.ts instead of a second copy of
PASSTHROUGH_KEYS that could silently diverge.
- Drop the redundant chmodSync; writeFileSync({mode:0o600}) on a freshly
mkdtemp'd path sets 0600 atomically with no world-readable window.
httpServer.close()'s callback only fires once every connection drains, and it does not terminate idle keep-alive sockets. The Codex MCP client holds a persistent keep-alive socket for the stateful session, so close() (awaited in the adapter's finally) would hang indefinitely and wedge the stage. Call httpServer.closeAllConnections() before close().
boolish() treated any value other than the literal "false" as true, so a typo on the security-relevant codex_network_access toggle (e.g. "no", "0", "off") silently failed open to network-enabled. Replace with a z.enum(["true","false"]) parser that rejects unrecognized values, matching the other codex enum inputs.
Contributor
There was a problem hiding this comment.
Shopfloor agent review: clean across 4/4 reviewers.
- No compliance violations: spec path, pnpm command references, committed dist bundle, and Key Files updates all conform to CLAUDE.md (the only convention file in this repo).
- Traced Codex adapter error-mapping branches, MCP HTTP bridge handshake/cleanup, prepareReviewBase fetch refspec + warn-and-continue path, and the prompt template's two-dot diff change. No correctness defects traceable at ≥75 confidence.
- No exploitable security issues found: the new MCP bridge is loopback-only with a 256-bit random bearer; the Codex auth.json is written via mkdtempSync + 0o600; subprocess env is allowlisted via collectPassthroughEnv so secrets don't leak; git argv form prevents injection through baseRef; and strictBool defends the security-relevant toggles from silent coercion.
- No maintainability smells worth flagging — the new Codex adapter, MCP bridge, and shared helpers mirror existing Claude-side patterns, and the
collectPassthroughEnvextraction actually removes a duplication risk between the two adapter env builders.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a second
AgentAdapterso Shopfloor stages can run through OpenAI's Codex (@openai/codex-sdk) instead of Claude. Selection is global via a newagent_provider: claude | codexinput (defaults toclaude, no behavior change for existing consumers). Everything downstream of the adapter interface is untouched — orchestrator,StageContext, stage runners, and the state machine depend only on theAgentAdaptercontract and theAgentErrortaxonomy.Implements
docs/superpowers/specs/2026-06-11-codex-adapter-design.md.CodexAgentAdapter(src/agents/codex.ts) — wraps@openai/codex-sdkwith the same structured-output andAgentErrorsemantics as the Claude adapter. System prompt is prepended to the user prompt (Codex has no separate system-prompt field).budgetUsd/maxTurnsare accepted and dropped with a warning (Codex surfaces neither).src/agents/mcp-http-bridge.ts) — exposes the implement stage's singleupdate_progresstool to the Codex CLI subprocess via a Streamable HTTP MCP server bound to loopback on an ephemeral port, guarded by a random bearer token, reusing the live in-memory Octokit. Runs in stateful mode (session-id handshake) because the SDK transport refuses more than one request per stateless instance.SdkTool.inputSchemaretyped fromunknowntoz.ZodRawShape;update-progress.tsno longer depends on a provider SDK'stool()helper so it stays neutral.src/config/codex-options.ts) —openai_api_key(recommended) or a ChatGPTcodex_auth_jsonseeded into a run-scoped tempCODEX_HOME/auth.json(0600) each run.src/setup/ensure-codex-cli.ts) — locates/installs the nativecodexbinary, mirroringensure-claude-cli.ts.examples/shopfloor-codex.yml), README/CLAUDE.md docs.Test Plan
pnpm typecheckcleanpnpm test— 276/276 pass (Codex adapter option-mapping + error-kind mapping + abort→timeout + budget/turns warning; MCP bridge tool call over MCP + bearer rejection;buildCodexOptionsauth branches incl. 0600 auth.json + throw)pnpm buildreproducible (dist committed, no diff on rebuild); bundle loads without theimport.meta.urlfailurecodexbinary resolves from the committeddist/index.cjs(same class as the Claude CLI), and the Codex MCP client echoes theMcp-Session-Idheader against the in-process bridge