Faster evals with Jev: how we grade AI-written code / 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.
Published September 22, 2026. Editorial.
Key takeaways
- A Score is 2 to 10 levels in words. The score is each level number multiplied by its probability, added up, so 1.43 means the model split its probability between levels 1 and 2.
- Write one observable behaviour per level. A level that describes a feeling or a count cannot be graded reliably.
- Keep numbers out of the levels. TypeSafe lists numerical calibration as a weakness of Score, and a level such as 'fewer than three issues' asks the model to count, which it does badly.
- Use the fewest levels that separate the outcomes you act on differently. Three levels with a clear pass line beat ten levels nobody can tell apart.
- Set the pass line on the combined score and treat the confidence number as a second gate, so a score that passes with the probability spread across levels still goes to a person.
Most rubrics start life as a table with numbers down the side: 1 is poor, 5 is excellent, and a paragraph of adjectives in between. That table was written for a person, and a person fills in the meaning of "poor" from experience. A decision model has no experience to fill it in from. It has the words you wrote, read exactly as written, which TypeSafe's own jaggedness page states as the first weakness of the model: it answers the question you wrote, not the one you meant [1]. Rewriting a rubric as a Score question is the work of making the words carry the whole meaning. Reveneau grades its eval suite with Jev, and every rubric in it went through the process on this page.
What a Score returns
TypeSafe's Score primitive takes a spectrum of 2 to 10 levels, each described in words, and returns a probability for each level, a combined score, and a confidence number. The score is each level number multiplied by its probability, added up. The docs' own example returns a score of 1.43 with confidence 0.35 and probabilities of 0.0 on level 0, 0.57 on level 1, and 0.43 on level 2 [2]. Read that as: the model thinks the state is most likely at level 1, with a real chance it is at level 2, and it is not confident which.
Two things follow. The score is continuous even though the levels are discrete, so 1.43 is a real answer and a pass line at 1.5 is a meaningful threshold. And the confidence number, which for Choice and Score is computed from how spread out the probabilities are [3], tells you whether the score is a decision or an admission that the model cannot tell. A score of 1.5 from probabilities of 0.5 on level 1 and 0.5 on level 2 is that admission. The full definitions are in Jev and System One models; this page uses them.
Rule one: one observable behaviour per level
A level is a sentence that describes something a reader could point at in the state. For a code change, the state is the diff and the spec line, so the level has to describe something visible in a diff.
Take a rubric for error handling that a person would have written as "1: no error handling, 2: some error handling, 3: good error handling". "Some" and "good" are judgments, and a judgment is what the model is supposed to produce, so putting one in the level asks the model to grade against its own opinion. Rewrite each level as a behaviour:
Level 0: the new code path has no handling for a failed call; an exception from the dependency propagates to the caller unchanged.
Level 1: the failed call is caught, and the handler returns a generic error to the caller without the error code the spec names.
Level 2: the failed call is caught, the handler returns the error code the spec names, and the failure is logged with the request identifier.
Each level names things a reader could find: a catch block, an error code, a log line. The ordering is clear, and a model that reads exactly as written has something to read. This is the same discipline as writing a testable spec line, covered in writing specs an agent can verify, applied to the grader rather than the requirement.
Rule two: keep numbers out
TypeSafe lists Score levels as weak in numerical calibration, and separately says the model does not count reliably and cannot reliably judge whether two numeric values are near each other [1]. A level that says "fewer than three unhandled cases" asks the model to count unhandled cases and compare the count to three, which is two of the three listed weaknesses in one sentence.
The fix is to describe what the count would look like instead of the count. "No unhandled case remains in the new code path" is a level. "One or more unhandled cases remain" is the level below it. If you need to know how many, that is a deterministic check: a static analysis rule, a test per case, or a lint rule (an automated style and error check), all of which count correctly and cost nothing. What to check in an eval suite covers where those belong.
The same rule applies to dates, sizes, and percentages. "The migration completes within the maintenance window" is a level that asks the model to compare a duration to a window; the model reads dates as text [1]. Make the duration a deterministic measurement and grade only what has no exact answer.
Rule three: fewest levels that separate the outcomes you act on
TypeSafe allows up to 10 levels [2]. Most eval rubrics need three or four, and the test for the right number is whether your pipeline does something different at each level. If levels 2 and 3 both pass and both do nothing else, they are one level. If level 1 fails and level 0 fails and there is no difference in what happens next, they are one level.
There are two reasons to keep the count low. Adjacent levels that differ by a shade of wording split the probability between them and pull the confidence number down, so a ten-level rubric produces low-confidence scores on changes that a three-level rubric would grade cleanly. And every level is a sentence that has to be maintained when the spec changes. Fewer sentences, fewer places for the rubric to drift from the requirement.
The one case for more levels is when the score feeds a composite, where you combine several Scores in code with weights, and you want finer resolution on one dimension. That is the pattern in composite scoring for code quality, and even there five levels is usually the ceiling.
A worked rubric for a code change
Here is a Score we would write for the question of whether a change is consistent with the conventions file in a repository. The state holds the diff, the relevant section of the conventions file, and nothing else, because TypeSafe says accuracy falls as the state grows with unrelated content [1].
Level 0: the diff introduces a pattern the conventions file names as prohibited, for example a direct database call from a route handler where the file requires a repository layer.
Level 1: the diff follows the required structure but names things in a way the conventions file rules out, for example an abbreviation in a public function name where the file requires full words.
Level 2: the diff follows the required structure and the naming rules, and any deviation is in a file the conventions document exempts.
Level 3: the diff follows every rule in the section provided and also extends an existing pattern rather than adding a parallel one.
Four levels. Each names something a reader could find by comparing the diff to the conventions text. Levels 0 and 1 fail, levels 2 and 3 pass, and the difference between 2 and 3 exists because we record it as a quality signal over time even though both pass. The pass line is 1.5.
Note the phrase "in the section provided". The model has only the state, and TypeSafe names hidden context, relying on model knowledge instead of the state, as an anti-pattern [4]. If the conventions section is not in the state, the model is grading against nothing, and the words in the level should say what it is grading against.
Reading a score between levels
With the pass line at 1.5, a score of 1.43 fails and a score of 1.62 passes. Both are near the line, and both probably came from probability split across levels 1 and 2. The confidence number tells you how split. TypeSafe's guidance is to act automatically at 0.9 and above and to route to a person below 0.5 [3], and for a Score we apply that as a second gate on top of the pass line: a score above 1.5 with confidence below 0.5 does not pass automatically; it goes to a person with the score, the probabilities, and the diff. Thresholds, confidence and escalation covers the bands and where to set them by consequence.
The person's label matters more than the grade in these cases, because it is the label that tells you whether the rubric or the model is at fault. If people keep passing changes the model scored at 1.4, the levels are too strict or too vague near the line. If people keep failing changes at 1.6, the level 2 description is letting something through. Either way the fix is in the words, and calibrating Jev against your own human labels is how to find out which.
Where the rubric comes from
At Reveneau the rubric is written from the specification before the code exists, in the same pass as the deterministic checks, and that order is not negotiable. A rubric written after the code is finished describes the code, and the point of never let the model grade its own work is that a check derived from the implementation can only agree with it. The Score levels are the standard; the diff has to meet them; the grader reports how well it does.
Rewriting our rubrics as Score questions took longer than switching the API call did. Every level that said "adequate" or "mostly" had to become a sentence about something in the diff, every number had to move to a deterministic check, and several rubrics lost half their levels because the pipeline did the same thing at each. The rubrics are better for it. A level a decision model can grade is also a level two engineers will agree on, which the older guide on using a model as a judge names as the precondition for any model grading at all.
Write the level you could point at in the diff. Then the score means what it says.
Best for
- Rubrics where each level can be described as a behaviour visible in the diff or trace
- Quality dimensions you want to track over time as well as pass or fail
- Replacing a language-model rubric grade that was slow or varied between runs
Avoid if
- Any level would need a count, a date comparison, or a numeric threshold to grade
- Two of your own engineers would not agree which level a given change sits at
- The rubric has to catch problems nobody has described in a level yet
Check before you decide
- Every level names one observable behaviour and no number
- The pass line sits between two levels your pipeline treats differently
- A score above the pass line with confidence below 0.5 still goes to a person
Common questions
How many levels should a Score rubric have?
The fewest that separate outcomes your pipeline treats differently, which is usually three or four. TypeSafe allows 2 to 10. Adjacent levels that differ only in wording split the probability between them and lower the confidence number, so a ten-level rubric produces uncertain scores on changes a three-level one grades cleanly. Add a level only when something different happens at it.
How is a Score computed when it lands between levels?
Each level number is multiplied by its probability and the results are added up. The docs' example gives probabilities of 0.0 on level 0, 0.57 on level 1, and 0.43 on level 2, which yields a score of 1.43. The number between levels is a real answer: it says the model leans to level 1 with 0.43 on level 2, and the confidence number of 0.35 says the lean is weak.
Why should a rubric level never contain a number?
Because TypeSafe lists Score levels as weak in numerical calibration and separately says the model does not count reliably and cannot reliably compare two numeric values. A level such as fewer than three unhandled cases asks for a count and a comparison in one sentence. Describe what the count would look like instead, and move the count itself to a deterministic check such as a linter or a test per case.
Where should the pass line go on a Score?
Between the two levels your pipeline treats differently, and at the midpoint unless calibration says otherwise. On a four-level rubric where 0 and 1 fail and 2 and 3 pass, the line is 1.5. Then treat the confidence number as a second condition: a score above the line with confidence below 0.5 goes to a person rather than passing automatically, because the probability was split across levels.
What should be in the state for a rubric grade?
Only what the levels refer to: the diff, the spec line or conventions section the levels mention, and nothing else. TypeSafe says accuracy falls as the state grows with content unrelated to the decision, and names hidden context, relying on model knowledge instead of the state, as an anti-pattern. If a level says the conventions file, the conventions section must be in the state, or the model is grading against nothing.
Can one Score grade several qualities at once?
No, and the attempt produces levels nobody can order. A level that combines naming, structure, and error handling cannot say which is better, a change with good naming and bad error handling or the reverse. Write one Score per quality, ask them in the same request so they run in parallel, and combine them in code with weights, which is the composite scoring pattern TypeSafe recommends for broad judgments.
How do I know whether a bad score is the rubric's fault or the model's?
From the labels people give on the uncertain band. If reviewers keep passing changes the model scored just below the line, the level descriptions near the line are too strict or too vague. If reviewers keep failing changes just above it, the passing level is letting something through. Both fixes are in the words. Only when the levels are clear and agreement is still low is the model the limit.
When should a rubric be written?
From the specification, before the code exists, in the same pass as the deterministic checks. A rubric written after reading the finished code describes what the code does and can only agree with it. Reveneau writes every Score rubric this way and treats the levels as the standard the diff has to meet, so the grader reports how well the change meets a standard that existed before the change.
Should a rubric use a level for problems not yet described?
No. A level such as some other serious problem is present asks the model to grade against an open list, which is the broad question TypeSafe names as an anti-pattern, and the answer will be unreliable. A decision model grades what the levels describe. For problems nobody has described yet, keep an advisory language-model pass or a person on the changes where a missed unknown would cost most.
How do Score rubrics compare with the small-scale rule for LLM judges?
They agree. The older guidance for a language-model judge was to use pass or fail or a three-point scale, because asking for a score out of ten produces sevens. A Score question makes the same point structural: each level is a worded behaviour rather than a number, and the model returns a probability per level. The discipline of few, clearly separated levels carries over unchanged.
References
- [1] TypeSafe docs, Model jaggedness for jev-1.13: answers the question you wrote, not the one you meant; Score levels are weak in numerical calibration; does not count reliably; cannot reliably judge whether two numeric values are near each other; reads dates as text; accuracy falls as the state grows with unrelated content.
- [2] TypeSafe docs, Score primitive: 2 to 10 levels described in words; the score is each level number multiplied by its probability, added up; example score 1.43, confidence 0.35, probabilities {0: 0.0, 1: 0.57, 2: 0.43}.
- [3] TypeSafe docs, Confidence: a single number from 0 to 1 computed from how spread out the probabilities are; 0.9 and above act automatically; below 0.5 route to a human.
- [4] TypeSafe docs, How to build with System One: hidden context (relying on model knowledge instead of the state) is an anti-pattern.
Related reading
How to write an acceptance test a machine can run
Most acceptance criteria are written for a human reader who will add the missing details. A machine adds nothing. Here is how to write the sentence so an automated check can enforce it.
What a good technical spec looks like when a model writes the code
The old advice was to keep specs short and stop where writing the code is faster. That advice assumed a person was reading it. When a model writes the code, the cost of an unanswered question changes, and so does the right length of a spec.
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
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.
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.