Part of #340
Context
In agents.json the ops role has oo gh api* as a blanket allow — it matches any endpoint, including repos/OWNER/REPO/pulls/N/merge. oo git push* likewise covers --force and --force-with-lease. Meanwhile gh pr close and gh pr merge are absent and fall through to the catch-all *: ask. The safe single-purpose commands are gated; the arbitrary-endpoint API is not.
This is a latent incentive, not an active one. Under trust mode (the default for interactive host) permission-guard.ts short-circuits before resolveToolPermission loads agents.json, so no verdict is consulted and the agent cannot see which patterns are allow vs ask. Fixing this would NOT have prevented the incident that motivated the epic. It is worth doing because strict and headless modes exist, and because the config should not encode the inverse of the intended posture.
Acceptance criteria
Technical context
matchBashSubcommand in extension/src/bash-command-parser.ts supports only three pattern forms: pattern * (word-boundary prefix), pattern* (loose prefix), pattern (exact). There are no mid-string globs and no path-aware matching, so narrowing oo gh api* by endpoint is NOT possible. Removing the blanket allow is the only implementable move. Do not attempt a pattern like oo gh api repos/*/pulls/*/merge* — it will not match.
Out of scope
- Endpoint-aware pattern matching in
bash-command-parser.ts — open question on the parent epic
- Any change to trust mode's default
Part of #340
Context
In
agents.jsonthe ops role hasoo gh api*as a blanket allow — it matches any endpoint, includingrepos/OWNER/REPO/pulls/N/merge.oo git push*likewise covers--forceand--force-with-lease. Meanwhilegh pr closeandgh pr mergeare absent and fall through to the catch-all*: ask. The safe single-purpose commands are gated; the arbitrary-endpoint API is not.This is a latent incentive, not an active one. Under trust mode (the default for interactive host)
permission-guard.tsshort-circuits beforeresolveToolPermissionloadsagents.json, so no verdict is consulted and the agent cannot see which patterns are allow vs ask. Fixing this would NOT have prevented the incident that motivated the epic. It is worth doing because strict and headless modes exist, and because the config should not encode the inverse of the intended posture.Acceptance criteria
oo gh api*allow is removed, so raw arbitrary-endpoint API falls through to*: askoo gh pr close*andoo gh pr merge*ops.permission.bash["*"]remainsaskgh apiconsumer — issue-body reads, PR status queries, CI run queries — and states for each whether it keeps an explicit allow or accepts the strict-mode promptbun run build+cd extension && bunx tsc --noEmit && bun run check+ full offline smoke tests passTechnical context
matchBashSubcommandinextension/src/bash-command-parser.tssupports only three pattern forms:pattern *(word-boundary prefix),pattern*(loose prefix),pattern(exact). There are no mid-string globs and no path-aware matching, so narrowingoo gh api*by endpoint is NOT possible. Removing the blanket allow is the only implementable move. Do not attempt a pattern likeoo gh api repos/*/pulls/*/merge*— it will not match.Out of scope
bash-command-parser.ts— open question on the parent epic