My Subagents Kept Asking Permission for Everything: The Config Ordering Trap
Every time my release-manager subagent tried to run grep or git log, OpenCode prompted me for approval. I had explicit "grep *": allow rules in the config. They weren't working.
This tripped me up for way longer than it should have. (I'm running OpenCode v1.4 — permission behavior may differ in other versions.)
My Config
The release-manager agent handles version bumps, changelogs, and git tagging for my Projex project. Its config in .opencode/agents/release-manager.md had bash permissions like this:
bash:
"grep *": allow
"rg *": allow
"cat *": allow
"git log -- *": allow
"git diff -- *": allow
"git status": allow
"*": ask
Looks reasonable. Specific commands are allowed, everything else asks. Except every single command was prompting. grep, git log, cat — all asking for permission.
The Problem
I re-read the config about five times. The rules were right there — "grep *": allow — clear as day. I tried shuffling the order around. Tried different wildcard syntax. Nothing worked.
Eventually I went back to the OpenCode permissions docs and actually looked at the examples. Every single one puts the catch-all "*": "ask" at the top. Mine was at the bottom.
Turns out OpenCode permission rules work on a "last matching rule wins" principle.
The "*" wildcard matches everything. Including grep. Including git log. So when OpenCode evaluates git log --oneline -20 against my config, both "git log -- *" and "*" match. Since "*" is listed last, it wins. Result: ask.
Every. Single. Time.
The fix was embarrassingly simple. Move the catch-all to the top:
bash:
"*": ask
"grep *": allow
"rg *": allow
"cat *": allow
"git log -- *": allow
"git diff -- *": allow
"git status": allow
Now "*" matches first, but then "grep *" matches after and wins because it's the last matching rule. Specific overrides general. The way it should work.
I'd put the catch-all at the bottom out of habit — like a default case in a switch statement. I just... didn't read carefully enough.
The Second Problem
After fixing the ordering, grep worked fine. But git log --oneline -20 still prompted.
I tested a few more variations to narrow it down. git log -- somefile (with the path separator) matched. git log --oneline didn't. The difference clicked pretty fast after that.
The pattern "git log -- *" only matches commands that literally have -- in them. Like git log -- somefile. It doesn't match git log --oneline -20 because --oneline is a flag, not the -- path separator.
I'd been too specific. Changed to:
"git log *": allow
"git log": allow
"git diff *": allow
"git diff": allow
"git status *": allow
"git status": allow
Both forms — with and without arguments. The * wildcard matches zero or more of any character. So "git log *" covers git log --oneline -20. But the space before * is literal. The pattern requires "git log" followed by a space, then anything. A bare git log with no trailing space won't match it. That's why "git log" (no wildcard) is also needed.
The Third Problem
Even after both fixes, it still prompted. I'd been eyeing the tools field suspiciously since the start. The config had both the deprecated tools block and the newer permission block.
tools:
read: true
write: true
edit: true
bash: true
permission:
bash:
"*": ask
"grep *": allow
The docs say tools.bash: true is equivalent to {"*": "allow"}. Having both tools and permission for the same thing seemed like it could cause conflicts — one saying "allow everything" and the other saying "ask for everything except these." I don't know exactly how OpenCode resolves this internally, but removing the tools block entirely fixed the remaining issues.
The Working Config
The final config for the release-manager agent looks like this:
---
description: Create releases following Projex's version bump, changelog, and git tagging workflow
mode: subagent
temperature: 0.1
permission:
edit:
"*": ask
"packages/core/package.json": allow
"CHANGELOG.md": allow
"README.md": allow
"packages/core/README.md": allow
"packages/docs/**/*": allow
bash:
"*": ask
"pnpm --filter * build": allow
"pnpm --filter * lint": allow
"pnpm --filter * typecheck": allow
"pnpm --filter * test": allow
"head *": allow
"ls *": allow
"ls -la *": allow
"find *": allow
"grep *": allow
"rg *": allow
"cat *": allow
"tail *": allow
"wc *": allow
"wc -l *": allow
"sort *": allow
"uniq *": allow
"git log *": allow
"git log": allow
"git diff *": allow
"git diff": allow
"git status *": allow
"git status": allow
"git tag *": allow
webfetch: deny
color: success
---
Three separate issues stacked on top of each other. Rule ordering, overly narrow patterns, and deprecated config conflicting with new config.
TL;DR
Three things I learned about OpenCode subagent permissions:
- Catch-all goes first.
"*": askneeds to be at the top of the permission block. Last matching rule wins. - Patterns need to match actual command syntax.
"git log -- *"doesn't matchgit log --oneline. I had to use"git log *"instead. - Don't mix
toolsandpermission. Removing the deprecatedtoolsfield fixed the remaining issues for me.
I fixed the same issues in my documentation-manager agent too. Both agents now run without prompting on every command. Which is how it should have been from the start.