Summary
Agent definition files (, , etc.) declare "gh pr merge *": "deny" to prevent subagents from merging PRs. This is correct: only the ship agent and the main session should merge. But these deny rules leak into the MAIN agent's effective permission set, overriding the profile-level "gh pr merge *": "allow" in opencode.json.
Incident (2026-08-04, Grift repo)
The main agent successfully merged PRs (anomalyco#1763-anomalyco#1775) on 2026-08-03 using gh pr merge. On 2026-08-04, after the entrypoint was re-pinned to the test-reliability-guards worktree (PR #291), gh pr merge started being blocked by a second permission layer that includes "gh pr merge *": "deny" sourced from agent definition files.
The profile's opencode.json has:
"gh pr merge *": "allow" ← main agent permission
But the agent files (e.g. agents/implement.md) have:
"gh pr merge *": "deny" ← subagent-only restriction
The deny is being applied to the main agent, making the effective permission deny.
Root Cause
OpenCode merges ALL agent-level permission rules into a second layer that is applied on top of the main agent's permissions. Deny rules in any agent definition effectively restrict the main agent, because the intersection (most restrictive) wins.
This means: adding a new agent with "X": "deny" silently restricts the main agent's ability to do X, even if the profile explicitly allows it.
Expected Behavior
Agent-level permission rules should apply only when that specific agent is spawned as a subagent. The main agent should use only:
- The profile-level
opencode.json permissions
- The managed
opencode.json permissions
Agent definition permissions should be a subset applied at spawn time, not a global intersection.
Actual Behavior
All agent-level deny rules from all defined agents are accumulated into a second permission layer, applied to every session including the main agent. This is functionally equivalent to "any agent can veto any permission for everyone".
Impact
- The main agent cannot perform
gh pr merge despite the profile explicitly allowing it
- Workaround: restart the session or have a human merge from the terminal
- No visible warning or override mechanism (the deny is silent and unexplained)
- This affected 4 ready-to-merge PRs that were blocked for hours
Reproduction
- Pin entrypoint to any worktree whose profile defines agents with
"gh pr merge *": "deny"
- From the main agent, attempt
gh pr merge <PR> --merge
- Observe: blocked by the deny rule, despite
opencode.json having "allow"
- The error shows two permission layers: first has
allow, second has deny
Proposed Fix
- Agent permissions should be isolated: when spawning a subagent, merge the agent's permissions ON TOP of the profile permissions for that subagent's session only. Do not accumulate them globally.
- Main agent should not inherit agent-level deny rules: the main session's permission source is the profile + managed
opencode.json, not agent definitions.
- Warning when deny overrides allow: if a deny rule overrides an explicit allow, emit a warning so the user knows the source.
Refs
Summary
Agent definition files (, , etc.) declare
"gh pr merge *": "deny"to prevent subagents from merging PRs. This is correct: only theshipagent and the main session should merge. But these deny rules leak into the MAIN agent's effective permission set, overriding the profile-level"gh pr merge *": "allow"inopencode.json.Incident (2026-08-04, Grift repo)
The main agent successfully merged PRs (anomalyco#1763-anomalyco#1775) on 2026-08-03 using
gh pr merge. On 2026-08-04, after the entrypoint was re-pinned to thetest-reliability-guardsworktree (PR #291),gh pr mergestarted being blocked by a second permission layer that includes"gh pr merge *": "deny"sourced from agent definition files.The profile's
opencode.jsonhas:But the agent files (e.g.
agents/implement.md) have:The deny is being applied to the main agent, making the effective permission
deny.Root Cause
OpenCode merges ALL agent-level permission rules into a second layer that is applied on top of the main agent's permissions. Deny rules in any agent definition effectively restrict the main agent, because the intersection (most restrictive) wins.
This means: adding a new agent with
"X": "deny"silently restricts the main agent's ability to do X, even if the profile explicitly allows it.Expected Behavior
Agent-level permission rules should apply only when that specific agent is spawned as a subagent. The main agent should use only:
opencode.jsonpermissionsopencode.jsonpermissionsAgent definition permissions should be a subset applied at spawn time, not a global intersection.
Actual Behavior
All agent-level deny rules from all defined agents are accumulated into a second permission layer, applied to every session including the main agent. This is functionally equivalent to "any agent can veto any permission for everyone".
Impact
gh pr mergedespite the profile explicitly allowing itReproduction
"gh pr merge *": "deny"gh pr merge <PR> --mergeopencode.jsonhaving"allow"allow, second hasdenyProposed Fix
opencode.json, not agent definitions.Refs
packages/guardrails/profile/agents/*.mdhave"gh pr merge *": "deny"opencode.jsonhas"gh pr merge *": "allow"shipagent correctly has"gh pr merge*": "allow"as an exception, but this exception also leaks