Skip to content

Count Claude subagents named only in the structured tool result - #2777

Merged
peyton-alt merged 6 commits into
mainfrom
peyton/claude-structured-subagent-ids
Oct 16, 2026
Merged

peyton-alt merged 6 commits into
mainfrom
peyton/claude-structured-subagent-ids

Conversation

@peyton-alt

@peyton-alt peyton-alt commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

https://entire.io/gh/entireio/cli/trails/1574

Problem

Foreground Explore and Plan subagents in Claude Code are missing from checkpoints. Their tokens never reach subagent_tokens, and their transcripts are never searched for modified files.

spawnedAgentIDs finds subagents from the agentId: <id> line in an Agent tool result's text. Foreground Explore and Plan results are a hand-back report with no such line. This is verified on Claude Code 2.1.289, 2.1.294, 2.1.296 and 2.1.300. So subagents/agent-<id>.jsonl is never read. In one run that was about 52k tokens, roughly 40% of the session.

The SubagentStop hook doesn't make up for it: a foreground agent that changed no files gets no task record.

Foreground general-purpose results and background (async_launched) results still print the line, so they were counted correctly.

Fix

Every Agent result line names its agent in the structured toolUseResult.agentId, whatever its status. The forked-skill scan already read that field for status: "forked".

  • Every status: the scan now reads toolUseResult.agentId for every status. Empty or path-unsafe IDs are ignored.
  • The structured ID wins: if a tool result names an agent there, that result's agentId: text is never read, because the text can quote other agents' IDs.
  • Text as the fallback: results with no structured ID, as in older transcripts, still fall back to the text. The fallback now reads the last agentId:, which is the trailer Claude Code appends, instead of the first.

Both callers, CalculateTotalTokenUsage and ExtractAllModifiedFiles, go through spawnedAgentIDs, so they are unchanged. Subagent transcripts are keyed by agent ID, so an agent found both ways is still read once. The parent's own usage counts only assistant lines, so it can't absorb a subagent's.

Tests

  • TestStructuredAgentCountsTowardSession: a table with a forked-skill case and a foreground-agent case. Each goes through both tokens and modified files. The foreground case fails on main.
  • TestToolResultAgentIDs: covers every status, follow-ups, inline skills, empty and unsafe IDs, and assistant lines.
  • TestSpawnedAgentIDs_StructuredIDWinsOverText: checks that a quoted ID never wins, including in a follow-up, and that results with no structured ID, or with a non-object toolUseResult, fall back to the text.
  • TestExtractAgentIDFromText: a new row where the report quotes another ID before the trailer.

Recorded transcripts

CalculateTotalTokenUsage was run on six recorded sessions. Values are input / cache creation / cache read / output / API calls, with ground truth taken from the subagent transcripts after deduplicating by message id:

Case Main This branch (equals ground truth)
Foreground Explore, 2.1.296 none 6 / 18243 / 33410 / 785 / 3
Foreground Explore, 2.1.294 none 6 / 14725 / 26348 / 810 / 3
Foreground Explore, 2.1.289 none 26 / 15504 / 28614 / 553 / 3
Foreground Plan, 2.1.296 none 4 / 11254 / 22435 / 729 / 2
Background, 2.1.296 same 6 / 16544 / 29990 / 856 / 3
Foreground general-purpose, 2.1.296 same 6 / 18651 / 33969 / 379 / 3

Live check (Claude Code 2.1.300, Codex 0.156.1)

Each run went through the whole path in a fresh scratch repo: entire enable, then the agent prompt with real hooks, then git commit, then reading the checkpoint metadata. Each agent ran once with a binary built from main and once with this branch. Live numbers vary from run to run, so each run is compared with its own ground truth.

Agent Binary Parent token_usage subagent_tokens Ground truth (subagent)
Claude, foreground Explore main 8 / 22714 / 107234 / 770 / 4 (exact) missing 4 / 19786 / 17502 / 446 / 2
Claude, foreground Explore this branch 8 / 22224 / 108230 / 911 / 4 (exact) 4 / 19899 / 17600 / 513 / 2 4 / 19899 / 17600 / 513 / 2
Codex, spawn_agent child main 11471 / 0 / 50176 / 310 / 4 (exact) 10112 / 0 / 98816 / 719 / 6 exact
Codex, spawn_agent child this branch 10990 / 0 / 50176 / 292 / 4 (exact) 13805 / 0 / 48128 / 461 / 4 exact

Codex counts child threads through its SubagentStart/SubagentStop ledger, which this change doesn't touch. It behaves the same on both binaries: exact subagent tokens, the child's task record, and docs/red.md attributed.

Follow-up

Factory Droid has its own text-only ExtractSpawnedAgentIDs (factoryaidroid/transcript.go). It hasn't been checked for the same gap.

🤖 Generated with Claude Code


Note

Medium Risk
Changes how spawned subagent transcripts are resolved for token usage and modified-file extraction; incorrect parsing could mis-attribute session metadata, but scope is limited to Claude Code transcript parsing with extensive new tests.

Overview
Fixes missing foreground Explore/Plan (and other) Claude subagents in checkpoints by discovering agent IDs from structured toolUseResult.agentId on tool results, not only from agentId: text or forked-skill status: "forked".

forkedSkillAgentIDs is replaced by toolResultAgentIDs, which scans any user tool result with toolUseResult.agentId (completed, async_launched, forked, etc.), tracks which tool_use_ids were named structurally, and keeps the first launcher per agent ID. spawnedAgentIDs merges those IDs first, then falls back to text parsing only for results that did not name an agent structurally (older transcripts).

extractAgentIDFromText now takes the last agentId: in the result text so quoted IDs in the report body do not win over Claude’s trailer line.

Tests cover structured-vs-text precedence, follow-ups, foreground agents in token/file aggregation, and the last-agentId: behavior.

Reviewed by Cursor Bugbot for commit 1cbb38f. Configure here.

peyton-alt and others added 3 commits October 14, 2026 12:50
…sult

Foreground Explore and Plan results never print "agentId:" in their
text, so their tokens and files were missing from checkpoints.
toolUseResult.agentId names the agent for every Agent result; it now wins
for its tool call, and the text is read only for results that name none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Require "toolUseResult" as well as "agentId" before decoding a line, so
subagent progress lines are not decoded; pin that a non-object
toolUseResult still falls back to the text; refresh a stale comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@peyton-alt
peyton-alt requested a review from a team as a code owner October 14, 2026 20:16
Copilot AI balanced review requested due to automatic review settings October 14, 2026 20:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Filtering after deduplication can discard legitimate fallback agents, omitting their tokens and modified files.

1 open finding
What changed in this PR

Fixes missing Claude foreground subagent tokens and modified files in checkpoint metadata.

Changes:

  • Discovers structured agent IDs across all result statuses.
  • Prefers structured IDs, with trailer-based text fallback.
  • Adds parsing and token/file aggregation regression tests.
File Description
cmd/​entire/​cli/​agent/​claudecode/​transcript.go Updates subagent discovery and text fallback precedence.
cmd/​entire/​cli/​agent/​claudecode/​transcript_test.go Tests structured results, precedence, and session aggregation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 1cbb38f. Configure here.

peyton-alt and others added 3 commits October 14, 2026 13:45
A structured result without a tool_result block marked the empty
tool_use_id as named, which would skip the text of any other result that
also lacks one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… IDs

A later structured result quoting an earlier text-only agent's ID took
over that agent's entry in the last-wins text map, and the named-result
filter then dropped it, losing the agent's tokens and files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…red-subagent-ids

# Conflicts:
#	cmd/entire/cli/agent/claudecode/transcript.go
@peyton-alt
peyton-alt merged commit 9254299 into main Oct 16, 2026
19 checks passed
@peyton-alt
peyton-alt deleted the peyton/claude-structured-subagent-ids branch October 16, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants