redhat-developer / redhat-developer/rhdh-fullsend
Claude Code scoped disallowedTools rules remove Bash from --agent sessions
Nobody has claimed this yet.
- Dominant language
- Dockerfile
- Stars
- 2
- Forks
- 7
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 8
Description
Summary
When a named Claude Code agent contains a scoped Bash rule in its frontmatter, such as:
disallowedTools: Bash(sed *)
Claude Code removes the entire Bash tool from the top-level --agent session. The problem is not the --agent flag by itself.
The resulting error is:
Error: No such tool available: Bash. Bash exists but is not enabled in this context.
Confirmed with Claude Code v2.1.156 in the fullsend sandbox and v2.1.207 locally.
Validated root cause
Controlled tests on v2.1.207:
| Agent configuration | Tool count | Bash available? |
|---|---|---|
No --agent |
24 | Yes |
| Named agent, no tool restrictions | 24 | Yes |
disallowedTools: Write, Edit |
22 | Yes |
disallowedTools: Bash(sed) |
23 | No |
disallowedTools: Bash(sed *) |
23 | No |
| Full code-agent Bash deny patterns | 23 | No |
The same result occurs with --bare, which disables hooks, plugins, custom settings, and other integrations.
Claude's documentation says a scoped rule such as Bash(rm *) should keep Bash visible and deny only matching commands. Named sessions launched with --agent are documented to use the agent's tool restrictions. The observed behavior therefore appears to be a Claude Code bug in scoped disallowedTools handling.
Documentation:
Minimal reproduction
Run a named agent with a scoped Bash deny rule:
claude \
--agents '{"bash-test":{"description":"Minimal test agent","prompt":"Reply OK.","disallowedTools":["Bash(sed *)"]}}' \
--agent bash-test \
--print --verbose --output-format stream-json \
'Reply exactly OK without tools.' |
jq 'select(.type == "system" and .subtype == "init") |
{tool_count: (.tools | length), has_bash: (.tools | index("Bash") != null)}'
Actual result:
{"tool_count":23,"has_bash":false}
Control case: remove disallowedTools from the inline agent definition. Bash remains present and the tool count is 24.
Fullsend impact
Fullsend launches named sessions with:
claude --agent '<name>' --dangerously-skip-permissions
The upstream code and fix agents currently contain multiple scoped Bash rules, including Bash(sed *), Bash(awk *), and Bash(git push *). As a result, those top-level agents start without Bash and delegate shell work to subagents.
The first observed production example was:
- https://github.com/redhat-developer/rhdh-plugins/actions/runs/29083351902
- Total duration: 38m52s
- Agent duration: 34m48s
- Total cost: $5.99
- Tool calls across the agent tree: 141
- Top-level Bash failure followed by four spawned subagents
This confirms significant overhead, although the exact incremental cost and slowdown require a controlled comparison with an equivalent fixed run.
How we discovered it: the agent self-recovers through subagents
The failure did not initially look like a hard runtime error because the top-level agent worked around it autonomously. The transcript from the cited run shows the complete recovery path:
| Time (UTC) | Event |
|---|---|
| 09:35:09 | The top-level code agent attempts its required Bash setup command and receives No such tool available: Bash. |
| 09:35:28 | It explicitly recognizes the limitation: “I don't have direct Bash access in this context” and decides to spawn a subagent. |
| 09:35:36 | It spawns another subagent_type: code to perform the implementation. That agent inherits the same broken scoped disallowedTools rules, attempts Bash twice, and also fails. |
| 09:39:36 | The top-level agent probes a generic subagent_type: claude with echo, pwd, and git status. Bash works immediately. |
| 09:40:11 | Having identified a working agent type, it delegates the complete implementation to a generic claude subagent. |
| 10:08:53 | The generic subagent completes the implementation after 75 tool calls, including 49 Bash calls. |
| 10:08:59 | The top-level agent spawns a second generic claude subagent to verify the branch, commit, diff, and output file using six more Bash calls. |
| 10:09:46 | The top-level agent reports success. |
The resulting session tree was:
| Session | Role | Tool calls | Bash calls | Bash availability errors |
|---|---|---|---|---|
Top-level code agent |
Orchestration | 12 | 1 | 1 |
Spawned code agent |
First implementation attempt | 47 | 2 | 2 |
Generic claude agent |
Bash availability probe | 1 | 1 | 0 |
Generic claude agent |
Actual implementation | 75 | 49 | 0 |
Generic claude agent |
Final verification | 6 | 6 | 0 |
This behavior is important for diagnosis:
- The run succeeds instead of failing fast, so the underlying tool-inventory bug is easy to miss.
- Bash works in the same sandbox when a generic
claudeagent is used, ruling out the sandbox image and OpenShell policy as the cause. - A spawned
codeagent fails in the same way as the top-levelcodeagent, tying the failure to that agent definition. - The workaround multiplies contexts and tool calls: the top-level session performed only 12 tool calls, while the complete agent tree performed 141.
- The successful result therefore masks substantial latency and token cost caused by automatic delegation.
The evidence is in the fullsend-code artifact attached to:
Findings ruled out
--agentalone: a named agent without tool restrictions retains Bash.- Security hooks: reproduced with
--bare; hooks cannot explain the missing init tool. - Managed agent definitions: the minimal inline-agent reproduction does not depend on them.
- Built-in
codeagent: Claude Code has no built-in agent with that name. agent: ""harness override: fullsend composition treats an empty scalar as unset and inherits the base agent, so this does not clear the agent field.
Workarounds and proper fix
Interim workaround
Remove scoped disallowedTools: Bash(...) entries from the code and fix agent frontmatter. This restores Bash access.
Implemented for rhdh-agentic in:
Do not rely on a frontmatter Bash allowlist with fullsend
A definition such as tools: Bash(gh,echo) keeps Bash in the inventory. However, fullsend uses --dangerously-skip-permissions; under that mode, testing showed that an unlisted sed command still executed. This is not suitable for command-level enforcement in fullsend.
Recommended fullsend fix
Use permissions.deny in the generated project .claude/settings.json.
Validated on v2.1.207 with --dangerously-skip-permissions:
echoexecuted successfully.sedremained a Bash tool call but was denied specifically.- Bash stayed present in the init tool inventory.
Fullsend already generates /sandbox/workspace/.claude/settings.json for hooks, but the generated structure currently has no permissions block. Extend the harness schema and settings generator to support scoped deny rules, then remove the broken frontmatter rules.
Related upstream reports
These are related tool-inventory bugs, but they are not the same minimal reproduction:
- https://github.com/anthropics/claude-code/issues/13533 — closed stale
- https://github.com/anthropics/claude-code/issues/23506 — closed as duplicate
- https://github.com/anthropics/claude-code/issues/60237 — closed completed
Related fullsend-ai work
A search of open and closed issues and pull requests in fullsend-ai/fullsend found the following related work:
| Item | Status | Relationship |
|---|---|---|
| fullsend#303 | Open | Direct implementation issue for adding permissions.deny to generated .claude/settings.json. This is the primary fullsend-side fix. |
| fullsend#4437 | Open | Direct workaround issue for removing broken disallowedTools entries from code/fix agents. The actual agent definitions now live in fullsend-ai/agents. |
| fullsend#558 | Open | Related linting proposal for agent frontmatter. Its premise that tools and disallowedTools are inert under --agent is outdated and should be corrected. |
| fullsend#3874 | Closed | Earlier production evidence of the same recovery pattern: a code agent without Bash falls back to a generic claude subagent, adding time, tool calls, and cost. |
| fullsend PR #293 | Merged | Added ADR-0027 and selected permissions.deny as the intended steering mechanism. The ADR's claim that agent frontmatter is inert under --agent is now stale. |
| fullsend PR #2098 | Merged | Fixed adjacent subagent-dispatch and agent-definition problems, but does not fix the top-level scoped-disallowedTools bug. |
| fullsend#2826 | Open | Adjacent work on separating permission profiles between inference and deterministic post-run automation. |
No open pull request currently implements permissions.deny, a permissions_deny harness field, or an extension to GenerateClaudeSettings().
Work-item split
- This issue: Claude Code bug, minimal reproduction, and production evidence.
- fullsend#303: generate and install scoped
permissions.denyrules for top-level agent sessions. - fullsend#4437 / fullsend-ai/agents: remove the broken code/fix frontmatter rules and eliminate per-repository agent forks.
Action items
- File a new upstream Claude Code issue with the minimal scoped-
disallowedToolsreproduction. - Implement and track
permissions.denysupport in fullsend#303. - Coordinate agent-definition cleanup through fullsend#4437 and
fullsend-ai/agents. - Update ADR-0027 from fullsend PR #293: current Claude Code and its documentation apply agent tool restrictions to
--agentsessions. - Close redhat-developer/rhdh-plugins#3723 because
agent: ""inherits the base value and cannot fix the problem. - Merge redhat-developer/rhdh-agentic#91 as the interim workaround.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with fullsend#303, then inspect the harness schema and GenerateClaudeSettings() that produce /sandbox/workspace/.claude/settings.json. The related work is split across the upstream report, permissions.deny support, and agent-definition cleanup; completion is tracked by those separate items rather than by this issue alone.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- json, shell, yaml
- Domain
- cli, developer-experience, tooling
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100