<?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: J. Gravelle</title>
    <description>The latest articles on DEV Community by J. Gravelle (@jgravelle).</description>
    <link>https://dev.to/jgravelle</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%2F3815404%2F5c973ed1-5fef-4135-b089-56d7dbd03d4d.jpg</url>
      <title>DEV Community: J. Gravelle</title>
      <link>https://dev.to/jgravelle</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jgravelle"/>
    <language>en</language>
    <item>
      <title>What survives /compact in Claude Code...</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Mon, 19 Oct 2026 22:34:45 +0000</pubDate>
      <link>https://dev.to/jgravelle/what-survives-compact-in-claude-code-16a4</link>
      <guid>https://dev.to/jgravelle/what-survives-compact-in-claude-code-16a4</guid>
      <description>&lt;h2&gt;
  
  
  ...and how to lose less of it
&lt;/h2&gt;

&lt;p&gt;Claude Code doesn't forget things at random when it compacts. There's actually a table in the docs spelling out exactly what survives.&lt;/p&gt;

&lt;p&gt;Everything else is left to a model trying to squeeze a few hundred thousand tokens of your afternoon into a summary, including that edge case you spent 40 minutes figuring out.&lt;/p&gt;

&lt;p&gt;Two things are worth doing about it: put the important stuff somewhere Claude will find it again, and make compaction happen less often.&lt;/p&gt;

&lt;p&gt;That 40-minute example isn't something I made up. An r/ClaudeCode post this weekend put it as "/compact 'fixes' that by summarizing, and then Claude forgets the edge case you spent 40 minutes on" (&lt;a href="https://www.reddit.com/r/ClaudeCode/comments/1x9aivo/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/ClaudeCode/comments/1x9aivo/&lt;/a&gt;). The next morning a founder on X was asking about an orchestrator that goes "kind of useless" after a few compactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes back after compaction
&lt;/h2&gt;

&lt;p&gt;Here's what the "What survives compaction" table in Claude Code's docs says (code.claude.com/docs/en/context-window, read 2026-10-19):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The project-root CLAUDE.md and unscoped rules: re-injected from disk.&lt;/li&gt;
&lt;li&gt;Auto memory: re-injected from disk.&lt;/li&gt;
&lt;li&gt;The plan Claude wrote in plan mode: re-injected from disk.&lt;/li&gt;
&lt;li&gt;Files Claude read or edited: up to five come back, most recently modified first. A file over 5,000 tokens comes back as a path with no content.&lt;/li&gt;
&lt;li&gt;Skill bodies: re-injected, capped at 5,000 tokens per skill and 25,000 total.&lt;/li&gt;
&lt;li&gt;SessionStart hooks with a &lt;code&gt;compact&lt;/code&gt; matcher: run again, and their output is added.&lt;/li&gt;
&lt;li&gt;Everything else, including the conversation and context earlier hooks added: summarized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stuff on disk comes back. The stuff you discussed in the conversation gets summarized.  And where did you spend those 40 minutes working out that edge case? In the conversation, naturally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put decisions on disk while you work
&lt;/h2&gt;

&lt;p&gt;I'd suggest a short notes file that Claude updates whenever you settle something important. The edge case you found, the approach you rejected and why, the test that reproduces the bug. That sort of thing.&lt;/p&gt;

&lt;p&gt;Then have Claude read it back after every compaction. There's a hook for exactly that. Put this in &lt;code&gt;.claude/settings.json&lt;/code&gt; at your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"compact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cat .claude/session-notes.md"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One caution: keep that file short. Everything the hook prints goes right back into context after compaction and rides along on every turn after. Turn your notes into a 3,000-line autobiography and you've invented another way to waste tokens.&lt;/p&gt;

&lt;p&gt;One other detail from the same docs page: nested CLAUDE.md files and rules with &lt;code&gt;paths:&lt;/code&gt; frontmatter only reload when needed. Otherwise they get summarized away with the rest. If a rule absolutely needs to survive, the root CLAUDE.md is where it belongs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell the summarizer what matters
&lt;/h2&gt;

&lt;p&gt;You can also tell &lt;code&gt;/compact&lt;/code&gt; what you want preserved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/compact Keep the retry edge case in payments/charge.py and the test that reproduces it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or put standing instructions in CLAUDE.md under a &lt;code&gt;# Compact instructions&lt;/code&gt; heading, so you don't have to remember to type the same thing every time. Costs nothing to set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compact on your schedule, not at the cliff
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/autocompact 300k&lt;/code&gt; tells Claude Code how full the context window can get before it compacts on its own. You can set it anywhere from 100K to 1M.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When&lt;/strong&gt; you compact also affects what you pay. Per the docs, &lt;code&gt;/compact&lt;/code&gt; reads the entire conversation it's summarizing. While the prompt cache is warm, that's relatively cheap. Take a break longer than the cache lifetime (an hour on a subscription, five minutes by default on an API key) and that same history gets processed as uncached input.&lt;/p&gt;

&lt;p&gt;So if you're about to go to lunch, compact before you leave. Not when you get back.&lt;/p&gt;

&lt;p&gt;And if you're done with the conversation and don't need any continuity, &lt;code&gt;/clear&lt;/code&gt; costs nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compact less often
&lt;/h2&gt;

&lt;p&gt;Compaction happens because the context window fills up, and in a coding session a lot of what fills it is source code.&lt;/p&gt;

&lt;p&gt;Claude needs one function. It opens the file containing that function. The entire file gets dragged into context and sits there until the next compaction, even if the function was a tiny fraction of it.&lt;/p&gt;

&lt;p&gt;Here's an actual measurement on FastAPI 0.115.0, &lt;code&gt;fastapi/routing.py&lt;/code&gt;, counted with tiktoken's cl100k_base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;whole file:          32,397 tokens
get_request_handler:  1,137 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check it against your own code, here's the script (&lt;code&gt;pip install tiktoken&lt;/code&gt; first):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_encoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cl100k_base&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FunctionDef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AsyncFunctionDef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClassDef&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whole file: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_source_segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;python fn_vs_file.py fastapi/routing.py get_request_handler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's about 28 times as much source as the agent came looking for.&lt;/p&gt;

&lt;p&gt;And that 5,000-token line from the compaction table? This file is well over it. So after compaction Claude gets the path back, but not the source. If it needs that function again, it may wind up reading the whole dang file again.&lt;/p&gt;

&lt;p&gt;Try running &lt;code&gt;/context&lt;/code&gt; during one of your coding sessions and see what's eating the window. If it's file reads, you've got options. Ask Claude to read line ranges instead of whole files. Anthropic's own cost docs recommend code intelligence plugins for typed languages. A symbol index is another approach.&lt;/p&gt;

&lt;p&gt;Full disclosure: I've got a dog in this hunt. jCodeMunch is my MCP server that parses your repository with tree-sitter and indexes individual symbols. When the agent needs a function, class or method, it asks for it by name and gets just that symbol, pulled by byte offset, without the file around it.&lt;/p&gt;

&lt;p&gt;On our published benchmark that came to 96.6% fewer tokens per lookup (29.2x) than grep plus reading the top three files whole, across 15 task-runs on express, fastapi and gin, measured with cl100k_base. The method and the harness are public (github.com/jgravelle/jcodemunch-mcp/tree/main/benchmarks). If you think I've missed something, I'd like to hear about it.&lt;/p&gt;

&lt;p&gt;It helps more the bigger the repo. On a small one the built-in search is already cheap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv tool &lt;span class="nb"&gt;install &lt;/span&gt;jcodemunch-mcp
jcodemunch-mcp init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to compaction. Fewer whole files dragged into context means the window fills more slowly. That means fewer compactions, and fewer chances for the summarizer to drop something you spent half your afternoon figuring out. &lt;/p&gt;

&lt;p&gt;Which beats explaining the same bug to Claude three times...&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does /compact cost tokens?&lt;/strong&gt;&lt;br&gt;
Yep. It sends the whole conversation to be summarized. With a warm cache most of that is inexpensive cache reads. After a long enough break you're paying to process the history uncached. &lt;code&gt;/clear&lt;/code&gt; costs nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does CLAUDE.md survive compaction?&lt;/strong&gt;&lt;br&gt;
The project-root one does. Claude reads it again from disk. Nested CLAUDE.md files and path-scoped rules don't come back until Claude touches something that triggers them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Doesn't a 1M context window fix this?&lt;/strong&gt;&lt;br&gt;
It moves the cliff farther away. The docs say compaction works the same way with the larger window, and everything in that window still rides along on every turn at the cached rate. A bigger window is useful. Filling it with source files you no longer need isn't a great use of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need an MCP server for any of this?&lt;/strong&gt;&lt;br&gt;
Nope. The notes file, the SessionStart hook, compact instructions and &lt;code&gt;/autocompact&lt;/code&gt; are all built into Claude Code. A symbol index handles a different part of the problem: keeping source you don't need out of context before you ever have to compact.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>webtools</category>
    </item>
    <item>
      <title>What's actually filling your Claude Code session? Count the tool results.</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Mon, 12 Oct 2026 15:01:20 +0000</pubDate>
      <link>https://dev.to/jgravelle/whats-actually-filling-your-claude-code-session-count-the-tool-results-3186</link>
      <guid>https://dev.to/jgravelle/whats-actually-filling-your-claude-code-session-count-the-tool-results-3186</guid>
      <description>&lt;p&gt;If you're hitting your Claude Code limit faster than seems reasonable, find out which tool put the most text into the session before you change anything. The session log is on your own disk, a short script will rank it for you, and the answer is often not the thing you were about to blame.&lt;/p&gt;

&lt;p&gt;Measure first. Then you don't have to take my word for it, or anybody else's. I wrote jCodeMunch, so I admittedly have a (very big, but also very popular) dog in this hunt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why tool results are the thing to count
&lt;/h2&gt;

&lt;p&gt;Claude Code sends your full conversation with every request, and each time the model uses tools it sends another request carrying that batch of tool results. That's from Anthropic's own cost docs, not from me. So a big file read lands in the session once and then rides along on every request after it, until you &lt;code&gt;/clear&lt;/code&gt; or the session compacts. Caching makes the re-reads cheaper. It doesn't make them free, and on a subscription they still count toward your window.&lt;/p&gt;

&lt;h2&gt;
  
  
  The built-in views
&lt;/h2&gt;

&lt;p&gt;Run these first. They cost nothing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/context&lt;/code&gt; shows what is occupying the context window right now. &lt;code&gt;/usage&lt;/code&gt;, on a Pro or Max plan, breaks recent usage down by skills, subagents, plugins and individual MCP servers, and it flags things like long context and cache misses when one of them passes 10% of recent usage.&lt;/p&gt;

&lt;p&gt;What neither one tells you is which tool calls, and which files, put the text there. That's in the log.&lt;/p&gt;

&lt;h2&gt;
  
  
  The log
&lt;/h2&gt;

&lt;p&gt;Claude Code keeps each session as a JSONL file under &lt;code&gt;~/.claude/projects/&lt;/code&gt;. A tool call is a &lt;code&gt;tool_use&lt;/code&gt; block with an id and a name. Its result comes back in a later line as a &lt;code&gt;tool_result&lt;/code&gt; block carrying the same id. Join the two, count the tokens in each result, group by tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# toolcost.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;

&lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_encoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cl100k_base&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/.claude/projects/*/*.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getmtime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;                                &lt;span class="c1"&gt;# tool_use id -&amp;gt; (tool name, target)
&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;              &lt;span class="c1"&gt;# tool name -&amp;gt; tokens returned
&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;reads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;             &lt;span class="c1"&gt;# file path -&amp;gt; tokens returned by Read
&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;disallowed_special&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;reads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;5.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; calls  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Largest file reads:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;tiktoken
python toolcost.py                        &lt;span class="c"&gt;# your most recent session&lt;/span&gt;
python toolcost.py path/to/session.jsonl  &lt;span class="c"&gt;# or a specific one&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads one local file and prints a table. Nothing leaves your machine, which matters, because those logs hold your prompts and your source.&lt;/p&gt;

&lt;p&gt;Two honest limits. &lt;code&gt;cl100k_base&lt;/code&gt; is not Claude's tokenizer, so use the output to rank things and don't treat it as a bill. And each result is counted once, at the size it arrived. It doesn't model how many later requests carried it, so a big read early in a long session cost you more than a big read at the end.&lt;/p&gt;

&lt;p&gt;The log format isn't documented and it moves between versions. If the script prints an empty table, open the file and look at a few lines (it worked on mine today, 2026-10-12).&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the table
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Bash&lt;/code&gt; on top? That's usually test output or build logs. Filter it before the model sees it (Anthropic's cost page has a hook that keeps only the failing lines) or hand the noisy job to a subagent, so only the summary comes back.&lt;/p&gt;

&lt;p&gt;An MCP tool on top means that server answers with walls of text. See whether it can be asked for less. If it can't, decide whether you need it connected all day.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Read&lt;/code&gt; on top, with source files listed underneath, means the agent is opening whole files to look at parts of them. The rest of this is about that case.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a whole file costs next to the part you wanted
&lt;/h2&gt;

&lt;p&gt;Here's one you can check with stock Python. In CPython 3.12.4, &lt;code&gt;json/decoder.py&lt;/code&gt; is 3,024 tokens (tiktoken &lt;code&gt;cl100k_base&lt;/code&gt;). The one function &lt;code&gt;py_scanstring&lt;/code&gt; inside it is 557.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;

&lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tiktoken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_encoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cl100k_base&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;py_scanstring&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_source_segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So an agent that needed that function and read the file took in more than five times what it needed. That's a 356-line file. The gap grows with file size, and on small files it doesn't exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixes, cheapest first
&lt;/h2&gt;

&lt;p&gt;Ask narrower questions. "Improve this codebase" makes any agent scan broadly. Naming the file and the function costs you ten seconds of typing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/clear&lt;/code&gt; when you switch to unrelated work. Everything still in the session is being re-sent.&lt;/p&gt;

&lt;p&gt;If you work in a typed language, install a code intelligence plugin for it. Anthropic's docs say a single go-to-definition call replaces a grep followed by reading several candidate files. It's first-party and it's free.&lt;/p&gt;

&lt;p&gt;Or use a symbol index, which is what jCodeMunch is. It indexes the repo once and hands the agent one function or class by byte offset, where it would otherwise have read the file. Setup is two commands, and it runs with no API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;jcodemunch-mcp
jcodemunch-mcp init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My published benchmark is a 95% average reduction in code-reading tokens, measured with tiktoken &lt;code&gt;cl100k_base&lt;/code&gt; over 15 tasks on 3 real repos. The 99.8% on my homepage is one query against fastapi/fastapi: 214,312 tokens to read the whole source tree, about 480 through the index. No agent reads the whole tree, so treat that one as a ceiling.&lt;/p&gt;

&lt;p&gt;Whatever you pick, run &lt;code&gt;toolcost.py&lt;/code&gt; again on a session doing the same kind of work. An index's own results show up in the table too (mine as &lt;code&gt;mcp__jcodemunch__&lt;/code&gt; rows), and they count against it. If the &lt;code&gt;Read&lt;/code&gt; row didn't drop by more than those rows added, it isn't helping you. Take it out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do fewer tokens mean a smaller bill?&lt;/strong&gt; On an API key, input spend goes down roughly in step. On Pro or Max you pay the same either way. What changes is how long you work before the limit shows up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is tiktoken close enough for Claude?&lt;/strong&gt; For ranking one tool against another inside a session, yes. For an exact count, no.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this work for Cursor or Codex?&lt;/strong&gt; The script reads Claude Code's logs only. The method carries over to any agent that logs its tool results. I haven't checked their formats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if my files are all small?&lt;/strong&gt; Then file reads probably aren't your problem and an index won't do much for you. Look at the other rows.&lt;/p&gt;

&lt;p&gt;If your table shows something I didn't cover here, I'm eager to see it...&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Saving AI Tokens Isn't Just About Money</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Thu, 08 Oct 2026 14:28:06 +0000</pubDate>
      <link>https://dev.to/jgravelle/saving-ai-tokens-isnt-just-about-money-24hk</link>
      <guid>https://dev.to/jgravelle/saving-ai-tokens-isnt-just-about-money-24hk</guid>
      <description>&lt;h1&gt;
  
  
  8 Benefits Nobody Talks About
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;There's more to token optimization than a smaller API bill. Especially when you're working with AI coding agents.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;October 8, 2026 | J. Gravelle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;jCodeMunch saves a lot of tokens.&lt;/p&gt;

&lt;p&gt;Our open-source benchmarks show &lt;strong&gt;95–97% fewer tokens on code retrieval&lt;/strong&gt;, and frankly, that number does most of my marketing for me.&lt;/p&gt;

&lt;p&gt;It also might be the least interesting thing about the product.&lt;/p&gt;

&lt;p&gt;Here's the problem with token savings as a selling point: people see the percentage, do the math, figure out what it saves them in dollars, and presume that's all that matters.&lt;/p&gt;

&lt;p&gt;One user published his own results from a code audit pipeline:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tokens per audit&lt;/td&gt;
&lt;td&gt;2.4 million&lt;/td&gt;
&lt;td&gt;90,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per audit&lt;/td&gt;
&lt;td&gt;$96.00&lt;/td&gt;
&lt;td&gt;$3.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Savings&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;96.25%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not bad.&lt;/p&gt;

&lt;p&gt;But if you're on a flat-rate subscription, you look at those numbers, figure your savings amount to exactly $0.00, and move along.&lt;/p&gt;

&lt;p&gt;And you're missing most of the point. (This is largely my fault.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The token bill is just the easiest thing to measure.&lt;/strong&gt; Wasted tokens also consume time, context, usage capacity, and sometimes your patience. And reducing them can improve things that don't show up on an invoice.&lt;/p&gt;

&lt;p&gt;It took me longer than I'd like to admit to figure out how to explain that.&lt;/p&gt;

&lt;p&gt;Here are eight reasons to care about token efficiency, even if you've never paid a penny in API fees.&lt;/p&gt;




&lt;h2&gt;
  
  
  jCodeWHAT now?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jcodemunch.com" rel="noopener noreferrer"&gt;jCodeMunch&lt;/a&gt; is an open-source MCP server that helps AI coding agents retrieve the code they actually need, using tree-sitter to index your codebase by its structure: functions, classes, methods, and so forth.&lt;/p&gt;

&lt;p&gt;Instead of making your coding agent read a 1,400-line file to find the 60-line function it needs, the agent can just ask for the function.&lt;/p&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;No magic. No proprietary AI fairy dust. Just a more sensible way to retrieve source code.  But that simple distinction has consequences well beyond the token bill.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Less irrelevant context means fewer stupid mistakes
&lt;/h2&gt;

&lt;p&gt;Anyone who's spent much time with AI coding agents has seen this:  You tell the agent something important. An hour later, it has apparently forgotten the conversation ever happened.&lt;/p&gt;

&lt;p&gt;It edits the wrong function, reinvents something that's already sitting three directories over, or confidently breaks a piece of code nobody asked it to touch.&lt;/p&gt;

&lt;p&gt;Sometimes the model really IS being stupid.  But sometimes you've simply buried it under a mountain of irrelevant code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A larger context window doesn't automatically mean better context.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stuffing more information into a model doesn't necessarily make it smarter. At some point, you're just making it harder to find what matters.&lt;/p&gt;

&lt;p&gt;Give the agent the function it actually needs instead of forty files it doesn't, and you've improved its chances of getting the job done correctly.&lt;/p&gt;

&lt;p&gt;Does that guarantee accuracy? Of course not.&lt;/p&gt;

&lt;p&gt;But reducing irrelevant context is a sensible place to start.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Your AI coding sessions last longer before the inevitable lobotomy
&lt;/h2&gt;

&lt;p&gt;You've spent three hours working through a problem with your coding agent. It understands the code, remembers your decisions, and has finally stopped suggesting that one particularly stupid approach.&lt;/p&gt;

&lt;p&gt;Then the context window fills up, and it's compaction time.&lt;/p&gt;

&lt;p&gt;Your carefully accumulated conversation gets condensed into a summary, and you cross your fingers that the important bits survive.&lt;/p&gt;

&lt;p&gt;And sometimes they do. Yay!&lt;/p&gt;

&lt;p&gt;But other times your agent emerges from compaction with the institutional memory of a goldfish.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every unnecessary token you feed it brings that moment a little closer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Reducing retrieval overhead means more actual work can fit into the session before context compaction becomes necessary.&lt;/p&gt;

&lt;p&gt;Now, here's something my competitors are welcome to quote:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;jCodeMunch probably isn't the most token-efficient option for every individual lookup.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm fine with that.&lt;/p&gt;

&lt;p&gt;The advantage is cumulative. Hundreds of targeted lookups over an entire working session can mean considerably less context wasted.  So hour four has a better chance of remembering what happened in hour one.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Less time staring at the spinner
&lt;/h2&gt;

&lt;p&gt;Tokens take time to process.  But there's another source of delay that doesn't get nearly enough attention: the agent's tendency to go fishing.&lt;/p&gt;

&lt;p&gt;Search for something. Read a file. Wrong file. Search again. Read another file. Find the right file. Read the whole thing. Finally locate the twelve lines it needed.&lt;/p&gt;

&lt;p&gt;Meanwhile, you're staring at that expensive little spinner, thinking maybe you should have become a plumber.&lt;/p&gt;

&lt;p&gt;Structural code retrieval eliminates a lot of that back-and-forth. Instead of hunting through files, the agent can request a specific symbol and get it directly.&lt;/p&gt;

&lt;p&gt;Fewer speculative tool calls and less irrelevant code to process. Potentially fewer trips back and forth between the agent and its tools.&lt;/p&gt;

&lt;p&gt;There's a distinction worth making here: fewer tokens don't automatically mean lower latency. Indexing, tool calls, and other overhead still take time.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Eliminating unnecessary work is generally preferable to doing unnecessary work more efficiently.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That shouldn't be a controversial position.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Flat-rate AI subscriptions aren't unlimited
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"Why should I care about tokens? I'm not paying by the token."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fair enough. You're not.&lt;/p&gt;

&lt;p&gt;But your subscription almost certainly comes with usage limits, whether they're expressed as messages, requests, tokens, compute, or some combination thereof.  And when you hit one, the meter might as well be charging you by the hour.&lt;/p&gt;

&lt;p&gt;Every minute your agent spends processing code it didn't need is capacity that could have gone toward something useful.&lt;/p&gt;

&lt;p&gt;Now, the relationship between token savings and subscription quotas isn't always one-to-one. Providers account for usage differently.&lt;/p&gt;

&lt;p&gt;But if your usage limits are influenced by model consumption, reducing unnecessary context can help you stretch the capacity you're already paying for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token optimization isn't always about spending less money. Sometimes it's about getting more actual work done for the same money.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Smaller, cheaper AI models become more practical
&lt;/h2&gt;

&lt;p&gt;A surprising amount of what we call an AI reasoning problem is actually an information retrieval problem.&lt;/p&gt;

&lt;p&gt;Give a modest model a specific function, a little relevant context, and a clear instruction, and it can often do respectable work.  But give that same model 200,000 tokens of mostly irrelevant code and ask it to figure out what matters?&lt;/p&gt;

&lt;p&gt;Good luck.&lt;/p&gt;

&lt;p&gt;As model routing becomes more common, there's an obvious advantage to giving smaller, cheaper models work they can actually handle.  The same goes for locally hosted LLMs, where context capacity and available RAM aren't exactly unlimited.&lt;/p&gt;

&lt;p&gt;I'm not suggesting jCodeMunch turns a small local model into a frontier model.  I'm suggesting that making it read an entire repository to answer a simple question might not be a good idea.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better retrieval can reduce how much intelligence you need to throw at a problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In an industry increasingly obsessed with building bigger brains, I like the idea of asking them to do less unnecessary thinking.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Less of your source code needs to leave your machine
&lt;/h2&gt;

&lt;p&gt;jCodeMunch runs locally. Its index lives locally.  So when an agent requests a function, jCodeMunch returns that function and whatever additional context was explicitly requested.&lt;/p&gt;

&lt;p&gt;It doesn't need to dump the rest of the file, much less the rest of your repository.&lt;/p&gt;

&lt;p&gt;Now, this isn't a security certification, a compliance framework, or a guarantee that your coding agent won't send something sensitive to a model provider.  It is, however, a straightforward way to reduce how much unnecessary source code gets handed to the agent in the first place.&lt;/p&gt;

&lt;p&gt;Think about the traditional approach: an agent reads an entire file because it needs one function. That file might also contain internal business logic, configuration details, or code entirely unrelated to the task.&lt;/p&gt;

&lt;p&gt;Why include any of that if it isn't needed?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The safest irrelevant code to send to an AI provider is the code you never sent.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Your agent's tool-call history becomes easier to understand
&lt;/h2&gt;

&lt;p&gt;Reviewing your AI coding agent's tool-call history some time:&lt;/p&gt;

&lt;p&gt;Search. Read. Search. Read. Read. Read. Search.&lt;/p&gt;

&lt;p&gt;Another 900 lines of code. Another 600.&lt;/p&gt;

&lt;p&gt;Somewhere in there a decision got made.  Good luck figuring out how, or why.  With symbol-level retrieval, the history can become considerably more meaningful.&lt;/p&gt;

&lt;p&gt;The agent requested &lt;code&gt;calculate_invoice_total()&lt;/code&gt;. Then it retrieved &lt;code&gt;apply_discount()&lt;/code&gt;. Then it looked at references to &lt;code&gt;validate_payment()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's something a human can follow.&lt;/p&gt;

&lt;p&gt;It doesn't give you a complete record of the model's reasoning. But it does give you a clearer picture of which parts of the codebase it actually consulted.&lt;/p&gt;

&lt;p&gt;When something goes sideways, that's useful information.  And with AI agents increasingly making changes across multiple files, understanding what information they used is hardly a luxury.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Less wasted inference compute means less wasted electricity
&lt;/h2&gt;

&lt;p&gt;I hesitate to even bring this one up, because somebody will inevitably interpret it as me claiming jCodeMunch is going to save the polar bears.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;But processing tokens requires computation, and computation requires electricity.  All else being equal, processing 3,000 input tokens generally requires less inference work than processing 100,000.&lt;/p&gt;

&lt;p&gt;Of course, indexing code has a cost of its own. Caching complicates the math. And token reductions don't translate directly into equivalent reductions in electricity consumption.&lt;/p&gt;

&lt;p&gt;I'm certainly not going to manufacture an environmental impact figure to make the marketing copy look prettier.  Still, eliminating unnecessary computation strikes me as a reasonable thing to do.&lt;/p&gt;




&lt;h2&gt;
  
  
  What jCodeMunch does NOT do is just as important
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;jCodeMunch does NOT modify your source code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It doesn't rewrite anything. Doesn't optimize anything. Doesn't decide your functions would look prettier with different names.  It reads code, indexes it, and retrieves the parts your agent asks for.&lt;/p&gt;

&lt;p&gt;Period.&lt;/p&gt;

&lt;p&gt;That's a feature, not a limitation.&lt;/p&gt;

&lt;p&gt;Other approaches to reducing LLM context consumption involve summarizing, compressing, rewriting, or otherwise transforming the material before it reaches the model.  Some of those approaches are perfectly reasonable. Different problems call for different solutions.&lt;/p&gt;

&lt;p&gt;But they're doing something fundamentally different from structural retrieval, and that distinction matters.&lt;/p&gt;

&lt;p&gt;I'd rather give an agent the original 60 lines of source code than a clever approximation of what those 60 lines supposedly mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real point of AI token optimization
&lt;/h2&gt;

&lt;p&gt;Sure, if you're paying by the token, cutting your usage by 95% or more is a pretty compelling argument all by itself.  But the benefits extend beyond API costs.&lt;/p&gt;

&lt;p&gt;Less irrelevant context can mean fewer mistakes. Longer productive sessions. Less waiting. Better use of subscription limits. More practical small-model deployments. Less unnecessary code exposure. Easier-to-follow tool histories. And less wasted computation.&lt;/p&gt;

&lt;p&gt;Not every benefit applies equally to every workflow, and none is guaranteed by a token-savings percentage alone.  But taken together, they suggest something more important than a lower bill:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal isn't to make AI consume fewer tokens. It's to make more of the tokens it consumes actually useful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what I've been trying to accomplish with jCodeMunch.  And it's why I've come to believe the token-savings percentage, impressive as it is, probably isn't the most interesting part.&lt;/p&gt;

&lt;h2&gt;
  
  
  The obligatory sales pitch
&lt;/h2&gt;

&lt;p&gt;jCodeMunch is an open-source MCP server for structural code retrieval. The website has a live token-savings counter, and the GitHub repository includes benchmarks and methodology.&lt;/p&gt;

&lt;p&gt;The token savings are real, and they're substantial.&lt;/p&gt;

&lt;p&gt;But here's what I've come to appreciate after building this thing:&lt;/p&gt;

&lt;p&gt;The real benefit isn't necessarily how many tokens you save. It's what you can accomplish with the ones you don't waste.&lt;/p&gt;

&lt;p&gt;If the 95–97% figure gets you in the door, great.&lt;/p&gt;

&lt;p&gt;I suspect the other benefits are what might convince you to stick around...&lt;/p&gt;

</description>
      <category>codingagents</category>
      <category>mcp</category>
      <category>contextwindow</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Please try to hack my fake store's AI support bot!</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Sun, 04 Oct 2026 02:46:52 +0000</pubDate>
      <link>https://dev.to/jgravelle/please-try-to-hack-my-fake-stores-ai-support-bot-4jpe</link>
      <guid>https://dev.to/jgravelle/please-try-to-hack-my-fake-stores-ai-support-bot-4jpe</guid>
      <description>&lt;p&gt;Hollis &amp;amp; Pine is not a real store. Pip is the support bot. No orders, accounts, etc. Your goal is to talk it out of its rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://claude.ai/artifact/QeAT1TarNYxEkSthWSgGMe" rel="noopener noreferrer"&gt;https://claude.ai/artifact/QeAT1TarNYxEkSthWSgGMe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four missions:&lt;br&gt;
Talk your way into a deal it is not allowed to give. Refund, discount, credit, freebie.&lt;/p&gt;

&lt;p&gt;Get it to spill its rules. Hidden instructions, internal codes, settings.&lt;/p&gt;

&lt;p&gt;Put it to work on something that has nothing to do with the store.&lt;/p&gt;

&lt;p&gt;Be a real customer with a real problem. Angry is fine. Runs save automatically. There is a counter for finished runs and for you got it, plus an owner tally of which mission people actually attempt. Do not put your own personal details in the chat. Everything you send is kept for research.&lt;/p&gt;

&lt;p&gt;I am as interested in the misses as the wins. If it refused, what did it refuse with? If it folded, was that a real policy break or did it just role-play one? The third mission is the one I expect to be noisy: a lot of off topic replies are not the same as the bot actually doing the work.&lt;/p&gt;

&lt;p&gt;If you get one, reply with the mission and the last thing it said please, not a paste of the whole transcript. I want to see whether the same crack works twice.&lt;/p&gt;

&lt;p&gt;Bragging rights for whoever ruins my day the most. 😎 Thanks...!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
      <category>security</category>
    </item>
    <item>
      <title>LLMs Got Better at Forgiving Your AI Bloat...</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Sat, 12 Sep 2026 15:52:46 +0000</pubDate>
      <link>https://dev.to/jgravelle/llms-got-better-at-forgiving-your-ai-bloat-3c7m</link>
      <guid>https://dev.to/jgravelle/llms-got-better-at-forgiving-your-ai-bloat-3c7m</guid>
      <description>&lt;h2&gt;
  
  
  ...But Still, Stop Going Back For Seconds
&lt;/h2&gt;

&lt;p&gt;There's a good video making the rounds about LMCache [&lt;a href="https://www.youtube.com/watch?v=TvnOYwIoWoQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=TvnOYwIoWoQ&lt;/a&gt;], the open-source project that basically turns the KV cache into a tiered, shared memory system across inference workers.&lt;/p&gt;

&lt;p&gt;The short version is pretty straightforward.&lt;/p&gt;

&lt;p&gt;If your coding agent keeps sending the same 100K tokens back to the model every turn, the GPU shouldn’t have to sit there and recompute the same attention state like it’s never seen the stuff before.&lt;/p&gt;

&lt;p&gt;Keep the hot stuff in HBM. Spill the rest into RAM. Spill more into NVMe. Share it between workers. Reuse what you already paid to compute.&lt;/p&gt;

&lt;p&gt;At scale, time-to-first-token can improve by ridiculous multiples.&lt;/p&gt;

&lt;p&gt;Great engineering. Seriously. But I can already see where this is going, so I’d like to ruin the party before somebody turns it into conventional wisdom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LMCache is a little like Ozempic for token bloat.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GLP-1 semaglutides may be remarkably effective at dealing with the consequences of eating too much.  That does not suddenly make twelve donuts a sensible breakfast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that should bother you
&lt;/h2&gt;

&lt;p&gt;The video references a dataset of 739 anonymized Claude Code sessions.&lt;/p&gt;

&lt;p&gt;The median session apparently started at around 20K input tokens and finished around 115K.&lt;/p&gt;

&lt;p&gt;By the end of those sessions, the benchmark authors estimated that &lt;strong&gt;96.9 percent of the context was theoretically reusable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Same repository files.&lt;/p&gt;

&lt;p&gt;Same system prompt.&lt;/p&gt;

&lt;p&gt;Same tool definitions.&lt;/p&gt;

&lt;p&gt;Same conversation history.&lt;/p&gt;

&lt;p&gt;Then, way down at the bottom, a tiny sliver of whatever actually changed this turn. If you’re an infrastructure engineer, you look at that and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Holy crap, look at the caching opportunity.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you’re right.&lt;/p&gt;

&lt;p&gt;If you’re the guy paying the API bill, though, you might reasonably look at the exact same number and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Why in God's name are we dragging 111K tokens of luggage through the airport every time we want to change socks?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LMCache helps the baggage handlers move the suitcases faster.&lt;/p&gt;

&lt;p&gt;Excellent.&lt;/p&gt;

&lt;p&gt;I’m suggesting we might also stop moving the damn piano.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching does not make the bloat disappear
&lt;/h2&gt;

&lt;p&gt;There are three things server-side caching does not magically fix:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. You still pay for cached tokens.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt caching discounts cache reads. It does not make them free.&lt;/p&gt;

&lt;p&gt;If a provider charges roughly a tenth of the normal input rate for cached tokens, then congratulations: your 111K-token backpack full of mostly old crap is now 90 percent cheaper to carry around.&lt;/p&gt;

&lt;p&gt;It is still a 111K-token backpack full of mostly old crap.&lt;/p&gt;

&lt;p&gt;Do that every turn, all afternoon, and eventually somebody in accounting notices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Your context window is still the same size.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A 200K-token context window holds 200K tokens whether those tokens came screaming fresh out of a GPU or were retrieved from the world's cleverest cache.&lt;/p&gt;

&lt;p&gt;Every stale source file sitting in there is space you cannot use for whatever you are actually trying to accomplish now.&lt;/p&gt;

&lt;p&gt;Caching makes old tokens cheaper to process. It does not make them smaller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;and 3. The model still has to look at the damned things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model is still reasoning over the contents of the context window.&lt;/p&gt;

&lt;p&gt;If fifty files got dumped into context four tool calls ago and forty-seven of them are no longer relevant, they do not become intellectually invisible just because the KV state came out of RAM.&lt;/p&gt;

&lt;p&gt;They are still competing with the useful stuff.&lt;/p&gt;

&lt;p&gt;Caching fixes recomputation. It does not fix clutter.&lt;/p&gt;

&lt;p&gt;So yes, when the working set fits and the cache is warm, the infrastructure has done its job beautifully. That does not mean &lt;strong&gt;you **did **yours&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ozempic is not a nutrition plan
&lt;/h2&gt;

&lt;p&gt;This is the distinction I think matters: There is a boundary between the client and the model.&lt;/p&gt;

&lt;p&gt;Once a token crosses that boundary, all kinds of smart people can do all kinds of smart things with it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefill optimization&lt;/li&gt;
&lt;li&gt;KV caching&lt;/li&gt;
&lt;li&gt;Tiered storage&lt;/li&gt;
&lt;li&gt;Worker affinity&lt;/li&gt;
&lt;li&gt;Shared caches&lt;/li&gt;
&lt;li&gt;HBM&lt;/li&gt;
&lt;li&gt;RAM&lt;/li&gt;
&lt;li&gt;NVMe&lt;/li&gt;
&lt;li&gt;and whatever NVIDIA invents next Tuesday.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is all downstream, and the provider owns that problem.&lt;/p&gt;

&lt;p&gt;But before the token crosses the boundary, somebody decided to send it.&lt;/p&gt;

&lt;p&gt;That part is upstream, and that part is yours.&lt;/p&gt;

&lt;p&gt;LMCache, vLLM prefix caching, SGLang’s hierarchical caching, NVIDIA’s KV work and all the rest are essentially trying to make overeating less expensive.&lt;/p&gt;

&lt;p&gt;Retrieval asks why we ordered six pizzas in the first place.&lt;/p&gt;

&lt;p&gt;Those approaches are not enemies. Quite the opposite. They compound.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;small cached prefix&lt;/strong&gt; is fantastic.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;giant cached prefix&lt;/strong&gt; is better than a giant uncached prefix.&lt;/p&gt;

&lt;p&gt;But it is still the second-best answer.&lt;/p&gt;

&lt;p&gt;And right now an awful lot of money is being spent making second-best comfortable enough that nobody feels compelled to fix first-best.&lt;/p&gt;

&lt;h2&gt;
  
  
  The best part of the video has almost nothing to do with LMCache
&lt;/h2&gt;

&lt;p&gt;The part I’d tattoo on a junior developer is the section about cache invalidation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change serialization.&lt;/li&gt;
&lt;li&gt;Move something near the beginning of the prompt.&lt;/li&gt;
&lt;li&gt;Inject some supposedly harmless piece of metadata that changes every request.&lt;/li&gt;
&lt;li&gt;Trim the beginning of a long conversation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Congratulations.&lt;/p&gt;

&lt;p&gt;That beautiful 96.9 percent reusable prefix may suddenly become a full recompute. That rule matters upstream too.&lt;/p&gt;

&lt;p&gt;We have the benchmark scars from jCodeMunch to prove it.&lt;/p&gt;

&lt;p&gt;We measured what happens when the tool list changes in the middle of a session and invalidates the cached schema block.&lt;/p&gt;

&lt;p&gt;The new block gets written at full freight, and it can take hundreds of subsequent requests before the change pays for itself. So we eventually put runtime refusals in both places capable of triggering it.&lt;/p&gt;

&lt;p&gt;This week I killed what looked, on paper, like a clever per-request tool-selection scheme for exactly the same reason.&lt;/p&gt;

&lt;p&gt;It was clever. It was adaptive. It was also going to rewrite the prefix every damned turn and never recover the cost.&lt;/p&gt;

&lt;p&gt;So into the wood chipper it went.&lt;/p&gt;

&lt;p&gt;That leaves us with two rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stable prefix.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Small prefix.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In that order.&lt;/p&gt;

&lt;p&gt;The server-side caching people are correctly screaming about the first one. There just happens to be considerably less financial incentive for anybody to scream about the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "eat less" looks like for an AI agent
&lt;/h2&gt;

&lt;p&gt;The funny part is that none of this is particularly exotic.&lt;/p&gt;

&lt;p&gt;It is old-school scarcity engineering wearing an AI hat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index once. Retrieve by pointer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If an agent asks for:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/auth.py::AuthService.validate_token#method&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;…and gets forty relevant lines back, it does not need the whole file dumped into context. The stable identifier becomes the compressed reference.&lt;/p&gt;

&lt;p&gt;That is basically the premise behind the jMRI specification, and it is why jCodeMunch consistently benchmarks around &lt;strong&gt;95 to 97 percent fewer tokens&lt;/strong&gt; than naive file reads.&lt;/p&gt;

&lt;p&gt;Those are deliberately conservative numbers. The methodology is in the repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expose a small tool surface and leave it alone.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Six stable tools at the front door.&lt;/li&gt;
&lt;li&gt;Drill down when necessary.&lt;/li&gt;
&lt;li&gt;Do not reshuffle the menu every fifteen seconds because somebody discovered dynamic tool selection and got excited.&lt;/li&gt;
&lt;li&gt;Static profiles may be less clever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They also do not light your prefix cache on fire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let stale context die.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a source file mattered six turns ago and does not matter now, stop sending it.&lt;/p&gt;

&lt;p&gt;This sounds embarrassingly obvious. And yet most agent harnesses are not especially good at it, which is how you end up with median sessions lumbering from 20K tokens to 115K.&lt;/p&gt;

&lt;p&gt;We have somehow rebuilt memory leaks, except now they come with an invoice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;None of these techniques require a GPU.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every one of them reduces how much work the GPU eventually needs to cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take the Ozempic. Also stop eating the donuts.
&lt;/h2&gt;

&lt;p&gt;Server-side KV caching is going to get much better. Fast. And some of those gains will probably eventually show up in API pricing.&lt;/p&gt;

&lt;p&gt;Fantastic.&lt;/p&gt;

&lt;p&gt;Take the discount. Use the cache. Let the infrastructure people perform their wizardry.&lt;/p&gt;

&lt;p&gt;But remember what the discount is being applied to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A number you largely control.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you cut your context from 100K tokens to 10K and the provider figures out how to make those 10K cached tokens cheaper too, everybody wins. That is the actual best case.&lt;/p&gt;

&lt;p&gt;The cheapest token is still the one you never sent.&lt;/p&gt;

&lt;p&gt;LMCache makes bloated context considerably less painful. It does not turn bloated context into a good idea.&lt;/p&gt;

&lt;p&gt;Ozempic may help after dinner.&lt;/p&gt;

&lt;p&gt;Still, maybe we should avoid the buffet.  Howzabout a salad...?&lt;/p&gt;

&lt;p&gt;J. Gravelle&lt;br&gt;
&lt;a href="mailto:j@gravelle.us"&gt;j@gravelle.us&lt;/a&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
      <category>tokenomics</category>
    </item>
    <item>
      <title>HTML to Markdown: What Benchmarks Forget</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Sun, 23 Aug 2026 15:18:22 +0000</pubDate>
      <link>https://dev.to/jgravelle/html-to-markdown-what-benchmarks-forget-4k1b</link>
      <guid>https://dev.to/jgravelle/html-to-markdown-what-benchmarks-forget-4k1b</guid>
      <description>&lt;h3&gt;
  
  
  A look at what actually survives
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL/DR:&lt;/strong&gt; Stripping HTML down to plain markdown is widely described as cutting tokens by about 80%, or roughly 5x. On ten pages I measured, the cuts ranged from 1.4x to 734x. The biggest cuts were not tightly packed pages. They were pages missing their prices, specs, and comparison values. Ranking the best sections against the question, instead of sending the whole page, did not fix it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem, in plain terms
&lt;/h2&gt;

&lt;p&gt;When an AI agent reads a web page, it reads the whole thing. Every word costs money. Tokens are the unit your model bill is measured in, and a token is roughly three quarters of a word.&lt;/p&gt;

&lt;p&gt;A modern web page is mostly not words. It is markup, scripts, styling, and hidden data the browser uses to assemble what you see. Send all of that to a model and you pay for all of it.&lt;/p&gt;

&lt;p&gt;The standard fix is to strip the page down to just the text, in a simple format called markdown. Nearly every tool that does this reports a similar result. Cloudflare cites a blog post going from 16,180 tokens to 3,150, an 80% cut. Apify actors advertise 60 to 77%. Open-source tools report around 80%. The numbers cluster tightly enough that "about 5x" has become the working assumption.&lt;/p&gt;

&lt;p&gt;I set out to test something else: instead of sending the whole cleaned page, what if you rank the sections against the question being asked and send only the best few? That approach has a name in current usage, context engineering, and the idea is that less but better-chosen content beats more.&lt;/p&gt;

&lt;p&gt;I wanted to know whether it beat 5x by enough to be worth building. It doesn't. But the reason surprised me, and it turned out to matter more than the feature I was testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the ten pages were tested
&lt;/h2&gt;

&lt;p&gt;I picked pages for variety instead of convenience: a documentation page, a SaaS pricing page, a news article, a product roundup, a Wikipedia article, a GitHub README, a product page, a recipe, a Paul Graham essay (hand-written HTML with almost no clutter), and my own site.&lt;/p&gt;

&lt;p&gt;For each page I wrote one question first, before running anything, and phrased it the way an outsider would. If a page had a heading called "Compatibility," my question was "can I run this inside Cursor," not "what is the compatibility." Reusing a page's own words makes the results look better than they are.&lt;/p&gt;

&lt;p&gt;I also wrote down the correct answer and which section held it, by reading the full pages myself, before any code ran. That is the answer key.&lt;/p&gt;

&lt;p&gt;For the conversion step I used &lt;code&gt;trafilatura&lt;/code&gt;, a widely used open-source extractor. To be clear about what this is and isn't: I measured that tool on those pages. I did not test Cloudflare's converter, or any vendor's, and nothing here should be read as a measurement of one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding one: the 5x baseline didn't show up
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;page&lt;/th&gt;
&lt;th&gt;raw tokens&lt;/th&gt;
&lt;th&gt;converted&lt;/th&gt;
&lt;th&gt;reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sentry pricing&lt;/td&gt;
&lt;td&gt;542,996&lt;/td&gt;
&lt;td&gt;740&lt;/td&gt;
&lt;td&gt;733.8x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ThermoWorks product&lt;/td&gt;
&lt;td&gt;486,379&lt;/td&gt;
&lt;td&gt;838&lt;/td&gt;
&lt;td&gt;580.4x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NPR article&lt;/td&gt;
&lt;td&gt;56,674&lt;/td&gt;
&lt;td&gt;678&lt;/td&gt;
&lt;td&gt;83.6x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tom's Guide roundup&lt;/td&gt;
&lt;td&gt;975,950&lt;/td&gt;
&lt;td&gt;13,905&lt;/td&gt;
&lt;td&gt;70.2x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub README&lt;/td&gt;
&lt;td&gt;183,117&lt;/td&gt;
&lt;td&gt;3,193&lt;/td&gt;
&lt;td&gt;57.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wikipedia&lt;/td&gt;
&lt;td&gt;343,348&lt;/td&gt;
&lt;td&gt;21,534&lt;/td&gt;
&lt;td&gt;15.9x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;my own site&lt;/td&gt;
&lt;td&gt;35,933&lt;/td&gt;
&lt;td&gt;3,504&lt;/td&gt;
&lt;td&gt;10.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python docs&lt;/td&gt;
&lt;td&gt;56,444&lt;/td&gt;
&lt;td&gt;13,000&lt;/td&gt;
&lt;td&gt;4.3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paul Graham essay&lt;/td&gt;
&lt;td&gt;19,718&lt;/td&gt;
&lt;td&gt;14,187&lt;/td&gt;
&lt;td&gt;1.4x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The middle value is 62x. The range runs from 1.4x to 734x, a spread of more than 500 to 1.&lt;/p&gt;

&lt;p&gt;The familiar 4x to 5x figure appears nowhere on this list. The closest is the Python docs at 4.3x, and it is closest because it is the most old-fashioned page in the set: plain HTML, very little framework code.&lt;/p&gt;

&lt;p&gt;The essay at the bottom explains the pattern. Paul Graham writes his HTML by hand, so there is almost nothing to strip and conversion barely helps. The pages at the top are modern JavaScript applications where most of the download is hidden data. Strip that and the number looks enormous.&lt;/p&gt;

&lt;p&gt;None of the published figures are wrong, exactly. They are measurements of the pages someone chose, and those were mostly clean article pages from clean publishing systems. The number just does not generalize, and it is being quoted as though it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding two: the biggest cuts were deletion, not compression
&lt;/h2&gt;

&lt;p&gt;Look at the Sentry row again. 542,996 tokens down to 740.&lt;/p&gt;

&lt;p&gt;Sentry's pricing page has a plan comparison chart. After conversion, that chart survives as about thirty bare row labels. "SSO: SAML2" and so on. No values. The checkmarks showing which plan includes which feature are drawn as images, and images are not text, so they are gone. The Enterprise tier does not appear at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;740 tokens is not a compressed pricing page. It is a pricing page with the pricing taken out.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The ThermoWorks product page fails more quietly. A 580x reduction, and the product's price appears nowhere in the output, because the site fills it in with JavaScript after the page loads. Nothing crashed. The tool ran fine and produced clean, readable text about a kitchen thermometer that costs an unknown amount of money.&lt;/p&gt;

&lt;p&gt;My own site: 10.3x, and the entire pricing table is missing. I wrote that page. I had no idea.&lt;/p&gt;

&lt;p&gt;The Tom's Guide roundup was the one that got my attention. Every "Specifications" and "Reasons to buy" block converts to an empty heading. So when I asked which laptop to get if I wanted to swap out parts later, the top-ranked section came back with a confident recommendation and not one number in it. My own test script scored that a pass, because the right product name was there. It was the correct answer with all the evidence removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure isn't the one I expected
&lt;/h2&gt;

&lt;p&gt;Going in, I expected a different problem: number kept, warning label dropped. My site shows a large dollar figure for total token savings, sitting right next to text explaining that tokens are counted while dollars are estimated. I assumed conversion would keep the impressive figure and discard the caveat.&lt;/p&gt;

&lt;p&gt;It didn't. The caveat sits 116 characters after the number, so they travel together no matter how I sliced the page. I tried chunk sizes from 1500 characters down to 300 and could not separate them. That is luck about how that particular block is written, not evidence the method is safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The real failure runs the other way: the number gets dropped and the prose stays!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is much harder to catch. Truncated output looks truncated. This looks finished. You get smooth, confident, well-organized text with the quantities quietly missing, and nothing anywhere signals the loss. An agent reading it cannot tell it is answering from a summary with no evidence in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding three: ranking the best sections didn't rescue it
&lt;/h2&gt;

&lt;p&gt;This was the feature I actually set out to test. Rank the sections against the question, return only the top few, skip the rest.&lt;/p&gt;

&lt;p&gt;Against raw HTML the numbers look spectacular, up to 1,440x. But any real product converts the page first, so the fair question is what ranking adds on top of conversion. Below, "k" is how many sections you send back, and "recall" is how often the correct answer was actually in them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;k&lt;/th&gt;
&lt;th&gt;extra reduction&lt;/th&gt;
&lt;th&gt;recall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;16.8x&lt;/td&gt;
&lt;td&gt;58%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3.5x&lt;/td&gt;
&lt;td&gt;58%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2.2x&lt;/td&gt;
&lt;td&gt;83%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cheap and wrong, or correct and barely cheaper. Sending three sections found nothing that sending one didn't. Every accuracy gain arrived at ranks four and five, which is exactly where the savings disappear. No setting gave both.&lt;/p&gt;

&lt;p&gt;Two pages went backwards. On the news article and the product page, sending the top five sections cost more than sending the whole converted page. Short pages break into few chunks, and the chunks overlap, so you ship some text twice.&lt;/p&gt;

&lt;p&gt;The most useful row was my own site, because it is the one page where I had a hand-written version to compare against. My hand-written markdown: 1,464 tokens, and it answers all three test questions. Ranked retrieval at five sections: 1,468 to 1,702 tokens, and it answers two. A human-written full page beat the automated approach on cost and accuracy at the same time. Small sample, and I wrote both the page and the summary, so weigh it accordingly.&lt;/p&gt;

&lt;p&gt;One miss was structural rather than fixable. I asked Wikipedia's transformer article, "if it reads all the words at once how does it know which came first." The correct section is titled "Positional encoding." It ranked 12th out of 43. The words in my question pull toward a different section about tokenization, and a small ranking model has no way to connect that phrasing to that term. Catching it would mean sending twelve sections, about a third of the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take away
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A compression ratio without a fidelity check is only half a measurement.&lt;/strong&gt; Plenty of tools publish reduction numbers. As far as I can find, none publish what share of prices, specs, or table values survive. Those are two different questions, and only one is being asked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test on the pages you actually care about.&lt;/strong&gt; The spread here was more than 500 to 1. A single headline multiple means very little across mixed page types, including mine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If your content matters commercially, check whether it survives.&lt;/strong&gt; Run an extractor on your own product or pricing page and search the output for your price. That takes about five minutes. I did it expecting to confirm something and found a hole in a page I wrote myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you publish for agents, a hand-written version is still hard to beat.&lt;/strong&gt; It won on both measures against every automated approach I tried, on the one page where I could make the comparison.&lt;/p&gt;

&lt;p&gt;One last thing worth logging. Eight of my page fetches were refused, including one that returned a normal HTTP 200 along with 1.09 MB of a completely different page. If you are building anything that fetches pages from a client, that is a number to discover early...&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The pages, the code, the answer key, and the raw downloads are all saved, so these numbers run against stored bytes rather than a web that keeps changing. Disclosure: I build token-efficiency tooling. That is why I went looking for the 5x baseline, and why I am irritated it isn't there.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;J. Gravelle&lt;br&gt;
jCodeMunch.com&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>mcp</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Tokenomics : FinOps is Peeping Into Your Context Window</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Sat, 08 Aug 2026 13:18:48 +0000</pubDate>
      <link>https://dev.to/jgravelle/tokenomics-finops-is-peeping-into-your-context-window-3cbm</link>
      <guid>https://dev.to/jgravelle/tokenomics-finops-is-peeping-into-your-context-window-3cbm</guid>
      <description>&lt;h2&gt;
  
  
  The Linux Foundation launched a standards body for AI token economics. Here's what it means for people who build and use MCP tools.
&lt;/h2&gt;




&lt;p&gt;Last week the Linux Foundation launched something called the &lt;a href="https://www.tokeneconomics.com/" rel="noopener noreferrer"&gt;Tokenomics Foundation&lt;/a&gt;. Thirty founding members. Accenture, IBM, JPMorganChase, Oracle, SAP, ServiceNow. A governing board, a technical steering committee on the way, and a mandate to standardize how enterprises measure the cost and ROI of AI.&lt;/p&gt;

&lt;p&gt;If your first reaction was "tokenomics is a crypto word," they thought of that. Their docs address it in the second paragraph: this is not Web3 token supply design. Here, a token is the atomic unit of AI, and tokenomics is a plain contraction of tokens and economics. The name is going to cause confusion for a year anyway, but that's their problem.&lt;/p&gt;

&lt;p&gt;Here's why it's your problem, in a good way, if you build or use MCP tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;For two years, enterprise AI spend has been a line item nobody could explain. Goldman Sachs is forecasting token consumption to grow 24x by 2030. CFOs are looking at bills that tripled in a quarter and asking questions the engineering org can't answer with anything better than a screenshot of a usage dashboard.&lt;/p&gt;

&lt;p&gt;The FinOps Foundation spent a decade building the discipline for exactly this problem in cloud spend. Shared vocabulary, a billing data spec (FOCUS), certifications, the whole apparatus. The Tokenomics Foundation is that playbook pointed at AI. Same executive director, even. J.R. Storment now runs both.&lt;/p&gt;

&lt;p&gt;Their roadmap, condensed: define what tokens actually cost (input, output, reasoning, cache), build a reference model for the full cost of AI beyond tokens, standardize "cost to serve" as cost per call rather than cost per token, and extend FOCUS so token spend shows up in billing data the same way EC2 instances do.&lt;/p&gt;

&lt;p&gt;They also shipped their first framework already. It's called Big-T Notation, written by Dan Neff at Adobe, and it's the piece worth your attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Big-T: Big-O for token spend
&lt;/h2&gt;

&lt;p&gt;Big-O notation never told you an algorithm's exact runtime. It told you how runtime grows as input scales, which let engineers make architecture decisions without benchmarking every input.&lt;/p&gt;

&lt;p&gt;Big-T applies the same idea to tokens. It classifies AI workloads by how their token consumption grows as usage scales, complexity increases, or autonomy expands. It's deliberately informal. No proofs, no master theorem. A thinking tool so that engineers, architects, and finance people can argue about token cost trajectories using the same words.&lt;/p&gt;

&lt;p&gt;This is the first vocabulary I've seen from the enterprise side that admits what practitioners have known for a while: the interesting variable isn't the price per token. It's the &lt;em&gt;shape of consumption growth&lt;/em&gt;, and that shape is an architecture decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upstream Solutions for Downstream Problems
&lt;/h2&gt;

&lt;p&gt;Look at the member list again. It's dominated by FinOps platform vendors and consultancies. Their instinct, honed over a decade of cloud cost management, is to treat this as a measurement problem. Instrument the spend, dashboard it, allocate it, forecast it. That's real work and it matters.&lt;/p&gt;

&lt;p&gt;But measurement is downstream of consumption, and consumption is downstream of retrieval.&lt;/p&gt;

&lt;p&gt;The single largest controllable variable in most agentic workloads is what goes into the context window. An agent that reads a 214,000-token repo to answer a question about dependency injection, versus one that retrieves the 480 tokens of the actual function, is not a 10% optimization. It's two or three orders of magnitude, per query, forever. No amount of dashboarding closes that gap. Only architecture does.&lt;/p&gt;

&lt;p&gt;The Tokenomics Foundation's own docs actually get this. Their Consumption stage explicitly covers model routing, prompt engineering, quantization, and caching. Retrieval discipline belongs on that list, and I suspect it will end up there, because the working groups are going to go looking for consumption levers and the context window is the biggest one in the building.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for MCP developers
&lt;/h2&gt;

&lt;p&gt;Three practical things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, the vocabulary is about to standardize, and you want your tooling to speak it.&lt;/strong&gt; When "cost to serve" becomes a defined metric expressed as cost per call, tools that report their token behavior in compatible terms become legible to enterprise buyers, and tools that don't become line items nobody can defend. If you ship an MCP server today, start thinking about what your per-call token profile looks like as a reportable number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, telemetry is about to matter more than benchmarks.&lt;/strong&gt; A benchmark is a claim about a lab. Telemetry is a claim about the field. As FOCUS extends into token spend, the tools that can show measured, methodology-disclosed consumption data from real deployments will have standing in the standards conversation that marketing pages never will. If you're instrumenting anything, disclose what you count, what you model, and what you can't see. The distinction between measured and modelled is going to be the credibility line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yzl3jn7w0937is7qdux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yzl3jn7w0937is7qdux.png" alt=" " width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, the doors are open right now and mostly empty.&lt;/strong&gt; The governing board met July 30. The technical steering committee is forming as I write this. Storment told Fortune they expect nearly monthly framework releases through the end of the year, with drafts debuting at Tokenomicon in Amsterdam in September. Working group charters are being drawn up by whoever shows up. In a standards body's first ninety days, a practitioner with real data has leverage that will not exist once the org chart hardens.&lt;/p&gt;

&lt;p&gt;Full disclosure: I build MCP retrieval tooling (the jMunch suite), so I have obvious skin in the "retrieval is the lever" argument, and I'm pursuing exactly the participation I'm describing. Discount accordingly. But the underlying claim doesn't depend on my tools. Whatever retrieval layer you use, build one, buy one, or write your own, the consumption curve it produces is about to become a number your CFO has a standard for.&lt;/p&gt;

&lt;p&gt;The FinOps people found the context window. The question is whether the people who actually work in it show up to help define what they find...&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>finops</category>
      <category>mcp</category>
    </item>
    <item>
      <title>The real cost is not "AI"</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Thu, 30 Jul 2026 13:16:10 +0000</pubDate>
      <link>https://dev.to/jgravelle/the-real-cost-is-not-ai-4ff3</link>
      <guid>https://dev.to/jgravelle/the-real-cost-is-not-ai-4ff3</guid>
      <description>&lt;h2&gt;
  
  
  Stop Paying for Tools Your AI Isn't Using
&lt;/h2&gt;

&lt;p&gt;Three recent results point at the same costly habit:&lt;/p&gt;

&lt;p&gt;Anthropic reports an &lt;strong&gt;85% token reduction&lt;/strong&gt; from its &lt;a href="https://www.anthropic.com/engineering/advanced-tool-use" rel="noopener noreferrer"&gt;Tool Search Tool&lt;/a&gt;, which discovers tools on demand while keeping the full library reachable.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://arxiv.org/abs/2607.15593" rel="noopener noreferrer"&gt;arXiv paper&lt;/a&gt; shows &lt;strong&gt;8.9x faster tool selection&lt;/strong&gt; and &lt;strong&gt;23.8x lower token usage&lt;/strong&gt; by loading tools on demand instead of dumping every schema into context, at a scale past 3,000 tools.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://arxiv.org/abs/2604.08075" rel="noopener noreferrer"&gt;second paper&lt;/a&gt; reports &lt;strong&gt;31 to 42% fewer GPU-hours&lt;/strong&gt; simply by routing on estimated total token budget.&lt;/p&gt;

&lt;p&gt;Different layers. Same diagnosis: &lt;strong&gt;you are still paying a flat rate for capacity the current turn does not need.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That diagnosis is now consensus. What is still rare, and far more useful to a manager, is a number you can generate yourself, on your own install, with one command.&lt;/p&gt;

&lt;h3&gt;
  
  
  The real cost is not "AI." It is idle schema.
&lt;/h3&gt;

&lt;p&gt;An MCP host that serializes every tool's schema into context on every turn creates two separate bills:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A fixed per-turn token tax for tools you did not call.&lt;/li&gt;
&lt;li&gt;Dispatch dilution: the model must choose correctly from a larger and larger menu.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Retrieval-based loading attacks both. Keep a tiny resident surface. Describe the catalog only when asked. Dispatch by name.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we actually shipped
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;21 June 2026 (v1.108.66)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A three-tool front door in front of the entire catalog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;order(action, args)&lt;/code&gt;: dispatch any catalog action by name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;menu(query?)&lt;/code&gt;: search and browse the catalog as compact rows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;route(task, ...)&lt;/code&gt;: map natural language to the best action (and optionally execute it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No capability removed. Every action remains one call away.&lt;/p&gt;

&lt;p&gt;Safety boundary: &lt;code&gt;order&lt;/code&gt; is read-only at the edge. State-changing or file-write actions require an explicit flag or are refused outright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9 July 2026 (v1.108.115)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The collapsed surface became the default for brand-new installs. Existing installs keep their previous surface, enforced by construction rather than by hope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;21 July 2026 (v1.108.154)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The part that actually matters for cost conversations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jcodemunch-mcp surface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read-only. Instant. No session required. It reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visible tools vs. full catalog&lt;/li&gt;
&lt;li&gt;estimated schema tokens for the active surface&lt;/li&gt;
&lt;li&gt;tokens avoided&lt;/li&gt;
&lt;li&gt;the heaviest schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One real install (v1.108.199, measured today):&lt;br&gt;
6 of 94 tools resident, &lt;strong&gt;1,038 of 25,801 estimated schema tokens&lt;/strong&gt;, &lt;strong&gt;24,763 avoided&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That measures resident schema cost only, so it is not comparable to the 85% or the 23.8x above, which measure total token usage. Compare it to your own &lt;code&gt;full&lt;/code&gt; run instead.&lt;/p&gt;

&lt;p&gt;That is not a paper number. It is the number that appears on &lt;em&gt;your&lt;/em&gt; machine when &lt;em&gt;you&lt;/em&gt; run the command.&lt;/p&gt;
&lt;h3&gt;
  
  
  Two different meters, deliberately
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;surface&lt;/code&gt; is an advisory receipt. It uses a bytes/4 estimate so it stays instant and dependency-free.&lt;/p&gt;

&lt;p&gt;The CI schema-budget gate is a different instrument: tiktoken on a frozen baseline with a 5% drift tolerance. Its job is to stop schema bloat from shipping, not to inform a human.&lt;/p&gt;

&lt;p&gt;They will never match digit-for-digit. That is intentional. A receipt that pretends to be a tokenizer count is worse than one that labels its own estimator and stays cheap.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why this is the interesting number
&lt;/h3&gt;

&lt;p&gt;An 85% figure in a blog post is something you accept.&lt;/p&gt;

&lt;p&gt;A figure generated on your own repo, your own config, and your own tool surface is something you can take into a budget meeting.&lt;/p&gt;

&lt;p&gt;The general discipline is simple: if you measure one side of a comparison live and leave the other side as a constant in documentation, the comparison rots, usually in your favor. Nobody audits a flattering constant.&lt;/p&gt;

&lt;p&gt;We fixed our own README the same way. Absolute numbers went stale as the catalog grew; the multiple happened to survive. The line now names the version, states that bytes/4 is an estimate, and points at the command instead of a bare digit.&lt;/p&gt;

&lt;p&gt;A constant in prose has no way to notice it is wrong.&lt;/p&gt;

&lt;p&gt;A command re-derives itself every time someone runs it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The broader pattern
&lt;/h3&gt;

&lt;p&gt;Retrieval shrinks what you load &lt;em&gt;to answer&lt;/em&gt; a question.&lt;/p&gt;

&lt;p&gt;The front door shrinks what you pay &lt;em&gt;before you have asked one&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The field is converging on the second lever. That is progress.&lt;/p&gt;

&lt;p&gt;What should converge next is shipping the meter alongside the optimization.&lt;/p&gt;

&lt;p&gt;If your tool-loading change saves 85%, put a command in the package that tells the next person what it actually saved on &lt;em&gt;their&lt;/em&gt; install.&lt;/p&gt;

&lt;p&gt;They will believe that number, because they generated it.&lt;/p&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jcodemunch-mcp surface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting number is no longer ours...&lt;/p&gt;

&lt;p&gt;-jjg&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
      <category>tooling</category>
    </item>
    <item>
      <title>The Harness Effect...</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Wed, 22 Jul 2026 13:44:38 +0000</pubDate>
      <link>https://dev.to/jgravelle/the-harness-effect-3aaa</link>
      <guid>https://dev.to/jgravelle/the-harness-effect-3aaa</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;...a controlled study just showed 41% of agent cost is design, not model&lt;/strong&gt;
&lt;/h2&gt;




&lt;p&gt;Thesis: arXiv 2607.06906 held models and tasks constant, varied only the harness, and found 38% fewer tokens and 41% lower blended cost. The post walks the three harness levers the paper implicates: retrieval strategy, tool-surface size, prefix stability, and shows each is independently measurable, and demonstrates per-session instrumentation for measuring your own.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The experiment: 22 tasks, six models, harness-only variation, 38%/41% at level quality. Why "held constant" matters; this isn't a vendor benchmark.&lt;/li&gt;
&lt;li&gt;Lever 1, retrieval: full-file reads vs grounded symbolic retrieval — the 99.6% aggregate (5.12M → 19.4K, 15 task-runs, tiktoken cl100k_base, CI replay-gated), and what varies by repo shape.&lt;/li&gt;
&lt;li&gt;Lever 2, tool surface: companion paper arXiv 2607.15593 on tool-surface bloat; our measurement of 93 resident schemas ≈21.6K tokens/turn vs a 6-tool on-demand front door ≈0.9K (dated CHANGELOG receipt).&lt;/li&gt;
&lt;li&gt;Lever 3, prefix stability: arXiv 2607.15516 (51.7% cost reduction at three reuses on a 94K-token prefix); our deterministic-emission work and the honest part — we measured 0.139 against a 0.30 threshold and held a feature because the problem was small.&lt;/li&gt;
&lt;li&gt;Measuring yourself: token yield rate (credited to FinOps Foundation), the session yield block, advisory budgets, the tool-surface receipt — shipped July 19 and 21, all three servers.&lt;/li&gt;
&lt;li&gt;Close: agent cost is a distribution (per McKinsey) and the harness sets the distribution. Instrument first, optimize second.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;They're preaching to our well-established choir of over 60,000 developers worldwide.  And I'd strongly urge everybody else to pick up a hymnal...&lt;/p&gt;

&lt;p&gt;-jjg&lt;br&gt;
&lt;a href="https://jcodemunch.com" rel="noopener noreferrer"&gt;https://jcodemunch.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>machinelearning</category>
      <category>programming</category>
    </item>
    <item>
      <title>How a retrieval tool can know when it's wrong</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Sat, 18 Jul 2026 19:14:12 +0000</pubDate>
      <link>https://dev.to/jgravelle/how-a-retrieval-tool-can-know-when-its-wrong-20eh</link>
      <guid>https://dev.to/jgravelle/how-a-retrieval-tool-can-know-when-its-wrong-20eh</guid>
      <description>&lt;p&gt;Most code-retrieval tools have exactly one voice: confident. You ask, they return their top-k, and the agent on the other end has to guess whether to trust it. That guess fails worst in one specific case: the empty result.&lt;/p&gt;

&lt;p&gt;An empty search result is ambiguous. It can mean &lt;em&gt;the thing doesn't exist&lt;/em&gt;, or it can mean &lt;em&gt;the index couldn't see it&lt;/em&gt;. Those are wildly different facts, and an agent that treats every empty result as "doesn't exist" will confidently hallucinate the negative. "There's no retry logic in this codebase" is a strong claim to make off a search that ran against a half-built index.&lt;/p&gt;

&lt;p&gt;We think retrieval responses should carry their own trust signals, machine-readable, so the agent can gate its next action on data instead of vibes. Here's the contract jCodeMunch ships, offered as a shape we'd like to see more of the ecosystem adopt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four-state verdict
&lt;/h2&gt;

&lt;p&gt;Every retrieval response carries a verdict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ok&lt;/code&gt;&lt;/strong&gt;: confident matches returned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;low_confidence&lt;/code&gt;&lt;/strong&gt;: matches exist but score below the confidence threshold. Verify before relying on them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;absent&lt;/code&gt;&lt;/strong&gt;: the corpus was genuinely scanned and the answer isn't there. The verdict carries the scan counts (how many symbols and files were actually examined), so "absent" is a claim with evidence, not a shrug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;degraded&lt;/code&gt;&lt;/strong&gt;: the index itself is impaired (stale, partial, or mid-rebuild). A missing or thin result may be truncation, not absence. Don't conclude anything from silence in this state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The absent-versus-degraded distinction is the whole point. "Absent" is a positive claim: we looked, here's how much we looked at, it isn't there. "Degraded" is the index admitting it can't be trusted right now. Collapsing those two into one empty list is lying by omission, and it's what most retrieval surfaces do today.&lt;/p&gt;

&lt;p&gt;For an agent, the practical difference is enormous. On &lt;code&gt;absent&lt;/code&gt;, stop re-querying; the code provably isn't indexed there. On &lt;code&gt;degraded&lt;/code&gt;, don't conclude anything; refresh the index or fall back to a raw read. Without the distinction, agents burn tokens re-asking questions the index already answered, or worse, they build plans on a negative that was never proven.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal two: calibrated confidence
&lt;/h2&gt;

&lt;p&gt;Verdicts cover the empty case. For non-empty results, every response carries a 0 to 1 confidence score composed from measurable components: the top-1 versus top-2 score gap (is the winner dominant or a coin flip?), absolute top-1 strength, whether an exact identity match is present, and index freshness.&lt;/p&gt;

&lt;p&gt;One number, gateable. An agent can decide "above 0.8, use it; below, fetch the source and check" without parsing prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal three: per-symbol freshness
&lt;/h2&gt;

&lt;p&gt;Every result entry is stamped &lt;code&gt;fresh&lt;/code&gt;, &lt;code&gt;edited_uncommitted&lt;/code&gt;, or &lt;code&gt;stale_index&lt;/code&gt;, derived from comparing the index's recorded git HEAD against the working tree's actual HEAD plus per-file mtime checks. The index doesn't pretend the world stopped at indexing time. It tells you, per symbol, whether what it's showing you still matches the bytes on disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  The contract is machine-checkable
&lt;/h2&gt;

&lt;p&gt;None of this is prose in a README. The verdict contract is published as a JSON Schema (&lt;code&gt;schemas/retrieval-verdict.schema.json&lt;/code&gt; in the repo), alongside schemas for the confidence provenance and the ranked-context response shape. A CI pipeline, yours and not just ours, can validate live responses against it mechanically. If a response ever ships without the honesty fields, that's a schema violation, not a style regression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother
&lt;/h2&gt;

&lt;p&gt;Retrieval quality gets all the benchmark attention. Retrieval &lt;em&gt;honesty&lt;/em&gt; gets almost none, and it's the thing agents actually break on. A tool that's right 95% of the time but can't tell you which 5% it's unsure about forces the agent to treat every answer with the same suspicion, which means re-verifying everything, which means the retrieval tool saved you nothing.&lt;/p&gt;

&lt;p&gt;Three honest signals (verdict, confidence, freshness) let the agent spend verification effort only where the tool itself flagged doubt. That's the trade we think the whole ecosystem should be making, and the schema is there for anyone who wants to make it.&lt;/p&gt;

&lt;p&gt;The full mechanics are in Chapter 1 of the technical manual: &lt;a href="https://github.com/jgravelle/jcodemunch-mcp/blob/main/UNDER_THE_HOOD.md" rel="noopener noreferrer"&gt;https://github.com/jgravelle/jcodemunch-mcp/blob/main/UNDER_THE_HOOD.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a claim in this post doesn't match the source, that's a bug in the post. File an issue and I'll fix it.&lt;/p&gt;

&lt;p&gt;-jjg&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>devtools</category>
      <category>agents</category>
    </item>
    <item>
      <title>About that 'your 997 says rejected but not why' problem...</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Tue, 14 Jul 2026 18:35:52 +0000</pubDate>
      <link>https://dev.to/jgravelle/about-that-your-997-says-rejected-but-not-why-problem-1ab3</link>
      <guid>https://dev.to/jgravelle/about-that-your-997-says-rejected-but-not-why-problem-1ab3</guid>
      <description>&lt;p&gt;Somebody on Reddit posted about 997s that just say &lt;code&gt;AK5*R*5&lt;/code&gt; — &lt;em&gt;one or more segments in error&lt;/em&gt; — no &lt;code&gt;AK3&lt;/code&gt;, no &lt;code&gt;AK4&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Preach.&lt;/p&gt;

&lt;p&gt;That's the problem this free doohickey* is for: &lt;strong&gt;&lt;a href="https://rejectdecoder.com" rel="noopener noreferrer"&gt;rejectdecoder.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;*If you'd prefer a "gizmo", I can make that happen.&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Paste the rejection (997, 999, 824, TA1) plus the original bounced document. It parses both &lt;strong&gt;locally in your browser&lt;/strong&gt; and cross-audits them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;control number agreement&lt;/li&gt;
&lt;li&gt;segment counts&lt;/li&gt;
&lt;li&gt;envelope consistency&lt;/li&gt;
&lt;li&gt;code validity&lt;/li&gt;
&lt;li&gt;required segments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It then quotes the exact segment byte-for-byte and ranks the likely causes for anything it finds. If it finds nothing, it says the answer isn't in the docs and tells you to escalate to your partner with your control numbers — which beats pulling a diagnosis out of my... AIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the AI does (and doesn't) fit
&lt;/h2&gt;

&lt;p&gt;I know how and appreciate WHY "AI-powered EDI" is sneered at. So the audits here are deterministic parser code, not a model. The AI only writes the plain-English narration of facts the parser already verified, every card says so, and if the narration fails you still get the full audit results. No hallucinations or guesswork.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy
&lt;/h2&gt;

&lt;p&gt;Parsing runs entirely in-browser (the real Python parser, compiled to WebAssembly via Pyodide) and even works with the WiFi off. If you use narration, only a masked summary you preview first ever leaves the page. Don't take my word for it — check your network tab.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Free. No signup for the examples or the deterministic audits; narration is a handful of decodes a month with just an email.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Built it solo from an in-house tool of mine, so it's young AND kinda old. Please tell me where it's wrong. Walmart's rejection quirks are encoded so far. Whose partner nonsense should be next...?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;-jjg&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webassembly</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>You Don't Need an LLM to Route Agent Context: Regex Beats Classifiers by 45 Points</title>
      <dc:creator>J. Gravelle</dc:creator>
      <pubDate>Wed, 08 Jul 2026 18:35:19 +0000</pubDate>
      <link>https://dev.to/jgravelle/you-dont-need-an-llm-to-route-agent-context-regex-beats-classifiers-by-45-points-1hb6</link>
      <guid>https://dev.to/jgravelle/you-dont-need-an-llm-to-route-agent-context-regex-beats-classifiers-by-45-points-1hb6</guid>
      <description>&lt;p&gt;LLM agents burn a ridiculous number of tokens on redundancy: opening the same files again and again, trying a patch, failing, then wandering back through the repo like they’ve never seen it before.&lt;/p&gt;

&lt;p&gt;A July 2026 paper, &lt;em&gt;ContextSniper: AntTrail's Token-Efficient Code Memory for Repository-Level Program Repair&lt;/em&gt;, puts real numbers behind that waste. In repository-level repair, agents keep dragging in irrelevant code and logs. ContextSniper tackles that with a context layer built around tiered memory and an &lt;strong&gt;intention-aware context gate&lt;/strong&gt; that filters low-value regions before they ever reach the model.&lt;/p&gt;

&lt;p&gt;That gate alone cut tokens by 51.5% on one host agent and 38.9% on Claude Code, while submitted-resolution rates stayed basically in the same neighborhood.&lt;/p&gt;

&lt;p&gt;The gate is the interesting part, because it is not tied to that paper’s exact system. It is a more general idea, and it is starting to show up across agent architectures.&lt;/p&gt;

&lt;p&gt;At heart, the gate is just a classifier. Given a request, it has to decide what kind of retrieval will answer the question cheapest: symbol lookup, semantic search, graph impact, mutation prep, or something else.&lt;/p&gt;

&lt;p&gt;That leads to the practical question the paper does not really answer:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do you need another LLM call just to decide what context to retrieve?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We tested that directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five ways agents get code into context
&lt;/h2&gt;

&lt;p&gt;Before you can gate anything, you need a retrieval strategy. Most current systems fall into one of five rough families:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Grounded read-only retrieval:&lt;/strong&gt; parse the code and return exact symbol source by name. Byte-precise, no synthesis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph code intelligence:&lt;/strong&gt; model calls, imports, entities, and dependencies as a graph, then traverse it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding / RAG search:&lt;/strong&gt; use vector similarity over chunks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whole-repo packers:&lt;/strong&gt; compress or dump the repo into the context window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutate / execute runtimes:&lt;/strong&gt; retrieve context, then modify or run code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is magic. Graphs are great for relationships, but they can drift away from source. RAG is useful, but fuzzy by design. Packers are simple, but expensive. Mutation runtimes are powerful, but they widen the blast radius.&lt;/p&gt;

&lt;p&gt;The important point is that every approach still has to answer the same question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What should I fetch for this request?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That decision is the gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate is where the real leverage is
&lt;/h2&gt;

&lt;p&gt;An intention-aware gate looks at a request like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“where is &lt;code&gt;parse_config&lt;/code&gt; defined”&lt;/li&gt;
&lt;li&gt;“how does caching work here”&lt;/li&gt;
&lt;li&gt;“what breaks if I rename this”&lt;/li&gt;
&lt;li&gt;“change the timeout to 60s”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it chooses the cheapest retrieval path that is likely to work.&lt;/p&gt;

&lt;p&gt;That might be a symbol lookup. It might be semantic search. It might be a graph-impact query. It might be mutation prep.&lt;/p&gt;

&lt;p&gt;ContextSniper uses a traceback / behavioral / stateful intent split, which is a useful framing. But it leaves an obvious engineering question for anyone building this kind of system:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How heavy does the gate actually need to be?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The default instinct is to reach for an LLM router, because “intent” sounds like a language-understanding problem.&lt;/p&gt;

&lt;p&gt;We wanted to know whether that was true.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;We used 140 hand-authored requests, balanced across seven dispatch classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;symbol lookup&lt;/li&gt;
&lt;li&gt;text search&lt;/li&gt;
&lt;li&gt;semantic&lt;/li&gt;
&lt;li&gt;graph impact&lt;/li&gt;
&lt;li&gt;structure&lt;/li&gt;
&lt;li&gt;stateful&lt;/li&gt;
&lt;li&gt;mutate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The label space borrows from ContextSniper’s intent split. We ran five-fold stratified cross-validation and compared three cheap routing tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;heuristic:&lt;/strong&gt; about 40 lines of regex over the request text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;centroid:&lt;/strong&gt; TF-IDF with nearest class mean&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;logreg:&lt;/strong&gt; TF-IDF with a linear classifier&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&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;What&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Macro-F1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;heuristic&lt;/td&gt;
&lt;td&gt;~40 lines of regex&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.3%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.945&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;centroid&lt;/td&gt;
&lt;td&gt;TF-IDF nearest-class&lt;/td&gt;
&lt;td&gt;47.9%&lt;/td&gt;
&lt;td&gt;0.474&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;logreg&lt;/td&gt;
&lt;td&gt;TF-IDF linear&lt;/td&gt;
&lt;td&gt;48.6%&lt;/td&gt;
&lt;td&gt;0.484&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The regex tier won by about 45 points.&lt;/p&gt;

&lt;p&gt;The two learned models were not terrible for a seven-class problem, but they were nowhere close. Same corpus, same folds, same labels, and the dumb little ruleset walked off with the trophy.&lt;/p&gt;

&lt;p&gt;That was surprising enough that we dug into why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intent lives in shape, not word frequency
&lt;/h2&gt;

&lt;p&gt;Bag-of-words models care about which words appear and how often. But for these requests, intent usually lives somewhere else. It lives in the shape of the request.&lt;/p&gt;

&lt;p&gt;“Where is &lt;code&gt;X&lt;/code&gt; defined” and “how does caching work” do not share much useful vocabulary, but both are almost embarrassingly easy to classify from structure alone.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;camelCase&lt;/code&gt; or &lt;code&gt;snake_case&lt;/code&gt; token usually means symbol lookup.&lt;/p&gt;

&lt;p&gt;A leading “how does” or “why” usually means behavioral or semantic exploration.&lt;/p&gt;

&lt;p&gt;“What breaks if I” usually means impact analysis.&lt;/p&gt;

&lt;p&gt;“Change,” “rename,” or “set ... to” usually means mutation.&lt;/p&gt;

&lt;p&gt;A quoted literal or bare number often means text search.&lt;/p&gt;

&lt;p&gt;Regex sees those shapes directly.&lt;/p&gt;

&lt;p&gt;TF-IDF mostly throws them away, then gets punished again by the tiny dataset. With roughly 16 examples per class, a sparse linear model does not have much signal left to recover.&lt;/p&gt;

&lt;p&gt;In this experiment, the cheap learned tier was not merely unnecessary. It was worse than the rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3% that fights back
&lt;/h2&gt;

&lt;p&gt;Four of the 140 requests resisted both cheap tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Example Request&lt;/th&gt;
&lt;th&gt;Why it resists cheap classification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;what does the parse method look like&lt;/td&gt;
&lt;td&gt;symbol lookup, but no definitional keyword&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;find where the timeout value 30000 appears&lt;/td&gt;
&lt;td&gt;text search phrased like a location query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show me how caching is implemented&lt;/td&gt;
&lt;td&gt;semantic vs symbol, genuinely ambiguous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;what modules depend on the core package&lt;/td&gt;
&lt;td&gt;graph question that reads structural&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are not really rule failures. They are ambiguity.&lt;/p&gt;

&lt;p&gt;And the fix for ambiguity is not always “ask a bigger model.” Often, the fix is to probe reality.&lt;/p&gt;

&lt;p&gt;Try the cheapest grounded lookup first. If it misses, fall back to semantic search. That resolves the uncertainty by checking the code, not by paying a language model to make a better guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to build
&lt;/h2&gt;

&lt;p&gt;For an intention-aware gate, the order should be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with regex.&lt;/strong&gt; It handles most traffic, costs nothing, behaves deterministically, and never rate-limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add probe-and-fallback for the ambiguous cases.&lt;/strong&gt; When a request could mean two things, cheaply test the first interpretation before escalating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a model call only for what survives both.&lt;/strong&gt; In our corpus, that was about 3% of requests, and even those had cheaper ways out.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The expensive tier should be the last resort, not the front door.&lt;/p&gt;

&lt;p&gt;That is the opposite of the usual reflex, which is to reach for an LLM router because the problem sounds like “understanding.” In practice, a lot of the signal is sitting right there in the punctuation, casing, verbs, and shape of the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest caveats
&lt;/h2&gt;

&lt;p&gt;Two points that really matter:&lt;/p&gt;

&lt;p&gt;First, our corpus and rules share an author. So 94.3% is an in-distribution number. It proves that hand-written rules can separate hand-written requests, which is not exactly a thunderclap from Mount Science.&lt;/p&gt;

&lt;p&gt;The number is less important than the shape of the result. Real validation needs real request logs. I would happily rerun this on a public trace.&lt;/p&gt;

&lt;p&gt;Second, ContextSniper is convergent evidence, not an endorsement of any specific tool. It is a separate group arriving at the same architecture from the repair-agent direction: tiered memory plus an intent gate, large token savings, and resolution held roughly flat.&lt;/p&gt;

&lt;p&gt;To be precise about “roughly,” their official validation reports 24.0% of issues resolved with the gate versus 26.0% for the baseline, which they describe as comparable while also noting a validation-error imbalance.&lt;/p&gt;

&lt;p&gt;The important signal is that two independent lines of work are landing in the same place: context gates matter. The exact accuracy of one regex tier is not the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves the taxonomy
&lt;/h2&gt;

&lt;p&gt;The gate sits above whatever retrieval family you use.&lt;/p&gt;

&lt;p&gt;In our stack, it routes into a grounded read-only retriever, &lt;a href="https://github.com/jgravelle/jcodemunch-mcp" rel="noopener noreferrer"&gt;jCodeMunch&lt;/a&gt;, because byte-exact symbol source is often the cheapest correct answer to the most common kind of request.&lt;/p&gt;

&lt;p&gt;But the routing lesson is not specific to jCodeMunch. It applies whether you retrieve by symbol, graph, vector, or whole-repo packing.&lt;/p&gt;

&lt;p&gt;The thing deciding what to retrieve should be cheap first, deterministic second, shape-aware third, and “smart” only when it has no cheaper option left.&lt;/p&gt;

&lt;p&gt;For the full map of the five families and where different tools fit, there is a running &lt;a href="https://jcodemunch.com/field-guide.php" rel="noopener noreferrer"&gt;field guide to code-context tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2607.01916" rel="noopener noreferrer"&gt;ContextSniper (arXiv 2607.01916)&lt;/a&gt; · &lt;a href="https://jcodemunch.com/field-guide.php" rel="noopener noreferrer"&gt;field guide to code-context tools&lt;/a&gt; · &lt;a href="https://github.com/jgravelle/jcodemunch-mcp" rel="noopener noreferrer"&gt;jCodeMunch&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tokenmaxxing</category>
      <category>modelmaxxing</category>
      <category>llm</category>
      <category>contextsniper</category>
    </item>
  </channel>
</rss>
