Worktree-Relative Deny Rules in OpenCode: Stop Child Agents From Escaping Project Boundaries

Parallel agent workflows often use Git worktrees: one checkout per task, one branch per agent. That improves isolation, but it also creates a subtle permissions problem. A child agent running in one worktree may still try to read or edit sibling worktrees, parent directories, or shared files outside its assigned project scope.

OpenCode's permission rules and external_directory guard are the controls to tighten.

The short version

  • Start OpenCode from the worktree that defines the agent's boundary.
  • Deny or ask on external_directory; do not casually allow parent folders.
  • Use granular read, edit, grep, glob, and bash rules for sensitive paths.
  • Remember that OpenCode permission patterns use wildcard matching and last match wins.
  • Treat sibling worktrees as external directories unless intentionally shared.

Why worktrees change the threat model

Git worktrees are separate working directories attached to one Git repository. They are excellent for parallel agent work because each task can have its own branch and filesystem state without a full clone.

But the filesystem still matters. A common layout looks like this:

~/src/app/                  # main checkout
~/src/app.worktrees/task-a/  # agent A
~/src/app.worktrees/task-b/  # agent B
~/src/app.worktrees/task-c/  # agent C

If an agent in task-a can freely access ../task-b, it can inspect or modify another agent's work. If it can access ~/src/app, it may bypass the task branch entirely. If it can read ~, it may reach credentials, local notes, or unrelated repositories.

The boundary should be the worktree root, not the parent folder that happens to contain all worktrees.

OpenCode's relevant permissions

OpenCode permissions resolve actions to allow, ask, or deny. Permissions can be configured globally or per tool, and many tools support object rules that match inputs. The docs also note two important details:

  • rules are evaluated by pattern match, with the last matching rule winning;
  • external_directory is triggered when a tool touches paths outside the working directory where OpenCode was started.

That means the simplest control is operational: start OpenCode inside the assigned worktree.

cd ~/src/app.worktrees/task-a
opencode

Now paths outside task-a should hit external_directory.

A conservative project permission profile

For child agents, prefer a default-deny posture for writes and external access:

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "read": "allow",
    "grep": "allow",
    "glob": "allow",
    "edit": {
      "*": "deny",
      "src/**": "allow",
      "tests/**": "allow",
      "package.json": "ask"
    },
    "bash": {
      "*": "ask",
      "git status*": "allow",
      "git diff*": "allow",
      "npm test*": "allow",
      "rm *": "deny",
      "git push*": "deny"
    },
    "external_directory": "deny"
  }
}

This is intentionally strict. Adapt it to your repo, but keep external_directory narrow. If the agent needs a shared SDK or generated artifact outside the worktree, allow only that path and only the necessary tools.

Deny sibling worktrees explicitly

Depending on your layout and OpenCode version, the external_directory guard may already catch sibling paths. Still, explicit rules make intent clear and protect against later config changes.

{
  "permission": {
    "external_directory": {
      "*": "deny",
      "~/src/app.worktrees/shared-readonly/**": "ask"
    },
    "read": {
      "*": "allow",
      "../**": "deny",
      "../../**": "deny"
    },
    "edit": {
      "*": "deny",
      "src/**": "allow",
      "tests/**": "allow",
      "../**": "deny"
    }
  }
}

The exact patterns should be tested in your environment. OpenCode supports simple wildcards (* and ?) rather than a full policy language, so verify with harmless read/edit attempts before handing the profile to agents.

Bash needs special attention

Path-aware tool permissions are not enough if bash is broadly allowed. A shell command can read, copy, remove, or exfiltrate files.

Avoid this:

{ "permission": { "bash": "allow" } }

Prefer command prefixes:

{
  "permission": {
    "bash": {
      "*": "ask",
      "git status*": "allow",
      "git diff*": "allow",
      "npm test*": "allow",
      "cat ../*": "deny",
      "cp ../*": "deny",
      "rm *": "deny",
      "curl *": "ask"
    }
  }
}

This is not a replacement for OS sandboxing. It is a tool-level guardrail. For high-risk agents, combine OpenCode permissions with containers, restricted users, and network egress controls.

Agent-specific overrides

OpenCode supports per-agent permission overrides. Use that to give child agents narrower rules than the primary session.

For example:

  • planner: read/search allowed, no edits;
  • implementer: edit only under assigned package;
  • tester: run tests, no writes except coverage output;
  • release agent: no direct git push, only PR creation through a reviewed workflow.

This mirrors human least privilege: not every worker needs the same access.

Gotchas

Last match wins. Put catch-all rules first and more specific rules later.

Home expansion helps patterns such as ~/projects/*, but it does not make an external path part of the workspace. You still need an external_directory decision.

Generated code may live outside the worktree. Either move generation into the worktree or allow a very specific path.

Permissions are not a sandbox. A determined or compromised tool path may need OS-level isolation.

We're building Grass with this boundary in mind: each agent should work inside an isolated sandbox, and the result should come back for review instead of bleeding across local worktrees or sibling tasks. If you want that workflow without hand-rolling every boundary yourself, you can try Grass at https://codeongrass.com.

Conclusion

Parallel worktrees are great for agent throughput, but each worktree must become a real permission boundary. Start OpenCode from the worktree root, deny external directories by default, restrict bash, and use per-agent overrides. Worktree-relative rules keep child agents from turning parallelism into cross-branch chaos.

Sources

  • OpenCode permissions documentation
  • Git worktree workflow documentation and common parallel-agent worktree patterns