[FEATURE] Subagent observability gap — parent agents and operators are blind to subagent tool calls
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
When Claude Code spawns a subagent via the built-in Agent tool, the parent agent receives only the final text output plus basic metadata (token count, duration, tool use count). The operator, even in --output-format stream-json --verbose mode, sees only 4 event types: task_started, rate_limit_event, task_notification, and tool_use_result. None of these contain the individual tool calls the subagent made.
This creates three cascading problems:
- No audit trail — if a subagent overwrites a file, runs a destructive command, or leaks data, there is no record of it outside the subagent's own (inaccessible) context.
- No enforcement verification — even if hooks are implemented for subagents (per #45427), you cannot verify they actually fired.
- No learning loop input — governance feedback systems that learn from execution history (signal detection, recurrence analysis, pattern optimization) are blind to subagent behavior. Subagents cannot improve because there is no data to improve from.
You cannot govern what you cannot see. You cannot improve what you cannot measure.
Related RFC: #45427 (Dimitri Geelen — Deterministic tool gate). His RFC addresses enforcement; this RFC addresses the complementary observability side.
What the parent agent sees
The parent agent describes the subagent as "essentially a black box." It receives:
- Final text output from the subagent
- Basic metadata: token count, duration, number of tool uses
It does not receive: individual tool calls, tool results, reasoning/thinking, conversation history, file modification details, or bash command details.
What the operator sees (stream-json)
| Event | Contains |
|---|---|
| task_started | Subagent was spawned |
| rate_limit_event | Rate limit encountered (if any) |
| task_notification | Status: completed |
| tool_use_result | Final text output from subagent |
What external processes provide
When spawning a separate claude -p --output-format stream-json --verbose process instead of using the built-in Agent tool, you get full observability: every tool_use event, every tool_result event, every file edit, every bash command. This asymmetry is the core problem.
Impact on learning loops
Governance feedback systems that convert runtime outcomes into reusable signals (recurrence detection, pattern confidence adjustment) depend on receipt data. Receipts capture what the dispatched worker reported, not what subagents did internally. When a subagent causes a failure, the learning loop sees the symptom (GATE_FAILURE) but not the cause (which subagent tool call created the problem). The system learns that things fail but not why subagents cause them to fail. This makes feedback loops progressively less useful as subagent usage increases.
| Learning Loop Function | Requires Subagent Events? | Status with Built-in Agent Tool |
|---|---|---|
| Signal extraction from receipts | No | Works (symptom-level) |
| Recurrence detection across dispatches | No | Works (pattern-level) |
| Root cause attribution | Yes | Broken — can't trace to subagent action |
| Pattern confidence adjustment | Yes | Broken — no subagent pattern data |
| Failure class derivation | Yes | Degraded — misattributed to parent |
Proposed Solution
Forward subagent tool call events to the parent stream in stream-json mode.
Minimum event set per subagent tool call
{
"type": "tool_use",
"agent_id": "subagent_abc123",
"agent_depth": 1,
"parent_agent_id": "parent_xyz789",
"tool": "Write",
"input": { "file_path": "/path/to/file.py", "content": "..." },
"call_id": "call_001"
}
Key properties
- Agent attribution — every event carries
agent_idandagent_depthso operators can distinguish parent from subagent events - Nested forwarding — events bubble up through all levels with incrementing
agent_depth - Opt-in granularity — operators can filter by
agent_depthif they only want parent events (backward compatible) - No content truncation — tool inputs and outputs forwarded as-is
- Append-only semantics — events are ordered and immutable once emitted
The Three Pillars of Agent Governance
| Pillar | Purpose | RFC |
|---|---|---|
| Enforcement | Prevent unauthorized actions | #45427 (Tool Gate) |
| Observability | Prove what happened | This RFC (Event Forwarding) |
| Learning | Improve from execution history | Enabled by this RFC |
Minimum viable ask
- Forward
tool_useandtool_resultevents from subagents to the parent stream instream-jsonmode, withagent_idandagent_depthfields - Include subagent events in
--verboseoutput so operators see the full activity log - Provide a
--subagent-eventsflag (or similar) to control forwarding granularity - Document the event schema for subagent events so tooling can be built on top
- Emit subagent lifecycle events (
subagent_started,subagent_completed,subagent_failed) with summary metadata
Alternative Solutions
| Approach | Verdict | Why |
|---|---|---|
| External process spawning (our workaround) | Works but wrong long-term | Requires building full process management; duplicates what Agent tool should provide |
| Post-hoc session log retrieval | Insufficient | No standard API to retrieve subagent session logs; timing gap between action and detection |
| Parent-level output parsing | Fragile | Parsing final text output for clues is unreliable and lossy |
| Periodic polling of filesystem | Fragile | Detecting file changes doesn't tell you which agent made them |
We currently work around this in VNX Orchestration by never using the built-in Agent tool. Instead we spawn all workers as external claude -p --output-format stream-json --verbose processes via a SubprocessAdapter, which gives full stream capture. But this is a workaround — the built-in Agent tool should provide the same observability.
Priority
Medium - Would be very helpful
Feature Category
CLI commands and flags
Use Case Example
- I operate VNX Orchestration (https://github.com/Vinix24/vnx-orchestration), a multi-terminal agent system on Claude Code
- An orchestrator (T0) dispatches work to worker terminals (T1/T2/T3) through a structured dispatch queue with human approval
- A worker spawns a subagent via the Agent tool to "refactor the utility module"
- The subagent rewrites
utils.pywith a breaking change - Gate fails: tests don't pass
- Receipt records:
GATE_FAILURE,codex_gate,test_failure - But the root cause — the subagent's destructive rewrite — is invisible
- The learning loop recommendation targets the gate or the dispatch, never the subagent behavior
- With subagent event forwarding, I would see the exact
tool_useevent where the subagent called Write onutils.py, enabling root cause attribution
Additional Context
Related issues: #43772, #32376, #32193, #31250, #39903
Related RFC: #45427 (Dimitri Geelen — Deterministic tool gate)
Tested on: Claude Code v2.1.100, --output-format stream-json --verbose
This RFC is complementary to #45427. Enforcement (#45427) without observability (this RFC) is unverifiable. Observability without enforcement is informational only. Together they form a complete governance framework.
Evidence and workaround implementation: https://github.com/Vinix24/vnx-orchestration (see scripts/lib/subprocess_adapter.py for full stream capture)
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗