Subagent identity confusion: clicking an agent chip silently redirects user input to the subagent, whose env is byte-identical to the parent (same CLAUDE_CODE_SESSION_ID) — enabled unintended pushes under the parent session's identity
Summary
Two compounding design flaws let a subagent silently assume the full identity and write authority of its parent session, leading to a real incident in a multi-session workflow: a read-only investigation subagent ended up pushing commits to a shared git mainline under the parent session's identity, which a coordinating session then had to detect and revert as "impersonation". Total confusion lasted ~a day across several sessions and one human operator.
Environment
- Claude Code CLI on Linux (Ubuntu, kernel 6.8),
CLAUDE_CODE_ENTRYPOINT=cli - Model: claude-fable-5
- Workflow: several concurrent Claude Code sessions ("organs") working one shared repo, coordinated by an external lock/gate tool keyed on
CLAUDE_CODE_SESSION_ID, with a human mentor arbitrating
Flaw 1 — UI: clicking a subagent chip silently moves the user's prompt into the subagent's conversation
When a session spawns an agent (Agent/Task tool), the running/finished agent appears as a clickable element at the bottom of the screen. Clicking it switches the input box to that agent's conversation — with no persistent banner or visual distinction from the main session.
In our incident the human operator clicked it unknowingly, believed he was talking to the main session, and issued normal work instructions ("commit this", "push", "write the handoff"). The subagent — launched with a read-only investigation brief — followed the human's explicit instructions. From its point of view it had direct user authorization; from the main session's point of view, rogue commits appeared under its own name; from the human's point of view, his work kept being mysteriously reverted (by the main session, which was doctrinally correct to distrust artifacts it never produced). Nobody misbehaved locally; the identity seam misled everyone globally.
Flaw 2 — Subagent execution context is byte-identical to the parent's: no way for external tooling to tell them apart
Verified empirically (parent Bash tool call vs a general-purpose subagent instructed to run the same command):
env | grep -iE "claude|agent|session" | sort
produces identical output in both contexts — same CLAUDE_CODE_SESSION_ID, same AI_AGENT, same CLAUDE_CODE_CHILD_SESSION=1, no agent_id-like variable. Any external coordination mechanism keyed on session identity (multi-session commit locks, per-session write gates, attribution trails) necessarily treats a subagent as its parent. A subagent can therefore enroll, take locks, commit, and push as the parent organ.
What does work (partial mitigation we found)
PreToolUse hooks fire for subagent tool calls and the hook payload carries agent_id / agent_type (absent for main-session calls). We built a project-level hook denying git mutations from payloads with agent_id. This is the only layer we found that can see the difference — and it requires every project to know about the trap and build its own guard.
Requests
- UI: a persistent, unmistakable indicator whenever the user's input is focused on a subagent conversation (e.g. a banner: "You are messaging subagent <type> spawned by this session"), and ideally an explicit confirmation the first time a human types into a subagent.
- Env: set a documented environment variable in subagent Bash context (e.g.
CLAUDE_CODE_AGENT_ID=<id>/CLAUDE_CODE_IS_SUBAGENT=1) so external tools (locks, hooks-path git gates, audit) can distinguish subagent from parent without a Claude-Code-level hook. - Docs: document that user-typed messages can reach subagents via the chip click, and that subagent tool calls share the parent's entire environment — both were surprises to an experienced operator.
- Consider whether subagents should inherit write-relevant identity by default at all: a launch-brief field like
permissionMode: read-onlyenforced at the harness level would have prevented the entire incident.
Impact
- 3 commits pushed to a shared mainline under a false (inherited) identity; later reverted.
- Hours of multi-session forensic work to reconstruct who did what; near-miss on reverting legitimate peer commits that the confused narrative had lumped in.
- The failure mode is silent and reproducible in any repo where a human can click an agent chip and where subagents can run
git.