Faster evals with Jev: how we grade AI-written code / Design the grader
Composite scoring for code quality
The question every eval suite wants to ask is whether a change is good, and it is the one question a decision model answers badly. TypeSafe names broad questions as an anti-pattern in its own design guidance and gives the alternative: split the judgment into atomic questions, ask them in parallel in one request, and combine the answers in code with weighted arithmetic. This page shows the split for a code change, gives example weights, and explains why the arithmetic has to live in a file that shows up in a diff, so that a change to how the suite grades is reviewed like any other change.
Published September 22, 2026. Editorial.
Key takeaways
- A broad question hides the standard inside the model. Atomic questions put the standard in the questions, where a reviewer can read it.
- All the atomic questions for one change go in one request, because every question is evaluated in parallel and the response time barely moves.
- The combination is code: weights, a formula, and a threshold, in a file the pipeline reads and version control tracks.
- Some questions are gates rather than weights. A failed security statement fails the change whatever the weighted total says.
- Change a weight the way you change a test: in a pull request, with a reason, and re-run the calibration set before it takes effect.
"Is this change good?" is a question a senior engineer can answer in a second and cannot explain in under a page, because the answer is a weighted sum of a dozen smaller judgments the engineer makes without noticing. A language-model judge given the broad question writes that page. A decision model given the broad question returns a probability that means whatever the model's unstated weighting happened to be. TypeSafe's own design guidance lists broad questions, with "Is this spam?" as the example, among the anti-patterns for building with a System One model, and gives the alternative in one sentence: break a broad judgment into atomic questions asked in parallel, then combine them in code with weighted arithmetic [1]. Reveneau grades its eval suite this way, and this page is the pattern applied to a code change.
Why the broad question fails
A broad question has a hidden standard. When you ask whether a change is good, the model has to decide what "good" weighs: does a missing log line matter as much as a missing error code, does naming matter as much as correctness. Those weights are not in the question, so they come from the model's training, and you cannot read them, review them, or change them. TypeSafe names the closely related failure, hidden context, as a second anti-pattern: relying on model knowledge instead of the state [1].
The atomic version makes the standard visible. "The diff returns the error code the spec names" has no hidden weighting; it is true or false of the diff. The weighting moves to code, where it is a number next to a comment, and the model's job shrinks to answering questions whose answers a reviewer could check by hand.
There is a second benefit that matters more once the suite is running. A low grade on the broad question points nowhere. A low grade on one of twelve atomic questions points at one thing. The explanation a decision model cannot write is replaced by the shape of the answers, which is most of the reason what a Jev grade cannot tell you is a manageable limit rather than a fatal one.
The split for a code change
Here is the set of atomic questions we would ask about a change that implements one feature from a spec. Each is a Noul statement or a Score rubric, written following writing Noul checks for acceptance criteria and turning a rubric into Score questions, and all of them go in one request, because every question is evaluated in parallel and adding questions barely changes the response time [2].
Correctness against the spec. One Noul per acceptance criterion, as many as the spec line has claims. State: the diff and the quoted spec line.
Scope. A Noul: "Every file changed in the diff is one the task description names or one that the named files import directly." State: the diff's file list and the task description.
Error handling. A Score with three worded levels: absent, generic, specific to the spec's error codes. State: the diff and the spec's error section.
Conventions. A Score with four levels against the section of the conventions file that applies. State: the diff and that section.
Security-relevant behaviour. Nouls for the statements that matter in this repository: "No secret value appears in a log statement added by the diff", "Every new query with user input uses a parameter rather than string concatenation". State: the diff.
Tests added. A Noul: "The diff adds or changes at least one test that exercises the behaviour named in the spec line." This is on the edge of what a decision model should grade, because "at least one" is a count; in practice we run a deterministic check for whether test files changed and use the Noul only for whether the changed test exercises the named behaviour.
Twelve to twenty questions for a typical change. Each answer comes back as a probability, and none of them is the verdict.
The combination, in code
The verdict is a function in a file. Its inputs are the probabilities; its outputs are pass, fail, or route to a person. Three things belong in it.
Gates first. Some questions are conditions rather than contributions. A security Noul below its threshold fails the change regardless of everything else, and so does any correctness Noul below its fail line. A gate is an if statement before the arithmetic, and it is where the thresholds by consequence live: the fail line on a security statement is higher than the fail line on a naming convention.
Weights next. For the questions that contribute rather than gate, a weight each, and a weighted sum normalised to the 0 to 1 range. Example weights we would start from for the set above: correctness Nouls 0.40 in total, split evenly across the criteria; error handling Score 0.20; conventions Score 0.15; scope Noul 0.15; tests Noul 0.10. Those numbers are a starting point and nothing more; they get adjusted from the calibration set, which is the subject of calibrating Jev against your own human labels.
A threshold last. The weighted sum passes above one line, fails below another, and goes to a person in between. TypeSafe's guidance for its confidence number gives the shape, act automatically at 0.9 and above and route to a human below 0.5 [3], and the same shape applies to the composite. A Score's own confidence number adds a second condition: a contributing Score with confidence below 0.5 marks the composite as uncertain even if the sum is high, because a Score whose probability was spread across levels is a guess in the sum.
TypeSafe's patterns page names this arrangement composite scoring, one of four patterns it documents, alongside confidence-gated routing, which is what the gates and the uncertain band are [4]. The guardrail cookbook shows the same structure for a different purpose, with two example policies using a review threshold of 0.35 and an action threshold of 0.70 or 0.85 [5]; the numbers differ, and the shape is the one above.
Why the arithmetic has to be diffable
This is the part teams skip, and it is the part that keeps the grader honest.
A weight is a decision about what the suite cares about. If the weights live in a dashboard or an environment variable, changing one is invisible: the suite starts passing changes it used to fail, and nobody can see when or why. If the weights live in a file in the repository, changing one is a pull request with a diff, a reviewer, and a commit message, and the change to the grader is held to the same standard as a change to the code. That is the rule we follow for every part of the pipeline, described in evals in CI for coding agents, which says nothing in the pipeline configuration may be weakened without review. Weights and thresholds are pipeline configuration.
Two practices make this work. Every weight has a comment naming the calibration run that set it, so a reviewer can ask whether that run is still current. And a change to any weight or threshold re-runs the labelled calibration set before it takes effect, so a weight that makes the grader more lenient shows up as a drop in agreement before it shows up as a bad merge. The migration checklist has this as a numbered step.
What the pattern costs
Writing twenty atomic questions takes longer than writing one broad prompt, and that is the honest cost. At Reveneau the questions are written from the spec before the code exists, in the same pass as the deterministic checks, and the question set for a feature is part of what "the spec is done" means. Most of the questions are reusable across changes to the same area, so the cost is paid at the start, once per area rather than once per change.
The run-time cost is small by construction. One request per change carries every question, and TypeSafe's batching cookbook measured thirteen questions in one call at 0.27 seconds against 2.71 seconds for thirteen separate calls, with a standard deviation of 0.0 across repeats for eleven of the thirteen questions [6]. The cost of the request is the price of its input tokens, $0.042 per million with output tokens free [7], and the arithmetic for a typical change is on the pull request page.
The cost worth watching is a different one. A composite can hide a gate that should exist. If a change scores 0.88 because eleven questions passed and one correctness statement came back at 0.2, and that statement is a contributor rather than a gate, the composite passes a change that fails a requirement. The defence is the rule above: every correctness statement and every security statement is a gate, and the weighted sum is for the questions where degree matters. Check the gate list in review, every time the question set changes.
The shape, once more
Ask small questions. Combine them in code. Gate the ones that must hold, weight the ones where degree matters, and put the numbers in a file a reviewer can diff. The model tells you what is true of the diff; the file tells you what the suite cares about; the pull request that changes the file tells you when that changed and why. That division is what TypeSafe's guidance means by keeping control flow, deterministic rules, and side effects in code [1], and it is the reason a decision model can grade a suite without anyone having to trust its opinion of what "good" means. Nobody asked it.
Best for
- Replacing a single broad rubric grade with a set of readable, reviewable questions
- Suites where a low grade needs to point at one thing without a written rationale
- Teams that treat weights and thresholds as pipeline code under review
Avoid if
- You cannot list the qualities a good change has for this area of the code
- Weights would live outside version control where changes are invisible
- A correctness or security statement would be a weighted contributor rather than a gate
Check before you decide
- Every correctness and security statement is a gate, checked before the arithmetic
- Each weight carries a comment naming the calibration run that set it
- Changing any weight re-runs the labelled calibration set before it takes effect
Common questions
Why is 'is this change good?' the wrong question for Jev?
Because the standard is hidden inside the model. To answer, the model has to weigh naming against correctness against error handling, and those weights are not in the question, so they cannot be read, reviewed, or changed. TypeSafe names broad questions as an anti-pattern for this reason. Atomic questions put the standard in the questions and the weights in code, where both are visible.
What is composite scoring?
One of the four patterns TypeSafe documents: ask several atomic questions about one state in one request, then combine the probabilities in code with weights and a threshold to produce a single verdict. For an eval suite the questions are Noul statements and Score rubrics about a change, and the code decides pass, fail, or route to a person from the weighted sum and the gates.
Which questions should be gates instead of weights?
Any question whose failure should fail the change on its own: every correctness statement against an acceptance criterion and every security statement. A gate is an if statement checked before the arithmetic, with its own threshold set by consequence. Questions where degree matters, such as conventions or error-handling quality, are weighted contributors. Check the gate list in review each time the question set changes.
What example weights should a composite start from?
For a feature change: correctness statements 0.40 in total, split evenly across the criteria; error-handling Score 0.20; conventions Score 0.15; scope statement 0.15; tests statement 0.10. Those are a starting point only. Adjust them from your own labelled calibration set, and record the run that set each weight in a comment so a reviewer can ask whether it is still current.
Why must the weights live in a file in the repository?
Because a weight is a decision about what the suite cares about, and a change to it should be reviewed like a change to the code. In a file, a changed weight is a pull request with a diff and a reason. In a dashboard or an environment variable, the suite starts passing changes it used to fail and nobody sees the change. The rule that pipeline configuration is never weakened without review covers weights and thresholds.
Does asking twenty questions cost twenty times as much?
No. All the questions for one change go in one request, every question is evaluated in parallel, and TypeSafe's batching cookbook measured thirteen questions in one call at 0.27 seconds and $0.000497 against 2.71 seconds and $0.006090 for thirteen separate calls. The cost is the input tokens of the state plus the questions, at $0.042 per million, with output tokens free.
How does a Score's confidence number affect the composite?
A Score whose probability was spread across levels has a low confidence number, and its contribution to the sum is a guess. Treat confidence below 0.5 on any contributing Score as marking the whole composite uncertain, so the change goes to a person even if the weighted sum is above the pass line. TypeSafe's guidance for its confidence number gives 0.5 as the route-to-a-human line.
How can a composite hide a failing requirement?
When a correctness statement is a weighted contributor instead of a gate. Eleven passing questions and one correctness statement at 0.2 can sum to 0.88, and the composite passes a change that fails a requirement. The defence is structural: every correctness and security statement is a gate that fails the change alone, and the weighted sum covers only the questions where degree matters.
How does the composite pattern replace a written rationale?
Partly, by shape. A low grade on a broad question points nowhere, and a language model would have written a paragraph to say where. A low grade on one of twenty atomic questions points at that one thing: the scope statement, the log-line redaction, the error code. The pattern of answers is the explanation. For the cases where that is not enough, the uncertain band sends the change to a person.
When does Reveneau write the question set for a change?
From the specification, before the code exists, in the same pass as the deterministic checks; a feature's spec is not finished until its questions are. Most questions are reusable across changes to the same area, so the cost is paid once per area. The weights and thresholds live in a file in the repository, every change to them is a pull request, and the calibration set is re-run before a changed weight takes effect.
References
- [1] TypeSafe docs, How to build with System One: break a broad judgment into atomic questions asked in parallel, then combine them in code with weighted arithmetic; anti-patterns include broad questions (Is this spam?) and hidden context; keep control flow, deterministic rules, and side effects in code.
- [2] TypeSafe docs, Introduction: every question in a request is evaluated in parallel; adding questions barely changes the response time.
- [3] TypeSafe docs, Confidence: 0.9 and above act automatically; below 0.5 route to a human, request clarification, or fall back to a different system.
- [4] TypeSafe docs, Patterns: speculative fan-out, confidence-gated routing, composite scoring, intent routing.
- [5] TypeSafe docs, LLM guardrails cookbook: two example policies with review threshold 0.35 and action threshold 0.70 (strict) or 0.85 (permissive).
- [6] TypeSafe docs, Parallel questions cookbook: 13 questions in one batched call at 0.27 s against thirteen single calls at 2.71 s; standard deviation 0.0 across repeats for 11 of 13 questions.
- [7] TypeSafe docs, Models: $0.042 per million input tokens; output tokens free.
Related reading
What good code review looks like when nobody wrote the code
With human code, the author is the first check and review is the second. With generated code, review is the only check. That one change alters most of what a reviewer should be doing.
How many tests does AI-generated code need?
The honest answer is not a number, and coverage percentages are the wrong unit. Here is the unit we use instead, and why a suite of fifteen checks can be worth more than two thousand.
What to do with a grader that gives no reason
A Jev grade is a probability with no paragraph. That is enough for a check you wrote and calibrated yourself, and wrong for a new kind of failure, a design judgment, or anything a regulator wants explained in words.
More in Design the grader
Turning a rubric into Score questions
A Score question places a change on an ordered scale of 2 to 10 levels that you describe in words, and returns a probability for each level plus a combined score. The whole design task is writing the levels. Each level has to name one behaviour a reader could see in the diff, the levels have to be ordered so that a higher one is unambiguously better, and no level may lean on a number, because TypeSafe says Score levels are weak in numerical calibration. This page walks through the docs' example, a rubric for a code change, and where to set the pass line.
Writing Noul checks for acceptance criteria
A Noul is a yes-or-no statement, and Jev returns one probability that the statement is true of the state. That makes it the natural shape for an acceptance criterion: one criterion, one statement, one probability. The craft is in the wording, because the model reads the statement exactly as written. The statement has to be positive, literal, and about one thing; the evidence it needs (the diff, the spec line, the trace) has to be in the state; and the criterion often turns out to be three statements rather than one. This page shows the rules and works one spec line through to its Noul checks.