Skip to content

codex-companion.mjs hardcodes workspace-write sandbox for write tasks — git push fails with DNS error, work stuck in local worktree #304

Open

Description

@ogoldberg

Summary

When the plugin is invoked for a write-mode task (e.g. via /codex:rescue or any flow that ends in runAppServerTurn(... { write: true })), codex-companion.mjs hardcodes the sandbox to "workspace-write" at line 460:

sandbox: request.write ? "workspace-write" : "read-only",

workspace-write permits filesystem writes inside the workspace but blocks network egress. As a result, any task whose deliverable involves git push (e.g. mechanical sweep PRs, autonomous code-fix PRs) fails inside the Codex shell with:

git push origin <branch>
fatal: unable to access 'https://github.com/<org>/<repo>.git/': Could not resolve host: github.com

The plugin then returns the task as "completed" — the commits exist in a local worktree (typically .claude/worktrees/<branch> of the workspace repo) but they're never pushed and no PR is opened. Recovering the work requires the operator to:

  1. Read the per-job log under ~/.claude/plugins/data/codex-openai-codex/state/<workspace-hash>/jobs/task-*.log
  2. Discover the worktree path from the log
  3. Manually push the branch from a shell with working network
  4. Manually open the PR

Reproduction

  1. Install the plugin (1.0.2 confirmed; recent versions likely affected).
  2. Invoke a write-mode task that ends with a push, e.g. via the rescue agent in Claude Code:

    "Update the gitignore in this repo to add <some-path>. Open a PR titled chore: ...."

  3. Codex completes the local work (branch + commit), then attempts git push.
  4. Push fails with the DNS error above; plugin returns success-shaped output.

Why this matters

Write-mode tasks are the highest-leverage use of the plugin — they're the "fire-and-forget delegation" path that makes the plugin valuable vs the bare CLI. The current behavior turns every push-required task into an archaeology exercise, which removes most of the plugin's UX win over codex exec directly.

Proposed fix

Make the sandbox mode configurable, with safer defaults than danger-full-access but with git push capability. Two options:

Option 1 (minimal): pass through caller-provided sandbox

Allow callers of runWriteTask (or whatever the entry is) to override the sandbox via the request object:

sandbox: request.sandbox ?? (request.write ? "workspace-write" : "read-only"),

This lets internal callers (e.g. the rescue agent) opt into danger-full-access for tasks that need push, while leaving the conservative default for everything else.

Option 2 (better): respect ~/.codex/config.toml trust_level

The Codex CLI's config already defines per-project trust_level (e.g. [projects."/Users/og/src/exit"] trust_level = "trusted"). When the workspace root matches a project with trust_level = "trusted", default the sandbox to danger-full-access (or whatever the closest equivalent is that permits network for git push). This makes the trust model explicit and centralized, rather than hardcoded in plugin logic.

Option 3 (fallback): post-Codex push step

If Option 1/2 are too invasive, add a post-completion step in the plugin that runs git push from outside the sandbox (i.e., in the plugin's own shell, which has full network access). This separates "Codex generates commits inside sandbox" from "plugin pushes to remote outside sandbox" — cleaner separation of concerns.

Workaround in use

For now, we're routing push-required tasks through codex exec --sandbox danger-full-access "<prompt>" directly via Bash, bypassing the plugin entirely. This loses the plugin's auto-PR scaffolding but recovers reliable push behavior.

Environment

  • Plugin version: 1.0.2 (latest as of 2026-05-07)
  • Codex CLI: 0.129.0
  • Platform: macOS (Apple Silicon)
  • Workspace: trusted in ~/.codex/config.toml

Activity

  1. creatornader commented on Jul 14, 2026

    @creatornader

    Corroborating evidence for the hardcoded sandbox override, from a different failure surface: under the forced workspace-write seatbelt on macOS, vitest's default forks pool cannot create its child-process IPC channel (socketpair), so delegated test runs hang indefinitely rather than failing. Workaround that unblocked us: pass --pool=threads to vitest for any run under the companion. Also confirming the quoted expression is still present in plugin v1.0.6 (the line number has drifted from the one cited here; it sits near line 491 in that version), and that a user-level sandbox_mode = "danger-full-access" in ~/.codex/config.toml is still ignored by the companion path while honored by direct codex exec. If honoring the user config is unacceptable as a default for safety reasons, an explicit per-invocation opt-in flag would resolve both the write-scope and the test-IPC failure classes.

  2. ZalmyAI commented on Aug 3, 2026

    @ZalmyAI

    Confirming this is still live on plugin v1.0.5, reproduced independently today across 4 separate /codex:rescue-dispatched write-mode jobs in the same session (all hit Could not resolve host: github.com inside the Codex shell after doing real, correctly-tested work in an isolated worktree — none could push or open a PR themselves). Root cause matches #482's more general framing (sandbox is always explicitly set on thread/start/thread/resume, so ~/.codex/config.toml's sandbox_mode = "danger-full-access" never applies to plugin-dispatched jobs) rather than being specific to one code path.

    Workaround we're using in the meantime: treat every write-mode dispatch that ends in a PR as "build in the sandbox, then have the orchestrating Claude Code session (which has real network access) push the worktree branch and open/merge the PR" rather than expecting the job to self-publish. Worth noting for anyone hitting this: the job's own codex-companion.mjs result <job-id> output still reports the worktree path and commit SHAs even when publication fails, so the work isn't lost — just needs a manual handoff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions