<?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: SYED-RAFI-NAQVI</title>
    <description>The latest articles on DEV Community by SYED-RAFI-NAQVI (@syedrafinaqvi).</description>
    <link>https://dev.to/syedrafinaqvi</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%2F559203%2Fe43d6951-3b84-4c4e-8b53-ad7ffdd77a7d.jpeg</url>
      <title>DEV Community: SYED-RAFI-NAQVI</title>
      <link>https://dev.to/syedrafinaqvi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syedrafinaqvi"/>
    <language>en</language>
    <item>
      <title>Jev Can't Write a Sentence. Here's How I'd Test Its Decisions.</title>
      <dc:creator>SYED-RAFI-NAQVI</dc:creator>
      <pubDate>Sat, 19 Sep 2026 16:11:48 +0000</pubDate>
      <link>https://dev.to/syedrafinaqvi/jev-cant-write-a-sentence-heres-how-id-test-its-decisions-15n3</link>
      <guid>https://dev.to/syedrafinaqvi/jev-cant-write-a-sentence-heres-how-id-test-its-decisions-15n3</guid>
      <description>&lt;p&gt;There's a particular kind of 2am I think every one of us knows. Not the heroic kind. Not the launch-night kind. The stupid kind.&lt;/p&gt;

&lt;p&gt;You're staring at a retry loop that's hammering an endpoint because the model wrapped its JSON in a markdown fence. Again. You have a regex for it. You have a repair prompt for when the regex fails. You have a fallback that calls the model a second time to fix the first call's output. And somewhere in that pile there's a try/except with a comment that says # should never happen, which is the saddest comment in all of software, because it keeps happening.&lt;/p&gt;

&lt;p&gt;I keep thinking about that pile. Because this week a company called TypeSafe AI shipped a model that deletes the whole thing by deleting the text. It's called Jev. It cannot write a sentence. Not won't. Can't. No summaries, no code, no explanations of its reasoning. You hand it program state and typed questions, and it hands you back decisions.&lt;/p&gt;

&lt;p&gt;I've spent the week reading everything about it. I haven't run it myself, access is waitlisted, and I'd be lying if I pretended otherwise. But I think the idea is worth taking seriously, and I think the launch numbers deserve a closer look than they're getting. So here's my honest guide: what it is, what the numbers actually say, and what I'd do before I let it anywhere near anything real.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually is
&lt;/h2&gt;

&lt;p&gt;Three primitives. That's the whole API, and that's the point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;choice&lt;/strong&gt; picks one option from a set you define, up to 255 of them. It returns the pick, the probability of every option, and a confidence score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;score&lt;/strong&gt; rates against a rubric you describe in words, 2 to 10 levels. The answer can land between levels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;noul&lt;/strong&gt; answers yes or no as a single probability from 0 to 1. There's no confidence field, because the number already is the belief.&lt;/p&gt;

&lt;p&gt;Every question runs in parallel against the same state, in a single call. TypeSafe reports 70 to 500 milliseconds end to end, at $0.042 per million input tokens, with output tokens free.&lt;/p&gt;

&lt;p&gt;Install it with &lt;code&gt;npm install @typesafe-ai/sdk&lt;/code&gt;. Node 20 or newer, ships ESM, CommonJS, and TypeScript declarations, with answer types inferred from your questions. (Python users want &lt;code&gt;pip install typesafe-sdk&lt;/code&gt;.) If you're still on the waitlist, the community reports Jev is also reachable through Vercel's AI Gateway as &lt;code&gt;typesafe-ai/jev&lt;/code&gt; at the same price. I haven't verified that myself, so treat it as a lead, not a fact.&lt;/p&gt;

&lt;p&gt;The founder is Diogo Almeida. He co-authored InstructGPT, the paper that introduced RLHF and led to ChatGPT, then left with roughly this thesis: we built models that are superhuman at pleasing humans and useless at automation. He spent two years in stealth building this instead. $40M seed round, led by DCVC.&lt;/p&gt;

&lt;p&gt;The training is the part I find genuinely interesting. Instead of optimizing for what human raters like, they train for calibration. When the model says 0.85, it should be right about 85% of the time. Anyone who has built on an LLM's "confidence" knows why that matters. The confidence numbers we're used to are vibes. These are supposed to be measurements.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scorecard, and what I actually believe
&lt;/h2&gt;

&lt;p&gt;Here's where I want to slow down, because the launch chart is doing a lot of work.&lt;/p&gt;

&lt;p&gt;TypeSafe's numbers, self-run, unreproduced by anyone: Jev scores 67.8% across their four workflows. GPT-5.6 Terra gets 67.9%. GPT-5.6 Sol gets 74.1%, Claude Opus 5 gets 73.1%. Jev costs about $0.0004 per case against Terra's $0.0304, and answers in 0.4 seconds against 10.1.&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;Jev&lt;/th&gt;
&lt;th&gt;GPT-5.6 Terra&lt;/th&gt;
&lt;th&gt;GPT-5.6 Sol&lt;/th&gt;
&lt;th&gt;Claude Opus 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Eval agreement&lt;/td&gt;
&lt;td&gt;67.8%&lt;/td&gt;
&lt;td&gt;67.9%&lt;/td&gt;
&lt;td&gt;74.1%&lt;/td&gt;
&lt;td&gt;73.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per case&lt;/td&gt;
&lt;td&gt;~$0.0004&lt;/td&gt;
&lt;td&gt;~$0.0304&lt;/td&gt;
&lt;td&gt;~$0.0836&lt;/td&gt;
&lt;td&gt;~$0.1761&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;0.4s&lt;/td&gt;
&lt;td&gt;10.1s&lt;/td&gt;
&lt;td&gt;23.3s&lt;/td&gt;
&lt;td&gt;37.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Striking table. Here's what it doesn't say.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no ground truth.&lt;/strong&gt; The reference answers are the average of GPT-6 Astra and Claude Fable 5.1 thinking hard. So the eval measures agreement with two frontier models, not correctness. TypeSafe admits this openly and acknowledges the bias toward OpenAI and Anthropic. I respect the honesty. It's still models grading models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Cannot hallucinate" is narrower than it sounds.&lt;/strong&gt; It means Jev cannot return a value outside your schema. It can absolutely return the wrong valid value, confidently. Their 0% schema-error figure is, in TypeSafe's own words, "not empirical." A type guarantee answers "can my program read this." It doesn't answer "should my program trust this." Someone at Temperature Zero put it better than I could: it doesn't hallucinate, it decides wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The price may be subsidized.&lt;/strong&gt; TypeSafe says they can't prove it isn't.&lt;/p&gt;

&lt;p&gt;So here's what I believe. This is a genuinely interesting primitive with a promising cost and latency profile, reported by the vendor, on the vendor's tests. It is not proof of anything. And honestly, "interesting primitive, unproven claims" describes about half the tools I actually use. The question was never whether the chart is perfect. It's whether the idea survives contact with your own data.&lt;/p&gt;

&lt;p&gt;Which is the part I care about most, and the part nobody is writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API in 60 seconds
&lt;/h2&gt;

&lt;p&gt;One endpoint, one SDK call. This is from TypeSafe's official JS SDK README, which I've read closely in the absence of an API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TypeSafeClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@typesafe-ai/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TypeSafeClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// reads TYPESAFE_API_KEY from env&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PR_TRIAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Which subsystem does `diff` change the most?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login, sessions, tokens, permissions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;billing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Charges, invoices, refunds, subscriptions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Components, styling, client-side behaviour&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;infra&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Build, deploy, CI, configuration&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Documentation or comments only&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How risky is `diff` to deploy on a Friday afternoon?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Trivial; docs or comments only&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Routine; well covered by existing tests&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Needs a careful review&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Needs a second reviewer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Do not ship without the owner present&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="na"&gt;touchesMigrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Does `diff` add or modify a database migration?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;triagePr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="nl"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;systemOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jev-1.13.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// pin it, do not ship "jev-latest"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PR_TRIAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;area&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;areaConfidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;area&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touchesMigrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things I'd internalize before anything else.&lt;/p&gt;

&lt;p&gt;First, that &lt;code&gt;other: null&lt;/code&gt; entry isn't optional. Without it the model picks the closest wrong thing instead of telling you nothing fits. And it will. Cheerfully.&lt;/p&gt;

&lt;p&gt;Second, state accepts a string, a JSON object, or an array of text, so structure it and let each question reference the field by name.&lt;/p&gt;

&lt;p&gt;Third, pin your model version. &lt;code&gt;jev-latest&lt;/code&gt; is the SDK default, currently resolving to jev-1.13.0, and it will move. Log the model ID on every call. Otherwise your regression tests will start failing on a day you didn't deploy anything, and you'll spend a morning learning that lesson the hard way.&lt;/p&gt;

&lt;p&gt;One caveat on the code above: the call shape and the choice/score/noul factories are from TypeSafe's official JS SDK README. The &lt;code&gt;.confidence&lt;/code&gt; and &lt;code&gt;.probabilities&lt;/code&gt; fields mirror the shared wire format. The SDK is four days old, so check docs.typesafe.ai/sdk/javascript before you paste.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ideas I'm stealing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ask everything up front.&lt;/strong&gt; The questions run in parallel, so a sixth question costs tokens but almost no time. Stop chaining follow-up calls. Ask everything you might branch on in one shot and throw away what you don't use. Their cookbook claims a 13-question batch ran 12x cheaper and 10x faster than asking one at a time. Even if the exact multiple is optimistic, the direction is right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let the threshold follow the stakes.&lt;/strong&gt; This is the one that changes how you design. Don't write one confidence threshold for the whole system. Write one per action, based on what being wrong costs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ACTION_THRESHOLDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// cheap to be wrong, just retry&lt;/span&gt;
  &lt;span class="na"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// embarrassing to be wrong&lt;/span&gt;
  &lt;span class="na"&gt;issueRefund&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// expensive to be wrong&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ACTION_THRESHOLDS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;human_review&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;escalate_to_frontier_model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Choice and Score expose a confidence field.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;ACTION_THRESHOLDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;human_review&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;escalate_to_frontier_model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Noul has no confidence field. The probability IS the belief,&lt;/span&gt;
&lt;span class="c1"&gt;// so gate it from both ends and treat the middle as unknown.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;routeNoul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto_yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto_no&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;human_review&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ACTION_THRESHOLDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto_yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto_no&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;human_review&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That Noul distinction is the thing people get wrong first. A Noul at 0.08 is a confident no, just as actionable as one at 0.92. If you write &lt;code&gt;if (p &amp;gt; 0.5)&lt;/code&gt; you've thrown away the entire signal and turned a calibrated probability back into a coin flip.&lt;/p&gt;

&lt;p&gt;And if the probabilities come back flat across options, that's usually your criteria being vague, not the model being confused. Fix the question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cascade.&lt;/strong&gt; Jev doesn't replace your frontier model. It decides which requests deserve one. Classify cheaply, handle the easy majority in plain code, escalate the uncertain minority to the expensive model or a human. You end up paying frontier prices on maybe a tenth of your traffic.&lt;/p&gt;

&lt;p&gt;People are already building on it. All self-reported, all launch-week, so take them as sketches, not proof. Someone classified a thousand papers for eight cents. A browser agent booked a flight in seven seconds. And my favorite: a drone project that uses Jev as advisory-only at 2.5Hz, with code owning flight control and safety. That's the healthiest framing I've seen. The model suggests. Your code decides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before I'd trust it with anything real
&lt;/h2&gt;

&lt;p&gt;This is the section I wish someone wrote for every model launch. A decision model isn't a chatbot you eyeball. It's a classifier. And classifiers get evaluated, or they get you.&lt;/p&gt;

&lt;p&gt;I haven't run the API, so consider this the checklist I'd run the day I get in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Label your own traffic.&lt;/strong&gt; Two hundred real examples, by hand, from your distribution. Not synthetic, not the vendor's eval. A spreadsheet with three columns: the input, the expected answer, your notes. Label blind if you can manage it, or at least label before you look at the model's answer. It's a day of work and it's the entire foundation. Everything below is decoration without it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Score each question separately.&lt;/strong&gt; A blended 85% can hide a question running at 55%. Every question gets its own number, or you don't know what you have. You want a little table that reads: area 91%, risk 78%, touchesMigrations 99%. Now you know exactly which question to fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Check the calibration.&lt;/strong&gt; Bucket predictions by reported confidence and measure actual accuracy inside each bucket. Make the table: bucket 0.9 to 1.0, 64 predictions, right 88% of the time. Bucket 0.7 to 0.8, 41 predictions, right 61% of the time. That second row is where your thresholds lie to you. If your 0.9 bucket is right 70% of the time, your thresholds are fiction and every gate you built on them is decorative. The whole "calibrated decisions" claim has to survive this test. Run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Set thresholds by cost of error.&lt;/strong&gt; Not by feel. This is the table from the routing section above. Write it in code where a reviewer can see it, not in a doc nobody reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Try to break it.&lt;/strong&gt; Get specific. A PR description that says "this is definitely not a migration" sitting above a diff that adds one. Two criteria that contradict each other. A state field stuffed with ten times the irrelevant text. User-controlled text that argues for its own classification. TypeSafe's own docs admit the model reads literally and doesn't treat state as hostile. Believe them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Pin the version, log everything, re-run on every bump.&lt;/strong&gt; Model ID, question versions, probabilities, outcomes. Store the probabilities, not just the picks, because the picks won't tell you the model got less sure. When jev-1.14.0 drops, re-run the same 200 and diff the answers. A few dollars per run. It's the only thing standing between you and a silent regression in production.&lt;/p&gt;

&lt;p&gt;None of this is exotic. It's the boring work nobody posts about, which is exactly why I'm writing it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it's bad at
&lt;/h2&gt;

&lt;p&gt;TypeSafe publishes a page called "jaggedness" that lists this stuff, and it's unusually honest for a launch. It reads literally. It can't count or do date math, so keep that in code. Accuracy decays as state fills with irrelevant material, so retrieve narrowly. User-controlled text can steer it. Contradictory criteria confuse it. And it generates nothing. Ever. No text, no code, no summaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it fits, and where it doesn't
&lt;/h2&gt;

&lt;p&gt;Good fit: classification and routing in a hot loop. Agent step selection. Moderation triage. Anything where the answer is an enum. Anything you're currently doing with a cheap model plus a JSON repair step. Anything where 100ms matters.&lt;/p&gt;

&lt;p&gt;Bad fit: anything that needs prose out. Math or date arithmetic. Open-ended reasoning over long documents. One-shot high-stakes calls with no human in the loop. Any decision you can't afford to be confidently wrong about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shift
&lt;/h2&gt;

&lt;p&gt;Here's what I keep coming back to. Jev isn't a cheaper LLM. It's a different primitive: a function call that happens to be intelligent. Once a decision costs a fraction of a cent and 200 milliseconds, you stop rationing judgment calls and start putting them where you'd previously have written a brittle heuristic. That's an architecture change, not a vendor swap.&lt;/p&gt;

&lt;p&gt;And it raises the next question, the one I can't stop thinking about. When decisions move into the agent loop at 100ms a pop, "did the model sound right" stops being the question. The question becomes what the agent did with the answer. Which branch it took. What state it changed. Whether the world ended up right.&lt;/p&gt;

&lt;p&gt;That's why I'm building &lt;a href="https://silo.burn0.dev" rel="noopener noreferrer"&gt;Silo&lt;/a&gt;: an open-source simulation layer for running agents against realistic business worlds and verifying outcomes deterministically, instead of grading prose. It's early, pre-1.0, MIT-licensed. Take it as a direction, not a dependency.&lt;/p&gt;

&lt;p&gt;If you've gotten Jev access and I'm wrong about any of this, I'd genuinely like to know. And if you've lived the other story, the 2am regex pile, with some other tool, tell me that too. We're all carrying some version of it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>I gave a 10-line agent a CRM world and asked it for one number</title>
      <dc:creator>SYED-RAFI-NAQVI</dc:creator>
      <pubDate>Fri, 18 Sep 2026 13:49:14 +0000</pubDate>
      <link>https://dev.to/syedrafinaqvi/i-gave-a-10-line-agent-a-crm-world-and-asked-it-for-one-number-32ep</link>
      <guid>https://dev.to/syedrafinaqvi/i-gave-a-10-line-agent-a-crm-world-and-asked-it-for-one-number-32ep</guid>
      <description>&lt;h1&gt;
  
  
  I gave a 10-line agent a CRM world and asked it for one number
&lt;/h1&gt;

&lt;p&gt;Last week I wanted to know whether my agent could do sales operations, not just talk about them. Asking it "what is the weighted pipeline?" and checking the answer by eye is a vibe check, not a test. So I built a small test world for it instead. Here is the full loop: ten lines of agent, one number, a failure I could actually debug, and a pass that meant something.&lt;/p&gt;

&lt;h2&gt;
  
  
  The world
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://silo.burn0.dev" rel="noopener noreferrer"&gt;Silo&lt;/a&gt; is a local-first simulation layer for testing AI agents. You bring your own agent (any framework, even a raw API loop), and Silo drops it into a realistic simulated business environment with tools, seeded state, tasks, and deterministic verifiers that grade what the agent did, not what it said. It is TypeScript-first and open source, at v0.4.0, early and pre-1.0, so breaking changes are expected.&lt;/p&gt;

&lt;p&gt;Setup took about two minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @burn0/silo
npx @burn0/silo init demo &lt;span class="nt"&gt;--template&lt;/span&gt; crm
npx @burn0/silo &lt;span class="nb"&gt;env &lt;/span&gt;validate &lt;span class="nt"&gt;--env&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validation came back clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OK   demo
data=9 tasks=6 tools=42 verifiers=6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One honest gotcha: my scratch project's &lt;code&gt;package.json&lt;/code&gt; was missing &lt;code&gt;"type": "module"&lt;/code&gt;, and the verifier typecheck failed with a cryptic complaint about top-level exports in a CommonJS module. Adding &lt;code&gt;"type": "module"&lt;/code&gt; fixed it. The CRM template ships with 6 tasks, 42 tools (listing leads, converting them, reassigning opportunities, running pipeline forecasts), and 6 verifiers. Everything lives as plain TypeScript and JSON inside &lt;code&gt;.silo/environments/demo&lt;/code&gt;, so you can read and edit all of it.&lt;/p&gt;

&lt;p&gt;The task I picked was &lt;code&gt;TASK-004&lt;/code&gt;: &lt;em&gt;"Amara Osei needs a single number for the board: the probability-weighted value of everything currently open across the whole team, in US dollars. Report that figure."&lt;/em&gt; Easy difficulty, a question rather than a mutation. A good first blood.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent
&lt;/h2&gt;

&lt;p&gt;Silo does not give you an agent. It gives you a world and a boundary. Your agent is one ordinary file that default-exports a function. Silo calls it with the task, the tool list, and a &lt;code&gt;callTool&lt;/code&gt; function, and your function returns &lt;code&gt;{ output }&lt;/code&gt;. Mine has no LLM in it at all. It is deliberately dumb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;callTool&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pipeline_summary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weighted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalWeightedAmount&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The probability-weighted value of open pipeline is $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weighted&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; USD.`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten lines. It asks the world for the pipeline summary and reports the weighted total. The point of testing a dumb agent first is that when something fails, you know it was the agent, not the prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npx @burn0/silo run &lt;span class="nt"&gt;--env&lt;/span&gt; demo &lt;span class="nt"&gt;--task&lt;/span&gt; TASK-004
&lt;span class="go"&gt;
  Task          TASK-004 - Report the weighted value of open pipeline
  Result        FAIL
  Reward        0.33

  Failed
  ✗ The reported weighted total is correct
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things made this failure worth having. First, the verifier did not grade my vibes. It compared my answer against a number it derived from the seeded world itself: &lt;em&gt;"answered 0, expected 507500"&lt;/em&gt;. I had guessed the wrong shape for the tool output, so my agent reported $0. The correct figure was $507,500, and it was not written down anywhere for the agent to peek at.&lt;/p&gt;

&lt;p&gt;Second, the failure left a legible trail. Every run saves a directory with &lt;code&gt;result.json&lt;/code&gt;, &lt;code&gt;trace.jsonl&lt;/code&gt;, and &lt;code&gt;state-diff.json&lt;/code&gt;. The trace showed the exact tool call, the exact tool result, and my exact output in sequence. I found the bug in about thirty seconds: &lt;code&gt;callTool&lt;/code&gt; returns &lt;code&gt;{ output, isError }&lt;/code&gt;, not the raw output, and the weighted total sits at &lt;code&gt;totalWeightedAmount.amount&lt;/code&gt;. I also noticed something instructive in &lt;code&gt;result.json&lt;/code&gt;: even while failing, my agent scored 0.33, because an optional check still passed. Partial credit is explicit here, which is a good property in an eval.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pass
&lt;/h2&gt;

&lt;p&gt;Two-line fix, rerun:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Task          TASK-004 - Report the weighted value of open pipeline
  Result        PASS
  Reward        1.00

  Checks        3 / 3
  Required      1 / 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what actually got checked, straight from the verifier labels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The reported weighted total is correct.&lt;/strong&gt; My answer matched the world: $507,500.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The total is the figure the answer closes on.&lt;/strong&gt; The answer has to end on the number, not bury it in a paragraph. A small thing, but it is exactly the kind of sloppy answer an LLM would give that an eye-test would forgive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answering left the world unchanged.&lt;/strong&gt; &lt;code&gt;state-diff.json&lt;/code&gt; confirmed nothing mutated. This is the one that sold me. A question task should never change the CRM. If my agent had helpfully "tidied up" an opportunity while reading it, that check would have caught it, and an answer-only eval never could.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every rollout starts from a fresh copy of the world, so runs cannot contaminate each other. The same agent on the same task is deterministic here, which makes the number a real regression gate rather than a flaky metric.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the medium one
&lt;/h2&gt;

&lt;p&gt;If you run this yourself, &lt;code&gt;TASK-002&lt;/code&gt; is the more interesting homework: &lt;em&gt;"Tomas Bergstrom has left the company and his account is deactivated, but his open deals are still sitting under his name. Move every one of his open opportunities to Priya Nair (USR-003)... Deals he already closed stay as they are."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is the task where an agent can name the right rep, sound completely confident, and still forget to reassign half the deals, or reassign the closed ones too. The verifier checks the world afterward, so confidence does not score. &lt;code&gt;state-diff.json&lt;/code&gt; will show you exactly which deals moved and which did not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is going
&lt;/h2&gt;

&lt;p&gt;Silo is at v0.4.0 with CRM and ERP templates, a blank template for your own domain, and finance, support, and project-management worlds marked as coming soon. Everything runs on your machine: no account, no API key, nothing leaves your laptop.&lt;/p&gt;

&lt;p&gt;If you are shipping an agent that touches a CRM, an ERP, or anything with state that matters, stop grading answers and start grading worlds. The repo is &lt;a href="https://github.com/burn0-dev/silo" rel="noopener noreferrer"&gt;github.com/burn0-dev/silo&lt;/a&gt;, docs are at &lt;a href="https://docs.burn0.dev/silo/introduction" rel="noopener noreferrer"&gt;docs.burn0.dev/silo&lt;/a&gt;, and &lt;code&gt;npm install @burn0/silo&lt;/code&gt; is the whole install.&lt;/p&gt;

&lt;p&gt;My next run: wire a real LLM into that 10-line agent and watch &lt;code&gt;TASK-002&lt;/code&gt; go sideways. I will report back with the trace.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>testing</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I caught my AI agent lying to me</title>
      <dc:creator>SYED-RAFI-NAQVI</dc:creator>
      <pubDate>Thu, 17 Sep 2026 13:49:14 +0000</pubDate>
      <link>https://dev.to/syedrafinaqvi/i-caught-my-ai-agent-lying-to-me-5h0e</link>
      <guid>https://dev.to/syedrafinaqvi/i-caught-my-ai-agent-lying-to-me-5h0e</guid>
      <description>&lt;p&gt;Last week I gave an AI agent a simple job: look at a sales pipeline and tell me what it is worth. It answered in seconds, confident, well formatted, numbers included. My eval script graded the response and gave it a PASS.&lt;/p&gt;

&lt;p&gt;Then I looked at what the agent actually did. Nothing. It never touched the data. It guessed.&lt;/p&gt;

&lt;p&gt;That is the moment I stopped trusting text-based evals for agents, and the reason I have been playing with &lt;a href="https://silo.burn0.dev" rel="noopener noreferrer"&gt;Silo&lt;/a&gt; since.&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%2F4tw7zul83hlqixxz6mus.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%2F4tw7zul83hlqixxz6mus.png" alt="The Silo landing page: the simulation layer for AI agents" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Your eval grades essays. Your agent does surgery.
&lt;/h2&gt;

&lt;p&gt;Here is the thing nobody says out loud: most agent evals are essay grading. You show the agent a prompt, it writes something back, and you (or an LLM judge) score how good the writing looks. That was fine when agents were chatbots.&lt;/p&gt;

&lt;p&gt;Modern agents are not chatbots. They call tools, update records, move inventory, change the state of systems. An agent can write a beautiful summary of a pipeline while leaving every deal in the wrong stage. If your eval only reads the transcript, that failure is invisible. You ship it, and production finds out for you.&lt;/p&gt;

&lt;p&gt;There are roughly three levels to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unit tests.&lt;/strong&gt; Does the code work?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt evals.&lt;/strong&gt; Does the answer look right?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actually watching what the agent did.&lt;/strong&gt; Did the world end up in the right state?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Silo is a tool for level 3. It is open source (MIT), local-first, TypeScript-first, and it gives your existing agent a realistic simulated world to work in: tools, seeded data, tasks, and verifiers that grade outcomes deterministically. Nothing leaves your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five-minute version
&lt;/h2&gt;

&lt;p&gt;You need Node 22+ and &lt;code&gt;"type": "module"&lt;/code&gt; in your package.json. Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @burn0/silo
npx @burn0/silo init demo &lt;span class="nt"&gt;--template&lt;/span&gt; crm
npx @burn0/silo &lt;span class="nb"&gt;env &lt;/span&gt;validate &lt;span class="nt"&gt;--env&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OK   demo
data=9 tasks=6 tools=42 verifiers=6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a realistic simulated B2B sales pipeline on your laptop: 9 data collections, 6 tasks, 42 tools an agent can call, 6 verifiers that decide pass or fail. Small enough to hold in your head, which is exactly why it is the best template to start with. (There is also an ERP template with 185 tools and deliberately messy seed data. An invoice that bills more than was received. A payment that failed on stale bank details. Someone on the Silo team has seen things.)&lt;/p&gt;

&lt;p&gt;Write an agent. Anything that default-exports a function counts, any framework or a raw API loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// silo.agent.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;callTool&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;forecast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;forecast_report&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;weightedAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Open pipeline is worth $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weightedAmount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it against a real task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @burn0/silo run &lt;span class="nt"&gt;--env&lt;/span&gt; demo &lt;span class="nt"&gt;--task&lt;/span&gt; TASK-004 &lt;span class="nt"&gt;--agent&lt;/span&gt; ./silo.agent.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Task          TASK-004 — Report the weighted value of open pipeline
  Result        PASS
  Reward        1.00

  Tool calls    1
  Checks        3 / 3
  Required      1 / 1

  Run saved: .silo/runs/run_20260915012734_4t01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fine. Now here is the part that got me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verifier does not read your agent's essay
&lt;/h2&gt;

&lt;p&gt;In Silo, your agent never sees the world state. It gets the task instruction, the tool schemas, and cloned tool output. If it wants to know something, it has to call a tool for it, exactly like production.&lt;/p&gt;

&lt;p&gt;Grading works the same way in reverse. A task passes because the world changed correctly, never because the agent said the right words. Remember my lying agent from the top of this post? Under Silo it fails loudly: zero tool calls, checks unmet, done. No partial credit for a confident tone.&lt;/p&gt;

&lt;p&gt;Two design rules keep this honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time is simulated.&lt;/strong&gt; &lt;code&gt;state.now&lt;/code&gt; is the only clock. Nothing reads &lt;code&gt;Date.now()&lt;/code&gt;, so a run from Tuesday reproduces exactly on Friday.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every run leaves evidence.&lt;/strong&gt; Each rollout writes a directory: &lt;code&gt;trace.jsonl&lt;/code&gt; (every event, append-only), &lt;code&gt;result.json&lt;/code&gt; (checks, reward, tool errors), &lt;code&gt;state-diff.json&lt;/code&gt; (exactly what changed), &lt;code&gt;run.json&lt;/code&gt; (task, verifier, timings).&lt;/li&gt;
&lt;/ul&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%2Fj94192ws7clie5r81q9v.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%2Fj94192ws7clie5r81q9v.png" alt="How Silo works: your agent acts inside an isolated runtime, and the outcome is inspected with traces, state diffs, and run artifacts" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My favorite detail: &lt;code&gt;result.json&lt;/code&gt; and &lt;code&gt;state-diff.json&lt;/code&gt; contain no timestamps or run ids. That makes them an exact regression oracle. Diff two runs and any difference is a genuine behavioral change, not clock noise. And when your agent is non-deterministic, &lt;code&gt;--runs 5&lt;/code&gt; repeats the task so you see the real distribution instead of the lucky run you would have screenshotted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest caveats
&lt;/h2&gt;

&lt;p&gt;Silo is early, v0.4.0, and the maintainers say plainly to expect breaking changes before 1.0. Packaged adapters for LangChain, Vercel AI SDK, OpenAI, Anthropic, and Mastra are still roadmap, as are an MCP server and LLM judges to sit alongside the deterministic checks. If you need those today, you will be writing some glue.&lt;/p&gt;

&lt;p&gt;But the core loop works right now, and it rearranged how I think about agent testing in about an afternoon. Stop asking "did it write a good answer" and start asking "is the world right." Those are different questions, and only one of them protects production.&lt;/p&gt;

&lt;p&gt;Links, since you will ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Site: &lt;a href="https://silo.burn0.dev" rel="noopener noreferrer"&gt;silo.burn0.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.burn0.dev/silo/introduction" rel="noopener noreferrer"&gt;docs.burn0.dev/silo/introduction&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/burn0-dev/silo" rel="noopener noreferrer"&gt;github.com/burn0-dev/silo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with the CRM template. Then try to make your agent lie to the verifier. It is harder than you think, and that is the point.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>testing</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
