[BUG] Manual mode does not review model-generated file content written through Bash

Status Open
Reported on v2.1.222
Maintainer reply None cached
Activity 0 comments · opened Aug 7, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In manual permission mode, the diff review that gates Edit and Write does not apply to file content written through the Bash tool. Approving a shell command is not the same as reviewing the content that command produces, so model-generated bytes reach disk with no diff shown.

The gap is wider than direct writes into the working tree. The model can generate content outside the project, for example in a git worktree or a temporary directory, then carry it in later through ordinary git operations. The review is bypassed at generation time, so a guard scoped to the repository does not close it.

In one session this produced eight merge conflict resolutions, written with awk into a separate worktree and committed with git plumbing (hash-object, update-index, commit-tree). Each resolution arbitrated between an upstream bug fix and a local style rule. Nothing was ever presented for approval, although manual mode had been selected precisely to arbitrate such decisions. The same path also bypassed the encoding handling of Edit and mixed ISO-8859 and UTF-8 content in one file.

What Should Happen?

In manual mode, any model-generated byte stream that lands on disk should require the same review as an Edit, regardless of the tool that carries it and regardless of the directory it lands in.

The harness can make a distinction that a user-side hook cannot: content generated by the model is present in the tool input, whereas content transplanted by git from existing commits (rebase, merge, checkout) is not. The review should be bound to the origin of the content, not to the name of the tool.

Error Messages/Logs

No error is produced. The writes succeed silently, which is the problem.
Examples of the unreviewed path observed in a real session:

awk -f transform.awk file > tmp && mv tmp FDN/Sql/Basic.pm
git hash-object -w folded.pm
git update-index --cacheinfo 100644,<sha>,FDN/Sql/Basic.pm
git commit-tree <tree> -p <parent> -F message
git worktree add /tmp/scratch <branch>

Steps to Reproduce

  1. Start Claude Code in a git repository with permission mode set to manual.
  2. Ask for a task that requires resolving merge conflicts, for example rebasing a branch onto another branch where the same lines changed on both sides.
  3. Observe that conflicts may be resolved by writing files with shell redirection, with in-place editors, or with git plumbing, and that no diff review is presented for that content.
  4. Ask for a large refactor of a file. Observe that the model may create a git worktree outside the project, generate content there, then bring it in with git commands. No review is presented at any point.
  5. Compare with the same work done through Edit: every change is presented for approval.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.222 (Claude Code)

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

IntelliJ IDEA terminal

Additional Information

Why user-side mitigation does not close this:

  • A PreToolUse hook on Bash fires only after the model has planned and composed the command. It can stop the write, but not the tokens already spent, and it cannot tell a legitimate move of existing bytes from an injection of generated ones.
  • Blocking shell writers by name (mv, cp, tee, sed -i, redirection) blocks legitimate use and still misses indirect generation, for example an awk program run from a file.
  • Instructions in CLAUDE.md are read once per session and lose weight over long conversations.

Suggested remedies, in order of value:

  1. Bind the review to the origin of the content: when a Bash command embeds a payload and a write target, present the resulting diff for approval as an Edit would.
  2. Offer a manual-mode setting that denies shell-initiated writes, with an explicit allowance for commit-transplanting git operations.
  3. At minimum, expose to hooks whether a tool call carries model-generated content, and document that Bash bypasses the edit gate in manual mode.

Impact: semantic decisions land unreviewed, encoding handling is bypassed, and work done outside the reviewed path has to be redone inside it, so the user pays for both passes.

View original on GitHub ↗