PricingSign inDownload

Blog / claude code

When a Claude Code subagent says it changed files

Subagents can report edits that never reached the disk. A hook puts git's real list of changed files next to every subagent's report, so the main agent checks.

On this page
  1. Check it yourself first
  2. A hook that shows the real changes
  3. What happened when we ran it
  4. The catch: background subagents
  5. What this doesn't catch
  6. Where Joxo fits
  7. Frequently asked questions
  8. Why does a Claude Code subagent say it made changes it didn't make?
  9. Can a hook see what a subagent changed?
  10. Does the hook work for background subagents?
  11. Which subagents can edit files?
  12. Try it with your team

Claude Code hands part of a job to a subagent. The subagent comes back with "Added the subtract function to math.js", the main agent passes that on to you, and you move on. Later you find math.js unchanged.

It happens for ordinary reasons. The subagent was a read-only type (Explore has no Edit or Write tool), a permission prompt it couldn't answer blocked the write, it edited a file in another folder, or it described the change it meant to make as if it had made it. The main agent only sees the subagent's final message, not what happened on disk, so it repeats the claim.

The fix is to put the facts next to the claim: after every subagent returns, show the main agent what git says changed.

Check it yourself first#

When a subagent reports edits, these two commands settle it in a second:

sh
git status --short
git diff --stat

git status --short lists every file that differs from the last commit, including new files; git diff --stat shows how many lines changed in each. If the file the subagent named isn't there, the edit didn't happen.

You can also ask for it in CLAUDE.md: "When a subagent says it changed files, run git status --short and compare before you report its work." That works most of the time. The hook below makes it happen every time.

A hook that shows the real changes#

Claude Code runs PostToolUse hooks after a tool call finishes, and subagents run through a tool (named Agent, or Task in older versions). A PostToolUse hook can return additionalContext, text that Claude Code places next to the tool's result. So the main agent reads the subagent's report and, right after it, git's list of changed files.

Save this as .claude/hooks/show-real-changes.sh:

sh
#!/bin/sh
# After a subagent returns, show the main agent what really changed on disk.
input=$(cat)
# A background subagent returns at launch; there is nothing to check yet.
[ "$(printf '%s' "$input" | jq -r '.tool_response.status // empty')" = "async_launched" ] && exit 0
cd "$CLAUDE_PROJECT_DIR" || exit 0
changes=$(git status --short --untracked-files=all -- . ':!.claude' 2>/dev/null)
[ -z "$changes" ] && changes="(no files changed since the last commit)"
jq -n --arg c "$changes" '{
  hookSpecificOutput: {
    hookEventName: "PostToolUse",
    additionalContext: ("Files that differ from the last commit, from git status, after this subagent:\n" + $c + "\nCompare this with what the subagent said it changed before you report its work.")
  }
}'

Make it executable with chmod +x .claude/hooks/show-real-changes.sh, then register it in .claude/settings.json:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Agent|Task",
        "hooks": [
          { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/show-real-changes.sh" }
        ]
      }
    ]
  }
}

The script needs jq. The ':!.claude' part leaves Claude Code's own settings folder out of the list.

What happened when we ran it#

We made a small repository with one committed file, math.js, and asked Claude Code (Haiku) to have a subagent add a subtract function.

A subagent that couldn't edit. We asked for the Explore subagent, which is read-only. It came back with the code it would have added. Right after its report, the main agent received:

Files that differ from the last commit, from git status, after this subagent:
(no files changed since the last commit)
Compare this with what the subagent said it changed before you report its work.

The main agent's answer to us started with "subtract was not added. No files changed." and offered the three lines to apply.

A subagent that did edit. With the general-purpose subagent, the hook reported M math.js, and the main agent told us the subagent's report matched the diff.

The catch: background subagents#

Our first run gave a misleading answer. Claude Code had started the subagent in the background, and for a background subagent the tool returns the moment it launches, with status: "async_launched". The hook ran then, before the subagent had done anything, and reported no changes. That's why the script now exits early on async_launched.

For a background subagent, check git status --short after it finishes, or ask for subagents that edit files to run in the foreground ("run the subagent in the foreground" in your prompt or in CLAUDE.md). If background subagents change files while the main session sits idle, a status line with refreshInterval set shows the change count as it moves; see See which branch each Claude Code session is on.

What this doesn't catch#

  • Wrong edits. The hook proves a file changed, not that the change is right. Pair it with a test run before the agent says it's done: Make Claude Code run the tests before it says done.
  • Changes outside the project folder. It runs git status in the project only.
  • Files that were already changed. It lists everything that differs from the last commit, so in a dirty tree it shows earlier edits too. Committing between steps keeps the list honest.

Where Joxo fits#

The same question comes up between people: a teammate's agent says a task is finished, and you want to know what actually landed. In Joxo, a task counts as done only with evidence such as a merged pull request, and you see each agent's work on the project board from your phone or desktop. Setup is one line pasted into your agent.

Frequently asked questions#

Why does a Claude Code subagent say it made changes it didn't make?#

The main agent only sees the subagent's final message. If the subagent couldn't write (a read-only type, a blocked permission) or described a planned change as done, nothing on disk contradicts it unless someone checks.

Can a hook see what a subagent changed?#

Yes. A PostToolUse hook matched on Agent|Task runs when a foreground subagent returns. It can run git status and pass the result to the main agent as additionalContext.

Does the hook work for background subagents?#

Not at return time: the tool returns at launch with status: "async_launched", before any work happens. Check git status after the subagent finishes, or run edit-making subagents in the foreground.

Which subagents can edit files?#

It depends on the tools each one has. Built-in Explore is read-only; general-purpose can edit. A custom subagent can edit only if its tools list includes Edit or Write (or it inherits them).