dmesg --follow
[ 69798180.000 ] posts.x: ↻ @atlantajug: Maintainers are drowning in AI-generated slop. How do you make sure your PR isn't part of the problem? 🤖 @CoreyGallon shar…  |   [ 69798120.000 ] posts.x: ↻ @devnexus: Maintainers are drowning in AI-generated slop. How do you make sure your PR isn't part of the problem? 🤖 At #Devnexus2027, @…  |   [ 69626580.000 ] posts.x: @sentinelwayne Not following … ticketing systems are typically async, nay?  |   [ 69483540.000 ] posts.x: @sentinelwayne The chat dropped a couple of times, due to timeout. It’s just the same as if I was distracted on another screen and didn’t respond in…  |   [ 69435300.000 ] posts.x: Happy V for Vendetta Day!  |   [ 69435120.000 ] posts.x: Happy V for Vendetta Day!  |   [ 69346140.000 ] posts.x: This is a *chef’s kiss* salutation from the app in the morning. Nicely done, Claude team! 👌  |   [ 69303240.000 ] posts.x: @markfenner Nyope! It all went off perfectly, as though I had engaged in the chat myself.  |   [ 69303180.000 ] posts.x: @noelkimx Happy to share more — what are you after?  |   [ 69285720.000 ] posts.x: @noelkimx It doesn't have the password -- I logged in myself, then handed the browser session over. It's dodgy practice to ask for a customer…  |   [ 69280380.000 ] posts.x: @not_sacke Yeah, it's handled things so nicely! I had to take photos of the physical device at the request of the customer support rep, so it pinged…  |   [ 69279960.000 ] posts.x: One of my agents is working a device support issue with a live customer service agent on my behalf. I've got it sending me notifications when it…  |   [ 69280080.000 ] posts.x: Here's the GitHub repo for chrome-agent, so your agent can use the web with, and for, you:  |   [ 69268140.000 ] posts.x: This is particularly useful for agents that you want to update with new information or additional tasks during their run.  |  
corey@gallon.me:~/til$

Letting Codex Agents Commit: Making .git Writable in the workspace-write Sandbox

FIGURE 1 ⋅ Letting Codex Agents Commit: Making .git Writable in the workspace-write Sandbox

Codex CLI's workspace-write sandbox lets the agent edit anything in your repo -- except commit. Every git commit dies with:

fatal: Unable to create '/path/to/repo/.git/index.lock': Read-only file system

This is deliberate. On Linux, Codex's bubblewrap sandbox mounts your writable roots read-write, then re-applies .git (plus .codex and .agents) as read-only on top. The security rationale is sound: a writable .git/hooks would let the agent plant a hook that executes outside the sandbox the next time you run git yourself.

But if you're already running with --ask-for-approval never -- YOLO mode -- you've made that trust decision, and an agent that can't commit its own work is a constant irritation. There's no official toggle: no allow_git_writes option exists anywhere in the codebase, and the open issues asking for one (#15505, #12280, #14338) have no maintainer response.

The trick: name the .git path itself

The obvious fixes don't work. Adding the repo root to sandbox_workspace_write.writable_roots fails because the .git protection is applied per writable root -- a writable parent still gets a read-only .git carved out of it (#15505). Passing extra directories with --add-dir fails because the read-only mount is applied after the command-line write dirs, so it wins (#14338).

But reading the sandbox policy source (codex-rs/protocol/src/permissions.rs), the carveout is added by a function named append_default_read_only_path_if_no_explicit_rule. The name says it all: if an explicit rule already covers the exact .git path, the read-only protection is skipped.

So point writable_roots at the .git directory itself:

codex exec --sandbox workspace-write \
  -c 'sandbox_workspace_write.writable_roots=["/path/to/repo/.git"]' \
  "commit your work"

Verified on codex-cli 0.144.6: without the flag, the commit fails with the read-only error above; with it, the commit lands -- confirmed in git log, not just the agent's self-report.

Wiring it into a shell function

The writable path is per-repo, so compute it at invocation time. My yolo-codex wrapper now does:

# Make .git writable inside the workspace-write sandbox so the agent can
# commit. Codex mounts .git read-only unless an explicit writable_roots
# entry names the .git path itself. Includes the common gitdir so linked
# worktrees can commit too.
local -a git_writable=()
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  local git_dir git_common_dir
  git_dir=$(git rev-parse --absolute-git-dir)
  git_common_dir=$(git rev-parse --path-format=absolute --git-common-dir)
  if [[ "$git_dir" == "$git_common_dir" ]]; then
    git_writable=(-c "sandbox_workspace_write.writable_roots=[\"$git_dir\"]")
  else
    git_writable=(-c "sandbox_workspace_write.writable_roots=[\"$git_dir\",\"$git_common_dir\"]")
  fi
fi

codex --ask-for-approval never --sandbox workspace-write "${git_writable[@]}" "$@"

Two details worth the extra lines:

  • Linked worktrees. In a git worktree checkout, --absolute-git-dir points at the per-worktree gitdir, but commits also write objects and refs into the common gitdir back in the main repo. Both paths need to be writable. Tested: a commit from inside a linked worktree works with both entries, and a plain repo needs only the one.
  • The -c flag sets only writable_roots. Anything else you have under [sandbox_workspace_write] in ~/.codex/config.toml (like network_access = true) is untouched.

Outside a git repo, the array stays empty and the function behaves exactly as before.

Caveats

  • This is undocumented behavior. The explicit-rule exemption is an implementation detail of the sandbox policy, not a documented feature. A future Codex release could re-protect .git unconditionally -- if agent commits start failing after an upgrade, this is the first thing to re-check.
  • You're removing a real protection, not working around a bug. A writable .git means a writable .git/hooks, and hooks run unsandboxed when you next invoke git. If you're not already running approval-free, think twice.
  • The blunt alternative is --sandbox danger-full-access, which trades the entire filesystem sandbox to fix one directory. Carving out .git keeps everything else protected.

Tested on codex-cli 0.144.6, Linux (bubblewrap sandbox).

corey@gallon.me:~$ tail -f /writing Attach to the stream. An email when I have something worth sending. Replies encouraged!
corey@gallon.me:~$ ls -lt /til ↑2026-10-13 I Believe Everything My Agents Tell Me
▸2026-07-22 Letting Codex Agents Commit: Making .git Writable in the workspace-write Sandbox ⋅ you are here
↓2026-07-16 The Dark Arts of Web Automation