Build1 publisher2 min readPublished
A Stop hook clocked Claude Code's code-reviewer subagent at 37 seconds per call
Claude Code hands a Stop hook the full session transcript when a session ends. Joining each tool_use to its tool_result on tool_use_id yields per-agent seconds and error flags, and one operator says the numbers cut his weekly bill 15 to 20 percent.
The Engineer · Build desk

What happened
- Timing every subagent invocation put the general-purpose agent at 18 seconds on average, Explore at 22 and code-reviewer at 37, the reverse of what the operator expected to find.
- The Stop hook is a shell script Claude Code runs when a session ends, handed JSON on stdin that carries the session id and the path to the session's transcript.
- Subagent launches show up in that transcript as tool_use blocks named "Agent", even though the interface labels the same tool "Task".
- A two-pass Python script builds dictionaries of calls and results, then matches them on tool_use_id to compute how long each invocation took.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Operators running mixed subagents can settle which one to stop invoking by reflex from a file of durations instead of an impression formed while waiting.
- exposure Any ledger built this way depends on an internal transcript field name, so a rename in a future Claude Code release stops the counting without failing loudly.
- capability The same tool_use to tool_result join times any tool the transcript records, so per-tool latency is available without vendor telemetry or an SDK.
- cost The saving is quoted as a share of an undisclosed weekly bill, so a team cannot budget against it or say what the same habit change would return.
The parse is a join. A subagent launch lands in the transcript as a tool_use block, and its outcome lands later, on its own line, as a tool_result carrying the same tool_use_id [10]. Pass one of the script builds two dictionaries, uses and results; pass two matches them on that id and computes the duration [11]. The success bit is is_error on the result line: present and true means the call ended in error, absent means it worked [10].
Grep the transcript for the tool name you see in the interface and you get nothing back. Claude Code's UI displays "Task"; the log records "name": "Agent" [7]. The script's comment spells it out: `In Claude Code transcripts, the 'Task' tool is recorded as name="Agent"` [8]. Which agent ran is one level down, in input.subagent_type [9].
The author expected the general-purpose agent to be the slow one [2]. Code-reviewer's 37-second average is about 2.1 times general-purpose's 18 [17], and 15 seconds past Explore [18]. Acting on the hunch would have meant tuning the fastest of the three agents he measured [19]. The author wrote that a sense of an agent being heavy is not data [15].
What accumulates in the ledger at ~/.claude/logs/agent-invocations.jsonl is durations and error flags, one line per invocation [12]. Token counts are not among the fields the post describes reading [5]. The reported outcome is a weekly Claude Code spend cut of roughly 15 to 20 percent after one habit changed [3], and the yen figure the post does put on the table is the setup's monthly revenue, about 1.2M yen [16]. For that percentage to carry into another setup, duration would have to track token spend across the agents you run, and code-reviewer would have to be invoked about as often in your workflow as in his.
The hook fires on session end [4]. A session that mixes Explore, code-reviewer and general-purpose calls can run for tens of minutes [14], and none of it reaches the ledger until it stops [20]. Aggregation is a weekly jq pass over the file [13].
Adoption is one line in ~/.claude/settings.json, and the hook changes none of Claude Code's behaviour because it reads an artifact the session already writes [6][5].
What to watch
- Per-call token counts appearing in the transcript would let the same tool_use_id join price agents in tokens instead of seconds.
- A rename of the Agent tool name, or a change to the transcript's JSONL layout, would break every hook built on this parse.
- A published baseline weekly bill would make the 15 to 20 percent figure checkable by anyone else.