Jev: The Cheap Judge That Can Still Cost You
Jev answers typed questions about your agent's evidence in a few hundred milliseconds for fractions of a cent. This module is about the question that price hides: where in a Claude Code build→verify→fix loop should a cheap judge sit, and when does adding one make the whole system worse?
Learning Objectives
- Explain what a System One model like Jev is, and how it differs from a generative LLM such as Claude
- Write Jev’s three question types (
noul,choice,score) and read their typed answers, including the difference between probability and confidence - Explain why a schema guarantees the shape of a decision but never its correctness
- Compare four placements of a judge in a coding-agent loop, and compute how a hard gate caps end-to-end recall
- Design a shadow-mode rollout that earns a judge its authority one region at a time
- Wire Jev into Claude Code as an advisory hook that can never block the agent
Prerequisites: M05 (tool use), M18 (evaluation). M22C (quality gates) and M26 (hooks) help with the last two sections. | Level: Intermediate
What Jev Is — A Model That Decides Instead of Writes
Picture an emergency department with one senior physician. Before triage existed, every patient waited for the physician, whether they had a broken leg or a paper cut. The physician spent the night on paper cuts, and the patient with chest pain waited behind them.
A triage nurse fixes that. In ninety seconds the nurse asks a fixed set of questions and assigns a category: immediate, urgent, can wait. The nurse never diagnoses or treats anyone. The job is to decide, quickly and cheaply, who the physician sees first. But a triage protocol has a failure mode that every hospital designs around: the patient who looks fine and isn’t. Good hospitals never let triage send anyone home. Triage orders the queue; it does not empty it.
Jev is the triage nurse, and Claude is the physician. Jev reads the evidence a coding agent produces (test logs, diffs, run history) and answers a few fixed questions with numbers attached. Claude does the diagnosis and the fix. The rest of this module is about the patient who looks fine: the real regressionA bug that a code change introduces into something that used to work. Here: a test that passed before this diff and fails because of it. that Jev reads as a flaky testA test that sometimes passes and sometimes fails on the same code, usually because of timing, ordering or shared state. Rerunning it often “fixes” it, which is exactly why a real bug that looks flaky is dangerous..
Jev is the first System One modelTypeSafe AI’s name for a class of models built for fast, structured decisions that software consumes directly. The name borrows from psychology’s “System 1”: fast, intuitive judgement, as opposed to slow, deliberate reasoning. from TypeSafe AI, released in early access on 15 September 2026. You send it two things: a state (the evidence, as a string or a JSON object) and a set of typed questions. It returns one typed answer per question, with probabilities attached. It never returns prose.
Claude produces text one token at a time, and each token depends on the last. That is autoregressive generationGenerating output one token at a time, where each new token is conditioned on every token before it. It is how Claude writes, and why long answers take longer.. TypeSafe says Jev instead uses parallel sampling: it produces all of its answers in one pass, because an answer is a category or a number, not a sentence. That is where the speed comes from.
There is one more difference. TypeSafe trains Jev for calibrationA model is calibrated when its stated probabilities match reality: of all the answers it gives with probability 0.8, about 80% should be right. A calibrated 0.55 is useful information; an overconfident 0.99 is not.: the goal is that its probabilities mean what they say. A calibrated 0.55 tells your code “genuinely unsure”, and your code can act on that.
The published characteristics
| Property | Value | Source |
|---|---|---|
| Price | $0.042 per 1M input tokens; output tokens are not charged | TypeSafe, Vercel model page |
| Latency | 70–500 ms end to end | TypeSafe launch post |
| Context | 64k tokens per request; 32k for the state plus the longest single question | Vercel guide, TypeSafe docs |
| Endpoint | POST https://api.typesafe.ai/v1/systemone | TypeSafe API reference |
| Other ways in | Vercel AI GatewayVercel’s hosted proxy that serves many providers’ models behind one API and one bill. You reach Jev there through the Vercel AI SDK rather than TypeSafe’s native endpoint. (typesafe-ai/jev), Cloudflare Workers AI (typesafe/jev), OpenRouter | Vendor docs |
| Does not do | Write code, explain its reasoning, run multi-step investigations | TypeSafe docs |
Here is the arithmetic that makes Jev tempting. A CIContinuous integration: the automated system that builds the code and runs the test suite on every change, before anyone merges it. pipeline that fails 2,000 tests a day, each with about 1,500 tokens of logs and diff, sends Jev 3 million input tokens a day. At $0.042 per million, that is about 13 cents a day. Sending the same 2,000 failures to Claude Sonnet 5 at $2 per million input tokens costs about $6 in input alone, before thinking and output tokens. That is a gap of roughly 48×. The rest of this module explains why the gap is real and still the wrong number to optimise.
The Three Question Types
Each question you send to Jev has an id you choose, a type, some instructions, and usually some criteria. Jev evaluates every question independently and in parallel. Adding a question never changes the answer to another one.
| Type | You send | You get back | Limits |
|---|---|---|---|
noul (yes/no) | Optional {"true": ..., "false": ...} descriptions | noul: probability of “yes”, from 0 to 1 | — |
choice | A map of option → description | choice, probabilities for every option, confidence | 2–255 options |
score | An ordered list of level descriptions, low to high | score (probability-weighted), legend, probabilities, confidence | 2–10 levels |
A real response for the three questions this module uses throughout looks like this:
This is the lab’s replay response for test F10, with the legend abridged. It follows the documented response shape, but the numbers are synthetic, not a recorded Jev response. A live response names a real model version, such as jev-1.13.0.
Probability vs. confidence: two different numbers
These are easy to mix up, and the policy code later depends on keeping them apart.
- Probability is about one outcome.
"flaky": 0.84means Jev puts 84% on “flaky”. - Confidence is about the whole distribution: how concentrated it is, from 0 to 1. TypeSafe says it is derived from the probabilities but does not publish the formula. Two answers can both pick “flaky” at 0.84 and still differ in confidence, depending on how the remaining 16% is spread.
- Score is a probability-weighted mean. In the example it is 0.55: between Low (0) and Medium (1), leaning Low. It is not a class label, so read
probabilitiesif you need one.
Vercel’s guide adds a practical wrinkle: probabilities are rounded, so a distribution can sum to 0.99. Never check sum == 1.
The Medium article’s code uses the Vercel AI SDK (experimental_evaluate, AI SDK 7.0.105 or later). That SDK names things differently from the native API, and copying a field name from one into code for the other fails quietly:
- Yes/no type: native
"noul"→ Vercel"boolean" - Yes/no answer: native
answers.x.noul→ Vercelanswers.x.probability - Confidence: native
answers.x.confidence→ Vercelresult.providerMetadata?.typesafe?.confidence?.x - Model id: native
"jev-latest"or a pinned"jev-1.13.0"→ Vercel"typesafe-ai/jev"
Writing good questions
The Vercel guide gives three rules, and all three apply here:
- Ask atomic questions, ones a person could answer in seconds. Combine the answers in code, not in the question.
- Describe options; don’t just label them. “The diff plausibly causes the observed failure” gives the model far more to work with than “yes”.
- Send only the relevant state. Every token of state is billed and dilutes the evidence. A diff hunkOne contiguous block of changed lines in a diff, with a few lines of context around it. Sending only the hunk that touches the failing code keeps the state small and on topic. beats the whole diff.
“Typed” Prevents Malformed Answers, Not Wrong Ones
TypeSafe markets Jev as a model that “can’t hallucinate”. That is true in a narrow and useful sense. If a question allows only regression, flaky, environment and unknown, Jev cannot invent a fifth category, wrap its answer in an apology, or return JSON your parser chokes on. A whole class of production bugs goes away: the ones where a generative model answers "Probably flaky, but…" and your if statement never matches.
What typing cannot do is make the chosen category correct. Jev can pick flaky, a perfectly valid option, for a test that fails because the diff removed a lock. Vercel’s guide says it plainly: the schema constrains the answers but doesn’t guarantee they’re correct. Arize’s independent analysis makes the same distinction: a schema-correct classification can still be wrong.
This matters because of what happens after the wrong answer. If Jev’s answer is one signal among several, a wrong answer is a weak signal that other checks can outvote. If Jev’s answer decides whether Claude ever sees the failure, a wrong “flaky” means the stronger model never gets the chance to catch it.
- “It can’t hallucinate, so it’s always right.” It can’t produce a malformed answer. It can produce a wrong one.
- “A low regression probability means the change is safe.” It means Jev leans away from “regression” on this evidence. Absence of a signal is not proof of correctness.
- “It’s 50× cheaper, so the system is 50× cheaper.” The judge’s bill is rarely the expensive part. A missed regression that ships costs more than a year of Jev calls.
- “Calibrated means accurate.” Calibrated means the probabilities are honest. A calibrated judge that says 0.55 is telling you it doesn’t know, and your policy has to handle that.
- “It beat Sonnet on a benchmark, so it will improve my loop.” The benchmark compared judges on one task. Your loop is a different system (see The Published Numbers).
Where Jev Sits in a Build → Verify → Fix Loop
A coding agent like Claude Code works in a loop: edit the code, run the checks, read what failed, fix it, check again. The tempting design drops Jev into the middle. Deterministic tools run, Jev cheaply screens whatever fails, and Claude only investigates what Jev considers important.
The order of the stages is the most important design decision in this module. Compilers, linters, type checkers, test runners, security scanners and invariant checks come first, and they stay authoritative wherever they can give a deterministic verdict. A type error is a type error; no model should be asked to “judge” it. Jev belongs after those tools, working on the evidence they produce, never in their place. And the step after Jev is not Claude directly but policy code: ordinary, testable code that turns Jev’s numbers into an action.
Press play to follow one failure through the loop.
Every stage you move ahead of a deterministic check trades a certain answer for a probable one. A 50-millisecond type checker is right every time and costs nothing. Putting a model before it saves nothing and adds a way to be wrong. Jev earns its place only on the evidence the deterministic tools cannot classify, such as “is this failure caused by the diff, or by a slow CI runner?”
Four Architectures, Four Different Costs of Being Wrong
| Pattern | What Jev controls | Main benefit | Main risk |
|---|---|---|---|
| Hard gate | Whether Claude sees an item at all | Maximum call reduction | False negativesA real problem the judge scored as “not a problem”. Here, a genuine regression Jev called flaky or environmental. become invisible |
| Soft prioritizer | Order and urgency | Faster attention, nothing discarded | Low-ranked items may still be neglected |
| Independent signal | One input to deterministic policy | Combines semantic judgement with explicit rules | More policy and monitoring work |
| Uncertainty router | Which ambiguous items escalate | Automates clear cases, keeps a fallback | ThresholdsThe cut-off values your policy code compares Jev’s probabilities against, such as “investigate when P(regression) ≥ 0.75”. Choosing them sets the trade-off between misses and false alarms. need calibrating on your data |
Why a hard gate caps the whole pipeline
RecallOf all the real regressions, the share that got caught. 90% recall means 1 real regression in 10 slipped through. is the share of real regressions that get caught. With a hard gate in front, a regression is caught only if both stages catch it: the gate has to pass it and the agent has to recognise it. Ignoring correlated errors, the probabilities multiply:
That example is illustrative, not a Jev benchmark. The point it makes is structural. The 97%-capable downstream model didn’t get worse. The system got worse, because 10% of the real regressions never reached it. The gate’s recall is a ceiling on everything after it. Drag the sliders to see how fast that ceiling bites:
The same 24 failures, two placements
This is the lab’s data: 24 CI failures from a fictional order-tracking service, 10 of them real regressions. The Jev answers are the lab’s synthetic fixtures. Watch where each failure lands, especially F10, a release-blocking race condition that Jev reads as flaky.
Claude investigates
Rerun (passed)
Never seen by Claude
Chip colours: amber = real regression, red = real HIGH-severity regression, green ✓ = regression Claude caught, grey = flaky/environment.
Under the hard gate, Claude sees 9 failures and the pipeline catches 7 of the 10 regressions: 70%. The uncertainty router sends Jev’s unsure cases to Claude and reruns its confident “flaky” and “environment” cases, escalating any rerun that fails again. F10’s rerun fails, so it reaches Claude. The router catches 9 of 10, the same as sending everything to Claude, with 14 Claude calls instead of 24.
The soft prioritizer deserves one warning. It keeps everything visible, so on paper its recall equals the baseline. But if the queue is cut at the same 9 items the gate allowed, because the reviewer ran out of time, it catches exactly what the gate caught. A queue nobody drains is a gate with extra steps.
Optimise expected total cost, not the API bill
If the workflow processes 10,000 items and one false negative can ship a serious regression, saving a few thousand model calls is not automatically an improvement. In the lab, with illustrative costs of $5,000 for a missed high-severity regression and $500 for a medium one, the hard gate saves $0.035 in API calls and adds $5,500 in expected losses.
In the lab, Jev screens all 24 failures for about $0.0003. Claude Sonnet 5 investigating all 24 cost $0.056 in a live run: 506 input and 132 output tokens per failure on average. The judge’s price was never the expensive part. The only question that matters is what its mistakes cost after they leave it.
What the Published Numbers Actually Show
Ben Greenberg: Jev vs. Claude on one bounded decision
Greenberg took 102 archived submissions and ran each one three times, for 306 decisions. Every decision applied a fixed policy to an evidence packet and picked one of three states: satisfied, not satisfied, or insufficient evidence. The post reports:
- Accuracy: Jev scored 100.0% with a choice question plus four diagnostic
noulquestions. Claude Sonnet 5 with high reasoning scored 99.0%. Jev’s choice question on its own made 2 errors in 306. Rebuilding the policy in code from the four nouls did worst, at 94.1%. - Median latency: 378 ms for Jev, 3,554 ms for Sonnet
- Cost per 10,000 evaluations: about $2.27 vs. $129.74, roughly 57×
- Expected Calibration Error (ECE)How far a model’s stated confidence is from its actual accuracy, averaged across confidence levels. 0 is perfectly calibrated; lower is better.: 0.037 for Jev vs. 0.058 for Sonnet. Sonnet’s few errors sat in its 0.9–1.0 confidence band. Both of the choice-only Jev errors sat at 0.2–0.3.
Two lessons come out of this for system design. First, a policy hand-assembled from several signals is not automatically better than a well-posed single question; the hand-built one was the weakest setup in the post. Keep policy code simple, and measure it like everything else.
Second, and most useful, is calibration. An error made at 0.25 confidence can be routed to review. An error made at 0.95 confidence goes straight through any threshold. Greenberg calls his setup “a deliberately unfair place to make a sweeping model comparison”, and notes that Jev does not replace the parts of his workflow that need code analysis and written explanations.
Arize: Jev as an LLM-as-judge replacement
Arize compared Jev with LLM judgesAlso called LLM-as-judge: using a general-purpose language model to grade or classify another system’s output, usually with a rubric in the prompt and an explanation in the reply. Covered in M18. on hallucination detection. As summarised in its published write-up, Jev matched Claude Opus 5 at 87% accuracy on a held-out set after each judge’s decision threshold was tuned, at roughly 1/300 of the cost and 23× the speed. Arize describes the evidence as promising but early and task-dependent. It also names the biggest loss: Jev gives no explanation, so its suggested pattern is to run Jev on every trace and re-run a sample of the failures through an LLM judge for the reasoning.
Both are judge-vs-judge comparisons on one bounded task. Neither measures a coding-agent loop with Jev in front of Claude. “Jev matched Sonnet at classifying X” says nothing about the recall of a pipeline where Jev decides what Sonnet gets to see. Jev’s price and latency are broadly attractive, but its accuracy has to be established separately for each question, dataset, threshold and position in your workflow.
A Runnable Screening Layer
One function sends a failing test’s evidence to Jev and returns one of three actions. The Python and Node.js tabs call the native REST API directly. The third tab shows the same logic through the Vercel AI SDK, which is the form the source article uses, so you can see the naming differences side by side.
- Chunk 1, the questions. What: three atomic questions: a
noul, a four-waychoicethat includesunknown, and a three-levelscore. Why: theunknownoption gives Jev an honest way to say “not enough evidence” instead of forcing a guess into another bucket. Gotcha: the criteria are sentences, not labels. “The failure is probably flaky, environmental, or unrelated” is a much better “false” than “no”. - Chunk 2, the call. What: one POST with a bearer token and a 10-second timeout, retrying only on 429 (rate limited) and 529 (overloaded). Why: those are the two statuses TypeSafe documents as retryable. A 422 is your bug, and retrying it just burns time. Gotcha: in Node,
fetchhas no timeout of its own, henceAbortSignal.timeout(). - Chunk 3, failing toward scrutiny. What: any Jev failure (network, auth, timeout) returns
review-ambiguous-case. Why: a judge that is down must never turn into a judge that says “fine”. Gotcha: a bareexcept: return "rerun"here would silently turn every Jev outage into suppressed failures. - Chunk 4, the policy. What: three thresholds on three different numbers: regression probability, choice confidence, and the chosen option’s probability. Why: the policy lives in code, where you can read it, test it and version it. Gotcha: the thresholds are examples. A security gate, a flaky-test router and a docs scorer need different cutoffs, chosen from labelled cases like your own.
"""Screen one failing CI test with Jev. Python 3.10+; pip install "httpx>=0.27"; export TYPESAFE_API_KEY=..."""
import os
import time
import httpx
API_URL = "https://api.typesafe.ai/v1/systemone"
MODEL = "jev-latest" # pin e.g. "jev-1.13.0" once your thresholds are tuned against it
# --- Chunk 1: atomic questions; criteria DESCRIBE each option ---
QUESTIONS = {
"is_regression": {
"type": "noul",
"instructions": "Did the current code change probably cause this test failure?",
"criteria": {"true": "The diff plausibly causes the observed failure.",
"false": "The failure is probably flaky, environmental, or unrelated to the diff."},
},
"failure_type": {
"type": "choice",
"instructions": "What best explains this failure?",
"criteria": {"regression": "The current diff introduced a product defect.",
"flaky": "The test is nondeterministic or timing-sensitive.",
"environment": "Infrastructure or configuration caused the failure.",
"unknown": "The evidence is insufficient to choose another category."},
},
"severity": {
"type": "score",
"instructions": "How strongly should this failure block the change?",
"criteria": ["Low: non-blocking or clearly unrelated",
"Medium: plausible issue requiring more evidence",
"High: likely release-blocking regression"],
},
}
class JevError(RuntimeError):
pass
# --- Chunk 2: one call, retry only what the API says is retryable ---
def ask_jev(state: dict, attempts: int = 3) -> dict:
key = os.environ.get("TYPESAFE_API_KEY")
if not key:
raise JevError("TYPESAFE_API_KEY is not set")
body = {"model": MODEL, "state": state, "questions": QUESTIONS}
for attempt in range(1, attempts + 1):
try:
r = httpx.post(API_URL, json=body, timeout=10.0,
headers={"Authorization": f"Bearer {key}"})
except httpx.HTTPError as e:
if attempt == attempts:
raise JevError(f"network error: {e}") from e
else:
if r.status_code == 200:
return r.json()["answers"]
if r.status_code not in (429, 529) or attempt == attempts:
raise JevError(f"HTTP {r.status_code}: {r.text[:200]}")
time.sleep(0.5 * 2 ** attempt)
raise JevError("unreachable")
def route(state: dict) -> str:
# --- Chunk 3: a judge that is down fails toward MORE scrutiny ---
try:
a = ask_jev(state)
except JevError as e:
print(f"[jev] unavailable, escalating: {e}")
return "review-ambiguous-case"
# --- Chunk 4: policy in code. EXAMPLE thresholds -- tune on your own labelled cases ---
p_reg = a["is_regression"]["noul"]
ft = a["failure_type"]
type_prob = ft["probabilities"].get(ft["choice"], 0.0)
if p_reg >= 0.75 and ft.get("confidence", 0.0) >= 0.60 and type_prob >= 0.70:
return "investigate-with-claude"
if p_reg <= 0.20 and ft["choice"] in ("flaky", "environment") and type_prob >= 0.75:
return "rerun-and-collect-evidence"
return "review-ambiguous-case"
if __name__ == "__main__":
failure = {
"testName": "test_sla_credit_concurrent_updates",
"stdout": "FAILED tests/test_sla.py::test_sla_credit_concurrent_updates\n"
"assert po.credit_total == 2\n where po.credit_total = 4",
"stderr": "",
"diffHunk": "- with po_lock(po.id):\n- apply_credit(po)\n+ apply_credit(po)",
"recentRuns": [{"passed": True, "commit": "a41c"}, {"passed": False, "commit": "b7e2"}],
}
print(route(failure))
// Screen one failing CI test with Jev. Node 18+ (built-in fetch). TYPESAFE_API_KEY=... node screen.mjs
const API_URL = "https://api.typesafe.ai/v1/systemone";
const MODEL = "jev-latest"; // pin e.g. "jev-1.13.0" once your thresholds are tuned against it
// --- Chunk 1: atomic questions; criteria DESCRIBE each option ---
const QUESTIONS = {
is_regression: {
type: "noul",
instructions: "Did the current code change probably cause this test failure?",
criteria: {
true: "The diff plausibly causes the observed failure.",
false: "The failure is probably flaky, environmental, or unrelated to the diff.",
},
},
failure_type: {
type: "choice",
instructions: "What best explains this failure?",
criteria: {
regression: "The current diff introduced a product defect.",
flaky: "The test is nondeterministic or timing-sensitive.",
environment: "Infrastructure or configuration caused the failure.",
unknown: "The evidence is insufficient to choose another category.",
},
},
severity: {
type: "score",
instructions: "How strongly should this failure block the change?",
criteria: [
"Low: non-blocking or clearly unrelated",
"Medium: plausible issue requiring more evidence",
"High: likely release-blocking regression",
],
},
};
class JevError extends Error {}
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// --- Chunk 2: one call, retry only what the API says is retryable ---
async function askJev(state, attempts = 3) {
const key = process.env.TYPESAFE_API_KEY;
if (!key) throw new JevError("TYPESAFE_API_KEY is not set");
for (let attempt = 1; attempt <= attempts; attempt++) {
let r;
try {
r = await fetch(API_URL, {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: MODEL, state, questions: QUESTIONS }),
signal: AbortSignal.timeout(10_000), // fetch has no timeout of its own
});
} catch (e) {
if (attempt === attempts) throw new JevError(`network error: ${e.message}`);
}
if (r) {
if (r.status === 200) return (await r.json()).answers;
if (![429, 529].includes(r.status) || attempt === attempts) {
throw new JevError(`HTTP ${r.status}: ${(await r.text()).slice(0, 200)}`);
}
}
await sleep(500 * 2 ** attempt);
}
throw new JevError("unreachable");
}
export async function route(state) {
// --- Chunk 3: a judge that is down fails toward MORE scrutiny ---
let a;
try {
a = await askJev(state);
} catch (e) {
if (!(e instanceof JevError)) throw e;
console.error(`[jev] unavailable, escalating: ${e.message}`);
return "review-ambiguous-case";
}
// --- Chunk 4: policy in code. EXAMPLE thresholds -- tune on your own labelled cases ---
const pReg = a.is_regression.noul;
const ft = a.failure_type;
const typeProb = ft.probabilities?.[ft.choice] ?? 0;
if (pReg >= 0.75 && (ft.confidence ?? 0) >= 0.6 && typeProb >= 0.7) return "investigate-with-claude";
if (pReg <= 0.2 && ["flaky", "environment"].includes(ft.choice) && typeProb >= 0.75) {
return "rerun-and-collect-evidence";
}
return "review-ambiguous-case";
}
const failure = {
testName: "test_sla_credit_concurrent_updates",
stdout: "FAILED tests/test_sla.py::test_sla_credit_concurrent_updates\nassert po.credit_total == 2\n where po.credit_total = 4",
stderr: "",
diffHunk: "- with po_lock(po.id):\n- apply_credit(po)\n+ apply_credit(po)",
recentRuns: [{ passed: true, commit: "a41c" }, { passed: false, commit: "b7e2" }],
};
console.log(await route(failure));
// Same policy through the Vercel AI SDK (ai >= 7.0.105) and Vercel AI Gateway.
// npm i ai ; vercel link && vercel env pull (local OIDC tokens expire after 12h -- re-pull on 401)
// NOTE the different vocabulary: "boolean" not "noul", .probability not .noul,
// confidence under providerMetadata, model id "typesafe-ai/jev".
import { experimental_evaluate as evaluate } from "ai";
export async function route(failure) {
let result;
try {
result = await evaluate({
model: "typesafe-ai/jev",
state: failure,
questions: {
isRegression: {
type: "boolean",
instructions: "Did the current code change probably cause this test failure?",
criteria: {
true: "The diff plausibly causes the observed failure.",
false: "The failure is probably flaky, environmental, or unrelated to the diff.",
},
},
failureType: {
type: "choice",
instructions: "What best explains this failure?",
criteria: {
regression: "The current diff introduced a product defect.",
flaky: "The test is nondeterministic or timing-sensitive.",
environment: "Infrastructure or configuration caused the failure.",
unknown: "The evidence is insufficient to choose another category.",
},
},
},
providerOptions: { gateway: { zeroDataRetention: true } },
});
} catch (e) {
console.error(`[jev] unavailable, escalating: ${e.message}`);
return "review-ambiguous-case";
}
const { isRegression, failureType } = result.answers;
const confidence = result.providerMetadata?.typesafe?.confidence?.failureType ?? 0;
const typeProb = failureType.probabilities?.[failureType.choice] ?? 0;
if (isRegression.probability >= 0.75 && confidence >= 0.6 && typeProb >= 0.7) {
return "investigate-with-claude";
}
if (isRegression.probability <= 0.2 && ["flaky", "environment"].includes(failureType.choice) && typeProb >= 0.75) {
return "rerun-and-collect-evidence";
}
return "review-ambiguous-case";
}
With the lab’s fixture for this failure (noul 0.14, flaky at 0.84, confidence 0.58), the output is:
Jev looked at a test that fails because the diff removed a lock, and called it flaky with a regression probability of 0.14. The policy did exactly what it was told and routed it to a rerun. Nothing in this function is wrong; the judge was wrong in a well-formed way.
That is why the next step matters so much. Behind a hard gate, this failure is gone. Behind an uncertainty router, the rerun fails again (it is a real race), the case escalates, and Claude reads the diff and finds the lock. The code is identical; only the architecture decides the outcome.
Measure the System You Are Actually Deploying
Model accuracy alone is not enough. Before changing the loop, freeze a set of historical cases with known outcomes. Then run the complete variants against it, not just the judge:
- Claude-only baseline: every eligible item goes straight to the current coding model.
- Jev-only judgement: is the bounded question itself one Jev answers well?
- Jev hard gate → Claude: only Jev-positive items reach the coding model.
- Jev soft priority → Claude: everything stays visible, but Jev sets the order.
- Jev uncertainty router: clear cases follow policy, and ambiguous ones escalate.
- Cheaper general-model baseline: before adding a whole new component, try a smaller Claude tier such as Haiku 4.5 in the same position.
For every variant, capture:
- End-to-end recall and precisionOf everything the pipeline flagged as a regression, the share that really was one. Low precision means engineers waste time on false alarms., not just the judge’s standalone accuracy
- False negatives broken down by severity. One missed high-severity regression outweighs ten missed cosmetic ones.
- The percentage of items escalated to Claude
- Total input and output tokens across the whole path
- Median and p95 latencyThe latency that 95% of requests beat. The slow tail is what users and CI timeouts feel, even when the median looks fine.
- Retries, timeouts, and what happens when the model is unavailable
- Human-review volume
- The estimated cost of a missed defect, not only the API cost
The lab’s Step 3 does exactly this for four of the variants. Its output, in replay mode with recorded agent verdicts, is:
The hard gate has the best precision on the table (100%) and the worst outcome. Precision only looks at what was flagged; it cannot see what was dropped. Reporting a gated pipeline’s precision alone would make it look like an upgrade. The multiplication line predicts 0.72 and observes 0.70. They differ because errors correlate: the cases a judge finds hard are often the ones an agent finds hard too. That is why you measure instead of just multiplying.
The table above uses recorded verdicts, so its numbers are reproducible. On 2026-09-25 we reran it with --live-agent: Claude Sonnet 5 investigated for real, while Jev stayed on the synthetic fixtures. The run made 24 calls and cost $0.056.
Real Claude was a better agent than the stand-in: it caught every regression it saw, and made one false positiveSomething flagged as a problem that isn’t one. Here: a failure Claude called a regression when it was really environmental. It costs an engineer’s time, not a shipped bug.. The hard gate still scored 80%, because F09 and the high-severity F10 never reached Claude. Improving the agent behind a gate cannot raise recall above the gate’s own. The gate is the ceiling. (Only Claude’s verdicts were measured here. The Jev answers remain synthetic.)
This is the exam’s evaluation domain in a new setting. Expect questions that contrast a component metric (a judge’s accuracy, a guardrail’s precision) with a system metric (end-to-end recall), and that ask which is the right acceptance criterion. The answer is almost always the system metric, measured on a frozen, labelled set. See M18 for eval-set design and M17 for why guardrails escalate to a human instead of silently passing.
When Jev Is a Strong Fit, and When It Isn’t
Jev is most compelling when a decision is narrow, repeated, measurable and reversible:
- Classifying a failure as regression, flaky, environment or unknown
- Prioritising a large queue of test failures or review comments
- Routing clear cases while escalating uncertain ones
- Scoring evidence against an explicit rubric
- Producing typed signals for deterministic policy
- Running an eval on every trace, where an LLM judge would only be affordable on a sample
It is a weaker fit when the task needs repository-wide causal reasoning, arithmetic, multi-step investigation, weighing contradictory evidence, an explanation, or a generated fix. Those remain jobs for deterministic tools, a capable coding model such as Claude, or a human reviewer.
“Reversible” is the word teams skip. Rerunning a test Jev called flaky is reversible: if it fails again, you escalate and have lost a few CI minutes. Auto-merging a change Jev scored low-risk is not reversible, because the regression is in production before anyone looks. Use the same model with the same accuracy in the first place and never in the second.
The Rollout to Trust: Start in Shadow Mode
Do not begin with a hard gate. Begin with shadow modeRunning a new component beside the live system on real traffic, logging what it would have done, without letting it change a single outcome. You compare its would-be decisions with what actually happened.: run Jev beside the existing workflow without changing any route. Promote it one region at a time, only when the disagreement data says it has earned that authority.
Jev runs on every failure; routes are unchanged. Log probabilities, choices, confidence, latency, cost.
exit when: enough volume to measure each regionJoin the log with final outcomes. Investigate every case Jev would have sent away that was a real regression.
exit when: every false negative is explainedJev sets the order; nothing is suppressed. Claude still sees every failure.
exit when: queue is reliably drained, no regressions found lateClear cases follow policy (rerun, then escalate on repeat failure); unsure cases always escalate.
exit when: per-region miss rate is inside your budgetOnly where errors are rare, cheap and reversible. Keep fallbacks for low confidence, timeouts, schema changes and outages.
always: a tested rollback at every rungPress play to climb the ladder.
The lab’s Step 4 turns a shadow log into a recommendation for each region:
This is less dramatic than dropping a cheap judge into every step of the loop. It is also how you find out whether the new component saves money in your system, rather than merely having a low list price.
Jev Inside Claude Code: An Advisory Hook
A PostToolUse hookA command Claude Code runs after a tool call finishes. It receives the call and its result as JSON on stdin, and can reply on stdout, for example to add context for Claude or, if you choose, to block. Covered in M26. runs after every tool call. The lab ships one that watches for Bash test commands. When a test fails, it asks Jev about the failure and hands Claude one line of additionalContextA field in a hook’s JSON reply whose text is added to what Claude sees next. It informs; it does not stop or rewrite anything.. That line is phrased as a hint, and Claude still decides.
Note that the advice is wrong: this is F10 again. That is the whole design. Because the hook only advises, a wrong hint costs one sentence of context, and Claude, reading the diff, sees the removed lock. The hook deliberately never does three things:
- Block: no
"decision": "block". A cheap judge must not gate the agent. - Rewrite: no
updatedToolOutput. Claude sees the real test output. - Fail shut: if Jev is down, the hook prints nothing and exits 0.
- Reading
tool_resultinstead oftool_response.tool_resultis a Messages API content-block type, not a hook field. The hook reads nothing and exits 0. - Echoing the payload to stdout “to pass it through”. Stdout is the decision channel, so a payload there is a malformed decision. Diagnostics go to stderr.
- A flat
{matcher, command}insettings.json. The real shape isevent → [{matcher, hooks: [{type, command, timeout}]}]; the flat one parses and never fires.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python \"$CLAUDE_PROJECT_DIR/labs/BONUS-jev-judge/hooks/jev_advisory_hook.py\"",
"timeout": 15
}
]
}
]
}
}
The hook itself (Python and Node.js) is in labs/BONUS-jev-judge/hooks/. For the full hook contract, see M26. For blocking gates that should block, because they are deterministic, see M22C.
Hands-On Lab: labs/BONUS-jev-judge
The lab works on 24 frozen CI failures from the fictional Northwind order-tracking service (course Domain B), in Python and Node.js. It runs free with no Jev key: without TYPESAFE_API_KEY, Jev answers come from synthetic replay fixtures in the documented response shape. Replay proves your code handles real responses. It says nothing about Jev’s accuracy, and the lab says so every time it runs.
| Step | You write | You’ll see |
|---|---|---|
| 1 · Screen | The Jev call and the policy | F10, a real race, routed to “rerun” |
| 2 · Investigate | One Claude call returning a validated JSON verdict | Claude naming the removed lock |
| 3 · Compare | Hard gate, soft priority and uncertainty router routes | 70% vs. 90% end-to-end recall on identical signals |
| 4 · Shadow | Promotion rules for each region | “Never suppress the rerun region”, backed by data |
| 5 · Hook | Nothing: read and wire up | A wrong hint that costs nothing |
Start with labs/BONUS-jev-judge/README.md. Everything is Tier 1: raw REST for Jev and the raw Messages API for Claude.
Knowledge Check
1. Jev’s schema allows only regression, flaky, environment or unknown. What does that guarantee?
2. A hard gate passes 80% of real regressions to an agent that recognises 90% of what it sees. Ignoring correlation, what is end-to-end recall?
3. The screening function can’t reach Jev (a timeout). What should it return?
4. In the lab, the hard gate shows 100% precision and the uncertainty router shows 90%. What is the right conclusion?
5. Greenberg found that Sonnet’s few errors sat in its 0.9–1.0 confidence band, while Jev’s sat around 0.2–0.3. Why does that matter for a pipeline?
6. You wire Jev into Claude Code as an advisory PostToolUse hook on failing tests. Which of these must the hook never do? (select all that apply, then check)
"decision": "block" when Jev says the failure is flakyupdatedToolOutput to hide failures Jev calls flakyadditionalContextadditionalContext (C) is what it is for, and exiting 0 silently when Jev is down (E) is the correct fail-open behaviour.7. Put the rollout of Jev for flaky-test triage in the right order. (choose a step number for each, then check)
Your Score
Module Summary
Key Concepts
- Jev is a System One model: state and typed questions in, typed answers with probabilities out. It is fast (70–500 ms) and cheap ($0.042 per 1M input tokens), and it writes no prose.
- Typed ≠ correct. The schema removes malformed answers, not wrong ones.
- Probability is not confidence. Probability is about one outcome; confidence is about the whole distribution. Good policies use both.
- Placement decides the cost of an error. A hard gate multiplies its misses into the pipeline. An uncertainty router escalates what it is unsure of and reruns before it dismisses anything.
- Measure systems, not judges. Use end-to-end recall, false negatives by severity and expected total cost on a frozen, labelled set.
- Earn authority. Go from shadow, to disagreement study, to soft priority, to uncertainty routing, to narrow automation, with a rollback at every rung.
- In Claude Code, advise; don’t gate. Jev belongs in
additionalContext, never in"decision": "block".
The right question is not “Is Jev cheaper than Claude?” It is: which bounded decision should Jev own, what happens when it is uncertain, and how will you detect the cases it gets wrong?
How This Maps to the Rest of the Course
| Idea here | Course module(s) |
|---|---|
| Eval sets, end-to-end metrics | M18 |
| Escalate, don’t silently pass | M17 |
| Deterministic quality gates in a coding loop | M22C |
| Model routing on cost | M22 |
| Hook contract, PostToolUse | M26 |
| Routing as a workflow pattern | Bonus: Building Effective Agents |
Sources and Further Reading
- Udaykiran Estari, “Jev + Claude Code: The Cheap Judge That Can Still Cost You”, Medium, September 2026. This module’s source article.
- TypeSafe AI: Introducing System One Models & Jev
- TypeSafe AI: API reference (
/v1/systemone) - Vercel: How to classify, route, and score with Jev and AI SDK
- Ben Greenberg: Jev vs Claude: Who Wins?
- Arize AI: TypeSafe Jev: Can Decision Models Replace LLM Judges?
Illustrative numbers on this page (the 0.90 × 0.97 example and every Jev answer from the lab) are labelled as such. The lab’s Jev answers are synthetic fixtures, not recordings.