<?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: shitianfang</title>
    <description>The latest articles on DEV Community by shitianfang (@shitianfang).</description>
    <link>https://dev.to/shitianfang</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%2F1026686%2Fa40ffe41-ff7e-47c2-bd54-eb8df8abbada.png</url>
      <title>DEV Community: shitianfang</title>
      <link>https://dev.to/shitianfang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shitianfang"/>
    <language>en</language>
    <item>
      <title>Your agent waits a full second to send the number 3</title>
      <dc:creator>shitianfang</dc:creator>
      <pubDate>Sat, 19 Sep 2026 18:19:46 +0000</pubDate>
      <link>https://dev.to/shitianfang/your-agent-waits-a-full-second-to-send-the-number-3-2513</link>
      <guid>https://dev.to/shitianfang/your-agent-waits-a-full-second-to-send-the-number-3-2513</guid>
      <description>&lt;p&gt;Put a timer around the steps of any agent loop and the same shape shows up: a pause of one to three seconds, then a single small action. Click element 3. Keep the file. Stop waiting, the build is done.&lt;/p&gt;

&lt;p&gt;In that pause a language model is writing something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking at the current page, I can see several navigation options. The most relevant to the user's goal appears to be the third link in the results list, which matches the destination they mentioned...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your code parses that and keeps one number: &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A second of wall clock and a few hundred tokens, to transmit a value between 1 and 30. And the loop does it again on the next step, and the next.&lt;/p&gt;

&lt;p&gt;If your first reaction is that this is just a badly configured call — constrain the output to an enum, cap the tokens, turn the thinking off — you are right, and it turns out to be worth most of the gap. I measured that too, and the number it leaves behind is further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of steps
&lt;/h2&gt;

&lt;p&gt;Go through what an agent loop actually asks for, step by step, and they sort into two piles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps that must produce text.&lt;/strong&gt; Write the commit message. Summarize the transcript. Generate the patch. Explain the failure. Only a language model does these.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps that only produce a decision.&lt;/strong&gt; Is the build finished? Which of these 30 elements do I click? Is this shell command safe to run? Keep this message or drop it? Is this output a success?&lt;/p&gt;

&lt;p&gt;LLMs do both piles, and they are structurally wasteful on the second: they answer a multiple-choice question by generating prose, which your code then throws away after extracting the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Jev is, before I sell you anything
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;Jev&lt;/a&gt; is TypeSafe's "System One" model, released four days ago. It takes a state plus typed questions — yes/no, pick-one, rate — and returns answers with confidences in a single forward pass. There is no token stream to wait for because it has nothing to generate.&lt;/p&gt;

&lt;p&gt;The things you should know before adopting it, up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is &lt;strong&gt;API-only&lt;/strong&gt; today — TypeSafe direct, OpenRouter, or the Vercel AI Gateway. There is no self-hosted path. The state you judge leaves your machine: page DOM, command output, transcript messages, the shell command you're asking about.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;weeks old&lt;/strong&gt;, and so is everything around it, including what I built.&lt;/li&gt;
&lt;li&gt;It answers, it does not explain. If you need a reason in words, you still need an LLM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built &lt;a href="https://github.com/shitianfang/jev-use" rel="noopener noreferrer"&gt;jev-use&lt;/a&gt; on top of it: a plugin for Claude Code, Codex and &lt;a href="https://github.com/badlogic/pi-mono" rel="noopener noreferrer"&gt;pi&lt;/a&gt; that routes the no-text steps to Jev and leaves the LLM everything that actually produces writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;Jev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rate&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;jev-use&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;jev&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;Jev&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;jev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ciRunSummary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Did the run fully succeed?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Next action?&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;merge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rerun&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;looks flaky&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hold&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 attention&lt;/span&gt;&lt;span class="dl"&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;rate&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?&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;routine&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;worth a look&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;incident&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;answers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// "merge"&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;next&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="c1"&gt;// 0.93&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;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;escalate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three questions about one state ride one call, and latency is flat in the number of questions, so there is no reason to ask them one at a time.&lt;/p&gt;

&lt;p&gt;The other branch is the part that matters. Anything Jev can't or shouldn't decide comes back with &lt;code&gt;escalate: true&lt;/code&gt; and a typed reason:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;reason&lt;/th&gt;
&lt;th&gt;meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;writing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the step's output is text; the LLM has to do it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;open_ended&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the answer doesn't fit a yes/no, a choice, or a scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oversized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the state is too big to judge honestly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unsure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;it answered, but not decisively enough to act on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unreachable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the backend failed — the question goes back to the LLM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;unreachable&lt;/code&gt; never resolves to a default answer. That distinction is load-bearing when the question was "is this command safe to run": a judge that can't be reached hands the question back, it does not wave the command through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things I measured
&lt;/h2&gt;

&lt;p&gt;Every number below comes from a script in &lt;a href="https://github.com/shitianfang/jev-use/tree/main/bench/examples" rel="noopener noreferrer"&gt;&lt;code&gt;bench/examples/&lt;/code&gt;&lt;/a&gt; that you can re-run with your own key, and every GIF is a 1× recording of a real run with no cuts.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Decision rate — and what the number looks like after you fix the baseline
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F65lakviz7cooh11b9fps.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F65lakviz7cooh11b9fps.gif" alt="Three Pong lanes: the Jev ball sweeps the field while the LLM balls crawl" width="719" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One paddle decision per ball step, three lanes, 20 seconds, the same three-option question to each model. The ball's speed on screen &lt;em&gt;is&lt;/em&gt; the decision latency. Jev gets 86 decisions in 20 s; claude-haiku-4.5 gets 6 and gemini-3-flash 3 — all three called the way agent loops usually call them, which is the wrong way. Take the GIF as a feel for what decision latency looks like, not as a model comparison. The comparison is the table below.&lt;/p&gt;

&lt;p&gt;That 14× is what a naive caller gets, and I don't think it's an honest model comparison — so I ran the fixed version before publishing it. Strict JSON-schema enum output on both baselines, and &lt;code&gt;thinkingBudget: 0&lt;/code&gt; for Gemini. 40 fresh states per arm, twice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;arm&lt;/th&gt;
&lt;th&gt;p50&lt;/th&gt;
&lt;th&gt;cost / 1k judgments&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jev&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;225 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.018&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude-haiku-4.5, called the way agent loops usually call it&lt;/td&gt;
&lt;td&gt;2,874 ms&lt;/td&gt;
&lt;td&gt;$1.67&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claude-haiku-4.5, enum-constrained&lt;/td&gt;
&lt;td&gt;691 ms&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemini-3-flash, called the usual way&lt;/td&gt;
&lt;td&gt;6,406 ms&lt;/td&gt;
&lt;td&gt;$2.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gemini-3-flash, enum-constrained, thinking off&lt;/td&gt;
&lt;td&gt;1,027 ms&lt;/td&gt;
&lt;td&gt;$0.09&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The honest latency lead is 3×, not 14×.&lt;/strong&gt; What survives the fair fight is cost — 5× cheaper than the constrained Gemini call and 16× cheaper than the constrained Haiku one — and that the answer is inside the option set by construction rather than parsed back out of prose. Note where that leaves you: a properly configured gemini-3-flash is the real competition, not the 6.4-second version of itself. On decision quality the five arms are indistinguishable: 24 to 30 correct out of 40 against a geometric reference, Jev included.&lt;/p&gt;

&lt;p&gt;Two mechanism findings that cost me an afternoon, in case they save you one: on Haiku, &lt;code&gt;reasoning_effort: "none"&lt;/code&gt; &lt;em&gt;enables&lt;/em&gt; extended thinking (400+ reasoning tokens) where simply omitting the parameter gives zero; and Gemini's thinking only switches off through &lt;code&gt;providerOptions.google.thinkingConfig.thinkingBudget: 0&lt;/code&gt; — &lt;code&gt;thinking_level&lt;/code&gt; and the top-level variants are silently ignored.&lt;/p&gt;

&lt;p&gt;Two things you should see next to that 86. First, 69 of those decisions came back flagged &lt;code&gt;unsure&lt;/code&gt; — through this gateway Jev returns no confidence field, so jev-use reconstructs it from the distribution margin, which is structurally thin on many-option questions; if your router escalates on &lt;code&gt;unsure&lt;/code&gt;, the effective count is 17, not 86. Second, Jev answered &lt;code&gt;stay&lt;/code&gt; &lt;strong&gt;zero times in 80 calls&lt;/strong&gt;, including the 13 states where holding position was the right answer; its directional judgment on the other 27 was perfect. A model that never once picks one of your options fails silently, so check the answer distribution and not only the accuracy.&lt;/p&gt;

&lt;p&gt;(All latencies here are client-side from a Linux container in Europe, network included. Yours will differ.)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A browser task where the two models split the work
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2For9u1q7pof118sivgp62.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2For9u1q7pof118sivgp62.gif" alt="OpenStreetMap directions: Jev picks controls, the LLM types locations, a wrong route is rejected and repaired" width="79" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenStreetMap directions, 20.7 seconds end to end. Jev makes all 10 click decisions (p50 274 ms); the LLM takes over at exactly the four moments where text goes into a field.&lt;/p&gt;

&lt;p&gt;The interesting part isn't the click latency, it's a fault I didn't write into the script. OSM's geocoder resolves "Eiffel Tower" to a location 1,809 km from the intended one. It does that deterministically, which is why the repair reproduces in 8 runs out of 8 — a fixture, not luck, and the cleanest way I have to show the escalation path under a real failure. Jev's goal-level check ("does this route match what the user asked for?") rejected the route at 0.33 confidence and escalated. Because the fix is more text, the fields went back to the LLM with the geocoder's actual answer attached as evidence, and the second attempt verified at 0.94 on the real 3.7 km walking route.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Context compaction
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjt4ipd2stuf52figgj0d.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjt4ipd2stuf52figgj0d.gif" alt="A transcript at 94% of the window is judged message by message, then compacted to 44%" width="600" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A real transcript filling 94.6% of the context window. Jev judges 200 messages keep-or-drop in 7 batched calls (p50 408 ms); the LLM writes one 104-word paragraph to replace the dropped block. The window falls to 44.3% and 3 of 3 recall checks on the dropped facts still pass.&lt;/p&gt;

&lt;p&gt;This one also produced the bug worth repeating to anyone building compaction: the first summary said "216 tests" where the source output said 58. The prompt now forbids the summarizer from computing numbers at all, and the demo traces every number in the paragraph back to a message on screen — 16 of 17 traceable in the run I kept, with the untraceable one flagged. If you compact context with an LLM, check its arithmetic.&lt;/p&gt;

&lt;p&gt;One fair warning before you copy this pattern: compaction is the family Jev scored &lt;em&gt;worst&lt;/em&gt; on in the accuracy run below — 56.3%. It works here because the keep-or-drop rule is written into the messages themselves. Read the next section before building on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it right, though?
&lt;/h2&gt;

&lt;p&gt;Speed is the easy half. A 224 ms wrong answer is worth less than a 3 s right one, and until today every number in this repo was a latency number, with correctness samples of n=8.&lt;/p&gt;

&lt;p&gt;So I measured it: &lt;strong&gt;454 judgments over 422 real states&lt;/strong&gt; across five families — shell commands an agent proposed to run, real command output with its real exit code, Hacker News rows, transcript messages judged keep-or-drop, and commits and PRs to triage. Nothing invented; every command was really run, every row really fetched.&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;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agreement with the reference&lt;/td&gt;
&lt;td&gt;82.2% (373/454)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escalated back to the LLM&lt;/td&gt;
&lt;td&gt;14.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agreement among the verdicts it acted on&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;89.5%&lt;/strong&gt; (349/390)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Always-answer-the-majority-class baseline&lt;/td&gt;
&lt;td&gt;68.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whole corpus&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$0.0051&lt;/strong&gt;, 77 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same corpus, one &lt;code&gt;claude-haiku-4.5&lt;/code&gt; call per judgment&lt;/td&gt;
&lt;td&gt;~$0.50, ~443 s (extrapolated from a random 45 of the 454)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;89.5% is the operational number, because the escalated ones go back to the LLM by design — and escalation earns its place: of the 47 escalations that carried a provisional verdict, 51% would have been right, against 87.5% for the ones it acted on. (The other 17 escalations — &lt;code&gt;writing&lt;/code&gt;, &lt;code&gt;open_ended&lt;/code&gt;, &lt;code&gt;oversized&lt;/code&gt;, &lt;code&gt;unreachable&lt;/code&gt; — produce no answer to score.)&lt;/p&gt;

&lt;p&gt;Now the parts that don't flatter it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The grader is an LLM, and LLMs agree with LLMs.&lt;/strong&gt; I scored against claude-opus-5 asked the identical typed question over the identical state. On a random 45-item subsample, claude-haiku-4.5 — weaker and far cheaper than the grader — agreed with it 35/45 where Jev agreed 33/45. Some of that gap is grader bias rather than Jev being worse, and this experiment cannot separate the two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I hand-audited 34 reference labels and disagreed with 3.&lt;/strong&gt; So roughly nine points of this is reference noise. Don't read any difference smaller than that, mine included.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It splits cleanly on whether the criterion is written into the state.&lt;/strong&gt; Command completion, scored against real exit codes — actual ground truth, not an opinion — 73/73. Hacker News topical matching, 94.2%. Shell-command gating, 80.9% — and the direction of its errors is the part worth knowing. Of the 22 commands the reference labelled dangerous, Jev denied 18 and escalated 4: &lt;strong&gt;none were wrongly allowed.&lt;/strong&gt; All four of its mistakes are over-refusals among the 88 safe ones, and all four are commands that mutate nothing (&lt;code&gt;git merge nonexistent-ref&lt;/code&gt;, &lt;code&gt;cp /nope ./x&lt;/code&gt;). It reads intent-to-mutate, not outcome. Note it clears that family's majority baseline by 0.9 points, i.e. not at all — read the per-class numbers, not the headline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context compaction was 56.3% — worse than always giving the same answer.&lt;/strong&gt; That is the result I'd want to know before adopting this, so: don't hand Jev a keep-or-drop rule that lives in your head rather than in the state. (The honest counter-caveat is that 29 of that family's 38 disagreements trace to one reference decision at a batch boundary, so its effective sample is closer to 10 than 87 — which cuts both ways.)&lt;/p&gt;

&lt;p&gt;Corpus, provenance, per-family confusion patterns, verbatim disagreements and the run-to-run variance are in &lt;a href="https://github.com/shitianfang/jev-use/blob/main/bench/RESULTS.md" rel="noopener noreferrer"&gt;bench/RESULTS.md&lt;/a&gt;; &lt;code&gt;node bench/examples/agree.mjs&lt;/code&gt; re-runs the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this does not help
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-off judgments.&lt;/strong&gt; Saving 300 ms once is invisible. This only pays inside loops that make the same kind of decision over and over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loops that are mostly generation.&lt;/strong&gt; If nearly every step writes code, the router hands nearly everything back to the LLM and you have saved nothing. &lt;a href="https://github.com/compozy/yoshi" rel="noopener noreferrer"&gt;yoshi&lt;/a&gt; measured −0.03% on exactly that shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retroactive context pruning.&lt;/strong&gt; Judging &lt;em&gt;old&lt;/em&gt; context for "will I need this later" dropped later-needed information 73% of the time in &lt;a href="https://github.com/Nyarlathoteppppp/pi-jev-context" rel="noopener noreferrer"&gt;pi-jev-context's measurements&lt;/a&gt;, and stays shadow-only in that project. Judge at write time or not at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decisions a 20-line heuristic already makes.&lt;/strong&gt; If a regex or a local 0.5B classifier answers your question at 20 ms for nothing and never leaves the machine, use that. This is for the open-ended states you cannot enumerate — and for those, a round trip to a third party is the price.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the caveats, the variance, and the runs that went badly are in &lt;a href="https://github.com/shitianfang/jev-use/blob/main/bench/RESULTS.md" rel="noopener noreferrer"&gt;bench/RESULTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; jev-use &lt;span class="nb"&gt;install&lt;/span&gt;     &lt;span class="c"&gt;# wires Claude Code, Codex and pi — whichever it finds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command does not hand-edit your config. It runs each harness's own registration command — &lt;code&gt;claude mcp add --scope user jev -- npx -y jev-use serve&lt;/code&gt;, the Codex equivalent, &lt;code&gt;pi install&lt;/code&gt; — so you undo it the same way you undo any other MCP server: &lt;code&gt;claude mcp remove jev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then set &lt;code&gt;TYPESAFE_API_KEY&lt;/code&gt;, &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt; or &lt;code&gt;AI_GATEWAY_API_KEY&lt;/code&gt; for whichever provider you want, or &lt;code&gt;JEV_BACKEND=mock&lt;/code&gt; for a keyless dry run that makes no network calls at all. &lt;code&gt;npx -y jev-use doctor&lt;/code&gt; prints what it found.&lt;/p&gt;

&lt;p&gt;One thing the install line doesn't say, and it matters. As an MCP server, the harness still spends an LLM turn deciding to call &lt;code&gt;jev_judge&lt;/code&gt; — so what the plugin buys you there is a cheaper, guaranteed-in-set answer and the ability to batch thirty questions into one call, not a faster individual step. The two paths that actually delete LLM latency are the PreToolUse gate, which runs as a hook with no model turn at all, and the library: &lt;code&gt;npm i jev-use&lt;/code&gt;, your own loop calling &lt;code&gt;jev.judge()&lt;/code&gt;, no runtime dependencies on the judgment path. The 3× in this post is the library path. Use the plugin for cost, gating and batching; use the library if you want the latency.&lt;/p&gt;

&lt;p&gt;Repo, scripts, and the full measurements: &lt;strong&gt;&lt;a href="https://github.com/shitianfang/jev-use" rel="noopener noreferrer"&gt;github.com/shitianfang/jev-use&lt;/a&gt;&lt;/strong&gt; (MIT).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The code was substantially written with Claude Code. The measurements are mine, and every one of them re-runs from the repo.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>How We Stopped Merge Hell in Our AI-First Dev Team</title>
      <dc:creator>shitianfang</dc:creator>
      <pubDate>Tue, 07 Apr 2026 01:17:43 +0000</pubDate>
      <link>https://dev.to/shitianfang/how-we-stopped-merge-hell-in-our-ai-first-dev-team-44ae</link>
      <guid>https://dev.to/shitianfang/how-we-stopped-merge-hell-in-our-ai-first-dev-team-44ae</guid>
      <description>&lt;h2&gt;
  
  
  The Problem No One Talks About
&lt;/h2&gt;

&lt;p&gt;Everyone's excited about vibe coding. But here's what happens &lt;br&gt;
when a team of 3 uses Claude Code / Cursor simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dev A's agent: &lt;code&gt;GET /api/users/:id&lt;/code&gt; → &lt;code&gt;{ userId, name }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dev B's agent: &lt;code&gt;GET /api/users/:id&lt;/code&gt; → &lt;code&gt;{ user_id, username }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dev C's frontend: expects &lt;code&gt;{ id, displayName }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Merge day = disaster. Every. Single. Time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Happens
&lt;/h2&gt;

&lt;p&gt;AI agents are stateless. Each session starts fresh with no &lt;br&gt;
knowledge of what your teammates' agents decided. They make &lt;br&gt;
reasonable but incompatible assumptions about the same API.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: Shared Living Specs
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://coware.team" rel="noopener noreferrer"&gt;Coware&lt;/a&gt; around one idea: &lt;br&gt;
every agent reads the same spec before writing code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste one prompt into your AI agent&lt;/li&gt;
&lt;li&gt;Agent reads &lt;code&gt;/llms.txt&lt;/code&gt; → auto-setup&lt;/li&gt;
&lt;li&gt;Scans codebase → generates interface specs (Markdown)&lt;/li&gt;
&lt;li&gt;Specs sync to server → every teammate's agent pulls them&lt;/li&gt;
&lt;li&gt;All agents code against the same contract&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API merge conflicts: &lt;strong&gt;zero&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Time re-explaining context to agents: &lt;strong&gt;zero&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Setup: &lt;strong&gt;30 seconds, one paste&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open source, self-hostable, works with Claude Code, Cursor, &lt;br&gt;
Copilot, Windsurf.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://coware.team" rel="noopener noreferrer"&gt;coware.team&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/livespecs/coware-living-spec" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Curious how other teams handle this — what's your workflow?&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>showdev</category>
      <category>vibecoding</category>
    </item>
  </channel>
</rss>
