Skip to content

Sandbox: git add/commit fail in linked worktrees (git-admin dir outside workdir) #30

Description

@NoRiceToday

Problem

omac sandbox run grants the workdir read+write. For a plain clone this covers .git (it lives inside the workdir), so git operations work. For a linked worktree, git splits repo state across two paths:

  • workdir: …/<repo>/.worktrees/<name>/ ← granted
  • git-admin dir: …/<repo>/.git/worktrees/<name>/ ← NOT granted (sibling, outside the workdir)

When the agent runs git add / git commit, git touches the admin dir (index, HEAD, ORIG_HEAD, logs, objects, refs) and gets EPERM from the sandbox (Seatbelt on macOS, Landlock on Linux). The agent can't commit from any linked worktree.

This affects every harness (opencode, claude, codex) and any host app that materializes agent workspaces as linked worktrees under <repo>/.worktrees/.

Root cause

The sandbox's "workdir = self-contained unit" invariant holds for plain clones but not for linked worktrees: a linked worktree's .git is a file pointing at git rev-parse --git-common-dir, which sits outside the workdir.

Suggested fix

In the workdir-grant codepath of omac sandbox run, after resolving the workdir, detect a linked worktree (.git is a file, not a dir). If so, resolve git rev-parse --git-common-dir and auto-grant the commit-relevant subdirs of the common dir:

--allow <common>/objects              # shared object store
--allow <common>/refs                 # shared refs
--allow <common>/logs                 # reflogs
--allow <common>/worktrees/<name>     # per-worktree index/HEAD/ORIG_HEAD/logs
--read  <common>/config              # read-only: git reads, agent can't mutate
--read  <common>/info
--deny  <common>/hooks/*              # closes the host-side hook persistence vector

Why this scope

  • objects/, refs/, logs/, worktrees/<name>/ are what git add/commit actually write. Granting them restores parity with a plain clone (which the sandbox already permits by virtue of .git being inside the workdir).
  • hooks/* is denied because a writable hooks dir lets a sandboxed agent plant pre-commit/core.hooksPath that runs un-sandboxed, as the host user on the next host-side commit — a clean persistence/exfil primitive that escapes the sandbox boundary entirely.
  • config is read-only to block core.hooksPath / credential.helper / url.insteadOf mutation by the sandboxed agent.

Cross-worktree note

objects/ and refs/ are shared across all linked worktrees of the same repo, so an agent in worktree A can rewrite refs/objects visible to sibling worktree B. This is inherent to "the agent can commit" — refs/objects are global by design. The scoped grant doesn't widen this beyond the plain-clone baseline; it only restores parity.

Alternatives considered

  • Per-launch --allow via the harness wrapper (e.g. oco): works as a workaround but must be re-implemented in every harness wrapper and leaks the omac-internal worktree geometry into every caller.

Expected behavior

After the fix, git add / git commit succeed inside any linked worktree under omac sandbox run, for any harness, with the same security profile as a plain clone.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions