<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kosta Stamboldzhiev</title>
    <description>The latest articles on DEV Community by Kosta Stamboldzhiev (@kostakurta).</description>
    <link>https://dev.to/kostakurta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4131205%2F18b23550-2ecd-4d66-b486-141bc7f32249.png</url>
      <title>DEV Community: Kosta Stamboldzhiev</title>
      <link>https://dev.to/kostakurta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kostakurta"/>
    <language>en</language>
    <item>
      <title>Three ways I miscounted my own AI agents, and what the transcripts actually say</title>
      <dc:creator>Kosta Stamboldzhiev</dc:creator>
      <pubDate>Fri, 18 Sep 2026 09:06:44 +0000</pubDate>
      <link>https://dev.to/kostakurta/three-ways-i-miscounted-my-own-ai-agents-and-what-the-transcripts-actually-say-4kjg</link>
      <guid>https://dev.to/kostakurta/three-ways-i-miscounted-my-own-ai-agents-and-what-the-transcripts-actually-say-4kjg</guid>
      <description>&lt;p&gt;Claude Code writes a JSONL transcript for every session, and another one for every subagent it spawns. They sit in &lt;code&gt;~/.claude/projects/&lt;/code&gt; right now, on your machine, and nobody reads them.&lt;/p&gt;

&lt;p&gt;I have spent six weeks reading mine. I got the arithmetic wrong three times in public, and each mistake turned out to be a property of the file format rather than a slip. Here they are, with the one-liners that expose them, so you can skip the part where you post a wrong number on Reddit and get corrected by a stranger with a better corpus.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Most of the output is in files you are not looking at
&lt;/h2&gt;

&lt;p&gt;The transcript you watch live is the parent. Every subagent writes its own file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude/projects/&amp;lt;project-slug&amp;gt;/&amp;lt;session-id&amp;gt;.jsonl          the session you were watching
~/.claude/projects/&amp;lt;project-slug&amp;gt;/&amp;lt;session-id&amp;gt;/subagents/     everything it spawned
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my machine, across 51 runs that used subagents, the children wrote &lt;strong&gt;58.4% of all output tokens&lt;/strong&gt;. If you are judging your own usage by what scrolls past you, you are looking at under half of a fan-out run.&lt;/p&gt;

&lt;p&gt;There is a second tier that is easy to miss entirely, because it lives one directory deeper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;session&amp;gt;/subagents/&amp;lt;id&amp;gt;.jsonl                          a Task subagent
&amp;lt;session&amp;gt;/subagents/workflows/&amp;lt;wf_id&amp;gt;/agent-*.jsonl     a workflow agent
&amp;lt;session&amp;gt;/subagents/workflows/&amp;lt;wf_id&amp;gt;/journal.jsonl     NOT an agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My first walker stopped at the top level and under-counted children by about half. My second walker filtered on &lt;code&gt;.jsonl&lt;/code&gt; and counted 34 workflow &lt;strong&gt;journals&lt;/strong&gt; as silent agents, inflating the count by 7% and dragging down every per-child average. Same directory, opposite errors, two weeks apart.&lt;/p&gt;

&lt;p&gt;Count them properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.claude/projects/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/subagents/&lt;span class="k"&gt;*&lt;/span&gt;.jsonl | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.claude/projects/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/subagents/workflows/&lt;span class="k"&gt;*&lt;/span&gt;/agent-&lt;span class="k"&gt;*&lt;/span&gt;.jsonl | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.claude/projects/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/subagents/workflows/&lt;span class="k"&gt;*&lt;/span&gt;/journal.jsonl | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Summing usage per line over-reports you by about 3x
&lt;/h2&gt;

&lt;p&gt;This is the one that will bite anyone writing a cost tool.&lt;/p&gt;

&lt;p&gt;A single assistant response with several content blocks — thinking, then a tool call, then text — is written as &lt;strong&gt;several lines&lt;/strong&gt;, and every one of those lines repeats the same &lt;em&gt;complete&lt;/em&gt; usage object. It is not a running total. It is the same number, three times.&lt;/p&gt;

&lt;p&gt;On my corpus: 130,002 lines carrying a usage block describe &lt;strong&gt;63,014 responses&lt;/strong&gt;. Summed per line that is 90,955,151 output tokens. Deduplicated on &lt;code&gt;message.id&lt;/code&gt; it is 46,959,967. Plus 94%.&lt;/p&gt;

&lt;p&gt;And here is the part I only saw after splitting it by tier:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tier&lt;/th&gt;
&lt;th&gt;over-count if you sum per line&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;main transcripts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+195%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;subagent transcripts&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+2%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Subagent files mostly write one line per response. So a reader with this bug looks &lt;em&gt;correct&lt;/em&gt; on the child files — which are exactly the files you would spot-check when validating it. That is why the bug survived two of my own reviews.&lt;/p&gt;

&lt;p&gt;Deduplicate on &lt;code&gt;message.id&lt;/code&gt;, keeping the last line you see for an id, because a response is written progressively and the final line is the complete one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A hook firing writes two records, and sometimes zero
&lt;/h2&gt;

&lt;p&gt;Every hook firing is written into the transcript, which means the question "has this guardrail of mine ever actually run?" is answerable &lt;strong&gt;retroactively, for your entire history&lt;/strong&gt;, with no instrumentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-ho&lt;/span&gt; &lt;span class="s1"&gt;'"hookName":"[^"]*"'&lt;/span&gt; ~/.claude/projects/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.jsonl | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mine says &lt;code&gt;PostToolUse:Edit&lt;/code&gt; 505 and &lt;code&gt;PostToolUse:Bash&lt;/code&gt; 4. Four. Ever. I would have told you that shell guard was load-bearing.&lt;/p&gt;

&lt;p&gt;Except those are records, not firings. One firing writes &lt;strong&gt;two&lt;/strong&gt; lines that share a &lt;code&gt;toolUseID&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hook_success&lt;/code&gt; — the command, its exit code, duration and stdout&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hook_additional_context&lt;/code&gt; — the text it injected back into the session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deduplicate and my 8,831 records become &lt;strong&gt;6,898 firings&lt;/strong&gt;. Counting lines over-reports your own guardrails by 28%.&lt;/p&gt;

&lt;p&gt;The payoff is that &lt;code&gt;hook_success&lt;/code&gt; names the &lt;em&gt;command&lt;/em&gt;, so a matcher carrying two hooks splits apart. Mine on &lt;code&gt;Edit&lt;/code&gt; runs &lt;code&gt;format-after-edit&lt;/code&gt; 185 times against &lt;code&gt;lint-after-edit&lt;/code&gt; 52 — I had no idea one was running a third as often as the other.&lt;/p&gt;

&lt;p&gt;Then the caveat that cost me my favourite claim. I had been saying that a hook absent from that list has never fired. A reader on r/ClaudeAI disproved it on a much bigger corpus: her &lt;code&gt;PreCompact&lt;/code&gt; hook fires and writes &lt;strong&gt;no &lt;code&gt;hookName&lt;/code&gt; at all&lt;/strong&gt; — she proved it by lining its own output file up with nine compaction boundaries. So absence is three cases, not two:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;never registered&lt;/li&gt;
&lt;li&gt;registered, never fired&lt;/li&gt;
&lt;li&gt;fired, never recorded&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The transcript cannot tell them apart. What &lt;em&gt;appears&lt;/em&gt; definitely ran; what is missing needs a second check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The command
&lt;/h2&gt;

&lt;p&gt;I got tired of maintaining scratch scripts that were wrong in a new way each week, so the reader I build has it as a flag now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx github:Kostakurta8/roundtable &lt;span class="nt"&gt;--stats&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads every transcript under &lt;code&gt;~/.claude&lt;/code&gt;, prints one screen, and exits — no server, no port, nothing written, nothing leaving the machine. It prints the traps rather than silently correcting them, including the caveat that a missing hook proves nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  1,469 session transcripts, 51 of them spawned subagents
  513 children  (327 task, 186 workflow)  median 7 per run

  children wrote 58.4% of the output of the runs that spawned them
  cold start: 46,669 cache-creation tokens per child, before it does any work
  median child: 65 tool calls over 1,286 s
  13 of 513 children (2.5%) made no tool call and wrote nothing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line is my favourite, because it is the failure you cannot see from the orchestrator: thirteen children that ran for hours and produced nothing at all. Twelve of them belong to a single run — one lane of a fan-out died quietly while the run as a whole looked busy and healthy.&lt;/p&gt;

&lt;p&gt;The rest of the project is a viewer for the same files: it draws the session as a pixel-art office where each agent is a person at a desk, and the timeline rebuilds the room at any second by replaying the events rather than approximating them. It is read-only by construction — 405 to every non-GET, no outbound connection — and MIT.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Kostakurta8/roundtable" rel="noopener noreferrer"&gt;github.com/Kostakurta8/roundtable&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;--stats&lt;/code&gt;, I would genuinely like to know your child share. Mine is 58.4% and I have no idea whether that is normal.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>ai</category>
      <category>claude</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
