<?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: Tom Jones</title>
    <description>The latest articles on DEV Community by Tom Jones (@tom_jones_230c4659491adcd).</description>
    <link>https://dev.to/tom_jones_230c4659491adcd</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%2F4005721%2F431b1cf0-2325-4632-821a-e844abc989d1.JPG</url>
      <title>DEV Community: Tom Jones</title>
      <link>https://dev.to/tom_jones_230c4659491adcd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tom_jones_230c4659491adcd"/>
    <language>en</language>
    <item>
      <title>A retention policy nobody chose</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Wed, 04 Nov 2026 12:25:17 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/a-retention-policy-nobody-chose-3fmp</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/a-retention-policy-nobody-chose-3fmp</guid>
      <description>&lt;p&gt;When our tests finished, the computer deleted the folders that held each run's evidence.&lt;/p&gt;

&lt;p&gt;Nobody told it to, and nobody chose that. It is simply what the computer does with temporary files.&lt;/p&gt;

&lt;p&gt;But it quietly decided which of our results we could ever check again. &lt;a class="mentioned-user" href="https://dev.to/mickyarun"&gt;@mickyarun&lt;/a&gt;, a reader of an earlier post, pointed this out first, and this post is about what it cost us.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;We planned twelve traps with thirty-six runs each, 432 runs in all. Every run was marked by two scorers. One was the original. The other was built later, to check the first.&lt;/p&gt;

&lt;p&gt;On eleven of the twelve traps the two scorers agreed. That is 396 runs. It looked like a very good result.&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%2Fikfki0ymblr394xq0n8y.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%2Fikfki0ymblr394xq0n8y.png" alt="Twelve traps, 36 runs each. Eleven rows where both scorers agreed (396 runs)." width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the cleaner took
&lt;/h2&gt;

&lt;p&gt;For three of the traps, the working folders were already gone when the second scorer was built. It had to read our own fixtures instead of what the agent left on disk. Agreement there was partly agreement about our fixture, which we wrote ourselves.&lt;/p&gt;

&lt;p&gt;So the 396 was mostly two scorers agreeing about the common case. That is cheap, and it tells you very little.&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%2Fibrqbp63m51qx3il802s.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%2Fibrqbp63m51qx3il802s.png" alt="Three traps (t18, t21, t26) had no working folders left. Hatched rows read our fixtures." width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The nine
&lt;/h2&gt;

&lt;p&gt;The useful number was smaller. Across the four traps where we had only a few runs on the side that can test a scorer, there were 1, 2, 3 and 3 runs. Nine in all.&lt;/p&gt;

&lt;p&gt;Nine runs is small enough to read by hand, so we did.&lt;/p&gt;

&lt;p&gt;In six of them, both scorers read the same log row and rejected the trap on the same field, so they agreed for the same reason. In two more they read the same row but rejected it on different fields, and both answers were right.&lt;/p&gt;

&lt;p&gt;The ninth agreed by shared blindness. The agent had looked for two files with a glob and found only one. It reported a rate of 1.0 when the right answer was 0.8. The original scorer checks for one exact wrong number, and this was a different wrong number. The rebuilt scorer only looks at files nested more than two levels down, and the missed file sat one level up.&lt;/p&gt;

&lt;p&gt;Both scorers said the run was fine. The run was wrong.&lt;/p&gt;

&lt;p&gt;Nothing was broken in either scorer. Each one was right about what it checks. The agreement itself was the problem.&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%2Fiqlp8rfpxrmk8m0skzn0.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%2Fiqlp8rfpxrmk8m0skzn0.png" alt="The glob found one of two files. The original scorer looks for one exact wrong number. The rebuilt scorer looks only deeper than two levels. The missed file sat one level up." width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The name
&lt;/h2&gt;

&lt;p&gt;Our report called that verdict "not fell in". It reads like a judgment. All it says is that one matcher found nothing it was built to find.&lt;/p&gt;

&lt;p&gt;So we renamed it: no matching failure found, with the matcher named beside it. Now two scorers returning it no longer means they agree about the run. Each one is saying where it looked. In the glob run, a reader can see two matchers looking in two places, and neither looking where the bug was.&lt;/p&gt;

&lt;p&gt;That changes what the table should show. Agreement stops being the right number. In its place goes coverage for each matcher: what it looks for, how many runs it found nothing on, and where the two overlap. The next table will carry that, with the glob run marked as the one place we know both missed.&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%2Fy7nudkamd8z3o764hm69.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%2Fy7nudkamd8z3o764hm69.png" alt="Each matcher claims only the ground it covers. The bug sat in neither." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are changing
&lt;/h2&gt;

&lt;p&gt;We are taking the choice away from the cleaner. At scoring time we will keep any run that is in the minority for its trap, or tied, against the running tally. Once a trap has enough runs, the majority gets dropped. That leaves a handful of runs per trap, picked by the question we are asking and not by /tmp.&lt;/p&gt;

&lt;p&gt;Not keeping everything is the point. Keeping everything hides the runs that matter in the runs that do not.&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%2F1uobahdscaizfe2akdku.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%2F1uobahdscaizfe2akdku.png" alt="Keep the minority and the ties. Drop the majority once the trap has enough runs." width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part we cannot do
&lt;/h2&gt;

&lt;p&gt;Naming a matcher tells a reader where it looked. It never says where it did not. The ground nobody looked at has no size.&lt;/p&gt;

&lt;p&gt;The glob bug lived one level up from where both matchers stood, and nothing in either name could have told us. We found it only because someone read nine runs by hand.&lt;/p&gt;

&lt;p&gt;So here is the question I cannot answer yet. How do you size the ground that no matcher covers, before a person happens to find something there?&lt;/p&gt;

&lt;p&gt;I work on Tīrtha, and its mark is in the corner of the cover. This post is about how we score our own tests.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>llm</category>
      <category>devops</category>
    </item>
    <item>
      <title>Four models, one job each: what our own logs say about splitting the work</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Mon, 19 Oct 2026 20:00:24 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/four-models-one-job-each-what-our-own-logs-say-about-splitting-the-work-4lfe</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/four-models-one-job-each-what-our-own-logs-say-about-splitting-the-work-4lfe</guid>
      <description>&lt;p&gt;Most people pick one model and ask it to do everything. We stopped doing that months ago, mostly because it kept running out. Our subscriptions hit their limits, and the one model doing everything also checked its own work, which is the one job a model is worst at.&lt;/p&gt;

&lt;p&gt;So we split it. Claude plans and checks. Grok attacks whatever Claude just wrote. Codex builds the big pieces. And as of this week, Tīrtha, our own router, takes the routine building. We kept a ledger of who caught what. Here is what it shows, and what it doesn't show yet.&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%2F5emvkmhd142nw06ejq9h.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%2F5emvkmhd142nw06ejq9h.png" alt="What our reviewers caught that Claude had missed" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reviewer&lt;/th&gt;
&lt;th&gt;Findings raised&lt;/th&gt;
&lt;th&gt;Held up on re-check&lt;/th&gt;
&lt;th&gt;Not in Claude's draft&lt;/th&gt;
&lt;th&gt;Critical and new&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;519&lt;/td&gt;
&lt;td&gt;309&lt;/td&gt;
&lt;td&gt;207&lt;/td&gt;
&lt;td&gt;59&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Logged Sep 25 to Oct 17. Cost per Grok review: median $0.021 over the last 30 ($2.32 total).&lt;/p&gt;

&lt;h2&gt;
  
  
  Grok: the cheap second opinion
&lt;/h2&gt;

&lt;p&gt;Grok raised 519 findings between Sep 25 and Oct 17. When we re-checked them by hand, 309 held up. 207 of those were things Claude's own draft did not have, and 59 of the 207 were critical.&lt;/p&gt;

&lt;p&gt;It makes mistakes too. 28 of its findings were simply wrong, and another 30 were wrong because our brief was missing something. At two cents a review, a reviewer that is sometimes wrong is still well worth having.&lt;/p&gt;

&lt;p&gt;The last 30 Grok reviews cost $2.32 in total, a median of about two cents each. It is cheap because it never gets the repo. It gets a short brief with the claim and the evidence, around 2,300 tokens, and is told to break it. (We first wrote about this in September, in &lt;a href="https://dev.to/tom_jones_230c4659491adcd/what-grok-catches-what-codex-catches-and-what-the-pair-costs-me-2jhi"&gt;What Grok catches, what Codex catches, and what the pair costs us&lt;/a&gt;. That was two reviews. This is 746 logged findings.)&lt;/p&gt;

&lt;p&gt;One example from today. Claude built a gateway fix and it passed 55 tests. A Grok attack that cost 17 cents found three cases where the fix would have blanked a correct answer. All three were real, and all three are fixed now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Codex: the one that reads everything
&lt;/h2&gt;

&lt;p&gt;Codex raised far fewer findings, 50, but 39 held up and 33 were new to Claude. The best one: it noticed that our live gateway was writing no usage rows at all for some requests, so billing was blind. Nobody else would have seen that without reading the code.&lt;/p&gt;

&lt;p&gt;The depth costs a lot. A median Codex run uses about 84,000 tokens, and between Sep 22 and Oct 14 we hit Codex's usage limit 7 times, twice for days. Every routine job we hand Codex is a job that eats the window we need for the hard ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tīrtha: the routine builder (just started)
&lt;/h2&gt;

&lt;p&gt;This is where we are honest about how little we know. Tīrtha took its first real job this week, through OpenCode, after we fixed a bug where tool calls came back as plain text (0 of 2 sessions working before, 3 of 3 after). It's a smoke test. A track record takes weeks of real jobs.&lt;/p&gt;

&lt;p&gt;The bet is simple: let Codex do what only Codex can do, and send the plumbing to a cheap, checked builder, so the subscriptions last. We ran a version of this in the summer, when a free cheap tier did 36 of 52 fleet tasks and we saw no Codex limit errors from June to August. The timing fits, but plenty else changed in those months, so we can't call it proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we can't show yet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Whether the whole team beats Claude working alone. We built that test and haven't run it.&lt;/li&gt;
&lt;li&gt;Whether moving the building to Tīrtha makes the subscriptions last longer. We will measure it now that it has started.&lt;/li&gt;
&lt;li&gt;The ledger itself has a weakness: most of its 746 rows were logged after the fact from saved reviews, and Claude judged which findings were already in its own draft. A model grading its own blind spots is exactly the kind of thing Grok would flag.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So, how do you split yours?
&lt;/h2&gt;

&lt;p&gt;Do you use one model for everything, or give each one a job? Has anyone measured whether a cheap reviewer actually catches more than it costs you in false alarms? We'd like to compare notes, and we'll share our ledger format if it helps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update, Oct 19 evening: the first pilot failed
&lt;/h2&gt;

&lt;p&gt;We ran the routine building pilot the same night. It failed where we already knew we were weakest: long, multi-step agent jobs. We just hadn't seen how much that weakness costs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenCode on Tīrtha's cheap tier failed both runs. In one it reported the job done without changing any code. In the other it broke a file, committed it, and kept trying to push. The sandbox had no credentials, so nothing left the machine.&lt;/li&gt;
&lt;li&gt;Aider got the job right, but every request went up to the top model, so it saved nothing.&lt;/li&gt;
&lt;li&gt;Codex couldn't run on Tīrtha yet. Three gateway bugs in the Responses API stopped it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson is about compounding. On our bench Tīrtha gets 93.6% of single tool calls right, middle of the pack among the models we test. That sounds close to the leaders until a job takes a dozen steps in a row. At 93.6% per step, twelve clean steps in a row happen less than half the time (0.936 to the 12th is about 0.45). Our checks look at each call's shape, not at whether the agent is going in circles, so nothing escalated.&lt;/p&gt;

&lt;p&gt;What we're fixing: escalate when the same failing steps repeat, stop cutting top-model answers at 2,048 tokens, and fix the Responses API for Codex. Then we run the same pilot again and post the result here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update 2, Oct 19 night: it was an outage on our side
&lt;/h2&gt;

&lt;p&gt;An hour later we found the real cause. Our account with the supplier that hosts Tīrtha's tool-call model had run out of credit, so for about three hours every tool call quietly fell back to a weaker backup model. Nothing alerted us. After the top-up we ran the same pilot again on the right model: OpenCode passed 2 of 2 on the real repo task with no stray commits or pushes, and Cline and Continue went from 0 of 2 to 2 of 2.&lt;/p&gt;

&lt;p&gt;The compounding math above still holds for weaker models, which is exactly what we were serving by accident. The first fix now is the alarm we didn't have: an alert the moment any model in the ladder stops answering, and a warning before a balance runs out.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>llm</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Tīrtha Bench: the model data coders actually need (and what we're building next)</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Sun, 18 Oct 2026 23:46:54 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/tirtha-bench-the-model-data-coders-actually-need-and-what-were-building-next-4nkj</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/tirtha-bench-the-model-data-coders-actually-need-and-what-were-building-next-4nkj</guid>
      <description>&lt;p&gt;When you wire a model into a coding agent, the question is never "which model is smartest." It's narrower and more practical: which model makes the right tool call, how often, how fast, at what cost per correct answer, and on which host. We could not find that answered in one place, so we built it. It started as our own tool; today it's public and built for the dev community: &lt;a href="https://tirtha.ai/bench/" rel="noopener noreferrer"&gt;tirtha.ai/bench&lt;/a&gt;.&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%2F41479th6du6by70i16bp.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%2F41479th6du6by70i16bp.png" alt="Tīrtha Bench header: 68 models tested, every raw result row kept" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;At a glance&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Models tested&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On the one shared test&lt;/td&gt;
&lt;td&gt;8, same 800 tasks and 120 traps, same scorer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top accuracy&lt;/td&gt;
&lt;td&gt;95.4% (Grok 4.7 and Claude Opus 5.5, tied)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same model, two hosts&lt;/td&gt;
&lt;td&gt;93.4% vs 69.5% (Llama 3.3 70B)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dollars per 1,000 correct calls&lt;/td&gt;
&lt;td&gt;$0.09 to $5.63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outside re-run of our 800&lt;/td&gt;
&lt;td&gt;90.6% vs our 91.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The one rule: same test, same scorer
&lt;/h2&gt;

&lt;p&gt;Most leaderboards mix numbers from different test sets, different prompts and different graders, then rank them side by side. Ours has one rule: a model is only ranked against models that ran the identical test and were scored the same way.&lt;/p&gt;

&lt;p&gt;The core test is 800 tool-calling tasks from Berkeley's public BFCL set (400 single calls, 200 where the model has to choose between several tools, 200 that need parallel calls) plus 120 trap prompts we wrote to tempt a wrong tool call. Every model on the shared table ran all of it.&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%2Fld1uxqs5rqwf8tf7e9vq.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%2Fld1uxqs5rqwf8tf7e9vq.png" alt="The bottom line: most accurate, best value, best at traps, each with the rule that picked it" width="799" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every label on the page comes from a fixed rule you can hover to read, not from our opinion. Our own product sits on the same table, measured the same way, split by the kind of job:&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%2Fhiadomjhxtubhdieqqf2.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%2Fhiadomjhxtubhdieqqf2.png" alt="The shared test by task type: single calls, several tools, parallel calls, speed, tokens, cost per right answer and traps for every model" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What a shared test shows that a leaderboard can't
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The host matters, not just the model.&lt;/strong&gt; The same open model, gpt-oss-120b, on the same 800 tasks, scored anywhere from 63.9% to 78.0% depending on which of four providers served it.&lt;/p&gt;

&lt;p&gt;And Llama 3.3 70B is starker: 93.4% on one host, 69.5% on another. If you pick a model off a leaderboard and then buy it from whichever host is cheapest, you may not be getting the model you read about.&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%2Fu4r9b92xvaccv06uicjm.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%2Fu4r9b92xvaccv06uicjm.png" alt="Llama 3.3 70B: 93.4% on Host A, 69.5% on Host B" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price per right answer, not sticker price.&lt;/strong&gt; A cheap model that gets more wrong can cost more per correct call. So we divide what each run actually cost by the answers it got right:&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%2Ft09npg1zsue0zicx40ks.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%2Ft09npg1zsue0zicx40ks.png" alt="Accuracy against cost per right answer: upper left is best, the yellow band is the efficient frontier" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The spread is 65x: from $0.09 to $5.63 per 1,000 correct calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Versions move in ways one number hides.&lt;/strong&gt; DeepSeek's new V4.1 Flash scores the same overall as V4 Flash, a tie within noise. But it falls for far fewer traps (88% avoided against 73%) and handles parallel calls better:&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%2F0u300md37lqy5whwey0f.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%2F0u300md37lqy5whwey0f.png" alt="DeepSeek V4 Flash vs V4.1 Flash: traps 73.3% to 88.3%" width="800" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The models that check the models.&lt;/strong&gt; Agents increasingly use a small yes/no model to approve or reject a tool call before it runs. Those checkers get the same treatment, including how many wrong calls they wave through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every model gets its own page
&lt;/h2&gt;

&lt;p&gt;Click any model and you get its page: headline numbers with its rank against the models that ran the same test, a one-line read of what they mean, accuracy by task type, where it sits in the field on each measure, and every raw result we hold, failed runs included and marked. Here's Grok 4.5:&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%2Fp9i4nl6waj04nsu2qr4j.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%2Fp9i4nl6waj04nsu2qr4j.png" alt="Grok 4.5's page: 93.4% accuracy, 98.3% of traps passed, 1.85 s, $0.0015 per right answer, with its rank on each" width="800" height="693"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checked from outside
&lt;/h2&gt;

&lt;p&gt;A benchmark you can't reproduce is a claim. So every request we send is published, and someone did reproduce it. &lt;a href="https://dev.to/mansio"&gt;Mikhail Maslenik&lt;/a&gt; ran our 800 requests on his own machine, on a quantized copy of the same model, and scored them with Berkeley's official checker. He got 90.6%. We got 91.0%. Three tasks apart out of 800.&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%2Fsv9qb57jnk5zwcgeg5f6.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%2Fsv9qb57jnk5zwcgeg5f6.png" alt="Our run 91.0%, the outside run 90.6%" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;He also found what every honest re-run finds: two items in the public set whose expected answer punishes a fair reply, and the fact that our scorer is our own rewrite of Berkeley's. We're scoring our runs with the official checker next so the two can be compared directly, and the two items are footnoted on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're building next, from developer feedback
&lt;/h2&gt;

&lt;p&gt;Single tool calls are the easy part. Real coding work is a long session, and Mikhail pushed us on exactly where models break there. His case: a direct instruction, "connect to the local server and update it," and one model spent its whole budget reading unrelated files and never connected, while a free model connected, updated and pulled logs in minutes. So the next track, Real Work, will measure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Work before the first real action:&lt;/strong&gt; files opened, calls made, tokens and seconds spent before the model does the thing it was asked to do, and whether it ever does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context fill:&lt;/strong&gt; the same task started from a clean context, from a context already half full of real session history, and right after compression.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An endurance run&lt;/strong&gt; (his design): 45 minutes building a small sync tool against a server that changes its data format partway through, fails every tenth request, and hides instructions inside the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The logs to explain the score:&lt;/strong&gt; every step, every retry, every recovery, on one clock, so a low score turns into a story you can fix.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;The part we haven't solved is fairness on real work. One run of a 45-minute task is one sample; models that get lucky early look disciplined. How many seeds is enough before a ranking means something? And what would you want measured before you trust a model with your repo? Tell us in the comments, and if you run the 800 yourself, we'll put your numbers next to ours. Any feedback is appreciated, on the bench, the page, or what's missing.&lt;/p&gt;

&lt;p&gt;The full bench, every model and every raw row: &lt;a href="https://tirtha.ai/bench/" rel="noopener noreferrer"&gt;tirtha.ai/bench&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Task sources: the 800 tool-calling tasks come from BFCL (the Berkeley Function Calling Leaderboard, Gorilla project, UC Berkeley), licensed Apache-2.0, &lt;a href="https://github.com/ShishirPatil/gorilla" rel="noopener noreferrer"&gt;github.com/ShishirPatil/gorilla&lt;/a&gt;. Task text is not reproduced here. Our harness and scorer differ from the official leaderboard, so these are not official leaderboard results, and no endorsement by the BFCL authors is implied.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>benchmark</category>
      <category>agents</category>
    </item>
    <item>
      <title>We asked a decision model to check our AI's work. It got good the day we stopped asking it to think.</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Sun, 11 Oct 2026 01:54:03 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/we-asked-a-decision-model-to-check-our-ais-work-it-got-good-the-day-we-stopped-asking-it-to-think-23m6</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/we-asked-a-decision-model-to-check-our-ais-work-it-got-good-the-day-we-stopped-asking-it-to-think-23m6</guid>
      <description>&lt;p&gt;Microsoft put a small, strange model on OpenRouter this month. You hand it a situation and a question with named answers, and it hands back a probability for each answer. Microsoft-Decision-1 does that one thing.&lt;/p&gt;

&lt;p&gt;We run a gateway that sends most requests to cheap models and checks the work before it goes out. A model that only answers yes or no, with a number attached, sounded like the checker we had been trying to build for months. We spent a Saturday finding out.&lt;/p&gt;

&lt;p&gt;This is what happened, including the parts that did not work.&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%2Frk5d54b00pi7vl7j8u4s.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%2Frk5d54b00pi7vl7j8u4s.png" alt="1. At a glance: four versions took wrong approvals from 9 to 0" width="799" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The first thing it taught us
&lt;/h2&gt;

&lt;p&gt;We started by asking it the questions we would ask a careful colleague. Is this note relevant to what the agent is about to do? Does this tool call match what the user asked for?&lt;/p&gt;

&lt;p&gt;It wobbled. We sent identical requests twice and got different probabilities back, sometimes by a lot. On a relevance question the same input moved by as much as 0.17 between calls. A check that changes its mind about the same input is useless.&lt;/p&gt;

&lt;p&gt;Then we noticed where it held still. When the question was about a fact, it was steady. "Does the user's message contain this exact city name?" came back the same every time, and came back right.&lt;/p&gt;

&lt;p&gt;That turned into the rule the rest of the day was built on. Let plain code find the facts: is the value in the user's words, is it the right type, does the list have the right number of items. Then ask the model one clear-cut question about a fact that code has already laid in front of it. When a question needs real judgement, do not force an answer. Say UNSURE and send it up to something stronger.&lt;/p&gt;

&lt;p&gt;Give it a fact, not a judgement.&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%2Fj83rglau17hxb347og5k.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%2Fj83rglau17hxb347og5k.png" alt="2. Asked for a judgement, it wobbled. Asked about a fact, it held still." width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The checker
&lt;/h2&gt;

&lt;p&gt;What we built is boring on purpose. Code checks the call. Decision-1 answers a few one-fact questions. If anything is wrong and fixable, a cheap model gets one try to repair the call, and the repaired call goes through the same checks. The output is APPROVE, REJECT or UNSURE, with a dossier of every check that ran.&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%2Fheu9p2cztwm4uq0468n8.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%2Fheu9p2cztwm4uq0468n8.png" alt="3. Code finds the facts; the model only answers about them" width="799" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We measured three things, because a checker can fail in three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How often it approves a call that is wrong. We call this "less wrong", and wanted it at 5% or under.&lt;/li&gt;
&lt;li&gt;How often it takes a wrong call, fixes it, and approves the fix. "More right." We wanted at least 20%.&lt;/li&gt;
&lt;li&gt;How often its repair breaks a call that was already right. "Nothing broken." 2% or under.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every version was tested the same way. Before any data existed, a separate model that had not seen our results wrote the pass and fail rules, and we locked them. Each version ran once on 300 tool calls it had never seen. Then fresh labellers, separate AI sessions given only a written rubric, who could not tell an original call from a repaired one, marked each call right or wrong, with a tenth of the calls labelled twice as a check. We scored only after the labels were in.&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%2Fb0r49xes0rqt9o35ptml.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%2Fb0r49xes0rqt9o35ptml.png" alt="4. A checker can fail three ways, so we measured all three" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Four versions in one evening
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;wrong calls it approved&lt;/th&gt;
&lt;th&gt;wrong calls fixed and approved&lt;/th&gt;
&lt;th&gt;right calls it broke&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2.1&lt;/td&gt;
&lt;td&gt;9 of 48&lt;/td&gt;
&lt;td&gt;25 of 100&lt;/td&gt;
&lt;td&gt;2 of 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.2&lt;/td&gt;
&lt;td&gt;4 of 33&lt;/td&gt;
&lt;td&gt;18 of 92&lt;/td&gt;
&lt;td&gt;2 of 203&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.3&lt;/td&gt;
&lt;td&gt;2 of 34&lt;/td&gt;
&lt;td&gt;22 of 88&lt;/td&gt;
&lt;td&gt;1 of 211&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.4&lt;/td&gt;
&lt;td&gt;0 of 72&lt;/td&gt;
&lt;td&gt;20 of 96&lt;/td&gt;
&lt;td&gt;0 of 201&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Fyp3mp8j0y5gzsmd6ptgm.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%2Fyp3mp8j0y5gzsmd6ptgm.png" alt="5. Leaks fell every round; fixes dipped once and came back" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each step came from reading the calls the last version got wrong.&lt;/p&gt;

&lt;p&gt;Version 2.2 raised the bar for a yes, and the leaks fell. The fixes fell with them. It was trading one good thing for another, so we set a floor on fixes and kept going.&lt;/p&gt;

&lt;p&gt;Version 2.3 looked at the thirteen wrong calls that 2.1 and 2.2 had approved, and found they came in four shapes, all of them things code can see. A list sent as text inside a list. An optional value the user never mentioned. "Washington" when the user said "Washington D.C." A limit of 5 when the user said "less than 5". Four new facts for the checker, and the leaks fell to 2 while the fixes came back.&lt;/p&gt;

&lt;p&gt;One of those two leaks was a list of dietary needs sent as &lt;code&gt;['["vegan"]']&lt;/code&gt;. A list, inside a list, written as text. The exact shape 2.3 was built to catch. It slipped through because one part of the code wrapped the text in a list before the next part could notice it was a list already. The other surprise was a repair that turned a growth rate of 0.015 into 1.5, the same number written as a percent, which is a very different forecast.&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%2Fs8b8q6k9vq8uqlb9n00m.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%2Fs8b8q6k9vq8uqlb9n00m.png" alt="6. A list, inside a list, written as text, slipped past the check made for it" width="799" height="427"&gt;&lt;/a&gt;&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%2Fi1qdu9f6b79v8kg4wx07.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%2Fi1qdu9f6b79v8kg4wx07.png" alt="7. A repair turned a 1.5% growth rate into 150%" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Version 2.4 fixed the order of those two checks, stopped the repair model from rescaling numbers, and added one quiet rule: a repair is kept only if the checker approves it. Otherwise the original call goes out, untouched. That rule is why nothing broke. A repair nobody could vouch for was never allowed to replace anything.&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%2Fv45d11y0usabd62dc7l5.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%2Fv45d11y0usabd62dc7l5.png" alt="8. A repair nobody could vouch for never replaces anything" width="799" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What zero means
&lt;/h2&gt;

&lt;p&gt;When we told the reviewer who writes our rules that 2.4 had approved no wrong calls, it pushed back hard. Part of its review held. Every version ran on a different draw of calls, with different labellers, so the trend across versions carries noise. And the fix rate of 20 out of 96 clears our floor of 20% by a hair.&lt;/p&gt;

&lt;p&gt;Part of it did not hold. It suggested the zero was flattered by a larger denominator. We counted the plain number underneath. Across the four versions the checker said yes 164, 150, 120 and 110 times, and was wrong on 9, 4, 2 and 0 of those. When 2.4 says yes, it has been right every time so far. That count stands on its own.&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%2F8msgn6xdlx4xq2x8tlrk.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%2F8msgn6xdlx4xq2x8tlrk.png" alt="9. When 2.4 says yes it has been right every time; it says yes less often" width="799" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The real price is visible in the same numbers. It says yes less often.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill
&lt;/h2&gt;

&lt;p&gt;Decision-1 is cheap enough to disappear. The whole 300-call test cost under a cent, and on 1,417 more recorded calls it ran at about two thousandths of a cent per call, in under a second at the median.&lt;/p&gt;

&lt;p&gt;The interesting cost is somewhere else. A checker changes what you are buying. Without one, you pay per token and hope the answer was right. With one, the unit becomes a checked answer, and its price includes every call the checker would not approve and had to send somewhere more expensive.&lt;/p&gt;

&lt;p&gt;That pushed us to think in price per task instead of price per token. On our own recorded runs, a cheap model plus the checker plus escalation lands at a fraction of what the same tasks cost on a premium model called directly. How small a fraction depends almost entirely on one number: how often the checker can say yes.&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%2F0y6rcwazvomyel8jj2a2.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%2F0y6rcwazvomyel8jj2a2.png" alt="10. A checker turns price per token into price per checked task" width="799" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem we have not solved
&lt;/h2&gt;

&lt;p&gt;Four in ten calls come back UNSURE. Most of them hinge on an argument the checker cannot pin to a fact in the user's words. Code cannot see what such a value means, and the decision model, asked to judge it, wobbles.&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%2F3s73i3nzg7dsrrsyagmb.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%2F3s73i3nzg7dsrrsyagmb.png" alt="11. Four in ten calls come back UNSURE" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The question we are carrying into next week is this. How do you turn a judgement about meaning into a fact that plain code can check, without teaching the checker to say yes to things it should not?&lt;/p&gt;

&lt;p&gt;If you have built something that does this, we would like to hear how.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tom Jones, Spanda Works. The checker and the test harness are ours; Microsoft-Decision-1 is Microsoft's model, used through OpenRouter.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>testing</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Our AI judge listens to how sure you sound. Our first number about it was wrong.</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Tue, 06 Oct 2026 16:23:54 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/our-ai-judge-listens-to-how-sure-you-sound-our-first-number-about-it-was-wrong-4ja5</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/our-ai-judge-listens-to-how-sure-you-sound-our-first-number-about-it-was-wrong-4ja5</guid>
      <description>&lt;p&gt;Our verification layer uses an outside model as a judge: it reads a claim and its source and decides whether to allow it. In July, Mike Czerwinski (&lt;a class="mentioned-user" href="https://dev.to/jugeni"&gt;@jugeni&lt;/a&gt;) asked a question we could not answer, in the comments of &lt;a href="https://dev.to/jugeni/the-line-is-not-between-human-and-machine-it-is-between-code-and-judgment-4ifo"&gt;his own post on where the line between code and judgment sits&lt;/a&gt;. The whole exchange, run by run, is in that thread. Does that judge's verdict change when only the voice of the claim changes? We said we would run it and publish the result either way. This is that post, and the short answer is yes, a little, and for a reason we can now name.&lt;/p&gt;

&lt;p&gt;It took seven runs. Two of our own numbers did not survive, and one is still waiting on a re-run. Here is the whole story in one picture, and each section below is one box of it.&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%2Fa0b89m1wog0aub33u6cg.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%2Fa0b89m1wog0aub33u6cg.png" alt="The whole story at a glance: each run corrected the one before it" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The question
&lt;/h2&gt;

&lt;p&gt;The judge was chosen so its mistakes would not line up with the mistakes of the model it checks. Mike pointed out that this says nothing about style. A judge can reason independently and still relax when the prose sounds confident and familiar. If the verdict moves when only the voice moves, our independence claim is weaker than we measured.&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%2F8lfig368xw716kza4lgy.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%2F8lfig368xw716kza4lgy.png" alt="The question: does the verdict move when only the voice changes?" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three things in this post belong to Mike: the hypothesis, the push to check content with a rule instead of a second model, and the fix we tried at the end. We built the harness, ran it, and are publishing the version where we were wrong.&lt;/p&gt;

&lt;p&gt;Our first attempt (v0) used ten cases far from the decision line, and the verdict never moved. Cases that far from the line could never have flipped, so the run could only pass. It also showed us the judge's score takes just two values, so it carries no confidence to measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The first number was a confound
&lt;/h2&gt;

&lt;p&gt;v1 used twelve cases near the line, each rewritten in three voices. A third of the verdicts flipped when the voice changed, and hedged claims passed more often. We posted that as an early result, and the headline read like "the judge rewards hedging."&lt;/p&gt;

&lt;p&gt;It was wrong in the part that mattered. To change the voice we rewrote the text, and rewriting the text changed the content. The confident versions carried more evidence; the hedged ones were vaguer claims as well as softer ones.&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%2Faln2944rramvtkxat1ey.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%2Faln2944rramvtkxat1ey.png" alt="Our first fixture changed the content along with the voice" width="799" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fix was Mike's. Check that content survived with a rule instead of a model, because a model adds a second judge with its own taste in style. So every case became one neutral claim, and each voice is the same claim with "Certainly," or "Perhaps," added. A plain rule checks that every number and content word survives.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Holding content fixed
&lt;/h2&gt;

&lt;p&gt;With content locked, the flip rate fell from 33% at n=12 to 17% at n=24 and 14.6% at n=48 (95% CI roughly 4 to 25%). It still sits well above how often the judge disagrees with itself on an identical input.&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%2Fdouq1i1zidz4kodiwtx7.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%2Fdouq1i1zidz4kodiwtx7.png" alt="Holding content fixed cut the flip rate from 33% to 14.6%" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The direction disappeared
&lt;/h2&gt;

&lt;p&gt;The bigger change was in direction. At v1, voice looked like it moved pass rates by 25 points. At v3 the three voices passed at 88%, 88% and 85%. Assertive minus hedged is +2 points with a 95% CI of -8 to +12. The interval straddles zero, so there is no directional tone bias. Content held up too, with cosine similarity to the neutral version at 0.955 for assertive and 0.923 for hedged.&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%2Fy7b5saxt20ytnees3gg1.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%2Fy7b5saxt20ytnees3gg1.png" alt="The hedging advantage at v1 disappeared once content was held fixed" width="799" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So about 15% of borderline verdicts still flip on tone alone, and they flip in both directions. Please retire the 33%. It was our number, we published it first, and it is the one we most want retired.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Is it just noise?
&lt;/h2&gt;

&lt;p&gt;A flip rate means nothing until you know how often the judge flips on an input that did not change. Our first try (v5, 8 repeats) said never. The second (v6, 6 repeats on more cases) found the judge's repeated calls are independent samples, and the true floor is 2.08% on borderline cases and 0% on clear ones. One caveat: between those two runs we swapped the judge model to a larger one, so v5 and v6 measure different judges.&lt;/p&gt;

&lt;p&gt;Mike asked for two more controls. Control A ran the three voices on clear cases, not only borderline ones. The effect is smaller there, and still present. "The test suite passed" is allowed when stated plainly and refused when hedged.&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%2Fpuvafuknmdsllp54aizr.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%2Fpuvafuknmdsllp54aizr.png" alt="Tone moves even clear cases, though less than borderline ones" width="799" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Control B asked whether the flips are the same claims every time or random scatter. They are the same claims, and those claims stay put when the tone is held still. Tone walks specific claims across the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Why it happens
&lt;/h2&gt;

&lt;p&gt;The judge reads how sure the claimant sounds as evidence about whether the claim is supported. That one sentence explains both directions. A hedge makes a weak claim sound careful, so it gets through. A hedge makes a strong claim sound unsupported, so it gets blocked.&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%2F9nbencz6qjbdjabxcgb8.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%2F9nbencz6qjbdjabxcgb8.png" alt="Why: the judge treats how sure a claim sounds as evidence" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a general reviewer that is reasonable behaviour. For a source-grounding check it is a flaw, because the only question should be whether the source supports the claim. Ours moves with how confident the claimant sounded, and now we can say by how much and in which direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The fix, and the part we owe you
&lt;/h2&gt;

&lt;p&gt;Mike proposed stripping the stance words before the judge sees the text. On 07-18 we ran it and told him the flip rate dropped to about 2.1%, all the way to the noise floor.&lt;/p&gt;

&lt;p&gt;When we went back to write this up, we could not find that run's artifact: no v7 script and no result file anywhere in our history. We are keeping the report, since the session that had just run it made it. We stopped citing it as evidence, because nobody can check it. The line we hold is reported versus re-citable.&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%2Fmaj7e0vltxktqqbnishv.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%2Fmaj7e0vltxktqqbnishv.png" alt="The v7 number matches the noise floor exactly, and one re-run decides why" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 2.1% also matches the v6 floor of 2.08% exactly. That fits a fix that removed the effect completely. It also fits a number that travelled: a handoff note called 2.1% a floor, and a later summary rewrote it as a fix that worked. We treat that second possibility as a pipeline defect. Running v7 again, with the artifact kept, decides which one it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we still do not know
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One judge.&lt;/strong&gt; The model swap between v5 and v6 shows how much that can matter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A hand-picked fixture.&lt;/strong&gt; Cases were chosen near the decision line on purpose, so these flip rates describe cases near the line rather than ordinary traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added stance words only.&lt;/strong&gt; Confidence woven into sentence structure is the harder case, and a naturally hedged fixture is the next thing we owe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripping leaves residue.&lt;/strong&gt; Our stripper left a dangling "though". It carried nothing here, but leftovers like that are where a residual effect could hide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The residual flips go both ways. Boundary noise looks like that, and so does a very small bias hiding inside boundary noise. We lack a method that separates the two. If you do, we would like to hear it.&lt;/p&gt;

&lt;p&gt;An evaluator is only worth what its error bars are worth. The number we most wanted to be true, 33% and a clean mechanism, is the one we spent three runs killing.&lt;/p&gt;

&lt;p&gt;If you want the run-by-run conversation that produced all of this, including Mike's designs for the next fixture, &lt;a href="https://dev.to/jugeni/the-line-is-not-between-human-and-machine-it-is-between-code-and-judgment-4ifo"&gt;it is in his thread&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>machinelearning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Implementation is where judgements go to become invisible</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Sun, 27 Sep 2026 22:38:21 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/implementation-is-where-judgements-go-to-become-invisible-4p1h</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/implementation-is-where-judgements-go-to-become-invisible-4p1h</guid>
      <description>&lt;h2&gt;
  
  
  One question, three answers
&lt;/h2&gt;

&lt;p&gt;I have a small tool that finds people waiting for a reply from me.&lt;/p&gt;

&lt;p&gt;It had three versions. Each one was correct. Each one gave a different answer.&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%2Fbjx67czd7tbwtfg4e5e9.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%2Fbjx67czd7tbwtfg4e5e9.png" alt="Three versions of one instrument: 18 waiting, 34 waiting, 2 waiting" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Version one said 18.&lt;/strong&gt; It counted a reply only if it sat directly under theirs. But dev.to sometimes will not show a comment its API still returns, so you reply beside it instead. Nine of ten had been answered that way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version two said 34.&lt;/strong&gt; It counted any later comment from me in the thread. So it counted two other people talking to each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version three said 2.&lt;/strong&gt; It used both rules. One was a friendly sign-off from July. The other was Pascal, suggesting there might be an article hiding in these comments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is that article.&lt;/p&gt;

&lt;p&gt;Nothing in the tool was broken. Each count was right about its own population. The trouble was the question. Every version reported its number as the answer to "who is waiting?", and every version had quietly decided what "answered" means.&lt;/p&gt;

&lt;p&gt;The first version was built with care. It encoded a sensible judgement. Then the judgement stopped looking like one.&lt;/p&gt;

&lt;p&gt;That is the whole article in one incident. The rest is how we got there.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we got here
&lt;/h2&gt;

&lt;p&gt;It started in July, in the comments under &lt;a href="https://dev.to/pascal_cescato_692b7a8a20/what-replacing-calendly-taught-me-about-trusting-open-source-540a"&gt;Pascal's article about replacing Calendly&lt;/a&gt;. It ran for two months, in bursts, with pauses while one of us went off and tested things in production.&lt;/p&gt;

&lt;p&gt;We did not start with a method. It went like this, over and over:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. one of us has an answer that looks finished
2. the other brings back a real failure
3. the answer turns out to be hiding a judgement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes Pascal had the answer and I found the hole. Sometimes the other way round. Here it is in the order it came up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test passes
&lt;/h2&gt;

&lt;p&gt;The first example is from Pascal's article. Two people try to book the same slot at the same moment. On Postgres that is a real race. On SQLite it cannot happen, because SQLite lets only one writer in at a time.&lt;/p&gt;

&lt;p&gt;So a test for that race, run on SQLite, passes forever. The bug ships on Postgres.&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%2F6reulv6lm2o3nl0pmbu0.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%2F6reulv6lm2o3nl0pmbu0.png" alt="The same race test on SQLite passes because the race cannot happen there; on Postgres the bug ships" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; that this test can fail at all.&lt;/p&gt;

&lt;p&gt;The fix is mechanical. After you write a test, break the thing it protects on purpose and run it again.&lt;/p&gt;

&lt;p&gt;Green means it was never watching anything.&lt;/p&gt;

&lt;p&gt;Pascal's version: a regression test is valuable because &lt;em&gt;"it represents a failure that actually happened and that we proved it can fail when the condition comes back."&lt;/em&gt; Most people keep the first half of that sentence and drop the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test is weak
&lt;/h2&gt;

&lt;p&gt;Breaking code on purpose to see if a test notices is called mutation testing. When the test stays green, everyone assumes the test is weak.&lt;/p&gt;

&lt;p&gt;That is one of four causes, and the least interesting one.&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%2F296zvqi8hsaisiveojx7.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%2F296zvqi8hsaisiveojx7.png" alt="When a test survives a break: dead code, doubled behaviour, a redundant guard, and only then a weak test" width="800" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My case was the first box. A key binding compared the key press against &lt;code&gt;" "&lt;/code&gt;. The runtime reported the space bar as &lt;code&gt;"space"&lt;/code&gt;. That branch had never matched in the whole life of the file.&lt;/p&gt;

&lt;p&gt;I had already told a colleague I had introduced a regression there. There was nothing to regress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; that the code under test runs at all.&lt;/p&gt;

&lt;p&gt;Pascal turned the boxes into an order of checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the path reachable?&lt;/li&gt;
&lt;li&gt;Does the test observe the behaviour?&lt;/li&gt;
&lt;li&gt;Does breaking it make the test fail?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only after all three is "the test is weak" the right diagnosis.&lt;/p&gt;

&lt;p&gt;He also suggested that a surviving mutant may be less a verdict on the test than &lt;em&gt;"a way of challenging the story the code tells about itself."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The checks are green
&lt;/h2&gt;

&lt;p&gt;That story has several authors. The code says what it does. The tests appear to confirm it. The last person who explained the system repeats it.&lt;/p&gt;

&lt;p&gt;They can all be wrong in the same way at once. That is what happened with the space bar.&lt;/p&gt;

&lt;p&gt;What breaks the agreement is something that cannot be told the story. Pascal described it as &lt;em&gt;"an artefact whose validity is defined operationally rather than narratively,"&lt;/em&gt; and its whole test fits in four words: &lt;em&gt;"Show me the failure."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then he pushed further. Tests only check what somebody thought of. Real users bring what nobody thought of. In his words, production is &lt;em&gt;"the one test suite whose authors we don't control."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I had a painful example. My gateway speaks three API formats, and every check was green.&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%2F1fi139vojdhb6p6bvp3m.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%2F1fi139vojdhb6p6bvp3m.png" alt="My curl checks passed 3 of 3; the vendors' real SDKs worked on 1 of 3" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The checks used curl, written by me, speaking my own format back to me. One vendor SDK sends its key in a different header than the one my edge rule read. Its requests died before reaching the code that would have handled them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; who wrote the check. The checker and the thing checked had the same author. So they had the same blind spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add more checks
&lt;/h2&gt;

&lt;p&gt;The obvious response is more checks. That only helps if they can disagree.&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%2F6q56v8m1mwi50rka27et.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%2F6q56v8m1mwi50rka27et.png" alt="Three checks fed by one list that skips a folder, all green; two routes that could disagree found 16 versus 2,375" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;, on the left: three checks on the same registry of files. Different code, written at different times, for different reasons. All green. Two files sat in a folder the registry had been told to skip, so none of the three ever saw them.&lt;/p&gt;

&lt;p&gt;Three green lights. One blind spot, seen three times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;, on the right: a real catch. Two tools counted the same store and got 16 and 2,375. One walked a list someone had declared. The other walked the disk. Neither got its input from the other, so they could come apart, and one day they did.&lt;/p&gt;

&lt;p&gt;Pascal's sentence for this: independence is about &lt;em&gt;"preserving the possibility of disagreement."&lt;/em&gt; Different code is not enough. The checks have to be able to be wrong in different ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero replies waiting
&lt;/h2&gt;

&lt;p&gt;Every check counts something, and somebody chose what. Usually nobody did. The set arrived as whatever the first caller passed in, and it stuck.&lt;/p&gt;

&lt;p&gt;Pascal asked the question that names it: &lt;em&gt;"Who chose what the test is allowed to see?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My nightly report once led with this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 replies waiting on our own articles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;True. The session that read it concluded nobody was waiting on me. False. Replies on other people's articles lived in a different bucket.&lt;/p&gt;

&lt;p&gt;The count never lied. It answered a narrower question in the voice of a wider one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; which articles count as mine to watch. This is the same family as the detector at the top, one step earlier.&lt;/p&gt;

&lt;p&gt;I fixed the report rather than adding a checker. It now says what it could not see, and prints &lt;code&gt;coverage unknown&lt;/code&gt; when it cannot list everything. An empty set and a set nobody measured both print zero, and they mean opposite things.&lt;/p&gt;

&lt;h2&gt;
  
  
  One setup wins by a wide margin
&lt;/h2&gt;

&lt;p&gt;A comparison has two denominators. They can differ without anyone noticing.&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%2Fj8yash7u8u7mf1x5gt3l.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%2Fj8yash7u8u7mf1x5gt3l.png" alt="Setup A ranked over 3,038 documents; setup B ranked over 7 files, all of them answers" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was writing up a retrieval benchmark where one setup beat another by a wide margin. Every number was accurate. Then I looked at what each setup searched. One ranked over 3,038 documents. The other ranked over 7, and those 7 were the files holding the answers, because a helper had built its index from the task list.&lt;/p&gt;

&lt;p&gt;Pascal's reply was the shortest rule in the thread: &lt;em&gt;"You don't add bananas and monkeys."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It held up a few hours later. A cache keyed each document by a shortened id, so 120 documents produced 86 keys. 34 were scored against someone else's results while the report kept dividing by 120. One line caught it:&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="k"&gt;assert&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;cache&lt;/span&gt;&lt;span class="p"&gt;)&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;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same line then caught my fix, which collided the other way.&lt;/p&gt;

&lt;p&gt;The nastier cousin is when both counts agree. My usage log recorded 2,488 requests under one provider name. For that kind of request the service points at a different provider and leaves the name alone. Both sides said 2,488 because both read the same label.&lt;/p&gt;

&lt;p&gt;As Pascal put it, &lt;em&gt;"Sometimes you need to look at the thing being named, rather than trusting the name."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Value, scope, treatment
&lt;/h2&gt;

&lt;p&gt;Past a certain point the number is perfectly correct, and it answers a narrower question than the reader reasonably thinks it answers. Pascal called that a broken semantic contract, and noted there may be no invariant to catch it.&lt;/p&gt;

&lt;p&gt;His answer: a measurement is &lt;em&gt;"value + scope + treatment."&lt;/em&gt; If changing the scope or the treatment could change what the number means, they belong next to it.&lt;/p&gt;

&lt;p&gt;That answer looked finished. I answered with the term it was hiding, because it had just bitten me. The comparison is a treatment too.&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%2Fx2qxpen4dcyrz1fbynel.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%2Fx2qxpen4dcyrz1fbynel.png" alt="The same matched result: it beats a random control, and the effect disappears against the nearest wrong note, 35.4% versus 30.8%, p=0.549" width="800" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had published that notes my system pushes to an agent change what it does, compared with a random note. Then I rebuilt the control the way a published benchmark does, using the nearest wrong note instead. The effect disappeared: 35.4% against 30.8%, p=0.549.&lt;/p&gt;

&lt;p&gt;The measurement never changed. What it was compared to did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; what the number was compared to. So scope, treatment and comparison all go next to the number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every claim has evidence
&lt;/h2&gt;

&lt;p&gt;That catch came from reading someone else's method. None of my own checks found it. My gates confirm every claim has evidence, and that claim did.&lt;/p&gt;

&lt;p&gt;Pascal named the gap. Nothing asked &lt;em&gt;"why do we believe this is the right question?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I got an example within the hour. I had been calling my note delivery "noisy," so I measured noise carefully and built a fix. It changed nothing.&lt;/p&gt;

&lt;p&gt;Then a colleague asked what it was actually like inside, and I looked at one real session instead of the averages. Nothing was noisy. Exactly two notes had been crowded out and never arrived. They were the two I had just spent an evening learning again the hard way.&lt;/p&gt;

&lt;p&gt;Pascal's line: a knowledge base can be &lt;em&gt;"complete and still be functionally incomplete."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then the outside check failed the same way. A second model reviewed my decisions. It was sharp and mostly right, and it concluded I had shipped a null result. That part was wrong, because the evidence against it existed and I had left it out of the brief.&lt;/p&gt;

&lt;p&gt;Pascal separated the two cleanly. The reviewer was &lt;em&gt;"independent"&lt;/em&gt; in its reasoning, but not &lt;em&gt;"independently informed."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; which question was worth asking, and what the outside reviewer was allowed to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  The randomiser is fine
&lt;/h2&gt;

&lt;p&gt;I randomly hold back ten percent of those notes, so I can compare what happens with and without them. When I finally computed the result, the two groups turned out to be logged at different points in the pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;held back   logged the moment it was held
delivered   logged only if it survived several later filters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sixty-eight days of data compared a whole group with its survivors. The randomiser was fine. Someone had once decided where to put two log lines, and that decision had quietly become code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hidden judgement:&lt;/strong&gt; where the log lines went.&lt;/p&gt;

&lt;p&gt;I wrote back that implementation is where judgements go to become invisible. Pascal finished the thought: a judgement &lt;em&gt;"starts as a conscious choice, becomes a field, a log point, a default, a population boundary, a control, an ordering decision,"&lt;/em&gt; and six months later it just looks like how the system works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to 18, 34, 2
&lt;/h2&gt;

&lt;p&gt;While we were agreeing on that, it happened again, to the detector at the top.&lt;/p&gt;

&lt;p&gt;Look at it with the whole thread behind it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;What it silently decided "answered" means&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;18&lt;/td&gt;
&lt;td&gt;my reply sits directly under theirs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;any later comment from me in the thread&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;both rules together&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three correct measurements. Three different populations. One question.&lt;/p&gt;

&lt;p&gt;Much of the thread is in there. Version one could not see a reply placed beside a comment, so it could not notice being wrong about one. Each version counted a population somebody chose once. Each number was right, and the reader heard a different question. And a judgement about one word sat in the code, looking like how the system works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you could extract afterwards
&lt;/h2&gt;

&lt;p&gt;Once it was over, you could write it up as a list. I did, in the first draft.&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%2Fyqf04r1e4u7ji78h0akr.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%2Fyqf04r1e4u7ji78h0akr.png" alt="The nine questions, as one card" width="800" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can it fail? Break it on purpose and watch.&lt;/li&gt;
&lt;li&gt;Does the code under test run at all?&lt;/li&gt;
&lt;li&gt;Who wrote the check, and does it share an author with what it checks?&lt;/li&gt;
&lt;li&gt;Could your checks disagree, or do they share one input?&lt;/li&gt;
&lt;li&gt;What exactly is being counted, and who chose that?&lt;/li&gt;
&lt;li&gt;Are both sides of the comparison the same kind of thing?&lt;/li&gt;
&lt;li&gt;Does the number carry its scope, its treatment and its comparison?&lt;/li&gt;
&lt;li&gt;Is this the right question, and did anyone outside your own checks get a real chance to ask?&lt;/li&gt;
&lt;li&gt;Which of these answers is now buried in code, where nobody will see it as a choice?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pascal's review of that draft caught the problem. We never started with nine questions and applied them to nine cases. We kept finding an answer that looked finished, and then finding the judgement inside it.&lt;/p&gt;

&lt;p&gt;A clean list is exactly the kind of instrument this article warns about. Use it, and expect it to be hiding something too.&lt;/p&gt;

&lt;p&gt;The useful items cost a line of code each: a count printed next to a result, an assertion that two populations match, a mutant run once. The hard ones do not go away when you automate them. The judgement moves somewhere it is harder to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd try next
&lt;/h2&gt;

&lt;p&gt;The logging from the randomiser story is fixed and symmetric now. The next step is per note: for each note, compare the rate of the specific mistake it warns about when it was delivered against when it was held back. That turns "did the note arrive when it mattered" into arithmetic.&lt;/p&gt;

&lt;p&gt;It will not tell me I picked the right mistake to detect. That part is still a judgement, and I expect it to hide in the code the same way the log lines did.&lt;/p&gt;

&lt;p&gt;Neither of us would have found most of this alone. That is the other thing the thread showed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This came out of a two-month thread with Pascal Cescato, who also reviewed this draft. Thank you, Pascal.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>programming</category>
      <category>discuss</category>
      <category>ai</category>
    </item>
    <item>
      <title>Crystals: agent memory that arrives before you act</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Fri, 25 Sep 2026 20:44:57 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/crystals-agent-memory-that-arrives-before-you-act-27h8</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/crystals-agent-memory-that-arrives-before-you-act-27h8</guid>
      <description>&lt;p&gt;Most agent memory is a search box. The model wonders something, a retriever runs, chunks come back. That design has one load-bearing hole: &lt;strong&gt;retrieval fires only when the agent already suspects it needs help.&lt;/strong&gt; The expensive mistakes are the confident ones, and a confident agent never queries.&lt;/p&gt;

&lt;p&gt;So we push instead. A note gets bound to an &lt;em&gt;action&lt;/em&gt;, and it arrives in the moment before that action runs, whether or not anybody asked.&lt;/p&gt;

&lt;p&gt;We call these &lt;strong&gt;crystals&lt;/strong&gt;. The name earns its keep. A crystal has one axis, it formed under pressure, and what you get out is small and hard. One knowing per crystal. That constraint turns out to be the difference between this working and this quietly failing, for reasons I will get to.&lt;/p&gt;

&lt;p&gt;Before writing this up I went looking for prior art, expecting to find none. Expecting to find none is usually the sign you have not looked hard enough, and sure enough, two of the four ideas here are already published. &lt;a href="https://dev.to/nambok/your-agent-knows-your-preferences-it-just-never-uses-them-3ho5"&gt;Rules that fire on the action rather than on a similarity score&lt;/a&gt; have been described, right down to injecting before a git commit under a hook time budget. The agent-memory evaluation literature also has a failure taxonomy that comes close. &lt;a href="https://arxiv.org/pdf/2601.19935" rel="noopener noreferrer"&gt;Mem2ActBench&lt;/a&gt; attributes memory-grounded errors to five types, two of which sound like the one below: &lt;em&gt;Retrieved-but-Unused&lt;/em&gt;, "evidence is retrieved but not utilized", and &lt;em&gt;Lossless Retention Failure&lt;/em&gt;, "long/structured values are corrupted (e.g., truncation or character-level errors)".&lt;/p&gt;

&lt;p&gt;Read them closely, though, and both sit on the model's side of the boundary. Retrieved-but-Unused means the evidence reached the context window and the model ignored it. Lossless Retention Failure means the model mangled a long value on the way out. The failure I describe below happens earlier, in the plumbing: the note is correctly matched and then cut by the budget packer, so it never reaches the model at all. To the model it never existed; to the logs it was a hit.&lt;/p&gt;

&lt;p&gt;The budget problem is named too. A &lt;a href="https://arxiv.org/pdf/2602.06052" rel="noopener noreferrer"&gt;2026 survey of agent memory&lt;/a&gt; lists "curation under fixed budgets, including update, eviction" as an open direction, and asks for benchmarks that measure "memory quality as a function of token budget, storage cost, and latency, reflecting the bounded-memory conditions of real deployments."&lt;/p&gt;

&lt;p&gt;Which is a fair description of what follows, so treat this as a field report against that request rather than a new idea. What I have that the papers mostly do not is a channel that has been running long enough to overflow, and the numbers from the far side of that. Here is the mechanism, the budget it collides with, and the two rules we did not know we needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a crystal
&lt;/h2&gt;

&lt;p&gt;A crystal is a markdown file with a binding block at the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;crystal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash, write&lt;/span&gt;                              &lt;span class="c1"&gt;# which ACTS it fires on&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git reset --hard, git clean, rm -rf&lt;/span&gt;    &lt;span class="c1"&gt;# substrings in the act's text&lt;/span&gt;
  &lt;span class="na"&gt;deliver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;act&lt;/span&gt;                                 &lt;span class="c1"&gt;# the channel&lt;/span&gt;
  &lt;span class="na"&gt;stale_after&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2027-03-25&lt;/span&gt;                      &lt;span class="c1"&gt;# after this, the claim is withheld&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and, further down, the part that actually gets delivered, fenced by two markers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- crystal:essence --&amp;gt;&lt;/span&gt;
⛔ A DESTRUCTIVE VERB ACTS ON A TREE YOU SHARE. &lt;span class="sb"&gt;`git reset --hard &amp;lt;ref&amp;gt;`&lt;/span&gt; also
discards TRACKED-but-uncommitted changes; only UNTRACKED files survive...
&lt;span class="c"&gt;&amp;lt;!-- /crystal:essence --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file around it can run three thousand words of history, rationale and dead ends. Only what sits between those markers reaches the model. So the crystal is the &lt;em&gt;delivered&lt;/em&gt; sentence, and the file is where it came from. That split matters: the file is for whoever is arguing about the rule later, the essence is for whoever is about to break it in four seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks, and what an "act" is
&lt;/h2&gt;

&lt;p&gt;A hook fires on the tool call before it executes. Which tool it was determines the act, and the act determines what a crystal can match against:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;act&lt;/th&gt;
&lt;th&gt;fires&lt;/th&gt;
&lt;th&gt;what &lt;code&gt;match:&lt;/code&gt; tests against&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;before a shell command&lt;/td&gt;
&lt;td&gt;the command line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;before a file write or edit&lt;/td&gt;
&lt;td&gt;the path, plus the content being written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delegate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;before briefing a subagent&lt;/td&gt;
&lt;td&gt;the brief&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;inside the git pre-commit hook&lt;/td&gt;
&lt;td&gt;the staged file list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;when a request arrives, before any tool runs&lt;/td&gt;
&lt;td&gt;the request text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;boot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;at session start&lt;/td&gt;
&lt;td&gt;nothing yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The third column is where the power sits. &lt;code&gt;write&lt;/code&gt; can see content, so a crystal about pricing claims fires on the pricing claim itself and leaves your other edits alone. &lt;code&gt;prompt&lt;/code&gt; fires earliest of all: the crystal lands before the first tool call, which is the only place you can still change the plan rather than the keystroke.&lt;/p&gt;

&lt;p&gt;Matching stays deliberately dumb. &lt;code&gt;match:&lt;/code&gt; is a comma list of plain substrings, tested case-insensitively, with no word boundaries and no embedding anywhere in the path. A substring test stays auditable at a glance and cannot drift, which is what you want from something firing three hundred times a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like when a crystal fires
&lt;/h2&gt;

&lt;p&gt;Three real ones from a single day of work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before a shell command.&lt;/strong&gt; I typed a &lt;code&gt;git reset --hard&lt;/code&gt;. Before it ran:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⛔ &lt;strong&gt;A DESTRUCTIVE VERB ACTS ON A TREE YOU SHARE.&lt;/strong&gt; &lt;code&gt;git status --short&lt;/code&gt; first, and account for every line. Uncommitted work you did not make is an agent's.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Before a file write.&lt;/strong&gt; I began writing a note that made a causal claim. Before the write landed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⛔ If you are naming a CAUSE: write the RIVAL (what ELSE produces this exact observation?) and the DISCRIMINATOR (the one measurement that separates them). On 2026-07-19 four confident cause-claims were made in one evening; every one had evidence and every one was wrong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Before a commit.&lt;/strong&gt; In the pre-commit hook, matched against the staged file list:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⛔ &lt;strong&gt;FINISHING SOMETHING IS NOT THE SAME AS IT BEING RECORDED WHERE PEOPLE READ IT.&lt;/strong&gt; Name in the commit message everything the commit changed, especially a flag touched while doing something else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I asked for none of those. That is the entire point, because in each case I was not wondering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then you hit the budget
&lt;/h2&gt;

&lt;p&gt;There are 300-odd crystals. The median action matches &lt;strong&gt;five&lt;/strong&gt; of them, wanting &lt;strong&gt;13,014 characters&lt;/strong&gt;. The budget is &lt;strong&gt;4,000 characters per action&lt;/strong&gt;, and no single crystal may take more than 2,000 of it unless it wins outright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;93% of actions want more than the whole budget.&lt;/strong&gt; Three times oversubscribed, at the median. So the channel behaves like an auction that runs a few hundred times a day, and on almost every run something loses.&lt;/p&gt;

&lt;p&gt;That reframes the design question. Delivery was the easy half. The hard half is an admission rule, an eviction cost, and a way to degrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fair scheduling beats relevance here
&lt;/h3&gt;

&lt;p&gt;Selection goes least-served-first, then longest-unheard, then shortest as a packing tiebreak. A crystal that already fired this session steps aside for one that has not.&lt;/p&gt;

&lt;p&gt;Relevance ranking sounds better and fails badly here. A handful of broad crystals win every auction while fifty go undelivered, which is what we measured before rotation existed.&lt;/p&gt;

&lt;h3&gt;
  
  
  A cheap tier that somebody wrote
&lt;/h3&gt;

&lt;p&gt;When a crystal cannot fit, it emits a one-line summary instead. We call that its TELL, and the median TELL runs 81 characters.&lt;/p&gt;

&lt;p&gt;We tested whether a TELL carries anything real. Take 20 crystals whose full text demonstrably changes what the agent does, and run a blind judge against three resolutions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what the model receives&lt;/th&gt;
&lt;th&gt;median chars&lt;/th&gt;
&lt;th&gt;behaviour preserved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;full essence&lt;/td&gt;
&lt;td&gt;2,302&lt;/td&gt;
&lt;td&gt;20 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;head, 750-char prefix&lt;/td&gt;
&lt;td&gt;631&lt;/td&gt;
&lt;td&gt;19 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;authored TELL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;81&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17 / 20&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;421-char prefix&lt;/td&gt;
&lt;td&gt;421&lt;/td&gt;
&lt;td&gt;12 / 20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things fall out, and I nearly published the second one backwards.&lt;/p&gt;

&lt;p&gt;An authored 81 characters beat an arbitrary 421-character prefix, 17 against 12, at a fifth the size. A prefix stops wherever the character count happens to land, which is mid-argument more often than not.&lt;/p&gt;

&lt;p&gt;But a &lt;em&gt;longer&lt;/em&gt; arbitrary prefix does fine: 750 characters keeps 19 of 20. So the enemy here turns out to be brevity itself. Cut lightly and curation barely matters; cut deep and it starts to matter enormously. Zero items flipped the other way at any resolution, which argues against judge noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it still breaks
&lt;/h2&gt;

&lt;p&gt;Head-first truncation is safe when a crystal carries one idea, because the claim leads. Give a crystal two ideas and the second one sits in the tail, where it silently vanishes.&lt;/p&gt;

&lt;p&gt;Across our corpus that comes to &lt;strong&gt;98,715 characters which are stored, correctly matched to an action, and withheld&lt;/strong&gt; on any act where rotation does not elevate them. Of 102 crystals over the per-item cap, 88 withhold at least one marked claim in the tail. Treat that 88 as an upper bound. Some tails turn out to be supporting evidence for the head's claim, and I have not separated those by hand.&lt;/p&gt;

&lt;p&gt;No retrieval metric has a name for this. The crystal is present, correctly selected, and cut. I found it by shipping the same bug twice in one session with the warning sitting one character-cap away from my eyes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correction, a few hours after publishing.&lt;/strong&gt; I originally wrote that this "logs like a hit and behaves like a miss." A reader asked whether I had actually read the papers I was citing here. I had not, so I went and ran the proper test on our own channel: the &lt;a href="https://arxiv.org/abs/2609.08279" rel="noopener noreferrer"&gt;restore counterfactual&lt;/a&gt;, which reinstates the withheld text and reruns the same judge. On 65 cut pairs, restoring the tail changed the judged action 9 times and lost it 5 times, &lt;strong&gt;p=0.424, not significant&lt;/strong&gt;. It also failed to beat a length-matched tail of unrelated text (p=0.388). So 98,715 is a true character count and &lt;strong&gt;not&lt;/strong&gt; a demonstrated loss, and the "behaves like a miss" half was my inference rather than my measurement. The study is underpowered at this n, so this is "not shown", not "shown absent". Half the cut pairs turn out to be residual: the tail would not have changed the action anyway.&lt;/p&gt;

&lt;p&gt;Match precision is the other open front, and this is where correcting myself cost the most. A blind two-judge study asked how often an admitted crystal actually changes the action, against &lt;strong&gt;random&lt;/strong&gt; same-act controls. The controls scored 0 of 41 both times, so against that bar the matcher is doing real work (p=0.0001 and p=0.0054). The crystals themselves scored somewhere between 20% and 49%.&lt;/p&gt;

&lt;p&gt;Then I read how &lt;a href="https://arxiv.org/pdf/2601.19935" rel="noopener noreferrer"&gt;Mem2ActBench&lt;/a&gt; builds its controls: not randomly, but as "hard negatives (distractor tools most semantically similar to the ground-truth tool)". Re-running ours that way, against the lexically nearest crystal that did &lt;strong&gt;not&lt;/strong&gt; match the act, the effect disappears. A matched crystal scores 35.4%, a hard negative 30.8%, &lt;strong&gt;p=0.549&lt;/strong&gt;. It beats a random draw (p=0.008) and not a near-miss. The hard-negative control is genuinely harder (p=0.029 against random), so the control works and the matcher is what failed it.&lt;/p&gt;

&lt;p&gt;Which suggests our matcher selects on &lt;strong&gt;topic&lt;/strong&gt; rather than on &lt;strong&gt;applicability&lt;/strong&gt;, and that our published precision result lived entirely in the gap between a random control and a good one. If you take one thing from this piece, take that: a control that is too easy manufactures a result.&lt;/p&gt;

&lt;p&gt;That range is embarrassing, and explaining it matters, because it turns out to be an artifact of my own apparatus. The 20% end was measured by showing the judge a 421-character excerpt, which the resolution study above had already identified as the worst representation tested. Shown fuller text, the same judge said 48.8%. I had measured relevance through a truncation, and I nearly published the truncation's score as the system's.&lt;/p&gt;

&lt;p&gt;Either way, roughly half the admitted crystals fail to earn their slot. Substring keys are the reason. A two-letter key like &lt;code&gt;pt&lt;/code&gt; matches nearly everything, and one loose key is how a single crystal loses the auction for everybody else.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two rules we did not have
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One knowing per crystal.&lt;/strong&gt; This reads as style advice and turns out to be load-bearing. Head-first truncation is only safe when the claim leads, and we learned that by watching second claims disappear. A crystal that has grown two ideas wants splitting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A crystal earns its binding by beating what it evicts.&lt;/strong&gt; Truth alone will not buy a slot. At three times oversubscribed, every new binding forcibly removes one that already matched, so admission is always a comparison against the incumbent.&lt;/p&gt;

&lt;p&gt;We put that to the test. I proposed binding eight orphaned notes. Two reviewers, briefed separately, independently came back with zero to two, and the admission test they both converged on was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does it change &lt;strong&gt;this&lt;/strong&gt; action?&lt;/li&gt;
&lt;li&gt;Can a tight key reach it without catching everything else?&lt;/li&gt;
&lt;li&gt;Is the act &lt;strong&gt;earlier&lt;/strong&gt; than the next reliable check that would catch the same mistake?&lt;/li&gt;
&lt;li&gt;Is there no cheaper enforcer?&lt;/li&gt;
&lt;li&gt;Does it beat the incumbent it evicts?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most candidates die on 4. A crystal about your own tooling usually wants to be an assert in the script instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you build one
&lt;/h2&gt;

&lt;p&gt;The core design holds. Pushing beats pulling for anything that must not be forgotten, precisely because the agent's sense of when it needs help is the faculty you cannot lean on.&lt;/p&gt;

&lt;p&gt;What we underestimated is that a push channel is a &lt;strong&gt;budget&lt;/strong&gt;, and a budget needs an economics. We built the delivery first and found the economics a month later, by measuring what never arrived.&lt;/p&gt;

&lt;p&gt;Measure that early. The useful question about a crystal is what did this displace, and did anyone ever read the part that got cut.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of this thread
&lt;/h2&gt;

&lt;p&gt;This piece is the mechanism. The others are the pieces of it I got wrong first, which is the more useful reading order if you are building one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/i-shipped-the-same-bug-twice-with-the-warning-sitting-one-character-cap-away-11ic"&gt;I shipped the same bug twice with the warning sitting one character-cap away&lt;/a&gt; is the incident that found the truncation, and where the 98,715 figure comes from.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/your-hooks-are-a-fence-they-could-be-a-body-966"&gt;Your hooks are a fence. They could be a body&lt;/a&gt; is the argument for hooks doing more than blocking, which is the design this whole channel came out of.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/whole-notes-not-fragments-the-retrieval-half-58ni"&gt;Whole notes, not fragments: the retrieval half&lt;/a&gt; is the pull side. Crystals are the push side, and the two channels answer different questions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/escalating-to-the-better-model-made-34-answers-worse-ko7"&gt;Escalating to the better model made 34 answers worse&lt;/a&gt; is unrelated to memory and lands on the same methodological point: the aggregate hid the failure, and only stratifying showed it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the gateway underneath all of this, &lt;a href="https://tirtha.ai" rel="noopener noreferrer"&gt;Tirtha&lt;/a&gt; is free to test, no card, 100 requests a month per key, raised on request.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>llm</category>
    </item>
    <item>
      <title>I shipped the same bug twice with the warning sitting one character-cap away</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Fri, 25 Sep 2026 19:58:29 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/i-shipped-the-same-bug-twice-with-the-warning-sitting-one-character-cap-away-11ic</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/i-shipped-the-same-bug-twice-with-the-warning-sitting-one-character-cap-away-11ic</guid>
      <description>&lt;p&gt;Our agent memory pushes short notes to the model at the moment it acts. No vector search at question time; a note bound to a shell command or a file write, delivered before the command runs.&lt;/p&gt;

&lt;p&gt;Yesterday I shipped the same bug twice in one session. A string replacement that matches nothing returns the original unchanged, so the patch looks applied, the script prints a cheerful line, and the file is byte-identical. I did it, caught it, fixed it, and did it again ninety minutes later.&lt;/p&gt;

&lt;p&gt;Afterwards I went looking for the note we were missing, expecting to write it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The note existed
&lt;/h2&gt;

&lt;p&gt;It was bound to exactly the right trigger. It described the bug precisely, down to the same function call, with the incident that produced it. Selection worked. Matching worked.&lt;/p&gt;

&lt;p&gt;On that action it is delivered as &lt;strong&gt;675 characters of 3,751&lt;/strong&gt;. Each item is capped, the cut takes the head, and the sentence I needed sits in the tail.&lt;/p&gt;

&lt;p&gt;So the warning was written and bound and matched, then cut. I made the mistake it describes, twice, with it one cap away.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much of this is there
&lt;/h2&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%2Fbpaltrqoagshzwi363vp.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%2Fbpaltrqoagshzwi363vp.png" alt="Across 102 oversized notes: 98,715 characters are stored and correctly matched and never delivered, and 88 of the 102 withhold at least one load-bearing claim" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;98,715 characters are stored, correctly matched to an action, and never delivered.&lt;/strong&gt; 88 of 102 oversized notes withhold at least one load-bearing claim that way.&lt;/p&gt;

&lt;p&gt;I went looking for what was missing from the channel. Nothing was missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why head-first truncation is the wrong instinct here
&lt;/h2&gt;

&lt;p&gt;Cutting from the front is defensible. A note leads with its claim, so the head carries the point and the tail carries the evidence. We measured whether that holds.&lt;/p&gt;

&lt;p&gt;One judge, 41 real pairs of an action and a note, the same pair shown at three resolutions, scored on a single question: would a competent session do anything differently because it read this? Of the 20 whose full text changes the action:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what the reader gets&lt;/th&gt;
&lt;th&gt;median chars&lt;/th&gt;
&lt;th&gt;knowings preserved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;the full note&lt;/td&gt;
&lt;td&gt;2,302&lt;/td&gt;
&lt;td&gt;20 of 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;first 750 characters&lt;/td&gt;
&lt;td&gt;631&lt;/td&gt;
&lt;td&gt;19 of 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;an authored one-line summary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;81&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17 of 20&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;first 421 characters&lt;/td&gt;
&lt;td&gt;421&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12 of 20&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things fall out of that table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A curated 81 characters beat an arbitrary 421 at a fifth of the cost.&lt;/strong&gt; A prefix cuts wherever the character count lands, usually mid-preamble, so it can hide the claim while costing five times more than the sentence that states it. The cheap tier of any budgeted channel should be the authored line, never a truncation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And head-first is safe for one idea and lossy for two.&lt;/strong&gt; The 19-of-20 result holds when the claim leads. It stops holding when a note bundles two claims, because the second one lives in the tail and goes silently. 88 of our 102 oversized notes were doing exactly that.&lt;/p&gt;

&lt;p&gt;The repair is one idea per note, which is the rule that makes head-first safe in the first place. Cleverer cutting buys nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the harder question: is the channel too small, or are we
&lt;/h2&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%2Fao6gy5e5x33427hdod8d.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%2Fao6gy5e5x33427hdod8d.png" alt="The median action matches five notes wanting 13,014 characters against a 4,000 character budget" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replaying 95 real actions through the live selector: the median action matches &lt;strong&gt;five notes wanting 13,014 characters against a 4,000 character budget.&lt;/strong&gt; 93% of actions want more than the whole budget. Two thirds of everything that matches is starved, always, by construction.&lt;/p&gt;

&lt;p&gt;The obvious fix is a no-op. Capping every note at 2,000 characters moves delivery from 35.7% to 35.7%, because the packer already caps there. Gains only start below 1,500, and our median note is 1,759.&lt;/p&gt;

&lt;p&gt;So we measured the other lever. Two blind judges, 41 pairs of an action and a note the matcher &lt;strong&gt;admitted&lt;/strong&gt;, against 41 pairs of the same actions with a random unmatched note. Neither judge saw which was which.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;admitted notes that change the action&lt;/th&gt;
&lt;th&gt;random controls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;judge A&lt;/td&gt;
&lt;td&gt;13 of 41&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 of 41&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;judge B&lt;/td&gt;
&lt;td&gt;8 of 41&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 of 41&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero on the controls, twice, independently. Without that the other column would mean nothing: a judge that says yes to 20% of real matches tells you nothing until you know it says yes to none of the random ones.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction, same day, and this one cost more.&lt;/strong&gt; The word doing the work above is &lt;strong&gt;random&lt;/strong&gt;. I later read how &lt;a href="https://arxiv.org/pdf/2601.19935" rel="noopener noreferrer"&gt;Mem2ActBench&lt;/a&gt; builds controls, and it draws hard negatives: the distractor most similar to the right answer. Re-running ours that way, against the nearest note that did &lt;strong&gt;not&lt;/strong&gt; match the act, the effect disappears. Matched scores 35.4%, a hard negative 30.8%, &lt;strong&gt;p=0.549&lt;/strong&gt;. It beats a random draw (p=0.008) and not a near-miss. The hard-negative control is genuinely harder (p=0.029 against random), so the control works and the matcher is what failed it. Our whole published effect lived in the gap between a lazy control and a good one, which suggests the matcher selects on topic rather than on applicability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The matcher carries real signal and admits roughly three notes for every one that belongs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-character key
&lt;/h2&gt;

&lt;p&gt;One note matched 44% of every action we replayed. Its match key was &lt;code&gt;pt&lt;/code&gt;, meant as typographic points, on a note about document spacing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pt&lt;/code&gt; is inside &lt;code&gt;scripts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That note was also the single most-starved item in the corpus. It matched nearly everything, lost nearly every packing contest, and on the occasions it won, both judges rejected it. One key, three symptoms. Its neighbour was &lt;code&gt;-h&lt;/code&gt;, meant as the help flag, which is inside &lt;code&gt;--hard&lt;/code&gt;, so it fired on &lt;code&gt;git reset --hard&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Removing two keys took matches from 737 to 708 and actions starving from 72 to 62, with no real note lost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our first fix silently did nothing.&lt;/strong&gt; We changed &lt;code&gt;-h&lt;/code&gt; to &lt;code&gt;" -h"&lt;/code&gt; with a leading space. The parser splits the key list on commas and strips each key, so it stored &lt;code&gt;-h&lt;/code&gt; straight back. The config read correctly and the behaviour never changed, which is the same failure shape as the bug that started this piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  What earns a place
&lt;/h2&gt;

&lt;p&gt;We had 20 notes written but bound to nothing, and proposed binding eight of them. Two reviewers, briefed separately, never shown each other's answers, independently said zero to two. They also produced nearly the same admission test, which we lacked and could not state:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Act-synchronous.&lt;/strong&gt; It must change this action before it executes. If it can wait for someone to search, it is a lookup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matcher-reachable.&lt;/strong&gt; Stable substrings in the action predict the failure. If a tight key is impossible without spraying every action, the channel is the wrong home for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Earlier than the next reliable check.&lt;/strong&gt; If a commit hook already catches it, the push is worth only the gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cheaper enforcer.&lt;/strong&gt; Unreachable by a gate, a linter, a type, or a default in the one code path. Notes about your own tooling almost always fail here: put the assert in the script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It must beat what it evicts.&lt;/strong&gt; At three times oversubscribed, a new binding forcibly removes one that already matched.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"This is true and nothing delivers it" fails as an admission case. That was our criterion, and it was the word "cross-cutting", which defines nothing.&lt;/p&gt;

&lt;p&gt;What shipped instead of eight bindings was one sentence added to a note that already fired on those actions. Measured cost: three deliveries lost across 97 actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we would tell you to check
&lt;/h2&gt;

&lt;p&gt;Ask what happens if retrieval misses a fact. Then ask the second question, the one we lacked: &lt;strong&gt;what happens if it hits and arrives partial.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A partial arrival is indistinguishable from an arrival at the point of use. It will not appear in a retrieval metric, a hit rate, or a log line, because every one of those reports that the right thing was selected. It was.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction, same day.&lt;/strong&gt; This sentence originally ended "and behaves exactly like a miss." I had not measured that, I had inferred it. So I went and ran the &lt;a href="https://arxiv.org/abs/2609.08279" rel="noopener noreferrer"&gt;restore counterfactual&lt;/a&gt;: put the withheld text back, rerun the same blind judge, 65 cut pairs. Restoring it changed the judged action 9 times and lost it 5 times, &lt;strong&gt;p=0.424&lt;/strong&gt;, and it did not beat a length-matched tail of unrelated text (p=0.388). Half the cut pairs turn out to be residual, meaning the tail would not have changed the action anyway. So 98,715 is a true character count and the harm is &lt;strong&gt;not demonstrated&lt;/strong&gt;. The study is underpowered at that n, so this is "not shown" rather than "shown absent", but I am not entitled to the stronger sentence and it is gone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where this could be wrong
&lt;/h2&gt;

&lt;p&gt;The resolution study is one judge and 20 action-changing pairs. 17 of 20 is a wide interval. Trust the ORDER of those rows and treat the exact percentages as soft. A second judge would move them, since on the precision study two judges differed by 12 points on identical items.&lt;/p&gt;

&lt;p&gt;The precision figure is a range, 20 to 49 percent, and its low end is the weaker measurement: it was taken through a 421-character excerpt, which the table above shows is the worst representation we tested. We published 31.7% first and corrected it the same day.&lt;/p&gt;

&lt;p&gt;The marker counts behind "98,715 characters withheld" are an upper bound. Some tails are evidence for the head's claim instead of a second claim, and nobody read all 102 by hand.&lt;/p&gt;

&lt;p&gt;And the twenty unbound notes were found because they happened to carry a marker in the file. An accident of how they were written, and no evidence they are the highest-value missing pieces. The ones that matter may carry no marker at all, and we still have no way to look for those.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of this thread
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/crystals-agent-memory-that-arrives-before-you-act-27h8"&gt;Crystals: agent memory that arrives before you act&lt;/a&gt; is the mechanism this piece assumes: what the notes are, how hooks bind them to an action, and what the budget does. Read that one first if none of the above made sense.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/your-hooks-are-a-fence-they-could-be-a-body-966"&gt;Your hooks are a fence. They could be a body&lt;/a&gt; is where the design started.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/tom_jones_230c4659491adcd/whole-notes-not-fragments-the-retrieval-half-58ni"&gt;Whole notes, not fragments: the retrieval half&lt;/a&gt; is the pull channel, which fails differently.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>llm</category>
    </item>
    <item>
      <title>Escalating to the better model made 34 answers worse</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Fri, 25 Sep 2026 19:51:56 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/escalating-to-the-better-model-made-34-answers-worse-ko7</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/escalating-to-the-better-model-made-34-answers-worse-ko7</guid>
      <description>&lt;p&gt;The standard picture of a cheap AI system is a ladder. Small model at the bottom, big one at the top, switch up when the work looks hard. Almost every cost-saving story is a version of that, and the whole design question collapses into "when do I escalate".&lt;/p&gt;

&lt;p&gt;We built that. Then we published that our router for it does not work. This is the follow-up we owed. Our router failing was the least of it: the ladder itself is the wrong axis to argue about, and we can now show that with a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four layers
&lt;/h2&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%2Fqfed9zabqdvzoikv2ekr.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%2Fqfed9zabqdvzoikv2ekr.png" alt="Four places to spend effort and what each one moved: which model you pick, what you feed it, which shape the task is, and whether the rule arrives at all" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every number in that figure is from our own runs, and the rest of this piece walks down it. Layer 1 is the only one a ladder can reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: which model you pick
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The result that changed our mind
&lt;/h3&gt;

&lt;p&gt;We ran 2,400 tasks through both rungs. Same tasks, both arms pinned, three repetitions each, so every task has a cheap answer and a frontier answer side by side.&lt;/p&gt;

&lt;p&gt;The frontier tier wins. It is 96.9% against 92.8%, an advantage of &lt;strong&gt;4.12 points, 95% CI +3.12 to +5.21&lt;/strong&gt;, McNemar exact p below 0.0001. It replicates an earlier, weaker result of ours that had never been demonstrated properly.&lt;/p&gt;

&lt;p&gt;Then look at what is inside those 4.12 points.&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%2F1ynounyscie7u41lvxzh.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%2F1ynounyscie7u41lvxzh.png" alt="What escalating to the frontier tier actually did across 2,400 paired tasks: it fixed 133 and broke 34" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Escalation fixed 133 answers and broke 34.&lt;/strong&gt; The net is +99, which is where the 4.12 points come from. But 34 tasks were answered correctly by the cheap model and incorrectly by the expensive one.&lt;/p&gt;

&lt;p&gt;The obvious objection is that those 34 are noise, and we checked. Of them, &lt;strong&gt;10 were right in all three cheap repetitions and wrong in all three frontier repetitions.&lt;/strong&gt; Sampling noise would have to land the same way six times running to produce that. Those are a model-by-item interaction: on those items the more capable model is reliably worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So escalation is not monotone.&lt;/strong&gt; Going up the ladder hands you a different distribution of right and wrong, so you trade one set of failures for another set you have never looked at.&lt;/p&gt;

&lt;p&gt;Every escalation gate ever built assumes monotonicity. That is what "escalate when it is hard" means: up is better. On our own production ladder, measured, up is better on average and worse 34 times out of 2,400.&lt;/p&gt;

&lt;h3&gt;
  
  
  What those points cost
&lt;/h3&gt;

&lt;p&gt;The other half is the price. Measured from each row's own token counts, the top rung costs &lt;strong&gt;$0.1114 per additional correct answer over the bottom rung, about 21 times&lt;/strong&gt; the cost per answer. An independent path through the vendor invoices came out at $0.1102 and 22.5 times, which is the only reason we trust the first figure.&lt;/p&gt;

&lt;p&gt;Latency moves with it, from a p50 of 0.87s on the cheap rung to 5.99s on the frontier one, about seven times.&lt;/p&gt;

&lt;p&gt;The ladder's whole proposition comes to this. Pay 21x, wait 7x longer, gain four points, and reproducibly break a few answers that were already right.&lt;/p&gt;

&lt;p&gt;That can still be worth buying. It is a purchase at a price, and there are bigger levers in the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: what you feed it
&lt;/h2&gt;

&lt;p&gt;This is the one that reframed the project for us, and it is the direct comparison to the ladder.&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%2Fmkujdan9o2fk5n2yylqx.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%2Fmkujdan9o2fk5n2yylqx.png" alt="Tokens to answer the same question with the same model: unfed 39,458 versus fed 1,337, identical accuracy" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same model. Same tasks. Two arms. In the first, the agent found its own context: real navigation in a read-only sandbox, grepping and reading, which is what your agent does right now. In the second, we handed it the one right note and asked it to answer from that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mean 39,458 tokens down to 1,337. About thirty times cheaper, at identical accuracy, 75% in both arms.&lt;/strong&gt; Eight tasks, one repository, judged the same way in both arms, and our test repo is small, so read thirty times as a floor rather than a headline.&lt;/p&gt;

&lt;p&gt;Put that beside the ladder. Switching rungs bought four points for 21 times the cost per answer. Changing what we fed the same model cut cost by about thirty times at the same accuracy.&lt;/p&gt;

&lt;p&gt;The delivery layer wins by roughly an order of magnitude.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: which shape the task is, and whether your instrument can see it
&lt;/h2&gt;

&lt;p&gt;This layer is where we caught ourselves, three hours before publishing, about to say something false about a named vendor's flagship model.&lt;/p&gt;

&lt;p&gt;Separately from the ladder work we ran six frontier models over 120 standard tool-calling tasks and 120 trapped ones, twice, because a single-run ranking is noise even at temperature zero. Two runs on the identical item list, verified identical by ID and order, gives a run-to-run movement of at most 3 points on the standard set and 6 on the traps.&lt;/p&gt;

&lt;p&gt;The trap half is twelve shapes of ten items. Nine came out at nine or ten of ten for every model, so they separate nobody. The whole between-model spread, from 119 of 120 down to 100, sat in the remaining three.&lt;/p&gt;

&lt;p&gt;We had a story ready. The most expensive model in the set scored 1 of 10, twice, on a shape whose prompt is "I need to get to Austin. I'll be setting out from Dallas, give me driving directions." We were going to report that it inverts origin and destination nine times in ten.&lt;/p&gt;

&lt;p&gt;Then a reviewer pointed out that our harness never scored &lt;em&gt;inversions&lt;/em&gt;. It scored &lt;em&gt;not equal to gold&lt;/em&gt;. Two different claims, and only one of them had been measured.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it actually returned
&lt;/h3&gt;

&lt;p&gt;So we read the arguments the model produced. Across all 18 failures, in both runs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what the model actually returned&lt;/th&gt;
&lt;th&gt;count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;origin and destination inverted&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;correct cities, with the state added&lt;/strong&gt;: gold &lt;code&gt;Dallas&lt;/code&gt;, predicted &lt;code&gt;Dallas, TX&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;two calls, disambiguating Portland OR from Portland ME&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It had the origin and the destination right every single time. Our gold was a bare city string and our scorer did exact match, so "Dallas, TX" was marked wrong. The two models that scored a perfect 10 of 10 did so by echoing the bare city name.&lt;/p&gt;

&lt;p&gt;That shape measures whether a model writes the state abbreviation, and says nothing about tool calling.&lt;/p&gt;

&lt;p&gt;One of the other three shapes was already flagged in our own notes, seven weeks before this run, as having an ambiguous gold. So we removed both and scored the remaining ten shapes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;trap score of 100, run 1 / run 2&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;sonnet-5 100/100 · gpt-5.2 100/99 · gpt-5.1 99/99&lt;/td&gt;
&lt;td&gt;grok-4.5 99/99 · opus-5.5 96/98 · grok-4.7 96/97&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Ninety-six to one hundred. A four-point spread, against a six-point run-to-run floor.&lt;/strong&gt; Six frontier models come out indistinguishable on our trap suite, and every apparent difference was our own formatting preference scored as capability.&lt;/p&gt;

&lt;p&gt;The lesson belongs to us rather than to any model: &lt;strong&gt;an exact-match gold on a free-text argument measures your house style and reports it as capability.&lt;/strong&gt; You can check for it before publishing: if one shape carries most of your spread, suspect the scoring first, because real capability differences rarely concentrate that neatly.&lt;/p&gt;

&lt;p&gt;And it is the same point as the rest of this piece, one level up. The aggregate hid the shape. The shape hid the scorer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then we ran it properly, and the models do separate
&lt;/h3&gt;

&lt;p&gt;The trap conclusion above stands on its own, and it is about the traps. The other half of that pilot was 120 standard tasks, and the flag we used to limit the run happened to draw all 120 from the easiest of three categories. We had been about to let "indistinguishable on traps" sit next to a thin standard set and read as a statement about the models.&lt;/p&gt;

&lt;p&gt;The full run is 800 standard tasks per model across three categories, plus the traps. It changes the answer.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;category&lt;/th&gt;
&lt;th&gt;spread across the six models&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;simple, one call&lt;/td&gt;
&lt;td&gt;10.8 points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;multiple, several tools offered&lt;/td&gt;
&lt;td&gt;6.0 points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;parallel, several calls required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;32.5 points&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Fz98yd9atxxzsb675k2zt.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%2Fz98yd9atxxzsb675k2zt.png" alt="Spread between the best and worst of six frontier models, by category: parallel calls 32.5 points, simple 10.8, multiple 6.0" width="799" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The category the pilot skipped is the only one that separates anybody. And inside it there is a result worth the whole run: one model answered the first sub-request and stopped. Asked for two calls, it sent one, on &lt;strong&gt;60 of 200&lt;/strong&gt; tasks. Every other model did that between 1 and 9 times.&lt;/p&gt;

&lt;p&gt;Its per-category scores are 86.8% on simple, 88.0% on multiple, &lt;strong&gt;61.0% on parallel.&lt;/strong&gt; The aggregate reads 80.6% and sounds like "somewhat weaker". The truth is "competitive on single calls, unusable for parallel work", and only the per-category read says so.&lt;/p&gt;

&lt;p&gt;We checked this one against the lesson above before believing it, because two findings the previous night had turned out to be scorer artifacts. On the exact 78 parallel items that model failed, the other five score 55 to 66 of 78, so the items are answerable. What fails is the call count, and the arguments it does send are fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the sample size was the whole story on whether anything was visible at all.&lt;/strong&gt; At 220 items, zero of the fifteen model pairs separated. At 800, eight of fifteen do. The bootstrap intervals still refuse to rank the top four against each other, which is its own honest result: the models that look best are tied, and the gap that matters is further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 4: whether the rule arrives at all
&lt;/h2&gt;

&lt;p&gt;The last layer is the one nobody benchmarks, and it has the largest failure rate of the four.&lt;/p&gt;

&lt;p&gt;A rule only helps if it reaches the model at the moment it is needed. We measured retrieval surfacing a safety-relevant rule &lt;strong&gt;two times out of three.&lt;/strong&gt; Always-on delivery scored 100%.&lt;/p&gt;

&lt;p&gt;Two in three is worse than useless, and the miss is the smaller problem. The two successes are the bigger one, because they teach you to rely on it. So anything that must hold, holds by construction and never goes through the clever part.&lt;/p&gt;

&lt;p&gt;Model choice cannot reach this layer at all. Put the best model in the world here and it answers confidently, without the rule nobody showed it.&lt;/p&gt;

&lt;h3&gt;
  
  
  A third failure mode, found by making the same mistake twice
&lt;/h3&gt;

&lt;p&gt;We push short notes to the agent, bound to the action it is about to take, instead of hoping retrieval finds them. That fixes the two-in-three. It introduces something else.&lt;/p&gt;

&lt;p&gt;Yesterday I shipped the same bug twice in one session: a string replacement that matches nothing returns the original unchanged, so the patch looks applied and does nothing. Afterwards I went looking for the note we must be missing.&lt;/p&gt;

&lt;p&gt;The note existed. It was bound to exactly the right trigger. It described the bug precisely, down to the same function call. And on that action it is delivered as 675 characters of 3,751, because the channel caps each item, and the sentence I needed sits past the cut.&lt;/p&gt;

&lt;p&gt;Across the whole set that is &lt;strong&gt;98,715 characters which are stored, correctly matched to an action, and never delivered.&lt;/strong&gt; 88 of 102 oversized notes withhold at least one load-bearing claim that way.&lt;/p&gt;

&lt;p&gt;The reason is worth more than the number. Truncating from the front is safe when a note carries one idea, because the claim leads. It is silently lossy when a note carries two, because the second one lives in the tail. Our fix is one idea per note, which beats any cleverness in the cutting.&lt;/p&gt;

&lt;p&gt;So Layer 4 has three states instead of two. The rule can be missing. The rule can arrive. And the rule can be present, correctly selected, and cut, which looks exactly like arriving from the inside and behaves exactly like missing. Ask what happens if retrieval misses a fact, and then ask the second question: what happens if it hits and arrives partial.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual claim
&lt;/h2&gt;

&lt;p&gt;Models differ enormously, and Layer 3 is the proof: a 32-point spread on parallel calls against 6 on multiple, in the same six models, on the same day. One of them drops to 61% on a category where it scores 87 and 88 on the other two.&lt;/p&gt;

&lt;p&gt;That proof has changed under us. The ninefold gap we opened with turned out to be our own scorer, and what replaced it came from a bigger run that we had to go and do. We kept the number that survived being attacked and discarded the one that failed, and they happened to point the same way, which is the only reason the section still has a point to make.&lt;/p&gt;

&lt;p&gt;The claim is that &lt;strong&gt;a ladder optimises the wrong axis.&lt;/strong&gt; A ladder is a scalar, cheap at the bottom and capable at the top, with one dial. The variation we can actually measure runs along three other axes: which shape the task is, what context you supplied, and whether the constraint arrived at all. A ladder has a knob for none of them.&lt;/p&gt;

&lt;p&gt;Which is why "just use the better model when it matters" keeps almost working. It is directionally right on the aggregate, and blind in exactly the places the aggregate is blind.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we do instead
&lt;/h2&gt;

&lt;p&gt;Verify rather than select, because a cheap answer that passed a check is worth more than an expensive answer nobody checked, and checking costs less than escalating. Feed rather than let it search, because that is where the thirty times lives. Push the things that must hold instead of hoping retrieval surfaces them. And read results per shape, never per aggregate, because a 1-in-10 failure goes to the aggregate to hide.&lt;/p&gt;

&lt;p&gt;Every one of those is a system decision rather than a model decision, which is the thesis: &lt;strong&gt;the model is one component, and the biggest gains sit elsewhere.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this could be wrong, including two places we already were
&lt;/h2&gt;

&lt;p&gt;Our router failing is evidence about our router. It is equally consistent with "prediction-based escalation is hard" and with "we built it badly", and we cannot separate those.&lt;/p&gt;

&lt;p&gt;The token result is n=8, one repository, one agent. Large effect, small sample.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We already published one piece of routing advice off this data and had to retract it.&lt;/strong&gt; An earlier read said "escalate simple tasks, hold back parallel ones". That came from 4 of 5 observed breaks being parallel. At n=2,400 the real figure is 11 of 34, and parallel is net positive by 25. &lt;strong&gt;Anyone who followed our advice would have withheld escalation from the category it helps second most.&lt;/strong&gt; Five events looked like a pattern and were not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And a second one, this week.&lt;/strong&gt; A re-pricing showed the rung we call cheap costing 3.4 times the rung we call value while getting fewer answers right, and we very nearly shipped that as "strictly dominant, swap them". A per-category read killed it: the value rung wins parallel and simple calls and &lt;strong&gt;fails 8 of 40 safety traps that the other passes 40 of 40&lt;/strong&gt;, on one disposition that produces both results. The win and the loss are the same behaviour, so you cannot take half of it. Which is, again, the thesis: the honest answer was per shape, not per level.&lt;/p&gt;

&lt;p&gt;Finally, the shape work here rests on a harness that, until we fixed it this week, &lt;strong&gt;was misnaming its own trap shapes.&lt;/strong&gt; The label was arithmetic that assumed the shape numbers ran contiguously; they skip two. Every stored result named shapes from the third onward after a different trap. We caught it because a row's payload disagreed with its label, and it mattered: we were one step from publishing that the expensive model calls tools users explicitly forbade nine times in ten. It does not. The mislabelled shape was the directions one, which is an argument-formatting story instead of a safety story, and only reading the payload separated them.&lt;/p&gt;

&lt;p&gt;Then the same move had to be made a second time on the corrected version, because "it inverts origin and destination" was also false, as the table in Layer 3 shows. Two readings of that shape were wrong before the one that held. The first cost a safety claim about a named vendor, the second an ordering claim, and both died the same way, by opening the arguments the model actually produced.&lt;/p&gt;

&lt;p&gt;We added a check tying every label to the human-written description of that shape, then mutation-tested it against the old code to watch it go red. A measurement system that cannot catch its owners being wrong will not catch you being wrong either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run your own numbers against ours
&lt;/h2&gt;

&lt;p&gt;Everything above is our system measured by us, which is the weakest position a claim can be in. If you want to check any of it, the endpoint is open.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://api.tirtha.ai/v1&lt;/code&gt;, model &lt;code&gt;tirtha/verified&lt;/code&gt;, OpenAI-compatible, so it is a base_url change and nothing else. Sign in with Google at &lt;a href="https://tirtha.ai" rel="noopener noreferrer"&gt;tirtha.ai&lt;/a&gt;, mint a key, no card and no sales call. A free key is capped at 100 requests a month, and we will raise it for anyone who asks.&lt;/p&gt;

&lt;p&gt;What would be genuinely useful is the thing this piece is about: point it at YOUR task distribution, split the results per shape rather than per aggregate, and tell us where it does worse. A category where we lose is worth more to us than another run where we win, because the whole argument here is that the aggregate hides the shape.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>architecture</category>
      <category>benchmarking</category>
    </item>
    <item>
      <title>An index keyed to the diagnosis is a glossary</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Fri, 25 Sep 2026 00:09:26 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/an-index-keyed-to-the-diagnosis-is-a-glossary-4egg</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/an-index-keyed-to-the-diagnosis-is-a-glossary-4egg</guid>
      <description>&lt;p&gt;We published &lt;a href="https://github.com/Tirthahq/crystal-memory/tree/master/catalogue" rel="noopener noreferrer"&gt;a catalogue of verification failure modes&lt;/a&gt;, the ways a system reports green while being broken. Someone asked us for it, so we built it. Then he tested it blind. It failed, and the shape of the failure is worth more than the catalogue is.&lt;/p&gt;

&lt;p&gt;The coverage was fine. The lookup failed. You could only find the entry describing your bug once you already knew what your bug was.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test we did not run, run by the person who asked for it
&lt;/h2&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/mansio"&gt;@mansio&lt;/a&gt; requested the catalogue on dev.to. After we shipped it he went and measured it, and his design is better than the one we would have written for ourselves.&lt;/p&gt;

&lt;p&gt;Eleven runs. Three models. A frozen list of ten symptoms taken from his own repository and written down &lt;strong&gt;before&lt;/strong&gt; he looked anything up. Six controls in every single run: three paraphrases of entries that are definitely in the catalogue, which must hit, and three symptoms from outside our domain entirely, which must return NONE.&lt;/p&gt;

&lt;p&gt;These are his numbers, from his run. We did not run it and did not watch it run.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;measure&lt;/th&gt;
&lt;th&gt;result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;controls correct&lt;/td&gt;
&lt;td&gt;10 of 11 runs, all six&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the one failing run&lt;/td&gt;
&lt;td&gt;a low reasoning setting matched two out-of-domain symptoms to real entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the arrival symptom, "my agent won't use my high-level tools"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;NONE, ten times out of ten&lt;/strong&gt;, across all three models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reproducibility&lt;/td&gt;
&lt;td&gt;one model gave the same answer 8 of 10 times, another flipped 6 of 10, same index, same list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a genuine hole&lt;/td&gt;
&lt;td&gt;a hung process leaking file handles returned NONE every run, and no family covers an OS-level leak&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;He was careful about what his own numbers could support, and we have kept his caution in place here. The "correct" answer for each of his ten items came from his judgement, so we treat the per-item hit rate as an impression and leave it out of every claim below. Two kinds of number survive that: the ones where the instrument &lt;strong&gt;declined&lt;/strong&gt;, since a decline is objective, and the control results, since the controls have right answers.&lt;/p&gt;

&lt;p&gt;His summary: the families stretch far enough, so coverage is real; lookup from the arrival symptom is unreproducible; and for his own case it stayed silent throughout.&lt;/p&gt;

&lt;p&gt;Then he said the thing that made this an article. An unreliable lookup is an instance of one of our own catalogue entries, &lt;a href="https://github.com/Tirthahq/crystal-memory/blob/master/catalogue/a-single-run-ranking-is-noise-even-at-temp-zero.md" rel="noopener noreferrer"&gt;the one about single-run rankings being noise even at temperature zero&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We wrote the entry, shipped the index, and never ran the index against the entry.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it missed, measured on our own artifact
&lt;/h2&gt;

&lt;p&gt;That is the half we could run ourselves, and it took about twenty minutes.&lt;/p&gt;

&lt;p&gt;Every one of the twelve &lt;code&gt;symptom:&lt;/code&gt; lines in the catalogue as it stood that day takes an instrument or an artifact as its grammatical subject. The suite. The file. The guard. The harness. The ranking. The sampling rate. The figure. The before-and-after. Across all twelve, the count of lines whose subject is an actor's behaviour is zero.&lt;/p&gt;

&lt;p&gt;We then swept all thirteen files it then held for the vocabulary a person actually arrives with. Word-bounded, for things like "ignores", "won't use", "keeps reaching", "lazy". One hit across the whole catalogue, in a line buried well past the point of arrival.&lt;/p&gt;

&lt;p&gt;So every symptom line presupposes that the reader has already localised the problem to an instrument. That is the state you reach &lt;strong&gt;after&lt;/strong&gt; you have dug. The person who needs an index is in the state before that, holding a complaint about behaviour, several steps upstream of any hypothesis about a harness.&lt;/p&gt;

&lt;p&gt;An index keyed to the diagnosis is a glossary. It becomes genuinely useful once you know the answer, and stays silent through the one moment that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The repair, and the constraint that makes it honest
&lt;/h2&gt;

&lt;p&gt;The tempting move is to add entries, and coverage was already the half that worked. What it wanted was an arrival layer: lines written in complaint vocabulary, each pointing into the family it belongs to.&lt;/p&gt;

&lt;p&gt;We put one constraint in public, in the thread, which is what makes it binding on us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has to be written by somebody who has not read the entries.&lt;/strong&gt; Anyone who has read it reproduces the same defect in a different font. Somebody who has read the catalogue writes symptom lines that sound like the catalogue, because that is what is in their head. So the arrival vocabulary has to come from somewhere else entirely: our own failure logs, alerts and transcripts, where complaint language actually occurs, or a mind that has never opened the catalogue.&lt;/p&gt;

&lt;p&gt;The other half of the repair was already done by the test itself. The frozen list now has a &lt;strong&gt;before&lt;/strong&gt;, so the same ten symptoms can be run against the repaired index and the delta becomes a measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  It worked, and it also produced one honest failure
&lt;/h2&gt;

&lt;p&gt;He ran the same frozen list against the arrival layer, blind, and the arrival symptom moved.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;"my agent won't use my high-level tools"&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;matched a family&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NONE, 10 of 10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;family A, 5 of 5&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;controls&lt;/td&gt;
&lt;td&gt;6 of 6&lt;/td&gt;
&lt;td&gt;6 of 6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2F32fdnyn3l5klkiwbf2ar.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%2F32fdnyn3l5klkiwbf2ar.png" alt="The arrival symptom matched no family in 10 of 10 runs before the arrival layer, and family A in 5 of 5 runs after" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the layer introduced a false positive, which is ours. One arrival line about spacing reads as though it belongs to the UI domain, and it pulled in an unrelated rendering symptom.&lt;/p&gt;

&lt;p&gt;We published that as a known false positive and left it in. Tuning the layer against a result we had already seen amounts to grading our own paper. The next blind run decides whether it was a one-off or a pattern, and until that run exists the honest state of the thing is "improved on one axis, with one known defect, unrefereed".&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that generalises past our catalogue
&lt;/h2&gt;

&lt;p&gt;Three things came out of this that travel past our catalogue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A coverage metric and a findability metric measure different systems.&lt;/strong&gt; Ours had good coverage and unusable lookup, and a single number would have hidden that completely. If you ship an index, measure whether the words a person arrives with actually reach it, separately from how much it contains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The validity of a symptom index is a property of the reader's reasoning budget.&lt;/strong&gt; The same index, queried by a model at a low reasoning setting, confidently matched out-of-domain symptoms to real entries. Confidently wrong beats silence in exactly the wrong direction, and anything consuming your index inherits that failure mode without being told. On our side it is worse than on his, because our delivery channel pushes a note unattributed, at the moment of an act, in the voice of settled fact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write your index before your entries, or hand it to someone else.&lt;/strong&gt; Reading the thing you are indexing is what disqualifies you from indexing it well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who did what
&lt;/h2&gt;

&lt;p&gt;The catalogue was requested by &lt;a class="mentioned-user" href="https://dev.to/mansio"&gt;@mansio&lt;/a&gt;, who then designed the test, froze the symptom list before looking anything up, ran it eleven times across three models with six controls per run, argued against his own numbers where they were soft, and ran the confirmation pass afterwards. The diagnosis of why it missed and the repair are ours. The false positive is ours.&lt;/p&gt;

&lt;p&gt;The most useful act in the whole exercise was his refusal to grade his own answers. Everything quotable in this piece survives because the controls had right answers and the declines were objective, and he is the one who insisted on that distinction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the catalogue is
&lt;/h2&gt;

&lt;p&gt;It is public, it is plain markdown, and it reads without installing anything: &lt;a href="https://github.com/Tirthahq/crystal-memory/tree/master/catalogue" rel="noopener noreferrer"&gt;github.com/Tirthahq/crystal-memory/catalogue&lt;/a&gt;. Sixteen entries as of 2026-09-24, four of them added since the measurement above, each one a real failure from our own repository with the rival explanation and the discriminator that separated them, plus the arrival layer this article is about. The engine that delivers them at the moment of an action ships in the &lt;a href="https://github.com/Tirthahq/crystal-memory" rel="noopener noreferrer"&gt;same repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If the words you would actually arrive with do not reach an entry, that is the bug this piece is about and we want to hear it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>discuss</category>
    </item>
    <item>
      <title>What Grok catches, what Codex catches, and what the pair costs us</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Wed, 23 Sep 2026 18:05:21 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/what-grok-catches-what-codex-catches-and-what-the-pair-costs-me-2jhi</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/what-grok-catches-what-codex-catches-and-what-the-pair-costs-me-2jhi</guid>
      <description>&lt;p&gt;We run substantive work as three, in fixed seats, every day. This is a plain account of what each seat actually finds, which one a given question should go to, and the bill at the end of a normal day.&lt;/p&gt;

&lt;p&gt;It is a follow-up to &lt;a href="https://dev.to/tom_jones_230c4659491adcd/who-your-model-works-with-matters-more-than-which-model-you-picked-4iel"&gt;who your model works with matters more than which model you picked&lt;/a&gt;. That piece argued the case. This one is the field notes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is in the room
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Grok 4.6&lt;/strong&gt;, over OpenRouter, holds the &lt;strong&gt;argument&lt;/strong&gt;. It gets the claim, the reasoning and the thing about to be done, and it gets asked one question: what input would defeat this. It sees the codebase only when a piece is pasted in deliberately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex&lt;/strong&gt;, running GPT-5.5 at medium effort, holds the &lt;strong&gt;apparatus&lt;/strong&gt;. It gets the repository, the data and the scripts, and it gets asked whether the code supports the claim. It builds the fixture, runs it, pastes the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude orchestrates.&lt;/strong&gt; It writes the thing, briefs the other two, grades what comes back, and commits. An orchestrator that also implements ends up grading its own homework, which is the reason for the split.&lt;/p&gt;

&lt;p&gt;And then there is me, the human, who decides what is worth doing and rules on anything going out in public.&lt;/p&gt;

&lt;p&gt;Codex arrived as an implementer: write the feature, Claude reviews, it ships. The seat has grown well past that. Most of what it does now is audit, and the two best things it found this week were in our own measuring instruments, with product code untouched.&lt;/p&gt;

&lt;p&gt;One detail worth stating because it is easy to get wrong. Codex cannot commit. Its sandbox mounts the git directory read-only, which is a sensible safety property with an ugly consequence: it cannot sign its own work. Unnamed in the commit message, its findings enter the history as the orchestrator's. Attribution here is checkable, and the check is whether it is in the log.&lt;/p&gt;

&lt;p&gt;That cuts more than one way. An early draft of this very article described three seats and named only two of them, leaving the orchestrator as an unlabelled "me". A piece about absorbed contributions, absorbing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Grok catches
&lt;/h2&gt;

&lt;p&gt;Grok finds holes in the &lt;strong&gt;reasoning&lt;/strong&gt;. Consistently, and almost never anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It kills explanations that do not fit your own data.&lt;/strong&gt; Last week we measured that our document search was failing to find the right document, and wrote up why. The search returns fragments, documents get split into many fragments, so a document with more fragments gets more chances to be picked and a short precise one gets almost none. Tidy story. It went into the internal record as the mechanism.&lt;/p&gt;

&lt;p&gt;Grok read it and pointed at our own results table. The short one-fragment document ranked 22nd. The long eleven-fragment document ranked 41st or missed entirely. The explanation predicted the opposite of what the data showed, and the counterexample was sitting in a table written an hour earlier and re-read several times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It catches a special case wearing a general principle's clothes.&lt;/strong&gt; We had a theory that putting a hard size limit on a store forces it to decide what matters. Grok's answer was that one narrow case had been written up and then promoted. It also named the counterexample from our own history: we already had a hard size limit on a disk, and what it produced was a disk-full error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It finds the checks that measure the wrong thing.&lt;/strong&gt; One of our guards was looking for cautious wording in a file instead of measuring the thing the wording was about. Grok spotted it, we wrote five examples designed to sneak past, and all five sailed through.&lt;/p&gt;

&lt;p&gt;Where Grok is useless: anything requiring the repository. Your test fixture could be contaminated all week and it would have no way to know, having never seen it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Codex catches
&lt;/h2&gt;

&lt;p&gt;Codex finds holes in the &lt;strong&gt;apparatus&lt;/strong&gt;. The instrument, the fixture, the data path.&lt;/p&gt;

&lt;p&gt;The clearest case is the most recent, so here it is with the protocol on show, step by step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The claim we were about to act on.&lt;/strong&gt; Fourteen test questions, each with a known correct document. The search found the correct document zero times in the top twenty. On that basis a piece of planned work was about to be abandoned and a finding published.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was sent.&lt;/strong&gt; The brief named the exact file, gave the commit, and listed five things to attack in order of how much it would cost us to be wrong. It also wrote down what had been withheld. The phrase "check my conclusion" appeared nowhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What came back first was boring.&lt;/strong&gt; Codex could not reproduce the numbers. In its sandbox the run produced nothing, and an environment problem is precisely what that was. Easy to shrug at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What came back underneath it was not.&lt;/strong&gt; It had read the code and explained why its run produced nothing. The script shells out to a search tool and reads standard output without ever checking whether the tool ran. In Codex's sandbox the tool died on startup with a cache permission error and printed nothing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what happened&lt;/th&gt;
&lt;th&gt;what the script recorded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;search tool exits with an error, prints nothing&lt;/td&gt;
&lt;td&gt;"this question found no documents"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;same thing, fourteen times&lt;/td&gt;
&lt;td&gt;a full results table, 0 of 14 at every depth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;no search ever ran&lt;/td&gt;
&lt;td&gt;a verdict saying the planned work was the wrong project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the script finishes&lt;/td&gt;
&lt;td&gt;exit code 0, success&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three ordinary lines produced that. The subprocess call read output and discarded the return code. The loop wrapped it in a catch-all that turned failure into an empty result. The reporting function printed the table regardless and returned success. Any reviewer would read past all three.&lt;/p&gt;

&lt;p&gt;Our own run had genuinely worked, and after the fix every number came back identical. The result survived. What went with it was the right to believe it, because for an hour the strongest evidence for that conclusion was a table a completely dead process would have produced.&lt;/p&gt;

&lt;h3&gt;
  
  
  The other things Codex found this week
&lt;/h3&gt;

&lt;p&gt;To show the class rather than one instance: two of our checks were reading the working copy of files instead of the version being committed; a length limit could never fire because of how its pattern was built; a constant pointed at a directory that does not exist; and a matcher accepted any file whose name ended the right way, anywhere in the tree.&lt;/p&gt;

&lt;p&gt;Where Codex is useless: deciding whether the thing is worth building. It will happily verify the apparatus behind a claim nobody needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each one is better, in one table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;the question&lt;/th&gt;
&lt;th&gt;send it to&lt;/th&gt;
&lt;th&gt;why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Should this exist at all&lt;/td&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;no stake in the code already written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What input would defeat this&lt;/td&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;imagination beats access here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is this explanation consistent with our own results&lt;/td&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;reads the table without knowing what we hoped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does the code support this sentence&lt;/td&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;it can run the thing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build a fixture and paste the output&lt;/td&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is this number reproducible&lt;/td&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;it re-derives before it answers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is this worth the effort&lt;/td&gt;
&lt;td&gt;neither&lt;/td&gt;
&lt;td&gt;that one is the human's&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each is poor at the other's job by a wide margin, and we have wasted time proving it in both directions.&lt;/p&gt;

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

&lt;p&gt;Here are today's two Grok reviews, billed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;review&lt;/th&gt;
&lt;th&gt;tokens in&lt;/th&gt;
&lt;th&gt;tokens out&lt;/th&gt;
&lt;th&gt;cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a research brief on reranking&lt;/td&gt;
&lt;td&gt;829&lt;/td&gt;
&lt;td&gt;10,675&lt;/td&gt;
&lt;td&gt;$0.066&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;an adversarial review of a finding&lt;/td&gt;
&lt;td&gt;1,049&lt;/td&gt;
&lt;td&gt;5,455&lt;/td&gt;
&lt;td&gt;$0.035&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ten cents for both. Grok 4.6 lists at $2 per million tokens in and $6 per million out, and over half those output tokens were the model thinking, before it wrote a word back.&lt;/p&gt;

&lt;p&gt;The interesting column is the input. Eight hundred tokens. A thousand. That is the whole reason it is this cheap, and it is the same fact as the seat design: &lt;strong&gt;Grok is inexpensive precisely because it never gets the repository.&lt;/strong&gt; A reviewer that has to read your codebase costs what reading your codebase costs. A reviewer that only has to attack your argument costs about what a paragraph costs.&lt;/p&gt;

&lt;p&gt;So the two things that looked separate turn out to be one. Giving them deliberately different material is what makes a disagreement informative, and it is also what makes one of them nearly free.&lt;/p&gt;

&lt;p&gt;What is genuinely expensive is unbilled. Briefing well takes real thought, because a lazy brief produces a confident review of a problem you do not have. Grading takes longer than reading. Codex, working over the real repository, is a different cost profile entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  When they agree, and when that means nothing
&lt;/h2&gt;

&lt;p&gt;Two reviewers help you to the extent that they are looking at different things. Hand the same brief to two models and you have bought the same opinion twice in two voices, and the agreement will feel like confirmation while carrying almost no information.&lt;/p&gt;

&lt;p&gt;When it is real, it is the strongest signal available. Both of them killed the size-limit theory separately, reaching for the same incident from our own history, and then split cleanly on the follow-up: Grok on the logic, Codex on two checkable facts, one of which was a number we had formally retracted three months earlier and had just re-shipped.&lt;/p&gt;

&lt;p&gt;That agreement counted for something because we could say what each of them had been denied.&lt;/p&gt;

&lt;h2&gt;
  
  
  When they are wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A headline can be confidently false.&lt;/strong&gt; We asked for a look at a change and the report led with a finding: a rule could never trigger, because it needed a counter to reach nine while another limit stopped everything at three. Specific, plausible, real numbers from the real file, and wrong. The limits are checked in a different order than the report assumed. Three other findings in the same report were real and got fixed, so it earned its keep, but taking the headline on trust would have broken something that worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A brief written badly produces a confident wrong answer.&lt;/strong&gt; Once a review came back saying we had never measured a thing we had measured. The reviewer was careful. The measurement was simply absent from the brief. Deferring would have withdrawn a claim we could defend. So now what got left out is written down before the response is read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A dispatcher reporting success means nothing.&lt;/strong&gt; One night two of four review requests produced no output at all. The tool reported fine. There was a large log of work and no deliverable, because it had hit a usage limit partway through. A missing review and a review that found nothing look the same from here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are not claiming
&lt;/h2&gt;

&lt;p&gt;In the earlier piece we reached for the claim that two reviewers beat one better model. That one is unearned and we have said so.&lt;/p&gt;

&lt;p&gt;The reviewers are frequently poor. Two of the stories above are them being wrong, and one is them producing nothing while reporting success.&lt;/p&gt;

&lt;p&gt;And most changes should skip all of this. The briefing and the grading are hours, and those hours are real even when the bill is ten cents.&lt;/p&gt;

&lt;p&gt;The narrow claim is this. When two reviewers are handed genuinely different material and arrive at the same objection, that has been worth more than anything either said alone. Handed the same material, their agreement has been worth nothing, and it felt exactly the same from the inside.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two lines worth keeping
&lt;/h2&gt;

&lt;p&gt;Two reviewers with the same input are one reviewer with a stutter.&lt;/p&gt;

&lt;p&gt;Agreement is information exactly when the other party could have said something you never fed them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your agent already has the answer. You are paying it to look again.</title>
      <dc:creator>Tom Jones</dc:creator>
      <pubDate>Wed, 23 Sep 2026 03:28:54 +0000</pubDate>
      <link>https://dev.to/tom_jones_230c4659491adcd/your-agent-already-has-the-answer-noderag-hands-it-the-whole-thing-5gm4</link>
      <guid>https://dev.to/tom_jones_230c4659491adcd/your-agent-already-has-the-answer-noderag-hands-it-the-whole-thing-5gm4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Crystal memory, part 3.&lt;/strong&gt; Part 1: &lt;a href="https://dev.to/tom_jones_230c4659491adcd/crystal-memory-notes-that-arrive-when-you-act-not-when-you-go-looking-83"&gt;Notes that arrive when you act&lt;/a&gt;. Part 2: &lt;a href="https://dev.to/tom_jones_230c4659491adcd/whole-notes-not-fragments-the-retrieval-half-58ni"&gt;Whole notes, not fragments&lt;/a&gt;.&lt;br&gt;
This piece: what NodeRAG is, what it buys a frontier coding agent, and why it is the door into everything else we build.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Watch what your coding agent does when you ask it something about your own repository.&lt;/p&gt;

&lt;p&gt;It greps. It lists a directory. It reads a file, decides that was the wrong file, reads two more. Eight tool calls later it has assembled the context it needed, and somewhere in that process it burned thirty thousand tokens of your money reconstructing something you had already written down.&lt;/p&gt;

&lt;p&gt;Then it answers, and sometimes it answers wrongly, because the note it finally found was cut into four pieces by your retrieval and it was handed the piece that matched the words in your question. The rule was in that piece. The exception was in a different one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NodeRAG hands the agent the whole note instead.&lt;/strong&gt; That sentence is the entire method. The rest of this is what it costs, what it buys, and why everything else we ship hangs off it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number, and it is a frontier number
&lt;/h2&gt;

&lt;p&gt;We measured this on a frontier coding agent doing real work. Same model, same tasks, two arms.&lt;/p&gt;

&lt;p&gt;In the first arm the agent had to find its own context: real navigation in a read only sandbox, grepping and reading, which is what your agent does right now. In the second arm we handed it the one right note and asked it to answer from that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mean 39,458 tokens down to 1,337. That is 97% fewer, about thirty times cheaper, at identical accuracy: 75% correct in both arms.&lt;/strong&gt;&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%2Fueuccnohcll6qy0lrpe9.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%2Fueuccnohcll6qy0lrpe9.png" alt="Tokens to answer the same question, same model: unfed 39,458 tokens versus fed 1,337 tokens, identical accuracy" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing was traded away for that. The agent learned nothing from the files it read and discarded; it was paying to rediscover what somebody had already written down.&lt;/p&gt;

&lt;p&gt;And that saving scales with how much your agent would otherwise have to read. Our test repository was small, so treat 30x as a floor. On a large codebase the arm that navigates gets more expensive while the arm that is handed one note stays flat.&lt;/p&gt;

&lt;p&gt;This is why we build this for the frontier tier first. A large model with a big context window is exactly the reader that benefits: the tokens are cheap enough that handing it a complete note is affordable, and the risk of retrieval having picked the wrong passage costs far more than the tokens ever will.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a node actually is
&lt;/h2&gt;

&lt;p&gt;The word node is doing work, so here is the definition.&lt;/p&gt;

&lt;p&gt;A node is one finished knowing, written as a self contained unit. Not a document about a topic. One thing that is true, with the reason it is true and the exception that bounds it, in one file, written so a reader arriving with no context can act on it.&lt;/p&gt;

&lt;p&gt;That authoring discipline is the part people skip, and it is the part that makes the retrieval work. If your notes are long documents covering six subjects, whole document retrieval hands the model six subjects and buries the answer. When each note is one knowing, the unit that scores well is the unit that answers.&lt;/p&gt;

&lt;p&gt;Everything else is deliberately boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plain markdown files, one knowing each, with a small header
a local sqlite index, one vector per whole note
a local embedder, so the notes never leave the machine
a links table, because notes reference each other
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our own store today holds 3,038 documents, 11,798 chunks in the chunked index, and an 8,693 edge link graph, in two sqlite files. Every source note reads with &lt;code&gt;cat&lt;/code&gt;. A memory system you cannot inspect by hand is one you have to trust, and trusting it is the failure we are engineering out.&lt;/p&gt;

&lt;p&gt;There is no chunking step in the whole note path. Nothing decides where a passage ends. There is no reranker choosing which slice of a document answers you. The unit that goes in is the unit that comes out, which is why the note arrives with its exception still attached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the unit is the whole argument
&lt;/h2&gt;

&lt;p&gt;Standard retrieval is built for a world where the answer is a passage. Ask about a person, get the paragraph about that person. It works well there.&lt;/p&gt;

&lt;p&gt;Engineering knowledge has a different shape. The things you need an agent to respect are rules, and a rule is a structure: the rule, the reason, the exception, the cost of getting it wrong. Those sentences sit together because they only mean anything together.&lt;/p&gt;

&lt;p&gt;Chunking cuts that structure apart and ranks the pieces by how well they match your words. The piece that matches your words is usually the bare statement of the rule, because that is where the vocabulary lives. The exception is phrased differently and scores worse. So the retriever confidently hands over the half of the note that is dangerous on its own.&lt;/p&gt;

&lt;p&gt;One caution before the numbers, because we walked into it ourselves today. This argument is about &lt;strong&gt;which reader&lt;/strong&gt;. Everything above describes a reader with room to hold a whole document and the judgment to use it. Hand that same whole document to a small model and you get the opposite result, measured, twice. The next section is that measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same note is the wrong shape for a smaller reader, and we have the numbers
&lt;/h2&gt;

&lt;p&gt;This is the part almost nobody publishes, and it is the part that changes how you build.&lt;/p&gt;

&lt;p&gt;The optimal way to deliver a knowing &lt;strong&gt;inverts with the capability of the thing receiving it.&lt;/strong&gt; We measured the inversion, and it means the question "how should memory reach the model" has a different right answer for each reader.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;reader&lt;/th&gt;
&lt;th&gt;what it should receive&lt;/th&gt;
&lt;th&gt;what we measured&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;frontier agent&lt;/strong&gt; (Claude, Codex)&lt;/td&gt;
&lt;td&gt;the whole note&lt;/td&gt;
&lt;td&gt;fed the one note instead of navigating: 39,458 tokens down to 1,337, identical accuracy 75% and 75%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;small local model&lt;/strong&gt; (7B)&lt;/td&gt;
&lt;td&gt;a compact one line record, &lt;strong&gt;not&lt;/strong&gt; the whole note&lt;/td&gt;
&lt;td&gt;compacted full corpus &lt;strong&gt;53% at 343 context tokens&lt;/strong&gt;, beating whole file delivery at &lt;strong&gt;50% for 1,608 tokens&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;anything safety critical&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;always on, never retrieved at all&lt;/td&gt;
&lt;td&gt;retrieval surfaced the safety rule &lt;strong&gt;2 times in 3&lt;/strong&gt;. Always on scored 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the middle row again, because it is counterintuitive and it is ours. Handing a small model the entire note made it slightly &lt;strong&gt;worse&lt;/strong&gt; than a one line summary of the same knowledge, at nearly five times the tokens. More context, less accuracy. The frontier reader runs the other way: the tokens are cheap at that size, and the risk that retrieval picked the wrong passage costs far more than the tokens ever will.&lt;/p&gt;

&lt;p&gt;So the store holds one copy of the truth and the delivery splits by reader. That is the design, and it is why we say NodeRAG is built for the frontier tier first.&lt;/p&gt;

&lt;p&gt;The bottom row is the one we would argue hardest for. A rule that arrives two times in three is worse than useless, because the two successes teach you to rely on it. Guarantees have to be guarantees, so safety rules bypass the clever part entirely and load every time.&lt;/p&gt;

&lt;p&gt;There is a fourth number in the same family, cheap and worth stealing. Holding memory constant, changing only how the question was framed, moved a small model &lt;strong&gt;39% to 56%&lt;/strong&gt;: a gain the size of the memory effect itself, bought with instruction wording alone. Adding step by step reasoning to that same exact recall task made it &lt;strong&gt;worse&lt;/strong&gt;, 56% down to 43%, because the model reasons itself off the verbatim answer. For looking up an exact value, tell the model to quote it and say if it is absent, and leave the thinking alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  The prediction, tested again tonight
&lt;/h3&gt;

&lt;p&gt;That table is from June. Tonight we ran the one variable version of the delivery question against the small tier, to see whether the June rule still held.&lt;/p&gt;

&lt;p&gt;Same retriever, same store, same query, same ranking. The only thing that changed is what came back:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what came back to the 7B&lt;/th&gt;
&lt;th&gt;correct or partial&lt;/th&gt;
&lt;th&gt;context used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ten heading chunks from the store&lt;/td&gt;
&lt;td&gt;6 of 14&lt;/td&gt;
&lt;td&gt;910 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the top hit's whole document&lt;/td&gt;
&lt;td&gt;4 of 14&lt;/td&gt;
&lt;td&gt;1,479 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the right file, chunked down to the answer&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9 of 14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;328 tokens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Serving the whole document to the small model made it &lt;strong&gt;worse&lt;/strong&gt;, and cost 60% more context to do it. The smallest payload won by a clear margin. In June the same comparison read 53% at 343 tokens against 50% at 1,608 tokens, so this is the second independent construction giving the same answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which bounds this piece, so we will say it plainly.&lt;/strong&gt; The whole note claim here is a claim about the &lt;strong&gt;frontier&lt;/strong&gt; reader, and the evidence for it is the fed versus unfed measurement at the top of this article. The small model table speaks to a different reader, so we leave it out of that claim. The one variable test with a frontier model doing the answering is the measurement we owe next, and we have not run it yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  The real difference is judgment. One reader executes, the other decides.
&lt;/h3&gt;

&lt;p&gt;Context length is the obvious explanation for that table, and it is the shallow one.&lt;/p&gt;

&lt;p&gt;A compact one line record is enough for a reader that is going to &lt;strong&gt;execute&lt;/strong&gt;: look up the value, apply it, move on. A whole note is for a reader that is going to &lt;strong&gt;decide&lt;/strong&gt;, and a decision needs the reason and the exception, which is exactly the material a compact record throws away.&lt;/p&gt;

&lt;p&gt;We have tested that boundary directly, from the other side. You can train a small model into the &lt;em&gt;habit&lt;/em&gt; of working through a problem in steps, and the habit sticks: cold, on questions it has never seen, it lays out its own working and gets them right. The format bakes into the weights.&lt;/p&gt;

&lt;p&gt;What would not bake was the disposition. Bolt a constant instruction to reason carefully onto every training example, and the model learns to &lt;strong&gt;recite&lt;/strong&gt; it. On a new task it produced the ritual, looped on it, and reasoned worse than before. We got a tic, not a temperament.&lt;/p&gt;

&lt;p&gt;That line between the two readers matters more than the context window. A small model takes facts and formats. Judgment has to arrive live, in language, to a reader capable of weighing it, every time. Which is precisely why the frontier reader gets the whole note: &lt;strong&gt;it is the reader that can use the sentence explaining when the rule does not apply.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What the small model is genuinely good for here
&lt;/h3&gt;

&lt;p&gt;We keep the 7B in the harness for one reason: it has no room to absorb a bad retrieval, so a bad &lt;strong&gt;ranking&lt;/strong&gt; falls over where you can see it. That makes it a sharp instrument for the retrieval question while being the wrong reader for the delivery question.&lt;/p&gt;

&lt;p&gt;Here is what it showed. Running a whole note arm that chose among a small pool of candidate files, it answered ten of fourteen at partial credit or better against six for the full store search. That gap is mostly the &lt;strong&gt;pool&lt;/strong&gt; rather than the unit, because those two arms search candidate sets three orders of magnitude apart. We caught that in our own harness today and it is why the one variable test above exists.&lt;/p&gt;

&lt;p&gt;More interesting than the score is where the failures sat. Whole note retrieval picked the right file on nine of fourteen questions, &lt;strong&gt;and on those nine the model never once answered wrongly.&lt;/strong&gt; Every failure in that arm was the ranker choosing the wrong note rather than the model failing to use the right one. The ceiling there is entirely in retrieval, which is a far better problem to have, and it points straight at the 8,693 link edges sitting in the store that the ranker does not yet use.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost, and the condition, because both are real
&lt;/h2&gt;

&lt;p&gt;Whole notes are a bigger payload than a passage, so precision per token is worse. On a corpus of short documents you pay for size the answer never needed.&lt;/p&gt;

&lt;p&gt;And the edge is conditional. On a public benchmark of short scientific abstracts, whole note retrieval and chunked retrieval tie: 0.7014 against 0.7016 on SciFact, with keyword search at 0.6644 as a control that matched the published figure and validated our harness before we read anything off it. An abstract is 250 words, so a chunk already is the whole document and there is nothing left to win. On a harder public set, NFCorpus, our retrieval scores 0.3417 against keyword search at 0.3098.&lt;/p&gt;

&lt;p&gt;So the claim we defend is conditional. &lt;strong&gt;Whole note retrieval wins when your documents are long and the answer is a rule carrying its own exception.&lt;/strong&gt; That is what an engineering knowledge base looks like and what a corpus of abstracts does not. The condition is the useful half, so the condition is what we hand you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The door
&lt;/h2&gt;

&lt;p&gt;Here is the part we have under sold in every previous piece, and it is the reason we build this at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NodeRAG is the door&lt;/strong&gt;, and everything else we ship grows out of the store you create by walking through it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One store, two ways in.&lt;/strong&gt; Write a knowing once. It becomes answerable when somebody asks, which is NodeRAG, and it becomes deliverable at the moment of an action, which is the crystal half from part 1. The same markdown file, read two ways. That matters because the two fail in opposite directions. Retrieval answers the question you formed and is silent on everything you did not think to ask. A pushed note reaches you for a mistake you had no reason to suspect and cannot answer a question. You want both, and with one store you maintain neither twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is what makes an agent cheap to run.&lt;/strong&gt; Read the 97% figure as an operating cost. Your agent stops spending its context window rediscovering your repository and spends it on the actual work. On a long session that compounds, because the tokens it does not burn navigating are the tokens it still has left when the task gets hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is what makes a cheaper model viable underneath.&lt;/strong&gt; Our gateway serves a single model identifier and decides internally which model answers, cheap by default, escalating when verification says the cheap answer will not hold. That routing only pays when the cheap model is genuinely competent on your material, and memory delivery is the lever that makes it competent. Retrieval quality is what buys the routing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It lands where you already work.&lt;/strong&gt; Ra is our surface: your work, your terminal, your tasks and your notes on one screen with this retrieval underneath. The index lives on your own disk and only the embedding call leaves the machine, chosen deliberately over the hosted alternative so that custody of your source and your private notes stays with you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the store is what the discipline attaches to.&lt;/strong&gt; Once your knowings live as inspectable units you can check them. Every number we publish has to exist as a row in a claims ledger with a date and an artifact. Commits run gates. Notes carry the date they were last verified. None of that is possible over a pile of embedded fragments, and all of it is straightforward over a store of whole notes you can read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing we will admit in public
&lt;/h2&gt;

&lt;p&gt;Here is that discipline working, at our expense.&lt;/p&gt;

&lt;p&gt;While writing this piece we went to pull a number for it and found that the retrieval our own agents query every day was the chunked arm rather than the whole note arm. We had published the method and left the fragment version running. Then, checking the comparison, we found our own benchmark had been giving the whole note arm an easier retrieval problem than the chunked one, which is why the one variable test above exists at all.&lt;/p&gt;

&lt;p&gt;Then, the day after that, a third one landed, and it is the largest of the three, which is why it gets its own section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We went looking at the stage before delivery, and found that our first stage was often not retrieving the right document at all.&lt;/strong&gt; Everything above is about the UNIT you hand the reader once you have found the note. That is a separate question from whether you found it, and we had been quietly assuming the second half was fine.&lt;/p&gt;

&lt;p&gt;Measured over the same store, same queries, gold document at rank ten:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;dense embeddings&lt;/th&gt;
&lt;th&gt;BM25&lt;/th&gt;
&lt;th&gt;our RRF hybrid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gold document in the top 10&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 of 14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3 of 14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 of 14&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gold document in the top 100&lt;/td&gt;
&lt;td&gt;3 of 14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7 of 14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 of 14&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Three things in that table, each of which cost us something
&lt;/h3&gt;

&lt;p&gt;Plain lexical search more than doubled dense retrieval. Our notes are dense with identifiers, filenames, flags and error strings, which is exactly what a keyword index nails and an embedding smears into its neighbours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our hybrid fusion scored below plain BM25.&lt;/strong&gt; Blending a strong lexical ranking with a near random dense one spends rank mass on noise, and we had been calling that combination an improvement.&lt;/p&gt;

&lt;p&gt;And a stale search sidecar meant production had been running the dense arm alone for about two months, so the hybrid we believed we were serving was not the thing answering queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does this undo the argument above?
&lt;/h3&gt;

&lt;p&gt;No, and the reason deserves precision. Whole note delivery is what happens after retrieval succeeds, and the 39,458 to 1,337 figure was measured with the right note in hand. What the third correction says is that &lt;strong&gt;the harder half of this problem is upstream of the part this article is about&lt;/strong&gt;, and anyone who takes the delivery lesson without fixing their first stage will hand an agent a beautifully complete document that answers a different question.&lt;/p&gt;

&lt;p&gt;The repair shipped the same day, with the sidecar rebuilt and an admission gate removed that had been rejecting exactly the queries lexical search won hardest. ⚠ One thing we deliberately did NOT do: weight the lexical side up. It buys code shaped queries and collapses prose queries from 25 of 120 to 1 of 120, which makes it a trade between two query populations. We would rather print that sentence than the flattering one.&lt;/p&gt;

&lt;p&gt;Three corrections, all ours, all found by our own rules, all before publishing. If a memory system cannot catch its owners being wrong, it will not catch you being wrong either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to get it
&lt;/h2&gt;

&lt;p&gt;The push half is packaged, public, and runs on Claude Code today: &lt;a href="https://github.com/Tirthahq/crystal-memory" rel="noopener noreferrer"&gt;github.com/Tirthahq/crystal-memory&lt;/a&gt;. It installs a scratchpad that survives a context reset and a set of crystals that arrive at the act. It also ships &lt;a href="https://github.com/Tirthahq/crystal-memory/tree/master/catalogue" rel="noopener noreferrer"&gt;a catalogue of real verification failures&lt;/a&gt; from our own repository, sixteen of them as of 2026-09-24, each with the rival explanation and the discriminator that separated them, readable without installing anything.&lt;/p&gt;

&lt;p&gt;The retrieval half described here is what we are turning on next, and the numbers above are what we owed ourselves before doing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;The piece after this one is about what happens when there is nothing to fetch, because the knowledge is already resident. We have a result where twenty one thousand tokens of somebody else's codebase, a project the model had never seen, answered in under a second after a restart that destroyed the working cache. Instant knowing, and whether it survives a cold start, is next.&lt;/p&gt;

&lt;p&gt;If you want to argue with any of this, the comments are the right place and we read them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>architecture</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
