Read this before you ship
TypeSafe publishes a page listing its own model's weaknesses, which is more than most labs do. It is also the most useful page they have written. This is that list, plus what it means for the code you are about to deploy.
By the end you'll be able to
- Recognise the judgment shapes Jev is bad at
- Move arithmetic, counting and date logic into code
- Write instructions that survive a literal reading
- Treat state as a security boundary
On this page
- 1. It answers the question you wrote
- 2. It is not a calculator
- 3. Dates are text, not ordered quantities
- 4. Indirection costs accuracy
- 5. Irrelevant context is a cost, not a cushion
- 6. State is not treated as hostile
- 7. Criteria that argue with the instruction
- 8. Structural invariants do not hold
- 9. It does not generate
- The four-line summary TypeSafe puts at the bottom
- And one failure mode that is not on their list
- Next
| # | Failure mode | Do this instead |
|---|---|---|
| 1 | Literal reading | Write the exact condition, criteria for each available option |
| 2 | Math and numbers | Keep the arithmetic in code |
| 3 | Date and time comparison | Extract components; compare in code |
| 4 | Indirection | Reduce hops; point to the relevant state |
| 5 | Large state full of irrelevant detail | Filter first; send only what the question needs |
| 6 | Adversarial content | Write precise prompts, and test edge cases before deploying |
| 7 | Contradictory instructions and criteria | Align the criteria and instruction |
| 8 | Common-sense structural invariants | Ask each decision one way; enforce identities in code |
| 9 | Generation | Use a generative model |
1. It answers the question you wrote#
Scoping words, negations and implied conditions are read at face value. A person reads intent; jev-1.13 reads words.
# Vague — relies on shared understanding of "recent" and "problem".
Noul(instructions="Has this customer had problems recently?")
# Literal — survives being read exactly as written.
Noul(
instructions=(
"Does `ticket_history` contain a ticket with status 'escalated' "
"in the last 30 days, according to `days_since` on each ticket?"
),
criteria={
"true": "At least one escalated ticket with days_since <= 30",
"false": "No escalated tickets, or all are older than 30 days",
},
)Though note what the second example is really telling you: if days_since already exists as a number, the comparison belongs in code and the model has nothing to add. Which is failure mode 2.
2. It is not a calculator#
TypeSafe recommends implementing any mathematical logic in code, without qualification. Three sub-cases are named:
- Counting. Characters in a word, occurrences of a term, items in a list. "The model recognizes the shape of an answer rather than tallying, and the error grows with the size of the thing being counted."
- Numeric representations. Hex colours underperform colour names. Assembly underperforms high-level languages. Given RGB triples it cannot reliably judge whether two values are near each other.
- Interpolating a Score. You may threshold the expectation. You may not reconstruct an exact number between two levels —
jev-1.13's score levels are weak in numerical calibration.
The counting workaround is the fan-out pattern: one Noul per item, sum in code. Before you write it, though, apply TypeSafe's own test — if a regular expression or a parser can find the unit, the count belongs in code and the model has nothing to add.
result = client.system_one(
{"items": items},
{
f"item_{i}": Noul(instructions=f"Is `items[{i}]` the name of a fruit?")
for i in range(len(items))
},
)
count = sum(result.nouls[f"item_{i}"].noul > 0.5 for i in range(len(items)))3. Dates are text, not ordered quantities#
Asking which of two dates comes first, how far apart they are, or whether one falls inside a window is unreliable — and it gets worse with mixed formats, relative references, and domain boundaries like quarters or settlement windows.
The fix is a genuinely elegant division of labour. Every part of a date is a small closed set: twelve months, thirty-one days, a bounded range of years. That makes extraction a Choice over enumerated options rather than free-form parsing — and it gives you somewhere to put an explicit "not stated" option, so a missing part is reported rather than guessed.
from typesafe_sdk import Choice
MONTHS = ["january", "february", "march", "april", "may", "june", "july",
"august", "september", "october", "november", "december"]
questions = {
"month": Choice(
instructions="Which month does `document` name as the effective date?",
criteria={**{m: None for m in MONTHS}, "not_stated": "No month is given"},
),
"year": Choice(
instructions="Which year does `document` name as the effective date?",
criteria={**{str(y): None for y in range(2020, 2031)},
"not_stated": "No year is given"},
),
}
# Code assembles the parts and owns everything after that:
# ordering, duration, offset, weekday.Judgment to the model, arithmetic to the runtime. TypeSafe's date extraction cookbook has the full version including relative dates and confidence gating.
4. Indirection costs accuracy#
Double negatives and multi-hop reasoning — a property of a property, a conclusion that requires chaining two facts — are answered less reliably. Write instructions as directly as you can, and name the relevant parts of state explicitly rather than making the model find them.
5. Irrelevant context is a cost, not a cushion#
Covered in full in Designing state. The short version: accuracy falls as the state grows with content unrelated to the decision, and a large state makes a wrong answer harder to diagnose. Retrieve and filter in code first.
6. State is not treated as hostile#
State is data, and jev-1.13 does not treat it as hostile by default. Content written to adversarially steer the model, whether that is an injected instruction, a deliberately misleading framing, or text that argues for its own classification, can move the answer. We expect to improve on this in the future.- Never let a single Jev answer be the last thing between untrusted input and an irreversible action.
- Keep the consequences of a decision under code control — permissions, limits and rollback do not belong in the model.
- Build an adversarial test set before launch, not after an incident.
- Consider a separate screening request over the raw input before the decision request sees it, as in the RAG passage cookbook.
7. Criteria that argue with the instruction#
When instructions and criteria pull in different directions, the model gets confused. TypeSafe's example: a Noul where true maps to "no" and false maps to "yes" will perform worse. Treat the criteria as an extension of the instruction, and aim for phrasing an average reader would find unambiguous.
8. Structural invariants do not hold#
Covered in Noul, Choice and Score, and worth repeating because it breaks intuition rather than merely bending it. A Noul and an equivalent yes/no Choice can disagree flatly, and a question and its negation do not sum to 1.
| Ticket | Question | Noul | Choice yes | Choice no | Choice confidence |
|---|---|---|---|---|---|
| "I'm not happy with the fit. What are my options here?" | Is the customer asking for a refund? | 0.22 | 0.01 | 0.99 | 0.97 |
TypeSafe notes the model is otherwise extremely consistent — semantically similar inputs give quantitatively similar outputs. The inconsistency is across primitives, not across inputs. Ask each decision one way, and enforce any identity you need in code.
9. It does not generate#
Jev is not trained to produce text. You can force it by chaining Choices; it will be bad and very slow. When you need a value out of a document, the move is to bound the answer space first — find candidates with a regex or a generative model, then have Jev pick the right one.
TypeSafe's pre-parsed value extraction cookbook does exactly this: regexes find candidate emails, phone numbers and amounts; Jev selects the requested span; code normalises the verbatim value.
The four-line summary TypeSafe puts at the bottom#
And one failure mode that is not on their list#
Jev returns no rationale. There is no explanation field, and none is planned as far as anyone has said. For debugging that means your only diagnostic tool is the distribution — which is a real reason to log probabilities, not just the summary value. For regulated decisions it means Jev cannot be the system of record for why; the common workaround, suggested by DataCamp, is to reserve Jev for the high-volume routing layer and escalate flagged or low-confidence cases to a model that can produce a written explanation.
The other route to an explanation is the one this site keeps coming back to: decompose. A single opaque score explains nothing, but five named sub-scores and a weight vector in version control explain a great deal — not because the model told you, but because you built the structure that the answer had to fit.
Next#
You know where it breaks. Now the numbers: what has actually been measured, by whom, and how to benchmark it yourself. Continue to What the numbers actually say.
Sources for this page
- TypeSafe — jev-1.13 jaggedness
- TypeSafe — Date extraction cookbook
- TypeSafe — Pre-parsed value extraction cookbook
- TypeSafe — Classifying RAG passages cookbook
- DataCamp — System One models and Jev
Last reviewed 2026-09-18. Jev is days old and moving — where a claim is TypeSafe's own rather than independently verified, this page says so in the sentence that carries it.