fix(guardrails): resolve team single-task deadlock and permission deny ordering#88
fix(guardrails): resolve team single-task deadlock and permission deny ordering#88
Conversation
…atibility Permission.evaluate() uses findLast semantics where the LAST matching rule wins. When deny rules appeared immediately after the wildcard, a project-level merge could append additional wildcards that override the denies. Moving all deny rules to the end of each permission section ensures they always take precedence regardless of merge order. Changes: - profile/opencode.json: bash deny rules after wildcard+allow, read deny after allow - managed/opencode.json: same reordering applied - profile/agents/implement.md: add wildcard before deny rules, add missing deny rules Fixes #31 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ves #32 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…atibility Restores permission ordering fix that was accidentally reverted during team.ts amend. Deny rules must appear AFTER wildcards for findLast to return deny for dangerous commands. Fixes #31 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
There was a problem hiding this comment.
Pull request overview
This PR updates the Guardrails profile to avoid “team tool” dead-ends during large-request enforcement and to adjust permission rule ordering so last-match precedence behaves as intended.
Changes:
- Allow single-task
teamdelegations and exempt clearly read-only investigation prompts from the “big request” parallel gate. - Improve the context-budget exceeded error to explicitly recommend delegating via
team. - Rework Guardrails profile/managed permission rules (notably bash/read) to better align with last-match (
findLast) precedence.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/guardrails/profile/plugins/team.ts | Permits 1-task team runs; adds read-only investigation exemption in big(); updates enforcement message text. |
| packages/guardrails/profile/plugins/guardrail.ts | Updates context-budget error message to explicitly recommend team delegation. |
| packages/guardrails/profile/opencode.json | Updates bash/read permission rules and ordering in the Guardrails profile config. |
| packages/guardrails/profile/agents/implement.md | Adds missing default bash permission and additional deny patterns for implement agent frontmatter. |
| packages/guardrails/managed/opencode.json | Mirrors permission rule updates for the managed Guardrails config. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "*.env.example": "allow", | ||
| "*.env": "deny", | ||
| "*.env.*": "deny", |
There was a problem hiding this comment.
read permission rules are ordered such that *.env.example will still be denied: it matches both *.env.example and the later *.env.*, and evaluate() uses findLast(), so the later deny wins. Move the *.env.example allow rule after the *.env / *.env.* deny rules (or otherwise ensure the allow is the last match).
| "*.env.example": "allow", | |
| "*.env": "deny", | |
| "*.env.*": "deny", | |
| "*.env": "deny", | |
| "*.env.*": "deny", | |
| "*.env.example": "allow", |
| "*.env.example": "allow", | ||
| "*.env": "deny", | ||
| "*.env.*": "deny", |
There was a problem hiding this comment.
read permission rules are ordered such that *.env.example will still be denied: it matches both *.env.example and the later *.env.*, and evaluate() uses findLast(), so the later deny wins. Move the *.env.example allow rule after the *.env / *.env.* deny rules (or otherwise ensure the allow is the last match).
| "*.env.example": "allow", | |
| "*.env": "deny", | |
| "*.env.*": "deny", | |
| "*.env": "deny", | |
| "*.env.*": "deny", | |
| "*.env.example": "allow", |
| "git branch*": "allow", | ||
| "git remote*": "allow", | ||
| "git stash*": "allow", | ||
| "gh *": "allow", |
There was a problem hiding this comment.
The bash allow patterns git branch*, git remote*, and git stash* also match mutating/destructive subcommands (e.g. git branch -D, git remote add/remove, git stash drop/pop) and will bypass permission prompting. Consider narrowing these patterns to read-only forms or changing them to ask to keep guardrails conservative.
| "git branch*": "allow", | |
| "git remote*": "allow", | |
| "git stash*": "allow", | |
| "gh *": "allow", | |
| "git branch*": "ask", | |
| "git remote*": "ask", | |
| "git stash*": "ask", | |
| "gh *": "ask", |
| "git branch*": "allow", | ||
| "git remote*": "allow", | ||
| "git stash*": "allow", | ||
| "gh *": "allow", |
There was a problem hiding this comment.
The bash allow patterns git branch*, git remote*, and git stash* also match mutating/destructive subcommands (e.g. git branch -D, git remote add/remove, git stash drop/pop) and will bypass permission prompting. Consider narrowing these patterns to read-only forms or changing them to ask to keep guardrails conservative.
| "gh *": "allow", | |
| "gh *": "ask", |
…31 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Issue for this PR
Closes #32
Closes #29
Addresses #31
Type of change
What does this PR do?
Fixes three guardrails profile bugs that block day-to-day workflow:
team.ts (#32, #29):
team.ts:483: Hardcoded minimum 2 tasks changed to 1 — enables single-task delegation for isolated worker patternteam.ts:168-173:big()function addedreadOnlyexemption — read verbs (investigate|review|audit|inspect|...) lacking write-intent (fix|add|edit|write|modify|...) bypass the parallel gateteam.ts:746: Hook injection text updated from "at least two" to "at least one worker task"guardrail.ts:440: Context budget error message now suggeststeamtool delegationPermission ordering (#31):
profile/opencode.json+managed/opencode.json: Deny rules (rm -rf,sudo,git push --force) placed after wildcard"*": "ask"— ensuresfindLastreturns denyprofile/agents/implement.md: Added"*": askbefore deny rules + missing deny rulesreadsection:*.env.example(allow) after*.env.*(deny) so findLast returns allowRoot cause for #31: upstream
Permission.evaluate()usesfindLastandPermission.merge()usesrulesets.flat(). Profile-layer workaround via rule ordering; upstream issue to be filed separately.How did you verify your code works?
git diff dev..HEAD --stat: 5 files changed, 52 insertions, 5 deletionsteam.ts:483readsif (args.tasks.length < 1)team.ts:746reads "at least one worker task"opencode.json:96-98has correct findLast order:*.envdeny,*.env.*deny,*.env.exampleallowChecklist
🤖 Generated with Claude Code